Use a more natural guard for the callback definition
[kugel-rb.git] / apps / plugins / jewels.c
blob74e8d6ec111ebe6e36f75df41e94bf6ad9e2b4ca
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 #ifdef HAVE_LCD_BITMAP
32 PLUGIN_HEADER
34 /* button definitions */
35 #if CONFIG_KEYPAD == RECORDER_PAD
36 #define JEWELS_UP BUTTON_UP
37 #define JEWELS_DOWN BUTTON_DOWN
38 #define JEWELS_LEFT BUTTON_LEFT
39 #define JEWELS_RIGHT BUTTON_RIGHT
40 #define JEWELS_SELECT BUTTON_PLAY
41 #define JEWELS_CANCEL BUTTON_OFF
42 #define HK_SELECT "PLAY"
43 #define HK_CANCEL "OFF"
45 #elif CONFIG_KEYPAD == ARCHOS_AV300_PAD
46 #define JEWELS_UP BUTTON_UP
47 #define JEWELS_DOWN BUTTON_DOWN
48 #define JEWELS_LEFT BUTTON_LEFT
49 #define JEWELS_RIGHT BUTTON_RIGHT
50 #define JEWELS_SELECT BUTTON_SELECT
51 #define JEWELS_CANCEL BUTTON_OFF
52 #define HK_SELECT "SELECT"
53 #define HK_CANCEL "OFF"
55 #elif CONFIG_KEYPAD == ONDIO_PAD
56 #define JEWELS_UP BUTTON_UP
57 #define JEWELS_DOWN BUTTON_DOWN
58 #define JEWELS_LEFT BUTTON_LEFT
59 #define JEWELS_RIGHT BUTTON_RIGHT
60 #define JEWELS_SELECT BUTTON_MENU
61 #define JEWELS_CANCEL BUTTON_OFF
62 #define HK_SELECT "MENU"
63 #define HK_CANCEL "OFF"
65 #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD)
66 #define JEWELS_UP BUTTON_UP
67 #define JEWELS_DOWN BUTTON_DOWN
68 #define JEWELS_LEFT BUTTON_LEFT
69 #define JEWELS_RIGHT BUTTON_RIGHT
70 #define JEWELS_SELECT BUTTON_SELECT
71 #define JEWELS_CANCEL BUTTON_OFF
72 #define JEWELS_RC_CANCEL BUTTON_RC_STOP
73 #define HK_SELECT "SELECT"
74 #define HK_CANCEL "OFF"
76 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
77 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
78 #define JEWELS_SCROLLWHEEL
79 #define JEWELS_UP BUTTON_MENU
80 #define JEWELS_DOWN BUTTON_PLAY
81 #define JEWELS_LEFT BUTTON_LEFT
82 #define JEWELS_RIGHT BUTTON_RIGHT
83 #define JEWELS_PREV BUTTON_SCROLL_BACK
84 #define JEWELS_NEXT BUTTON_SCROLL_FWD
85 #define JEWELS_SELECT BUTTON_SELECT
86 #define JEWELS_CANCEL (BUTTON_SELECT | BUTTON_MENU)
87 #define HK_SELECT "SELECT"
88 #define HK_CANCEL "SEL + MENU"
90 #elif (CONFIG_KEYPAD == IPOD_3G_PAD)
91 #define JEWELS_LEFT BUTTON_LEFT
92 #define JEWELS_RIGHT BUTTON_RIGHT
93 #define JEWELS_UP BUTTON_SCROLL_BACK
94 #define JEWELS_DOWN BUTTON_SCROLL_FWD
95 #define JEWELS_SELECT BUTTON_SELECT
96 #define JEWELS_CANCEL BUTTON_MENU
97 #define HK_SELECT "SELECT"
98 #define HK_CANCEL "MENU"
100 #elif CONFIG_KEYPAD == IRIVER_IFP7XX_PAD
101 #define JEWELS_UP BUTTON_UP
102 #define JEWELS_DOWN BUTTON_DOWN
103 #define JEWELS_LEFT BUTTON_LEFT
104 #define JEWELS_RIGHT BUTTON_RIGHT
105 #define JEWELS_SELECT BUTTON_SELECT
106 #define JEWELS_CANCEL BUTTON_PLAY
107 #define HK_SELECT "SELECT"
108 #define HK_CANCEL "PLAY"
110 #elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
111 #define JEWELS_UP BUTTON_UP
112 #define JEWELS_DOWN BUTTON_DOWN
113 #define JEWELS_LEFT BUTTON_LEFT
114 #define JEWELS_RIGHT BUTTON_RIGHT
115 #define JEWELS_SELECT BUTTON_SELECT
116 #define JEWELS_CANCEL BUTTON_POWER
117 #define HK_SELECT "SELECT"
118 #define HK_CANCEL "POWER"
120 #elif CONFIG_KEYPAD == GIGABEAT_PAD
121 #define JEWELS_UP BUTTON_UP
122 #define JEWELS_DOWN BUTTON_DOWN
123 #define JEWELS_LEFT BUTTON_LEFT
124 #define JEWELS_RIGHT BUTTON_RIGHT
125 #define JEWELS_SELECT BUTTON_SELECT
126 #define JEWELS_CANCEL BUTTON_POWER
127 #define HK_SELECT "SELECT"
128 #define HK_CANCEL "POWER"
130 #elif CONFIG_KEYPAD == SANSA_E200_PAD
131 #define JEWELS_SCROLLWHEEL
132 #define JEWELS_UP BUTTON_UP
133 #define JEWELS_DOWN BUTTON_DOWN
134 #define JEWELS_LEFT BUTTON_LEFT
135 #define JEWELS_RIGHT BUTTON_RIGHT
136 #define JEWELS_PREV BUTTON_SCROLL_BACK
137 #define JEWELS_NEXT BUTTON_SCROLL_FWD
138 #define JEWELS_SELECT BUTTON_SELECT
139 #define JEWELS_CANCEL BUTTON_POWER
140 #define HK_SELECT "SELECT"
141 #define HK_CANCEL "POWER"
143 #elif (CONFIG_KEYPAD == SANSA_FUZE_PAD)
144 #define JEWELS_SCROLLWHEEL
145 #define JEWELS_UP BUTTON_UP
146 #define JEWELS_DOWN BUTTON_DOWN
147 #define JEWELS_LEFT BUTTON_LEFT
148 #define JEWELS_RIGHT BUTTON_RIGHT
149 #define JEWELS_PREV BUTTON_SCROLL_BACK
150 #define JEWELS_NEXT BUTTON_SCROLL_FWD
151 #define JEWELS_SELECT BUTTON_SELECT
152 #define JEWELS_CANCEL (BUTTON_HOME|BUTTON_REPEAT)
153 #define HK_SELECT "SELECT"
154 #define HK_CANCEL "HOME"
156 #elif CONFIG_KEYPAD == SANSA_C200_PAD || \
157 CONFIG_KEYPAD == SANSA_CLIP_PAD || \
158 CONFIG_KEYPAD == SANSA_M200_PAD
159 #define JEWELS_UP BUTTON_UP
160 #define JEWELS_DOWN BUTTON_DOWN
161 #define JEWELS_LEFT BUTTON_LEFT
162 #define JEWELS_RIGHT BUTTON_RIGHT
163 #define JEWELS_SELECT BUTTON_SELECT
164 #define JEWELS_CANCEL BUTTON_POWER
165 #define HK_SELECT "SELECT"
166 #define HK_CANCEL "POWER"
168 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
169 #define JEWELS_UP BUTTON_SCROLL_UP
170 #define JEWELS_DOWN BUTTON_SCROLL_DOWN
171 #define JEWELS_LEFT BUTTON_LEFT
172 #define JEWELS_RIGHT BUTTON_RIGHT
173 #define JEWELS_SELECT BUTTON_PLAY
174 #define JEWELS_CANCEL BUTTON_POWER
175 #define HK_SELECT "PLAY"
176 #define HK_CANCEL "POWER"
178 #elif CONFIG_KEYPAD == GIGABEAT_S_PAD
179 #define JEWELS_UP BUTTON_UP
180 #define JEWELS_DOWN BUTTON_DOWN
181 #define JEWELS_LEFT BUTTON_LEFT
182 #define JEWELS_RIGHT BUTTON_RIGHT
183 #define JEWELS_SELECT BUTTON_SELECT
184 #define JEWELS_CANCEL BUTTON_BACK
185 #define HK_SELECT "SELECT"
186 #define HK_CANCEL "BACK"
188 #elif CONFIG_KEYPAD == MROBE100_PAD
189 #define JEWELS_UP BUTTON_UP
190 #define JEWELS_DOWN BUTTON_DOWN
191 #define JEWELS_LEFT BUTTON_LEFT
192 #define JEWELS_RIGHT BUTTON_RIGHT
193 #define JEWELS_SELECT BUTTON_SELECT
194 #define JEWELS_CANCEL BUTTON_POWER
195 #define HK_SELECT "SELECT"
196 #define HK_CANCEL "POWER"
198 #elif CONFIG_KEYPAD == IAUDIO_M3_PAD
199 #define JEWELS_UP BUTTON_RC_VOL_UP
200 #define JEWELS_DOWN BUTTON_RC_VOL_DOWN
201 #define JEWELS_LEFT BUTTON_RC_REW
202 #define JEWELS_RIGHT BUTTON_RC_FF
203 #define JEWELS_SELECT BUTTON_RC_PLAY
204 #define JEWELS_CANCEL BUTTON_RC_REC
205 #define HK_SELECT "PLAY"
206 #define HK_CANCEL "REC"
208 #define JEWELS_RC_CANCEL BUTTON_REC
210 #elif CONFIG_KEYPAD == COWON_D2_PAD
211 #define JEWELS_CANCEL BUTTON_POWER
212 #define HK_CANCEL "POWER"
214 #elif CONFIG_KEYPAD == IAUDIO67_PAD
215 #define JEWELS_UP BUTTON_STOP
216 #define JEWELS_DOWN BUTTON_PLAY
217 #define JEWELS_LEFT BUTTON_LEFT
218 #define JEWELS_RIGHT BUTTON_RIGHT
219 #define JEWELS_SELECT BUTTON_MENU
220 #define JEWELS_CANCEL BUTTON_POWER
221 #define HK_SELECT "MENU"
222 #define HK_CANCEL "POWER"
224 #elif CONFIG_KEYPAD == CREATIVEZVM_PAD
225 #define JEWELS_UP BUTTON_UP
226 #define JEWELS_DOWN BUTTON_DOWN
227 #define JEWELS_LEFT BUTTON_LEFT
228 #define JEWELS_RIGHT BUTTON_RIGHT
229 #define JEWELS_SELECT BUTTON_SELECT
230 #define JEWELS_CANCEL BUTTON_BACK
231 #define HK_SELECT "MIDDLE"
232 #define HK_CANCEL "BACK"
234 #elif CONFIG_KEYPAD == PHILIPS_HDD1630_PAD
235 #define JEWELS_UP BUTTON_UP
236 #define JEWELS_DOWN BUTTON_DOWN
237 #define JEWELS_LEFT BUTTON_LEFT
238 #define JEWELS_RIGHT BUTTON_RIGHT
239 #define JEWELS_SELECT BUTTON_SELECT
240 #define JEWELS_CANCEL BUTTON_POWER
241 #define HK_SELECT "SELECT"
242 #define HK_CANCEL "POWER"
244 #elif CONFIG_KEYPAD == PHILIPS_SA9200_PAD
245 #define JEWELS_UP BUTTON_UP
246 #define JEWELS_DOWN BUTTON_DOWN
247 #define JEWELS_LEFT BUTTON_PREV
248 #define JEWELS_RIGHT BUTTON_NEXT
249 #define JEWELS_SELECT BUTTON_PLAY
250 #define JEWELS_CANCEL BUTTON_POWER
251 #define HK_SELECT "PLAY"
252 #define HK_CANCEL "POWER"
254 #elif CONFIG_KEYPAD == ONDAVX747_PAD || \
255 CONFIG_KEYPAD == ONDAVX777_PAD || \
256 CONFIG_KEYPAD == MROBE500_PAD
257 #define JEWELS_CANCEL BUTTON_POWER
258 #define HK_CANCEL "POWER"
260 #elif CONFIG_KEYPAD == SAMSUNG_YH_PAD
261 #define JEWELS_UP BUTTON_UP
262 #define JEWELS_DOWN BUTTON_DOWN
263 #define JEWELS_LEFT BUTTON_LEFT
264 #define JEWELS_RIGHT BUTTON_RIGHT
265 #define JEWELS_SELECT BUTTON_PLAY
266 #define JEWELS_CANCEL BUTTON_REW
267 #define HK_SELECT "PLAY"
268 #define HK_CANCEL "REWIND"
270 #elif CONFIG_KEYPAD == PBELL_VIBE500_PAD
271 #define JEWELS_UP BUTTON_UP
272 #define JEWELS_DOWN BUTTON_DOWN
273 #define JEWELS_LEFT BUTTON_PREV
274 #define JEWELS_RIGHT BUTTON_NEXT
275 #define JEWELS_SELECT BUTTON_OK
276 #define JEWELS_CANCEL BUTTON_REC
277 #define HK_SELECT "OK"
278 #define HK_CANCEL "REC"
280 #else
281 #error No keymap defined!
282 #endif
284 #ifdef HAVE_TOUCHSCREEN
285 #ifndef JEWELS_UP
286 #define JEWELS_UP BUTTON_TOPMIDDLE
287 #endif
288 #ifndef JEWELS_DOWN
289 #define JEWELS_DOWN BUTTON_BOTTOMMIDDLE
290 #endif
291 #ifndef JEWELS_LEFT
292 #define JEWELS_LEFT BUTTON_MIDLEFT
293 #endif
294 #ifndef JEWELS_RIGHT
295 #define JEWELS_RIGHT BUTTON_MIDRIGHT
296 #endif
297 #ifndef JEWELS_SELECT
298 #define JEWELS_SELECT BUTTON_CENTER
299 #define HK_SELECT "CENTER"
300 #endif
301 #ifndef JEWELS_CANCEL
302 #define JEWELS_CANCEL BUTTON_TOPLEFT
303 #define HK_CANCEL "TOPLEFT"
304 #endif
305 #endif
307 #define TILE_WIDTH BMPWIDTH_jewels
308 #define TILE_HEIGHT (BMPHEIGHT_jewels/23)
310 #if LCD_HEIGHT < LCD_WIDTH
311 /* This calculation assumes integer division w/ LCD_HEIGHT/TILE_HEIGHT */
312 #define YOFS LCD_HEIGHT-((LCD_HEIGHT/TILE_HEIGHT)*TILE_HEIGHT)
313 #else
314 #define YOFS 0
315 #endif
317 #define NUM_SCORES 5
319 /* swap directions */
320 #define SWAP_UP 0
321 #define SWAP_RIGHT 1
322 #define SWAP_DOWN 2
323 #define SWAP_LEFT 3
325 /* play board dimension */
326 #define BJ_HEIGHT 9
327 #define BJ_WIDTH 8
329 /* next level threshold */
330 #define LEVEL_PTS 100
332 /* animation frame rate */
333 #define MAX_FPS 20
335 /* text margin */
336 #define MARGIN 5
338 /* Game types */
339 enum game_type {
340 GAME_TYPE_NORMAL,
341 GAME_TYPE_PUZZLE
344 /* external bitmaps */
345 extern const fb_data jewels[];
347 /* tile background colors */
348 #ifdef HAVE_LCD_COLOR
349 static const unsigned jewels_bkgd[2] = {
350 LCD_RGBPACK(104, 63, 63),
351 LCD_RGBPACK(83, 44, 44)
353 #endif
355 /* the tile struct
356 * type is the jewel number 0-7
357 * falling if the jewel is falling
358 * delete marks the jewel for deletion
360 struct tile {
361 int type;
362 bool falling;
363 bool delete;
366 /* the game context struct
367 * score is the current level score
368 * segments is the number of cleared segments in the current run
369 * level is the current level
370 * tmp_type is the select type in the menu
371 * type is the game type (normal or puzzle)
372 * playboard is the game playing board (first row is hidden)
373 * num_jewels is the number of different jewels to use
375 struct game_context {
376 unsigned int score;
377 unsigned int segments;
378 unsigned int level;
379 unsigned int type;
380 unsigned int tmp_type;
381 struct tile playboard[BJ_HEIGHT][BJ_WIDTH];
382 unsigned int num_jewels;
385 #define MAX_NUM_JEWELS 7
387 #define MAX_PUZZLE_TILES 4
388 #define NUM_PUZZLE_LEVELS 10
390 struct puzzle_tile {
391 int x;
392 int y;
393 int tile_type;
396 struct puzzle_level {
397 unsigned int num_jewels;
398 unsigned int num_tiles;
399 struct puzzle_tile tiles[MAX_PUZZLE_TILES];
402 #define PUZZLE_TILE_UP 1
403 #define PUZZLE_TILE_DOWN 2
404 #define PUZZLE_TILE_LEFT 4
405 #define PUZZLE_TILE_RIGHT 8
407 struct puzzle_level puzzle_levels[NUM_PUZZLE_LEVELS] = {
408 { 5, 2, { {3, 3, PUZZLE_TILE_RIGHT},
409 {4, 2, PUZZLE_TILE_LEFT} } },
410 { 5, 2, { {3, 2, PUZZLE_TILE_DOWN},
411 {3, 4, PUZZLE_TILE_UP} } },
412 { 6, 3, { {3, 2, PUZZLE_TILE_DOWN},
413 {3, 4, PUZZLE_TILE_UP|PUZZLE_TILE_DOWN},
414 {3, 6, PUZZLE_TILE_UP} } },
415 { 6, 3, { {3, 2, PUZZLE_TILE_RIGHT},
416 {4, 3, PUZZLE_TILE_LEFT|PUZZLE_TILE_RIGHT},
417 {5, 4, PUZZLE_TILE_LEFT} } },
418 { 6, 2, { {3, 4, PUZZLE_TILE_RIGHT},
419 {4, 2, PUZZLE_TILE_LEFT} } },
420 { 6, 2, { {3, 2, PUZZLE_TILE_DOWN},
421 {4, 4, PUZZLE_TILE_UP} } },
422 { 7, 4, { {3, 2, PUZZLE_TILE_RIGHT|PUZZLE_TILE_DOWN},
423 {4, 3, PUZZLE_TILE_LEFT|PUZZLE_TILE_DOWN},
424 {3, 4, PUZZLE_TILE_RIGHT|PUZZLE_TILE_UP},
425 {4, 4, PUZZLE_TILE_LEFT|PUZZLE_TILE_UP} } },
426 { 6, 3, { {3, 2, PUZZLE_TILE_DOWN},
427 {4, 4, PUZZLE_TILE_UP|PUZZLE_TILE_DOWN},
428 {3, 6, PUZZLE_TILE_UP} } },
429 { 7, 3, { {2, 2, PUZZLE_TILE_RIGHT},
430 {4, 1, PUZZLE_TILE_LEFT|PUZZLE_TILE_RIGHT},
431 {5, 4, PUZZLE_TILE_LEFT} } },
432 { 7, 4, { {3, 0, PUZZLE_TILE_RIGHT|PUZZLE_TILE_DOWN},
433 {5, 0, PUZZLE_TILE_LEFT|PUZZLE_TILE_DOWN},
434 {2, 7, PUZZLE_TILE_RIGHT|PUZZLE_TILE_UP},
435 {4, 7, PUZZLE_TILE_LEFT|PUZZLE_TILE_UP} } },
438 #define SAVE_FILE PLUGIN_GAMES_DIR "/jewels.save"
439 #define SCORE_FILE PLUGIN_GAMES_DIR "/jewels.score"
440 struct highscore highscores[NUM_SCORES];
442 static bool resume_file = false;
444 /*****************************************************************************
445 * jewels_setcolors() set the foreground and background colors.
446 ******************************************************************************/
447 static inline void jewels_setcolors(void) {
448 #ifdef HAVE_LCD_COLOR
449 rb->lcd_set_background(LCD_RGBPACK(49, 26, 26));
450 rb->lcd_set_foreground(LCD_RGBPACK(210, 181, 181));
451 #endif
454 /*****************************************************************************
455 * jewels_loadgame() loads the saved game and returns load success.
456 ******************************************************************************/
457 static bool jewels_loadgame(struct game_context* bj)
459 int fd;
460 bool loaded = false;
462 /* open game file */
463 fd = rb->open(SAVE_FILE, O_RDONLY);
464 if(fd < 0) return loaded;
466 /* read in saved game */
467 while(true) {
468 if(rb->read(fd, &bj->tmp_type, sizeof(bj->tmp_type)) <= 0) break;
469 if(rb->read(fd, &bj->type, sizeof(bj->type)) <= 0) break;
470 if(rb->read(fd, &bj->score, sizeof(bj->score)) <= 0) break;
471 if(rb->read(fd, &bj->level, sizeof(bj->level)) <= 0) break;
472 if(rb->read(fd, &bj->segments, sizeof(bj->segments)) <= 0) break;
473 if(rb->read(fd, &bj->num_jewels, sizeof(bj->num_jewels)) <= 0) break;
474 if(rb->read(fd, bj->playboard, sizeof(bj->playboard)) <= 0) break;
475 loaded = true;
476 break;
479 rb->close(fd);
481 return loaded;
484 /*****************************************************************************
485 * jewels_savegame() saves the current game state.
486 ******************************************************************************/
487 static void jewels_savegame(struct game_context* bj)
489 int fd;
490 /* write out the game state to the save file */
491 fd = rb->open(SAVE_FILE, O_WRONLY|O_CREAT);
492 if(fd < 0) return;
494 rb->write(fd, &bj->tmp_type, sizeof(bj->tmp_type));
495 rb->write(fd, &bj->type, sizeof(bj->type));
496 rb->write(fd, &bj->score, sizeof(bj->score));
497 rb->write(fd, &bj->level, sizeof(bj->level));
498 rb->write(fd, &bj->segments, sizeof(bj->segments));
499 rb->write(fd, &bj->num_jewels, sizeof(bj->num_jewels));
500 rb->write(fd, bj->playboard, sizeof(bj->playboard));
501 rb->close(fd);
504 /*****************************************************************************
505 * jewels_drawboard() redraws the entire game board.
506 ******************************************************************************/
507 static void jewels_drawboard(struct game_context* bj) {
508 int i, j;
509 int w, h;
510 unsigned int tempscore;
511 unsigned int size;
512 char *title = "Level";
513 char str[10];
515 if (bj->type == GAME_TYPE_NORMAL) {
516 tempscore = (bj->score>LEVEL_PTS ? LEVEL_PTS : bj->score);
517 size = LEVEL_PTS;
518 } else {
519 tempscore = (bj->level>NUM_PUZZLE_LEVELS ? NUM_PUZZLE_LEVELS : bj->level);
520 size = NUM_PUZZLE_LEVELS;
523 /* clear screen */
524 rb->lcd_clear_display();
526 /* dispay playing board */
527 for(i=0; i<BJ_HEIGHT-1; i++){
528 for(j=0; j<BJ_WIDTH; j++){
529 #ifdef HAVE_LCD_COLOR
530 rb->lcd_set_foreground(jewels_bkgd[(i+j)%2]);
531 rb->lcd_fillrect(j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
532 TILE_WIDTH, TILE_HEIGHT);
533 rb->lcd_bitmap_transparent_part(jewels,
534 0, TILE_HEIGHT*(bj->playboard[i+1][j].type),
535 STRIDE( SCREEN_MAIN,
536 BMPWIDTH_jewels, BMPHEIGHT_jewels),
537 j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
538 TILE_WIDTH, TILE_HEIGHT);
539 #else
540 rb->lcd_bitmap_part(jewels,
541 0, TILE_HEIGHT*(bj->playboard[i+1][j].type),
542 STRIDE( SCREEN_MAIN,
543 BMPWIDTH_jewels, BMPHEIGHT_jewels),
544 j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
545 TILE_WIDTH, TILE_HEIGHT);
546 #endif
550 #if LCD_WIDTH > LCD_HEIGHT /* horizontal layout */
552 /* draw separator lines */
553 jewels_setcolors();
554 rb->lcd_vline(BJ_WIDTH*TILE_WIDTH, 0, LCD_HEIGHT-1);
556 rb->lcd_hline(BJ_WIDTH*TILE_WIDTH, LCD_WIDTH-1, 18);
557 rb->lcd_hline(BJ_WIDTH*TILE_WIDTH, LCD_WIDTH-1, LCD_HEIGHT-10);
559 /* draw progress bar */
560 #ifdef HAVE_LCD_COLOR
561 rb->lcd_set_foreground(LCD_RGBPACK(104, 63, 63));
562 #endif
563 rb->lcd_fillrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4,
564 (LCD_HEIGHT-10)-(((LCD_HEIGHT-10)-18)*
565 tempscore/size),
566 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2,
567 ((LCD_HEIGHT-10)-18)*tempscore/size);
568 #ifdef HAVE_LCD_COLOR
569 rb->lcd_set_foreground(LCD_RGBPACK(83, 44, 44));
570 rb->lcd_drawrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4+1,
571 (LCD_HEIGHT-10)-(((LCD_HEIGHT-10)-18)*
572 tempscore/size)+1,
573 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-2,
574 ((LCD_HEIGHT-10)-18)*tempscore/size-1);
575 jewels_setcolors();
576 rb->lcd_drawrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4,
577 (LCD_HEIGHT-10)-(((LCD_HEIGHT-10)-18)*tempscore/size),
578 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2,
579 ((LCD_HEIGHT-10)-18)*tempscore/size+1);
580 #endif
582 /* print text */
583 rb->lcd_getstringsize(title, &w, &h);
584 rb->lcd_putsxy(LCD_WIDTH-(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-w/2, 1, title);
585 rb->snprintf(str, 4, "%d", bj->level);
586 rb->lcd_getstringsize(str, &w, &h);
587 rb->lcd_putsxy(LCD_WIDTH-(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-w/2, 10, str);
589 if (bj->type == GAME_TYPE_NORMAL) {
590 rb->snprintf(str, 6, "%d", (bj->level-1)*LEVEL_PTS+bj->score);
591 rb->lcd_getstringsize(str, &w, &h);
592 rb->lcd_putsxy(LCD_WIDTH-(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-w/2,
593 LCD_HEIGHT-8, str);
596 #elif LCD_WIDTH < LCD_HEIGHT /* vertical layout */
598 /* draw separator lines */
599 jewels_setcolors();
600 rb->lcd_hline(0, LCD_WIDTH-1, 8*TILE_HEIGHT+YOFS);
601 rb->lcd_hline(0, LCD_WIDTH-1, LCD_HEIGHT-14);
602 rb->lcd_vline(LCD_WIDTH/2, LCD_HEIGHT-14, LCD_HEIGHT-1);
604 /* draw progress bar */
605 #ifdef HAVE_LCD_COLOR
606 rb->lcd_set_foreground(LCD_RGBPACK(104, 63, 63));
607 #endif
608 rb->lcd_fillrect(0, (8*TILE_HEIGHT+YOFS)
609 +(LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/4,
610 LCD_WIDTH*tempscore/size,
611 (LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/2);
612 #ifdef HAVE_LCD_COLOR
613 rb->lcd_set_foreground(LCD_RGBPACK(83, 44, 44));
614 rb->lcd_drawrect(1, (8*TILE_HEIGHT+YOFS)
615 +(LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/4+1,
616 LCD_WIDTH*tempscore/size-1,
617 (LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/2-2);
618 jewels_setcolors();
619 rb->lcd_drawrect(0, (8*TILE_HEIGHT+YOFS)
620 +(LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/4,
621 LCD_WIDTH*tempscore/size+1,
622 (LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/2);
623 #endif
625 /* print text */
626 rb->snprintf(str, 10, "%s %d", title, bj->level);
627 rb->lcd_putsxy(1, LCD_HEIGHT-10, str);
629 if (bj->type == GAME_TYPE_NORMAL) {
630 rb->snprintf(str, 6, "%d", (bj->level-1)*LEVEL_PTS+bj->score);
631 rb->lcd_getstringsize(str, &w, &h);
632 rb->lcd_putsxy((LCD_WIDTH-2)-w, LCD_HEIGHT-10, str);
636 #else /* square layout */
638 /* draw separator lines */
639 jewels_setcolors();
640 rb->lcd_hline(0, LCD_WIDTH-1, 8*TILE_HEIGHT+YOFS);
641 rb->lcd_vline(BJ_WIDTH*TILE_WIDTH, 0, 8*TILE_HEIGHT+YOFS);
642 rb->lcd_vline(LCD_WIDTH/2, 8*TILE_HEIGHT+YOFS, LCD_HEIGHT-1);
644 /* draw progress bar */
645 #ifdef HAVE_LCD_COLOR
646 rb->lcd_set_foreground(LCD_RGBPACK(104, 63, 63));
647 #endif
648 rb->lcd_fillrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4,
649 (8*TILE_HEIGHT+YOFS)-(8*TILE_HEIGHT+YOFS)*tempscore/size,
650 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2,
651 (8*TILE_HEIGHT+YOFS)*tempscore/size);
652 #ifdef HAVE_LCD_COLOR
653 rb->lcd_set_foreground(LCD_RGBPACK(83, 44, 44));
654 rb->lcd_drawrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4+1,
655 (8*TILE_HEIGHT+YOFS)-(8*TILE_HEIGHT+YOFS)
656 *tempscore/size+1,
657 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-2,
658 (8*TILE_HEIGHT+YOFS)*tempscore/size-1);
659 jewels_setcolors();
660 rb->lcd_drawrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4,
661 (8*TILE_HEIGHT+YOFS)-(8*TILE_HEIGHT+YOFS)
662 *tempscore/size,
663 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2,
664 (8*TILE_HEIGHT+YOFS)*tempscore/size+1);
665 #endif
667 /* print text */
668 rb->snprintf(str, 10, "%s %d", title, bj->level);
669 rb->lcd_putsxy(1, LCD_HEIGHT-(LCD_HEIGHT-(8*TILE_HEIGHT+YOFS))/2-3, str);
671 if (bj->type == GAME_TYPE_NORMAL) {
672 rb->snprintf(str, 6, "%d", (bj->level-1)*LEVEL_PTS+bj->score);
673 rb->lcd_getstringsize(str, &w, &h);
674 rb->lcd_putsxy((LCD_WIDTH-2)-w,
675 LCD_HEIGHT-(LCD_HEIGHT-(8*TILE_HEIGHT+YOFS))/2-3, str);
678 #endif /* layout */
680 rb->lcd_update();
683 /*****************************************************************************
684 * jewels_putjewels() makes the jewels fall to fill empty spots and adds
685 * new random jewels at the empty spots at the top of each row.
686 ******************************************************************************/
687 static void jewels_putjewels(struct game_context* bj){
688 int i, j, k;
689 bool mark, done;
690 long lasttick, currenttick;
692 /* loop to make all the jewels fall */
693 while(true) {
694 /* mark falling jewels and add new jewels to hidden top row*/
695 mark = false;
696 done = true;
697 for(j=0; j<BJ_WIDTH; j++) {
698 if(bj->playboard[1][j].type == 0) {
699 bj->playboard[0][j].type = rb->rand()%bj->num_jewels+1;
701 for(i=BJ_HEIGHT-2; i>=0; i--) {
702 if(!mark && bj->playboard[i+1][j].type == 0) {
703 mark = true;
704 done = false;
706 if(mark) bj->playboard[i][j].falling = true;
708 /*if(bj->playboard[1][j].falling) {
709 bj->playboard[0][j].type = rb->rand()%bj->num_jewels+1;
710 bj->playboard[0][j].falling = true;
712 mark = false;
715 /* break if there are no falling jewels */
716 if(done) break;
718 /* animate falling jewels */
719 lasttick = *rb->current_tick;
721 for(k=1; k<=8; k++) {
722 for(i=BJ_HEIGHT-2; i>=0; i--) {
723 for(j=0; j<BJ_WIDTH; j++) {
724 if(bj->playboard[i][j].falling &&
725 bj->playboard[i][j].type != 0) {
726 /* clear old position */
727 #ifdef HAVE_LCD_COLOR
728 if(i == 0 && YOFS) {
729 rb->lcd_set_foreground(rb->lcd_get_background());
730 } else {
731 rb->lcd_set_foreground(jewels_bkgd[(i-1+j)%2]);
733 rb->lcd_fillrect(j*TILE_WIDTH, (i-1)*TILE_HEIGHT+YOFS,
734 TILE_WIDTH, TILE_HEIGHT);
735 if(bj->playboard[i+1][j].type == 0) {
736 rb->lcd_set_foreground(jewels_bkgd[(i+j)%2]);
737 rb->lcd_fillrect(j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
738 TILE_WIDTH, TILE_HEIGHT);
740 #else
741 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
742 rb->lcd_fillrect(j*TILE_WIDTH, (i-1)*TILE_HEIGHT+YOFS,
743 TILE_WIDTH, TILE_HEIGHT);
744 if(bj->playboard[i+1][j].type == 0) {
745 rb->lcd_fillrect(j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
746 TILE_WIDTH, TILE_HEIGHT);
748 rb->lcd_set_drawmode(DRMODE_SOLID);
749 #endif
751 /* draw new position */
752 #ifdef HAVE_LCD_COLOR
753 rb->lcd_bitmap_transparent_part(jewels, 0,
754 TILE_HEIGHT*(bj->playboard[i][j].type),
755 STRIDE( SCREEN_MAIN,
756 BMPWIDTH_jewels,
757 BMPHEIGHT_jewels),
758 j*TILE_WIDTH,
759 (i-1)*TILE_HEIGHT+YOFS+
760 ((((TILE_HEIGHT<<10)*k)/8)>>10),
761 TILE_WIDTH, TILE_HEIGHT);
762 #else
763 rb->lcd_bitmap_part(jewels, 0,
764 TILE_HEIGHT*(bj->playboard[i][j].type),
765 STRIDE( SCREEN_MAIN,
766 BMPWIDTH_jewels,
767 BMPHEIGHT_jewels),
768 j*TILE_WIDTH,
769 (i-1)*TILE_HEIGHT+YOFS+
770 ((((TILE_HEIGHT<<10)*k)/8)>>10),
771 TILE_WIDTH, TILE_HEIGHT);
772 #endif
777 rb->lcd_update_rect(0, 0, TILE_WIDTH*8, LCD_HEIGHT);
778 jewels_setcolors();
780 /* framerate limiting */
781 currenttick = *rb->current_tick;
782 if(currenttick-lasttick < HZ/MAX_FPS) {
783 rb->sleep((HZ/MAX_FPS)-(currenttick-lasttick));
784 } else {
785 rb->yield();
787 lasttick = currenttick;
790 /* shift jewels down */
791 for(j=0; j<BJ_WIDTH; j++) {
792 for(i=BJ_HEIGHT-1; i>=1; i--) {
793 if(bj->playboard[i-1][j].falling) {
794 bj->playboard[i][j].type = bj->playboard[i-1][j].type;
799 /* clear out top row */
800 for(j=0; j<BJ_WIDTH; j++) {
801 bj->playboard[0][j].type = 0;
804 /* mark everything not falling */
805 for(i=0; i<BJ_HEIGHT; i++) {
806 for(j=0; j<BJ_WIDTH; j++) {
807 bj->playboard[i][j].falling = false;
813 /*****************************************************************************
814 * jewels_clearjewels() finds all the connected rows and columns and
815 * calculates and returns the points earned.
816 ******************************************************************************/
817 static unsigned int jewels_clearjewels(struct game_context* bj) {
818 int i, j;
819 int last, run;
820 unsigned int points = 0;
822 /* check for connected rows */
823 for(i=1; i<BJ_HEIGHT; i++) {
824 last = 0;
825 run = 1;
826 for(j=0; j<BJ_WIDTH; j++) {
827 if(bj->playboard[i][j].type == last &&
828 bj->playboard[i][j].type != 0 &&
829 bj->playboard[i][j].type <= MAX_NUM_JEWELS) {
830 run++;
832 if(run == 3) {
833 bj->segments++;
834 points += bj->segments;
835 bj->playboard[i][j].delete = true;
836 bj->playboard[i][j-1].delete = true;
837 bj->playboard[i][j-2].delete = true;
838 } else if(run > 3) {
839 points++;
840 bj->playboard[i][j].delete = true;
842 } else {
843 run = 1;
844 last = bj->playboard[i][j].type;
849 /* check for connected columns */
850 for(j=0; j<BJ_WIDTH; j++) {
851 last = 0;
852 run = 1;
853 for(i=1; i<BJ_HEIGHT; i++) {
854 if(bj->playboard[i][j].type != 0 &&
855 bj->playboard[i][j].type == last &&
856 bj->playboard[i][j].type <= MAX_NUM_JEWELS) {
857 run++;
859 if(run == 3) {
860 bj->segments++;
861 points += bj->segments;
862 bj->playboard[i][j].delete = true;
863 bj->playboard[i-1][j].delete = true;
864 bj->playboard[i-2][j].delete = true;
865 } else if(run > 3) {
866 points++;
867 bj->playboard[i][j].delete = true;
869 } else {
870 run = 1;
871 last = bj->playboard[i][j].type;
876 /* clear deleted jewels */
877 for(i=1; i<BJ_HEIGHT; i++) {
878 for(j=0; j<BJ_WIDTH; j++) {
879 if(bj->playboard[i][j].delete) {
880 bj->playboard[i][j].delete = false;
881 bj->playboard[i][j].type = 0;
886 return points;
889 /*****************************************************************************
890 * jewels_runboard() runs the board until it settles in a fixed state and
891 * returns points earned.
892 ******************************************************************************/
893 static unsigned int jewels_runboard(struct game_context* bj) {
894 unsigned int points = 0;
895 unsigned int ret;
897 bj->segments = 0;
899 while((ret = jewels_clearjewels(bj)) > 0) {
900 points += ret;
901 jewels_drawboard(bj);
902 jewels_putjewels(bj);
905 return points;
908 /*****************************************************************************
909 * jewels_swapjewels() swaps two jewels as long as it results in points and
910 * returns points earned.
911 ******************************************************************************/
912 static unsigned int jewels_swapjewels(struct game_context* bj,
913 int x, int y, int direc) {
914 int k;
915 int horzmod, vertmod;
916 int movelen = 0;
917 bool undo = false;
918 unsigned int points = 0;
919 long lasttick, currenttick;
921 /* check for invalid parameters */
922 if(x < 0 || x >= BJ_WIDTH || y < 0 || y >= BJ_HEIGHT-1 ||
923 direc < SWAP_UP || direc > SWAP_LEFT) {
924 return 0;
927 /* check for invalid directions */
928 if((x == 0 && direc == SWAP_LEFT) ||
929 (x == BJ_WIDTH-1 && direc == SWAP_RIGHT) ||
930 (y == 0 && direc == SWAP_UP) ||
931 (y == BJ_HEIGHT-2 && direc == SWAP_DOWN)) {
932 return 0;
935 /* set direction variables */
936 horzmod = 0;
937 vertmod = 0;
938 switch(direc) {
939 case SWAP_UP:
940 vertmod = -1;
941 movelen = TILE_HEIGHT;
942 break;
943 case SWAP_RIGHT:
944 horzmod = 1;
945 movelen = TILE_WIDTH;
946 break;
947 case SWAP_DOWN:
948 vertmod = 1;
949 movelen = TILE_HEIGHT;
950 break;
951 case SWAP_LEFT:
952 horzmod = -1;
953 movelen = TILE_WIDTH;
954 break;
957 while(true) {
958 lasttick = *rb->current_tick;
960 /* animate swapping jewels */
961 for(k=0; k<=8; k++) {
962 /* clear old position */
963 #ifdef HAVE_LCD_COLOR
964 rb->lcd_set_foreground(jewels_bkgd[(x+y)%2]);
965 rb->lcd_fillrect(x*TILE_WIDTH,
966 y*TILE_HEIGHT+YOFS,
967 TILE_WIDTH, TILE_HEIGHT);
968 rb->lcd_set_foreground(jewels_bkgd[(x+horzmod+y+vertmod)%2]);
969 rb->lcd_fillrect((x+horzmod)*TILE_WIDTH,
970 (y+vertmod)*TILE_HEIGHT+YOFS,
971 TILE_WIDTH, TILE_HEIGHT);
972 #else
973 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
974 rb->lcd_fillrect(x*TILE_WIDTH,
975 y*TILE_HEIGHT+YOFS,
976 TILE_WIDTH, TILE_HEIGHT);
977 rb->lcd_fillrect((x+horzmod)*TILE_WIDTH,
978 (y+vertmod)*TILE_HEIGHT+YOFS,
979 TILE_WIDTH, TILE_HEIGHT);
980 rb->lcd_set_drawmode(DRMODE_SOLID);
981 #endif
982 /* draw new position */
983 #ifdef HAVE_LCD_COLOR
984 rb->lcd_bitmap_transparent_part(jewels,
985 0, TILE_HEIGHT*(bj->playboard
986 [y+1+vertmod][x+horzmod].type),
987 STRIDE( SCREEN_MAIN,
988 BMPWIDTH_jewels, BMPHEIGHT_jewels),
989 (x+horzmod)*TILE_WIDTH-horzmod*
990 ((((movelen<<10)*k)/8)>>10),
991 (y+vertmod)*TILE_HEIGHT-vertmod*
992 ((((movelen<<10)*k)/8)>>10)+YOFS,
993 TILE_WIDTH, TILE_HEIGHT);
994 rb->lcd_bitmap_transparent_part(jewels,
995 0, TILE_HEIGHT*(bj->playboard[y+1][x].type),
996 STRIDE( SCREEN_MAIN,
997 BMPWIDTH_jewels, BMPHEIGHT_jewels),
998 x*TILE_WIDTH+horzmod*
999 ((((movelen<<10)*k)/8)>>10),
1000 y*TILE_HEIGHT+vertmod*
1001 ((((movelen<<10)*k)/8)>>10)+YOFS,
1002 TILE_WIDTH, TILE_HEIGHT);
1003 #else
1004 rb->lcd_bitmap_part(jewels,
1005 0, TILE_HEIGHT*(bj->playboard
1006 [y+1+vertmod][x+horzmod].type),
1007 STRIDE( SCREEN_MAIN,
1008 BMPWIDTH_jewels, BMPHEIGHT_jewels),
1009 (x+horzmod)*TILE_WIDTH-horzmod*
1010 ((((movelen<<10)*k)/8)>>10),
1011 (y+vertmod)*TILE_HEIGHT-vertmod*
1012 ((((movelen<<10)*k)/8)>>10)+YOFS,
1013 TILE_WIDTH, TILE_HEIGHT);
1014 rb->lcd_set_drawmode(DRMODE_FG);
1015 rb->lcd_bitmap_part(jewels,
1016 0, TILE_HEIGHT*(bj->playboard[y+1][x].type),
1017 STRIDE( SCREEN_MAIN,
1018 BMPWIDTH_jewels, BMPHEIGHT_jewels),
1019 x*TILE_WIDTH+horzmod*
1020 ((((movelen<<10)*k)/8)>>10),
1021 y*TILE_HEIGHT+vertmod*
1022 ((((movelen<<10)*k)/8)>>10)+YOFS,
1023 TILE_WIDTH, TILE_HEIGHT);
1024 rb->lcd_set_drawmode(DRMODE_SOLID);
1025 #endif
1027 rb->lcd_update_rect(0, 0, TILE_WIDTH*8, LCD_HEIGHT);
1028 jewels_setcolors();
1030 /* framerate limiting */
1031 currenttick = *rb->current_tick;
1032 if(currenttick-lasttick < HZ/MAX_FPS) {
1033 rb->sleep((HZ/MAX_FPS)-(currenttick-lasttick));
1034 } else {
1035 rb->yield();
1037 lasttick = currenttick;
1040 /* swap jewels */
1041 int temp = bj->playboard[y+1][x].type;
1042 bj->playboard[y+1][x].type =
1043 bj->playboard[y+1+vertmod][x+horzmod].type;
1044 bj->playboard[y+1+vertmod][x+horzmod].type = temp;
1046 if(undo) break;
1048 points = jewels_runboard(bj);
1049 if(points == 0) {
1050 undo = true;
1051 } else {
1052 break;
1056 return points;
1059 /*****************************************************************************
1060 * jewels_movesavail() uses pattern matching to see if there are any
1061 * available move left.
1062 ******************************************************************************/
1063 static bool jewels_movesavail(struct game_context* bj) {
1064 int i, j;
1065 bool moves = false;
1066 int mytype;
1068 for(i=1; i<BJ_HEIGHT; i++) {
1069 for(j=0; j<BJ_WIDTH; j++) {
1070 mytype = bj->playboard[i][j].type;
1071 if(mytype == 0 || mytype > MAX_NUM_JEWELS) continue;
1073 /* check horizontal patterns */
1074 if(j <= BJ_WIDTH-3) {
1075 if(i > 1) {
1076 if(bj->playboard[i-1][j+1].type == mytype) {
1077 if(bj->playboard[i-1][j+2].type == mytype)
1078 {moves = true; break;}
1079 if(bj->playboard[i][j+2].type == mytype)
1080 {moves = true; break;}
1082 if(bj->playboard[i][j+1].type == mytype) {
1083 if(bj->playboard[i-1][j+2].type == mytype)
1084 {moves = true; break;}
1088 if(j <= BJ_WIDTH-4) {
1089 if(bj->playboard[i][j+3].type == mytype) {
1090 if(bj->playboard[i][j+1].type == mytype)
1091 {moves = true; break;}
1092 if(bj->playboard[i][j+2].type == mytype)
1093 {moves = true; break;}
1097 if(i < BJ_HEIGHT-1) {
1098 if(bj->playboard[i][j+1].type == mytype) {
1099 if(bj->playboard[i+1][j+2].type == mytype)
1100 {moves = true; break;}
1102 if(bj->playboard[i+1][j+1].type == mytype) {
1103 if(bj->playboard[i][j+2].type == mytype)
1104 {moves = true; break;}
1105 if(bj->playboard[i+1][j+2].type == mytype)
1106 {moves = true; break;}
1111 /* check vertical patterns */
1112 if(i <= BJ_HEIGHT-3) {
1113 if(j > 0) {
1114 if(bj->playboard[i+1][j-1].type == mytype) {
1115 if(bj->playboard[i+2][j-1].type == mytype)
1116 {moves = true; break;}
1117 if(bj->playboard[i+2][j].type == mytype)
1118 {moves = true; break;}
1120 if(bj->playboard[i+1][j].type == mytype) {
1121 if(bj->playboard[i+2][j-1].type == mytype)
1122 {moves = true; break;}
1126 if(i <= BJ_HEIGHT-4) {
1127 if(bj->playboard[i+3][j].type == mytype) {
1128 if(bj->playboard[i+1][j].type == mytype)
1129 {moves = true; break;}
1130 if(bj->playboard[i+2][j].type == mytype)
1131 {moves = true; break;}
1135 if(j < BJ_WIDTH-1) {
1136 if(bj->playboard[i+1][j].type == mytype) {
1137 if(bj->playboard[i+2][j+1].type == mytype)
1138 {moves = true; break;}
1140 if(bj->playboard[i+1][j+1].type == mytype) {
1141 if(bj->playboard[i+2][j].type == mytype)
1142 {moves = true; break;}
1143 if (bj->playboard[i+2][j+1].type == mytype)
1144 {moves = true; break;}
1149 if(moves) break;
1151 return moves;
1154 /*****************************************************************************
1155 * jewels_puzzle_is_finished() checks if the puzzle is finished.
1156 ******************************************************************************/
1157 static bool jewels_puzzle_is_finished(struct game_context* bj) {
1158 unsigned int i, j;
1159 for(i=0; i<BJ_HEIGHT; i++) {
1160 for(j=0; j<BJ_WIDTH; j++) {
1161 int mytype = bj->playboard[i][j].type;
1162 if(mytype>MAX_NUM_JEWELS) {
1163 mytype -= MAX_NUM_JEWELS;
1164 if(mytype&PUZZLE_TILE_UP) {
1165 if(i==0 || bj->playboard[i-1][j].type<=MAX_NUM_JEWELS ||
1166 !((bj->playboard[i-1][j].type-MAX_NUM_JEWELS)
1167 &PUZZLE_TILE_DOWN))
1168 return false;
1170 if(mytype&PUZZLE_TILE_DOWN) {
1171 if(i==BJ_HEIGHT-1 ||
1172 bj->playboard[i+1][j].type<=MAX_NUM_JEWELS ||
1173 !((bj->playboard[i+1][j].type-MAX_NUM_JEWELS)
1174 &PUZZLE_TILE_UP))
1175 return false;
1177 if(mytype&PUZZLE_TILE_LEFT) {
1178 if(j==0 || bj->playboard[i][j-1].type<=MAX_NUM_JEWELS ||
1179 !((bj->playboard[i][j-1].type-MAX_NUM_JEWELS)
1180 &PUZZLE_TILE_RIGHT))
1181 return false;
1183 if(mytype&PUZZLE_TILE_RIGHT) {
1184 if(j==BJ_WIDTH-1 ||
1185 bj->playboard[i][j+1].type<=MAX_NUM_JEWELS ||
1186 !((bj->playboard[i][j+1].type-MAX_NUM_JEWELS)
1187 &PUZZLE_TILE_LEFT))
1188 return false;
1193 return true;
1196 /*****************************************************************************
1197 * jewels_initlevel() initialises a level.
1198 ******************************************************************************/
1199 static unsigned int jewels_initlevel(struct game_context* bj) {
1200 unsigned int points = 0;
1202 switch(bj->type) {
1203 case GAME_TYPE_NORMAL:
1204 bj->num_jewels = MAX_NUM_JEWELS;
1205 break;
1207 case GAME_TYPE_PUZZLE:
1209 unsigned int i, j;
1210 struct puzzle_tile *tile;
1212 bj->num_jewels = puzzle_levels[bj->level-1].num_jewels;
1214 for(i=0; i<BJ_HEIGHT; i++) {
1215 for(j=0; j<BJ_WIDTH; j++) {
1216 bj->playboard[i][j].type = (rb->rand()%bj->num_jewels)+1;
1217 bj->playboard[i][j].falling = false;
1218 bj->playboard[i][j].delete = false;
1221 jewels_runboard(bj);
1222 tile = puzzle_levels[bj->level-1].tiles;
1223 for(i=0; i<puzzle_levels[bj->level-1].num_tiles; i++, tile++) {
1224 bj->playboard[tile->y+1][tile->x].type = MAX_NUM_JEWELS
1225 +tile->tile_type;
1228 break;
1231 jewels_drawboard(bj);
1233 /* run the play board */
1234 jewels_putjewels(bj);
1235 points += jewels_runboard(bj);
1236 return points;
1239 /*****************************************************************************
1240 * jewels_init() initializes jewels data structures.
1241 ******************************************************************************/
1242 static void jewels_init(struct game_context* bj) {
1243 /* seed the rand generator */
1244 rb->srand(*rb->current_tick);
1246 bj->type = bj->tmp_type;
1247 bj->level = 1;
1248 bj->score = 0;
1249 bj->segments = 0;
1251 jewels_setcolors();
1253 /* clear playing board */
1254 rb->memset(bj->playboard, 0, sizeof(bj->playboard));
1255 do {
1256 bj->score += jewels_initlevel(bj);
1257 } while(!jewels_movesavail(bj));
1260 /*****************************************************************************
1261 * jewels_nextlevel() advances the game to the next bj->level and returns
1262 * points earned.
1263 ******************************************************************************/
1264 static void jewels_nextlevel(struct game_context* bj) {
1265 int i, x, y;
1266 unsigned int points = 0;
1268 switch(bj->type) {
1269 case GAME_TYPE_NORMAL:
1270 /* roll over score, change and display level */
1271 while(bj->score >= LEVEL_PTS) {
1272 bj->score -= LEVEL_PTS;
1273 bj->level++;
1274 rb->splashf(HZ*2, "Level %d", bj->level);
1275 jewels_drawboard(bj);
1278 /* randomly clear some jewels */
1279 for(i=0; i<16; i++) {
1280 x = rb->rand()%8;
1281 y = rb->rand()%8;
1283 if(bj->playboard[y][x].type != 0) {
1284 points++;
1285 bj->playboard[y][x].type = 0;
1288 break;
1290 case GAME_TYPE_PUZZLE:
1291 bj->level++;
1292 rb->splashf(HZ*2, "Level %d", bj->level);
1293 break;
1296 points += jewels_initlevel(bj);
1297 bj->score += points;
1300 static bool jewels_help(void)
1302 static char *help_text[] = {
1303 "Jewels", "", "Aim", "",
1304 "Swap", "pairs", "of", "jewels", "to", "form", "connected",
1305 "segments", "of", "three", "or", "more", "of", "the", "same",
1306 "type.", "",
1307 "The", "goal", "of", "the", "game", "is", "to", "score", "as", "many",
1308 "points", "as", "possible", "before", "running", "out", "of",
1309 "available", "moves.", "", "",
1310 "Controls", "",
1311 "Directions",
1312 #ifdef JEWELS_SCROLLWHEEL
1313 "or", "scroll",
1314 #endif
1315 "to", "move", "",
1316 HK_SELECT, "to", "select", "",
1317 HK_CANCEL, "to", "go", "to", "menu"
1319 static struct style_text formation[]={
1320 { 0, TEXT_CENTER|TEXT_UNDERLINE },
1321 { 2, C_RED },
1322 { 42, C_RED },
1323 LAST_STYLE_ITEM
1326 rb->lcd_setfont(FONT_UI);
1327 if (display_text(ARRAYLEN(help_text), help_text, formation, NULL, true))
1328 return true;
1329 rb->lcd_setfont(FONT_SYSFIXED);
1331 return false;
1334 static bool _ingame;
1335 static int jewels_menu_cb(int action, const struct menu_item_ex *this_item)
1337 int i = ((intptr_t)this_item);
1338 if(action == ACTION_REQUEST_MENUITEM
1339 && !_ingame && (i==0 || i==6))
1340 return ACTION_EXIT_MENUITEM;
1341 return action;
1343 /*****************************************************************************
1344 * jewels_game_menu() shows the game menu.
1345 ******************************************************************************/
1346 static int jewels_game_menu(struct game_context* bj, bool ingame)
1348 rb->button_clear_queue();
1349 int choice = 0;
1351 _ingame = ingame;
1353 static struct opt_items mode[] = {
1354 { "Normal", -1 },
1355 { "Puzzle", -1 },
1358 MENUITEM_STRINGLIST (main_menu, "Jewels Menu", jewels_menu_cb,
1359 "Resume Game",
1360 "Start New Game",
1361 "Mode",
1362 "Help",
1363 "High Scores",
1364 "Playback Control",
1365 "Quit without Saving",
1366 "Quit");
1368 while (1) {
1369 switch (rb->do_menu(&main_menu, &choice, NULL, false)) {
1370 case 0:
1371 jewels_setcolors();
1372 if(resume_file)
1373 rb->remove(SAVE_FILE);
1374 return 0;
1375 case 1:
1376 jewels_init(bj);
1377 return 0;
1378 case 2:
1379 rb->set_option("Mode", &bj->tmp_type, INT, mode, 2, NULL);
1380 break;
1381 case 3:
1382 if(jewels_help())
1383 return 1;
1384 break;
1385 case 4:
1386 highscore_show(-1, highscores, NUM_SCORES, true);
1387 break;
1388 case 5:
1389 playback_control(NULL);
1390 break;
1391 case 6:
1392 return 1;
1393 case 7:
1394 if (ingame) {
1395 rb->splash(HZ*1, "Saving game ...");
1396 jewels_savegame(bj);
1398 return 1;
1399 case MENU_ATTACHED_USB:
1400 return 1;
1401 default:
1402 break;
1407 static int jewels_main(struct game_context* bj) {
1408 int button;
1409 int position;
1410 bool selected = false;
1411 bool no_movesavail;
1412 int x=0, y=0;
1414 bool loaded = jewels_loadgame(bj);
1415 resume_file = loaded;
1416 if (jewels_game_menu(bj, loaded)!=0)
1417 return 0;
1419 resume_file = false;
1420 while(true) {
1421 no_movesavail = false;
1423 /* refresh the board */
1424 jewels_drawboard(bj);
1426 /* display the cursor */
1427 if(selected) {
1428 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
1429 rb->lcd_fillrect(x*TILE_WIDTH, y*TILE_HEIGHT+YOFS,
1430 TILE_WIDTH, TILE_HEIGHT);
1431 rb->lcd_set_drawmode(DRMODE_SOLID);
1432 } else {
1433 rb->lcd_drawrect(x*TILE_WIDTH, y*TILE_HEIGHT+YOFS,
1434 TILE_WIDTH, TILE_HEIGHT);
1436 rb->lcd_update_rect(x*TILE_WIDTH, y*TILE_HEIGHT+YOFS,
1437 TILE_WIDTH, TILE_HEIGHT);
1439 /* handle game button presses */
1440 rb->yield();
1441 button = rb->button_get(true);
1442 switch(button){
1443 case JEWELS_LEFT: /* move cursor left */
1444 case (JEWELS_LEFT|BUTTON_REPEAT):
1445 if(selected) {
1446 bj->score += jewels_swapjewels(bj, x, y, SWAP_LEFT);
1447 selected = false;
1448 if (!jewels_movesavail(bj)) no_movesavail = true;
1449 } else {
1450 x = (x+BJ_WIDTH-1)%BJ_WIDTH;
1452 break;
1454 case JEWELS_RIGHT: /* move cursor right */
1455 case (JEWELS_RIGHT|BUTTON_REPEAT):
1456 if(selected) {
1457 bj->score += jewels_swapjewels(bj, x, y, SWAP_RIGHT);
1458 selected = false;
1459 if (!jewels_movesavail(bj)) no_movesavail = true;
1460 } else {
1461 x = (x+1)%BJ_WIDTH;
1463 break;
1465 case JEWELS_DOWN: /* move cursor down */
1466 case (JEWELS_DOWN|BUTTON_REPEAT):
1467 if(selected) {
1468 bj->score += jewels_swapjewels(bj, x, y, SWAP_DOWN);
1469 selected = false;
1470 if (!jewels_movesavail(bj)) no_movesavail = true;
1471 } else {
1472 y = (y+1)%(BJ_HEIGHT-1);
1474 break;
1476 case JEWELS_UP: /* move cursor up */
1477 case (JEWELS_UP|BUTTON_REPEAT):
1478 if(selected) {
1479 bj->score += jewels_swapjewels(bj, x, y, SWAP_UP);
1480 selected = false;
1481 if (!jewels_movesavail(bj)) no_movesavail = true;
1482 } else {
1483 y = (y+(BJ_HEIGHT-1)-1)%(BJ_HEIGHT-1);
1485 break;
1487 #ifdef JEWELS_SCROLLWHEEL
1488 case JEWELS_PREV: /* scroll backwards */
1489 case (JEWELS_PREV|BUTTON_REPEAT):
1490 if(!selected) {
1491 if(x == 0) {
1492 y = (y+(BJ_HEIGHT-1)-1)%(BJ_HEIGHT-1);
1494 x = (x+BJ_WIDTH-1)%BJ_WIDTH;
1496 break;
1498 case JEWELS_NEXT: /* scroll forwards */
1499 case (JEWELS_NEXT|BUTTON_REPEAT):
1500 if(!selected) {
1501 if(x == BJ_WIDTH-1) {
1502 y = (y+1)%(BJ_HEIGHT-1);
1504 x = (x+1)%BJ_WIDTH;
1506 break;
1507 #endif
1509 case JEWELS_SELECT: /* toggle selected */
1510 selected = !selected;
1511 break;
1513 #ifdef JEWELS_RC_CANCEL
1514 case JEWELS_RC_CANCEL:
1515 #endif
1516 case JEWELS_CANCEL: /* end game */
1517 if (jewels_game_menu(bj, true)!=0)
1518 return 0;
1519 break;
1521 default:
1522 if (rb->default_event_handler (button) == SYS_USB_CONNECTED)
1523 return PLUGIN_USB_CONNECTED;
1524 break;
1527 switch(bj->type) {
1528 case GAME_TYPE_NORMAL:
1529 if(bj->score >= LEVEL_PTS)
1530 jewels_nextlevel(bj);
1531 break;
1532 case GAME_TYPE_PUZZLE:
1533 if (jewels_puzzle_is_finished(bj)) {
1534 if (bj->level < NUM_PUZZLE_LEVELS) {
1535 jewels_nextlevel(bj);
1536 } else {
1537 rb->splash(2*HZ, "Congratulations!");
1538 rb->splash(2*HZ, "You have finished the game!");
1539 if (jewels_game_menu(bj, false)!=0) {
1540 return 0;
1543 break;
1547 if (no_movesavail) {
1548 switch(bj->type) {
1549 case GAME_TYPE_NORMAL:
1550 rb->splash(HZ*2, "Game Over!");
1551 rb->lcd_clear_display();
1552 bj->score += (bj->level-1)*LEVEL_PTS;
1553 position=highscore_update(bj->score, bj->level, "",
1554 highscores, NUM_SCORES);
1555 if (position != -1)
1557 if (position == 0)
1558 rb->splash(HZ*2, "New High Score");
1559 highscore_show(position, highscores, NUM_SCORES, true);
1561 break;
1562 case GAME_TYPE_PUZZLE:
1563 rb->splash(2*HZ, "Game Over");
1564 break;
1566 if (jewels_game_menu(bj, false)!=0) {
1567 return 0;
1573 /* this is the plugin entry point */
1574 enum plugin_status plugin_start(const void* parameter)
1576 (void)parameter;
1578 /* load high scores */
1579 highscore_load(SCORE_FILE, highscores, NUM_SCORES);
1581 rb->lcd_setfont(FONT_SYSFIXED);
1582 #if LCD_DEPTH > 1
1583 rb->lcd_set_backdrop(NULL);
1584 #endif
1586 struct game_context bj;
1587 bj.tmp_type = GAME_TYPE_NORMAL;
1588 jewels_main(&bj);
1589 highscore_save(SCORE_FILE, highscores, NUM_SCORES);
1590 rb->lcd_setfont(FONT_UI);
1592 return PLUGIN_OK;
1595 #endif /* HAVE_LCD_BITMAP */