application: Enable chipper and fft plugins.
[maemo-rb.git] / apps / plugins / chopper.c
blobb2c3653aebf40a02af7c231d1ffa90ee752f0063
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_REC
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 !defined(HAVE_TOUCHSCREEN)
184 #error No keymap defined!
185 #endif
187 #ifdef HAVE_TOUCHSCREEN
188 #ifndef QUIT
189 #define QUIT BUTTON_TOPLEFT
190 #endif
191 #ifndef ACTION
192 #define ACTION BUTTON_BOTTOMLEFT
193 #endif
194 #ifndef ACTION2
195 #define ACTION2 BUTTON_BOTTOMRIGHT
196 #endif
197 #ifndef ACTIONTEXT
198 #define ACTIONTEXT "BOTTOMRIGHT"
199 #endif
200 #endif
202 #define NUMBER_OF_BLOCKS 8
203 #define NUMBER_OF_PARTICLES 3
204 #define MAX_TERRAIN_NODES 15
206 #define LEVEL_MODE_NORMAL 0
207 #define LEVEL_MODE_STEEP 1
209 #if LCD_HEIGHT <= 64
210 #define CYCLES 100
211 static inline int SCALE(int x)
213 return x == 1 ? x : x >> 1;
215 #define SIZE 2
216 #else
217 #define CYCLES 60
218 #define SCALE(x) (x)
219 #define SIZE 1
220 #endif
222 /* in 10 milisecond (ticks) */
223 #define CYCLETIME ((CYCLES*HZ)/1000)
225 /*Chopper's local variables to track the terrain position etc*/
226 static int chopCounter;
227 static int iRotorOffset;
228 static int iScreenX;
229 static int iScreenY;
230 static int iPlayerPosX;
231 static int iPlayerPosY;
232 static int iCameraPosX;
233 static int iPlayerSpeedX;
234 static int iPlayerSpeedY;
235 static int iLastBlockPlacedPosX;
236 static int iGravityTimerCountdown;
237 static int iPlayerAlive;
238 static int iLevelMode, iCurrLevelMode;
239 static int blockh,blockw;
240 static int highscore;
241 static int score;
243 #define CFG_FILE "chopper.cfg"
244 #define MAX_POINTS 50000
245 static struct configdata config[] =
247 {TYPE_INT, 0, MAX_POINTS, { .int_p = &highscore }, "highscore", NULL}
250 struct CBlock
252 int iWorldX;
253 int iWorldY;
255 int iSizeX;
256 int iSizeY;
258 int bIsActive;
261 struct CParticle
263 int iWorldX;
264 int iWorldY;
266 int iSpeedX;
267 int iSpeedY;
269 int bIsActive;
272 struct CTerrainNode
274 int x;
275 int y;
278 struct CTerrain
280 struct CTerrainNode mNodes[MAX_TERRAIN_NODES];
281 int iNodesCount;
282 int iLastNodePlacedPosX;
285 struct CBlock mBlocks[NUMBER_OF_BLOCKS];
286 struct CParticle mParticles[NUMBER_OF_PARTICLES];
288 struct CTerrain mGround;
289 struct CTerrain mRoof;
291 /*Function declarations*/
292 static void chopDrawParticle(struct CParticle *mParticle);
293 static void chopDrawBlock(struct CBlock *mBlock);
294 static void chopRenderTerrain(struct CTerrain *ter, bool isground);
295 static void chopper_load(bool newgame);
297 static void chopDrawPlayer(int x,int y) /* These are SCREEN coords, not world!*/
300 #if LCD_DEPTH > 2
301 rb->lcd_set_foreground(LCD_RGBPACK(50,50,200));
302 #elif LCD_DEPTH == 2
303 rb->lcd_set_foreground(LCD_DARKGRAY);
304 #endif
305 rb->lcd_fillrect(SCALE(x+6), SCALE(y+2), SCALE(12), SCALE(9));
306 rb->lcd_fillrect(SCALE(x-3), SCALE(y+6), SCALE(20), SCALE(3));
308 #if LCD_DEPTH > 2
309 rb->lcd_set_foreground(LCD_RGBPACK(50,50,50));
310 #elif LCD_DEPTH == 2
311 rb->lcd_set_foreground(LCD_DARKGRAY);
312 #endif
313 rb->lcd_fillrect(SCALE(x+10), SCALE(y), SCALE(2), SCALE(3));
314 rb->lcd_fillrect(SCALE(x+10), SCALE(y), SCALE(1), SCALE(3));
316 #if LCD_DEPTH > 2
317 rb->lcd_set_foreground(LCD_RGBPACK(40,40,100));
318 #elif LCD_DEPTH == 2
319 rb->lcd_set_foreground(LCD_BLACK);
320 #endif
321 rb->lcd_drawline(SCALE(x), SCALE(y+iRotorOffset), SCALE(x+20),
322 SCALE(y-iRotorOffset));
324 #if LCD_DEPTH > 2
325 rb->lcd_set_foreground(LCD_RGBPACK(20,20,50));
326 #elif LCD_DEPTH == 2
327 rb->lcd_set_foreground(LCD_BLACK);
328 #endif
329 rb->lcd_fillrect(SCALE(x - 2), SCALE(y + 5), SCALE(2), SCALE(5));
333 static void chopClearTerrain(struct CTerrain *ter)
335 ter->iNodesCount = 0;
339 static int iR(int low,int high)
341 return low+rb->rand()%(high-low+1);
344 static void chopCopyTerrain(struct CTerrain *src, struct CTerrain *dest,
345 int xOffset,int yOffset)
347 int i=0;
349 while(i < src->iNodesCount)
351 dest->mNodes[i].x = src->mNodes[i].x + xOffset;
352 dest->mNodes[i].y = src->mNodes[i].y + yOffset;
354 i++;
357 dest->iNodesCount = src->iNodesCount;
358 dest->iLastNodePlacedPosX = src->iLastNodePlacedPosX;
362 static void chopAddTerrainNode(struct CTerrain *ter, int x, int y)
364 int i=0;
366 if(ter->iNodesCount + 1 >= MAX_TERRAIN_NODES)
368 /* DEBUGF("ERROR: Not enough nodes!\n"); */
369 return;
372 ter->iNodesCount++;
374 i = ter->iNodesCount - 1;
376 ter->mNodes[i].x = x;
377 ter->mNodes[i].y= y;
379 ter->iLastNodePlacedPosX = x;
383 static void chopTerrainNodeDeleteAndShift(struct CTerrain *ter,int nodeIndex)
385 int i=nodeIndex;
387 while( i < ter->iNodesCount )
389 ter->mNodes[i - 1] = ter->mNodes[i];
390 i++;
393 ter->iNodesCount--;
398 static int chopUpdateTerrainRecycling(struct CTerrain *ter)
400 int i=1;
401 int iNewNodePos,g,v;
402 while(i < ter->iNodesCount)
405 if( iCameraPosX > ter->mNodes[i].x)
408 chopTerrainNodeDeleteAndShift(ter,i);
410 iNewNodePos = ter->iLastNodePlacedPosX + 50;
411 g = iScreenY - 10;
413 v = 3*iPlayerSpeedX;
414 if(v>50)
415 v=50;
416 if(iCurrLevelMode == LEVEL_MODE_STEEP)
417 v*=5;
419 chopAddTerrainNode(ter,iNewNodePos,g - iR(-v,v));
422 i++;
426 return 1;
429 static int chopTerrainHeightAtPoint(struct CTerrain *ter, int pX)
432 int iNodeIndexOne=0,iNodeIndexTwo=0, h, terY1, terY2, terX2, a, b;
433 float c,d;
435 int i=0;
436 for(i=1;i<MAX_TERRAIN_NODES;i++)
438 if(ter->mNodes[i].x > pX)
440 iNodeIndexOne = i - 1;
441 break;
446 iNodeIndexTwo = iNodeIndexOne + 1;
447 terY1 = ter->mNodes[iNodeIndexOne].y;
448 terY2 = ter->mNodes[iNodeIndexTwo].y;
450 /* terX1 = 0; */
451 terX2 = ter->mNodes[iNodeIndexTwo].x - ter->mNodes[iNodeIndexOne].x;
453 pX-= ter->mNodes[iNodeIndexOne].x;
455 a = terY2 - terY1;
456 b = terX2;
457 c = pX;
458 d = (c/b) * a;
460 h = d + terY1;
462 return h;
466 static int chopPointInTerrain(struct CTerrain *ter, int pX, int pY, int iTestType)
468 int h = chopTerrainHeightAtPoint(ter, pX);
470 if(iTestType == 0)
471 return (pY > h);
472 else
473 return (pY < h);
476 static void chopAddBlock(int x,int y,int sx,int sy, int indexOverride)
478 int i=0;
480 if(indexOverride < 0)
482 while(mBlocks[i].bIsActive && i < NUMBER_OF_BLOCKS)
483 i++;
484 if(i==NUMBER_OF_BLOCKS)
486 DEBUGF("No blocks!\n");
487 return;
490 else
491 i = indexOverride;
493 mBlocks[i].bIsActive = 1;
494 mBlocks[i].iWorldX = x;
495 mBlocks[i].iWorldY = y;
496 mBlocks[i].iSizeX = sx;
497 mBlocks[i].iSizeY = sy;
499 iLastBlockPlacedPosX = x;
502 static void chopAddParticle(int x,int y,int sx,int sy)
504 int i=0;
506 while(mParticles[i].bIsActive && i < NUMBER_OF_PARTICLES)
507 i++;
509 if(i==NUMBER_OF_PARTICLES)
510 return;
512 mParticles[i].bIsActive = 1;
513 mParticles[i].iWorldX = x;
514 mParticles[i].iWorldY = y;
515 mParticles[i].iSpeedX = sx;
516 mParticles[i].iSpeedY = sy;
519 static void chopGenerateBlockIfNeeded(void)
521 int i=0;
522 int DistSpeedX = iPlayerSpeedX * 5;
523 if(DistSpeedX<200) DistSpeedX = 200;
525 while(i < NUMBER_OF_BLOCKS)
527 if(!mBlocks[i].bIsActive)
529 int iX,iY,sX,sY;
531 iX = iLastBlockPlacedPosX + (350-DistSpeedX);
532 sX = blockw;
534 iY = iR(0,iScreenY);
535 sY = blockh + iR(1,blockh/3);
537 chopAddBlock(iX,iY,sX,sY,i);
540 i++;
545 static int chopBlockCollideWithPlayer(struct CBlock *mBlock)
547 int px = iPlayerPosX;
548 int py = iPlayerPosY;
550 int x = mBlock->iWorldX-17;
551 int y = mBlock->iWorldY-11;
553 int x2 = x + mBlock->iSizeX+17;
554 int y2 = y + mBlock->iSizeY+11;
556 if(px>x && px<x2)
558 if(py>y && py<y2)
560 return 1;
564 return 0;
567 static int chopBlockOffscreen(struct CBlock *mBlock)
569 if(mBlock->iWorldX + mBlock->iSizeX < iCameraPosX)
570 return 1;
571 else
572 return 0;
575 static int chopParticleOffscreen(struct CParticle *mParticle)
577 if (mParticle->iWorldX < iCameraPosX || mParticle->iWorldY < 0 ||
578 mParticle->iWorldY > iScreenY || mParticle->iWorldX > iCameraPosX +
579 iScreenX)
581 return 1;
583 else
584 return 0;
587 static void chopKillPlayer(void)
589 int i;
591 for (i = 0; i < NUMBER_OF_PARTICLES; i++) {
592 mParticles[i].bIsActive = 0;
593 chopAddParticle(iPlayerPosX + iR(0,20), iPlayerPosY + iR(0,20),
594 iR(-2,2), iR(-2,2));
597 iPlayerAlive--;
599 if (iPlayerAlive == 0) {
600 rb->splash(HZ, "Game Over");
602 if (score > highscore) {
603 char scoretext[30];
604 highscore = score;
605 rb->snprintf(scoretext, sizeof(scoretext), "New High Score: %d",
606 highscore);
607 rb->splash(HZ*2, scoretext);
609 } else
610 chopper_load(false);
613 static void chopDrawTheWorld(void)
615 int i=0;
617 while(i < NUMBER_OF_BLOCKS)
619 if(mBlocks[i].bIsActive)
621 if(chopBlockOffscreen(&mBlocks[i]) == 1)
622 mBlocks[i].bIsActive = 0;
623 else
624 chopDrawBlock(&mBlocks[i]);
627 i++;
630 i=0;
632 while(i < NUMBER_OF_PARTICLES)
634 if(mParticles[i].bIsActive)
636 if(chopParticleOffscreen(&mParticles[i]) == 1)
637 mParticles[i].bIsActive = 0;
638 else
639 chopDrawParticle(&mParticles[i]);
642 i++;
645 chopRenderTerrain(&mGround, true);
646 chopRenderTerrain(&mRoof, false);
650 static void chopDrawParticle(struct CParticle *mParticle)
653 int iPosX = (mParticle->iWorldX - iCameraPosX);
654 int iPosY = (mParticle->iWorldY);
655 #if LCD_DEPTH > 2
656 rb->lcd_set_foreground(LCD_RGBPACK(192,192,192));
657 #elif LCD_DEPTH == 2
658 rb->lcd_set_foreground(LCD_LIGHTGRAY);
659 #endif
660 rb->lcd_fillrect(SCALE(iPosX), SCALE(iPosY), SCALE(3), SCALE(3));
664 static void chopDrawScene(void)
666 char s[30];
667 int w;
668 #if LCD_DEPTH > 2
669 rb->lcd_set_background(LCD_BLACK);
670 #elif LCD_DEPTH == 2
671 rb->lcd_set_background(LCD_WHITE);
672 #endif
673 rb->lcd_clear_display();
674 chopDrawTheWorld();
675 chopDrawPlayer(iPlayerPosX - iCameraPosX, iPlayerPosY);
677 score = -20 + iPlayerPosX/3;
679 #if LCD_DEPTH == 1
680 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
681 #else
682 rb->lcd_set_drawmode(DRMODE_FG);
683 #endif
685 #if LCD_DEPTH > 2
686 rb->lcd_set_foreground(LCD_BLACK);
687 #elif LCD_DEPTH == 2
688 rb->lcd_set_foreground(LCD_WHITE);
689 #endif
691 #if LCD_WIDTH <= 128
692 rb->snprintf(s, sizeof(s), "Dist: %d", score);
693 #else
694 rb->snprintf(s, sizeof(s), "Distance: %d", score);
695 #endif
696 rb->lcd_getstringsize(s, &w, NULL);
697 rb->lcd_putsxy(2, 2, s);
698 if (score < highscore)
700 int w2;
701 #if LCD_WIDTH <= 128
702 rb->snprintf(s, sizeof(s), "Hi: %d", highscore);
703 #else
704 rb->snprintf(s, sizeof(s), "Best: %d", highscore);
705 #endif
706 rb->lcd_getstringsize(s, &w2, NULL);
707 if (LCD_WIDTH - 2 - w2 > w + 2)
708 rb->lcd_putsxy(LCD_WIDTH - 2 - w2, 2, s);
710 rb->lcd_set_drawmode(DRMODE_SOLID);
712 rb->lcd_update();
715 static bool _ingame;
716 static int chopMenuCb(int action, const struct menu_item_ex *this_item)
718 if(action == ACTION_REQUEST_MENUITEM
719 && !_ingame && ((intptr_t)this_item)==0)
720 return ACTION_EXIT_MENUITEM;
721 return action;
723 static int chopMenu(int menunum)
725 int result = 0;
726 int res = 0;
727 bool menu_quit = false;
729 static const struct opt_items levels[2] = {
730 { "Normal", -1 },
731 { "Steep", -1 },
734 MENUITEM_STRINGLIST(menu,"Chopper Menu",chopMenuCb,
735 "Resume Game","Start New Game",
736 "Level","Playback Control","Quit");
737 _ingame = (menunum!=0);
739 #ifdef HAVE_LCD_COLOR
740 rb->lcd_set_foreground(LCD_WHITE);
741 rb->lcd_set_background(LCD_BLACK);
742 #elif LCD_DEPTH == 2
743 rb->lcd_set_foreground(LCD_BLACK);
744 rb->lcd_set_background(LCD_WHITE);
745 #endif
747 rb->lcd_clear_display();
748 rb->button_clear_queue();
750 while (!menu_quit) {
751 switch(rb->do_menu(&menu, &result, NULL, false))
753 case 0: /* Resume Game */
754 menu_quit=true;
755 res = -1;
756 break;
757 case 1: /* Start New Game */
758 menu_quit=true;
759 chopper_load(true);
760 res = -1;
761 break;
762 case 2:
763 rb->set_option("Level", &iLevelMode, INT, levels, 2, NULL);
764 break;
765 case 3:
766 playback_control(NULL);
767 break;
768 case 4:
769 menu_quit=true;
770 res = PLUGIN_OK;
771 break;
772 case MENU_ATTACHED_USB:
773 menu_quit=true;
774 res = PLUGIN_USB_CONNECTED;
775 break;
778 rb->lcd_clear_display();
779 return res;
782 static int chopGameLoop(void)
784 int move_button, ret;
785 bool exit=false;
786 int end, i=0, bdelay=0, last_button=BUTTON_NONE;
788 if (chopUpdateTerrainRecycling(&mGround) == 1)
789 /* mirror the sky if we've changed the ground */
790 chopCopyTerrain(&mGround, &mRoof, 0, - ( (iScreenY * 3) / 4));
792 ret = chopMenu(0);
793 if (ret != -1)
794 return PLUGIN_OK;
796 chopDrawScene();
798 while (!exit) {
800 end = *rb->current_tick + CYCLETIME;
802 if(chopUpdateTerrainRecycling(&mGround) == 1)
803 /* mirror the sky if we've changed the ground */
804 chopCopyTerrain(&mGround, &mRoof, 0, - ( (iScreenY * 3) / 4));
806 iRotorOffset = iR(-1,1);
808 /* We need to have this here so particles move when we're dead */
810 for (i=0; i < NUMBER_OF_PARTICLES; i++)
811 if(mParticles[i].bIsActive == 1)
813 mParticles[i].iWorldX += mParticles[i].iSpeedX;
814 mParticles[i].iWorldY += mParticles[i].iSpeedY;
817 /* Redraw the main window: */
818 chopDrawScene();
821 iGravityTimerCountdown--;
823 if(iGravityTimerCountdown <= 0)
825 iGravityTimerCountdown = 3;
826 chopAddParticle(iPlayerPosX, iPlayerPosY+5, 0, 0);
829 if(iCurrLevelMode == LEVEL_MODE_NORMAL)
830 chopGenerateBlockIfNeeded();
833 move_button=rb->button_status();
834 if (rb->button_get(false) == QUIT) {
835 ret = chopMenu(1);
836 if (ret != -1)
837 return PLUGIN_OK;
838 bdelay = 0;
839 last_button = BUTTON_NONE;
840 move_button = BUTTON_NONE;
843 switch (move_button) {
844 case ACTION:
845 #ifdef ACTION2
846 case ACTION2:
847 #endif
848 if (last_button != ACTION
849 #ifdef ACTION2
850 && last_button != ACTION2
851 #endif
853 bdelay = -2;
854 if (bdelay == 0)
855 iPlayerSpeedY = -3;
856 break;
858 default:
859 if (last_button == ACTION
860 #ifdef ACTION2
861 || last_button == ACTION2
862 #endif
864 bdelay = 3;
865 if (bdelay == 0)
866 iPlayerSpeedY = 4;
868 if (rb->default_event_handler(move_button) == SYS_USB_CONNECTED)
869 return PLUGIN_USB_CONNECTED;
870 break;
872 last_button = move_button;
874 if (bdelay < 0) {
875 iPlayerSpeedY = bdelay;
876 bdelay++;
877 } else if (bdelay > 0) {
878 iPlayerSpeedY = bdelay;
879 bdelay--;
882 iCameraPosX = iPlayerPosX - 25;
883 iPlayerPosX += iPlayerSpeedX;
884 iPlayerPosY += iPlayerSpeedY;
886 chopCounter++;
887 /* increase speed as we go along */
888 if (chopCounter == 100){
889 iPlayerSpeedX++;
890 chopCounter=0;
893 if (iPlayerPosY > iScreenY-10 || iPlayerPosY < -5 ||
894 chopPointInTerrain(&mGround, iPlayerPosX, iPlayerPosY + 10, 0) ||
895 chopPointInTerrain(&mRoof, iPlayerPosX ,iPlayerPosY, 1))
897 chopKillPlayer();
898 chopDrawScene();
899 ret = chopMenu(0);
900 if (ret != -1)
901 return ret;
904 for (i=0; i < NUMBER_OF_BLOCKS; i++)
905 if(mBlocks[i].bIsActive == 1)
906 if(chopBlockCollideWithPlayer(&mBlocks[i])) {
907 chopKillPlayer();
908 chopDrawScene();
909 ret = chopMenu(0);
910 if (ret != -1)
911 return ret;
914 if (TIME_BEFORE(*rb->current_tick, end))
915 rb->sleep(end - *rb->current_tick); /* wait until time is over */
916 else
917 rb->yield();
920 return PLUGIN_OK;
923 static void chopDrawBlock(struct CBlock *mBlock)
925 int iPosX = (mBlock->iWorldX - iCameraPosX);
926 int iPosY = (mBlock->iWorldY);
927 #if LCD_DEPTH > 2
928 rb->lcd_set_foreground(LCD_RGBPACK(100,255,100));
929 #elif LCD_DEPTH == 2
930 rb->lcd_set_foreground(LCD_BLACK);
931 #endif
932 rb->lcd_fillrect(SCALE(iPosX), SCALE(iPosY), SCALE(mBlock->iSizeX),
933 SCALE(mBlock->iSizeY));
937 static void chopRenderTerrain(struct CTerrain *ter, bool isground)
940 int i = 1;
942 int oldx = 0;
944 while(i < ter->iNodesCount && oldx < iScreenX)
947 int x = ter->mNodes[i-1].x - iCameraPosX;
948 int y = ter->mNodes[i-1].y;
950 int x2 = ter->mNodes[i].x - iCameraPosX;
951 int y2 = ter->mNodes[i].y;
953 int ax, ay;
955 if ((y < y2) != isground)
957 ax = x2;
958 ay = y;
960 else
962 ax = x;
963 ay = y2;
965 #if LCD_DEPTH > 2
966 rb->lcd_set_foreground(LCD_RGBPACK(100,255,100));
967 #elif LCD_DEPTH == 2
968 rb->lcd_set_foreground(LCD_DARKGRAY);
969 #endif
971 rb->lcd_drawline(SCALE(x), SCALE(y), SCALE(x2), SCALE(y2));
973 xlcd_filltriangle(SCALE(x), SCALE(y), SCALE(x2), SCALE(y2),
974 SCALE(ax), SCALE(ay));
976 if (isground)
978 y = ay;
979 y2 = (LCD_HEIGHT*SIZE);
981 else
983 y = 0;
984 y2 = ay;
986 if (y2-y > 0)
987 rb->lcd_fillrect(SCALE(x), SCALE(y), SCALE(x2-x)+1, SCALE(y2-y)+1);
989 oldx = x;
990 i++;
994 static void chopper_load(bool newgame)
997 int i;
998 int g;
1000 if (newgame) {
1001 iScreenX = LCD_WIDTH * SIZE;
1002 iScreenY = LCD_HEIGHT * SIZE;
1003 blockh = iScreenY / 5;
1004 blockw = iScreenX / 20;
1005 iPlayerAlive = 1;
1006 iCurrLevelMode = iLevelMode;
1007 score = 0;
1009 iRotorOffset = 0;
1010 iPlayerPosX = 60;
1011 iPlayerPosY = (iScreenY * 4) / 10;
1012 iLastBlockPlacedPosX = 0;
1013 iGravityTimerCountdown = 2;
1014 chopCounter = 0;
1015 iPlayerSpeedX = 3;
1016 iPlayerSpeedY = 0;
1017 iCameraPosX = 30;
1019 for (i=0; i < NUMBER_OF_PARTICLES; i++)
1020 mParticles[i].bIsActive = 0;
1022 for (i=0; i < NUMBER_OF_BLOCKS; i++)
1023 mBlocks[i].bIsActive = 0;
1025 g = iScreenY - 10;
1026 chopClearTerrain(&mGround);
1028 for (i=0; i < MAX_TERRAIN_NODES; i++)
1029 chopAddTerrainNode(&mGround,i * 30,g - iR(0,20));
1031 if (chopUpdateTerrainRecycling(&mGround) == 1)
1032 /* mirror the sky if we've changed the ground */
1033 chopCopyTerrain(&mGround, &mRoof, 0, - ( (iScreenY * 3) / 4));
1035 if (iCurrLevelMode == LEVEL_MODE_NORMAL)
1036 /* make it a bit more exciting, cause it's easy terrain... */
1037 iPlayerSpeedX *= 2;
1040 /* this is the plugin entry point */
1041 enum plugin_status plugin_start(const void* parameter)
1043 (void)parameter;
1044 int ret;
1046 rb->lcd_setfont(FONT_SYSFIXED);
1047 #if LCD_DEPTH > 1
1048 rb->lcd_set_backdrop(NULL);
1049 #endif
1050 #ifdef HAVE_LCD_COLOR
1051 rb->lcd_set_background(LCD_BLACK);
1052 rb->lcd_set_foreground(LCD_WHITE);
1053 #endif
1055 /* Turn off backlight timeout */
1056 backlight_ignore_timeout();
1058 rb->srand( *rb->current_tick );
1060 configfile_load(CFG_FILE, config, 1, 0);
1062 chopper_load(true);
1063 ret = chopGameLoop();
1065 configfile_save(CFG_FILE, config, 1, 0);
1067 rb->lcd_setfont(FONT_UI);
1068 /* Turn on backlight timeout (revert to settings) */
1069 backlight_use_settings();
1071 return ret;