Fix a possiblity where viewport_set_default left the viewport unchanged. Improve...
[kugel-rb.git] / apps / plugins / jewels.c
bloba55b1b2dae18f876103067b6fbeed11199cb2f81
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 == COWOND2_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 == ONDAVX747_PAD || \
245 CONFIG_KEYPAD == ONDAVX777_PAD || \
246 CONFIG_KEYPAD == MROBE500_PAD
247 #define JEWELS_CANCEL BUTTON_POWER
248 #define HK_CANCEL "POWER"
250 #elif CONFIG_KEYPAD == SAMSUNG_YH_PAD
251 #define JEWELS_UP BUTTON_UP
252 #define JEWELS_DOWN BUTTON_DOWN
253 #define JEWELS_LEFT BUTTON_LEFT
254 #define JEWELS_RIGHT BUTTON_RIGHT
255 #define JEWELS_SELECT BUTTON_PLAY
256 #define JEWELS_CANCEL BUTTON_REW
257 #define HK_SELECT "PLAY"
258 #define HK_CANCEL "REWIND"
260 #else
261 #error No keymap defined!
262 #endif
264 #ifdef HAVE_TOUCHSCREEN
265 #ifndef JEWELS_UP
266 #define JEWELS_UP BUTTON_TOPMIDDLE
267 #endif
268 #ifndef JEWELS_DOWN
269 #define JEWELS_DOWN BUTTON_BOTTOMMIDDLE
270 #endif
271 #ifndef JEWELS_LEFT
272 #define JEWELS_LEFT BUTTON_MIDLEFT
273 #endif
274 #ifndef JEWELS_RIGHT
275 #define JEWELS_RIGHT BUTTON_MIDRIGHT
276 #endif
277 #ifndef JEWELS_SELECT
278 #define JEWELS_SELECT BUTTON_CENTER
279 #define HK_SELECT "CENTER"
280 #endif
281 #ifndef JEWELS_CANCEL
282 #define JEWELS_CANCEL BUTTON_TOPLEFT
283 #define HK_CANCEL "TOPLEFT"
284 #endif
285 #endif
287 #define TILE_WIDTH BMPWIDTH_jewels
288 #define TILE_HEIGHT (BMPHEIGHT_jewels/23)
290 #if LCD_HEIGHT < LCD_WIDTH
291 /* This calculation assumes integer division w/ LCD_HEIGHT/TILE_HEIGHT */
292 #define YOFS LCD_HEIGHT-((LCD_HEIGHT/TILE_HEIGHT)*TILE_HEIGHT)
293 #else
294 #define YOFS 0
295 #endif
297 #define NUM_SCORES 5
299 /* swap directions */
300 #define SWAP_UP 0
301 #define SWAP_RIGHT 1
302 #define SWAP_DOWN 2
303 #define SWAP_LEFT 3
305 /* play board dimension */
306 #define BJ_HEIGHT 9
307 #define BJ_WIDTH 8
309 /* next level threshold */
310 #define LEVEL_PTS 100
312 /* animation frame rate */
313 #define MAX_FPS 20
315 /* text margin */
316 #define MARGIN 5
318 /* Game types */
319 enum game_type {
320 GAME_TYPE_NORMAL,
321 GAME_TYPE_PUZZLE
324 /* external bitmaps */
325 extern const fb_data jewels[];
327 /* tile background colors */
328 #ifdef HAVE_LCD_COLOR
329 static const unsigned jewels_bkgd[2] = {
330 LCD_RGBPACK(104, 63, 63),
331 LCD_RGBPACK(83, 44, 44)
333 #endif
335 /* the tile struct
336 * type is the jewel number 0-7
337 * falling if the jewel is falling
338 * delete marks the jewel for deletion
340 struct tile {
341 int type;
342 bool falling;
343 bool delete;
346 /* the game context struct
347 * score is the current level score
348 * segments is the number of cleared segments in the current run
349 * level is the current level
350 * tmp_type is the select type in the menu
351 * type is the game type (normal or puzzle)
352 * playboard is the game playing board (first row is hidden)
353 * num_jewels is the number of different jewels to use
355 struct game_context {
356 unsigned int score;
357 unsigned int segments;
358 unsigned int level;
359 unsigned int type;
360 unsigned int tmp_type;
361 struct tile playboard[BJ_HEIGHT][BJ_WIDTH];
362 unsigned int num_jewels;
365 #define MAX_NUM_JEWELS 7
367 #define MAX_PUZZLE_TILES 4
368 #define NUM_PUZZLE_LEVELS 10
370 struct puzzle_tile {
371 int x;
372 int y;
373 int tile_type;
376 struct puzzle_level {
377 unsigned int num_jewels;
378 unsigned int num_tiles;
379 struct puzzle_tile tiles[MAX_PUZZLE_TILES];
382 #define PUZZLE_TILE_UP 1
383 #define PUZZLE_TILE_DOWN 2
384 #define PUZZLE_TILE_LEFT 4
385 #define PUZZLE_TILE_RIGHT 8
387 struct puzzle_level puzzle_levels[NUM_PUZZLE_LEVELS] = {
388 { 5, 2, { {3, 3, PUZZLE_TILE_RIGHT},
389 {4, 2, PUZZLE_TILE_LEFT} } },
390 { 5, 2, { {3, 2, PUZZLE_TILE_DOWN},
391 {3, 4, PUZZLE_TILE_UP} } },
392 { 6, 3, { {3, 2, PUZZLE_TILE_DOWN},
393 {3, 4, PUZZLE_TILE_UP|PUZZLE_TILE_DOWN},
394 {3, 6, PUZZLE_TILE_UP} } },
395 { 6, 3, { {3, 2, PUZZLE_TILE_RIGHT},
396 {4, 3, PUZZLE_TILE_LEFT|PUZZLE_TILE_RIGHT},
397 {5, 4, PUZZLE_TILE_LEFT} } },
398 { 6, 2, { {3, 4, PUZZLE_TILE_RIGHT},
399 {4, 2, PUZZLE_TILE_LEFT} } },
400 { 6, 2, { {3, 2, PUZZLE_TILE_DOWN},
401 {4, 4, PUZZLE_TILE_UP} } },
402 { 7, 4, { {3, 2, PUZZLE_TILE_RIGHT|PUZZLE_TILE_DOWN},
403 {4, 3, PUZZLE_TILE_LEFT|PUZZLE_TILE_DOWN},
404 {3, 4, PUZZLE_TILE_RIGHT|PUZZLE_TILE_UP},
405 {4, 4, PUZZLE_TILE_LEFT|PUZZLE_TILE_UP} } },
406 { 6, 3, { {3, 2, PUZZLE_TILE_DOWN},
407 {4, 4, PUZZLE_TILE_UP|PUZZLE_TILE_DOWN},
408 {3, 6, PUZZLE_TILE_UP} } },
409 { 7, 3, { {2, 2, PUZZLE_TILE_RIGHT},
410 {4, 1, PUZZLE_TILE_LEFT|PUZZLE_TILE_RIGHT},
411 {5, 4, PUZZLE_TILE_LEFT} } },
412 { 7, 4, { {3, 0, PUZZLE_TILE_RIGHT|PUZZLE_TILE_DOWN},
413 {5, 0, PUZZLE_TILE_LEFT|PUZZLE_TILE_DOWN},
414 {2, 7, PUZZLE_TILE_RIGHT|PUZZLE_TILE_UP},
415 {4, 7, PUZZLE_TILE_LEFT|PUZZLE_TILE_UP} } },
418 #define SAVE_FILE PLUGIN_GAMES_DIR "/jewels.save"
420 #define HIGH_SCORE PLUGIN_GAMES_DIR "/jewels.score"
421 struct highscore highest[NUM_SCORES];
423 static bool resume_file = false;
425 /*****************************************************************************
426 * jewels_setcolors() set the foreground and background colors.
427 ******************************************************************************/
428 static inline void jewels_setcolors(void) {
429 #ifdef HAVE_LCD_COLOR
430 rb->lcd_set_background(LCD_RGBPACK(49, 26, 26));
431 rb->lcd_set_foreground(LCD_RGBPACK(210, 181, 181));
432 #endif
435 /*****************************************************************************
436 * jewels_loadgame() loads the saved game and returns load success.
437 ******************************************************************************/
438 static bool jewels_loadgame(struct game_context* bj)
440 int fd;
441 bool loaded = false;
443 /* open game file */
444 fd = rb->open(SAVE_FILE, O_RDONLY);
445 if(fd < 0) return loaded;
447 /* read in saved game */
448 while(true) {
449 if(rb->read(fd, &bj->tmp_type, sizeof(bj->tmp_type)) <= 0) break;
450 if(rb->read(fd, &bj->type, sizeof(bj->type)) <= 0) break;
451 if(rb->read(fd, &bj->score, sizeof(bj->score)) <= 0) break;
452 if(rb->read(fd, &bj->level, sizeof(bj->level)) <= 0) break;
453 if(rb->read(fd, &bj->segments, sizeof(bj->segments)) <= 0) break;
454 if(rb->read(fd, &bj->num_jewels, sizeof(bj->num_jewels)) <= 0) break;
455 if(rb->read(fd, bj->playboard, sizeof(bj->playboard)) <= 0) break;
456 loaded = true;
457 break;
460 rb->close(fd);
462 return loaded;
465 /*****************************************************************************
466 * jewels_savegame() saves the current game state.
467 ******************************************************************************/
468 static void jewels_savegame(struct game_context* bj)
470 int fd;
471 /* write out the game state to the save file */
472 fd = rb->open(SAVE_FILE, O_WRONLY|O_CREAT);
473 rb->write(fd, &bj->tmp_type, sizeof(bj->tmp_type));
474 rb->write(fd, &bj->type, sizeof(bj->type));
475 rb->write(fd, &bj->score, sizeof(bj->score));
476 rb->write(fd, &bj->level, sizeof(bj->level));
477 rb->write(fd, &bj->segments, sizeof(bj->segments));
478 rb->write(fd, &bj->num_jewels, sizeof(bj->num_jewels));
479 rb->write(fd, bj->playboard, sizeof(bj->playboard));
480 rb->close(fd);
483 /*****************************************************************************
484 * jewels_drawboard() redraws the entire game board.
485 ******************************************************************************/
486 static void jewels_drawboard(struct game_context* bj) {
487 int i, j;
488 int w, h;
489 unsigned int tempscore;
490 unsigned int size;
491 char *title = "Level";
492 char str[10];
494 if (bj->type == GAME_TYPE_NORMAL) {
495 tempscore = (bj->score>LEVEL_PTS ? LEVEL_PTS : bj->score);
496 size = LEVEL_PTS;
497 } else {
498 tempscore = (bj->level>NUM_PUZZLE_LEVELS ? NUM_PUZZLE_LEVELS : bj->level);
499 size = NUM_PUZZLE_LEVELS;
502 /* clear screen */
503 rb->lcd_clear_display();
505 /* dispay playing board */
506 for(i=0; i<BJ_HEIGHT-1; i++){
507 for(j=0; j<BJ_WIDTH; j++){
508 #ifdef HAVE_LCD_COLOR
509 rb->lcd_set_foreground(jewels_bkgd[(i+j)%2]);
510 rb->lcd_fillrect(j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
511 TILE_WIDTH, TILE_HEIGHT);
512 rb->lcd_bitmap_transparent_part(jewels,
513 0, TILE_HEIGHT*(bj->playboard[i+1][j].type),
514 STRIDE( SCREEN_MAIN,
515 BMPWIDTH_jewels, BMPHEIGHT_jewels),
516 j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
517 TILE_WIDTH, TILE_HEIGHT);
518 #else
519 rb->lcd_bitmap_part(jewels,
520 0, TILE_HEIGHT*(bj->playboard[i+1][j].type),
521 STRIDE( SCREEN_MAIN,
522 BMPWIDTH_jewels, BMPHEIGHT_jewels),
523 j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
524 TILE_WIDTH, TILE_HEIGHT);
525 #endif
529 #if LCD_WIDTH > LCD_HEIGHT /* horizontal layout */
531 /* draw separator lines */
532 jewels_setcolors();
533 rb->lcd_vline(BJ_WIDTH*TILE_WIDTH, 0, LCD_HEIGHT-1);
535 rb->lcd_hline(BJ_WIDTH*TILE_WIDTH, LCD_WIDTH-1, 18);
536 rb->lcd_hline(BJ_WIDTH*TILE_WIDTH, LCD_WIDTH-1, LCD_HEIGHT-10);
538 /* draw progress bar */
539 #ifdef HAVE_LCD_COLOR
540 rb->lcd_set_foreground(LCD_RGBPACK(104, 63, 63));
541 #endif
542 rb->lcd_fillrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4,
543 (LCD_HEIGHT-10)-(((LCD_HEIGHT-10)-18)*
544 tempscore/size),
545 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2,
546 ((LCD_HEIGHT-10)-18)*tempscore/size);
547 #ifdef HAVE_LCD_COLOR
548 rb->lcd_set_foreground(LCD_RGBPACK(83, 44, 44));
549 rb->lcd_drawrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4+1,
550 (LCD_HEIGHT-10)-(((LCD_HEIGHT-10)-18)*
551 tempscore/size)+1,
552 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-2,
553 ((LCD_HEIGHT-10)-18)*tempscore/size-1);
554 jewels_setcolors();
555 rb->lcd_drawrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4,
556 (LCD_HEIGHT-10)-(((LCD_HEIGHT-10)-18)*tempscore/size),
557 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2,
558 ((LCD_HEIGHT-10)-18)*tempscore/size+1);
559 #endif
561 /* print text */
562 rb->lcd_getstringsize(title, &w, &h);
563 rb->lcd_putsxy(LCD_WIDTH-(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-w/2, 1, title);
564 rb->snprintf(str, 4, "%d", bj->level);
565 rb->lcd_getstringsize(str, &w, &h);
566 rb->lcd_putsxy(LCD_WIDTH-(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-w/2, 10, str);
568 if (bj->type == GAME_TYPE_NORMAL) {
569 rb->snprintf(str, 6, "%d", (bj->level-1)*LEVEL_PTS+bj->score);
570 rb->lcd_getstringsize(str, &w, &h);
571 rb->lcd_putsxy(LCD_WIDTH-(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-w/2,
572 LCD_HEIGHT-8, str);
575 #elif LCD_WIDTH < LCD_HEIGHT /* vertical layout */
577 /* draw separator lines */
578 jewels_setcolors();
579 rb->lcd_hline(0, LCD_WIDTH-1, 8*TILE_HEIGHT+YOFS);
580 rb->lcd_hline(0, LCD_WIDTH-1, LCD_HEIGHT-14);
581 rb->lcd_vline(LCD_WIDTH/2, LCD_HEIGHT-14, LCD_HEIGHT-1);
583 /* draw progress bar */
584 #ifdef HAVE_LCD_COLOR
585 rb->lcd_set_foreground(LCD_RGBPACK(104, 63, 63));
586 #endif
587 rb->lcd_fillrect(0, (8*TILE_HEIGHT+YOFS)
588 +(LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/4,
589 LCD_WIDTH*tempscore/size,
590 (LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/2);
591 #ifdef HAVE_LCD_COLOR
592 rb->lcd_set_foreground(LCD_RGBPACK(83, 44, 44));
593 rb->lcd_drawrect(1, (8*TILE_HEIGHT+YOFS)
594 +(LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/4+1,
595 LCD_WIDTH*tempscore/size-1,
596 (LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/2-2);
597 jewels_setcolors();
598 rb->lcd_drawrect(0, (8*TILE_HEIGHT+YOFS)
599 +(LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/4,
600 LCD_WIDTH*tempscore/size+1,
601 (LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/2);
602 #endif
604 /* print text */
605 rb->snprintf(str, 10, "%s %d", title, bj->level);
606 rb->lcd_putsxy(1, LCD_HEIGHT-10, str);
608 if (bj->type == GAME_TYPE_NORMAL) {
609 rb->snprintf(str, 6, "%d", (bj->level-1)*LEVEL_PTS+bj->score);
610 rb->lcd_getstringsize(str, &w, &h);
611 rb->lcd_putsxy((LCD_WIDTH-2)-w, LCD_HEIGHT-10, str);
615 #else /* square layout */
617 /* draw separator lines */
618 jewels_setcolors();
619 rb->lcd_hline(0, LCD_WIDTH-1, 8*TILE_HEIGHT+YOFS);
620 rb->lcd_vline(BJ_WIDTH*TILE_WIDTH, 0, 8*TILE_HEIGHT+YOFS);
621 rb->lcd_vline(LCD_WIDTH/2, 8*TILE_HEIGHT+YOFS, LCD_HEIGHT-1);
623 /* draw progress bar */
624 #ifdef HAVE_LCD_COLOR
625 rb->lcd_set_foreground(LCD_RGBPACK(104, 63, 63));
626 #endif
627 rb->lcd_fillrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4,
628 (8*TILE_HEIGHT+YOFS)-(8*TILE_HEIGHT+YOFS)*tempscore/size,
629 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2,
630 (8*TILE_HEIGHT+YOFS)*tempscore/size);
631 #ifdef HAVE_LCD_COLOR
632 rb->lcd_set_foreground(LCD_RGBPACK(83, 44, 44));
633 rb->lcd_drawrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4+1,
634 (8*TILE_HEIGHT+YOFS)-(8*TILE_HEIGHT+YOFS)
635 *tempscore/size+1,
636 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-2,
637 (8*TILE_HEIGHT+YOFS)*tempscore/size-1);
638 jewels_setcolors();
639 rb->lcd_drawrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4,
640 (8*TILE_HEIGHT+YOFS)-(8*TILE_HEIGHT+YOFS)
641 *tempscore/size,
642 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2,
643 (8*TILE_HEIGHT+YOFS)*tempscore/size+1);
644 #endif
646 /* print text */
647 rb->snprintf(str, 10, "%s %d", title, bj->level);
648 rb->lcd_putsxy(1, LCD_HEIGHT-(LCD_HEIGHT-(8*TILE_HEIGHT+YOFS))/2-3, str);
650 if (bj->type == GAME_TYPE_NORMAL) {
651 rb->snprintf(str, 6, "%d", (bj->level-1)*LEVEL_PTS+bj->score);
652 rb->lcd_getstringsize(str, &w, &h);
653 rb->lcd_putsxy((LCD_WIDTH-2)-w,
654 LCD_HEIGHT-(LCD_HEIGHT-(8*TILE_HEIGHT+YOFS))/2-3, str);
657 #endif /* layout */
659 rb->lcd_update();
662 /*****************************************************************************
663 * jewels_putjewels() makes the jewels fall to fill empty spots and adds
664 * new random jewels at the empty spots at the top of each row.
665 ******************************************************************************/
666 static void jewels_putjewels(struct game_context* bj){
667 int i, j, k;
668 bool mark, done;
669 long lasttick, currenttick;
671 /* loop to make all the jewels fall */
672 while(true) {
673 /* mark falling jewels and add new jewels to hidden top row*/
674 mark = false;
675 done = true;
676 for(j=0; j<BJ_WIDTH; j++) {
677 if(bj->playboard[1][j].type == 0) {
678 bj->playboard[0][j].type = rb->rand()%bj->num_jewels+1;
680 for(i=BJ_HEIGHT-2; i>=0; i--) {
681 if(!mark && bj->playboard[i+1][j].type == 0) {
682 mark = true;
683 done = false;
685 if(mark) bj->playboard[i][j].falling = true;
687 /*if(bj->playboard[1][j].falling) {
688 bj->playboard[0][j].type = rb->rand()%bj->num_jewels+1;
689 bj->playboard[0][j].falling = true;
691 mark = false;
694 /* break if there are no falling jewels */
695 if(done) break;
697 /* animate falling jewels */
698 lasttick = *rb->current_tick;
700 for(k=1; k<=8; k++) {
701 for(i=BJ_HEIGHT-2; i>=0; i--) {
702 for(j=0; j<BJ_WIDTH; j++) {
703 if(bj->playboard[i][j].falling &&
704 bj->playboard[i][j].type != 0) {
705 /* clear old position */
706 #ifdef HAVE_LCD_COLOR
707 if(i == 0 && YOFS) {
708 rb->lcd_set_foreground(rb->lcd_get_background());
709 } else {
710 rb->lcd_set_foreground(jewels_bkgd[(i-1+j)%2]);
712 rb->lcd_fillrect(j*TILE_WIDTH, (i-1)*TILE_HEIGHT+YOFS,
713 TILE_WIDTH, TILE_HEIGHT);
714 if(bj->playboard[i+1][j].type == 0) {
715 rb->lcd_set_foreground(jewels_bkgd[(i+j)%2]);
716 rb->lcd_fillrect(j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
717 TILE_WIDTH, TILE_HEIGHT);
719 #else
720 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
721 rb->lcd_fillrect(j*TILE_WIDTH, (i-1)*TILE_HEIGHT+YOFS,
722 TILE_WIDTH, TILE_HEIGHT);
723 if(bj->playboard[i+1][j].type == 0) {
724 rb->lcd_fillrect(j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
725 TILE_WIDTH, TILE_HEIGHT);
727 rb->lcd_set_drawmode(DRMODE_SOLID);
728 #endif
730 /* draw new position */
731 #ifdef HAVE_LCD_COLOR
732 rb->lcd_bitmap_transparent_part(jewels, 0,
733 TILE_HEIGHT*(bj->playboard[i][j].type),
734 STRIDE( SCREEN_MAIN,
735 BMPWIDTH_jewels,
736 BMPHEIGHT_jewels),
737 j*TILE_WIDTH,
738 (i-1)*TILE_HEIGHT+YOFS+
739 ((((TILE_HEIGHT<<10)*k)/8)>>10),
740 TILE_WIDTH, TILE_HEIGHT);
741 #else
742 rb->lcd_bitmap_part(jewels, 0,
743 TILE_HEIGHT*(bj->playboard[i][j].type),
744 STRIDE( SCREEN_MAIN,
745 BMPWIDTH_jewels,
746 BMPHEIGHT_jewels),
747 j*TILE_WIDTH,
748 (i-1)*TILE_HEIGHT+YOFS+
749 ((((TILE_HEIGHT<<10)*k)/8)>>10),
750 TILE_WIDTH, TILE_HEIGHT);
751 #endif
756 rb->lcd_update_rect(0, 0, TILE_WIDTH*8, LCD_HEIGHT);
757 jewels_setcolors();
759 /* framerate limiting */
760 currenttick = *rb->current_tick;
761 if(currenttick-lasttick < HZ/MAX_FPS) {
762 rb->sleep((HZ/MAX_FPS)-(currenttick-lasttick));
763 } else {
764 rb->yield();
766 lasttick = currenttick;
769 /* shift jewels down */
770 for(j=0; j<BJ_WIDTH; j++) {
771 for(i=BJ_HEIGHT-1; i>=1; i--) {
772 if(bj->playboard[i-1][j].falling) {
773 bj->playboard[i][j].type = bj->playboard[i-1][j].type;
778 /* clear out top row */
779 for(j=0; j<BJ_WIDTH; j++) {
780 bj->playboard[0][j].type = 0;
783 /* mark everything not falling */
784 for(i=0; i<BJ_HEIGHT; i++) {
785 for(j=0; j<BJ_WIDTH; j++) {
786 bj->playboard[i][j].falling = false;
792 /*****************************************************************************
793 * jewels_clearjewels() finds all the connected rows and columns and
794 * calculates and returns the points earned.
795 ******************************************************************************/
796 static unsigned int jewels_clearjewels(struct game_context* bj) {
797 int i, j;
798 int last, run;
799 unsigned int points = 0;
801 /* check for connected rows */
802 for(i=1; i<BJ_HEIGHT; i++) {
803 last = 0;
804 run = 1;
805 for(j=0; j<BJ_WIDTH; j++) {
806 if(bj->playboard[i][j].type == last &&
807 bj->playboard[i][j].type != 0 &&
808 bj->playboard[i][j].type <= MAX_NUM_JEWELS) {
809 run++;
811 if(run == 3) {
812 bj->segments++;
813 points += bj->segments;
814 bj->playboard[i][j].delete = true;
815 bj->playboard[i][j-1].delete = true;
816 bj->playboard[i][j-2].delete = true;
817 } else if(run > 3) {
818 points++;
819 bj->playboard[i][j].delete = true;
821 } else {
822 run = 1;
823 last = bj->playboard[i][j].type;
828 /* check for connected columns */
829 for(j=0; j<BJ_WIDTH; j++) {
830 last = 0;
831 run = 1;
832 for(i=1; i<BJ_HEIGHT; i++) {
833 if(bj->playboard[i][j].type != 0 &&
834 bj->playboard[i][j].type == last &&
835 bj->playboard[i][j].type <= MAX_NUM_JEWELS) {
836 run++;
838 if(run == 3) {
839 bj->segments++;
840 points += bj->segments;
841 bj->playboard[i][j].delete = true;
842 bj->playboard[i-1][j].delete = true;
843 bj->playboard[i-2][j].delete = true;
844 } else if(run > 3) {
845 points++;
846 bj->playboard[i][j].delete = true;
848 } else {
849 run = 1;
850 last = bj->playboard[i][j].type;
855 /* clear deleted jewels */
856 for(i=1; i<BJ_HEIGHT; i++) {
857 for(j=0; j<BJ_WIDTH; j++) {
858 if(bj->playboard[i][j].delete) {
859 bj->playboard[i][j].delete = false;
860 bj->playboard[i][j].type = 0;
865 return points;
868 /*****************************************************************************
869 * jewels_runboard() runs the board until it settles in a fixed state and
870 * returns points earned.
871 ******************************************************************************/
872 static unsigned int jewels_runboard(struct game_context* bj) {
873 unsigned int points = 0;
874 unsigned int ret;
876 bj->segments = 0;
878 while((ret = jewels_clearjewels(bj)) > 0) {
879 points += ret;
880 jewels_drawboard(bj);
881 jewels_putjewels(bj);
884 return points;
887 /*****************************************************************************
888 * jewels_swapjewels() swaps two jewels as long as it results in points and
889 * returns points earned.
890 ******************************************************************************/
891 static unsigned int jewels_swapjewels(struct game_context* bj,
892 int x, int y, int direc) {
893 int k;
894 int horzmod, vertmod;
895 int movelen = 0;
896 bool undo = false;
897 unsigned int points = 0;
898 long lasttick, currenttick;
900 /* check for invalid parameters */
901 if(x < 0 || x >= BJ_WIDTH || y < 0 || y >= BJ_HEIGHT-1 ||
902 direc < SWAP_UP || direc > SWAP_LEFT) {
903 return 0;
906 /* check for invalid directions */
907 if((x == 0 && direc == SWAP_LEFT) ||
908 (x == BJ_WIDTH-1 && direc == SWAP_RIGHT) ||
909 (y == 0 && direc == SWAP_UP) ||
910 (y == BJ_HEIGHT-2 && direc == SWAP_DOWN)) {
911 return 0;
914 /* set direction variables */
915 horzmod = 0;
916 vertmod = 0;
917 switch(direc) {
918 case SWAP_UP:
919 vertmod = -1;
920 movelen = TILE_HEIGHT;
921 break;
922 case SWAP_RIGHT:
923 horzmod = 1;
924 movelen = TILE_WIDTH;
925 break;
926 case SWAP_DOWN:
927 vertmod = 1;
928 movelen = TILE_HEIGHT;
929 break;
930 case SWAP_LEFT:
931 horzmod = -1;
932 movelen = TILE_WIDTH;
933 break;
936 while(true) {
937 lasttick = *rb->current_tick;
939 /* animate swapping jewels */
940 for(k=0; k<=8; k++) {
941 /* clear old position */
942 #ifdef HAVE_LCD_COLOR
943 rb->lcd_set_foreground(jewels_bkgd[(x+y)%2]);
944 rb->lcd_fillrect(x*TILE_WIDTH,
945 y*TILE_HEIGHT+YOFS,
946 TILE_WIDTH, TILE_HEIGHT);
947 rb->lcd_set_foreground(jewels_bkgd[(x+horzmod+y+vertmod)%2]);
948 rb->lcd_fillrect((x+horzmod)*TILE_WIDTH,
949 (y+vertmod)*TILE_HEIGHT+YOFS,
950 TILE_WIDTH, TILE_HEIGHT);
951 #else
952 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
953 rb->lcd_fillrect(x*TILE_WIDTH,
954 y*TILE_HEIGHT+YOFS,
955 TILE_WIDTH, TILE_HEIGHT);
956 rb->lcd_fillrect((x+horzmod)*TILE_WIDTH,
957 (y+vertmod)*TILE_HEIGHT+YOFS,
958 TILE_WIDTH, TILE_HEIGHT);
959 rb->lcd_set_drawmode(DRMODE_SOLID);
960 #endif
961 /* draw new position */
962 #ifdef HAVE_LCD_COLOR
963 rb->lcd_bitmap_transparent_part(jewels,
964 0, TILE_HEIGHT*(bj->playboard
965 [y+1+vertmod][x+horzmod].type),
966 STRIDE( SCREEN_MAIN,
967 BMPWIDTH_jewels, BMPHEIGHT_jewels),
968 (x+horzmod)*TILE_WIDTH-horzmod*
969 ((((movelen<<10)*k)/8)>>10),
970 (y+vertmod)*TILE_HEIGHT-vertmod*
971 ((((movelen<<10)*k)/8)>>10)+YOFS,
972 TILE_WIDTH, TILE_HEIGHT);
973 rb->lcd_bitmap_transparent_part(jewels,
974 0, TILE_HEIGHT*(bj->playboard[y+1][x].type),
975 STRIDE( SCREEN_MAIN,
976 BMPWIDTH_jewels, BMPHEIGHT_jewels),
977 x*TILE_WIDTH+horzmod*
978 ((((movelen<<10)*k)/8)>>10),
979 y*TILE_HEIGHT+vertmod*
980 ((((movelen<<10)*k)/8)>>10)+YOFS,
981 TILE_WIDTH, TILE_HEIGHT);
982 #else
983 rb->lcd_bitmap_part(jewels,
984 0, TILE_HEIGHT*(bj->playboard
985 [y+1+vertmod][x+horzmod].type),
986 STRIDE( SCREEN_MAIN,
987 BMPWIDTH_jewels, BMPHEIGHT_jewels),
988 (x+horzmod)*TILE_WIDTH-horzmod*
989 ((((movelen<<10)*k)/8)>>10),
990 (y+vertmod)*TILE_HEIGHT-vertmod*
991 ((((movelen<<10)*k)/8)>>10)+YOFS,
992 TILE_WIDTH, TILE_HEIGHT);
993 rb->lcd_set_drawmode(DRMODE_FG);
994 rb->lcd_bitmap_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 rb->lcd_set_drawmode(DRMODE_SOLID);
1004 #endif
1006 rb->lcd_update_rect(0, 0, TILE_WIDTH*8, LCD_HEIGHT);
1007 jewels_setcolors();
1009 /* framerate limiting */
1010 currenttick = *rb->current_tick;
1011 if(currenttick-lasttick < HZ/MAX_FPS) {
1012 rb->sleep((HZ/MAX_FPS)-(currenttick-lasttick));
1013 } else {
1014 rb->yield();
1016 lasttick = currenttick;
1019 /* swap jewels */
1020 int temp = bj->playboard[y+1][x].type;
1021 bj->playboard[y+1][x].type =
1022 bj->playboard[y+1+vertmod][x+horzmod].type;
1023 bj->playboard[y+1+vertmod][x+horzmod].type = temp;
1025 if(undo) break;
1027 points = jewels_runboard(bj);
1028 if(points == 0) {
1029 undo = true;
1030 } else {
1031 break;
1035 return points;
1038 /*****************************************************************************
1039 * jewels_movesavail() uses pattern matching to see if there are any
1040 * available move left.
1041 ******************************************************************************/
1042 static bool jewels_movesavail(struct game_context* bj) {
1043 int i, j;
1044 bool moves = false;
1045 int mytype;
1047 for(i=1; i<BJ_HEIGHT; i++) {
1048 for(j=0; j<BJ_WIDTH; j++) {
1049 mytype = bj->playboard[i][j].type;
1050 if(mytype == 0 || mytype > MAX_NUM_JEWELS) continue;
1052 /* check horizontal patterns */
1053 if(j <= BJ_WIDTH-3) {
1054 if(i > 1) {
1055 if(bj->playboard[i-1][j+1].type == mytype) {
1056 if(bj->playboard[i-1][j+2].type == mytype)
1057 {moves = true; break;}
1058 if(bj->playboard[i][j+2].type == mytype)
1059 {moves = true; break;}
1061 if(bj->playboard[i][j+1].type == mytype) {
1062 if(bj->playboard[i-1][j+2].type == mytype)
1063 {moves = true; break;}
1067 if(j <= BJ_WIDTH-4) {
1068 if(bj->playboard[i][j+3].type == mytype) {
1069 if(bj->playboard[i][j+1].type == mytype)
1070 {moves = true; break;}
1071 if(bj->playboard[i][j+2].type == mytype)
1072 {moves = true; break;}
1076 if(i < BJ_HEIGHT-1) {
1077 if(bj->playboard[i][j+1].type == mytype) {
1078 if(bj->playboard[i+1][j+2].type == mytype)
1079 {moves = true; break;}
1081 if(bj->playboard[i+1][j+1].type == mytype) {
1082 if(bj->playboard[i][j+2].type == mytype)
1083 {moves = true; break;}
1084 if(bj->playboard[i+1][j+2].type == mytype)
1085 {moves = true; break;}
1090 /* check vertical patterns */
1091 if(i <= BJ_HEIGHT-3) {
1092 if(j > 0) {
1093 if(bj->playboard[i+1][j-1].type == mytype) {
1094 if(bj->playboard[i+2][j-1].type == mytype)
1095 {moves = true; break;}
1096 if(bj->playboard[i+2][j].type == mytype)
1097 {moves = true; break;}
1099 if(bj->playboard[i+1][j].type == mytype) {
1100 if(bj->playboard[i+2][j-1].type == mytype)
1101 {moves = true; break;}
1105 if(i <= BJ_HEIGHT-4) {
1106 if(bj->playboard[i+3][j].type == mytype) {
1107 if(bj->playboard[i+1][j].type == mytype)
1108 {moves = true; break;}
1109 if(bj->playboard[i+2][j].type == mytype)
1110 {moves = true; break;}
1114 if(j < BJ_WIDTH-1) {
1115 if(bj->playboard[i+1][j].type == mytype) {
1116 if(bj->playboard[i+2][j+1].type == mytype)
1117 {moves = true; break;}
1119 if(bj->playboard[i+1][j+1].type == mytype) {
1120 if(bj->playboard[i+2][j].type == mytype)
1121 {moves = true; break;}
1122 if (bj->playboard[i+2][j+1].type == mytype)
1123 {moves = true; break;}
1128 if(moves) break;
1130 return moves;
1133 /*****************************************************************************
1134 * jewels_puzzle_is_finished() checks if the puzzle is finished.
1135 ******************************************************************************/
1136 static bool jewels_puzzle_is_finished(struct game_context* bj) {
1137 unsigned int i, j;
1138 for(i=0; i<BJ_HEIGHT; i++) {
1139 for(j=0; j<BJ_WIDTH; j++) {
1140 int mytype = bj->playboard[i][j].type;
1141 if(mytype>MAX_NUM_JEWELS) {
1142 mytype -= MAX_NUM_JEWELS;
1143 if(mytype&PUZZLE_TILE_UP) {
1144 if(i==0 || bj->playboard[i-1][j].type<=MAX_NUM_JEWELS ||
1145 !((bj->playboard[i-1][j].type-MAX_NUM_JEWELS)
1146 &PUZZLE_TILE_DOWN))
1147 return false;
1149 if(mytype&PUZZLE_TILE_DOWN) {
1150 if(i==BJ_HEIGHT-1 ||
1151 bj->playboard[i+1][j].type<=MAX_NUM_JEWELS ||
1152 !((bj->playboard[i+1][j].type-MAX_NUM_JEWELS)
1153 &PUZZLE_TILE_UP))
1154 return false;
1156 if(mytype&PUZZLE_TILE_LEFT) {
1157 if(j==0 || bj->playboard[i][j-1].type<=MAX_NUM_JEWELS ||
1158 !((bj->playboard[i][j-1].type-MAX_NUM_JEWELS)
1159 &PUZZLE_TILE_RIGHT))
1160 return false;
1162 if(mytype&PUZZLE_TILE_RIGHT) {
1163 if(j==BJ_WIDTH-1 ||
1164 bj->playboard[i][j+1].type<=MAX_NUM_JEWELS ||
1165 !((bj->playboard[i][j+1].type-MAX_NUM_JEWELS)
1166 &PUZZLE_TILE_LEFT))
1167 return false;
1172 return true;
1175 /*****************************************************************************
1176 * jewels_initlevel() initialises a level.
1177 ******************************************************************************/
1178 static unsigned int jewels_initlevel(struct game_context* bj) {
1179 unsigned int points = 0;
1181 switch(bj->type) {
1182 case GAME_TYPE_NORMAL:
1183 bj->num_jewels = MAX_NUM_JEWELS;
1184 break;
1186 case GAME_TYPE_PUZZLE:
1188 unsigned int i, j;
1189 struct puzzle_tile *tile;
1191 bj->num_jewels = puzzle_levels[bj->level-1].num_jewels;
1193 for(i=0; i<BJ_HEIGHT; i++) {
1194 for(j=0; j<BJ_WIDTH; j++) {
1195 bj->playboard[i][j].type = (rb->rand()%bj->num_jewels)+1;
1196 bj->playboard[i][j].falling = false;
1197 bj->playboard[i][j].delete = false;
1200 jewels_runboard(bj);
1201 tile = puzzle_levels[bj->level-1].tiles;
1202 for(i=0; i<puzzle_levels[bj->level-1].num_tiles; i++, tile++) {
1203 bj->playboard[tile->y+1][tile->x].type = MAX_NUM_JEWELS
1204 +tile->tile_type;
1207 break;
1210 jewels_drawboard(bj);
1212 /* run the play board */
1213 jewels_putjewels(bj);
1214 points += jewels_runboard(bj);
1215 return points;
1218 /*****************************************************************************
1219 * jewels_init() initializes jewels data structures.
1220 ******************************************************************************/
1221 static void jewels_init(struct game_context* bj) {
1222 /* seed the rand generator */
1223 rb->srand(*rb->current_tick);
1225 bj->type = bj->tmp_type;
1226 bj->level = 1;
1227 bj->score = 0;
1228 bj->segments = 0;
1230 jewels_setcolors();
1232 /* clear playing board */
1233 rb->memset(bj->playboard, 0, sizeof(bj->playboard));
1234 do {
1235 bj->score += jewels_initlevel(bj);
1236 } while(!jewels_movesavail(bj));
1239 /*****************************************************************************
1240 * jewels_nextlevel() advances the game to the next bj->level and returns
1241 * points earned.
1242 ******************************************************************************/
1243 static void jewels_nextlevel(struct game_context* bj) {
1244 int i, x, y;
1245 unsigned int points = 0;
1247 switch(bj->type) {
1248 case GAME_TYPE_NORMAL:
1249 /* roll over score, change and display level */
1250 while(bj->score >= LEVEL_PTS) {
1251 bj->score -= LEVEL_PTS;
1252 bj->level++;
1253 rb->splashf(HZ*2, "Level %d", bj->level);
1254 jewels_drawboard(bj);
1257 /* randomly clear some jewels */
1258 for(i=0; i<16; i++) {
1259 x = rb->rand()%8;
1260 y = rb->rand()%8;
1262 if(bj->playboard[y][x].type != 0) {
1263 points++;
1264 bj->playboard[y][x].type = 0;
1267 break;
1269 case GAME_TYPE_PUZZLE:
1270 bj->level++;
1271 rb->splashf(HZ*2, "Level %d", bj->level);
1272 break;
1275 points += jewels_initlevel(bj);
1276 bj->score += points;
1279 static bool jewels_help(void)
1281 rb->lcd_setfont(FONT_UI);
1282 #define WORDS (sizeof help_text / sizeof (char*))
1283 static char *help_text[] = {
1284 "Jewels", "", "Aim", "",
1285 "Swap", "pairs", "of", "jewels", "to", "form", "connected",
1286 "segments", "of", "three", "or", "more", "of", "the", "same",
1287 "type.", "",
1288 "The", "goal", "of", "the", "game", "is", "to", "score", "as", "many",
1289 "points", "as", "possible", "before", "running", "out", "of",
1290 "available", "moves.", "", "",
1291 "Controls", "",
1292 "Directions",
1293 #ifdef JEWELS_SCROLLWHEEL
1294 "or", "scroll",
1295 #endif
1296 "to", "move", "",
1297 HK_SELECT, "to", "select", "",
1298 HK_CANCEL, "to", "go", "to", "menu"
1300 static struct style_text formation[]={
1301 { 0, TEXT_CENTER|TEXT_UNDERLINE },
1302 { 2, C_RED },
1303 { 42, C_RED },
1304 { -1, 0 }
1306 #ifdef HAVE_LCD_COLOR
1307 rb->lcd_set_background(LCD_BLACK);
1308 rb->lcd_set_foreground(LCD_WHITE);
1309 #endif
1310 int button;
1311 if (display_text(WORDS, help_text, formation, NULL))
1312 return true;
1313 do {
1314 button = rb->button_get(true);
1315 if (rb->default_event_handler (button) == SYS_USB_CONNECTED) {
1316 return true;
1318 } while( ( button == BUTTON_NONE )
1319 || ( button & (BUTTON_REL|BUTTON_REPEAT) ) );
1320 rb->lcd_setfont(FONT_SYSFIXED);
1322 return false;
1325 static bool _ingame;
1326 static int jewels_menu_cb(int action, const struct menu_item_ex *this_item)
1328 int i = ((intptr_t)this_item);
1329 if(action == ACTION_REQUEST_MENUITEM
1330 && !_ingame && (i==0 || i==6))
1331 return ACTION_EXIT_MENUITEM;
1332 return action;
1334 /*****************************************************************************
1335 * jewels_game_menu() shows the game menu.
1336 ******************************************************************************/
1337 static int jewels_game_menu(struct game_context* bj, bool ingame)
1339 rb->button_clear_queue();
1340 int choice = 0;
1342 _ingame = ingame;
1344 static struct opt_items mode[] = {
1345 { "Normal", -1 },
1346 { "Puzzle", -1 },
1349 MENUITEM_STRINGLIST (main_menu, "Jewels Menu", jewels_menu_cb,
1350 "Resume Game",
1351 "Start New Game",
1352 "Mode",
1353 "Help",
1354 "High Scores",
1355 "Playback Control",
1356 "Quit without Saving",
1357 "Quit");
1359 while (1) {
1360 switch (rb->do_menu(&main_menu, &choice, NULL, false)) {
1361 case 0:
1362 jewels_setcolors();
1363 if(resume_file)
1364 rb->remove(SAVE_FILE);
1365 return 0;
1366 case 1:
1367 jewels_init(bj);
1368 return 0;
1369 case 2:
1370 rb->set_option("Mode", &bj->tmp_type, INT, mode, 2, NULL);
1371 break;
1372 case 3:
1373 if(jewels_help())
1374 return 1;
1375 break;
1376 case 4:
1377 highscore_show(NUM_SCORES, highest, NUM_SCORES, true);
1378 break;
1379 case 5:
1380 playback_control(NULL);
1381 break;
1382 case 6:
1383 return 1;
1384 case 7:
1385 if (ingame) {
1386 rb->splash(HZ*1, "Saving game ...");
1387 jewels_savegame(bj);
1389 return 1;
1390 case MENU_ATTACHED_USB:
1391 return 1;
1392 default:
1393 break;
1398 static int jewels_main(struct game_context* bj) {
1399 int button;
1400 int position;
1401 bool selected = false;
1402 bool no_movesavail;
1403 int x=0, y=0;
1405 bool loaded = jewels_loadgame(bj);
1406 resume_file = loaded;
1407 if (jewels_game_menu(bj, loaded)!=0)
1408 return 0;
1410 resume_file = false;
1411 while(true) {
1412 no_movesavail = false;
1414 /* refresh the board */
1415 jewels_drawboard(bj);
1417 /* display the cursor */
1418 if(selected) {
1419 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
1420 rb->lcd_fillrect(x*TILE_WIDTH, y*TILE_HEIGHT+YOFS,
1421 TILE_WIDTH, TILE_HEIGHT);
1422 rb->lcd_set_drawmode(DRMODE_SOLID);
1423 } else {
1424 rb->lcd_drawrect(x*TILE_WIDTH, y*TILE_HEIGHT+YOFS,
1425 TILE_WIDTH, TILE_HEIGHT);
1427 rb->lcd_update_rect(x*TILE_WIDTH, y*TILE_HEIGHT+YOFS,
1428 TILE_WIDTH, TILE_HEIGHT);
1430 /* handle game button presses */
1431 rb->yield();
1432 button = rb->button_get(true);
1433 switch(button){
1434 case JEWELS_LEFT: /* move cursor left */
1435 case (JEWELS_LEFT|BUTTON_REPEAT):
1436 if(selected) {
1437 bj->score += jewels_swapjewels(bj, x, y, SWAP_LEFT);
1438 selected = false;
1439 if (!jewels_movesavail(bj)) no_movesavail = true;
1440 } else {
1441 x = (x+BJ_WIDTH-1)%BJ_WIDTH;
1443 break;
1445 case JEWELS_RIGHT: /* move cursor right */
1446 case (JEWELS_RIGHT|BUTTON_REPEAT):
1447 if(selected) {
1448 bj->score += jewels_swapjewels(bj, x, y, SWAP_RIGHT);
1449 selected = false;
1450 if (!jewels_movesavail(bj)) no_movesavail = true;
1451 } else {
1452 x = (x+1)%BJ_WIDTH;
1454 break;
1456 case JEWELS_DOWN: /* move cursor down */
1457 case (JEWELS_DOWN|BUTTON_REPEAT):
1458 if(selected) {
1459 bj->score += jewels_swapjewels(bj, x, y, SWAP_DOWN);
1460 selected = false;
1461 if (!jewels_movesavail(bj)) no_movesavail = true;
1462 } else {
1463 y = (y+1)%(BJ_HEIGHT-1);
1465 break;
1467 case JEWELS_UP: /* move cursor up */
1468 case (JEWELS_UP|BUTTON_REPEAT):
1469 if(selected) {
1470 bj->score += jewels_swapjewels(bj, x, y, SWAP_UP);
1471 selected = false;
1472 if (!jewels_movesavail(bj)) no_movesavail = true;
1473 } else {
1474 y = (y+(BJ_HEIGHT-1)-1)%(BJ_HEIGHT-1);
1476 break;
1478 #ifdef JEWELS_SCROLLWHEEL
1479 case JEWELS_PREV: /* scroll backwards */
1480 case (JEWELS_PREV|BUTTON_REPEAT):
1481 if(!selected) {
1482 if(x == 0) {
1483 y = (y+(BJ_HEIGHT-1)-1)%(BJ_HEIGHT-1);
1485 x = (x+BJ_WIDTH-1)%BJ_WIDTH;
1487 break;
1489 case JEWELS_NEXT: /* scroll forwards */
1490 case (JEWELS_NEXT|BUTTON_REPEAT):
1491 if(!selected) {
1492 if(x == BJ_WIDTH-1) {
1493 y = (y+1)%(BJ_HEIGHT-1);
1495 x = (x+1)%BJ_WIDTH;
1497 break;
1498 #endif
1500 case JEWELS_SELECT: /* toggle selected */
1501 selected = !selected;
1502 break;
1504 #ifdef JEWELS_RC_CANCEL
1505 case JEWELS_RC_CANCEL:
1506 #endif
1507 case JEWELS_CANCEL: /* end game */
1508 if (jewels_game_menu(bj, true)!=0)
1509 return 0;
1510 break;
1512 default:
1513 if (rb->default_event_handler (button) == SYS_USB_CONNECTED)
1514 return PLUGIN_USB_CONNECTED;
1515 break;
1518 switch(bj->type) {
1519 case GAME_TYPE_NORMAL:
1520 if(bj->score >= LEVEL_PTS)
1521 jewels_nextlevel(bj);
1522 break;
1523 case GAME_TYPE_PUZZLE:
1524 if (jewels_puzzle_is_finished(bj)) {
1525 if (bj->level < NUM_PUZZLE_LEVELS) {
1526 jewels_nextlevel(bj);
1527 } else {
1528 rb->splash(2*HZ, "Congratulations!");
1529 rb->splash(2*HZ, "You have finished the game!");
1530 if (jewels_game_menu(bj, false)!=0) {
1531 return 0;
1534 break;
1538 if (no_movesavail) {
1539 switch(bj->type) {
1540 case GAME_TYPE_NORMAL:
1541 rb->splash(HZ*2, "Game Over!");
1542 rb->lcd_clear_display();
1543 bj->score += (bj->level-1)*LEVEL_PTS;
1544 position=highscore_update(bj->score, bj->level, "",
1545 highest, NUM_SCORES);
1546 if (position == 0)
1547 rb->splash(HZ*2, "New High Score");
1548 if (position != -1)
1549 highscore_show(position, highest, NUM_SCORES, true);
1550 break;
1551 case GAME_TYPE_PUZZLE:
1552 rb->splash(2*HZ, "Game Over");
1553 break;
1555 if (jewels_game_menu(bj, false)!=0) {
1556 return 0;
1562 /* this is the plugin entry point */
1563 enum plugin_status plugin_start(const void* parameter)
1565 (void)parameter;
1567 /* load high scores */
1568 highscore_load(HIGH_SCORE,highest,NUM_SCORES);
1570 rb->lcd_setfont(FONT_SYSFIXED);
1571 #if LCD_DEPTH > 1
1572 rb->lcd_set_backdrop(NULL);
1573 #endif
1575 struct game_context bj;
1576 bj.tmp_type = GAME_TYPE_NORMAL;
1577 jewels_main(&bj);
1578 highscore_save(HIGH_SCORE,highest,NUM_SCORES);
1579 rb->lcd_setfont(FONT_UI);
1581 return PLUGIN_OK;
1584 #endif /* HAVE_LCD_BITMAP */