A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita
audio
rust
zig
deno
mpris
rockbox
mpd
1/***************************************************************************
2* __________ __ ___.
3* Open \______ \ ____ ____ | | _\_ |__ _______ ___
4* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7* \/ \/ \/ \/ \/
8* $Id$
9*
10* Copyright (C) 2005 Adam Boot
11*
12* Color graphics from Gweled (http://sebdelestaing.free.fr/gweled/)
13*
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.
18*
19* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20* KIND, either express or implied.
21*
22****************************************************************************/
23
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"
29
30/* button definitions */
31#if (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD)
32#define JEWELS_UP BUTTON_UP
33#define JEWELS_DOWN BUTTON_DOWN
34#define JEWELS_LEFT BUTTON_LEFT
35#define JEWELS_RIGHT BUTTON_RIGHT
36#define JEWELS_SELECT BUTTON_SELECT
37#define JEWELS_CANCEL BUTTON_OFF
38#define JEWELS_RC_CANCEL BUTTON_RC_STOP
39#define HK_SELECT "SELECT"
40#define HK_CANCEL "OFF"
41
42#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
43 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
44#define JEWELS_SCROLLWHEEL
45#define JEWELS_UP BUTTON_MENU
46#define JEWELS_DOWN BUTTON_PLAY
47#define JEWELS_LEFT BUTTON_LEFT
48#define JEWELS_RIGHT BUTTON_RIGHT
49#define JEWELS_PREV BUTTON_SCROLL_BACK
50#define JEWELS_NEXT BUTTON_SCROLL_FWD
51#define JEWELS_SELECT BUTTON_SELECT
52#define JEWELS_CANCEL (BUTTON_SELECT | BUTTON_REPEAT)
53#define HK_SELECT "SELECT"
54#define HK_CANCEL "Long SELECT"
55
56#elif (CONFIG_KEYPAD == IPOD_3G_PAD)
57#define JEWELS_LEFT BUTTON_LEFT
58#define JEWELS_RIGHT BUTTON_RIGHT
59#define JEWELS_UP BUTTON_SCROLL_BACK
60#define JEWELS_DOWN BUTTON_SCROLL_FWD
61#define JEWELS_SELECT BUTTON_SELECT
62#define JEWELS_CANCEL BUTTON_MENU
63#define HK_SELECT "SELECT"
64#define HK_CANCEL "MENU"
65
66#elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
67#define JEWELS_UP BUTTON_UP
68#define JEWELS_DOWN BUTTON_DOWN
69#define JEWELS_LEFT BUTTON_LEFT
70#define JEWELS_RIGHT BUTTON_RIGHT
71#define JEWELS_SELECT BUTTON_SELECT
72#define JEWELS_CANCEL BUTTON_POWER
73#define HK_SELECT "SELECT"
74#define HK_CANCEL "POWER"
75
76#elif CONFIG_KEYPAD == GIGABEAT_PAD
77#define JEWELS_UP BUTTON_UP
78#define JEWELS_DOWN BUTTON_DOWN
79#define JEWELS_LEFT BUTTON_LEFT
80#define JEWELS_RIGHT BUTTON_RIGHT
81#define JEWELS_SELECT BUTTON_SELECT
82#define JEWELS_CANCEL BUTTON_POWER
83#define HK_SELECT "SELECT"
84#define HK_CANCEL "POWER"
85
86
87#elif CONFIG_KEYPAD == SONY_NWZ_PAD
88#define JEWELS_UP BUTTON_UP
89#define JEWELS_DOWN BUTTON_DOWN
90#define JEWELS_LEFT BUTTON_LEFT
91#define JEWELS_RIGHT BUTTON_RIGHT
92#define JEWELS_SELECT BUTTON_PLAY
93#define JEWELS_CANCEL BUTTON_BACK
94#define HK_SELECT "PLAY"
95#define HK_CANCEL "BACK"
96
97#elif CONFIG_KEYPAD == CREATIVE_ZEN_PAD
98#define JEWELS_UP BUTTON_UP
99#define JEWELS_DOWN BUTTON_DOWN
100#define JEWELS_LEFT BUTTON_LEFT
101#define JEWELS_RIGHT BUTTON_RIGHT
102#define JEWELS_SELECT BUTTON_SELECT
103#define JEWELS_CANCEL BUTTON_BACK
104#define HK_SELECT "SELECT"
105#define HK_CANCEL "BACK"
106
107
108#elif (CONFIG_KEYPAD == SANSA_E200_PAD) || \
109 (CONFIG_KEYPAD == SANSA_CONNECT_PAD)
110#define JEWELS_SCROLLWHEEL
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_PREV BUTTON_SCROLL_BACK
116#define JEWELS_NEXT BUTTON_SCROLL_FWD
117#define JEWELS_SELECT BUTTON_SELECT
118#define JEWELS_CANCEL BUTTON_POWER
119#define HK_SELECT "SELECT"
120#define HK_CANCEL "POWER"
121
122#elif (CONFIG_KEYPAD == SANSA_FUZE_PAD)
123#define JEWELS_SCROLLWHEEL
124#define JEWELS_UP BUTTON_UP
125#define JEWELS_DOWN BUTTON_DOWN
126#define JEWELS_LEFT BUTTON_LEFT
127#define JEWELS_RIGHT BUTTON_RIGHT
128#define JEWELS_PREV BUTTON_SCROLL_BACK
129#define JEWELS_NEXT BUTTON_SCROLL_FWD
130#define JEWELS_SELECT BUTTON_SELECT
131#define JEWELS_CANCEL (BUTTON_HOME|BUTTON_REPEAT)
132#define HK_SELECT "SELECT"
133#define HK_CANCEL "HOME"
134
135#elif CONFIG_KEYPAD == SANSA_C200_PAD || \
136CONFIG_KEYPAD == SANSA_CLIP_PAD || \
137CONFIG_KEYPAD == SANSA_M200_PAD
138#define JEWELS_UP BUTTON_UP
139#define JEWELS_DOWN BUTTON_DOWN
140#define JEWELS_LEFT BUTTON_LEFT
141#define JEWELS_RIGHT BUTTON_RIGHT
142#define JEWELS_SELECT BUTTON_SELECT
143#define JEWELS_CANCEL BUTTON_POWER
144#define HK_SELECT "SELECT"
145#define HK_CANCEL "POWER"
146
147#elif CONFIG_KEYPAD == IRIVER_H10_PAD
148#define JEWELS_UP BUTTON_SCROLL_UP
149#define JEWELS_DOWN BUTTON_SCROLL_DOWN
150#define JEWELS_LEFT BUTTON_LEFT
151#define JEWELS_RIGHT BUTTON_RIGHT
152#define JEWELS_SELECT BUTTON_PLAY
153#define JEWELS_CANCEL BUTTON_POWER
154#define HK_SELECT "PLAY"
155#define HK_CANCEL "POWER"
156
157#elif CONFIG_KEYPAD == GIGABEAT_S_PAD || \
158 CONFIG_KEYPAD == SAMSUNG_YPR0_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_BACK
165#define HK_SELECT "SELECT"
166#define HK_CANCEL "BACK"
167
168#elif CONFIG_KEYPAD == MROBE100_PAD
169#define JEWELS_UP BUTTON_UP
170#define JEWELS_DOWN BUTTON_DOWN
171#define JEWELS_LEFT BUTTON_LEFT
172#define JEWELS_RIGHT BUTTON_RIGHT
173#define JEWELS_SELECT BUTTON_SELECT
174#define JEWELS_CANCEL BUTTON_POWER
175#define HK_SELECT "SELECT"
176#define HK_CANCEL "POWER"
177
178#elif CONFIG_KEYPAD == IAUDIO_M3_PAD
179#define JEWELS_UP BUTTON_RC_VOL_UP
180#define JEWELS_DOWN BUTTON_RC_VOL_DOWN
181#define JEWELS_LEFT BUTTON_RC_REW
182#define JEWELS_RIGHT BUTTON_RC_FF
183#define JEWELS_SELECT BUTTON_RC_PLAY
184#define JEWELS_CANCEL BUTTON_RC_REC
185#define HK_SELECT "PLAY"
186#define HK_CANCEL "REC"
187
188#define JEWELS_RC_CANCEL BUTTON_REC
189
190#elif CONFIG_KEYPAD == COWON_D2_PAD
191#define JEWELS_CANCEL BUTTON_POWER
192#define HK_CANCEL "POWER"
193
194#elif CONFIG_KEYPAD == CREATIVEZVM_PAD
195#define JEWELS_UP BUTTON_UP
196#define JEWELS_DOWN BUTTON_DOWN
197#define JEWELS_LEFT BUTTON_LEFT
198#define JEWELS_RIGHT BUTTON_RIGHT
199#define JEWELS_SELECT BUTTON_SELECT
200#define JEWELS_CANCEL BUTTON_BACK
201#define HK_SELECT "MIDDLE"
202#define HK_CANCEL "BACK"
203
204#elif CONFIG_KEYPAD == CREATIVE_ZENXFI3_PAD
205#define JEWELS_UP BUTTON_UP
206#define JEWELS_DOWN BUTTON_DOWN
207#define JEWELS_LEFT BUTTON_BACK
208#define JEWELS_RIGHT BUTTON_MENU
209#define JEWELS_SELECT BUTTON_VOL_UP
210#define JEWELS_CANCEL BUTTON_VOL_DOWN
211#define HK_SELECT "VOL+"
212#define HK_CANCEL "VOL-"
213
214#elif CONFIG_KEYPAD == PHILIPS_HDD1630_PAD
215#define JEWELS_UP BUTTON_UP
216#define JEWELS_DOWN BUTTON_DOWN
217#define JEWELS_LEFT BUTTON_LEFT
218#define JEWELS_RIGHT BUTTON_RIGHT
219#define JEWELS_SELECT BUTTON_SELECT
220#define JEWELS_CANCEL BUTTON_POWER
221#define HK_SELECT "SELECT"
222#define HK_CANCEL "POWER"
223
224#elif CONFIG_KEYPAD == PHILIPS_HDD6330_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_PLAY
230#define JEWELS_CANCEL BUTTON_POWER
231#define HK_SELECT "PLAY"
232#define HK_CANCEL "POWER"
233
234#elif CONFIG_KEYPAD == PHILIPS_SA9200_PAD
235#define JEWELS_UP BUTTON_UP
236#define JEWELS_DOWN BUTTON_DOWN
237#define JEWELS_LEFT BUTTON_PREV
238#define JEWELS_RIGHT BUTTON_NEXT
239#define JEWELS_SELECT BUTTON_PLAY
240#define JEWELS_CANCEL BUTTON_POWER
241#define HK_SELECT "PLAY"
242#define HK_CANCEL "POWER"
243
244#elif CONFIG_KEYPAD == ONDAVX747_PAD || \
245CONFIG_KEYPAD == ONDAVX777_PAD || \
246CONFIG_KEYPAD == MROBE500_PAD
247#define JEWELS_CANCEL BUTTON_POWER
248#define HK_CANCEL "POWER"
249
250#elif (CONFIG_KEYPAD == SAMSUNG_YH820_PAD) || \
251 (CONFIG_KEYPAD == SAMSUNG_YH92X_PAD)
252#define JEWELS_UP BUTTON_UP
253#define JEWELS_DOWN BUTTON_DOWN
254#define JEWELS_LEFT BUTTON_LEFT
255#define JEWELS_RIGHT BUTTON_RIGHT
256#define JEWELS_SELECT BUTTON_PLAY
257#define JEWELS_CANCEL BUTTON_REW
258#define HK_SELECT "PLAY"
259#define HK_CANCEL "REWIND"
260
261#elif CONFIG_KEYPAD == PBELL_VIBE500_PAD
262#define JEWELS_UP BUTTON_UP
263#define JEWELS_DOWN BUTTON_DOWN
264#define JEWELS_LEFT BUTTON_PREV
265#define JEWELS_RIGHT BUTTON_NEXT
266#define JEWELS_SELECT BUTTON_OK
267#define JEWELS_CANCEL BUTTON_REC
268#define HK_SELECT "OK"
269#define HK_CANCEL "REC"
270
271#elif CONFIG_KEYPAD == MPIO_HD200_PAD
272#define JEWELS_LEFT BUTTON_VOL_DOWN
273#define JEWELS_RIGHT BUTTON_VOL_UP
274#define JEWELS_UP BUTTON_REW
275#define JEWELS_DOWN BUTTON_FF
276#define JEWELS_SELECT BUTTON_FUNC
277#define JEWELS_CANCEL BUTTON_REC
278#define HK_SELECT "FUNC"
279#define HK_CANCEL "REC"
280
281#elif CONFIG_KEYPAD == MPIO_HD300_PAD
282#define JEWELS_LEFT BUTTON_REW
283#define JEWELS_RIGHT BUTTON_FF
284#define JEWELS_UP BUTTON_UP
285#define JEWELS_DOWN BUTTON_DOWN
286#define JEWELS_SELECT BUTTON_ENTER
287#define JEWELS_CANCEL BUTTON_MENU
288#define HK_SELECT "ENTER"
289#define HK_CANCEL "MENU"
290
291#elif CONFIG_KEYPAD == SANSA_FUZEPLUS_PAD
292#define JEWELS_LEFT BUTTON_LEFT
293#define JEWELS_RIGHT BUTTON_RIGHT
294#define JEWELS_UP BUTTON_UP
295#define JEWELS_DOWN BUTTON_DOWN
296#define JEWELS_SELECT BUTTON_SELECT
297#define JEWELS_CANCEL BUTTON_POWER
298#define HK_SELECT "SELECT"
299#define HK_CANCEL "POWER"
300
301#elif CONFIG_KEYPAD == AGPTEK_ROCKER_PAD
302#define JEWELS_LEFT BUTTON_LEFT
303#define JEWELS_RIGHT BUTTON_RIGHT
304#define JEWELS_UP BUTTON_UP
305#define JEWELS_DOWN BUTTON_DOWN
306#define JEWELS_SELECT BUTTON_SELECT
307#define JEWELS_CANCEL BUTTON_POWER
308#define HK_SELECT "SELECT"
309#define HK_CANCEL "POWER"
310
311#elif (CONFIG_KEYPAD == HM60X_PAD) || \
312 (CONFIG_KEYPAD == HM801_PAD)
313#define JEWELS_LEFT BUTTON_LEFT
314#define JEWELS_RIGHT BUTTON_RIGHT
315#define JEWELS_UP BUTTON_UP
316#define JEWELS_DOWN BUTTON_DOWN
317#define JEWELS_SELECT BUTTON_SELECT
318#define JEWELS_CANCEL BUTTON_POWER
319#define HK_SELECT "SELECT"
320#define HK_CANCEL "POWER"
321
322#elif CONFIG_KEYPAD == DX50_PAD
323#define JEWELS_CANCEL BUTTON_POWER
324#define HK_CANCEL "Power"
325
326#elif CONFIG_KEYPAD == CREATIVE_ZENXFI2_PAD
327#define JEWELS_CANCEL BUTTON_POWER
328#define HK_CANCEL "Power"
329
330#elif CONFIG_KEYPAD == XDUOO_X3_PAD || CONFIG_KEYPAD == XDUOO_X3II_PAD || CONFIG_KEYPAD == XDUOO_X20_PAD
331#define JEWELS_UP BUTTON_HOME
332#define JEWELS_DOWN BUTTON_OPTION
333#define JEWELS_LEFT BUTTON_PREV
334#define JEWELS_RIGHT BUTTON_NEXT
335#define JEWELS_SELECT BUTTON_PLAY
336#define JEWELS_CANCEL BUTTON_POWER
337#define HK_SELECT "PLAY"
338#define HK_CANCEL "POWER"
339
340#elif CONFIG_KEYPAD == FIIO_M3K_LINUX_PAD
341#define JEWELS_UP BUTTON_HOME
342#define JEWELS_DOWN BUTTON_OPTION
343#define JEWELS_LEFT BUTTON_PREV
344#define JEWELS_RIGHT BUTTON_NEXT
345#define JEWELS_SELECT BUTTON_PLAY
346#define JEWELS_CANCEL BUTTON_POWER
347#define HK_SELECT "PLAY"
348#define HK_CANCEL "POWER"
349
350#elif CONFIG_KEYPAD == IHIFI_770_PAD || CONFIG_KEYPAD == IHIFI_800_PAD
351#define JEWELS_UP BUTTON_PREV
352#define JEWELS_DOWN BUTTON_NEXT
353#define JEWELS_LEFT BUTTON_HOME
354#define JEWELS_RIGHT BUTTON_VOL_DOWN
355#define JEWELS_SELECT BUTTON_PLAY
356#define JEWELS_CANCEL BUTTON_POWER
357#define HK_SELECT "PLAY"
358#define HK_CANCEL "POWER"
359
360#elif CONFIG_KEYPAD == EROSQ_PAD
361#define JEWELS_UP BUTTON_PREV
362#define JEWELS_DOWN BUTTON_NEXT
363#define JEWELS_LEFT BUTTON_SCROLL_BACK
364#define JEWELS_RIGHT BUTTON_SCROLL_FWD
365#define JEWELS_SELECT BUTTON_PLAY
366#define JEWELS_CANCEL BUTTON_BACK
367#define HK_SELECT "PLAY"
368#define HK_CANCEL "BACK"
369
370#elif CONFIG_KEYPAD == FIIO_M3K_PAD
371#define JEWELS_UP BUTTON_UP
372#define JEWELS_DOWN BUTTON_DOWN
373#define JEWELS_LEFT BUTTON_LEFT
374#define JEWELS_RIGHT BUTTON_RIGHT
375#define JEWELS_SELECT BUTTON_SELECT
376#define JEWELS_CANCEL BUTTON_BACK
377#define HK_SELECT "SELECT"
378#define HK_CANCEL "BACK"
379
380#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
381/* use touchscreen */
382
383#elif CONFIG_KEYPAD == SDL_PAD
384/* use touchscreen */
385#elif CONFIG_KEYPAD == MA_PAD
386#define JEWELS_UP BUTTON_UP
387#define JEWELS_DOWN BUTTON_DOWN
388#define JEWELS_LEFT BUTTON_LEFT
389#define JEWELS_RIGHT BUTTON_RIGHT
390#define JEWELS_SELECT BUTTON_PLAY
391#define JEWELS_CANCEL BUTTON_BACK
392#define HK_SELECT "PLAY"
393#define HK_CANCEL "BACK"
394
395#elif CONFIG_KEYPAD == RG_NANO_PAD
396#define JEWELS_UP BUTTON_UP
397#define JEWELS_DOWN BUTTON_DOWN
398#define JEWELS_LEFT BUTTON_LEFT
399#define JEWELS_RIGHT BUTTON_RIGHT
400#define JEWELS_SELECT BUTTON_A
401#define JEWELS_CANCEL BUTTON_START
402#define HK_SELECT "A"
403#define HK_CANCEL "START"
404
405#else
406#error No keymap defined!
407#endif
408
409#ifdef HAVE_TOUCHSCREEN
410#ifndef JEWELS_UP
411#define JEWELS_UP BUTTON_TOPMIDDLE
412#endif
413#ifndef JEWELS_DOWN
414#define JEWELS_DOWN BUTTON_BOTTOMMIDDLE
415#endif
416#ifndef JEWELS_LEFT
417#define JEWELS_LEFT BUTTON_MIDLEFT
418#endif
419#ifndef JEWELS_RIGHT
420#define JEWELS_RIGHT BUTTON_MIDRIGHT
421#endif
422#ifndef JEWELS_SELECT
423#define JEWELS_SELECT BUTTON_CENTER
424#define HK_SELECT "CENTER"
425#endif
426#ifndef JEWELS_CANCEL
427#define JEWELS_CANCEL BUTTON_TOPLEFT
428#define HK_CANCEL "TOPLEFT"
429#endif
430#endif
431
432#define TILE_WIDTH BMPWIDTH_jewels
433#define TILE_HEIGHT (BMPHEIGHT_jewels/23)
434
435#if LCD_HEIGHT < LCD_WIDTH
436/* This calculation assumes integer division w/ LCD_HEIGHT/TILE_HEIGHT */
437#define YOFS LCD_HEIGHT-((LCD_HEIGHT/TILE_HEIGHT)*TILE_HEIGHT)
438#else
439#define YOFS 0
440#endif
441
442#define NUM_SCORES 5
443
444/* swap directions */
445#define SWAP_UP 0
446#define SWAP_RIGHT 1
447#define SWAP_DOWN 2
448#define SWAP_LEFT 3
449
450/* play board dimension */
451#define BJ_HEIGHT 9
452#define BJ_WIDTH 8
453
454/* next level threshold */
455#define LEVEL_PTS 100
456
457/* animation frame rate */
458#define MAX_FPS 20
459
460/* text margin */
461#define MARGIN 5
462
463/* Game types */
464enum game_type {
465 GAME_TYPE_NORMAL,
466 GAME_TYPE_PUZZLE
467};
468
469/* external bitmaps */
470extern const fb_data jewels[];
471
472/* tile background colors */
473#ifdef HAVE_LCD_COLOR
474static const unsigned jewels_bkgd[2] = {
475 LCD_RGBPACK(104, 63, 63),
476 LCD_RGBPACK(83, 44, 44)
477};
478#endif
479
480/* the tile struct
481 * type is the jewel number 0-7
482 * falling if the jewel is falling
483 * delete marks the jewel for deletion
484 */
485struct tile {
486 int type;
487 bool falling;
488 bool delete;
489};
490
491/* the game context struct
492 * score is the current level score
493 * segments is the number of cleared segments in the current run
494 * level is the current level
495 * tmp_type is the select type in the menu
496 * type is the game type (normal or puzzle)
497 * playboard is the game playing board (first row is hidden)
498 * num_jewels is the number of different jewels to use
499 */
500struct game_context {
501 unsigned int score;
502 unsigned int segments;
503 unsigned int level;
504 unsigned int type;
505 unsigned int tmp_type;
506 struct tile playboard[BJ_HEIGHT][BJ_WIDTH];
507 unsigned int num_jewels;
508};
509
510#define MAX_NUM_JEWELS 7
511
512#define MAX_PUZZLE_TILES 4
513#define NUM_PUZZLE_LEVELS 10
514
515struct puzzle_tile {
516 int x;
517 int y;
518 int tile_type;
519};
520
521struct puzzle_level {
522 unsigned int num_jewels;
523 unsigned int num_tiles;
524 struct puzzle_tile tiles[MAX_PUZZLE_TILES];
525};
526
527#define PUZZLE_TILE_UP 1
528#define PUZZLE_TILE_DOWN 2
529#define PUZZLE_TILE_LEFT 4
530#define PUZZLE_TILE_RIGHT 8
531
532struct puzzle_level puzzle_levels[NUM_PUZZLE_LEVELS] = {
533 { 5, 2, { {3, 3, PUZZLE_TILE_RIGHT},
534 {4, 2, PUZZLE_TILE_LEFT} } },
535 { 5, 2, { {3, 2, PUZZLE_TILE_DOWN},
536 {3, 4, PUZZLE_TILE_UP} } },
537 { 6, 3, { {3, 2, PUZZLE_TILE_DOWN},
538 {3, 4, PUZZLE_TILE_UP|PUZZLE_TILE_DOWN},
539 {3, 6, PUZZLE_TILE_UP} } },
540 { 6, 3, { {3, 2, PUZZLE_TILE_RIGHT},
541 {4, 3, PUZZLE_TILE_LEFT|PUZZLE_TILE_RIGHT},
542 {5, 4, PUZZLE_TILE_LEFT} } },
543 { 6, 2, { {3, 4, PUZZLE_TILE_RIGHT},
544 {4, 2, PUZZLE_TILE_LEFT} } },
545 { 6, 2, { {3, 2, PUZZLE_TILE_DOWN},
546 {4, 4, PUZZLE_TILE_UP} } },
547 { 7, 4, { {3, 2, PUZZLE_TILE_RIGHT|PUZZLE_TILE_DOWN},
548 {4, 3, PUZZLE_TILE_LEFT|PUZZLE_TILE_DOWN},
549 {3, 4, PUZZLE_TILE_RIGHT|PUZZLE_TILE_UP},
550 {4, 4, PUZZLE_TILE_LEFT|PUZZLE_TILE_UP} } },
551 { 6, 3, { {3, 2, PUZZLE_TILE_DOWN},
552 {4, 4, PUZZLE_TILE_UP|PUZZLE_TILE_DOWN},
553 {3, 6, PUZZLE_TILE_UP} } },
554 { 7, 3, { {2, 2, PUZZLE_TILE_RIGHT},
555 {4, 1, PUZZLE_TILE_LEFT|PUZZLE_TILE_RIGHT},
556 {5, 4, PUZZLE_TILE_LEFT} } },
557 { 7, 4, { {3, 0, PUZZLE_TILE_RIGHT|PUZZLE_TILE_DOWN},
558 {5, 0, PUZZLE_TILE_LEFT|PUZZLE_TILE_DOWN},
559 {2, 7, PUZZLE_TILE_RIGHT|PUZZLE_TILE_UP},
560 {4, 7, PUZZLE_TILE_LEFT|PUZZLE_TILE_UP} } },
561};
562
563#define SAVE_FILE PLUGIN_GAMES_DATA_DIR "/jewels.save"
564#define SCORE_FILE PLUGIN_GAMES_DATA_DIR "/jewels.score"
565struct highscore highscores[NUM_SCORES];
566
567static bool resume_file = false;
568
569/*****************************************************************************
570* jewels_setcolors() set the foreground and background colors.
571******************************************************************************/
572static inline void jewels_setcolors(void) {
573#ifdef HAVE_LCD_COLOR
574 rb->lcd_set_background(LCD_RGBPACK(49, 26, 26));
575 rb->lcd_set_foreground(LCD_RGBPACK(210, 181, 181));
576#endif
577}
578
579/*****************************************************************************
580* jewels_loadgame() loads the saved game and returns load success.
581******************************************************************************/
582static bool jewels_loadgame(struct game_context* bj)
583{
584 int fd;
585 bool loaded = false;
586
587 /* open game file */
588 fd = rb->open(SAVE_FILE, O_RDONLY);
589 if(fd < 0) return loaded;
590
591 /* read in saved game */
592 while(true) {
593 if(rb->read(fd, &bj->tmp_type, sizeof(bj->tmp_type)) <= 0) break;
594 if(rb->read(fd, &bj->type, sizeof(bj->type)) <= 0) break;
595 if(rb->read(fd, &bj->score, sizeof(bj->score)) <= 0) break;
596 if(rb->read(fd, &bj->level, sizeof(bj->level)) <= 0) break;
597 if(rb->read(fd, &bj->segments, sizeof(bj->segments)) <= 0) break;
598 if(rb->read(fd, &bj->num_jewels, sizeof(bj->num_jewels)) <= 0) break;
599 if(rb->read(fd, bj->playboard, sizeof(bj->playboard)) <= 0) break;
600 loaded = true;
601 break;
602 }
603
604 rb->close(fd);
605
606 return loaded;
607}
608
609/*****************************************************************************
610* jewels_savegame() saves the current game state.
611******************************************************************************/
612static void jewels_savegame(struct game_context* bj)
613{
614 int fd;
615 /* write out the game state to the save file */
616 fd = rb->open(SAVE_FILE, O_WRONLY|O_CREAT, 0666);
617 if(fd < 0) return;
618
619 rb->write(fd, &bj->tmp_type, sizeof(bj->tmp_type));
620 rb->write(fd, &bj->type, sizeof(bj->type));
621 rb->write(fd, &bj->score, sizeof(bj->score));
622 rb->write(fd, &bj->level, sizeof(bj->level));
623 rb->write(fd, &bj->segments, sizeof(bj->segments));
624 rb->write(fd, &bj->num_jewels, sizeof(bj->num_jewels));
625 rb->write(fd, bj->playboard, sizeof(bj->playboard));
626 rb->close(fd);
627}
628
629/*****************************************************************************
630* jewels_drawboard() redraws the entire game board.
631******************************************************************************/
632static void jewels_drawboard(struct game_context* bj) {
633 int i, j;
634 int w, h;
635 unsigned int tempscore;
636 unsigned int size;
637 char *title = "Level";
638 char str[10];
639
640 if (bj->type == GAME_TYPE_NORMAL) {
641 tempscore = (bj->score>LEVEL_PTS ? LEVEL_PTS : bj->score);
642 size = LEVEL_PTS;
643 } else {
644 tempscore = (bj->level>NUM_PUZZLE_LEVELS ? NUM_PUZZLE_LEVELS : bj->level);
645 size = NUM_PUZZLE_LEVELS;
646 }
647
648 /* clear screen */
649 rb->lcd_clear_display();
650
651 /* dispay playing board */
652 for(i=0; i<BJ_HEIGHT-1; i++){
653 for(j=0; j<BJ_WIDTH; j++){
654#ifdef HAVE_LCD_COLOR
655 rb->lcd_set_foreground(jewels_bkgd[(i+j)%2]);
656 rb->lcd_fillrect(j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
657 TILE_WIDTH, TILE_HEIGHT);
658 rb->lcd_bitmap_transparent_part(jewels,
659 0, TILE_HEIGHT*(bj->playboard[i+1][j].type),
660 STRIDE( SCREEN_MAIN,
661 BMPWIDTH_jewels, BMPHEIGHT_jewels),
662 j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
663 TILE_WIDTH, TILE_HEIGHT);
664#else
665 rb->lcd_bitmap_part(jewels,
666 0, TILE_HEIGHT*(bj->playboard[i+1][j].type),
667 STRIDE( SCREEN_MAIN,
668 BMPWIDTH_jewels, BMPHEIGHT_jewels),
669 j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
670 TILE_WIDTH, TILE_HEIGHT);
671#endif
672 }
673 }
674
675#if LCD_WIDTH > LCD_HEIGHT /* horizontal layout */
676
677 /* draw separator lines */
678 jewels_setcolors();
679 rb->lcd_vline(BJ_WIDTH*TILE_WIDTH, 0, LCD_HEIGHT-1);
680
681 rb->lcd_hline(BJ_WIDTH*TILE_WIDTH, LCD_WIDTH-1, 18);
682 rb->lcd_hline(BJ_WIDTH*TILE_WIDTH, LCD_WIDTH-1, LCD_HEIGHT-10);
683
684 /* draw progress bar */
685#ifdef HAVE_LCD_COLOR
686 rb->lcd_set_foreground(LCD_RGBPACK(104, 63, 63));
687#endif
688 rb->lcd_fillrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4,
689 (LCD_HEIGHT-10)-(((LCD_HEIGHT-10)-18)*
690 tempscore/size),
691 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2,
692 ((LCD_HEIGHT-10)-18)*tempscore/size);
693#ifdef HAVE_LCD_COLOR
694 rb->lcd_set_foreground(LCD_RGBPACK(83, 44, 44));
695 rb->lcd_drawrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4+1,
696 (LCD_HEIGHT-10)-(((LCD_HEIGHT-10)-18)*
697 tempscore/size)+1,
698 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-2,
699 ((LCD_HEIGHT-10)-18)*tempscore/size-1);
700 jewels_setcolors();
701 rb->lcd_drawrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4,
702 (LCD_HEIGHT-10)-(((LCD_HEIGHT-10)-18)*tempscore/size),
703 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2,
704 ((LCD_HEIGHT-10)-18)*tempscore/size+1);
705#endif
706
707 /* print text */
708 rb->lcd_getstringsize(title, &w, &h);
709 rb->lcd_putsxy(LCD_WIDTH-(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-w/2, 1, title);
710 rb->snprintf(str, 4, "%d", bj->level);
711 rb->lcd_getstringsize(str, &w, &h);
712 rb->lcd_putsxy(LCD_WIDTH-(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-w/2, 10, str);
713
714 if (bj->type == GAME_TYPE_NORMAL) {
715 rb->snprintf(str, 6, "%d", (bj->level-1)*LEVEL_PTS+bj->score);
716 rb->lcd_getstringsize(str, &w, &h);
717 rb->lcd_putsxy(LCD_WIDTH-(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-w/2,
718 LCD_HEIGHT-8, str);
719 }
720
721#elif LCD_WIDTH < LCD_HEIGHT /* vertical layout */
722
723 /* draw separator lines */
724 jewels_setcolors();
725 rb->lcd_hline(0, LCD_WIDTH-1, 8*TILE_HEIGHT+YOFS);
726 rb->lcd_hline(0, LCD_WIDTH-1, LCD_HEIGHT-14);
727 rb->lcd_vline(LCD_WIDTH/2, LCD_HEIGHT-14, LCD_HEIGHT-1);
728
729 /* draw progress bar */
730#ifdef HAVE_LCD_COLOR
731 rb->lcd_set_foreground(LCD_RGBPACK(104, 63, 63));
732#endif
733 rb->lcd_fillrect(0, (8*TILE_HEIGHT+YOFS)
734 +(LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/4,
735 LCD_WIDTH*tempscore/size,
736 (LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/2);
737#ifdef HAVE_LCD_COLOR
738 rb->lcd_set_foreground(LCD_RGBPACK(83, 44, 44));
739 rb->lcd_drawrect(1, (8*TILE_HEIGHT+YOFS)
740 +(LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/4+1,
741 LCD_WIDTH*tempscore/size-1,
742 (LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/2-2);
743 jewels_setcolors();
744 rb->lcd_drawrect(0, (8*TILE_HEIGHT+YOFS)
745 +(LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/4,
746 LCD_WIDTH*tempscore/size+1,
747 (LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/2);
748#endif
749
750 /* print text */
751 rb->lcd_putsxyf(1, LCD_HEIGHT-10, "%s %d", title, bj->level);
752
753 if (bj->type == GAME_TYPE_NORMAL) {
754 rb->snprintf(str, 6, "%d", (bj->level-1)*LEVEL_PTS+bj->score);
755 rb->lcd_getstringsize(str, &w, &h);
756 rb->lcd_putsxy((LCD_WIDTH-2)-w, LCD_HEIGHT-10, str);
757 }
758
759
760#else /* square layout */
761
762 /* draw separator lines */
763 jewels_setcolors();
764 rb->lcd_hline(0, LCD_WIDTH-1, 8*TILE_HEIGHT+YOFS);
765 rb->lcd_vline(BJ_WIDTH*TILE_WIDTH, 0, 8*TILE_HEIGHT+YOFS);
766 rb->lcd_vline(LCD_WIDTH/2, 8*TILE_HEIGHT+YOFS, LCD_HEIGHT-1);
767
768 /* draw progress bar */
769#ifdef HAVE_LCD_COLOR
770 rb->lcd_set_foreground(LCD_RGBPACK(104, 63, 63));
771#endif
772 rb->lcd_fillrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4,
773 (8*TILE_HEIGHT+YOFS)-(8*TILE_HEIGHT+YOFS)*tempscore/size,
774 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2,
775 (8*TILE_HEIGHT+YOFS)*tempscore/size);
776#ifdef HAVE_LCD_COLOR
777 rb->lcd_set_foreground(LCD_RGBPACK(83, 44, 44));
778 rb->lcd_drawrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4+1,
779 (8*TILE_HEIGHT+YOFS)-(8*TILE_HEIGHT+YOFS)
780 *tempscore/size+1,
781 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-2,
782 (8*TILE_HEIGHT+YOFS)*tempscore/size-1);
783 jewels_setcolors();
784 rb->lcd_drawrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4,
785 (8*TILE_HEIGHT+YOFS)-(8*TILE_HEIGHT+YOFS)
786 *tempscore/size,
787 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2,
788 (8*TILE_HEIGHT+YOFS)*tempscore/size+1);
789#endif
790
791 /* print text */
792 rb->lcd_putsxyf(1, LCD_HEIGHT-(LCD_HEIGHT-(8*TILE_HEIGHT+YOFS))/2-3,"%s %d",
793 title, bj->level);
794
795 if (bj->type == GAME_TYPE_NORMAL) {
796 rb->snprintf(str, 6, "%d", (bj->level-1)*LEVEL_PTS+bj->score);
797 rb->lcd_getstringsize(str, &w, &h);
798 rb->lcd_putsxy((LCD_WIDTH-2)-w,
799 LCD_HEIGHT-(LCD_HEIGHT-(8*TILE_HEIGHT+YOFS))/2-3, str);
800 }
801
802#endif /* layout */
803
804 rb->lcd_update();
805}
806
807/*****************************************************************************
808* jewels_putjewels() makes the jewels fall to fill empty spots and adds
809* new random jewels at the empty spots at the top of each row.
810******************************************************************************/
811static void jewels_putjewels(struct game_context* bj){
812 int i, j, k;
813 bool mark, done;
814 long lasttick, currenttick;
815
816 /* loop to make all the jewels fall */
817 while(true) {
818 /* mark falling jewels and add new jewels to hidden top row*/
819 mark = false;
820 done = true;
821 for(j=0; j<BJ_WIDTH; j++) {
822 if(bj->playboard[1][j].type == 0) {
823 bj->playboard[0][j].type = rb->rand()%bj->num_jewels+1;
824 }
825 for(i=BJ_HEIGHT-2; i>=0; i--) {
826 if(!mark && bj->playboard[i+1][j].type == 0) {
827 mark = true;
828 done = false;
829 }
830 if(mark) bj->playboard[i][j].falling = true;
831 }
832 /*if(bj->playboard[1][j].falling) {
833 bj->playboard[0][j].type = rb->rand()%bj->num_jewels+1;
834 bj->playboard[0][j].falling = true;
835 }*/
836 mark = false;
837 }
838
839 /* break if there are no falling jewels */
840 if(done) break;
841
842 /* animate falling jewels */
843 lasttick = *rb->current_tick;
844
845 for(k=1; k<=8; k++) {
846 for(i=BJ_HEIGHT-2; i>=0; i--) {
847 for(j=0; j<BJ_WIDTH; j++) {
848 if(bj->playboard[i][j].falling &&
849 bj->playboard[i][j].type != 0) {
850 /* clear old position */
851#ifdef HAVE_LCD_COLOR
852 if(i == 0 && YOFS) {
853 rb->lcd_set_foreground(rb->lcd_get_background());
854 } else {
855 rb->lcd_set_foreground(jewels_bkgd[(i-1+j)%2]);
856 }
857 rb->lcd_fillrect(j*TILE_WIDTH, (i-1)*TILE_HEIGHT+YOFS,
858 TILE_WIDTH, TILE_HEIGHT);
859 if(bj->playboard[i+1][j].type == 0) {
860 rb->lcd_set_foreground(jewels_bkgd[(i+j)%2]);
861 rb->lcd_fillrect(j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
862 TILE_WIDTH, TILE_HEIGHT);
863 }
864#else
865 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
866 rb->lcd_fillrect(j*TILE_WIDTH, (i-1)*TILE_HEIGHT+YOFS,
867 TILE_WIDTH, TILE_HEIGHT);
868 if(bj->playboard[i+1][j].type == 0) {
869 rb->lcd_fillrect(j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
870 TILE_WIDTH, TILE_HEIGHT);
871 }
872 rb->lcd_set_drawmode(DRMODE_SOLID);
873#endif
874
875 /* draw new position */
876#ifdef HAVE_LCD_COLOR
877 rb->lcd_bitmap_transparent_part(jewels, 0,
878 TILE_HEIGHT*(bj->playboard[i][j].type),
879 STRIDE( SCREEN_MAIN,
880 BMPWIDTH_jewels,
881 BMPHEIGHT_jewels),
882 j*TILE_WIDTH,
883 (i-1)*TILE_HEIGHT+YOFS+
884 ((((TILE_HEIGHT<<10)*k)/8)>>10),
885 TILE_WIDTH, TILE_HEIGHT);
886#else
887 rb->lcd_bitmap_part(jewels, 0,
888 TILE_HEIGHT*(bj->playboard[i][j].type),
889 STRIDE( SCREEN_MAIN,
890 BMPWIDTH_jewels,
891 BMPHEIGHT_jewels),
892 j*TILE_WIDTH,
893 (i-1)*TILE_HEIGHT+YOFS+
894 ((((TILE_HEIGHT<<10)*k)/8)>>10),
895 TILE_WIDTH, TILE_HEIGHT);
896#endif
897 }
898 }
899 }
900
901 rb->lcd_update_rect(0, 0, TILE_WIDTH*8, LCD_HEIGHT);
902 jewels_setcolors();
903
904 /* framerate limiting */
905 currenttick = *rb->current_tick;
906 if(currenttick-lasttick < HZ/MAX_FPS) {
907 rb->sleep((HZ/MAX_FPS)-(currenttick-lasttick));
908 } else {
909 rb->yield();
910 }
911 lasttick = currenttick;
912 }
913
914 /* shift jewels down */
915 for(j=0; j<BJ_WIDTH; j++) {
916 for(i=BJ_HEIGHT-1; i>=1; i--) {
917 if(bj->playboard[i-1][j].falling) {
918 bj->playboard[i][j].type = bj->playboard[i-1][j].type;
919 }
920 }
921 }
922
923 /* clear out top row */
924 for(j=0; j<BJ_WIDTH; j++) {
925 bj->playboard[0][j].type = 0;
926 }
927
928 /* mark everything not falling */
929 for(i=0; i<BJ_HEIGHT; i++) {
930 for(j=0; j<BJ_WIDTH; j++) {
931 bj->playboard[i][j].falling = false;
932 }
933 }
934 }
935}
936
937/*****************************************************************************
938* jewels_clearjewels() finds all the connected rows and columns and
939* calculates and returns the points earned.
940******************************************************************************/
941static unsigned int jewels_clearjewels(struct game_context* bj) {
942 int i, j;
943 int last, run;
944 unsigned int points = 0;
945
946 /* check for connected rows */
947 for(i=1; i<BJ_HEIGHT; i++) {
948 last = 0;
949 run = 1;
950 for(j=0; j<BJ_WIDTH; j++) {
951 if(bj->playboard[i][j].type == last &&
952 bj->playboard[i][j].type != 0 &&
953 bj->playboard[i][j].type <= MAX_NUM_JEWELS) {
954 run++;
955
956 if(run == 3) {
957 bj->segments++;
958 points += bj->segments;
959 bj->playboard[i][j].delete = true;
960 bj->playboard[i][j-1].delete = true;
961 bj->playboard[i][j-2].delete = true;
962 } else if(run > 3) {
963 points++;
964 bj->playboard[i][j].delete = true;
965 }
966 } else {
967 run = 1;
968 last = bj->playboard[i][j].type;
969 }
970 }
971 }
972
973 /* check for connected columns */
974 for(j=0; j<BJ_WIDTH; j++) {
975 last = 0;
976 run = 1;
977 for(i=1; i<BJ_HEIGHT; i++) {
978 if(bj->playboard[i][j].type != 0 &&
979 bj->playboard[i][j].type == last &&
980 bj->playboard[i][j].type <= MAX_NUM_JEWELS) {
981 run++;
982
983 if(run == 3) {
984 bj->segments++;
985 points += bj->segments;
986 bj->playboard[i][j].delete = true;
987 bj->playboard[i-1][j].delete = true;
988 bj->playboard[i-2][j].delete = true;
989 } else if(run > 3) {
990 points++;
991 bj->playboard[i][j].delete = true;
992 }
993 } else {
994 run = 1;
995 last = bj->playboard[i][j].type;
996 }
997 }
998 }
999
1000 /* clear deleted jewels */
1001 for(i=1; i<BJ_HEIGHT; i++) {
1002 for(j=0; j<BJ_WIDTH; j++) {
1003 if(bj->playboard[i][j].delete) {
1004 bj->playboard[i][j].delete = false;
1005 bj->playboard[i][j].type = 0;
1006 }
1007 }
1008 }
1009
1010 return points;
1011}
1012
1013/*****************************************************************************
1014* jewels_runboard() runs the board until it settles in a fixed state and
1015* returns points earned.
1016******************************************************************************/
1017static unsigned int jewels_runboard(struct game_context* bj) {
1018 unsigned int points = 0;
1019 unsigned int ret;
1020
1021 bj->segments = 0;
1022
1023 while((ret = jewels_clearjewels(bj)) > 0) {
1024 points += ret;
1025 jewels_drawboard(bj);
1026 jewels_putjewels(bj);
1027 }
1028
1029 return points;
1030}
1031
1032/*****************************************************************************
1033* jewels_swapjewels() swaps two jewels as long as it results in points and
1034* returns points earned.
1035******************************************************************************/
1036static unsigned int jewels_swapjewels(struct game_context* bj,
1037 int x, int y, int direc) {
1038 int k;
1039 int horzmod, vertmod;
1040 int movelen = 0;
1041 bool undo = false;
1042 unsigned int points = 0;
1043 long lasttick, currenttick;
1044
1045 /* check for invalid parameters */
1046 if(x < 0 || x >= BJ_WIDTH || y < 0 || y >= BJ_HEIGHT-1 ||
1047 direc < SWAP_UP || direc > SWAP_LEFT) {
1048 return 0;
1049 }
1050
1051 /* check for invalid directions */
1052 if((x == 0 && direc == SWAP_LEFT) ||
1053 (x == BJ_WIDTH-1 && direc == SWAP_RIGHT) ||
1054 (y == 0 && direc == SWAP_UP) ||
1055 (y == BJ_HEIGHT-2 && direc == SWAP_DOWN)) {
1056 return 0;
1057 }
1058
1059 /* set direction variables */
1060 horzmod = 0;
1061 vertmod = 0;
1062 switch(direc) {
1063 case SWAP_UP:
1064 vertmod = -1;
1065 movelen = TILE_HEIGHT;
1066 break;
1067 case SWAP_RIGHT:
1068 horzmod = 1;
1069 movelen = TILE_WIDTH;
1070 break;
1071 case SWAP_DOWN:
1072 vertmod = 1;
1073 movelen = TILE_HEIGHT;
1074 break;
1075 case SWAP_LEFT:
1076 horzmod = -1;
1077 movelen = TILE_WIDTH;
1078 break;
1079 }
1080
1081 while(true) {
1082 lasttick = *rb->current_tick;
1083
1084 /* animate swapping jewels */
1085 for(k=0; k<=8; k++) {
1086 /* clear old position */
1087#ifdef HAVE_LCD_COLOR
1088 rb->lcd_set_foreground(jewels_bkgd[(x+y)%2]);
1089 rb->lcd_fillrect(x*TILE_WIDTH,
1090 y*TILE_HEIGHT+YOFS,
1091 TILE_WIDTH, TILE_HEIGHT);
1092 rb->lcd_set_foreground(jewels_bkgd[(x+horzmod+y+vertmod)%2]);
1093 rb->lcd_fillrect((x+horzmod)*TILE_WIDTH,
1094 (y+vertmod)*TILE_HEIGHT+YOFS,
1095 TILE_WIDTH, TILE_HEIGHT);
1096#else
1097 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
1098 rb->lcd_fillrect(x*TILE_WIDTH,
1099 y*TILE_HEIGHT+YOFS,
1100 TILE_WIDTH, TILE_HEIGHT);
1101 rb->lcd_fillrect((x+horzmod)*TILE_WIDTH,
1102 (y+vertmod)*TILE_HEIGHT+YOFS,
1103 TILE_WIDTH, TILE_HEIGHT);
1104 rb->lcd_set_drawmode(DRMODE_SOLID);
1105#endif
1106 /* draw new position */
1107#ifdef HAVE_LCD_COLOR
1108 rb->lcd_bitmap_transparent_part(jewels,
1109 0, TILE_HEIGHT*(bj->playboard
1110 [y+1+vertmod][x+horzmod].type),
1111 STRIDE( SCREEN_MAIN,
1112 BMPWIDTH_jewels, BMPHEIGHT_jewels),
1113 (x+horzmod)*TILE_WIDTH-horzmod*
1114 ((((movelen<<10)*k)/8)>>10),
1115 (y+vertmod)*TILE_HEIGHT-vertmod*
1116 ((((movelen<<10)*k)/8)>>10)+YOFS,
1117 TILE_WIDTH, TILE_HEIGHT);
1118 rb->lcd_bitmap_transparent_part(jewels,
1119 0, TILE_HEIGHT*(bj->playboard[y+1][x].type),
1120 STRIDE( SCREEN_MAIN,
1121 BMPWIDTH_jewels, BMPHEIGHT_jewels),
1122 x*TILE_WIDTH+horzmod*
1123 ((((movelen<<10)*k)/8)>>10),
1124 y*TILE_HEIGHT+vertmod*
1125 ((((movelen<<10)*k)/8)>>10)+YOFS,
1126 TILE_WIDTH, TILE_HEIGHT);
1127#else
1128 rb->lcd_bitmap_part(jewels,
1129 0, TILE_HEIGHT*(bj->playboard
1130 [y+1+vertmod][x+horzmod].type),
1131 STRIDE( SCREEN_MAIN,
1132 BMPWIDTH_jewels, BMPHEIGHT_jewels),
1133 (x+horzmod)*TILE_WIDTH-horzmod*
1134 ((((movelen<<10)*k)/8)>>10),
1135 (y+vertmod)*TILE_HEIGHT-vertmod*
1136 ((((movelen<<10)*k)/8)>>10)+YOFS,
1137 TILE_WIDTH, TILE_HEIGHT);
1138 rb->lcd_set_drawmode(DRMODE_FG);
1139 rb->lcd_bitmap_part(jewels,
1140 0, TILE_HEIGHT*(bj->playboard[y+1][x].type),
1141 STRIDE( SCREEN_MAIN,
1142 BMPWIDTH_jewels, BMPHEIGHT_jewels),
1143 x*TILE_WIDTH+horzmod*
1144 ((((movelen<<10)*k)/8)>>10),
1145 y*TILE_HEIGHT+vertmod*
1146 ((((movelen<<10)*k)/8)>>10)+YOFS,
1147 TILE_WIDTH, TILE_HEIGHT);
1148 rb->lcd_set_drawmode(DRMODE_SOLID);
1149#endif
1150
1151 rb->lcd_update_rect(0, 0, TILE_WIDTH*8, LCD_HEIGHT);
1152 jewels_setcolors();
1153
1154 /* framerate limiting */
1155 currenttick = *rb->current_tick;
1156 if(currenttick-lasttick < HZ/MAX_FPS) {
1157 rb->sleep((HZ/MAX_FPS)-(currenttick-lasttick));
1158 } else {
1159 rb->yield();
1160 }
1161 lasttick = currenttick;
1162 }
1163
1164 /* swap jewels */
1165 int temp = bj->playboard[y+1][x].type;
1166 bj->playboard[y+1][x].type =
1167 bj->playboard[y+1+vertmod][x+horzmod].type;
1168 bj->playboard[y+1+vertmod][x+horzmod].type = temp;
1169
1170 if(undo) break;
1171
1172 points = jewels_runboard(bj);
1173 if(points == 0) {
1174 undo = true;
1175 } else {
1176 break;
1177 }
1178 }
1179
1180 return points;
1181}
1182
1183/*****************************************************************************
1184* jewels_movesavail() uses pattern matching to see if there are any
1185* available move left.
1186******************************************************************************/
1187static bool jewels_movesavail(struct game_context* bj) {
1188 int i, j;
1189 bool moves = false;
1190 int mytype;
1191
1192 for(i=1; i<BJ_HEIGHT; i++) {
1193 for(j=0; j<BJ_WIDTH; j++) {
1194 mytype = bj->playboard[i][j].type;
1195 if(mytype == 0 || mytype > MAX_NUM_JEWELS) continue;
1196
1197 /* check horizontal patterns */
1198 if(j <= BJ_WIDTH-3) {
1199 if(i > 1) {
1200 if(bj->playboard[i-1][j+1].type == mytype) {
1201 if(bj->playboard[i-1][j+2].type == mytype)
1202 {moves = true; break;}
1203 if(bj->playboard[i][j+2].type == mytype)
1204 {moves = true; break;}
1205 }
1206 if(bj->playboard[i][j+1].type == mytype) {
1207 if(bj->playboard[i-1][j+2].type == mytype)
1208 {moves = true; break;}
1209 }
1210 }
1211
1212 if(j <= BJ_WIDTH-4) {
1213 if(bj->playboard[i][j+3].type == mytype) {
1214 if(bj->playboard[i][j+1].type == mytype)
1215 {moves = true; break;}
1216 if(bj->playboard[i][j+2].type == mytype)
1217 {moves = true; break;}
1218 }
1219 }
1220
1221 if(i < BJ_HEIGHT-1) {
1222 if(bj->playboard[i][j+1].type == mytype) {
1223 if(bj->playboard[i+1][j+2].type == mytype)
1224 {moves = true; break;}
1225 }
1226 if(bj->playboard[i+1][j+1].type == mytype) {
1227 if(bj->playboard[i][j+2].type == mytype)
1228 {moves = true; break;}
1229 if(bj->playboard[i+1][j+2].type == mytype)
1230 {moves = true; break;}
1231 }
1232 }
1233 }
1234
1235 /* check vertical patterns */
1236 if(i <= BJ_HEIGHT-3) {
1237 if(j > 0) {
1238 if(bj->playboard[i+1][j-1].type == mytype) {
1239 if(bj->playboard[i+2][j-1].type == mytype)
1240 {moves = true; break;}
1241 if(bj->playboard[i+2][j].type == mytype)
1242 {moves = true; break;}
1243 }
1244 if(bj->playboard[i+1][j].type == mytype) {
1245 if(bj->playboard[i+2][j-1].type == mytype)
1246 {moves = true; break;}
1247 }
1248 }
1249
1250 if(i <= BJ_HEIGHT-4) {
1251 if(bj->playboard[i+3][j].type == mytype) {
1252 if(bj->playboard[i+1][j].type == mytype)
1253 {moves = true; break;}
1254 if(bj->playboard[i+2][j].type == mytype)
1255 {moves = true; break;}
1256 }
1257 }
1258
1259 if(j < BJ_WIDTH-1) {
1260 if(bj->playboard[i+1][j].type == mytype) {
1261 if(bj->playboard[i+2][j+1].type == mytype)
1262 {moves = true; break;}
1263 }
1264 if(bj->playboard[i+1][j+1].type == mytype) {
1265 if(bj->playboard[i+2][j].type == mytype)
1266 {moves = true; break;}
1267 if (bj->playboard[i+2][j+1].type == mytype)
1268 {moves = true; break;}
1269 }
1270 }
1271 }
1272 }
1273 if(moves) break;
1274 }
1275 return moves;
1276}
1277
1278/*****************************************************************************
1279* jewels_puzzle_is_finished() checks if the puzzle is finished.
1280******************************************************************************/
1281static bool jewels_puzzle_is_finished(struct game_context* bj) {
1282 unsigned int i, j;
1283 for(i=0; i<BJ_HEIGHT; i++) {
1284 for(j=0; j<BJ_WIDTH; j++) {
1285 int mytype = bj->playboard[i][j].type;
1286 if(mytype>MAX_NUM_JEWELS) {
1287 mytype -= MAX_NUM_JEWELS;
1288 if(mytype&PUZZLE_TILE_UP) {
1289 if(i==0 || bj->playboard[i-1][j].type<=MAX_NUM_JEWELS ||
1290 !((bj->playboard[i-1][j].type-MAX_NUM_JEWELS)
1291 &PUZZLE_TILE_DOWN))
1292 return false;
1293 }
1294 if(mytype&PUZZLE_TILE_DOWN) {
1295 if(i==BJ_HEIGHT-1 ||
1296 bj->playboard[i+1][j].type<=MAX_NUM_JEWELS ||
1297 !((bj->playboard[i+1][j].type-MAX_NUM_JEWELS)
1298 &PUZZLE_TILE_UP))
1299 return false;
1300 }
1301 if(mytype&PUZZLE_TILE_LEFT) {
1302 if(j==0 || bj->playboard[i][j-1].type<=MAX_NUM_JEWELS ||
1303 !((bj->playboard[i][j-1].type-MAX_NUM_JEWELS)
1304 &PUZZLE_TILE_RIGHT))
1305 return false;
1306 }
1307 if(mytype&PUZZLE_TILE_RIGHT) {
1308 if(j==BJ_WIDTH-1 ||
1309 bj->playboard[i][j+1].type<=MAX_NUM_JEWELS ||
1310 !((bj->playboard[i][j+1].type-MAX_NUM_JEWELS)
1311 &PUZZLE_TILE_LEFT))
1312 return false;
1313 }
1314 }
1315 }
1316 }
1317 return true;
1318}
1319
1320/*****************************************************************************
1321* jewels_initlevel() initialises a level.
1322******************************************************************************/
1323static unsigned int jewels_initlevel(struct game_context* bj) {
1324 unsigned int points = 0;
1325
1326 switch(bj->type) {
1327 case GAME_TYPE_NORMAL:
1328 bj->num_jewels = MAX_NUM_JEWELS;
1329 break;
1330
1331 case GAME_TYPE_PUZZLE:
1332 {
1333 unsigned int i, j;
1334 struct puzzle_tile *tile;
1335
1336 bj->num_jewels = puzzle_levels[bj->level-1].num_jewels;
1337
1338 for(i=0; i<BJ_HEIGHT; i++) {
1339 for(j=0; j<BJ_WIDTH; j++) {
1340 bj->playboard[i][j].type = (rb->rand()%bj->num_jewels)+1;
1341 bj->playboard[i][j].falling = false;
1342 bj->playboard[i][j].delete = false;
1343 }
1344 }
1345 jewels_runboard(bj);
1346 tile = puzzle_levels[bj->level-1].tiles;
1347 for(i=0; i<puzzle_levels[bj->level-1].num_tiles; i++, tile++) {
1348 bj->playboard[tile->y+1][tile->x].type = MAX_NUM_JEWELS
1349 +tile->tile_type;
1350 }
1351 }
1352 break;
1353 }
1354
1355 jewels_drawboard(bj);
1356
1357 /* run the play board */
1358 jewels_putjewels(bj);
1359 points += jewels_runboard(bj);
1360 return points;
1361}
1362
1363/*****************************************************************************
1364* jewels_init() initializes jewels data structures.
1365******************************************************************************/
1366static void jewels_init(struct game_context* bj) {
1367 /* seed the rand generator */
1368 rb->srand(*rb->current_tick);
1369
1370 bj->type = bj->tmp_type;
1371 bj->level = 1;
1372 bj->score = 0;
1373 bj->segments = 0;
1374
1375 jewels_setcolors();
1376
1377 /* clear playing board */
1378 rb->memset(bj->playboard, 0, sizeof(bj->playboard));
1379 do {
1380 bj->score += jewels_initlevel(bj);
1381 } while(!jewels_movesavail(bj));
1382}
1383
1384/*****************************************************************************
1385* jewels_nextlevel() advances the game to the next bj->level and returns
1386* points earned.
1387******************************************************************************/
1388static void jewels_nextlevel(struct game_context* bj) {
1389 int i, x, y;
1390 unsigned int points = 0;
1391
1392 switch(bj->type) {
1393 case GAME_TYPE_NORMAL:
1394 /* roll over score, change and display level */
1395 while(bj->score >= LEVEL_PTS) {
1396 bj->score -= LEVEL_PTS;
1397 bj->level++;
1398 rb->splashf(HZ*2, "Level %d", bj->level);
1399 jewels_drawboard(bj);
1400 }
1401
1402 /* randomly clear some jewels */
1403 for(i=0; i<16; i++) {
1404 x = rb->rand()%8;
1405 y = rb->rand()%8;
1406
1407 if(bj->playboard[y][x].type != 0) {
1408 points++;
1409 bj->playboard[y][x].type = 0;
1410 }
1411 }
1412 break;
1413
1414 case GAME_TYPE_PUZZLE:
1415 bj->level++;
1416 rb->splashf(HZ*2, "Level %d", bj->level);
1417 break;
1418 }
1419
1420 points += jewels_initlevel(bj);
1421 bj->score += points;
1422}
1423
1424static bool jewels_help(void)
1425{
1426 static char *help_text[] = {
1427 "Jewels", "", "Aim", "",
1428 "Swap", "pairs", "of", "jewels", "to", "form", "connected",
1429 "segments", "of", "three", "or", "more", "of", "the", "same",
1430 "type.", "",
1431 "The", "goal", "of", "the", "game", "is", "to", "score", "as", "many",
1432 "points", "as", "possible", "before", "running", "out", "of",
1433 "available", "moves.", "", "",
1434 "Controls", "",
1435 "Directions",
1436#ifdef JEWELS_SCROLLWHEEL
1437 "or", "scroll",
1438#endif
1439 "to", "move", "",
1440 HK_SELECT, "to", "select", "",
1441 HK_CANCEL, "to", "go", "to", "menu"
1442 };
1443 static struct style_text formation[]={
1444 { 0, TEXT_CENTER|TEXT_UNDERLINE },
1445 { 2, C_RED },
1446 { 42, C_RED },
1447 LAST_STYLE_ITEM
1448 };
1449
1450 rb->lcd_setfont(FONT_UI);
1451 if (display_text(ARRAYLEN(help_text), help_text, formation, NULL, true))
1452 return true;
1453 rb->lcd_setfont(FONT_SYSFIXED);
1454
1455 return false;
1456}
1457
1458static bool _ingame;
1459static int jewels_menu_cb(int action,
1460 const struct menu_item_ex *this_item,
1461 struct gui_synclist *this_list)
1462{
1463 (void)this_list;
1464 int i = ((intptr_t)this_item);
1465 if(action == ACTION_REQUEST_MENUITEM
1466 && !_ingame && (i==0 || i==6))
1467 return ACTION_EXIT_MENUITEM;
1468 return action;
1469}
1470/*****************************************************************************
1471* jewels_game_menu() shows the game menu.
1472******************************************************************************/
1473static int jewels_game_menu(struct game_context* bj, bool ingame)
1474{
1475 rb->button_clear_queue();
1476 int choice = 0;
1477
1478 _ingame = ingame;
1479
1480 static struct opt_items mode[] = {
1481 { "Normal", -1 },
1482 { "Puzzle", -1 },
1483 };
1484
1485 MENUITEM_STRINGLIST (main_menu, "Jewels Menu", jewels_menu_cb,
1486 "Resume Game",
1487 "Start New Game",
1488 "Mode",
1489 "Help",
1490 "High Scores",
1491 "Playback Control",
1492 "Quit without Saving",
1493 "Quit");
1494
1495 while (1) {
1496 switch (rb->do_menu(&main_menu, &choice, NULL, false)) {
1497 case 0:
1498 jewels_setcolors();
1499 if(resume_file)
1500 rb->remove(SAVE_FILE);
1501 return 0;
1502 case 1:
1503 jewels_init(bj);
1504 return 0;
1505 case 2:
1506 rb->set_option("Mode", &bj->tmp_type, RB_INT, mode, 2, NULL);
1507 break;
1508 case 3:
1509 if(jewels_help())
1510 return 1;
1511 break;
1512 case 4:
1513 highscore_show(-1, highscores, NUM_SCORES, true);
1514 break;
1515 case 5:
1516 playback_control(NULL);
1517 break;
1518 case 6:
1519 return 1;
1520 case 7:
1521 if (ingame) {
1522 rb->splash(HZ*1, "Saving game ...");
1523 jewels_savegame(bj);
1524 }
1525 return 1;
1526 case MENU_ATTACHED_USB:
1527 return 1;
1528 default:
1529 break;
1530 }
1531 }
1532}
1533
1534static int jewels_main(struct game_context* bj) {
1535 int button;
1536 int position;
1537 bool selected = false;
1538 int x=0, y=0;
1539
1540 bool loaded = jewels_loadgame(bj);
1541 resume_file = loaded;
1542 if (jewels_game_menu(bj, loaded)!=0)
1543 return 0;
1544
1545 resume_file = false;
1546 while(true) {
1547 /* refresh the board */
1548 jewels_drawboard(bj);
1549
1550 /* display the cursor */
1551 if(selected) {
1552 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
1553 rb->lcd_fillrect(x*TILE_WIDTH, y*TILE_HEIGHT+YOFS,
1554 TILE_WIDTH, TILE_HEIGHT);
1555 rb->lcd_set_drawmode(DRMODE_SOLID);
1556 } else {
1557 rb->lcd_drawrect(x*TILE_WIDTH, y*TILE_HEIGHT+YOFS,
1558 TILE_WIDTH, TILE_HEIGHT);
1559 }
1560 rb->lcd_update_rect(x*TILE_WIDTH, y*TILE_HEIGHT+YOFS,
1561 TILE_WIDTH, TILE_HEIGHT);
1562
1563 /* handle game button presses */
1564 rb->yield();
1565 button = rb->button_get(true);
1566 switch(button){
1567 case JEWELS_LEFT: /* move cursor left */
1568 case (JEWELS_LEFT|BUTTON_REPEAT):
1569 if(selected) {
1570 bj->score += jewels_swapjewels(bj, x, y, SWAP_LEFT);
1571 selected = false;
1572 } else {
1573 x = (x+BJ_WIDTH-1)%BJ_WIDTH;
1574 }
1575 break;
1576
1577 case JEWELS_RIGHT: /* move cursor right */
1578 case (JEWELS_RIGHT|BUTTON_REPEAT):
1579 if(selected) {
1580 bj->score += jewels_swapjewels(bj, x, y, SWAP_RIGHT);
1581 selected = false;
1582 } else {
1583 x = (x+1)%BJ_WIDTH;
1584 }
1585 break;
1586
1587 case JEWELS_DOWN: /* move cursor down */
1588 case (JEWELS_DOWN|BUTTON_REPEAT):
1589 if(selected) {
1590 bj->score += jewels_swapjewels(bj, x, y, SWAP_DOWN);
1591 selected = false;
1592 } else {
1593 y = (y+1)%(BJ_HEIGHT-1);
1594 }
1595 break;
1596
1597 case JEWELS_UP: /* move cursor up */
1598 case (JEWELS_UP|BUTTON_REPEAT):
1599 if(selected) {
1600 bj->score += jewels_swapjewels(bj, x, y, SWAP_UP);
1601 selected = false;
1602 } else {
1603 y = (y+(BJ_HEIGHT-1)-1)%(BJ_HEIGHT-1);
1604 }
1605 break;
1606
1607#ifdef JEWELS_SCROLLWHEEL
1608 case JEWELS_PREV: /* scroll backwards */
1609 case (JEWELS_PREV|BUTTON_REPEAT):
1610 if(!selected) {
1611 if(x == 0) {
1612 y = (y+(BJ_HEIGHT-1)-1)%(BJ_HEIGHT-1);
1613 }
1614 x = (x+BJ_WIDTH-1)%BJ_WIDTH;
1615 }
1616 break;
1617
1618 case JEWELS_NEXT: /* scroll forwards */
1619 case (JEWELS_NEXT|BUTTON_REPEAT):
1620 if(!selected) {
1621 if(x == BJ_WIDTH-1) {
1622 y = (y+1)%(BJ_HEIGHT-1);
1623 }
1624 x = (x+1)%BJ_WIDTH;
1625 }
1626 break;
1627#endif
1628
1629 case JEWELS_SELECT: /* toggle selected */
1630 selected = !selected;
1631 break;
1632
1633#ifdef JEWELS_RC_CANCEL
1634 case JEWELS_RC_CANCEL:
1635#endif
1636 case JEWELS_CANCEL: /* end game */
1637 if (jewels_game_menu(bj, true)!=0)
1638 return 0;
1639 break;
1640
1641 default:
1642 if (rb->default_event_handler (button) == SYS_USB_CONNECTED)
1643 return PLUGIN_USB_CONNECTED;
1644 break;
1645 }
1646
1647 switch(bj->type) {
1648 case GAME_TYPE_NORMAL:
1649 if(bj->score >= LEVEL_PTS)
1650 jewels_nextlevel(bj);
1651 break;
1652 case GAME_TYPE_PUZZLE:
1653 if (jewels_puzzle_is_finished(bj)) {
1654 if (bj->level < NUM_PUZZLE_LEVELS) {
1655 jewels_nextlevel(bj);
1656 } else {
1657 rb->splash(2*HZ, "Congratulations!");
1658 rb->splash(2*HZ, "You have finished the game!");
1659 if (jewels_game_menu(bj, false)!=0) {
1660 return 0;
1661 }
1662 }
1663 break;
1664 }
1665 }
1666
1667 if (!jewels_movesavail(bj)) {
1668 switch(bj->type) {
1669 case GAME_TYPE_NORMAL:
1670 rb->splash(HZ*2, "Game Over!");
1671 rb->lcd_clear_display();
1672 bj->score += (bj->level-1)*LEVEL_PTS;
1673 position=highscore_update(bj->score, bj->level, "",
1674 highscores, NUM_SCORES);
1675 if (position != -1)
1676 {
1677 if (position == 0)
1678 rb->splash(HZ*2, "New High Score");
1679 highscore_show(position, highscores, NUM_SCORES, true);
1680 }
1681 break;
1682 case GAME_TYPE_PUZZLE:
1683 rb->splash(2*HZ, "Game Over");
1684 break;
1685 }
1686 if (jewels_game_menu(bj, false)!=0) {
1687 return 0;
1688 }
1689 }
1690 }
1691}
1692
1693/* this is the plugin entry point */
1694enum plugin_status plugin_start(const void* parameter)
1695{
1696 (void)parameter;
1697
1698 /* load high scores */
1699 highscore_load(SCORE_FILE, highscores, NUM_SCORES);
1700
1701 rb->lcd_setfont(FONT_SYSFIXED);
1702#if LCD_DEPTH > 1
1703 rb->lcd_set_backdrop(NULL);
1704#endif
1705
1706 struct game_context bj;
1707 bj.tmp_type = GAME_TYPE_NORMAL;
1708 jewels_main(&bj);
1709 highscore_save(SCORE_FILE, highscores, NUM_SCORES);
1710 rb->lcd_setfont(FONT_UI);
1711
1712 return PLUGIN_OK;
1713}