chopper: fix FS#11000: Drawing issue with steep mode. don't change level while in...
[kugel-rb.git] / apps / plugins / chopper.c
blobc48e1a995110b3ce11c3d5470836748372dc9b62
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"
29 PLUGIN_HEADER
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 #define QUIT BUTTON_POWER
75 #define ACTION BUTTON_SELECT
76 #define ACTIONTEXT "SELECT"
78 #elif (CONFIG_KEYPAD == SANSA_FUZE_PAD)
79 #define QUIT BUTTON_HOME
80 #define ACTION BUTTON_SELECT
81 #define ACTIONTEXT "SELECT"
83 #elif CONFIG_KEYPAD == GIGABEAT_PAD
84 #define QUIT BUTTON_MENU
85 #define ACTION BUTTON_SELECT
86 #define ACTIONTEXT "SELECT"
88 #elif CONFIG_KEYPAD == RECORDER_PAD
89 #define QUIT BUTTON_OFF
90 #define ACTION BUTTON_PLAY
91 #define ACTIONTEXT "PLAY"
93 #elif CONFIG_KEYPAD == ONDIO_PAD
94 #define QUIT BUTTON_OFF
95 #define ACTION BUTTON_UP
96 #define ACTION2 BUTTON_MENU
97 #define ACTIONTEXT "UP"
99 #elif CONFIG_KEYPAD == GIGABEAT_S_PAD
100 #define QUIT BUTTON_BACK
101 #define ACTION BUTTON_SELECT
102 #define ACTION2 BUTTON_MENU
103 #define ACTIONTEXT "SELECT"
105 #elif CONFIG_KEYPAD == MROBE100_PAD
106 #define QUIT BUTTON_POWER
107 #define ACTION BUTTON_SELECT
108 #define ACTIONTEXT "SELECT"
110 #elif CONFIG_KEYPAD == IAUDIO_M3_PAD
111 #define QUIT BUTTON_RC_REC
112 #define ACTION BUTTON_RC_PLAY
113 #define ACTION2 BUTTON_RC_MODE
114 #define ACTIONTEXT "PLAY"
116 #elif CONFIG_KEYPAD == COWON_D2_PAD
117 #define QUIT BUTTON_POWER
119 #elif CONFIG_KEYPAD == IAUDIO67_PAD
120 #define QUIT BUTTON_POWER
121 #define ACTION BUTTON_PLAY
122 #define ACTION2 BUTTON_STOP
123 #define ACTIONTEXT "PLAY"
125 #elif CONFIG_KEYPAD == CREATIVEZVM_PAD
126 #define QUIT BUTTON_BACK
127 #define ACTION BUTTON_UP
128 #define ACTION2 BUTTON_MENU
129 #define ACTIONTEXT "UP"
131 #elif CONFIG_KEYPAD == PHILIPS_HDD1630_PAD
132 #define QUIT BUTTON_POWER
133 #define ACTION BUTTON_MENU
134 #define ACTION2 BUTTON_SELECT
135 #define ACTIONTEXT "MENU"
137 #elif CONFIG_KEYPAD == PHILIPS_SA9200_PAD
138 #define QUIT BUTTON_POWER
139 #define ACTION BUTTON_MENU
140 #define ACTION2 BUTTON_PLAY
141 #define ACTIONTEXT "MENU"
143 #elif CONFIG_KEYPAD == ONDAVX747_PAD || \
144 CONFIG_KEYPAD == ONDAVX777_PAD || \
145 CONFIG_KEYPAD == MROBE500_PAD
146 #define QUIT BUTTON_POWER
148 #elif CONFIG_KEYPAD == SAMSUNG_YH_PAD
149 #define QUIT BUTTON_LEFT
150 #define ACTION BUTTON_RIGHT
151 #define ACTIONTEXT "RIGHT"
153 #else
154 #error No keymap defined!
155 #endif
157 #ifdef HAVE_TOUCHSCREEN
158 #ifndef QUIT
159 #define QUIT BUTTON_TOPLEFT
160 #endif
161 #ifndef ACTION
162 #define ACTION BUTTON_BOTTOMLEFT
163 #endif
164 #ifndef ACTION2
165 #define ACTION2 BUTTON_BOTTOMRIGHT
166 #endif
167 #ifndef ACTIONTEXT
168 #define ACTIONTEXT "BOTTOMRIGHT"
169 #endif
170 #endif
172 #define NUMBER_OF_BLOCKS 8
173 #define NUMBER_OF_PARTICLES 3
174 #define MAX_TERRAIN_NODES 15
176 #define LEVEL_MODE_NORMAL 0
177 #define LEVEL_MODE_STEEP 1
179 #if LCD_HEIGHT <= 64
180 #define CYCLES 100
181 static inline int SCALE(int x)
183 return x == 1 ? x : x >> 1;
185 #define SIZE 2
186 #else
187 #define CYCLES 60
188 #define SCALE(x) (x)
189 #define SIZE 1
190 #endif
192 /* in 10 milisecond (ticks) */
193 #define CYCLETIME ((CYCLES*HZ)/1000)
195 /*Chopper's local variables to track the terrain position etc*/
196 static int chopCounter;
197 static int iRotorOffset;
198 static int iScreenX;
199 static int iScreenY;
200 static int iPlayerPosX;
201 static int iPlayerPosY;
202 static int iCameraPosX;
203 static int iPlayerSpeedX;
204 static int iPlayerSpeedY;
205 static int iLastBlockPlacedPosX;
206 static int iGravityTimerCountdown;
207 static int iPlayerAlive;
208 static int iLevelMode, iCurrLevelMode;
209 static int blockh,blockw;
210 static int highscore;
211 static int score;
213 #define CFG_FILE "chopper.cfg"
214 #define MAX_POINTS 50000
215 static struct configdata config[] =
217 {TYPE_INT, 0, MAX_POINTS, { .int_p = &highscore }, "highscore", NULL}
220 struct CBlock
222 int iWorldX;
223 int iWorldY;
225 int iSizeX;
226 int iSizeY;
228 int bIsActive;
231 struct CParticle
233 int iWorldX;
234 int iWorldY;
236 int iSpeedX;
237 int iSpeedY;
239 int bIsActive;
242 struct CTerrainNode
244 int x;
245 int y;
248 struct CTerrain
250 struct CTerrainNode mNodes[MAX_TERRAIN_NODES];
251 int iNodesCount;
252 int iLastNodePlacedPosX;
255 struct CBlock mBlocks[NUMBER_OF_BLOCKS];
256 struct CParticle mParticles[NUMBER_OF_PARTICLES];
258 struct CTerrain mGround;
259 struct CTerrain mRoof;
261 /*Function declarations*/
262 static void chopDrawParticle(struct CParticle *mParticle);
263 static void chopDrawBlock(struct CBlock *mBlock);
264 static void chopRenderTerrain(struct CTerrain *ter, bool isground);
265 void chopper_load(bool newgame);
266 void cleanup_chopper(void);
268 static void chopDrawPlayer(int x,int y) /* These are SCREEN coords, not world!*/
271 #if LCD_DEPTH > 2
272 rb->lcd_set_foreground(LCD_RGBPACK(50,50,200));
273 #elif LCD_DEPTH == 2
274 rb->lcd_set_foreground(LCD_DARKGRAY);
275 #endif
276 rb->lcd_fillrect(SCALE(x+6), SCALE(y+2), SCALE(12), SCALE(9));
277 rb->lcd_fillrect(SCALE(x-3), SCALE(y+6), SCALE(20), SCALE(3));
279 #if LCD_DEPTH > 2
280 rb->lcd_set_foreground(LCD_RGBPACK(50,50,50));
281 #elif LCD_DEPTH == 2
282 rb->lcd_set_foreground(LCD_DARKGRAY);
283 #endif
284 rb->lcd_fillrect(SCALE(x+10), SCALE(y), SCALE(2), SCALE(3));
285 rb->lcd_fillrect(SCALE(x+10), SCALE(y), SCALE(1), SCALE(3));
287 #if LCD_DEPTH > 2
288 rb->lcd_set_foreground(LCD_RGBPACK(40,40,100));
289 #elif LCD_DEPTH == 2
290 rb->lcd_set_foreground(LCD_BLACK);
291 #endif
292 rb->lcd_drawline(SCALE(x), SCALE(y+iRotorOffset), SCALE(x+20),
293 SCALE(y-iRotorOffset));
295 #if LCD_DEPTH > 2
296 rb->lcd_set_foreground(LCD_RGBPACK(20,20,50));
297 #elif LCD_DEPTH == 2
298 rb->lcd_set_foreground(LCD_BLACK);
299 #endif
300 rb->lcd_fillrect(SCALE(x - 2), SCALE(y + 5), SCALE(2), SCALE(5));
304 static void chopClearTerrain(struct CTerrain *ter)
306 ter->iNodesCount = 0;
310 int iR(int low,int high)
312 return low+rb->rand()%(high-low+1);
315 static void chopCopyTerrain(struct CTerrain *src, struct CTerrain *dest,
316 int xOffset,int yOffset)
318 int i=0;
320 while(i < src->iNodesCount)
322 dest->mNodes[i].x = src->mNodes[i].x + xOffset;
323 dest->mNodes[i].y = src->mNodes[i].y + yOffset;
325 i++;
328 dest->iNodesCount = src->iNodesCount;
329 dest->iLastNodePlacedPosX = src->iLastNodePlacedPosX;
333 static void chopAddTerrainNode(struct CTerrain *ter, int x, int y)
335 int i=0;
337 if(ter->iNodesCount + 1 >= MAX_TERRAIN_NODES)
339 /* DEBUGF("ERROR: Not enough nodes!\n"); */
340 return;
343 ter->iNodesCount++;
345 i = ter->iNodesCount - 1;
347 ter->mNodes[i].x = x;
348 ter->mNodes[i].y= y;
350 ter->iLastNodePlacedPosX = x;
354 static void chopTerrainNodeDeleteAndShift(struct CTerrain *ter,int nodeIndex)
356 int i=nodeIndex;
358 while( i < ter->iNodesCount )
360 ter->mNodes[i - 1] = ter->mNodes[i];
361 i++;
364 ter->iNodesCount--;
369 int chopUpdateTerrainRecycling(struct CTerrain *ter)
371 int i=1;
372 int ret = 0;
373 int iNewNodePos,g,v;
374 while(i < ter->iNodesCount)
377 if( iCameraPosX > ter->mNodes[i].x)
380 chopTerrainNodeDeleteAndShift(ter,i);
382 iNewNodePos = ter->iLastNodePlacedPosX + 50;
383 g = iScreenY - 10;
385 v = 3*iPlayerSpeedX;
386 if(v>50)
387 v=50;
388 if(iCurrLevelMode == LEVEL_MODE_STEEP)
389 v*=5;
391 chopAddTerrainNode(ter,iNewNodePos,g - iR(-v,v));
392 ret=1;
396 i++;
400 return 1;
403 int chopTerrainHeightAtPoint(struct CTerrain *ter, int pX)
406 int iNodeIndexOne=0,iNodeIndexTwo=0, h, terY1, terY2, terX1, terX2, a, b;
407 float c,d;
409 int i=0;
410 for(i=1;i<MAX_TERRAIN_NODES;i++)
412 if(ter->mNodes[i].x > pX)
414 iNodeIndexOne = i - 1;
415 break;
420 iNodeIndexTwo = iNodeIndexOne + 1;
421 terY1 = ter->mNodes[iNodeIndexOne].y;
422 terY2 = ter->mNodes[iNodeIndexTwo].y;
424 terX1 = 0;
425 terX2 = ter->mNodes[iNodeIndexTwo].x - ter->mNodes[iNodeIndexOne].x;
427 pX-= ter->mNodes[iNodeIndexOne].x;
429 a = terY2 - terY1;
430 b = terX2;
431 c = pX;
432 d = (c/b) * a;
434 h = d + terY1;
436 return h;
440 int chopPointInTerrain(struct CTerrain *ter, int pX, int pY, int iTestType)
442 int h = chopTerrainHeightAtPoint(ter, pX);
444 if(iTestType == 0)
445 return (pY > h);
446 else
447 return (pY < h);
450 static void chopAddBlock(int x,int y,int sx,int sy, int indexOverride)
452 int i=0;
454 if(indexOverride < 0)
456 while(mBlocks[i].bIsActive && i < NUMBER_OF_BLOCKS)
457 i++;
458 if(i==NUMBER_OF_BLOCKS)
460 DEBUGF("No blocks!\n");
461 return;
464 else
465 i = indexOverride;
467 mBlocks[i].bIsActive = 1;
468 mBlocks[i].iWorldX = x;
469 mBlocks[i].iWorldY = y;
470 mBlocks[i].iSizeX = sx;
471 mBlocks[i].iSizeY = sy;
473 iLastBlockPlacedPosX = x;
476 static void chopAddParticle(int x,int y,int sx,int sy)
478 int i=0;
480 while(mParticles[i].bIsActive && i < NUMBER_OF_PARTICLES)
481 i++;
483 if(i==NUMBER_OF_PARTICLES)
484 return;
486 mParticles[i].bIsActive = 1;
487 mParticles[i].iWorldX = x;
488 mParticles[i].iWorldY = y;
489 mParticles[i].iSpeedX = sx;
490 mParticles[i].iSpeedY = sy;
493 static void chopGenerateBlockIfNeeded(void)
495 int i=0;
496 int DistSpeedX = iPlayerSpeedX * 5;
497 if(DistSpeedX<200) DistSpeedX = 200;
499 while(i < NUMBER_OF_BLOCKS)
501 if(!mBlocks[i].bIsActive)
503 int iX,iY,sX,sY;
505 iX = iLastBlockPlacedPosX + (350-DistSpeedX);
506 sX = blockw;
508 iY = iR(0,iScreenY);
509 sY = blockh + iR(1,blockh/3);
511 chopAddBlock(iX,iY,sX,sY,i);
514 i++;
519 static int chopBlockCollideWithPlayer(struct CBlock *mBlock)
521 int px = iPlayerPosX;
522 int py = iPlayerPosY;
524 int x = mBlock->iWorldX-17;
525 int y = mBlock->iWorldY-11;
527 int x2 = x + mBlock->iSizeX+17;
528 int y2 = y + mBlock->iSizeY+11;
530 if(px>x && px<x2)
532 if(py>y && py<y2)
534 return 1;
538 return 0;
541 static int chopBlockOffscreen(struct CBlock *mBlock)
543 if(mBlock->iWorldX + mBlock->iSizeX < iCameraPosX)
544 return 1;
545 else
546 return 0;
549 static int chopParticleOffscreen(struct CParticle *mParticle)
551 if (mParticle->iWorldX < iCameraPosX || mParticle->iWorldY < 0 ||
552 mParticle->iWorldY > iScreenY || mParticle->iWorldX > iCameraPosX +
553 iScreenX)
555 return 1;
557 else
558 return 0;
561 static void chopKillPlayer(void)
563 int i;
565 for (i = 0; i < NUMBER_OF_PARTICLES; i++) {
566 mParticles[i].bIsActive = 0;
567 chopAddParticle(iPlayerPosX + iR(0,20), iPlayerPosY + iR(0,20),
568 iR(-2,2), iR(-2,2));
571 iPlayerAlive--;
573 if (iPlayerAlive == 0) {
574 rb->splash(HZ, "Game Over");
576 if (score > highscore) {
577 char scoretext[30];
578 highscore = score;
579 rb->snprintf(scoretext, sizeof(scoretext), "New High Score: %d",
580 highscore);
581 rb->splash(HZ*2, scoretext);
583 } else
584 chopper_load(false);
587 static void chopDrawTheWorld(void)
589 int i=0;
591 while(i < NUMBER_OF_BLOCKS)
593 if(mBlocks[i].bIsActive)
595 if(chopBlockOffscreen(&mBlocks[i]) == 1)
596 mBlocks[i].bIsActive = 0;
597 else
598 chopDrawBlock(&mBlocks[i]);
601 i++;
604 i=0;
606 while(i < NUMBER_OF_PARTICLES)
608 if(mParticles[i].bIsActive)
610 if(chopParticleOffscreen(&mParticles[i]) == 1)
611 mParticles[i].bIsActive = 0;
612 else
613 chopDrawParticle(&mParticles[i]);
616 i++;
619 chopRenderTerrain(&mGround, true);
620 chopRenderTerrain(&mRoof, false);
624 static void chopDrawParticle(struct CParticle *mParticle)
627 int iPosX = (mParticle->iWorldX - iCameraPosX);
628 int iPosY = (mParticle->iWorldY);
629 #if LCD_DEPTH > 2
630 rb->lcd_set_foreground(LCD_RGBPACK(192,192,192));
631 #elif LCD_DEPTH == 2
632 rb->lcd_set_foreground(LCD_LIGHTGRAY);
633 #endif
634 rb->lcd_fillrect(SCALE(iPosX), SCALE(iPosY), SCALE(3), SCALE(3));
638 static void chopDrawScene(void)
640 char s[30];
641 int w;
642 #if LCD_DEPTH > 2
643 rb->lcd_set_background(LCD_BLACK);
644 #elif LCD_DEPTH == 2
645 rb->lcd_set_background(LCD_WHITE);
646 #endif
647 rb->lcd_clear_display();
648 chopDrawTheWorld();
649 chopDrawPlayer(iPlayerPosX - iCameraPosX, iPlayerPosY);
651 score = -20 + iPlayerPosX/3;
653 #if LCD_DEPTH == 1
654 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
655 #else
656 rb->lcd_set_drawmode(DRMODE_FG);
657 #endif
659 #if LCD_DEPTH > 2
660 rb->lcd_set_foreground(LCD_BLACK);
661 #elif LCD_DEPTH == 2
662 rb->lcd_set_foreground(LCD_WHITE);
663 #endif
665 #if LCD_WIDTH <= 128
666 rb->snprintf(s, sizeof(s), "Dist: %d", score);
667 #else
668 rb->snprintf(s, sizeof(s), "Distance: %d", score);
669 #endif
670 rb->lcd_getstringsize(s, &w, NULL);
671 rb->lcd_putsxy(2, 2, s);
672 if (score < highscore)
674 int w2;
675 #if LCD_WIDTH <= 128
676 rb->snprintf(s, sizeof(s), "Hi: %d", highscore);
677 #else
678 rb->snprintf(s, sizeof(s), "Best: %d", highscore);
679 #endif
680 rb->lcd_getstringsize(s, &w2, NULL);
681 if (LCD_WIDTH - 2 - w2 > w + 2)
682 rb->lcd_putsxy(LCD_WIDTH - 2 - w2, 2, s);
684 rb->lcd_set_drawmode(DRMODE_SOLID);
686 rb->lcd_update();
689 static bool _ingame;
690 static int chopMenuCb(int action, const struct menu_item_ex *this_item)
692 if(action == ACTION_REQUEST_MENUITEM
693 && !_ingame && ((intptr_t)this_item)==0)
694 return ACTION_EXIT_MENUITEM;
695 return action;
697 static int chopMenu(int menunum)
699 int result = 0;
700 int res = 0;
701 bool menu_quit = false;
703 static const struct opt_items levels[2] = {
704 { "Normal", -1 },
705 { "Steep", -1 },
708 MENUITEM_STRINGLIST(menu,"Chopper Menu",chopMenuCb,
709 "Resume Game","Start New Game",
710 "Level","Playback Control","Quit");
711 _ingame = (menunum!=0);
713 #ifdef HAVE_LCD_COLOR
714 rb->lcd_set_foreground(LCD_WHITE);
715 rb->lcd_set_background(LCD_BLACK);
716 #elif LCD_DEPTH == 2
717 rb->lcd_set_foreground(LCD_BLACK);
718 rb->lcd_set_background(LCD_WHITE);
719 #endif
721 rb->lcd_clear_display();
722 rb->button_clear_queue();
724 while (!menu_quit) {
725 switch(rb->do_menu(&menu, &result, NULL, false))
727 case 0: /* Resume Game */
728 menu_quit=true;
729 res = -1;
730 break;
731 case 1: /* Start New Game */
732 menu_quit=true;
733 chopper_load(true);
734 res = -1;
735 break;
736 case 2:
737 rb->set_option("Level", &iLevelMode, INT, levels, 2, NULL);
738 break;
739 case 3:
740 playback_control(NULL);
741 break;
742 case 4:
743 menu_quit=true;
744 res = PLUGIN_OK;
745 break;
746 case MENU_ATTACHED_USB:
747 menu_quit=true;
748 res = PLUGIN_USB_CONNECTED;
749 break;
752 rb->lcd_clear_display();
753 return res;
756 static int chopGameLoop(void)
758 int move_button, ret;
759 bool exit=false;
760 int end, i=0, bdelay=0, last_button=BUTTON_NONE;
762 if (chopUpdateTerrainRecycling(&mGround) == 1)
763 /* mirror the sky if we've changed the ground */
764 chopCopyTerrain(&mGround, &mRoof, 0, - ( (iScreenY * 3) / 4));
766 ret = chopMenu(0);
767 if (ret != -1)
768 return PLUGIN_OK;
770 chopDrawScene();
772 while (!exit) {
774 end = *rb->current_tick + CYCLETIME;
776 if(chopUpdateTerrainRecycling(&mGround) == 1)
777 /* mirror the sky if we've changed the ground */
778 chopCopyTerrain(&mGround, &mRoof, 0, - ( (iScreenY * 3) / 4));
780 iRotorOffset = iR(-1,1);
782 /* We need to have this here so particles move when we're dead */
784 for (i=0; i < NUMBER_OF_PARTICLES; i++)
785 if(mParticles[i].bIsActive == 1)
787 mParticles[i].iWorldX += mParticles[i].iSpeedX;
788 mParticles[i].iWorldY += mParticles[i].iSpeedY;
791 /* Redraw the main window: */
792 chopDrawScene();
795 iGravityTimerCountdown--;
797 if(iGravityTimerCountdown <= 0)
799 iGravityTimerCountdown = 3;
800 chopAddParticle(iPlayerPosX, iPlayerPosY+5, 0, 0);
803 if(iCurrLevelMode == LEVEL_MODE_NORMAL)
804 chopGenerateBlockIfNeeded();
807 move_button=rb->button_status();
808 if (rb->button_get(false) == QUIT) {
809 ret = chopMenu(1);
810 if (ret != -1)
811 return PLUGIN_OK;
812 bdelay = 0;
813 last_button = BUTTON_NONE;
814 move_button = BUTTON_NONE;
817 switch (move_button) {
818 case ACTION:
819 #ifdef ACTION2
820 case ACTION2:
821 #endif
822 if (last_button != ACTION
823 #ifdef ACTION2
824 && last_button != ACTION2
825 #endif
827 bdelay = -2;
828 if (bdelay == 0)
829 iPlayerSpeedY = -3;
830 break;
832 default:
833 if (last_button == ACTION
834 #ifdef ACTION2
835 || last_button == ACTION2
836 #endif
838 bdelay = 3;
839 if (bdelay == 0)
840 iPlayerSpeedY = 4;
842 if (rb->default_event_handler(move_button) == SYS_USB_CONNECTED)
843 return PLUGIN_USB_CONNECTED;
844 break;
846 last_button = move_button;
848 if (bdelay < 0) {
849 iPlayerSpeedY = bdelay;
850 bdelay++;
851 } else if (bdelay > 0) {
852 iPlayerSpeedY = bdelay;
853 bdelay--;
856 iCameraPosX = iPlayerPosX - 25;
857 iPlayerPosX += iPlayerSpeedX;
858 iPlayerPosY += iPlayerSpeedY;
860 chopCounter++;
861 /* increase speed as we go along */
862 if (chopCounter == 100){
863 iPlayerSpeedX++;
864 chopCounter=0;
867 if (iPlayerPosY > iScreenY-10 || iPlayerPosY < -5 ||
868 chopPointInTerrain(&mGround, iPlayerPosX, iPlayerPosY + 10, 0) ||
869 chopPointInTerrain(&mRoof, iPlayerPosX ,iPlayerPosY, 1))
871 chopKillPlayer();
872 chopDrawScene();
873 ret = chopMenu(0);
874 if (ret != -1)
875 return ret;
878 for (i=0; i < NUMBER_OF_BLOCKS; i++)
879 if(mBlocks[i].bIsActive == 1)
880 if(chopBlockCollideWithPlayer(&mBlocks[i])) {
881 chopKillPlayer();
882 chopDrawScene();
883 ret = chopMenu(0);
884 if (ret != -1)
885 return ret;
888 if (TIME_BEFORE(*rb->current_tick, end))
889 rb->sleep(end - *rb->current_tick); /* wait until time is over */
890 else
891 rb->yield();
894 return PLUGIN_OK;
897 static void chopDrawBlock(struct CBlock *mBlock)
899 int iPosX = (mBlock->iWorldX - iCameraPosX);
900 int iPosY = (mBlock->iWorldY);
901 #if LCD_DEPTH > 2
902 rb->lcd_set_foreground(LCD_RGBPACK(100,255,100));
903 #elif LCD_DEPTH == 2
904 rb->lcd_set_foreground(LCD_BLACK);
905 #endif
906 rb->lcd_fillrect(SCALE(iPosX), SCALE(iPosY), SCALE(mBlock->iSizeX),
907 SCALE(mBlock->iSizeY));
911 static void chopRenderTerrain(struct CTerrain *ter, bool isground)
914 int i = 1;
916 int oldx = 0;
918 while(i < ter->iNodesCount && oldx < iScreenX)
921 int x = ter->mNodes[i-1].x - iCameraPosX;
922 int y = ter->mNodes[i-1].y;
924 int x2 = ter->mNodes[i].x - iCameraPosX;
925 int y2 = ter->mNodes[i].y;
927 int ax, ay;
929 if ((y < y2) != isground)
931 ax = x2;
932 ay = y;
934 else
936 ax = x;
937 ay = y2;
939 #if LCD_DEPTH > 2
940 rb->lcd_set_foreground(LCD_RGBPACK(100,255,100));
941 #elif LCD_DEPTH == 2
942 rb->lcd_set_foreground(LCD_DARKGRAY);
943 #endif
945 rb->lcd_drawline(SCALE(x), SCALE(y), SCALE(x2), SCALE(y2));
947 xlcd_filltriangle(SCALE(x), SCALE(y), SCALE(x2), SCALE(y2),
948 SCALE(ax), SCALE(ay));
950 if (isground)
952 y = ay;
953 y2 = (LCD_HEIGHT*SIZE);
955 else
957 y = 0;
958 y2 = ay;
960 if (y2-y > 0)
961 rb->lcd_fillrect(SCALE(x), SCALE(y), SCALE(x2-x)+1, SCALE(y2-y)+1);
963 oldx = x;
964 i++;
968 void chopper_load(bool newgame)
971 int i;
972 int g;
974 if (newgame) {
975 iScreenX = LCD_WIDTH * SIZE;
976 iScreenY = LCD_HEIGHT * SIZE;
977 blockh = iScreenY / 5;
978 blockw = iScreenX / 20;
979 iPlayerAlive = 1;
980 iCurrLevelMode = iLevelMode;
981 score = 0;
983 iRotorOffset = 0;
984 iPlayerPosX = 60;
985 iPlayerPosY = (iScreenY * 4) / 10;
986 iLastBlockPlacedPosX = 0;
987 iGravityTimerCountdown = 2;
988 chopCounter = 0;
989 iPlayerSpeedX = 3;
990 iPlayerSpeedY = 0;
991 iCameraPosX = 30;
993 for (i=0; i < NUMBER_OF_PARTICLES; i++)
994 mParticles[i].bIsActive = 0;
996 for (i=0; i < NUMBER_OF_BLOCKS; i++)
997 mBlocks[i].bIsActive = 0;
999 g = iScreenY - 10;
1000 chopClearTerrain(&mGround);
1002 for (i=0; i < MAX_TERRAIN_NODES; i++)
1003 chopAddTerrainNode(&mGround,i * 30,g - iR(0,20));
1005 if (chopUpdateTerrainRecycling(&mGround) == 1)
1006 /* mirror the sky if we've changed the ground */
1007 chopCopyTerrain(&mGround, &mRoof, 0, - ( (iScreenY * 3) / 4));
1009 if (iCurrLevelMode == LEVEL_MODE_NORMAL)
1010 /* make it a bit more exciting, cause it's easy terrain... */
1011 iPlayerSpeedX *= 2;
1014 /* this is the plugin entry point */
1015 enum plugin_status plugin_start(const void* parameter)
1017 (void)parameter;
1018 int ret;
1020 rb->lcd_setfont(FONT_SYSFIXED);
1021 #if LCD_DEPTH > 1
1022 rb->lcd_set_backdrop(NULL);
1023 #endif
1024 #ifdef HAVE_LCD_COLOR
1025 rb->lcd_set_background(LCD_BLACK);
1026 rb->lcd_set_foreground(LCD_WHITE);
1027 #endif
1029 /* Turn off backlight timeout */
1030 backlight_force_on(); /* backlight control in lib/helper.c */
1032 rb->srand( *rb->current_tick );
1034 configfile_load(CFG_FILE, config, 1, 0);
1036 chopper_load(true);
1037 ret = chopGameLoop();
1039 configfile_save(CFG_FILE, config, 1, 0);
1041 rb->lcd_setfont(FONT_UI);
1042 /* Turn on backlight timeout (revert to settings) */
1043 backlight_use_settings(); /* backlight control in lib/helper.c */
1045 return ret;