Fix voicing of incorrect run time (top time instead of run time). Simplify runtime...
[kugel-rb.git] / apps / plugins / chopper.c
blob9b854ae0e170d412d4ced12df8af67b5b6b3ea1c
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 "xlcd.h"
25 #include "configfile.h"
26 #include "helper.h"
28 PLUGIN_HEADER
31 Still To do:
32 - Make original speed and further increases in speed depend more on screen size
33 - attempt to make the tunnels get narrower as the game goes on
34 - make the chopper look better, maybe a picture, and scale according
35 to screen size
36 - use textures for the color screens for background and terrain,
37 eg stars on background
38 - allow choice of different levels [later: different screen themes]
39 - better high score handling, improved screen etc.
42 #if (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD)
44 #define QUIT BUTTON_OFF
45 #define ACTION BUTTON_UP
46 #define ACTION2 BUTTON_SELECT
47 #define ACTIONTEXT "SELECT"
49 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
50 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
51 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
53 #define QUIT BUTTON_MENU
54 #define ACTION BUTTON_SELECT
55 #define ACTIONTEXT "SELECT"
57 #elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD /* grayscale at the moment */
59 #define QUIT BUTTON_POWER
60 #define ACTION BUTTON_UP
61 #define ACTION2 BUTTON_SELECT
62 #define ACTIONTEXT "SELECT"
64 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
65 #define QUIT BUTTON_POWER
66 #define ACTION BUTTON_RIGHT
67 #define ACTIONTEXT "RIGHT"
69 #elif (CONFIG_KEYPAD == SANSA_E200_PAD) || \
70 (CONFIG_KEYPAD == SANSA_C200_PAD)
71 #define QUIT BUTTON_POWER
72 #define ACTION BUTTON_SELECT
73 #define ACTIONTEXT "SELECT"
75 #elif CONFIG_KEYPAD == GIGABEAT_PAD
76 #define QUIT BUTTON_MENU
77 #define ACTION BUTTON_SELECT
78 #define ACTIONTEXT "SELECT"
80 #elif CONFIG_KEYPAD == RECORDER_PAD
81 #define QUIT BUTTON_OFF
82 #define ACTION BUTTON_PLAY
83 #define ACTIONTEXT "PLAY"
85 #elif CONFIG_KEYPAD == ONDIO_PAD
86 #define QUIT BUTTON_OFF
87 #define ACTION BUTTON_UP
88 #define ACTION2 BUTTON_MENU
89 #define ACTIONTEXT "UP"
91 #elif CONFIG_KEYPAD == GIGABEAT_S_PAD
92 #define QUIT BUTTON_BACK
93 #define ACTION BUTTON_SELECT
94 #define ACTION2 BUTTON_MENU
95 #define ACTIONTEXT "SELECT"
97 #elif CONFIG_KEYPAD == MROBE100_PAD
98 #define QUIT BUTTON_POWER
99 #define ACTION BUTTON_SELECT
100 #define ACTIONTEXT "SELECT"
102 #elif CONFIG_KEYPAD == IAUDIO_M3_PAD
103 #define QUIT BUTTON_RC_REC
104 #define ACTION BUTTON_RC_PLAY
105 #define ACTION2 BUTTON_RC_MODE
106 #define ACTIONTEXT "PLAY"
108 #elif CONFIG_KEYPAD == COWOND2_PAD
109 #define QUIT BUTTON_POWER
111 #else
112 #error No keymap defined!
113 #endif
115 #ifdef HAVE_TOUCHPAD
116 #ifndef QUIT
117 #define QUIT BUTTON_TOPLEFT
118 #endif
119 #ifndef ACTION
120 #define ACTION BUTTON_BOTTOMLEFT
121 #endif
122 #ifndef ACTION2
123 #define ACTION2 BUTTON_BOTTOMRIGHT
124 #endif
125 #ifndef ACTIONTEXT
126 #define ACTIONTEXT "BOTTOMRIGHT"
127 #endif
128 #endif
130 static const struct plugin_api* rb;
132 #define NUMBER_OF_BLOCKS 8
133 #define NUMBER_OF_PARTICLES 3
134 #define MAX_TERRAIN_NODES 15
136 #define LEVEL_MODE_NORMAL 0
137 #define LEVEL_MODE_STEEP 1
139 #if LCD_WIDTH <= 112
140 #define CYCLETIME 100
141 #define SCALE(x) ((x)==1 ? (x) : ((x) >> 1))
142 #define SIZE 2
143 #else
144 #define CYCLETIME 60
145 #define SCALE(x) (x)
146 #define SIZE 1
147 #endif
149 /*Chopper's local variables to track the terrain position etc*/
150 static int chopCounter;
151 static int iRotorOffset;
152 static int iScreenX;
153 static int iScreenY;
154 static int iPlayerPosX;
155 static int iPlayerPosY;
156 static int iCameraPosX;
157 static int iPlayerSpeedX;
158 static int iPlayerSpeedY;
159 static int iLastBlockPlacedPosX;
160 static int iGravityTimerCountdown;
161 static int iPlayerAlive;
162 static int iLevelMode;
163 static int blockh,blockw;
164 static int highscore;
165 static int score;
167 #define CFG_FILE "chopper.cfg"
168 #define MAX_POINTS 50000
169 static struct configdata config[] =
171 {TYPE_INT, 0, MAX_POINTS, &highscore, "highscore", NULL, NULL}
174 struct CBlock
176 int iWorldX;
177 int iWorldY;
179 int iSizeX;
180 int iSizeY;
182 int bIsActive;
185 struct CParticle
187 int iWorldX;
188 int iWorldY;
190 int iSpeedX;
191 int iSpeedY;
193 int bIsActive;
196 struct CTerrainNode
198 int x;
199 int y;
202 struct CTerrain
204 struct CTerrainNode mNodes[MAX_TERRAIN_NODES];
205 int iNodesCount;
206 int iLastNodePlacedPosX;
209 struct CBlock mBlocks[NUMBER_OF_BLOCKS];
210 struct CParticle mParticles[NUMBER_OF_PARTICLES];
212 struct CTerrain mGround;
213 struct CTerrain mRoof;
215 /*Function declarations*/
216 static void chopDrawParticle(struct CParticle *mParticle);
217 static void chopDrawBlock(struct CBlock *mBlock);
218 static void chopRenderTerrain(struct CTerrain *ter);
219 void chopper_load(bool newgame);
220 void cleanup_chopper(void);
222 static void chopDrawPlayer(int x,int y) /* These are SCREEN coords, not world!*/
225 #if LCD_DEPTH > 2
226 rb->lcd_set_foreground(LCD_RGBPACK(50,50,200));
227 #elif LCD_DEPTH == 2
228 rb->lcd_set_foreground(LCD_DARKGRAY);
229 #endif
230 rb->lcd_fillrect(SCALE(x+6), SCALE(y+2), SCALE(12), SCALE(9));
231 rb->lcd_fillrect(SCALE(x-3), SCALE(y+6), SCALE(20), SCALE(3));
233 #if LCD_DEPTH > 2
234 rb->lcd_set_foreground(LCD_RGBPACK(50,50,50));
235 #elif LCD_DEPTH == 2
236 rb->lcd_set_foreground(LCD_DARKGRAY);
237 #endif
238 rb->lcd_fillrect(SCALE(x+10), SCALE(y), SCALE(2), SCALE(3));
239 rb->lcd_fillrect(SCALE(x+10), SCALE(y), SCALE(1), SCALE(3));
241 #if LCD_DEPTH > 2
242 rb->lcd_set_foreground(LCD_RGBPACK(40,40,100));
243 #elif LCD_DEPTH == 2
244 rb->lcd_set_foreground(LCD_BLACK);
245 #endif
246 rb->lcd_drawline(SCALE(x), SCALE(y+iRotorOffset), SCALE(x+20),
247 SCALE(y-iRotorOffset));
249 #if LCD_DEPTH > 2
250 rb->lcd_set_foreground(LCD_RGBPACK(20,20,50));
251 #elif LCD_DEPTH == 2
252 rb->lcd_set_foreground(LCD_BLACK);
253 #endif
254 rb->lcd_fillrect(SCALE(x - 2), SCALE(y + 5), SCALE(2), SCALE(5));
258 static void chopClearTerrain(struct CTerrain *ter)
260 ter->iNodesCount = 0;
264 int iR(int low,int high)
266 return low+rb->rand()%(high-low+1);
269 static void chopCopyTerrain(struct CTerrain *src, struct CTerrain *dest,
270 int xOffset,int yOffset)
272 int i=0;
274 while(i < src->iNodesCount)
276 dest->mNodes[i].x = src->mNodes[i].x + xOffset;
277 dest->mNodes[i].y = src->mNodes[i].y + yOffset;
279 i++;
282 dest->iNodesCount = src->iNodesCount;
283 dest->iLastNodePlacedPosX = src->iLastNodePlacedPosX;
287 static void chopAddTerrainNode(struct CTerrain *ter, int x, int y)
289 int i=0;
291 if(ter->iNodesCount + 1 >= MAX_TERRAIN_NODES)
293 /* DEBUGF("ERROR: Not enough nodes!\n"); */
294 return;
297 ter->iNodesCount++;
299 i = ter->iNodesCount - 1;
301 ter->mNodes[i].x = x;
302 ter->mNodes[i].y= y;
304 ter->iLastNodePlacedPosX = x;
308 static void chopTerrainNodeDeleteAndShift(struct CTerrain *ter,int nodeIndex)
310 int i=nodeIndex;
312 while( i < ter->iNodesCount )
314 ter->mNodes[i - 1] = ter->mNodes[i];
315 i++;
318 ter->iNodesCount--;
323 int chopUpdateTerrainRecycling(struct CTerrain *ter)
325 int i=1;
326 int ret = 0;
327 int iNewNodePos,g,v;
328 while(i < ter->iNodesCount)
331 if( iCameraPosX > ter->mNodes[i].x)
334 chopTerrainNodeDeleteAndShift(ter,i);
336 iNewNodePos = ter->iLastNodePlacedPosX + 50;
337 g = iScreenY - 10;
339 v = 3*iPlayerSpeedX;
340 if(v>50)
341 v=50;
342 if(iLevelMode == LEVEL_MODE_STEEP)
343 v*=5;
345 chopAddTerrainNode(ter,iNewNodePos,g - iR(-v,v));
346 ret=1;
350 i++;
354 return 1;
357 int chopTerrainHeightAtPoint(struct CTerrain *ter, int pX)
360 int iNodeIndexOne=0,iNodeIndexTwo=0, h, terY1, terY2, terX1, terX2, a, b;
361 float c,d;
363 int i=0;
364 for(i=1;i<MAX_TERRAIN_NODES;i++)
366 if(ter->mNodes[i].x > pX)
368 iNodeIndexOne = i - 1;
369 break;
374 iNodeIndexTwo = iNodeIndexOne + 1;
375 terY1 = ter->mNodes[iNodeIndexOne].y;
376 terY2 = ter->mNodes[iNodeIndexTwo].y;
378 terX1 = 0;
379 terX2 = ter->mNodes[iNodeIndexTwo].x - ter->mNodes[iNodeIndexOne].x;
381 pX-= ter->mNodes[iNodeIndexOne].x;
383 a = terY2 - terY1;
384 b = terX2;
385 c = pX;
386 d = (c/b) * a;
388 h = d + terY1;
390 return h;
394 int chopPointInTerrain(struct CTerrain *ter, int pX, int pY, int iTestType)
396 int h = chopTerrainHeightAtPoint(ter, pX);
398 if(iTestType == 0)
399 return (pY > h);
400 else
401 return (pY < h);
404 static void chopAddBlock(int x,int y,int sx,int sy, int indexOverride)
406 int i=0;
408 if(indexOverride < 0)
410 while(mBlocks[i].bIsActive && i < NUMBER_OF_BLOCKS)
411 i++;
412 if(i==NUMBER_OF_BLOCKS)
414 DEBUGF("No blocks!\n");
415 return;
418 else
419 i = indexOverride;
421 mBlocks[i].bIsActive = 1;
422 mBlocks[i].iWorldX = x;
423 mBlocks[i].iWorldY = y;
424 mBlocks[i].iSizeX = sx;
425 mBlocks[i].iSizeY = sy;
427 iLastBlockPlacedPosX = x;
430 static void chopAddParticle(int x,int y,int sx,int sy)
432 int i=0;
434 while(mParticles[i].bIsActive && i < NUMBER_OF_PARTICLES)
435 i++;
437 if(i==NUMBER_OF_PARTICLES)
438 return;
440 mParticles[i].bIsActive = 1;
441 mParticles[i].iWorldX = x;
442 mParticles[i].iWorldY = y;
443 mParticles[i].iSpeedX = sx;
444 mParticles[i].iSpeedY = sy;
447 static void chopGenerateBlockIfNeeded(void)
449 int i=0;
450 int DistSpeedX = iPlayerSpeedX * 5;
451 if(DistSpeedX<200) DistSpeedX = 200;
453 while(i < NUMBER_OF_BLOCKS)
455 if(!mBlocks[i].bIsActive)
457 int iX,iY,sX,sY;
459 iX = iLastBlockPlacedPosX + (350-DistSpeedX);
460 sX = blockw;
462 iY = iR(0,iScreenY);
463 sY = blockh + iR(1,blockh/3);
465 chopAddBlock(iX,iY,sX,sY,i);
468 i++;
473 static int chopBlockCollideWithPlayer(struct CBlock *mBlock)
475 int px = iPlayerPosX;
476 int py = iPlayerPosY;
478 int x = mBlock->iWorldX-17;
479 int y = mBlock->iWorldY-11;
481 int x2 = x + mBlock->iSizeX+17;
482 int y2 = y + mBlock->iSizeY+11;
484 if(px>x && px<x2)
486 if(py>y && py<y2)
488 return 1;
492 return 0;
495 static int chopBlockOffscreen(struct CBlock *mBlock)
497 if(mBlock->iWorldX + mBlock->iSizeX < iCameraPosX)
498 return 1;
499 else
500 return 0;
503 static int chopParticleOffscreen(struct CParticle *mParticle)
505 if (mParticle->iWorldX < iCameraPosX || mParticle->iWorldY < 0 ||
506 mParticle->iWorldY > iScreenY || mParticle->iWorldX > iCameraPosX +
507 iScreenX)
509 return 1;
511 else
512 return 0;
515 static void chopKillPlayer(void)
517 int i, button;
519 for (i = 0; i < NUMBER_OF_PARTICLES; i++) {
520 mParticles[i].bIsActive = 0;
521 chopAddParticle(iPlayerPosX + iR(0,20), iPlayerPosY + iR(0,20),
522 iR(-2,2), iR(-2,2));
525 iPlayerAlive--;
527 if (iPlayerAlive == 0) {
528 rb->lcd_set_drawmode(DRMODE_FG);
529 #if LCD_DEPTH >= 2
530 rb->lcd_set_foreground(LCD_LIGHTGRAY);
531 #endif
532 rb->splash(HZ, "Game Over");
534 if (score > highscore) {
535 char scoretext[30];
536 highscore = score;
537 rb->snprintf(scoretext, sizeof(scoretext), "New High Score: %d",
538 highscore);
539 rb->splash(HZ*2, scoretext);
542 rb->splash(HZ/4, "Press " ACTIONTEXT " to continue");
543 rb->lcd_update();
545 rb->lcd_set_drawmode(DRMODE_SOLID);
547 while (true) {
548 button = rb->button_get(true);
549 if (button == ACTION
550 #ifdef ACTION2
551 || button == ACTION2
552 #endif
554 while (true) {
555 button = rb->button_get(true);
556 if (button == (ACTION | BUTTON_REL)
557 #ifdef ACTION2
558 || button == (ACTION2 | BUTTON_REL)
559 #endif
561 chopper_load(true);
562 return;
568 } else
569 chopper_load(false);
573 static void chopDrawTheWorld(void)
575 int i=0;
577 while(i < NUMBER_OF_BLOCKS)
579 if(mBlocks[i].bIsActive)
581 if(chopBlockOffscreen(&mBlocks[i]) == 1)
582 mBlocks[i].bIsActive = 0;
583 else
584 chopDrawBlock(&mBlocks[i]);
587 i++;
590 i=0;
592 while(i < NUMBER_OF_PARTICLES)
594 if(mParticles[i].bIsActive)
596 if(chopParticleOffscreen(&mParticles[i]) == 1)
597 mParticles[i].bIsActive = 0;
598 else
599 chopDrawParticle(&mParticles[i]);
602 i++;
605 chopRenderTerrain(&mGround);
606 chopRenderTerrain(&mRoof);
610 static void chopDrawParticle(struct CParticle *mParticle)
613 int iPosX = (mParticle->iWorldX - iCameraPosX);
614 int iPosY = (mParticle->iWorldY);
615 #if LCD_DEPTH > 2
616 rb->lcd_set_foreground(LCD_RGBPACK(192,192,192));
617 #elif LCD_DEPTH == 2
618 rb->lcd_set_foreground(LCD_LIGHTGRAY);
619 #endif
620 rb->lcd_fillrect(SCALE(iPosX), SCALE(iPosY), SCALE(3), SCALE(3));
624 static void chopDrawScene(void)
626 char s[30];
627 int w;
628 #if LCD_DEPTH > 2
629 rb->lcd_set_background(LCD_BLACK);
630 #elif LCD_DEPTH == 2
631 rb->lcd_set_background(LCD_WHITE);
632 #endif
633 rb->lcd_clear_display();
635 chopDrawTheWorld();
636 chopDrawPlayer(iPlayerPosX - iCameraPosX, iPlayerPosY);
638 score = -20 + iPlayerPosX/3;
640 #if LCD_DEPTH == 1
641 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
642 #else
643 rb->lcd_set_drawmode(DRMODE_FG);
644 #endif
646 #if LCD_DEPTH > 2
647 rb->lcd_set_foreground(LCD_BLACK);
648 #elif LCD_DEPTH == 2
649 rb->lcd_set_foreground(LCD_WHITE);
650 #endif
652 #if LCD_WIDTH <= 128
653 rb->snprintf(s, sizeof(s), "Dist: %d", score);
654 #else
655 rb->snprintf(s, sizeof(s), "Distance: %d", score);
656 #endif
657 rb->lcd_getstringsize(s, &w, NULL);
658 rb->lcd_putsxy(2, 2, s);
659 if (score < highscore)
661 int w2;
662 #if LCD_WIDTH <= 128
663 rb->snprintf(s, sizeof(s), "Hi: %d", highscore);
664 #else
665 rb->snprintf(s, sizeof(s), "Best: %d", highscore);
666 #endif
667 rb->lcd_getstringsize(s, &w2, NULL);
668 if (LCD_WIDTH - 2 - w2 > w + 2)
669 rb->lcd_putsxy(LCD_WIDTH - 2 - w2, 2, s);
671 rb->lcd_set_drawmode(DRMODE_SOLID);
673 rb->lcd_update();
676 static int chopMenu(int menunum)
678 int result = (menunum==0)?0:1;
679 int res = 0;
680 bool menu_quit = false;
682 static const struct opt_items levels[2] = {
683 { "Normal", -1 },
684 { "Steep", -1 },
687 MENUITEM_STRINGLIST(menu,"Chopper Menu",NULL,"Start New Game","Resume Game",
688 "Level","Quit");
690 #ifdef HAVE_LCD_COLOR
691 rb->lcd_set_foreground(LCD_WHITE);
692 rb->lcd_set_background(LCD_BLACK);
693 #elif LCD_DEPTH == 2
694 rb->lcd_set_foreground(LCD_BLACK);
695 rb->lcd_set_background(LCD_WHITE);
696 #endif
698 rb->lcd_clear_display();
700 while (!menu_quit) {
701 switch(rb->do_menu(&menu, &result, NULL, false))
703 case 0: /* Start New Game */
704 menu_quit=true;
705 chopper_load(true);
706 res = -1;
707 break;
708 case 1: /* Resume Game */
709 if(menunum==1) {
710 menu_quit=true;
711 res = -1;
712 } else if(menunum==0){
713 rb->splash(HZ, "No game to resume");
715 break;
716 case 2:
717 rb->set_option("Level", &iLevelMode, INT, levels, 2, NULL);
718 break;
719 case 3:
720 menu_quit=true;
721 res = PLUGIN_OK;
722 break;
723 case MENU_ATTACHED_USB:
724 menu_quit=true;
725 res = PLUGIN_USB_CONNECTED;
726 break;
729 rb->lcd_clear_display();
730 return res;
733 static int chopGameLoop(void)
735 int move_button, ret;
736 bool exit=false;
737 int end, i=0, bdelay=0, last_button=BUTTON_NONE;
739 if (chopUpdateTerrainRecycling(&mGround) == 1)
740 /* mirror the sky if we've changed the ground */
741 chopCopyTerrain(&mGround, &mRoof, 0, - ( (iScreenY * 3) / 4));
743 ret = chopMenu(0);
744 if (ret != -1)
745 return PLUGIN_OK;
747 chopDrawScene();
749 while (!exit) {
751 end = *rb->current_tick + (CYCLETIME * HZ) / 1000;
753 if(chopUpdateTerrainRecycling(&mGround) == 1)
754 /* mirror the sky if we've changed the ground */
755 chopCopyTerrain(&mGround, &mRoof, 0, - ( (iScreenY * 3) / 4));
757 iRotorOffset = iR(-1,1);
759 /* We need to have this here so particles move when we're dead */
761 for (i=0; i < NUMBER_OF_PARTICLES; i++)
762 if(mParticles[i].bIsActive == 1)
764 mParticles[i].iWorldX += mParticles[i].iSpeedX;
765 mParticles[i].iWorldY += mParticles[i].iSpeedY;
768 /* Redraw the main window: */
769 chopDrawScene();
772 iGravityTimerCountdown--;
774 if(iGravityTimerCountdown <= 0)
776 iGravityTimerCountdown = 3;
777 chopAddParticle(iPlayerPosX, iPlayerPosY+5, 0, 0);
780 if(iLevelMode == LEVEL_MODE_NORMAL)
781 chopGenerateBlockIfNeeded();
784 move_button=rb->button_status();
785 if (rb->button_get(false) == QUIT) {
786 ret = chopMenu(1);
787 if (ret != -1)
788 return PLUGIN_OK;
789 bdelay = 0;
790 last_button = BUTTON_NONE;
791 move_button = BUTTON_NONE;
794 switch (move_button) {
795 case ACTION:
796 #ifdef ACTION2
797 case ACTION2:
798 #endif
799 if (last_button != ACTION
800 #ifdef ACTION2
801 && last_button != ACTION2
802 #endif
804 bdelay = -2;
805 if (bdelay == 0)
806 iPlayerSpeedY = -3;
807 break;
809 default:
810 if (last_button == ACTION
811 #ifdef ACTION2
812 || last_button == ACTION2
813 #endif
815 bdelay = 3;
816 if (bdelay == 0)
817 iPlayerSpeedY = 4;
819 if (rb->default_event_handler(move_button) == SYS_USB_CONNECTED)
820 return PLUGIN_USB_CONNECTED;
821 break;
823 last_button = move_button;
825 if (bdelay < 0) {
826 iPlayerSpeedY = bdelay;
827 bdelay++;
828 } else if (bdelay > 0) {
829 iPlayerSpeedY = bdelay;
830 bdelay--;
833 iCameraPosX = iPlayerPosX - 25;
834 iPlayerPosX += iPlayerSpeedX;
835 iPlayerPosY += iPlayerSpeedY;
837 chopCounter++;
838 /* increase speed as we go along */
839 if (chopCounter == 100){
840 iPlayerSpeedX++;
841 chopCounter=0;
844 if (iPlayerPosY > iScreenY-10 || iPlayerPosY < -5 ||
845 chopPointInTerrain(&mGround, iPlayerPosX, iPlayerPosY + 10, 0) ||
846 chopPointInTerrain(&mRoof, iPlayerPosX ,iPlayerPosY, 1))
848 chopKillPlayer();
849 chopDrawScene();
850 ret = chopMenu(0);
851 if (ret != -1)
852 return ret;
855 for (i=0; i < NUMBER_OF_BLOCKS; i++)
856 if(mBlocks[i].bIsActive == 1)
857 if(chopBlockCollideWithPlayer(&mBlocks[i])) {
858 chopKillPlayer();
859 chopDrawScene();
860 ret = chopMenu(0);
861 if (ret != -1)
862 return ret;
865 if (end > *rb->current_tick)
866 rb->sleep(end-*rb->current_tick);
867 else
868 rb->yield();
871 return PLUGIN_OK;
874 static void chopDrawBlock(struct CBlock *mBlock)
876 int iPosX = (mBlock->iWorldX - iCameraPosX);
877 int iPosY = (mBlock->iWorldY);
878 #if LCD_DEPTH > 2
879 rb->lcd_set_foreground(LCD_RGBPACK(100,255,100));
880 #elif LCD_DEPTH == 2
881 rb->lcd_set_foreground(LCD_BLACK);
882 #endif
883 rb->lcd_fillrect(SCALE(iPosX), SCALE(iPosY), SCALE(mBlock->iSizeX),
884 SCALE(mBlock->iSizeY));
888 static void chopRenderTerrain(struct CTerrain *ter)
891 int i=1;
893 int oldx=0;
895 int ay=0;
896 if(ter->mNodes[0].y < (LCD_HEIGHT*SIZE)/2)
897 ay=0;
898 else
899 ay=(LCD_HEIGHT*SIZE);
901 while(i < ter->iNodesCount && oldx < iScreenX)
904 int x = ter->mNodes[i-1].x - iCameraPosX;
905 int y = ter->mNodes[i-1].y;
907 int x2 = ter->mNodes[i].x - iCameraPosX;
908 int y2 = ter->mNodes[i].y;
909 #if LCD_DEPTH > 2
910 rb->lcd_set_foreground(LCD_RGBPACK(100,255,100));
911 #elif LCD_DEPTH == 2
912 rb->lcd_set_foreground(LCD_DARKGRAY);
913 #endif
915 rb->lcd_drawline(SCALE(x), SCALE(y), SCALE(x2), SCALE(y2));
917 xlcd_filltriangle(SCALE(x), SCALE(y), SCALE(x2), SCALE(y2),
918 SCALE(x2), SCALE(ay));
919 xlcd_filltriangle(SCALE(x), SCALE(ay), SCALE(x2), SCALE(y2),
920 SCALE(x2), SCALE(ay));
922 if (ay == 0)
923 xlcd_filltriangle(SCALE(x), SCALE(ay), SCALE(x), SCALE(y),
924 SCALE(x2), SCALE(y2 / 2));
925 else
926 xlcd_filltriangle(SCALE(x), SCALE(ay), SCALE(x), SCALE(y),
927 SCALE(x2), SCALE((LCD_HEIGHT*SIZE) -
928 ((LCD_HEIGHT*SIZE) - y2) / 2));
930 oldx = x;
931 i++;
937 void chopper_load(bool newgame)
940 int i;
941 int g;
943 if (newgame) {
944 iScreenX = LCD_WIDTH * SIZE;
945 iScreenY = LCD_HEIGHT * SIZE;
946 blockh = iScreenY / 5;
947 blockw = iScreenX / 20;
948 iPlayerAlive = 1;
949 score = 0;
951 iRotorOffset = 0;
952 iPlayerPosX = 60;
953 iPlayerPosY = (iScreenY * 4) / 10;
954 iLastBlockPlacedPosX = 0;
955 iGravityTimerCountdown = 2;
956 chopCounter = 0;
957 iPlayerSpeedX = 3;
958 iPlayerSpeedY = 0;
959 iCameraPosX = 30;
961 for (i=0; i < NUMBER_OF_PARTICLES; i++)
962 mParticles[i].bIsActive = 0;
964 for (i=0; i < NUMBER_OF_BLOCKS; i++)
965 mBlocks[i].bIsActive = 0;
967 g = iScreenY - 10;
968 chopClearTerrain(&mGround);
970 for (i=0; i < MAX_TERRAIN_NODES; i++)
971 chopAddTerrainNode(&mGround,i * 30,g - iR(0,20));
973 if (chopUpdateTerrainRecycling(&mGround) == 1)
974 /* mirror the sky if we've changed the ground */
975 chopCopyTerrain(&mGround, &mRoof, 0, - ( (iScreenY * 3) / 4));
977 iLevelMode = LEVEL_MODE_NORMAL;
978 if (iLevelMode == LEVEL_MODE_NORMAL)
979 /* make it a bit more exciting, cause it's easy terrain... */
980 iPlayerSpeedX *= 2;
983 /* this is the plugin entry point */
984 enum plugin_status plugin_start(const struct plugin_api* api, const void* parameter)
986 (void)parameter;
987 rb = api;
988 int ret;
990 rb->lcd_setfont(FONT_SYSFIXED);
991 #if LCD_DEPTH > 1
992 rb->lcd_set_backdrop(NULL);
993 #endif
994 #ifdef HAVE_LCD_COLOR
995 rb->lcd_set_background(LCD_BLACK);
996 rb->lcd_set_foreground(LCD_WHITE);
997 #endif
999 /* Turn off backlight timeout */
1000 backlight_force_on(rb); /* backlight control in lib/helper.c */
1002 rb->srand( *rb->current_tick );
1004 xlcd_init(rb);
1005 configfile_init(rb);
1006 configfile_load(CFG_FILE, config, 1, 0);
1008 chopper_load(true);
1009 ret = chopGameLoop();
1011 configfile_save(CFG_FILE, config, 1, 0);
1013 rb->lcd_setfont(FONT_UI);
1014 /* Turn on backlight timeout (revert to settings) */
1015 backlight_use_settings(rb); /* backlight control in lib/helper.c */
1017 return ret;