rockbox/apps/plugins/jewels.c
Simon Rothen 0b5ad60c26 Introducing Targets iBasso DX50 & iBasso DX90
The port to for this two targets has been entirely developped by Ilia Sergachev (alias Il or xzcc). His source
can be found at https://bitbucket.org/isergachev/rockbox . The few necesary modifications for the DX90 port
was done by headwhacker form head-fi.org. Unfortunately i could not try out the final state of the DX90 port.
The port is hosted on android (without java) as standalone app. The official Firmware is required to run this port.
Ilia did modify the source files for the "android" target in the rockbox source to make the DX port work. The work I did
was to separate the code for DX50 (&DX90) from the android target.
On this Target Ilia used source from tinyalsa from AOSP. I did not touch that part of the code because I do not understand it.
What else I changed from Ilias sources besides the separation from the target "android":
* removed a dirty hack to keep backlight off
* changed value battery meter to voltage battery meter
* made all plugins compile (named target as "standalone") and added keymaps
* i added the graphics for the manual but did not do anything else for the manual yet
* minor optimizations

known bugs:
* timers are slowed donw when playback is active (tinyalsa related?)
* some minor bugs

Things to do:
* The main prolem will be how to install the app correctly. A guy called DOC2008 added a CWM (by androtab.info) to the
  official firmware and Ilia made a CWM installation script and a dualboot selector (rbutils/ibassoboot, build with
  ndk-build). We will have to find a way to install rockbox in a proper way without breaking any copyrights.
  Maybe ADB is an option but it is not enable with OF by default. Patching the OF is probably the way to go.
* All the wiki and manual

to build:
needed: android ndk installed, android sdk installed with additional build-tools 19.1.0 installed

./tools/configure
select iBasso DX50 or iBasso DX90
make -j apk

the content of rockbox.zip/.rockbox needs to be copied to /system/rockbox/app_rockbox/rockbox/ (rockbox app not needed)
the content of libs/armeabi to /system/rockbox/lib/ (rockbox app needed)

The boot selector is needed as /system/bin/MangoPlayer and the iBasso app as /system/bin/MangoPlayer_original. There
is also the "vold" file. The one from OF does not work with DX50 rockbox (DX90 works!?), the one from Ilia is necessary.

Until we have found a proper way to install it, it can only be installed following the instructions of Ilia on his
bitbucket page, using the CWM-OF and his installation script package.

Change-Id: Ic4faaf84824c162aabcc08e492cee6e0068719d0
Reviewed-on: http://gerrit.rockbox.org/941
Tested: Chiwen Chang <rock1104.tw@yahoo.com.tw>
Reviewed-by: Michael Giacomelli <giac2000@hotmail.com>
2014-09-18 18:19:01 +02:00

1671 lines
56 KiB
C

