use lib display text to display help messages (FS#10099).
[kugel-rb.git] / apps / plugins / jewels.c
blobf19c63fd24ed4421a818912ee0fe790212374558
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"
29 #ifdef HAVE_LCD_BITMAP
31 PLUGIN_HEADER
33 /* button definitions */
34 #if CONFIG_KEYPAD == RECORDER_PAD
35 #define JEWELS_UP BUTTON_UP
36 #define JEWELS_DOWN BUTTON_DOWN
37 #define JEWELS_LEFT BUTTON_LEFT
38 #define JEWELS_RIGHT BUTTON_RIGHT
39 #define JEWELS_SELECT BUTTON_PLAY
40 #define JEWELS_CANCEL BUTTON_OFF
41 #define HK_SELECT "PLAY"
42 #define HK_CANCEL "OFF"
44 #elif CONFIG_KEYPAD == ARCHOS_AV300_PAD
45 #define JEWELS_UP BUTTON_UP
46 #define JEWELS_DOWN BUTTON_DOWN
47 #define JEWELS_LEFT BUTTON_LEFT
48 #define JEWELS_RIGHT BUTTON_RIGHT
49 #define JEWELS_SELECT BUTTON_SELECT
50 #define JEWELS_CANCEL BUTTON_OFF
51 #define HK_SELECT "SELECT"
52 #define HK_CANCEL "OFF"
54 #elif CONFIG_KEYPAD == ONDIO_PAD
55 #define JEWELS_UP BUTTON_UP
56 #define JEWELS_DOWN BUTTON_DOWN
57 #define JEWELS_LEFT BUTTON_LEFT
58 #define JEWELS_RIGHT BUTTON_RIGHT
59 #define JEWELS_SELECT BUTTON_MENU
60 #define JEWELS_CANCEL BUTTON_OFF
61 #define HK_SELECT "MENU"
62 #define HK_CANCEL "OFF"
64 #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD)
65 #define JEWELS_UP BUTTON_UP
66 #define JEWELS_DOWN BUTTON_DOWN
67 #define JEWELS_LEFT BUTTON_LEFT
68 #define JEWELS_RIGHT BUTTON_RIGHT
69 #define JEWELS_SELECT BUTTON_SELECT
70 #define JEWELS_CANCEL BUTTON_OFF
71 #define JEWELS_RC_CANCEL BUTTON_RC_STOP
72 #define HK_SELECT "SELECT"
73 #define HK_CANCEL "OFF"
75 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
76 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
77 #define JEWELS_SCROLLWHEEL
78 #define JEWELS_UP BUTTON_MENU
79 #define JEWELS_DOWN BUTTON_PLAY
80 #define JEWELS_LEFT BUTTON_LEFT
81 #define JEWELS_RIGHT BUTTON_RIGHT
82 #define JEWELS_PREV BUTTON_SCROLL_BACK
83 #define JEWELS_NEXT BUTTON_SCROLL_FWD
84 #define JEWELS_SELECT BUTTON_SELECT
85 #define JEWELS_CANCEL (BUTTON_SELECT | BUTTON_MENU)
86 #define HK_SELECT "SELECT"
87 #define HK_CANCEL "SEL + MENU"
89 #elif (CONFIG_KEYPAD == IPOD_3G_PAD)
90 #define JEWELS_LEFT BUTTON_LEFT
91 #define JEWELS_RIGHT BUTTON_RIGHT
92 #define JEWELS_UP BUTTON_SCROLL_BACK
93 #define JEWELS_DOWN BUTTON_SCROLL_FWD
94 #define JEWELS_SELECT BUTTON_SELECT
95 #define JEWELS_CANCEL BUTTON_MENU
96 #define HK_SELECT "SELECT"
97 #define HK_CANCEL "MENU"
99 #elif CONFIG_KEYPAD == IRIVER_IFP7XX_PAD
100 #define JEWELS_UP BUTTON_UP
101 #define JEWELS_DOWN BUTTON_DOWN
102 #define JEWELS_LEFT BUTTON_LEFT
103 #define JEWELS_RIGHT BUTTON_RIGHT
104 #define JEWELS_SELECT BUTTON_SELECT
105 #define JEWELS_CANCEL BUTTON_PLAY
106 #define HK_SELECT "SELECT"
107 #define HK_CANCEL "PLAY"
109 #elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
110 #define JEWELS_UP BUTTON_UP
111 #define JEWELS_DOWN BUTTON_DOWN
112 #define JEWELS_LEFT BUTTON_LEFT
113 #define JEWELS_RIGHT BUTTON_RIGHT
114 #define JEWELS_SELECT BUTTON_SELECT
115 #define JEWELS_CANCEL BUTTON_POWER
116 #define HK_SELECT "SELECT"
117 #define HK_CANCEL "POWER"
119 #elif CONFIG_KEYPAD == GIGABEAT_PAD
120 #define JEWELS_UP BUTTON_UP
121 #define JEWELS_DOWN BUTTON_DOWN
122 #define JEWELS_LEFT BUTTON_LEFT
123 #define JEWELS_RIGHT BUTTON_RIGHT
124 #define JEWELS_SELECT BUTTON_SELECT
125 #define JEWELS_CANCEL BUTTON_POWER
126 #define HK_SELECT "SELECT"
127 #define HK_CANCEL "POWER"
129 #elif CONFIG_KEYPAD == SANSA_E200_PAD
130 #define JEWELS_SCROLLWHEEL
131 #define JEWELS_UP BUTTON_UP
132 #define JEWELS_DOWN BUTTON_DOWN
133 #define JEWELS_LEFT BUTTON_LEFT
134 #define JEWELS_RIGHT BUTTON_RIGHT
135 #define JEWELS_PREV BUTTON_SCROLL_BACK
136 #define JEWELS_NEXT BUTTON_SCROLL_FWD
137 #define JEWELS_SELECT BUTTON_SELECT
138 #define JEWELS_CANCEL BUTTON_POWER
139 #define HK_SELECT "SELECT"
140 #define HK_CANCEL "POWER"
142 #elif (CONFIG_KEYPAD == SANSA_FUZE_PAD)
143 #define JEWELS_SCROLLWHEEL
144 #define JEWELS_UP BUTTON_UP
145 #define JEWELS_DOWN BUTTON_DOWN
146 #define JEWELS_LEFT BUTTON_LEFT
147 #define JEWELS_RIGHT BUTTON_RIGHT
148 #define JEWELS_PREV BUTTON_SCROLL_BACK
149 #define JEWELS_NEXT BUTTON_SCROLL_FWD
150 #define JEWELS_SELECT BUTTON_SELECT
151 #define JEWELS_CANCEL (BUTTON_HOME|BUTTON_REPEAT)
152 #define HK_SELECT "SELECT"
153 #define HK_CANCEL "HOME"
155 #elif CONFIG_KEYPAD == SANSA_C200_PAD || \
156 CONFIG_KEYPAD == SANSA_CLIP_PAD || \
157 CONFIG_KEYPAD == SANSA_M200_PAD
158 #define JEWELS_UP BUTTON_UP
159 #define JEWELS_DOWN BUTTON_DOWN
160 #define JEWELS_LEFT BUTTON_LEFT
161 #define JEWELS_RIGHT BUTTON_RIGHT
162 #define JEWELS_SELECT BUTTON_SELECT
163 #define JEWELS_CANCEL BUTTON_POWER
164 #define HK_SELECT "SELECT"
165 #define HK_CANCEL "POWER"
167 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
168 #define JEWELS_UP BUTTON_SCROLL_UP
169 #define JEWELS_DOWN BUTTON_SCROLL_DOWN
170 #define JEWELS_LEFT BUTTON_LEFT
171 #define JEWELS_RIGHT BUTTON_RIGHT
172 #define JEWELS_SELECT BUTTON_PLAY
173 #define JEWELS_CANCEL BUTTON_POWER
174 #define HK_SELECT "PLAY"
175 #define HK_CANCEL "POWER"
177 #elif CONFIG_KEYPAD == GIGABEAT_S_PAD
178 #define JEWELS_UP BUTTON_UP
179 #define JEWELS_DOWN BUTTON_DOWN
180 #define JEWELS_LEFT BUTTON_LEFT
181 #define JEWELS_RIGHT BUTTON_RIGHT
182 #define JEWELS_SELECT BUTTON_SELECT
183 #define JEWELS_CANCEL BUTTON_BACK
184 #define HK_SELECT "SELECT"
185 #define HK_CANCEL "BACK"
187 #elif CONFIG_KEYPAD == MROBE100_PAD
188 #define JEWELS_UP BUTTON_UP
189 #define JEWELS_DOWN BUTTON_DOWN
190 #define JEWELS_LEFT BUTTON_LEFT
191 #define JEWELS_RIGHT BUTTON_RIGHT
192 #define JEWELS_SELECT BUTTON_SELECT
193 #define JEWELS_CANCEL BUTTON_POWER
194 #define HK_SELECT "SELECT"
195 #define HK_CANCEL "POWER"
197 #elif CONFIG_KEYPAD == IAUDIO_M3_PAD
198 #define JEWELS_UP BUTTON_RC_VOL_UP
199 #define JEWELS_DOWN BUTTON_RC_VOL_DOWN
200 #define JEWELS_LEFT BUTTON_RC_REW
201 #define JEWELS_RIGHT BUTTON_RC_FF
202 #define JEWELS_SELECT BUTTON_RC_PLAY
203 #define JEWELS_CANCEL BUTTON_RC_REC
204 #define HK_SELECT "PLAY"
205 #define HK_CANCEL "REC"
207 #define JEWELS_RC_CANCEL BUTTON_REC
209 #elif CONFIG_KEYPAD == COWOND2_PAD
210 #define JEWELS_CANCEL BUTTON_POWER
211 #define HK_CANCEL "POWER"
213 #elif CONFIG_KEYPAD == IAUDIO67_PAD
214 #define JEWELS_UP BUTTON_STOP
215 #define JEWELS_DOWN BUTTON_PLAY
216 #define JEWELS_LEFT BUTTON_LEFT
217 #define JEWELS_RIGHT BUTTON_RIGHT
218 #define JEWELS_SELECT BUTTON_MENU
219 #define JEWELS_CANCEL BUTTON_POWER
220 #define HK_SELECT "MENU"
221 #define HK_CANCEL "POWER"
223 #elif CONFIG_KEYPAD == CREATIVEZVM_PAD
224 #define JEWELS_UP BUTTON_UP
225 #define JEWELS_DOWN BUTTON_DOWN
226 #define JEWELS_LEFT BUTTON_LEFT
227 #define JEWELS_RIGHT BUTTON_RIGHT
228 #define JEWELS_SELECT BUTTON_SELECT
229 #define JEWELS_CANCEL BUTTON_BACK
230 #define HK_SELECT "MIDDLE"
231 #define HK_CANCEL "BACK"
233 #elif CONFIG_KEYPAD == PHILIPS_HDD1630_PAD
234 #define JEWELS_UP BUTTON_UP
235 #define JEWELS_DOWN BUTTON_DOWN
236 #define JEWELS_LEFT BUTTON_LEFT
237 #define JEWELS_RIGHT BUTTON_RIGHT
238 #define JEWELS_SELECT BUTTON_SELECT
239 #define JEWELS_CANCEL BUTTON_POWER
240 #define HK_SELECT "SELECT"
241 #define HK_CANCEL "POWER"
243 #elif CONFIG_KEYPAD == ONDAVX747_PAD || \
244 CONFIG_KEYPAD == MROBE500_PAD
245 #define JEWELS_CANCEL BUTTON_POWER
246 #define HK_CANCEL "POWER"
248 #else
249 #error No keymap defined!
250 #endif
252 #ifdef HAVE_TOUCHSCREEN
253 #ifndef JEWELS_UP
254 #define JEWELS_UP BUTTON_TOPMIDDLE
255 #endif
256 #ifndef JEWELS_DOWN
257 #define JEWELS_DOWN BUTTON_BOTTOMMIDDLE
258 #endif
259 #ifndef JEWELS_LEFT
260 #define JEWELS_LEFT BUTTON_MIDLEFT
261 #endif
262 #ifndef JEWELS_RIGHT
263 #define JEWELS_RIGHT BUTTON_MIDRIGHT
264 #endif
265 #ifndef JEWELS_SELECT
266 #define JEWELS_SELECT BUTTON_CENTER
267 #define HK_SELECT "CENTER"
268 #endif
269 #ifndef JEWELS_CANCEL
270 #define JEWELS_CANCEL BUTTON_TOPLEFT
271 #define HK_CANCEL "TOPLEFT"
272 #endif
273 #endif
275 /* use 30x30 tiles (iPod Video, Gigabeat, Onda VX747) */
276 #if (LCD_HEIGHT == 240) && (LCD_WIDTH == 320) || \
277 ((LCD_HEIGHT == 320) && (LCD_WIDTH == 240)) || \
278 ((LCD_HEIGHT == 400) && (LCD_WIDTH == 240))
279 #define TILE_WIDTH 30
280 #define TILE_HEIGHT 30
281 #define YOFS 0
282 #define NUM_SCORES 10
284 /* use 22x22 tiles (H300, iPod Color) */
285 #elif ((LCD_HEIGHT == 176) && (LCD_WIDTH == 220)) || \
286 ((LCD_HEIGHT == 220) && (LCD_WIDTH == 176))
287 #define TILE_WIDTH 22
288 #define TILE_HEIGHT 22
289 #define YOFS 0
290 #define NUM_SCORES 5
292 /* use 16x16 tiles (iPod Nano) */
293 #elif (LCD_HEIGHT == 132) && (LCD_WIDTH == 176)
294 #define TILE_WIDTH 16
295 #define TILE_HEIGHT 16
296 #define YOFS 4
297 #define NUM_SCORES 5
299 /* use 16x16 tiles (H100, iAudio X5, iPod 3G, iPod 4G grayscale) */
300 #elif (LCD_HEIGHT == 128) && (LCD_WIDTH == 160)
301 #define TILE_WIDTH 16
302 #define TILE_HEIGHT 16
303 #define YOFS 0
304 #define NUM_SCORES 5
306 /* use 14x14 tiles (H10 5/6 GB) */
307 #elif (LCD_HEIGHT == 128) && (LCD_WIDTH == 128)
308 #define TILE_WIDTH 14
309 #define TILE_HEIGHT 14
310 #define YOFS 0
311 #define NUM_SCORES 5
313 /* use 13x13 tiles (iPod Mini) */
314 #elif (LCD_HEIGHT == 110) && (LCD_WIDTH == 138)
315 #define TILE_WIDTH 13
316 #define TILE_HEIGHT 13
317 #define YOFS 6
318 #define NUM_SCORES 5
320 /* use 12x12 tiles (iAudio M3) */
321 #elif (LCD_HEIGHT == 96) && (LCD_WIDTH == 128)
322 #define TILE_WIDTH 12
323 #define TILE_HEIGHT 12
324 #define YOFS 0
325 #define NUM_SCORES 5
327 /* use 10x10 tiles (Sansa c200) */
328 #elif (LCD_HEIGHT == 80) && (LCD_WIDTH == 132)
329 #define TILE_WIDTH 10
330 #define TILE_HEIGHT 10
331 #define YOFS 0
332 #define NUM_SCORES 5
334 /* use 10x8 tiles (iFP 700) */
335 #elif (LCD_HEIGHT == 64) && (LCD_WIDTH == 128)
336 #define TILE_WIDTH 10
337 #define TILE_HEIGHT 8
338 #define YOFS 0
339 #define NUM_SCORES 5
341 /* use 10x8 tiles (Recorder, Ondio) */
342 #elif (LCD_HEIGHT == 64) && (LCD_WIDTH == 112)
343 #define TILE_WIDTH 10
344 #define TILE_HEIGHT 8
345 #define YOFS 0
346 #define NUM_SCORES 5
348 #else
349 #error JEWELS: Unsupported LCD
350 #endif
352 /* swap directions */
353 #define SWAP_UP 0
354 #define SWAP_RIGHT 1
355 #define SWAP_DOWN 2
356 #define SWAP_LEFT 3
358 /* play board dimension */
359 #define BJ_HEIGHT 9
360 #define BJ_WIDTH 8
362 /* next level threshold */
363 #define LEVEL_PTS 100
365 /* animation frame rate */
366 #define MAX_FPS 20
368 /* text margin */
369 #define MARGIN 5
371 /* Game types */
372 enum game_type {
373 GAME_TYPE_NORMAL,
374 GAME_TYPE_PUZZLE
377 /* external bitmaps */
378 extern const fb_data jewels[];
380 /* tile background colors */
381 #ifdef HAVE_LCD_COLOR
382 static const unsigned jewels_bkgd[2] = {
383 LCD_RGBPACK(104, 63, 63),
384 LCD_RGBPACK(83, 44, 44)
386 #endif
388 /* the tile struct
389 * type is the jewel number 0-7
390 * falling if the jewel is falling
391 * delete marks the jewel for deletion
393 struct tile {
394 int type;
395 bool falling;
396 bool delete;
399 /* the game context struct
400 * score is the current level score
401 * segments is the number of cleared segments in the current run
402 * level is the current level
403 * tmp_type is the select type in the menu
404 * type is the game type (normal or puzzle)
405 * playboard is the game playing board (first row is hidden)
406 * num_jewels is the number of different jewels to use
408 struct game_context {
409 unsigned int score;
410 unsigned int segments;
411 unsigned int level;
412 unsigned int type;
413 unsigned int tmp_type;
414 struct tile playboard[BJ_HEIGHT][BJ_WIDTH];
415 unsigned int num_jewels;
418 #define MAX_NUM_JEWELS 7
420 #define MAX_PUZZLE_TILES 4
421 #define NUM_PUZZLE_LEVELS 10
423 struct puzzle_tile {
424 int x;
425 int y;
426 int tile_type;
429 struct puzzle_level {
430 unsigned int num_jewels;
431 unsigned int num_tiles;
432 struct puzzle_tile tiles[MAX_PUZZLE_TILES];
435 #define PUZZLE_TILE_UP 1
436 #define PUZZLE_TILE_DOWN 2
437 #define PUZZLE_TILE_LEFT 4
438 #define PUZZLE_TILE_RIGHT 8
440 struct puzzle_level puzzle_levels[NUM_PUZZLE_LEVELS] = {
441 { 5, 2, { {3, 3, PUZZLE_TILE_RIGHT},
442 {4, 2, PUZZLE_TILE_LEFT} } },
443 { 5, 2, { {3, 2, PUZZLE_TILE_DOWN},
444 {3, 4, PUZZLE_TILE_UP} } },
445 { 6, 3, { {3, 2, PUZZLE_TILE_DOWN},
446 {3, 4, PUZZLE_TILE_UP|PUZZLE_TILE_DOWN},
447 {3, 6, PUZZLE_TILE_UP} } },
448 { 6, 3, { {3, 2, PUZZLE_TILE_RIGHT},
449 {4, 3, PUZZLE_TILE_LEFT|PUZZLE_TILE_RIGHT},
450 {5, 4, PUZZLE_TILE_LEFT} } },
451 { 6, 2, { {3, 4, PUZZLE_TILE_RIGHT},
452 {4, 2, PUZZLE_TILE_LEFT} } },
453 { 6, 2, { {3, 2, PUZZLE_TILE_DOWN},
454 {4, 4, PUZZLE_TILE_UP} } },
455 { 7, 4, { {3, 2, PUZZLE_TILE_RIGHT|PUZZLE_TILE_DOWN},
456 {4, 3, PUZZLE_TILE_LEFT|PUZZLE_TILE_DOWN},
457 {3, 4, PUZZLE_TILE_RIGHT|PUZZLE_TILE_UP},
458 {4, 4, PUZZLE_TILE_LEFT|PUZZLE_TILE_UP} } },
459 { 6, 3, { {3, 2, PUZZLE_TILE_DOWN},
460 {4, 4, PUZZLE_TILE_UP|PUZZLE_TILE_DOWN},
461 {3, 6, PUZZLE_TILE_UP} } },
462 { 7, 3, { {2, 2, PUZZLE_TILE_RIGHT},
463 {4, 1, PUZZLE_TILE_LEFT|PUZZLE_TILE_RIGHT},
464 {5, 4, PUZZLE_TILE_LEFT} } },
465 { 7, 4, { {3, 0, PUZZLE_TILE_RIGHT|PUZZLE_TILE_DOWN},
466 {5, 0, PUZZLE_TILE_LEFT|PUZZLE_TILE_DOWN},
467 {2, 7, PUZZLE_TILE_RIGHT|PUZZLE_TILE_UP},
468 {4, 7, PUZZLE_TILE_LEFT|PUZZLE_TILE_UP} } },
471 #define SAVE_FILE PLUGIN_GAMES_DIR "/jewels.save"
473 #define HIGH_SCORE PLUGIN_GAMES_DIR "/jewels.score"
474 struct highscore highest[NUM_SCORES];
475 bool highest_updated = false;
478 /*****************************************************************************
479 * jewels_setcolors() set the foreground and background colors.
480 ******************************************************************************/
481 static inline void jewels_setcolors(void) {
482 #ifdef HAVE_LCD_COLOR
483 rb->lcd_set_background(LCD_RGBPACK(49, 26, 26));
484 rb->lcd_set_foreground(LCD_RGBPACK(210, 181, 181));
485 #endif
488 /*****************************************************************************
489 * jewels_loadgame() loads the saved game and returns load success.
490 ******************************************************************************/
491 static bool jewels_loadgame(struct game_context* bj)
493 int fd;
494 bool loaded = false;
496 /* open game file */
497 fd = rb->open(SAVE_FILE, O_RDONLY);
498 if(fd < 0) return loaded;
500 /* read in saved game */
501 while(true) {
502 if(rb->read(fd, &bj->tmp_type, sizeof(bj->tmp_type)) <= 0) break;
503 if(rb->read(fd, &bj->type, sizeof(bj->type)) <= 0) break;
504 if(rb->read(fd, &bj->score, sizeof(bj->score)) <= 0) break;
505 if(rb->read(fd, &bj->level, sizeof(bj->level)) <= 0) break;
506 if(rb->read(fd, &bj->segments, sizeof(bj->segments)) <= 0) break;
507 if(rb->read(fd, &bj->num_jewels, sizeof(bj->num_jewels)) <= 0) break;
508 if(rb->read(fd, bj->playboard, sizeof(bj->playboard)) <= 0) break;
509 loaded = true;
510 break;
513 rb->close(fd);
515 /* delete saved file */
516 rb->remove(SAVE_FILE);
517 return loaded;
520 /*****************************************************************************
521 * jewels_savegame() saves the current game state.
522 ******************************************************************************/
523 static void jewels_savegame(struct game_context* bj)
525 int fd;
526 /* write out the game state to the save file */
527 fd = rb->open(SAVE_FILE, O_WRONLY|O_CREAT);
528 rb->write(fd, &bj->tmp_type, sizeof(bj->tmp_type));
529 rb->write(fd, &bj->type, sizeof(bj->type));
530 rb->write(fd, &bj->score, sizeof(bj->score));
531 rb->write(fd, &bj->level, sizeof(bj->level));
532 rb->write(fd, &bj->segments, sizeof(bj->segments));
533 rb->write(fd, &bj->num_jewels, sizeof(bj->num_jewels));
534 rb->write(fd, bj->playboard, sizeof(bj->playboard));
535 rb->close(fd);
538 /*****************************************************************************
539 * jewels_drawboard() redraws the entire game board.
540 ******************************************************************************/
541 static void jewels_drawboard(struct game_context* bj) {
542 int i, j;
543 int w, h;
544 unsigned int tempscore;
545 char *title = "Level";
546 char str[10];
548 tempscore = (bj->score>LEVEL_PTS ? LEVEL_PTS : bj->score);
550 /* clear screen */
551 rb->lcd_clear_display();
553 /* dispay playing board */
554 for(i=0; i<BJ_HEIGHT-1; i++){
555 for(j=0; j<BJ_WIDTH; j++){
556 #ifdef HAVE_LCD_COLOR
557 rb->lcd_set_foreground(jewels_bkgd[(i+j)%2]);
558 rb->lcd_fillrect(j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
559 TILE_WIDTH, TILE_HEIGHT);
560 rb->lcd_bitmap_transparent_part(jewels,
561 0, TILE_HEIGHT*(bj->playboard[i+1][j].type),
562 TILE_WIDTH, j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
563 TILE_WIDTH, TILE_HEIGHT);
564 #else
565 rb->lcd_bitmap_part(jewels,
566 0, TILE_HEIGHT*(bj->playboard[i+1][j].type),
567 TILE_WIDTH, j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
568 TILE_WIDTH, TILE_HEIGHT);
569 #endif
573 #if LCD_WIDTH > LCD_HEIGHT /* horizontal layout */
575 /* draw separator lines */
576 jewels_setcolors();
577 rb->lcd_vline(BJ_WIDTH*TILE_WIDTH, 0, LCD_HEIGHT-1);
578 rb->lcd_hline(BJ_WIDTH*TILE_WIDTH, LCD_WIDTH-1, 18);
579 rb->lcd_hline(BJ_WIDTH*TILE_WIDTH, LCD_WIDTH-1, LCD_HEIGHT-10);
581 /* draw progress bar */
582 #ifdef HAVE_LCD_COLOR
583 rb->lcd_set_foreground(LCD_RGBPACK(104, 63, 63));
584 #endif
585 rb->lcd_fillrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4,
586 (LCD_HEIGHT-10)-(((LCD_HEIGHT-10)-18)*
587 tempscore/LEVEL_PTS),
588 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2,
589 ((LCD_HEIGHT-10)-18)*tempscore/LEVEL_PTS);
590 #ifdef HAVE_LCD_COLOR
591 rb->lcd_set_foreground(LCD_RGBPACK(83, 44, 44));
592 rb->lcd_drawrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4+1,
593 (LCD_HEIGHT-10)-(((LCD_HEIGHT-10)-18)*
594 tempscore/LEVEL_PTS)+1,
595 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-2,
596 ((LCD_HEIGHT-10)-18)*tempscore/LEVEL_PTS-1);
597 jewels_setcolors();
598 rb->lcd_drawrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4,
599 (LCD_HEIGHT-10)-(((LCD_HEIGHT-10)-18)*
600 tempscore/LEVEL_PTS),
601 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2,
602 ((LCD_HEIGHT-10)-18)*tempscore/LEVEL_PTS+1);
603 #endif
605 /* print text */
606 rb->lcd_getstringsize(title, &w, &h);
607 rb->lcd_putsxy(LCD_WIDTH-(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-w/2, 1, title);
609 rb->snprintf(str, 4, "%d", bj->level);
610 rb->lcd_getstringsize(str, &w, &h);
611 rb->lcd_putsxy(LCD_WIDTH-(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-w/2, 10, str);
613 rb->snprintf(str, 6, "%d", (bj->level-1)*LEVEL_PTS+bj->score);
614 rb->lcd_getstringsize(str, &w, &h);
615 rb->lcd_putsxy(LCD_WIDTH-(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-w/2,
616 LCD_HEIGHT-8, str);
618 #elif LCD_WIDTH < LCD_HEIGHT /* vertical layout */
620 /* draw separator lines */
621 jewels_setcolors();
622 rb->lcd_hline(0, LCD_WIDTH-1, 8*TILE_HEIGHT+YOFS);
623 rb->lcd_hline(0, LCD_WIDTH-1, LCD_HEIGHT-14);
624 rb->lcd_vline(LCD_WIDTH/2, LCD_HEIGHT-14, LCD_HEIGHT-1);
626 /* draw progress bar */
627 #ifdef HAVE_LCD_COLOR
628 rb->lcd_set_foreground(LCD_RGBPACK(104, 63, 63));
629 #endif
630 rb->lcd_fillrect(0, (8*TILE_HEIGHT+YOFS)
631 +(LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/4,
632 LCD_WIDTH*tempscore/LEVEL_PTS,
633 (LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/2);
634 #ifdef HAVE_LCD_COLOR
635 rb->lcd_set_foreground(LCD_RGBPACK(83, 44, 44));
636 rb->lcd_drawrect(1, (8*TILE_HEIGHT+YOFS)
637 +(LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/4+1,
638 LCD_WIDTH*tempscore/LEVEL_PTS-1,
639 (LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/2-2);
640 jewels_setcolors();
641 rb->lcd_drawrect(0, (8*TILE_HEIGHT+YOFS)
642 +(LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/4,
643 LCD_WIDTH*tempscore/LEVEL_PTS+1,
644 (LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/2);
645 #endif
647 /* print text */
648 rb->snprintf(str, 10, "%s %d", title, bj->level);
649 rb->lcd_putsxy(1, LCD_HEIGHT-10, str);
651 rb->snprintf(str, 6, "%d", (bj->level-1)*LEVEL_PTS+bj->score);
652 rb->lcd_getstringsize(str, &w, &h);
653 rb->lcd_putsxy((LCD_WIDTH-2)-w, LCD_HEIGHT-10, str);
655 #else /* square layout */
657 /* draw separator lines */
658 jewels_setcolors();
659 rb->lcd_hline(0, LCD_WIDTH-1, 8*TILE_HEIGHT+YOFS);
660 rb->lcd_vline(BJ_WIDTH*TILE_WIDTH, 0, 8*TILE_HEIGHT+YOFS);
661 rb->lcd_vline(LCD_WIDTH/2, 8*TILE_HEIGHT+YOFS, LCD_HEIGHT-1);
663 /* draw progress bar */
664 #ifdef HAVE_LCD_COLOR
665 rb->lcd_set_foreground(LCD_RGBPACK(104, 63, 63));
666 #endif
667 rb->lcd_fillrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4,
668 (8*TILE_HEIGHT+YOFS)-(8*TILE_HEIGHT+YOFS)
669 *tempscore/LEVEL_PTS,
670 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2,
671 (8*TILE_HEIGHT+YOFS)*tempscore/LEVEL_PTS);
672 #ifdef HAVE_LCD_COLOR
673 rb->lcd_set_foreground(LCD_RGBPACK(83, 44, 44));
674 rb->lcd_drawrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4+1,
675 (8*TILE_HEIGHT+YOFS)-(8*TILE_HEIGHT+YOFS)
676 *tempscore/LEVEL_PTS+1,
677 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-2,
678 (8*TILE_HEIGHT+YOFS)*tempscore/LEVEL_PTS-1);
679 jewels_setcolors();
680 rb->lcd_drawrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4,
681 (8*TILE_HEIGHT+YOFS)-(8*TILE_HEIGHT+YOFS)
682 *tempscore/LEVEL_PTS,
683 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2,
684 (8*TILE_HEIGHT+YOFS)*tempscore/LEVEL_PTS+1);
685 #endif
687 /* print text */
688 rb->snprintf(str, 10, "%s %d", title, bj->level);
689 rb->lcd_putsxy(1, LCD_HEIGHT-(LCD_HEIGHT-(8*TILE_HEIGHT+YOFS))/2-3, str);
691 rb->snprintf(str, 6, "%d", (bj->level-1)*LEVEL_PTS+bj->score);
692 rb->lcd_getstringsize(str, &w, &h);
693 rb->lcd_putsxy((LCD_WIDTH-2)-w,
694 LCD_HEIGHT-(LCD_HEIGHT-(8*TILE_HEIGHT+YOFS))/2-3, str);
696 #endif /* layout */
698 rb->lcd_update();
701 /*****************************************************************************
702 * jewels_putjewels() makes the jewels fall to fill empty spots and adds
703 * new random jewels at the empty spots at the top of each row.
704 ******************************************************************************/
705 static void jewels_putjewels(struct game_context* bj){
706 int i, j, k;
707 bool mark, done;
708 long lasttick, currenttick;
710 /* loop to make all the jewels fall */
711 while(true) {
712 /* mark falling jewels and add new jewels to hidden top row*/
713 mark = false;
714 done = true;
715 for(j=0; j<BJ_WIDTH; j++) {
716 if(bj->playboard[1][j].type == 0) {
717 bj->playboard[0][j].type = rb->rand()%bj->num_jewels+1;
719 for(i=BJ_HEIGHT-2; i>=0; i--) {
720 if(!mark && bj->playboard[i+1][j].type == 0) {
721 mark = true;
722 done = false;
724 if(mark) bj->playboard[i][j].falling = true;
726 /*if(bj->playboard[1][j].falling) {
727 bj->playboard[0][j].type = rb->rand()%bj->num_jewels+1;
728 bj->playboard[0][j].falling = true;
730 mark = false;
733 /* break if there are no falling jewels */
734 if(done) break;
736 /* animate falling jewels */
737 lasttick = *rb->current_tick;
739 for(k=1; k<=8; k++) {
740 for(i=BJ_HEIGHT-2; i>=0; i--) {
741 for(j=0; j<BJ_WIDTH; j++) {
742 if(bj->playboard[i][j].falling &&
743 bj->playboard[i][j].type != 0) {
744 /* clear old position */
745 #ifdef HAVE_LCD_COLOR
746 if(i == 0 && YOFS) {
747 rb->lcd_set_foreground(rb->lcd_get_background());
748 } else {
749 rb->lcd_set_foreground(jewels_bkgd[(i-1+j)%2]);
751 rb->lcd_fillrect(j*TILE_WIDTH, (i-1)*TILE_HEIGHT+YOFS,
752 TILE_WIDTH, TILE_HEIGHT);
753 if(bj->playboard[i+1][j].type == 0) {
754 rb->lcd_set_foreground(jewels_bkgd[(i+j)%2]);
755 rb->lcd_fillrect(j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
756 TILE_WIDTH, TILE_HEIGHT);
758 #else
759 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
760 rb->lcd_fillrect(j*TILE_WIDTH, (i-1)*TILE_HEIGHT+YOFS,
761 TILE_WIDTH, TILE_HEIGHT);
762 if(bj->playboard[i+1][j].type == 0) {
763 rb->lcd_fillrect(j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
764 TILE_WIDTH, TILE_HEIGHT);
766 rb->lcd_set_drawmode(DRMODE_SOLID);
767 #endif
769 /* draw new position */
770 #ifdef HAVE_LCD_COLOR
771 rb->lcd_bitmap_transparent_part(jewels, 0,
772 TILE_HEIGHT*(bj->playboard[i][j].type),
773 TILE_WIDTH, j*TILE_WIDTH,
774 (i-1)*TILE_HEIGHT+YOFS+
775 ((((TILE_HEIGHT<<10)*k)/8)>>10),
776 TILE_WIDTH, TILE_HEIGHT);
777 #else
778 rb->lcd_bitmap_part(jewels, 0,
779 TILE_HEIGHT*(bj->playboard[i][j].type),
780 TILE_WIDTH, j*TILE_WIDTH,
781 (i-1)*TILE_HEIGHT+YOFS+
782 ((((TILE_HEIGHT<<10)*k)/8)>>10),
783 TILE_WIDTH, TILE_HEIGHT);
784 #endif
789 rb->lcd_update_rect(0, 0, TILE_WIDTH*8, LCD_HEIGHT);
790 jewels_setcolors();
792 /* framerate limiting */
793 currenttick = *rb->current_tick;
794 if(currenttick-lasttick < HZ/MAX_FPS) {
795 rb->sleep((HZ/MAX_FPS)-(currenttick-lasttick));
796 } else {
797 rb->yield();
799 lasttick = currenttick;
802 /* shift jewels down */
803 for(j=0; j<BJ_WIDTH; j++) {
804 for(i=BJ_HEIGHT-1; i>=1; i--) {
805 if(bj->playboard[i-1][j].falling) {
806 bj->playboard[i][j].type = bj->playboard[i-1][j].type;
811 /* clear out top row */
812 for(j=0; j<BJ_WIDTH; j++) {
813 bj->playboard[0][j].type = 0;
816 /* mark everything not falling */
817 for(i=0; i<BJ_HEIGHT; i++) {
818 for(j=0; j<BJ_WIDTH; j++) {
819 bj->playboard[i][j].falling = false;
825 /*****************************************************************************
826 * jewels_clearjewels() finds all the connected rows and columns and
827 * calculates and returns the points earned.
828 ******************************************************************************/
829 static unsigned int jewels_clearjewels(struct game_context* bj) {
830 int i, j;
831 int last, run;
832 unsigned int points = 0;
834 /* check for connected rows */
835 for(i=1; i<BJ_HEIGHT; i++) {
836 last = 0;
837 run = 1;
838 for(j=0; j<BJ_WIDTH; j++) {
839 if(bj->playboard[i][j].type == last &&
840 bj->playboard[i][j].type != 0 &&
841 bj->playboard[i][j].type <= MAX_NUM_JEWELS) {
842 run++;
844 if(run == 3) {
845 bj->segments++;
846 points += bj->segments;
847 bj->playboard[i][j].delete = true;
848 bj->playboard[i][j-1].delete = true;
849 bj->playboard[i][j-2].delete = true;
850 } else if(run > 3) {
851 points++;
852 bj->playboard[i][j].delete = true;
854 } else {
855 run = 1;
856 last = bj->playboard[i][j].type;
861 /* check for connected columns */
862 for(j=0; j<BJ_WIDTH; j++) {
863 last = 0;
864 run = 1;
865 for(i=1; i<BJ_HEIGHT; i++) {
866 if(bj->playboard[i][j].type != 0 &&
867 bj->playboard[i][j].type == last &&
868 bj->playboard[i][j].type <= MAX_NUM_JEWELS) {
869 run++;
871 if(run == 3) {
872 bj->segments++;
873 points += bj->segments;
874 bj->playboard[i][j].delete = true;
875 bj->playboard[i-1][j].delete = true;
876 bj->playboard[i-2][j].delete = true;
877 } else if(run > 3) {
878 points++;
879 bj->playboard[i][j].delete = true;
881 } else {
882 run = 1;
883 last = bj->playboard[i][j].type;
888 /* clear deleted jewels */
889 for(i=1; i<BJ_HEIGHT; i++) {
890 for(j=0; j<BJ_WIDTH; j++) {
891 if(bj->playboard[i][j].delete) {
892 bj->playboard[i][j].delete = false;
893 bj->playboard[i][j].type = 0;
898 return points;
901 /*****************************************************************************
902 * jewels_runboard() runs the board until it settles in a fixed state and
903 * returns points earned.
904 ******************************************************************************/
905 static unsigned int jewels_runboard(struct game_context* bj) {
906 unsigned int points = 0;
907 unsigned int ret;
909 bj->segments = 0;
911 while((ret = jewels_clearjewels(bj)) > 0) {
912 points += ret;
913 jewels_drawboard(bj);
914 jewels_putjewels(bj);
917 return points;
920 /*****************************************************************************
921 * jewels_swapjewels() swaps two jewels as long as it results in points and
922 * returns points earned.
923 ******************************************************************************/
924 static unsigned int jewels_swapjewels(struct game_context* bj,
925 int x, int y, int direc) {
926 int k;
927 int horzmod, vertmod;
928 int movelen = 0;
929 bool undo = false;
930 unsigned int points = 0;
931 long lasttick, currenttick;
933 /* check for invalid parameters */
934 if(x < 0 || x >= BJ_WIDTH || y < 0 || y >= BJ_HEIGHT-1 ||
935 direc < SWAP_UP || direc > SWAP_LEFT) {
936 return 0;
939 /* check for invalid directions */
940 if((x == 0 && direc == SWAP_LEFT) ||
941 (x == BJ_WIDTH-1 && direc == SWAP_RIGHT) ||
942 (y == 0 && direc == SWAP_UP) ||
943 (y == BJ_HEIGHT-2 && direc == SWAP_DOWN)) {
944 return 0;
947 /* set direction variables */
948 horzmod = 0;
949 vertmod = 0;
950 switch(direc) {
951 case SWAP_UP:
952 vertmod = -1;
953 movelen = TILE_HEIGHT;
954 break;
955 case SWAP_RIGHT:
956 horzmod = 1;
957 movelen = TILE_WIDTH;
958 break;
959 case SWAP_DOWN:
960 vertmod = 1;
961 movelen = TILE_HEIGHT;
962 break;
963 case SWAP_LEFT:
964 horzmod = -1;
965 movelen = TILE_WIDTH;
966 break;
969 while(true) {
970 lasttick = *rb->current_tick;
972 /* animate swapping jewels */
973 for(k=0; k<=8; k++) {
974 /* clear old position */
975 #ifdef HAVE_LCD_COLOR
976 rb->lcd_set_foreground(jewels_bkgd[(x+y)%2]);
977 rb->lcd_fillrect(x*TILE_WIDTH,
978 y*TILE_HEIGHT+YOFS,
979 TILE_WIDTH, TILE_HEIGHT);
980 rb->lcd_set_foreground(jewels_bkgd[(x+horzmod+y+vertmod)%2]);
981 rb->lcd_fillrect((x+horzmod)*TILE_WIDTH,
982 (y+vertmod)*TILE_HEIGHT+YOFS,
983 TILE_WIDTH, TILE_HEIGHT);
984 #else
985 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
986 rb->lcd_fillrect(x*TILE_WIDTH,
987 y*TILE_HEIGHT+YOFS,
988 TILE_WIDTH, TILE_HEIGHT);
989 rb->lcd_fillrect((x+horzmod)*TILE_WIDTH,
990 (y+vertmod)*TILE_HEIGHT+YOFS,
991 TILE_WIDTH, TILE_HEIGHT);
992 rb->lcd_set_drawmode(DRMODE_SOLID);
993 #endif
994 /* draw new position */
995 #ifdef HAVE_LCD_COLOR
996 rb->lcd_bitmap_transparent_part(jewels,
997 0, TILE_HEIGHT*(bj->playboard
998 [y+1+vertmod][x+horzmod].type), TILE_WIDTH,
999 (x+horzmod)*TILE_WIDTH-horzmod*
1000 ((((movelen<<10)*k)/8)>>10),
1001 (y+vertmod)*TILE_HEIGHT-vertmod*
1002 ((((movelen<<10)*k)/8)>>10)+YOFS,
1003 TILE_WIDTH, TILE_HEIGHT);
1004 rb->lcd_bitmap_transparent_part(jewels,
1005 0, TILE_HEIGHT*(bj->playboard[y+1][x].type),
1006 TILE_WIDTH, x*TILE_WIDTH+horzmod*
1007 ((((movelen<<10)*k)/8)>>10),
1008 y*TILE_HEIGHT+vertmod*
1009 ((((movelen<<10)*k)/8)>>10)+YOFS,
1010 TILE_WIDTH, TILE_HEIGHT);
1011 #else
1012 rb->lcd_bitmap_part(jewels,
1013 0, TILE_HEIGHT*(bj->playboard
1014 [y+1+vertmod][x+horzmod].type), TILE_WIDTH,
1015 (x+horzmod)*TILE_WIDTH-horzmod*
1016 ((((movelen<<10)*k)/8)>>10),
1017 (y+vertmod)*TILE_HEIGHT-vertmod*
1018 ((((movelen<<10)*k)/8)>>10)+YOFS,
1019 TILE_WIDTH, TILE_HEIGHT);
1020 rb->lcd_set_drawmode(DRMODE_FG);
1021 rb->lcd_bitmap_part(jewels,
1022 0, TILE_HEIGHT*(bj->playboard[y+1][x].type),
1023 TILE_WIDTH, x*TILE_WIDTH+horzmod*
1024 ((((movelen<<10)*k)/8)>>10),
1025 y*TILE_HEIGHT+vertmod*
1026 ((((movelen<<10)*k)/8)>>10)+YOFS,
1027 TILE_WIDTH, TILE_HEIGHT);
1028 rb->lcd_set_drawmode(DRMODE_SOLID);
1029 #endif
1031 rb->lcd_update_rect(0, 0, TILE_WIDTH*8, LCD_HEIGHT);
1032 jewels_setcolors();
1034 /* framerate limiting */
1035 currenttick = *rb->current_tick;
1036 if(currenttick-lasttick < HZ/MAX_FPS) {
1037 rb->sleep((HZ/MAX_FPS)-(currenttick-lasttick));
1038 } else {
1039 rb->yield();
1041 lasttick = currenttick;
1044 /* swap jewels */
1045 int temp = bj->playboard[y+1][x].type;
1046 bj->playboard[y+1][x].type =
1047 bj->playboard[y+1+vertmod][x+horzmod].type;
1048 bj->playboard[y+1+vertmod][x+horzmod].type = temp;
1050 if(undo) break;
1052 points = jewels_runboard(bj);
1053 if(points == 0) {
1054 undo = true;
1055 } else {
1056 break;
1060 return points;
1063 /*****************************************************************************
1064 * jewels_movesavail() uses pattern matching to see if there are any
1065 * available move left.
1066 ******************************************************************************/
1067 static bool jewels_movesavail(struct game_context* bj) {
1068 int i, j;
1069 bool moves = false;
1070 int mytype;
1072 for(i=1; i<BJ_HEIGHT; i++) {
1073 for(j=0; j<BJ_WIDTH; j++) {
1074 mytype = bj->playboard[i][j].type;
1075 if(mytype == 0 || mytype > MAX_NUM_JEWELS) continue;
1077 /* check horizontal patterns */
1078 if(j <= BJ_WIDTH-3) {
1079 if(i > 1) {
1080 if(bj->playboard[i-1][j+1].type == mytype) {
1081 if(bj->playboard[i-1][j+2].type == mytype)
1082 {moves = true; break;}
1083 if(bj->playboard[i][j+2].type == mytype)
1084 {moves = true; break;}
1086 if(bj->playboard[i][j+1].type == mytype) {
1087 if(bj->playboard[i-1][j+2].type == mytype)
1088 {moves = true; break;}
1092 if(j <= BJ_WIDTH-4) {
1093 if(bj->playboard[i][j+3].type == mytype) {
1094 if(bj->playboard[i][j+1].type == mytype)
1095 {moves = true; break;}
1096 if(bj->playboard[i][j+2].type == mytype)
1097 {moves = true; break;}
1101 if(i < BJ_HEIGHT-1) {
1102 if(bj->playboard[i][j+1].type == mytype) {
1103 if(bj->playboard[i+1][j+2].type == mytype)
1104 {moves = true; break;}
1106 if(bj->playboard[i+1][j+1].type == mytype) {
1107 if(bj->playboard[i][j+2].type == mytype)
1108 {moves = true; break;}
1109 if(bj->playboard[i+1][j+2].type == mytype)
1110 {moves = true; break;}
1115 /* check vertical patterns */
1116 if(i <= BJ_HEIGHT-3) {
1117 if(j > 0) {
1118 if(bj->playboard[i+1][j-1].type == mytype) {
1119 if(bj->playboard[i+2][j-1].type == mytype)
1120 {moves = true; break;}
1121 if(bj->playboard[i+2][j].type == mytype)
1122 {moves = true; break;}
1124 if(bj->playboard[i+1][j].type == mytype) {
1125 if(bj->playboard[i+2][j-1].type == mytype)
1126 {moves = true; break;}
1130 if(i <= BJ_HEIGHT-4) {
1131 if(bj->playboard[i+3][j].type == mytype) {
1132 if(bj->playboard[i+1][j].type == mytype)
1133 {moves = true; break;}
1134 if(bj->playboard[i+2][j].type == mytype)
1135 {moves = true; break;}
1139 if(j < BJ_WIDTH-1) {
1140 if(bj->playboard[i+1][j].type == mytype) {
1141 if(bj->playboard[i+2][j+1].type == mytype)
1142 {moves = true; break;}
1144 if(bj->playboard[i+1][j+1].type == mytype) {
1145 if(bj->playboard[i+2][j].type == mytype)
1146 {moves = true; break;}
1147 if (bj->playboard[i+2][j+1].type == mytype)
1148 {moves = true; break;}
1153 if(moves) break;
1155 return moves;
1158 /*****************************************************************************
1159 * jewels_puzzle_is_finished() checks if the puzzle is finished.
1160 ******************************************************************************/
1161 static bool jewels_puzzle_is_finished(struct game_context* bj) {
1162 unsigned int i, j;
1163 for(i=0; i<BJ_HEIGHT; i++) {
1164 for(j=0; j<BJ_WIDTH; j++) {
1165 int mytype = bj->playboard[i][j].type;
1166 if(mytype>MAX_NUM_JEWELS) {
1167 mytype -= MAX_NUM_JEWELS;
1168 if(mytype&PUZZLE_TILE_UP) {
1169 if(i==0 || bj->playboard[i-1][j].type<=MAX_NUM_JEWELS ||
1170 !((bj->playboard[i-1][j].type-MAX_NUM_JEWELS)
1171 &PUZZLE_TILE_DOWN))
1172 return false;
1174 if(mytype&PUZZLE_TILE_DOWN) {
1175 if(i==BJ_HEIGHT-1 ||
1176 bj->playboard[i+1][j].type<=MAX_NUM_JEWELS ||
1177 !((bj->playboard[i+1][j].type-MAX_NUM_JEWELS)
1178 &PUZZLE_TILE_UP))
1179 return false;
1181 if(mytype&PUZZLE_TILE_LEFT) {
1182 if(j==0 || bj->playboard[i][j-1].type<=MAX_NUM_JEWELS ||
1183 !((bj->playboard[i][j-1].type-MAX_NUM_JEWELS)
1184 &PUZZLE_TILE_RIGHT))
1185 return false;
1187 if(mytype&PUZZLE_TILE_RIGHT) {
1188 if(j==BJ_WIDTH-1 ||
1189 bj->playboard[i][j+1].type<=MAX_NUM_JEWELS ||
1190 !((bj->playboard[i][j+1].type-MAX_NUM_JEWELS)
1191 &PUZZLE_TILE_LEFT))
1192 return false;
1197 return true;
1200 /*****************************************************************************
1201 * jewels_initlevel() initialises a level.
1202 ******************************************************************************/
1203 static unsigned int jewels_initlevel(struct game_context* bj) {
1204 unsigned int points = 0;
1206 switch(bj->type) {
1207 case GAME_TYPE_NORMAL:
1208 bj->num_jewels = MAX_NUM_JEWELS;
1209 break;
1211 case GAME_TYPE_PUZZLE:
1213 unsigned int i, j;
1214 struct puzzle_tile *tile;
1216 bj->num_jewels = puzzle_levels[bj->level-1].num_jewels;
1218 for(i=0; i<BJ_HEIGHT; i++) {
1219 for(j=0; j<BJ_WIDTH; j++) {
1220 bj->playboard[i][j].type = (rb->rand()%bj->num_jewels)+1;
1221 bj->playboard[i][j].falling = false;
1222 bj->playboard[i][j].delete = false;
1225 jewels_runboard(bj);
1226 tile = puzzle_levels[bj->level-1].tiles;
1227 for(i=0; i<puzzle_levels[bj->level-1].num_tiles; i++, tile++) {
1228 bj->playboard[tile->y+1][tile->x].type = MAX_NUM_JEWELS
1229 +tile->tile_type;
1232 break;
1235 jewels_drawboard(bj);
1237 /* run the play board */
1238 jewels_putjewels(bj);
1239 points += jewels_runboard(bj);
1240 return points;
1243 /*****************************************************************************
1244 * jewels_init() initializes jewels data structures.
1245 ******************************************************************************/
1246 static void jewels_init(struct game_context* bj) {
1247 /* seed the rand generator */
1248 rb->srand(*rb->current_tick);
1250 bj->type = bj->tmp_type;
1251 bj->level = 1;
1252 bj->score = 0;
1253 bj->segments = 0;
1255 jewels_setcolors();
1257 /* clear playing board */
1258 rb->memset(bj->playboard, 0, sizeof(bj->playboard));
1259 do {
1260 bj->score += jewels_initlevel(bj);
1261 } while(!jewels_movesavail(bj));
1264 /*****************************************************************************
1265 * jewels_nextlevel() advances the game to the next bj->level and returns
1266 * points earned.
1267 ******************************************************************************/
1268 static void jewels_nextlevel(struct game_context* bj) {
1269 int i, x, y;
1270 unsigned int points = 0;
1272 switch(bj->type) {
1273 case GAME_TYPE_NORMAL:
1274 /* roll over score, change and display level */
1275 while(bj->score >= LEVEL_PTS) {
1276 bj->score -= LEVEL_PTS;
1277 bj->level++;
1278 rb->splashf(HZ*2, "Level %d", bj->level);
1279 jewels_drawboard(bj);
1282 /* randomly clear some jewels */
1283 for(i=0; i<16; i++) {
1284 x = rb->rand()%8;
1285 y = rb->rand()%8;
1287 if(bj->playboard[y][x].type != 0) {
1288 points++;
1289 bj->playboard[y][x].type = 0;
1292 break;
1294 case GAME_TYPE_PUZZLE:
1295 bj->level++;
1296 rb->splashf(HZ*2, "Level %d", bj->level);
1297 break;
1300 points += jewels_initlevel(bj);
1301 bj->score += points;
1304 static void jewels_show_highscores(int position)
1306 int i, w, h;
1307 char str[30];
1309 #ifdef HAVE_LCD_COLOR
1310 rb->lcd_set_background(LCD_BLACK);
1311 rb->lcd_set_foreground(LCD_WHITE);
1312 #endif
1313 rb->button_clear_queue();
1314 rb->lcd_clear_display();
1316 rb->lcd_setfont(FONT_UI);
1317 rb->lcd_getstringsize("High Scores", &w, &h);
1318 /* check wether it fits on screen */
1319 if ((4*h + h*(NUM_SCORES-1) + MARGIN) > LCD_HEIGHT) {
1320 rb->lcd_setfont(FONT_SYSFIXED);
1321 rb->lcd_getstringsize("High Scores", &w, &h);
1323 rb->lcd_putsxy(LCD_WIDTH/2-w/2, MARGIN, "High Scores");
1324 rb->lcd_putsxy(LCD_WIDTH/4-w/4,2*h, "Score");
1325 rb->lcd_putsxy(LCD_WIDTH*3/4-w/4,2*h, "Level");
1327 for (i = 0; i<NUM_SCORES; i++)
1329 #ifdef HAVE_LCD_COLOR
1330 if (i == position) {
1331 rb->lcd_set_foreground(LCD_RGBPACK(245,0,0));
1333 #endif
1334 rb->snprintf (str, sizeof (str), "%d)", i+1);
1335 rb->lcd_putsxy (MARGIN,3*h + h*i, str);
1336 rb->snprintf (str, sizeof (str), "%d", highest[i].score);
1337 rb->lcd_putsxy (LCD_WIDTH/4-w/4,3*h + h*i, str);
1338 rb->snprintf (str, sizeof (str), "%d", highest[i].level);
1339 rb->lcd_putsxy (LCD_WIDTH*3/4-w/4,3*h + h*i, str);
1340 if(i == position) {
1341 #ifdef HAVE_LCD_COLOR
1342 rb->lcd_set_foreground(LCD_WHITE);
1343 #else
1344 rb->lcd_hline(MARGIN, LCD_WIDTH-MARGIN, 3*h + h*(i+1));
1345 #endif
1348 rb->lcd_update();
1349 rb->button_get(true);
1350 rb->lcd_setfont(FONT_SYSFIXED);
1353 static int jewels_help(void)
1355 rb->lcd_setfont(FONT_UI);
1356 #define WORDS (sizeof help_text / sizeof (char*))
1357 static char *help_text[] = {
1358 "Jewels", "", "Aim", "",
1359 "Swap", "pairs", "of", "jewels", "to", "form", "connected",
1360 "segments", "of", "three", "or", "more", "of", "the", "same",
1361 "type.", "",
1362 "The", "goal", "of", "the", "game", "is", "to", "score", "as", "many",
1363 "points", "as", "possible", "before", "running", "out", "of",
1364 "available", "moves.", "", "",
1365 "Controls", "",
1366 "Directions",
1367 #ifdef JEWELS_SCROLLWHEEL
1368 "or", "scroll",
1369 #endif
1370 "to", "move", "",
1371 HK_SELECT, "to", "select", "",
1372 HK_CANCEL, "to", "go", "to", "menu"
1374 static struct style_text formation[]={
1375 { 0, TEXT_CENTER|TEXT_UNDERLINE },
1376 { 2, C_RED },
1377 { -1, 0 }
1380 #ifdef HAVE_LCD_COLOR
1381 rb->lcd_set_background(LCD_BLACK);
1382 rb->lcd_set_foreground(LCD_WHITE);
1383 #endif
1384 if (display_text(WORDS, help_text, formation, NULL)==PLUGIN_USB_CONNECTED)
1385 return PLUGIN_USB_CONNECTED;
1386 int button;
1387 do {
1388 button = rb->button_get(true);
1389 if (button == SYS_USB_CONNECTED) {
1390 return PLUGIN_USB_CONNECTED;
1392 } while( ( button == BUTTON_NONE )
1393 || ( button & (BUTTON_REL|BUTTON_REPEAT) ) );
1394 rb->lcd_setfont(FONT_SYSFIXED);
1396 return 0;
1399 static bool _ingame;
1400 static int jewels_menu_cb(int action, const struct menu_item_ex *this_item)
1402 int i = ((intptr_t)this_item);
1403 if(action == ACTION_REQUEST_MENUITEM
1404 && !_ingame && (i==0 || i==6))
1405 return ACTION_EXIT_MENUITEM;
1406 return action;
1408 /*****************************************************************************
1409 * jewels_game_menu() shows the game menu.
1410 ******************************************************************************/
1411 static int jewels_game_menu(struct game_context* bj, bool ingame)
1413 rb->button_clear_queue();
1414 int choice = 0;
1416 _ingame = ingame;
1418 static struct opt_items mode[] = {
1419 { "Normal", -1 },
1420 { "Puzzle", -1 },
1423 MENUITEM_STRINGLIST (main_menu, "Jewels Menu", jewels_menu_cb,
1424 "Resume Game",
1425 "Start New Game",
1426 "Mode",
1427 "Help",
1428 "High Score",
1429 "Playback Control",
1430 "Save and Quit",
1431 "Quit");
1433 while (1) {
1434 switch (rb->do_menu(&main_menu, &choice, NULL, false)) {
1435 case 0:
1436 jewels_setcolors();
1437 return 0;
1438 case 1:
1439 jewels_init(bj);
1440 return 0;
1441 case 2:
1442 rb->set_option("Mode", &bj->tmp_type, INT, mode, 2, NULL);
1443 break;
1444 case 3:
1445 jewels_help();
1446 break;
1447 case 4:
1448 jewels_show_highscores(NUM_SCORES);
1449 break;
1450 case 5:
1451 playback_control(NULL);
1452 break;
1453 case 6:
1454 if (ingame) {
1455 rb->splash(HZ*1, "Saving game ...");
1456 jewels_savegame(bj);
1458 return 1;
1459 case 7:
1460 return 1;
1461 case MENU_ATTACHED_USB:
1462 return 1;
1463 default:
1464 break;
1469 static int jewels_main(struct game_context* bj) {
1470 int button;
1471 int position;
1472 bool selected = false;
1473 bool no_movesavail;
1474 int x=0, y=0;
1476 bool loaded = jewels_loadgame(bj);
1477 if (jewels_game_menu(bj, loaded)!=0)
1478 return 0;
1480 while(true) {
1481 no_movesavail = false;
1483 /* refresh the board */
1484 jewels_drawboard(bj);
1486 /* display the cursor */
1487 if(selected) {
1488 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
1489 rb->lcd_fillrect(x*TILE_WIDTH, y*TILE_HEIGHT+YOFS,
1490 TILE_WIDTH, TILE_HEIGHT);
1491 rb->lcd_set_drawmode(DRMODE_SOLID);
1492 } else {
1493 rb->lcd_drawrect(x*TILE_WIDTH, y*TILE_HEIGHT+YOFS,
1494 TILE_WIDTH, TILE_HEIGHT);
1496 rb->lcd_update_rect(x*TILE_WIDTH, y*TILE_HEIGHT+YOFS,
1497 TILE_WIDTH, TILE_HEIGHT);
1499 /* handle game button presses */
1500 rb->yield();
1501 button = rb->button_get(true);
1502 switch(button){
1503 case JEWELS_LEFT: /* move cursor left */
1504 case (JEWELS_LEFT|BUTTON_REPEAT):
1505 if(selected) {
1506 bj->score += jewels_swapjewels(bj, x, y, SWAP_LEFT);
1507 selected = false;
1508 if (!jewels_movesavail(bj)) no_movesavail = true;
1509 } else {
1510 x = (x+BJ_WIDTH-1)%BJ_WIDTH;
1512 break;
1514 case JEWELS_RIGHT: /* move cursor right */
1515 case (JEWELS_RIGHT|BUTTON_REPEAT):
1516 if(selected) {
1517 bj->score += jewels_swapjewels(bj, x, y, SWAP_RIGHT);
1518 selected = false;
1519 if (!jewels_movesavail(bj)) no_movesavail = true;
1520 } else {
1521 x = (x+1)%BJ_WIDTH;
1523 break;
1525 case JEWELS_DOWN: /* move cursor down */
1526 case (JEWELS_DOWN|BUTTON_REPEAT):
1527 if(selected) {
1528 bj->score += jewels_swapjewels(bj, x, y, SWAP_DOWN);
1529 selected = false;
1530 if (!jewels_movesavail(bj)) no_movesavail = true;
1531 } else {
1532 y = (y+1)%(BJ_HEIGHT-1);
1534 break;
1536 case JEWELS_UP: /* move cursor up */
1537 case (JEWELS_UP|BUTTON_REPEAT):
1538 if(selected) {
1539 bj->score += jewels_swapjewels(bj, x, y, SWAP_UP);
1540 selected = false;
1541 if (!jewels_movesavail(bj)) no_movesavail = true;
1542 } else {
1543 y = (y+(BJ_HEIGHT-1)-1)%(BJ_HEIGHT-1);
1545 break;
1547 #ifdef JEWELS_SCROLLWHEEL
1548 case JEWELS_PREV: /* scroll backwards */
1549 case (JEWELS_PREV|BUTTON_REPEAT):
1550 if(!selected) {
1551 if(x == 0) {
1552 y = (y+(BJ_HEIGHT-1)-1)%(BJ_HEIGHT-1);
1554 x = (x+BJ_WIDTH-1)%BJ_WIDTH;
1556 break;
1558 case JEWELS_NEXT: /* scroll forwards */
1559 case (JEWELS_NEXT|BUTTON_REPEAT):
1560 if(!selected) {
1561 if(x == BJ_WIDTH-1) {
1562 y = (y+1)%(BJ_HEIGHT-1);
1564 x = (x+1)%BJ_WIDTH;
1566 break;
1567 #endif
1569 case JEWELS_SELECT: /* toggle selected */
1570 selected = !selected;
1571 break;
1573 #ifdef JEWELS_RC_CANCEL
1574 case JEWELS_RC_CANCEL:
1575 #endif
1576 case JEWELS_CANCEL: /* end game */
1577 if (jewels_game_menu(bj, true)!=0)
1578 return 0;
1579 break;
1581 default:
1582 if (rb->default_event_handler (button) == SYS_USB_CONNECTED)
1583 return PLUGIN_USB_CONNECTED;
1584 break;
1587 switch(bj->type) {
1588 case GAME_TYPE_NORMAL:
1589 if(bj->score >= LEVEL_PTS)
1590 jewels_nextlevel(bj);
1591 break;
1592 case GAME_TYPE_PUZZLE:
1593 if (jewels_puzzle_is_finished(bj)) {
1594 if (bj->level < NUM_PUZZLE_LEVELS) {
1595 jewels_nextlevel(bj);
1596 } else {
1597 rb->splash(2*HZ, "Congratulations!");
1598 rb->splash(2*HZ, "You have finished the game!");
1599 if (jewels_game_menu(bj, false)!=0) {
1600 return 0;
1603 break;
1607 if (no_movesavail) {
1608 switch(bj->type) {
1609 case GAME_TYPE_NORMAL:
1610 rb->splash(HZ*2, "Game Over!");
1611 rb->lcd_clear_display();
1612 if (highscore_would_update(bj->score, highest,
1613 NUM_SCORES)) {
1614 position=highscore_update(bj->score,
1615 bj->level, "",
1616 highest,NUM_SCORES);
1617 highest_updated = true;
1618 if (position == 0) {
1619 rb->splash(HZ*2, "New High Score");
1621 jewels_show_highscores(position);
1623 break;
1624 case GAME_TYPE_PUZZLE:
1625 rb->splash(2*HZ, "Game Over");
1626 break;
1628 if (jewels_game_menu(bj, false)!=0) {
1629 return 0;
1635 /* this is the plugin entry point */
1636 enum plugin_status plugin_start(const void* parameter)
1638 (void)parameter;
1640 /* load high scores */
1641 highscore_load(HIGH_SCORE,highest,NUM_SCORES);
1642 highest_updated = false;
1644 rb->lcd_setfont(FONT_SYSFIXED);
1645 #if LCD_DEPTH > 1
1646 rb->lcd_set_backdrop(NULL);
1647 #endif
1649 struct game_context bj;
1650 bj.tmp_type = GAME_TYPE_NORMAL;
1651 jewels_main(&bj);
1652 if(highest_updated)
1653 highscore_save(HIGH_SCORE,highest,NUM_SCORES);
1654 rb->lcd_setfont(FONT_UI);
1656 return PLUGIN_OK;
1659 #endif /* HAVE_LCD_BITMAP */