code police (and commit test)
[Rockbox.git] / apps / plugins / chopper.c
blob5e27a5e6e755dc5a0cd8701ebeba7180fc663311
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
29 Still To do:
30 - Make original speed and further increases in speed depend more on screen size
31 - attempt to make the tunnels get narrower as the game goes on
32 - make the chopper look better, maybe a picture, and scale according
33 to screen size
34 - use textures for the color screens for background and terrain,
35 eg stars on background
36 - allow choice of different levels [later: different screen themes]
37 - better high score handling, improved screen etc.
40 #if (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD)
42 #define QUIT BUTTON_OFF
43 #define ACTION BUTTON_UP
44 #define ACTION2 BUTTON_SELECT
45 #define ACTIONTEXT "SELECT"
47 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
48 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
49 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
51 #define QUIT BUTTON_MENU
52 #define ACTION BUTTON_SELECT
53 #define ACTIONTEXT "SELECT"
55 #elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD /* grayscale at the moment */
57 #define QUIT BUTTON_POWER
58 #define ACTION BUTTON_UP
59 #define ACTION2 BUTTON_SELECT
60 #define ACTIONTEXT "SELECT"
62 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
63 #define QUIT BUTTON_POWER
64 #define ACTION BUTTON_RIGHT
65 #define ACTIONTEXT "RIGHT"
67 #elif CONFIG_KEYPAD == SANSA_E200_PAD
68 #define QUIT BUTTON_POWER
69 #define ACTION BUTTON_SELECT
70 #define ACTIONTEXT "SELECT"
72 #elif CONFIG_KEYPAD == GIGABEAT_PAD
73 #define QUIT BUTTON_MENU
74 #define ACTION BUTTON_SELECT
75 #define ACTIONTEXT "SELECT"
77 #elif CONFIG_KEYPAD == RECORDER_PAD
78 #define QUIT BUTTON_OFF
79 #define ACTION BUTTON_PLAY
80 #define ACTIONTEXT "PLAY"
82 #elif CONFIG_KEYPAD == ONDIO_PAD
83 #define QUIT BUTTON_OFF
84 #define ACTION BUTTON_UP
85 #define ACTION2 BUTTON_MENU
86 #define ACTIONTEXT "UP"
88 #else
89 #error Unsupported keypad
90 #endif
92 static struct plugin_api* rb;
94 #define NUMBER_OF_BLOCKS 8
95 #define NUMBER_OF_PARTICLES 3
96 #define MAX_TERRAIN_NODES 15
98 #define LEVEL_MODE_NORMAL 0
99 #define LEVEL_MODE_STEEP 1
101 #if LCD_WIDTH <= 112
102 #define CYCLETIME 100
103 #define SCALE(x) ((x)==1 ? (x) : ((x) >> 1))
104 #define SIZE 2
105 #else
106 #define CYCLETIME 60
107 #define SCALE(x) (x)
108 #define SIZE 1
109 #endif
111 /*Chopper's local variables to track the terrain position etc*/
112 static int chopCounter;
113 static int iRotorOffset;
114 static int iScreenX;
115 static int iScreenY;
116 static int iPlayerPosX;
117 static int iPlayerPosY;
118 static int iCameraPosX;
119 static int iPlayerSpeedX;
120 static int iPlayerSpeedY;
121 static int iLastBlockPlacedPosX;
122 static int iGravityTimerCountdown;
123 static int iPlayerAlive;
124 static int iLevelMode;
125 static int blockh,blockw;
126 static int highscore;
127 static int score;
129 #define CFG_FILE "chopper.cfg"
130 #define MAX_POINTS 50000
131 static struct configdata config[] =
133 {TYPE_INT, 0, MAX_POINTS, &highscore, "highscore", NULL, NULL}
136 struct CBlock
138 int iWorldX;
139 int iWorldY;
141 int iSizeX;
142 int iSizeY;
144 int bIsActive;
147 struct CParticle
149 int iWorldX;
150 int iWorldY;
152 int iSpeedX;
153 int iSpeedY;
155 int bIsActive;
158 struct CTerrainNode
160 int x;
161 int y;
164 struct CTerrain
166 struct CTerrainNode mNodes[MAX_TERRAIN_NODES];
167 int iNodesCount;
168 int iLastNodePlacedPosX;
171 struct CBlock mBlocks[NUMBER_OF_BLOCKS];
172 struct CParticle mParticles[NUMBER_OF_PARTICLES];
174 struct CTerrain mGround;
175 struct CTerrain mRoof;
177 /*Function declarations*/
178 static void chopDrawParticle(struct CParticle *mParticle);
179 static void chopDrawBlock(struct CBlock *mBlock);
180 static void chopRenderTerrain(struct CTerrain *ter);
181 void chopper_load(bool newgame);
182 void cleanup_chopper(void);
184 static void chopDrawPlayer(int x,int y) /* These are SCREEN coords, not world!*/
187 #if LCD_DEPTH > 2
188 rb->lcd_set_foreground(LCD_RGBPACK(50,50,200));
189 #elif LCD_DEPTH == 2
190 rb->lcd_set_foreground(LCD_DARKGRAY);
191 #endif
192 rb->lcd_fillrect(SCALE(x+6), SCALE(y+2), SCALE(12), SCALE(9));
193 rb->lcd_fillrect(SCALE(x-3), SCALE(y+6), SCALE(20), SCALE(3));
195 #if LCD_DEPTH > 2
196 rb->lcd_set_foreground(LCD_RGBPACK(50,50,50));
197 #elif LCD_DEPTH == 2
198 rb->lcd_set_foreground(LCD_DARKGRAY);
199 #endif
200 rb->lcd_fillrect(SCALE(x+10), SCALE(y), SCALE(2), SCALE(3));
201 rb->lcd_fillrect(SCALE(x+10), SCALE(y), SCALE(1), SCALE(3));
203 #if LCD_DEPTH > 2
204 rb->lcd_set_foreground(LCD_RGBPACK(40,40,100));
205 #elif LCD_DEPTH == 2
206 rb->lcd_set_foreground(LCD_BLACK);
207 #endif
208 rb->lcd_drawline(SCALE(x), SCALE(y+iRotorOffset), SCALE(x+20),
209 SCALE(y-iRotorOffset));
211 #if LCD_DEPTH > 2
212 rb->lcd_set_foreground(LCD_RGBPACK(20,20,50));
213 #elif LCD_DEPTH == 2
214 rb->lcd_set_foreground(LCD_BLACK);
215 #endif
216 rb->lcd_fillrect(SCALE(x - 2), SCALE(y + 5), SCALE(2), SCALE(5));
220 static void chopClearTerrain(struct CTerrain *ter)
222 ter->iNodesCount = 0;
226 int iR(int low,int high)
228 return low+rb->rand()%(high-low+1);
231 static void chopCopyTerrain(struct CTerrain *src, struct CTerrain *dest,
232 int xOffset,int yOffset)
234 int i=0;
236 while(i < src->iNodesCount)
238 dest->mNodes[i].x = src->mNodes[i].x + xOffset;
239 dest->mNodes[i].y = src->mNodes[i].y + yOffset;
241 i++;
244 dest->iNodesCount = src->iNodesCount;
245 dest->iLastNodePlacedPosX = src->iLastNodePlacedPosX;
249 static void chopAddTerrainNode(struct CTerrain *ter, int x, int y)
251 int i=0;
253 if(ter->iNodesCount + 1 >= MAX_TERRAIN_NODES)
255 /* DEBUGF("ERROR: Not enough nodes!\n"); */
256 return;
259 ter->iNodesCount++;
261 i = ter->iNodesCount - 1;
263 ter->mNodes[i].x = x;
264 ter->mNodes[i].y= y;
266 ter->iLastNodePlacedPosX = x;
270 static void chopTerrainNodeDeleteAndShift(struct CTerrain *ter,int nodeIndex)
272 int i=nodeIndex;
274 while( i < ter->iNodesCount )
276 ter->mNodes[i - 1] = ter->mNodes[i];
277 i++;
280 ter->iNodesCount--;
285 int chopUpdateTerrainRecycling(struct CTerrain *ter)
287 int i=1;
288 int ret = 0;
289 int iNewNodePos,g,v;
290 while(i < ter->iNodesCount)
293 if( iCameraPosX > ter->mNodes[i].x)
296 chopTerrainNodeDeleteAndShift(ter,i);
298 iNewNodePos = ter->iLastNodePlacedPosX + 50;
299 g = iScreenY - 10;
301 v = 3*iPlayerSpeedX;
302 if(v>50)
303 v=50;
304 if(iLevelMode == LEVEL_MODE_STEEP)
305 v*=5;
307 chopAddTerrainNode(ter,iNewNodePos,g - iR(-v,v));
308 ret=1;
312 i++;
316 return 1;
319 int chopTerrainHeightAtPoint(struct CTerrain *ter, int pX)
322 int iNodeIndexOne=0,iNodeIndexTwo=0, h, terY1, terY2, terX1, terX2, a, b;
323 float c,d;
325 int i=0;
326 for(i=1;i<MAX_TERRAIN_NODES;i++)
328 if(ter->mNodes[i].x > pX)
330 iNodeIndexOne = i - 1;
331 break;
336 iNodeIndexTwo = iNodeIndexOne + 1;
337 terY1 = ter->mNodes[iNodeIndexOne].y;
338 terY2 = ter->mNodes[iNodeIndexTwo].y;
340 terX1 = 0;
341 terX2 = ter->mNodes[iNodeIndexTwo].x - ter->mNodes[iNodeIndexOne].x;
343 pX-= ter->mNodes[iNodeIndexOne].x;
345 a = terY2 - terY1;
346 b = terX2;
347 c = pX;
348 d = (c/b) * a;
350 h = d + terY1;
352 return h;
356 int chopPointInTerrain(struct CTerrain *ter, int pX, int pY, int iTestType)
358 int h = chopTerrainHeightAtPoint(ter, pX);
360 if(iTestType == 0)
361 return (pY > h);
362 else
363 return (pY < h);
366 static void chopAddBlock(int x,int y,int sx,int sy, int indexOverride)
368 int i=0;
370 if(indexOverride < 0)
372 while(mBlocks[i].bIsActive && i < NUMBER_OF_BLOCKS)
373 i++;
374 if(i==NUMBER_OF_BLOCKS)
376 DEBUGF("No blocks!\n");
377 return;
380 else
381 i = indexOverride;
383 mBlocks[i].bIsActive = 1;
384 mBlocks[i].iWorldX = x;
385 mBlocks[i].iWorldY = y;
386 mBlocks[i].iSizeX = sx;
387 mBlocks[i].iSizeY = sy;
389 iLastBlockPlacedPosX = x;
392 static void chopAddParticle(int x,int y,int sx,int sy)
394 int i=0;
396 while(mParticles[i].bIsActive && i < NUMBER_OF_PARTICLES)
397 i++;
399 if(i==NUMBER_OF_PARTICLES)
400 return;
402 mParticles[i].bIsActive = 1;
403 mParticles[i].iWorldX = x;
404 mParticles[i].iWorldY = y;
405 mParticles[i].iSpeedX = sx;
406 mParticles[i].iSpeedY = sy;
409 static void chopGenerateBlockIfNeeded(void)
411 int i=0;
412 int DistSpeedX = iPlayerSpeedX * 5;
413 if(DistSpeedX<200) DistSpeedX = 200;
415 while(i < NUMBER_OF_BLOCKS)
417 if(!mBlocks[i].bIsActive)
419 int iX,iY,sX,sY;
421 iX = iLastBlockPlacedPosX + (350-DistSpeedX);
422 sX = blockw;
424 iY = iR(0,iScreenY);
425 sY = blockh + iR(1,blockh/3);
427 chopAddBlock(iX,iY,sX,sY,i);
430 i++;
435 static int chopBlockCollideWithPlayer(struct CBlock *mBlock)
437 int px = iPlayerPosX;
438 int py = iPlayerPosY;
440 int x = mBlock->iWorldX-17;
441 int y = mBlock->iWorldY-11;
443 int x2 = x + mBlock->iSizeX+17;
444 int y2 = y + mBlock->iSizeY+11;
446 if(px>x && px<x2)
448 if(py>y && py<y2)
450 return 1;
454 return 0;
457 static int chopBlockOffscreen(struct CBlock *mBlock)
459 if(mBlock->iWorldX + mBlock->iSizeX < iCameraPosX)
460 return 1;
461 else
462 return 0;
465 static int chopParticleOffscreen(struct CParticle *mParticle)
467 if (mParticle->iWorldX < iCameraPosX || mParticle->iWorldY < 0 ||
468 mParticle->iWorldY > iScreenY || mParticle->iWorldX > iCameraPosX +
469 iScreenX)
471 return 1;
473 else
474 return 0;
477 static void chopKillPlayer(void)
479 int i, button;
481 for (i = 0; i < NUMBER_OF_PARTICLES; i++) {
482 mParticles[i].bIsActive = 0;
483 chopAddParticle(iPlayerPosX + iR(0,20), iPlayerPosY + iR(0,20),
484 iR(-2,2), iR(-2,2));
487 iPlayerAlive--;
489 if (iPlayerAlive == 0) {
490 rb->lcd_set_drawmode(DRMODE_FG);
491 #if LCD_DEPTH >= 2
492 rb->lcd_set_foreground(LCD_LIGHTGRAY);
493 #endif
494 rb->splash(HZ, "Game Over");
496 if (score > highscore) {
497 char scoretext[30];
498 highscore = score;
499 rb->snprintf(scoretext, sizeof(scoretext), "New High Score: %d",
500 highscore);
501 rb->splash(HZ*2, scoretext);
504 rb->splash(HZ/4, "Press " ACTIONTEXT " to continue");
505 rb->lcd_update();
507 rb->lcd_set_drawmode(DRMODE_SOLID);
509 while (true) {
510 button = rb->button_get(true);
511 if (button == ACTION
512 #ifdef ACTION2
513 || button == ACTION2
514 #endif
516 while (true) {
517 button = rb->button_get(true);
518 if (button == (ACTION | BUTTON_REL)
519 #ifdef ACTION2
520 || button == (ACTION2 | BUTTON_REL)
521 #endif
523 chopper_load(true);
524 return;
530 } else
531 chopper_load(false);
535 static void chopDrawTheWorld(void)
537 int i=0;
539 while(i < NUMBER_OF_BLOCKS)
541 if(mBlocks[i].bIsActive)
543 if(chopBlockOffscreen(&mBlocks[i]) == 1)
544 mBlocks[i].bIsActive = 0;
545 else
546 chopDrawBlock(&mBlocks[i]);
549 i++;
552 i=0;
554 while(i < NUMBER_OF_PARTICLES)
556 if(mParticles[i].bIsActive)
558 if(chopParticleOffscreen(&mParticles[i]) == 1)
559 mParticles[i].bIsActive = 0;
560 else
561 chopDrawParticle(&mParticles[i]);
564 i++;
567 chopRenderTerrain(&mGround);
568 chopRenderTerrain(&mRoof);
572 static void chopDrawParticle(struct CParticle *mParticle)
575 int iPosX = (mParticle->iWorldX - iCameraPosX);
576 int iPosY = (mParticle->iWorldY);
577 #if LCD_DEPTH > 2
578 rb->lcd_set_foreground(LCD_RGBPACK(192,192,192));
579 #elif LCD_DEPTH == 2
580 rb->lcd_set_foreground(LCD_LIGHTGRAY);
581 #endif
582 rb->lcd_fillrect(SCALE(iPosX), SCALE(iPosY), SCALE(3), SCALE(3));
586 static void chopDrawScene(void)
588 char s[30];
589 int w;
590 #if LCD_DEPTH > 2
591 rb->lcd_set_background(LCD_BLACK);
592 #elif LCD_DEPTH == 2
593 rb->lcd_set_background(LCD_WHITE);
594 #endif
595 rb->lcd_clear_display();
597 chopDrawTheWorld();
598 chopDrawPlayer(iPlayerPosX - iCameraPosX, iPlayerPosY);
600 score = -20 + iPlayerPosX/3;
602 #if LCD_DEPTH == 1
603 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
604 #else
605 rb->lcd_set_drawmode(DRMODE_FG);
606 #endif
608 #if LCD_DEPTH > 2
609 rb->lcd_set_foreground(LCD_BLACK);
610 #elif LCD_DEPTH == 2
611 rb->lcd_set_foreground(LCD_WHITE);
612 #endif
614 #if LCD_WIDTH <= 128
615 rb->snprintf(s, sizeof(s), "Dist: %d", score);
616 #else
617 rb->snprintf(s, sizeof(s), "Distance: %d", score);
618 #endif
619 rb->lcd_getstringsize(s, &w, NULL);
620 rb->lcd_putsxy(2, 2, s);
621 if (score < highscore)
623 int w2;
624 #if LCD_WIDTH <= 128
625 rb->snprintf(s, sizeof(s), "Hi: %d", highscore);
626 #else
627 rb->snprintf(s, sizeof(s), "Best: %d", highscore);
628 #endif
629 rb->lcd_getstringsize(s, &w2, NULL);
630 if (LCD_WIDTH - 2 - w2 > w + 2)
631 rb->lcd_putsxy(LCD_WIDTH - 2 - w2, 2, s);
633 rb->lcd_set_drawmode(DRMODE_SOLID);
635 rb->lcd_update();
638 static int chopMenu(int menunum)
640 int result = (menunum==0)?0:1;
641 int res = 0;
642 bool menu_quit = false;
644 static const struct opt_items levels[2] = {
645 { "Normal", -1 },
646 { "Steep", -1 },
649 MENUITEM_STRINGLIST(menu,"Chopper Menu",NULL,"Start New Game","Resume Game",
650 "Level","Quit");
652 #ifdef HAVE_LCD_COLOR
653 rb->lcd_set_foreground(LCD_WHITE);
654 rb->lcd_set_background(LCD_BLACK);
655 #elif LCD_DEPTH == 2
656 rb->lcd_set_foreground(LCD_BLACK);
657 rb->lcd_set_background(LCD_WHITE);
658 #endif
660 rb->lcd_clear_display();
662 while (!menu_quit) {
663 switch(rb->do_menu(&menu, &result))
665 case 0: /* Start New Game */
666 menu_quit=true;
667 chopper_load(true);
668 res = -1;
669 break;
670 case 1: /* Resume Game */
671 if(menunum==1) {
672 menu_quit=true;
673 res = -1;
674 } else if(menunum==0){
675 rb->splash(HZ, "No game to resume");
677 break;
678 case 2:
679 rb->set_option("Level", &iLevelMode, INT, levels, 2, NULL);
680 break;
681 case 3:
682 menu_quit=true;
683 res = PLUGIN_OK;
684 break;
685 case MENU_ATTACHED_USB:
686 menu_quit=true;
687 res = PLUGIN_USB_CONNECTED;
688 break;
691 rb->lcd_clear_display();
692 return res;
695 static int chopGameLoop(void)
697 int move_button, ret;
698 bool exit=false;
699 int end, i=0, bdelay=0, last_button=BUTTON_NONE;
701 if (chopUpdateTerrainRecycling(&mGround) == 1)
702 /* mirror the sky if we've changed the ground */
703 chopCopyTerrain(&mGround, &mRoof, 0, - ( (iScreenY * 3) / 4));
705 ret = chopMenu(0);
706 if (ret != -1)
707 return PLUGIN_OK;
709 chopDrawScene();
711 while (!exit) {
713 end = *rb->current_tick + (CYCLETIME * HZ) / 1000;
715 if(chopUpdateTerrainRecycling(&mGround) == 1)
716 /* mirror the sky if we've changed the ground */
717 chopCopyTerrain(&mGround, &mRoof, 0, - ( (iScreenY * 3) / 4));
719 iRotorOffset = iR(-1,1);
721 /* We need to have this here so particles move when we're dead */
723 for (i=0; i < NUMBER_OF_PARTICLES; i++)
724 if(mParticles[i].bIsActive == 1)
726 mParticles[i].iWorldX += mParticles[i].iSpeedX;
727 mParticles[i].iWorldY += mParticles[i].iSpeedY;
730 /* Redraw the main window: */
731 chopDrawScene();
734 iGravityTimerCountdown--;
736 if(iGravityTimerCountdown <= 0)
738 iGravityTimerCountdown = 3;
739 chopAddParticle(iPlayerPosX, iPlayerPosY+5, 0, 0);
742 if(iLevelMode == LEVEL_MODE_NORMAL)
743 chopGenerateBlockIfNeeded();
746 move_button=rb->button_status();
747 if (rb->button_get(false) == QUIT) {
748 ret = chopMenu(1);
749 if (ret != -1)
750 return PLUGIN_OK;
751 bdelay = 0;
752 last_button = BUTTON_NONE;
753 move_button = BUTTON_NONE;
756 switch (move_button) {
757 case ACTION:
758 #ifdef ACTION2
759 case ACTION2:
760 #endif
761 if (last_button != ACTION
762 #ifdef ACTION2
763 && last_button != ACTION2
764 #endif
766 bdelay = -2;
767 if (bdelay == 0)
768 iPlayerSpeedY = -3;
769 break;
771 default:
772 if (last_button == ACTION
773 #ifdef ACTION2
774 || last_button == ACTION2
775 #endif
777 bdelay = 3;
778 if (bdelay == 0)
779 iPlayerSpeedY = 4;
781 if (rb->default_event_handler(move_button) == SYS_USB_CONNECTED)
782 return PLUGIN_USB_CONNECTED;
783 break;
785 last_button = move_button;
787 if (bdelay < 0) {
788 iPlayerSpeedY = bdelay;
789 bdelay++;
790 } else if (bdelay > 0) {
791 iPlayerSpeedY = bdelay;
792 bdelay--;
795 iCameraPosX = iPlayerPosX - 25;
796 iPlayerPosX += iPlayerSpeedX;
797 iPlayerPosY += iPlayerSpeedY;
799 chopCounter++;
800 /* increase speed as we go along */
801 if (chopCounter == 100){
802 iPlayerSpeedX++;
803 chopCounter=0;
806 if (iPlayerPosY > iScreenY-10 || iPlayerPosY < -5 ||
807 chopPointInTerrain(&mGround, iPlayerPosX, iPlayerPosY + 10, 0) ||
808 chopPointInTerrain(&mRoof, iPlayerPosX ,iPlayerPosY, 1))
810 chopKillPlayer();
811 chopDrawScene();
812 ret = chopMenu(0);
813 if (ret != -1)
814 return ret;
817 for (i=0; i < NUMBER_OF_BLOCKS; i++)
818 if(mBlocks[i].bIsActive == 1)
819 if(chopBlockCollideWithPlayer(&mBlocks[i])) {
820 chopKillPlayer();
821 chopDrawScene();
822 ret = chopMenu(0);
823 if (ret != -1)
824 return ret;
827 if (end > *rb->current_tick)
828 rb->sleep(end-*rb->current_tick);
829 else
830 rb->yield();
833 return PLUGIN_OK;
836 static void chopDrawBlock(struct CBlock *mBlock)
838 int iPosX = (mBlock->iWorldX - iCameraPosX);
839 int iPosY = (mBlock->iWorldY);
840 #if LCD_DEPTH > 2
841 rb->lcd_set_foreground(LCD_RGBPACK(100,255,100));
842 #elif LCD_DEPTH == 2
843 rb->lcd_set_foreground(LCD_BLACK);
844 #endif
845 rb->lcd_fillrect(SCALE(iPosX), SCALE(iPosY), SCALE(mBlock->iSizeX),
846 SCALE(mBlock->iSizeY));
850 static void chopRenderTerrain(struct CTerrain *ter)
853 int i=1;
855 int oldx=0;
857 int ay=0;
858 if(ter->mNodes[0].y < (LCD_HEIGHT*SIZE)/2)
859 ay=0;
860 else
861 ay=(LCD_HEIGHT*SIZE);
863 while(i < ter->iNodesCount && oldx < iScreenX)
866 int x = ter->mNodes[i-1].x - iCameraPosX;
867 int y = ter->mNodes[i-1].y;
869 int x2 = ter->mNodes[i].x - iCameraPosX;
870 int y2 = ter->mNodes[i].y;
871 #if LCD_DEPTH > 2
872 rb->lcd_set_foreground(LCD_RGBPACK(100,255,100));
873 #elif LCD_DEPTH == 2
874 rb->lcd_set_foreground(LCD_DARKGRAY);
875 #endif
877 rb->lcd_drawline(SCALE(x), SCALE(y), SCALE(x2), SCALE(y2));
879 xlcd_filltriangle(SCALE(x), SCALE(y), SCALE(x2), SCALE(y2),
880 SCALE(x2), SCALE(ay));
881 xlcd_filltriangle(SCALE(x), SCALE(ay), SCALE(x2), SCALE(y2),
882 SCALE(x2), SCALE(ay));
884 if (ay == 0)
885 xlcd_filltriangle(SCALE(x), SCALE(ay), SCALE(x), SCALE(y),
886 SCALE(x2), SCALE(y2 / 2));
887 else
888 xlcd_filltriangle(SCALE(x), SCALE(ay), SCALE(x), SCALE(y),
889 SCALE(x2), SCALE((LCD_HEIGHT*SIZE) -
890 ((LCD_HEIGHT*SIZE) - y2) / 2));
892 oldx = x;
893 i++;
899 void chopper_load(bool newgame)
902 int i;
903 int g;
905 if (newgame) {
906 iScreenX = LCD_WIDTH * SIZE;
907 iScreenY = LCD_HEIGHT * SIZE;
908 blockh = iScreenY / 5;
909 blockw = iScreenX / 20;
910 iPlayerAlive = 1;
911 score = 0;
913 iRotorOffset = 0;
914 iPlayerPosX = 60;
915 iPlayerPosY = (iScreenY * 4) / 10;
916 iLastBlockPlacedPosX = 0;
917 iGravityTimerCountdown = 2;
918 chopCounter = 0;
919 iPlayerSpeedX = 3;
920 iPlayerSpeedY = 0;
921 iCameraPosX = 30;
923 for (i=0; i < NUMBER_OF_PARTICLES; i++)
924 mParticles[i].bIsActive = 0;
926 for (i=0; i < NUMBER_OF_BLOCKS; i++)
927 mBlocks[i].bIsActive = 0;
929 g = iScreenY - 10;
930 chopClearTerrain(&mGround);
932 for (i=0; i < MAX_TERRAIN_NODES; i++)
933 chopAddTerrainNode(&mGround,i * 30,g - iR(0,20));
935 if (chopUpdateTerrainRecycling(&mGround) == 1)
936 /* mirror the sky if we've changed the ground */
937 chopCopyTerrain(&mGround, &mRoof, 0, - ( (iScreenY * 3) / 4));
939 iLevelMode = LEVEL_MODE_NORMAL;
940 if (iLevelMode == LEVEL_MODE_NORMAL)
941 /* make it a bit more exciting, cause it's easy terrain... */
942 iPlayerSpeedX *= 2;
945 /* this is the plugin entry point */
946 enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
948 (void)parameter;
949 rb = api;
950 int ret;
952 rb->lcd_setfont(FONT_SYSFIXED);
953 #if LCD_DEPTH > 1
954 rb->lcd_set_backdrop(NULL);
955 #endif
956 #ifdef HAVE_LCD_COLOR
957 rb->lcd_set_background(LCD_BLACK);
958 rb->lcd_set_foreground(LCD_WHITE);
959 #endif
961 /* Turn off backlight timeout */
962 backlight_force_on(rb); /* backlight control in lib/helper.c */
964 rb->srand( *rb->current_tick );
966 xlcd_init(rb);
967 configfile_init(rb);
968 configfile_load(CFG_FILE, config, 1, 0);
970 chopper_load(true);
971 ret = chopGameLoop();
973 configfile_save(CFG_FILE, config, 1, 0);
975 rb->lcd_setfont(FONT_UI);
976 /* Turn on backlight timeout (revert to settings) */
977 backlight_use_settings(rb); /* backlight control in lib/helper.c */
979 return ret;