Something went wrong. Try again.
A modern, network-enabled music player platform built on Rockbox technology. rockboxd.tsiry-sandratraina.com
rust deno navidrome airplay libadwaita zig mpris snapcast mpd rockbox audio subsonic
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131/*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ * * 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 == 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 == 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 || CONFIG_KEYPAD == XDUOO_X3II_PAD || CONFIG_KEYPAD == XDUOO_X20_PAD || CONFIG_KEYPAD == FIIO_M3K_PAD || CONFIG_KEYPAD == IHIFI_770_PAD || CONFIG_KEYPAD == IHIFI_800_PAD || CONFIG_KEYPAD == EROSQ_PAD#define QUIT BUTTON_POWER#define ACTION BUTTON_PLAY#define ACTIONTEXT "PLAY"
#elif CONFIG_KEYPAD == SDL_PAD#define QUIT BUTTON_TOPLEFT#define ACTION BUTTON_TOPMIDDLE#define ACTION2 BUTTON_BOTTOMMIDDLE#define ACTIONTEXT "SELECT"
#elif CONFIG_KEYPAD == MA_PAD#define QUIT BUTTON_BACK#define ACTION BUTTON_PLAY#define ACTIONTEXT "PLAY"
#elif CONFIG_KEYPAD == RG_NANO_PAD#define QUIT BUTTON_START#define ACTION BUTTON_A#define ACTION2 BUTTON_B#define ACTIONTEXT "A"
#elif CONFIG_KEYPAD == N3DS_PAD#define QUIT BUTTON_BACK#define ACTION BUTTON_SELECT#define ACTION2 BUTTON_MENU#define ACTIONTEXT "A"
#elif CONFIG_KEYPAD == ECHO_R1_PAD#define QUIT BUTTON_POWER#define ACTION BUTTON_A#define ACTION2 BUTTON_UP#define ACTIONTEXT "A"
#elif CONFIG_KEYPAD == PSP_PAD#define QUIT BUTTON_BACK#define ACTION BUTTON_SELECT#define ACTION2 BUTTON_MENU#define ACTIONTEXT "Cross"
#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 100static 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 50000static 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){ for(int i = 0; i < NUMBER_OF_PARTICLES; ++i) { if(!mParticles[i].bIsActive) { mParticles[i].bIsActive = 1; mParticles[i].iWorldX = x; mParticles[i].iWorldY = y; mParticles[i].iSpeedX = sx; mParticles[i].iSpeedY = sy; return; } }}
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, struct gui_synclist *this_list){ (void)this_list; 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",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, RB_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();
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;}