Fix some quotation marks. Thanks to Alexander Levin for pointing it out.
[Rockbox.git] / apps / plugins / chopper.c
blob71f62f44ccf77c2d9c60dc77f0274d40b96ea89e
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id: $
10 * Originally by Joshua Oreman, improved by Prashant Varanasi
11 * Ported to Rockbox by Ben Basha (Paprica)
13 * All files in this archive are subject to the GNU General Public License.
14 * See the file COPYING in the source tree root for full license agreement.
16 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
17 * KIND, either express or implied.
19 ****************************************************************************/
21 #include "plugin.h"
22 #include "xlcd.h"
23 #include "configfile.h"
24 #include "helper.h"
26 PLUGIN_HEADER
29 Still To do:
30 - Make original speed and further increases in speed depend more on screen size
31 - attempt to make the tunnels get narrower as the game goes on
32 - make the chopper look better, maybe a picture, and scale according
33 to screen size
34 - use textures for the color screens for background and terrain,
35 eg stars on background
36 - allow choice of different levels [later: different screen themes]
37 - better high score handling, improved screen etc.
40 #if (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD)
42 #define QUIT BUTTON_OFF
43 #define ACTION BUTTON_UP
44 #define ACTION2 BUTTON_SELECT
45 #define ACTIONTEXT "SELECT"
47 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
48 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
49 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
51 #define QUIT BUTTON_MENU
52 #define ACTION BUTTON_SELECT
53 #define ACTIONTEXT "SELECT"
55 #elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD /* grayscale at the moment */
57 #define QUIT BUTTON_POWER
58 #define ACTION BUTTON_UP
59 #define ACTION2 BUTTON_SELECT
60 #define ACTIONTEXT "SELECT"
62 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
63 #define QUIT BUTTON_POWER
64 #define ACTION BUTTON_RIGHT
65 #define ACTIONTEXT "RIGHT"
67 #elif (CONFIG_KEYPAD == SANSA_E200_PAD) || \
68 (CONFIG_KEYPAD == SANSA_C200_PAD)
69 #define QUIT BUTTON_POWER
70 #define ACTION BUTTON_SELECT
71 #define ACTIONTEXT "SELECT"
73 #elif CONFIG_KEYPAD == GIGABEAT_PAD
74 #define QUIT BUTTON_MENU
75 #define ACTION BUTTON_SELECT
76 #define ACTIONTEXT "SELECT"
78 #elif CONFIG_KEYPAD == RECORDER_PAD
79 #define QUIT BUTTON_OFF
80 #define ACTION BUTTON_PLAY
81 #define ACTIONTEXT "PLAY"
83 #elif CONFIG_KEYPAD == ONDIO_PAD
84 #define QUIT BUTTON_OFF
85 #define ACTION BUTTON_UP
86 #define ACTION2 BUTTON_MENU
87 #define ACTIONTEXT "UP"
89 #elif CONFIG_KEYPAD == GIGABEAT_S_PAD
90 #define QUIT BUTTON_BACK
91 #define ACTION BUTTON_SELECT
92 #define ACTION2 BUTTON_MENU
93 #define ACTIONTEXT "SELECT"
95 #elif CONFIG_KEYPAD == MROBE100_PAD
96 #define QUIT BUTTON_POWER
97 #define ACTION BUTTON_SELECT
98 #define ACTIONTEXT "SELECT"
100 #elif CONFIG_KEYPAD == IAUDIO_M3_PAD
101 #define QUIT BUTTON_RC_REC
102 #define ACTION BUTTON_RC_PLAY
103 #define ACTION2 BUTTON_RC_MODE
104 #define ACTIONTEXT "PLAY"
106 #elif CONFIG_KEYPAD == COWOND2_PAD
107 #define QUIT BUTTON_POWER
108 #define ACTION BUTTON_UP
109 #define ACTION2 BUTTON_MENU
110 #define ACTIONTEXT "UP"
112 #else
113 #error No keymap defined!
114 #endif
116 static struct plugin_api* rb;
118 #define NUMBER_OF_BLOCKS 8
119 #define NUMBER_OF_PARTICLES 3
120 #define MAX_TERRAIN_NODES 15
122 #define LEVEL_MODE_NORMAL 0
123 #define LEVEL_MODE_STEEP 1
125 #if LCD_WIDTH <= 112
126 #define CYCLETIME 100
127 #define SCALE(x) ((x)==1 ? (x) : ((x) >> 1))
128 #define SIZE 2
129 #else
130 #define CYCLETIME 60
131 #define SCALE(x) (x)
132 #define SIZE 1
133 #endif
135 /*Chopper's local variables to track the terrain position etc*/
136 static int chopCounter;
137 static int iRotorOffset;
138 static int iScreenX;
139 static int iScreenY;
140 static int iPlayerPosX;
141 static int iPlayerPosY;
142 static int iCameraPosX;
143 static int iPlayerSpeedX;
144 static int iPlayerSpeedY;
145 static int iLastBlockPlacedPosX;
146 static int iGravityTimerCountdown;
147 static int iPlayerAlive;
148 static int iLevelMode;
149 static int blockh,blockw;
150 static int highscore;
151 static int score;
153 #define CFG_FILE "chopper.cfg"
154 #define MAX_POINTS 50000
155 static struct configdata config[] =
157 {TYPE_INT, 0, MAX_POINTS, &highscore, "highscore", NULL, NULL}
160 struct CBlock
162 int iWorldX;
163 int iWorldY;
165 int iSizeX;
166 int iSizeY;
168 int bIsActive;
171 struct CParticle
173 int iWorldX;
174 int iWorldY;
176 int iSpeedX;
177 int iSpeedY;
179 int bIsActive;
182 struct CTerrainNode
184 int x;
185 int y;
188 struct CTerrain
190 struct CTerrainNode mNodes[MAX_TERRAIN_NODES];
191 int iNodesCount;
192 int iLastNodePlacedPosX;
195 struct CBlock mBlocks[NUMBER_OF_BLOCKS];
196 struct CParticle mParticles[NUMBER_OF_PARTICLES];
198 struct CTerrain mGround;
199 struct CTerrain mRoof;
201 /*Function declarations*/
202 static void chopDrawParticle(struct CParticle *mParticle);
203 static void chopDrawBlock(struct CBlock *mBlock);
204 static void chopRenderTerrain(struct CTerrain *ter);
205 void chopper_load(bool newgame);
206 void cleanup_chopper(void);
208 static void chopDrawPlayer(int x,int y) /* These are SCREEN coords, not world!*/
211 #if LCD_DEPTH > 2
212 rb->lcd_set_foreground(LCD_RGBPACK(50,50,200));
213 #elif LCD_DEPTH == 2
214 rb->lcd_set_foreground(LCD_DARKGRAY);
215 #endif
216 rb->lcd_fillrect(SCALE(x+6), SCALE(y+2), SCALE(12), SCALE(9));
217 rb->lcd_fillrect(SCALE(x-3), SCALE(y+6), SCALE(20), SCALE(3));
219 #if LCD_DEPTH > 2
220 rb->lcd_set_foreground(LCD_RGBPACK(50,50,50));
221 #elif LCD_DEPTH == 2
222 rb->lcd_set_foreground(LCD_DARKGRAY);
223 #endif
224 rb->lcd_fillrect(SCALE(x+10), SCALE(y), SCALE(2), SCALE(3));
225 rb->lcd_fillrect(SCALE(x+10), SCALE(y), SCALE(1), SCALE(3));
227 #if LCD_DEPTH > 2
228 rb->lcd_set_foreground(LCD_RGBPACK(40,40,100));
229 #elif LCD_DEPTH == 2
230 rb->lcd_set_foreground(LCD_BLACK);
231 #endif
232 rb->lcd_drawline(SCALE(x), SCALE(y+iRotorOffset), SCALE(x+20),
233 SCALE(y-iRotorOffset));
235 #if LCD_DEPTH > 2
236 rb->lcd_set_foreground(LCD_RGBPACK(20,20,50));
237 #elif LCD_DEPTH == 2
238 rb->lcd_set_foreground(LCD_BLACK);
239 #endif
240 rb->lcd_fillrect(SCALE(x - 2), SCALE(y + 5), SCALE(2), SCALE(5));
244 static void chopClearTerrain(struct CTerrain *ter)
246 ter->iNodesCount = 0;
250 int iR(int low,int high)
252 return low+rb->rand()%(high-low+1);
255 static void chopCopyTerrain(struct CTerrain *src, struct CTerrain *dest,
256 int xOffset,int yOffset)
258 int i=0;
260 while(i < src->iNodesCount)
262 dest->mNodes[i].x = src->mNodes[i].x + xOffset;
263 dest->mNodes[i].y = src->mNodes[i].y + yOffset;
265 i++;
268 dest->iNodesCount = src->iNodesCount;
269 dest->iLastNodePlacedPosX = src->iLastNodePlacedPosX;
273 static void chopAddTerrainNode(struct CTerrain *ter, int x, int y)
275 int i=0;
277 if(ter->iNodesCount + 1 >= MAX_TERRAIN_NODES)
279 /* DEBUGF("ERROR: Not enough nodes!\n"); */
280 return;
283 ter->iNodesCount++;
285 i = ter->iNodesCount - 1;
287 ter->mNodes[i].x = x;
288 ter->mNodes[i].y= y;
290 ter->iLastNodePlacedPosX = x;
294 static void chopTerrainNodeDeleteAndShift(struct CTerrain *ter,int nodeIndex)
296 int i=nodeIndex;
298 while( i < ter->iNodesCount )
300 ter->mNodes[i - 1] = ter->mNodes[i];
301 i++;
304 ter->iNodesCount--;
309 int chopUpdateTerrainRecycling(struct CTerrain *ter)
311 int i=1;
312 int ret = 0;
313 int iNewNodePos,g,v;
314 while(i < ter->iNodesCount)
317 if( iCameraPosX > ter->mNodes[i].x)
320 chopTerrainNodeDeleteAndShift(ter,i);
322 iNewNodePos = ter->iLastNodePlacedPosX + 50;
323 g = iScreenY - 10;
325 v = 3*iPlayerSpeedX;
326 if(v>50)
327 v=50;
328 if(iLevelMode == LEVEL_MODE_STEEP)
329 v*=5;
331 chopAddTerrainNode(ter,iNewNodePos,g - iR(-v,v));
332 ret=1;
336 i++;
340 return 1;
343 int chopTerrainHeightAtPoint(struct CTerrain *ter, int pX)
346 int iNodeIndexOne=0,iNodeIndexTwo=0, h, terY1, terY2, terX1, terX2, a, b;
347 float c,d;
349 int i=0;
350 for(i=1;i<MAX_TERRAIN_NODES;i++)
352 if(ter->mNodes[i].x > pX)
354 iNodeIndexOne = i - 1;
355 break;
360 iNodeIndexTwo = iNodeIndexOne + 1;
361 terY1 = ter->mNodes[iNodeIndexOne].y;
362 terY2 = ter->mNodes[iNodeIndexTwo].y;
364 terX1 = 0;
365 terX2 = ter->mNodes[iNodeIndexTwo].x - ter->mNodes[iNodeIndexOne].x;
367 pX-= ter->mNodes[iNodeIndexOne].x;
369 a = terY2 - terY1;
370 b = terX2;
371 c = pX;
372 d = (c/b) * a;
374 h = d + terY1;
376 return h;
380 int chopPointInTerrain(struct CTerrain *ter, int pX, int pY, int iTestType)
382 int h = chopTerrainHeightAtPoint(ter, pX);
384 if(iTestType == 0)
385 return (pY > h);
386 else
387 return (pY < h);
390 static void chopAddBlock(int x,int y,int sx,int sy, int indexOverride)
392 int i=0;
394 if(indexOverride < 0)
396 while(mBlocks[i].bIsActive && i < NUMBER_OF_BLOCKS)
397 i++;
398 if(i==NUMBER_OF_BLOCKS)
400 DEBUGF("No blocks!\n");
401 return;
404 else
405 i = indexOverride;
407 mBlocks[i].bIsActive = 1;
408 mBlocks[i].iWorldX = x;
409 mBlocks[i].iWorldY = y;
410 mBlocks[i].iSizeX = sx;
411 mBlocks[i].iSizeY = sy;
413 iLastBlockPlacedPosX = x;
416 static void chopAddParticle(int x,int y,int sx,int sy)
418 int i=0;
420 while(mParticles[i].bIsActive && i < NUMBER_OF_PARTICLES)
421 i++;
423 if(i==NUMBER_OF_PARTICLES)
424 return;
426 mParticles[i].bIsActive = 1;
427 mParticles[i].iWorldX = x;
428 mParticles[i].iWorldY = y;
429 mParticles[i].iSpeedX = sx;
430 mParticles[i].iSpeedY = sy;
433 static void chopGenerateBlockIfNeeded(void)
435 int i=0;
436 int DistSpeedX = iPlayerSpeedX * 5;
437 if(DistSpeedX<200) DistSpeedX = 200;
439 while(i < NUMBER_OF_BLOCKS)
441 if(!mBlocks[i].bIsActive)
443 int iX,iY,sX,sY;
445 iX = iLastBlockPlacedPosX + (350-DistSpeedX);
446 sX = blockw;
448 iY = iR(0,iScreenY);
449 sY = blockh + iR(1,blockh/3);
451 chopAddBlock(iX,iY,sX,sY,i);
454 i++;
459 static int chopBlockCollideWithPlayer(struct CBlock *mBlock)
461 int px = iPlayerPosX;
462 int py = iPlayerPosY;
464 int x = mBlock->iWorldX-17;
465 int y = mBlock->iWorldY-11;
467 int x2 = x + mBlock->iSizeX+17;
468 int y2 = y + mBlock->iSizeY+11;
470 if(px>x && px<x2)
472 if(py>y && py<y2)
474 return 1;
478 return 0;
481 static int chopBlockOffscreen(struct CBlock *mBlock)
483 if(mBlock->iWorldX + mBlock->iSizeX < iCameraPosX)
484 return 1;
485 else
486 return 0;
489 static int chopParticleOffscreen(struct CParticle *mParticle)
491 if (mParticle->iWorldX < iCameraPosX || mParticle->iWorldY < 0 ||
492 mParticle->iWorldY > iScreenY || mParticle->iWorldX > iCameraPosX +
493 iScreenX)
495 return 1;
497 else
498 return 0;
501 static void chopKillPlayer(void)
503 int i, button;
505 for (i = 0; i < NUMBER_OF_PARTICLES; i++) {
506 mParticles[i].bIsActive = 0;
507 chopAddParticle(iPlayerPosX + iR(0,20), iPlayerPosY + iR(0,20),
508 iR(-2,2), iR(-2,2));
511 iPlayerAlive--;
513 if (iPlayerAlive == 0) {
514 rb->lcd_set_drawmode(DRMODE_FG);
515 #if LCD_DEPTH >= 2
516 rb->lcd_set_foreground(LCD_LIGHTGRAY);
517 #endif
518 rb->splash(HZ, "Game Over");
520 if (score > highscore) {
521 char scoretext[30];
522 highscore = score;
523 rb->snprintf(scoretext, sizeof(scoretext), "New High Score: %d",
524 highscore);
525 rb->splash(HZ*2, scoretext);
528 rb->splash(HZ/4, "Press " ACTIONTEXT " to continue");
529 rb->lcd_update();
531 rb->lcd_set_drawmode(DRMODE_SOLID);
533 while (true) {
534 button = rb->button_get(true);
535 if (button == ACTION
536 #ifdef ACTION2
537 || button == ACTION2
538 #endif
540 while (true) {
541 button = rb->button_get(true);
542 if (button == (ACTION | BUTTON_REL)
543 #ifdef ACTION2
544 || button == (ACTION2 | BUTTON_REL)
545 #endif
547 chopper_load(true);
548 return;
554 } else
555 chopper_load(false);
559 static void chopDrawTheWorld(void)
561 int i=0;
563 while(i < NUMBER_OF_BLOCKS)
565 if(mBlocks[i].bIsActive)
567 if(chopBlockOffscreen(&mBlocks[i]) == 1)
568 mBlocks[i].bIsActive = 0;
569 else
570 chopDrawBlock(&mBlocks[i]);
573 i++;
576 i=0;
578 while(i < NUMBER_OF_PARTICLES)
580 if(mParticles[i].bIsActive)
582 if(chopParticleOffscreen(&mParticles[i]) == 1)
583 mParticles[i].bIsActive = 0;
584 else
585 chopDrawParticle(&mParticles[i]);
588 i++;
591 chopRenderTerrain(&mGround);
592 chopRenderTerrain(&mRoof);
596 static void chopDrawParticle(struct CParticle *mParticle)
599 int iPosX = (mParticle->iWorldX - iCameraPosX);
600 int iPosY = (mParticle->iWorldY);
601 #if LCD_DEPTH > 2
602 rb->lcd_set_foreground(LCD_RGBPACK(192,192,192));
603 #elif LCD_DEPTH == 2
604 rb->lcd_set_foreground(LCD_LIGHTGRAY);
605 #endif
606 rb->lcd_fillrect(SCALE(iPosX), SCALE(iPosY), SCALE(3), SCALE(3));
610 static void chopDrawScene(void)
612 char s[30];
613 int w;
614 #if LCD_DEPTH > 2
615 rb->lcd_set_background(LCD_BLACK);
616 #elif LCD_DEPTH == 2
617 rb->lcd_set_background(LCD_WHITE);
618 #endif
619 rb->lcd_clear_display();
621 chopDrawTheWorld();
622 chopDrawPlayer(iPlayerPosX - iCameraPosX, iPlayerPosY);
624 score = -20 + iPlayerPosX/3;
626 #if LCD_DEPTH == 1
627 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
628 #else
629 rb->lcd_set_drawmode(DRMODE_FG);
630 #endif
632 #if LCD_DEPTH > 2
633 rb->lcd_set_foreground(LCD_BLACK);
634 #elif LCD_DEPTH == 2
635 rb->lcd_set_foreground(LCD_WHITE);
636 #endif
638 #if LCD_WIDTH <= 128
639 rb->snprintf(s, sizeof(s), "Dist: %d", score);
640 #else
641 rb->snprintf(s, sizeof(s), "Distance: %d", score);
642 #endif
643 rb->lcd_getstringsize(s, &w, NULL);
644 rb->lcd_putsxy(2, 2, s);
645 if (score < highscore)
647 int w2;
648 #if LCD_WIDTH <= 128
649 rb->snprintf(s, sizeof(s), "Hi: %d", highscore);
650 #else
651 rb->snprintf(s, sizeof(s), "Best: %d", highscore);
652 #endif
653 rb->lcd_getstringsize(s, &w2, NULL);
654 if (LCD_WIDTH - 2 - w2 > w + 2)
655 rb->lcd_putsxy(LCD_WIDTH - 2 - w2, 2, s);
657 rb->lcd_set_drawmode(DRMODE_SOLID);
659 rb->lcd_update();
662 static int chopMenu(int menunum)
664 int result = (menunum==0)?0:1;
665 int res = 0;
666 bool menu_quit = false;
668 static const struct opt_items levels[2] = {
669 { "Normal", -1 },
670 { "Steep", -1 },
673 MENUITEM_STRINGLIST(menu,"Chopper Menu",NULL,"Start New Game","Resume Game",
674 "Level","Quit");
676 #ifdef HAVE_LCD_COLOR
677 rb->lcd_set_foreground(LCD_WHITE);
678 rb->lcd_set_background(LCD_BLACK);
679 #elif LCD_DEPTH == 2
680 rb->lcd_set_foreground(LCD_BLACK);
681 rb->lcd_set_background(LCD_WHITE);
682 #endif
684 rb->lcd_clear_display();
686 while (!menu_quit) {
687 switch(rb->do_menu(&menu, &result, NULL, false))
689 case 0: /* Start New Game */
690 menu_quit=true;
691 chopper_load(true);
692 res = -1;
693 break;
694 case 1: /* Resume Game */
695 if(menunum==1) {
696 menu_quit=true;
697 res = -1;
698 } else if(menunum==0){
699 rb->splash(HZ, "No game to resume");
701 break;
702 case 2:
703 rb->set_option("Level", &iLevelMode, INT, levels, 2, NULL);
704 break;
705 case 3:
706 menu_quit=true;
707 res = PLUGIN_OK;
708 break;
709 case MENU_ATTACHED_USB:
710 menu_quit=true;
711 res = PLUGIN_USB_CONNECTED;
712 break;
715 rb->lcd_clear_display();
716 return res;
719 static int chopGameLoop(void)
721 int move_button, ret;
722 bool exit=false;
723 int end, i=0, bdelay=0, last_button=BUTTON_NONE;
725 if (chopUpdateTerrainRecycling(&mGround) == 1)
726 /* mirror the sky if we've changed the ground */
727 chopCopyTerrain(&mGround, &mRoof, 0, - ( (iScreenY * 3) / 4));
729 ret = chopMenu(0);
730 if (ret != -1)
731 return PLUGIN_OK;
733 chopDrawScene();
735 while (!exit) {
737 end = *rb->current_tick + (CYCLETIME * HZ) / 1000;
739 if(chopUpdateTerrainRecycling(&mGround) == 1)
740 /* mirror the sky if we've changed the ground */
741 chopCopyTerrain(&mGround, &mRoof, 0, - ( (iScreenY * 3) / 4));
743 iRotorOffset = iR(-1,1);
745 /* We need to have this here so particles move when we're dead */
747 for (i=0; i < NUMBER_OF_PARTICLES; i++)
748 if(mParticles[i].bIsActive == 1)
750 mParticles[i].iWorldX += mParticles[i].iSpeedX;
751 mParticles[i].iWorldY += mParticles[i].iSpeedY;
754 /* Redraw the main window: */
755 chopDrawScene();
758 iGravityTimerCountdown--;
760 if(iGravityTimerCountdown <= 0)
762 iGravityTimerCountdown = 3;
763 chopAddParticle(iPlayerPosX, iPlayerPosY+5, 0, 0);
766 if(iLevelMode == LEVEL_MODE_NORMAL)
767 chopGenerateBlockIfNeeded();
770 move_button=rb->button_status();
771 if (rb->button_get(false) == QUIT) {
772 ret = chopMenu(1);
773 if (ret != -1)
774 return PLUGIN_OK;
775 bdelay = 0;
776 last_button = BUTTON_NONE;
777 move_button = BUTTON_NONE;
780 switch (move_button) {
781 case ACTION:
782 #ifdef ACTION2
783 case ACTION2:
784 #endif
785 if (last_button != ACTION
786 #ifdef ACTION2
787 && last_button != ACTION2
788 #endif
790 bdelay = -2;
791 if (bdelay == 0)
792 iPlayerSpeedY = -3;
793 break;
795 default:
796 if (last_button == ACTION
797 #ifdef ACTION2
798 || last_button == ACTION2
799 #endif
801 bdelay = 3;
802 if (bdelay == 0)
803 iPlayerSpeedY = 4;
805 if (rb->default_event_handler(move_button) == SYS_USB_CONNECTED)
806 return PLUGIN_USB_CONNECTED;
807 break;
809 last_button = move_button;
811 if (bdelay < 0) {
812 iPlayerSpeedY = bdelay;
813 bdelay++;
814 } else if (bdelay > 0) {
815 iPlayerSpeedY = bdelay;
816 bdelay--;
819 iCameraPosX = iPlayerPosX - 25;
820 iPlayerPosX += iPlayerSpeedX;
821 iPlayerPosY += iPlayerSpeedY;
823 chopCounter++;
824 /* increase speed as we go along */
825 if (chopCounter == 100){
826 iPlayerSpeedX++;
827 chopCounter=0;
830 if (iPlayerPosY > iScreenY-10 || iPlayerPosY < -5 ||
831 chopPointInTerrain(&mGround, iPlayerPosX, iPlayerPosY + 10, 0) ||
832 chopPointInTerrain(&mRoof, iPlayerPosX ,iPlayerPosY, 1))
834 chopKillPlayer();
835 chopDrawScene();
836 ret = chopMenu(0);
837 if (ret != -1)
838 return ret;
841 for (i=0; i < NUMBER_OF_BLOCKS; i++)
842 if(mBlocks[i].bIsActive == 1)
843 if(chopBlockCollideWithPlayer(&mBlocks[i])) {
844 chopKillPlayer();
845 chopDrawScene();
846 ret = chopMenu(0);
847 if (ret != -1)
848 return ret;
851 if (end > *rb->current_tick)
852 rb->sleep(end-*rb->current_tick);
853 else
854 rb->yield();
857 return PLUGIN_OK;
860 static void chopDrawBlock(struct CBlock *mBlock)
862 int iPosX = (mBlock->iWorldX - iCameraPosX);
863 int iPosY = (mBlock->iWorldY);
864 #if LCD_DEPTH > 2
865 rb->lcd_set_foreground(LCD_RGBPACK(100,255,100));
866 #elif LCD_DEPTH == 2
867 rb->lcd_set_foreground(LCD_BLACK);
868 #endif
869 rb->lcd_fillrect(SCALE(iPosX), SCALE(iPosY), SCALE(mBlock->iSizeX),
870 SCALE(mBlock->iSizeY));
874 static void chopRenderTerrain(struct CTerrain *ter)
877 int i=1;
879 int oldx=0;
881 int ay=0;
882 if(ter->mNodes[0].y < (LCD_HEIGHT*SIZE)/2)
883 ay=0;
884 else
885 ay=(LCD_HEIGHT*SIZE);
887 while(i < ter->iNodesCount && oldx < iScreenX)
890 int x = ter->mNodes[i-1].x - iCameraPosX;
891 int y = ter->mNodes[i-1].y;
893 int x2 = ter->mNodes[i].x - iCameraPosX;
894 int y2 = ter->mNodes[i].y;
895 #if LCD_DEPTH > 2
896 rb->lcd_set_foreground(LCD_RGBPACK(100,255,100));
897 #elif LCD_DEPTH == 2
898 rb->lcd_set_foreground(LCD_DARKGRAY);
899 #endif
901 rb->lcd_drawline(SCALE(x), SCALE(y), SCALE(x2), SCALE(y2));
903 xlcd_filltriangle(SCALE(x), SCALE(y), SCALE(x2), SCALE(y2),
904 SCALE(x2), SCALE(ay));
905 xlcd_filltriangle(SCALE(x), SCALE(ay), SCALE(x2), SCALE(y2),
906 SCALE(x2), SCALE(ay));
908 if (ay == 0)
909 xlcd_filltriangle(SCALE(x), SCALE(ay), SCALE(x), SCALE(y),
910 SCALE(x2), SCALE(y2 / 2));
911 else
912 xlcd_filltriangle(SCALE(x), SCALE(ay), SCALE(x), SCALE(y),
913 SCALE(x2), SCALE((LCD_HEIGHT*SIZE) -
914 ((LCD_HEIGHT*SIZE) - y2) / 2));
916 oldx = x;
917 i++;
923 void chopper_load(bool newgame)
926 int i;
927 int g;
929 if (newgame) {
930 iScreenX = LCD_WIDTH * SIZE;
931 iScreenY = LCD_HEIGHT * SIZE;
932 blockh = iScreenY / 5;
933 blockw = iScreenX / 20;
934 iPlayerAlive = 1;
935 score = 0;
937 iRotorOffset = 0;
938 iPlayerPosX = 60;
939 iPlayerPosY = (iScreenY * 4) / 10;
940 iLastBlockPlacedPosX = 0;
941 iGravityTimerCountdown = 2;
942 chopCounter = 0;
943 iPlayerSpeedX = 3;
944 iPlayerSpeedY = 0;
945 iCameraPosX = 30;
947 for (i=0; i < NUMBER_OF_PARTICLES; i++)
948 mParticles[i].bIsActive = 0;
950 for (i=0; i < NUMBER_OF_BLOCKS; i++)
951 mBlocks[i].bIsActive = 0;
953 g = iScreenY - 10;
954 chopClearTerrain(&mGround);
956 for (i=0; i < MAX_TERRAIN_NODES; i++)
957 chopAddTerrainNode(&mGround,i * 30,g - iR(0,20));
959 if (chopUpdateTerrainRecycling(&mGround) == 1)
960 /* mirror the sky if we've changed the ground */
961 chopCopyTerrain(&mGround, &mRoof, 0, - ( (iScreenY * 3) / 4));
963 iLevelMode = LEVEL_MODE_NORMAL;
964 if (iLevelMode == LEVEL_MODE_NORMAL)
965 /* make it a bit more exciting, cause it's easy terrain... */
966 iPlayerSpeedX *= 2;
969 /* this is the plugin entry point */
970 enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
972 (void)parameter;
973 rb = api;
974 int ret;
976 rb->lcd_setfont(FONT_SYSFIXED);
977 #if LCD_DEPTH > 1
978 rb->lcd_set_backdrop(NULL);
979 #endif
980 #ifdef HAVE_LCD_COLOR
981 rb->lcd_set_background(LCD_BLACK);
982 rb->lcd_set_foreground(LCD_WHITE);
983 #endif
985 /* Turn off backlight timeout */
986 backlight_force_on(rb); /* backlight control in lib/helper.c */
988 rb->srand( *rb->current_tick );
990 xlcd_init(rb);
991 configfile_init(rb);
992 configfile_load(CFG_FILE, config, 1, 0);
994 chopper_load(true);
995 ret = chopGameLoop();
997 configfile_save(CFG_FILE, config, 1, 0);
999 rb->lcd_setfont(FONT_UI);
1000 /* Turn on backlight timeout (revert to settings) */
1001 backlight_use_settings(rb); /* backlight control in lib/helper.c */
1003 return ret;