Prepare new maemo release
[maemo-rb.git] / apps / plugins / chopper.c
blob77af9756eb5f53c500dcd9966d1f2b1788b4ea45
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 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 ****************************************************************************/
23 #include "plugin.h"
24 #include "lib/xlcd.h"
25 #include "lib/configfile.h"
26 #include "lib/helper.h"
27 #include "lib/playback_control.h"
32 Still To do:
33 - Make original speed and further increases in speed depend more on screen size
34 - attempt to make the tunnels get narrower as the game goes on
35 - make the chopper look better, maybe a picture, and scale according
36 to screen size
37 - use textures for the color screens for background and terrain,
38 eg stars on background
39 - allow choice of different levels [later: different screen themes]
40 - better high score handling, improved screen etc.
43 #if (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD)
45 #define QUIT BUTTON_OFF
46 #define ACTION BUTTON_UP
47 #define ACTION2 BUTTON_SELECT
48 #define ACTIONTEXT "SELECT"
50 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
51 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
52 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
54 #define QUIT BUTTON_MENU
55 #define ACTION BUTTON_SELECT
56 #define ACTIONTEXT "SELECT"
58 #elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD /* grayscale at the moment */
60 #define QUIT BUTTON_POWER
61 #define ACTION BUTTON_UP
62 #define ACTION2 BUTTON_SELECT
63 #define ACTIONTEXT "SELECT"
65 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
66 #define QUIT BUTTON_POWER
67 #define ACTION BUTTON_RIGHT
68 #define ACTIONTEXT "RIGHT"
70 #elif (CONFIG_KEYPAD == SANSA_E200_PAD) || \
71 (CONFIG_KEYPAD == SANSA_C200_PAD) || \
72 (CONFIG_KEYPAD == SANSA_CLIP_PAD) || \
73 (CONFIG_KEYPAD == SANSA_M200_PAD) || \
74 (CONFIG_KEYPAD == SANSA_CONNECT_PAD)
75 #define QUIT BUTTON_POWER
76 #define ACTION BUTTON_SELECT
77 #define ACTIONTEXT "SELECT"
79 #elif (CONFIG_KEYPAD == SANSA_FUZE_PAD)
80 #define QUIT BUTTON_HOME
81 #define ACTION BUTTON_SELECT
82 #define ACTIONTEXT "SELECT"
84 #elif CONFIG_KEYPAD == GIGABEAT_PAD
85 #define QUIT BUTTON_MENU
86 #define ACTION BUTTON_SELECT
87 #define ACTIONTEXT "SELECT"
89 #elif CONFIG_KEYPAD == RECORDER_PAD
90 #define QUIT BUTTON_OFF
91 #define ACTION BUTTON_PLAY
92 #define ACTIONTEXT "PLAY"
94 #elif CONFIG_KEYPAD == ONDIO_PAD
95 #define QUIT BUTTON_OFF
96 #define ACTION BUTTON_UP
97 #define ACTION2 BUTTON_MENU
98 #define ACTIONTEXT "UP"
100 #elif CONFIG_KEYPAD == GIGABEAT_S_PAD \
101 || CONFIG_KEYPAD == SAMSUNG_YPR0_PAD
102 #define QUIT BUTTON_BACK
103 #define ACTION BUTTON_SELECT
104 #define ACTION2 BUTTON_MENU
105 #define ACTIONTEXT "SELECT"
107 #elif CONFIG_KEYPAD == MROBE100_PAD
108 #define QUIT BUTTON_POWER
109 #define ACTION BUTTON_SELECT
110 #define ACTIONTEXT "SELECT"
112 #elif CONFIG_KEYPAD == IAUDIO_M3_PAD
113 #define QUIT BUTTON_RC_REC
114 #define ACTION BUTTON_RC_PLAY
115 #define ACTION2 BUTTON_RC_MODE
116 #define ACTIONTEXT "PLAY"
118 #elif CONFIG_KEYPAD == COWON_D2_PAD
119 #define QUIT BUTTON_POWER
120 #define ACTION2 BUTTON_PLUS
122 #elif CONFIG_KEYPAD == IAUDIO67_PAD
123 #define QUIT BUTTON_POWER
124 #define ACTION BUTTON_PLAY
125 #define ACTION2 BUTTON_STOP
126 #define ACTIONTEXT "PLAY"
128 #elif CONFIG_KEYPAD == CREATIVEZVM_PAD
129 #define QUIT BUTTON_BACK
130 #define ACTION BUTTON_UP
131 #define ACTION2 BUTTON_MENU
132 #define ACTIONTEXT "UP"
134 #elif CONFIG_KEYPAD == PHILIPS_HDD1630_PAD
135 #define QUIT BUTTON_POWER
136 #define ACTION BUTTON_MENU
137 #define ACTION2 BUTTON_SELECT
138 #define ACTIONTEXT "MENU"
140 #elif CONFIG_KEYPAD == PHILIPS_HDD6330_PAD
141 #define QUIT BUTTON_POWER
142 #define ACTION BUTTON_MENU
143 #define ACTION2 BUTTON_PLAY
144 #define ACTIONTEXT "MENU"
146 #elif CONFIG_KEYPAD == PHILIPS_SA9200_PAD
147 #define QUIT BUTTON_POWER
148 #define ACTION BUTTON_MENU
149 #define ACTION2 BUTTON_PLAY
150 #define ACTIONTEXT "MENU"
152 #elif CONFIG_KEYPAD == ONDAVX747_PAD || \
153 CONFIG_KEYPAD == ONDAVX777_PAD || \
154 CONFIG_KEYPAD == MROBE500_PAD
155 #define QUIT BUTTON_POWER
157 #elif CONFIG_KEYPAD == SAMSUNG_YH_PAD
158 #define QUIT BUTTON_LEFT
159 #define ACTION BUTTON_RIGHT
160 #define ACTIONTEXT "RIGHT"
162 #elif CONFIG_KEYPAD == PBELL_VIBE500_PAD
163 #define QUIT BUTTON_REC
164 #define ACTION BUTTON_PLAY
165 #define ACTION2 BUTTON_UP
166 #define ACTIONTEXT "PLAY"
168 #elif CONFIG_KEYPAD == MPIO_HD200_PAD
169 #define QUIT (BUTTON_REC|BUTTON_PLAY)
170 #define ACTION BUTTON_FUNC
171 #define ACTIONTEXT "FUNC"
173 #elif CONFIG_KEYPAD == MPIO_HD300_PAD
174 #define QUIT (BUTTON_MENU|BUTTON_REPEAT)
175 #define ACTION BUTTON_ENTER
176 #define ACTIONTEXT "ENTER"
178 #elif CONFIG_KEYPAD == SANSA_FUZEPLUS_PAD
179 #define QUIT BUTTON_POWER
180 #define ACTION BUTTON_SELECT
181 #define ACTIONTEXT "SELECT"
183 #elif (CONFIG_KEYPAD == HM60X_PAD) || \
184 (CONFIG_KEYPAD == HM801_PAD)
185 #define QUIT BUTTON_POWER
186 #define ACTION BUTTON_SELECT
187 #define ACTIONTEXT "SELECT"
189 #elif !defined(HAVE_TOUCHSCREEN)
190 #error No keymap defined!
191 #endif
193 #ifdef HAVE_TOUCHSCREEN
194 #ifndef QUIT
195 #define QUIT BUTTON_TOPLEFT
196 #endif
197 #ifndef ACTION
198 #define ACTION BUTTON_BOTTOMLEFT
199 #endif
200 #ifndef ACTION2
201 #define ACTION2 BUTTON_BOTTOMRIGHT
202 #endif
203 #ifndef ACTIONTEXT
204 #define ACTIONTEXT "BOTTOMRIGHT"
205 #endif
206 #endif
208 #define NUMBER_OF_BLOCKS 8
209 #define NUMBER_OF_PARTICLES 3
210 #define MAX_TERRAIN_NODES 15
212 #define LEVEL_MODE_NORMAL 0
213 #define LEVEL_MODE_STEEP 1
215 #if LCD_HEIGHT <= 64
216 #define CYCLES 100
217 static inline int SCALE(int x)
219 return x == 1 ? x : x >> 1;
221 #define SIZE 2
222 #else
223 #define CYCLES 60
224 #define SCALE(x) (x)
225 #define SIZE 1
226 #endif
228 /* in 10 milisecond (ticks) */
229 #define CYCLETIME ((CYCLES*HZ)/1000)
231 /*Chopper's local variables to track the terrain position etc*/
232 static int chopCounter;
233 static int iRotorOffset;
234 static int iScreenX;
235 static int iScreenY;
236 static int iPlayerPosX;
237 static int iPlayerPosY;
238 static int iCameraPosX;
239 static int iPlayerSpeedX;
240 static int iPlayerSpeedY;
241 static int iLastBlockPlacedPosX;
242 static int iGravityTimerCountdown;
243 static int iPlayerAlive;
244 static int iLevelMode, iCurrLevelMode;
245 static int blockh,blockw;
246 static int highscore;
247 static int score;
249 #define CFG_FILE "chopper.cfg"
250 #define MAX_POINTS 50000
251 static struct configdata config[] =
253 {TYPE_INT, 0, MAX_POINTS, { .int_p = &highscore }, "highscore", NULL}
256 struct CBlock
258 int iWorldX;
259 int iWorldY;
261 int iSizeX;
262 int iSizeY;
264 int bIsActive;
267 struct CParticle
269 int iWorldX;
270 int iWorldY;
272 int iSpeedX;
273 int iSpeedY;
275 int bIsActive;
278 struct CTerrainNode
280 int x;
281 int y;
284 struct CTerrain
286 struct CTerrainNode mNodes[MAX_TERRAIN_NODES];
287 int iNodesCount;
288 int iLastNodePlacedPosX;
291 struct CBlock mBlocks[NUMBER_OF_BLOCKS];
292 struct CParticle mParticles[NUMBER_OF_PARTICLES];
294 struct CTerrain mGround;
295 struct CTerrain mRoof;
297 /*Function declarations*/
298 static void chopDrawParticle(struct CParticle *mParticle);
299 static void chopDrawBlock(struct CBlock *mBlock);
300 static void chopRenderTerrain(struct CTerrain *ter, bool isground);
301 static void chopper_load(bool newgame);
303 static void chopDrawPlayer(int x,int y) /* These are SCREEN coords, not world!*/
306 #if LCD_DEPTH > 2
307 rb->lcd_set_foreground(LCD_RGBPACK(50,50,200));
308 #elif LCD_DEPTH == 2
309 rb->lcd_set_foreground(LCD_DARKGRAY);
310 #endif
311 rb->lcd_fillrect(SCALE(x+6), SCALE(y+2), SCALE(12), SCALE(9));
312 rb->lcd_fillrect(SCALE(x-3), SCALE(y+6), SCALE(20), SCALE(3));
314 #if LCD_DEPTH > 2
315 rb->lcd_set_foreground(LCD_RGBPACK(50,50,50));
316 #elif LCD_DEPTH == 2
317 rb->lcd_set_foreground(LCD_DARKGRAY);
318 #endif
319 rb->lcd_fillrect(SCALE(x+10), SCALE(y), SCALE(2), SCALE(3));
320 rb->lcd_fillrect(SCALE(x+10), SCALE(y), SCALE(1), SCALE(3));
322 #if LCD_DEPTH > 2
323 rb->lcd_set_foreground(LCD_RGBPACK(40,40,100));
324 #elif LCD_DEPTH == 2
325 rb->lcd_set_foreground(LCD_BLACK);
326 #endif
327 rb->lcd_drawline(SCALE(x), SCALE(y+iRotorOffset), SCALE(x+20),
328 SCALE(y-iRotorOffset));
330 #if LCD_DEPTH > 2
331 rb->lcd_set_foreground(LCD_RGBPACK(20,20,50));
332 #elif LCD_DEPTH == 2
333 rb->lcd_set_foreground(LCD_BLACK);
334 #endif
335 rb->lcd_fillrect(SCALE(x - 2), SCALE(y + 5), SCALE(2), SCALE(5));
339 static void chopClearTerrain(struct CTerrain *ter)
341 ter->iNodesCount = 0;
345 static int iR(int low,int high)
347 return low+rb->rand()%(high-low+1);
350 static void chopCopyTerrain(struct CTerrain *src, struct CTerrain *dest,
351 int xOffset,int yOffset)
353 int i=0;
355 while(i < src->iNodesCount)
357 dest->mNodes[i].x = src->mNodes[i].x + xOffset;
358 dest->mNodes[i].y = src->mNodes[i].y + yOffset;
360 i++;
363 dest->iNodesCount = src->iNodesCount;
364 dest->iLastNodePlacedPosX = src->iLastNodePlacedPosX;
368 static void chopAddTerrainNode(struct CTerrain *ter, int x, int y)
370 int i=0;
372 if(ter->iNodesCount + 1 >= MAX_TERRAIN_NODES)
374 /* DEBUGF("ERROR: Not enough nodes!\n"); */
375 return;
378 ter->iNodesCount++;
380 i = ter->iNodesCount - 1;
382 ter->mNodes[i].x = x;
383 ter->mNodes[i].y= y;
385 ter->iLastNodePlacedPosX = x;
389 static void chopTerrainNodeDeleteAndShift(struct CTerrain *ter,int nodeIndex)
391 int i=nodeIndex;
393 while( i < ter->iNodesCount )
395 ter->mNodes[i - 1] = ter->mNodes[i];
396 i++;
399 ter->iNodesCount--;
404 static int chopUpdateTerrainRecycling(struct CTerrain *ter)
406 int i=1;
407 int iNewNodePos,g,v;
408 while(i < ter->iNodesCount)
411 if( iCameraPosX > ter->mNodes[i].x)
414 chopTerrainNodeDeleteAndShift(ter,i);
416 iNewNodePos = ter->iLastNodePlacedPosX + 50;
417 g = iScreenY - 10;
419 v = 3*iPlayerSpeedX;
420 if(v>50)
421 v=50;
422 if(iCurrLevelMode == LEVEL_MODE_STEEP)
423 v*=5;
425 chopAddTerrainNode(ter,iNewNodePos,g - iR(-v,v));
428 i++;
432 return 1;
435 static int chopTerrainHeightAtPoint(struct CTerrain *ter, int pX)
438 int iNodeIndexOne=0,iNodeIndexTwo=0, h, terY1, terY2, terX2, a, b;
439 float c,d;
441 int i=0;
442 for(i=1;i<MAX_TERRAIN_NODES;i++)
444 if(ter->mNodes[i].x > pX)
446 iNodeIndexOne = i - 1;
447 break;
452 iNodeIndexTwo = iNodeIndexOne + 1;
453 terY1 = ter->mNodes[iNodeIndexOne].y;
454 terY2 = ter->mNodes[iNodeIndexTwo].y;
456 /* terX1 = 0; */
457 terX2 = ter->mNodes[iNodeIndexTwo].x - ter->mNodes[iNodeIndexOne].x;
459 pX-= ter->mNodes[iNodeIndexOne].x;
461 a = terY2 - terY1;
462 b = terX2;
463 c = pX;
464 d = (c/b) * a;
466 h = d + terY1;
468 return h;
472 static int chopPointInTerrain(struct CTerrain *ter, int pX, int pY, int iTestType)
474 int h = chopTerrainHeightAtPoint(ter, pX);
476 if(iTestType == 0)
477 return (pY > h);
478 else
479 return (pY < h);
482 static void chopAddBlock(int x,int y,int sx,int sy, int indexOverride)
484 int i=0;
486 if(indexOverride < 0)
488 while(mBlocks[i].bIsActive && i < NUMBER_OF_BLOCKS)
489 i++;
490 if(i==NUMBER_OF_BLOCKS)
492 DEBUGF("No blocks!\n");
493 return;
496 else
497 i = indexOverride;
499 mBlocks[i].bIsActive = 1;
500 mBlocks[i].iWorldX = x;
501 mBlocks[i].iWorldY = y;
502 mBlocks[i].iSizeX = sx;
503 mBlocks[i].iSizeY = sy;
505 iLastBlockPlacedPosX = x;
508 static void chopAddParticle(int x,int y,int sx,int sy)
510 int i=0;
512 while(mParticles[i].bIsActive && i < NUMBER_OF_PARTICLES)
513 i++;
515 if(i==NUMBER_OF_PARTICLES)
516 return;
518 mParticles[i].bIsActive = 1;
519 mParticles[i].iWorldX = x;
520 mParticles[i].iWorldY = y;
521 mParticles[i].iSpeedX = sx;
522 mParticles[i].iSpeedY = sy;
525 static void chopGenerateBlockIfNeeded(void)
527 int i=0;
528 int DistSpeedX = iPlayerSpeedX * 5;
529 if(DistSpeedX<200) DistSpeedX = 200;
531 while(i < NUMBER_OF_BLOCKS)
533 if(!mBlocks[i].bIsActive)
535 int iX,iY,sX,sY;
537 iX = iLastBlockPlacedPosX + (350-DistSpeedX);
538 sX = blockw;
540 iY = iR(0,iScreenY);
541 sY = blockh + iR(1,blockh/3);
543 chopAddBlock(iX,iY,sX,sY,i);
546 i++;
551 static int chopBlockCollideWithPlayer(struct CBlock *mBlock)
553 int px = iPlayerPosX;
554 int py = iPlayerPosY;
556 int x = mBlock->iWorldX-17;
557 int y = mBlock->iWorldY-11;
559 int x2 = x + mBlock->iSizeX+17;
560 int y2 = y + mBlock->iSizeY+11;
562 if(px>x && px<x2)
564 if(py>y && py<y2)
566 return 1;
570 return 0;
573 static int chopBlockOffscreen(struct CBlock *mBlock)
575 if(mBlock->iWorldX + mBlock->iSizeX < iCameraPosX)
576 return 1;
577 else
578 return 0;
581 static int chopParticleOffscreen(struct CParticle *mParticle)
583 if (mParticle->iWorldX < iCameraPosX || mParticle->iWorldY < 0 ||
584 mParticle->iWorldY > iScreenY || mParticle->iWorldX > iCameraPosX +
585 iScreenX)
587 return 1;
589 else
590 return 0;
593 static void chopKillPlayer(void)
595 int i;
597 for (i = 0; i < NUMBER_OF_PARTICLES; i++) {
598 mParticles[i].bIsActive = 0;
599 chopAddParticle(iPlayerPosX + iR(0,20), iPlayerPosY + iR(0,20),
600 iR(-2,2), iR(-2,2));
603 iPlayerAlive--;
605 if (iPlayerAlive == 0) {
606 rb->splash(HZ, "Game Over");
608 if (score > highscore) {
609 char scoretext[30];
610 highscore = score;
611 rb->snprintf(scoretext, sizeof(scoretext), "New High Score: %d",
612 highscore);
613 rb->splash(HZ*2, scoretext);
615 } else
616 chopper_load(false);
619 static void chopDrawTheWorld(void)
621 int i=0;
623 while(i < NUMBER_OF_BLOCKS)
625 if(mBlocks[i].bIsActive)
627 if(chopBlockOffscreen(&mBlocks[i]) == 1)
628 mBlocks[i].bIsActive = 0;
629 else
630 chopDrawBlock(&mBlocks[i]);
633 i++;
636 i=0;
638 while(i < NUMBER_OF_PARTICLES)
640 if(mParticles[i].bIsActive)
642 if(chopParticleOffscreen(&mParticles[i]) == 1)
643 mParticles[i].bIsActive = 0;
644 else
645 chopDrawParticle(&mParticles[i]);
648 i++;
651 chopRenderTerrain(&mGround, true);
652 chopRenderTerrain(&mRoof, false);
656 static void chopDrawParticle(struct CParticle *mParticle)
659 int iPosX = (mParticle->iWorldX - iCameraPosX);
660 int iPosY = (mParticle->iWorldY);
661 #if LCD_DEPTH > 2
662 rb->lcd_set_foreground(LCD_RGBPACK(192,192,192));
663 #elif LCD_DEPTH == 2
664 rb->lcd_set_foreground(LCD_LIGHTGRAY);
665 #endif
666 rb->lcd_fillrect(SCALE(iPosX), SCALE(iPosY), SCALE(3), SCALE(3));
670 static void chopDrawScene(void)
672 char s[30];
673 int w;
674 #if LCD_DEPTH > 2
675 rb->lcd_set_background(LCD_BLACK);
676 #elif LCD_DEPTH == 2
677 rb->lcd_set_background(LCD_WHITE);
678 #endif
679 rb->lcd_clear_display();
680 chopDrawTheWorld();
681 chopDrawPlayer(iPlayerPosX - iCameraPosX, iPlayerPosY);
683 score = -20 + iPlayerPosX/3;
685 #if LCD_DEPTH == 1
686 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
687 #else
688 rb->lcd_set_drawmode(DRMODE_FG);
689 #endif
691 #if LCD_DEPTH > 2
692 rb->lcd_set_foreground(LCD_BLACK);
693 #elif LCD_DEPTH == 2
694 rb->lcd_set_foreground(LCD_WHITE);
695 #endif
697 #if LCD_WIDTH <= 128
698 rb->snprintf(s, sizeof(s), "Dist: %d", score);
699 #else
700 rb->snprintf(s, sizeof(s), "Distance: %d", score);
701 #endif
702 rb->lcd_getstringsize(s, &w, NULL);
703 rb->lcd_putsxy(2, 2, s);
704 if (score < highscore)
706 int w2;
707 #if LCD_WIDTH <= 128
708 rb->snprintf(s, sizeof(s), "Hi: %d", highscore);
709 #else
710 rb->snprintf(s, sizeof(s), "Best: %d", highscore);
711 #endif
712 rb->lcd_getstringsize(s, &w2, NULL);
713 if (LCD_WIDTH - 2 - w2 > w + 2)
714 rb->lcd_putsxy(LCD_WIDTH - 2 - w2, 2, s);
716 rb->lcd_set_drawmode(DRMODE_SOLID);
718 rb->lcd_update();
721 static bool _ingame;
722 static int chopMenuCb(int action, const struct menu_item_ex *this_item)
724 if(action == ACTION_REQUEST_MENUITEM
725 && !_ingame && ((intptr_t)this_item)==0)
726 return ACTION_EXIT_MENUITEM;
727 return action;
729 static int chopMenu(int menunum)
731 int result = 0;
732 int res = 0;
733 bool menu_quit = false;
735 static const struct opt_items levels[2] = {
736 { "Normal", -1 },
737 { "Steep", -1 },
740 MENUITEM_STRINGLIST(menu,"Chopper Menu",chopMenuCb,
741 "Resume Game","Start New Game",
742 "Level","Playback Control","Quit");
743 _ingame = (menunum!=0);
745 #ifdef HAVE_LCD_COLOR
746 rb->lcd_set_foreground(LCD_WHITE);
747 rb->lcd_set_background(LCD_BLACK);
748 #elif LCD_DEPTH == 2
749 rb->lcd_set_foreground(LCD_BLACK);
750 rb->lcd_set_background(LCD_WHITE);
751 #endif
753 rb->lcd_clear_display();
754 rb->button_clear_queue();
756 while (!menu_quit) {
757 switch(rb->do_menu(&menu, &result, NULL, false))
759 case 0: /* Resume Game */
760 menu_quit=true;
761 res = -1;
762 break;
763 case 1: /* Start New Game */
764 menu_quit=true;
765 chopper_load(true);
766 res = -1;
767 break;
768 case 2:
769 rb->set_option("Level", &iLevelMode, INT, levels, 2, NULL);
770 break;
771 case 3:
772 playback_control(NULL);
773 break;
774 case 4:
775 menu_quit=true;
776 res = PLUGIN_OK;
777 break;
778 case MENU_ATTACHED_USB:
779 menu_quit=true;
780 res = PLUGIN_USB_CONNECTED;
781 break;
784 rb->lcd_clear_display();
785 return res;
788 static int chopGameLoop(void)
790 int move_button, ret;
791 bool exit=false;
792 int end, i=0, bdelay=0, last_button=BUTTON_NONE;
794 if (chopUpdateTerrainRecycling(&mGround) == 1)
795 /* mirror the sky if we've changed the ground */
796 chopCopyTerrain(&mGround, &mRoof, 0, - ( (iScreenY * 3) / 4));
798 ret = chopMenu(0);
799 if (ret != -1)
800 return PLUGIN_OK;
802 chopDrawScene();
804 while (!exit) {
806 end = *rb->current_tick + CYCLETIME;
808 if(chopUpdateTerrainRecycling(&mGround) == 1)
809 /* mirror the sky if we've changed the ground */
810 chopCopyTerrain(&mGround, &mRoof, 0, - ( (iScreenY * 3) / 4));
812 iRotorOffset = iR(-1,1);
814 /* We need to have this here so particles move when we're dead */
816 for (i=0; i < NUMBER_OF_PARTICLES; i++)
817 if(mParticles[i].bIsActive == 1)
819 mParticles[i].iWorldX += mParticles[i].iSpeedX;
820 mParticles[i].iWorldY += mParticles[i].iSpeedY;
823 /* Redraw the main window: */
824 chopDrawScene();
827 iGravityTimerCountdown--;
829 if(iGravityTimerCountdown <= 0)
831 iGravityTimerCountdown = 3;
832 chopAddParticle(iPlayerPosX, iPlayerPosY+5, 0, 0);
835 if(iCurrLevelMode == LEVEL_MODE_NORMAL)
836 chopGenerateBlockIfNeeded();
839 move_button=rb->button_status();
840 if (rb->button_get(false) == QUIT) {
841 ret = chopMenu(1);
842 if (ret != -1)
843 return PLUGIN_OK;
844 bdelay = 0;
845 last_button = BUTTON_NONE;
846 move_button = BUTTON_NONE;
849 switch (move_button) {
850 case ACTION:
851 #ifdef ACTION2
852 case ACTION2:
853 #endif
854 if (last_button != ACTION
855 #ifdef ACTION2
856 && last_button != ACTION2
857 #endif
859 bdelay = -2;
860 if (bdelay == 0)
861 iPlayerSpeedY = -3;
862 break;
864 default:
865 if (last_button == ACTION
866 #ifdef ACTION2
867 || last_button == ACTION2
868 #endif
870 bdelay = 3;
871 if (bdelay == 0)
872 iPlayerSpeedY = 4;
874 if (rb->default_event_handler(move_button) == SYS_USB_CONNECTED)
875 return PLUGIN_USB_CONNECTED;
876 break;
878 last_button = move_button;
880 if (bdelay < 0) {
881 iPlayerSpeedY = bdelay;
882 bdelay++;
883 } else if (bdelay > 0) {
884 iPlayerSpeedY = bdelay;
885 bdelay--;
888 iCameraPosX = iPlayerPosX - 25;
889 iPlayerPosX += iPlayerSpeedX;
890 iPlayerPosY += iPlayerSpeedY;
892 chopCounter++;
893 /* increase speed as we go along */
894 if (chopCounter == 100){
895 iPlayerSpeedX++;
896 chopCounter=0;
899 if (iPlayerPosY > iScreenY-10 || iPlayerPosY < -5 ||
900 chopPointInTerrain(&mGround, iPlayerPosX, iPlayerPosY + 10, 0) ||
901 chopPointInTerrain(&mRoof, iPlayerPosX ,iPlayerPosY, 1))
903 chopKillPlayer();
904 chopDrawScene();
905 ret = chopMenu(0);
906 if (ret != -1)
907 return ret;
910 for (i=0; i < NUMBER_OF_BLOCKS; i++)
911 if(mBlocks[i].bIsActive == 1)
912 if(chopBlockCollideWithPlayer(&mBlocks[i])) {
913 chopKillPlayer();
914 chopDrawScene();
915 ret = chopMenu(0);
916 if (ret != -1)
917 return ret;
920 if (TIME_BEFORE(*rb->current_tick, end))
921 rb->sleep(end - *rb->current_tick); /* wait until time is over */
922 else
923 rb->yield();
926 return PLUGIN_OK;
929 static void chopDrawBlock(struct CBlock *mBlock)
931 int iPosX = (mBlock->iWorldX - iCameraPosX);
932 int iPosY = (mBlock->iWorldY);
933 #if LCD_DEPTH > 2
934 rb->lcd_set_foreground(LCD_RGBPACK(100,255,100));
935 #elif LCD_DEPTH == 2
936 rb->lcd_set_foreground(LCD_BLACK);
937 #endif
938 rb->lcd_fillrect(SCALE(iPosX), SCALE(iPosY), SCALE(mBlock->iSizeX),
939 SCALE(mBlock->iSizeY));
943 static void chopRenderTerrain(struct CTerrain *ter, bool isground)
946 int i = 1;
948 int oldx = 0;
950 while(i < ter->iNodesCount && oldx < iScreenX)
953 int x = ter->mNodes[i-1].x - iCameraPosX;
954 int y = ter->mNodes[i-1].y;
956 int x2 = ter->mNodes[i].x - iCameraPosX;
957 int y2 = ter->mNodes[i].y;
959 int ax, ay;
961 if ((y < y2) != isground)
963 ax = x2;
964 ay = y;
966 else
968 ax = x;
969 ay = y2;
971 #if LCD_DEPTH > 2
972 rb->lcd_set_foreground(LCD_RGBPACK(100,255,100));
973 #elif LCD_DEPTH == 2
974 rb->lcd_set_foreground(LCD_DARKGRAY);
975 #endif
977 rb->lcd_drawline(SCALE(x), SCALE(y), SCALE(x2), SCALE(y2));
979 xlcd_filltriangle(SCALE(x), SCALE(y), SCALE(x2), SCALE(y2),
980 SCALE(ax), SCALE(ay));
982 if (isground)
984 y = ay;
985 y2 = (LCD_HEIGHT*SIZE);
987 else
989 y = 0;
990 y2 = ay;
992 if (y2-y > 0)
993 rb->lcd_fillrect(SCALE(x), SCALE(y), SCALE(x2-x)+1, SCALE(y2-y)+1);
995 oldx = x;
996 i++;
1000 static void chopper_load(bool newgame)
1003 int i;
1004 int g;
1006 if (newgame) {
1007 iScreenX = LCD_WIDTH * SIZE;
1008 iScreenY = LCD_HEIGHT * SIZE;
1009 blockh = iScreenY / 5;
1010 blockw = iScreenX / 20;
1011 iPlayerAlive = 1;
1012 iCurrLevelMode = iLevelMode;
1013 score = 0;
1015 iRotorOffset = 0;
1016 iPlayerPosX = 60;
1017 iPlayerPosY = (iScreenY * 4) / 10;
1018 iLastBlockPlacedPosX = 0;
1019 iGravityTimerCountdown = 2;
1020 chopCounter = 0;
1021 iPlayerSpeedX = 3;
1022 iPlayerSpeedY = 0;
1023 iCameraPosX = 30;
1025 for (i=0; i < NUMBER_OF_PARTICLES; i++)
1026 mParticles[i].bIsActive = 0;
1028 for (i=0; i < NUMBER_OF_BLOCKS; i++)
1029 mBlocks[i].bIsActive = 0;
1031 g = iScreenY - 10;
1032 chopClearTerrain(&mGround);
1034 for (i=0; i < MAX_TERRAIN_NODES; i++)
1035 chopAddTerrainNode(&mGround,i * 30,g - iR(0,20));
1037 if (chopUpdateTerrainRecycling(&mGround) == 1)
1038 /* mirror the sky if we've changed the ground */
1039 chopCopyTerrain(&mGround, &mRoof, 0, - ( (iScreenY * 3) / 4));
1041 if (iCurrLevelMode == LEVEL_MODE_NORMAL)
1042 /* make it a bit more exciting, cause it's easy terrain... */
1043 iPlayerSpeedX *= 2;
1046 /* this is the plugin entry point */
1047 enum plugin_status plugin_start(const void* parameter)
1049 (void)parameter;
1050 int ret;
1052 rb->lcd_setfont(FONT_SYSFIXED);
1053 #if LCD_DEPTH > 1
1054 rb->lcd_set_backdrop(NULL);
1055 #endif
1056 #ifdef HAVE_LCD_COLOR
1057 rb->lcd_set_background(LCD_BLACK);
1058 rb->lcd_set_foreground(LCD_WHITE);
1059 #endif
1061 /* Turn off backlight timeout */
1062 backlight_ignore_timeout();
1064 rb->srand( *rb->current_tick );
1066 configfile_load(CFG_FILE, config, 1, 0);
1068 chopper_load(true);
1069 ret = chopGameLoop();
1071 configfile_save(CFG_FILE, config, 1, 0);
1073 rb->lcd_setfont(FONT_UI);
1074 /* Turn on backlight timeout (revert to settings) */
1075 backlight_use_settings();
1077 return ret;