summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdam Boot <rotator@gmail.com>2005-11-05 17:18:52 +0000
committerAdam Boot <rotator@gmail.com>2005-11-05 17:18:52 +0000
commit63fbc0729f66ad55413579da4cb93b9ea51db223 (patch)
tree78761703541d167e266ac0bb4011095d4f46c5af
parent0bfcabeafdf31dacf60eb4d61085087dc4206e3e (diff)
downloadrockbox-63fbc0729f66ad55413579da4cb93b9ea51db223.tar.gz
rockbox-63fbc0729f66ad55413579da4cb93b9ea51db223.zip
New plugin: Bejeweled game for Recorder, Ondio, and iRiver.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@7758 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r--apps/plugins/SOURCES1
-rw-r--r--apps/plugins/jewels.c1055
2 files changed, 1056 insertions, 0 deletions
diff --git a/apps/plugins/SOURCES b/apps/plugins/SOURCES
index efe6439bf0..6c5d6f243f 100644
--- a/apps/plugins/SOURCES
+++ b/apps/plugins/SOURCES
@@ -18,6 +18,7 @@ viewer.c
18dict.c 18dict.c
19 19
20#ifdef HAVE_LCD_BITMAP /* Recorder/Ondio models only */ 20#ifdef HAVE_LCD_BITMAP /* Recorder/Ondio models only */
21bejeweled.c
21bounce.c 22bounce.c
22calculator.c 23calculator.c
23chip8.c 24chip8.c
diff --git a/apps/plugins/jewels.c b/apps/plugins/jewels.c
new file mode 100644
index 0000000000..f5c1000183
--- /dev/null
+++ b/apps/plugins/jewels.c
@@ -0,0 +1,1055 @@
1/***************************************************************************
2* __________ __ ___.
3* Open \______ \ ____ ____ | | _\_ |__ _______ ___
4* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7* \/ \/ \/ \/ \/
8* $Id$
9*
10* Copyright (C) 2005 Adam Boot
11*
12* All files in this archive are subject to the GNU General Public License.
13* See the file COPYING in the source tree root for full license agreement.
14*
15* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16* KIND, either express or implied.
17*
18****************************************************************************/
19
20#include "plugin.h"
21#include "button.h"
22#include "lcd.h"
23
24#ifdef HAVE_LCD_BITMAP
25
26/* save files */
27#define SCORE_FILE PLUGIN_DIR "/bejeweled.score"
28#define SAVE_FILE PLUGIN_DIR "/bejeweled.save"
29
30/* final game return status */
31#define BJ_END 3
32#define BJ_USB 2
33#define BJ_QUIT 1
34#define BJ_LOSE 0
35
36/* button definitions */
37#if CONFIG_KEYPAD == RECORDER_PAD
38#define BEJEWELED_QUIT BUTTON_OFF
39#define BEJEWELED_START BUTTON_ON
40#define BEJEWELED_SELECT BUTTON_PLAY
41#define BEJEWELED_RESUME BUTTON_F1
42
43#elif CONFIG_KEYPAD == ONDIO_PAD
44#define BEJEWELED_QUIT BUTTON_OFF
45#define BEJEWELED_START BUTTON_RIGHT
46#define BEJEWELED_SELECT (BUTTON_MENU|BUTTON_REL)
47#define BEJEWELED_SELECT_PRE BUTTON_MENU
48#define BEJEWELED_RESUME (BUTTON_MENU|BUTTON_OFF)
49
50#elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD)
51#define BEJEWELED_QUIT BUTTON_OFF
52#define BEJEWELED_START BUTTON_ON
53#define BEJEWELED_SELECT BUTTON_SELECT
54#define BEJEWELED_RESUME BUTTON_MODE
55
56#elif
57 #error BEJEWELED: Unsupported keypad
58#endif
59
60/* swap directions */
61#define SWAP_UP 0
62#define SWAP_RIGHT 1
63#define SWAP_DOWN 2
64#define SWAP_LEFT 3
65
66/* play board dimension */
67#define BJ_HEIGHT 9
68#define BJ_WIDTH 8
69
70/* next level threshold */
71#define LEVEL_PTS 100
72
73/* sleep time for animations (1/x seconds) */
74#define FALL_TIMER 30
75#define SWAP_TIMER 30
76
77#if (LCD_HEIGHT == 128) && (LCD_WIDTH == 160)
78/* Use 16x16 tiles */
79
80/* size of a tile */
81#define TILE_SZ 16
82
83/* number of high scores to save */
84#define NUM_SCORES 15
85
86/* bitmaps for the jewels */
87static unsigned char jewel[8][32] = {
88 /* empty */
89 {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
90 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
91 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
92 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
93 /* square */
94 {0x00, 0x00, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
95 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0x00, 0x00,
96 0x00, 0x00, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
97 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x00, 0x00},
98 /* plus */
99 {0x00, 0xe0, 0xe0, 0x60, 0x60, 0x7e, 0x7e, 0x06,
100 0x7e, 0x7e, 0x60, 0x60, 0xe0, 0xe0, 0x00, 0x00,
101 0x00, 0x03, 0x03, 0x03, 0x03, 0x3f, 0x3f, 0x30,
102 0x3f, 0x3f, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00},
103 /* triangle */
104 {0x00, 0x00, 0x00, 0x00, 0xc0, 0xf0, 0x7c, 0x1e,
105 0x7c, 0xf0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00,
106 0x00, 0x30, 0x3c, 0x3f, 0x37, 0x31, 0x30, 0x30,
107 0x30, 0x31, 0x37, 0x3f, 0x3c, 0x30, 0x00, 0x00},
108 /* diamond */
109 {0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe,
110 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80, 0x00, 0x00,
111 0x00, 0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f,
112 0x1f, 0x0f, 0x07, 0x03, 0x01, 0x00, 0x00, 0x00},
113 /* star */
114 {0x00, 0x40, 0xc0, 0xc0, 0xc0, 0xc0, 0xf8, 0xfe,
115 0xf8, 0xc0, 0xc0, 0xc0, 0xc0, 0x40, 0x00, 0x00,
116 0x00, 0x00, 0x00, 0x30, 0x1f, 0x1f, 0x0f, 0x07,
117 0x0f, 0x1f, 0x1f, 0x30, 0x00, 0x00, 0x00, 0x00},
118 /* circle */
119 {0x00, 0xe0, 0xf8, 0xfc, 0x3c, 0x1e, 0x0e, 0x0e,
120 0x0e, 0x1e, 0x3c, 0xfc, 0xf8, 0xe0, 0x00, 0x00,
121 0x00, 0x03, 0x0f, 0x1f, 0x1e, 0x3c, 0x38, 0x38,
122 0x38, 0x3c, 0x1e, 0x1f, 0x0f, 0x03, 0x00, 0x00},
123 /* heart */
124 {0x00, 0x78, 0xfc, 0xfe, 0xfe, 0xfc, 0xf8, 0xf0,
125 0xf8, 0xfc, 0xfe, 0xfe, 0xfc, 0x78, 0x00, 0x00,
126 0x00, 0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f,
127 0x1f, 0x0f, 0x07, 0x03, 0x01, 0x00, 0x00, 0x00}
128};
129
130#elif (LCD_HEIGHT == 64) && (LCD_WIDTH == 112)
131/* Use 8x8 tiles */
132
133/* size of a tile */
134#define TILE_SZ 8
135
136/* number of high scores to save */
137#define NUM_SCORES 8
138
139/* bitmaps for the jewels */
140static unsigned char jewel[8][8] = {
141 /* empty */
142 {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
143 /* square */
144 {0x00, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x00},
145 /* plus */
146 {0x1c, 0x14, 0x77, 0x41, 0x77, 0x14, 0x1c, 0x00},
147 /* triangle */
148 {0x60, 0x78, 0x4e, 0x43, 0x4e, 0x78, 0x60, 0x00},
149 /* diamond */
150 {0x08, 0x1c, 0x3e, 0x7f, 0x3e, 0x1c, 0x08, 0x00},
151 /* star */
152 {0x08, 0x68, 0x3c, 0x1f, 0x3c, 0x68, 0x08, 0x00},
153 /* circle */
154 {0x1c, 0x3e, 0x63, 0x63, 0x63, 0x3e, 0x1c, 0x00},
155 /* heart */
156 {0x0e, 0x1f, 0x3e, 0x7c, 0x3e, 0x1f, 0x0e, 0x00}
157};
158
159#elif
160 #error BEJEWELED: Unsupported LCD size
161#endif
162
163/* global rockbox api */
164static struct plugin_api* rb;
165
166/* the tile struct
167 * type is the jewel number 0-7
168 * falling if the jewel is falling
169 * delete marks the jewel for deletion
170 */
171struct tile {
172 int type;
173 bool falling;
174 bool delete;
175};
176
177/* the game context struct
178 * score is the current level score
179 * segments is the number of cleared segments in the current run
180 * level is the current level
181 * highscores is the list of high scores
182 * resume denotes whether to resume the currently loaded game
183 * dirty denotes whether the high scores are out of sync with the saved file
184 * playboard is the game playing board (first row is hidden)
185 */
186struct game_context {
187 unsigned int score;
188 unsigned int segments;
189 unsigned int level;
190 unsigned short highscores[NUM_SCORES];
191 bool resume;
192 bool dirty;
193 struct tile playboard[BJ_WIDTH][BJ_HEIGHT];
194};
195
196/*****************************************************************************
197* bejeweled_init() initializes bejeweled data structures.
198******************************************************************************/
199void bejeweled_init(struct game_context* bj) {
200 /* seed the rand generator */
201 rb->srand(*rb->current_tick);
202
203 /* check for resumed game */
204 if(bj->resume) {
205 bj->resume = false;
206 return;
207 }
208
209 /* reset scoring */
210 bj->level = 1;
211 bj->score = 0;
212 bj->segments = 0;
213
214 /* clear playing board */
215 rb->memset(bj->playboard, 0, sizeof(bj->playboard));
216}
217
218/*****************************************************************************
219* bejeweled_drawboard() redraws the entire game board.
220******************************************************************************/
221void bejeweled_drawboard(struct game_context* bj) {
222 int i, j;
223 int w, h;
224 unsigned int tempscore;
225 char *title = "Level";
226 char str[6];
227
228 tempscore = (bj->score>LEVEL_PTS ? LEVEL_PTS : bj->score);
229
230 /* clear screen */
231 rb->lcd_clear_display();
232
233 /* draw separator lines */
234 rb->lcd_vline(BJ_WIDTH*TILE_SZ, 0, LCD_HEIGHT);
235 rb->lcd_hline(BJ_WIDTH*TILE_SZ, LCD_WIDTH, 18);
236 rb->lcd_hline(BJ_WIDTH*TILE_SZ, LCD_WIDTH, LCD_HEIGHT-10);
237
238 /* draw progress bar */
239 rb->lcd_fillrect(BJ_WIDTH*TILE_SZ+(LCD_WIDTH-BJ_WIDTH*TILE_SZ)/4,
240 (LCD_HEIGHT-10)-(((LCD_HEIGHT-10)-18)*
241 tempscore/LEVEL_PTS),
242 (LCD_WIDTH-BJ_WIDTH*TILE_SZ)/2,
243 ((LCD_HEIGHT-10)-18)*tempscore/LEVEL_PTS);
244
245 /* dispay playing board */
246 for(i=0; i<BJ_HEIGHT-1; i++){
247 for(j=0; j<BJ_WIDTH; j++){
248 rb->lcd_mono_bitmap(jewel[bj->playboard[j][i+1].type],
249 j*TILE_SZ, i*TILE_SZ, TILE_SZ, TILE_SZ);
250 }
251 }
252
253 /* print text */
254 rb->lcd_getstringsize(title, &w, &h);
255 rb->lcd_putsxy(LCD_WIDTH-(LCD_WIDTH-BJ_WIDTH*TILE_SZ)/2-w/2, 1, title);
256
257 rb->snprintf(str, 4, "%d", bj->level);
258 rb->lcd_getstringsize(str, &w, &h);
259 rb->lcd_putsxy(LCD_WIDTH-(LCD_WIDTH-BJ_WIDTH*TILE_SZ)/2-w/2, 10, str);
260
261 rb->snprintf(str, 6, "%d", (bj->level-1)*LEVEL_PTS+bj->score);
262 rb->lcd_getstringsize(str, &w, &h);
263 rb->lcd_putsxy(LCD_WIDTH-(LCD_WIDTH-BJ_WIDTH*TILE_SZ)/2-w/2,
264 LCD_HEIGHT-8,
265 str);
266
267 rb->lcd_update();
268}
269
270/*****************************************************************************
271* bejeweled_putjewels() makes the jewels fall to fill empty spots and adds
272* new random jewels at the empty spots at the top of each row.
273******************************************************************************/
274void bejeweled_putjewels(struct game_context* bj){
275 int i, j, k;
276 bool mark, done;
277
278 /* loop to make all the jewels fall */
279 while(true) {
280 /* mark falling jewels and add new jewels to hidden top row*/
281 mark = false;
282 done = true;
283 for(j=0; j<BJ_WIDTH; j++) {
284 if(bj->playboard[j][1].type == 0) {
285 bj->playboard[j][0].type = rb->rand()%7+1;
286 }
287 for(i=BJ_HEIGHT-2; i>=0; i--) {
288 if(!mark && bj->playboard[j][i+1].type == 0) {
289 mark = true;
290 done = false;
291 }
292 if(mark) bj->playboard[j][i].falling = true;
293 }
294 /*if(bj->playboard[1][j].falling) {
295 bj->playboard[0][j].type = rb->rand()%7+1;
296 bj->playboard[0][j].falling = true;
297 }*/
298 mark = false;
299 }
300
301 /* break if there are no falling jewels */
302 if(done) break;
303
304 /* animate falling jewels */
305 for(k=TILE_SZ/8; k<=TILE_SZ; k+=TILE_SZ/8) {
306 rb->sleep(HZ/FALL_TIMER);
307 for(i=0; i<BJ_HEIGHT-1; i++) {
308 for(j=0; j<BJ_WIDTH; j++) {
309 if(bj->playboard[j][i].falling &&
310 bj->playboard[j][i].type != 0) {
311 /* clear old position */
312 rb->lcd_mono_bitmap(jewel[0],
313 j*TILE_SZ,
314 (i-1)*TILE_SZ+k-2,
315 TILE_SZ, TILE_SZ);
316 /* draw new position */
317 rb->lcd_mono_bitmap(jewel[bj->playboard[j][i].type],
318 j*TILE_SZ,
319 (i-1)*TILE_SZ+k,
320 TILE_SZ, TILE_SZ);
321 }
322 }
323 }
324 rb->lcd_update();
325 }
326
327 /* shift jewels down */
328 for(j=0; j<BJ_WIDTH; j++) {
329 for(i=BJ_HEIGHT-1; i>=1; i--) {
330 if(bj->playboard[j][i-1].falling) {
331 bj->playboard[j][i].type = bj->playboard[j][i-1].type;
332 }
333 }
334 }
335
336 /* clear out top row */
337 for(j=0; j<BJ_WIDTH; j++) {
338 bj->playboard[j][0].type = 0;
339 }
340
341 /* mark everything not falling */
342 for(i=0; i<BJ_HEIGHT; i++) {
343 for(j=0; j<BJ_WIDTH; j++) {
344 bj->playboard[j][i].falling = false;
345 }
346 }
347 }
348}
349
350/*****************************************************************************
351* bejeweled_clearjewels() finds all the connected rows and columns and
352* calculates and returns the points earned.
353******************************************************************************/
354unsigned int bejeweled_clearjewels(struct game_context* bj) {
355 int i, j;
356 int last, run;
357 unsigned int points = 0;
358
359 /* check for connected rows */
360 for(i=1; i<BJ_HEIGHT; i++) {
361 last = 0;
362 run = 1;
363 for(j=0; j<BJ_WIDTH; j++) {
364 if(bj->playboard[j][i].type == last &&
365 bj->playboard[j][i].type != 0) {
366 run++;
367
368 if(run == 3) {
369 bj->segments++;
370 points += bj->segments;
371 bj->playboard[j][i].delete = true;
372 bj->playboard[j-1][i].delete = true;
373 bj->playboard[j-2][i].delete = true;
374 } else if(run > 3) {
375 points++;
376 bj->playboard[j][i].delete = true;
377 }
378 } else {
379 run = 1;
380 last = bj->playboard[j][i].type;
381 }
382 }
383 }
384
385 /* check for connected columns */
386 for(j=0; j<BJ_WIDTH; j++) {
387 last = 0;
388 run = 1;
389 for(i=1; i<BJ_HEIGHT; i++) {
390 if(bj->playboard[j][i].type != 0 &&
391 bj->playboard[j][i].type == last) {
392 run++;
393
394 if(run == 3) {
395 bj->segments++;
396 points += bj->segments;
397 bj->playboard[j][i].delete = true;
398 bj->playboard[j][i-1].delete = true;
399 bj->playboard[j][i-2].delete = true;
400 } else if(run > 3) {
401 points++;
402 bj->playboard[j][i].delete = true;
403 }
404 } else {
405 run = 1;
406 last = bj->playboard[j][i].type;
407 }
408 }
409 }
410
411 /* clear deleted jewels */
412 for(i=1; i<BJ_HEIGHT; i++) {
413 for(j=0; j<BJ_WIDTH; j++) {
414 if(bj->playboard[j][i].delete) {
415 bj->playboard[j][i].delete = false;
416 bj->playboard[j][i].type = 0;
417 }
418 }
419 }
420
421 bejeweled_drawboard(bj);
422 return points;
423}
424
425/*****************************************************************************
426* bejeweled_runboard() runs the board until it settles in a fixed state and
427* returns points earned.
428******************************************************************************/
429unsigned int bejeweled_runboard(struct game_context* bj) {
430 unsigned int points = 0;
431 unsigned int ret;
432
433 bj->segments = 0;
434
435 while((ret = bejeweled_clearjewels(bj)) > 0) {
436 points += ret;
437 bejeweled_putjewels(bj);
438 }
439
440 return points;
441}
442
443/*****************************************************************************
444* bejeweled_swapjewels() swaps two jewels as long as it results in points and
445* returns points earned.
446******************************************************************************/
447unsigned int bejeweled_swapjewels(struct game_context* bj,
448 int x, int y, int direc) {
449 int k;
450 int horzmod, vertmod;
451 bool undo = false;
452 unsigned int points = 0;
453
454 /* check for invalid parameters */
455 if(x < 0 || x >= BJ_WIDTH || y < 0 || y >= BJ_HEIGHT-1 ||
456 direc < SWAP_UP || direc > SWAP_LEFT) return 0;
457
458 /* check for invalid directions */
459 if((x == 0 && direc == SWAP_LEFT) ||
460 (x == BJ_WIDTH-1 && direc == SWAP_RIGHT) ||
461 (y == 0 && direc == SWAP_UP) ||
462 (y == BJ_HEIGHT-2 && direc == SWAP_DOWN)) {
463 return 0;
464 }
465
466 /* set direction variables */
467 horzmod = 0;
468 vertmod = 0;
469 switch(direc) {
470 case SWAP_UP:
471 vertmod = -1; break;
472 case SWAP_RIGHT:
473 horzmod = 1; break;
474 case SWAP_DOWN:
475 vertmod = 1; break;
476 case SWAP_LEFT:
477 horzmod = -1; break;
478 }
479
480 while(true) {
481 /* animate swapping jewels */
482 for(k=TILE_SZ/8; k<=TILE_SZ; k+=TILE_SZ/8) {
483 rb->sleep(HZ/SWAP_TIMER);
484 /* clear old position */
485 rb->lcd_mono_bitmap(jewel[0],
486 x*TILE_SZ+horzmod*(k-TILE_SZ/8),
487 y*TILE_SZ+vertmod*(k-TILE_SZ/8),
488 TILE_SZ, TILE_SZ);
489 rb->lcd_mono_bitmap(jewel[0],
490 (x+horzmod)*TILE_SZ+horzmod*(k-TILE_SZ/8)*-1,
491 (y+vertmod)*TILE_SZ+vertmod*(k-TILE_SZ/8)*-1,
492 TILE_SZ, TILE_SZ);
493 /* draw new position */
494 rb->lcd_mono_bitmap(jewel[bj->playboard[x][y+1].type],
495 x*TILE_SZ+horzmod*k,
496 y*TILE_SZ+vertmod*k,
497 TILE_SZ, TILE_SZ);
498 rb->lcd_set_drawmode(DRMODE_FG);
499 rb->lcd_mono_bitmap(jewel[bj->playboard
500 [x+horzmod][y+1+vertmod].type],
501 (x+horzmod)*TILE_SZ+horzmod*k*-1,
502 (y+vertmod)*TILE_SZ+vertmod*k*-1,
503 TILE_SZ, TILE_SZ);
504 rb->lcd_set_drawmode(DRMODE_SOLID);
505
506 rb->lcd_update();
507 }
508
509 /* swap jewels */
510 int temp = bj->playboard[x][y+1].type;
511 bj->playboard[x][y+1].type =
512 bj->playboard[x+horzmod][y+1+vertmod].type;
513 bj->playboard[x+horzmod][y+1+vertmod].type = temp;
514
515 if(undo) break;
516
517 points = bejeweled_runboard(bj);
518 if(points == 0) {undo = true;} else {break;}
519 }
520
521 return points;
522}
523
524/*****************************************************************************
525* bejeweled_movesavail() uses pattern matching to see if there are any
526* available move left.
527******************************************************************************/
528bool bejeweled_movesavail(struct game_context* bj) {
529 int i, j;
530 bool moves = false;
531 int mytype;
532
533 for(i=1; i<BJ_HEIGHT; i++) {
534 for(j=0; j<BJ_WIDTH; j++) {
535 mytype = bj->playboard[j][i].type;
536
537 /* check horizontal patterns */
538 if(j <= BJ_WIDTH-3) {
539 if(i > 1) {
540 if(bj->playboard[j+1][i-1].type == mytype) {
541 if(bj->playboard[j+2][i-1].type == mytype)
542 {moves = true; break;}
543 if(bj->playboard[j+2][i].type == mytype)
544 {moves = true; break;}
545 }
546 if(bj->playboard[j+1][i].type == mytype) {
547 if(bj->playboard[j+2][i-1].type == mytype)
548 {moves = true; break;}
549 }
550 }
551
552 if(j <= BJ_WIDTH-4) {
553 if(bj->playboard[j+3][i].type == mytype) {
554 if(bj->playboard[j+1][i].type == mytype)
555 {moves = true; break;}
556 if(bj->playboard[j+2][i].type == mytype)
557 {moves = true; break;}
558 }
559 }
560
561 if(i < BJ_HEIGHT-1) {
562 if(bj->playboard[j+1][i].type == mytype) {
563 if(bj->playboard[j+2][i+1].type == mytype)
564 {moves = true; break;}
565 }
566 if(bj->playboard[j+1][i+1].type == mytype) {
567 if(bj->playboard[j+2][i].type == mytype)
568 {moves = true; break;}
569 if(bj->playboard[j+2][i+1].type == mytype)
570 {moves = true; break;}
571 }
572 }
573 }
574
575 /* check vertical patterns */
576 if(i <= BJ_HEIGHT-3) {
577 if(j > 0) {
578 if(bj->playboard[j-1][i+1].type == mytype) {
579 if(bj->playboard[j-1][i+2].type == mytype)
580 {moves = true; break;}
581 if(bj->playboard[j][i+2].type == mytype)
582 {moves = true; break;}
583 }
584 if(bj->playboard[j][i+1].type == mytype) {
585 if(bj->playboard[j-1][i+2].type == mytype)
586 {moves = true; break;}
587 }
588 }
589
590 if(i <= BJ_HEIGHT-4) {
591 if(bj->playboard[j][i+3].type == mytype) {
592 if(bj->playboard[j][i+1].type == mytype)
593 {moves = true; break;}
594 if(bj->playboard[j][i+2].type == mytype)
595 {moves = true; break;}
596 }
597 }
598
599 if(j < BJ_WIDTH-1) {
600 if(bj->playboard[j][i+1].type == mytype) {
601 if(bj->playboard[j+1][i+2].type == mytype)
602 {moves = true; break;}
603 }
604 if(bj->playboard[j+1][i+1].type == mytype) {
605 if(bj->playboard[j][i+2].type == mytype)
606 {moves = true; break;}
607 if (bj->playboard[j+1][i+2].type == mytype)
608 {moves = true; break;}
609 }
610 }
611 }
612 }
613
614 if(moves) break;
615 }
616
617 return moves;
618}
619
620/*****************************************************************************
621* bejeweled_nextlevel() advances the game to the next level and returns
622* points earned.
623******************************************************************************/
624unsigned int bejeweled_nextlevel(struct game_context* bj) {
625 int i, x, y;
626 unsigned int points = 0;
627
628 /* roll over score, change and display level */
629 while(bj->score >= LEVEL_PTS) {
630 bj->score -= LEVEL_PTS;
631 bj->level++;
632 bejeweled_drawboard(bj);
633 rb->splash(HZ*2, true, "Level %d", bj->level);
634 bejeweled_drawboard(bj);
635 }
636
637 /* randomly clear some jewels */
638 for(i=0; i<16; i++) {
639 x = rb->rand()%8;
640 y = rb->rand()%8;
641
642 if(bj->playboard[x][y].type != 0) {
643 points++;
644 bj->playboard[x][y].type = 0;
645 }
646 }
647 bejeweled_drawboard(bj);
648
649 /* run the play board */
650 bejeweled_putjewels(bj);
651 points += bejeweled_runboard(bj);
652 return points;
653}
654
655/*****************************************************************************
656* bejeweld_recordscore() inserts a high score into the high scores list and
657* returns the high score position.
658******************************************************************************/
659 int bejeweled_recordscore(struct game_context* bj) {
660 int i;
661 int position = 0;
662 unsigned short current, temp;
663
664 /* calculate total score */
665 current = (bj->level-1)*LEVEL_PTS+bj->score;
666 if(current <= 0) return 0;
667
668 /* insert the current score into the high scores */
669 for(i=0; i<NUM_SCORES; i++) {
670 if(current >= bj->highscores[i]) {
671 if(!position) {
672 position = i+1;
673 bj->dirty = true;
674 }
675 temp = bj->highscores[i];
676 bj->highscores[i] = current;
677 current = temp;
678 }
679 }
680
681 return position;
682 }
683
684/*****************************************************************************
685* bejeweled_loadscores() loads the high scores saved file.
686******************************************************************************/
687void bejeweled_loadscores(struct game_context* bj) {
688 int fd;
689
690 bj->dirty = false;
691
692 /* clear high scores */
693 rb->memset(bj->highscores, 0, sizeof(bj->highscores));
694
695 /* open scores file */
696 fd = rb->open(SCORE_FILE, O_RDONLY);
697 if(fd < 0) return;
698
699 /* read in high scores */
700 if(rb->read(fd, bj->highscores, sizeof(bj->highscores)) <= 0) {
701 /* scores are bad, reset */
702 rb->memset(bj->highscores, 0, sizeof(bj->highscores));
703 }
704
705 rb->close(fd);
706}
707
708/*****************************************************************************
709* bejeweled_savescores() saves the high scores.
710******************************************************************************/
711void bejeweled_savescores(struct game_context* bj) {
712 int fd;
713
714 /* write out the high scores to the save file */
715 fd = rb->open(SCORE_FILE, O_WRONLY|O_CREAT);
716 rb->write(fd, bj->highscores, sizeof(bj->highscores));
717 rb->close(fd);
718 bj->dirty = false;
719}
720
721/*****************************************************************************
722* bejeweled_loadgame() loads the saved game and returns load success.
723******************************************************************************/
724bool bejeweled_loadgame(struct game_context* bj) {
725 int fd;
726 bool loaded = false;
727
728 /* open game file */
729 fd = rb->open(SAVE_FILE, O_RDONLY);
730 if(fd < 0) return loaded;
731
732 /* read in saved game */
733 while(true) {
734 if(rb->read(fd, &bj->score, sizeof(bj->score)) <= 0) break;
735 if(rb->read(fd, &bj->level, sizeof(bj->level)) <= 0) break;
736 if(rb->read(fd, bj->playboard, sizeof(bj->playboard)) <= 0) break;
737 bj->resume = true;
738 loaded = true;
739 break;
740 }
741
742 rb->close(fd);
743
744 /* delete saved file */
745 rb->remove(SAVE_FILE);
746 return loaded;
747}
748
749/*****************************************************************************
750* bejeweled_savegame() saves the current game state.
751******************************************************************************/
752void bejeweled_savegame(struct game_context* bj) {
753 int fd;
754
755 /* write out the game state to the save file */
756 fd = rb->open(SAVE_FILE, O_WRONLY|O_CREAT);
757 rb->write(fd, &bj->score, sizeof(bj->score));
758 rb->write(fd, &bj->level, sizeof(bj->level));
759 rb->write(fd, bj->playboard, sizeof(bj->playboard));
760 rb->close(fd);
761
762 bj->resume = true;
763}
764
765/*****************************************************************************
766* bejeweled_callback() is the default event handler callback which is called
767* on usb connect and shutdown.
768******************************************************************************/
769void bejeweled_callback(void* param) {
770 struct game_context* bj = (struct game_context*) param;
771 if(bj->dirty) {
772 rb->splash(HZ, true, "Saving high scores...");
773 bejeweled_savescores(bj);
774 }
775}
776
777/*****************************************************************************
778* bejeweled() is the main game subroutine, it returns the final game status.
779******************************************************************************/
780int bejeweled(struct game_context* bj) {
781 int i, j;
782 int w, h;
783 int button;
784 int lastbutton = BUTTON_NONE;
785 char str[18];
786 char *title = "Bejeweled";
787 bool breakout = false;
788 bool showscores = false;
789 bool selected = false;
790
791 /* the cursor coordinates */
792 int x=0, y=0;
793
794 /* don't resume by deafult */
795 bj->resume = false;
796
797 /********************
798 * menu *
799 ********************/
800 while(true){
801 rb->lcd_clear_display();
802
803 if(!showscores) {
804 /* welcome screen to display key bindings */
805 rb->lcd_getstringsize(title, &w, &h);
806 rb->lcd_putsxy((LCD_WIDTH-w)/2, 0, title);
807#if CONFIG_KEYPAD == RECORDER_PAD
808 rb->lcd_puts(0, 1, "ON to start");
809 rb->lcd_puts(0, 2, "F1 to save/resume");
810 rb->lcd_puts(0, 3, "OFF to exit");
811 rb->lcd_puts(0, 4, "PLAY to select");
812 rb->lcd_puts(0, 5, "& show high scores");
813 rb->lcd_puts(0, 6, "Directions to move");
814 rb->snprintf(str, 18, "High Score: %d", bj->highscores[0]);
815 rb->lcd_puts(0, 7, str);
816#elif CONFIG_KEYPAD == ONDIO_PAD
817 rb->lcd_puts(0, 1, "RIGHT to start");
818 rb->lcd_puts(0, 2, "MENU+OFF to sv/res");
819 rb->lcd_puts(0, 3, "OFF to exit");
820 rb->lcd_puts(0, 4, "MENU to select");
821 rb->lcd_puts(0, 5, "& show high scores");
822 rb->lcd_puts(0, 6, "Directions to move");
823 rb->snprintf(str, 18, "High Score: %d", bj->highscores[0]);
824 rb->lcd_puts(0, 7, str);
825#elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD)
826 rb->lcd_puts(0, 2, "ON to start");
827 rb->lcd_puts(0, 3, "MODE to save/resume");
828 rb->lcd_puts(0, 4, "OFF to exit");
829 rb->lcd_puts(0, 5, "SELECT to select");
830 rb->lcd_puts(0, 6, " and show high scores");
831 rb->lcd_puts(0, 7, "Directions to move");
832 rb->snprintf(str, 18, "High Score: %d", bj->highscores[0]);
833 rb->lcd_puts(0, 9, str);
834#endif
835 } else {
836 /* room for a title? */
837 j = 0;
838 if(LCD_HEIGHT-NUM_SCORES*8 >= 8) {
839 rb->snprintf(str, 12, "%s", "High Scores");
840 rb->lcd_getstringsize(str, &w, &h);
841 rb->lcd_putsxy((LCD_WIDTH-w)/2, 0, str);
842 j = 1;
843 }
844
845 /* print high scores */
846 for(i=0; i<NUM_SCORES; i++) {
847 rb->snprintf(str, 11, "#%02d: %d", i+1, bj->highscores[i]);
848 rb->lcd_puts(0, i+j, str);
849 }
850 }
851
852 rb->lcd_update();
853
854 /* handle menu button presses */
855 button = rb->button_get(true);
856 switch(button){
857 case BEJEWELED_START: /* start playing */
858 breakout = true;
859 break;
860
861 case BEJEWELED_QUIT: /* quit program */
862 if(showscores) {
863 showscores = 0;
864 break;
865 }
866 return BJ_QUIT;
867
868 case BEJEWELED_RESUME:/* resume game */
869 if(!bejeweled_loadgame(bj)) {
870 rb->splash(HZ*2, true, "Nothing to resume");
871 } else {
872 breakout = true;
873 }
874 break;
875
876 case BEJEWELED_SELECT:/* toggle high scores */
877#ifdef BEJEWELED_SELECT_PRE
878 if(lastbutton != BEJEWELED_SELECT_PRE) break;
879#endif
880 showscores ^= 1;
881 break;
882
883 default:
884 if(rb->default_event_handler_ex(button, bejeweled_callback,
885 (void*) bj) == SYS_USB_CONNECTED)
886 return BJ_USB;
887 break;
888 }
889
890 if(breakout) break;
891 if(button != BUTTON_NONE) lastbutton = button;
892 }
893
894 lastbutton = BUTTON_NONE;
895
896 /********************
897 * init *
898 ********************/
899 bejeweled_init(bj);
900
901 /********************
902 * setup the board *
903 ********************/
904 bejeweled_drawboard(bj);
905 bejeweled_putjewels(bj);
906 bj->score += bejeweled_runboard(bj);
907
908 /**********************
909 * play *
910 **********************/
911 while(true) {
912 /* refresh the board */
913 bejeweled_drawboard(bj);
914
915 /* display the cursor */
916 if(selected) {
917 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
918 rb->lcd_fillrect(x*TILE_SZ, y*TILE_SZ, TILE_SZ, TILE_SZ);
919 rb->lcd_set_drawmode(DRMODE_SOLID);
920 } else {
921 rb->lcd_drawrect(x*TILE_SZ, y*TILE_SZ, TILE_SZ, TILE_SZ);
922 }
923 rb->lcd_update_rect(x*TILE_SZ, y*TILE_SZ, TILE_SZ, TILE_SZ);
924
925 /* handle game button presses */
926 button = rb->button_get(true);
927 switch(button){
928 case BEJEWELED_RESUME: /* save and end game */
929 rb->splash(HZ, true, "Saving game...");
930 bejeweled_savegame(bj);
931 /* fall through to BEJEWELED_QUIT */
932
933 case BEJEWELED_QUIT: /* end game */
934 return BJ_END;
935
936 case BUTTON_LEFT: /* move cursor left */
937 case (BUTTON_LEFT|BUTTON_REPEAT):
938 if(selected) {
939 bj->score += bejeweled_swapjewels(bj, x, y, SWAP_LEFT);
940 selected = false;
941 if (!bejeweled_movesavail(bj)) return BJ_LOSE;
942 } else {
943 x = (x+BJ_WIDTH-1)%BJ_WIDTH;
944 }
945 break;
946
947 case BUTTON_RIGHT: /* move cursor right */
948 case (BUTTON_RIGHT|BUTTON_REPEAT):
949 if(selected) {
950 bj->score += bejeweled_swapjewels(bj, x, y, SWAP_RIGHT);
951 selected = false;
952 if (!bejeweled_movesavail(bj)) return BJ_LOSE;
953 } else {
954 x = (x+1)%BJ_WIDTH;
955 }
956 break;
957
958 case BUTTON_DOWN: /* move cursor down */
959 case (BUTTON_DOWN|BUTTON_REPEAT):
960 if(selected) {
961 bj->score += bejeweled_swapjewels(bj, x, y, SWAP_DOWN);
962 selected = false;
963 if (!bejeweled_movesavail(bj)) return BJ_LOSE;
964 } else {
965 y = (y+1)%(BJ_HEIGHT-1);
966 }
967 break;
968
969 case BUTTON_UP: /* move cursor up */
970 case (BUTTON_UP|BUTTON_REPEAT):
971 if(selected) {
972 bj->score += bejeweled_swapjewels(bj, x, y, SWAP_UP);
973 selected = false;
974 if (!bejeweled_movesavail(bj)) return BJ_LOSE;
975 } else {
976 y = (y+(BJ_HEIGHT-1)-1)%(BJ_HEIGHT-1);
977 }
978 break;
979
980 case BEJEWELED_SELECT: /* toggle selected */
981#ifdef BEJEWELED_SELECT_PRE
982 if(lastbutton != BEJEWELED_SELECT_PRE) break;
983#endif
984 selected ^= 1;
985 break;
986
987 default:
988 if(rb->default_event_handler_ex(button, bejeweled_callback,
989 (void*) bj) == SYS_USB_CONNECTED)
990 return BJ_USB;
991 break;
992 }
993
994 if(button != BUTTON_NONE) lastbutton = button;
995 if(bj->score >= LEVEL_PTS) bj->score = bejeweled_nextlevel(bj);
996 }
997}
998
999/*****************************************************************************
1000* plugin entry point.
1001******************************************************************************/
1002enum plugin_status plugin_start(struct plugin_api* api, void* parameter) {
1003 struct game_context bj;
1004 bool exit = false;
1005 int position;
1006 char str[19];
1007
1008 /* plugin init */
1009 TEST_PLUGIN_API(api);
1010 (void)parameter;
1011 rb = api;
1012 /* end of plugin init */
1013
1014 /* load high scores */
1015 bejeweled_loadscores(&bj);
1016
1017 rb->lcd_setfont(FONT_SYSFIXED);
1018
1019 while(!exit) {
1020 switch(bejeweled(&bj)){
1021 case BJ_LOSE:
1022 rb->splash(HZ*2, true, "No more moves!");
1023 /* fall through to BJ_END */
1024
1025 case BJ_END:
1026 if(!bj.resume) {
1027 if((position = bejeweled_recordscore(&bj))) {
1028 rb->snprintf(str, 19, "New high score #%d!", position);
1029 rb->splash(HZ*2, true, str);
1030 }
1031 }
1032 break;
1033
1034 case BJ_USB:
1035 rb->lcd_setfont(FONT_UI);
1036 return PLUGIN_USB_CONNECTED;
1037
1038 case BJ_QUIT:
1039 if(bj.dirty) {
1040 rb->splash(HZ, true, "Saving high scores...");
1041 bejeweled_savescores(&bj);
1042 }
1043 exit = true;
1044 break;
1045
1046 default:
1047 break;
1048 }
1049 }
1050
1051 rb->lcd_setfont(FONT_UI);
1052 return PLUGIN_OK;
1053}
1054
1055#endif