New plugin: FFT, A frequency analyzer plugin
[kugel-rb.git] / apps / plugins / jewels.c
blob6c718b4f990139bd88d2d839c36620a3ee3f4f9f
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 #else
271 #error No keymap defined!
272 #endif
274 #ifdef HAVE_TOUCHSCREEN
275 #ifndef JEWELS_UP
276 #define JEWELS_UP BUTTON_TOPMIDDLE
277 #endif
278 #ifndef JEWELS_DOWN
279 #define JEWELS_DOWN BUTTON_BOTTOMMIDDLE
280 #endif
281 #ifndef JEWELS_LEFT
282 #define JEWELS_LEFT BUTTON_MIDLEFT
283 #endif
284 #ifndef JEWELS_RIGHT
285 #define JEWELS_RIGHT BUTTON_MIDRIGHT
286 #endif
287 #ifndef JEWELS_SELECT
288 #define JEWELS_SELECT BUTTON_CENTER
289 #define HK_SELECT "CENTER"
290 #endif
291 #ifndef JEWELS_CANCEL
292 #define JEWELS_CANCEL BUTTON_TOPLEFT
293 #define HK_CANCEL "TOPLEFT"
294 #endif
295 #endif
297 #define TILE_WIDTH BMPWIDTH_jewels
298 #define TILE_HEIGHT (BMPHEIGHT_jewels/23)
300 #if LCD_HEIGHT < LCD_WIDTH
301 /* This calculation assumes integer division w/ LCD_HEIGHT/TILE_HEIGHT */
302 #define YOFS LCD_HEIGHT-((LCD_HEIGHT/TILE_HEIGHT)*TILE_HEIGHT)
303 #else
304 #define YOFS 0
305 #endif
307 #define NUM_SCORES 5
309 /* swap directions */
310 #define SWAP_UP 0
311 #define SWAP_RIGHT 1
312 #define SWAP_DOWN 2
313 #define SWAP_LEFT 3
315 /* play board dimension */
316 #define BJ_HEIGHT 9
317 #define BJ_WIDTH 8
319 /* next level threshold */
320 #define LEVEL_PTS 100
322 /* animation frame rate */
323 #define MAX_FPS 20
325 /* text margin */
326 #define MARGIN 5
328 /* Game types */
329 enum game_type {
330 GAME_TYPE_NORMAL,
331 GAME_TYPE_PUZZLE
334 /* external bitmaps */
335 extern const fb_data jewels[];
337 /* tile background colors */
338 #ifdef HAVE_LCD_COLOR
339 static const unsigned jewels_bkgd[2] = {
340 LCD_RGBPACK(104, 63, 63),
341 LCD_RGBPACK(83, 44, 44)
343 #endif
345 /* the tile struct
346 * type is the jewel number 0-7
347 * falling if the jewel is falling
348 * delete marks the jewel for deletion
350 struct tile {
351 int type;
352 bool falling;
353 bool delete;
356 /* the game context struct
357 * score is the current level score
358 * segments is the number of cleared segments in the current run
359 * level is the current level
360 * tmp_type is the select type in the menu
361 * type is the game type (normal or puzzle)
362 * playboard is the game playing board (first row is hidden)
363 * num_jewels is the number of different jewels to use
365 struct game_context {
366 unsigned int score;
367 unsigned int segments;
368 unsigned int level;
369 unsigned int type;
370 unsigned int tmp_type;
371 struct tile playboard[BJ_HEIGHT][BJ_WIDTH];
372 unsigned int num_jewels;
375 #define MAX_NUM_JEWELS 7
377 #define MAX_PUZZLE_TILES 4
378 #define NUM_PUZZLE_LEVELS 10
380 struct puzzle_tile {
381 int x;
382 int y;
383 int tile_type;
386 struct puzzle_level {
387 unsigned int num_jewels;
388 unsigned int num_tiles;
389 struct puzzle_tile tiles[MAX_PUZZLE_TILES];
392 #define PUZZLE_TILE_UP 1
393 #define PUZZLE_TILE_DOWN 2
394 #define PUZZLE_TILE_LEFT 4
395 #define PUZZLE_TILE_RIGHT 8
397 struct puzzle_level puzzle_levels[NUM_PUZZLE_LEVELS] = {
398 { 5, 2, { {3, 3, PUZZLE_TILE_RIGHT},
399 {4, 2, PUZZLE_TILE_LEFT} } },
400 { 5, 2, { {3, 2, PUZZLE_TILE_DOWN},
401 {3, 4, PUZZLE_TILE_UP} } },
402 { 6, 3, { {3, 2, PUZZLE_TILE_DOWN},
403 {3, 4, PUZZLE_TILE_UP|PUZZLE_TILE_DOWN},
404 {3, 6, PUZZLE_TILE_UP} } },
405 { 6, 3, { {3, 2, PUZZLE_TILE_RIGHT},
406 {4, 3, PUZZLE_TILE_LEFT|PUZZLE_TILE_RIGHT},
407 {5, 4, PUZZLE_TILE_LEFT} } },
408 { 6, 2, { {3, 4, PUZZLE_TILE_RIGHT},
409 {4, 2, PUZZLE_TILE_LEFT} } },
410 { 6, 2, { {3, 2, PUZZLE_TILE_DOWN},
411 {4, 4, PUZZLE_TILE_UP} } },
412 { 7, 4, { {3, 2, PUZZLE_TILE_RIGHT|PUZZLE_TILE_DOWN},
413 {4, 3, PUZZLE_TILE_LEFT|PUZZLE_TILE_DOWN},
414 {3, 4, PUZZLE_TILE_RIGHT|PUZZLE_TILE_UP},
415 {4, 4, PUZZLE_TILE_LEFT|PUZZLE_TILE_UP} } },
416 { 6, 3, { {3, 2, PUZZLE_TILE_DOWN},
417 {4, 4, PUZZLE_TILE_UP|PUZZLE_TILE_DOWN},
418 {3, 6, PUZZLE_TILE_UP} } },
419 { 7, 3, { {2, 2, PUZZLE_TILE_RIGHT},
420 {4, 1, PUZZLE_TILE_LEFT|PUZZLE_TILE_RIGHT},
421 {5, 4, PUZZLE_TILE_LEFT} } },
422 { 7, 4, { {3, 0, PUZZLE_TILE_RIGHT|PUZZLE_TILE_DOWN},
423 {5, 0, PUZZLE_TILE_LEFT|PUZZLE_TILE_DOWN},
424 {2, 7, PUZZLE_TILE_RIGHT|PUZZLE_TILE_UP},
425 {4, 7, PUZZLE_TILE_LEFT|PUZZLE_TILE_UP} } },
428 #define SAVE_FILE PLUGIN_GAMES_DIR "/jewels.save"
430 #define HIGH_SCORE PLUGIN_GAMES_DIR "/jewels.score"
431 struct highscore highest[NUM_SCORES];
433 static bool resume_file = false;
435 /*****************************************************************************
436 * jewels_setcolors() set the foreground and background colors.
437 ******************************************************************************/
438 static inline void jewels_setcolors(void) {
439 #ifdef HAVE_LCD_COLOR
440 rb->lcd_set_background(LCD_RGBPACK(49, 26, 26));
441 rb->lcd_set_foreground(LCD_RGBPACK(210, 181, 181));
442 #endif
445 /*****************************************************************************
446 * jewels_loadgame() loads the saved game and returns load success.
447 ******************************************************************************/
448 static bool jewels_loadgame(struct game_context* bj)
450 int fd;
451 bool loaded = false;
453 /* open game file */
454 fd = rb->open(SAVE_FILE, O_RDONLY);
455 if(fd < 0) return loaded;
457 /* read in saved game */
458 while(true) {
459 if(rb->read(fd, &bj->tmp_type, sizeof(bj->tmp_type)) <= 0) break;
460 if(rb->read(fd, &bj->type, sizeof(bj->type)) <= 0) break;
461 if(rb->read(fd, &bj->score, sizeof(bj->score)) <= 0) break;
462 if(rb->read(fd, &bj->level, sizeof(bj->level)) <= 0) break;
463 if(rb->read(fd, &bj->segments, sizeof(bj->segments)) <= 0) break;
464 if(rb->read(fd, &bj->num_jewels, sizeof(bj->num_jewels)) <= 0) break;
465 if(rb->read(fd, bj->playboard, sizeof(bj->playboard)) <= 0) break;
466 loaded = true;
467 break;
470 rb->close(fd);
472 return loaded;
475 /*****************************************************************************
476 * jewels_savegame() saves the current game state.
477 ******************************************************************************/
478 static void jewels_savegame(struct game_context* bj)
480 int fd;
481 /* write out the game state to the save file */
482 fd = rb->open(SAVE_FILE, O_WRONLY|O_CREAT);
483 if(fd < 0) return;
485 rb->write(fd, &bj->tmp_type, sizeof(bj->tmp_type));
486 rb->write(fd, &bj->type, sizeof(bj->type));
487 rb->write(fd, &bj->score, sizeof(bj->score));
488 rb->write(fd, &bj->level, sizeof(bj->level));
489 rb->write(fd, &bj->segments, sizeof(bj->segments));
490 rb->write(fd, &bj->num_jewels, sizeof(bj->num_jewels));
491 rb->write(fd, bj->playboard, sizeof(bj->playboard));
492 rb->close(fd);
495 /*****************************************************************************
496 * jewels_drawboard() redraws the entire game board.
497 ******************************************************************************/
498 static void jewels_drawboard(struct game_context* bj) {
499 int i, j;
500 int w, h;
501 unsigned int tempscore;
502 unsigned int size;
503 char *title = "Level";
504 char str[10];
506 if (bj->type == GAME_TYPE_NORMAL) {
507 tempscore = (bj->score>LEVEL_PTS ? LEVEL_PTS : bj->score);
508 size = LEVEL_PTS;
509 } else {
510 tempscore = (bj->level>NUM_PUZZLE_LEVELS ? NUM_PUZZLE_LEVELS : bj->level);
511 size = NUM_PUZZLE_LEVELS;
514 /* clear screen */
515 rb->lcd_clear_display();
517 /* dispay playing board */
518 for(i=0; i<BJ_HEIGHT-1; i++){
519 for(j=0; j<BJ_WIDTH; j++){
520 #ifdef HAVE_LCD_COLOR
521 rb->lcd_set_foreground(jewels_bkgd[(i+j)%2]);
522 rb->lcd_fillrect(j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
523 TILE_WIDTH, TILE_HEIGHT);
524 rb->lcd_bitmap_transparent_part(jewels,
525 0, TILE_HEIGHT*(bj->playboard[i+1][j].type),
526 STRIDE( SCREEN_MAIN,
527 BMPWIDTH_jewels, BMPHEIGHT_jewels),
528 j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
529 TILE_WIDTH, TILE_HEIGHT);
530 #else
531 rb->lcd_bitmap_part(jewels,
532 0, TILE_HEIGHT*(bj->playboard[i+1][j].type),
533 STRIDE( SCREEN_MAIN,
534 BMPWIDTH_jewels, BMPHEIGHT_jewels),
535 j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
536 TILE_WIDTH, TILE_HEIGHT);
537 #endif
541 #if LCD_WIDTH > LCD_HEIGHT /* horizontal layout */
543 /* draw separator lines */
544 jewels_setcolors();
545 rb->lcd_vline(BJ_WIDTH*TILE_WIDTH, 0, LCD_HEIGHT-1);
547 rb->lcd_hline(BJ_WIDTH*TILE_WIDTH, LCD_WIDTH-1, 18);
548 rb->lcd_hline(BJ_WIDTH*TILE_WIDTH, LCD_WIDTH-1, LCD_HEIGHT-10);
550 /* draw progress bar */
551 #ifdef HAVE_LCD_COLOR
552 rb->lcd_set_foreground(LCD_RGBPACK(104, 63, 63));
553 #endif
554 rb->lcd_fillrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4,
555 (LCD_HEIGHT-10)-(((LCD_HEIGHT-10)-18)*
556 tempscore/size),
557 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2,
558 ((LCD_HEIGHT-10)-18)*tempscore/size);
559 #ifdef HAVE_LCD_COLOR
560 rb->lcd_set_foreground(LCD_RGBPACK(83, 44, 44));
561 rb->lcd_drawrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4+1,
562 (LCD_HEIGHT-10)-(((LCD_HEIGHT-10)-18)*
563 tempscore/size)+1,
564 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-2,
565 ((LCD_HEIGHT-10)-18)*tempscore/size-1);
566 jewels_setcolors();
567 rb->lcd_drawrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4,
568 (LCD_HEIGHT-10)-(((LCD_HEIGHT-10)-18)*tempscore/size),
569 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2,
570 ((LCD_HEIGHT-10)-18)*tempscore/size+1);
571 #endif
573 /* print text */
574 rb->lcd_getstringsize(title, &w, &h);
575 rb->lcd_putsxy(LCD_WIDTH-(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-w/2, 1, title);
576 rb->snprintf(str, 4, "%d", bj->level);
577 rb->lcd_getstringsize(str, &w, &h);
578 rb->lcd_putsxy(LCD_WIDTH-(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-w/2, 10, str);
580 if (bj->type == GAME_TYPE_NORMAL) {
581 rb->snprintf(str, 6, "%d", (bj->level-1)*LEVEL_PTS+bj->score);
582 rb->lcd_getstringsize(str, &w, &h);
583 rb->lcd_putsxy(LCD_WIDTH-(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-w/2,
584 LCD_HEIGHT-8, str);
587 #elif LCD_WIDTH < LCD_HEIGHT /* vertical layout */
589 /* draw separator lines */
590 jewels_setcolors();
591 rb->lcd_hline(0, LCD_WIDTH-1, 8*TILE_HEIGHT+YOFS);
592 rb->lcd_hline(0, LCD_WIDTH-1, LCD_HEIGHT-14);
593 rb->lcd_vline(LCD_WIDTH/2, LCD_HEIGHT-14, LCD_HEIGHT-1);
595 /* draw progress bar */
596 #ifdef HAVE_LCD_COLOR
597 rb->lcd_set_foreground(LCD_RGBPACK(104, 63, 63));
598 #endif
599 rb->lcd_fillrect(0, (8*TILE_HEIGHT+YOFS)
600 +(LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/4,
601 LCD_WIDTH*tempscore/size,
602 (LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/2);
603 #ifdef HAVE_LCD_COLOR
604 rb->lcd_set_foreground(LCD_RGBPACK(83, 44, 44));
605 rb->lcd_drawrect(1, (8*TILE_HEIGHT+YOFS)
606 +(LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/4+1,
607 LCD_WIDTH*tempscore/size-1,
608 (LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/2-2);
609 jewels_setcolors();
610 rb->lcd_drawrect(0, (8*TILE_HEIGHT+YOFS)
611 +(LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/4,
612 LCD_WIDTH*tempscore/size+1,
613 (LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/2);
614 #endif
616 /* print text */
617 rb->snprintf(str, 10, "%s %d", title, bj->level);
618 rb->lcd_putsxy(1, LCD_HEIGHT-10, str);
620 if (bj->type == GAME_TYPE_NORMAL) {
621 rb->snprintf(str, 6, "%d", (bj->level-1)*LEVEL_PTS+bj->score);
622 rb->lcd_getstringsize(str, &w, &h);
623 rb->lcd_putsxy((LCD_WIDTH-2)-w, LCD_HEIGHT-10, str);
627 #else /* square layout */
629 /* draw separator lines */
630 jewels_setcolors();
631 rb->lcd_hline(0, LCD_WIDTH-1, 8*TILE_HEIGHT+YOFS);
632 rb->lcd_vline(BJ_WIDTH*TILE_WIDTH, 0, 8*TILE_HEIGHT+YOFS);
633 rb->lcd_vline(LCD_WIDTH/2, 8*TILE_HEIGHT+YOFS, LCD_HEIGHT-1);
635 /* draw progress bar */
636 #ifdef HAVE_LCD_COLOR
637 rb->lcd_set_foreground(LCD_RGBPACK(104, 63, 63));
638 #endif
639 rb->lcd_fillrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4,
640 (8*TILE_HEIGHT+YOFS)-(8*TILE_HEIGHT+YOFS)*tempscore/size,
641 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2,
642 (8*TILE_HEIGHT+YOFS)*tempscore/size);
643 #ifdef HAVE_LCD_COLOR
644 rb->lcd_set_foreground(LCD_RGBPACK(83, 44, 44));
645 rb->lcd_drawrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4+1,
646 (8*TILE_HEIGHT+YOFS)-(8*TILE_HEIGHT+YOFS)
647 *tempscore/size+1,
648 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-2,
649 (8*TILE_HEIGHT+YOFS)*tempscore/size-1);
650 jewels_setcolors();
651 rb->lcd_drawrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4,
652 (8*TILE_HEIGHT+YOFS)-(8*TILE_HEIGHT+YOFS)
653 *tempscore/size,
654 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2,
655 (8*TILE_HEIGHT+YOFS)*tempscore/size+1);
656 #endif
658 /* print text */
659 rb->snprintf(str, 10, "%s %d", title, bj->level);
660 rb->lcd_putsxy(1, LCD_HEIGHT-(LCD_HEIGHT-(8*TILE_HEIGHT+YOFS))/2-3, str);
662 if (bj->type == GAME_TYPE_NORMAL) {
663 rb->snprintf(str, 6, "%d", (bj->level-1)*LEVEL_PTS+bj->score);
664 rb->lcd_getstringsize(str, &w, &h);
665 rb->lcd_putsxy((LCD_WIDTH-2)-w,
666 LCD_HEIGHT-(LCD_HEIGHT-(8*TILE_HEIGHT+YOFS))/2-3, str);
669 #endif /* layout */
671 rb->lcd_update();
674 /*****************************************************************************
675 * jewels_putjewels() makes the jewels fall to fill empty spots and adds
676 * new random jewels at the empty spots at the top of each row.
677 ******************************************************************************/
678 static void jewels_putjewels(struct game_context* bj){
679 int i, j, k;
680 bool mark, done;
681 long lasttick, currenttick;
683 /* loop to make all the jewels fall */
684 while(true) {
685 /* mark falling jewels and add new jewels to hidden top row*/
686 mark = false;
687 done = true;
688 for(j=0; j<BJ_WIDTH; j++) {
689 if(bj->playboard[1][j].type == 0) {
690 bj->playboard[0][j].type = rb->rand()%bj->num_jewels+1;
692 for(i=BJ_HEIGHT-2; i>=0; i--) {
693 if(!mark && bj->playboard[i+1][j].type == 0) {
694 mark = true;
695 done = false;
697 if(mark) bj->playboard[i][j].falling = true;
699 /*if(bj->playboard[1][j].falling) {
700 bj->playboard[0][j].type = rb->rand()%bj->num_jewels+1;
701 bj->playboard[0][j].falling = true;
703 mark = false;
706 /* break if there are no falling jewels */
707 if(done) break;
709 /* animate falling jewels */
710 lasttick = *rb->current_tick;
712 for(k=1; k<=8; k++) {
713 for(i=BJ_HEIGHT-2; i>=0; i--) {
714 for(j=0; j<BJ_WIDTH; j++) {
715 if(bj->playboard[i][j].falling &&
716 bj->playboard[i][j].type != 0) {
717 /* clear old position */
718 #ifdef HAVE_LCD_COLOR
719 if(i == 0 && YOFS) {
720 rb->lcd_set_foreground(rb->lcd_get_background());
721 } else {
722 rb->lcd_set_foreground(jewels_bkgd[(i-1+j)%2]);
724 rb->lcd_fillrect(j*TILE_WIDTH, (i-1)*TILE_HEIGHT+YOFS,
725 TILE_WIDTH, TILE_HEIGHT);
726 if(bj->playboard[i+1][j].type == 0) {
727 rb->lcd_set_foreground(jewels_bkgd[(i+j)%2]);
728 rb->lcd_fillrect(j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
729 TILE_WIDTH, TILE_HEIGHT);
731 #else
732 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
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_fillrect(j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
737 TILE_WIDTH, TILE_HEIGHT);
739 rb->lcd_set_drawmode(DRMODE_SOLID);
740 #endif
742 /* draw new position */
743 #ifdef HAVE_LCD_COLOR
744 rb->lcd_bitmap_transparent_part(jewels, 0,
745 TILE_HEIGHT*(bj->playboard[i][j].type),
746 STRIDE( SCREEN_MAIN,
747 BMPWIDTH_jewels,
748 BMPHEIGHT_jewels),
749 j*TILE_WIDTH,
750 (i-1)*TILE_HEIGHT+YOFS+
751 ((((TILE_HEIGHT<<10)*k)/8)>>10),
752 TILE_WIDTH, TILE_HEIGHT);
753 #else
754 rb->lcd_bitmap_part(jewels, 0,
755 TILE_HEIGHT*(bj->playboard[i][j].type),
756 STRIDE( SCREEN_MAIN,
757 BMPWIDTH_jewels,
758 BMPHEIGHT_jewels),
759 j*TILE_WIDTH,
760 (i-1)*TILE_HEIGHT+YOFS+
761 ((((TILE_HEIGHT<<10)*k)/8)>>10),
762 TILE_WIDTH, TILE_HEIGHT);
763 #endif
768 rb->lcd_update_rect(0, 0, TILE_WIDTH*8, LCD_HEIGHT);
769 jewels_setcolors();
771 /* framerate limiting */
772 currenttick = *rb->current_tick;
773 if(currenttick-lasttick < HZ/MAX_FPS) {
774 rb->sleep((HZ/MAX_FPS)-(currenttick-lasttick));
775 } else {
776 rb->yield();
778 lasttick = currenttick;
781 /* shift jewels down */
782 for(j=0; j<BJ_WIDTH; j++) {
783 for(i=BJ_HEIGHT-1; i>=1; i--) {
784 if(bj->playboard[i-1][j].falling) {
785 bj->playboard[i][j].type = bj->playboard[i-1][j].type;
790 /* clear out top row */
791 for(j=0; j<BJ_WIDTH; j++) {
792 bj->playboard[0][j].type = 0;
795 /* mark everything not falling */
796 for(i=0; i<BJ_HEIGHT; i++) {
797 for(j=0; j<BJ_WIDTH; j++) {
798 bj->playboard[i][j].falling = false;
804 /*****************************************************************************
805 * jewels_clearjewels() finds all the connected rows and columns and
806 * calculates and returns the points earned.
807 ******************************************************************************/
808 static unsigned int jewels_clearjewels(struct game_context* bj) {
809 int i, j;
810 int last, run;
811 unsigned int points = 0;
813 /* check for connected rows */
814 for(i=1; i<BJ_HEIGHT; i++) {
815 last = 0;
816 run = 1;
817 for(j=0; j<BJ_WIDTH; j++) {
818 if(bj->playboard[i][j].type == last &&
819 bj->playboard[i][j].type != 0 &&
820 bj->playboard[i][j].type <= MAX_NUM_JEWELS) {
821 run++;
823 if(run == 3) {
824 bj->segments++;
825 points += bj->segments;
826 bj->playboard[i][j].delete = true;
827 bj->playboard[i][j-1].delete = true;
828 bj->playboard[i][j-2].delete = true;
829 } else if(run > 3) {
830 points++;
831 bj->playboard[i][j].delete = true;
833 } else {
834 run = 1;
835 last = bj->playboard[i][j].type;
840 /* check for connected columns */
841 for(j=0; j<BJ_WIDTH; j++) {
842 last = 0;
843 run = 1;
844 for(i=1; i<BJ_HEIGHT; i++) {
845 if(bj->playboard[i][j].type != 0 &&
846 bj->playboard[i][j].type == last &&
847 bj->playboard[i][j].type <= MAX_NUM_JEWELS) {
848 run++;
850 if(run == 3) {
851 bj->segments++;
852 points += bj->segments;
853 bj->playboard[i][j].delete = true;
854 bj->playboard[i-1][j].delete = true;
855 bj->playboard[i-2][j].delete = true;
856 } else if(run > 3) {
857 points++;
858 bj->playboard[i][j].delete = true;
860 } else {
861 run = 1;
862 last = bj->playboard[i][j].type;
867 /* clear deleted jewels */
868 for(i=1; i<BJ_HEIGHT; i++) {
869 for(j=0; j<BJ_WIDTH; j++) {
870 if(bj->playboard[i][j].delete) {
871 bj->playboard[i][j].delete = false;
872 bj->playboard[i][j].type = 0;
877 return points;
880 /*****************************************************************************
881 * jewels_runboard() runs the board until it settles in a fixed state and
882 * returns points earned.
883 ******************************************************************************/
884 static unsigned int jewels_runboard(struct game_context* bj) {
885 unsigned int points = 0;
886 unsigned int ret;
888 bj->segments = 0;
890 while((ret = jewels_clearjewels(bj)) > 0) {
891 points += ret;
892 jewels_drawboard(bj);
893 jewels_putjewels(bj);
896 return points;
899 /*****************************************************************************
900 * jewels_swapjewels() swaps two jewels as long as it results in points and
901 * returns points earned.
902 ******************************************************************************/
903 static unsigned int jewels_swapjewels(struct game_context* bj,
904 int x, int y, int direc) {
905 int k;
906 int horzmod, vertmod;
907 int movelen = 0;
908 bool undo = false;
909 unsigned int points = 0;
910 long lasttick, currenttick;
912 /* check for invalid parameters */
913 if(x < 0 || x >= BJ_WIDTH || y < 0 || y >= BJ_HEIGHT-1 ||
914 direc < SWAP_UP || direc > SWAP_LEFT) {
915 return 0;
918 /* check for invalid directions */
919 if((x == 0 && direc == SWAP_LEFT) ||
920 (x == BJ_WIDTH-1 && direc == SWAP_RIGHT) ||
921 (y == 0 && direc == SWAP_UP) ||
922 (y == BJ_HEIGHT-2 && direc == SWAP_DOWN)) {
923 return 0;
926 /* set direction variables */
927 horzmod = 0;
928 vertmod = 0;
929 switch(direc) {
930 case SWAP_UP:
931 vertmod = -1;
932 movelen = TILE_HEIGHT;
933 break;
934 case SWAP_RIGHT:
935 horzmod = 1;
936 movelen = TILE_WIDTH;
937 break;
938 case SWAP_DOWN:
939 vertmod = 1;
940 movelen = TILE_HEIGHT;
941 break;
942 case SWAP_LEFT:
943 horzmod = -1;
944 movelen = TILE_WIDTH;
945 break;
948 while(true) {
949 lasttick = *rb->current_tick;
951 /* animate swapping jewels */
952 for(k=0; k<=8; k++) {
953 /* clear old position */
954 #ifdef HAVE_LCD_COLOR
955 rb->lcd_set_foreground(jewels_bkgd[(x+y)%2]);
956 rb->lcd_fillrect(x*TILE_WIDTH,
957 y*TILE_HEIGHT+YOFS,
958 TILE_WIDTH, TILE_HEIGHT);
959 rb->lcd_set_foreground(jewels_bkgd[(x+horzmod+y+vertmod)%2]);
960 rb->lcd_fillrect((x+horzmod)*TILE_WIDTH,
961 (y+vertmod)*TILE_HEIGHT+YOFS,
962 TILE_WIDTH, TILE_HEIGHT);
963 #else
964 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
965 rb->lcd_fillrect(x*TILE_WIDTH,
966 y*TILE_HEIGHT+YOFS,
967 TILE_WIDTH, TILE_HEIGHT);
968 rb->lcd_fillrect((x+horzmod)*TILE_WIDTH,
969 (y+vertmod)*TILE_HEIGHT+YOFS,
970 TILE_WIDTH, TILE_HEIGHT);
971 rb->lcd_set_drawmode(DRMODE_SOLID);
972 #endif
973 /* draw new position */
974 #ifdef HAVE_LCD_COLOR
975 rb->lcd_bitmap_transparent_part(jewels,
976 0, TILE_HEIGHT*(bj->playboard
977 [y+1+vertmod][x+horzmod].type),
978 STRIDE( SCREEN_MAIN,
979 BMPWIDTH_jewels, BMPHEIGHT_jewels),
980 (x+horzmod)*TILE_WIDTH-horzmod*
981 ((((movelen<<10)*k)/8)>>10),
982 (y+vertmod)*TILE_HEIGHT-vertmod*
983 ((((movelen<<10)*k)/8)>>10)+YOFS,
984 TILE_WIDTH, TILE_HEIGHT);
985 rb->lcd_bitmap_transparent_part(jewels,
986 0, TILE_HEIGHT*(bj->playboard[y+1][x].type),
987 STRIDE( SCREEN_MAIN,
988 BMPWIDTH_jewels, BMPHEIGHT_jewels),
989 x*TILE_WIDTH+horzmod*
990 ((((movelen<<10)*k)/8)>>10),
991 y*TILE_HEIGHT+vertmod*
992 ((((movelen<<10)*k)/8)>>10)+YOFS,
993 TILE_WIDTH, TILE_HEIGHT);
994 #else
995 rb->lcd_bitmap_part(jewels,
996 0, TILE_HEIGHT*(bj->playboard
997 [y+1+vertmod][x+horzmod].type),
998 STRIDE( SCREEN_MAIN,
999 BMPWIDTH_jewels, BMPHEIGHT_jewels),
1000 (x+horzmod)*TILE_WIDTH-horzmod*
1001 ((((movelen<<10)*k)/8)>>10),
1002 (y+vertmod)*TILE_HEIGHT-vertmod*
1003 ((((movelen<<10)*k)/8)>>10)+YOFS,
1004 TILE_WIDTH, TILE_HEIGHT);
1005 rb->lcd_set_drawmode(DRMODE_FG);
1006 rb->lcd_bitmap_part(jewels,
1007 0, TILE_HEIGHT*(bj->playboard[y+1][x].type),
1008 STRIDE( SCREEN_MAIN,
1009 BMPWIDTH_jewels, BMPHEIGHT_jewels),
1010 x*TILE_WIDTH+horzmod*
1011 ((((movelen<<10)*k)/8)>>10),
1012 y*TILE_HEIGHT+vertmod*
1013 ((((movelen<<10)*k)/8)>>10)+YOFS,
1014 TILE_WIDTH, TILE_HEIGHT);
1015 rb->lcd_set_drawmode(DRMODE_SOLID);
1016 #endif
1018 rb->lcd_update_rect(0, 0, TILE_WIDTH*8, LCD_HEIGHT);
1019 jewels_setcolors();
1021 /* framerate limiting */
1022 currenttick = *rb->current_tick;
1023 if(currenttick-lasttick < HZ/MAX_FPS) {
1024 rb->sleep((HZ/MAX_FPS)-(currenttick-lasttick));
1025 } else {
1026 rb->yield();
1028 lasttick = currenttick;
1031 /* swap jewels */
1032 int temp = bj->playboard[y+1][x].type;
1033 bj->playboard[y+1][x].type =
1034 bj->playboard[y+1+vertmod][x+horzmod].type;
1035 bj->playboard[y+1+vertmod][x+horzmod].type = temp;
1037 if(undo) break;
1039 points = jewels_runboard(bj);
1040 if(points == 0) {
1041 undo = true;
1042 } else {
1043 break;
1047 return points;
1050 /*****************************************************************************
1051 * jewels_movesavail() uses pattern matching to see if there are any
1052 * available move left.
1053 ******************************************************************************/
1054 static bool jewels_movesavail(struct game_context* bj) {
1055 int i, j;
1056 bool moves = false;
1057 int mytype;
1059 for(i=1; i<BJ_HEIGHT; i++) {
1060 for(j=0; j<BJ_WIDTH; j++) {
1061 mytype = bj->playboard[i][j].type;
1062 if(mytype == 0 || mytype > MAX_NUM_JEWELS) continue;
1064 /* check horizontal patterns */
1065 if(j <= BJ_WIDTH-3) {
1066 if(i > 1) {
1067 if(bj->playboard[i-1][j+1].type == mytype) {
1068 if(bj->playboard[i-1][j+2].type == mytype)
1069 {moves = true; break;}
1070 if(bj->playboard[i][j+2].type == mytype)
1071 {moves = true; break;}
1073 if(bj->playboard[i][j+1].type == mytype) {
1074 if(bj->playboard[i-1][j+2].type == mytype)
1075 {moves = true; break;}
1079 if(j <= BJ_WIDTH-4) {
1080 if(bj->playboard[i][j+3].type == mytype) {
1081 if(bj->playboard[i][j+1].type == mytype)
1082 {moves = true; break;}
1083 if(bj->playboard[i][j+2].type == mytype)
1084 {moves = true; break;}
1088 if(i < BJ_HEIGHT-1) {
1089 if(bj->playboard[i][j+1].type == mytype) {
1090 if(bj->playboard[i+1][j+2].type == mytype)
1091 {moves = true; break;}
1093 if(bj->playboard[i+1][j+1].type == mytype) {
1094 if(bj->playboard[i][j+2].type == mytype)
1095 {moves = true; break;}
1096 if(bj->playboard[i+1][j+2].type == mytype)
1097 {moves = true; break;}
1102 /* check vertical patterns */
1103 if(i <= BJ_HEIGHT-3) {
1104 if(j > 0) {
1105 if(bj->playboard[i+1][j-1].type == mytype) {
1106 if(bj->playboard[i+2][j-1].type == mytype)
1107 {moves = true; break;}
1108 if(bj->playboard[i+2][j].type == mytype)
1109 {moves = true; break;}
1111 if(bj->playboard[i+1][j].type == mytype) {
1112 if(bj->playboard[i+2][j-1].type == mytype)
1113 {moves = true; break;}
1117 if(i <= BJ_HEIGHT-4) {
1118 if(bj->playboard[i+3][j].type == mytype) {
1119 if(bj->playboard[i+1][j].type == mytype)
1120 {moves = true; break;}
1121 if(bj->playboard[i+2][j].type == mytype)
1122 {moves = true; break;}
1126 if(j < BJ_WIDTH-1) {
1127 if(bj->playboard[i+1][j].type == mytype) {
1128 if(bj->playboard[i+2][j+1].type == mytype)
1129 {moves = true; break;}
1131 if(bj->playboard[i+1][j+1].type == mytype) {
1132 if(bj->playboard[i+2][j].type == mytype)
1133 {moves = true; break;}
1134 if (bj->playboard[i+2][j+1].type == mytype)
1135 {moves = true; break;}
1140 if(moves) break;
1142 return moves;
1145 /*****************************************************************************
1146 * jewels_puzzle_is_finished() checks if the puzzle is finished.
1147 ******************************************************************************/
1148 static bool jewels_puzzle_is_finished(struct game_context* bj) {
1149 unsigned int i, j;
1150 for(i=0; i<BJ_HEIGHT; i++) {
1151 for(j=0; j<BJ_WIDTH; j++) {
1152 int mytype = bj->playboard[i][j].type;
1153 if(mytype>MAX_NUM_JEWELS) {
1154 mytype -= MAX_NUM_JEWELS;
1155 if(mytype&PUZZLE_TILE_UP) {
1156 if(i==0 || bj->playboard[i-1][j].type<=MAX_NUM_JEWELS ||
1157 !((bj->playboard[i-1][j].type-MAX_NUM_JEWELS)
1158 &PUZZLE_TILE_DOWN))
1159 return false;
1161 if(mytype&PUZZLE_TILE_DOWN) {
1162 if(i==BJ_HEIGHT-1 ||
1163 bj->playboard[i+1][j].type<=MAX_NUM_JEWELS ||
1164 !((bj->playboard[i+1][j].type-MAX_NUM_JEWELS)
1165 &PUZZLE_TILE_UP))
1166 return false;
1168 if(mytype&PUZZLE_TILE_LEFT) {
1169 if(j==0 || bj->playboard[i][j-1].type<=MAX_NUM_JEWELS ||
1170 !((bj->playboard[i][j-1].type-MAX_NUM_JEWELS)
1171 &PUZZLE_TILE_RIGHT))
1172 return false;
1174 if(mytype&PUZZLE_TILE_RIGHT) {
1175 if(j==BJ_WIDTH-1 ||
1176 bj->playboard[i][j+1].type<=MAX_NUM_JEWELS ||
1177 !((bj->playboard[i][j+1].type-MAX_NUM_JEWELS)
1178 &PUZZLE_TILE_LEFT))
1179 return false;
1184 return true;
1187 /*****************************************************************************
1188 * jewels_initlevel() initialises a level.
1189 ******************************************************************************/
1190 static unsigned int jewels_initlevel(struct game_context* bj) {
1191 unsigned int points = 0;
1193 switch(bj->type) {
1194 case GAME_TYPE_NORMAL:
1195 bj->num_jewels = MAX_NUM_JEWELS;
1196 break;
1198 case GAME_TYPE_PUZZLE:
1200 unsigned int i, j;
1201 struct puzzle_tile *tile;
1203 bj->num_jewels = puzzle_levels[bj->level-1].num_jewels;
1205 for(i=0; i<BJ_HEIGHT; i++) {
1206 for(j=0; j<BJ_WIDTH; j++) {
1207 bj->playboard[i][j].type = (rb->rand()%bj->num_jewels)+1;
1208 bj->playboard[i][j].falling = false;
1209 bj->playboard[i][j].delete = false;
1212 jewels_runboard(bj);
1213 tile = puzzle_levels[bj->level-1].tiles;
1214 for(i=0; i<puzzle_levels[bj->level-1].num_tiles; i++, tile++) {
1215 bj->playboard[tile->y+1][tile->x].type = MAX_NUM_JEWELS
1216 +tile->tile_type;
1219 break;
1222 jewels_drawboard(bj);
1224 /* run the play board */
1225 jewels_putjewels(bj);
1226 points += jewels_runboard(bj);
1227 return points;
1230 /*****************************************************************************
1231 * jewels_init() initializes jewels data structures.
1232 ******************************************************************************/
1233 static void jewels_init(struct game_context* bj) {
1234 /* seed the rand generator */
1235 rb->srand(*rb->current_tick);
1237 bj->type = bj->tmp_type;
1238 bj->level = 1;
1239 bj->score = 0;
1240 bj->segments = 0;
1242 jewels_setcolors();
1244 /* clear playing board */
1245 rb->memset(bj->playboard, 0, sizeof(bj->playboard));
1246 do {
1247 bj->score += jewels_initlevel(bj);
1248 } while(!jewels_movesavail(bj));
1251 /*****************************************************************************
1252 * jewels_nextlevel() advances the game to the next bj->level and returns
1253 * points earned.
1254 ******************************************************************************/
1255 static void jewels_nextlevel(struct game_context* bj) {
1256 int i, x, y;
1257 unsigned int points = 0;
1259 switch(bj->type) {
1260 case GAME_TYPE_NORMAL:
1261 /* roll over score, change and display level */
1262 while(bj->score >= LEVEL_PTS) {
1263 bj->score -= LEVEL_PTS;
1264 bj->level++;
1265 rb->splashf(HZ*2, "Level %d", bj->level);
1266 jewels_drawboard(bj);
1269 /* randomly clear some jewels */
1270 for(i=0; i<16; i++) {
1271 x = rb->rand()%8;
1272 y = rb->rand()%8;
1274 if(bj->playboard[y][x].type != 0) {
1275 points++;
1276 bj->playboard[y][x].type = 0;
1279 break;
1281 case GAME_TYPE_PUZZLE:
1282 bj->level++;
1283 rb->splashf(HZ*2, "Level %d", bj->level);
1284 break;
1287 points += jewels_initlevel(bj);
1288 bj->score += points;
1291 static bool jewels_help(void)
1293 rb->lcd_setfont(FONT_UI);
1294 #define WORDS (sizeof help_text / sizeof (char*))
1295 static char *help_text[] = {
1296 "Jewels", "", "Aim", "",
1297 "Swap", "pairs", "of", "jewels", "to", "form", "connected",
1298 "segments", "of", "three", "or", "more", "of", "the", "same",
1299 "type.", "",
1300 "The", "goal", "of", "the", "game", "is", "to", "score", "as", "many",
1301 "points", "as", "possible", "before", "running", "out", "of",
1302 "available", "moves.", "", "",
1303 "Controls", "",
1304 "Directions",
1305 #ifdef JEWELS_SCROLLWHEEL
1306 "or", "scroll",
1307 #endif
1308 "to", "move", "",
1309 HK_SELECT, "to", "select", "",
1310 HK_CANCEL, "to", "go", "to", "menu"
1312 static struct style_text formation[]={
1313 { 0, TEXT_CENTER|TEXT_UNDERLINE },
1314 { 2, C_RED },
1315 { 42, C_RED },
1316 { -1, 0 }
1318 #ifdef HAVE_LCD_COLOR
1319 rb->lcd_set_background(LCD_BLACK);
1320 rb->lcd_set_foreground(LCD_WHITE);
1321 #endif
1322 int button;
1323 if (display_text(WORDS, help_text, formation, NULL))
1324 return true;
1325 do {
1326 button = rb->button_get(true);
1327 if (rb->default_event_handler (button) == SYS_USB_CONNECTED) {
1328 return true;
1330 } while( ( button == BUTTON_NONE )
1331 || ( button & (BUTTON_REL|BUTTON_REPEAT) ) );
1332 rb->lcd_setfont(FONT_SYSFIXED);
1334 return false;
1337 static bool _ingame;
1338 static int jewels_menu_cb(int action, const struct menu_item_ex *this_item)
1340 int i = ((intptr_t)this_item);
1341 if(action == ACTION_REQUEST_MENUITEM
1342 && !_ingame && (i==0 || i==6))
1343 return ACTION_EXIT_MENUITEM;
1344 return action;
1346 /*****************************************************************************
1347 * jewels_game_menu() shows the game menu.
1348 ******************************************************************************/
1349 static int jewels_game_menu(struct game_context* bj, bool ingame)
1351 rb->button_clear_queue();
1352 int choice = 0;
1354 _ingame = ingame;
1356 static struct opt_items mode[] = {
1357 { "Normal", -1 },
1358 { "Puzzle", -1 },
1361 MENUITEM_STRINGLIST (main_menu, "Jewels Menu", jewels_menu_cb,
1362 "Resume Game",
1363 "Start New Game",
1364 "Mode",
1365 "Help",
1366 "High Scores",
1367 "Playback Control",
1368 "Quit without Saving",
1369 "Quit");
1371 while (1) {
1372 switch (rb->do_menu(&main_menu, &choice, NULL, false)) {
1373 case 0:
1374 jewels_setcolors();
1375 if(resume_file)
1376 rb->remove(SAVE_FILE);
1377 return 0;
1378 case 1:
1379 jewels_init(bj);
1380 return 0;
1381 case 2:
1382 rb->set_option("Mode", &bj->tmp_type, INT, mode, 2, NULL);
1383 break;
1384 case 3:
1385 if(jewels_help())
1386 return 1;
1387 break;
1388 case 4:
1389 highscore_show(NUM_SCORES, highest, NUM_SCORES, true);
1390 break;
1391 case 5:
1392 playback_control(NULL);
1393 break;
1394 case 6:
1395 return 1;
1396 case 7:
1397 if (ingame) {
1398 rb->splash(HZ*1, "Saving game ...");
1399 jewels_savegame(bj);
1401 return 1;
1402 case MENU_ATTACHED_USB:
1403 return 1;
1404 default:
1405 break;
1410 static int jewels_main(struct game_context* bj) {
1411 int button;
1412 int position;
1413 bool selected = false;
1414 bool no_movesavail;
1415 int x=0, y=0;
1417 bool loaded = jewels_loadgame(bj);
1418 resume_file = loaded;
1419 if (jewels_game_menu(bj, loaded)!=0)
1420 return 0;
1422 resume_file = false;
1423 while(true) {
1424 no_movesavail = false;
1426 /* refresh the board */
1427 jewels_drawboard(bj);
1429 /* display the cursor */
1430 if(selected) {
1431 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
1432 rb->lcd_fillrect(x*TILE_WIDTH, y*TILE_HEIGHT+YOFS,
1433 TILE_WIDTH, TILE_HEIGHT);
1434 rb->lcd_set_drawmode(DRMODE_SOLID);
1435 } else {
1436 rb->lcd_drawrect(x*TILE_WIDTH, y*TILE_HEIGHT+YOFS,
1437 TILE_WIDTH, TILE_HEIGHT);
1439 rb->lcd_update_rect(x*TILE_WIDTH, y*TILE_HEIGHT+YOFS,
1440 TILE_WIDTH, TILE_HEIGHT);
1442 /* handle game button presses */
1443 rb->yield();
1444 button = rb->button_get(true);
1445 switch(button){
1446 case JEWELS_LEFT: /* move cursor left */
1447 case (JEWELS_LEFT|BUTTON_REPEAT):
1448 if(selected) {
1449 bj->score += jewels_swapjewels(bj, x, y, SWAP_LEFT);
1450 selected = false;
1451 if (!jewels_movesavail(bj)) no_movesavail = true;
1452 } else {
1453 x = (x+BJ_WIDTH-1)%BJ_WIDTH;
1455 break;
1457 case JEWELS_RIGHT: /* move cursor right */
1458 case (JEWELS_RIGHT|BUTTON_REPEAT):
1459 if(selected) {
1460 bj->score += jewels_swapjewels(bj, x, y, SWAP_RIGHT);
1461 selected = false;
1462 if (!jewels_movesavail(bj)) no_movesavail = true;
1463 } else {
1464 x = (x+1)%BJ_WIDTH;
1466 break;
1468 case JEWELS_DOWN: /* move cursor down */
1469 case (JEWELS_DOWN|BUTTON_REPEAT):
1470 if(selected) {
1471 bj->score += jewels_swapjewels(bj, x, y, SWAP_DOWN);
1472 selected = false;
1473 if (!jewels_movesavail(bj)) no_movesavail = true;
1474 } else {
1475 y = (y+1)%(BJ_HEIGHT-1);
1477 break;
1479 case JEWELS_UP: /* move cursor up */
1480 case (JEWELS_UP|BUTTON_REPEAT):
1481 if(selected) {
1482 bj->score += jewels_swapjewels(bj, x, y, SWAP_UP);
1483 selected = false;
1484 if (!jewels_movesavail(bj)) no_movesavail = true;
1485 } else {
1486 y = (y+(BJ_HEIGHT-1)-1)%(BJ_HEIGHT-1);
1488 break;
1490 #ifdef JEWELS_SCROLLWHEEL
1491 case JEWELS_PREV: /* scroll backwards */
1492 case (JEWELS_PREV|BUTTON_REPEAT):
1493 if(!selected) {
1494 if(x == 0) {
1495 y = (y+(BJ_HEIGHT-1)-1)%(BJ_HEIGHT-1);
1497 x = (x+BJ_WIDTH-1)%BJ_WIDTH;
1499 break;
1501 case JEWELS_NEXT: /* scroll forwards */
1502 case (JEWELS_NEXT|BUTTON_REPEAT):
1503 if(!selected) {
1504 if(x == BJ_WIDTH-1) {
1505 y = (y+1)%(BJ_HEIGHT-1);
1507 x = (x+1)%BJ_WIDTH;
1509 break;
1510 #endif
1512 case JEWELS_SELECT: /* toggle selected */
1513 selected = !selected;
1514 break;
1516 #ifdef JEWELS_RC_CANCEL
1517 case JEWELS_RC_CANCEL:
1518 #endif
1519 case JEWELS_CANCEL: /* end game */
1520 if (jewels_game_menu(bj, true)!=0)
1521 return 0;
1522 break;
1524 default:
1525 if (rb->default_event_handler (button) == SYS_USB_CONNECTED)
1526 return PLUGIN_USB_CONNECTED;
1527 break;
1530 switch(bj->type) {
1531 case GAME_TYPE_NORMAL:
1532 if(bj->score >= LEVEL_PTS)
1533 jewels_nextlevel(bj);
1534 break;
1535 case GAME_TYPE_PUZZLE:
1536 if (jewels_puzzle_is_finished(bj)) {
1537 if (bj->level < NUM_PUZZLE_LEVELS) {
1538 jewels_nextlevel(bj);
1539 } else {
1540 rb->splash(2*HZ, "Congratulations!");
1541 rb->splash(2*HZ, "You have finished the game!");
1542 if (jewels_game_menu(bj, false)!=0) {
1543 return 0;
1546 break;
1550 if (no_movesavail) {
1551 switch(bj->type) {
1552 case GAME_TYPE_NORMAL:
1553 rb->splash(HZ*2, "Game Over!");
1554 rb->lcd_clear_display();
1555 bj->score += (bj->level-1)*LEVEL_PTS;
1556 position=highscore_update(bj->score, bj->level, "",
1557 highest, NUM_SCORES);
1558 if (position == 0)
1559 rb->splash(HZ*2, "New High Score");
1560 if (position != -1)
1561 highscore_show(position, highest, NUM_SCORES, true);
1562 break;
1563 case GAME_TYPE_PUZZLE:
1564 rb->splash(2*HZ, "Game Over");
1565 break;
1567 if (jewels_game_menu(bj, false)!=0) {
1568 return 0;
1574 /* this is the plugin entry point */
1575 enum plugin_status plugin_start(const void* parameter)
1577 (void)parameter;
1579 /* load high scores */
1580 highscore_load(HIGH_SCORE,highest,NUM_SCORES);
1582 rb->lcd_setfont(FONT_SYSFIXED);
1583 #if LCD_DEPTH > 1
1584 rb->lcd_set_backdrop(NULL);
1585 #endif
1587 struct game_context bj;
1588 bj.tmp_type = GAME_TYPE_NORMAL;
1589 jewels_main(&bj);
1590 highscore_save(HIGH_SCORE,highest,NUM_SCORES);
1591 rb->lcd_setfont(FONT_UI);
1593 return PLUGIN_OK;
1596 #endif /* HAVE_LCD_BITMAP */