Packard Bell Vibe 500: More plugin keymaps (second portion).
[kugel-rb.git] / apps / plugins / jewels.c
blob711a65591e8861c39e52967d554ca8d36ef2a1be
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"
440 #define HIGH_SCORE PLUGIN_GAMES_DIR "/jewels.score"
441 struct highscore highest[NUM_SCORES];
443 static bool resume_file = false;
445 /*****************************************************************************
446 * jewels_setcolors() set the foreground and background colors.
447 ******************************************************************************/
448 static inline void jewels_setcolors(void) {
449 #ifdef HAVE_LCD_COLOR
450 rb->lcd_set_background(LCD_RGBPACK(49, 26, 26));
451 rb->lcd_set_foreground(LCD_RGBPACK(210, 181, 181));
452 #endif
455 /*****************************************************************************
456 * jewels_loadgame() loads the saved game and returns load success.
457 ******************************************************************************/
458 static bool jewels_loadgame(struct game_context* bj)
460 int fd;
461 bool loaded = false;
463 /* open game file */
464 fd = rb->open(SAVE_FILE, O_RDONLY);
465 if(fd < 0) return loaded;
467 /* read in saved game */
468 while(true) {
469 if(rb->read(fd, &bj->tmp_type, sizeof(bj->tmp_type)) <= 0) break;
470 if(rb->read(fd, &bj->type, sizeof(bj->type)) <= 0) break;
471 if(rb->read(fd, &bj->score, sizeof(bj->score)) <= 0) break;
472 if(rb->read(fd, &bj->level, sizeof(bj->level)) <= 0) break;
473 if(rb->read(fd, &bj->segments, sizeof(bj->segments)) <= 0) break;
474 if(rb->read(fd, &bj->num_jewels, sizeof(bj->num_jewels)) <= 0) break;
475 if(rb->read(fd, bj->playboard, sizeof(bj->playboard)) <= 0) break;
476 loaded = true;
477 break;
480 rb->close(fd);
482 return loaded;
485 /*****************************************************************************
486 * jewels_savegame() saves the current game state.
487 ******************************************************************************/
488 static void jewels_savegame(struct game_context* bj)
490 int fd;
491 /* write out the game state to the save file */
492 fd = rb->open(SAVE_FILE, O_WRONLY|O_CREAT);
493 if(fd < 0) return;
495 rb->write(fd, &bj->tmp_type, sizeof(bj->tmp_type));
496 rb->write(fd, &bj->type, sizeof(bj->type));
497 rb->write(fd, &bj->score, sizeof(bj->score));
498 rb->write(fd, &bj->level, sizeof(bj->level));
499 rb->write(fd, &bj->segments, sizeof(bj->segments));
500 rb->write(fd, &bj->num_jewels, sizeof(bj->num_jewels));
501 rb->write(fd, bj->playboard, sizeof(bj->playboard));
502 rb->close(fd);
505 /*****************************************************************************
506 * jewels_drawboard() redraws the entire game board.
507 ******************************************************************************/
508 static void jewels_drawboard(struct game_context* bj) {
509 int i, j;
510 int w, h;
511 unsigned int tempscore;
512 unsigned int size;
513 char *title = "Level";
514 char str[10];
516 if (bj->type == GAME_TYPE_NORMAL) {
517 tempscore = (bj->score>LEVEL_PTS ? LEVEL_PTS : bj->score);
518 size = LEVEL_PTS;
519 } else {
520 tempscore = (bj->level>NUM_PUZZLE_LEVELS ? NUM_PUZZLE_LEVELS : bj->level);
521 size = NUM_PUZZLE_LEVELS;
524 /* clear screen */
525 rb->lcd_clear_display();
527 /* dispay playing board */
528 for(i=0; i<BJ_HEIGHT-1; i++){
529 for(j=0; j<BJ_WIDTH; j++){
530 #ifdef HAVE_LCD_COLOR
531 rb->lcd_set_foreground(jewels_bkgd[(i+j)%2]);
532 rb->lcd_fillrect(j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
533 TILE_WIDTH, TILE_HEIGHT);
534 rb->lcd_bitmap_transparent_part(jewels,
535 0, TILE_HEIGHT*(bj->playboard[i+1][j].type),
536 STRIDE( SCREEN_MAIN,
537 BMPWIDTH_jewels, BMPHEIGHT_jewels),
538 j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
539 TILE_WIDTH, TILE_HEIGHT);
540 #else
541 rb->lcd_bitmap_part(jewels,
542 0, TILE_HEIGHT*(bj->playboard[i+1][j].type),
543 STRIDE( SCREEN_MAIN,
544 BMPWIDTH_jewels, BMPHEIGHT_jewels),
545 j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
546 TILE_WIDTH, TILE_HEIGHT);
547 #endif
551 #if LCD_WIDTH > LCD_HEIGHT /* horizontal layout */
553 /* draw separator lines */
554 jewels_setcolors();
555 rb->lcd_vline(BJ_WIDTH*TILE_WIDTH, 0, LCD_HEIGHT-1);
557 rb->lcd_hline(BJ_WIDTH*TILE_WIDTH, LCD_WIDTH-1, 18);
558 rb->lcd_hline(BJ_WIDTH*TILE_WIDTH, LCD_WIDTH-1, LCD_HEIGHT-10);
560 /* draw progress bar */
561 #ifdef HAVE_LCD_COLOR
562 rb->lcd_set_foreground(LCD_RGBPACK(104, 63, 63));
563 #endif
564 rb->lcd_fillrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4,
565 (LCD_HEIGHT-10)-(((LCD_HEIGHT-10)-18)*
566 tempscore/size),
567 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2,
568 ((LCD_HEIGHT-10)-18)*tempscore/size);
569 #ifdef HAVE_LCD_COLOR
570 rb->lcd_set_foreground(LCD_RGBPACK(83, 44, 44));
571 rb->lcd_drawrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4+1,
572 (LCD_HEIGHT-10)-(((LCD_HEIGHT-10)-18)*
573 tempscore/size)+1,
574 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-2,
575 ((LCD_HEIGHT-10)-18)*tempscore/size-1);
576 jewels_setcolors();
577 rb->lcd_drawrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4,
578 (LCD_HEIGHT-10)-(((LCD_HEIGHT-10)-18)*tempscore/size),
579 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2,
580 ((LCD_HEIGHT-10)-18)*tempscore/size+1);
581 #endif
583 /* print text */
584 rb->lcd_getstringsize(title, &w, &h);
585 rb->lcd_putsxy(LCD_WIDTH-(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-w/2, 1, title);
586 rb->snprintf(str, 4, "%d", bj->level);
587 rb->lcd_getstringsize(str, &w, &h);
588 rb->lcd_putsxy(LCD_WIDTH-(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-w/2, 10, str);
590 if (bj->type == GAME_TYPE_NORMAL) {
591 rb->snprintf(str, 6, "%d", (bj->level-1)*LEVEL_PTS+bj->score);
592 rb->lcd_getstringsize(str, &w, &h);
593 rb->lcd_putsxy(LCD_WIDTH-(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-w/2,
594 LCD_HEIGHT-8, str);
597 #elif LCD_WIDTH < LCD_HEIGHT /* vertical layout */
599 /* draw separator lines */
600 jewels_setcolors();
601 rb->lcd_hline(0, LCD_WIDTH-1, 8*TILE_HEIGHT+YOFS);
602 rb->lcd_hline(0, LCD_WIDTH-1, LCD_HEIGHT-14);
603 rb->lcd_vline(LCD_WIDTH/2, LCD_HEIGHT-14, LCD_HEIGHT-1);
605 /* draw progress bar */
606 #ifdef HAVE_LCD_COLOR
607 rb->lcd_set_foreground(LCD_RGBPACK(104, 63, 63));
608 #endif
609 rb->lcd_fillrect(0, (8*TILE_HEIGHT+YOFS)
610 +(LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/4,
611 LCD_WIDTH*tempscore/size,
612 (LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/2);
613 #ifdef HAVE_LCD_COLOR
614 rb->lcd_set_foreground(LCD_RGBPACK(83, 44, 44));
615 rb->lcd_drawrect(1, (8*TILE_HEIGHT+YOFS)
616 +(LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/4+1,
617 LCD_WIDTH*tempscore/size-1,
618 (LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/2-2);
619 jewels_setcolors();
620 rb->lcd_drawrect(0, (8*TILE_HEIGHT+YOFS)
621 +(LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/4,
622 LCD_WIDTH*tempscore/size+1,
623 (LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/2);
624 #endif
626 /* print text */
627 rb->snprintf(str, 10, "%s %d", title, bj->level);
628 rb->lcd_putsxy(1, LCD_HEIGHT-10, str);
630 if (bj->type == GAME_TYPE_NORMAL) {
631 rb->snprintf(str, 6, "%d", (bj->level-1)*LEVEL_PTS+bj->score);
632 rb->lcd_getstringsize(str, &w, &h);
633 rb->lcd_putsxy((LCD_WIDTH-2)-w, LCD_HEIGHT-10, str);
637 #else /* square layout */
639 /* draw separator lines */
640 jewels_setcolors();
641 rb->lcd_hline(0, LCD_WIDTH-1, 8*TILE_HEIGHT+YOFS);
642 rb->lcd_vline(BJ_WIDTH*TILE_WIDTH, 0, 8*TILE_HEIGHT+YOFS);
643 rb->lcd_vline(LCD_WIDTH/2, 8*TILE_HEIGHT+YOFS, LCD_HEIGHT-1);
645 /* draw progress bar */
646 #ifdef HAVE_LCD_COLOR
647 rb->lcd_set_foreground(LCD_RGBPACK(104, 63, 63));
648 #endif
649 rb->lcd_fillrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4,
650 (8*TILE_HEIGHT+YOFS)-(8*TILE_HEIGHT+YOFS)*tempscore/size,
651 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2,
652 (8*TILE_HEIGHT+YOFS)*tempscore/size);
653 #ifdef HAVE_LCD_COLOR
654 rb->lcd_set_foreground(LCD_RGBPACK(83, 44, 44));
655 rb->lcd_drawrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4+1,
656 (8*TILE_HEIGHT+YOFS)-(8*TILE_HEIGHT+YOFS)
657 *tempscore/size+1,
658 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-2,
659 (8*TILE_HEIGHT+YOFS)*tempscore/size-1);
660 jewels_setcolors();
661 rb->lcd_drawrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4,
662 (8*TILE_HEIGHT+YOFS)-(8*TILE_HEIGHT+YOFS)
663 *tempscore/size,
664 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2,
665 (8*TILE_HEIGHT+YOFS)*tempscore/size+1);
666 #endif
668 /* print text */
669 rb->snprintf(str, 10, "%s %d", title, bj->level);
670 rb->lcd_putsxy(1, LCD_HEIGHT-(LCD_HEIGHT-(8*TILE_HEIGHT+YOFS))/2-3, str);
672 if (bj->type == GAME_TYPE_NORMAL) {
673 rb->snprintf(str, 6, "%d", (bj->level-1)*LEVEL_PTS+bj->score);
674 rb->lcd_getstringsize(str, &w, &h);
675 rb->lcd_putsxy((LCD_WIDTH-2)-w,
676 LCD_HEIGHT-(LCD_HEIGHT-(8*TILE_HEIGHT+YOFS))/2-3, str);
679 #endif /* layout */
681 rb->lcd_update();
684 /*****************************************************************************
685 * jewels_putjewels() makes the jewels fall to fill empty spots and adds
686 * new random jewels at the empty spots at the top of each row.
687 ******************************************************************************/
688 static void jewels_putjewels(struct game_context* bj){
689 int i, j, k;
690 bool mark, done;
691 long lasttick, currenttick;
693 /* loop to make all the jewels fall */
694 while(true) {
695 /* mark falling jewels and add new jewels to hidden top row*/
696 mark = false;
697 done = true;
698 for(j=0; j<BJ_WIDTH; j++) {
699 if(bj->playboard[1][j].type == 0) {
700 bj->playboard[0][j].type = rb->rand()%bj->num_jewels+1;
702 for(i=BJ_HEIGHT-2; i>=0; i--) {
703 if(!mark && bj->playboard[i+1][j].type == 0) {
704 mark = true;
705 done = false;
707 if(mark) bj->playboard[i][j].falling = true;
709 /*if(bj->playboard[1][j].falling) {
710 bj->playboard[0][j].type = rb->rand()%bj->num_jewels+1;
711 bj->playboard[0][j].falling = true;
713 mark = false;
716 /* break if there are no falling jewels */
717 if(done) break;
719 /* animate falling jewels */
720 lasttick = *rb->current_tick;
722 for(k=1; k<=8; k++) {
723 for(i=BJ_HEIGHT-2; i>=0; i--) {
724 for(j=0; j<BJ_WIDTH; j++) {
725 if(bj->playboard[i][j].falling &&
726 bj->playboard[i][j].type != 0) {
727 /* clear old position */
728 #ifdef HAVE_LCD_COLOR
729 if(i == 0 && YOFS) {
730 rb->lcd_set_foreground(rb->lcd_get_background());
731 } else {
732 rb->lcd_set_foreground(jewels_bkgd[(i-1+j)%2]);
734 rb->lcd_fillrect(j*TILE_WIDTH, (i-1)*TILE_HEIGHT+YOFS,
735 TILE_WIDTH, TILE_HEIGHT);
736 if(bj->playboard[i+1][j].type == 0) {
737 rb->lcd_set_foreground(jewels_bkgd[(i+j)%2]);
738 rb->lcd_fillrect(j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
739 TILE_WIDTH, TILE_HEIGHT);
741 #else
742 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
743 rb->lcd_fillrect(j*TILE_WIDTH, (i-1)*TILE_HEIGHT+YOFS,
744 TILE_WIDTH, TILE_HEIGHT);
745 if(bj->playboard[i+1][j].type == 0) {
746 rb->lcd_fillrect(j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
747 TILE_WIDTH, TILE_HEIGHT);
749 rb->lcd_set_drawmode(DRMODE_SOLID);
750 #endif
752 /* draw new position */
753 #ifdef HAVE_LCD_COLOR
754 rb->lcd_bitmap_transparent_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 #else
764 rb->lcd_bitmap_part(jewels, 0,
765 TILE_HEIGHT*(bj->playboard[i][j].type),
766 STRIDE( SCREEN_MAIN,
767 BMPWIDTH_jewels,
768 BMPHEIGHT_jewels),
769 j*TILE_WIDTH,
770 (i-1)*TILE_HEIGHT+YOFS+
771 ((((TILE_HEIGHT<<10)*k)/8)>>10),
772 TILE_WIDTH, TILE_HEIGHT);
773 #endif
778 rb->lcd_update_rect(0, 0, TILE_WIDTH*8, LCD_HEIGHT);
779 jewels_setcolors();
781 /* framerate limiting */
782 currenttick = *rb->current_tick;
783 if(currenttick-lasttick < HZ/MAX_FPS) {
784 rb->sleep((HZ/MAX_FPS)-(currenttick-lasttick));
785 } else {
786 rb->yield();
788 lasttick = currenttick;
791 /* shift jewels down */
792 for(j=0; j<BJ_WIDTH; j++) {
793 for(i=BJ_HEIGHT-1; i>=1; i--) {
794 if(bj->playboard[i-1][j].falling) {
795 bj->playboard[i][j].type = bj->playboard[i-1][j].type;
800 /* clear out top row */
801 for(j=0; j<BJ_WIDTH; j++) {
802 bj->playboard[0][j].type = 0;
805 /* mark everything not falling */
806 for(i=0; i<BJ_HEIGHT; i++) {
807 for(j=0; j<BJ_WIDTH; j++) {
808 bj->playboard[i][j].falling = false;
814 /*****************************************************************************
815 * jewels_clearjewels() finds all the connected rows and columns and
816 * calculates and returns the points earned.
817 ******************************************************************************/
818 static unsigned int jewels_clearjewels(struct game_context* bj) {
819 int i, j;
820 int last, run;
821 unsigned int points = 0;
823 /* check for connected rows */
824 for(i=1; i<BJ_HEIGHT; i++) {
825 last = 0;
826 run = 1;
827 for(j=0; j<BJ_WIDTH; j++) {
828 if(bj->playboard[i][j].type == last &&
829 bj->playboard[i][j].type != 0 &&
830 bj->playboard[i][j].type <= MAX_NUM_JEWELS) {
831 run++;
833 if(run == 3) {
834 bj->segments++;
835 points += bj->segments;
836 bj->playboard[i][j].delete = true;
837 bj->playboard[i][j-1].delete = true;
838 bj->playboard[i][j-2].delete = true;
839 } else if(run > 3) {
840 points++;
841 bj->playboard[i][j].delete = true;
843 } else {
844 run = 1;
845 last = bj->playboard[i][j].type;
850 /* check for connected columns */
851 for(j=0; j<BJ_WIDTH; j++) {
852 last = 0;
853 run = 1;
854 for(i=1; i<BJ_HEIGHT; i++) {
855 if(bj->playboard[i][j].type != 0 &&
856 bj->playboard[i][j].type == last &&
857 bj->playboard[i][j].type <= MAX_NUM_JEWELS) {
858 run++;
860 if(run == 3) {
861 bj->segments++;
862 points += bj->segments;
863 bj->playboard[i][j].delete = true;
864 bj->playboard[i-1][j].delete = true;
865 bj->playboard[i-2][j].delete = true;
866 } else if(run > 3) {
867 points++;
868 bj->playboard[i][j].delete = true;
870 } else {
871 run = 1;
872 last = bj->playboard[i][j].type;
877 /* clear deleted jewels */
878 for(i=1; i<BJ_HEIGHT; i++) {
879 for(j=0; j<BJ_WIDTH; j++) {
880 if(bj->playboard[i][j].delete) {
881 bj->playboard[i][j].delete = false;
882 bj->playboard[i][j].type = 0;
887 return points;
890 /*****************************************************************************
891 * jewels_runboard() runs the board until it settles in a fixed state and
892 * returns points earned.
893 ******************************************************************************/
894 static unsigned int jewels_runboard(struct game_context* bj) {
895 unsigned int points = 0;
896 unsigned int ret;
898 bj->segments = 0;
900 while((ret = jewels_clearjewels(bj)) > 0) {
901 points += ret;
902 jewels_drawboard(bj);
903 jewels_putjewels(bj);
906 return points;
909 /*****************************************************************************
910 * jewels_swapjewels() swaps two jewels as long as it results in points and
911 * returns points earned.
912 ******************************************************************************/
913 static unsigned int jewels_swapjewels(struct game_context* bj,
914 int x, int y, int direc) {
915 int k;
916 int horzmod, vertmod;
917 int movelen = 0;
918 bool undo = false;
919 unsigned int points = 0;
920 long lasttick, currenttick;
922 /* check for invalid parameters */
923 if(x < 0 || x >= BJ_WIDTH || y < 0 || y >= BJ_HEIGHT-1 ||
924 direc < SWAP_UP || direc > SWAP_LEFT) {
925 return 0;
928 /* check for invalid directions */
929 if((x == 0 && direc == SWAP_LEFT) ||
930 (x == BJ_WIDTH-1 && direc == SWAP_RIGHT) ||
931 (y == 0 && direc == SWAP_UP) ||
932 (y == BJ_HEIGHT-2 && direc == SWAP_DOWN)) {
933 return 0;
936 /* set direction variables */
937 horzmod = 0;
938 vertmod = 0;
939 switch(direc) {
940 case SWAP_UP:
941 vertmod = -1;
942 movelen = TILE_HEIGHT;
943 break;
944 case SWAP_RIGHT:
945 horzmod = 1;
946 movelen = TILE_WIDTH;
947 break;
948 case SWAP_DOWN:
949 vertmod = 1;
950 movelen = TILE_HEIGHT;
951 break;
952 case SWAP_LEFT:
953 horzmod = -1;
954 movelen = TILE_WIDTH;
955 break;
958 while(true) {
959 lasttick = *rb->current_tick;
961 /* animate swapping jewels */
962 for(k=0; k<=8; k++) {
963 /* clear old position */
964 #ifdef HAVE_LCD_COLOR
965 rb->lcd_set_foreground(jewels_bkgd[(x+y)%2]);
966 rb->lcd_fillrect(x*TILE_WIDTH,
967 y*TILE_HEIGHT+YOFS,
968 TILE_WIDTH, TILE_HEIGHT);
969 rb->lcd_set_foreground(jewels_bkgd[(x+horzmod+y+vertmod)%2]);
970 rb->lcd_fillrect((x+horzmod)*TILE_WIDTH,
971 (y+vertmod)*TILE_HEIGHT+YOFS,
972 TILE_WIDTH, TILE_HEIGHT);
973 #else
974 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
975 rb->lcd_fillrect(x*TILE_WIDTH,
976 y*TILE_HEIGHT+YOFS,
977 TILE_WIDTH, TILE_HEIGHT);
978 rb->lcd_fillrect((x+horzmod)*TILE_WIDTH,
979 (y+vertmod)*TILE_HEIGHT+YOFS,
980 TILE_WIDTH, TILE_HEIGHT);
981 rb->lcd_set_drawmode(DRMODE_SOLID);
982 #endif
983 /* draw new position */
984 #ifdef HAVE_LCD_COLOR
985 rb->lcd_bitmap_transparent_part(jewels,
986 0, TILE_HEIGHT*(bj->playboard
987 [y+1+vertmod][x+horzmod].type),
988 STRIDE( SCREEN_MAIN,
989 BMPWIDTH_jewels, BMPHEIGHT_jewels),
990 (x+horzmod)*TILE_WIDTH-horzmod*
991 ((((movelen<<10)*k)/8)>>10),
992 (y+vertmod)*TILE_HEIGHT-vertmod*
993 ((((movelen<<10)*k)/8)>>10)+YOFS,
994 TILE_WIDTH, TILE_HEIGHT);
995 rb->lcd_bitmap_transparent_part(jewels,
996 0, TILE_HEIGHT*(bj->playboard[y+1][x].type),
997 STRIDE( SCREEN_MAIN,
998 BMPWIDTH_jewels, BMPHEIGHT_jewels),
999 x*TILE_WIDTH+horzmod*
1000 ((((movelen<<10)*k)/8)>>10),
1001 y*TILE_HEIGHT+vertmod*
1002 ((((movelen<<10)*k)/8)>>10)+YOFS,
1003 TILE_WIDTH, TILE_HEIGHT);
1004 #else
1005 rb->lcd_bitmap_part(jewels,
1006 0, TILE_HEIGHT*(bj->playboard
1007 [y+1+vertmod][x+horzmod].type),
1008 STRIDE( SCREEN_MAIN,
1009 BMPWIDTH_jewels, BMPHEIGHT_jewels),
1010 (x+horzmod)*TILE_WIDTH-horzmod*
1011 ((((movelen<<10)*k)/8)>>10),
1012 (y+vertmod)*TILE_HEIGHT-vertmod*
1013 ((((movelen<<10)*k)/8)>>10)+YOFS,
1014 TILE_WIDTH, TILE_HEIGHT);
1015 rb->lcd_set_drawmode(DRMODE_FG);
1016 rb->lcd_bitmap_part(jewels,
1017 0, TILE_HEIGHT*(bj->playboard[y+1][x].type),
1018 STRIDE( SCREEN_MAIN,
1019 BMPWIDTH_jewels, BMPHEIGHT_jewels),
1020 x*TILE_WIDTH+horzmod*
1021 ((((movelen<<10)*k)/8)>>10),
1022 y*TILE_HEIGHT+vertmod*
1023 ((((movelen<<10)*k)/8)>>10)+YOFS,
1024 TILE_WIDTH, TILE_HEIGHT);
1025 rb->lcd_set_drawmode(DRMODE_SOLID);
1026 #endif
1028 rb->lcd_update_rect(0, 0, TILE_WIDTH*8, LCD_HEIGHT);
1029 jewels_setcolors();
1031 /* framerate limiting */
1032 currenttick = *rb->current_tick;
1033 if(currenttick-lasttick < HZ/MAX_FPS) {
1034 rb->sleep((HZ/MAX_FPS)-(currenttick-lasttick));
1035 } else {
1036 rb->yield();
1038 lasttick = currenttick;
1041 /* swap jewels */
1042 int temp = bj->playboard[y+1][x].type;
1043 bj->playboard[y+1][x].type =
1044 bj->playboard[y+1+vertmod][x+horzmod].type;
1045 bj->playboard[y+1+vertmod][x+horzmod].type = temp;
1047 if(undo) break;
1049 points = jewels_runboard(bj);
1050 if(points == 0) {
1051 undo = true;
1052 } else {
1053 break;
1057 return points;
1060 /*****************************************************************************
1061 * jewels_movesavail() uses pattern matching to see if there are any
1062 * available move left.
1063 ******************************************************************************/
1064 static bool jewels_movesavail(struct game_context* bj) {
1065 int i, j;
1066 bool moves = false;
1067 int mytype;
1069 for(i=1; i<BJ_HEIGHT; i++) {
1070 for(j=0; j<BJ_WIDTH; j++) {
1071 mytype = bj->playboard[i][j].type;
1072 if(mytype == 0 || mytype > MAX_NUM_JEWELS) continue;
1074 /* check horizontal patterns */
1075 if(j <= BJ_WIDTH-3) {
1076 if(i > 1) {
1077 if(bj->playboard[i-1][j+1].type == mytype) {
1078 if(bj->playboard[i-1][j+2].type == mytype)
1079 {moves = true; break;}
1080 if(bj->playboard[i][j+2].type == mytype)
1081 {moves = true; break;}
1083 if(bj->playboard[i][j+1].type == mytype) {
1084 if(bj->playboard[i-1][j+2].type == mytype)
1085 {moves = true; break;}
1089 if(j <= BJ_WIDTH-4) {
1090 if(bj->playboard[i][j+3].type == mytype) {
1091 if(bj->playboard[i][j+1].type == mytype)
1092 {moves = true; break;}
1093 if(bj->playboard[i][j+2].type == mytype)
1094 {moves = true; break;}
1098 if(i < BJ_HEIGHT-1) {
1099 if(bj->playboard[i][j+1].type == mytype) {
1100 if(bj->playboard[i+1][j+2].type == mytype)
1101 {moves = true; break;}
1103 if(bj->playboard[i+1][j+1].type == mytype) {
1104 if(bj->playboard[i][j+2].type == mytype)
1105 {moves = true; break;}
1106 if(bj->playboard[i+1][j+2].type == mytype)
1107 {moves = true; break;}
1112 /* check vertical patterns */
1113 if(i <= BJ_HEIGHT-3) {
1114 if(j > 0) {
1115 if(bj->playboard[i+1][j-1].type == mytype) {
1116 if(bj->playboard[i+2][j-1].type == mytype)
1117 {moves = true; break;}
1118 if(bj->playboard[i+2][j].type == mytype)
1119 {moves = true; break;}
1121 if(bj->playboard[i+1][j].type == mytype) {
1122 if(bj->playboard[i+2][j-1].type == mytype)
1123 {moves = true; break;}
1127 if(i <= BJ_HEIGHT-4) {
1128 if(bj->playboard[i+3][j].type == mytype) {
1129 if(bj->playboard[i+1][j].type == mytype)
1130 {moves = true; break;}
1131 if(bj->playboard[i+2][j].type == mytype)
1132 {moves = true; break;}
1136 if(j < BJ_WIDTH-1) {
1137 if(bj->playboard[i+1][j].type == mytype) {
1138 if(bj->playboard[i+2][j+1].type == mytype)
1139 {moves = true; break;}
1141 if(bj->playboard[i+1][j+1].type == mytype) {
1142 if(bj->playboard[i+2][j].type == mytype)
1143 {moves = true; break;}
1144 if (bj->playboard[i+2][j+1].type == mytype)
1145 {moves = true; break;}
1150 if(moves) break;
1152 return moves;
1155 /*****************************************************************************
1156 * jewels_puzzle_is_finished() checks if the puzzle is finished.
1157 ******************************************************************************/
1158 static bool jewels_puzzle_is_finished(struct game_context* bj) {
1159 unsigned int i, j;
1160 for(i=0; i<BJ_HEIGHT; i++) {
1161 for(j=0; j<BJ_WIDTH; j++) {
1162 int mytype = bj->playboard[i][j].type;
1163 if(mytype>MAX_NUM_JEWELS) {
1164 mytype -= MAX_NUM_JEWELS;
1165 if(mytype&PUZZLE_TILE_UP) {
1166 if(i==0 || bj->playboard[i-1][j].type<=MAX_NUM_JEWELS ||
1167 !((bj->playboard[i-1][j].type-MAX_NUM_JEWELS)
1168 &PUZZLE_TILE_DOWN))
1169 return false;
1171 if(mytype&PUZZLE_TILE_DOWN) {
1172 if(i==BJ_HEIGHT-1 ||
1173 bj->playboard[i+1][j].type<=MAX_NUM_JEWELS ||
1174 !((bj->playboard[i+1][j].type-MAX_NUM_JEWELS)
1175 &PUZZLE_TILE_UP))
1176 return false;
1178 if(mytype&PUZZLE_TILE_LEFT) {
1179 if(j==0 || bj->playboard[i][j-1].type<=MAX_NUM_JEWELS ||
1180 !((bj->playboard[i][j-1].type-MAX_NUM_JEWELS)
1181 &PUZZLE_TILE_RIGHT))
1182 return false;
1184 if(mytype&PUZZLE_TILE_RIGHT) {
1185 if(j==BJ_WIDTH-1 ||
1186 bj->playboard[i][j+1].type<=MAX_NUM_JEWELS ||
1187 !((bj->playboard[i][j+1].type-MAX_NUM_JEWELS)
1188 &PUZZLE_TILE_LEFT))
1189 return false;
1194 return true;
1197 /*****************************************************************************
1198 * jewels_initlevel() initialises a level.
1199 ******************************************************************************/
1200 static unsigned int jewels_initlevel(struct game_context* bj) {
1201 unsigned int points = 0;
1203 switch(bj->type) {
1204 case GAME_TYPE_NORMAL:
1205 bj->num_jewels = MAX_NUM_JEWELS;
1206 break;
1208 case GAME_TYPE_PUZZLE:
1210 unsigned int i, j;
1211 struct puzzle_tile *tile;
1213 bj->num_jewels = puzzle_levels[bj->level-1].num_jewels;
1215 for(i=0; i<BJ_HEIGHT; i++) {
1216 for(j=0; j<BJ_WIDTH; j++) {
1217 bj->playboard[i][j].type = (rb->rand()%bj->num_jewels)+1;
1218 bj->playboard[i][j].falling = false;
1219 bj->playboard[i][j].delete = false;
1222 jewels_runboard(bj);
1223 tile = puzzle_levels[bj->level-1].tiles;
1224 for(i=0; i<puzzle_levels[bj->level-1].num_tiles; i++, tile++) {
1225 bj->playboard[tile->y+1][tile->x].type = MAX_NUM_JEWELS
1226 +tile->tile_type;
1229 break;
1232 jewels_drawboard(bj);
1234 /* run the play board */
1235 jewels_putjewels(bj);
1236 points += jewels_runboard(bj);
1237 return points;
1240 /*****************************************************************************
1241 * jewels_init() initializes jewels data structures.
1242 ******************************************************************************/
1243 static void jewels_init(struct game_context* bj) {
1244 /* seed the rand generator */
1245 rb->srand(*rb->current_tick);
1247 bj->type = bj->tmp_type;
1248 bj->level = 1;
1249 bj->score = 0;
1250 bj->segments = 0;
1252 jewels_setcolors();
1254 /* clear playing board */
1255 rb->memset(bj->playboard, 0, sizeof(bj->playboard));
1256 do {
1257 bj->score += jewels_initlevel(bj);
1258 } while(!jewels_movesavail(bj));
1261 /*****************************************************************************
1262 * jewels_nextlevel() advances the game to the next bj->level and returns
1263 * points earned.
1264 ******************************************************************************/
1265 static void jewels_nextlevel(struct game_context* bj) {
1266 int i, x, y;
1267 unsigned int points = 0;
1269 switch(bj->type) {
1270 case GAME_TYPE_NORMAL:
1271 /* roll over score, change and display level */
1272 while(bj->score >= LEVEL_PTS) {
1273 bj->score -= LEVEL_PTS;
1274 bj->level++;
1275 rb->splashf(HZ*2, "Level %d", bj->level);
1276 jewels_drawboard(bj);
1279 /* randomly clear some jewels */
1280 for(i=0; i<16; i++) {
1281 x = rb->rand()%8;
1282 y = rb->rand()%8;
1284 if(bj->playboard[y][x].type != 0) {
1285 points++;
1286 bj->playboard[y][x].type = 0;
1289 break;
1291 case GAME_TYPE_PUZZLE:
1292 bj->level++;
1293 rb->splashf(HZ*2, "Level %d", bj->level);
1294 break;
1297 points += jewels_initlevel(bj);
1298 bj->score += points;
1301 static bool jewels_help(void)
1303 rb->lcd_setfont(FONT_UI);
1304 #define WORDS (sizeof help_text / sizeof (char*))
1305 static char *help_text[] = {
1306 "Jewels", "", "Aim", "",
1307 "Swap", "pairs", "of", "jewels", "to", "form", "connected",
1308 "segments", "of", "three", "or", "more", "of", "the", "same",
1309 "type.", "",
1310 "The", "goal", "of", "the", "game", "is", "to", "score", "as", "many",
1311 "points", "as", "possible", "before", "running", "out", "of",
1312 "available", "moves.", "", "",
1313 "Controls", "",
1314 "Directions",
1315 #ifdef JEWELS_SCROLLWHEEL
1316 "or", "scroll",
1317 #endif
1318 "to", "move", "",
1319 HK_SELECT, "to", "select", "",
1320 HK_CANCEL, "to", "go", "to", "menu"
1322 static struct style_text formation[]={
1323 { 0, TEXT_CENTER|TEXT_UNDERLINE },
1324 { 2, C_RED },
1325 { 42, C_RED },
1326 { -1, 0 }
1328 #ifdef HAVE_LCD_COLOR
1329 rb->lcd_set_background(LCD_BLACK);
1330 rb->lcd_set_foreground(LCD_WHITE);
1331 #endif
1332 int button;
1333 if (display_text(WORDS, help_text, formation, NULL))
1334 return true;
1335 do {
1336 button = rb->button_get(true);
1337 if (rb->default_event_handler (button) == SYS_USB_CONNECTED) {
1338 return true;
1340 } while( ( button == BUTTON_NONE )
1341 || ( button & (BUTTON_REL|BUTTON_REPEAT) ) );
1342 rb->lcd_setfont(FONT_SYSFIXED);
1344 return false;
1347 static bool _ingame;
1348 static int jewels_menu_cb(int action, const struct menu_item_ex *this_item)
1350 int i = ((intptr_t)this_item);
1351 if(action == ACTION_REQUEST_MENUITEM
1352 && !_ingame && (i==0 || i==6))
1353 return ACTION_EXIT_MENUITEM;
1354 return action;
1356 /*****************************************************************************
1357 * jewels_game_menu() shows the game menu.
1358 ******************************************************************************/
1359 static int jewels_game_menu(struct game_context* bj, bool ingame)
1361 rb->button_clear_queue();
1362 int choice = 0;
1364 _ingame = ingame;
1366 static struct opt_items mode[] = {
1367 { "Normal", -1 },
1368 { "Puzzle", -1 },
1371 MENUITEM_STRINGLIST (main_menu, "Jewels Menu", jewels_menu_cb,
1372 "Resume Game",
1373 "Start New Game",
1374 "Mode",
1375 "Help",
1376 "High Scores",
1377 "Playback Control",
1378 "Quit without Saving",
1379 "Quit");
1381 while (1) {
1382 switch (rb->do_menu(&main_menu, &choice, NULL, false)) {
1383 case 0:
1384 jewels_setcolors();
1385 if(resume_file)
1386 rb->remove(SAVE_FILE);
1387 return 0;
1388 case 1:
1389 jewels_init(bj);
1390 return 0;
1391 case 2:
1392 rb->set_option("Mode", &bj->tmp_type, INT, mode, 2, NULL);
1393 break;
1394 case 3:
1395 if(jewels_help())
1396 return 1;
1397 break;
1398 case 4:
1399 highscore_show(NUM_SCORES, highest, NUM_SCORES, true);
1400 break;
1401 case 5:
1402 playback_control(NULL);
1403 break;
1404 case 6:
1405 return 1;
1406 case 7:
1407 if (ingame) {
1408 rb->splash(HZ*1, "Saving game ...");
1409 jewels_savegame(bj);
1411 return 1;
1412 case MENU_ATTACHED_USB:
1413 return 1;
1414 default:
1415 break;
1420 static int jewels_main(struct game_context* bj) {
1421 int button;
1422 int position;
1423 bool selected = false;
1424 bool no_movesavail;
1425 int x=0, y=0;
1427 bool loaded = jewels_loadgame(bj);
1428 resume_file = loaded;
1429 if (jewels_game_menu(bj, loaded)!=0)
1430 return 0;
1432 resume_file = false;
1433 while(true) {
1434 no_movesavail = false;
1436 /* refresh the board */
1437 jewels_drawboard(bj);
1439 /* display the cursor */
1440 if(selected) {
1441 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
1442 rb->lcd_fillrect(x*TILE_WIDTH, y*TILE_HEIGHT+YOFS,
1443 TILE_WIDTH, TILE_HEIGHT);
1444 rb->lcd_set_drawmode(DRMODE_SOLID);
1445 } else {
1446 rb->lcd_drawrect(x*TILE_WIDTH, y*TILE_HEIGHT+YOFS,
1447 TILE_WIDTH, TILE_HEIGHT);
1449 rb->lcd_update_rect(x*TILE_WIDTH, y*TILE_HEIGHT+YOFS,
1450 TILE_WIDTH, TILE_HEIGHT);
1452 /* handle game button presses */
1453 rb->yield();
1454 button = rb->button_get(true);
1455 switch(button){
1456 case JEWELS_LEFT: /* move cursor left */
1457 case (JEWELS_LEFT|BUTTON_REPEAT):
1458 if(selected) {
1459 bj->score += jewels_swapjewels(bj, x, y, SWAP_LEFT);
1460 selected = false;
1461 if (!jewels_movesavail(bj)) no_movesavail = true;
1462 } else {
1463 x = (x+BJ_WIDTH-1)%BJ_WIDTH;
1465 break;
1467 case JEWELS_RIGHT: /* move cursor right */
1468 case (JEWELS_RIGHT|BUTTON_REPEAT):
1469 if(selected) {
1470 bj->score += jewels_swapjewels(bj, x, y, SWAP_RIGHT);
1471 selected = false;
1472 if (!jewels_movesavail(bj)) no_movesavail = true;
1473 } else {
1474 x = (x+1)%BJ_WIDTH;
1476 break;
1478 case JEWELS_DOWN: /* move cursor down */
1479 case (JEWELS_DOWN|BUTTON_REPEAT):
1480 if(selected) {
1481 bj->score += jewels_swapjewels(bj, x, y, SWAP_DOWN);
1482 selected = false;
1483 if (!jewels_movesavail(bj)) no_movesavail = true;
1484 } else {
1485 y = (y+1)%(BJ_HEIGHT-1);
1487 break;
1489 case JEWELS_UP: /* move cursor up */
1490 case (JEWELS_UP|BUTTON_REPEAT):
1491 if(selected) {
1492 bj->score += jewels_swapjewels(bj, x, y, SWAP_UP);
1493 selected = false;
1494 if (!jewels_movesavail(bj)) no_movesavail = true;
1495 } else {
1496 y = (y+(BJ_HEIGHT-1)-1)%(BJ_HEIGHT-1);
1498 break;
1500 #ifdef JEWELS_SCROLLWHEEL
1501 case JEWELS_PREV: /* scroll backwards */
1502 case (JEWELS_PREV|BUTTON_REPEAT):
1503 if(!selected) {
1504 if(x == 0) {
1505 y = (y+(BJ_HEIGHT-1)-1)%(BJ_HEIGHT-1);
1507 x = (x+BJ_WIDTH-1)%BJ_WIDTH;
1509 break;
1511 case JEWELS_NEXT: /* scroll forwards */
1512 case (JEWELS_NEXT|BUTTON_REPEAT):
1513 if(!selected) {
1514 if(x == BJ_WIDTH-1) {
1515 y = (y+1)%(BJ_HEIGHT-1);
1517 x = (x+1)%BJ_WIDTH;
1519 break;
1520 #endif
1522 case JEWELS_SELECT: /* toggle selected */
1523 selected = !selected;
1524 break;
1526 #ifdef JEWELS_RC_CANCEL
1527 case JEWELS_RC_CANCEL:
1528 #endif
1529 case JEWELS_CANCEL: /* end game */
1530 if (jewels_game_menu(bj, true)!=0)
1531 return 0;
1532 break;
1534 default:
1535 if (rb->default_event_handler (button) == SYS_USB_CONNECTED)
1536 return PLUGIN_USB_CONNECTED;
1537 break;
1540 switch(bj->type) {
1541 case GAME_TYPE_NORMAL:
1542 if(bj->score >= LEVEL_PTS)
1543 jewels_nextlevel(bj);
1544 break;
1545 case GAME_TYPE_PUZZLE:
1546 if (jewels_puzzle_is_finished(bj)) {
1547 if (bj->level < NUM_PUZZLE_LEVELS) {
1548 jewels_nextlevel(bj);
1549 } else {
1550 rb->splash(2*HZ, "Congratulations!");
1551 rb->splash(2*HZ, "You have finished the game!");
1552 if (jewels_game_menu(bj, false)!=0) {
1553 return 0;
1556 break;
1560 if (no_movesavail) {
1561 switch(bj->type) {
1562 case GAME_TYPE_NORMAL:
1563 rb->splash(HZ*2, "Game Over!");
1564 rb->lcd_clear_display();
1565 bj->score += (bj->level-1)*LEVEL_PTS;
1566 position=highscore_update(bj->score, bj->level, "",
1567 highest, NUM_SCORES);
1568 if (position == 0)
1569 rb->splash(HZ*2, "New High Score");
1570 if (position != -1)
1571 highscore_show(position, highest, NUM_SCORES, true);
1572 break;
1573 case GAME_TYPE_PUZZLE:
1574 rb->splash(2*HZ, "Game Over");
1575 break;
1577 if (jewels_game_menu(bj, false)!=0) {
1578 return 0;
1584 /* this is the plugin entry point */
1585 enum plugin_status plugin_start(const void* parameter)
1587 (void)parameter;
1589 /* load high scores */
1590 highscore_load(HIGH_SCORE,highest,NUM_SCORES);
1592 rb->lcd_setfont(FONT_SYSFIXED);
1593 #if LCD_DEPTH > 1
1594 rb->lcd_set_backdrop(NULL);
1595 #endif
1597 struct game_context bj;
1598 bj.tmp_type = GAME_TYPE_NORMAL;
1599 jewels_main(&bj);
1600 highscore_save(HIGH_SCORE,highest,NUM_SCORES);
1601 rb->lcd_setfont(FONT_UI);
1603 return PLUGIN_OK;
1606 #endif /* HAVE_LCD_BITMAP */