rockbox/apps/plugins/xobox.c

1293 lines
35 KiB
C
Raw Normal View History

/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2006 Eli Sherer
* 2007 Antoine Cellerier
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "plugin.h"
#include "lib/helper.h"
#include "lib/playback_control.h"
#include "lib/highscore.h"
#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 == SANSA_CLIP_PAD)
#define QUIT BUTTON_POWER
#define LEFT BUTTON_LEFT
#define RIGHT BUTTON_RIGHT
#define UP BUTTON_UP
#define DOWN BUTTON_DOWN
#define PAUSE BUTTON_HOME
#elif (CONFIG_KEYPAD == SANSA_FUZE_PAD)
#define QUIT (BUTTON_HOME|BUTTON_REPEAT)
#define LEFT BUTTON_LEFT
#define RIGHT BUTTON_RIGHT
#define UP BUTTON_UP
#define DOWN BUTTON_DOWN
#define PAUSE BUTTON_SELECT
#elif (CONFIG_KEYPAD == SANSA_M200_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
#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 == COWON_D2_PAD
#define QUIT BUTTON_POWER
#elif CONFIG_KEYPAD == IAUDIO67_PAD
#define QUIT BUTTON_POWER
#define LEFT BUTTON_LEFT
#define RIGHT BUTTON_RIGHT
#define UP BUTTON_STOP
#define DOWN BUTTON_PLAY
#define PAUSE BUTTON_MENU
#elif CONFIG_KEYPAD == CREATIVEZVM_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 == CREATIVE_ZENXFI3_PAD
#define QUIT BUTTON_POWER
#define LEFT BUTTON_BACK
#define RIGHT BUTTON_MENU
#define UP BUTTON_UP
#define DOWN BUTTON_DOWN
#define PAUSE BUTTON_PLAY
#elif CONFIG_KEYPAD == PHILIPS_HDD1630_PAD
#define QUIT BUTTON_POWER
#define LEFT BUTTON_LEFT
#define RIGHT BUTTON_RIGHT
#define UP BUTTON_UP
#define DOWN BUTTON_DOWN
#define PAUSE BUTTON_VIEW
#elif CONFIG_KEYPAD == PHILIPS_HDD6330_PAD
#define QUIT BUTTON_POWER
#define LEFT BUTTON_PREV
#define RIGHT BUTTON_NEXT
#define UP BUTTON_UP
#define DOWN BUTTON_DOWN
#define PAUSE BUTTON_MENU
#elif CONFIG_KEYPAD == PHILIPS_SA9200_PAD
#define QUIT BUTTON_POWER
#define LEFT BUTTON_PREV
#define RIGHT BUTTON_NEXT
#define UP BUTTON_UP
#define DOWN BUTTON_DOWN
#define PAUSE BUTTON_MENU
#elif CONFIG_KEYPAD == ONDAVX747_PAD || \
CONFIG_KEYPAD == ONDAVX777_PAD || \
CONFIG_KEYPAD == MROBE500_PAD
#define QUIT BUTTON_POWER
#elif CONFIG_KEYPAD == SAMSUNG_YH820_PAD || \
CONFIG_KEYPAD == SAMSUNG_YH92X_PAD
#define QUIT BUTTON_REW
#define LEFT BUTTON_LEFT
#define RIGHT BUTTON_RIGHT
#define UP BUTTON_UP
#define DOWN BUTTON_DOWN
#define PAUSE BUTTON_PLAY
#elif CONFIG_KEYPAD == PBELL_VIBE500_PAD
#define QUIT BUTTON_REC
#define LEFT BUTTON_PREV
#define RIGHT BUTTON_NEXT
#define UP BUTTON_UP
#define DOWN BUTTON_DOWN
#define PAUSE BUTTON_PLAY
#elif CONFIG_KEYPAD == MPIO_HD200_PAD
#define QUIT (BUTTON_REC|BUTTON_PLAY)
#define LEFT BUTTON_VOL_DOWN
#define RIGHT BUTTON_VOL_UP
#define UP BUTTON_REW
#define DOWN BUTTON_FF
#define PAUSE BUTTON_PLAY
#elif CONFIG_KEYPAD == MPIO_HD300_PAD
#define QUIT (BUTTON_MENU | BUTTON_REPEAT)
#define LEFT BUTTON_REW
#define RIGHT BUTTON_FF
#define UP BUTTON_UP
#define DOWN BUTTON_DOWN
#define PAUSE BUTTON_PLAY
#elif CONFIG_KEYPAD == SANSA_FUZEPLUS_PAD
#define QUIT BUTTON_POWER
#define LEFT BUTTON_LEFT
#define RIGHT BUTTON_RIGHT
#define UP BUTTON_UP
#define DOWN BUTTON_DOWN
#define PAUSE BUTTON_PLAYPAUSE
#elif CONFIG_KEYPAD == SANSA_CONNECT_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
#elif (CONFIG_KEYPAD == SAMSUNG_YPR0_PAD)
#define QUIT BUTTON_BACK
#define LEFT BUTTON_LEFT
#define RIGHT BUTTON_RIGHT
#define UP BUTTON_UP
#define DOWN BUTTON_DOWN
#define PAUSE BUTTON_SELECT
#elif (CONFIG_KEYPAD == HM60X_PAD) || \
(CONFIG_KEYPAD == HM801_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
#elif CONFIG_KEYPAD == SONY_NWZ_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 == CREATIVE_ZEN_PAD
#define QUIT BUTTON_BACK
#define LEFT BUTTON_LEFT
#define RIGHT BUTTON_RIGHT
#define UP BUTTON_UP
#define DOWN BUTTON_DOWN
#define PAUSE BUTTON_PLAYPAUSE
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 QUIT BUTTON_POWER
#define LEFT BUTTON_LEFT
#define RIGHT BUTTON_RIGHT
#define UP BUTTON_VOL_UP
#define DOWN BUTTON_VOL_DOWN
#define PAUSE BUTTON_PLAY
#else
#error No keymap defined!
#endif
#ifdef HAVE_TOUCHSCREEN
#ifndef QUIT
#define QUIT BUTTON_TOPLEFT
#endif
#ifndef LEFT
#define LEFT BUTTON_MIDLEFT
#endif
#ifndef RIGHT
#define RIGHT BUTTON_MIDRIGHT
#endif
#ifndef UP
#define UP BUTTON_TOPMIDDLE
#endif
#ifndef DOWN
#define DOWN BUTTON_BOTTOMMIDDLE
#endif
#ifndef PAUSE
#define PAUSE BUTTON_CENTER
#endif
#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
/* 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 bool quit = false;
static bool _ingame = false;
#define RESUME_FILE PLUGIN_GAMES_DATA_DIR "/xobox.resume"
#define SCORE_FILE PLUGIN_GAMES_DATA_DIR "/xobox.score"
#define NUM_SCORES 5
static struct highscore highscores[NUM_SCORES];
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;
int x;
#if LCD_DEPTH>=2
rb->lcd_set_background (LCD_BLACK);
#endif
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] )
{
#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->lcd_putsxyf (BOARD_X, BOARD_Y, "Level %d", player.level + 1);
rb->lcd_putsxyf (BOARD_X + CUBE_SIZE * BOARD_W - 24, BOARD_Y, "%d%%",
percentage_cache);
rb->lcd_putsxyf (BOARD_X, BOARD_Y + CUBE_SIZE * BOARD_H - 8, "Score: %d",
player.score);
#if LCD_DEPTH>=2
x = BOARD_X + CUBE_SIZE * BOARD_W - 60;
#else
x = BOARD_X + CUBE_SIZE * BOARD_W - 40;
#endif
rb->lcd_putsxyf (x, BOARD_Y + CUBE_SIZE * BOARD_H - 8,
(player.lives != 1) ? "%d Lives" : "%d Life", player.lives);
#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 */
refresh_board ();
rb->splashf (HZ * 2, "Level %d finished", player.level+1);
player.score += percentage_cache;
if (player.level < MAX_LEVEL)
player.level++;
init_board ();
refresh_board ();
rb->button_clear_queue();
rb->splash (HZ * 2, "Ready?");
}
}
/* init game's variables */
static void init_game (void)
{
player.level = 0;
player.score = 0;
player.lives = 3;
player.gameover = false;
player.drawing = false;
init_board ();
refresh_board ();
rb->splash (HZ * 2, "Ready?");
}
static bool load_game(void)
{
int fd = rb->open(RESUME_FILE, O_RDONLY);
if (fd < 0) {
return true;
}
bool load_success =
rb->read(fd, &player, sizeof(player)) == sizeof(player) &&
rb->read(fd, &qixes, sizeof(qixes)) == sizeof(qixes) &&
rb->read(fd, &stack, sizeof(stack)) == sizeof(stack) &&
rb->read(fd, &board, sizeof(board)) == sizeof(board) &&
rb->read(fd, &testboard, sizeof(testboard)) == sizeof(testboard) &&
rb->read(fd, &speed, sizeof(speed)) == sizeof(speed) &&
rb->read(fd, &difficulty, sizeof(difficulty)) == sizeof(difficulty) &&
rb->read(fd, &stackPointer, sizeof(stackPointer)) == sizeof(stackPointer) &&
rb->read(fd, &percentage_cache,
sizeof(percentage_cache)) == sizeof(percentage_cache);
rb->close(fd);
_ingame = load_success;
return load_success;
}
static bool save_game(void)
{
int fd = rb->open(RESUME_FILE, O_WRONLY|O_CREAT, 0666);
if (fd < 0) {
return false;
}
bool save_success =
rb->write(fd, &player, sizeof(player)) > 0 &&
rb->write(fd, &qixes, sizeof(qixes)) > 0 &&
rb->write(fd, &stack, sizeof(stack)) > 0 &&
rb->write(fd, &board, sizeof(board)) > 0 &&
rb->write(fd, &testboard, sizeof(testboard)) > 0 &&
rb->write(fd, &speed, sizeof(speed)) > 0 &&
rb->write(fd, &difficulty, sizeof(difficulty)) > 0 &&
rb->write(fd, &stackPointer, sizeof(stackPointer)) > 0 &&
rb->write(fd, &percentage_cache, sizeof(percentage_cache)) > 0;
rb->close(fd);
if (!save_success) {
rb->remove(RESUME_FILE);
}
return save_success;
}
/* the main menu */
static int xobox_menu_cb(int action, const struct menu_item_ex *this_item)
{
intptr_t item = (intptr_t)this_item;
if(action == ACTION_REQUEST_MENUITEM
&& !_ingame && (item == 0 || item == 6))
return ACTION_EXIT_MENUITEM;
return action;
}
static int xobox_menu(bool ingame)
{
rb->button_clear_queue();
int selection = 0;
MENUITEM_STRINGLIST(main_menu, "Xobox Menu", xobox_menu_cb,
"Resume Game", "Start New Game",
"Speed", "Difficulty",
"High Scores", "Playback Control",
"Quit Without Saving", "Quit");
_ingame = ingame;
while (true) {
switch (rb->do_menu(&main_menu, &selection, NULL, false)) {
case 0:
rb->remove(RESUME_FILE);
refresh_board();
rb->splash (HZ*2, "Ready?");
return 0;
case 1:
init_game ();
return 0;
case 2:
rb->set_int ("Speed", "", UNIT_INT, &speed, NULL, 1, 1, 10, NULL);
break;
case 3:
rb->set_int ("Difficulty", "", UNIT_INT, &difficulty, NULL,
5, 50, 95, NULL);
break;
case 4:
highscore_show(-1, highscores, NUM_SCORES, true);
break;
case 5:
playback_control(NULL);
break;
case 6:
return 1;
case 7:
if (_ingame) {
rb->splash(HZ, "Saving game...");
if (!save_game()) {
rb->splash(HZ, "Failed to save game");
}
}
return 1;
case MENU_ATTACHED_USB:
return 1;
default:
break;
}
}
}
/* general keypad handler loop */
static int xobox_loop (void)
{
int button = 0;
bool pause = false;
int end;
if (xobox_menu(_ingame)) {
return PLUGIN_OK;
}
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 (1);
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:
if (!pause) {
if (xobox_menu(true)) {
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!");
int pos = highscore_update(player.score, player.level, "",
highscores, NUM_SCORES);
if (pos != -1) {
if (pos == 0) {
rb->splashf(HZ, "New High Score: %d", player.score);
}
highscore_show(pos, highscores, NUM_SCORES, true);
}
if (xobox_menu(false)) {
quit = true;
}
}
if (TIME_BEFORE(*rb->current_tick, end))
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 (const void *parameter)
{
int ret = PLUGIN_OK;
(void) parameter;
rb->lcd_setfont (FONT_SYSFIXED);
#if LCD_DEPTH>=2
rb->lcd_set_backdrop(NULL);
#endif
/* Turn off backlight timeout */
backlight_ignore_timeout();
highscore_load(SCORE_FILE, highscores, NUM_SCORES);
if (!load_game()) {
rb->splash(HZ, "Failed to load saved game");
}
randomize ();
ret = xobox_loop ();
/* Turn on backlight timeout (revert to settings) */
backlight_use_settings();
rb->lcd_setfont (FONT_UI);
highscore_save(SCORE_FILE, highscores, NUM_SCORES);
return ret;
}