rockbox/apps/plugins/xobox.c
Jens Arnold 85a226d4e7 Rename some macros to account for the recently added M5 port. No code changes.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@12809 a1c6a512-1295-4272-9138-f99709370657
2007-03-16 23:02:39 +00:00

890 lines
26 KiB
C

/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2006 Eli Sherer
*
* 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"
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 SELECT BUTTON_SELECT
#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
#define SELECT BUTTON_SELECT
#elif (CONFIG_KEYPAD == IPOD_3G_PAD) || \
(CONFIG_KEYPAD == IPOD_4G_PAD)
#define QUIT (BUTTON_SELECT | BUTTON_MENU)
#define LEFT BUTTON_LEFT
#define RIGHT BUTTON_RIGHT
#define PAUSE BUTTON_SELECT
#define SELECT 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 SELECT BUTTON_SELECT
#define UP BUTTON_UP
#define DOWN BUTTON_DOWN
#define PAUSE BUTTON_PLAY
#elif (CONFIG_KEYPAD == GIGABEAT_PAD)
#define QUIT BUTTON_A
#define LEFT BUTTON_LEFT
#define RIGHT BUTTON_RIGHT
#define SELECT BUTTON_SELECT
#define UP BUTTON_UP
#define DOWN BUTTON_DOWN
#define PAUSE BUTTON_MENU
#elif (CONFIG_KEYPAD == SANSA_E200_PAD)
#define QUIT BUTTON_POWER
#define LEFT BUTTON_LEFT
#define RIGHT BUTTON_RIGHT
#define SELECT BUTTON_SELECT
#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 SELECT BUTTON_REW
#define UP BUTTON_SCROLL_UP
#define DOWN BUTTON_SCROLL_DOWN
#define PAUSE BUTTON_PLAY
#else
#error Unsupported keypad
#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
#define MOVE_UUR 0 /* ball movement (12 ways) */
#define MOVE_UR 1
#define MOVE_URR 2
#define MOVE_DRR 3 /* UUL110UUR */
#define MOVE_DR 4 /* UL10 1UR */
#define MOVE_DDR 5 /* ULL9 . 2URR */
#define MOVE_DDL 6 /* DLL8 3DRR */
#define MOVE_DL 7 /* DL7 4DR */
#define MOVE_DLL 8 /* DDL6 5DDR */
#define MOVE_ULL 9
#define MOVE_UL 10
#define MOVE_UUL 11
#define CUBE_SIZE 8 /* 8x22=176 */
#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_BLUE LCD_RGBPACK(0,0,128) /* used for menu selection */
#define CLR_LTBLUE LCD_RGBPACK(125, 145, 180) /* used for frame and filling */
#define PLR_COL LCD_WHITE /* color used for the player */
#else
#define CLR_RED LCD_DARKGRAY /* used to imply danger */
#define CLR_BLUE LCD_BLACK /* used for menu selection */
#define CLR_LTBLUE LCD_LIGHTGRAY /* used for frame and filling */
#define PLR_COL LCD_BLACK /* color used for the player */
#endif
#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
#define UNCHECKED 0
#define CHECKED 1
#define PIC_QIX 0
#define PIC_PLAYER 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.
*/
#define CYCLETIME 50
static struct plugin_api *rb;
static bool quit = false;
static unsigned short board[BOARD_H][BOARD_W],
testboard[BOARD_H][BOARD_W], boardcopy[BOARD_H][BOARD_W];
/*
00001000 0x08 - 01110111 0x77
00011100 0x1c - 01110111 0x77
00111110 0x3e - 01100011 0x63
01111111 0x7f - 00000000 0x00
00111110 0x3e - 01100011 0x63
00011100 0x1c - 01110111 0x77
00001000 0x08 - 01110111 0x77
00000000 0x00 - 00000000 0x00
*/
const unsigned char pics[2][8] = {
{0x08, 0x1c, 0x3e, 0x7f, 0x3e, 0x1c, 0x08, 0x00}, /* Alien (QIX) */
{0x77, 0x77, 0x63, 0x00, 0x63, 0x77, 0x77, 0x00} /* Player (XONIX) */
};
static struct qix
{
short velocity; /* velocity */
short x, y; /* position on screen */
short angle; /* angle */
} qixes[MAX_QIXES]; /* black_qix */
static struct splayer
{
short i, j; /* position on board */
short move, score, level, lives;
bool drawing;
bool gameover;
} player;
/*************************** STACK STUFF **********************/
/* the stack */
#define STACK_SIZE BOARD_W*BOARD_H
static struct pos
{
int x, y; /* position on board */
} stack[STACK_SIZE];
static int stackPointer;
#define div(a,b) (((a)/(b)))
static 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 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 void emptyStack (void)
{
stackPointer = 0;
/* int x, y;
while(pop(&x, &y)); */
}
/*********************** END OF STACK STUFF *********************/
/* calculate the new x coordinate of the ball according to angle and speed */
static int get_newx (int x, int len, int deg)
{
int dx;
if ((deg == MOVE_DRR) || (deg == MOVE_URR))
dx = 2;
else if ((deg == MOVE_DDR) || (deg == MOVE_UUR) || (deg == MOVE_DR)
|| (deg == MOVE_UR))
dx = 1;
else if ((deg == MOVE_DDL) || (deg == MOVE_UUL) || (deg == MOVE_DL)
|| (deg == MOVE_UL))
dx = -1;
else
dx = -2;
return x + dx * len;
}
/* calculate the new y coordinate of the ball according to angle and speed */
static int get_newy (int y, int len, int deg)
{
int dy;
if ((deg == MOVE_DRR) || (deg == MOVE_DLL) || (deg == MOVE_DR)
|| (deg == MOVE_DL))
dy = 1;
else if ((deg == MOVE_DDR) || (deg == MOVE_DDL))
dy = 2;
else if ((deg == MOVE_UUR) || (deg == MOVE_UUL))
dy = -2;
else
dy = -1;
return y + dy * len;
}
/* 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 i, j; /* testboard */
for (j = 0; j < BOARD_H; j++)
for (i = 0; i < BOARD_W; i++)
testboard[j][i] = UNCHECKED;
}
/* 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 =
BOARD_X + t_rand (((BOARD_W - 4) * CUBE_SIZE) - 2 * CUBE_SIZE) +
2 * CUBE_SIZE;
qixes[j].y =
BOARD_Y + t_rand (((BOARD_H - 6) * CUBE_SIZE) - 2 * CUBE_SIZE) +
3 * CUBE_SIZE;
qixes[j].angle = t_rand (12);
}
/*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;
}
/* 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];
rb->lcd_set_background (LCD_BLACK);
rb->lcd_clear_display ();
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] )
{
rb->lcd_set_foreground (last_color);
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;
}
}
rb->lcd_set_foreground (last_color);
rb->lcd_fillrect (BOARD_X + CUBE_SIZE * (last_i),
BOARD_Y + CUBE_SIZE * j,
CUBE_SIZE * (i - last_i), CUBE_SIZE);
}
rb->lcd_set_foreground (LCD_BLACK);
rb->lcd_set_background (CLR_LTBLUE);
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 ());
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);
rb->lcd_putsxy (BOARD_X + CUBE_SIZE * BOARD_W - 60,
BOARD_Y + CUBE_SIZE * BOARD_H - 8, str);
rb->lcd_set_foreground (PLR_COL);
rb->lcd_set_background (board[player.j][player.i]);
rb->lcd_mono_bitmap (pics[PIC_PLAYER], player.i * CUBE_SIZE + BOARD_X,
player.j * CUBE_SIZE + BOARD_Y, CUBE_SIZE, CUBE_SIZE);
rb->lcd_set_background (EMPTIED);
rb->lcd_set_foreground (LCD_WHITE);
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);
rb->lcd_set_foreground (LCD_BLACK);
rb->lcd_update ();
}
static inline int infested_area (int i, int j)
{
struct pos p, p1, p2, p3, p4;
bool hit = false;
p.x = i;
p.y = j;
emptyStack ();
init_testboard ();
if (!push (&p))
return -1;
while (pop (&p)) {
hit = (boardcopy[p.y][p.x] == QIX);
testboard[p.y][p.x] = CHECKED;
if (hit)
return true; /*save some time and space */
p1.x = p.x + 1;
p1.y = p.y;
p2.x = p.x - 1;
p2.y = p.y;
p3.x = p.x;
p3.y = p.y + 1;
p4.x = p.x;
p4.y = p.y - 1;
if ((p1.x < BOARD_W) && (testboard[p1.y][p1.x] == UNCHECKED))
if (board[p1.y][p1.x] != FILLED)
if (!push (&p1))
return -1;
if ((p2.x >= 0) && (testboard[p2.y][p2.x] == UNCHECKED))
if (board[p2.y][p2.x] != FILLED)
if (!push (&p2))
return -1;
if ((p3.y < BOARD_H) && (testboard[p3.y][p3.x] == UNCHECKED))
if (board[p3.y][p3.x] != FILLED)
if (!push (&p3))
return -1;
if ((p4.y >= 0) && (testboard[p4.y][p4.x] == UNCHECKED))
if (board[p4.y][p4.x] != FILLED)
if (!push (&p4))
return -1;
}
return (hit ? 1 : 0);
}
static inline int fill_area (int i, int j)
{
struct pos p, p1, p2, p3, p4;
p.x = i;
p.y = j;
emptyStack ();
init_testboard ();
if (!push (&p))
return -1;
while (pop (&p)) {
board[p.y][p.x] = FILLED;
testboard[p.y][p.x] = CHECKED;
p1.x = p.x + 1;
p1.y = p.y;
p2.x = p.x - 1;
p2.y = p.y;
p3.x = p.x;
p3.y = p.y + 1;
p4.x = p.x;
p4.y = p.y - 1;
if ((p1.x < BOARD_W) && (testboard[p1.y][p1.x] == UNCHECKED))
if (board[p1.y][p1.x] == EMPTIED)
if (!push (&p1))
return -1;
if ((p2.x >= 0) && (testboard[p2.y][p2.x] == UNCHECKED))
if (board[p2.y][p2.x] == EMPTIED)
if (!push (&p2))
return -1;
if ((p3.y < BOARD_H) && (testboard[p3.y][p3.x] == UNCHECKED))
if (board[p3.y][p3.x] == EMPTIED)
if (!push (&p3))
return -1;
if ((p4.y >= 0) && (testboard[p4.y][p4.x] == UNCHECKED))
if (board[p4.y][p4.x] == EMPTIED)
if (!push (&p4))
return -1;
}
return 1;
}
/* 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;
}
boardcopy[j][i] = board[j][i];
}
if (fill) {
for (i = 0; i < player.level + STARTING_QIXES; i++) /* add qixes to board */
boardcopy[div(qixes[i].y - BOARD_Y, CUBE_SIZE)]
[div(qixes[i].x - BOARD_X, CUBE_SIZE)] = QIX;
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);
if (ret < 0 || ( ret == 0 && fill_area (i, j) < 0 ) )
quit = true;
}
}
rb->button_clear_queue();
}
/* returns the color the real pixel(x,y) on the lcd is pointing at */
static unsigned short getpixel (int x, int y)
{
int a = div (x - BOARD_X, CUBE_SIZE), b = div (y - BOARD_Y, CUBE_SIZE);
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 short 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;
}
/* returns true if the (side) of the block -***-
starting from (newx,newy) has any filled pixels * *
-***-
*/
static bool line_check (int newx, int newy, int side)
{
int i = 0;
bool filled = false;
for (i = 3; ((i < CUBE_SIZE - 3) && (!filled)); i++) {
switch (side) {
case MOVE_LT:
filled = getpixel (newx, newy + i) == FILLED;
break;
case MOVE_RT:
filled = getpixel (newx + CUBE_SIZE - 1, newy + i) == FILLED;
break;
case MOVE_UP:
filled = getpixel (newx + i, newy) == FILLED;
break;
case MOVE_DN:
filled = getpixel (newx + i, newy + CUBE_SIZE - 1) == FILLED;
break;
}
}
return filled;
}
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;
}
}
static void move_qix (struct qix *q)
{
int newx, newy, dir;
unsigned short nexthit;
newx = get_newx (q->x, q->velocity, q->angle);
newy = get_newy (q->y, q->velocity, q->angle);
nexthit = next_hit (newx, newy);
if (nexthit == EMPTIED) {
q->x = newx;
q->y = newy;
} else if (nexthit == FILLED) {
dir = q->angle;
switch (dir) {
case MOVE_URR:
case MOVE_UUR:
case MOVE_UR: /* up-right (can hit ceiling or right wall) */
if (line_check (newx, newy, MOVE_UP)
&& line_check (newx, newy, MOVE_RT))
q->angle = q->angle + 6;
else if (line_check (newx, newy, MOVE_UP))
q->angle = 5 - q->angle; /* 5=180/(360/12)-1 */
else
q->angle = 11 - q->angle; /* 11=360/(360/12)-1 */
break;
case MOVE_ULL:
case MOVE_UUL:
case MOVE_UL: /* up-left (can hit ceiling or left wall) */
if (line_check (newx, newy, MOVE_UP)
&& line_check (newx, newy, MOVE_LT))
q->angle = q->angle - 6;
else if (line_check (newx, newy, MOVE_UP))
q->angle = 17 - q->angle; /* 17=540/(360/12)-1 */
else
q->angle = 11 - q->angle; /* 11=360/(360/12)-1 */
break;
case MOVE_DLL:
case MOVE_DDL:
case MOVE_DL: /* down-left (can hit floor or left wall) */
if (line_check (newx, newy, MOVE_DN)
&& line_check (newx, newy, MOVE_LT))
q->angle = q->angle - 6;
else if (line_check (newx, newy, MOVE_DN))
q->angle = 17 - q->angle; /* 17=540/(360/12)-1 */
else
q->angle = 11 - q->angle; /* 11=360/(360/12)-1 */
break;
case MOVE_DRR:
case MOVE_DDR:
case MOVE_DR: /* down-right (can hit floor or right wall) */
if (line_check (newx, newy, MOVE_DN)
&& line_check (newx, newy, MOVE_RT))
q->angle = q->angle + 6;
else if (line_check (newx, newy, MOVE_DN))
q->angle = 5 - q->angle; /* 5=180/(360/12)-1 */
else
q->angle = 11 - q->angle; /* 11=360/(360/12)-1 */
break;
}
q->x = newx;
q->y = newy;
} else if (nexthit == TRAIL) {
die();
}
}
/* 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;
}
j = percentage ();
if (j > 75) { /* finished level */
rb->splash (HZ * 2, "Level %d finished", player.level+1);
player.score += j;
if (player.level < MAX_LEVEL)
player.level++;
init_board ();
refresh_board ();
rb->splash (HZ * 2, "Ready?");
}
}
/* the main menu */
#define MAIN_MENU_SIZE 2
#define MENU_START 0
#define MENU_QUIT 1
static int game_menu (void)
{
static char menu[MAIN_MENU_SIZE][15] = {
"Start New Game",
"Quit"
};
int button, selection = 0, sw, sh, i;
bool quit = false;
rb->lcd_setfont(FONT_UI);
rb->lcd_getstringsize("A", &sw, &sh);
if(sw*20 > LCD_WIDTH || sh*4 > LCD_HEIGHT)
rb->lcd_setfont(FONT_SYSFIXED);
rb->lcd_getstringsize ("XOBOX", &sw, &sh);
sh++;
rb->lcd_set_background (LCD_WHITE);
rb->lcd_set_foreground (LCD_BLACK);
rb->lcd_clear_display ();
rb->lcd_putsxy (LCD_WIDTH / 2 - sw / 2, 2, "XOBOX");
while (!quit) {
for (i = 0; i < MAIN_MENU_SIZE; i++) {
rb->lcd_set_foreground ((i == selection ? LCD_WHITE : LCD_BLACK));
rb->lcd_set_background ((i == selection ? CLR_BLUE : LCD_WHITE));
rb->lcd_putsxy (10, sh + 4 + i * sh, menu[i]);
}
rb->lcd_update ();
button = rb->button_get (true);
switch (button) {
#ifdef MENU_UP
case MENU_UP:
#else
case UP:
#endif
selection = (selection + MAIN_MENU_SIZE - 1) % MAIN_MENU_SIZE;
break;
#ifdef MENU_UP
case MENU_DOWN:
#else
case DOWN:
#endif
selection = (selection + 1) % MAIN_MENU_SIZE;
break;
case SELECT:
case RIGHT:
quit = true;
break;
#ifdef RC_QUIT
case RC_QUIT:
#endif
case QUIT:
selection = MENU_QUIT;
quit = true;
break;
}
}
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 + (CYCLETIME * HZ) / 1000;
#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;
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);
rb->lcd_set_backdrop(NULL);
/* Permanently enable the backlight (unless the user has turned it off) */
if (rb->global_settings->backlight_timeout > 0)
rb->backlight_set_timeout (1);
randomize ();
if (game_menu () == MENU_START) {
init_game ();
ret = xobox_loop ();
}
rb->backlight_set_timeout (rb->global_settings->backlight_timeout);
rb->lcd_setfont (FONT_UI);
return ret;
}