Get rid of the 'center' parameter for splashes. There were only 2 of almost 500 splas...
[Rockbox.git] / apps / plugins / chopper.c
blob1efe20294650be111854aab2b45741a75c492e98
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"
25 PLUGIN_HEADER
27 /*Still To do:*/
28 /* - Make original speed and further increases in speed depend more on screen size*/
29 /* - attempt to make the tunnels get narrower as the game goes on*/
30 /* - make the chopper look better, maybe a picture, and scale according to screen size*/
31 /* - use textures for the color screens for background and terrain, eg stars on background*/
32 /* - allow choice of different levels [later: different screen themes]*/
33 /* - better high score handling, improved screen etc. */
35 #if (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD)
37 #define QUIT BUTTON_OFF
38 #define ACTION BUTTON_UP
39 #define ACTION2 BUTTON_SELECT
40 #define ACTIONTEXT "SELECT"
42 #elif (CONFIG_KEYPAD == IPOD_3G_PAD) || \
43 (CONFIG_KEYPAD == IPOD_4G_PAD)
45 #define QUIT BUTTON_MENU
46 #define ACTION BUTTON_SELECT
47 #define ACTIONTEXT "SELECT"
49 #elif CONFIG_KEYPAD == IAUDIO_X5_PAD /* grayscale at the moment */
51 #define QUIT BUTTON_POWER
52 #define ACTION BUTTON_UP
53 #define ACTION2 BUTTON_SELECT
54 #define ACTIONTEXT "SELECT"
56 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
57 #define QUIT BUTTON_POWER
58 #define ACTION BUTTON_RIGHT
59 #define ACTIONTEXT "RIGHT"
61 #elif CONFIG_KEYPAD == SANSA_E200_PAD
62 #define QUIT BUTTON_POWER
63 #define ACTION BUTTON_SELECT
64 #define ACTIONTEXT "SELECT"
66 #elif CONFIG_KEYPAD == GIGABEAT_PAD
67 #define QUIT BUTTON_MENU
68 #define ACTION BUTTON_SELECT
69 #define ACTIONTEXT "SELECT"
71 #elif CONFIG_KEYPAD == RECORDER_PAD
72 #define QUIT BUTTON_OFF
73 #define ACTION BUTTON_PLAY
74 #define ACTIONTEXT "PLAY"
76 #elif CONFIG_KEYPAD == ONDIO_PAD
77 #define QUIT BUTTON_OFF
78 #define ACTION BUTTON_UP
79 #define ACTION2 BUTTON_MENU
80 #define ACTIONTEXT "UP"
82 #else
83 #error Unsupported keypad
84 #endif
86 static struct plugin_api* rb;
88 #define NUMBER_OF_BLOCKS 8
89 #define NUMBER_OF_PARTICLES 3
90 #define MAX_TERRAIN_NODES 15
92 #define LEVEL_MODE_NORMAL 0
93 #define LEVEL_MODE_STEEP 1
95 #if LCD_WIDTH <= 112
96 #define CYCLETIME 100
97 #define SCALE(x) ((x)==1 ? (x) : ((x) >> 1))
98 #define SIZE 2
99 #else
100 #define CYCLETIME 60
101 #define SCALE(x) (x)
102 #define SIZE 1
103 #endif
105 /*Chopper's local variables to track the terrain position etc*/
106 static int chopCounter;
107 static int iRotorOffset;
108 static int iScreenX;
109 static int iScreenY;
110 static int iPlayerPosX;
111 static int iPlayerPosY;
112 static int iCameraPosX;
113 static int iPlayerSpeedX;
114 static int iPlayerSpeedY;
115 static int iLastBlockPlacedPosX;
116 static int iGravityTimerCountdown;
117 static int iPlayerAlive;
118 static int iLevelMode;
119 static int blockh,blockw;
120 static int highscore;
121 static int score;
123 #define CFG_FILE "chopper.cfg"
124 #define MAX_POINTS 50000
125 static struct configdata config[] =
127 {TYPE_INT, 0, MAX_POINTS, &highscore, "highscore", NULL, NULL}
130 struct CBlock
132 int iWorldX;
133 int iWorldY;
135 int iSizeX;
136 int iSizeY;
138 int bIsActive;
141 struct CParticle
143 int iWorldX;
144 int iWorldY;
146 int iSpeedX;
147 int iSpeedY;
149 int bIsActive;
152 struct CTerrainNode
154 int x;
155 int y;
158 struct CTerrain
160 struct CTerrainNode mNodes[MAX_TERRAIN_NODES];
161 int iNodesCount;
162 int iLastNodePlacedPosX;
165 struct CBlock mBlocks[NUMBER_OF_BLOCKS];
166 struct CParticle mParticles[NUMBER_OF_PARTICLES];
168 struct CTerrain mGround;
169 struct CTerrain mRoof;
171 /*Function declarations*/
172 static void chopDrawParticle(struct CParticle *mParticle);
173 static void chopDrawBlock(struct CBlock *mBlock);
174 static void chopRenderTerrain(struct CTerrain *ter);
175 void chopper_load(bool newgame);
176 void cleanup_chopper(void);
178 static void chopDrawPlayer(int x,int y) /* These are SCREEN coords, not world! */
181 #if LCD_DEPTH > 2
182 rb->lcd_set_foreground(LCD_RGBPACK(50,50,200));
183 #elif LCD_DEPTH == 2
184 rb->lcd_set_foreground(LCD_DARKGRAY);
185 #endif
186 rb->lcd_fillrect(SCALE(x+6), SCALE(y+2), SCALE(12), SCALE(9));
187 rb->lcd_fillrect(SCALE(x-3), SCALE(y+6), SCALE(20), SCALE(3));
189 #if LCD_DEPTH > 2
190 rb->lcd_set_foreground(LCD_RGBPACK(50,50,50));
191 #elif LCD_DEPTH == 2
192 rb->lcd_set_foreground(LCD_DARKGRAY);
193 #endif
194 rb->lcd_fillrect(SCALE(x+10), SCALE(y), SCALE(2), SCALE(3));
195 rb->lcd_fillrect(SCALE(x+10), SCALE(y), SCALE(1), SCALE(3));
197 #if LCD_DEPTH > 2
198 rb->lcd_set_foreground(LCD_RGBPACK(40,40,100));
199 #elif LCD_DEPTH == 2
200 rb->lcd_set_foreground(LCD_BLACK);
201 #endif
202 rb->lcd_drawline(SCALE(x), SCALE(y+iRotorOffset), SCALE(x+20), SCALE(y-iRotorOffset));
204 #if LCD_DEPTH > 2
205 rb->lcd_set_foreground(LCD_RGBPACK(20,20,50));
206 #elif LCD_DEPTH == 2
207 rb->lcd_set_foreground(LCD_BLACK);
208 #endif
209 rb->lcd_fillrect(SCALE(x - 2), SCALE(y + 5), SCALE(2), SCALE(5));
213 static void chopClearTerrain(struct CTerrain *ter)
215 ter->iNodesCount = 0;
219 int iR(int low,int high)
221 return low+rb->rand()%(high-low+1);
224 static void chopCopyTerrain(struct CTerrain *src, struct CTerrain *dest,
225 int xOffset,int yOffset)
227 int i=0;
229 while(i < src->iNodesCount)
231 dest->mNodes[i].x = src->mNodes[i].x + xOffset;
232 dest->mNodes[i].y = src->mNodes[i].y + yOffset;
234 i++;
237 dest->iNodesCount = src->iNodesCount;
238 dest->iLastNodePlacedPosX = src->iLastNodePlacedPosX;
242 static void chopAddTerrainNode(struct CTerrain *ter, int x, int y)
244 int i=0;
246 if(ter->iNodesCount + 1 >= MAX_TERRAIN_NODES)
248 /* DEBUGF("ERROR: Not enough nodes!\n"); */
249 return;
252 ter->iNodesCount++;
254 i = ter->iNodesCount - 1;
256 ter->mNodes[i].x = x;
257 ter->mNodes[i].y= y;
259 ter->iLastNodePlacedPosX = x;
263 static void chopTerrainNodeDeleteAndShift(struct CTerrain *ter,int nodeIndex)
265 int i=nodeIndex;
267 while( i < ter->iNodesCount )
269 ter->mNodes[i - 1] = ter->mNodes[i];
270 i++;
273 ter->iNodesCount--;
278 int chopUpdateTerrainRecycling(struct CTerrain *ter)
280 int i=1;
281 int ret = 0;
282 int iNewNodePos,g,v;
283 while(i < ter->iNodesCount)
286 if( iCameraPosX > ter->mNodes[i].x)
289 chopTerrainNodeDeleteAndShift(ter,i);
291 iNewNodePos = ter->iLastNodePlacedPosX + 50;
292 g = iScreenY - 10;
294 v = 3*iPlayerSpeedX;
295 if(v>50)
296 v=50;
297 if(iLevelMode == LEVEL_MODE_STEEP)
298 v*=5;
300 chopAddTerrainNode(ter,iNewNodePos,g - iR(-v,v));
301 ret=1;
305 i++;
309 return 1;
312 int chopTerrainHeightAtPoint(struct CTerrain *ter, int pX)
315 int iNodeIndexOne=0,iNodeIndexTwo=0, h, terY1, terY2, terX1, terX2, a, b;
316 float c,d;
318 int i=0;
319 for(i=1;i<MAX_TERRAIN_NODES;i++)
321 if(ter->mNodes[i].x > pX)
323 iNodeIndexOne = i - 1;
324 break;
329 iNodeIndexTwo = iNodeIndexOne + 1;
330 terY1 = ter->mNodes[iNodeIndexOne].y;
331 terY2 = ter->mNodes[iNodeIndexTwo].y;
333 terX1 = 0;
334 terX2 = ter->mNodes[iNodeIndexTwo].x - ter->mNodes[iNodeIndexOne].x;
336 pX-= ter->mNodes[iNodeIndexOne].x;
338 a = terY2 - terY1;
339 b = terX2;
340 c = pX;
341 d = (c/b) * a;
343 h = d + terY1;
345 return h;
349 int chopPointInTerrain(struct CTerrain *ter, int pX, int pY, int iTestType)
351 int h = chopTerrainHeightAtPoint(ter, pX);
353 if(iTestType == 0)
354 return (pY > h);
355 else
356 return (pY < h);
359 static void chopAddBlock(int x,int y,int sx,int sy, int indexOverride)
361 int i=0;
363 if(indexOverride < 0)
365 while(mBlocks[i].bIsActive && i < NUMBER_OF_BLOCKS)
366 i++;
367 if(i==NUMBER_OF_BLOCKS)
369 DEBUGF("No blocks!\n");
370 return;
373 else
374 i = indexOverride;
376 mBlocks[i].bIsActive = 1;
377 mBlocks[i].iWorldX = x;
378 mBlocks[i].iWorldY = y;
379 mBlocks[i].iSizeX = sx;
380 mBlocks[i].iSizeY = sy;
382 iLastBlockPlacedPosX = x;
385 static void chopAddParticle(int x,int y,int sx,int sy)
387 int i=0;
389 while(mParticles[i].bIsActive && i < NUMBER_OF_PARTICLES)
390 i++;
392 if(i==NUMBER_OF_PARTICLES)
393 return;
395 mParticles[i].bIsActive = 1;
396 mParticles[i].iWorldX = x;
397 mParticles[i].iWorldY = y;
398 mParticles[i].iSpeedX = sx;
399 mParticles[i].iSpeedY = sy;
402 static void chopGenerateBlockIfNeeded(void)
404 int i=0;
405 int DistSpeedX = iPlayerSpeedX * 5;
406 if(DistSpeedX<200) DistSpeedX = 200;
408 while(i < NUMBER_OF_BLOCKS)
410 if(!mBlocks[i].bIsActive)
412 int iX,iY,sX,sY;
414 iX = iLastBlockPlacedPosX + (350-DistSpeedX);
415 sX = blockw;
417 iY = iR(0,iScreenY);
418 sY = blockh + iR(1,blockh/3);
420 chopAddBlock(iX,iY,sX,sY,i);
423 i++;
428 static int chopBlockCollideWithPlayer(struct CBlock *mBlock)
430 int px = iPlayerPosX;
431 int py = iPlayerPosY;
433 int x = mBlock->iWorldX-17;
434 int y = mBlock->iWorldY-11;
436 int x2 = x + mBlock->iSizeX+17;
437 int y2 = y + mBlock->iSizeY+11;
439 if(px>x && px<x2)
441 if(py>y && py<y2)
443 return 1;
447 return 0;
450 static int chopBlockOffscreen(struct CBlock *mBlock)
452 if(mBlock->iWorldX + mBlock->iSizeX < iCameraPosX)
453 return 1;
454 else
455 return 0;
458 static int chopParticleOffscreen(struct CParticle *mParticle)
460 if (mParticle->iWorldX < iCameraPosX || mParticle->iWorldY < 0 ||
461 mParticle->iWorldY > iScreenY || mParticle->iWorldX > iCameraPosX +
462 iScreenX)
464 return 1;
466 else
467 return 0;
470 static void chopKillPlayer(void)
472 int i, button;
474 for (i = 0; i < NUMBER_OF_PARTICLES; i++) {
475 mParticles[i].bIsActive = 0;
476 chopAddParticle(iPlayerPosX + iR(0,20), iPlayerPosY + iR(0,20),
477 iR(-2,2), iR(-2,2));
480 iPlayerAlive--;
482 if (iPlayerAlive == 0) {
483 rb->lcd_set_drawmode(DRMODE_FG);
484 #if LCD_DEPTH >= 2
485 rb->lcd_set_foreground(LCD_LIGHTGRAY);
486 #endif
487 rb->splash(HZ, "Game Over");
489 if (score > highscore) {
490 char scoretext[30];
491 highscore = score;
492 rb->snprintf(scoretext, sizeof(scoretext), "New High Score: %d",
493 highscore);
494 rb->splash(HZ*2, scoretext);
497 rb->splash(HZ/4, "Press " ACTIONTEXT " to continue");
498 rb->lcd_update();
500 rb->lcd_set_drawmode(DRMODE_SOLID);
502 while (true) {
503 button = rb->button_get(true);
504 if (button == ACTION
505 #ifdef ACTION2
506 || button == ACTION2
507 #endif
509 while (true) {
510 button = rb->button_get(true);
511 if (button == (ACTION | BUTTON_REL)
512 #ifdef ACTION2
513 || button == (ACTION2 | BUTTON_REL)
514 #endif
516 chopper_load(true);
517 return;
523 } else
524 chopper_load(false);
528 static void chopDrawTheWorld(void)
530 int i=0;
532 while(i < NUMBER_OF_BLOCKS)
534 if(mBlocks[i].bIsActive)
536 if(chopBlockOffscreen(&mBlocks[i]) == 1)
537 mBlocks[i].bIsActive = 0;
538 else
539 chopDrawBlock(&mBlocks[i]);
542 i++;
545 i=0;
547 while(i < NUMBER_OF_PARTICLES)
549 if(mParticles[i].bIsActive)
551 if(chopParticleOffscreen(&mParticles[i]) == 1)
552 mParticles[i].bIsActive = 0;
553 else
554 chopDrawParticle(&mParticles[i]);
557 i++;
560 chopRenderTerrain(&mGround);
561 chopRenderTerrain(&mRoof);
565 static void chopDrawParticle(struct CParticle *mParticle)
568 int iPosX = (mParticle->iWorldX - iCameraPosX);
569 int iPosY = (mParticle->iWorldY);
570 #if LCD_DEPTH > 2
571 rb->lcd_set_foreground(LCD_RGBPACK(192,192,192));
572 #elif LCD_DEPTH == 2
573 rb->lcd_set_foreground(LCD_LIGHTGRAY);
574 #endif
575 rb->lcd_fillrect(SCALE(iPosX), SCALE(iPosY), SCALE(3), SCALE(3));
579 static void chopDrawScene(void)
581 char s[30];
582 int w;
583 #if LCD_DEPTH > 2
584 rb->lcd_set_background(LCD_BLACK);
585 #elif LCD_DEPTH == 2
586 rb->lcd_set_background(LCD_WHITE);
587 #endif
588 rb->lcd_clear_display();
590 chopDrawTheWorld();
591 chopDrawPlayer(iPlayerPosX - iCameraPosX, iPlayerPosY);
593 score = -20 + iPlayerPosX/3;
595 #if LCD_DEPTH == 1
596 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
597 #else
598 rb->lcd_set_drawmode(DRMODE_FG);
599 #endif
601 #if LCD_DEPTH > 2
602 rb->lcd_set_foreground(LCD_BLACK);
603 #elif LCD_DEPTH == 2
604 rb->lcd_set_foreground(LCD_WHITE);
605 #endif
607 #if LCD_WIDTH <= 128
608 rb->snprintf(s, sizeof(s), "Dist: %d", score);
609 rb->lcd_putsxy(1, 1, s);
610 rb->snprintf(s, sizeof(s), "Hi: %d", highscore);
611 rb->lcd_getstringsize(s, &w, NULL);
612 rb->lcd_putsxy(LCD_WIDTH - 1 - w, 1, s);
613 #else
614 rb->snprintf(s, sizeof(s), "Distance: %d", score);
615 rb->lcd_putsxy(2, 2, s);
616 rb->snprintf(s, sizeof(s), "Best: %d", highscore);
617 rb->lcd_getstringsize(s, &w, NULL);
618 rb->lcd_putsxy(LCD_WIDTH - 2 - w, 2, s);
619 #endif
620 rb->lcd_set_drawmode(DRMODE_SOLID);
622 rb->lcd_update();
625 static int chopMenu(int menunum)
627 int m;
628 int result;
629 int res = 0;
630 bool menu_quit = false;
632 static const struct menu_item items[] = {
633 { "Start New Game", NULL },
634 { "Resume Game", NULL },
635 { "Level", NULL },
636 { "Quit", NULL },
639 static const struct opt_items levels[2] = {
640 { "Normal", -1 },
641 { "Steep", -1 },
644 #ifdef HAVE_LCD_COLOR
645 rb->lcd_set_foreground(LCD_WHITE);
646 rb->lcd_set_background(LCD_BLACK);
647 #elif LCD_DEPTH == 2
648 rb->lcd_set_foreground(LCD_BLACK);
649 rb->lcd_set_background(LCD_WHITE);
650 #endif
652 rb->lcd_clear_display();
654 m = rb->menu_init(items, sizeof(items) / sizeof(*items),
655 NULL, NULL, NULL, NULL);
657 while (!menu_quit) {
658 result=rb->menu_show(m);
659 switch (result)
661 case 0: /* Start New Game */
662 menu_quit=true;
663 chopper_load(true);
664 res = -1;
665 break;
666 case 1: /* Resume Game */
667 if(menunum==1) {
668 menu_quit=true;
669 res = -1;
670 } else if(menunum==0){
671 rb->splash(HZ, "No game to resume");
673 break;
674 case 2:
675 rb->set_option("Level", &iLevelMode, INT, levels, 2, NULL);
676 break;
677 case 3:
678 menu_quit=true;
679 res = PLUGIN_OK;
680 break;
681 case MENU_ATTACHED_USB:
682 menu_quit=true;
683 res = PLUGIN_USB_CONNECTED;
684 break;
687 rb->lcd_clear_display();
688 rb->menu_exit(m);
689 return res;
692 static int chopGameLoop(void)
694 int move_button, ret;
695 bool exit=false;
696 int end, i=0, bdelay=0, last_button=BUTTON_NONE;
698 if (chopUpdateTerrainRecycling(&mGround) == 1)
699 /* mirror the sky if we've changed the ground */
700 chopCopyTerrain(&mGround, &mRoof, 0, - ( (iScreenY * 3) / 4));
702 ret = chopMenu(0);
703 if (ret != -1)
704 return PLUGIN_OK;
706 chopDrawScene();
708 while (!exit) {
710 end = *rb->current_tick + (CYCLETIME * HZ) / 1000;
712 if(chopUpdateTerrainRecycling(&mGround) == 1)
713 /* mirror the sky if we've changed the ground */
714 chopCopyTerrain(&mGround, &mRoof, 0, - ( (iScreenY * 3) / 4));
716 iRotorOffset = iR(-1,1);
718 /* We need to have this here so particles move when we're dead */
720 for (i=0; i < NUMBER_OF_PARTICLES; i++)
721 if(mParticles[i].bIsActive == 1)
723 mParticles[i].iWorldX += mParticles[i].iSpeedX;
724 mParticles[i].iWorldY += mParticles[i].iSpeedY;
727 /* Redraw the main window: */
728 chopDrawScene();
731 iGravityTimerCountdown--;
733 if(iGravityTimerCountdown <= 0)
735 iGravityTimerCountdown = 3;
736 chopAddParticle(iPlayerPosX, iPlayerPosY+5, 0, 0);
739 if(iLevelMode == LEVEL_MODE_NORMAL)
740 chopGenerateBlockIfNeeded();
743 move_button=rb->button_status();
744 if (rb->button_get(false) == QUIT) {
745 ret = chopMenu(1);
746 if (ret != -1)
747 return PLUGIN_OK;
748 bdelay = 0;
749 last_button = BUTTON_NONE;
750 move_button = BUTTON_NONE;
753 switch (move_button) {
754 case ACTION:
755 #ifdef ACTION2
756 case ACTION2:
757 #endif
758 if (last_button != ACTION
759 #ifdef ACTION2
760 && last_button != ACTION2
761 #endif
763 bdelay = -2;
764 if (bdelay == 0)
765 iPlayerSpeedY = -3;
766 break;
768 default:
769 if (last_button == ACTION
770 #ifdef ACTION2
771 || last_button == ACTION2
772 #endif
774 bdelay = 3;
775 if (bdelay == 0)
776 iPlayerSpeedY = 4;
778 if (rb->default_event_handler(move_button) == SYS_USB_CONNECTED)
779 return PLUGIN_USB_CONNECTED;
780 break;
782 last_button = move_button;
784 if (bdelay < 0) {
785 iPlayerSpeedY = bdelay;
786 bdelay++;
787 } else if (bdelay > 0) {
788 iPlayerSpeedY = bdelay;
789 bdelay--;
792 iCameraPosX = iPlayerPosX - 25;
793 iPlayerPosX += iPlayerSpeedX;
794 iPlayerPosY += iPlayerSpeedY;
796 chopCounter++;
797 /* increase speed as we go along */
798 if (chopCounter == 100){
799 iPlayerSpeedX++;
800 chopCounter=0;
803 if (iPlayerPosY > iScreenY-10 || iPlayerPosY < -5 ||
804 chopPointInTerrain(&mGround, iPlayerPosX, iPlayerPosY + 10, 0) ||
805 chopPointInTerrain(&mRoof, iPlayerPosX ,iPlayerPosY, 1))
807 chopKillPlayer();
808 chopDrawScene();
809 ret = chopMenu(0);
810 if (ret != -1)
811 return ret;
814 for (i=0; i < NUMBER_OF_BLOCKS; i++)
815 if(mBlocks[i].bIsActive == 1)
816 if(chopBlockCollideWithPlayer(&mBlocks[i])) {
817 chopKillPlayer();
818 chopDrawScene();
819 ret = chopMenu(0);
820 if (ret != -1)
821 return ret;
824 if (end > *rb->current_tick)
825 rb->sleep(end-*rb->current_tick);
826 else
827 rb->yield();
830 return PLUGIN_OK;
833 static void chopDrawBlock(struct CBlock *mBlock)
835 int iPosX = (mBlock->iWorldX - iCameraPosX);
836 int iPosY = (mBlock->iWorldY);
837 #if LCD_DEPTH > 2
838 rb->lcd_set_foreground(LCD_RGBPACK(100,255,100));
839 #elif LCD_DEPTH == 2
840 rb->lcd_set_foreground(LCD_BLACK);
841 #endif
842 rb->lcd_fillrect(SCALE(iPosX), SCALE(iPosY), SCALE(mBlock->iSizeX),
843 SCALE(mBlock->iSizeY));
847 static void chopRenderTerrain(struct CTerrain *ter)
850 int i=1;
852 int oldx=0;
854 int ay=0;
855 if(ter->mNodes[0].y < (LCD_HEIGHT*SIZE)/2)
856 ay=0;
857 else
858 ay=(LCD_HEIGHT*SIZE);
860 while(i < ter->iNodesCount && oldx < iScreenX)
863 int x = ter->mNodes[i-1].x - iCameraPosX;
864 int y = ter->mNodes[i-1].y;
866 int x2 = ter->mNodes[i].x - iCameraPosX;
867 int y2 = ter->mNodes[i].y;
868 #if LCD_DEPTH > 2
869 rb->lcd_set_foreground(LCD_RGBPACK(100,255,100));
870 #elif LCD_DEPTH == 2
871 rb->lcd_set_foreground(LCD_DARKGRAY);
872 #endif
874 rb->lcd_drawline(SCALE(x), SCALE(y), SCALE(x2), SCALE(y2));
876 xlcd_filltriangle(SCALE(x), SCALE(y), SCALE(x2), SCALE(y2), SCALE(x2), SCALE(ay));
877 xlcd_filltriangle(SCALE(x), SCALE(ay), SCALE(x2), SCALE(y2), SCALE(x2), SCALE(ay));
879 if (ay == 0)
880 xlcd_filltriangle(SCALE(x), SCALE(ay), SCALE(x), SCALE(y), SCALE(x2), SCALE(y2 / 2));
881 else
882 xlcd_filltriangle(SCALE(x), SCALE(ay), SCALE(x), SCALE(y), SCALE(x2), SCALE((LCD_HEIGHT*SIZE) - ((LCD_HEIGHT*SIZE) - y2) / 2));
884 oldx = x;
885 i++;
891 void chopper_load(bool newgame)
894 int i;
895 int g;
897 if (newgame) {
898 iScreenX = LCD_WIDTH * SIZE;
899 iScreenY = LCD_HEIGHT * SIZE;
900 blockh = iScreenY / 5;
901 blockw = iScreenX / 20;
902 iPlayerAlive = 1;
903 score = 0;
905 iRotorOffset = 0;
906 iPlayerPosX = 60;
907 iPlayerPosY = (iScreenY * 4) / 10;
908 iLastBlockPlacedPosX = 0;
909 iGravityTimerCountdown = 2;
910 chopCounter = 0;
911 iPlayerSpeedX = 3;
912 iPlayerSpeedY = 0;
913 iCameraPosX = 30;
915 for (i=0; i < NUMBER_OF_PARTICLES; i++)
916 mParticles[i].bIsActive = 0;
918 for (i=0; i < NUMBER_OF_BLOCKS; i++)
919 mBlocks[i].bIsActive = 0;
921 g = iScreenY - 10;
922 chopClearTerrain(&mGround);
924 for (i=0; i < MAX_TERRAIN_NODES; i++)
925 chopAddTerrainNode(&mGround,i * 30,g - iR(0,20));
927 if (chopUpdateTerrainRecycling(&mGround) == 1)
928 /* mirror the sky if we've changed the ground */
929 chopCopyTerrain(&mGround, &mRoof, 0, - ( (iScreenY * 3) / 4));
931 iLevelMode = LEVEL_MODE_NORMAL;
932 if (iLevelMode == LEVEL_MODE_NORMAL)
933 /* make it a bit more exciting, cause it's easy terrain... */
934 iPlayerSpeedX *= 2;
937 /* this is the plugin entry point */
938 enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
940 (void)parameter;
941 rb = api;
942 int ret;
944 rb->lcd_setfont(FONT_SYSFIXED);
945 #if LCD_DEPTH > 1
946 rb->lcd_set_backdrop(NULL);
947 #endif
948 #ifdef HAVE_LCD_COLOR
949 rb->lcd_set_background(LCD_BLACK);
950 rb->lcd_set_foreground(LCD_WHITE);
951 #endif
953 /* Permanently enable the backlight (unless the user has turned it off) */
954 if (rb->global_settings->backlight_timeout > 0)
955 rb->backlight_set_timeout(1);
957 rb->srand( *rb->current_tick );
959 xlcd_init(rb);
960 configfile_init(rb);
961 configfile_load(CFG_FILE, config, 1, 0);
963 chopper_load(true);
964 ret = chopGameLoop();
966 configfile_save(CFG_FILE, config, 1, 0);
968 /* Restore user's original backlight setting */
969 rb->lcd_setfont(FONT_UI);
970 rb->backlight_set_timeout(rb->global_settings->backlight_timeout);
972 return ret;