554d7ed902
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@16751 a1c6a512-1295-4272-9138-f99709370657
989 lines
28 KiB
C
989 lines
28 KiB
C
/***************************************************************************
|
|
* __________ __ ___.
|
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
|
* \/ \/ \/ \/ \/
|
|
* $Id$
|
|
*
|
|
* Copyright (C) 2006 Eli Sherer
|
|
* 2007 Antoine Cellerier
|
|
*
|
|
* 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 "helper.h"
|
|
|
|
PLUGIN_HEADER
|
|
|
|
#if (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD)
|
|
|
|
#define QUIT BUTTON_OFF
|
|
#define LEFT BUTTON_LEFT
|
|
#define RIGHT BUTTON_RIGHT
|
|
#define PAUSE BUTTON_MODE
|
|
#define UP BUTTON_UP
|
|
#define DOWN BUTTON_DOWN
|
|
|
|
#define RC_QUIT BUTTON_RC_STOP
|
|
|
|
#elif (CONFIG_KEYPAD == ARCHOS_AV300_PAD)
|
|
|
|
#define QUIT BUTTON_OFF
|
|
#define LEFT BUTTON_LEFT
|
|
#define RIGHT BUTTON_RIGHT
|
|
#define PAUSE BUTTON_ON
|
|
#define UP BUTTON_UP
|
|
#define DOWN BUTTON_DOWN
|
|
|
|
#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
|
|
(CONFIG_KEYPAD == IPOD_3G_PAD) || \
|
|
(CONFIG_KEYPAD == IPOD_1G2G_PAD)
|
|
|
|
#define QUIT (BUTTON_SELECT | BUTTON_MENU)
|
|
#define LEFT BUTTON_LEFT
|
|
#define RIGHT BUTTON_RIGHT
|
|
#define PAUSE BUTTON_SELECT
|
|
#define MENU_UP BUTTON_SCROLL_FWD
|
|
#define MENU_DOWN BUTTON_SCROLL_BACK
|
|
#define UP BUTTON_MENU
|
|
#define DOWN BUTTON_PLAY
|
|
|
|
#elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
|
|
|
|
#define QUIT BUTTON_POWER
|
|
#define LEFT BUTTON_LEFT
|
|
#define RIGHT BUTTON_RIGHT
|
|
#define UP BUTTON_UP
|
|
#define DOWN BUTTON_DOWN
|
|
#define PAUSE BUTTON_PLAY
|
|
|
|
#elif (CONFIG_KEYPAD == GIGABEAT_PAD)
|
|
|
|
#define QUIT BUTTON_POWER
|
|
#define LEFT BUTTON_LEFT
|
|
#define RIGHT BUTTON_RIGHT
|
|
#define UP BUTTON_UP
|
|
#define DOWN BUTTON_DOWN
|
|
#define PAUSE BUTTON_A
|
|
|
|
#elif (CONFIG_KEYPAD == SANSA_E200_PAD) || \
|
|
(CONFIG_KEYPAD == SANSA_C200_PAD)
|
|
|
|
#define QUIT BUTTON_POWER
|
|
#define LEFT BUTTON_LEFT
|
|
#define RIGHT BUTTON_RIGHT
|
|
#define UP BUTTON_UP
|
|
#define DOWN BUTTON_DOWN
|
|
#define PAUSE BUTTON_REC
|
|
|
|
|
|
#elif CONFIG_KEYPAD == IRIVER_H10_PAD
|
|
|
|
#define QUIT BUTTON_POWER
|
|
#define LEFT BUTTON_LEFT
|
|
#define RIGHT BUTTON_RIGHT
|
|
#define UP BUTTON_SCROLL_UP
|
|
#define DOWN BUTTON_SCROLL_DOWN
|
|
#define PAUSE BUTTON_PLAY
|
|
|
|
#elif CONFIG_KEYPAD == RECORDER_PAD
|
|
|
|
#define QUIT BUTTON_OFF
|
|
#define LEFT BUTTON_LEFT
|
|
#define RIGHT BUTTON_RIGHT
|
|
#define DOWN BUTTON_DOWN
|
|
#define UP BUTTON_UP
|
|
#define PAUSE BUTTON_PLAY
|
|
|
|
#elif CONFIG_KEYPAD == ONDIO_PAD
|
|
|
|
#define QUIT BUTTON_OFF
|
|
#define LEFT BUTTON_LEFT
|
|
#define RIGHT BUTTON_RIGHT
|
|
#define DOWN BUTTON_DOWN
|
|
#define UP BUTTON_UP
|
|
#define PAUSE BUTTON_MENU
|
|
|
|
#elif (CONFIG_KEYPAD == GIGABEAT_S_PAD)
|
|
|
|
#define QUIT BUTTON_BACK
|
|
#define LEFT BUTTON_LEFT
|
|
#define RIGHT BUTTON_RIGHT
|
|
#define UP BUTTON_UP
|
|
#define DOWN BUTTON_DOWN
|
|
#define PAUSE BUTTON_PLAY
|
|
|
|
#elif (CONFIG_KEYPAD == MROBE100_PAD)
|
|
|
|
#define QUIT BUTTON_POWER
|
|
#define LEFT BUTTON_LEFT
|
|
#define RIGHT BUTTON_RIGHT
|
|
#define UP BUTTON_UP
|
|
#define DOWN BUTTON_DOWN
|
|
#define PAUSE BUTTON_DISPLAY
|
|
|
|
#elif CONFIG_KEYPAD == IAUDIO_M3_PAD
|
|
|
|
#define QUIT BUTTON_RC_REC
|
|
#define LEFT BUTTON_RC_REW
|
|
#define RIGHT BUTTON_RC_FF
|
|
#define UP BUTTON_RC_VOL_UP
|
|
#define DOWN BUTTON_RC_VOL_DOWN
|
|
#define PAUSE BUTTON_RC_PLAY
|
|
|
|
#elif CONFIG_KEYPAD == COWOND2_PAD
|
|
|
|
#define QUIT BUTTON_POWER
|
|
#define LEFT BUTTON_LEFT
|
|
#define RIGHT BUTTON_RIGHT
|
|
#define UP BUTTON_UP
|
|
#define DOWN BUTTON_DOWN
|
|
#define PAUSE BUTTON_SELECT
|
|
|
|
#else
|
|
#error No keymap defined!
|
|
#endif
|
|
|
|
#define MOVE_NO 0 /* player movement */
|
|
#define MOVE_UP 1 /* 1 */
|
|
#define MOVE_DN 2 /* 3 0 4 */
|
|
#define MOVE_LT 3 /* 2 */
|
|
#define MOVE_RT 4
|
|
|
|
/* ball movement (12 ways) */
|
|
/* UUL UR */
|
|
/* UL UR */
|
|
/* ULL . URR */
|
|
/* DLL DRR */
|
|
/* DL DR */
|
|
/* DDL DDR */
|
|
|
|
#define DIR_UU (1<<7)
|
|
#define DIR_U (1<<6)
|
|
#define DIR_RR (1<<5)
|
|
#define DIR_R (1<<4)
|
|
#define DIR_DD (1<<3)
|
|
#define DIR_D (1<<2)
|
|
#define DIR_LL (1<<1)
|
|
#define DIR_L (1<<0)
|
|
|
|
#define MOVE_UUR ( DIR_UU | DIR_R )
|
|
#define MOVE_UR ( DIR_U | DIR_R )
|
|
#define MOVE_URR ( DIR_U | DIR_RR )
|
|
#define MOVE_DRR ( DIR_D | DIR_RR )
|
|
#define MOVE_DR ( DIR_D | DIR_R )
|
|
#define MOVE_DDR ( DIR_DD | DIR_R )
|
|
#define MOVE_DDL ( DIR_DD | DIR_L )
|
|
#define MOVE_DL ( DIR_D | DIR_L )
|
|
#define MOVE_DLL ( DIR_D | DIR_LL )
|
|
#define MOVE_ULL ( DIR_U | DIR_LL )
|
|
#define MOVE_UL ( DIR_U | DIR_L )
|
|
#define MOVE_UUL ( DIR_UU | DIR_L )
|
|
|
|
#if (LCD_WIDTH>112) && (LCD_HEIGHT>64)
|
|
# define CUBE_SIZE 8 /* 8x22=176 */
|
|
# define pos(a) ((a)>>3)
|
|
#else
|
|
# define CUBE_SIZE 4
|
|
# define pos(a) ((a)>>2)
|
|
#endif
|
|
|
|
#define STARTING_QIXES 2
|
|
#define MAX_LEVEL 10
|
|
#define MAX_QIXES MAX_LEVEL+STARTING_QIXES
|
|
#define BOARD_W ((int)(LCD_WIDTH/CUBE_SIZE))
|
|
#define BOARD_H ((int)(LCD_HEIGHT/CUBE_SIZE))
|
|
#define BOARD_X (LCD_WIDTH-BOARD_W*CUBE_SIZE)/2
|
|
#define BOARD_Y (LCD_HEIGHT-BOARD_H*CUBE_SIZE)/2
|
|
|
|
#ifdef HAVE_LCD_COLOR
|
|
#define CLR_RED LCD_RGBPACK(255,0,0) /* used to imply danger */
|
|
#define CLR_LTBLUE LCD_RGBPACK(125, 145, 180) /* used for frame and filling */
|
|
#define PLR_COL LCD_WHITE /* color used for the player */
|
|
#elif LCD_DEPTH>=2
|
|
#define CLR_RED LCD_DARKGRAY /* used to imply danger */
|
|
#define CLR_LTBLUE LCD_LIGHTGRAY /* used for frame and filling */
|
|
#define PLR_COL LCD_BLACK /* color used for the player */
|
|
#endif
|
|
|
|
#if LCD_DEPTH>=2
|
|
#define EMPTIED LCD_BLACK /* empty spot */
|
|
#define FILLED CLR_LTBLUE /* filled spot */
|
|
#define TRAIL CLR_RED /* the red trail of the player */
|
|
#define QIX LCD_WHITE
|
|
#else
|
|
#define EMPTIED 0
|
|
#define FILLED 1
|
|
#define TRAIL 2
|
|
#define QIX 3
|
|
#endif
|
|
#define UNCHECKED 0
|
|
#define CHECKED 1
|
|
#define PAINTED -1
|
|
#define PIC_QIX 0
|
|
#define PIC_PLAYER 1
|
|
|
|
#define MENU_START 0
|
|
#define MENU_QUIT 1
|
|
|
|
/* The time (in ms) for one iteration through the game loop - decrease this
|
|
to speed up the game - note that current_tick is (currently) only accurate
|
|
to 10ms.
|
|
*/
|
|
static int speed = 6; /* CYCLETIME = (11-speed)*10 ms */
|
|
static int difficulty = 75; /* Percentage of screen that needs to be filled
|
|
* in order to win the game */
|
|
|
|
static struct plugin_api *rb;
|
|
|
|
MEM_FUNCTION_WRAPPERS(rb);
|
|
|
|
static bool quit = false;
|
|
|
|
static unsigned int board[BOARD_H][BOARD_W];
|
|
static int testboard[BOARD_H][BOARD_W];
|
|
|
|
#if CUBE_SIZE == 8
|
|
/*
|
|
00011000 0x18 - 11100111 0xe7
|
|
00111100 0x3c - 11100111 0xe7
|
|
01111110 0x7e - 11000011 0xc3
|
|
11111111 0xff - 00000000 0x00
|
|
11111111 0xff - 00000000 0x00
|
|
01111110 0x7e - 11000011 0xc3
|
|
00111100 0x3c - 11100111 0xe7
|
|
00011000 0x18 - 11100111 0xe7
|
|
*/
|
|
const unsigned char pics[2][8] = {
|
|
{0x18, 0x3c, 0x7e, 0xff, 0xff, 0x7e, 0x3c, 0x18}, /* Alien (QIX) */
|
|
{0xe7, 0xe7, 0xc3, 0x00, 0x00, 0xc3, 0xe7, 0xe7} /* Player (XONIX) */
|
|
};
|
|
#elif CUBE_SIZE == 4
|
|
/*
|
|
0110 0x6 - 1001 0x9
|
|
1111 0xf - 0110 0x6
|
|
1111 0xf - 0110 0x6
|
|
0110 0x6 - 1001 0x9
|
|
*/
|
|
const unsigned char pics[2][4] = {
|
|
{0x6, 0xf, 0xf, 0x6}, /* Alien (QIX) */
|
|
{0x9, 0x6, 0x6, 0x9} /* Player (XONIX) */
|
|
};
|
|
#else
|
|
#error Incorrect CUBE_SIZE value.
|
|
#endif
|
|
|
|
static struct qix
|
|
{
|
|
int velocity; /* velocity */
|
|
int x, y; /* position on screen */
|
|
int angle; /* angle */
|
|
} qixes[MAX_QIXES]; /* black_qix */
|
|
|
|
static struct splayer
|
|
{
|
|
int i, j; /* position on board */
|
|
int move, score, level, lives;
|
|
bool drawing;
|
|
bool gameover;
|
|
} player;
|
|
|
|
static int percentage_cache;
|
|
|
|
/*************************** STACK STUFF **********************/
|
|
|
|
/* the stack */
|
|
#define STACK_SIZE (2*BOARD_W*BOARD_H)
|
|
static struct pos
|
|
{
|
|
int x, y; /* position on board */
|
|
} stack[STACK_SIZE];
|
|
static int stackPointer;
|
|
|
|
static inline bool pop (struct pos *p)
|
|
{
|
|
if (stackPointer > 0) {
|
|
p->x = stack[stackPointer].x;
|
|
p->y = stack[stackPointer].y;
|
|
stackPointer--;
|
|
return true;
|
|
} else
|
|
return false; /* SE */
|
|
}
|
|
|
|
static inline bool push (struct pos *p)
|
|
{
|
|
if (stackPointer < STACK_SIZE - 1) {
|
|
stackPointer++;
|
|
stack[stackPointer].x = p->x;
|
|
stack[stackPointer].y = p->y;
|
|
return true;
|
|
} else
|
|
return false; /* SOF */
|
|
}
|
|
|
|
static inline void emptyStack (void)
|
|
{
|
|
stackPointer = 0;
|
|
}
|
|
|
|
/*********************** END OF STACK STUFF *********************/
|
|
|
|
/* calculate the new x coordinate of the ball according to angle and speed */
|
|
static inline int get_newx (int x, int len, int deg)
|
|
{
|
|
if (deg & DIR_R)
|
|
return x + len;
|
|
else if (deg & DIR_L)
|
|
return x - len;
|
|
else if (deg & DIR_RR)
|
|
return x + len * 2;
|
|
else /* (def & DIR_LL) */
|
|
return x - len * 2;
|
|
}
|
|
|
|
/* calculate the new y coordinate of the ball according to angle and speed */
|
|
static inline int get_newy (int y, int len, int deg)
|
|
{
|
|
if (deg & DIR_D)
|
|
return y + len;
|
|
else if (deg & DIR_U)
|
|
return y - len;
|
|
else if (deg & DIR_DD)
|
|
return y + len * 2;
|
|
else /* (deg & DIR_UU) */
|
|
return y - len * 2;
|
|
}
|
|
|
|
/* make random function get it's value from the device ticker */
|
|
static inline void randomize (void)
|
|
{
|
|
rb->srand (*rb->current_tick);
|
|
}
|
|
|
|
/* get a random number between 0 and range-1 */
|
|
static int t_rand (int range)
|
|
{
|
|
return rb->rand () % range;
|
|
}
|
|
|
|
/* initializes the test help board */
|
|
static void init_testboard (void)
|
|
{
|
|
int j; /* testboard */
|
|
for (j = 0; j < BOARD_H; j++)
|
|
/* UNCHEKED == (int)0 */
|
|
rb->memset( testboard[j], 0, BOARD_W * sizeof( int ) );
|
|
}
|
|
|
|
/* initializes the game board on with the player,qix's and black qix */
|
|
static void init_board (void)
|
|
{
|
|
int i, j;
|
|
for (j = 0; j < BOARD_H; j++)
|
|
for (i = 0; i < BOARD_W; i++) { /* make a nice cyan frame */
|
|
if ((i == 0) || (j <= 1) || (i == BOARD_W - 1)
|
|
|| (j >= BOARD_H - 2))
|
|
board[j][i] = FILLED;
|
|
else
|
|
board[j][i] = EMPTIED;
|
|
}
|
|
|
|
/* (level+2) is the number of qixes */
|
|
for (j = 0; j < player.level + STARTING_QIXES; j++) {
|
|
qixes[j].velocity = t_rand (2) + 1; /* 1 or 2 pix-per-sec */
|
|
|
|
/* not on frame */
|
|
qixes[j].x = CUBE_SIZE*2 + 2*t_rand (((BOARD_W-4)*CUBE_SIZE)/2);
|
|
qixes[j].y = CUBE_SIZE*2 + 2*t_rand (((BOARD_H-4)*CUBE_SIZE)/2);
|
|
|
|
const int angle_table[] = {
|
|
MOVE_UUR, MOVE_UR, MOVE_URR, MOVE_DRR, MOVE_DR, MOVE_DDR,
|
|
MOVE_UUL, MOVE_UL, MOVE_ULL, MOVE_DLL, MOVE_DL, MOVE_DDL };
|
|
qixes[j].angle = angle_table[t_rand (12)];
|
|
#if CUBE_SIZE == 4
|
|
/* Work arround a nasty bug. FIXME */
|
|
if( qixes[j].angle & (DIR_LL|DIR_RR|DIR_UU|DIR_DD) )
|
|
qixes[j].velocity = 1;
|
|
#endif
|
|
}
|
|
/*black_qix.velocity=1;
|
|
black_qix.x=BOARD_X+(BOARD_W*CUBE_SIZE)/2-CUBE_SIZE/2;
|
|
black_qix.y=BOARD_Y+(BOARD_H*CUBE_SIZE)-CUBE_SIZE-CUBE_SIZE/2;
|
|
black_qix.angle=MOVE_UR; */
|
|
player.move = MOVE_NO;
|
|
player.drawing = false;
|
|
player.i = BOARD_W / 2;
|
|
player.j = 1;
|
|
|
|
percentage_cache = 0;
|
|
}
|
|
|
|
/* calculates the percentage of the screen filling */
|
|
static int percentage (void)
|
|
{
|
|
int i, j, filled = 0;
|
|
for (j = 2; j < BOARD_H - 2; j++)
|
|
for (i = 1; i < BOARD_W - 1; i++)
|
|
if (board[j][i] == FILLED)
|
|
filled++;
|
|
return (filled * 100) / ((BOARD_W - 2) * (BOARD_H - 4));
|
|
}
|
|
|
|
/* draw the board on with all the game figures */
|
|
static void refresh_board (void)
|
|
{
|
|
int i, j;
|
|
char str[25];
|
|
|
|
#if LCD_DEPTH>=2
|
|
rb->lcd_set_background (LCD_BLACK);
|
|
#else
|
|
rb->lcd_clear_display ();
|
|
#endif
|
|
for (j = 0; j < BOARD_H; j++)
|
|
{
|
|
unsigned last_color = board[j][0];
|
|
int last_i = 0;
|
|
for (i = 1; i < BOARD_W; i++) {
|
|
if( last_color != board[j][i] )
|
|
{
|
|
#if LCD_DEPTH>=2
|
|
rb->lcd_set_foreground (last_color);
|
|
#else
|
|
if (last_color != EMPTIED)
|
|
#endif
|
|
rb->lcd_fillrect (BOARD_X + CUBE_SIZE * (last_i),
|
|
BOARD_Y + CUBE_SIZE * j,
|
|
CUBE_SIZE * (i - last_i), CUBE_SIZE );
|
|
last_color = board[j][i];
|
|
last_i = i;
|
|
}
|
|
}
|
|
#if LCD_DEPTH>=2
|
|
rb->lcd_set_foreground (last_color);
|
|
#else
|
|
if (last_color != EMPTIED)
|
|
#endif
|
|
rb->lcd_fillrect (BOARD_X + CUBE_SIZE * (last_i),
|
|
BOARD_Y + CUBE_SIZE * j,
|
|
CUBE_SIZE * (i - last_i), CUBE_SIZE);
|
|
}
|
|
|
|
#if LCD_DEPTH>=2
|
|
rb->lcd_set_foreground (LCD_BLACK);
|
|
rb->lcd_set_background (CLR_LTBLUE);
|
|
#else
|
|
rb->lcd_set_drawmode (DRMODE_COMPLEMENT);
|
|
#endif
|
|
rb->snprintf (str, sizeof (str), "Level %d", player.level + 1);
|
|
rb->lcd_putsxy (BOARD_X, BOARD_Y, str);
|
|
rb->snprintf (str, sizeof (str), "%d%%", percentage_cache);
|
|
rb->lcd_putsxy (BOARD_X + CUBE_SIZE * BOARD_W - 24, BOARD_Y, str);
|
|
rb->snprintf (str, sizeof (str), "Score: %d", player.score);
|
|
rb->lcd_putsxy (BOARD_X, BOARD_Y + CUBE_SIZE * BOARD_H - 8, str);
|
|
rb->snprintf (str, sizeof (str), "%d Lives", player.lives);
|
|
#if LCD_DEPTH>=2
|
|
rb->lcd_putsxy (BOARD_X + CUBE_SIZE * BOARD_W - 60,
|
|
BOARD_Y + CUBE_SIZE * BOARD_H - 8, str);
|
|
#else
|
|
rb->lcd_putsxy (BOARD_X + CUBE_SIZE * BOARD_W - 40,
|
|
BOARD_Y + CUBE_SIZE * BOARD_H - 8, str);
|
|
#endif
|
|
|
|
#if LCD_DEPTH>=2
|
|
rb->lcd_set_foreground (PLR_COL);
|
|
rb->lcd_set_background (board[player.j][player.i]);
|
|
#endif
|
|
rb->lcd_mono_bitmap (pics[PIC_PLAYER], player.i * CUBE_SIZE + BOARD_X,
|
|
player.j * CUBE_SIZE + BOARD_Y, CUBE_SIZE, CUBE_SIZE);
|
|
|
|
#if LCD_DEPTH>=2
|
|
rb->lcd_set_background (EMPTIED);
|
|
rb->lcd_set_foreground (LCD_WHITE);
|
|
rb->lcd_set_drawmode (DRMODE_FG);
|
|
#else
|
|
rb->lcd_set_drawmode (DRMODE_FG);
|
|
#endif
|
|
for (j = 0; j < player.level + STARTING_QIXES; j++)
|
|
rb->lcd_mono_bitmap (pics[PIC_QIX], qixes[j].x + BOARD_X,
|
|
qixes[j].y + BOARD_Y, CUBE_SIZE, CUBE_SIZE);
|
|
#if LCD_DEPTH>=2
|
|
rb->lcd_set_foreground (LCD_BLACK);
|
|
#endif
|
|
rb->lcd_set_drawmode (DRMODE_SOLID);
|
|
|
|
rb->lcd_update ();
|
|
}
|
|
|
|
static inline int infested_area (int i, int j, int v)
|
|
{
|
|
struct pos p;
|
|
p.x = i;
|
|
p.y = j;
|
|
emptyStack ();
|
|
if (!push (&p))
|
|
return -1;
|
|
while (pop (&p)) {
|
|
if (testboard[p.y][p.x] == v) continue;
|
|
if (testboard[p.y][p.x] > UNCHECKED)
|
|
return 1; /* This area was previously flagged as infested */
|
|
testboard[p.y][p.x] = v;
|
|
if (board[p.y][p.x] == QIX)
|
|
return 1; /* Infested area */
|
|
{
|
|
struct pos p1 = { p.x+1, p.y };
|
|
if ((p1.x < BOARD_W)
|
|
&& (board[p1.y][p1.x] != FILLED)
|
|
&& (!push (&p1)))
|
|
return -1;
|
|
}
|
|
{
|
|
struct pos p1 = { p.x-1, p.y };
|
|
if ((p1.x >= 0)
|
|
&& (board[p1.y][p1.x] != FILLED)
|
|
&& (!push (&p1)))
|
|
return -1;
|
|
}
|
|
{
|
|
struct pos p1 = { p.x, p.y+1 };
|
|
if ((p1.y < BOARD_H)
|
|
&& (board[p1.y][p1.x] != FILLED)
|
|
&& (!push (&p1)))
|
|
return -1;
|
|
}
|
|
{
|
|
struct pos p1 = { p.x, p.y-1 };
|
|
if ((p1.y >= 0)
|
|
&& (board[p1.y][p1.x] != FILLED)
|
|
&& (!push (&p1)))
|
|
return -1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static inline int fill_area (int i, int j)
|
|
{
|
|
struct pos p;
|
|
p.x = i;
|
|
p.y = j;
|
|
int v = testboard[p.y][p.x];
|
|
emptyStack ();
|
|
if (!push (&p))
|
|
return -1;
|
|
while (pop (&p)) {
|
|
board[p.y][p.x] = FILLED;
|
|
testboard[p.y][p.x] = PAINTED;
|
|
{
|
|
struct pos p1 = { p.x+1, p.y };
|
|
if ((p1.x < BOARD_W)
|
|
&& (testboard[p1.y][p1.x] == v)
|
|
&& (!push (&p1)))
|
|
return -1;
|
|
}
|
|
{
|
|
struct pos p1 = { p.x-1, p.y };
|
|
if ((p1.x >= 0)
|
|
&& (testboard[p1.y][p1.x] == v)
|
|
&& (!push (&p1)))
|
|
return -1;
|
|
}
|
|
{
|
|
struct pos p1 = { p.x, p.y+1 };
|
|
if ((p1.y < BOARD_H)
|
|
&& (testboard[p1.y][p1.x] == v)
|
|
&& (!push (&p1)))
|
|
return -1;
|
|
}
|
|
{
|
|
struct pos p1 = { p.x, p.y-1 };
|
|
if ((p1.y >= 0)
|
|
&& (testboard[p1.y][p1.x] == v)
|
|
&& (!push (&p1)))
|
|
return -1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* take care of stuff after xonix has landed on a filled spot */
|
|
static void complete_trail (int fill)
|
|
{
|
|
int i, j, ret;
|
|
for (j = 0; j < BOARD_H; j++) {
|
|
for (i = 0; i < BOARD_W; i++) {
|
|
if (board[j][i] == TRAIL) {
|
|
if (fill)
|
|
board[j][i] = FILLED;
|
|
else
|
|
board[j][i] = EMPTIED;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (fill) {
|
|
int v = CHECKED;
|
|
for (i = 0; i < player.level + STARTING_QIXES; i++) /* add qixes to board */
|
|
board[pos(qixes[i].y - BOARD_Y)]
|
|
[pos(qixes[i].x - BOARD_X)] = QIX;
|
|
|
|
init_testboard();
|
|
for (j = 1; j < BOARD_H - 1; j++) {
|
|
for (i = 0; i < BOARD_W - 0; i++) {
|
|
if (board[j][i] != FILLED) {
|
|
ret = infested_area (i, j, v);
|
|
if (ret < 0 || ( ret == 0 && fill_area (i, j) ) )
|
|
quit = true;
|
|
v++;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < player.level + STARTING_QIXES; i++) /* add qixes to board */
|
|
board[pos(qixes[i].y - BOARD_Y)]
|
|
[pos(qixes[i].x - BOARD_X)] = EMPTIED;
|
|
percentage_cache = percentage();
|
|
}
|
|
|
|
rb->button_clear_queue();
|
|
}
|
|
|
|
/* returns the color the real pixel(x,y) on the lcd is pointing at */
|
|
static inline unsigned int getpixel (int x, int y)
|
|
{
|
|
const int a = pos (x - BOARD_X), b = pos (y - BOARD_Y);
|
|
if ((a > 0) && (a < BOARD_W) && (b > 0) && (b < BOARD_H)) /* if inside board */
|
|
return board[b][a];
|
|
else
|
|
return FILLED;
|
|
}
|
|
|
|
/* returns the color the ball on (newx,newy) is heading at *----*
|
|
checks the four edge points of the square if 1st of all | |
|
|
are a trail (cause it's a lose life situation) and 2nd | |
|
|
if it's filled so it needs to bounce. *____*
|
|
*/
|
|
static inline unsigned int next_hit (int newx, int newy)
|
|
{
|
|
if ((getpixel (newx, newy) == TRAIL)
|
|
|| (getpixel (newx, newy + CUBE_SIZE - 1) == TRAIL)
|
|
|| (getpixel (newx + CUBE_SIZE - 1, newy) == TRAIL)
|
|
|| (getpixel (newx + CUBE_SIZE - 1, newy + CUBE_SIZE - 1) == TRAIL))
|
|
return TRAIL;
|
|
else if ((getpixel (newx, newy) == FILLED)
|
|
|| (getpixel (newx, newy + CUBE_SIZE - 1) == FILLED)
|
|
|| (getpixel (newx + CUBE_SIZE - 1, newy) == FILLED)
|
|
|| (getpixel (newx + CUBE_SIZE - 1, newy + CUBE_SIZE - 1) ==
|
|
FILLED))
|
|
return FILLED;
|
|
else
|
|
return EMPTIED;
|
|
}
|
|
|
|
static void die (void)
|
|
{
|
|
player.lives--;
|
|
if (player.lives == 0)
|
|
player.gameover = true;
|
|
else {
|
|
refresh_board ();
|
|
rb->splash (HZ, "Crash!");
|
|
complete_trail (false);
|
|
player.move = MOVE_NO;
|
|
player.drawing = false;
|
|
player.i = BOARD_W / 2;
|
|
player.j = 1;
|
|
}
|
|
}
|
|
|
|
/* returns true if the (side) of the block -***-
|
|
starting from (newx,newy) has any filled pixels * *
|
|
-***-
|
|
*/
|
|
static inline bool line_check_lt (int newx, int newy)
|
|
{
|
|
return getpixel (newx, newy + CUBE_SIZE/2-1) == FILLED
|
|
&& getpixel (newx, newy + CUBE_SIZE/2 ) == FILLED;
|
|
}
|
|
static inline bool line_check_rt (int newx, int newy)
|
|
{
|
|
return getpixel (newx + CUBE_SIZE-1, newy + CUBE_SIZE/2-1) == FILLED
|
|
&& getpixel (newx + CUBE_SIZE-1, newy + CUBE_SIZE/2 ) == FILLED;
|
|
}
|
|
static inline bool line_check_up (int newx, int newy)
|
|
{
|
|
return getpixel (newx + CUBE_SIZE/2-1, newy) == FILLED
|
|
&& getpixel (newx + CUBE_SIZE/2 , newy) == FILLED;
|
|
}
|
|
static inline bool line_check_dn (int newx, int newy)
|
|
{
|
|
return getpixel (newx + CUBE_SIZE/2-1, newy + CUBE_SIZE-1) == FILLED
|
|
&& getpixel (newx + CUBE_SIZE/2 , newy + CUBE_SIZE-1) == FILLED;
|
|
}
|
|
|
|
static inline void move_qix (struct qix *q)
|
|
{
|
|
int newx, newy;
|
|
newx = get_newx (q->x, q->velocity, q->angle);
|
|
newy = get_newy (q->y, q->velocity, q->angle);
|
|
switch (next_hit (newx, newy))
|
|
{
|
|
case EMPTIED:
|
|
q->x = newx;
|
|
q->y = newy;
|
|
break;
|
|
case FILLED:
|
|
{
|
|
const int a = q->angle;
|
|
q->angle =
|
|
((a&(DIR_UU|DIR_U))
|
|
? (line_check_up (newx, newy) ? ((a&(DIR_UU|DIR_U))>>4)
|
|
: (a&(DIR_UU|DIR_U)))
|
|
: 0)
|
|
|
|
|
((a&(DIR_RR|DIR_R))
|
|
? (line_check_rt (newx, newy) ? ((a&(DIR_RR|DIR_R))>>4)
|
|
: (a&(DIR_RR|DIR_R)))
|
|
: 0)
|
|
|
|
|
((a&(DIR_DD|DIR_D))
|
|
? (line_check_dn (newx, newy) ? ((a&(DIR_DD|DIR_D))<<4)
|
|
: (a&(DIR_DD|DIR_D)))
|
|
: 0)
|
|
|
|
|
((a&(DIR_LL|DIR_L))
|
|
? (line_check_lt (newx, newy) ? ((a&(DIR_LL|DIR_L))<<4)
|
|
: (a&(DIR_LL|DIR_L)))
|
|
: 0);
|
|
q->x = get_newx (q->x, q->velocity, q->angle);
|
|
q->y = get_newy (q->y, q->velocity, q->angle);
|
|
break;
|
|
}
|
|
case TRAIL:
|
|
die();
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* move the board forward timewise */
|
|
static inline void move_board (void)
|
|
{
|
|
int j, newi, newj;
|
|
|
|
for (j = 0; j < player.level + STARTING_QIXES; j++)
|
|
move_qix (&qixes[j]);
|
|
/* move_qix(&black_qix,true); */
|
|
if (player.move) {
|
|
newi = player.i;
|
|
newj = player.j;
|
|
switch (player.move) {
|
|
case MOVE_UP:
|
|
if (player.j > 1)
|
|
newj--;
|
|
break;
|
|
case MOVE_DN:
|
|
if (player.j < BOARD_H - 2)
|
|
newj++;
|
|
break;
|
|
case MOVE_LT:
|
|
if (player.i > 0)
|
|
newi--;
|
|
break;
|
|
case MOVE_RT:
|
|
if (player.i < BOARD_W - 1)
|
|
newi++;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if ((player.drawing) && (board[newj][newi] == EMPTIED)) /* continue drawing */
|
|
board[newj][newi] = TRAIL;
|
|
else if ((player.drawing) && (board[newj][newi] == FILLED)) { /* finish drawing */
|
|
player.move = MOVE_NO; /* stop moving */
|
|
player.drawing = false;
|
|
complete_trail (true);
|
|
} else if ((board[player.j][player.i] == FILLED)
|
|
&& (board[newj][newi] == EMPTIED)) {
|
|
/* start drawing */
|
|
player.drawing = true;
|
|
board[newj][newi] = TRAIL;
|
|
/* if the block after next is empty and we're moving onto filled, stop */
|
|
} else if ((board[newj][newi] == FILLED)
|
|
&& (board[newj + newj-player.j][newi + newi-player.i] == EMPTIED)) {
|
|
player.move = MOVE_NO;
|
|
}
|
|
player.i = newi;
|
|
player.j = newj;
|
|
}
|
|
if (percentage_cache >= difficulty) { /* finished level */
|
|
rb->splash (HZ * 2, "Level %d finished", player.level+1);
|
|
player.score += percentage_cache;
|
|
if (player.level < MAX_LEVEL)
|
|
player.level++;
|
|
init_board ();
|
|
refresh_board ();
|
|
rb->splash (HZ * 2, "Ready?");
|
|
}
|
|
}
|
|
|
|
/* the main menu */
|
|
static int game_menu (void)
|
|
{
|
|
MENUITEM_STRINGLIST(menu, "XOBOX Menu", NULL, "Start New Game",
|
|
"Speed","Difficulty","Quit");
|
|
int selection = 0;
|
|
#ifdef HAVE_LCD_COLOR
|
|
rb->lcd_set_foreground (rb->global_settings->fg_color);
|
|
rb->lcd_set_background (rb->global_settings->bg_color);
|
|
#elif LCD_DEPTH>=2
|
|
rb->lcd_set_foreground(LCD_BLACK);
|
|
rb->lcd_set_background(LCD_WHITE);
|
|
#endif
|
|
for (;;) {
|
|
rb->do_menu(&menu,&selection);
|
|
if (selection==1)
|
|
rb->set_int ("Speed", "", UNIT_INT, &speed, NULL, 1, 1, 10, NULL);
|
|
else if (selection==2)
|
|
rb->set_int ("Difficulty", "", UNIT_INT, &difficulty, NULL,
|
|
5, 50, 95, NULL);
|
|
else
|
|
break;
|
|
}
|
|
if (selection != MENU_START) {
|
|
selection = MENU_QUIT;
|
|
}
|
|
return selection;
|
|
}
|
|
|
|
/* init game's variables */
|
|
static void init_game (void)
|
|
{
|
|
player.level = 0;
|
|
player.score = 0;
|
|
player.lives = 3;
|
|
player.gameover = false;
|
|
player.drawing = false;
|
|
rb->lcd_setfont(FONT_SYSFIXED);
|
|
init_board ();
|
|
refresh_board ();
|
|
rb->splash (HZ * 2, "Ready?");
|
|
}
|
|
|
|
/* general keypad handler loop */
|
|
static int xobox_loop (void)
|
|
{
|
|
int button = 0, ret;
|
|
bool pause = false;
|
|
int end;
|
|
|
|
while (!quit) {
|
|
end = *rb->current_tick + ((11-speed)*HZ)/100;
|
|
|
|
#ifdef HAS_BUTTON_HOLD
|
|
if (rb->button_hold()) {
|
|
pause = true;
|
|
rb->splash (HZ, "PAUSED");
|
|
}
|
|
#endif
|
|
|
|
button = rb->button_get_w_tmo (true);
|
|
switch (button) {
|
|
case UP:
|
|
case UP|BUTTON_REPEAT:
|
|
player.move = MOVE_UP;
|
|
break;
|
|
case DOWN:
|
|
case DOWN|BUTTON_REPEAT:
|
|
player.move = MOVE_DN;
|
|
break;
|
|
case LEFT:
|
|
case LEFT|BUTTON_REPEAT:
|
|
player.move = MOVE_LT;
|
|
break;
|
|
case RIGHT:
|
|
case RIGHT|BUTTON_REPEAT:
|
|
player.move = MOVE_RT;
|
|
break;
|
|
case PAUSE:
|
|
pause = !pause;
|
|
if (pause)
|
|
rb->splash (HZ, "Paused");
|
|
break;
|
|
case QUIT:
|
|
ret = game_menu ();
|
|
if (ret == MENU_START)
|
|
init_game ();
|
|
else
|
|
{
|
|
quit = true;
|
|
continue;
|
|
}
|
|
break;
|
|
default:
|
|
if (rb->default_event_handler (button) == SYS_USB_CONNECTED)
|
|
return PLUGIN_USB_CONNECTED;
|
|
break;
|
|
}
|
|
if (!pause) {
|
|
move_board ();
|
|
refresh_board ();
|
|
}
|
|
if (player.gameover) {
|
|
rb->splash (HZ, "Game Over!");
|
|
ret = game_menu ();
|
|
if (ret == MENU_START)
|
|
init_game ();
|
|
else
|
|
quit = true;
|
|
}
|
|
|
|
if (end > *rb->current_tick)
|
|
rb->sleep (end - *rb->current_tick);
|
|
else
|
|
rb->yield ();
|
|
|
|
} /* end while */
|
|
return PLUGIN_OK; /* for no warnings on compiling */
|
|
}
|
|
|
|
/* plugin main procedure */
|
|
enum plugin_status plugin_start (struct plugin_api *api, void *parameter)
|
|
{
|
|
int ret = PLUGIN_OK;
|
|
|
|
(void) parameter;
|
|
rb = api;
|
|
|
|
rb->lcd_setfont (FONT_SYSFIXED);
|
|
#if LCD_DEPTH>=2
|
|
rb->lcd_set_backdrop(NULL);
|
|
#endif
|
|
|
|
/* Turn off backlight timeout */
|
|
backlight_force_on(rb); /* backlight control in lib/helper.c */
|
|
|
|
quit = false;
|
|
|
|
randomize ();
|
|
if (game_menu () == MENU_START) {
|
|
init_game ();
|
|
ret = xobox_loop ();
|
|
}
|
|
|
|
/* Turn on backlight timeout (revert to settings) */
|
|
backlight_use_settings(rb); /* backlight control in lib/helper.c */
|
|
rb->lcd_setfont (FONT_UI);
|
|
|
|
return ret;
|
|
}
|