Two tiny tweaks to some arm asm
[kugel-rb.git] / apps / plugins / jewels.c
blobc2340aa557661a4bd475407ea50ab1522dbfe226
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 #elif CONFIG_KEYPAD == MPIO_HD200_PAD
281 #define JEWELS_LEFT BUTTON_VOL_DOWN
282 #define JEWELS_RIGHT BUTTON_VOL_UP
283 #define JEWELS_UP BUTTON_PREV
284 #define JEWELS_DOWN BUTTON_NEXT
285 #define JEWELS_SELECT BUTTON_SELECT
286 #define JEWELS_CANCEL BUTTON_REC
287 #define HK_SELECT "SELECT"
288 #define HK_CANCEL "REC"
290 #else
291 #error No keymap defined!
292 #endif
294 #ifdef HAVE_TOUCHSCREEN
295 #ifndef JEWELS_UP
296 #define JEWELS_UP BUTTON_TOPMIDDLE
297 #endif
298 #ifndef JEWELS_DOWN
299 #define JEWELS_DOWN BUTTON_BOTTOMMIDDLE
300 #endif
301 #ifndef JEWELS_LEFT
302 #define JEWELS_LEFT BUTTON_MIDLEFT
303 #endif
304 #ifndef JEWELS_RIGHT
305 #define JEWELS_RIGHT BUTTON_MIDRIGHT
306 #endif
307 #ifndef JEWELS_SELECT
308 #define JEWELS_SELECT BUTTON_CENTER
309 #define HK_SELECT "CENTER"
310 #endif
311 #ifndef JEWELS_CANCEL
312 #define JEWELS_CANCEL BUTTON_TOPLEFT
313 #define HK_CANCEL "TOPLEFT"
314 #endif
315 #endif
317 #define TILE_WIDTH BMPWIDTH_jewels
318 #define TILE_HEIGHT (BMPHEIGHT_jewels/23)
320 #if LCD_HEIGHT < LCD_WIDTH
321 /* This calculation assumes integer division w/ LCD_HEIGHT/TILE_HEIGHT */
322 #define YOFS LCD_HEIGHT-((LCD_HEIGHT/TILE_HEIGHT)*TILE_HEIGHT)
323 #else
324 #define YOFS 0
325 #endif
327 #define NUM_SCORES 5
329 /* swap directions */
330 #define SWAP_UP 0
331 #define SWAP_RIGHT 1
332 #define SWAP_DOWN 2
333 #define SWAP_LEFT 3
335 /* play board dimension */
336 #define BJ_HEIGHT 9
337 #define BJ_WIDTH 8
339 /* next level threshold */
340 #define LEVEL_PTS 100
342 /* animation frame rate */
343 #define MAX_FPS 20
345 /* text margin */
346 #define MARGIN 5
348 /* Game types */
349 enum game_type {
350 GAME_TYPE_NORMAL,
351 GAME_TYPE_PUZZLE
354 /* external bitmaps */
355 extern const fb_data jewels[];
357 /* tile background colors */
358 #ifdef HAVE_LCD_COLOR
359 static const unsigned jewels_bkgd[2] = {
360 LCD_RGBPACK(104, 63, 63),
361 LCD_RGBPACK(83, 44, 44)
363 #endif
365 /* the tile struct
366 * type is the jewel number 0-7
367 * falling if the jewel is falling
368 * delete marks the jewel for deletion
370 struct tile {
371 int type;
372 bool falling;
373 bool delete;
376 /* the game context struct
377 * score is the current level score
378 * segments is the number of cleared segments in the current run
379 * level is the current level
380 * tmp_type is the select type in the menu
381 * type is the game type (normal or puzzle)
382 * playboard is the game playing board (first row is hidden)
383 * num_jewels is the number of different jewels to use
385 struct game_context {
386 unsigned int score;
387 unsigned int segments;
388 unsigned int level;
389 unsigned int type;
390 unsigned int tmp_type;
391 struct tile playboard[BJ_HEIGHT][BJ_WIDTH];
392 unsigned int num_jewels;
395 #define MAX_NUM_JEWELS 7
397 #define MAX_PUZZLE_TILES 4
398 #define NUM_PUZZLE_LEVELS 10
400 struct puzzle_tile {
401 int x;
402 int y;
403 int tile_type;
406 struct puzzle_level {
407 unsigned int num_jewels;
408 unsigned int num_tiles;
409 struct puzzle_tile tiles[MAX_PUZZLE_TILES];
412 #define PUZZLE_TILE_UP 1
413 #define PUZZLE_TILE_DOWN 2
414 #define PUZZLE_TILE_LEFT 4
415 #define PUZZLE_TILE_RIGHT 8
417 struct puzzle_level puzzle_levels[NUM_PUZZLE_LEVELS] = {
418 { 5, 2, { {3, 3, PUZZLE_TILE_RIGHT},
419 {4, 2, PUZZLE_TILE_LEFT} } },
420 { 5, 2, { {3, 2, PUZZLE_TILE_DOWN},
421 {3, 4, PUZZLE_TILE_UP} } },
422 { 6, 3, { {3, 2, PUZZLE_TILE_DOWN},
423 {3, 4, PUZZLE_TILE_UP|PUZZLE_TILE_DOWN},
424 {3, 6, PUZZLE_TILE_UP} } },
425 { 6, 3, { {3, 2, PUZZLE_TILE_RIGHT},
426 {4, 3, PUZZLE_TILE_LEFT|PUZZLE_TILE_RIGHT},
427 {5, 4, PUZZLE_TILE_LEFT} } },
428 { 6, 2, { {3, 4, PUZZLE_TILE_RIGHT},
429 {4, 2, PUZZLE_TILE_LEFT} } },
430 { 6, 2, { {3, 2, PUZZLE_TILE_DOWN},
431 {4, 4, PUZZLE_TILE_UP} } },
432 { 7, 4, { {3, 2, PUZZLE_TILE_RIGHT|PUZZLE_TILE_DOWN},
433 {4, 3, PUZZLE_TILE_LEFT|PUZZLE_TILE_DOWN},
434 {3, 4, PUZZLE_TILE_RIGHT|PUZZLE_TILE_UP},
435 {4, 4, PUZZLE_TILE_LEFT|PUZZLE_TILE_UP} } },
436 { 6, 3, { {3, 2, PUZZLE_TILE_DOWN},
437 {4, 4, PUZZLE_TILE_UP|PUZZLE_TILE_DOWN},
438 {3, 6, PUZZLE_TILE_UP} } },
439 { 7, 3, { {2, 2, PUZZLE_TILE_RIGHT},
440 {4, 1, PUZZLE_TILE_LEFT|PUZZLE_TILE_RIGHT},
441 {5, 4, PUZZLE_TILE_LEFT} } },
442 { 7, 4, { {3, 0, PUZZLE_TILE_RIGHT|PUZZLE_TILE_DOWN},
443 {5, 0, PUZZLE_TILE_LEFT|PUZZLE_TILE_DOWN},
444 {2, 7, PUZZLE_TILE_RIGHT|PUZZLE_TILE_UP},
445 {4, 7, PUZZLE_TILE_LEFT|PUZZLE_TILE_UP} } },
448 #define SAVE_FILE PLUGIN_GAMES_DIR "/jewels.save"
449 #define SCORE_FILE PLUGIN_GAMES_DIR "/jewels.score"
450 struct highscore highscores[NUM_SCORES];
452 static bool resume_file = false;
454 /*****************************************************************************
455 * jewels_setcolors() set the foreground and background colors.
456 ******************************************************************************/
457 static inline void jewels_setcolors(void) {
458 #ifdef HAVE_LCD_COLOR
459 rb->lcd_set_background(LCD_RGBPACK(49, 26, 26));
460 rb->lcd_set_foreground(LCD_RGBPACK(210, 181, 181));
461 #endif
464 /*****************************************************************************
465 * jewels_loadgame() loads the saved game and returns load success.
466 ******************************************************************************/
467 static bool jewels_loadgame(struct game_context* bj)
469 int fd;
470 bool loaded = false;
472 /* open game file */
473 fd = rb->open(SAVE_FILE, O_RDONLY);
474 if(fd < 0) return loaded;
476 /* read in saved game */
477 while(true) {
478 if(rb->read(fd, &bj->tmp_type, sizeof(bj->tmp_type)) <= 0) break;
479 if(rb->read(fd, &bj->type, sizeof(bj->type)) <= 0) break;
480 if(rb->read(fd, &bj->score, sizeof(bj->score)) <= 0) break;
481 if(rb->read(fd, &bj->level, sizeof(bj->level)) <= 0) break;
482 if(rb->read(fd, &bj->segments, sizeof(bj->segments)) <= 0) break;
483 if(rb->read(fd, &bj->num_jewels, sizeof(bj->num_jewels)) <= 0) break;
484 if(rb->read(fd, bj->playboard, sizeof(bj->playboard)) <= 0) break;
485 loaded = true;
486 break;
489 rb->close(fd);
491 return loaded;
494 /*****************************************************************************
495 * jewels_savegame() saves the current game state.
496 ******************************************************************************/
497 static void jewels_savegame(struct game_context* bj)
499 int fd;
500 /* write out the game state to the save file */
501 fd = rb->open(SAVE_FILE, O_WRONLY|O_CREAT, 0666);
502 if(fd < 0) return;
504 rb->write(fd, &bj->tmp_type, sizeof(bj->tmp_type));
505 rb->write(fd, &bj->type, sizeof(bj->type));
506 rb->write(fd, &bj->score, sizeof(bj->score));
507 rb->write(fd, &bj->level, sizeof(bj->level));
508 rb->write(fd, &bj->segments, sizeof(bj->segments));
509 rb->write(fd, &bj->num_jewels, sizeof(bj->num_jewels));
510 rb->write(fd, bj->playboard, sizeof(bj->playboard));
511 rb->close(fd);
514 /*****************************************************************************
515 * jewels_drawboard() redraws the entire game board.
516 ******************************************************************************/
517 static void jewels_drawboard(struct game_context* bj) {
518 int i, j;
519 int w, h;
520 unsigned int tempscore;
521 unsigned int size;
522 char *title = "Level";
523 char str[10];
525 if (bj->type == GAME_TYPE_NORMAL) {
526 tempscore = (bj->score>LEVEL_PTS ? LEVEL_PTS : bj->score);
527 size = LEVEL_PTS;
528 } else {
529 tempscore = (bj->level>NUM_PUZZLE_LEVELS ? NUM_PUZZLE_LEVELS : bj->level);
530 size = NUM_PUZZLE_LEVELS;
533 /* clear screen */
534 rb->lcd_clear_display();
536 /* dispay playing board */
537 for(i=0; i<BJ_HEIGHT-1; i++){
538 for(j=0; j<BJ_WIDTH; j++){
539 #ifdef HAVE_LCD_COLOR
540 rb->lcd_set_foreground(jewels_bkgd[(i+j)%2]);
541 rb->lcd_fillrect(j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
542 TILE_WIDTH, TILE_HEIGHT);
543 rb->lcd_bitmap_transparent_part(jewels,
544 0, TILE_HEIGHT*(bj->playboard[i+1][j].type),
545 STRIDE( SCREEN_MAIN,
546 BMPWIDTH_jewels, BMPHEIGHT_jewels),
547 j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
548 TILE_WIDTH, TILE_HEIGHT);
549 #else
550 rb->lcd_bitmap_part(jewels,
551 0, TILE_HEIGHT*(bj->playboard[i+1][j].type),
552 STRIDE( SCREEN_MAIN,
553 BMPWIDTH_jewels, BMPHEIGHT_jewels),
554 j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
555 TILE_WIDTH, TILE_HEIGHT);
556 #endif
560 #if LCD_WIDTH > LCD_HEIGHT /* horizontal layout */
562 /* draw separator lines */
563 jewels_setcolors();
564 rb->lcd_vline(BJ_WIDTH*TILE_WIDTH, 0, LCD_HEIGHT-1);
566 rb->lcd_hline(BJ_WIDTH*TILE_WIDTH, LCD_WIDTH-1, 18);
567 rb->lcd_hline(BJ_WIDTH*TILE_WIDTH, LCD_WIDTH-1, LCD_HEIGHT-10);
569 /* draw progress bar */
570 #ifdef HAVE_LCD_COLOR
571 rb->lcd_set_foreground(LCD_RGBPACK(104, 63, 63));
572 #endif
573 rb->lcd_fillrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4,
574 (LCD_HEIGHT-10)-(((LCD_HEIGHT-10)-18)*
575 tempscore/size),
576 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2,
577 ((LCD_HEIGHT-10)-18)*tempscore/size);
578 #ifdef HAVE_LCD_COLOR
579 rb->lcd_set_foreground(LCD_RGBPACK(83, 44, 44));
580 rb->lcd_drawrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4+1,
581 (LCD_HEIGHT-10)-(((LCD_HEIGHT-10)-18)*
582 tempscore/size)+1,
583 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-2,
584 ((LCD_HEIGHT-10)-18)*tempscore/size-1);
585 jewels_setcolors();
586 rb->lcd_drawrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4,
587 (LCD_HEIGHT-10)-(((LCD_HEIGHT-10)-18)*tempscore/size),
588 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2,
589 ((LCD_HEIGHT-10)-18)*tempscore/size+1);
590 #endif
592 /* print text */
593 rb->lcd_getstringsize(title, &w, &h);
594 rb->lcd_putsxy(LCD_WIDTH-(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-w/2, 1, title);
595 rb->snprintf(str, 4, "%d", bj->level);
596 rb->lcd_getstringsize(str, &w, &h);
597 rb->lcd_putsxy(LCD_WIDTH-(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-w/2, 10, str);
599 if (bj->type == GAME_TYPE_NORMAL) {
600 rb->snprintf(str, 6, "%d", (bj->level-1)*LEVEL_PTS+bj->score);
601 rb->lcd_getstringsize(str, &w, &h);
602 rb->lcd_putsxy(LCD_WIDTH-(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-w/2,
603 LCD_HEIGHT-8, str);
606 #elif LCD_WIDTH < LCD_HEIGHT /* vertical layout */
608 /* draw separator lines */
609 jewels_setcolors();
610 rb->lcd_hline(0, LCD_WIDTH-1, 8*TILE_HEIGHT+YOFS);
611 rb->lcd_hline(0, LCD_WIDTH-1, LCD_HEIGHT-14);
612 rb->lcd_vline(LCD_WIDTH/2, LCD_HEIGHT-14, LCD_HEIGHT-1);
614 /* draw progress bar */
615 #ifdef HAVE_LCD_COLOR
616 rb->lcd_set_foreground(LCD_RGBPACK(104, 63, 63));
617 #endif
618 rb->lcd_fillrect(0, (8*TILE_HEIGHT+YOFS)
619 +(LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/4,
620 LCD_WIDTH*tempscore/size,
621 (LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/2);
622 #ifdef HAVE_LCD_COLOR
623 rb->lcd_set_foreground(LCD_RGBPACK(83, 44, 44));
624 rb->lcd_drawrect(1, (8*TILE_HEIGHT+YOFS)
625 +(LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/4+1,
626 LCD_WIDTH*tempscore/size-1,
627 (LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/2-2);
628 jewels_setcolors();
629 rb->lcd_drawrect(0, (8*TILE_HEIGHT+YOFS)
630 +(LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/4,
631 LCD_WIDTH*tempscore/size+1,
632 (LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/2);
633 #endif
635 /* print text */
636 rb->snprintf(str, 10, "%s %d", title, bj->level);
637 rb->lcd_putsxy(1, LCD_HEIGHT-10, str);
639 if (bj->type == GAME_TYPE_NORMAL) {
640 rb->snprintf(str, 6, "%d", (bj->level-1)*LEVEL_PTS+bj->score);
641 rb->lcd_getstringsize(str, &w, &h);
642 rb->lcd_putsxy((LCD_WIDTH-2)-w, LCD_HEIGHT-10, str);
646 #else /* square layout */
648 /* draw separator lines */
649 jewels_setcolors();
650 rb->lcd_hline(0, LCD_WIDTH-1, 8*TILE_HEIGHT+YOFS);
651 rb->lcd_vline(BJ_WIDTH*TILE_WIDTH, 0, 8*TILE_HEIGHT+YOFS);
652 rb->lcd_vline(LCD_WIDTH/2, 8*TILE_HEIGHT+YOFS, LCD_HEIGHT-1);
654 /* draw progress bar */
655 #ifdef HAVE_LCD_COLOR
656 rb->lcd_set_foreground(LCD_RGBPACK(104, 63, 63));
657 #endif
658 rb->lcd_fillrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4,
659 (8*TILE_HEIGHT+YOFS)-(8*TILE_HEIGHT+YOFS)*tempscore/size,
660 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2,
661 (8*TILE_HEIGHT+YOFS)*tempscore/size);
662 #ifdef HAVE_LCD_COLOR
663 rb->lcd_set_foreground(LCD_RGBPACK(83, 44, 44));
664 rb->lcd_drawrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4+1,
665 (8*TILE_HEIGHT+YOFS)-(8*TILE_HEIGHT+YOFS)
666 *tempscore/size+1,
667 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-2,
668 (8*TILE_HEIGHT+YOFS)*tempscore/size-1);
669 jewels_setcolors();
670 rb->lcd_drawrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4,
671 (8*TILE_HEIGHT+YOFS)-(8*TILE_HEIGHT+YOFS)
672 *tempscore/size,
673 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2,
674 (8*TILE_HEIGHT+YOFS)*tempscore/size+1);
675 #endif
677 /* print text */
678 rb->snprintf(str, 10, "%s %d", title, bj->level);
679 rb->lcd_putsxy(1, LCD_HEIGHT-(LCD_HEIGHT-(8*TILE_HEIGHT+YOFS))/2-3, str);
681 if (bj->type == GAME_TYPE_NORMAL) {
682 rb->snprintf(str, 6, "%d", (bj->level-1)*LEVEL_PTS+bj->score);
683 rb->lcd_getstringsize(str, &w, &h);
684 rb->lcd_putsxy((LCD_WIDTH-2)-w,
685 LCD_HEIGHT-(LCD_HEIGHT-(8*TILE_HEIGHT+YOFS))/2-3, str);
688 #endif /* layout */
690 rb->lcd_update();
693 /*****************************************************************************
694 * jewels_putjewels() makes the jewels fall to fill empty spots and adds
695 * new random jewels at the empty spots at the top of each row.
696 ******************************************************************************/
697 static void jewels_putjewels(struct game_context* bj){
698 int i, j, k;
699 bool mark, done;
700 long lasttick, currenttick;
702 /* loop to make all the jewels fall */
703 while(true) {
704 /* mark falling jewels and add new jewels to hidden top row*/
705 mark = false;
706 done = true;
707 for(j=0; j<BJ_WIDTH; j++) {
708 if(bj->playboard[1][j].type == 0) {
709 bj->playboard[0][j].type = rb->rand()%bj->num_jewels+1;
711 for(i=BJ_HEIGHT-2; i>=0; i--) {
712 if(!mark && bj->playboard[i+1][j].type == 0) {
713 mark = true;
714 done = false;
716 if(mark) bj->playboard[i][j].falling = true;
718 /*if(bj->playboard[1][j].falling) {
719 bj->playboard[0][j].type = rb->rand()%bj->num_jewels+1;
720 bj->playboard[0][j].falling = true;
722 mark = false;
725 /* break if there are no falling jewels */
726 if(done) break;
728 /* animate falling jewels */
729 lasttick = *rb->current_tick;
731 for(k=1; k<=8; k++) {
732 for(i=BJ_HEIGHT-2; i>=0; i--) {
733 for(j=0; j<BJ_WIDTH; j++) {
734 if(bj->playboard[i][j].falling &&
735 bj->playboard[i][j].type != 0) {
736 /* clear old position */
737 #ifdef HAVE_LCD_COLOR
738 if(i == 0 && YOFS) {
739 rb->lcd_set_foreground(rb->lcd_get_background());
740 } else {
741 rb->lcd_set_foreground(jewels_bkgd[(i-1+j)%2]);
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_set_foreground(jewels_bkgd[(i+j)%2]);
747 rb->lcd_fillrect(j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
748 TILE_WIDTH, TILE_HEIGHT);
750 #else
751 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
752 rb->lcd_fillrect(j*TILE_WIDTH, (i-1)*TILE_HEIGHT+YOFS,
753 TILE_WIDTH, TILE_HEIGHT);
754 if(bj->playboard[i+1][j].type == 0) {
755 rb->lcd_fillrect(j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
756 TILE_WIDTH, TILE_HEIGHT);
758 rb->lcd_set_drawmode(DRMODE_SOLID);
759 #endif
761 /* draw new position */
762 #ifdef HAVE_LCD_COLOR
763 rb->lcd_bitmap_transparent_part(jewels, 0,
764 TILE_HEIGHT*(bj->playboard[i][j].type),
765 STRIDE( SCREEN_MAIN,
766 BMPWIDTH_jewels,
767 BMPHEIGHT_jewels),
768 j*TILE_WIDTH,
769 (i-1)*TILE_HEIGHT+YOFS+
770 ((((TILE_HEIGHT<<10)*k)/8)>>10),
771 TILE_WIDTH, TILE_HEIGHT);
772 #else
773 rb->lcd_bitmap_part(jewels, 0,
774 TILE_HEIGHT*(bj->playboard[i][j].type),
775 STRIDE( SCREEN_MAIN,
776 BMPWIDTH_jewels,
777 BMPHEIGHT_jewels),
778 j*TILE_WIDTH,
779 (i-1)*TILE_HEIGHT+YOFS+
780 ((((TILE_HEIGHT<<10)*k)/8)>>10),
781 TILE_WIDTH, TILE_HEIGHT);
782 #endif
787 rb->lcd_update_rect(0, 0, TILE_WIDTH*8, LCD_HEIGHT);
788 jewels_setcolors();
790 /* framerate limiting */
791 currenttick = *rb->current_tick;
792 if(currenttick-lasttick < HZ/MAX_FPS) {
793 rb->sleep((HZ/MAX_FPS)-(currenttick-lasttick));
794 } else {
795 rb->yield();
797 lasttick = currenttick;
800 /* shift jewels down */
801 for(j=0; j<BJ_WIDTH; j++) {
802 for(i=BJ_HEIGHT-1; i>=1; i--) {
803 if(bj->playboard[i-1][j].falling) {
804 bj->playboard[i][j].type = bj->playboard[i-1][j].type;
809 /* clear out top row */
810 for(j=0; j<BJ_WIDTH; j++) {
811 bj->playboard[0][j].type = 0;
814 /* mark everything not falling */
815 for(i=0; i<BJ_HEIGHT; i++) {
816 for(j=0; j<BJ_WIDTH; j++) {
817 bj->playboard[i][j].falling = false;
823 /*****************************************************************************
824 * jewels_clearjewels() finds all the connected rows and columns and
825 * calculates and returns the points earned.
826 ******************************************************************************/
827 static unsigned int jewels_clearjewels(struct game_context* bj) {
828 int i, j;
829 int last, run;
830 unsigned int points = 0;
832 /* check for connected rows */
833 for(i=1; i<BJ_HEIGHT; i++) {
834 last = 0;
835 run = 1;
836 for(j=0; j<BJ_WIDTH; j++) {
837 if(bj->playboard[i][j].type == last &&
838 bj->playboard[i][j].type != 0 &&
839 bj->playboard[i][j].type <= MAX_NUM_JEWELS) {
840 run++;
842 if(run == 3) {
843 bj->segments++;
844 points += bj->segments;
845 bj->playboard[i][j].delete = true;
846 bj->playboard[i][j-1].delete = true;
847 bj->playboard[i][j-2].delete = true;
848 } else if(run > 3) {
849 points++;
850 bj->playboard[i][j].delete = true;
852 } else {
853 run = 1;
854 last = bj->playboard[i][j].type;
859 /* check for connected columns */
860 for(j=0; j<BJ_WIDTH; j++) {
861 last = 0;
862 run = 1;
863 for(i=1; i<BJ_HEIGHT; i++) {
864 if(bj->playboard[i][j].type != 0 &&
865 bj->playboard[i][j].type == last &&
866 bj->playboard[i][j].type <= MAX_NUM_JEWELS) {
867 run++;
869 if(run == 3) {
870 bj->segments++;
871 points += bj->segments;
872 bj->playboard[i][j].delete = true;
873 bj->playboard[i-1][j].delete = true;
874 bj->playboard[i-2][j].delete = true;
875 } else if(run > 3) {
876 points++;
877 bj->playboard[i][j].delete = true;
879 } else {
880 run = 1;
881 last = bj->playboard[i][j].type;
886 /* clear deleted jewels */
887 for(i=1; i<BJ_HEIGHT; i++) {
888 for(j=0; j<BJ_WIDTH; j++) {
889 if(bj->playboard[i][j].delete) {
890 bj->playboard[i][j].delete = false;
891 bj->playboard[i][j].type = 0;
896 return points;
899 /*****************************************************************************
900 * jewels_runboard() runs the board until it settles in a fixed state and
901 * returns points earned.
902 ******************************************************************************/
903 static unsigned int jewels_runboard(struct game_context* bj) {
904 unsigned int points = 0;
905 unsigned int ret;
907 bj->segments = 0;
909 while((ret = jewels_clearjewels(bj)) > 0) {
910 points += ret;
911 jewels_drawboard(bj);
912 jewels_putjewels(bj);
915 return points;
918 /*****************************************************************************
919 * jewels_swapjewels() swaps two jewels as long as it results in points and
920 * returns points earned.
921 ******************************************************************************/
922 static unsigned int jewels_swapjewels(struct game_context* bj,
923 int x, int y, int direc) {
924 int k;
925 int horzmod, vertmod;
926 int movelen = 0;
927 bool undo = false;
928 unsigned int points = 0;
929 long lasttick, currenttick;
931 /* check for invalid parameters */
932 if(x < 0 || x >= BJ_WIDTH || y < 0 || y >= BJ_HEIGHT-1 ||
933 direc < SWAP_UP || direc > SWAP_LEFT) {
934 return 0;
937 /* check for invalid directions */
938 if((x == 0 && direc == SWAP_LEFT) ||
939 (x == BJ_WIDTH-1 && direc == SWAP_RIGHT) ||
940 (y == 0 && direc == SWAP_UP) ||
941 (y == BJ_HEIGHT-2 && direc == SWAP_DOWN)) {
942 return 0;
945 /* set direction variables */
946 horzmod = 0;
947 vertmod = 0;
948 switch(direc) {
949 case SWAP_UP:
950 vertmod = -1;
951 movelen = TILE_HEIGHT;
952 break;
953 case SWAP_RIGHT:
954 horzmod = 1;
955 movelen = TILE_WIDTH;
956 break;
957 case SWAP_DOWN:
958 vertmod = 1;
959 movelen = TILE_HEIGHT;
960 break;
961 case SWAP_LEFT:
962 horzmod = -1;
963 movelen = TILE_WIDTH;
964 break;
967 while(true) {
968 lasttick = *rb->current_tick;
970 /* animate swapping jewels */
971 for(k=0; k<=8; k++) {
972 /* clear old position */
973 #ifdef HAVE_LCD_COLOR
974 rb->lcd_set_foreground(jewels_bkgd[(x+y)%2]);
975 rb->lcd_fillrect(x*TILE_WIDTH,
976 y*TILE_HEIGHT+YOFS,
977 TILE_WIDTH, TILE_HEIGHT);
978 rb->lcd_set_foreground(jewels_bkgd[(x+horzmod+y+vertmod)%2]);
979 rb->lcd_fillrect((x+horzmod)*TILE_WIDTH,
980 (y+vertmod)*TILE_HEIGHT+YOFS,
981 TILE_WIDTH, TILE_HEIGHT);
982 #else
983 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
984 rb->lcd_fillrect(x*TILE_WIDTH,
985 y*TILE_HEIGHT+YOFS,
986 TILE_WIDTH, TILE_HEIGHT);
987 rb->lcd_fillrect((x+horzmod)*TILE_WIDTH,
988 (y+vertmod)*TILE_HEIGHT+YOFS,
989 TILE_WIDTH, TILE_HEIGHT);
990 rb->lcd_set_drawmode(DRMODE_SOLID);
991 #endif
992 /* draw new position */
993 #ifdef HAVE_LCD_COLOR
994 rb->lcd_bitmap_transparent_part(jewels,
995 0, TILE_HEIGHT*(bj->playboard
996 [y+1+vertmod][x+horzmod].type),
997 STRIDE( SCREEN_MAIN,
998 BMPWIDTH_jewels, BMPHEIGHT_jewels),
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 STRIDE( SCREEN_MAIN,
1007 BMPWIDTH_jewels, BMPHEIGHT_jewels),
1008 x*TILE_WIDTH+horzmod*
1009 ((((movelen<<10)*k)/8)>>10),
1010 y*TILE_HEIGHT+vertmod*
1011 ((((movelen<<10)*k)/8)>>10)+YOFS,
1012 TILE_WIDTH, TILE_HEIGHT);
1013 #else
1014 rb->lcd_bitmap_part(jewels,
1015 0, TILE_HEIGHT*(bj->playboard
1016 [y+1+vertmod][x+horzmod].type),
1017 STRIDE( SCREEN_MAIN,
1018 BMPWIDTH_jewels, BMPHEIGHT_jewels),
1019 (x+horzmod)*TILE_WIDTH-horzmod*
1020 ((((movelen<<10)*k)/8)>>10),
1021 (y+vertmod)*TILE_HEIGHT-vertmod*
1022 ((((movelen<<10)*k)/8)>>10)+YOFS,
1023 TILE_WIDTH, TILE_HEIGHT);
1024 rb->lcd_set_drawmode(DRMODE_FG);
1025 rb->lcd_bitmap_part(jewels,
1026 0, TILE_HEIGHT*(bj->playboard[y+1][x].type),
1027 STRIDE( SCREEN_MAIN,
1028 BMPWIDTH_jewels, BMPHEIGHT_jewels),
1029 x*TILE_WIDTH+horzmod*
1030 ((((movelen<<10)*k)/8)>>10),
1031 y*TILE_HEIGHT+vertmod*
1032 ((((movelen<<10)*k)/8)>>10)+YOFS,
1033 TILE_WIDTH, TILE_HEIGHT);
1034 rb->lcd_set_drawmode(DRMODE_SOLID);
1035 #endif
1037 rb->lcd_update_rect(0, 0, TILE_WIDTH*8, LCD_HEIGHT);
1038 jewels_setcolors();
1040 /* framerate limiting */
1041 currenttick = *rb->current_tick;
1042 if(currenttick-lasttick < HZ/MAX_FPS) {
1043 rb->sleep((HZ/MAX_FPS)-(currenttick-lasttick));
1044 } else {
1045 rb->yield();
1047 lasttick = currenttick;
1050 /* swap jewels */
1051 int temp = bj->playboard[y+1][x].type;
1052 bj->playboard[y+1][x].type =
1053 bj->playboard[y+1+vertmod][x+horzmod].type;
1054 bj->playboard[y+1+vertmod][x+horzmod].type = temp;
1056 if(undo) break;
1058 points = jewels_runboard(bj);
1059 if(points == 0) {
1060 undo = true;
1061 } else {
1062 break;
1066 return points;
1069 /*****************************************************************************
1070 * jewels_movesavail() uses pattern matching to see if there are any
1071 * available move left.
1072 ******************************************************************************/
1073 static bool jewels_movesavail(struct game_context* bj) {
1074 int i, j;
1075 bool moves = false;
1076 int mytype;
1078 for(i=1; i<BJ_HEIGHT; i++) {
1079 for(j=0; j<BJ_WIDTH; j++) {
1080 mytype = bj->playboard[i][j].type;
1081 if(mytype == 0 || mytype > MAX_NUM_JEWELS) continue;
1083 /* check horizontal patterns */
1084 if(j <= BJ_WIDTH-3) {
1085 if(i > 1) {
1086 if(bj->playboard[i-1][j+1].type == mytype) {
1087 if(bj->playboard[i-1][j+2].type == mytype)
1088 {moves = true; break;}
1089 if(bj->playboard[i][j+2].type == mytype)
1090 {moves = true; break;}
1092 if(bj->playboard[i][j+1].type == mytype) {
1093 if(bj->playboard[i-1][j+2].type == mytype)
1094 {moves = true; break;}
1098 if(j <= BJ_WIDTH-4) {
1099 if(bj->playboard[i][j+3].type == mytype) {
1100 if(bj->playboard[i][j+1].type == mytype)
1101 {moves = true; break;}
1102 if(bj->playboard[i][j+2].type == mytype)
1103 {moves = true; break;}
1107 if(i < BJ_HEIGHT-1) {
1108 if(bj->playboard[i][j+1].type == mytype) {
1109 if(bj->playboard[i+1][j+2].type == mytype)
1110 {moves = true; break;}
1112 if(bj->playboard[i+1][j+1].type == mytype) {
1113 if(bj->playboard[i][j+2].type == mytype)
1114 {moves = true; break;}
1115 if(bj->playboard[i+1][j+2].type == mytype)
1116 {moves = true; break;}
1121 /* check vertical patterns */
1122 if(i <= BJ_HEIGHT-3) {
1123 if(j > 0) {
1124 if(bj->playboard[i+1][j-1].type == mytype) {
1125 if(bj->playboard[i+2][j-1].type == mytype)
1126 {moves = true; break;}
1127 if(bj->playboard[i+2][j].type == mytype)
1128 {moves = true; break;}
1130 if(bj->playboard[i+1][j].type == mytype) {
1131 if(bj->playboard[i+2][j-1].type == mytype)
1132 {moves = true; break;}
1136 if(i <= BJ_HEIGHT-4) {
1137 if(bj->playboard[i+3][j].type == mytype) {
1138 if(bj->playboard[i+1][j].type == mytype)
1139 {moves = true; break;}
1140 if(bj->playboard[i+2][j].type == mytype)
1141 {moves = true; break;}
1145 if(j < BJ_WIDTH-1) {
1146 if(bj->playboard[i+1][j].type == mytype) {
1147 if(bj->playboard[i+2][j+1].type == mytype)
1148 {moves = true; break;}
1150 if(bj->playboard[i+1][j+1].type == mytype) {
1151 if(bj->playboard[i+2][j].type == mytype)
1152 {moves = true; break;}
1153 if (bj->playboard[i+2][j+1].type == mytype)
1154 {moves = true; break;}
1159 if(moves) break;
1161 return moves;
1164 /*****************************************************************************
1165 * jewels_puzzle_is_finished() checks if the puzzle is finished.
1166 ******************************************************************************/
1167 static bool jewels_puzzle_is_finished(struct game_context* bj) {
1168 unsigned int i, j;
1169 for(i=0; i<BJ_HEIGHT; i++) {
1170 for(j=0; j<BJ_WIDTH; j++) {
1171 int mytype = bj->playboard[i][j].type;
1172 if(mytype>MAX_NUM_JEWELS) {
1173 mytype -= MAX_NUM_JEWELS;
1174 if(mytype&PUZZLE_TILE_UP) {
1175 if(i==0 || bj->playboard[i-1][j].type<=MAX_NUM_JEWELS ||
1176 !((bj->playboard[i-1][j].type-MAX_NUM_JEWELS)
1177 &PUZZLE_TILE_DOWN))
1178 return false;
1180 if(mytype&PUZZLE_TILE_DOWN) {
1181 if(i==BJ_HEIGHT-1 ||
1182 bj->playboard[i+1][j].type<=MAX_NUM_JEWELS ||
1183 !((bj->playboard[i+1][j].type-MAX_NUM_JEWELS)
1184 &PUZZLE_TILE_UP))
1185 return false;
1187 if(mytype&PUZZLE_TILE_LEFT) {
1188 if(j==0 || bj->playboard[i][j-1].type<=MAX_NUM_JEWELS ||
1189 !((bj->playboard[i][j-1].type-MAX_NUM_JEWELS)
1190 &PUZZLE_TILE_RIGHT))
1191 return false;
1193 if(mytype&PUZZLE_TILE_RIGHT) {
1194 if(j==BJ_WIDTH-1 ||
1195 bj->playboard[i][j+1].type<=MAX_NUM_JEWELS ||
1196 !((bj->playboard[i][j+1].type-MAX_NUM_JEWELS)
1197 &PUZZLE_TILE_LEFT))
1198 return false;
1203 return true;
1206 /*****************************************************************************
1207 * jewels_initlevel() initialises a level.
1208 ******************************************************************************/
1209 static unsigned int jewels_initlevel(struct game_context* bj) {
1210 unsigned int points = 0;
1212 switch(bj->type) {
1213 case GAME_TYPE_NORMAL:
1214 bj->num_jewels = MAX_NUM_JEWELS;
1215 break;
1217 case GAME_TYPE_PUZZLE:
1219 unsigned int i, j;
1220 struct puzzle_tile *tile;
1222 bj->num_jewels = puzzle_levels[bj->level-1].num_jewels;
1224 for(i=0; i<BJ_HEIGHT; i++) {
1225 for(j=0; j<BJ_WIDTH; j++) {
1226 bj->playboard[i][j].type = (rb->rand()%bj->num_jewels)+1;
1227 bj->playboard[i][j].falling = false;
1228 bj->playboard[i][j].delete = false;
1231 jewels_runboard(bj);
1232 tile = puzzle_levels[bj->level-1].tiles;
1233 for(i=0; i<puzzle_levels[bj->level-1].num_tiles; i++, tile++) {
1234 bj->playboard[tile->y+1][tile->x].type = MAX_NUM_JEWELS
1235 +tile->tile_type;
1238 break;
1241 jewels_drawboard(bj);
1243 /* run the play board */
1244 jewels_putjewels(bj);
1245 points += jewels_runboard(bj);
1246 return points;
1249 /*****************************************************************************
1250 * jewels_init() initializes jewels data structures.
1251 ******************************************************************************/
1252 static void jewels_init(struct game_context* bj) {
1253 /* seed the rand generator */
1254 rb->srand(*rb->current_tick);
1256 bj->type = bj->tmp_type;
1257 bj->level = 1;
1258 bj->score = 0;
1259 bj->segments = 0;
1261 jewels_setcolors();
1263 /* clear playing board */
1264 rb->memset(bj->playboard, 0, sizeof(bj->playboard));
1265 do {
1266 bj->score += jewels_initlevel(bj);
1267 } while(!jewels_movesavail(bj));
1270 /*****************************************************************************
1271 * jewels_nextlevel() advances the game to the next bj->level and returns
1272 * points earned.
1273 ******************************************************************************/
1274 static void jewels_nextlevel(struct game_context* bj) {
1275 int i, x, y;
1276 unsigned int points = 0;
1278 switch(bj->type) {
1279 case GAME_TYPE_NORMAL:
1280 /* roll over score, change and display level */
1281 while(bj->score >= LEVEL_PTS) {
1282 bj->score -= LEVEL_PTS;
1283 bj->level++;
1284 rb->splashf(HZ*2, "Level %d", bj->level);
1285 jewels_drawboard(bj);
1288 /* randomly clear some jewels */
1289 for(i=0; i<16; i++) {
1290 x = rb->rand()%8;
1291 y = rb->rand()%8;
1293 if(bj->playboard[y][x].type != 0) {
1294 points++;
1295 bj->playboard[y][x].type = 0;
1298 break;
1300 case GAME_TYPE_PUZZLE:
1301 bj->level++;
1302 rb->splashf(HZ*2, "Level %d", bj->level);
1303 break;
1306 points += jewels_initlevel(bj);
1307 bj->score += points;
1310 static bool jewels_help(void)
1312 static char *help_text[] = {
1313 "Jewels", "", "Aim", "",
1314 "Swap", "pairs", "of", "jewels", "to", "form", "connected",
1315 "segments", "of", "three", "or", "more", "of", "the", "same",
1316 "type.", "",
1317 "The", "goal", "of", "the", "game", "is", "to", "score", "as", "many",
1318 "points", "as", "possible", "before", "running", "out", "of",
1319 "available", "moves.", "", "",
1320 "Controls", "",
1321 "Directions",
1322 #ifdef JEWELS_SCROLLWHEEL
1323 "or", "scroll",
1324 #endif
1325 "to", "move", "",
1326 HK_SELECT, "to", "select", "",
1327 HK_CANCEL, "to", "go", "to", "menu"
1329 static struct style_text formation[]={
1330 { 0, TEXT_CENTER|TEXT_UNDERLINE },
1331 { 2, C_RED },
1332 { 42, C_RED },
1333 LAST_STYLE_ITEM
1336 rb->lcd_setfont(FONT_UI);
1337 if (display_text(ARRAYLEN(help_text), help_text, formation, NULL, true))
1338 return true;
1339 rb->lcd_setfont(FONT_SYSFIXED);
1341 return false;
1344 static bool _ingame;
1345 static int jewels_menu_cb(int action, const struct menu_item_ex *this_item)
1347 int i = ((intptr_t)this_item);
1348 if(action == ACTION_REQUEST_MENUITEM
1349 && !_ingame && (i==0 || i==6))
1350 return ACTION_EXIT_MENUITEM;
1351 return action;
1353 /*****************************************************************************
1354 * jewels_game_menu() shows the game menu.
1355 ******************************************************************************/
1356 static int jewels_game_menu(struct game_context* bj, bool ingame)
1358 rb->button_clear_queue();
1359 int choice = 0;
1361 _ingame = ingame;
1363 static struct opt_items mode[] = {
1364 { "Normal", -1 },
1365 { "Puzzle", -1 },
1368 MENUITEM_STRINGLIST (main_menu, "Jewels Menu", jewels_menu_cb,
1369 "Resume Game",
1370 "Start New Game",
1371 "Mode",
1372 "Help",
1373 "High Scores",
1374 "Playback Control",
1375 "Quit without Saving",
1376 "Quit");
1378 while (1) {
1379 switch (rb->do_menu(&main_menu, &choice, NULL, false)) {
1380 case 0:
1381 jewels_setcolors();
1382 if(resume_file)
1383 rb->remove(SAVE_FILE);
1384 return 0;
1385 case 1:
1386 jewels_init(bj);
1387 return 0;
1388 case 2:
1389 rb->set_option("Mode", &bj->tmp_type, INT, mode, 2, NULL);
1390 break;
1391 case 3:
1392 if(jewels_help())
1393 return 1;
1394 break;
1395 case 4:
1396 highscore_show(-1, highscores, NUM_SCORES, true);
1397 break;
1398 case 5:
1399 playback_control(NULL);
1400 break;
1401 case 6:
1402 return 1;
1403 case 7:
1404 if (ingame) {
1405 rb->splash(HZ*1, "Saving game ...");
1406 jewels_savegame(bj);
1408 return 1;
1409 case MENU_ATTACHED_USB:
1410 return 1;
1411 default:
1412 break;
1417 static int jewels_main(struct game_context* bj) {
1418 int button;
1419 int position;
1420 bool selected = false;
1421 bool no_movesavail;
1422 int x=0, y=0;
1424 bool loaded = jewels_loadgame(bj);
1425 resume_file = loaded;
1426 if (jewels_game_menu(bj, loaded)!=0)
1427 return 0;
1429 resume_file = false;
1430 while(true) {
1431 no_movesavail = false;
1433 /* refresh the board */
1434 jewels_drawboard(bj);
1436 /* display the cursor */
1437 if(selected) {
1438 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
1439 rb->lcd_fillrect(x*TILE_WIDTH, y*TILE_HEIGHT+YOFS,
1440 TILE_WIDTH, TILE_HEIGHT);
1441 rb->lcd_set_drawmode(DRMODE_SOLID);
1442 } else {
1443 rb->lcd_drawrect(x*TILE_WIDTH, y*TILE_HEIGHT+YOFS,
1444 TILE_WIDTH, TILE_HEIGHT);
1446 rb->lcd_update_rect(x*TILE_WIDTH, y*TILE_HEIGHT+YOFS,
1447 TILE_WIDTH, TILE_HEIGHT);
1449 /* handle game button presses */
1450 rb->yield();
1451 button = rb->button_get(true);
1452 switch(button){
1453 case JEWELS_LEFT: /* move cursor left */
1454 case (JEWELS_LEFT|BUTTON_REPEAT):
1455 if(selected) {
1456 bj->score += jewels_swapjewels(bj, x, y, SWAP_LEFT);
1457 selected = false;
1458 if (!jewels_movesavail(bj)) no_movesavail = true;
1459 } else {
1460 x = (x+BJ_WIDTH-1)%BJ_WIDTH;
1462 break;
1464 case JEWELS_RIGHT: /* move cursor right */
1465 case (JEWELS_RIGHT|BUTTON_REPEAT):
1466 if(selected) {
1467 bj->score += jewels_swapjewels(bj, x, y, SWAP_RIGHT);
1468 selected = false;
1469 if (!jewels_movesavail(bj)) no_movesavail = true;
1470 } else {
1471 x = (x+1)%BJ_WIDTH;
1473 break;
1475 case JEWELS_DOWN: /* move cursor down */
1476 case (JEWELS_DOWN|BUTTON_REPEAT):
1477 if(selected) {
1478 bj->score += jewels_swapjewels(bj, x, y, SWAP_DOWN);
1479 selected = false;
1480 if (!jewels_movesavail(bj)) no_movesavail = true;
1481 } else {
1482 y = (y+1)%(BJ_HEIGHT-1);
1484 break;
1486 case JEWELS_UP: /* move cursor up */
1487 case (JEWELS_UP|BUTTON_REPEAT):
1488 if(selected) {
1489 bj->score += jewels_swapjewels(bj, x, y, SWAP_UP);
1490 selected = false;
1491 if (!jewels_movesavail(bj)) no_movesavail = true;
1492 } else {
1493 y = (y+(BJ_HEIGHT-1)-1)%(BJ_HEIGHT-1);
1495 break;
1497 #ifdef JEWELS_SCROLLWHEEL
1498 case JEWELS_PREV: /* scroll backwards */
1499 case (JEWELS_PREV|BUTTON_REPEAT):
1500 if(!selected) {
1501 if(x == 0) {
1502 y = (y+(BJ_HEIGHT-1)-1)%(BJ_HEIGHT-1);
1504 x = (x+BJ_WIDTH-1)%BJ_WIDTH;
1506 break;
1508 case JEWELS_NEXT: /* scroll forwards */
1509 case (JEWELS_NEXT|BUTTON_REPEAT):
1510 if(!selected) {
1511 if(x == BJ_WIDTH-1) {
1512 y = (y+1)%(BJ_HEIGHT-1);
1514 x = (x+1)%BJ_WIDTH;
1516 break;
1517 #endif
1519 case JEWELS_SELECT: /* toggle selected */
1520 selected = !selected;
1521 break;
1523 #ifdef JEWELS_RC_CANCEL
1524 case JEWELS_RC_CANCEL:
1525 #endif
1526 case JEWELS_CANCEL: /* end game */
1527 if (jewels_game_menu(bj, true)!=0)
1528 return 0;
1529 break;
1531 default:
1532 if (rb->default_event_handler (button) == SYS_USB_CONNECTED)
1533 return PLUGIN_USB_CONNECTED;
1534 break;
1537 switch(bj->type) {
1538 case GAME_TYPE_NORMAL:
1539 if(bj->score >= LEVEL_PTS)
1540 jewels_nextlevel(bj);
1541 break;
1542 case GAME_TYPE_PUZZLE:
1543 if (jewels_puzzle_is_finished(bj)) {
1544 if (bj->level < NUM_PUZZLE_LEVELS) {
1545 jewels_nextlevel(bj);
1546 } else {
1547 rb->splash(2*HZ, "Congratulations!");
1548 rb->splash(2*HZ, "You have finished the game!");
1549 if (jewels_game_menu(bj, false)!=0) {
1550 return 0;
1553 break;
1557 if (no_movesavail) {
1558 switch(bj->type) {
1559 case GAME_TYPE_NORMAL:
1560 rb->splash(HZ*2, "Game Over!");
1561 rb->lcd_clear_display();
1562 bj->score += (bj->level-1)*LEVEL_PTS;
1563 position=highscore_update(bj->score, bj->level, "",
1564 highscores, NUM_SCORES);
1565 if (position != -1)
1567 if (position == 0)
1568 rb->splash(HZ*2, "New High Score");
1569 highscore_show(position, highscores, NUM_SCORES, true);
1571 break;
1572 case GAME_TYPE_PUZZLE:
1573 rb->splash(2*HZ, "Game Over");
1574 break;
1576 if (jewels_game_menu(bj, false)!=0) {
1577 return 0;
1583 /* this is the plugin entry point */
1584 enum plugin_status plugin_start(const void* parameter)
1586 (void)parameter;
1588 /* load high scores */
1589 highscore_load(SCORE_FILE, highscores, NUM_SCORES);
1591 rb->lcd_setfont(FONT_SYSFIXED);
1592 #if LCD_DEPTH > 1
1593 rb->lcd_set_backdrop(NULL);
1594 #endif
1596 struct game_context bj;
1597 bj.tmp_type = GAME_TYPE_NORMAL;
1598 jewels_main(&bj);
1599 highscore_save(SCORE_FILE, highscores, NUM_SCORES);
1600 rb->lcd_setfont(FONT_UI);
1602 return PLUGIN_OK;
1605 #endif /* HAVE_LCD_BITMAP */