rockbox/apps/plugins/clix.c
Marcin Bukat 180cef835b xDuoo X3II and X20 port
Provided by Roman Stolyarov
Integration, Refactoring, and Upstreaming by Solomon Peachy

X3II confirmed working by forum tester, X20 is nearly identical.

This includes bootloader, main firmware, and the flash image patcher.

Eventual Todo:

 * Further refactor AGPTek Rocker & xduoo hiby bootloaders
 * Further refactor AGPTek Rocker & xduoo hosted platform code

Change-Id: I34a674051d368efcc75d1d18c725971fe46c3eee
2020-04-06 18:15:41 +02:00

1015 lines
31 KiB
C

/***************************************************************************
* __________ __ ___.
* 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_YH92X_PAD)
#define CLIX_BUTTON_QUIT BUTTON_REW
#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
#elif (CONFIG_KEYPAD == DX50_PAD)
#define CLIX_BUTTON_QUIT BUTTON_POWER
#elif CONFIG_KEYPAD == CREATIVE_ZENXFI2_PAD
#define CLIX_BUTTON_QUIT BUTTON_POWER
#define CLIX_BUTTON_UP BUTTON_TOPMIDDLE
#define CLIX_BUTTON_DOWN BUTTON_BOTTOMMIDDLE
#define CLIX_BUTTON_SCROLL_FWD BUTTON_TOPRIGHT
#define CLIX_BUTTON_SCROLL_BACK BUTTON_TOPLEFT
#define CLIX_BUTTON_LEFT BUTTON_MIDLEFT
#define CLIX_BUTTON_RIGHT BUTTON_MIDRIGHT
#define CLIX_BUTTON_CLICK BUTTON_CENTER
#elif (CONFIG_KEYPAD == AGPTEK_ROCKER_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_VOLUP)
#define CLIX_BUTTON_SCROLL_BACK (BUTTON_VOLDOWN)
#define CLIX_BUTTON_LEFT BUTTON_LEFT
#define CLIX_BUTTON_RIGHT BUTTON_RIGHT
#define CLIX_BUTTON_CLICK BUTTON_SELECT
#elif CONFIG_KEYPAD == XDUOO_X3_PAD
#define CLIX_BUTTON_QUIT BUTTON_POWER
#define CLIX_BUTTON_UP BUTTON_HOME
#define CLIX_BUTTON_DOWN BUTTON_OPTION
#define CLIX_BUTTON_LEFT BUTTON_PREV
#define CLIX_BUTTON_RIGHT BUTTON_NEXT
#define CLIX_BUTTON_CLICK BUTTON_PLAY
#elif CONFIG_KEYPAD == XDUOO_X3II_PAD
#define CLIX_BUTTON_QUIT BUTTON_POWER
#define CLIX_BUTTON_UP BUTTON_HOME
#define CLIX_BUTTON_DOWN BUTTON_OPTION
#define CLIX_BUTTON_LEFT BUTTON_PREV
#define CLIX_BUTTON_RIGHT BUTTON_NEXT
#define CLIX_BUTTON_CLICK BUTTON_PLAY
#elif CONFIG_KEYPAD == XDUOO_X20_PAD
#define CLIX_BUTTON_QUIT BUTTON_POWER
#define CLIX_BUTTON_UP BUTTON_HOME
#define CLIX_BUTTON_DOWN BUTTON_OPTION
#define CLIX_BUTTON_LEFT BUTTON_PREV
#define CLIX_BUTTON_RIGHT BUTTON_NEXT
#define CLIX_BUTTON_CLICK BUTTON_PLAY
#elif CONFIG_KEYPAD == IHIFI_770_PAD
#define CLIX_BUTTON_QUIT BUTTON_POWER
#define CLIX_BUTTON_UP BUTTON_PREV
#define CLIX_BUTTON_DOWN BUTTON_NEXT
#define CLIX_BUTTON_LEFT BUTTON_HOME
#define CLIX_BUTTON_RIGHT BUTTON_VOL_DOWN
#define CLIX_BUTTON_CLICK BUTTON_VOL_UP
#elif CONFIG_KEYPAD == IHIFI_800_PAD
#define CLIX_BUTTON_QUIT BUTTON_POWER
#define CLIX_BUTTON_UP BUTTON_PREV
#define CLIX_BUTTON_DOWN BUTTON_NEXT
#define CLIX_BUTTON_LEFT BUTTON_HOME
#define CLIX_BUTTON_RIGHT BUTTON_VOL_DOWN
#define CLIX_BUTTON_CLICK BUTTON_VOL_UP
#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;
}