rockbox/apps/plugins/chopper.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

1133 lines
26 KiB
C

/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Originally by Joshua Oreman, improved by Prashant Varanasi
* Ported to Rockbox by Ben Basha (Paprica)
*
* 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/xlcd.h"
#include "lib/configfile.h"
#include "lib/helper.h"
#include "lib/playback_control.h"
/*
Still To do:
- Make original speed and further increases in speed depend more on screen size
- attempt to make the tunnels get narrower as the game goes on
- make the chopper look better, maybe a picture, and scale according
to screen size
- use textures for the color screens for background and terrain,
eg stars on background
- allow choice of different levels [later: different screen themes]
- better high score handling, improved screen etc.
*/
#if (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD)
#define QUIT BUTTON_OFF
#define ACTION BUTTON_UP
#define ACTION2 BUTTON_SELECT
#define ACTIONTEXT "SELECT"
#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
(CONFIG_KEYPAD == IPOD_3G_PAD) || \
(CONFIG_KEYPAD == IPOD_1G2G_PAD)
#define QUIT BUTTON_MENU
#define ACTION BUTTON_SELECT
#define ACTIONTEXT "SELECT"
#elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD /* grayscale at the moment */
#define QUIT BUTTON_POWER
#define ACTION BUTTON_UP
#define ACTION2 BUTTON_SELECT
#define ACTIONTEXT "SELECT"
#elif CONFIG_KEYPAD == IRIVER_H10_PAD
#define QUIT BUTTON_POWER
#define ACTION BUTTON_RIGHT
#define ACTIONTEXT "RIGHT"
#elif (CONFIG_KEYPAD == SANSA_E200_PAD) || \
(CONFIG_KEYPAD == SANSA_C200_PAD) || \
(CONFIG_KEYPAD == SANSA_CLIP_PAD) || \
(CONFIG_KEYPAD == SANSA_M200_PAD) || \
(CONFIG_KEYPAD == SANSA_CONNECT_PAD)
#define QUIT BUTTON_POWER
#define ACTION BUTTON_SELECT
#define ACTIONTEXT "SELECT"
#elif (CONFIG_KEYPAD == SANSA_FUZE_PAD)
#define QUIT BUTTON_HOME
#define ACTION BUTTON_SELECT
#define ACTIONTEXT "SELECT"
#elif CONFIG_KEYPAD == GIGABEAT_PAD
#define QUIT BUTTON_MENU
#define ACTION BUTTON_SELECT
#define ACTIONTEXT "SELECT"
#elif CONFIG_KEYPAD == RECORDER_PAD
#define QUIT BUTTON_OFF
#define ACTION BUTTON_PLAY
#define ACTIONTEXT "PLAY"
#elif CONFIG_KEYPAD == ONDIO_PAD
#define QUIT BUTTON_OFF
#define ACTION BUTTON_UP
#define ACTION2 BUTTON_MENU
#define ACTIONTEXT "UP"
#elif CONFIG_KEYPAD == GIGABEAT_S_PAD \
|| CONFIG_KEYPAD == SAMSUNG_YPR0_PAD
#define QUIT BUTTON_BACK
#define ACTION BUTTON_SELECT
#define ACTION2 BUTTON_MENU
#define ACTIONTEXT "SELECT"
#elif CONFIG_KEYPAD == MROBE100_PAD
#define QUIT BUTTON_POWER
#define ACTION BUTTON_SELECT
#define ACTIONTEXT "SELECT"
#elif CONFIG_KEYPAD == IAUDIO_M3_PAD
#define QUIT BUTTON_RC_REC
#define ACTION BUTTON_RC_PLAY
#define ACTION2 BUTTON_RC_MODE
#define ACTIONTEXT "PLAY"
#elif CONFIG_KEYPAD == COWON_D2_PAD
#define QUIT BUTTON_POWER
#define ACTION2 BUTTON_PLUS
#elif CONFIG_KEYPAD == IAUDIO67_PAD
#define QUIT BUTTON_POWER
#define ACTION BUTTON_PLAY
#define ACTION2 BUTTON_STOP
#define ACTIONTEXT "PLAY"
#elif CONFIG_KEYPAD == CREATIVEZVM_PAD
#define QUIT BUTTON_BACK
#define ACTION BUTTON_UP
#define ACTION2 BUTTON_MENU
#define ACTIONTEXT "UP"
#elif CONFIG_KEYPAD == CREATIVE_ZENXFI3_PAD
#define QUIT BUTTON_POWER
#define ACTION BUTTON_UP
#define ACTION2 BUTTON_MENU
#define ACTIONTEXT "UP"
#elif CONFIG_KEYPAD == PHILIPS_HDD1630_PAD
#define QUIT BUTTON_POWER
#define ACTION BUTTON_MENU
#define ACTION2 BUTTON_SELECT
#define ACTIONTEXT "MENU"
#elif CONFIG_KEYPAD == PHILIPS_HDD6330_PAD
#define QUIT BUTTON_POWER
#define ACTION BUTTON_MENU
#define ACTION2 BUTTON_PLAY
#define ACTIONTEXT "MENU"
#elif CONFIG_KEYPAD == PHILIPS_SA9200_PAD
#define QUIT BUTTON_POWER
#define ACTION BUTTON_MENU
#define ACTION2 BUTTON_PLAY
#define ACTIONTEXT "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_LEFT
#define ACTION BUTTON_RIGHT
#define ACTION2 BUTTON_UP
#define ACTIONTEXT "RIGHT or UP"
#elif CONFIG_KEYPAD == PBELL_VIBE500_PAD
#define QUIT BUTTON_REC
#define ACTION BUTTON_PLAY
#define ACTION2 BUTTON_UP
#define ACTIONTEXT "PLAY"
#elif CONFIG_KEYPAD == MPIO_HD200_PAD
#define QUIT (BUTTON_REC|BUTTON_PLAY)
#define ACTION BUTTON_FUNC
#define ACTIONTEXT "FUNC"
#elif CONFIG_KEYPAD == MPIO_HD300_PAD
#define QUIT (BUTTON_MENU|BUTTON_REPEAT)
#define ACTION BUTTON_ENTER
#define ACTIONTEXT "ENTER"
#elif CONFIG_KEYPAD == SANSA_FUZEPLUS_PAD
#define QUIT BUTTON_POWER
#define ACTION BUTTON_SELECT
#define ACTIONTEXT "SELECT"
#elif (CONFIG_KEYPAD == HM60X_PAD) || \
(CONFIG_KEYPAD == HM801_PAD)
#define QUIT BUTTON_POWER
#define ACTION BUTTON_SELECT
#define ACTIONTEXT "SELECT"
#elif CONFIG_KEYPAD == SONY_NWZ_PAD
#define QUIT BUTTON_BACK
#define ACTION BUTTON_PLAY
#define ACTIONTEXT "PLAY"
#elif CONFIG_KEYPAD == CREATIVE_ZEN_PAD
#define QUIT BUTTON_BACK
#define ACTION BUTTON_SELECT
#define ACTIONTEXT "Select"
#elif CONFIG_KEYPAD == AGPTEK_ROCKER_PAD
#define QUIT BUTTON_POWER
#define ACTION BUTTON_SELECT
#define ACTIONTEXT "Select"
#elif CONFIG_KEYPAD == XDUOO_X3_PAD
#define QUIT BUTTON_POWER
#define ACTION BUTTON_PLAY
#define ACTIONTEXT "PLAY"
#elif CONFIG_KEYPAD == XDUOO_X3II_PAD
#define QUIT BUTTON_POWER
#define ACTION BUTTON_PLAY
#define ACTIONTEXT "PLAY"
#elif CONFIG_KEYPAD == XDUOO_X20_PAD
#define QUIT BUTTON_POWER
#define ACTION BUTTON_PLAY
#define ACTIONTEXT "PLAY"
#elif CONFIG_KEYPAD == IHIFI_770_PAD
#define QUIT BUTTON_POWER
#define ACTION BUTTON_PLAY
#define ACTIONTEXT "PLAY"
#elif CONFIG_KEYPAD == IHIFI_800_PAD
#define QUIT BUTTON_POWER
#define ACTION BUTTON_PLAY
#define ACTIONTEXT "PLAY"
#elif !defined(HAVE_TOUCHSCREEN)
#error No keymap defined!
#endif
#ifdef HAVE_TOUCHSCREEN
#ifndef QUIT
#define QUIT BUTTON_TOPLEFT
#endif
#ifndef ACTION
#define ACTION BUTTON_BOTTOMLEFT
#endif
#ifndef ACTION2
#define ACTION2 BUTTON_BOTTOMRIGHT
#endif
#ifndef ACTIONTEXT
#define ACTIONTEXT "BOTTOMRIGHT"
#endif
#endif
#define NUMBER_OF_BLOCKS 8
#define NUMBER_OF_PARTICLES 3
#define MAX_TERRAIN_NODES 15
#define LEVEL_MODE_NORMAL 0
#define LEVEL_MODE_STEEP 1
#if LCD_HEIGHT <= 64
#define CYCLES 100
static inline int SCALE(int x)
{
return x == 1 ? x : x >> 1;
}
#define SIZE 2
#else
#define CYCLES 60
#define SCALE(x) (x)
#define SIZE 1
#endif
/* in 10 milisecond (ticks) */
#define CYCLETIME ((CYCLES*HZ)/1000)
/*Chopper's local variables to track the terrain position etc*/
static int chopCounter;
static int iRotorOffset;
static int iScreenX;
static int iScreenY;
static int iPlayerPosX;
static int iPlayerPosY;
static int iCameraPosX;
static int iPlayerSpeedX;
static int iPlayerSpeedY;
static int iLastBlockPlacedPosX;
static int iGravityTimerCountdown;
static int iPlayerAlive;
static int iLevelMode, iCurrLevelMode;
static int blockh,blockw;
static int highscore;
static int score;
#define CFG_FILE "chopper.cfg"
#define MAX_POINTS 50000
static struct configdata config[] =
{
{TYPE_INT, 0, MAX_POINTS, { .int_p = &highscore }, "highscore", NULL}
};
struct CBlock
{
int iWorldX;
int iWorldY;
int iSizeX;
int iSizeY;
int bIsActive;
};
struct CParticle
{
int iWorldX;
int iWorldY;
int iSpeedX;
int iSpeedY;
int bIsActive;
};
struct CTerrainNode
{
int x;
int y;
};
struct CTerrain
{
struct CTerrainNode mNodes[MAX_TERRAIN_NODES];
int iNodesCount;
int iLastNodePlacedPosX;
};
struct CBlock mBlocks[NUMBER_OF_BLOCKS];
struct CParticle mParticles[NUMBER_OF_PARTICLES];
struct CTerrain mGround;
struct CTerrain mRoof;
/*Function declarations*/
static void chopDrawParticle(struct CParticle *mParticle);
static void chopDrawBlock(struct CBlock *mBlock);
static void chopRenderTerrain(struct CTerrain *ter, bool isground);
static void chopper_load(bool newgame);
static void chopDrawPlayer(int x,int y) /* These are SCREEN coords, not world!*/
{
#if LCD_DEPTH > 2
rb->lcd_set_foreground(LCD_RGBPACK(50,50,200));
#elif LCD_DEPTH == 2
rb->lcd_set_foreground(LCD_DARKGRAY);
#endif
rb->lcd_fillrect(SCALE(x+6), SCALE(y+2), SCALE(12), SCALE(9));
rb->lcd_fillrect(SCALE(x-3), SCALE(y+6), SCALE(20), SCALE(3));
#if LCD_DEPTH > 2
rb->lcd_set_foreground(LCD_RGBPACK(50,50,50));
#elif LCD_DEPTH == 2
rb->lcd_set_foreground(LCD_DARKGRAY);
#endif
rb->lcd_fillrect(SCALE(x+10), SCALE(y), SCALE(2), SCALE(3));
rb->lcd_fillrect(SCALE(x+10), SCALE(y), SCALE(1), SCALE(3));
#if LCD_DEPTH > 2
rb->lcd_set_foreground(LCD_RGBPACK(40,40,100));
#elif LCD_DEPTH == 2
rb->lcd_set_foreground(LCD_BLACK);
#endif
rb->lcd_drawline(SCALE(x), SCALE(y+iRotorOffset), SCALE(x+20),
SCALE(y-iRotorOffset));
#if LCD_DEPTH > 2
rb->lcd_set_foreground(LCD_RGBPACK(20,20,50));
#elif LCD_DEPTH == 2
rb->lcd_set_foreground(LCD_BLACK);
#endif
rb->lcd_fillrect(SCALE(x - 2), SCALE(y + 5), SCALE(2), SCALE(5));
}
static void chopClearTerrain(struct CTerrain *ter)
{
ter->iNodesCount = 0;
}
static int iR(int low,int high)
{
return low+rb->rand()%(high-low+1);
}
static void chopCopyTerrain(struct CTerrain *src, struct CTerrain *dest,
int xOffset,int yOffset)
{
int i=0;
while(i < src->iNodesCount)
{
dest->mNodes[i].x = src->mNodes[i].x + xOffset;
dest->mNodes[i].y = src->mNodes[i].y + yOffset;
i++;
}
dest->iNodesCount = src->iNodesCount;
dest->iLastNodePlacedPosX = src->iLastNodePlacedPosX;
}
static void chopAddTerrainNode(struct CTerrain *ter, int x, int y)
{
int i=0;
if(ter->iNodesCount + 1 >= MAX_TERRAIN_NODES)
{
/* DEBUGF("ERROR: Not enough nodes!\n"); */
return;
}
ter->iNodesCount++;
i = ter->iNodesCount - 1;
ter->mNodes[i].x = x;
ter->mNodes[i].y= y;
ter->iLastNodePlacedPosX = x;
}
static void chopTerrainNodeDeleteAndShift(struct CTerrain *ter,int nodeIndex)
{
int i=nodeIndex;
while( i < ter->iNodesCount )
{
ter->mNodes[i - 1] = ter->mNodes[i];
i++;
}
ter->iNodesCount--;
}
static int chopUpdateTerrainRecycling(struct CTerrain *ter)
{
int i=1;
int iNewNodePos,g,v;
while(i < ter->iNodesCount)
{
if( iCameraPosX > ter->mNodes[i].x)
{
chopTerrainNodeDeleteAndShift(ter,i);
iNewNodePos = ter->iLastNodePlacedPosX + 50;
g = iScreenY - 10;
v = 3*iPlayerSpeedX;
if(v>50)
v=50;
if(iCurrLevelMode == LEVEL_MODE_STEEP)
v*=5;
chopAddTerrainNode(ter,iNewNodePos,g - iR(-v,v));
}
i++;
}
return 1;
}
static int chopTerrainHeightAtPoint(struct CTerrain *ter, int pX)
{
int iNodeIndexOne=0,iNodeIndexTwo=0, h, terY1, terY2, terX2, a, b;
float c,d;
int i=0;
for(i=1;i<MAX_TERRAIN_NODES;i++)
{
if(ter->mNodes[i].x > pX)
{
iNodeIndexOne = i - 1;
break;
}
}
iNodeIndexTwo = iNodeIndexOne + 1;
terY1 = ter->mNodes[iNodeIndexOne].y;
terY2 = ter->mNodes[iNodeIndexTwo].y;
/* terX1 = 0; */
terX2 = ter->mNodes[iNodeIndexTwo].x - ter->mNodes[iNodeIndexOne].x;
pX-= ter->mNodes[iNodeIndexOne].x;
a = terY2 - terY1;
b = terX2;
c = pX;
d = (c/b) * a;
h = d + terY1;
return h;
}
static int chopPointInTerrain(struct CTerrain *ter, int pX, int pY, int iTestType)
{
int h = chopTerrainHeightAtPoint(ter, pX);
if(iTestType == 0)
return (pY > h);
else
return (pY < h);
}
static void chopAddBlock(int x,int y,int sx,int sy, int indexOverride)
{
int i=0;
if(indexOverride < 0)
{
while(mBlocks[i].bIsActive && i < NUMBER_OF_BLOCKS)
i++;
if(i==NUMBER_OF_BLOCKS)
{
DEBUGF("No blocks!\n");
return;
}
}
else
i = indexOverride;
mBlocks[i].bIsActive = 1;
mBlocks[i].iWorldX = x;
mBlocks[i].iWorldY = y;
mBlocks[i].iSizeX = sx;
mBlocks[i].iSizeY = sy;
iLastBlockPlacedPosX = x;
}
static void chopAddParticle(int x,int y,int sx,int sy)
{
int i=0;
while(mParticles[i].bIsActive && i < NUMBER_OF_PARTICLES)
i++;
if(i==NUMBER_OF_PARTICLES)
return;
mParticles[i].bIsActive = 1;
mParticles[i].iWorldX = x;
mParticles[i].iWorldY = y;
mParticles[i].iSpeedX = sx;
mParticles[i].iSpeedY = sy;
}
static void chopGenerateBlockIfNeeded(void)
{
int i=0;
int DistSpeedX = iPlayerSpeedX * 5;
if(DistSpeedX<200) DistSpeedX = 200;
while(i < NUMBER_OF_BLOCKS)
{
if(!mBlocks[i].bIsActive)
{
int iX,iY,sX,sY;
iX = iLastBlockPlacedPosX + (350-DistSpeedX);
sX = blockw;
iY = iR(0,iScreenY);
sY = blockh + iR(1,blockh/3);
chopAddBlock(iX,iY,sX,sY,i);
}
i++;
}
}
static int chopBlockCollideWithPlayer(struct CBlock *mBlock)
{
int px = iPlayerPosX;
int py = iPlayerPosY;
int x = mBlock->iWorldX-17;
int y = mBlock->iWorldY-11;
int x2 = x + mBlock->iSizeX+17;
int y2 = y + mBlock->iSizeY+11;
if(px>x && px<x2)
{
if(py>y && py<y2)
{
return 1;
}
}
return 0;
}
static int chopBlockOffscreen(struct CBlock *mBlock)
{
if(mBlock->iWorldX + mBlock->iSizeX < iCameraPosX)
return 1;
else
return 0;
}
static int chopParticleOffscreen(struct CParticle *mParticle)
{
if (mParticle->iWorldX < iCameraPosX || mParticle->iWorldY < 0 ||
mParticle->iWorldY > iScreenY || mParticle->iWorldX > iCameraPosX +
iScreenX)
{
return 1;
}
else
return 0;
}
static void chopKillPlayer(void)
{
int i;
for (i = 0; i < NUMBER_OF_PARTICLES; i++) {
mParticles[i].bIsActive = 0;
chopAddParticle(iPlayerPosX + iR(0,20), iPlayerPosY + iR(0,20),
iR(-2,2), iR(-2,2));
}
iPlayerAlive--;
if (iPlayerAlive == 0) {
rb->splash(HZ, "Game Over");
if (score > highscore) {
char scoretext[30];
highscore = score;
rb->snprintf(scoretext, sizeof(scoretext), "New High Score: %d",
highscore);
rb->splash(HZ*2, scoretext);
}
} else
chopper_load(false);
}
static void chopDrawTheWorld(void)
{
int i=0;
while(i < NUMBER_OF_BLOCKS)
{
if(mBlocks[i].bIsActive)
{
if(chopBlockOffscreen(&mBlocks[i]) == 1)
mBlocks[i].bIsActive = 0;
else
chopDrawBlock(&mBlocks[i]);
}
i++;
}
i=0;
while(i < NUMBER_OF_PARTICLES)
{
if(mParticles[i].bIsActive)
{
if(chopParticleOffscreen(&mParticles[i]) == 1)
mParticles[i].bIsActive = 0;
else
chopDrawParticle(&mParticles[i]);
}
i++;
}
chopRenderTerrain(&mGround, true);
chopRenderTerrain(&mRoof, false);
}
static void chopDrawParticle(struct CParticle *mParticle)
{
int iPosX = (mParticle->iWorldX - iCameraPosX);
int iPosY = (mParticle->iWorldY);
#if LCD_DEPTH > 2
rb->lcd_set_foreground(LCD_RGBPACK(192,192,192));
#elif LCD_DEPTH == 2
rb->lcd_set_foreground(LCD_LIGHTGRAY);
#endif
rb->lcd_fillrect(SCALE(iPosX), SCALE(iPosY), SCALE(3), SCALE(3));
}
static void chopDrawScene(void)
{
char s[30];
int w;
#if LCD_DEPTH > 2
rb->lcd_set_background(LCD_BLACK);
#elif LCD_DEPTH == 2
rb->lcd_set_background(LCD_WHITE);
#endif
rb->lcd_clear_display();
chopDrawTheWorld();
chopDrawPlayer(iPlayerPosX - iCameraPosX, iPlayerPosY);
score = -20 + iPlayerPosX/3;
#if LCD_DEPTH == 1
rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
#else
rb->lcd_set_drawmode(DRMODE_FG);
#endif
#if LCD_DEPTH > 2
rb->lcd_set_foreground(LCD_BLACK);
#elif LCD_DEPTH == 2
rb->lcd_set_foreground(LCD_WHITE);
#endif
#if LCD_WIDTH <= 128
rb->snprintf(s, sizeof(s), "Dist: %d", score);
#else
rb->snprintf(s, sizeof(s), "Distance: %d", score);
#endif
rb->lcd_getstringsize(s, &w, NULL);
rb->lcd_putsxy(2, 2, s);
if (score < highscore)
{
int w2;
#if LCD_WIDTH <= 128
rb->snprintf(s, sizeof(s), "Hi: %d", highscore);
#else
rb->snprintf(s, sizeof(s), "Best: %d", highscore);
#endif
rb->lcd_getstringsize(s, &w2, NULL);
if (LCD_WIDTH - 2 - w2 > w + 2)
rb->lcd_putsxy(LCD_WIDTH - 2 - w2, 2, s);
}
rb->lcd_set_drawmode(DRMODE_SOLID);
rb->lcd_update();
}
static bool _ingame;
static int chopMenuCb(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 chopMenu(int menunum)
{
int result = 0;
int res = 0;
bool menu_quit = false;
static const struct opt_items levels[2] = {
{ "Normal", -1 },
{ "Steep", -1 },
};
MENUITEM_STRINGLIST(menu,"Chopper Menu",chopMenuCb,
"Resume Game","Start New Game",
"Level","Playback Control","Quit");
_ingame = (menunum!=0);
#ifdef HAVE_LCD_COLOR
rb->lcd_set_foreground(LCD_WHITE);
rb->lcd_set_background(LCD_BLACK);
#elif LCD_DEPTH == 2
rb->lcd_set_foreground(LCD_BLACK);
rb->lcd_set_background(LCD_WHITE);
#endif
rb->lcd_clear_display();
rb->button_clear_queue();
while (!menu_quit) {
switch(rb->do_menu(&menu, &result, NULL, false))
{
case 0: /* Resume Game */
menu_quit=true;
res = -1;
break;
case 1: /* Start New Game */
menu_quit=true;
chopper_load(true);
res = -1;
break;
case 2:
rb->set_option("Level", &iLevelMode, INT, levels, 2, NULL);
break;
case 3:
playback_control(NULL);
break;
case 4:
menu_quit=true;
res = PLUGIN_OK;
break;
case MENU_ATTACHED_USB:
menu_quit=true;
res = PLUGIN_USB_CONNECTED;
break;
}
}
rb->lcd_clear_display();
return res;
}
static int chopGameLoop(void)
{
int move_button, ret;
bool exit=false;
bool showsplash=true;
int end, i=0, bdelay=0, last_button=BUTTON_NONE;
if (chopUpdateTerrainRecycling(&mGround) == 1)
/* mirror the sky if we've changed the ground */
chopCopyTerrain(&mGround, &mRoof, 0, - ( (iScreenY * 3) / 4));
ret = chopMenu(0);
if (ret != -1)
return PLUGIN_OK;
while (!exit) {
end = *rb->current_tick + CYCLETIME;
if(chopUpdateTerrainRecycling(&mGround) == 1)
/* mirror the sky if we've changed the ground */
chopCopyTerrain(&mGround, &mRoof, 0, - ( (iScreenY * 3) / 4));
iRotorOffset = iR(-1,1);
/* We need to have this here so particles move when we're dead */
for (i=0; i < NUMBER_OF_PARTICLES; i++)
if(mParticles[i].bIsActive == 1)
{
mParticles[i].iWorldX += mParticles[i].iSpeedX;
mParticles[i].iWorldY += mParticles[i].iSpeedY;
}
/* Redraw the main window: */
chopDrawScene();
iGravityTimerCountdown--;
if(iGravityTimerCountdown <= 0)
{
iGravityTimerCountdown = 3;
chopAddParticle(iPlayerPosX, iPlayerPosY+5, 0, 0);
}
if(iCurrLevelMode == LEVEL_MODE_NORMAL)
chopGenerateBlockIfNeeded();
if (showsplash) {
chopDrawScene();
rb->splash(HZ, "Get Ready!");
showsplash = false;
}
move_button=rb->button_status();
if (rb->button_get(false) == QUIT) {
ret = chopMenu(1);
if (ret != -1)
return PLUGIN_OK;
showsplash = true;
bdelay = 0;
last_button = BUTTON_NONE;
move_button = BUTTON_NONE;
}
switch (move_button) {
case ACTION:
#ifdef ACTION2
case ACTION2:
#endif
if (last_button != ACTION
#ifdef ACTION2
&& last_button != ACTION2
#endif
)
bdelay = -2;
if (bdelay == 0)
iPlayerSpeedY = -3;
break;
default:
if (last_button == ACTION
#ifdef ACTION2
|| last_button == ACTION2
#endif
)
bdelay = 3;
if (bdelay == 0)
iPlayerSpeedY = 4;
if (rb->default_event_handler(move_button) == SYS_USB_CONNECTED)
return PLUGIN_USB_CONNECTED;
break;
}
last_button = move_button;
if (bdelay < 0) {
iPlayerSpeedY = bdelay;
bdelay++;
} else if (bdelay > 0) {
iPlayerSpeedY = bdelay;
bdelay--;
}
iCameraPosX = iPlayerPosX - 25;
iPlayerPosX += iPlayerSpeedX;
iPlayerPosY += iPlayerSpeedY;
chopCounter++;
/* increase speed as we go along */
if (chopCounter == 100){
iPlayerSpeedX++;
chopCounter=0;
}
if (iPlayerPosY > iScreenY-10 || iPlayerPosY < -5 ||
chopPointInTerrain(&mGround, iPlayerPosX, iPlayerPosY + 10, 0) ||
chopPointInTerrain(&mRoof, iPlayerPosX ,iPlayerPosY, 1))
{
chopKillPlayer();
chopDrawScene();
ret = chopMenu(0);
if (ret != -1)
return ret;
showsplash = true;
}
for (i=0; i < NUMBER_OF_BLOCKS; i++)
if(mBlocks[i].bIsActive == 1)
if(chopBlockCollideWithPlayer(&mBlocks[i])) {
chopKillPlayer();
chopDrawScene();
ret = chopMenu(0);
if (ret != -1)
return ret;
showsplash = true;
}
if (TIME_BEFORE(*rb->current_tick, end))
rb->sleep(end - *rb->current_tick); /* wait until time is over */
else
rb->yield();
}
return PLUGIN_OK;
}
static void chopDrawBlock(struct CBlock *mBlock)
{
int iPosX = (mBlock->iWorldX - iCameraPosX);
int iPosY = (mBlock->iWorldY);
#if LCD_DEPTH > 2
rb->lcd_set_foreground(LCD_RGBPACK(100,255,100));
#elif LCD_DEPTH == 2
rb->lcd_set_foreground(LCD_BLACK);
#endif
rb->lcd_fillrect(SCALE(iPosX), SCALE(iPosY), SCALE(mBlock->iSizeX),
SCALE(mBlock->iSizeY));
}
static void chopRenderTerrain(struct CTerrain *ter, bool isground)
{
int i = 1;
int oldx = 0;
while(i < ter->iNodesCount && oldx < iScreenX)
{
int x = ter->mNodes[i-1].x - iCameraPosX;
int y = ter->mNodes[i-1].y;
int x2 = ter->mNodes[i].x - iCameraPosX;
int y2 = ter->mNodes[i].y;
int ax, ay;
if ((y < y2) != isground)
{
ax = x2;
ay = y;
}
else
{
ax = x;
ay = y2;
}
#if LCD_DEPTH > 2
rb->lcd_set_foreground(LCD_RGBPACK(100,255,100));
#elif LCD_DEPTH == 2
rb->lcd_set_foreground(LCD_DARKGRAY);
#endif
rb->lcd_drawline(SCALE(x), SCALE(y), SCALE(x2), SCALE(y2));
xlcd_filltriangle(SCALE(x), SCALE(y), SCALE(x2), SCALE(y2),
SCALE(ax), SCALE(ay));
if (isground)
{
y = ay;
y2 = (LCD_HEIGHT*SIZE);
}
else
{
y = 0;
y2 = ay;
}
if (y2-y > 0)
rb->lcd_fillrect(SCALE(x), SCALE(y), SCALE(x2-x)+1, SCALE(y2-y)+1);
oldx = x;
i++;
}
}
static void chopper_load(bool newgame)
{
int i;
int g;
if (newgame) {
iScreenX = LCD_WIDTH * SIZE;
iScreenY = LCD_HEIGHT * SIZE;
blockh = iScreenY / 5;
blockw = iScreenX / 20;
iPlayerAlive = 1;
iCurrLevelMode = iLevelMode;
score = 0;
}
iRotorOffset = 0;
iPlayerPosX = 60;
iPlayerPosY = (iScreenY * 4) / 10;
iLastBlockPlacedPosX = 0;
iGravityTimerCountdown = 2;
chopCounter = 0;
iPlayerSpeedX = 3;
iPlayerSpeedY = 0;
iCameraPosX = 30;
for (i=0; i < NUMBER_OF_PARTICLES; i++)
mParticles[i].bIsActive = 0;
for (i=0; i < NUMBER_OF_BLOCKS; i++)
mBlocks[i].bIsActive = 0;
g = iScreenY - 10;
chopClearTerrain(&mGround);
for (i=0; i < MAX_TERRAIN_NODES; i++)
chopAddTerrainNode(&mGround,i * 30,g - iR(0,20));
if (chopUpdateTerrainRecycling(&mGround) == 1)
/* mirror the sky if we've changed the ground */
chopCopyTerrain(&mGround, &mRoof, 0, - ( (iScreenY * 3) / 4));
if (iCurrLevelMode == LEVEL_MODE_NORMAL)
/* make it a bit more exciting, cause it's easy terrain... */
iPlayerSpeedX *= 2;
}
/* this is the plugin entry point */
enum plugin_status plugin_start(const void* parameter)
{
(void)parameter;
int ret;
rb->lcd_setfont(FONT_SYSFIXED);
#if LCD_DEPTH > 1
rb->lcd_set_backdrop(NULL);
#endif
#ifdef HAVE_LCD_COLOR
rb->lcd_set_background(LCD_BLACK);
rb->lcd_set_foreground(LCD_WHITE);
#endif
/* Turn off backlight timeout */
backlight_ignore_timeout();
rb->srand( *rb->current_tick );
configfile_load(CFG_FILE, config, 1, 0);
chopper_load(true);
ret = chopGameLoop();
configfile_save(CFG_FILE, config, 1, 0);
rb->lcd_setfont(FONT_UI);
/* Turn on backlight timeout (revert to settings) */
backlight_use_settings();
return ret;
}