rockbox/apps/plugins/clix.c

956 lines
29 KiB
C
Raw Normal View History

/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2008-2009 Rene Peinthor
* Contribution from Johannes Schwarz (new menu system, use of highscore lib)
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "plugin.h"
#include "lib/highscore.h"
#include "lib/playback_control.h"
#include "lib/display_text.h"
#if (CONFIG_KEYPAD == SANSA_E200_PAD) || \
(CONFIG_KEYPAD == SANSA_CONNECT_PAD)
#define CLIX_BUTTON_QUIT BUTTON_POWER
#define CLIX_BUTTON_UP BUTTON_UP
#define CLIX_BUTTON_DOWN BUTTON_DOWN
#define CLIX_BUTTON_SCROLL_FWD BUTTON_SCROLL_FWD
#define CLIX_BUTTON_SCROLL_BACK BUTTON_SCROLL_BACK
#define CLIX_BUTTON_LEFT BUTTON_LEFT
#define CLIX_BUTTON_RIGHT BUTTON_RIGHT
#define CLIX_BUTTON_CLICK BUTTON_SELECT
#elif (CONFIG_KEYPAD == SANSA_CLIP_PAD)
#define CLIX_BUTTON_QUIT BUTTON_POWER
#define CLIX_BUTTON_UP BUTTON_UP
#define CLIX_BUTTON_DOWN BUTTON_DOWN
#define CLIX_BUTTON_LEFT BUTTON_LEFT
#define CLIX_BUTTON_RIGHT BUTTON_RIGHT
#define CLIX_BUTTON_CLICK BUTTON_SELECT
#elif (CONFIG_KEYPAD == SANSA_FUZE_PAD)
#define CLIX_BUTTON_QUIT BUTTON_HOME
#define CLIX_BUTTON_UP BUTTON_UP
#define CLIX_BUTTON_DOWN BUTTON_DOWN
#define CLIX_BUTTON_SCROLL_FWD BUTTON_SCROLL_FWD
#define CLIX_BUTTON_SCROLL_BACK BUTTON_SCROLL_BACK
#define CLIX_BUTTON_LEFT BUTTON_LEFT
#define CLIX_BUTTON_RIGHT BUTTON_RIGHT
#define CLIX_BUTTON_CLICK BUTTON_SELECT
#elif (CONFIG_KEYPAD == SANSA_C200_PAD)
#define CLIX_BUTTON_QUIT BUTTON_POWER
#define CLIX_BUTTON_UP BUTTON_UP
#define CLIX_BUTTON_DOWN BUTTON_DOWN
#define CLIX_BUTTON_SCROLL_FWD BUTTON_VOL_UP
#define CLIX_BUTTON_SCROLL_BACK BUTTON_VOL_DOWN
#define CLIX_BUTTON_LEFT BUTTON_LEFT
#define CLIX_BUTTON_RIGHT BUTTON_RIGHT
#define CLIX_BUTTON_CLICK BUTTON_SELECT
#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
(CONFIG_KEYPAD == IPOD_3G_PAD) || \
(CONFIG_KEYPAD == IPOD_1G2G_PAD)
#define CLIX_BUTTON_QUIT (BUTTON_SELECT | BUTTON_MENU)
#define CLIX_BUTTON_UP BUTTON_MENU
#define CLIX_BUTTON_DOWN BUTTON_PLAY
#define CLIX_BUTTON_SCROLL_FWD BUTTON_SCROLL_FWD
#define CLIX_BUTTON_SCROLL_BACK BUTTON_SCROLL_BACK
#define CLIX_BUTTON_CLICK BUTTON_SELECT
#define CLIX_BUTTON_RIGHT BUTTON_RIGHT
#define CLIX_BUTTON_LEFT BUTTON_LEFT
#elif (CONFIG_KEYPAD == GIGABEAT_PAD)
#define CLIX_BUTTON_QUIT BUTTON_POWER
#define CLIX_BUTTON_LEFT BUTTON_LEFT
#define CLIX_BUTTON_RIGHT BUTTON_RIGHT
#define CLIX_BUTTON_CLICK BUTTON_SELECT
#define CLIX_BUTTON_UP BUTTON_UP
#define CLIX_BUTTON_DOWN BUTTON_DOWN
#elif (CONFIG_KEYPAD == GIGABEAT_S_PAD) || \
(CONFIG_KEYPAD == SAMSUNG_YPR0_PAD)
#define CLIX_BUTTON_QUIT BUTTON_BACK
#define CLIX_BUTTON_LEFT BUTTON_LEFT
#define CLIX_BUTTON_RIGHT BUTTON_RIGHT
#define CLIX_BUTTON_CLICK BUTTON_SELECT
#define CLIX_BUTTON_UP BUTTON_UP
#define CLIX_BUTTON_DOWN BUTTON_DOWN
#elif (CONFIG_KEYPAD == IRIVER_H10_PAD)
#define CLIX_BUTTON_QUIT BUTTON_POWER
#define CLIX_BUTTON_LEFT BUTTON_LEFT
#define CLIX_BUTTON_RIGHT BUTTON_RIGHT
#define CLIX_BUTTON_CLICK BUTTON_PLAY
#define CLIX_BUTTON_UP BUTTON_SCROLL_UP
#define CLIX_BUTTON_DOWN BUTTON_SCROLL_DOWN
#elif CONFIG_KEYPAD == IAUDIO67_PAD
#define CLIX_BUTTON_QUIT BUTTON_POWER
#define CLIX_BUTTON_LEFT BUTTON_LEFT
#define CLIX_BUTTON_RIGHT BUTTON_RIGHT
#define CLIX_BUTTON_CLICK BUTTON_PLAY
#define CLIX_BUTTON_UP BUTTON_STOP
#define CLIX_BUTTON_DOWN BUTTON_PLAY
#elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
#define CLIX_BUTTON_QUIT BUTTON_POWER
#define CLIX_BUTTON_LEFT BUTTON_LEFT
#define CLIX_BUTTON_RIGHT BUTTON_RIGHT
#define CLIX_BUTTON_CLICK BUTTON_SELECT
#define CLIX_BUTTON_UP BUTTON_UP
#define CLIX_BUTTON_DOWN BUTTON_DOWN
#elif (CONFIG_KEYPAD == IRIVER_H300_PAD)
#define CLIX_BUTTON_QUIT BUTTON_OFF
#define CLIX_BUTTON_LEFT BUTTON_LEFT
#define CLIX_BUTTON_RIGHT BUTTON_RIGHT
#define CLIX_BUTTON_CLICK BUTTON_SELECT
#define CLIX_BUTTON_UP BUTTON_UP
#define CLIX_BUTTON_DOWN BUTTON_DOWN
#elif CONFIG_KEYPAD == CREATIVEZVM_PAD
#define CLIX_BUTTON_QUIT BUTTON_BACK
#define CLIX_BUTTON_LEFT BUTTON_LEFT
#define CLIX_BUTTON_RIGHT BUTTON_RIGHT
#define CLIX_BUTTON_CLICK BUTTON_SELECT
#define CLIX_BUTTON_UP BUTTON_UP
#define CLIX_BUTTON_DOWN BUTTON_DOWN
#elif CONFIG_KEYPAD == CREATIVE_ZENXFI3_PAD
#define CLIX_BUTTON_QUIT BUTTON_POWER
#define CLIX_BUTTON_LEFT BUTTON_BACK
#define CLIX_BUTTON_RIGHT BUTTON_MENU
#define CLIX_BUTTON_CLICK BUTTON_PLAY
#define CLIX_BUTTON_UP BUTTON_UP
#define CLIX_BUTTON_DOWN BUTTON_DOWN
#elif (CONFIG_KEYPAD == PHILIPS_HDD1630_PAD)
#define CLIX_BUTTON_QUIT BUTTON_POWER
#define CLIX_BUTTON_LEFT BUTTON_LEFT
#define CLIX_BUTTON_RIGHT BUTTON_RIGHT
#define CLIX_BUTTON_CLICK BUTTON_SELECT
#define CLIX_BUTTON_UP BUTTON_UP
#define CLIX_BUTTON_DOWN BUTTON_DOWN
#elif (CONFIG_KEYPAD == PHILIPS_HDD6330_PAD)
#define CLIX_BUTTON_QUIT BUTTON_POWER
#define CLIX_BUTTON_LEFT BUTTON_PREV
#define CLIX_BUTTON_RIGHT BUTTON_NEXT
#define CLIX_BUTTON_CLICK BUTTON_PLAY
#define CLIX_BUTTON_UP BUTTON_UP
#define CLIX_BUTTON_DOWN BUTTON_DOWN
#elif (CONFIG_KEYPAD == PHILIPS_SA9200_PAD)
#define CLIX_BUTTON_QUIT BUTTON_POWER
#define CLIX_BUTTON_LEFT BUTTON_PREV
#define CLIX_BUTTON_RIGHT BUTTON_NEXT
#define CLIX_BUTTON_CLICK BUTTON_PLAY
#define CLIX_BUTTON_UP BUTTON_UP
#define CLIX_BUTTON_DOWN BUTTON_DOWN
#elif CONFIG_KEYPAD == COWON_D2_PAD
#define CLIX_BUTTON_QUIT BUTTON_POWER
#elif (CONFIG_KEYPAD == ONDAVX747_PAD)
#define CLIX_BUTTON_QUIT BUTTON_POWER
#define CLIX_BUTTON_CLICK BUTTON_MENU
#elif (CONFIG_KEYPAD == ONDAVX777_PAD)
#define CLIX_BUTTON_QUIT BUTTON_POWER
#elif (CONFIG_KEYPAD == ANDROID_PAD) \
|| (CONFIG_KEYPAD == SDL_PAD)
#define CLIX_BUTTON_QUIT BUTTON_BACK
#elif (CONFIG_KEYPAD == MROBE500_PAD)
#define CLIX_BUTTON_QUIT BUTTON_POWER
#elif (CONFIG_KEYPAD == SAMSUNG_YH820_PAD) || \
(CONFIG_KEYPAD == SAMSUNG_YH920_PAD)
#define CLIX_BUTTON_QUIT BUTTON_REC
#define CLIX_BUTTON_LEFT BUTTON_LEFT
#define CLIX_BUTTON_RIGHT BUTTON_RIGHT
#define CLIX_BUTTON_CLICK BUTTON_PLAY
#define CLIX_BUTTON_UP BUTTON_UP
#define CLIX_BUTTON_DOWN BUTTON_DOWN
#elif (CONFIG_KEYPAD == PBELL_VIBE500_PAD)
#define CLIX_BUTTON_QUIT BUTTON_REC
#define CLIX_BUTTON_UP BUTTON_UP
#define CLIX_BUTTON_DOWN BUTTON_DOWN
#define CLIX_BUTTON_SCROLL_FWD BUTTON_PLAY
#define CLIX_BUTTON_SCROLL_BACK BUTTON_MENU
#define CLIX_BUTTON_LEFT BUTTON_PREV
#define CLIX_BUTTON_RIGHT BUTTON_NEXT
#define CLIX_BUTTON_CLICK BUTTON_OK
#elif (CONFIG_KEYPAD == SANSA_FUZEPLUS_PAD)
#define CLIX_BUTTON_QUIT BUTTON_POWER
#define CLIX_BUTTON_UP BUTTON_UP
#define CLIX_BUTTON_DOWN BUTTON_DOWN
#define CLIX_BUTTON_LEFT BUTTON_LEFT
#define CLIX_BUTTON_RIGHT BUTTON_RIGHT
#define CLIX_BUTTON_SCROLL_FWD BUTTON_BOTTOMRIGHT
#define CLIX_BUTTON_SCROLL_BACK BUTTON_BOTTOMLEFT
#define CLIX_BUTTON_CLICK BUTTON_SELECT
#elif (CONFIG_KEYPAD == HM60X_PAD)
#define CLIX_BUTTON_QUIT BUTTON_POWER
#define CLIX_BUTTON_UP BUTTON_UP
#define CLIX_BUTTON_DOWN BUTTON_DOWN
#define CLIX_BUTTON_SCROLL_FWD (BUTTON_POWER | BUTTON_UP)
#define CLIX_BUTTON_SCROLL_BACK (BUTTON_POWER | BUTTON_DOWN)
#define CLIX_BUTTON_LEFT BUTTON_LEFT
#define CLIX_BUTTON_RIGHT BUTTON_RIGHT
#define CLIX_BUTTON_CLICK BUTTON_SELECT
#elif (CONFIG_KEYPAD == HM801_PAD)
#define CLIX_BUTTON_QUIT BUTTON_POWER
#define CLIX_BUTTON_UP BUTTON_UP
#define CLIX_BUTTON_DOWN BUTTON_DOWN
#define CLIX_BUTTON_SCROLL_FWD BUTTON_NEXT
#define CLIX_BUTTON_SCROLL_BACK BUTTON_PREV
#define CLIX_BUTTON_LEFT BUTTON_LEFT
#define CLIX_BUTTON_RIGHT BUTTON_RIGHT
#define CLIX_BUTTON_CLICK BUTTON_SELECT
#elif CONFIG_KEYPAD == SONY_NWZ_PAD
#define CLIX_BUTTON_QUIT BUTTON_BACK
#define CLIX_BUTTON_UP BUTTON_UP
#define CLIX_BUTTON_DOWN BUTTON_DOWN
#define CLIX_BUTTON_SCROLL_FWD (BUTTON_POWER|BUTTON_RIGHT)
#define CLIX_BUTTON_SCROLL_BACK (BUTTON_POWER|BUTTON_LEFT)
#define CLIX_BUTTON_LEFT BUTTON_LEFT
#define CLIX_BUTTON_RIGHT BUTTON_RIGHT
#define CLIX_BUTTON_CLICK BUTTON_PLAY
#elif CONFIG_KEYPAD == CREATIVE_ZEN_PAD
#define CLIX_BUTTON_QUIT BUTTON_BACK
#define CLIX_BUTTON_UP BUTTON_UP
#define CLIX_BUTTON_DOWN BUTTON_DOWN
#define CLIX_BUTTON_SCROLL_FWD BUTTON_PLAYPAUSE
#define CLIX_BUTTON_SCROLL_BACK BUTTON_MENU
#define CLIX_BUTTON_LEFT BUTTON_LEFT
#define CLIX_BUTTON_RIGHT BUTTON_RIGHT
#define CLIX_BUTTON_CLICK BUTTON_SELECT
#elif (CONFIG_KEYPAD == SAMSUNG_YPR1_PAD)
#define CLIX_BUTTON_QUIT BUTTON_POWER
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-08-30 11:15:53 +00:00
#elif (CONFIG_KEYPAD == DX50_PAD)
#define CLIX_BUTTON_QUIT BUTTON_POWER
#else
#error "no keymap"
#endif
#ifndef CLIX_BUTTON_CLICK
#define CLIX_BUTTON_CLICK BUTTON_CENTER
#endif
#define SCORE_FILE PLUGIN_GAMES_DATA_DIR "/clix.score"
#define NUM_SCORES 5
struct highscore highscores[NUM_SCORES];
#define NUM_LEVELS 9
#define BLINK_TICKCOUNT 25
#define MARGIN 5
#if (LCD_WIDTH >= LCD_HEIGHT)
#define BOARD_WIDTH 18
#define BOARD_HEIGHT 12
#else
#define BOARD_WIDTH 12
#define BOARD_HEIGHT 18
#endif
#if (LCD_WIDTH>=480)
#if (LCD_WIDTH/BOARD_WIDTH) > (LCD_HEIGHT/BOARD_HEIGHT)
#define CELL_SIZE (LCD_HEIGHT/BOARD_HEIGHT)
#else
#define CELL_SIZE (LCD_WIDTH/BOARD_WIDTH)
#endif
#elif (LCD_WIDTH >= 312 && LCD_HEIGHT >= 468)
#define CELL_SIZE 24
#elif (LCD_WIDTH >= 306 && LCD_HEIGHT>= 204)
#define CELL_SIZE 16
#elif (LCD_WIDTH >= 270 && LCD_HEIGHT>= 180)
#define CELL_SIZE 14
#elif (LCD_WIDTH >= 234 && LCD_HEIGHT>= 156)
#define CELL_SIZE 12
#elif (LCD_WIDTH >= 198 && LCD_HEIGHT>= 132)
#define CELL_SIZE 10
#elif (LCD_WIDTH >= 162 && LCD_HEIGHT>= 108)
#define CELL_SIZE 8
#elif (LCD_WIDTH >= 126 && LCD_HEIGHT>= 84)
#define CELL_SIZE 6
#elif (LCD_WIDTH >= 60)
#define CELL_SIZE 4
#endif
#define XYPOS(x,y) ((y) * BOARD_WIDTH + x)
#define XOFS LCD_WIDTH/2-(BOARD_WIDTH * (CELL_SIZE + 1)/2)
#define YOFS (LCD_HEIGHT+10)/2-(BOARD_HEIGHT * (CELL_SIZE + 1)/2)
struct clix_game_state_t {
int level; /* current level */
int score; /* current game score */
int x,y; /* current positions of the cursor */
int board[BOARD_WIDTH * BOARD_HEIGHT]; /* play board*/
/* state of selected fields,maybe we can store this in the play board too */
bool board_selected[ BOARD_WIDTH * BOARD_HEIGHT];
int selected_count;
int status;
bool blink; /* true if selected CELLS are currently white */
};
/* game state enum */
enum {
CLIX_GAMEOVER = -1,
CLIX_CONTINUE,
CLIX_CLEARED
};
/* cell color enum */
enum {
CC_BLACK = -1,
CC_BLUE,
CC_GREEN,
CC_RED,
CC_YELLOW,
CC_ORANGE,
CC_CYAN,
CC_BROWN,
CC_PINK,
CC_DARK_BLUE,
CC_DARK_GREEN
};
/* recursive function to check if a neighbour cell is of the same color
if so call the function with the neighbours position
*/
static void clix_set_selected(struct clix_game_state_t* state,
const int x, const int y)
{
state->selected_count++;
state->board_selected[ XYPOS( x, y)] = true;
int current_color = state->board[ XYPOS( x, y)];
if( (x - 1) >= 0 &&
state->board[ XYPOS( x - 1, y)] == current_color &&
state->board_selected[ XYPOS(x - 1, y)] == false)
clix_set_selected( state, x - 1, y);
if( (y + 1) < BOARD_HEIGHT &&
state->board[ XYPOS( x, y + 1)] == current_color &&
state->board_selected[ XYPOS(x, y + 1)] == false)
clix_set_selected( state, x, y + 1);
if( (x + 1) < BOARD_WIDTH &&
state->board[ XYPOS( x + 1, y)] == current_color &&
state->board_selected[ XYPOS(x + 1, y)] == false)
clix_set_selected( state, x + 1, y);
if( (y - 1) >= 0 &&
state->board[ XYPOS( x, y - 1)] == current_color &&
state->board_selected[ XYPOS(x, y - 1)] == false)
clix_set_selected( state, x, y - 1);
}
/* updates "blinking" cells by finding out which one is a valid neighbours */
static void clix_update_selected(struct clix_game_state_t* state)
{
int i;
for( i = 0; i < BOARD_WIDTH * BOARD_HEIGHT; ++i)
{
state->board_selected[i] = false;
}
state->selected_count = 0;
/* recursion starts here */
clix_set_selected( state, state->x, state->y);
}
/* inits the board with new random colors according to the level */
static void clix_init_new_level(struct clix_game_state_t* state)
{
int i;
int r;
state->y = BOARD_HEIGHT / 2;
state->x = BOARD_WIDTH / 2;
rb->srand( *rb->current_tick);
/* create a random colored board, according to the current level */
for(i = 0; i < BOARD_HEIGHT * BOARD_WIDTH; ++i)
{
r = rb->rand() % (state->level + 1);
state->board[i] = r;
}
}
/* this inits the game state structure */
static void clix_init(struct clix_game_state_t* state)
{
state->level = 1;
state->score = 0;
state->blink = false;
state->status = CLIX_CONTINUE;
clix_init_new_level(state);
clix_update_selected(state);
}
/* Function for drawing a cell */
static void clix_draw_cell(struct clix_game_state_t* state, const int x, const int y)
{
int realx = XOFS;
int realy = YOFS;
realx += x * (CELL_SIZE + 1);
realy += y * (CELL_SIZE + 1);
if (state->blink && state->board_selected[ XYPOS( x, y)]) {
rb->lcd_set_foreground(LCD_WHITE);
} else {
switch (state->board[ XYPOS( x, y)])
{
case CC_BLUE:
rb->lcd_set_foreground( LCD_RGBPACK( 25, 25, 255));
break;
case CC_GREEN:
rb->lcd_set_foreground( LCD_RGBPACK( 25, 255, 25));
break;
case CC_RED:
rb->lcd_set_foreground( LCD_RGBPACK( 255, 25, 25));
break;
case CC_YELLOW:
rb->lcd_set_foreground( LCD_RGBPACK( 225, 225, 25));
break;
case CC_ORANGE:
rb->lcd_set_foreground( LCD_RGBPACK( 230, 140, 15));
break;
case CC_CYAN:
rb->lcd_set_foreground( LCD_RGBPACK( 25, 245, 230));
break;
case CC_BROWN:
rb->lcd_set_foreground( LCD_RGBPACK(139, 69, 19));
break;
case CC_PINK:
rb->lcd_set_foreground( LCD_RGBPACK(255, 105, 180));
break;
case CC_DARK_GREEN:
rb->lcd_set_foreground( LCD_RGBPACK( 0, 100, 0));
break;
case CC_DARK_BLUE:
rb->lcd_set_foreground( LCD_RGBPACK( 280, 32, 144));
break;
default:
rb->lcd_set_foreground( LCD_BLACK);
break;
}
}
rb->lcd_fillrect( realx, realy, CELL_SIZE, CELL_SIZE);
/* draw cursor */
if ( x == state->x && y == state->y) {
rb->lcd_set_foreground( LCD_WHITE);
rb->lcd_drawrect( realx - 1, realy - 1, CELL_SIZE + 2, CELL_SIZE + 2);
}
}
/* main function of drawing the whole board and score... */
static void clix_draw(struct clix_game_state_t* state)
{
int i,j;
/* Clear screen */
rb->lcd_clear_display();
rb->lcd_set_foreground( LCD_WHITE);
rb->lcd_putsxy( MARGIN, MARGIN, "Score:");
rb->lcd_putsxyf( 43, MARGIN, "%d", state->score);
#if LCD_WIDTH <= 100
rb->lcd_putsxy( 75, MARGIN, "L:");
rb->lcd_putsxyf( 90, MARGIN, "%d", state->level);
#else
rb->lcd_putsxy( 75, MARGIN, "Level:");
rb->lcd_putsxyf( 113, MARGIN, "%d", state->level);
#endif
for( i = 0; i < BOARD_WIDTH; ++i)
{
for( j = 0; j < BOARD_HEIGHT; ++j)
{
clix_draw_cell( state, i, j);
}
}
rb->lcd_update();
rb->lcd_set_foreground(LCD_WHITE);
}
static void clix_move_cursor(struct clix_game_state_t* state, const bool left)
{
int x, y;
x = state->x;
do
{
y = state->y;
while(state->board[ XYPOS( x, y)] == CC_BLACK && y < BOARD_HEIGHT) y++;
if (y < BOARD_HEIGHT) {
state->y = y;
state->x = x;
}
else
{
if (left) {
if( x >= 0)
x--;
else
y = state->y;
}
else
{
if( x < BOARD_WIDTH - 1)
x++;
else
x = 0;
}
}
} while ( y != state->y);
}
/* returns the color of the given position, if out of bounds return CC_BLACK */
static int clix_get_color(struct clix_game_state_t* state, const int x, const int y)
{
if( x >= 0 && x < BOARD_WIDTH && y >= 0 && y < BOARD_HEIGHT)
return state->board[XYPOS( x, y)];
else
return CC_BLACK;
}
static int clix_clear_selected(struct clix_game_state_t* state)
{
int i, j, x, y;
bool found_move;
state->status = CLIX_CLEARED;
/* clear the selected blocks */
for( i = 0; i < BOARD_WIDTH; ++i)
{
for( j = 0; j < BOARD_HEIGHT; ++j)
{
if( state->board_selected[ XYPOS( i, j)] )
{
state->board_selected[ XYPOS( i, j)] = false;
state->board[ XYPOS( i, j)] = CC_BLACK;
}
}
}
/* count score */
state->score += state->selected_count * state->level;
state->selected_count = 0;
/* let blocks falling down */
for( i = BOARD_WIDTH - 1; i >= 0; --i)
{
for( j = BOARD_HEIGHT - 1; j >= 0; --j)
{
y = j;
while( (y + 1) < BOARD_HEIGHT &&
state->board[ XYPOS( i, y + 1)] == CC_BLACK
)
y++;
if (y != j) {
state->board[ XYPOS( i, y)] = state->board[ XYPOS( i, j)];
state->board[ XYPOS( i, j)] = CC_BLACK;
}
}
}
/* move columns to left side */
for( i = 0; i < BOARD_WIDTH; ++i)
{
x = i;
while( (x - 1) >= 0 &&
state->board[ XYPOS( x - 1, BOARD_HEIGHT - 1)] == CC_BLACK
)
x--;
if (x != i)
{
for( j = 0; j < BOARD_HEIGHT; ++j)
{
state->board[ XYPOS( x, j)] = state->board[ XYPOS( i, j)];
state->board[ XYPOS( i, j)] = CC_BLACK;
}
}
}
if(state->board[ XYPOS( 0, BOARD_HEIGHT - 1)] != CC_BLACK)
state->status = CLIX_CONTINUE;
if (state->status != CLIX_CLEARED) {
/* check if a move is still possible, otherwise the game is over.
tart from the left bottom, because there are the last fields
at the end of the game.
*/
found_move = false;
for( i = 0; !found_move && i < BOARD_WIDTH; ++i)
{
for( j = BOARD_HEIGHT - 1; !found_move && j >= 0; --j)
{
int color = state->board[ XYPOS( i, j)];
if (color != CC_BLACK) {
if (color == clix_get_color( state, i - 1, j) ||
color == clix_get_color( state, i + 1, j) ||
color == clix_get_color( state, i, j - 1) ||
color == clix_get_color( state, i, j + 1)
)
{
found_move = true;
}
}
}
}
/* if the loops ended without a possible move, the game is over */
if( !found_move)
state->status = CLIX_GAMEOVER;
/* set cursor to the right position */
if (state->status == CLIX_CONTINUE) {
clix_move_cursor( state, true);
clix_update_selected( state);
}
}
return state->status;
}
static bool clix_help(void)
{
static char *help_text[] = {
"Clix", "", "Aim", "",
"Remove", "all", "blocks", "from", "the", "board", "to", "achieve",
"the", "next", "level.", "You", "can", "only", "remove", "blocks,",
"if", "at", "least", "two", "blocks", "with", "the", "same", "color",
"have", "a", "direct", "connection.", "The", "more", "blocks", "you",
"remove", "per", "turn,", "the", "more", "points", "you", "get."
};
static struct style_text formation[]={
{ 0, TEXT_CENTER|TEXT_UNDERLINE },
{ 2, C_RED },
LAST_STYLE_ITEM
};
rb->lcd_setfont(FONT_UI);
rb->lcd_set_foreground(LCD_WHITE);
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 clix_menu_cb(int action, const struct menu_item_ex *this_item)
{
if(action == ACTION_REQUEST_MENUITEM
&& !_ingame && ((intptr_t)this_item)==0)
return ACTION_EXIT_MENUITEM;
return action;
}
static int clix_menu(struct clix_game_state_t* state, bool ingame)
{
rb->button_clear_queue();
int choice = 0;
bool leave_menu=false;
int ret=0;
_ingame = ingame;
MENUITEM_STRINGLIST (main_menu, "Clix Menu", clix_menu_cb,
"Resume Game",
"Start New Game",
"Help",
"High Scores",
"Playback Control",
"Quit");
while (!leave_menu) {
switch (rb->do_menu(&main_menu, &choice, NULL, false)) {
case 0:
leave_menu=true;
ret = 0;
break;
case 1:
clix_init(state);
leave_menu=true;
ret = 0;
break;
case 2:
if (clix_help()) {
leave_menu=true;
ret = 1;
}
break;
case 3:
highscore_show(-1, highscores, NUM_SCORES, true);
break;
case 4:
playback_control(NULL);
break;
case 5:
case MENU_ATTACHED_USB:
leave_menu=true;
ret = 1;
break;
default:
break;
}
}
return ret;
}
static int clix_click(struct clix_game_state_t* state)
{
int position;
if (state->selected_count <= 1) {
return 0;
}
switch( clix_clear_selected( state))
{
case CLIX_CLEARED:
state->score += state->level * 100;
clix_draw( state);
if (state->level < NUM_LEVELS) {
rb->splash(HZ*2, "Great! Next Level!");
state->level++;
clix_init_new_level( state);
clix_update_selected( state);
}
else {
rb->splash(HZ*2, "Congratulation!!!");
rb->splash(HZ*2, "You have finished the game.");
if(clix_menu(state, 0))
return 1;
}
break;
case CLIX_GAMEOVER:
clix_draw( state);
rb->splash(HZ*2, "Game Over!");
rb->lcd_clear_display();
position = highscore_update(state->score, state->level, "",
highscores, NUM_SCORES);
if (position != -1)
{
if (position == 0)
rb->splash(HZ*2, "New High Score");
highscore_show(position, highscores, NUM_SCORES, true);
}
if(clix_menu(state, 0))
return 1;
break;
default:
rb->sleep(10); /* prevent repeating clicks */
break;
}
return 0;
}
static int clix_handle_game(struct clix_game_state_t* state)
{
int button;
int blink_tick = *rb->current_tick + BLINK_TICKCOUNT;
int time;
int start;
int end;
int oldx, oldy;
if (clix_menu(state, 0))
return 1;
while(true)
{
if (TIME_AFTER(*rb->current_tick, blink_tick)) {
state->blink = !state->blink;
blink_tick = *rb->current_tick + BLINK_TICKCOUNT;
}
time = 6; /* number of ticks this function will loop reading keys */
start = *rb->current_tick;
end = start + time;
while(TIME_BEFORE(*rb->current_tick, end))
{
oldx = state->x;
oldy = state->y;
button = rb->button_get_w_tmo(end - *rb->current_tick);
#ifdef HAVE_TOUCHSCREEN
if(button & BUTTON_TOUCHSCREEN)
{
int x = rb->button_get_data() >> 16;
int y = rb->button_get_data() & 0xffff;
x -= XOFS;
y -= YOFS;
if(x >= 0 && y >= 0)
{
x /= CELL_SIZE + 1;
y /= CELL_SIZE + 1;
if(x < BOARD_WIDTH && y < BOARD_HEIGHT
&& state->board[XYPOS(x, y)] != CC_BLACK)
{
if(state->x == x && state->y == y && button & BUTTON_REL)
button = CLIX_BUTTON_CLICK;
else
{
state->x = x;
state->y = y;
}
}
}
}
#endif
switch( button)
{
#ifndef HAVE_TOUCHSCREEN
#ifdef CLIX_BUTTON_SCROLL_BACK
case CLIX_BUTTON_SCROLL_BACK:
case CLIX_BUTTON_SCROLL_BACK|BUTTON_REPEAT:
#endif
case CLIX_BUTTON_UP:
case CLIX_BUTTON_UP|BUTTON_REPEAT:
if( state->y == 0 ||
state->board[ XYPOS( state->x, state->y - 1)] ==
CC_BLACK
)
state->y = BOARD_HEIGHT - 1;
else
state->y--;
clix_move_cursor(state, true);
break;
#ifdef CLIX_BUTTON_SCROLL_FWD
case CLIX_BUTTON_SCROLL_FWD:
case CLIX_BUTTON_SCROLL_FWD|BUTTON_REPEAT:
#endif
case CLIX_BUTTON_DOWN:
case CLIX_BUTTON_DOWN|BUTTON_REPEAT:
if( state->y == (BOARD_HEIGHT - 1))
state->y = 0;
else
state->y++;
clix_move_cursor( state, true);
break;
case CLIX_BUTTON_RIGHT:
case CLIX_BUTTON_RIGHT|BUTTON_REPEAT:
if( state->x == (BOARD_WIDTH - 1))
state->x = 0;
else
state->x++;
clix_move_cursor(state, false);
break;
case CLIX_BUTTON_LEFT:
case CLIX_BUTTON_LEFT|BUTTON_REPEAT:
if( state->x == 0)
state->x = BOARD_WIDTH - 1;
else
state->x--;
clix_move_cursor(state, true);
break;
#endif
case CLIX_BUTTON_CLICK:
if (clix_click(state) == 1)
return 1;
break;
case CLIX_BUTTON_QUIT:
if (clix_menu(state, 1) != 0) {
rb->button_clear_queue();
return 1;
}
break;
default:
break;
}
if( (oldx != state->x || oldy != state->y) &&
state->board_selected[ XYPOS( oldx, oldy)] !=
state->board_selected[ XYPOS( state->x, state->y)]
)
{
clix_update_selected(state);
}
clix_draw(state);
rb->sleep(time);
}
}
}
/* this is the plugin entry point */
enum plugin_status plugin_start(const void* parameter)
{
(void)parameter;
rb->lcd_set_backdrop(NULL);
rb->lcd_set_foreground(LCD_WHITE);
rb->lcd_set_background(LCD_BLACK);
rb->lcd_setfont(FONT_SYSFIXED);
#ifdef HAVE_TOUCHSCREEN
rb->touchscreen_set_mode(TOUCHSCREEN_POINT);
#endif
highscore_load(SCORE_FILE, highscores, NUM_SCORES);
struct clix_game_state_t state;
clix_handle_game( &state);
highscore_save(SCORE_FILE, highscores, NUM_SCORES);
return PLUGIN_OK;
}