/*************************************************************************** * __________ __ ___. * 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 !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;imNodes[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 && pxy && pyiWorldX + 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; }