A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 1123 lines 27 kB view raw
1/*************************************************************************** 2 * __________ __ ___. 3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___ 4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / 5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < 6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ 7 * \/ \/ \/ \/ \/ 8 * $Id$ 9 * 10 * Originally by Joshua Oreman, improved by Prashant Varanasi 11 * Ported to Rockbox by Ben Basha (Paprica) 12 * 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. 17 * 18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 19 * KIND, either express or implied. 20 * 21 ****************************************************************************/ 22 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" 28 29 30 31/* 32Still 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. 41*/ 42 43#if (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD) 44 45#define QUIT BUTTON_OFF 46#define ACTION BUTTON_UP 47#define ACTION2 BUTTON_SELECT 48#define ACTIONTEXT "SELECT" 49 50#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \ 51 (CONFIG_KEYPAD == IPOD_3G_PAD) || \ 52 (CONFIG_KEYPAD == IPOD_1G2G_PAD) 53 54#define QUIT BUTTON_MENU 55#define ACTION BUTTON_SELECT 56#define ACTIONTEXT "SELECT" 57 58#elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD /* grayscale at the moment */ 59 60#define QUIT BUTTON_POWER 61#define ACTION BUTTON_UP 62#define ACTION2 BUTTON_SELECT 63#define ACTIONTEXT "SELECT" 64 65#elif CONFIG_KEYPAD == IRIVER_H10_PAD 66#define QUIT BUTTON_POWER 67#define ACTION BUTTON_RIGHT 68#define ACTIONTEXT "RIGHT" 69 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" 78 79#elif (CONFIG_KEYPAD == SANSA_FUZE_PAD) 80#define QUIT BUTTON_HOME 81#define ACTION BUTTON_SELECT 82#define ACTIONTEXT "SELECT" 83 84#elif CONFIG_KEYPAD == GIGABEAT_PAD 85#define QUIT BUTTON_MENU 86#define ACTION BUTTON_SELECT 87#define ACTIONTEXT "SELECT" 88 89#elif CONFIG_KEYPAD == GIGABEAT_S_PAD \ 90 || CONFIG_KEYPAD == SAMSUNG_YPR0_PAD 91#define QUIT BUTTON_BACK 92#define ACTION BUTTON_SELECT 93#define ACTION2 BUTTON_MENU 94#define ACTIONTEXT "SELECT" 95 96#elif CONFIG_KEYPAD == MROBE100_PAD 97#define QUIT BUTTON_POWER 98#define ACTION BUTTON_SELECT 99#define ACTIONTEXT "SELECT" 100 101#elif CONFIG_KEYPAD == IAUDIO_M3_PAD 102#define QUIT BUTTON_RC_REC 103#define ACTION BUTTON_RC_PLAY 104#define ACTION2 BUTTON_RC_MODE 105#define ACTIONTEXT "PLAY" 106 107#elif CONFIG_KEYPAD == COWON_D2_PAD 108#define QUIT BUTTON_POWER 109#define ACTION2 BUTTON_PLUS 110 111#elif CONFIG_KEYPAD == CREATIVEZVM_PAD 112#define QUIT BUTTON_BACK 113#define ACTION BUTTON_UP 114#define ACTION2 BUTTON_MENU 115#define ACTIONTEXT "UP" 116 117#elif CONFIG_KEYPAD == CREATIVE_ZENXFI3_PAD 118#define QUIT BUTTON_POWER 119#define ACTION BUTTON_UP 120#define ACTION2 BUTTON_MENU 121#define ACTIONTEXT "UP" 122 123#elif CONFIG_KEYPAD == PHILIPS_HDD1630_PAD 124#define QUIT BUTTON_POWER 125#define ACTION BUTTON_MENU 126#define ACTION2 BUTTON_SELECT 127#define ACTIONTEXT "MENU" 128 129#elif CONFIG_KEYPAD == PHILIPS_HDD6330_PAD 130#define QUIT BUTTON_POWER 131#define ACTION BUTTON_MENU 132#define ACTION2 BUTTON_PLAY 133#define ACTIONTEXT "MENU" 134 135#elif CONFIG_KEYPAD == PHILIPS_SA9200_PAD 136#define QUIT BUTTON_POWER 137#define ACTION BUTTON_MENU 138#define ACTION2 BUTTON_PLAY 139#define ACTIONTEXT "MENU" 140 141#elif CONFIG_KEYPAD == ONDAVX747_PAD || \ 142CONFIG_KEYPAD == ONDAVX777_PAD || \ 143CONFIG_KEYPAD == MROBE500_PAD 144#define QUIT BUTTON_POWER 145 146#elif (CONFIG_KEYPAD == SAMSUNG_YH820_PAD) || \ 147 (CONFIG_KEYPAD == SAMSUNG_YH92X_PAD) 148#define QUIT BUTTON_LEFT 149#define ACTION BUTTON_RIGHT 150#define ACTION2 BUTTON_UP 151#define ACTIONTEXT "RIGHT or UP" 152 153#elif CONFIG_KEYPAD == PBELL_VIBE500_PAD 154#define QUIT BUTTON_REC 155#define ACTION BUTTON_PLAY 156#define ACTION2 BUTTON_UP 157#define ACTIONTEXT "PLAY" 158 159#elif CONFIG_KEYPAD == MPIO_HD200_PAD 160#define QUIT (BUTTON_REC|BUTTON_PLAY) 161#define ACTION BUTTON_FUNC 162#define ACTIONTEXT "FUNC" 163 164#elif CONFIG_KEYPAD == MPIO_HD300_PAD 165#define QUIT (BUTTON_MENU|BUTTON_REPEAT) 166#define ACTION BUTTON_ENTER 167#define ACTIONTEXT "ENTER" 168 169#elif CONFIG_KEYPAD == SANSA_FUZEPLUS_PAD 170#define QUIT BUTTON_POWER 171#define ACTION BUTTON_SELECT 172#define ACTIONTEXT "SELECT" 173 174#elif (CONFIG_KEYPAD == HM60X_PAD) || \ 175 (CONFIG_KEYPAD == HM801_PAD) 176#define QUIT BUTTON_POWER 177#define ACTION BUTTON_SELECT 178#define ACTIONTEXT "SELECT" 179 180#elif CONFIG_KEYPAD == SONY_NWZ_PAD 181#define QUIT BUTTON_BACK 182#define ACTION BUTTON_PLAY 183#define ACTIONTEXT "PLAY" 184 185#elif CONFIG_KEYPAD == CREATIVE_ZEN_PAD 186#define QUIT BUTTON_BACK 187#define ACTION BUTTON_SELECT 188#define ACTIONTEXT "Select" 189 190#elif CONFIG_KEYPAD == AGPTEK_ROCKER_PAD 191#define QUIT BUTTON_POWER 192#define ACTION BUTTON_SELECT 193#define ACTIONTEXT "Select" 194 195#elif CONFIG_KEYPAD == XDUOO_X3_PAD || CONFIG_KEYPAD == XDUOO_X3II_PAD || CONFIG_KEYPAD == XDUOO_X20_PAD || CONFIG_KEYPAD == FIIO_M3K_LINUX_PAD || CONFIG_KEYPAD == IHIFI_770_PAD || CONFIG_KEYPAD == IHIFI_800_PAD || CONFIG_KEYPAD == EROSQ_PAD 196#define QUIT BUTTON_POWER 197#define ACTION BUTTON_PLAY 198#define ACTIONTEXT "PLAY" 199 200#elif CONFIG_KEYPAD == FIIO_M3K_PAD 201#define QUIT BUTTON_POWER 202#define ACTION BUTTON_PLAY 203#define ACTIONTEXT "PLAY" 204 205#elif CONFIG_KEYPAD == SDL_PAD 206#define QUIT BUTTON_TOPLEFT 207#define ACTION BUTTON_TOPMIDDLE 208#define ACTION2 BUTTON_BOTTOMMIDDLE 209#define ACTIONTEXT "SELECT" 210#elif CONFIG_KEYPAD == MA_PAD 211#define QUIT BUTTON_BACK 212#define ACTION BUTTON_PLAY 213#define ACTIONTEXT "PLAY" 214 215#elif CONFIG_KEYPAD == RG_NANO_PAD 216#define QUIT BUTTON_START 217#define ACTION BUTTON_A 218#define ACTION2 BUTTON_B 219#define ACTIONTEXT "A" 220 221#elif !defined(HAVE_TOUCHSCREEN) 222#error No keymap defined! 223#endif 224 225#ifdef HAVE_TOUCHSCREEN 226#ifndef QUIT 227#define QUIT BUTTON_TOPLEFT 228#endif 229#ifndef ACTION 230#define ACTION BUTTON_BOTTOMLEFT 231#endif 232#ifndef ACTION2 233#define ACTION2 BUTTON_BOTTOMRIGHT 234#endif 235#ifndef ACTIONTEXT 236#define ACTIONTEXT "BOTTOMRIGHT" 237#endif 238#endif 239 240#define NUMBER_OF_BLOCKS 8 241#define NUMBER_OF_PARTICLES 3 242#define MAX_TERRAIN_NODES 15 243 244#define LEVEL_MODE_NORMAL 0 245#define LEVEL_MODE_STEEP 1 246 247#if LCD_HEIGHT <= 64 248#define CYCLES 100 249static inline int SCALE(int x) 250{ 251 return x == 1 ? x : x >> 1; 252} 253#define SIZE 2 254#else 255#define CYCLES 60 256#define SCALE(x) (x) 257#define SIZE 1 258#endif 259 260/* in 10 milisecond (ticks) */ 261#define CYCLETIME ((CYCLES*HZ)/1000) 262 263/*Chopper's local variables to track the terrain position etc*/ 264static int chopCounter; 265static int iRotorOffset; 266static int iScreenX; 267static int iScreenY; 268static int iPlayerPosX; 269static int iPlayerPosY; 270static int iCameraPosX; 271static int iPlayerSpeedX; 272static int iPlayerSpeedY; 273static int iLastBlockPlacedPosX; 274static int iGravityTimerCountdown; 275static int iPlayerAlive; 276static int iLevelMode, iCurrLevelMode; 277static int blockh,blockw; 278static int highscore; 279static int score; 280 281#define CFG_FILE "chopper.cfg" 282#define MAX_POINTS 50000 283static struct configdata config[] = 284{ 285 {TYPE_INT, 0, MAX_POINTS, { .int_p = &highscore }, "highscore", NULL} 286}; 287 288struct CBlock 289{ 290 int iWorldX; 291 int iWorldY; 292 293 int iSizeX; 294 int iSizeY; 295 296 int bIsActive; 297}; 298 299struct CParticle 300{ 301 int iWorldX; 302 int iWorldY; 303 304 int iSpeedX; 305 int iSpeedY; 306 307 int bIsActive; 308}; 309 310struct CTerrainNode 311{ 312 int x; 313 int y; 314}; 315 316struct CTerrain 317{ 318 struct CTerrainNode mNodes[MAX_TERRAIN_NODES]; 319 int iNodesCount; 320 int iLastNodePlacedPosX; 321}; 322 323struct CBlock mBlocks[NUMBER_OF_BLOCKS]; 324struct CParticle mParticles[NUMBER_OF_PARTICLES]; 325 326struct CTerrain mGround; 327struct CTerrain mRoof; 328 329/*Function declarations*/ 330static void chopDrawParticle(struct CParticle *mParticle); 331static void chopDrawBlock(struct CBlock *mBlock); 332static void chopRenderTerrain(struct CTerrain *ter, bool isground); 333static void chopper_load(bool newgame); 334 335static void chopDrawPlayer(int x,int y) /* These are SCREEN coords, not world!*/ 336{ 337 338#if LCD_DEPTH > 2 339 rb->lcd_set_foreground(LCD_RGBPACK(50,50,200)); 340#elif LCD_DEPTH == 2 341 rb->lcd_set_foreground(LCD_DARKGRAY); 342#endif 343 rb->lcd_fillrect(SCALE(x+6), SCALE(y+2), SCALE(12), SCALE(9)); 344 rb->lcd_fillrect(SCALE(x-3), SCALE(y+6), SCALE(20), SCALE(3)); 345 346#if LCD_DEPTH > 2 347 rb->lcd_set_foreground(LCD_RGBPACK(50,50,50)); 348#elif LCD_DEPTH == 2 349 rb->lcd_set_foreground(LCD_DARKGRAY); 350#endif 351 rb->lcd_fillrect(SCALE(x+10), SCALE(y), SCALE(2), SCALE(3)); 352 rb->lcd_fillrect(SCALE(x+10), SCALE(y), SCALE(1), SCALE(3)); 353 354#if LCD_DEPTH > 2 355 rb->lcd_set_foreground(LCD_RGBPACK(40,40,100)); 356#elif LCD_DEPTH == 2 357 rb->lcd_set_foreground(LCD_BLACK); 358#endif 359 rb->lcd_drawline(SCALE(x), SCALE(y+iRotorOffset), SCALE(x+20), 360 SCALE(y-iRotorOffset)); 361 362#if LCD_DEPTH > 2 363 rb->lcd_set_foreground(LCD_RGBPACK(20,20,50)); 364#elif LCD_DEPTH == 2 365 rb->lcd_set_foreground(LCD_BLACK); 366#endif 367 rb->lcd_fillrect(SCALE(x - 2), SCALE(y + 5), SCALE(2), SCALE(5)); 368 369} 370 371static void chopClearTerrain(struct CTerrain *ter) 372{ 373 ter->iNodesCount = 0; 374} 375 376 377static int iR(int low,int high) 378{ 379 return low+rb->rand()%(high-low+1); 380} 381 382static void chopCopyTerrain(struct CTerrain *src, struct CTerrain *dest, 383 int xOffset,int yOffset) 384{ 385 int i=0; 386 387 while(i < src->iNodesCount) 388 { 389 dest->mNodes[i].x = src->mNodes[i].x + xOffset; 390 dest->mNodes[i].y = src->mNodes[i].y + yOffset; 391 392 i++; 393 } 394 395 dest->iNodesCount = src->iNodesCount; 396 dest->iLastNodePlacedPosX = src->iLastNodePlacedPosX; 397 398} 399 400static void chopAddTerrainNode(struct CTerrain *ter, int x, int y) 401{ 402 int i=0; 403 404 if(ter->iNodesCount + 1 >= MAX_TERRAIN_NODES) 405 { 406 /* DEBUGF("ERROR: Not enough nodes!\n"); */ 407 return; 408 } 409 410 ter->iNodesCount++; 411 412 i = ter->iNodesCount - 1; 413 414 ter->mNodes[i].x = x; 415 ter->mNodes[i].y= y; 416 417 ter->iLastNodePlacedPosX = x; 418 419} 420 421static void chopTerrainNodeDeleteAndShift(struct CTerrain *ter,int nodeIndex) 422{ 423 int i=nodeIndex; 424 425 while( i < ter->iNodesCount ) 426 { 427 ter->mNodes[i - 1] = ter->mNodes[i]; 428 i++; 429 } 430 431 ter->iNodesCount--; 432 433 434} 435 436static int chopUpdateTerrainRecycling(struct CTerrain *ter) 437{ 438 int i=1; 439 int iNewNodePos,g,v; 440 while(i < ter->iNodesCount) 441 { 442 443 if( iCameraPosX > ter->mNodes[i].x) 444 { 445 446 chopTerrainNodeDeleteAndShift(ter,i); 447 448 iNewNodePos = ter->iLastNodePlacedPosX + 50; 449 g = iScreenY - 10; 450 451 v = 3*iPlayerSpeedX; 452 if(v>50) 453 v=50; 454 if(iCurrLevelMode == LEVEL_MODE_STEEP) 455 v*=5; 456 457 chopAddTerrainNode(ter,iNewNodePos,g - iR(-v,v)); 458 } 459 460 i++; 461 462 } 463 464 return 1; 465} 466 467static int chopTerrainHeightAtPoint(struct CTerrain *ter, int pX) 468{ 469 470 int iNodeIndexOne=0,iNodeIndexTwo=0, h, terY1, terY2, terX2, a, b; 471 float c,d; 472 473 int i=0; 474 for(i=1;i<MAX_TERRAIN_NODES;i++) 475 { 476 if(ter->mNodes[i].x > pX) 477 { 478 iNodeIndexOne = i - 1; 479 break; 480 } 481 482 } 483 484 iNodeIndexTwo = iNodeIndexOne + 1; 485 terY1 = ter->mNodes[iNodeIndexOne].y; 486 terY2 = ter->mNodes[iNodeIndexTwo].y; 487 488 /* terX1 = 0; */ 489 terX2 = ter->mNodes[iNodeIndexTwo].x - ter->mNodes[iNodeIndexOne].x; 490 491 pX-= ter->mNodes[iNodeIndexOne].x; 492 493 a = terY2 - terY1; 494 b = terX2; 495 c = pX; 496 d = (c/b) * a; 497 498 h = d + terY1; 499 500 return h; 501 502} 503 504static int chopPointInTerrain(struct CTerrain *ter, int pX, int pY, int iTestType) 505{ 506 int h = chopTerrainHeightAtPoint(ter, pX); 507 508 if(iTestType == 0) 509 return (pY > h); 510 else 511 return (pY < h); 512} 513 514static void chopAddBlock(int x,int y,int sx,int sy, int indexOverride) 515{ 516 int i=0; 517 518 if(indexOverride < 0) 519 { 520 while(mBlocks[i].bIsActive && i < NUMBER_OF_BLOCKS) 521 i++; 522 if(i==NUMBER_OF_BLOCKS) 523 { 524 DEBUGF("No blocks!\n"); 525 return; 526 } 527 } 528 else 529 i = indexOverride; 530 531 mBlocks[i].bIsActive = 1; 532 mBlocks[i].iWorldX = x; 533 mBlocks[i].iWorldY = y; 534 mBlocks[i].iSizeX = sx; 535 mBlocks[i].iSizeY = sy; 536 537 iLastBlockPlacedPosX = x; 538} 539 540static void chopAddParticle(int x,int y,int sx,int sy) 541{ 542 for(int i = 0; i < NUMBER_OF_PARTICLES; ++i) 543 { 544 if(!mParticles[i].bIsActive) 545 { 546 mParticles[i].bIsActive = 1; 547 mParticles[i].iWorldX = x; 548 mParticles[i].iWorldY = y; 549 mParticles[i].iSpeedX = sx; 550 mParticles[i].iSpeedY = sy; 551 return; 552 } 553 } 554} 555 556static void chopGenerateBlockIfNeeded(void) 557{ 558 int i=0; 559 int DistSpeedX = iPlayerSpeedX * 5; 560 if(DistSpeedX<200) DistSpeedX = 200; 561 562 while(i < NUMBER_OF_BLOCKS) 563 { 564 if(!mBlocks[i].bIsActive) 565 { 566 int iX,iY,sX,sY; 567 568 iX = iLastBlockPlacedPosX + (350-DistSpeedX); 569 sX = blockw; 570 571 iY = iR(0,iScreenY); 572 sY = blockh + iR(1,blockh/3); 573 574 chopAddBlock(iX,iY,sX,sY,i); 575 } 576 577 i++; 578 } 579 580} 581 582static int chopBlockCollideWithPlayer(struct CBlock *mBlock) 583{ 584 int px = iPlayerPosX; 585 int py = iPlayerPosY; 586 587 int x = mBlock->iWorldX-17; 588 int y = mBlock->iWorldY-11; 589 590 int x2 = x + mBlock->iSizeX+17; 591 int y2 = y + mBlock->iSizeY+11; 592 593 if(px>x && px<x2) 594 { 595 if(py>y && py<y2) 596 { 597 return 1; 598 } 599 } 600 601 return 0; 602} 603 604static int chopBlockOffscreen(struct CBlock *mBlock) 605{ 606 if(mBlock->iWorldX + mBlock->iSizeX < iCameraPosX) 607 return 1; 608 else 609 return 0; 610} 611 612static int chopParticleOffscreen(struct CParticle *mParticle) 613{ 614 if (mParticle->iWorldX < iCameraPosX || mParticle->iWorldY < 0 || 615 mParticle->iWorldY > iScreenY || mParticle->iWorldX > iCameraPosX + 616 iScreenX) 617 { 618 return 1; 619 } 620 else 621 return 0; 622} 623 624static void chopKillPlayer(void) 625{ 626 int i; 627 628 for (i = 0; i < NUMBER_OF_PARTICLES; i++) { 629 mParticles[i].bIsActive = 0; 630 chopAddParticle(iPlayerPosX + iR(0,20), iPlayerPosY + iR(0,20), 631 iR(-2,2), iR(-2,2)); 632 } 633 634 iPlayerAlive--; 635 636 if (iPlayerAlive == 0) { 637 rb->splash(HZ, "Game Over"); 638 639 if (score > highscore) { 640 char scoretext[30]; 641 highscore = score; 642 rb->snprintf(scoretext, sizeof(scoretext), "New High Score: %d", 643 highscore); 644 rb->splash(HZ*2, scoretext); 645 } 646 } else 647 chopper_load(false); 648} 649 650static void chopDrawTheWorld(void) 651{ 652 int i=0; 653 654 while(i < NUMBER_OF_BLOCKS) 655 { 656 if(mBlocks[i].bIsActive) 657 { 658 if(chopBlockOffscreen(&mBlocks[i]) == 1) 659 mBlocks[i].bIsActive = 0; 660 else 661 chopDrawBlock(&mBlocks[i]); 662 } 663 664 i++; 665 } 666 667 i=0; 668 669 while(i < NUMBER_OF_PARTICLES) 670 { 671 if(mParticles[i].bIsActive) 672 { 673 if(chopParticleOffscreen(&mParticles[i]) == 1) 674 mParticles[i].bIsActive = 0; 675 else 676 chopDrawParticle(&mParticles[i]); 677 } 678 679 i++; 680 } 681 682 chopRenderTerrain(&mGround, true); 683 chopRenderTerrain(&mRoof, false); 684 685} 686 687static void chopDrawParticle(struct CParticle *mParticle) 688{ 689 690 int iPosX = (mParticle->iWorldX - iCameraPosX); 691 int iPosY = (mParticle->iWorldY); 692#if LCD_DEPTH > 2 693 rb->lcd_set_foreground(LCD_RGBPACK(192,192,192)); 694#elif LCD_DEPTH == 2 695 rb->lcd_set_foreground(LCD_LIGHTGRAY); 696#endif 697 rb->lcd_fillrect(SCALE(iPosX), SCALE(iPosY), SCALE(3), SCALE(3)); 698 699} 700 701static void chopDrawScene(void) 702{ 703 char s[30]; 704 int w; 705#if LCD_DEPTH > 2 706 rb->lcd_set_background(LCD_BLACK); 707#elif LCD_DEPTH == 2 708 rb->lcd_set_background(LCD_WHITE); 709#endif 710 rb->lcd_clear_display(); 711 chopDrawTheWorld(); 712 chopDrawPlayer(iPlayerPosX - iCameraPosX, iPlayerPosY); 713 714 score = -20 + iPlayerPosX/3; 715 716#if LCD_DEPTH == 1 717 rb->lcd_set_drawmode(DRMODE_COMPLEMENT); 718#else 719 rb->lcd_set_drawmode(DRMODE_FG); 720#endif 721 722#if LCD_DEPTH > 2 723 rb->lcd_set_foreground(LCD_BLACK); 724#elif LCD_DEPTH == 2 725 rb->lcd_set_foreground(LCD_WHITE); 726#endif 727 728#if LCD_WIDTH <= 128 729 rb->snprintf(s, sizeof(s), "Dist: %d", score); 730#else 731 rb->snprintf(s, sizeof(s), "Distance: %d", score); 732#endif 733 rb->lcd_getstringsize(s, &w, NULL); 734 rb->lcd_putsxy(2, 2, s); 735 if (score < highscore) 736 { 737 int w2; 738#if LCD_WIDTH <= 128 739 rb->snprintf(s, sizeof(s), "Hi: %d", highscore); 740#else 741 rb->snprintf(s, sizeof(s), "Best: %d", highscore); 742#endif 743 rb->lcd_getstringsize(s, &w2, NULL); 744 if (LCD_WIDTH - 2 - w2 > w + 2) 745 rb->lcd_putsxy(LCD_WIDTH - 2 - w2, 2, s); 746 } 747 rb->lcd_set_drawmode(DRMODE_SOLID); 748 749 rb->lcd_update(); 750} 751 752static bool _ingame; 753static int chopMenuCb(int action, 754 const struct menu_item_ex *this_item, 755 struct gui_synclist *this_list) 756{ 757 (void)this_list; 758 if(action == ACTION_REQUEST_MENUITEM 759 && !_ingame && ((intptr_t)this_item)==0) 760 return ACTION_EXIT_MENUITEM; 761 return action; 762} 763static int chopMenu(int menunum) 764{ 765 int result = 0; 766 int res = 0; 767 bool menu_quit = false; 768 769 static const struct opt_items levels[2] = { 770 { "Normal", -1 }, 771 { "Steep", -1 }, 772 }; 773 774 MENUITEM_STRINGLIST(menu,"Chopper Menu",chopMenuCb, 775 "Resume Game","Start New Game", 776 "Level","Playback Control","Quit"); 777 _ingame = (menunum!=0); 778 779#ifdef HAVE_LCD_COLOR 780 rb->lcd_set_foreground(LCD_WHITE); 781 rb->lcd_set_background(LCD_BLACK); 782#elif LCD_DEPTH == 2 783 rb->lcd_set_foreground(LCD_BLACK); 784 rb->lcd_set_background(LCD_WHITE); 785#endif 786 787 rb->lcd_clear_display(); 788 rb->button_clear_queue(); 789 790 while (!menu_quit) { 791 switch(rb->do_menu(&menu, &result, NULL, false)) 792 { 793 case 0: /* Resume Game */ 794 menu_quit=true; 795 res = -1; 796 break; 797 case 1: /* Start New Game */ 798 menu_quit=true; 799 chopper_load(true); 800 res = -1; 801 break; 802 case 2: 803 rb->set_option("Level", &iLevelMode, RB_INT, levels, 2, NULL); 804 break; 805 case 3: 806 playback_control(NULL); 807 break; 808 case 4: 809 menu_quit=true; 810 res = PLUGIN_OK; 811 break; 812 case MENU_ATTACHED_USB: 813 menu_quit=true; 814 res = PLUGIN_USB_CONNECTED; 815 break; 816 } 817 } 818 rb->lcd_clear_display(); 819 return res; 820} 821 822static int chopGameLoop(void) 823{ 824 int move_button, ret; 825 bool exit=false; 826 bool showsplash=true; 827 int end, i=0, bdelay=0, last_button=BUTTON_NONE; 828 829 if (chopUpdateTerrainRecycling(&mGround) == 1) 830 /* mirror the sky if we've changed the ground */ 831 chopCopyTerrain(&mGround, &mRoof, 0, - ( (iScreenY * 3) / 4)); 832 833 ret = chopMenu(0); 834 if (ret != -1) 835 return PLUGIN_OK; 836 837 while (!exit) { 838 839 end = *rb->current_tick + CYCLETIME; 840 841 if(chopUpdateTerrainRecycling(&mGround) == 1) 842 /* mirror the sky if we've changed the ground */ 843 chopCopyTerrain(&mGround, &mRoof, 0, - ( (iScreenY * 3) / 4)); 844 845 iRotorOffset = iR(-1,1); 846 847 /* We need to have this here so particles move when we're dead */ 848 849 for (i=0; i < NUMBER_OF_PARTICLES; i++) 850 if(mParticles[i].bIsActive == 1) 851 { 852 mParticles[i].iWorldX += mParticles[i].iSpeedX; 853 mParticles[i].iWorldY += mParticles[i].iSpeedY; 854 } 855 856 /* Redraw the main window: */ 857 chopDrawScene(); 858 859 860 iGravityTimerCountdown--; 861 862 if(iGravityTimerCountdown <= 0) 863 { 864 iGravityTimerCountdown = 3; 865 chopAddParticle(iPlayerPosX, iPlayerPosY+5, 0, 0); 866 } 867 868 if(iCurrLevelMode == LEVEL_MODE_NORMAL) 869 chopGenerateBlockIfNeeded(); 870 871 if (showsplash) { 872 chopDrawScene(); 873 rb->splash(HZ, "Get Ready!"); 874 showsplash = false; 875 } 876 877 move_button=rb->button_status(); 878 if (rb->button_get(false) == QUIT) { 879 ret = chopMenu(1); 880 if (ret != -1) 881 return PLUGIN_OK; 882 showsplash = true; 883 bdelay = 0; 884 last_button = BUTTON_NONE; 885 move_button = BUTTON_NONE; 886 } 887 888 switch (move_button) { 889 case ACTION: 890#ifdef ACTION2 891 case ACTION2: 892#endif 893 if (last_button != ACTION 894#ifdef ACTION2 895 && last_button != ACTION2 896#endif 897 ) 898 bdelay = -2; 899 if (bdelay == 0) 900 iPlayerSpeedY = -3; 901 break; 902 903 default: 904 if (last_button == ACTION 905#ifdef ACTION2 906 || last_button == ACTION2 907#endif 908 ) 909 bdelay = 3; 910 if (bdelay == 0) 911 iPlayerSpeedY = 4; 912 913 if (rb->default_event_handler(move_button) == SYS_USB_CONNECTED) 914 return PLUGIN_USB_CONNECTED; 915 break; 916 } 917 last_button = move_button; 918 919 if (bdelay < 0) { 920 iPlayerSpeedY = bdelay; 921 bdelay++; 922 } else if (bdelay > 0) { 923 iPlayerSpeedY = bdelay; 924 bdelay--; 925 } 926 927 iCameraPosX = iPlayerPosX - 25; 928 iPlayerPosX += iPlayerSpeedX; 929 iPlayerPosY += iPlayerSpeedY; 930 931 chopCounter++; 932 /* increase speed as we go along */ 933 if (chopCounter == 100){ 934 iPlayerSpeedX++; 935 chopCounter=0; 936 } 937 938 if (iPlayerPosY > iScreenY-10 || iPlayerPosY < -5 || 939 chopPointInTerrain(&mGround, iPlayerPosX, iPlayerPosY + 10, 0) || 940 chopPointInTerrain(&mRoof, iPlayerPosX ,iPlayerPosY, 1)) 941 { 942 chopKillPlayer(); 943 chopDrawScene(); 944 ret = chopMenu(0); 945 if (ret != -1) 946 return ret; 947 showsplash = true; 948 } 949 950 for (i=0; i < NUMBER_OF_BLOCKS; i++) 951 if(mBlocks[i].bIsActive == 1) 952 if(chopBlockCollideWithPlayer(&mBlocks[i])) { 953 chopKillPlayer(); 954 chopDrawScene(); 955 ret = chopMenu(0); 956 if (ret != -1) 957 return ret; 958 showsplash = true; 959 } 960 961 if (TIME_BEFORE(*rb->current_tick, end)) 962 rb->sleep(end - *rb->current_tick); /* wait until time is over */ 963 else 964 rb->yield(); 965 966 } 967 return PLUGIN_OK; 968} 969 970static void chopDrawBlock(struct CBlock *mBlock) 971{ 972 int iPosX = (mBlock->iWorldX - iCameraPosX); 973 int iPosY = (mBlock->iWorldY); 974#if LCD_DEPTH > 2 975 rb->lcd_set_foreground(LCD_RGBPACK(100,255,100)); 976#elif LCD_DEPTH == 2 977 rb->lcd_set_foreground(LCD_BLACK); 978#endif 979 rb->lcd_fillrect(SCALE(iPosX), SCALE(iPosY), SCALE(mBlock->iSizeX), 980 SCALE(mBlock->iSizeY)); 981} 982 983 984static void chopRenderTerrain(struct CTerrain *ter, bool isground) 985{ 986 987 int i = 1; 988 989 int oldx = 0; 990 991 while(i < ter->iNodesCount && oldx < iScreenX) 992 { 993 994 int x = ter->mNodes[i-1].x - iCameraPosX; 995 int y = ter->mNodes[i-1].y; 996 997 int x2 = ter->mNodes[i].x - iCameraPosX; 998 int y2 = ter->mNodes[i].y; 999 1000 int ax, ay; 1001 1002 if ((y < y2) != isground) 1003 { 1004 ax = x2; 1005 ay = y; 1006 } 1007 else 1008 { 1009 ax = x; 1010 ay = y2; 1011 } 1012#if LCD_DEPTH > 2 1013 rb->lcd_set_foreground(LCD_RGBPACK(100,255,100)); 1014#elif LCD_DEPTH == 2 1015 rb->lcd_set_foreground(LCD_DARKGRAY); 1016#endif 1017 1018 rb->lcd_drawline(SCALE(x), SCALE(y), SCALE(x2), SCALE(y2)); 1019 1020 xlcd_filltriangle(SCALE(x), SCALE(y), SCALE(x2), SCALE(y2), 1021 SCALE(ax), SCALE(ay)); 1022 1023 if (isground) 1024 { 1025 y = ay; 1026 y2 = (LCD_HEIGHT*SIZE); 1027 } 1028 else 1029 { 1030 y = 0; 1031 y2 = ay; 1032 } 1033 if (y2-y > 0) 1034 rb->lcd_fillrect(SCALE(x), SCALE(y), SCALE(x2-x)+1, SCALE(y2-y)+1); 1035 1036 oldx = x; 1037 i++; 1038 } 1039} 1040 1041static void chopper_load(bool newgame) 1042{ 1043 1044 int i; 1045 int g; 1046 1047 if (newgame) { 1048 iScreenX = LCD_WIDTH * SIZE; 1049 iScreenY = LCD_HEIGHT * SIZE; 1050 blockh = iScreenY / 5; 1051 blockw = iScreenX / 20; 1052 iPlayerAlive = 1; 1053 iCurrLevelMode = iLevelMode; 1054 score = 0; 1055 } 1056 iRotorOffset = 0; 1057 iPlayerPosX = 60; 1058 iPlayerPosY = (iScreenY * 4) / 10; 1059 iLastBlockPlacedPosX = 0; 1060 iGravityTimerCountdown = 2; 1061 chopCounter = 0; 1062 iPlayerSpeedX = 3; 1063 iPlayerSpeedY = 0; 1064 iCameraPosX = 30; 1065 1066 for (i=0; i < NUMBER_OF_PARTICLES; i++) 1067 mParticles[i].bIsActive = 0; 1068 1069 for (i=0; i < NUMBER_OF_BLOCKS; i++) 1070 mBlocks[i].bIsActive = 0; 1071 1072 g = iScreenY - 10; 1073 chopClearTerrain(&mGround); 1074 1075 for (i=0; i < MAX_TERRAIN_NODES; i++) 1076 chopAddTerrainNode(&mGround,i * 30,g - iR(0,20)); 1077 1078 if (chopUpdateTerrainRecycling(&mGround) == 1) 1079 /* mirror the sky if we've changed the ground */ 1080 chopCopyTerrain(&mGround, &mRoof, 0, - ( (iScreenY * 3) / 4)); 1081 1082 if (iCurrLevelMode == LEVEL_MODE_NORMAL) 1083 /* make it a bit more exciting, cause it's easy terrain... */ 1084 iPlayerSpeedX *= 2; 1085} 1086 1087/* this is the plugin entry point */ 1088enum plugin_status plugin_start(const void* parameter) 1089{ 1090 (void)parameter; 1091 int ret; 1092 1093 rb->lcd_setfont(FONT_SYSFIXED); 1094#if LCD_DEPTH > 1 1095 rb->lcd_set_backdrop(NULL); 1096#endif 1097#ifdef HAVE_LCD_COLOR 1098 rb->lcd_set_background(LCD_BLACK); 1099 rb->lcd_set_foreground(LCD_WHITE); 1100#endif 1101 1102 1103 /* Turn off backlight timeout */ 1104 backlight_ignore_timeout(); 1105 1106 1107 rb->srand( *rb->current_tick ); 1108 1109 configfile_load(CFG_FILE, config, 1, 0); 1110 1111 chopper_load(true); 1112 ret = chopGameLoop(); 1113 1114 configfile_save(CFG_FILE, config, 1, 0); 1115 1116 rb->lcd_setfont(FONT_UI); 1117 1118 /* Turn on backlight timeout (revert to settings) */ 1119 backlight_use_settings(); 1120 1121 1122 return ret; 1123}