/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2005 Adam Boot
*
* Color graphics from Gweled (http://sebdelestaing.free.fr/gweled/)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "plugin.h"
#include "lib/display_text.h"
#include "lib/highscore.h"
#include "lib/playback_control.h"
#include "pluginbitmaps/jewels.h"
/* button definitions */
#if CONFIG_KEYPAD == RECORDER_PAD
#define JEWELS_UP BUTTON_UP
#define JEWELS_DOWN BUTTON_DOWN
#define JEWELS_LEFT BUTTON_LEFT
#define JEWELS_RIGHT BUTTON_RIGHT
#define JEWELS_SELECT BUTTON_PLAY
#define JEWELS_CANCEL BUTTON_OFF
#define HK_SELECT "PLAY"
#define HK_CANCEL "OFF"
#elif CONFIG_KEYPAD == ARCHOS_AV300_PAD
#define JEWELS_UP BUTTON_UP
#define JEWELS_DOWN BUTTON_DOWN
#define JEWELS_LEFT BUTTON_LEFT
#define JEWELS_RIGHT BUTTON_RIGHT
#define JEWELS_SELECT BUTTON_SELECT
#define JEWELS_CANCEL BUTTON_OFF
#define HK_SELECT "SELECT"
#define HK_CANCEL "OFF"
#elif CONFIG_KEYPAD == ONDIO_PAD
#define JEWELS_UP BUTTON_UP
#define JEWELS_DOWN BUTTON_DOWN
#define JEWELS_LEFT BUTTON_LEFT
#define JEWELS_RIGHT BUTTON_RIGHT
#define JEWELS_SELECT BUTTON_MENU
#define JEWELS_CANCEL BUTTON_OFF
#define HK_SELECT "MENU"
#define HK_CANCEL "OFF"
#elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD)
#define JEWELS_UP BUTTON_UP
#define JEWELS_DOWN BUTTON_DOWN
#define JEWELS_LEFT BUTTON_LEFT
#define JEWELS_RIGHT BUTTON_RIGHT
#define JEWELS_SELECT BUTTON_SELECT
#define JEWELS_CANCEL BUTTON_OFF
#define JEWELS_RC_CANCEL BUTTON_RC_STOP
#define HK_SELECT "SELECT"
#define HK_CANCEL "OFF"
#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
(CONFIG_KEYPAD == IPOD_1G2G_PAD)
#define JEWELS_SCROLLWHEEL
#define JEWELS_UP BUTTON_MENU
#define JEWELS_DOWN BUTTON_PLAY
#define JEWELS_LEFT BUTTON_LEFT
#define JEWELS_RIGHT BUTTON_RIGHT
#define JEWELS_PREV BUTTON_SCROLL_BACK
#define JEWELS_NEXT BUTTON_SCROLL_FWD
#define JEWELS_SELECT BUTTON_SELECT
#define JEWELS_CANCEL (BUTTON_SELECT | BUTTON_MENU)
#define HK_SELECT "SELECT"
#define HK_CANCEL "SEL + MENU"
#elif (CONFIG_KEYPAD == IPOD_3G_PAD)
#define JEWELS_LEFT BUTTON_LEFT
#define JEWELS_RIGHT BUTTON_RIGHT
#define JEWELS_UP BUTTON_SCROLL_BACK
#define JEWELS_DOWN BUTTON_SCROLL_FWD
#define JEWELS_SELECT BUTTON_SELECT
#define JEWELS_CANCEL BUTTON_MENU
#define HK_SELECT "SELECT"
#define HK_CANCEL "MENU"
#elif CONFIG_KEYPAD == IRIVER_IFP7XX_PAD
#define JEWELS_UP BUTTON_UP
#define JEWELS_DOWN BUTTON_DOWN
#define JEWELS_LEFT BUTTON_LEFT
#define JEWELS_RIGHT BUTTON_RIGHT
#define JEWELS_SELECT BUTTON_SELECT
#define JEWELS_CANCEL BUTTON_PLAY
#define HK_SELECT "SELECT"
#define HK_CANCEL "PLAY"
#elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
#define JEWELS_UP BUTTON_UP
#define JEWELS_DOWN BUTTON_DOWN
#define JEWELS_LEFT BUTTON_LEFT
#define JEWELS_RIGHT BUTTON_RIGHT
#define JEWELS_SELECT BUTTON_SELECT
#define JEWELS_CANCEL BUTTON_POWER
#define HK_SELECT "SELECT"
#define HK_CANCEL "POWER"
#elif CONFIG_KEYPAD == GIGABEAT_PAD
#define JEWELS_UP BUTTON_UP
#define JEWELS_DOWN BUTTON_DOWN
#define JEWELS_LEFT BUTTON_LEFT
#define JEWELS_RIGHT BUTTON_RIGHT
#define JEWELS_SELECT BUTTON_SELECT
#define JEWELS_CANCEL BUTTON_POWER
#define HK_SELECT "SELECT"
#define HK_CANCEL "POWER"
#elif CONFIG_KEYPAD == SONY_NWZ_PAD
#define JEWELS_UP BUTTON_UP
#define JEWELS_DOWN BUTTON_DOWN
#define JEWELS_LEFT BUTTON_LEFT
#define JEWELS_RIGHT BUTTON_RIGHT
#define JEWELS_SELECT BUTTON_PLAY
#define JEWELS_CANCEL BUTTON_BACK
#define HK_SELECT "PLAY"
#define HK_CANCEL "BACK"
#elif CONFIG_KEYPAD == CREATIVE_ZEN_PAD
#define JEWELS_UP BUTTON_UP
#define JEWELS_DOWN BUTTON_DOWN
#define JEWELS_LEFT BUTTON_LEFT
#define JEWELS_RIGHT BUTTON_RIGHT
#define JEWELS_SELECT BUTTON_SELECT
#define JEWELS_CANCEL BUTTON_BACK
#define HK_SELECT "SELECT"
#define HK_CANCEL "BACK"
#elif (CONFIG_KEYPAD == SANSA_E200_PAD) || \
(CONFIG_KEYPAD == SANSA_CONNECT_PAD)
#define JEWELS_SCROLLWHEEL
#define JEWELS_UP BUTTON_UP
#define JEWELS_DOWN BUTTON_DOWN
#define JEWELS_LEFT BUTTON_LEFT
#define JEWELS_RIGHT BUTTON_RIGHT
#define JEWELS_PREV BUTTON_SCROLL_BACK
#define JEWELS_NEXT BUTTON_SCROLL_FWD
#define JEWELS_SELECT BUTTON_SELECT
#define JEWELS_CANCEL BUTTON_POWER
#define HK_SELECT "SELECT"
#define HK_CANCEL "POWER"
#elif (CONFIG_KEYPAD == SANSA_FUZE_PAD)
#define JEWELS_SCROLLWHEEL
#define JEWELS_UP BUTTON_UP
#define JEWELS_DOWN BUTTON_DOWN
#define JEWELS_LEFT BUTTON_LEFT
#define JEWELS_RIGHT BUTTON_RIGHT
#define JEWELS_PREV BUTTON_SCROLL_BACK
#define JEWELS_NEXT BUTTON_SCROLL_FWD
#define JEWELS_SELECT BUTTON_SELECT
#define JEWELS_CANCEL (BUTTON_HOME|BUTTON_REPEAT)
#define HK_SELECT "SELECT"
#define HK_CANCEL "HOME"
#elif CONFIG_KEYPAD == SANSA_C200_PAD || \
CONFIG_KEYPAD == SANSA_CLIP_PAD || \
CONFIG_KEYPAD == SANSA_M200_PAD
#define JEWELS_UP BUTTON_UP
#define JEWELS_DOWN BUTTON_DOWN
#define JEWELS_LEFT BUTTON_LEFT
#define JEWELS_RIGHT BUTTON_RIGHT
#define JEWELS_SELECT BUTTON_SELECT
#define JEWELS_CANCEL BUTTON_POWER
#define HK_SELECT "SELECT"
#define HK_CANCEL "POWER"
#elif CONFIG_KEYPAD == IRIVER_H10_PAD
#define JEWELS_UP BUTTON_SCROLL_UP
#define JEWELS_DOWN BUTTON_SCROLL_DOWN
#define JEWELS_LEFT BUTTON_LEFT
#define JEWELS_RIGHT BUTTON_RIGHT
#define JEWELS_SELECT BUTTON_PLAY
#define JEWELS_CANCEL BUTTON_POWER
#define HK_SELECT "PLAY"
#define HK_CANCEL "POWER"
#elif CONFIG_KEYPAD == GIGABEAT_S_PAD || \
CONFIG_KEYPAD == SAMSUNG_YPR0_PAD
#define JEWELS_UP BUTTON_UP
#define JEWELS_DOWN BUTTON_DOWN
#define JEWELS_LEFT BUTTON_LEFT
#define JEWELS_RIGHT BUTTON_RIGHT
#define JEWELS_SELECT BUTTON_SELECT
#define JEWELS_CANCEL BUTTON_BACK
#define HK_SELECT "SELECT"
#define HK_CANCEL "BACK"
#elif CONFIG_KEYPAD == MROBE100_PAD
#define JEWELS_UP BUTTON_UP
#define JEWELS_DOWN BUTTON_DOWN
#define JEWELS_LEFT BUTTON_LEFT
#define JEWELS_RIGHT BUTTON_RIGHT
#define JEWELS_SELECT BUTTON_SELECT
#define JEWELS_CANCEL BUTTON_POWER
#define HK_SELECT "SELECT"
#define HK_CANCEL "POWER"
#elif CONFIG_KEYPAD == IAUDIO_M3_PAD
#define JEWELS_UP BUTTON_RC_VOL_UP
#define JEWELS_DOWN BUTTON_RC_VOL_DOWN
#define JEWELS_LEFT BUTTON_RC_REW
#define JEWELS_RIGHT BUTTON_RC_FF
#define JEWELS_SELECT BUTTON_RC_PLAY
#define JEWELS_CANCEL BUTTON_RC_REC
#define HK_SELECT "PLAY"
#define HK_CANCEL "REC"
#define JEWELS_RC_CANCEL BUTTON_REC
#elif CONFIG_KEYPAD == COWON_D2_PAD
#define JEWELS_CANCEL BUTTON_POWER
#define HK_CANCEL "POWER"
#elif CONFIG_KEYPAD == IAUDIO67_PAD
#define JEWELS_UP BUTTON_STOP
#define JEWELS_DOWN BUTTON_PLAY
#define JEWELS_LEFT BUTTON_LEFT
#define JEWELS_RIGHT BUTTON_RIGHT
#define JEWELS_SELECT BUTTON_MENU
#define JEWELS_CANCEL BUTTON_POWER
#define HK_SELECT "MENU"
#define HK_CANCEL "POWER"
#elif CONFIG_KEYPAD == CREATIVEZVM_PAD
#define JEWELS_UP BUTTON_UP
#define JEWELS_DOWN BUTTON_DOWN
#define JEWELS_LEFT BUTTON_LEFT
#define JEWELS_RIGHT BUTTON_RIGHT
#define JEWELS_SELECT BUTTON_SELECT
#define JEWELS_CANCEL BUTTON_BACK
#define HK_SELECT "MIDDLE"
#define HK_CANCEL "BACK"
#elif CONFIG_KEYPAD == CREATIVE_ZENXFI3_PAD
#define JEWELS_UP BUTTON_UP
#define JEWELS_DOWN BUTTON_DOWN
#define JEWELS_LEFT BUTTON_BACK
#define JEWELS_RIGHT BUTTON_MENU
#define JEWELS_SELECT BUTTON_VOL_UP
#define JEWELS_CANCEL BUTTON_VOL_DOWN
#define HK_SELECT "VOL+"
#define HK_CANCEL "VOL-"
#elif CONFIG_KEYPAD == PHILIPS_HDD1630_PAD
#define JEWELS_UP BUTTON_UP
#define JEWELS_DOWN BUTTON_DOWN
#define JEWELS_LEFT BUTTON_LEFT
#define JEWELS_RIGHT BUTTON_RIGHT
#define JEWELS_SELECT BUTTON_SELECT
#define JEWELS_CANCEL BUTTON_POWER
#define HK_SELECT "SELECT"
#define HK_CANCEL "POWER"
#elif CONFIG_KEYPAD == PHILIPS_HDD6330_PAD
#define JEWELS_UP BUTTON_UP
#define JEWELS_DOWN BUTTON_DOWN
#define JEWELS_LEFT BUTTON_LEFT
#define JEWELS_RIGHT BUTTON_RIGHT
#define JEWELS_SELECT BUTTON_PLAY
#define JEWELS_CANCEL BUTTON_POWER
#define HK_SELECT "PLAY"
#define HK_CANCEL "POWER"
#elif CONFIG_KEYPAD == PHILIPS_SA9200_PAD
#define JEWELS_UP BUTTON_UP
#define JEWELS_DOWN BUTTON_DOWN
#define JEWELS_LEFT BUTTON_PREV
#define JEWELS_RIGHT BUTTON_NEXT
#define JEWELS_SELECT BUTTON_PLAY
#define JEWELS_CANCEL BUTTON_POWER
#define HK_SELECT "PLAY"
#define HK_CANCEL "POWER"
#elif CONFIG_KEYPAD == ONDAVX747_PAD || \
CONFIG_KEYPAD == ONDAVX777_PAD || \
CONFIG_KEYPAD == MROBE500_PAD
#define JEWELS_CANCEL BUTTON_POWER
#define HK_CANCEL "POWER"
#elif (CONFIG_KEYPAD == SAMSUNG_YH820_PAD) || \
(CONFIG_KEYPAD == SAMSUNG_YH920_PAD)
#define JEWELS_UP BUTTON_UP
#define JEWELS_DOWN BUTTON_DOWN
#define JEWELS_LEFT BUTTON_LEFT
#define JEWELS_RIGHT BUTTON_RIGHT
#define JEWELS_SELECT BUTTON_PLAY
#define JEWELS_CANCEL BUTTON_REW
#define HK_SELECT "PLAY"
#define HK_CANCEL "REWIND"
#elif CONFIG_KEYPAD == PBELL_VIBE500_PAD
#define JEWELS_UP BUTTON_UP
#define JEWELS_DOWN BUTTON_DOWN
#define JEWELS_LEFT BUTTON_PREV
#define JEWELS_RIGHT BUTTON_NEXT
#define JEWELS_SELECT BUTTON_OK
#define JEWELS_CANCEL BUTTON_REC
#define HK_SELECT "OK"
#define HK_CANCEL "REC"
#elif CONFIG_KEYPAD == MPIO_HD200_PAD
#define JEWELS_LEFT BUTTON_VOL_DOWN
#define JEWELS_RIGHT BUTTON_VOL_UP
#define JEWELS_UP BUTTON_REW
#define JEWELS_DOWN BUTTON_FF
#define JEWELS_SELECT BUTTON_FUNC
#define JEWELS_CANCEL BUTTON_REC
#define HK_SELECT "FUNC"
#define HK_CANCEL "REC"
#elif CONFIG_KEYPAD == MPIO_HD300_PAD
#define JEWELS_LEFT BUTTON_REW
#define JEWELS_RIGHT BUTTON_FF
#define JEWELS_UP BUTTON_UP
#define JEWELS_DOWN BUTTON_DOWN
#define JEWELS_SELECT BUTTON_ENTER
#define JEWELS_CANCEL BUTTON_MENU
#define HK_SELECT "ENTER"
#define HK_CANCEL "MENU"
#elif CONFIG_KEYPAD == SANSA_FUZEPLUS_PAD
#define JEWELS_LEFT BUTTON_LEFT
#define JEWELS_RIGHT BUTTON_RIGHT
#define JEWELS_UP BUTTON_UP
#define JEWELS_DOWN BUTTON_DOWN
#define JEWELS_SELECT BUTTON_SELECT
#define JEWELS_CANCEL BUTTON_POWER
#define HK_SELECT "SELECT"
#define HK_CANCEL "POWER"
#elif (CONFIG_KEYPAD == HM60X_PAD) || \
(CONFIG_KEYPAD == HM801_PAD)
#define JEWELS_LEFT BUTTON_LEFT
#define JEWELS_RIGHT BUTTON_RIGHT
#define JEWELS_UP BUTTON_UP
#define JEWELS_DOWN BUTTON_DOWN
#define JEWELS_SELECT BUTTON_SELECT
#define JEWELS_CANCEL BUTTON_POWER
#define HK_SELECT "SELECT"
#define HK_CANCEL "POWER"
#elif CONFIG_KEYPAD == DX50_PAD
#define JEWELS_CANCEL BUTTON_POWER
#define HK_CANCEL "Power"
#else
#error No keymap defined!
#endif
#ifdef HAVE_TOUCHSCREEN
#ifndef JEWELS_UP
#define JEWELS_UP BUTTON_TOPMIDDLE
#endif
#ifndef JEWELS_DOWN
#define JEWELS_DOWN BUTTON_BOTTOMMIDDLE
#endif
#ifndef JEWELS_LEFT
#define JEWELS_LEFT BUTTON_MIDLEFT
#endif
#ifndef JEWELS_RIGHT
#define JEWELS_RIGHT BUTTON_MIDRIGHT
#endif
#ifndef JEWELS_SELECT
#define JEWELS_SELECT BUTTON_CENTER
#define HK_SELECT "CENTER"
#endif
#ifndef JEWELS_CANCEL
#define JEWELS_CANCEL BUTTON_TOPLEFT
#define HK_CANCEL "TOPLEFT"
#endif
#endif
#define TILE_WIDTH BMPWIDTH_jewels
#define TILE_HEIGHT (BMPHEIGHT_jewels/23)
#if LCD_HEIGHT < LCD_WIDTH
/* This calculation assumes integer division w/ LCD_HEIGHT/TILE_HEIGHT */
#define YOFS LCD_HEIGHT-((LCD_HEIGHT/TILE_HEIGHT)*TILE_HEIGHT)
#else
#define YOFS 0
#endif
#define NUM_SCORES 5
/* swap directions */
#define SWAP_UP 0
#define SWAP_RIGHT 1
#define SWAP_DOWN 2
#define SWAP_LEFT 3
/* play board dimension */
#define BJ_HEIGHT 9
#define BJ_WIDTH 8
/* next level threshold */
#define LEVEL_PTS 100
/* animation frame rate */
#define MAX_FPS 20
/* text margin */
#define MARGIN 5
/* Game types */
enum game_type {
GAME_TYPE_NORMAL,
GAME_TYPE_PUZZLE
};
/* external bitmaps */
extern const fb_data jewels[];
/* tile background colors */
#ifdef HAVE_LCD_COLOR
static const unsigned jewels_bkgd[2] = {
LCD_RGBPACK(104, 63, 63),
LCD_RGBPACK(83, 44, 44)
};
#endif
/* the tile struct
* type is the jewel number 0-7
* falling if the jewel is falling
* delete marks the jewel for deletion
*/
struct tile {
int type;
bool falling;
bool delete;
};
/* the game context struct
* score is the current level score
* segments is the number of cleared segments in the current run
* level is the current level
* tmp_type is the select type in the menu
* type is the game type (normal or puzzle)
* playboard is the game playing board (first row is hidden)
* num_jewels is the number of different jewels to use
*/
struct game_context {
unsigned int score;
unsigned int segments;
unsigned int level;
unsigned int type;
unsigned int tmp_type;
struct tile playboard[BJ_HEIGHT][BJ_WIDTH];
unsigned int num_jewels;
};
#define MAX_NUM_JEWELS 7
#define MAX_PUZZLE_TILES 4
#define NUM_PUZZLE_LEVELS 10
struct puzzle_tile {
int x;
int y;
int tile_type;
};
struct puzzle_level {
unsigned int num_jewels;
unsigned int num_tiles;
struct puzzle_tile tiles[MAX_PUZZLE_TILES];
};
#define PUZZLE_TILE_UP 1
#define PUZZLE_TILE_DOWN 2
#define PUZZLE_TILE_LEFT 4
#define PUZZLE_TILE_RIGHT 8
struct puzzle_level puzzle_levels[NUM_PUZZLE_LEVELS] = {
{ 5, 2, { {3, 3, PUZZLE_TILE_RIGHT},
{4, 2, PUZZLE_TILE_LEFT} } },
{ 5, 2, { {3, 2, PUZZLE_TILE_DOWN},
{3, 4, PUZZLE_TILE_UP} } },
{ 6, 3, { {3, 2, PUZZLE_TILE_DOWN},
{3, 4, PUZZLE_TILE_UP|PUZZLE_TILE_DOWN},
{3, 6, PUZZLE_TILE_UP} } },
{ 6, 3, { {3, 2, PUZZLE_TILE_RIGHT},
{4, 3, PUZZLE_TILE_LEFT|PUZZLE_TILE_RIGHT},
{5, 4, PUZZLE_TILE_LEFT} } },
{ 6, 2, { {3, 4, PUZZLE_TILE_RIGHT},
{4, 2, PUZZLE_TILE_LEFT} } },
{ 6, 2, { {3, 2, PUZZLE_TILE_DOWN},
{4, 4, PUZZLE_TILE_UP} } },
{ 7, 4, { {3, 2, PUZZLE_TILE_RIGHT|PUZZLE_TILE_DOWN},
{4, 3, PUZZLE_TILE_LEFT|PUZZLE_TILE_DOWN},
{3, 4, PUZZLE_TILE_RIGHT|PUZZLE_TILE_UP},
{4, 4, PUZZLE_TILE_LEFT|PUZZLE_TILE_UP} } },
{ 6, 3, { {3, 2, PUZZLE_TILE_DOWN},
{4, 4, PUZZLE_TILE_UP|PUZZLE_TILE_DOWN},
{3, 6, PUZZLE_TILE_UP} } },
{ 7, 3, { {2, 2, PUZZLE_TILE_RIGHT},
{4, 1, PUZZLE_TILE_LEFT|PUZZLE_TILE_RIGHT},
{5, 4, PUZZLE_TILE_LEFT} } },
{ 7, 4, { {3, 0, PUZZLE_TILE_RIGHT|PUZZLE_TILE_DOWN},
{5, 0, PUZZLE_TILE_LEFT|PUZZLE_TILE_DOWN},
{2, 7, PUZZLE_TILE_RIGHT|PUZZLE_TILE_UP},
{4, 7, PUZZLE_TILE_LEFT|PUZZLE_TILE_UP} } },
};
#define SAVE_FILE PLUGIN_GAMES_DATA_DIR "/jewels.save"
#define SCORE_FILE PLUGIN_GAMES_DATA_DIR "/jewels.score"
struct highscore highscores[NUM_SCORES];
static bool resume_file = false;
/*****************************************************************************
* jewels_setcolors() set the foreground and background colors.
******************************************************************************/
static inline void jewels_setcolors(void) {
#ifdef HAVE_LCD_COLOR
rb->lcd_set_background(LCD_RGBPACK(49, 26, 26));
rb->lcd_set_foreground(LCD_RGBPACK(210, 181, 181));
#endif
}
/*****************************************************************************
* jewels_loadgame() loads the saved game and returns load success.
******************************************************************************/
static bool jewels_loadgame(struct game_context* bj)
{
int fd;
bool loaded = false;
/* open game file */
fd = rb->open(SAVE_FILE, O_RDONLY);
if(fd < 0) return loaded;
/* read in saved game */
while(true) {
if(rb->read(fd, &bj->tmp_type, sizeof(bj->tmp_type)) <= 0) break;
if(rb->read(fd, &bj->type, sizeof(bj->type)) <= 0) break;
if(rb->read(fd, &bj->score, sizeof(bj->score)) <= 0) break;
if(rb->read(fd, &bj->level, sizeof(bj->level)) <= 0) break;
if(rb->read(fd, &bj->segments, sizeof(bj->segments)) <= 0) break;
if(rb->read(fd, &bj->num_jewels, sizeof(bj->num_jewels)) <= 0) break;
if(rb->read(fd, bj->playboard, sizeof(bj->playboard)) <= 0) break;
loaded = true;
break;
}
rb->close(fd);
return loaded;
}
/*****************************************************************************
* jewels_savegame() saves the current game state.
******************************************************************************/
static void jewels_savegame(struct game_context* bj)
{
int fd;
/* write out the game state to the save file */
fd = rb->open(SAVE_FILE, O_WRONLY|O_CREAT, 0666);
if(fd < 0) return;
rb->write(fd, &bj->tmp_type, sizeof(bj->tmp_type));
rb->write(fd, &bj->type, sizeof(bj->type));
rb->write(fd, &bj->score, sizeof(bj->score));
rb->write(fd, &bj->level, sizeof(bj->level));
rb->write(fd, &bj->segments, sizeof(bj->segments));
rb->write(fd, &bj->num_jewels, sizeof(bj->num_jewels));
rb->write(fd, bj->playboard, sizeof(bj->playboard));
rb->close(fd);
}
/*****************************************************************************
* jewels_drawboard() redraws the entire game board.
******************************************************************************/
static void jewels_drawboard(struct game_context* bj) {
int i, j;
int w, h;
unsigned int tempscore;
unsigned int size;
char *title = "Level";
char str[10];
if (bj->type == GAME_TYPE_NORMAL) {
tempscore = (bj->score>LEVEL_PTS ? LEVEL_PTS : bj->score);
size = LEVEL_PTS;
} else {
tempscore = (bj->level>NUM_PUZZLE_LEVELS ? NUM_PUZZLE_LEVELS : bj->level);
size = NUM_PUZZLE_LEVELS;
}
/* clear screen */
rb->lcd_clear_display();
/* dispay playing board */
for(i=0; i<BJ_HEIGHT-1; i++){
for(j=0; j<BJ_WIDTH; j++){
#ifdef HAVE_LCD_COLOR
rb->lcd_set_foreground(jewels_bkgd[(i+j)%2]);
rb->lcd_fillrect(j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
TILE_WIDTH, TILE_HEIGHT);
rb->lcd_bitmap_transparent_part(jewels,
0, TILE_HEIGHT*(bj->playboard[i+1][j].type),
STRIDE( SCREEN_MAIN,
BMPWIDTH_jewels, BMPHEIGHT_jewels),
j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
TILE_WIDTH, TILE_HEIGHT);
#else
rb->lcd_bitmap_part(jewels,
0, TILE_HEIGHT*(bj->playboard[i+1][j].type),
STRIDE( SCREEN_MAIN,
BMPWIDTH_jewels, BMPHEIGHT_jewels),
j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
TILE_WIDTH, TILE_HEIGHT);
#endif
}
}
#if LCD_WIDTH > LCD_HEIGHT /* horizontal layout */
/* draw separator lines */
jewels_setcolors();
rb->lcd_vline(BJ_WIDTH*TILE_WIDTH, 0, LCD_HEIGHT-1);
rb->lcd_hline(BJ_WIDTH*TILE_WIDTH, LCD_WIDTH-1, 18);
rb->lcd_hline(BJ_WIDTH*TILE_WIDTH, LCD_WIDTH-1, LCD_HEIGHT-10);
/* draw progress bar */
#ifdef HAVE_LCD_COLOR
rb->lcd_set_foreground(LCD_RGBPACK(104, 63, 63));
#endif
rb->lcd_fillrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4,
(LCD_HEIGHT-10)-(((LCD_HEIGHT-10)-18)*
tempscore/size),
(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2,
((LCD_HEIGHT-10)-18)*tempscore/size);
#ifdef HAVE_LCD_COLOR
rb->lcd_set_foreground(LCD_RGBPACK(83, 44, 44));
rb->lcd_drawrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4+1,
(LCD_HEIGHT-10)-(((LCD_HEIGHT-10)-18)*
tempscore/size)+1,
(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-2,
((LCD_HEIGHT-10)-18)*tempscore/size-1);
jewels_setcolors();
rb->lcd_drawrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4,
(LCD_HEIGHT-10)-(((LCD_HEIGHT-10)-18)*tempscore/size),
(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2,
((LCD_HEIGHT-10)-18)*tempscore/size+1);
#endif
/* print text */
rb->lcd_getstringsize(title, &w, &h);
rb->lcd_putsxy(LCD_WIDTH-(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-w/2, 1, title);
rb->snprintf(str, 4, "%d", bj->level);
rb->lcd_getstringsize(str, &w, &h);
rb->lcd_putsxy(LCD_WIDTH-(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-w/2, 10, str);
if (bj->type == GAME_TYPE_NORMAL) {
rb->snprintf(str, 6, "%d", (bj->level-1)*LEVEL_PTS+bj->score);
rb->lcd_getstringsize(str, &w, &h);
rb->lcd_putsxy(LCD_WIDTH-(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-w/2,
LCD_HEIGHT-8, str);
}
#elif LCD_WIDTH < LCD_HEIGHT /* vertical layout */
/* draw separator lines */
jewels_setcolors();
rb->lcd_hline(0, LCD_WIDTH-1, 8*TILE_HEIGHT+YOFS);
rb->lcd_hline(0, LCD_WIDTH-1, LCD_HEIGHT-14);
rb->lcd_vline(LCD_WIDTH/2, LCD_HEIGHT-14, LCD_HEIGHT-1);
/* draw progress bar */
#ifdef HAVE_LCD_COLOR
rb->lcd_set_foreground(LCD_RGBPACK(104, 63, 63));
#endif
rb->lcd_fillrect(0, (8*TILE_HEIGHT+YOFS)
+(LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/4,
LCD_WIDTH*tempscore/size,
(LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/2);
#ifdef HAVE_LCD_COLOR
rb->lcd_set_foreground(LCD_RGBPACK(83, 44, 44));
rb->lcd_drawrect(1, (8*TILE_HEIGHT+YOFS)
+(LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/4+1,
LCD_WIDTH*tempscore/size-1,
(LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/2-2);
jewels_setcolors();
rb->lcd_drawrect(0, (8*TILE_HEIGHT+YOFS)
+(LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/4,
LCD_WIDTH*tempscore/size+1,
(LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/2);
#endif
/* print text */
rb->lcd_putsxyf(1, LCD_HEIGHT-10, "%s %d", title, bj->level);
if (bj->type == GAME_TYPE_NORMAL) {
rb->snprintf(str, 6, "%d", (bj->level-1)*LEVEL_PTS+bj->score);
rb->lcd_getstringsize(str, &w, &h);
rb->lcd_putsxy((LCD_WIDTH-2)-w, LCD_HEIGHT-10, str);
}
#else /* square layout */
/* draw separator lines */
jewels_setcolors();
rb->lcd_hline(0, LCD_WIDTH-1, 8*TILE_HEIGHT+YOFS);
rb->lcd_vline(BJ_WIDTH*TILE_WIDTH, 0, 8*TILE_HEIGHT+YOFS);
rb->lcd_vline(LCD_WIDTH/2, 8*TILE_HEIGHT+YOFS, LCD_HEIGHT-1);
/* draw progress bar */
#ifdef HAVE_LCD_COLOR
rb->lcd_set_foreground(LCD_RGBPACK(104, 63, 63));
#endif
rb->lcd_fillrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4,
(8*TILE_HEIGHT+YOFS)-(8*TILE_HEIGHT+YOFS)*tempscore/size,
(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2,
(8*TILE_HEIGHT+YOFS)*tempscore/size);
#ifdef HAVE_LCD_COLOR
rb->lcd_set_foreground(LCD_RGBPACK(83, 44, 44));
rb->lcd_drawrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4+1,
(8*TILE_HEIGHT+YOFS)-(8*TILE_HEIGHT+YOFS)
*tempscore/size+1,
(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-2,
(8*TILE_HEIGHT+YOFS)*tempscore/size-1);
jewels_setcolors();
rb->lcd_drawrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4,
(8*TILE_HEIGHT+YOFS)-(8*TILE_HEIGHT+YOFS)
*tempscore/size,
(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2,
(8*TILE_HEIGHT+YOFS)*tempscore/size+1);
#endif
/* print text */
rb->lcd_putsxyf(1, LCD_HEIGHT-(LCD_HEIGHT-(8*TILE_HEIGHT+YOFS))/2-3,"%s %d",
title, bj->level);
if (bj->type == GAME_TYPE_NORMAL) {
rb->snprintf(str, 6, "%d", (bj->level-1)*LEVEL_PTS+bj->score);
rb->lcd_getstringsize(str, &w, &h);
rb->lcd_putsxy((LCD_WIDTH-2)-w,
LCD_HEIGHT-(LCD_HEIGHT-(8*TILE_HEIGHT+YOFS))/2-3, str);
}
#endif /* layout */
rb->lcd_update();
}
/*****************************************************************************
* jewels_putjewels() makes the jewels fall to fill empty spots and adds
* new random jewels at the empty spots at the top of each row.
******************************************************************************/
static void jewels_putjewels(struct game_context* bj){
int i, j, k;
bool mark, done;
long lasttick, currenttick;
/* loop to make all the jewels fall */
while(true) {
/* mark falling jewels and add new jewels to hidden top row*/
mark = false;
done = true;
for(j=0; j<BJ_WIDTH; j++) {
if(bj->playboard[1][j].type == 0) {
bj->playboard[0][j].type = rb->rand()%bj->num_jewels+1;
}
for(i=BJ_HEIGHT-2; i>=0; i--) {
if(!mark && bj->playboard[i+1][j].type == 0) {
mark = true;
done = false;
}
if(mark) bj->playboard[i][j].falling = true;
}
/*if(bj->playboard[1][j].falling) {
bj->playboard[0][j].type = rb->rand()%bj->num_jewels+1;
bj->playboard[0][j].falling = true;
}*/
mark = false;
}
/* break if there are no falling jewels */
if(done) break;
/* animate falling jewels */
lasttick = *rb->current_tick;
for(k=1; k<=8; k++) {
for(i=BJ_HEIGHT-2; i>=0; i--) {
for(j=0; j<BJ_WIDTH; j++) {
if(bj->playboard[i][j].falling &&
bj->playboard[i][j].type != 0) {
/* clear old position */
#ifdef HAVE_LCD_COLOR
if(i == 0 && YOFS) {
rb->lcd_set_foreground(rb->lcd_get_background());
} else {
rb->lcd_set_foreground(jewels_bkgd[(i-1+j)%2]);
}
rb->lcd_fillrect(j*TILE_WIDTH, (i-1)*TILE_HEIGHT+YOFS,
TILE_WIDTH, TILE_HEIGHT);
if(bj->playboard[i+1][j].type == 0) {
rb->lcd_set_foreground(jewels_bkgd[(i+j)%2]);
rb->lcd_fillrect(j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
TILE_WIDTH, TILE_HEIGHT);
}
#else
rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
rb->lcd_fillrect(j*TILE_WIDTH, (i-1)*TILE_HEIGHT+YOFS,
TILE_WIDTH, TILE_HEIGHT);
if(bj->playboard[i+1][j].type == 0) {
rb->lcd_fillrect(j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
TILE_WIDTH, TILE_HEIGHT);
}
rb->lcd_set_drawmode(DRMODE_SOLID);
#endif
/* draw new position */
#ifdef HAVE_LCD_COLOR
rb->lcd_bitmap_transparent_part(jewels, 0,
TILE_HEIGHT*(bj->playboard[i][j].type),
STRIDE( SCREEN_MAIN,
BMPWIDTH_jewels,
BMPHEIGHT_jewels),
j*TILE_WIDTH,
(i-1)*TILE_HEIGHT+YOFS+
((((TILE_HEIGHT<<10)*k)/8)>>10),
TILE_WIDTH, TILE_HEIGHT);
#else
rb->lcd_bitmap_part(jewels, 0,
TILE_HEIGHT*(bj->playboard[i][j].type),
STRIDE( SCREEN_MAIN,
BMPWIDTH_jewels,
BMPHEIGHT_jewels),
j*TILE_WIDTH,
(i-1)*TILE_HEIGHT+YOFS+
((((TILE_HEIGHT<<10)*k)/8)>>10),
TILE_WIDTH, TILE_HEIGHT);
#endif
}
}
}
rb->lcd_update_rect(0, 0, TILE_WIDTH*8, LCD_HEIGHT);
jewels_setcolors();
/* framerate limiting */
currenttick = *rb->current_tick;
if(currenttick-lasttick < HZ/MAX_FPS) {
rb->sleep((HZ/MAX_FPS)-(currenttick-lasttick));
} else {
rb->yield();
}
lasttick = currenttick;
}
/* shift jewels down */
for(j=0; j<BJ_WIDTH; j++) {
for(i=BJ_HEIGHT-1; i>=1; i--) {
if(bj->playboard[i-1][j].falling) {
bj->playboard[i][j].type = bj->playboard[i-1][j].type;
}
}
}
/* clear out top row */
for(j=0; j<BJ_WIDTH; j++) {
bj->playboard[0][j].type = 0;
}
/* mark everything not falling */
for(i=0; i<BJ_HEIGHT; i++) {
for(j=0; j<BJ_WIDTH; j++) {
bj->playboard[i][j].falling = false;
}
}
}
}
/*****************************************************************************
* jewels_clearjewels() finds all the connected rows and columns and
* calculates and returns the points earned.
******************************************************************************/
static unsigned int jewels_clearjewels(struct game_context* bj) {
int i, j;
int last, run;
unsigned int points = 0;
/* check for connected rows */
for(i=1; i<BJ_HEIGHT; i++) {
last = 0;
run = 1;
for(j=0; j<BJ_WIDTH; j++) {
if(bj->playboard[i][j].type == last &&
bj->playboard[i][j].type != 0 &&
bj->playboard[i][j].type <= MAX_NUM_JEWELS) {
run++;
if(run == 3) {
bj->segments++;
points += bj->segments;
bj->playboard[i][j].delete = true;
bj->playboard[i][j-1].delete = true;
bj->playboard[i][j-2].delete = true;
} else if(run > 3) {
points++;
bj->playboard[i][j].delete = true;
}
} else {
run = 1;
last = bj->playboard[i][j].type;
}
}
}
/* check for connected columns */
for(j=0; j<BJ_WIDTH; j++) {
last = 0;
run = 1;
for(i=1; i<BJ_HEIGHT; i++) {
if(bj->playboard[i][j].type != 0 &&
bj->playboard[i][j].type == last &&
bj->playboard[i][j].type <= MAX_NUM_JEWELS) {
run++;
if(run == 3) {
bj->segments++;
points += bj->segments;
bj->playboard[i][j].delete = true;
bj->playboard[i-1][j].delete = true;
bj->playboard[i-2][j].delete = true;
} else if(run > 3) {
points++;
bj->playboard[i][j].delete = true;
}
} else {
run = 1;
last = bj->playboard[i][j].type;
}
}
}
/* clear deleted jewels */
for(i=1; i<BJ_HEIGHT; i++) {
for(j=0; j<BJ_WIDTH; j++) {
if(bj->playboard[i][j].delete) {
bj->playboard[i][j].delete = false;
bj->playboard[i][j].type = 0;
}
}
}
return points;
}
/*****************************************************************************
* jewels_runboard() runs the board until it settles in a fixed state and
* returns points earned.
******************************************************************************/
static unsigned int jewels_runboard(struct game_context* bj) {
unsigned int points = 0;
unsigned int ret;
bj->segments = 0;
while((ret = jewels_clearjewels(bj)) > 0) {
points += ret;
jewels_drawboard(bj);
jewels_putjewels(bj);
}
return points;
}
/*****************************************************************************
* jewels_swapjewels() swaps two jewels as long as it results in points and
* returns points earned.
******************************************************************************/
static unsigned int jewels_swapjewels(struct game_context* bj,
int x, int y, int direc) {
int k;
int horzmod, vertmod;
int movelen = 0;
bool undo = false;
unsigned int points = 0;
long lasttick, currenttick;
/* check for invalid parameters */
if(x < 0 || x >= BJ_WIDTH || y < 0 || y >= BJ_HEIGHT-1 ||
direc < SWAP_UP || direc > SWAP_LEFT) {
return 0;
}
/* check for invalid directions */
if((x == 0 && direc == SWAP_LEFT) ||
(x == BJ_WIDTH-1 && direc == SWAP_RIGHT) ||
(y == 0 && direc == SWAP_UP) ||
(y == BJ_HEIGHT-2 && direc == SWAP_DOWN)) {
return 0;
}
/* set direction variables */
horzmod = 0;
vertmod = 0;
switch(direc) {
case SWAP_UP:
vertmod = -1;
movelen = TILE_HEIGHT;
break;
case SWAP_RIGHT:
horzmod = 1;
movelen = TILE_WIDTH;
break;
case SWAP_DOWN:
vertmod = 1;
movelen = TILE_HEIGHT;
break;
case SWAP_LEFT:
horzmod = -1;
movelen = TILE_WIDTH;
break;
}
while(true) {
lasttick = *rb->current_tick;
/* animate swapping jewels */
for(k=0; k<=8; k++) {
/* clear old position */
#ifdef HAVE_LCD_COLOR
rb->lcd_set_foreground(jewels_bkgd[(x+y)%2]);
rb->lcd_fillrect(x*TILE_WIDTH,
y*TILE_HEIGHT+YOFS,
TILE_WIDTH, TILE_HEIGHT);
rb->lcd_set_foreground(jewels_bkgd[(x+horzmod+y+vertmod)%2]);
rb->lcd_fillrect((x+horzmod)*TILE_WIDTH,
(y+vertmod)*TILE_HEIGHT+YOFS,
TILE_WIDTH, TILE_HEIGHT);
#else
rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
rb->lcd_fillrect(x*TILE_WIDTH,
y*TILE_HEIGHT+YOFS,
TILE_WIDTH, TILE_HEIGHT);
rb->lcd_fillrect((x+horzmod)*TILE_WIDTH,
(y+vertmod)*TILE_HEIGHT+YOFS,
TILE_WIDTH, TILE_HEIGHT);
rb->lcd_set_drawmode(DRMODE_SOLID);
#endif
/* draw new position */
#ifdef HAVE_LCD_COLOR
rb->lcd_bitmap_transparent_part(jewels,
0, TILE_HEIGHT*(bj->playboard
[y+1+vertmod][x+horzmod].type),
STRIDE( SCREEN_MAIN,
BMPWIDTH_jewels, BMPHEIGHT_jewels),
(x+horzmod)*TILE_WIDTH-horzmod*
((((movelen<<10)*k)/8)>>10),
(y+vertmod)*TILE_HEIGHT-vertmod*
((((movelen<<10)*k)/8)>>10)+YOFS,
TILE_WIDTH, TILE_HEIGHT);
rb->lcd_bitmap_transparent_part(jewels,
0, TILE_HEIGHT*(bj->playboard[y+1][x].type),
STRIDE( SCREEN_MAIN,
BMPWIDTH_jewels, BMPHEIGHT_jewels),
x*TILE_WIDTH+horzmod*
((((movelen<<10)*k)/8)>>10),
y*TILE_HEIGHT+vertmod*
((((movelen<<10)*k)/8)>>10)+YOFS,
TILE_WIDTH, TILE_HEIGHT);
#else
rb->lcd_bitmap_part(jewels,
0, TILE_HEIGHT*(bj->playboard
[y+1+vertmod][x+horzmod].type),
STRIDE( SCREEN_MAIN,
BMPWIDTH_jewels, BMPHEIGHT_jewels),
(x+horzmod)*TILE_WIDTH-horzmod*
((((movelen<<10)*k)/8)>>10),
(y+vertmod)*TILE_HEIGHT-vertmod*
((((movelen<<10)*k)/8)>>10)+YOFS,
TILE_WIDTH, TILE_HEIGHT);
rb->lcd_set_drawmode(DRMODE_FG);
rb->lcd_bitmap_part(jewels,
0, TILE_HEIGHT*(bj->playboard[y+1][x].type),
STRIDE( SCREEN_MAIN,
BMPWIDTH_jewels, BMPHEIGHT_jewels),
x*TILE_WIDTH+horzmod*
((((movelen<<10)*k)/8)>>10),
y*TILE_HEIGHT+vertmod*
((((movelen<<10)*k)/8)>>10)+YOFS,
TILE_WIDTH, TILE_HEIGHT);
rb->lcd_set_drawmode(DRMODE_SOLID);
#endif
rb->lcd_update_rect(0, 0, TILE_WIDTH*8, LCD_HEIGHT);
jewels_setcolors();
/* framerate limiting */
currenttick = *rb->current_tick;
if(currenttick-lasttick < HZ/MAX_FPS) {
rb->sleep((HZ/MAX_FPS)-(currenttick-lasttick));
} else {
rb->yield();
}
lasttick = currenttick;
}
/* swap jewels */
int temp = bj->playboard[y+1][x].type;
bj->playboard[y+1][x].type =
bj->playboard[y+1+vertmod][x+horzmod].type;
bj->playboard[y+1+vertmod][x+horzmod].type = temp;
if(undo) break;
points = jewels_runboard(bj);
if(points == 0) {
undo = true;
} else {
break;
}
}
return points;
}
/*****************************************************************************
* jewels_movesavail() uses pattern matching to see if there are any
* available move left.
******************************************************************************/
static bool jewels_movesavail(struct game_context* bj) {
int i, j;
bool moves = false;
int mytype;
for(i=1; i<BJ_HEIGHT; i++) {
for(j=0; j<BJ_WIDTH; j++) {
mytype = bj->playboard[i][j].type;
if(mytype == 0 || mytype > MAX_NUM_JEWELS) continue;
/* check horizontal patterns */
if(j <= BJ_WIDTH-3) {
if(i > 1) {
if(bj->playboard[i-1][j+1].type == mytype) {
if(bj->playboard[i-1][j+2].type == mytype)
{moves = true; break;}
if(bj->playboard[i][j+2].type == mytype)
{moves = true; break;}
}
if(bj->playboard[i][j+1].type == mytype) {
if(bj->playboard[i-1][j+2].type == mytype)
{moves = true; break;}
}
}
if(j <= BJ_WIDTH-4) {
if(bj->playboard[i][j+3].type == mytype) {
if(bj->playboard[i][j+1].type == mytype)
{moves = true; break;}
if(bj->playboard[i][j+2].type == mytype)
{moves = true; break;}
}
}
if(i < BJ_HEIGHT-1) {
if(bj->playboard[i][j+1].type == mytype) {
if(bj->playboard[i+1][j+2].type == mytype)
{moves = true; break;}
}
if(bj->playboard[i+1][j+1].type == mytype) {
if(bj->playboard[i][j+2].type == mytype)
{moves = true; break;}
if(bj->playboard[i+1][j+2].type == mytype)
{moves = true; break;}
}
}
}
/* check vertical patterns */
if(i <= BJ_HEIGHT-3) {
if(j > 0) {
if(bj->playboard[i+1][j-1].type == mytype) {
if(bj->playboard[i+2][j-1].type == mytype)
{moves = true; break;}
if(bj->playboard[i+2][j].type == mytype)
{moves = true; break;}
}
if(bj->playboard[i+1][j].type == mytype) {
if(bj->playboard[i+2][j-1].type == mytype)
{moves = true; break;}
}
}
if(i <= BJ_HEIGHT-4) {
if(bj->playboard[i+3][j].type == mytype) {
if(bj->playboard[i+1][j].type == mytype)
{moves = true; break;}
if(bj->playboard[i+2][j].type == mytype)
{moves = true; break;}
}
}
if(j < BJ_WIDTH-1) {
if(bj->playboard[i+1][j].type == mytype) {
if(bj->playboard[i+2][j+1].type == mytype)
{moves = true; break;}
}
if(bj->playboard[i+1][j+1].type == mytype) {
if(bj->playboard[i+2][j].type == mytype)
{moves = true; break;}
if (bj->playboard[i+2][j+1].type == mytype)
{moves = true; break;}
}
}
}
}
if(moves) break;
}
return moves;
}
/*****************************************************************************
* jewels_puzzle_is_finished() checks if the puzzle is finished.
******************************************************************************/
static bool jewels_puzzle_is_finished(struct game_context* bj) {
unsigned int i, j;
for(i=0; i<BJ_HEIGHT; i++) {
for(j=0; j<BJ_WIDTH; j++) {
int mytype = bj->playboard[i][j].type;
if(mytype>MAX_NUM_JEWELS) {
mytype -= MAX_NUM_JEWELS;
if(mytype&PUZZLE_TILE_UP) {
if(i==0 || bj->playboard[i-1][j].type<=MAX_NUM_JEWELS ||
!((bj->playboard[i-1][j].type-MAX_NUM_JEWELS)
&PUZZLE_TILE_DOWN))
return false;
}
if(mytype&PUZZLE_TILE_DOWN) {
if(i==BJ_HEIGHT-1 ||
bj->playboard[i+1][j].type<=MAX_NUM_JEWELS ||
!((bj->playboard[i+1][j].type-MAX_NUM_JEWELS)
&PUZZLE_TILE_UP))
return false;
}
if(mytype&PUZZLE_TILE_LEFT) {
if(j==0 || bj->playboard[i][j-1].type<=MAX_NUM_JEWELS ||
!((bj->playboard[i][j-1].type-MAX_NUM_JEWELS)
&PUZZLE_TILE_RIGHT))
return false;
}
if(mytype&PUZZLE_TILE_RIGHT) {
if(j==BJ_WIDTH-1 ||
bj->playboard[i][j+1].type<=MAX_NUM_JEWELS ||
!((bj->playboard[i][j+1].type-MAX_NUM_JEWELS)
&PUZZLE_TILE_LEFT))
return false;
}
}
}
}
return true;
}
/*****************************************************************************
* jewels_initlevel() initialises a level.
******************************************************************************/
static unsigned int jewels_initlevel(struct game_context* bj) {
unsigned int points = 0;
switch(bj->type) {
case GAME_TYPE_NORMAL:
bj->num_jewels = MAX_NUM_JEWELS;
break;
case GAME_TYPE_PUZZLE:
{
unsigned int i, j;
struct puzzle_tile *tile;
bj->num_jewels = puzzle_levels[bj->level-1].num_jewels;
for(i=0; i<BJ_HEIGHT; i++) {
for(j=0; j<BJ_WIDTH; j++) {
bj->playboard[i][j].type = (rb->rand()%bj->num_jewels)+1;
bj->playboard[i][j].falling = false;
bj->playboard[i][j].delete = false;
}
}
jewels_runboard(bj);
tile = puzzle_levels[bj->level-1].tiles;
for(i=0; i<puzzle_levels[bj->level-1].num_tiles; i++, tile++) {
bj->playboard[tile->y+1][tile->x].type = MAX_NUM_JEWELS
+tile->tile_type;
}
}
break;
}
jewels_drawboard(bj);
/* run the play board */
jewels_putjewels(bj);
points += jewels_runboard(bj);
return points;
}
/*****************************************************************************
* jewels_init() initializes jewels data structures.
******************************************************************************/
static void jewels_init(struct game_context* bj) {
/* seed the rand generator */
rb->srand(*rb->current_tick);
bj->type = bj->tmp_type;
bj->level = 1;
bj->score = 0;
bj->segments = 0;
jewels_setcolors();
/* clear playing board */
rb->memset(bj->playboard, 0, sizeof(bj->playboard));
do {
bj->score += jewels_initlevel(bj);
} while(!jewels_movesavail(bj));
}
/*****************************************************************************
* jewels_nextlevel() advances the game to the next bj->level and returns
* points earned.
******************************************************************************/
static void jewels_nextlevel(struct game_context* bj) {
int i, x, y;
unsigned int points = 0;
switch(bj->type) {
case GAME_TYPE_NORMAL:
/* roll over score, change and display level */
while(bj->score >= LEVEL_PTS) {
bj->score -= LEVEL_PTS;
bj->level++;
rb->splashf(HZ*2, "Level %d", bj->level);
jewels_drawboard(bj);
}
/* randomly clear some jewels */
for(i=0; i<16; i++) {
x = rb->rand()%8;
y = rb->rand()%8;
if(bj->playboard[y][x].type != 0) {
points++;
bj->playboard[y][x].type = 0;
}
}
break;
case GAME_TYPE_PUZZLE:
bj->level++;
rb->splashf(HZ*2, "Level %d", bj->level);
break;
}
points += jewels_initlevel(bj);
bj->score += points;
}
static bool jewels_help(void)
{
static char *help_text[] = {
"Jewels", "", "Aim", "",
"Swap", "pairs", "of", "jewels", "to", "form", "connected",
"segments", "of", "three", "or", "more", "of", "the", "same",
"type.", "",
"The", "goal", "of", "the", "game", "is", "to", "score", "as", "many",
"points", "as", "possible", "before", "running", "out", "of",
"available", "moves.", "", "",
"Controls", "",
"Directions",
#ifdef JEWELS_SCROLLWHEEL
"or", "scroll",
#endif
"to", "move", "",
HK_SELECT, "to", "select", "",
HK_CANCEL, "to", "go", "to", "menu"
};
static struct style_text formation[]={
{ 0, TEXT_CENTER|TEXT_UNDERLINE },
{ 2, C_RED },
{ 42, C_RED },
LAST_STYLE_ITEM
};
rb->lcd_setfont(FONT_UI);
if (display_text(ARRAYLEN(help_text), help_text, formation, NULL, true))
return true;
rb->lcd_setfont(FONT_SYSFIXED);
return false;
}
static bool _ingame;
static int jewels_menu_cb(int action, const struct menu_item_ex *this_item)
{
int i = ((intptr_t)this_item);
if(action == ACTION_REQUEST_MENUITEM
&& !_ingame && (i==0 || i==6))
return ACTION_EXIT_MENUITEM;
return action;
}
/*****************************************************************************
* jewels_game_menu() shows the game menu.
******************************************************************************/
static int jewels_game_menu(struct game_context* bj, bool ingame)
{
rb->button_clear_queue();
int choice = 0;
_ingame = ingame;
static struct opt_items mode[] = {
{ "Normal", -1 },
{ "Puzzle", -1 },
};
MENUITEM_STRINGLIST (main_menu, "Jewels Menu", jewels_menu_cb,
"Resume Game",
"Start New Game",
"Mode",
"Help",
"High Scores",
"Playback Control",
"Quit without Saving",
"Quit");
while (1) {
switch (rb->do_menu(&main_menu, &choice, NULL, false)) {
case 0:
jewels_setcolors();
if(resume_file)
rb->remove(SAVE_FILE);
return 0;
case 1:
jewels_init(bj);
return 0;
case 2:
rb->set_option("Mode", &bj->tmp_type, INT, mode, 2, NULL);
break;
case 3:
if(jewels_help())
return 1;
break;
case 4:
highscore_show(-1, highscores, NUM_SCORES, true);
break;
case 5:
playback_control(NULL);
break;
case 6:
return 1;
case 7:
if (ingame) {
rb->splash(HZ*1, "Saving game ...");
jewels_savegame(bj);
}
return 1;
case MENU_ATTACHED_USB:
return 1;
default:
break;
}
}
}
static int jewels_main(struct game_context* bj) {
int button;
int position;
bool selected = false;
int x=0, y=0;
bool loaded = jewels_loadgame(bj);
resume_file = loaded;
if (jewels_game_menu(bj, loaded)!=0)
return 0;
resume_file = false;
while(true) {
/* refresh the board */
jewels_drawboard(bj);
/* display the cursor */
if(selected) {
rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
rb->lcd_fillrect(x*TILE_WIDTH, y*TILE_HEIGHT+YOFS,
TILE_WIDTH, TILE_HEIGHT);
rb->lcd_set_drawmode(DRMODE_SOLID);
} else {
rb->lcd_drawrect(x*TILE_WIDTH, y*TILE_HEIGHT+YOFS,
TILE_WIDTH, TILE_HEIGHT);
}
rb->lcd_update_rect(x*TILE_WIDTH, y*TILE_HEIGHT+YOFS,
TILE_WIDTH, TILE_HEIGHT);
/* handle game button presses */
rb->yield();
button = rb->button_get(true);
switch(button){
case JEWELS_LEFT: /* move cursor left */
case (JEWELS_LEFT|BUTTON_REPEAT):
if(selected) {
bj->score += jewels_swapjewels(bj, x, y, SWAP_LEFT);
selected = false;
} else {
x = (x+BJ_WIDTH-1)%BJ_WIDTH;
}
break;
case JEWELS_RIGHT: /* move cursor right */
case (JEWELS_RIGHT|BUTTON_REPEAT):
if(selected) {
bj->score += jewels_swapjewels(bj, x, y, SWAP_RIGHT);
selected = false;
} else {
x = (x+1)%BJ_WIDTH;
}
break;
case JEWELS_DOWN: /* move cursor down */
case (JEWELS_DOWN|BUTTON_REPEAT):
if(selected) {
bj->score += jewels_swapjewels(bj, x, y, SWAP_DOWN);
selected = false;
} else {
y = (y+1)%(BJ_HEIGHT-1);
}
break;
case JEWELS_UP: /* move cursor up */
case (JEWELS_UP|BUTTON_REPEAT):
if(selected) {
bj->score += jewels_swapjewels(bj, x, y, SWAP_UP);
selected = false;
} else {
y = (y+(BJ_HEIGHT-1)-1)%(BJ_HEIGHT-1);
}
break;
#ifdef JEWELS_SCROLLWHEEL
case JEWELS_PREV: /* scroll backwards */
case (JEWELS_PREV|BUTTON_REPEAT):
if(!selected) {
if(x == 0) {
y = (y+(BJ_HEIGHT-1)-1)%(BJ_HEIGHT-1);
}
x = (x+BJ_WIDTH-1)%BJ_WIDTH;
}
break;
case JEWELS_NEXT: /* scroll forwards */
case (JEWELS_NEXT|BUTTON_REPEAT):
if(!selected) {
if(x == BJ_WIDTH-1) {
y = (y+1)%(BJ_HEIGHT-1);
}
x = (x+1)%BJ_WIDTH;
}
break;
#endif
case JEWELS_SELECT: /* toggle selected */
selected = !selected;
break;
#ifdef JEWELS_RC_CANCEL
case JEWELS_RC_CANCEL:
#endif
case JEWELS_CANCEL: /* end game */
if (jewels_game_menu(bj, true)!=0)
return 0;
break;
default:
if (rb->default_event_handler (button) == SYS_USB_CONNECTED)
return PLUGIN_USB_CONNECTED;
break;
}
switch(bj->type) {
case GAME_TYPE_NORMAL:
if(bj->score >= LEVEL_PTS)
jewels_nextlevel(bj);
break;
case GAME_TYPE_PUZZLE:
if (jewels_puzzle_is_finished(bj)) {
if (bj->level < NUM_PUZZLE_LEVELS) {
jewels_nextlevel(bj);
} else {
rb->splash(2*HZ, "Congratulations!");
rb->splash(2*HZ, "You have finished the game!");
if (jewels_game_menu(bj, false)!=0) {
return 0;
}
}
break;
}
}
if (!jewels_movesavail(bj)) {
switch(bj->type) {
case GAME_TYPE_NORMAL:
rb->splash(HZ*2, "Game Over!");
rb->lcd_clear_display();
bj->score += (bj->level-1)*LEVEL_PTS;
position=highscore_update(bj->score, bj->level, "",
highscores, NUM_SCORES);
if (position != -1)
{
if (position == 0)
rb->splash(HZ*2, "New High Score");
highscore_show(position, highscores, NUM_SCORES, true);
}
break;
case GAME_TYPE_PUZZLE:
rb->splash(2*HZ, "Game Over");
break;
}
if (jewels_game_menu(bj, false)!=0) {
return 0;
}
}
}
}
/* this is the plugin entry point */
enum plugin_status plugin_start(const void* parameter)
{
(void)parameter;
/* load high scores */
highscore_load(SCORE_FILE, highscores, NUM_SCORES);
rb->lcd_setfont(FONT_SYSFIXED);
#if LCD_DEPTH > 1
rb->lcd_set_backdrop(NULL);
#endif
struct game_context bj;
bj.tmp_type = GAME_TYPE_NORMAL;
jewels_main(&bj);
highscore_save(SCORE_FILE, highscores, NUM_SCORES);
rb->lcd_setfont(FONT_UI);
return PLUGIN_OK;
}