Bring mpegplayer backlight fix to the other plugins, this also fixes some wrongly...
[Rockbox.git] / apps / plugins / chopper.c
blob43b076d763fe1fec957e551cac337c966c74d43d
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id: $
10 * Originally by Joshua Oreman, improved by Prashant Varanasi
11 * Ported to Rockbox by Ben Basha (Paprica)
13 * All files in this archive are subject to the GNU General Public License.
14 * See the file COPYING in the source tree root for full license agreement.
16 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
17 * KIND, either express or implied.
19 ****************************************************************************/
21 #include "plugin.h"
22 #include "xlcd.h"
23 #include "configfile.h"
24 #include "helper.h"
26 PLUGIN_HEADER
28 /*Still To do:*/
29 /* - Make original speed and further increases in speed depend more on screen size*/
30 /* - attempt to make the tunnels get narrower as the game goes on*/
31 /* - make the chopper look better, maybe a picture, and scale according to screen size*/
32 /* - use textures for the color screens for background and terrain, eg stars on background*/
33 /* - allow choice of different levels [later: different screen themes]*/
34 /* - better high score handling, improved screen etc. */
36 #if (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD)
38 #define QUIT BUTTON_OFF
39 #define ACTION BUTTON_UP
40 #define ACTION2 BUTTON_SELECT
41 #define ACTIONTEXT "SELECT"
43 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
44 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
45 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
47 #define QUIT BUTTON_MENU
48 #define ACTION BUTTON_SELECT
49 #define ACTIONTEXT "SELECT"
51 #elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD /* grayscale at the moment */
53 #define QUIT BUTTON_POWER
54 #define ACTION BUTTON_UP
55 #define ACTION2 BUTTON_SELECT
56 #define ACTIONTEXT "SELECT"
58 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
59 #define QUIT BUTTON_POWER
60 #define ACTION BUTTON_RIGHT
61 #define ACTIONTEXT "RIGHT"
63 #elif CONFIG_KEYPAD == SANSA_E200_PAD
64 #define QUIT BUTTON_POWER
65 #define ACTION BUTTON_SELECT
66 #define ACTIONTEXT "SELECT"
68 #elif CONFIG_KEYPAD == GIGABEAT_PAD
69 #define QUIT BUTTON_MENU
70 #define ACTION BUTTON_SELECT
71 #define ACTIONTEXT "SELECT"
73 #elif CONFIG_KEYPAD == RECORDER_PAD
74 #define QUIT BUTTON_OFF
75 #define ACTION BUTTON_PLAY
76 #define ACTIONTEXT "PLAY"
78 #elif CONFIG_KEYPAD == ONDIO_PAD
79 #define QUIT BUTTON_OFF
80 #define ACTION BUTTON_UP
81 #define ACTION2 BUTTON_MENU
82 #define ACTIONTEXT "UP"
84 #else
85 #error Unsupported keypad
86 #endif
88 static struct plugin_api* rb;
90 #define NUMBER_OF_BLOCKS 8
91 #define NUMBER_OF_PARTICLES 3
92 #define MAX_TERRAIN_NODES 15
94 #define LEVEL_MODE_NORMAL 0
95 #define LEVEL_MODE_STEEP 1
97 #if LCD_WIDTH <= 112
98 #define CYCLETIME 100
99 #define SCALE(x) ((x)==1 ? (x) : ((x) >> 1))
100 #define SIZE 2
101 #else
102 #define CYCLETIME 60
103 #define SCALE(x) (x)
104 #define SIZE 1
105 #endif
107 /*Chopper's local variables to track the terrain position etc*/
108 static int chopCounter;
109 static int iRotorOffset;
110 static int iScreenX;
111 static int iScreenY;
112 static int iPlayerPosX;
113 static int iPlayerPosY;
114 static int iCameraPosX;
115 static int iPlayerSpeedX;
116 static int iPlayerSpeedY;
117 static int iLastBlockPlacedPosX;
118 static int iGravityTimerCountdown;
119 static int iPlayerAlive;
120 static int iLevelMode;
121 static int blockh,blockw;
122 static int highscore;
123 static int score;
125 #define CFG_FILE "chopper.cfg"
126 #define MAX_POINTS 50000
127 static struct configdata config[] =
129 {TYPE_INT, 0, MAX_POINTS, &highscore, "highscore", NULL, NULL}
132 struct CBlock
134 int iWorldX;
135 int iWorldY;
137 int iSizeX;
138 int iSizeY;
140 int bIsActive;
143 struct CParticle
145 int iWorldX;
146 int iWorldY;
148 int iSpeedX;
149 int iSpeedY;
151 int bIsActive;
154 struct CTerrainNode
156 int x;
157 int y;
160 struct CTerrain
162 struct CTerrainNode mNodes[MAX_TERRAIN_NODES];
163 int iNodesCount;
164 int iLastNodePlacedPosX;
167 struct CBlock mBlocks[NUMBER_OF_BLOCKS];
168 struct CParticle mParticles[NUMBER_OF_PARTICLES];
170 struct CTerrain mGround;
171 struct CTerrain mRoof;
173 /*Function declarations*/
174 static void chopDrawParticle(struct CParticle *mParticle);
175 static void chopDrawBlock(struct CBlock *mBlock);
176 static void chopRenderTerrain(struct CTerrain *ter);
177 void chopper_load(bool newgame);
178 void cleanup_chopper(void);
180 static void chopDrawPlayer(int x,int y) /* These are SCREEN coords, not world! */
183 #if LCD_DEPTH > 2
184 rb->lcd_set_foreground(LCD_RGBPACK(50,50,200));
185 #elif LCD_DEPTH == 2
186 rb->lcd_set_foreground(LCD_DARKGRAY);
187 #endif
188 rb->lcd_fillrect(SCALE(x+6), SCALE(y+2), SCALE(12), SCALE(9));
189 rb->lcd_fillrect(SCALE(x-3), SCALE(y+6), SCALE(20), SCALE(3));
191 #if LCD_DEPTH > 2
192 rb->lcd_set_foreground(LCD_RGBPACK(50,50,50));
193 #elif LCD_DEPTH == 2
194 rb->lcd_set_foreground(LCD_DARKGRAY);
195 #endif
196 rb->lcd_fillrect(SCALE(x+10), SCALE(y), SCALE(2), SCALE(3));
197 rb->lcd_fillrect(SCALE(x+10), SCALE(y), SCALE(1), SCALE(3));
199 #if LCD_DEPTH > 2
200 rb->lcd_set_foreground(LCD_RGBPACK(40,40,100));
201 #elif LCD_DEPTH == 2
202 rb->lcd_set_foreground(LCD_BLACK);
203 #endif
204 rb->lcd_drawline(SCALE(x), SCALE(y+iRotorOffset), SCALE(x+20), SCALE(y-iRotorOffset));
206 #if LCD_DEPTH > 2
207 rb->lcd_set_foreground(LCD_RGBPACK(20,20,50));
208 #elif LCD_DEPTH == 2
209 rb->lcd_set_foreground(LCD_BLACK);
210 #endif
211 rb->lcd_fillrect(SCALE(x - 2), SCALE(y + 5), SCALE(2), SCALE(5));
215 static void chopClearTerrain(struct CTerrain *ter)
217 ter->iNodesCount = 0;
221 int iR(int low,int high)
223 return low+rb->rand()%(high-low+1);
226 static void chopCopyTerrain(struct CTerrain *src, struct CTerrain *dest,
227 int xOffset,int yOffset)
229 int i=0;
231 while(i < src->iNodesCount)
233 dest->mNodes[i].x = src->mNodes[i].x + xOffset;
234 dest->mNodes[i].y = src->mNodes[i].y + yOffset;
236 i++;
239 dest->iNodesCount = src->iNodesCount;
240 dest->iLastNodePlacedPosX = src->iLastNodePlacedPosX;
244 static void chopAddTerrainNode(struct CTerrain *ter, int x, int y)
246 int i=0;
248 if(ter->iNodesCount + 1 >= MAX_TERRAIN_NODES)
250 /* DEBUGF("ERROR: Not enough nodes!\n"); */
251 return;
254 ter->iNodesCount++;
256 i = ter->iNodesCount - 1;
258 ter->mNodes[i].x = x;
259 ter->mNodes[i].y= y;
261 ter->iLastNodePlacedPosX = x;
265 static void chopTerrainNodeDeleteAndShift(struct CTerrain *ter,int nodeIndex)
267 int i=nodeIndex;
269 while( i < ter->iNodesCount )
271 ter->mNodes[i - 1] = ter->mNodes[i];
272 i++;
275 ter->iNodesCount--;
280 int chopUpdateTerrainRecycling(struct CTerrain *ter)
282 int i=1;
283 int ret = 0;
284 int iNewNodePos,g,v;
285 while(i < ter->iNodesCount)
288 if( iCameraPosX > ter->mNodes[i].x)
291 chopTerrainNodeDeleteAndShift(ter,i);
293 iNewNodePos = ter->iLastNodePlacedPosX + 50;
294 g = iScreenY - 10;
296 v = 3*iPlayerSpeedX;
297 if(v>50)
298 v=50;
299 if(iLevelMode == LEVEL_MODE_STEEP)
300 v*=5;
302 chopAddTerrainNode(ter,iNewNodePos,g - iR(-v,v));
303 ret=1;
307 i++;
311 return 1;
314 int chopTerrainHeightAtPoint(struct CTerrain *ter, int pX)
317 int iNodeIndexOne=0,iNodeIndexTwo=0, h, terY1, terY2, terX1, terX2, a, b;
318 float c,d;
320 int i=0;
321 for(i=1;i<MAX_TERRAIN_NODES;i++)
323 if(ter->mNodes[i].x > pX)
325 iNodeIndexOne = i - 1;
326 break;
331 iNodeIndexTwo = iNodeIndexOne + 1;
332 terY1 = ter->mNodes[iNodeIndexOne].y;
333 terY2 = ter->mNodes[iNodeIndexTwo].y;
335 terX1 = 0;
336 terX2 = ter->mNodes[iNodeIndexTwo].x - ter->mNodes[iNodeIndexOne].x;
338 pX-= ter->mNodes[iNodeIndexOne].x;
340 a = terY2 - terY1;
341 b = terX2;
342 c = pX;
343 d = (c/b) * a;
345 h = d + terY1;
347 return h;
351 int chopPointInTerrain(struct CTerrain *ter, int pX, int pY, int iTestType)
353 int h = chopTerrainHeightAtPoint(ter, pX);
355 if(iTestType == 0)
356 return (pY > h);
357 else
358 return (pY < h);
361 static void chopAddBlock(int x,int y,int sx,int sy, int indexOverride)
363 int i=0;
365 if(indexOverride < 0)
367 while(mBlocks[i].bIsActive && i < NUMBER_OF_BLOCKS)
368 i++;
369 if(i==NUMBER_OF_BLOCKS)
371 DEBUGF("No blocks!\n");
372 return;
375 else
376 i = indexOverride;
378 mBlocks[i].bIsActive = 1;
379 mBlocks[i].iWorldX = x;
380 mBlocks[i].iWorldY = y;
381 mBlocks[i].iSizeX = sx;
382 mBlocks[i].iSizeY = sy;
384 iLastBlockPlacedPosX = x;
387 static void chopAddParticle(int x,int y,int sx,int sy)
389 int i=0;
391 while(mParticles[i].bIsActive && i < NUMBER_OF_PARTICLES)
392 i++;
394 if(i==NUMBER_OF_PARTICLES)
395 return;
397 mParticles[i].bIsActive = 1;
398 mParticles[i].iWorldX = x;
399 mParticles[i].iWorldY = y;
400 mParticles[i].iSpeedX = sx;
401 mParticles[i].iSpeedY = sy;
404 static void chopGenerateBlockIfNeeded(void)
406 int i=0;
407 int DistSpeedX = iPlayerSpeedX * 5;
408 if(DistSpeedX<200) DistSpeedX = 200;
410 while(i < NUMBER_OF_BLOCKS)
412 if(!mBlocks[i].bIsActive)
414 int iX,iY,sX,sY;
416 iX = iLastBlockPlacedPosX + (350-DistSpeedX);
417 sX = blockw;
419 iY = iR(0,iScreenY);
420 sY = blockh + iR(1,blockh/3);
422 chopAddBlock(iX,iY,sX,sY,i);
425 i++;
430 static int chopBlockCollideWithPlayer(struct CBlock *mBlock)
432 int px = iPlayerPosX;
433 int py = iPlayerPosY;
435 int x = mBlock->iWorldX-17;
436 int y = mBlock->iWorldY-11;
438 int x2 = x + mBlock->iSizeX+17;
439 int y2 = y + mBlock->iSizeY+11;
441 if(px>x && px<x2)
443 if(py>y && py<y2)
445 return 1;
449 return 0;
452 static int chopBlockOffscreen(struct CBlock *mBlock)
454 if(mBlock->iWorldX + mBlock->iSizeX < iCameraPosX)
455 return 1;
456 else
457 return 0;
460 static int chopParticleOffscreen(struct CParticle *mParticle)
462 if (mParticle->iWorldX < iCameraPosX || mParticle->iWorldY < 0 ||
463 mParticle->iWorldY > iScreenY || mParticle->iWorldX > iCameraPosX +
464 iScreenX)
466 return 1;
468 else
469 return 0;
472 static void chopKillPlayer(void)
474 int i, button;
476 for (i = 0; i < NUMBER_OF_PARTICLES; i++) {
477 mParticles[i].bIsActive = 0;
478 chopAddParticle(iPlayerPosX + iR(0,20), iPlayerPosY + iR(0,20),
479 iR(-2,2), iR(-2,2));
482 iPlayerAlive--;
484 if (iPlayerAlive == 0) {
485 rb->lcd_set_drawmode(DRMODE_FG);
486 #if LCD_DEPTH >= 2
487 rb->lcd_set_foreground(LCD_LIGHTGRAY);
488 #endif
489 rb->splash(HZ, "Game Over");
491 if (score > highscore) {
492 char scoretext[30];
493 highscore = score;
494 rb->snprintf(scoretext, sizeof(scoretext), "New High Score: %d",
495 highscore);
496 rb->splash(HZ*2, scoretext);
499 rb->splash(HZ/4, "Press " ACTIONTEXT " to continue");
500 rb->lcd_update();
502 rb->lcd_set_drawmode(DRMODE_SOLID);
504 while (true) {
505 button = rb->button_get(true);
506 if (button == ACTION
507 #ifdef ACTION2
508 || button == ACTION2
509 #endif
511 while (true) {
512 button = rb->button_get(true);
513 if (button == (ACTION | BUTTON_REL)
514 #ifdef ACTION2
515 || button == (ACTION2 | BUTTON_REL)
516 #endif
518 chopper_load(true);
519 return;
525 } else
526 chopper_load(false);
530 static void chopDrawTheWorld(void)
532 int i=0;
534 while(i < NUMBER_OF_BLOCKS)
536 if(mBlocks[i].bIsActive)
538 if(chopBlockOffscreen(&mBlocks[i]) == 1)
539 mBlocks[i].bIsActive = 0;
540 else
541 chopDrawBlock(&mBlocks[i]);
544 i++;
547 i=0;
549 while(i < NUMBER_OF_PARTICLES)
551 if(mParticles[i].bIsActive)
553 if(chopParticleOffscreen(&mParticles[i]) == 1)
554 mParticles[i].bIsActive = 0;
555 else
556 chopDrawParticle(&mParticles[i]);
559 i++;
562 chopRenderTerrain(&mGround);
563 chopRenderTerrain(&mRoof);
567 static void chopDrawParticle(struct CParticle *mParticle)
570 int iPosX = (mParticle->iWorldX - iCameraPosX);
571 int iPosY = (mParticle->iWorldY);
572 #if LCD_DEPTH > 2
573 rb->lcd_set_foreground(LCD_RGBPACK(192,192,192));
574 #elif LCD_DEPTH == 2
575 rb->lcd_set_foreground(LCD_LIGHTGRAY);
576 #endif
577 rb->lcd_fillrect(SCALE(iPosX), SCALE(iPosY), SCALE(3), SCALE(3));
581 static void chopDrawScene(void)
583 char s[30];
584 int w;
585 #if LCD_DEPTH > 2
586 rb->lcd_set_background(LCD_BLACK);
587 #elif LCD_DEPTH == 2
588 rb->lcd_set_background(LCD_WHITE);
589 #endif
590 rb->lcd_clear_display();
592 chopDrawTheWorld();
593 chopDrawPlayer(iPlayerPosX - iCameraPosX, iPlayerPosY);
595 score = -20 + iPlayerPosX/3;
597 #if LCD_DEPTH == 1
598 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
599 #else
600 rb->lcd_set_drawmode(DRMODE_FG);
601 #endif
603 #if LCD_DEPTH > 2
604 rb->lcd_set_foreground(LCD_BLACK);
605 #elif LCD_DEPTH == 2
606 rb->lcd_set_foreground(LCD_WHITE);
607 #endif
609 #if LCD_WIDTH <= 128
610 rb->snprintf(s, sizeof(s), "Dist: %d", score);
611 #else
612 rb->snprintf(s, sizeof(s), "Distance: %d", score);
613 #endif
614 rb->lcd_getstringsize(s, &w, NULL);
615 rb->lcd_putsxy(2, 2, s);
616 if (score < highscore)
618 int w2;
619 #if LCD_WIDTH <= 128
620 rb->snprintf(s, sizeof(s), "Hi: %d", highscore);
621 #else
622 rb->snprintf(s, sizeof(s), "Best: %d", highscore);
623 #endif
624 rb->lcd_getstringsize(s, &w2, NULL);
625 if (LCD_WIDTH - 2 - w2 > w + 2)
626 rb->lcd_putsxy(LCD_WIDTH - 2 - w2, 2, s);
628 rb->lcd_set_drawmode(DRMODE_SOLID);
630 rb->lcd_update();
633 static int chopMenu(int menunum)
635 int result = (menunum==0)?0:1;
636 int res = 0;
637 bool menu_quit = false;
639 static const struct opt_items levels[2] = {
640 { "Normal", -1 },
641 { "Steep", -1 },
644 MENUITEM_STRINGLIST(menu,"Chopper Menu",NULL,"Start New Game","Resume Game",
645 "Level","Quit");
647 #ifdef HAVE_LCD_COLOR
648 rb->lcd_set_foreground(LCD_WHITE);
649 rb->lcd_set_background(LCD_BLACK);
650 #elif LCD_DEPTH == 2
651 rb->lcd_set_foreground(LCD_BLACK);
652 rb->lcd_set_background(LCD_WHITE);
653 #endif
655 rb->lcd_clear_display();
657 while (!menu_quit) {
658 switch(rb->do_menu(&menu, &result))
660 case 0: /* Start New Game */
661 menu_quit=true;
662 chopper_load(true);
663 res = -1;
664 break;
665 case 1: /* Resume Game */
666 if(menunum==1) {
667 menu_quit=true;
668 res = -1;
669 } else if(menunum==0){
670 rb->splash(HZ, "No game to resume");
672 break;
673 case 2:
674 rb->set_option("Level", &iLevelMode, INT, levels, 2, NULL);
675 break;
676 case 3:
677 menu_quit=true;
678 res = PLUGIN_OK;
679 break;
680 case MENU_ATTACHED_USB:
681 menu_quit=true;
682 res = PLUGIN_USB_CONNECTED;
683 break;
686 rb->lcd_clear_display();
687 return res;
690 static int chopGameLoop(void)
692 int move_button, ret;
693 bool exit=false;
694 int end, i=0, bdelay=0, last_button=BUTTON_NONE;
696 if (chopUpdateTerrainRecycling(&mGround) == 1)
697 /* mirror the sky if we've changed the ground */
698 chopCopyTerrain(&mGround, &mRoof, 0, - ( (iScreenY * 3) / 4));
700 ret = chopMenu(0);
701 if (ret != -1)
702 return PLUGIN_OK;
704 chopDrawScene();
706 while (!exit) {
708 end = *rb->current_tick + (CYCLETIME * HZ) / 1000;
710 if(chopUpdateTerrainRecycling(&mGround) == 1)
711 /* mirror the sky if we've changed the ground */
712 chopCopyTerrain(&mGround, &mRoof, 0, - ( (iScreenY * 3) / 4));
714 iRotorOffset = iR(-1,1);
716 /* We need to have this here so particles move when we're dead */
718 for (i=0; i < NUMBER_OF_PARTICLES; i++)
719 if(mParticles[i].bIsActive == 1)
721 mParticles[i].iWorldX += mParticles[i].iSpeedX;
722 mParticles[i].iWorldY += mParticles[i].iSpeedY;
725 /* Redraw the main window: */
726 chopDrawScene();
729 iGravityTimerCountdown--;
731 if(iGravityTimerCountdown <= 0)
733 iGravityTimerCountdown = 3;
734 chopAddParticle(iPlayerPosX, iPlayerPosY+5, 0, 0);
737 if(iLevelMode == LEVEL_MODE_NORMAL)
738 chopGenerateBlockIfNeeded();
741 move_button=rb->button_status();
742 if (rb->button_get(false) == QUIT) {
743 ret = chopMenu(1);
744 if (ret != -1)
745 return PLUGIN_OK;
746 bdelay = 0;
747 last_button = BUTTON_NONE;
748 move_button = BUTTON_NONE;
751 switch (move_button) {
752 case ACTION:
753 #ifdef ACTION2
754 case ACTION2:
755 #endif
756 if (last_button != ACTION
757 #ifdef ACTION2
758 && last_button != ACTION2
759 #endif
761 bdelay = -2;
762 if (bdelay == 0)
763 iPlayerSpeedY = -3;
764 break;
766 default:
767 if (last_button == ACTION
768 #ifdef ACTION2
769 || last_button == ACTION2
770 #endif
772 bdelay = 3;
773 if (bdelay == 0)
774 iPlayerSpeedY = 4;
776 if (rb->default_event_handler(move_button) == SYS_USB_CONNECTED)
777 return PLUGIN_USB_CONNECTED;
778 break;
780 last_button = move_button;
782 if (bdelay < 0) {
783 iPlayerSpeedY = bdelay;
784 bdelay++;
785 } else if (bdelay > 0) {
786 iPlayerSpeedY = bdelay;
787 bdelay--;
790 iCameraPosX = iPlayerPosX - 25;
791 iPlayerPosX += iPlayerSpeedX;
792 iPlayerPosY += iPlayerSpeedY;
794 chopCounter++;
795 /* increase speed as we go along */
796 if (chopCounter == 100){
797 iPlayerSpeedX++;
798 chopCounter=0;
801 if (iPlayerPosY > iScreenY-10 || iPlayerPosY < -5 ||
802 chopPointInTerrain(&mGround, iPlayerPosX, iPlayerPosY + 10, 0) ||
803 chopPointInTerrain(&mRoof, iPlayerPosX ,iPlayerPosY, 1))
805 chopKillPlayer();
806 chopDrawScene();
807 ret = chopMenu(0);
808 if (ret != -1)
809 return ret;
812 for (i=0; i < NUMBER_OF_BLOCKS; i++)
813 if(mBlocks[i].bIsActive == 1)
814 if(chopBlockCollideWithPlayer(&mBlocks[i])) {
815 chopKillPlayer();
816 chopDrawScene();
817 ret = chopMenu(0);
818 if (ret != -1)
819 return ret;
822 if (end > *rb->current_tick)
823 rb->sleep(end-*rb->current_tick);
824 else
825 rb->yield();
828 return PLUGIN_OK;
831 static void chopDrawBlock(struct CBlock *mBlock)
833 int iPosX = (mBlock->iWorldX - iCameraPosX);
834 int iPosY = (mBlock->iWorldY);
835 #if LCD_DEPTH > 2
836 rb->lcd_set_foreground(LCD_RGBPACK(100,255,100));
837 #elif LCD_DEPTH == 2
838 rb->lcd_set_foreground(LCD_BLACK);
839 #endif
840 rb->lcd_fillrect(SCALE(iPosX), SCALE(iPosY), SCALE(mBlock->iSizeX),
841 SCALE(mBlock->iSizeY));
845 static void chopRenderTerrain(struct CTerrain *ter)
848 int i=1;
850 int oldx=0;
852 int ay=0;
853 if(ter->mNodes[0].y < (LCD_HEIGHT*SIZE)/2)
854 ay=0;
855 else
856 ay=(LCD_HEIGHT*SIZE);
858 while(i < ter->iNodesCount && oldx < iScreenX)
861 int x = ter->mNodes[i-1].x - iCameraPosX;
862 int y = ter->mNodes[i-1].y;
864 int x2 = ter->mNodes[i].x - iCameraPosX;
865 int y2 = ter->mNodes[i].y;
866 #if LCD_DEPTH > 2
867 rb->lcd_set_foreground(LCD_RGBPACK(100,255,100));
868 #elif LCD_DEPTH == 2
869 rb->lcd_set_foreground(LCD_DARKGRAY);
870 #endif
872 rb->lcd_drawline(SCALE(x), SCALE(y), SCALE(x2), SCALE(y2));
874 xlcd_filltriangle(SCALE(x), SCALE(y), SCALE(x2), SCALE(y2), SCALE(x2), SCALE(ay));
875 xlcd_filltriangle(SCALE(x), SCALE(ay), SCALE(x2), SCALE(y2), SCALE(x2), SCALE(ay));
877 if (ay == 0)
878 xlcd_filltriangle(SCALE(x), SCALE(ay), SCALE(x), SCALE(y), SCALE(x2), SCALE(y2 / 2));
879 else
880 xlcd_filltriangle(SCALE(x), SCALE(ay), SCALE(x), SCALE(y), SCALE(x2), SCALE((LCD_HEIGHT*SIZE) - ((LCD_HEIGHT*SIZE) - y2) / 2));
882 oldx = x;
883 i++;
889 void chopper_load(bool newgame)
892 int i;
893 int g;
895 if (newgame) {
896 iScreenX = LCD_WIDTH * SIZE;
897 iScreenY = LCD_HEIGHT * SIZE;
898 blockh = iScreenY / 5;
899 blockw = iScreenX / 20;
900 iPlayerAlive = 1;
901 score = 0;
903 iRotorOffset = 0;
904 iPlayerPosX = 60;
905 iPlayerPosY = (iScreenY * 4) / 10;
906 iLastBlockPlacedPosX = 0;
907 iGravityTimerCountdown = 2;
908 chopCounter = 0;
909 iPlayerSpeedX = 3;
910 iPlayerSpeedY = 0;
911 iCameraPosX = 30;
913 for (i=0; i < NUMBER_OF_PARTICLES; i++)
914 mParticles[i].bIsActive = 0;
916 for (i=0; i < NUMBER_OF_BLOCKS; i++)
917 mBlocks[i].bIsActive = 0;
919 g = iScreenY - 10;
920 chopClearTerrain(&mGround);
922 for (i=0; i < MAX_TERRAIN_NODES; i++)
923 chopAddTerrainNode(&mGround,i * 30,g - iR(0,20));
925 if (chopUpdateTerrainRecycling(&mGround) == 1)
926 /* mirror the sky if we've changed the ground */
927 chopCopyTerrain(&mGround, &mRoof, 0, - ( (iScreenY * 3) / 4));
929 iLevelMode = LEVEL_MODE_NORMAL;
930 if (iLevelMode == LEVEL_MODE_NORMAL)
931 /* make it a bit more exciting, cause it's easy terrain... */
932 iPlayerSpeedX *= 2;
935 /* this is the plugin entry point */
936 enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
938 (void)parameter;
939 rb = api;
940 int ret;
942 rb->lcd_setfont(FONT_SYSFIXED);
943 #if LCD_DEPTH > 1
944 rb->lcd_set_backdrop(NULL);
945 #endif
946 #ifdef HAVE_LCD_COLOR
947 rb->lcd_set_background(LCD_BLACK);
948 rb->lcd_set_foreground(LCD_WHITE);
949 #endif
951 /* Turn off backlight timeout */
952 backlight_force_on(); /* backlight control in lib/helper.c */
954 rb->srand( *rb->current_tick );
956 xlcd_init(rb);
957 configfile_init(rb);
958 configfile_load(CFG_FILE, config, 1, 0);
960 chopper_load(true);
961 ret = chopGameLoop();
963 configfile_save(CFG_FILE, config, 1, 0);
965 rb->lcd_setfont(FONT_UI);
966 /* Turn on backlight timeout (revert to settings) */
967 backlight_use_settings(); /* backlight control in lib/helper.c */
969 return ret;