A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 1988 lines 58 kB view raw
1/*************************************************************************** 2 * __________ __ ___. 3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___ 4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / 5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < 6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ 7 * \/ \/ \/ \/ \/ 8 * $Id$ 9 * 10 * Copyright (C) 2006 Albert Veli 11 * 12 * This program is free software; you can redistribute it and/or 13 * modify it under the terms of the GNU General Public License 14 * as published by the Free Software Foundation; either version 2 15 * of the License, or (at your option) any later version. 16 * 17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 18 * KIND, either express or implied. 19 * 20 ****************************************************************************/ 21 22/* Improvised creds goes to: 23 * 24 * - Anders Clausen for ingeniously inventing the name Invadrox. 25 * - Linus Nielsen-Feltzing for patiently answering n00b questions. 26 */ 27 28#include "plugin.h" 29#include "lib/highscore.h" 30#include "lib/helper.h" 31 32/* bitmaps */ 33#include "pluginbitmaps/invadrox_background.h" 34 35/* get dimensions for later use from the bitmaps */ 36#include "pluginbitmaps/invadrox_aliens.h" 37#include "pluginbitmaps/invadrox_ships.h" 38#include "pluginbitmaps/invadrox_bombs.h" 39#include "pluginbitmaps/invadrox_alien_explode.h" 40#include "pluginbitmaps/invadrox_shield.h" 41#include "pluginbitmaps/invadrox_ufo.h" 42#include "pluginbitmaps/invadrox_ufo_explode.h" 43#include "pluginbitmaps/invadrox_numbers.h" 44#include "pluginbitmaps/invadrox_fire.h" 45#define ALIEN_WIDTH (BMPWIDTH_invadrox_aliens/2) 46#define ALIEN_HEIGHT (BMPHEIGHT_invadrox_aliens/3) 47#define SHIP_WIDTH BMPWIDTH_invadrox_ships 48#define SHIP_HEIGHT (BMPHEIGHT_invadrox_ships/3) 49#define BOMB_WIDTH (BMPWIDTH_invadrox_bombs/3) 50#define BOMB_HEIGHT (BMPHEIGHT_invadrox_bombs/6) 51#define ALIEN_EXPLODE_WIDTH BMPWIDTH_invadrox_alien_explode 52#define ALIEN_EXPLODE_HEIGHT BMPHEIGHT_invadrox_alien_explode 53#define SHIELD_WIDTH BMPWIDTH_invadrox_shield 54#define SHIELD_HEIGHT BMPHEIGHT_invadrox_shield 55#define UFO_WIDTH BMPWIDTH_invadrox_ufo 56#define UFO_HEIGHT BMPHEIGHT_invadrox_ufo 57#define UFO_EXPLODE_WIDTH BMPWIDTH_invadrox_ufo_explode 58#define UFO_EXPLODE_HEIGHT BMPHEIGHT_invadrox_ufo_explode 59#define NUMBERS_WIDTH (BMPWIDTH_invadrox_numbers/10) 60#define FONT_HEIGHT BMPHEIGHT_invadrox_numbers 61#define FIRE_WIDTH BMPWIDTH_invadrox_fire 62#define FIRE_HEIGHT BMPHEIGHT_invadrox_fire 63 64 65 66/* Original graphics is only 1bpp so it should be portable 67 * to most targets. But for now, only support the simple ones. 68 */ 69#if (LCD_DEPTH < 2) 70 #error INVADROX: Unsupported LCD 71#endif 72 73/* #define DEBUG */ 74#ifdef DEBUG 75#define DBG(format, arg...) { DEBUGF("%s: " format, __FUNCTION__, ## arg); } 76#else 77#define DBG(format, arg...) {} 78#endif 79 80#ifndef ABS 81#define ABS(a) (((a) < 0) ? -(a) : (a)) 82#endif 83 84#if CONFIG_KEYPAD == IRIVER_H100_PAD 85 86#define QUIT BUTTON_OFF 87#define LEFT BUTTON_LEFT 88#define RIGHT BUTTON_RIGHT 89#define FIRE BUTTON_ON 90 91#elif CONFIG_KEYPAD == IRIVER_H300_PAD 92 93#define QUIT BUTTON_OFF 94#define LEFT BUTTON_LEFT 95#define RIGHT BUTTON_RIGHT 96#define FIRE BUTTON_SELECT 97 98#elif (CONFIG_KEYPAD == IRIVER_H10_PAD) 99 100#define QUIT BUTTON_POWER 101#define LEFT BUTTON_LEFT 102#define RIGHT BUTTON_RIGHT 103#define FIRE BUTTON_PLAY 104 105#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \ 106 (CONFIG_KEYPAD == IPOD_3G_PAD) || \ 107 (CONFIG_KEYPAD == IPOD_1G2G_PAD) 108 109#define QUIT BUTTON_MENU 110#define LEFT BUTTON_LEFT 111#define RIGHT BUTTON_RIGHT 112#define FIRE BUTTON_SELECT 113 114#elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD 115 116#define QUIT BUTTON_POWER 117#define LEFT BUTTON_LEFT 118#define RIGHT BUTTON_RIGHT 119#define FIRE BUTTON_SELECT 120 121#elif CONFIG_KEYPAD == GIGABEAT_PAD \ 122 || CONFIG_KEYPAD == SAMSUNG_YPR0_PAD 123 124#define QUIT BUTTON_POWER 125#define LEFT BUTTON_LEFT 126#define RIGHT BUTTON_RIGHT 127#define FIRE BUTTON_SELECT 128 129#elif (CONFIG_KEYPAD == SANSA_E200_PAD) || \ 130 (CONFIG_KEYPAD == SANSA_CONNECT_PAD) 131 132#define QUIT BUTTON_POWER 133#define LEFT BUTTON_LEFT 134#define RIGHT BUTTON_RIGHT 135#define FIRE BUTTON_SELECT 136 137#elif CONFIG_KEYPAD == SANSA_FUZE_PAD 138 139#define QUIT (BUTTON_HOME|BUTTON_REPEAT) 140#define LEFT BUTTON_LEFT 141#define RIGHT BUTTON_RIGHT 142#define FIRE BUTTON_SELECT 143 144#elif CONFIG_KEYPAD == GIGABEAT_S_PAD 145 146#define QUIT BUTTON_BACK 147#define LEFT BUTTON_LEFT 148#define RIGHT BUTTON_RIGHT 149#define FIRE BUTTON_SELECT 150 151#elif CONFIG_KEYPAD == COWON_D2_PAD 152 153#define QUIT BUTTON_POWER 154#define LEFT BUTTON_MINUS 155#define RIGHT BUTTON_PLUS 156#define FIRE BUTTON_MENU 157 158#elif CONFIG_KEYPAD == CREATIVEZVM_PAD 159 160#define QUIT BUTTON_BACK 161#define LEFT BUTTON_LEFT 162#define RIGHT BUTTON_RIGHT 163#define FIRE BUTTON_SELECT 164 165#elif CONFIG_KEYPAD == CREATIVE_ZENXFI3_PAD 166 167#define QUIT BUTTON_POWER 168#define LEFT BUTTON_BACK 169#define RIGHT BUTTON_MENU 170#define FIRE BUTTON_PLAY 171 172#elif CONFIG_KEYPAD == PHILIPS_HDD6330_PAD 173 174#define QUIT BUTTON_POWER 175#define LEFT BUTTON_LEFT 176#define RIGHT BUTTON_RIGHT 177#define FIRE BUTTON_PLAY 178 179#elif CONFIG_KEYPAD == PHILIPS_SA9200_PAD 180 181#define QUIT BUTTON_POWER 182#define LEFT BUTTON_PREV 183#define RIGHT BUTTON_NEXT 184#define FIRE BUTTON_PLAY 185 186#elif CONFIG_KEYPAD == ONDAVX747_PAD || \ 187CONFIG_KEYPAD == ONDAVX777_PAD || \ 188CONFIG_KEYPAD == MROBE500_PAD 189 190#define QUIT BUTTON_POWER 191 192#elif (CONFIG_KEYPAD == SAMSUNG_YH820_PAD) || \ 193 (CONFIG_KEYPAD == SAMSUNG_YH92X_PAD) 194 195#define QUIT BUTTON_REW 196#define LEFT BUTTON_LEFT 197#define RIGHT BUTTON_RIGHT 198#define FIRE BUTTON_PLAY 199#define FIRE2 BUTTON_UP 200 201#elif CONFIG_KEYPAD == PBELL_VIBE500_PAD 202 203#define QUIT BUTTON_REC 204#define LEFT BUTTON_PREV 205#define RIGHT BUTTON_NEXT 206#define FIRE BUTTON_OK 207 208#elif CONFIG_KEYPAD == MPIO_HD300_PAD 209 210#define QUIT BUTTON_MENU 211#define LEFT BUTTON_REW 212#define RIGHT BUTTON_FF 213#define FIRE BUTTON_ENTER 214 215#elif CONFIG_KEYPAD == SANSA_FUZEPLUS_PAD 216 217#define QUIT BUTTON_POWER 218#define LEFT BUTTON_LEFT 219#define RIGHT BUTTON_RIGHT 220#define FIRE BUTTON_SELECT 221 222#elif CONFIG_KEYPAD == SONY_NWZ_PAD 223 224#define QUIT BUTTON_BACK 225#define LEFT BUTTON_LEFT 226#define RIGHT BUTTON_RIGHT 227#define FIRE BUTTON_PLAY 228 229#elif CONFIG_KEYPAD == CREATIVE_ZEN_PAD 230 231#define QUIT BUTTON_BACK 232#define LEFT BUTTON_LEFT 233#define RIGHT BUTTON_RIGHT 234#define FIRE BUTTON_SELECT 235 236#elif (CONFIG_KEYPAD == HM60X_PAD) || \ 237 (CONFIG_KEYPAD == HM801_PAD) 238 239#define QUIT BUTTON_POWER 240#define LEFT BUTTON_LEFT 241#define RIGHT BUTTON_RIGHT 242#define FIRE BUTTON_SELECT 243 244#elif CONFIG_KEYPAD == DX50_PAD 245 246#define QUIT (BUTTON_POWER|BUTTON_REL) 247#define LEFT BUTTON_LEFT 248#define RIGHT BUTTON_PLAY 249#define FIRE BUTTON_RIGHT 250 251#elif CONFIG_KEYPAD == CREATIVE_ZENXFI2_PAD 252 253#define QUIT BUTTON_POWER 254#define FIRE BUTTON_MENU 255 256#elif CONFIG_KEYPAD == XDUOO_X3II_PAD || CONFIG_KEYPAD == XDUOO_X20_PAD 257 258#define QUIT BUTTON_POWER 259#define LEFT BUTTON_HOME 260#define RIGHT BUTTON_VOL_DOWN 261#define FIRE BUTTON_VOL_UP 262 263#elif CONFIG_KEYPAD == FIIO_M3K_LINUX_PAD 264 265#define QUIT BUTTON_POWER 266#define LEFT BUTTON_HOME 267#define RIGHT BUTTON_VOL_DOWN 268#define FIRE BUTTON_VOL_UP 269 270#elif CONFIG_KEYPAD == IHIFI_770_PAD || CONFIG_KEYPAD == IHIFI_800_PAD 271 272#define QUIT BUTTON_POWER 273#define LEFT BUTTON_HOME 274#define RIGHT BUTTON_VOL_DOWN 275#define FIRE BUTTON_VOL_UP 276 277#elif CONFIG_KEYPAD == EROSQ_PAD 278 279#define QUIT BUTTON_POWER 280#define LEFT BUTTON_SCROLL_BACK 281#define RIGHT BUTTON_SCROLL_FWD 282#define FIRE BUTTON_PLAY 283 284#elif CONFIG_KEYPAD == FIIO_M3K_PAD 285 286#define QUIT BUTTON_POWER 287#define LEFT BUTTON_LEFT 288#define RIGHT BUTTON_RIGHT 289#define FIRE BUTTON_SELECT 290 291#elif CONFIG_KEYPAD == SHANLING_Q1_PAD 292/* use touchscreen */ 293 294#elif CONFIG_KEYPAD == MA_PAD 295 296#define QUIT BUTTON_BACK 297#define LEFT BUTTON_LEFT 298#define RIGHT BUTTON_RIGHT 299#define FIRE BUTTON_PLAY 300 301#elif CONFIG_KEYPAD == RG_NANO_PAD 302 303#define QUIT BUTTON_START 304#define LEFT BUTTON_LEFT 305#define RIGHT BUTTON_RIGHT 306#define FIRE BUTTON_A 307 308#else 309 #error INVADROX: Unsupported keypad 310#endif 311 312#ifndef RC_QUIT 313#define RC_QUIT 0 314#endif 315 316#ifdef HAVE_TOUCHSCREEN 317 318#ifndef QUIT 319#define QUIT 0 320#endif 321#ifndef LEFT 322#define LEFT 0 323#endif 324#ifndef RIGHT 325#define RIGHT 0 326#endif 327#ifndef FIRE 328#define FIRE 0 329#endif 330 331#define TOUCHSCREEN_QUIT BUTTON_TOPLEFT 332#define TOUCHSCREEN_LEFT (BUTTON_MIDLEFT | BUTTON_BOTTOMLEFT) 333#define TOUCHSCREEN_RIGHT (BUTTON_MIDRIGHT | BUTTON_BOTTOMRIGHT) 334#define TOUCHSCREEN_FIRE (BUTTON_CENTER | BUTTON_BOTTOMMIDDLE) 335 336#define ACTION_QUIT (QUIT | TOUCHSCREEN_QUIT | RC_QUIT) 337#define ACTION_LEFT (LEFT | TOUCHSCREEN_LEFT) 338#define ACTION_RIGHT (RIGHT | TOUCHSCREEN_RIGHT) 339#define ACTION_FIRE (FIRE | TOUCHSCREEN_FIRE) 340 341#else /* HAVE_TOUCHSCREEN */ 342 343#define ACTION_QUIT (QUIT | RC_QUIT) 344#define ACTION_LEFT LEFT 345#define ACTION_RIGHT RIGHT 346#ifndef FIRE2 347#define ACTION_FIRE FIRE 348#else 349#define ACTION_FIRE (FIRE | FIRE2) 350#endif 351 352#endif 353 354#ifndef UNUSED 355#define UNUSED __attribute__ ((unused)) 356#endif 357 358/* Defines common to all models */ 359#define UFO_Y (SCORENUM_Y + FONT_HEIGHT + ALIEN_HEIGHT) 360#define PLAYFIELD_Y (LCD_HEIGHT - SHIP_HEIGHT - 2) 361#define PLAYFIELD_WIDTH (LCD_WIDTH - 2 * PLAYFIELD_X) 362#define LEVEL_X (LCD_WIDTH - PLAYFIELD_X - LIVES_X - 2 * NUMBERS_WIDTH - 3 * NUM_SPACING) 363#define SHIP_MIN_X (PLAYFIELD_X + PLAYFIELD_WIDTH / 5 - SHIELD_WIDTH / 2 - SHIP_WIDTH) 364#define SHIP_MAX_X (PLAYFIELD_X + 4 * PLAYFIELD_WIDTH / 5 + SHIELD_WIDTH / 2) 365/* SCORE_Y = 0 for most targets. Gigabeat redefines it later. */ 366#define SCORE_Y 0 367#define MAX_LIVES 8 368 369 370/* m:robe 500 defines */ 371#if ((LCD_WIDTH == 640) && (LCD_HEIGHT == 480)) || \ 372 ((LCD_WIDTH == 480) && (LCD_HEIGHT == 640)) 373 374/* Original arcade game size 224x240, 1bpp with 375 * red overlay at top and green overlay at bottom. 376 * 377 * M:Robe 500: 640x480x16 378 * ====================== 379 */ 380 381#define ARCADISH_GRAPHICS 382#define PLAYFIELD_X 48 383#define SHIP_Y (PLAYFIELD_Y - 2 * SHIP_HEIGHT) 384#define ALIEN_START_Y (UFO_Y + ALIEN_HEIGHT) 385#define SCORENUM_X (PLAYFIELD_X + NUMBERS_WIDTH) 386#define SCORENUM_Y (SCORE_Y + FONT_HEIGHT + 2) 387#define HISCORENUM_X (LCD_WIDTH - PLAYFIELD_X - 1 - 6 * NUMBERS_WIDTH - 5 * NUM_SPACING) 388#define SHIELD_Y (PLAYFIELD_Y - 5 * SHIP_HEIGHT) 389#define LIVES_X 10 390#define MAX_Y 18 391 392/* iPod Video defines */ 393#elif (LCD_WIDTH == 320) && (LCD_HEIGHT == 240) 394 395/* Original arcade game size 224x240, 1bpp with 396 * red overlay at top and green overlay at bottom. 397 * 398 * iPod Video: 320x240x16 399 * ====================== 400 * X: 48p padding at left/right gives 224p playfield in middle. 401 * 10p "border" gives 204p actual playfield. UFO use full 224p. 402 * Y: Use full 240p. 403 * 404 * MAX_X = (204 - 12) / 2 - 1 = 95 405 * 406 * Y: Score text 7 0 407 * Space 10 7 408 * Score 7 17 409 * Space 8 24 410 * 3 Ufo 7 32 411 * 2 Space Aliens start at 32 + 3 * 8 = 56 412 * 0 aliens 9*8 56 - 413 * space ~7*8 128 | 18.75 aliens space between 414 * shield 2*8 182 | first alien and ship. 415 * space 8 198 | MAX_Y = 18 416 * ship 8 206 - 417 * space 2*8 214 418 * hline 1 230 - PLAYFIELD_Y 419 * bottom border 10 240 420 * Lives and Level goes inside bottom border 421 */ 422 423#define ARCADISH_GRAPHICS 424#define PLAYFIELD_X 48 425#define SHIP_Y (PLAYFIELD_Y - 3 * SHIP_HEIGHT) 426#define ALIEN_START_Y (UFO_Y + 3 * ALIEN_HEIGHT) 427#define SCORENUM_X (PLAYFIELD_X + NUMBERS_WIDTH) 428#define SCORENUM_Y SCORE_Y + (2 * (FONT_HEIGHT + 1) + 1) 429#define HISCORENUM_X (LCD_WIDTH - PLAYFIELD_X - 1 - 6 * NUMBERS_WIDTH - 5 * NUM_SPACING) 430#define SHIELD_Y (PLAYFIELD_Y - 6 * SHIP_HEIGHT) 431#define LIVES_X 10 432#define MAX_Y 18 433 434/* Anbernic RG Nano defines */ 435#elif (LCD_WIDTH == 240) && (LCD_HEIGHT == 240) 436 437/* Original arcade game size 224x240, 1bpp with 438 * red overlay at top and green overlay at bottom. 439 * 440 * Anbernic RG Nano: 240x240x16 441 * ====================== 442 * X: 8p padding at left/right gives 224p playfield in middle. 443 * 10p "border" gives 204p actual playfield. UFO use full 224p. 444 * Y: Use full 240p. 445*/ 446 447#define ARCADISH_GRAPHICS 448#define PLAYFIELD_X 8 449#define SHIP_Y (PLAYFIELD_Y - 3 * SHIP_HEIGHT) 450#define ALIEN_START_Y (UFO_Y + 3 * ALIEN_HEIGHT) 451#define SCORENUM_X (PLAYFIELD_X + NUMBERS_WIDTH) + 10 452#define SCORENUM_Y SCORE_Y + (2 * (FONT_HEIGHT + 1) + 1) 453#define HISCORENUM_X (LCD_WIDTH - PLAYFIELD_X - 1 - 6 * NUMBERS_WIDTH - 8 * NUM_SPACING) 454#define SHIELD_Y (PLAYFIELD_Y - 6 * SHIP_HEIGHT) 455#define LIVES_X 10 456#define MAX_Y 18 457 458#elif (LCD_WIDTH == 176) && (LCD_HEIGHT == 220) 459 460/* Sandisk Sansa e200: 176x220x16 461 * ============================== 462 * X: No padding. 8p border -> 160p playfield. 463 * 464 * 8p Aliens with 3p spacing -> 88 + 30 = 118p aliens block. 465 * (160 - 118) / 2 = 21 rounds for whole block (more than original) 466 * MAX_X = (160 - 8) / 2 - 1 = 75 rounds for single alien (less than original) 467 * 468 * LOGO 70 0 469 * Score text 5 70 470 * Space 5 75 471 * Y Score 5 80 472 * Space 10 85 473 * 2 Ufo 5 95 474 * 2 Space 10 100 475 * 0 aliens 9*5 110 - 476 * space ~7*5 155 | 18.6 aliens space between 477 * shield 2*5 188 | first alien and ship. 478 * space 5 198 | MAX_Y = 18 479 * ship 5 203 - 480 * space 5 208 481 * hline 1 213 PLAYFIELD_Y 482 * bottom border 6 483 * LCD_HEIGHT 220 484 * Lives and Level goes inside bottom border 485 */ 486 487#define SMALL_GRAPHICS 488#define PLAYFIELD_X 0 489#define SHIP_Y (PLAYFIELD_Y - 2 * SHIP_HEIGHT) 490#define SHIELD_Y (SHIP_Y - SHIP_HEIGHT - SHIELD_HEIGHT) 491#define ALIEN_START_Y (UFO_Y + 3 * SHIP_HEIGHT) 492/* Redefine SCORE_Y */ 493#undef SCORE_Y 494#define SCORE_Y 70 495#define SCORENUM_X (PLAYFIELD_X + NUMBERS_WIDTH) 496#define SCORENUM_Y (SCORE_Y + 2 * FONT_HEIGHT) 497#define HISCORENUM_X (LCD_WIDTH - PLAYFIELD_X - 1 - 6 * NUMBERS_WIDTH - 5 * NUM_SPACING) 498#define LIVES_X 8 499#define MAX_Y 18 500 501 502#elif (LCD_WIDTH == 176) && (LCD_HEIGHT == 132) 503 504/* iPod Nano: 176x132x16 505 * ====================== 506 * X: No padding. 8p border -> 160p playfield. 507 * 508 * LIVES_X 8 509 * ALIEN_WIDTH 8 510 * ALIEN_HEIGHT 5 511 * ALIEN_SPACING 3 512 * SHIP_WIDTH 10 513 * SHIP_HEIGHT 5 514 * FONT_HEIGHT 5 515 * UFO_WIDTH 10 516 * UFO_HEIGHT 5 517 * SHIELD_WIDTH 15 518 * SHIELD_HEIGHT 10 519 * MAX_X 75 520 * MAX_Y = 18 521 * ALIEN_START_Y (UFO_Y + 12) 522 * 523 * 8p Aliens with 3p spacing -> 88 + 30 = 118p aliens block. 524 * (160 - 118) / 2 = 21 rounds for whole block (more than original) 525 * MAX_X = (160 - 8) / 2 - 1 = 75 rounds for single alien (less than original) 526 * 527 * Y: Scoreline 5 0 (combine scoretext and numbers on same line) 528 * Space 5 5 529 * 1 Ufo 5 10 530 * 3 Space 7 15 531 * 2 aliens 9*5 22 - 532 * space ~7*5 67 | Just above 18 aliens space between 533 * shield 2*5 100 | first alien and ship. 534 * space 5 110 | MAX_Y = 18 535 * ship 5 115 - 536 * space 5 120 537 * hline 1 125 PLAYFIELD_Y 538 * bottom border 6 126 539 * LCD_HEIGHT 131 540 * Lives and Level goes inside bottom border 541 */ 542 543#define SMALL_GRAPHICS 544#define PLAYFIELD_X 0 545#define SHIP_Y (PLAYFIELD_Y - 2 * SHIP_HEIGHT) 546#define ALIEN_START_Y (UFO_Y + 12) 547#define SCORENUM_X (PLAYFIELD_X + 6 * NUMBERS_WIDTH + 5 * NUM_SPACING) 548#define SCORENUM_Y SCORE_Y 549#define HISCORENUM_X (LCD_WIDTH - PLAYFIELD_X - 4 * NUMBERS_WIDTH - 3 * NUM_SPACING) 550#define SHIELD_Y (SHIP_Y - SHIP_HEIGHT - SHIELD_HEIGHT) 551#define LIVES_X 8 552#define MAX_Y 18 553 554#elif (LCD_WIDTH == 160) && (LCD_HEIGHT == 128) 555 556/* iAudio X5, iRiver H10 20Gb, iPod 3g/4g, H100, M5: 160x128 557 * ========================================================= 558 * X: No padding. No border -> 160p playfield. 559 * 560 * LIVES_X 0 561 * ALIEN_WIDTH 8 562 * ALIEN_HEIGHT 5 563 * ALIEN_SPACING 3 564 * SHIP_WIDTH 10 565 * SHIP_HEIGHT 5 566 * FONT_HEIGHT 5 567 * UFO_WIDTH 10 568 * UFO_HEIGHT 5 569 * SHIELD_WIDTH 15 570 * SHIELD_HEIGHT 10 571 * MAX_X 75 572 * MAX_Y = 18 573 * ALIEN_START_Y (UFO_Y + 10) 574 * 575 * 8p Aliens with 3p spacing -> 88 + 30 = 118p aliens block. 576 * (160 - 118) / 2 = 21 rounds for whole block (more than original) 577 * MAX_X = (160 - 8) / 2 - 1 = 75 rounds for single alien (less than original) 578 * 579 * Y: Scoreline 5 0 (combine scoretext and numbers on same line) 580 * Space 5 5 581 * 1 Ufo 5 10 582 * 2 Space 5 15 583 * 8 aliens 9*5 20 - 584 * space ~6*5 65 | Just above 18 aliens space between 585 * shield 2*5 96 | first alien and ship. 586 * space 5 106 | MAX_Y = 18 587 * ship 5 111 - 588 * space 5 116 589 * hline 1 121 PLAYFIELD_Y 590 * bottom border 6 122 591 * LCD_HEIGHT 128 592 * Lives and Level goes inside bottom border 593 */ 594 595#define SMALL_GRAPHICS 596#define PLAYFIELD_X 0 597#define SHIP_Y (PLAYFIELD_Y - 2 * SHIP_HEIGHT) 598#define ALIEN_START_Y (UFO_Y + 10) 599#define SCORENUM_X (PLAYFIELD_X + 6 * NUMBERS_WIDTH + 5 * NUM_SPACING) 600#define SCORENUM_Y SCORE_Y 601#define HISCORENUM_X (LCD_WIDTH - PLAYFIELD_X - 4 * NUMBERS_WIDTH - 3 * NUM_SPACING) 602#define SHIELD_Y (SHIP_Y - SHIP_HEIGHT - SHIELD_HEIGHT) 603#define LIVES_X 0 604#define MAX_Y 18 605 606 607#elif (LCD_WIDTH == 240) && ((LCD_HEIGHT == 320) || (LCD_HEIGHT == 400)) 608 609/* Gigabeat: 240x320x16 610 * ====================== 611 * X: 8p padding at left/right gives 224p playfield in middle. 612 * 10p "border" gives 204p actual playfield. UFO use full 224p. 613 * Y: Use bottom 240p for playfield and top 80 pixels for logo. 614 * 615 * MAX_X = (204 - 12) / 2 - 1 = 95 616 * 617 * Y: Score text 7 0 + 80 618 * Space 10 7 + 80 619 * Score 7 17 + 80 620 * Space 8 24 + 80 621 * 3 Ufo 7 32 + 80 622 * 2 Space Aliens start at 32 + 3 * 8 = 56 623 * 0 aliens 9*8 56 - 624 * space ~7*8 128 | 18.75 aliens space between 625 * shield 2*8 182 | first alien and ship. 626 * space 8 198 | MAX_Y = 18 627 * ship 8 206 - 628 * space 2*8 214 629 * hline 1 230 310 - PLAYFIELD_Y 630 * bottom border 10 240 320 631 * Lives and Level goes inside bottom border 632 */ 633 634#define ARCADISH_GRAPHICS 635#define PLAYFIELD_X 8 636#define SHIP_Y (PLAYFIELD_Y - 3 * SHIP_HEIGHT) 637#define ALIEN_START_Y (UFO_Y + 3 * ALIEN_HEIGHT) 638/* Redefine SCORE_Y */ 639#undef SCORE_Y 640#define SCORE_Y 80 641#define SCORENUM_X (PLAYFIELD_X + NUMBERS_WIDTH) 642#define SCORENUM_Y SCORE_Y + (2 * (FONT_HEIGHT + 1) + 1) 643#define HISCORENUM_X (LCD_WIDTH - PLAYFIELD_X - 1 - 6 * NUMBERS_WIDTH - 5 * NUM_SPACING) 644#define SHIELD_Y (PLAYFIELD_Y - 6 * SHIP_HEIGHT) 645#define LIVES_X 10 646#define MAX_Y 18 647 648#elif (LCD_WIDTH == 220) && (LCD_HEIGHT == 176) 649 650/* H300, iPod Color: 220x176x16 651 * ============================ 652 * X: 0p padding at left/right gives 220p playfield in middle. 653 * 8p "border" gives 204p actual playfield. UFO use full 220p. 654 * Y: Use full 176p for playfield. 655 * 656 * MAX_X = (204 - 12) / 2 - 1 = 95 657 * 658 * Y: Score text 7 0 659 * Space 8 7 660 * 1 Ufo 7 15 661 * 7 Space Aliens start at 15 + 3 * 8 = 56 662 * 6 aliens 9*8 25 - 663 * space ~7*8 103 | 15.6 aliens space between 664 * shield 2*8 126 | first alien and ship. 665 * space 8 142 | MAX_Y = 15 666 * ship 8 150 - 667 * space 8 158 668 * hline 1 166 - PLAYFIELD_Y 669 * bottom border 10 176 670 * Lives and Level goes inside bottom border 671 */ 672 673#define ARCADISH_GRAPHICS 674#define PLAYFIELD_X 0 675#define SHIP_Y (PLAYFIELD_Y - 2 * SHIP_HEIGHT) 676#define ALIEN_START_Y (UFO_Y + 10) 677#define SCORENUM_Y SCORE_Y 678#define SCORENUM_X (PLAYFIELD_X + 6 * NUMBERS_WIDTH + 6 * NUM_SPACING) 679#define HISCORENUM_X (LCD_WIDTH - PLAYFIELD_X - 4 * NUMBERS_WIDTH - 3 * NUM_SPACING) 680#define SHIELD_Y (PLAYFIELD_Y - 5 * SHIP_HEIGHT) 681#define LIVES_X 8 682#define MAX_Y 15 683 684#elif (LCD_WIDTH == 360) && (LCD_HEIGHT == 400) 685 686/* Shanling Q1 687 */ 688#define ARCADISH_GRAPHICS 689#define PLAYFIELD_X 32 690#define SHIP_Y (PLAYFIELD_Y - 3 * SHIP_HEIGHT) 691#define ALIEN_START_Y (UFO_Y + 3 * ALIEN_HEIGHT) 692/* Redefine SCORE_Y */ 693#undef SCORE_Y 694#define SCORE_Y 80 695#define SCORENUM_X (PLAYFIELD_X + NUMBERS_WIDTH) 696#define SCORENUM_Y SCORE_Y + (2 * (FONT_HEIGHT + 1) + 1) 697#define HISCORENUM_X (LCD_WIDTH - PLAYFIELD_X - 1 - 6 * NUMBERS_WIDTH - 5 * NUM_SPACING) 698#define SHIELD_Y (PLAYFIELD_Y - 6 * SHIP_HEIGHT) 699#define LIVES_X 10 700#define MAX_Y 18 701 702#else 703 #error INVADROX: Unsupported LCD type 704#endif 705 706#define MAX_X ((LCD_WIDTH-LIVES_X*2-PLAYFIELD_X*2 - ALIEN_WIDTH)/2 - 1) 707 708/* Defines common to each "graphic type" */ 709#ifdef ARCADISH_GRAPHICS 710 711#define SHOT_HEIGHT 5 712#define ALIEN_SPACING 4 713#define ALIEN_SPEED 2 714#define UFO_SPEED 1 715#define NUM_SPACING 3 716#define FIRE_SPEED 8 717#define BOMB_SPEED 3 718#define ALIENS 11 719 720#elif defined SMALL_GRAPHICS 721 722#define SHOT_HEIGHT 4 723#define ALIEN_SPACING 3 724#define ALIEN_SPEED 2 725#define UFO_SPEED 1 726#define NUM_SPACING 2 727#define FIRE_SPEED 6 728#define BOMB_SPEED 2 729#define ALIENS 11 730 731#else 732 #error Graphic type not defined 733#endif 734 735 736/* Colors */ 737#if (LCD_DEPTH >= 8) 738#define SLIME_GREEN LCD_RGBPACK(31, 254, 31) 739#define UFO_RED LCD_RGBPACK(254, 31, 31) 740#elif (LCD_DEPTH == 2) 741#define SLIME_GREEN LCD_LIGHTGRAY 742#define UFO_RED LCD_LIGHTGRAY 743#else 744#error LCD type not implemented yet 745#endif 746 747/* Alien states */ 748#define DEAD 0 749#define ALIVE 1 750#define BOMBER 2 751 752/* Fire/bomb/ufo states */ 753#define S_IDLE 0 754#define S_ACTIVE 1 755#define S_SHOWSCORE 2 756#define S_EXPLODE -9 757 758/* Fire/bomb targets */ 759#define TARGET_TOP 0 760#define TARGET_SHIELD 1 761#define TARGET_SHIP 2 762#define TARGET_BOTTOM 3 763#define TARGET_UFO 4 764 765#define HISCOREFILE PLUGIN_GAMES_DATA_DIR "/invadrox.high" 766 767 768/* The time (in ms) for one iteration through the game loop - decrease this 769 * to speed up the game - note that current_tick is (currently) only accurate 770 * to 10ms. 771 */ 772#define CYCLETIME 40 773 774 775/* Physical x is at PLAYFIELD_X + LIVES_X + x * ALIEN_SPEED 776 * Physical y is at y * ALIEN_HEIGHT 777 */ 778struct alien { 779 int x; /* x-coordinate (0 - 95) */ 780 int y; /* y-coordinate (0 - 18) */ 781 unsigned char type; /* 0 (Kang), 1 (Kodos), 2 (Serak) */ 782 unsigned char state; /* Dead, alive or bomber */ 783}; 784 785/* Aliens box 5 rows * ALIENS aliens in each row */ 786struct alien aliens[5 * ALIENS]; 787 788#define MAX_BOMBS 4 789struct bomb { 790 int x, y; 791 unsigned char type; 792 unsigned char frame; /* Current animation frame */ 793 unsigned char frames; /* Number of frames in animation */ 794 unsigned char target; /* Remember target during explosion frames */ 795 int state; /* 0 (IDLE) = inactive, 1 (FIRE) or negative, exploding */ 796}; 797struct bomb bombs[MAX_BOMBS]; 798/* Increase max_bombs at higher levels */ 799int max_bombs; 800 801/* Raw framebuffer value of shield/ship green color */ 802fb_data screen_green, screen_white; 803 804/* For optimization, precalculate startoffset of each scanline */ 805unsigned int ytab[LCD_HEIGHT]; 806 807int lives = 2; 808int score = 0; 809int scores[3] = { 30, 20, 10 }; 810int level = 0; 811struct highscore hiscore; 812bool game_over = false; 813int ship_x, old_ship_x, ship_dir, ship_acc, max_ship_speed; 814int ship_frame, ship_frame_counter; 815bool ship_hit; 816int fire, fire_target, fire_x, fire_y; 817int curr_alien, aliens_paralyzed, gamespeed; 818int ufo_state, ufo_x; 819bool level_finished; 820bool aliens_down, aliens_right, hit_left_border, hit_right_border; 821static fb_data *lcd_fb; 822 823/* No standard get_pixel function yet, use this hack instead */ 824#if (LCD_DEPTH >= 8) 825 826#if LCD_STRIDEFORMAT == VERTICAL_STRIDE 827static inline fb_data get_pixel(int x, int y) 828{ 829 return lcd_fb[x*LCD_HEIGHT+y]; 830} 831#else 832static inline fb_data get_pixel(int x, int y) 833{ 834 return lcd_fb[ytab[y] + x]; 835} 836#endif 837 838#elif (LCD_DEPTH == 2) 839 840#if (LCD_PIXELFORMAT == HORIZONTAL_PACKING) 841static const unsigned char shifts[4] = { 842 6, 4, 2, 0 843}; 844/* Horizontal packing */ 845static inline fb_data get_pixel(int x, int y) 846{ 847 return (lcd_fb[ytab[y] + (x >> 2)] >> shifts[x & 3]) & 3; 848} 849#else 850/* Vertical packing */ 851static const unsigned char shifts[4] = { 852 0, 2, 4, 6 853}; 854static inline fb_data get_pixel(int x, int y) 855{ 856 return (lcd_fb[ytab[y] + x] >> shifts[y & 3]) & 3; 857} 858#endif /* Horizontal/Vertical packing */ 859 860#else 861 #error get_pixel: pixelformat not implemented yet 862#endif 863 864 865/* Draw "digits" least significant digits of num at (x,y) */ 866static void draw_number(int x, int y, int num, int digits) 867{ 868 int i; 869 int d; 870 871 for (i = digits - 1; i >= 0; i--) { 872 d = num % 10; 873 num = num / 10; 874 rb->lcd_bitmap_part(invadrox_numbers, d * NUMBERS_WIDTH, 0, 875 STRIDE( SCREEN_MAIN, 876 BMPWIDTH_invadrox_numbers, 877 BMPHEIGHT_invadrox_numbers), 878 x + i * (NUMBERS_WIDTH + NUM_SPACING), y, 879 NUMBERS_WIDTH, FONT_HEIGHT); 880 } 881 /* Update lcd */ 882 rb->lcd_update_rect(x, y, 4 * NUMBERS_WIDTH + 3 * NUM_SPACING, FONT_HEIGHT); 883} 884 885 886static inline void draw_score(void) 887{ 888 draw_number(SCORENUM_X, SCORENUM_Y, score, 4); 889 if (score > hiscore.score) { 890 /* Draw new hiscore (same as score) */ 891 draw_number(HISCORENUM_X, SCORENUM_Y, score, 4); 892 } 893} 894 895 896static void draw_level(void) 897{ 898 draw_number(LEVEL_X + 2 * NUM_SPACING, PLAYFIELD_Y + 2, level, 2); 899} 900 901 902static void draw_lives(void) 903{ 904 int i; 905 /* Lives num */ 906 rb->lcd_bitmap_part(invadrox_numbers, lives * NUMBERS_WIDTH, 0, 907 STRIDE( SCREEN_MAIN, 908 BMPWIDTH_invadrox_numbers, 909 BMPHEIGHT_invadrox_numbers), 910 PLAYFIELD_X + LIVES_X, PLAYFIELD_Y + 2, 911 NUMBERS_WIDTH, FONT_HEIGHT); 912 913 /* Ships */ 914 for (i = 0; i < (lives - 1); i++) { 915 rb->lcd_bitmap_part(invadrox_ships, 0, 0, 916 STRIDE( SCREEN_MAIN, 917 BMPWIDTH_invadrox_ships, 918 BMPHEIGHT_invadrox_ships), 919 PLAYFIELD_X + LIVES_X + SHIP_WIDTH + i * (SHIP_WIDTH + NUM_SPACING), 920 PLAYFIELD_Y + 1, SHIP_WIDTH, SHIP_HEIGHT); 921 } 922 923 /* Erase ship to the right (if less than MAX_LIVES) */ 924 if (lives < MAX_LIVES) { 925 rb->lcd_fillrect(PLAYFIELD_X + LIVES_X + SHIP_WIDTH + i * (SHIP_WIDTH + NUM_SPACING), 926 PLAYFIELD_Y + 1, SHIP_WIDTH, SHIP_HEIGHT); 927 } 928 /* Update lives (and level) part of screen */ 929 rb->lcd_update_rect(PLAYFIELD_X + LIVES_X, PLAYFIELD_Y + 1, 930 PLAYFIELD_WIDTH - 2 * LIVES_X, MAX(FONT_HEIGHT + 1, SHIP_HEIGHT + 1)); 931} 932 933 934static inline void draw_aliens(void) 935{ 936 int i; 937 938 for (i = 0; i < 5 * ALIENS; i++) { 939 rb->lcd_bitmap_part(invadrox_aliens, aliens[i].x & 1 ? ALIEN_WIDTH : 0, 940 aliens[i].type * ALIEN_HEIGHT, 941 STRIDE( SCREEN_MAIN, 942 BMPWIDTH_invadrox_aliens, 943 BMPHEIGHT_invadrox_aliens), 944 PLAYFIELD_X + LIVES_X + aliens[i].x * ALIEN_SPEED, 945 ALIEN_START_Y + aliens[i].y * ALIEN_HEIGHT, 946 ALIEN_WIDTH, ALIEN_HEIGHT); 947 } 948} 949 950 951/* Return false if there is no next alive alien (round is over) */ 952static inline bool next_alien(void) 953{ 954 bool ret = true; 955 956 do { 957 curr_alien++; 958 if (curr_alien % ALIENS == 0) { 959 /* End of this row. Move up one row. */ 960 curr_alien -= 2 * ALIENS; 961 if (curr_alien < 0) { 962 /* No more aliens in this round. */ 963 curr_alien = 4 * ALIENS; 964 ret = false; 965 } 966 } 967 } while (aliens[curr_alien].state == DEAD && ret); 968 969 if (!ret) { 970 /* No more alive aliens. Round finished. */ 971 if (hit_right_border) { 972 if (hit_left_border) { 973 DBG("ERROR: both left and right borders are set (%d)\n", curr_alien); 974 } 975 /* Move down-left next round */ 976 aliens_right = false; 977 aliens_down = true; 978 hit_right_border = false; 979 } else if (hit_left_border) { 980 /* Move down-right next round */ 981 aliens_right = true; 982 aliens_down = true; 983 hit_left_border = false; 984 } else { 985 /* Not left nor right. Set down to false. */ 986 aliens_down = false; 987 } 988 } 989 990 return ret; 991} 992 993 994/* All aliens have been moved. 995 * Set curr_alien to first alive. 996 * Return false if no-one is left alive. 997 */ 998static bool first_alien(void) 999{ 1000 int i, y; 1001 1002 for (y = 4; y >= 0; y--) { 1003 for (i = y * ALIENS; i < (y + 1) * ALIENS; i++) { 1004 if (aliens[i].state != DEAD) { 1005 curr_alien = i; 1006 return true; 1007 } 1008 } 1009 } 1010 1011 /* All aliens dead. */ 1012 level_finished = true; 1013 1014 return false; 1015} 1016 1017 1018static bool move_aliens(void) 1019{ 1020 int x, y, old_x, old_y; 1021 1022 /* Move current alien (curr_alien is pointing to a living alien) */ 1023 1024 old_x = aliens[curr_alien].x; 1025 old_y = aliens[curr_alien].y; 1026 1027 if (aliens_down) { 1028 aliens[curr_alien].y++; 1029 if (aliens[curr_alien].y == MAX_Y) { 1030 /* Alien is at bottom. Game Over. */ 1031 DBG("Alien %d is at bottom. Game Over.\n", curr_alien); 1032 game_over = true; 1033 return false; 1034 } 1035 } 1036 1037 if (aliens_right) { 1038 /* Moving right */ 1039 if (aliens[curr_alien].x < MAX_X) { 1040 aliens[curr_alien].x++; 1041 } 1042 1043 /* Now, after move, check if we hit the right border. */ 1044 if (aliens[curr_alien].x == MAX_X) { 1045 hit_right_border = true; 1046 } 1047 1048 } else { 1049 /* Moving left */ 1050 if (aliens[curr_alien].x > 0) { 1051 aliens[curr_alien].x--; 1052 } 1053 1054 /* Now, after move, check if we hit the left border. */ 1055 if (aliens[curr_alien].x == 0) { 1056 hit_left_border = true; 1057 } 1058 } 1059 1060 /* Erase old position */ 1061 x = PLAYFIELD_X + LIVES_X + old_x * ALIEN_SPEED; 1062 y = ALIEN_START_Y + old_y * ALIEN_HEIGHT; 1063 if (aliens[curr_alien].y != old_y) { 1064 /* Moved in y-dir. Erase whole alien. */ 1065 rb->lcd_fillrect(x, y, ALIEN_WIDTH, ALIEN_HEIGHT); 1066 } else { 1067 if (aliens_right) { 1068 /* Erase left edge */ 1069 rb->lcd_fillrect(x, y, ALIEN_SPEED, ALIEN_HEIGHT); 1070 } else { 1071 /* Erase right edge */ 1072 x += ALIEN_WIDTH - ALIEN_SPEED; 1073 rb->lcd_fillrect(x, y, ALIEN_SPEED, ALIEN_HEIGHT); 1074 } 1075 } 1076 1077 /* Draw alien at new pos */ 1078 x = PLAYFIELD_X + LIVES_X + aliens[curr_alien].x * ALIEN_SPEED; 1079 y = ALIEN_START_Y + aliens[curr_alien].y * ALIEN_HEIGHT; 1080 rb->lcd_bitmap_part(invadrox_aliens, 1081 aliens[curr_alien].x & 1 ? ALIEN_WIDTH : 0, 1082 aliens[curr_alien].type * ALIEN_HEIGHT, 1083 STRIDE( SCREEN_MAIN, 1084 BMPWIDTH_invadrox_aliens, 1085 BMPHEIGHT_invadrox_aliens), 1086 x, y, ALIEN_WIDTH, ALIEN_HEIGHT); 1087 1088 if (!next_alien()) { 1089 /* Round finished. Set curr_alien to first alive from bottom. */ 1090 if (!first_alien()) { 1091 /* Should never happen. Taken care of in move_fire(). */ 1092 return false; 1093 } 1094 /* TODO: Play next background sound */ 1095 } 1096 1097 return true; 1098} 1099 1100 1101static inline void draw_ship(void) 1102{ 1103 /* Erase old ship */ 1104 if (old_ship_x < ship_x) { 1105 /* Move right. Erase leftmost part of ship. */ 1106 rb->lcd_fillrect(old_ship_x, SHIP_Y, ship_x - old_ship_x, SHIP_HEIGHT); 1107 } else if (old_ship_x > ship_x) { 1108 /* Move left. Erase rightmost part of ship. */ 1109 rb->lcd_fillrect(ship_x + SHIP_WIDTH, SHIP_Y, old_ship_x - ship_x, SHIP_HEIGHT); 1110 } 1111 1112 /* Draw ship */ 1113 rb->lcd_bitmap_part(invadrox_ships, 0, ship_frame * SHIP_HEIGHT, 1114 STRIDE( SCREEN_MAIN, 1115 BMPWIDTH_invadrox_ships, 1116 BMPHEIGHT_invadrox_ships), 1117 ship_x, SHIP_Y, SHIP_WIDTH, SHIP_HEIGHT); 1118 if (ship_hit) { 1119 /* Alternate between frame 1 and 2 during hit */ 1120 ship_frame_counter++; 1121 if (ship_frame_counter > 2) { 1122 ship_frame_counter = 0; 1123 ship_frame++; 1124 if (ship_frame > 2) { 1125 ship_frame = 1; 1126 } 1127 } 1128 } 1129 1130 /* Save ship_x for next time */ 1131 old_ship_x = ship_x; 1132} 1133 1134 1135static inline void fire_alpha(int xc, int yc, unsigned color) 1136{ 1137 int oldmode = rb->lcd_get_drawmode(); 1138 1139 rb->lcd_set_foreground(color); 1140 rb->lcd_set_drawmode(DRMODE_FG); 1141 1142 rb->lcd_mono_bitmap(invadrox_fire, xc - (FIRE_WIDTH/2), yc, FIRE_WIDTH, FIRE_HEIGHT); 1143 1144 rb->lcd_set_foreground(LCD_BLACK); 1145 rb->lcd_set_drawmode(oldmode); 1146} 1147 1148 1149static void move_fire(void) 1150{ 1151 bool hit_green = false; 1152 bool hit_white = false; 1153 int i, j; 1154 static int exploding_alien = -1; 1155 fb_data pix; 1156 1157 if (fire == S_IDLE) { 1158 return; 1159 } 1160 1161 /* Alien hit. Wait until explosion is finished. */ 1162 if (aliens_paralyzed < 0) { 1163 aliens_paralyzed++; 1164 if (aliens_paralyzed == 0) { 1165 /* Erase exploding_alien */ 1166 rb->lcd_fillrect(PLAYFIELD_X + LIVES_X + aliens[exploding_alien].x * ALIEN_SPEED, 1167 ALIEN_START_Y + aliens[exploding_alien].y * ALIEN_HEIGHT, 1168 ALIEN_EXPLODE_WIDTH, ALIEN_HEIGHT); 1169 fire = S_IDLE; 1170 /* Special case. We killed curr_alien. */ 1171 if (exploding_alien == curr_alien) { 1172 if (!next_alien()) { 1173 /* Round finished. Set curr_alien to first alive from bottom. */ 1174 first_alien(); 1175 } 1176 } 1177 } 1178 return; 1179 } 1180 1181 if (fire == S_ACTIVE) { 1182 1183 /* Erase */ 1184 rb->lcd_vline(fire_x, fire_y, fire_y + SHOT_HEIGHT); 1185 1186 /* Check top */ 1187 if (fire_y <= SCORENUM_Y + FONT_HEIGHT + 4) { 1188 1189 /* TODO: Play explode sound */ 1190 1191 fire = S_EXPLODE; 1192 fire_target = TARGET_TOP; 1193 fire_alpha(fire_x, fire_y, UFO_RED); 1194 return; 1195 } 1196 1197 /* Move */ 1198 fire_y -= FIRE_SPEED; 1199 1200 /* Hit UFO? */ 1201 if (ufo_state == S_ACTIVE) { 1202 if ((ABS(ufo_x + UFO_WIDTH / 2 - fire_x) <= UFO_WIDTH / 2) && 1203 (fire_y <= UFO_Y + UFO_HEIGHT)) { 1204 ufo_state = S_EXPLODE; 1205 fire = S_EXPLODE; 1206 fire_target = TARGET_UFO; 1207 /* Center explosion */ 1208 ufo_x -= (UFO_EXPLODE_WIDTH - UFO_WIDTH) / 2; 1209 rb->lcd_bitmap(invadrox_ufo_explode, ufo_x, UFO_Y - 1, 1210 UFO_EXPLODE_WIDTH, UFO_EXPLODE_HEIGHT); 1211 return; 1212 } 1213 } 1214 1215 /* Hit bomb? (check position, not pixel value) */ 1216 for (i = 0; i < max_bombs; i++) { 1217 if (bombs[i].state == S_ACTIVE) { 1218 /* Count as hit if within BOMB_WIDTH pixels */ 1219 if ((ABS(bombs[i].x - fire_x) < BOMB_WIDTH) && 1220 (fire_y - bombs[i].y < BOMB_HEIGHT)) { 1221 /* Erase bomb */ 1222 rb->lcd_fillrect(bombs[i].x, bombs[i].y, BOMB_WIDTH, BOMB_HEIGHT); 1223 bombs[i].state = S_IDLE; 1224 /* Explode ship fire */ 1225 fire = S_EXPLODE; 1226 fire_target = TARGET_SHIELD; 1227 fire_alpha(fire_x, fire_y, LCD_WHITE); 1228 return; 1229 } 1230 } 1231 } 1232 1233 /* Check for hit*/ 1234 for (i = FIRE_SPEED; i >= 0; i--) { 1235 pix = get_pixel(fire_x, fire_y + i); 1236 if(!memcmp(&pix, &screen_white, sizeof(fb_data))) { 1237 hit_white = true; 1238 fire_y += i; 1239 break; 1240 } 1241 if(!memcmp(&pix, &screen_green, sizeof(fb_data))) { 1242 hit_green = true; 1243 fire_y += i; 1244 break; 1245 } 1246 } 1247 1248 if (hit_green) { 1249 /* Hit shield */ 1250 1251 /* TODO: Play explode sound */ 1252 1253 fire = S_EXPLODE; 1254 fire_target = TARGET_SHIELD; 1255 /* Center explosion around hit pixel */ 1256 fire_y -= FIRE_HEIGHT / 2; 1257 fire_alpha(fire_x, fire_y, SLIME_GREEN); 1258 return; 1259 } 1260 1261 if (hit_white) { 1262 1263 /* Hit alien? */ 1264 for (i = 0; i < 5 * ALIENS; i++) { 1265 if (aliens[i].state != DEAD && 1266 (ABS(fire_x - (PLAYFIELD_X + LIVES_X + aliens[i].x * ALIEN_SPEED + 1267 ALIEN_WIDTH / 2)) <= ALIEN_WIDTH / 2) && 1268 (ABS(fire_y - (ALIEN_START_Y + aliens[i].y * ALIEN_HEIGHT + 1269 ALIEN_HEIGHT / 2)) <= ALIEN_HEIGHT / 2)) { 1270 1271 /* TODO: play alien hit sound */ 1272 1273 if (aliens[i].state == BOMBER) { 1274 /* Set (possible) alien above to bomber */ 1275 for (j = i - ALIENS; j >= 0; j -= ALIENS) { 1276 if (aliens[j].state != DEAD) { 1277 /* printf("New bomber (%d, %d)\n", j % ALIENS, j / ALIENS); */ 1278 aliens[j].state = BOMBER; 1279 break; 1280 } 1281 } 1282 } 1283 aliens[i].state = DEAD; 1284 exploding_alien = i; 1285 score += scores[aliens[i].type]; 1286 draw_score(); 1287 /* Update score part of screen */ 1288 rb->lcd_update_rect(SCORENUM_X, SCORENUM_Y, 1289 PLAYFIELD_WIDTH - 2 * NUMBERS_WIDTH, FONT_HEIGHT); 1290 1291 /* Paralyze aliens S_EXPLODE frames */ 1292 aliens_paralyzed = S_EXPLODE; 1293 rb->lcd_bitmap(invadrox_alien_explode, 1294 PLAYFIELD_X + LIVES_X + aliens[i].x * ALIEN_SPEED, 1295 ALIEN_START_Y + aliens[i].y * ALIEN_HEIGHT, 1296 ALIEN_EXPLODE_WIDTH, ALIEN_EXPLODE_HEIGHT); 1297 /* Since alien is 1 pixel taller than explosion sprite, erase bottom line */ 1298 rb->lcd_hline(PLAYFIELD_X + LIVES_X + aliens[i].x * ALIEN_SPEED, 1299 PLAYFIELD_X + LIVES_X + aliens[i].x * ALIEN_SPEED + ALIEN_WIDTH, 1300 ALIEN_START_Y + (aliens[i].y + 1) * ALIEN_HEIGHT - 1); 1301 return; 1302 } 1303 } 1304 } 1305 1306 /* Draw shot */ 1307 rb->lcd_set_foreground(LCD_WHITE); 1308 rb->lcd_vline(fire_x, fire_y, fire_y + SHOT_HEIGHT); 1309 rb->lcd_set_foreground(LCD_BLACK); 1310 } else if (fire < S_IDLE) { 1311 /* Count up towards S_IDLE, then erase explosion */ 1312 fire++; 1313 if (fire == S_IDLE) { 1314 /* Erase explosion */ 1315 if (fire_target == TARGET_TOP) { 1316 rb->lcd_fillrect(fire_x - (FIRE_WIDTH / 2), fire_y, FIRE_WIDTH, FIRE_HEIGHT); 1317 } else if (fire_target == TARGET_SHIELD) { 1318 /* Draw explosion with black pixels */ 1319 fire_alpha(fire_x, fire_y, LCD_BLACK); 1320 } 1321 } 1322 } 1323} 1324 1325 1326/* Return a BOMBER alien */ 1327static inline int random_bomber(void) 1328{ 1329 int i, col; 1330 1331 /* TODO: Weigh higher probability near ship */ 1332 col = rb->rand() % ALIENS; 1333 for (i = col + 4 * ALIENS; i >= 0; i -= ALIENS) { 1334 if (aliens[i].state == BOMBER) { 1335 return i; 1336 } 1337 } 1338 1339 /* No BOMBER found in this col */ 1340 1341 for (i = 0; i < 5 * ALIENS; i++) { 1342 if (aliens[i].state == BOMBER) { 1343 return i; 1344 } 1345 } 1346 1347 /* No BOMBER found at all (error?) */ 1348 1349 return -1; 1350} 1351 1352 1353static inline void draw_bomb(int i) 1354{ 1355 rb->lcd_bitmap_part(invadrox_bombs, bombs[i].type * BOMB_WIDTH, 1356 bombs[i].frame * BOMB_HEIGHT, 1357 STRIDE( SCREEN_MAIN, 1358 BMPWIDTH_invadrox_bombs, 1359 BMPHEIGHT_invadrox_bombs), 1360 bombs[i].x, bombs[i].y, 1361 BOMB_WIDTH, BOMB_HEIGHT); 1362 /* Advance frame */ 1363 bombs[i].frame++; 1364 if (bombs[i].frame == bombs[i].frames) { 1365 bombs[i].frame = 0; 1366 } 1367} 1368 1369 1370static void move_bombs(void) 1371{ 1372 int i, j, bomber; 1373 bool abort; 1374 1375 for (i = 0; i < max_bombs; i++) { 1376 1377 switch (bombs[i].state) { 1378 1379 case S_IDLE: 1380 if (ship_hit) { 1381 continue; 1382 } 1383 bomber = random_bomber(); 1384 if (bomber < 0) { 1385 DBG("ERROR: No bomber available\n"); 1386 continue; 1387 } 1388 /* x, y */ 1389 bombs[i].x = PLAYFIELD_X + LIVES_X + aliens[bomber].x * ALIEN_SPEED + ALIEN_WIDTH / 2; 1390 bombs[i].y = ALIEN_START_Y + (aliens[bomber].y + 1) * ALIEN_HEIGHT; 1391 1392 /* Check for duplets in x and y direction */ 1393 abort = false; 1394 for (j = i - 1; j >= 0; j--) { 1395 if ((bombs[j].state == S_ACTIVE) && 1396 ((bombs[i].x == bombs[j].x) || (bombs[i].y == bombs[j].y))) { 1397 abort = true; 1398 break; 1399 } 1400 } 1401 if (abort) { 1402 /* Skip this one, continue with next bomb */ 1403 /* printf("Bomb %d duplet of %d\n", i, j); */ 1404 continue; 1405 } 1406 1407 /* Passed, set type */ 1408 bombs[i].type = rb->rand() % 3; 1409 bombs[i].frame = 0; 1410 if (bombs[i].type == 0) { 1411 bombs[i].frames = 3; 1412 } else if (bombs[i].type == 1) { 1413 bombs[i].frames = 4; 1414 } else { 1415 bombs[i].frames = 6; 1416 } 1417 1418 /* Bombs away */ 1419 bombs[i].state = S_ACTIVE; 1420 draw_bomb(i); 1421 continue; 1422 1423 break; 1424 1425 case S_ACTIVE: 1426 /* Erase old position */ 1427 rb->lcd_fillrect(bombs[i].x, bombs[i].y, BOMB_WIDTH, BOMB_HEIGHT); 1428 1429 /* Move */ 1430 bombs[i].y += BOMB_SPEED; 1431 1432 /* Check if bottom hit */ 1433 if (bombs[i].y + BOMB_HEIGHT >= PLAYFIELD_Y) { 1434 bombs[i].y = PLAYFIELD_Y - FIRE_HEIGHT + 1; 1435 fire_alpha(bombs[i].x, bombs[i].y, LCD_WHITE); 1436 bombs[i].state = S_EXPLODE; 1437 bombs[i].target = TARGET_BOTTOM; 1438 break; 1439 } 1440 1441 /* Check for green (ship or shield) */ 1442 for (j = BOMB_HEIGHT; j >= BOMB_HEIGHT - BOMB_SPEED; j--) { 1443 bombs[i].target = 0; 1444 fb_data pix = get_pixel(bombs[i].x + BOMB_WIDTH / 2, bombs[i].y + j); 1445 if(!memcmp(&pix, &screen_green, sizeof(fb_data))) { 1446 /* Move to hit pixel */ 1447 bombs[i].x += BOMB_WIDTH / 2; 1448 bombs[i].y += j; 1449 1450 /* Check if ship is hit */ 1451 if (bombs[i].y > SHIELD_Y + SHIELD_HEIGHT && bombs[i].y < PLAYFIELD_Y) { 1452 1453 /* TODO: play ship hit sound */ 1454 1455 ship_hit = true; 1456 ship_frame = 1; 1457 ship_frame_counter = 0; 1458 bombs[i].state = S_EXPLODE * 4; 1459 bombs[i].target = TARGET_SHIP; 1460 rb->lcd_bitmap_part(invadrox_ships, 0, 1 * SHIP_HEIGHT, 1461 STRIDE( SCREEN_MAIN, 1462 BMPWIDTH_invadrox_ships, 1463 BMPHEIGHT_invadrox_ships), 1464 ship_x, SHIP_Y, 1465 SHIP_WIDTH, SHIP_HEIGHT); 1466 break; 1467 } 1468 /* Shield hit */ 1469 bombs[i].state = S_EXPLODE; 1470 bombs[i].target = TARGET_SHIELD; 1471 /* Center explosion around hit pixel in shield */ 1472 bombs[i].y -= FIRE_HEIGHT / 2; 1473 fire_alpha(bombs[i].x, bombs[i].y, SLIME_GREEN); 1474 break; 1475 } 1476 } 1477 1478 if (bombs[i].target != 0) { 1479 /* Hit ship or shield, continue */ 1480 continue; 1481 } 1482 1483 draw_bomb(i); 1484 break; 1485 1486 default: 1487 /* If we get here state should be < 0, exploding */ 1488 bombs[i].state++; 1489 if (bombs[i].state == S_IDLE) { 1490 if (ship_hit) { 1491 /* Erase explosion */ 1492 rb->lcd_fillrect(ship_x, SHIP_Y, SHIP_WIDTH, SHIP_HEIGHT); 1493 rb->lcd_update_rect(ship_x, SHIP_Y, SHIP_WIDTH, SHIP_HEIGHT); 1494 ship_hit = false; 1495 ship_frame = 0; 1496 ship_x = PLAYFIELD_X + 2 * LIVES_X; 1497 lives--; 1498 if (lives == 0) { 1499 game_over = true; 1500 return; 1501 } 1502 draw_lives(); 1503 /* Sleep 1s to give player time to examine lives left */ 1504 rb->sleep(HZ); 1505 } 1506 /* Erase explosion (even if ship hit, might be another bomb) */ 1507 fire_alpha(bombs[i].x, bombs[i].y, LCD_BLACK); 1508 } 1509 break; 1510 } 1511 } 1512} 1513 1514 1515static inline void move_ship(void) 1516{ 1517 ship_dir += ship_acc; 1518 if (ship_dir > max_ship_speed) { 1519 ship_dir = max_ship_speed; 1520 } 1521 if (ship_dir < -max_ship_speed) { 1522 ship_dir = -max_ship_speed; 1523 } 1524 ship_x += ship_dir; 1525 if (ship_x < SHIP_MIN_X) { 1526 ship_x = SHIP_MIN_X; 1527 } 1528 if (ship_x > SHIP_MAX_X) { 1529 ship_x = SHIP_MAX_X; 1530 } 1531 1532 draw_ship(); 1533} 1534 1535 1536/* Unidentified Flying Object */ 1537static void move_ufo(void) 1538{ 1539 static int ufo_speed; 1540 static int counter; 1541 int mystery_score; 1542 1543 switch (ufo_state) { 1544 1545 case S_IDLE: 1546 1547 if (rb->rand() % 500 == 0) { 1548 /* Uh-oh, it's time to launch a mystery UFO */ 1549 1550 /* TODO: Play UFO sound */ 1551 1552 if (rb->rand() % 2) { 1553 ufo_speed = UFO_SPEED; 1554 ufo_x = PLAYFIELD_X; 1555 } else { 1556 ufo_speed = -UFO_SPEED; 1557 ufo_x = LCD_WIDTH - PLAYFIELD_X - UFO_WIDTH; 1558 } 1559 ufo_state = S_ACTIVE; 1560 /* UFO will be drawn next frame */ 1561 } 1562 break; 1563 1564 case S_ACTIVE: 1565 /* Erase old pos */ 1566 rb->lcd_fillrect(ufo_x, UFO_Y, UFO_WIDTH, UFO_HEIGHT); 1567 /* Move */ 1568 ufo_x += ufo_speed; 1569 /* Check bounds */ 1570 if (ufo_x < PLAYFIELD_X || ufo_x > LCD_WIDTH - PLAYFIELD_X - UFO_WIDTH) { 1571 ufo_state = S_IDLE; 1572 break; 1573 } 1574 /* Draw new pos */ 1575 rb->lcd_bitmap(invadrox_ufo, ufo_x, UFO_Y, UFO_WIDTH, UFO_HEIGHT); 1576 break; 1577 1578 case S_SHOWSCORE: 1579 counter++; 1580 if (counter == S_IDLE) { 1581 /* Erase mystery number */ 1582 rb->lcd_fillrect(ufo_x, UFO_Y, 3 * NUMBERS_WIDTH + 2 * NUM_SPACING, FONT_HEIGHT); 1583 ufo_state = S_IDLE; 1584 } 1585 break; 1586 1587 default: 1588 /* Exploding */ 1589 ufo_state++; 1590 if (ufo_state == S_IDLE) { 1591 /* Erase explosion */ 1592 rb->lcd_fillrect(ufo_x, UFO_Y - 1, UFO_EXPLODE_WIDTH, UFO_EXPLODE_HEIGHT); 1593 ufo_state = S_SHOWSCORE; 1594 counter = S_EXPLODE * 4; 1595 /* Draw mystery_score, sleep, increase score and continue */ 1596 mystery_score = 50 + (rb->rand() % 6) * 50; 1597 if (mystery_score < 100) { 1598 draw_number(ufo_x, UFO_Y, mystery_score, 2); 1599 } else { 1600 draw_number(ufo_x, UFO_Y, mystery_score, 3); 1601 } 1602 score += mystery_score; 1603 draw_score(); 1604 } 1605 break; 1606 } 1607} 1608 1609 1610static void draw_background(void) 1611{ 1612 1613 rb->lcd_bitmap(invadrox_background, 0, 0, LCD_WIDTH, LCD_HEIGHT); 1614 rb->lcd_update(); 1615} 1616 1617 1618static void new_level(void) 1619{ 1620 int i; 1621 1622 draw_background(); 1623 /* Give an extra life for each new level */ 1624 if (lives < MAX_LIVES) { 1625 lives++; 1626 } 1627 draw_lives(); 1628 1629 /* Score */ 1630 draw_score(); 1631 draw_number(HISCORENUM_X, SCORENUM_Y, hiscore.score, 4); 1632 1633 level++; 1634 draw_level(); 1635 level_finished = false; 1636 1637 ufo_state = S_IDLE; 1638 1639 /* Init alien positions and states */ 1640 for (i = 0; i < 4 * ALIENS; i++) { 1641 aliens[i].x = 0 + (i % ALIENS) * ((ALIEN_WIDTH + ALIEN_SPACING) / ALIEN_SPEED); 1642 aliens[i].y = 2 * (i / ALIENS); 1643 aliens[i].state = ALIVE; 1644 } 1645 /* Last row, bombers */ 1646 for (i = 4 * ALIENS; i < 5 * ALIENS; i++) { 1647 aliens[i].x = 0 + (i % ALIENS) * ((ALIEN_WIDTH + ALIEN_SPACING) / ALIEN_SPEED); 1648 aliens[i].y = 2 * (i / ALIENS); 1649 aliens[i].state = BOMBER; 1650 } 1651 1652 /* Init bombs to inactive (S_IDLE) */ 1653 for (i = 0; i < MAX_BOMBS; i++) { 1654 bombs[i].state = S_IDLE; 1655 } 1656 1657 /* Start aliens closer to earth from level 2 */ 1658 for (i = 0; i < 5 * ALIENS; i++) { 1659 if (level < 6) { 1660 aliens[i].y += level - 1; 1661 } else { 1662 aliens[i].y += 5; 1663 } 1664 } 1665 1666 /* Max concurrent bombs */ 1667 max_bombs = 1; 1668 1669 gamespeed = 2; 1670 1671 if (level > 1) { 1672 max_bombs++; 1673 } 1674 1675 /* Increase speed */ 1676 if (level > 2) { 1677 gamespeed++; 1678 } 1679 1680 if (level > 3) { 1681 max_bombs++; 1682 } 1683 1684 /* Increase speed more */ 1685 if (level > 4) { 1686 gamespeed++; 1687 } 1688 1689 if (level > 5) { 1690 max_bombs++; 1691 } 1692 1693 /* 4 shields */ 1694 for (i = 1; i <= 4; i++) { 1695 rb->lcd_bitmap(invadrox_shield, 1696 PLAYFIELD_X + i * PLAYFIELD_WIDTH / 5 - SHIELD_WIDTH / 2, 1697 SHIELD_Y, SHIELD_WIDTH, SHIELD_HEIGHT); 1698 } 1699 1700 /* Bottom line */ 1701 rb->lcd_set_foreground(SLIME_GREEN); 1702 rb->lcd_hline(PLAYFIELD_X, LCD_WIDTH - PLAYFIELD_X, PLAYFIELD_Y); 1703 /* Restore foreground to black (for fast erase later). */ 1704 rb->lcd_set_foreground(LCD_BLACK); 1705 1706 ship_x = PLAYFIELD_X + 2 * LIVES_X; 1707 if (level == 1) { 1708 old_ship_x = ship_x; 1709 } 1710 ship_dir = 0; 1711 ship_acc = 0; 1712 ship_frame = 0; 1713 ship_hit = false; 1714 fire = S_IDLE; 1715 /* Start moving the bottom row left to right */ 1716 curr_alien = 4 * ALIENS; 1717 aliens_paralyzed = 0; 1718 aliens_right = true; 1719 aliens_down = false; 1720 hit_left_border = false; 1721 hit_right_border = false; 1722 /* TODO: Change max_ship_speed to 3 at higher levels */ 1723 max_ship_speed = 2; 1724 1725 draw_aliens(); 1726 1727 rb->lcd_update(); 1728} 1729 1730 1731static void init_invadrox(void) 1732{ 1733 int i; 1734 1735 /* Seed random number generator with a "random" number */ 1736 rb->srand(rb->get_time()->tm_sec + rb->get_time()->tm_min * 60); 1737 1738 /* Precalculate start of each scanline */ 1739 for (i = 0; i < LCD_HEIGHT; i++) { 1740#if (LCD_DEPTH >= 8) 1741 ytab[i] = i * LCD_WIDTH; 1742#elif (LCD_DEPTH == 2) && (LCD_PIXELFORMAT == HORIZONTAL_PACKING) 1743 ytab[i] = i * (LCD_WIDTH / 4); 1744#elif (LCD_DEPTH == 2) && (LCD_PIXELFORMAT == VERTICAL_PACKING) 1745 ytab[i] = (i / 4) * LCD_WIDTH; 1746#else 1747 #error pixelformat not implemented yet 1748#endif 1749 } 1750 1751 rb->lcd_set_background(LCD_BLACK); 1752 rb->lcd_set_foreground(LCD_BLACK); 1753 1754 if (highscore_load(HISCOREFILE, &hiscore, 1) < 0) { 1755 /* Init hiscore to 0 */ 1756 rb->strlcpy(hiscore.name, "Invader", sizeof(hiscore.name)); 1757 hiscore.score = 0; 1758 hiscore.level = 1; 1759 } 1760 1761 /* Init alien types in aliens array */ 1762 for (i = 0; i < 1 * ALIENS; i++) { 1763 aliens[i].type = 0; /* Kang */ 1764 } 1765 for (; i < 3 * ALIENS; i++) { 1766 aliens[i].type = 1; /* Kodos */ 1767 } 1768 for (; i < 5 * ALIENS; i++) { 1769 aliens[i].type = 2; /* Serak */ 1770 } 1771 1772 1773 /* Save screen white color */ 1774 rb->lcd_set_foreground(LCD_WHITE); 1775 rb->lcd_drawpixel(0, 0); 1776 rb->lcd_update_rect(0, 0, 1, 1); 1777 screen_white = get_pixel(0, 0); 1778 1779 /* Save screen green color */ 1780 rb->lcd_set_foreground(SLIME_GREEN); 1781 rb->lcd_drawpixel(0, 0); 1782 rb->lcd_update_rect(0, 0, 1, 1); 1783 screen_green = get_pixel(0, 0); 1784 1785 /* Restore black foreground */ 1786 rb->lcd_set_foreground(LCD_BLACK); 1787 1788 new_level(); 1789 1790 /* Flash score at start */ 1791 for (i = 0; i < 5; i++) { 1792 rb->lcd_fillrect(SCORENUM_X, SCORENUM_Y, 1793 4 * NUMBERS_WIDTH + 3 * NUM_SPACING, 1794 FONT_HEIGHT); 1795 rb->lcd_update_rect(SCORENUM_X, SCORENUM_Y, 1796 4 * NUMBERS_WIDTH + 3 * NUM_SPACING, 1797 FONT_HEIGHT); 1798 rb->sleep(HZ / 10); 1799 draw_number(SCORENUM_X, SCORENUM_Y, score, 4); 1800 rb->sleep(HZ / 10); 1801 } 1802} 1803 1804 1805static inline bool handle_buttons(void) 1806{ 1807 static unsigned int oldbuttonstate = 0; 1808 1809 unsigned int released, pressed, newbuttonstate; 1810 1811 if (ship_hit) { 1812 /* Don't allow ship movement during explosion */ 1813 newbuttonstate = 0; 1814 } else { 1815 newbuttonstate = rb->button_status(); 1816 } 1817 if(newbuttonstate == oldbuttonstate) { 1818 if (newbuttonstate == 0) { 1819 /* No button pressed. Stop ship. */ 1820 ship_acc = 0; 1821 if (ship_dir > 0) { 1822 ship_dir--; 1823 } 1824 if (ship_dir < 0) { 1825 ship_dir++; 1826 } 1827 } 1828 /* return false; */ 1829 goto check_usb; 1830 } 1831 released = ~newbuttonstate & oldbuttonstate; 1832 pressed = newbuttonstate & ~oldbuttonstate; 1833 oldbuttonstate = newbuttonstate; 1834 if (pressed) { 1835 if (pressed & ACTION_LEFT) { 1836 if (ship_acc > -1) { 1837 ship_acc--; 1838 } 1839 } 1840 if (pressed & ACTION_RIGHT) { 1841 if (ship_acc < 1) { 1842 ship_acc++; 1843 } 1844 } 1845 if (pressed & ACTION_FIRE) { 1846 if (fire == S_IDLE) { 1847 /* Fire shot */ 1848 fire_x = ship_x + SHIP_WIDTH / 2; 1849 fire_y = SHIP_Y - SHOT_HEIGHT; 1850 fire = S_ACTIVE; 1851 /* TODO: play fire sound */ 1852 } 1853 } 1854 if (pressed & ACTION_QUIT) { 1855 rb->splash(HZ * 1, "Quit"); 1856 return true; 1857 } 1858 } 1859 if (released) { 1860 if ((released & ACTION_LEFT)) { 1861 if (ship_acc < 1) { 1862 ship_acc++; 1863 } 1864 } 1865 if ((released & ACTION_RIGHT)) { 1866 if (ship_acc > -1) { 1867 ship_acc--; 1868 } 1869 } 1870 } 1871 1872check_usb: 1873 1874 /* Quit if USB is connected */ 1875 if (rb->button_get(false) == SYS_USB_CONNECTED) { 1876 return true; 1877 } 1878 1879 return false; 1880} 1881 1882 1883static void game_loop(void) 1884{ 1885 int i, end; 1886 1887 /* Print dimensions (just for debugging) */ 1888 DBG("%03dx%03dx%02d\n", LCD_WIDTH, LCD_HEIGHT, LCD_DEPTH); 1889 1890 /* Init */ 1891 init_invadrox(); 1892 1893 while (1) { 1894 /* Convert CYCLETIME (in ms) to HZ */ 1895 end = *rb->current_tick + (CYCLETIME * HZ) / 1000; 1896 1897 if (handle_buttons()) { 1898 return; 1899 } 1900 1901 /* Animate */ 1902 move_ship(); 1903 move_fire(); 1904 1905 /* Check if level is finished (marked by move_fire) */ 1906 if (level_finished) { 1907 /* TODO: Play level finished sound */ 1908 new_level(); 1909 } 1910 1911 move_ufo(); 1912 1913 /* Move aliens */ 1914 if (!aliens_paralyzed && !ship_hit) { 1915 for (i = 0; i < gamespeed; i++) { 1916 if (!move_aliens()) { 1917 if (game_over) { 1918 return; 1919 } 1920 } 1921 } 1922 } 1923 1924 /* Move alien bombs */ 1925 move_bombs(); 1926 if (game_over) { 1927 return; 1928 } 1929 1930 /* Update "playfield" rect */ 1931 rb->lcd_update_rect(PLAYFIELD_X, SCORENUM_Y + FONT_HEIGHT, 1932 PLAYFIELD_WIDTH, 1933 PLAYFIELD_Y + 1 - SCORENUM_Y - FONT_HEIGHT); 1934 1935 /* Wait until next frame */ 1936 DBG("%ld (%d)\n", end - *rb->current_tick, (CYCLETIME * HZ) / 1000); 1937 if (TIME_BEFORE(*rb->current_tick, end)) { 1938 rb->sleep(end - *rb->current_tick); 1939 } else { 1940 rb->yield(); 1941 } 1942 1943 } /* end while */ 1944} 1945 1946 1947/* this is the plugin entry point */ 1948enum plugin_status plugin_start(UNUSED const void* parameter) 1949{ 1950 rb->lcd_setfont(FONT_SYSFIXED); 1951 /* Turn off backlight timeout */ 1952#ifdef HAVE_BACKLIGHT 1953 backlight_ignore_timeout(); 1954#endif 1955 struct viewport *vp_main = rb->lcd_set_viewport(NULL); 1956 lcd_fb = vp_main->buffer->fb_ptr; 1957 /* now go ahead and have fun! */ 1958 game_loop(); 1959 1960 /* Game Over. */ 1961 /* TODO: Play game over sound */ 1962 rb->splash(HZ * 2, "Game Over"); 1963 if (score > hiscore.score) { 1964 /* Save new hiscore */ 1965 highscore_update(score, level, "Invader", &hiscore, 1); 1966 highscore_save(HISCOREFILE, &hiscore, 1); 1967 } 1968 1969 /* Restore user's original backlight setting */ 1970 rb->lcd_setfont(FONT_UI); 1971 /* Turn on backlight timeout (revert to settings) */ 1972#ifdef HAVE_BACKLIGHT 1973 backlight_use_settings(); 1974#endif 1975 return PLUGIN_OK; 1976} 1977 1978 1979 1980/** 1981 * GNU Emacs settings: Kernighan & Richie coding style with 1982 * 4 spaces indent and no tabs. 1983 * Local Variables: 1984 * c-file-style: "k&r" 1985 * c-basic-offset: 4 1986 * indent-tabs-mode: nil 1987 * End: 1988 */