841cffab5b
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@24634 a1c6a512-1295-4272-9138-f99709370657
1835 lines
52 KiB
C
1835 lines
52 KiB
C
/***************************************************************************
|
|
* __________ __ ___.
|
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
|
* \/ \/ \/ \/ \/
|
|
* $Id$
|
|
*
|
|
* Copyright (C) 2006 Albert Veli
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
|
* KIND, either express or implied.
|
|
*
|
|
****************************************************************************/
|
|
|
|
/* Improvised creds goes to:
|
|
*
|
|
* - Anders Clausen for ingeniously inventing the name Invadrox.
|
|
* - Linus Nielsen-Feltzing for patiently answering n00b questions.
|
|
*/
|
|
|
|
#include "plugin.h"
|
|
#include "lib/highscore.h"
|
|
#include "lib/helper.h"
|
|
|
|
/* bitmaps */
|
|
#include "pluginbitmaps/invadrox_background.h"
|
|
|
|
/* get dimensions for later use from the bitmaps */
|
|
#include "pluginbitmaps/invadrox_aliens.h"
|
|
#include "pluginbitmaps/invadrox_ships.h"
|
|
#include "pluginbitmaps/invadrox_bombs.h"
|
|
#include "pluginbitmaps/invadrox_alien_explode.h"
|
|
#include "pluginbitmaps/invadrox_shield.h"
|
|
#include "pluginbitmaps/invadrox_ufo.h"
|
|
#include "pluginbitmaps/invadrox_ufo_explode.h"
|
|
#include "pluginbitmaps/invadrox_numbers.h"
|
|
#include "pluginbitmaps/invadrox_fire.h"
|
|
#define ALIEN_WIDTH (BMPWIDTH_invadrox_aliens/2)
|
|
#define ALIEN_HEIGHT (BMPHEIGHT_invadrox_aliens/3)
|
|
#define SHIP_WIDTH BMPWIDTH_invadrox_ships
|
|
#define SHIP_HEIGHT (BMPHEIGHT_invadrox_ships/3)
|
|
#define BOMB_WIDTH (BMPWIDTH_invadrox_bombs/3)
|
|
#define BOMB_HEIGHT (BMPHEIGHT_invadrox_bombs/6)
|
|
#define ALIEN_EXPLODE_WIDTH BMPWIDTH_invadrox_alien_explode
|
|
#define ALIEN_EXPLODE_HEIGHT BMPHEIGHT_invadrox_alien_explode
|
|
#define SHIELD_WIDTH BMPWIDTH_invadrox_shield
|
|
#define SHIELD_HEIGHT BMPHEIGHT_invadrox_shield
|
|
#define UFO_WIDTH BMPWIDTH_invadrox_ufo
|
|
#define UFO_HEIGHT BMPHEIGHT_invadrox_ufo
|
|
#define UFO_EXPLODE_WIDTH BMPWIDTH_invadrox_ufo_explode
|
|
#define UFO_EXPLODE_HEIGHT BMPHEIGHT_invadrox_ufo_explode
|
|
#define NUMBERS_WIDTH (BMPWIDTH_invadrox_numbers/10)
|
|
#define FONT_HEIGHT BMPHEIGHT_invadrox_numbers
|
|
#define FIRE_WIDTH BMPWIDTH_invadrox_fire
|
|
#define FIRE_HEIGHT BMPHEIGHT_invadrox_fire
|
|
|
|
PLUGIN_HEADER
|
|
|
|
/* Original graphics is only 1bpp so it should be portable
|
|
* to most targets. But for now, only support the simple ones.
|
|
*/
|
|
#ifndef HAVE_LCD_BITMAP
|
|
#error INVADROX: Unsupported LCD
|
|
#endif
|
|
|
|
#if (LCD_DEPTH < 2)
|
|
#error INVADROX: Unsupported LCD
|
|
#endif
|
|
|
|
/* #define DEBUG */
|
|
#ifdef DEBUG
|
|
#define DBG(format, arg...) { DEBUGF("%s: " format, __FUNCTION__, ## arg); }
|
|
#else
|
|
#define DBG(format, arg...) {}
|
|
#endif
|
|
|
|
#if CONFIG_KEYPAD == IRIVER_H100_PAD
|
|
|
|
#define QUIT BUTTON_OFF
|
|
#define LEFT BUTTON_LEFT
|
|
#define RIGHT BUTTON_RIGHT
|
|
#define FIRE BUTTON_ON
|
|
|
|
#elif CONFIG_KEYPAD == IRIVER_H300_PAD
|
|
|
|
#define QUIT BUTTON_OFF
|
|
#define LEFT BUTTON_LEFT
|
|
#define RIGHT BUTTON_RIGHT
|
|
#define FIRE BUTTON_SELECT
|
|
|
|
#elif (CONFIG_KEYPAD == IRIVER_H10_PAD)
|
|
|
|
#define QUIT BUTTON_POWER
|
|
#define LEFT BUTTON_LEFT
|
|
#define RIGHT BUTTON_RIGHT
|
|
#define FIRE BUTTON_PLAY
|
|
|
|
#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
|
|
(CONFIG_KEYPAD == IPOD_3G_PAD) || \
|
|
(CONFIG_KEYPAD == IPOD_1G2G_PAD)
|
|
|
|
#define QUIT BUTTON_MENU
|
|
#define LEFT BUTTON_LEFT
|
|
#define RIGHT BUTTON_RIGHT
|
|
#define FIRE BUTTON_SELECT
|
|
|
|
#elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
|
|
|
|
#define QUIT BUTTON_POWER
|
|
#define LEFT BUTTON_LEFT
|
|
#define RIGHT BUTTON_RIGHT
|
|
#define FIRE BUTTON_SELECT
|
|
|
|
#elif CONFIG_KEYPAD == GIGABEAT_PAD
|
|
|
|
#define QUIT BUTTON_POWER
|
|
#define LEFT BUTTON_LEFT
|
|
#define RIGHT BUTTON_RIGHT
|
|
#define FIRE BUTTON_SELECT
|
|
|
|
#elif CONFIG_KEYPAD == SANSA_E200_PAD
|
|
|
|
#define QUIT BUTTON_POWER
|
|
#define LEFT BUTTON_LEFT
|
|
#define RIGHT BUTTON_RIGHT
|
|
#define FIRE BUTTON_SELECT
|
|
|
|
#elif CONFIG_KEYPAD == SANSA_FUZE_PAD
|
|
|
|
#define QUIT (BUTTON_HOME|BUTTON_REPEAT)
|
|
#define LEFT BUTTON_LEFT
|
|
#define RIGHT BUTTON_RIGHT
|
|
#define FIRE BUTTON_SELECT
|
|
|
|
#elif CONFIG_KEYPAD == TATUNG_TPJ1022_PAD
|
|
|
|
/* TODO: Figure out which buttons to use for Tatung Elio TPJ-1022 */
|
|
#define QUIT BUTTON_AB
|
|
#define LEFT BUTTON_LEFT
|
|
#define RIGHT BUTTON_RIGHT
|
|
#define FIRE BUTTON_MENU
|
|
|
|
#elif CONFIG_KEYPAD == GIGABEAT_S_PAD
|
|
|
|
#define QUIT BUTTON_BACK
|
|
#define LEFT BUTTON_LEFT
|
|
#define RIGHT BUTTON_RIGHT
|
|
#define FIRE BUTTON_SELECT
|
|
|
|
#elif CONFIG_KEYPAD == COWON_D2_PAD
|
|
|
|
#define QUIT BUTTON_POWER
|
|
#define LEFT BUTTON_MINUS
|
|
#define RIGHT BUTTON_PLUS
|
|
#define FIRE BUTTON_MENU
|
|
|
|
#elif CONFIG_KEYPAD == IAUDIO67_PAD
|
|
|
|
#define QUIT BUTTON_POWER
|
|
#define LEFT BUTTON_LEFT
|
|
#define RIGHT BUTTON_RIGHT
|
|
#define FIRE BUTTON_PLAY
|
|
|
|
#elif CONFIG_KEYPAD == CREATIVEZVM_PAD
|
|
|
|
#define QUIT BUTTON_BACK
|
|
#define LEFT BUTTON_LEFT
|
|
#define RIGHT BUTTON_RIGHT
|
|
#define FIRE BUTTON_SELECT
|
|
|
|
#elif CONFIG_KEYPAD == PHILIPS_SA9200_PAD
|
|
|
|
#define QUIT BUTTON_POWER
|
|
#define LEFT BUTTON_PREV
|
|
#define RIGHT BUTTON_NEXT
|
|
#define FIRE BUTTON_PLAY
|
|
|
|
#elif CONFIG_KEYPAD == ONDAVX747_PAD || \
|
|
CONFIG_KEYPAD == ONDAVX777_PAD || \
|
|
CONFIG_KEYPAD == MROBE500_PAD
|
|
|
|
#define QUIT BUTTON_POWER
|
|
|
|
#elif CONFIG_KEYPAD == SAMSUNG_YH_PAD
|
|
|
|
#define QUIT BUTTON_REC
|
|
#define LEFT BUTTON_LEFT
|
|
#define RIGHT BUTTON_RIGHT
|
|
#define FIRE BUTTON_PLAY
|
|
|
|
#elif CONFIG_KEYPAD == PBELL_VIBE500_PAD
|
|
|
|
#define QUIT BUTTON_REC
|
|
#define LEFT BUTTON_PREV
|
|
#define RIGHT BUTTON_NEXT
|
|
#define FIRE BUTTON_OK
|
|
|
|
#else
|
|
#error INVADROX: Unsupported keypad
|
|
#endif
|
|
|
|
#ifndef RC_QUIT
|
|
#define RC_QUIT 0
|
|
#endif
|
|
|
|
#ifdef HAVE_TOUCHSCREEN
|
|
|
|
#ifndef QUIT
|
|
#define QUIT 0
|
|
#endif
|
|
#ifndef LEFT
|
|
#define LEFT 0
|
|
#endif
|
|
#ifndef RIGHT
|
|
#define RIGHT 0
|
|
#endif
|
|
#ifndef FIRE
|
|
#define FIRE 0
|
|
#endif
|
|
|
|
#define TOUCHSCREEN_QUIT BUTTON_TOPLEFT
|
|
#define TOUCHSCREEN_LEFT (BUTTON_MIDLEFT | BUTTON_BOTTOMLEFT)
|
|
#define TOUCHSCREEN_RIGHT (BUTTON_MIDRIGHT | BUTTON_BOTTOMRIGHT)
|
|
#define TOUCHSCREEN_FIRE (BUTTON_CENTER | BUTTON_BOTTOMMIDDLE)
|
|
|
|
#define ACTION_QUIT (QUIT | TOUCHSCREEN_QUIT | RC_QUIT)
|
|
#define ACTION_LEFT (LEFT | TOUCHSCREEN_LEFT)
|
|
#define ACTION_RIGHT (RIGHT | TOUCHSCREEN_RIGHT)
|
|
#define ACTION_FIRE (FIRE | TOUCHSCREEN_FIRE)
|
|
|
|
#else /* HAVE_TOUCHSCREEN */
|
|
|
|
#define ACTION_QUIT (QUIT | RC_QUIT)
|
|
#define ACTION_LEFT LEFT
|
|
#define ACTION_RIGHT RIGHT
|
|
#define ACTION_FIRE FIRE
|
|
|
|
#endif
|
|
|
|
#ifndef UNUSED
|
|
#define UNUSED __attribute__ ((unused))
|
|
#endif
|
|
|
|
/* Defines common to all models */
|
|
#define UFO_Y (SCORENUM_Y + FONT_HEIGHT + ALIEN_HEIGHT)
|
|
#define PLAYFIELD_Y (LCD_HEIGHT - SHIP_HEIGHT - 2)
|
|
#define PLAYFIELD_WIDTH (LCD_WIDTH - 2 * PLAYFIELD_X)
|
|
#define LEVEL_X (LCD_WIDTH - PLAYFIELD_X - LIVES_X - 2 * NUMBERS_WIDTH - 3 * NUM_SPACING)
|
|
#define SHIP_MIN_X (PLAYFIELD_X + PLAYFIELD_WIDTH / 5 - SHIELD_WIDTH / 2 - SHIP_WIDTH)
|
|
#define SHIP_MAX_X (PLAYFIELD_X + 4 * PLAYFIELD_WIDTH / 5 + SHIELD_WIDTH / 2)
|
|
/* SCORE_Y = 0 for most targets. Gigabeat redefines it later. */
|
|
#define SCORE_Y 0
|
|
#define MAX_LIVES 8
|
|
|
|
|
|
/* m:robe 500 defines */
|
|
#if ((LCD_WIDTH == 640) && (LCD_HEIGHT == 480)) || \
|
|
((LCD_WIDTH == 480) && (LCD_HEIGHT == 640))
|
|
|
|
/* Original arcade game size 224x240, 1bpp with
|
|
* red overlay at top and green overlay at bottom.
|
|
*
|
|
* M:Robe 500: 640x480x16
|
|
* ======================
|
|
*/
|
|
|
|
#define ARCADISH_GRAPHICS
|
|
#define PLAYFIELD_X 48
|
|
#define SHIP_Y (PLAYFIELD_Y - 2 * SHIP_HEIGHT)
|
|
#define ALIEN_START_Y (UFO_Y + ALIEN_HEIGHT)
|
|
#define SCORENUM_X (PLAYFIELD_X + NUMBERS_WIDTH)
|
|
#define SCORENUM_Y (SCORE_Y + FONT_HEIGHT + 2)
|
|
#define HISCORENUM_X (LCD_WIDTH - PLAYFIELD_X - 1 - 6 * NUMBERS_WIDTH - 5 * NUM_SPACING)
|
|
#define SHIELD_Y (PLAYFIELD_Y - 5 * SHIP_HEIGHT)
|
|
#define LIVES_X 10
|
|
#define MAX_Y 18
|
|
|
|
/* iPod Video defines */
|
|
#elif (LCD_WIDTH == 320) && (LCD_HEIGHT == 240)
|
|
|
|
/* Original arcade game size 224x240, 1bpp with
|
|
* red overlay at top and green overlay at bottom.
|
|
*
|
|
* iPod Video: 320x240x16
|
|
* ======================
|
|
* X: 48p padding at left/right gives 224p playfield in middle.
|
|
* 10p "border" gives 204p actual playfield. UFO use full 224p.
|
|
* Y: Use full 240p.
|
|
*
|
|
* MAX_X = (204 - 12) / 2 - 1 = 95
|
|
*
|
|
* Y: Score text 7 0
|
|
* Space 10 7
|
|
* Score 7 17
|
|
* Space 8 24
|
|
* 3 Ufo 7 32
|
|
* 2 Space Aliens start at 32 + 3 * 8 = 56
|
|
* 0 aliens 9*8 56 -
|
|
* space ~7*8 128 | 18.75 aliens space between
|
|
* shield 2*8 182 | first alien and ship.
|
|
* space 8 198 | MAX_Y = 18
|
|
* ship 8 206 -
|
|
* space 2*8 214
|
|
* hline 1 230 - PLAYFIELD_Y
|
|
* bottom border 10 240
|
|
* Lives and Level goes inside bottom border
|
|
*/
|
|
|
|
#define ARCADISH_GRAPHICS
|
|
#define PLAYFIELD_X 48
|
|
#define SHIP_Y (PLAYFIELD_Y - 3 * SHIP_HEIGHT)
|
|
#define ALIEN_START_Y (UFO_Y + 3 * ALIEN_HEIGHT)
|
|
#define SCORENUM_X (PLAYFIELD_X + NUMBERS_WIDTH)
|
|
#define SCORENUM_Y SCORE_Y + (2 * (FONT_HEIGHT + 1) + 1)
|
|
#define HISCORENUM_X (LCD_WIDTH - PLAYFIELD_X - 1 - 6 * NUMBERS_WIDTH - 5 * NUM_SPACING)
|
|
#define SHIELD_Y (PLAYFIELD_Y - 6 * SHIP_HEIGHT)
|
|
#define LIVES_X 10
|
|
#define MAX_Y 18
|
|
|
|
#elif (LCD_WIDTH == 176) && (LCD_HEIGHT == 220)
|
|
|
|
/* Sandisk Sansa e200: 176x220x16
|
|
* ==============================
|
|
* X: No padding. 8p border -> 160p playfield.
|
|
*
|
|
* 8p Aliens with 3p spacing -> 88 + 30 = 118p aliens block.
|
|
* (160 - 118) / 2 = 21 rounds for whole block (more than original)
|
|
* MAX_X = (160 - 8) / 2 - 1 = 75 rounds for single alien (less than original)
|
|
*
|
|
* LOGO 70 0
|
|
* Score text 5 70
|
|
* Space 5 75
|
|
* Y Score 5 80
|
|
* Space 10 85
|
|
* 2 Ufo 5 95
|
|
* 2 Space 10 100
|
|
* 0 aliens 9*5 110 -
|
|
* space ~7*5 155 | 18.6 aliens space between
|
|
* shield 2*5 188 | first alien and ship.
|
|
* space 5 198 | MAX_Y = 18
|
|
* ship 5 203 -
|
|
* space 5 208
|
|
* hline 1 213 PLAYFIELD_Y
|
|
* bottom border 6
|
|
* LCD_HEIGHT 220
|
|
* Lives and Level goes inside bottom border
|
|
*/
|
|
|
|
#define SMALL_GRAPHICS
|
|
#define PLAYFIELD_X 0
|
|
#define SHIP_Y (PLAYFIELD_Y - 2 * SHIP_HEIGHT)
|
|
#define SHIELD_Y (SHIP_Y - SHIP_HEIGHT - SHIELD_HEIGHT)
|
|
#define ALIEN_START_Y (UFO_Y + 3 * SHIP_HEIGHT)
|
|
/* Redefine SCORE_Y */
|
|
#undef SCORE_Y
|
|
#define SCORE_Y 70
|
|
#define SCORENUM_X (PLAYFIELD_X + NUMBERS_WIDTH)
|
|
#define SCORENUM_Y (SCORE_Y + 2 * FONT_HEIGHT)
|
|
#define HISCORENUM_X (LCD_WIDTH - PLAYFIELD_X - 1 - 6 * NUMBERS_WIDTH - 5 * NUM_SPACING)
|
|
#define LIVES_X 8
|
|
#define MAX_Y 18
|
|
|
|
|
|
#elif (LCD_WIDTH == 176) && (LCD_HEIGHT == 132)
|
|
|
|
/* iPod Nano: 176x132x16
|
|
* ======================
|
|
* X: No padding. 8p border -> 160p playfield.
|
|
*
|
|
* LIVES_X 8
|
|
* ALIEN_WIDTH 8
|
|
* ALIEN_HEIGHT 5
|
|
* ALIEN_SPACING 3
|
|
* SHIP_WIDTH 10
|
|
* SHIP_HEIGHT 5
|
|
* FONT_HEIGHT 5
|
|
* UFO_WIDTH 10
|
|
* UFO_HEIGHT 5
|
|
* SHIELD_WIDTH 15
|
|
* SHIELD_HEIGHT 10
|
|
* MAX_X 75
|
|
* MAX_Y = 18
|
|
* ALIEN_START_Y (UFO_Y + 12)
|
|
*
|
|
* 8p Aliens with 3p spacing -> 88 + 30 = 118p aliens block.
|
|
* (160 - 118) / 2 = 21 rounds for whole block (more than original)
|
|
* MAX_X = (160 - 8) / 2 - 1 = 75 rounds for single alien (less than original)
|
|
*
|
|
* Y: Scoreline 5 0 (combine scoretext and numbers on same line)
|
|
* Space 5 5
|
|
* 1 Ufo 5 10
|
|
* 3 Space 7 15
|
|
* 2 aliens 9*5 22 -
|
|
* space ~7*5 67 | Just above 18 aliens space between
|
|
* shield 2*5 100 | first alien and ship.
|
|
* space 5 110 | MAX_Y = 18
|
|
* ship 5 115 -
|
|
* space 5 120
|
|
* hline 1 125 PLAYFIELD_Y
|
|
* bottom border 6 126
|
|
* LCD_HEIGHT 131
|
|
* Lives and Level goes inside bottom border
|
|
*/
|
|
|
|
#define SMALL_GRAPHICS
|
|
#define PLAYFIELD_X 0
|
|
#define SHIP_Y (PLAYFIELD_Y - 2 * SHIP_HEIGHT)
|
|
#define ALIEN_START_Y (UFO_Y + 12)
|
|
#define SCORENUM_X (PLAYFIELD_X + 6 * NUMBERS_WIDTH + 5 * NUM_SPACING)
|
|
#define SCORENUM_Y SCORE_Y
|
|
#define HISCORENUM_X (LCD_WIDTH - PLAYFIELD_X - 4 * NUMBERS_WIDTH - 3 * NUM_SPACING)
|
|
#define SHIELD_Y (SHIP_Y - SHIP_HEIGHT - SHIELD_HEIGHT)
|
|
#define LIVES_X 8
|
|
#define MAX_Y 18
|
|
|
|
#elif (LCD_WIDTH == 160) && (LCD_HEIGHT == 128)
|
|
|
|
/* iAudio X5, iRiver H10 20Gb, iPod 3g/4g, H100, M5: 160x128
|
|
* =========================================================
|
|
* X: No padding. No border -> 160p playfield.
|
|
*
|
|
* LIVES_X 0
|
|
* ALIEN_WIDTH 8
|
|
* ALIEN_HEIGHT 5
|
|
* ALIEN_SPACING 3
|
|
* SHIP_WIDTH 10
|
|
* SHIP_HEIGHT 5
|
|
* FONT_HEIGHT 5
|
|
* UFO_WIDTH 10
|
|
* UFO_HEIGHT 5
|
|
* SHIELD_WIDTH 15
|
|
* SHIELD_HEIGHT 10
|
|
* MAX_X 75
|
|
* MAX_Y = 18
|
|
* ALIEN_START_Y (UFO_Y + 10)
|
|
*
|
|
* 8p Aliens with 3p spacing -> 88 + 30 = 118p aliens block.
|
|
* (160 - 118) / 2 = 21 rounds for whole block (more than original)
|
|
* MAX_X = (160 - 8) / 2 - 1 = 75 rounds for single alien (less than original)
|
|
*
|
|
* Y: Scoreline 5 0 (combine scoretext and numbers on same line)
|
|
* Space 5 5
|
|
* 1 Ufo 5 10
|
|
* 2 Space 5 15
|
|
* 8 aliens 9*5 20 -
|
|
* space ~6*5 65 | Just above 18 aliens space between
|
|
* shield 2*5 96 | first alien and ship.
|
|
* space 5 106 | MAX_Y = 18
|
|
* ship 5 111 -
|
|
* space 5 116
|
|
* hline 1 121 PLAYFIELD_Y
|
|
* bottom border 6 122
|
|
* LCD_HEIGHT 128
|
|
* Lives and Level goes inside bottom border
|
|
*/
|
|
|
|
#define SMALL_GRAPHICS
|
|
#define PLAYFIELD_X 0
|
|
#define SHIP_Y (PLAYFIELD_Y - 2 * SHIP_HEIGHT)
|
|
#define ALIEN_START_Y (UFO_Y + 10)
|
|
#define SCORENUM_X (PLAYFIELD_X + 6 * NUMBERS_WIDTH + 5 * NUM_SPACING)
|
|
#define SCORENUM_Y SCORE_Y
|
|
#define HISCORENUM_X (LCD_WIDTH - PLAYFIELD_X - 4 * NUMBERS_WIDTH - 3 * NUM_SPACING)
|
|
#define SHIELD_Y (SHIP_Y - SHIP_HEIGHT - SHIELD_HEIGHT)
|
|
#define LIVES_X 0
|
|
#define MAX_Y 18
|
|
|
|
|
|
#elif (LCD_WIDTH == 240) && ((LCD_HEIGHT == 320) || (LCD_HEIGHT == 400))
|
|
|
|
/* Gigabeat: 240x320x16
|
|
* ======================
|
|
* X: 8p padding at left/right gives 224p playfield in middle.
|
|
* 10p "border" gives 204p actual playfield. UFO use full 224p.
|
|
* Y: Use bottom 240p for playfield and top 80 pixels for logo.
|
|
*
|
|
* MAX_X = (204 - 12) / 2 - 1 = 95
|
|
*
|
|
* Y: Score text 7 0 + 80
|
|
* Space 10 7 + 80
|
|
* Score 7 17 + 80
|
|
* Space 8 24 + 80
|
|
* 3 Ufo 7 32 + 80
|
|
* 2 Space Aliens start at 32 + 3 * 8 = 56
|
|
* 0 aliens 9*8 56 -
|
|
* space ~7*8 128 | 18.75 aliens space between
|
|
* shield 2*8 182 | first alien and ship.
|
|
* space 8 198 | MAX_Y = 18
|
|
* ship 8 206 -
|
|
* space 2*8 214
|
|
* hline 1 230 310 - PLAYFIELD_Y
|
|
* bottom border 10 240 320
|
|
* Lives and Level goes inside bottom border
|
|
*/
|
|
|
|
#define ARCADISH_GRAPHICS
|
|
#define PLAYFIELD_X 8
|
|
#define SHIP_Y (PLAYFIELD_Y - 3 * SHIP_HEIGHT)
|
|
#define ALIEN_START_Y (UFO_Y + 3 * ALIEN_HEIGHT)
|
|
/* Redefine SCORE_Y */
|
|
#undef SCORE_Y
|
|
#define SCORE_Y 80
|
|
#define SCORENUM_X (PLAYFIELD_X + NUMBERS_WIDTH)
|
|
#define SCORENUM_Y SCORE_Y + (2 * (FONT_HEIGHT + 1) + 1)
|
|
#define HISCORENUM_X (LCD_WIDTH - PLAYFIELD_X - 1 - 6 * NUMBERS_WIDTH - 5 * NUM_SPACING)
|
|
#define SHIELD_Y (PLAYFIELD_Y - 6 * SHIP_HEIGHT)
|
|
#define LIVES_X 10
|
|
#define MAX_Y 18
|
|
|
|
#elif (LCD_WIDTH == 220) && (LCD_HEIGHT == 176)
|
|
|
|
/* TPJ1022, H300, iPod Color: 220x176x16
|
|
* ============================
|
|
* X: 0p padding at left/right gives 220p playfield in middle.
|
|
* 8p "border" gives 204p actual playfield. UFO use full 220p.
|
|
* Y: Use full 176p for playfield.
|
|
*
|
|
* MAX_X = (204 - 12) / 2 - 1 = 95
|
|
*
|
|
* Y: Score text 7 0
|
|
* Space 8 7
|
|
* 1 Ufo 7 15
|
|
* 7 Space Aliens start at 15 + 3 * 8 = 56
|
|
* 6 aliens 9*8 25 -
|
|
* space ~7*8 103 | 15.6 aliens space between
|
|
* shield 2*8 126 | first alien and ship.
|
|
* space 8 142 | MAX_Y = 15
|
|
* ship 8 150 -
|
|
* space 8 158
|
|
* hline 1 166 - PLAYFIELD_Y
|
|
* bottom border 10 176
|
|
* Lives and Level goes inside bottom border
|
|
*/
|
|
|
|
#define ARCADISH_GRAPHICS
|
|
#define PLAYFIELD_X 0
|
|
#define SHIP_Y (PLAYFIELD_Y - 2 * SHIP_HEIGHT)
|
|
#define ALIEN_START_Y (UFO_Y + 10)
|
|
#define SCORENUM_Y SCORE_Y
|
|
#define SCORENUM_X (PLAYFIELD_X + 6 * NUMBERS_WIDTH + 6 * NUM_SPACING)
|
|
#define HISCORENUM_X (LCD_WIDTH - PLAYFIELD_X - 4 * NUMBERS_WIDTH - 3 * NUM_SPACING)
|
|
#define SHIELD_Y (PLAYFIELD_Y - 5 * SHIP_HEIGHT)
|
|
#define LIVES_X 8
|
|
#define MAX_Y 15
|
|
|
|
|
|
#else
|
|
#error INVADROX: Unsupported LCD type
|
|
#endif
|
|
|
|
#define MAX_X ((LCD_WIDTH-LIVES_X*2-PLAYFIELD_X*2 - ALIEN_WIDTH)/2 - 1)
|
|
|
|
/* Defines common to each "graphic type" */
|
|
#ifdef ARCADISH_GRAPHICS
|
|
|
|
#define SHOT_HEIGHT 5
|
|
#define ALIEN_SPACING 4
|
|
#define ALIEN_SPEED 2
|
|
#define UFO_SPEED 1
|
|
#define NUM_SPACING 3
|
|
#define FIRE_SPEED 8
|
|
#define BOMB_SPEED 3
|
|
#define ALIENS 11
|
|
|
|
#elif defined SMALL_GRAPHICS
|
|
|
|
#define SHOT_HEIGHT 4
|
|
#define ALIEN_SPACING 3
|
|
#define ALIEN_SPEED 2
|
|
#define UFO_SPEED 1
|
|
#define NUM_SPACING 2
|
|
#define FIRE_SPEED 6
|
|
#define BOMB_SPEED 2
|
|
#define ALIENS 11
|
|
|
|
#else
|
|
#error Graphic type not defined
|
|
#endif
|
|
|
|
|
|
/* Colors */
|
|
#if (LCD_DEPTH >= 8)
|
|
#define SLIME_GREEN LCD_RGBPACK(31, 254, 31)
|
|
#define UFO_RED LCD_RGBPACK(254, 31, 31)
|
|
#elif (LCD_DEPTH == 2)
|
|
#define SLIME_GREEN LCD_LIGHTGRAY
|
|
#define UFO_RED LCD_LIGHTGRAY
|
|
#else
|
|
#error LCD type not implemented yet
|
|
#endif
|
|
|
|
/* Alien states */
|
|
#define DEAD 0
|
|
#define ALIVE 1
|
|
#define BOMBER 2
|
|
|
|
/* Fire/bomb/ufo states */
|
|
#define S_IDLE 0
|
|
#define S_ACTIVE 1
|
|
#define S_SHOWSCORE 2
|
|
#define S_EXPLODE -9
|
|
|
|
/* Fire/bomb targets */
|
|
#define TARGET_TOP 0
|
|
#define TARGET_SHIELD 1
|
|
#define TARGET_SHIP 2
|
|
#define TARGET_BOTTOM 3
|
|
#define TARGET_UFO 4
|
|
|
|
#define HISCOREFILE PLUGIN_GAMES_DIR "/invadrox.high"
|
|
|
|
|
|
/* The time (in ms) for one iteration through the game loop - decrease this
|
|
* to speed up the game - note that current_tick is (currently) only accurate
|
|
* to 10ms.
|
|
*/
|
|
#define CYCLETIME 40
|
|
|
|
|
|
/* Physical x is at PLAYFIELD_X + LIVES_X + x * ALIEN_SPEED
|
|
* Physical y is at y * ALIEN_HEIGHT
|
|
*/
|
|
struct alien {
|
|
int x; /* x-coordinate (0 - 95) */
|
|
int y; /* y-coordinate (0 - 18) */
|
|
unsigned char type; /* 0 (Kang), 1 (Kodos), 2 (Serak) */
|
|
unsigned char state; /* Dead, alive or bomber */
|
|
};
|
|
|
|
/* Aliens box 5 rows * ALIENS aliens in each row */
|
|
struct alien aliens[5 * ALIENS];
|
|
|
|
#define MAX_BOMBS 4
|
|
struct bomb {
|
|
int x, y;
|
|
unsigned char type;
|
|
unsigned char frame; /* Current animation frame */
|
|
unsigned char frames; /* Number of frames in animation */
|
|
unsigned char target; /* Remember target during explosion frames */
|
|
int state; /* 0 (IDLE) = inactive, 1 (FIRE) or negative, exploding */
|
|
};
|
|
struct bomb bombs[MAX_BOMBS];
|
|
/* Increase max_bombs at higher levels */
|
|
int max_bombs;
|
|
|
|
/* Raw framebuffer value of shield/ship green color */
|
|
fb_data screen_green, screen_white;
|
|
|
|
/* For optimization, precalculate startoffset of each scanline */
|
|
unsigned int ytab[LCD_HEIGHT];
|
|
|
|
int lives = 2;
|
|
int score = 0;
|
|
int scores[3] = { 30, 20, 10 };
|
|
int level = 0;
|
|
struct highscore hiscore;
|
|
bool game_over = false;
|
|
int ship_x, old_ship_x, ship_dir, ship_acc, max_ship_speed;
|
|
int ship_frame, ship_frame_counter;
|
|
bool ship_hit;
|
|
int fire, fire_target, fire_x, fire_y;
|
|
int curr_alien, aliens_paralyzed, gamespeed;
|
|
int ufo_state, ufo_x;
|
|
bool level_finished;
|
|
bool aliens_down, aliens_right, hit_left_border, hit_right_border;
|
|
|
|
|
|
/* No standard get_pixel function yet, use this hack instead */
|
|
#if (LCD_DEPTH >= 8)
|
|
|
|
#if defined(LCD_STRIDEFORMAT) && LCD_STRIDEFORMAT == VERTICAL_STRIDE
|
|
inline fb_data get_pixel(int x, int y)
|
|
{
|
|
return rb->lcd_framebuffer[x*LCD_HEIGHT+y];
|
|
}
|
|
#else
|
|
inline fb_data get_pixel(int x, int y)
|
|
{
|
|
return rb->lcd_framebuffer[ytab[y] + x];
|
|
}
|
|
#endif
|
|
|
|
#elif (LCD_DEPTH == 2)
|
|
|
|
#if (LCD_PIXELFORMAT == HORIZONTAL_PACKING)
|
|
static const unsigned char shifts[4] = {
|
|
6, 4, 2, 0
|
|
};
|
|
/* Horizontal packing */
|
|
inline fb_data get_pixel(int x, int y)
|
|
{
|
|
return (rb->lcd_framebuffer[ytab[y] + (x >> 2)] >> shifts[x & 3]) & 3;
|
|
}
|
|
#else
|
|
/* Vertical packing */
|
|
static const unsigned char shifts[4] = {
|
|
0, 2, 4, 6
|
|
};
|
|
inline fb_data get_pixel(int x, int y)
|
|
{
|
|
return (rb->lcd_framebuffer[ytab[y] + x] >> shifts[y & 3]) & 3;
|
|
}
|
|
#endif /* Horizontal/Vertical packing */
|
|
|
|
#else
|
|
#error get_pixel: pixelformat not implemented yet
|
|
#endif
|
|
|
|
|
|
/* Draw "digits" least significant digits of num at (x,y) */
|
|
void draw_number(int x, int y, int num, int digits)
|
|
{
|
|
int i;
|
|
int d;
|
|
|
|
for (i = digits - 1; i >= 0; i--) {
|
|
d = num % 10;
|
|
num = num / 10;
|
|
rb->lcd_bitmap_part(invadrox_numbers, d * NUMBERS_WIDTH, 0,
|
|
STRIDE( SCREEN_MAIN,
|
|
BMPWIDTH_invadrox_numbers,
|
|
BMPHEIGHT_invadrox_numbers),
|
|
x + i * (NUMBERS_WIDTH + NUM_SPACING), y,
|
|
NUMBERS_WIDTH, FONT_HEIGHT);
|
|
}
|
|
/* Update lcd */
|
|
rb->lcd_update_rect(x, y, 4 * NUMBERS_WIDTH + 3 * NUM_SPACING, FONT_HEIGHT);
|
|
}
|
|
|
|
|
|
inline void draw_score(void)
|
|
{
|
|
draw_number(SCORENUM_X, SCORENUM_Y, score, 4);
|
|
if (score > hiscore.score) {
|
|
/* Draw new hiscore (same as score) */
|
|
draw_number(HISCORENUM_X, SCORENUM_Y, score, 4);
|
|
}
|
|
}
|
|
|
|
|
|
void draw_level(void)
|
|
{
|
|
draw_number(LEVEL_X + 2 * NUM_SPACING, PLAYFIELD_Y + 2, level, 2);
|
|
}
|
|
|
|
|
|
void draw_lives(void)
|
|
{
|
|
int i;
|
|
/* Lives num */
|
|
rb->lcd_bitmap_part(invadrox_numbers, lives * NUMBERS_WIDTH, 0,
|
|
STRIDE( SCREEN_MAIN,
|
|
BMPWIDTH_invadrox_numbers,
|
|
BMPHEIGHT_invadrox_numbers),
|
|
PLAYFIELD_X + LIVES_X, PLAYFIELD_Y + 2,
|
|
NUMBERS_WIDTH, FONT_HEIGHT);
|
|
|
|
/* Ships */
|
|
for (i = 0; i < (lives - 1); i++) {
|
|
rb->lcd_bitmap_part(invadrox_ships, 0, 0,
|
|
STRIDE( SCREEN_MAIN,
|
|
BMPWIDTH_invadrox_ships,
|
|
BMPHEIGHT_invadrox_ships),
|
|
PLAYFIELD_X + LIVES_X + SHIP_WIDTH + i * (SHIP_WIDTH + NUM_SPACING),
|
|
PLAYFIELD_Y + 1, SHIP_WIDTH, SHIP_HEIGHT);
|
|
}
|
|
|
|
/* Erase ship to the right (if less than MAX_LIVES) */
|
|
if (lives < MAX_LIVES) {
|
|
rb->lcd_fillrect(PLAYFIELD_X + LIVES_X + SHIP_WIDTH + i * (SHIP_WIDTH + NUM_SPACING),
|
|
PLAYFIELD_Y + 1, SHIP_WIDTH, SHIP_HEIGHT);
|
|
}
|
|
/* Update lives (and level) part of screen */
|
|
rb->lcd_update_rect(PLAYFIELD_X + LIVES_X, PLAYFIELD_Y + 1,
|
|
PLAYFIELD_WIDTH - 2 * LIVES_X, MAX(FONT_HEIGHT + 1, SHIP_HEIGHT + 1));
|
|
}
|
|
|
|
|
|
inline void draw_aliens(void)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < 5 * ALIENS; i++) {
|
|
rb->lcd_bitmap_part(invadrox_aliens, aliens[i].x & 1 ? ALIEN_WIDTH : 0,
|
|
aliens[i].type * ALIEN_HEIGHT,
|
|
STRIDE( SCREEN_MAIN,
|
|
BMPWIDTH_invadrox_aliens,
|
|
BMPHEIGHT_invadrox_aliens),
|
|
PLAYFIELD_X + LIVES_X + aliens[i].x * ALIEN_SPEED,
|
|
ALIEN_START_Y + aliens[i].y * ALIEN_HEIGHT,
|
|
ALIEN_WIDTH, ALIEN_HEIGHT);
|
|
}
|
|
}
|
|
|
|
|
|
/* Return false if there is no next alive alien (round is over) */
|
|
inline bool next_alien(void)
|
|
{
|
|
bool ret = true;
|
|
|
|
do {
|
|
curr_alien++;
|
|
if (curr_alien % ALIENS == 0) {
|
|
/* End of this row. Move up one row. */
|
|
curr_alien -= 2 * ALIENS;
|
|
if (curr_alien < 0) {
|
|
/* No more aliens in this round. */
|
|
curr_alien = 4 * ALIENS;
|
|
ret = false;
|
|
}
|
|
}
|
|
} while (aliens[curr_alien].state == DEAD && ret);
|
|
|
|
if (!ret) {
|
|
/* No more alive aliens. Round finished. */
|
|
if (hit_right_border) {
|
|
if (hit_left_border) {
|
|
DBG("ERROR: both left and right borders are set (%d)\n", curr_alien);
|
|
}
|
|
/* Move down-left next round */
|
|
aliens_right = false;
|
|
aliens_down = true;
|
|
hit_right_border = false;
|
|
} else if (hit_left_border) {
|
|
/* Move down-right next round */
|
|
aliens_right = true;
|
|
aliens_down = true;
|
|
hit_left_border = false;
|
|
} else {
|
|
/* Not left nor right. Set down to false. */
|
|
aliens_down = false;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/* All aliens have been moved.
|
|
* Set curr_alien to first alive.
|
|
* Return false if no-one is left alive.
|
|
*/
|
|
bool first_alien(void)
|
|
{
|
|
int i, y;
|
|
|
|
for (y = 4; y >= 0; y--) {
|
|
for (i = y * ALIENS; i < (y + 1) * ALIENS; i++) {
|
|
if (aliens[i].state != DEAD) {
|
|
curr_alien = i;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* All aliens dead. */
|
|
level_finished = true;
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
bool move_aliens(void)
|
|
{
|
|
int x, y, old_x, old_y;
|
|
|
|
/* Move current alien (curr_alien is pointing to a living alien) */
|
|
|
|
old_x = aliens[curr_alien].x;
|
|
old_y = aliens[curr_alien].y;
|
|
|
|
if (aliens_down) {
|
|
aliens[curr_alien].y++;
|
|
if (aliens[curr_alien].y == MAX_Y) {
|
|
/* Alien is at bottom. Game Over. */
|
|
DBG("Alien %d is at bottom. Game Over.\n", curr_alien);
|
|
game_over = true;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (aliens_right) {
|
|
/* Moving right */
|
|
if (aliens[curr_alien].x < MAX_X) {
|
|
aliens[curr_alien].x++;
|
|
}
|
|
|
|
/* Now, after move, check if we hit the right border. */
|
|
if (aliens[curr_alien].x == MAX_X) {
|
|
hit_right_border = true;
|
|
}
|
|
|
|
} else {
|
|
/* Moving left */
|
|
if (aliens[curr_alien].x > 0) {
|
|
aliens[curr_alien].x--;
|
|
}
|
|
|
|
/* Now, after move, check if we hit the left border. */
|
|
if (aliens[curr_alien].x == 0) {
|
|
hit_left_border = true;
|
|
}
|
|
}
|
|
|
|
/* Erase old position */
|
|
x = PLAYFIELD_X + LIVES_X + old_x * ALIEN_SPEED;
|
|
y = ALIEN_START_Y + old_y * ALIEN_HEIGHT;
|
|
if (aliens[curr_alien].y != old_y) {
|
|
/* Moved in y-dir. Erase whole alien. */
|
|
rb->lcd_fillrect(x, y, ALIEN_WIDTH, ALIEN_HEIGHT);
|
|
} else {
|
|
if (aliens_right) {
|
|
/* Erase left edge */
|
|
rb->lcd_fillrect(x, y, ALIEN_SPEED, ALIEN_HEIGHT);
|
|
} else {
|
|
/* Erase right edge */
|
|
x += ALIEN_WIDTH - ALIEN_SPEED;
|
|
rb->lcd_fillrect(x, y, ALIEN_SPEED, ALIEN_HEIGHT);
|
|
}
|
|
}
|
|
|
|
/* Draw alien at new pos */
|
|
x = PLAYFIELD_X + LIVES_X + aliens[curr_alien].x * ALIEN_SPEED;
|
|
y = ALIEN_START_Y + aliens[curr_alien].y * ALIEN_HEIGHT;
|
|
rb->lcd_bitmap_part(invadrox_aliens,
|
|
aliens[curr_alien].x & 1 ? ALIEN_WIDTH : 0,
|
|
aliens[curr_alien].type * ALIEN_HEIGHT,
|
|
STRIDE( SCREEN_MAIN,
|
|
BMPWIDTH_invadrox_aliens,
|
|
BMPHEIGHT_invadrox_aliens),
|
|
x, y, ALIEN_WIDTH, ALIEN_HEIGHT);
|
|
|
|
if (!next_alien()) {
|
|
/* Round finished. Set curr_alien to first alive from bottom. */
|
|
if (!first_alien()) {
|
|
/* Should never happen. Taken care of in move_fire(). */
|
|
return false;
|
|
}
|
|
/* TODO: Play next background sound */
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
inline void draw_ship(void)
|
|
{
|
|
/* Erase old ship */
|
|
if (old_ship_x < ship_x) {
|
|
/* Move right. Erase leftmost part of ship. */
|
|
rb->lcd_fillrect(old_ship_x, SHIP_Y, ship_x - old_ship_x, SHIP_HEIGHT);
|
|
} else if (old_ship_x > ship_x) {
|
|
/* Move left. Erase rightmost part of ship. */
|
|
rb->lcd_fillrect(ship_x + SHIP_WIDTH, SHIP_Y, old_ship_x - ship_x, SHIP_HEIGHT);
|
|
}
|
|
|
|
/* Draw ship */
|
|
rb->lcd_bitmap_part(invadrox_ships, 0, ship_frame * SHIP_HEIGHT,
|
|
STRIDE( SCREEN_MAIN,
|
|
BMPWIDTH_invadrox_ships,
|
|
BMPHEIGHT_invadrox_ships),
|
|
ship_x, SHIP_Y, SHIP_WIDTH, SHIP_HEIGHT);
|
|
if (ship_hit) {
|
|
/* Alternate between frame 1 and 2 during hit */
|
|
ship_frame_counter++;
|
|
if (ship_frame_counter > 2) {
|
|
ship_frame_counter = 0;
|
|
ship_frame++;
|
|
if (ship_frame > 2) {
|
|
ship_frame = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Save ship_x for next time */
|
|
old_ship_x = ship_x;
|
|
}
|
|
|
|
|
|
inline void fire_alpha(int xc, int yc, fb_data color)
|
|
{
|
|
int oldmode = rb->lcd_get_drawmode();
|
|
|
|
rb->lcd_set_foreground(color);
|
|
rb->lcd_set_drawmode(DRMODE_FG);
|
|
|
|
rb->lcd_mono_bitmap(invadrox_fire, xc - (FIRE_WIDTH/2), yc, FIRE_WIDTH, FIRE_HEIGHT);
|
|
|
|
rb->lcd_set_foreground(LCD_BLACK);
|
|
rb->lcd_set_drawmode(oldmode);
|
|
}
|
|
|
|
|
|
void move_fire(void)
|
|
{
|
|
bool hit_green = false;
|
|
bool hit_white = false;
|
|
int i, j;
|
|
static int exploding_alien = -1;
|
|
fb_data pix;
|
|
|
|
if (fire == S_IDLE) {
|
|
return;
|
|
}
|
|
|
|
/* Alien hit. Wait until explosion is finished. */
|
|
if (aliens_paralyzed < 0) {
|
|
aliens_paralyzed++;
|
|
if (aliens_paralyzed == 0) {
|
|
/* Erase exploding_alien */
|
|
rb->lcd_fillrect(PLAYFIELD_X + LIVES_X + aliens[exploding_alien].x * ALIEN_SPEED,
|
|
ALIEN_START_Y + aliens[exploding_alien].y * ALIEN_HEIGHT,
|
|
ALIEN_EXPLODE_WIDTH, ALIEN_HEIGHT);
|
|
fire = S_IDLE;
|
|
/* Special case. We killed curr_alien. */
|
|
if (exploding_alien == curr_alien) {
|
|
if (!next_alien()) {
|
|
/* Round finished. Set curr_alien to first alive from bottom. */
|
|
first_alien();
|
|
}
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (fire == S_ACTIVE) {
|
|
|
|
/* Erase */
|
|
rb->lcd_vline(fire_x, fire_y, fire_y + SHOT_HEIGHT);
|
|
|
|
/* Check top */
|
|
if (fire_y <= SCORENUM_Y + FONT_HEIGHT + 4) {
|
|
|
|
/* TODO: Play explode sound */
|
|
|
|
fire = S_EXPLODE;
|
|
fire_target = TARGET_TOP;
|
|
fire_alpha(fire_x, fire_y, UFO_RED);
|
|
return;
|
|
}
|
|
|
|
/* Move */
|
|
fire_y -= FIRE_SPEED;
|
|
|
|
/* Hit UFO? */
|
|
if (ufo_state == S_ACTIVE) {
|
|
if ((ABS(ufo_x + UFO_WIDTH / 2 - fire_x) <= UFO_WIDTH / 2) &&
|
|
(fire_y <= UFO_Y + UFO_HEIGHT)) {
|
|
ufo_state = S_EXPLODE;
|
|
fire = S_EXPLODE;
|
|
fire_target = TARGET_UFO;
|
|
/* Center explosion */
|
|
ufo_x -= (UFO_EXPLODE_WIDTH - UFO_WIDTH) / 2;
|
|
rb->lcd_bitmap(invadrox_ufo_explode, ufo_x, UFO_Y - 1,
|
|
UFO_EXPLODE_WIDTH, UFO_EXPLODE_HEIGHT);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* Hit bomb? (check position, not pixel value) */
|
|
for (i = 0; i < max_bombs; i++) {
|
|
if (bombs[i].state == S_ACTIVE) {
|
|
/* Count as hit if within BOMB_WIDTH pixels */
|
|
if ((ABS(bombs[i].x - fire_x) < BOMB_WIDTH) &&
|
|
(fire_y - bombs[i].y < BOMB_HEIGHT)) {
|
|
/* Erase bomb */
|
|
rb->lcd_fillrect(bombs[i].x, bombs[i].y, BOMB_WIDTH, BOMB_HEIGHT);
|
|
bombs[i].state = S_IDLE;
|
|
/* Explode ship fire */
|
|
fire = S_EXPLODE;
|
|
fire_target = TARGET_SHIELD;
|
|
fire_alpha(fire_x, fire_y, LCD_WHITE);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Check for hit*/
|
|
for (i = FIRE_SPEED; i >= 0; i--) {
|
|
pix = get_pixel(fire_x, fire_y + i);
|
|
if(pix == screen_white) {
|
|
hit_white = true;
|
|
fire_y += i;
|
|
break;
|
|
}
|
|
if(pix == screen_green) {
|
|
hit_green = true;
|
|
fire_y += i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (hit_green) {
|
|
/* Hit shield */
|
|
|
|
/* TODO: Play explode sound */
|
|
|
|
fire = S_EXPLODE;
|
|
fire_target = TARGET_SHIELD;
|
|
/* Center explosion around hit pixel */
|
|
fire_y -= FIRE_HEIGHT / 2;
|
|
fire_alpha(fire_x, fire_y, SLIME_GREEN);
|
|
return;
|
|
}
|
|
|
|
if (hit_white) {
|
|
|
|
/* Hit alien? */
|
|
for (i = 0; i < 5 * ALIENS; i++) {
|
|
if (aliens[i].state != DEAD &&
|
|
(ABS(fire_x - (PLAYFIELD_X + LIVES_X + aliens[i].x * ALIEN_SPEED +
|
|
ALIEN_WIDTH / 2)) <= ALIEN_WIDTH / 2) &&
|
|
(ABS(fire_y - (ALIEN_START_Y + aliens[i].y * ALIEN_HEIGHT +
|
|
ALIEN_HEIGHT / 2)) <= ALIEN_HEIGHT / 2)) {
|
|
|
|
/* TODO: play alien hit sound */
|
|
|
|
if (aliens[i].state == BOMBER) {
|
|
/* Set (possible) alien above to bomber */
|
|
for (j = i - ALIENS; j >= 0; j -= ALIENS) {
|
|
if (aliens[j].state != DEAD) {
|
|
/* printf("New bomber (%d, %d)\n", j % ALIENS, j / ALIENS); */
|
|
aliens[j].state = BOMBER;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
aliens[i].state = DEAD;
|
|
exploding_alien = i;
|
|
score += scores[aliens[i].type];
|
|
draw_score();
|
|
/* Update score part of screen */
|
|
rb->lcd_update_rect(SCORENUM_X, SCORENUM_Y,
|
|
PLAYFIELD_WIDTH - 2 * NUMBERS_WIDTH, FONT_HEIGHT);
|
|
|
|
/* Paralyze aliens S_EXPLODE frames */
|
|
aliens_paralyzed = S_EXPLODE;
|
|
rb->lcd_bitmap(invadrox_alien_explode,
|
|
PLAYFIELD_X + LIVES_X + aliens[i].x * ALIEN_SPEED,
|
|
ALIEN_START_Y + aliens[i].y * ALIEN_HEIGHT,
|
|
ALIEN_EXPLODE_WIDTH, ALIEN_EXPLODE_HEIGHT);
|
|
/* Since alien is 1 pixel taller than explosion sprite, erase bottom line */
|
|
rb->lcd_hline(PLAYFIELD_X + LIVES_X + aliens[i].x * ALIEN_SPEED,
|
|
PLAYFIELD_X + LIVES_X + aliens[i].x * ALIEN_SPEED + ALIEN_WIDTH,
|
|
ALIEN_START_Y + (aliens[i].y + 1) * ALIEN_HEIGHT - 1);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Draw shot */
|
|
rb->lcd_set_foreground(LCD_WHITE);
|
|
rb->lcd_vline(fire_x, fire_y, fire_y + SHOT_HEIGHT);
|
|
rb->lcd_set_foreground(LCD_BLACK);
|
|
} else if (fire < S_IDLE) {
|
|
/* Count up towards S_IDLE, then erase explosion */
|
|
fire++;
|
|
if (fire == S_IDLE) {
|
|
/* Erase explosion */
|
|
if (fire_target == TARGET_TOP) {
|
|
rb->lcd_fillrect(fire_x - (FIRE_WIDTH / 2), fire_y, FIRE_WIDTH, FIRE_HEIGHT);
|
|
} else if (fire_target == TARGET_SHIELD) {
|
|
/* Draw explosion with black pixels */
|
|
fire_alpha(fire_x, fire_y, LCD_BLACK);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* Return a BOMBER alien */
|
|
inline int random_bomber(void)
|
|
{
|
|
int i, col;
|
|
|
|
/* TODO: Weigh higher probability near ship */
|
|
col = rb->rand() % ALIENS;
|
|
for (i = col + 4 * ALIENS; i >= 0; i -= ALIENS) {
|
|
if (aliens[i].state == BOMBER) {
|
|
return i;
|
|
}
|
|
}
|
|
|
|
/* No BOMBER found in this col */
|
|
|
|
for (i = 0; i < 5 * ALIENS; i++) {
|
|
if (aliens[i].state == BOMBER) {
|
|
return i;
|
|
}
|
|
}
|
|
|
|
/* No BOMBER found at all (error?) */
|
|
|
|
return -1;
|
|
}
|
|
|
|
|
|
inline void draw_bomb(int i)
|
|
{
|
|
rb->lcd_bitmap_part(invadrox_bombs, bombs[i].type * BOMB_WIDTH,
|
|
bombs[i].frame * BOMB_HEIGHT,
|
|
STRIDE( SCREEN_MAIN,
|
|
BMPWIDTH_invadrox_bombs,
|
|
BMPHEIGHT_invadrox_bombs),
|
|
bombs[i].x, bombs[i].y,
|
|
BOMB_WIDTH, BOMB_HEIGHT);
|
|
/* Advance frame */
|
|
bombs[i].frame++;
|
|
if (bombs[i].frame == bombs[i].frames) {
|
|
bombs[i].frame = 0;
|
|
}
|
|
}
|
|
|
|
|
|
void move_bombs(void)
|
|
{
|
|
int i, j, bomber;
|
|
bool abort;
|
|
|
|
for (i = 0; i < max_bombs; i++) {
|
|
|
|
switch (bombs[i].state) {
|
|
|
|
case S_IDLE:
|
|
if (ship_hit) {
|
|
continue;
|
|
}
|
|
bomber = random_bomber();
|
|
if (bomber < 0) {
|
|
DBG("ERROR: No bomber available\n");
|
|
continue;
|
|
}
|
|
/* x, y */
|
|
bombs[i].x = PLAYFIELD_X + LIVES_X + aliens[bomber].x * ALIEN_SPEED + ALIEN_WIDTH / 2;
|
|
bombs[i].y = ALIEN_START_Y + (aliens[bomber].y + 1) * ALIEN_HEIGHT;
|
|
|
|
/* Check for duplets in x and y direction */
|
|
abort = false;
|
|
for (j = i - 1; j >= 0; j--) {
|
|
if ((bombs[j].state == S_ACTIVE) &&
|
|
((bombs[i].x == bombs[j].x) || (bombs[i].y == bombs[j].y))) {
|
|
abort = true;
|
|
break;
|
|
}
|
|
}
|
|
if (abort) {
|
|
/* Skip this one, continue with next bomb */
|
|
/* printf("Bomb %d duplet of %d\n", i, j); */
|
|
continue;
|
|
}
|
|
|
|
/* Passed, set type */
|
|
bombs[i].type = rb->rand() % 3;
|
|
bombs[i].frame = 0;
|
|
if (bombs[i].type == 0) {
|
|
bombs[i].frames = 3;
|
|
} else if (bombs[i].type == 1) {
|
|
bombs[i].frames = 4;
|
|
} else {
|
|
bombs[i].frames = 6;
|
|
}
|
|
|
|
/* Bombs away */
|
|
bombs[i].state = S_ACTIVE;
|
|
draw_bomb(i);
|
|
continue;
|
|
|
|
break;
|
|
|
|
case S_ACTIVE:
|
|
/* Erase old position */
|
|
rb->lcd_fillrect(bombs[i].x, bombs[i].y, BOMB_WIDTH, BOMB_HEIGHT);
|
|
|
|
/* Move */
|
|
bombs[i].y += BOMB_SPEED;
|
|
|
|
/* Check if bottom hit */
|
|
if (bombs[i].y + BOMB_HEIGHT >= PLAYFIELD_Y) {
|
|
bombs[i].y = PLAYFIELD_Y - FIRE_HEIGHT + 1;
|
|
fire_alpha(bombs[i].x, bombs[i].y, LCD_WHITE);
|
|
bombs[i].state = S_EXPLODE;
|
|
bombs[i].target = TARGET_BOTTOM;
|
|
break;
|
|
}
|
|
|
|
/* Check for green (ship or shield) */
|
|
for (j = BOMB_HEIGHT; j >= BOMB_HEIGHT - BOMB_SPEED; j--) {
|
|
bombs[i].target = 0;
|
|
if(get_pixel(bombs[i].x + BOMB_WIDTH / 2, bombs[i].y + j) == screen_green) {
|
|
/* Move to hit pixel */
|
|
bombs[i].x += BOMB_WIDTH / 2;
|
|
bombs[i].y += j;
|
|
|
|
/* Check if ship is hit */
|
|
if (bombs[i].y > SHIELD_Y + SHIELD_HEIGHT && bombs[i].y < PLAYFIELD_Y) {
|
|
|
|
/* TODO: play ship hit sound */
|
|
|
|
ship_hit = true;
|
|
ship_frame = 1;
|
|
ship_frame_counter = 0;
|
|
bombs[i].state = S_EXPLODE * 4;
|
|
bombs[i].target = TARGET_SHIP;
|
|
rb->lcd_bitmap_part(invadrox_ships, 0, 1 * SHIP_HEIGHT,
|
|
STRIDE( SCREEN_MAIN,
|
|
BMPWIDTH_invadrox_ships,
|
|
BMPHEIGHT_invadrox_ships),
|
|
ship_x, SHIP_Y,
|
|
SHIP_WIDTH, SHIP_HEIGHT);
|
|
break;
|
|
}
|
|
/* Shield hit */
|
|
bombs[i].state = S_EXPLODE;
|
|
bombs[i].target = TARGET_SHIELD;
|
|
/* Center explosion around hit pixel in shield */
|
|
bombs[i].y -= FIRE_HEIGHT / 2;
|
|
fire_alpha(bombs[i].x, bombs[i].y, SLIME_GREEN);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (bombs[i].target != 0) {
|
|
/* Hit ship or shield, continue */
|
|
continue;
|
|
}
|
|
|
|
draw_bomb(i);
|
|
break;
|
|
|
|
default:
|
|
/* If we get here state should be < 0, exploding */
|
|
bombs[i].state++;
|
|
if (bombs[i].state == S_IDLE) {
|
|
if (ship_hit) {
|
|
/* Erase explosion */
|
|
rb->lcd_fillrect(ship_x, SHIP_Y, SHIP_WIDTH, SHIP_HEIGHT);
|
|
rb->lcd_update_rect(ship_x, SHIP_Y, SHIP_WIDTH, SHIP_HEIGHT);
|
|
ship_hit = false;
|
|
ship_frame = 0;
|
|
ship_x = PLAYFIELD_X + 2 * LIVES_X;
|
|
lives--;
|
|
if (lives == 0) {
|
|
game_over = true;
|
|
return;
|
|
}
|
|
draw_lives();
|
|
/* Sleep 1s to give player time to examine lives left */
|
|
rb->sleep(HZ);
|
|
}
|
|
/* Erase explosion (even if ship hit, might be another bomb) */
|
|
fire_alpha(bombs[i].x, bombs[i].y, LCD_BLACK);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
inline void move_ship(void)
|
|
{
|
|
ship_dir += ship_acc;
|
|
if (ship_dir > max_ship_speed) {
|
|
ship_dir = max_ship_speed;
|
|
}
|
|
if (ship_dir < -max_ship_speed) {
|
|
ship_dir = -max_ship_speed;
|
|
}
|
|
ship_x += ship_dir;
|
|
if (ship_x < SHIP_MIN_X) {
|
|
ship_x = SHIP_MIN_X;
|
|
}
|
|
if (ship_x > SHIP_MAX_X) {
|
|
ship_x = SHIP_MAX_X;
|
|
}
|
|
|
|
draw_ship();
|
|
}
|
|
|
|
|
|
/* Unidentified Flying Object */
|
|
void move_ufo(void)
|
|
{
|
|
static int ufo_speed;
|
|
static int counter;
|
|
int mystery_score;
|
|
|
|
switch (ufo_state) {
|
|
|
|
case S_IDLE:
|
|
|
|
if (rb->rand() % 500 == 0) {
|
|
/* Uh-oh, it's time to launch a mystery UFO */
|
|
|
|
/* TODO: Play UFO sound */
|
|
|
|
if (rb->rand() % 2) {
|
|
ufo_speed = UFO_SPEED;
|
|
ufo_x = PLAYFIELD_X;
|
|
} else {
|
|
ufo_speed = -UFO_SPEED;
|
|
ufo_x = LCD_WIDTH - PLAYFIELD_X - UFO_WIDTH;
|
|
}
|
|
ufo_state = S_ACTIVE;
|
|
/* UFO will be drawn next frame */
|
|
}
|
|
break;
|
|
|
|
case S_ACTIVE:
|
|
/* Erase old pos */
|
|
rb->lcd_fillrect(ufo_x, UFO_Y, UFO_WIDTH, UFO_HEIGHT);
|
|
/* Move */
|
|
ufo_x += ufo_speed;
|
|
/* Check bounds */
|
|
if (ufo_x < PLAYFIELD_X || ufo_x > LCD_WIDTH - PLAYFIELD_X - UFO_WIDTH) {
|
|
ufo_state = S_IDLE;
|
|
break;
|
|
}
|
|
/* Draw new pos */
|
|
rb->lcd_bitmap(invadrox_ufo, ufo_x, UFO_Y, UFO_WIDTH, UFO_HEIGHT);
|
|
break;
|
|
|
|
case S_SHOWSCORE:
|
|
counter++;
|
|
if (counter == S_IDLE) {
|
|
/* Erase mystery number */
|
|
rb->lcd_fillrect(ufo_x, UFO_Y, 3 * NUMBERS_WIDTH + 2 * NUM_SPACING, FONT_HEIGHT);
|
|
ufo_state = S_IDLE;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
/* Exploding */
|
|
ufo_state++;
|
|
if (ufo_state == S_IDLE) {
|
|
/* Erase explosion */
|
|
rb->lcd_fillrect(ufo_x, UFO_Y - 1, UFO_EXPLODE_WIDTH, UFO_EXPLODE_HEIGHT);
|
|
ufo_state = S_SHOWSCORE;
|
|
counter = S_EXPLODE * 4;
|
|
/* Draw mystery_score, sleep, increase score and continue */
|
|
mystery_score = 50 + (rb->rand() % 6) * 50;
|
|
if (mystery_score < 100) {
|
|
draw_number(ufo_x, UFO_Y, mystery_score, 2);
|
|
} else {
|
|
draw_number(ufo_x, UFO_Y, mystery_score, 3);
|
|
}
|
|
score += mystery_score;
|
|
draw_score();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
void draw_background(void)
|
|
{
|
|
|
|
rb->lcd_bitmap(invadrox_background, 0, 0, LCD_WIDTH, LCD_HEIGHT);
|
|
rb->lcd_update();
|
|
}
|
|
|
|
|
|
void new_level(void)
|
|
{
|
|
int i;
|
|
|
|
draw_background();
|
|
/* Give an extra life for each new level */
|
|
if (lives < MAX_LIVES) {
|
|
lives++;
|
|
}
|
|
draw_lives();
|
|
|
|
/* Score */
|
|
draw_score();
|
|
draw_number(HISCORENUM_X, SCORENUM_Y, hiscore.score, 4);
|
|
|
|
level++;
|
|
draw_level();
|
|
level_finished = false;
|
|
|
|
ufo_state = S_IDLE;
|
|
|
|
/* Init alien positions and states */
|
|
for (i = 0; i < 4 * ALIENS; i++) {
|
|
aliens[i].x = 0 + (i % ALIENS) * ((ALIEN_WIDTH + ALIEN_SPACING) / ALIEN_SPEED);
|
|
aliens[i].y = 2 * (i / ALIENS);
|
|
aliens[i].state = ALIVE;
|
|
}
|
|
/* Last row, bombers */
|
|
for (i = 4 * ALIENS; i < 5 * ALIENS; i++) {
|
|
aliens[i].x = 0 + (i % ALIENS) * ((ALIEN_WIDTH + ALIEN_SPACING) / ALIEN_SPEED);
|
|
aliens[i].y = 2 * (i / ALIENS);
|
|
aliens[i].state = BOMBER;
|
|
}
|
|
|
|
/* Init bombs to inactive (S_IDLE) */
|
|
for (i = 0; i < MAX_BOMBS; i++) {
|
|
bombs[i].state = S_IDLE;
|
|
}
|
|
|
|
/* Start aliens closer to earth from level 2 */
|
|
for (i = 0; i < 5 * ALIENS; i++) {
|
|
if (level < 6) {
|
|
aliens[i].y += level - 1;
|
|
} else {
|
|
aliens[i].y += 5;
|
|
}
|
|
}
|
|
|
|
/* Max concurrent bombs */
|
|
max_bombs = 1;
|
|
|
|
gamespeed = 2;
|
|
|
|
if (level > 1) {
|
|
max_bombs++;
|
|
}
|
|
|
|
/* Increase speed */
|
|
if (level > 2) {
|
|
gamespeed++;
|
|
}
|
|
|
|
if (level > 3) {
|
|
max_bombs++;
|
|
}
|
|
|
|
/* Increase speed more */
|
|
if (level > 4) {
|
|
gamespeed++;
|
|
}
|
|
|
|
if (level > 5) {
|
|
max_bombs++;
|
|
}
|
|
|
|
/* 4 shields */
|
|
for (i = 1; i <= 4; i++) {
|
|
rb->lcd_bitmap(invadrox_shield,
|
|
PLAYFIELD_X + i * PLAYFIELD_WIDTH / 5 - SHIELD_WIDTH / 2,
|
|
SHIELD_Y, SHIELD_WIDTH, SHIELD_HEIGHT);
|
|
}
|
|
|
|
/* Bottom line */
|
|
rb->lcd_set_foreground(SLIME_GREEN);
|
|
rb->lcd_hline(PLAYFIELD_X, LCD_WIDTH - PLAYFIELD_X, PLAYFIELD_Y);
|
|
/* Restore foreground to black (for fast erase later). */
|
|
rb->lcd_set_foreground(LCD_BLACK);
|
|
|
|
ship_x = PLAYFIELD_X + 2 * LIVES_X;
|
|
if (level == 1) {
|
|
old_ship_x = ship_x;
|
|
}
|
|
ship_dir = 0;
|
|
ship_acc = 0;
|
|
ship_frame = 0;
|
|
ship_hit = false;
|
|
fire = S_IDLE;
|
|
/* Start moving the bottom row left to right */
|
|
curr_alien = 4 * ALIENS;
|
|
aliens_paralyzed = 0;
|
|
aliens_right = true;
|
|
aliens_down = false;
|
|
hit_left_border = false;
|
|
hit_right_border = false;
|
|
/* TODO: Change max_ship_speed to 3 at higher levels */
|
|
max_ship_speed = 2;
|
|
|
|
draw_aliens();
|
|
|
|
rb->lcd_update();
|
|
}
|
|
|
|
|
|
void init_invadrox(void)
|
|
{
|
|
int i;
|
|
|
|
/* Seed random number generator with a "random" number */
|
|
rb->srand(rb->get_time()->tm_sec + rb->get_time()->tm_min * 60);
|
|
|
|
/* Precalculate start of each scanline */
|
|
for (i = 0; i < LCD_HEIGHT; i++) {
|
|
#if (LCD_DEPTH >= 8)
|
|
ytab[i] = i * LCD_WIDTH;
|
|
#elif (LCD_DEPTH == 2) && (LCD_PIXELFORMAT == HORIZONTAL_PACKING)
|
|
ytab[i] = i * (LCD_WIDTH / 4);
|
|
#elif (LCD_DEPTH == 2) && (LCD_PIXELFORMAT == VERTICAL_PACKING)
|
|
ytab[i] = (i / 4) * LCD_WIDTH;
|
|
#else
|
|
#error pixelformat not implemented yet
|
|
#endif
|
|
}
|
|
|
|
rb->lcd_set_background(LCD_BLACK);
|
|
rb->lcd_set_foreground(LCD_BLACK);
|
|
|
|
if (highscore_load(HISCOREFILE, &hiscore, 1) < 0) {
|
|
/* Init hiscore to 0 */
|
|
rb->strlcpy(hiscore.name, "Invader", sizeof(hiscore.name));
|
|
hiscore.score = 0;
|
|
hiscore.level = 1;
|
|
}
|
|
|
|
/* Init alien types in aliens array */
|
|
for (i = 0; i < 1 * ALIENS; i++) {
|
|
aliens[i].type = 0; /* Kang */
|
|
}
|
|
for (; i < 3 * ALIENS; i++) {
|
|
aliens[i].type = 1; /* Kodos */
|
|
}
|
|
for (; i < 5 * ALIENS; i++) {
|
|
aliens[i].type = 2; /* Serak */
|
|
}
|
|
|
|
|
|
/* Save screen white color */
|
|
rb->lcd_set_foreground(LCD_WHITE);
|
|
rb->lcd_drawpixel(0, 0);
|
|
rb->lcd_update_rect(0, 0, 1, 1);
|
|
screen_white = get_pixel(0, 0);
|
|
|
|
/* Save screen green color */
|
|
rb->lcd_set_foreground(SLIME_GREEN);
|
|
rb->lcd_drawpixel(0, 0);
|
|
rb->lcd_update_rect(0, 0, 1, 1);
|
|
screen_green = get_pixel(0, 0);
|
|
|
|
/* Restore black foreground */
|
|
rb->lcd_set_foreground(LCD_BLACK);
|
|
|
|
new_level();
|
|
|
|
/* Flash score at start */
|
|
for (i = 0; i < 5; i++) {
|
|
rb->lcd_fillrect(SCORENUM_X, SCORENUM_Y,
|
|
4 * NUMBERS_WIDTH + 3 * NUM_SPACING,
|
|
FONT_HEIGHT);
|
|
rb->lcd_update_rect(SCORENUM_X, SCORENUM_Y,
|
|
4 * NUMBERS_WIDTH + 3 * NUM_SPACING,
|
|
FONT_HEIGHT);
|
|
rb->sleep(HZ / 10);
|
|
draw_number(SCORENUM_X, SCORENUM_Y, score, 4);
|
|
rb->sleep(HZ / 10);
|
|
}
|
|
}
|
|
|
|
|
|
inline bool handle_buttons(void)
|
|
{
|
|
static unsigned int oldbuttonstate = 0;
|
|
|
|
unsigned int released, pressed, newbuttonstate;
|
|
|
|
if (ship_hit) {
|
|
/* Don't allow ship movement during explosion */
|
|
newbuttonstate = 0;
|
|
} else {
|
|
newbuttonstate = rb->button_status();
|
|
}
|
|
if(newbuttonstate == oldbuttonstate) {
|
|
if (newbuttonstate == 0) {
|
|
/* No button pressed. Stop ship. */
|
|
ship_acc = 0;
|
|
if (ship_dir > 0) {
|
|
ship_dir--;
|
|
}
|
|
if (ship_dir < 0) {
|
|
ship_dir++;
|
|
}
|
|
}
|
|
/* return false; */
|
|
goto check_usb;
|
|
}
|
|
released = ~newbuttonstate & oldbuttonstate;
|
|
pressed = newbuttonstate & ~oldbuttonstate;
|
|
oldbuttonstate = newbuttonstate;
|
|
if (pressed) {
|
|
if (pressed & ACTION_LEFT) {
|
|
if (ship_acc > -1) {
|
|
ship_acc--;
|
|
}
|
|
}
|
|
if (pressed & ACTION_RIGHT) {
|
|
if (ship_acc < 1) {
|
|
ship_acc++;
|
|
}
|
|
}
|
|
if (pressed & ACTION_FIRE) {
|
|
if (fire == S_IDLE) {
|
|
/* Fire shot */
|
|
fire_x = ship_x + SHIP_WIDTH / 2;
|
|
fire_y = SHIP_Y - SHOT_HEIGHT;
|
|
fire = S_ACTIVE;
|
|
/* TODO: play fire sound */
|
|
}
|
|
}
|
|
if (pressed & ACTION_QUIT) {
|
|
rb->splash(HZ * 1, "Quit");
|
|
return true;
|
|
}
|
|
}
|
|
if (released) {
|
|
if ((released & ACTION_LEFT)) {
|
|
if (ship_acc < 1) {
|
|
ship_acc++;
|
|
}
|
|
}
|
|
if ((released & ACTION_RIGHT)) {
|
|
if (ship_acc > -1) {
|
|
ship_acc--;
|
|
}
|
|
}
|
|
}
|
|
|
|
check_usb:
|
|
|
|
/* Quit if USB is connected */
|
|
if (rb->button_get(false) == SYS_USB_CONNECTED) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
void game_loop(void)
|
|
{
|
|
int i, end;
|
|
|
|
/* Print dimensions (just for debugging) */
|
|
DBG("%03dx%03dx%02d\n", LCD_WIDTH, LCD_HEIGHT, LCD_DEPTH);
|
|
|
|
/* Init */
|
|
init_invadrox();
|
|
|
|
while (1) {
|
|
/* Convert CYCLETIME (in ms) to HZ */
|
|
end = *rb->current_tick + (CYCLETIME * HZ) / 1000;
|
|
|
|
if (handle_buttons()) {
|
|
return;
|
|
}
|
|
|
|
/* Animate */
|
|
move_ship();
|
|
move_fire();
|
|
|
|
/* Check if level is finished (marked by move_fire) */
|
|
if (level_finished) {
|
|
/* TODO: Play level finished sound */
|
|
new_level();
|
|
}
|
|
|
|
move_ufo();
|
|
|
|
/* Move aliens */
|
|
if (!aliens_paralyzed && !ship_hit) {
|
|
for (i = 0; i < gamespeed; i++) {
|
|
if (!move_aliens()) {
|
|
if (game_over) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Move alien bombs */
|
|
move_bombs();
|
|
if (game_over) {
|
|
return;
|
|
}
|
|
|
|
/* Update "playfield" rect */
|
|
rb->lcd_update_rect(PLAYFIELD_X, SCORENUM_Y + FONT_HEIGHT,
|
|
PLAYFIELD_WIDTH,
|
|
PLAYFIELD_Y + 1 - SCORENUM_Y - FONT_HEIGHT);
|
|
|
|
/* Wait until next frame */
|
|
DBG("%ld (%d)\n", end - *rb->current_tick, (CYCLETIME * HZ) / 1000);
|
|
if (TIME_BEFORE(*rb->current_tick, end)) {
|
|
rb->sleep(end - *rb->current_tick);
|
|
} else {
|
|
rb->yield();
|
|
}
|
|
|
|
} /* end while */
|
|
}
|
|
|
|
|
|
/* this is the plugin entry point */
|
|
enum plugin_status plugin_start(UNUSED const void* parameter)
|
|
{
|
|
rb->lcd_setfont(FONT_SYSFIXED);
|
|
/* Turn off backlight timeout */
|
|
backlight_force_on(); /* backlight control in lib/helper.c */
|
|
|
|
/* now go ahead and have fun! */
|
|
game_loop();
|
|
|
|
/* Game Over. */
|
|
/* TODO: Play game over sound */
|
|
rb->splash(HZ * 2, "Game Over");
|
|
if (score > hiscore.score) {
|
|
/* Save new hiscore */
|
|
highscore_update(score, level, "Invader", &hiscore, 1);
|
|
highscore_save(HISCOREFILE, &hiscore, 1);
|
|
}
|
|
|
|
/* Restore user's original backlight setting */
|
|
rb->lcd_setfont(FONT_UI);
|
|
/* Turn on backlight timeout (revert to settings) */
|
|
backlight_use_settings(); /* backlight control in lib/helper.c */
|
|
|
|
return PLUGIN_OK;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* GNU Emacs settings: Kernighan & Richie coding style with
|
|
* 4 spaces indent and no tabs.
|
|
* Local Variables:
|
|
* c-file-style: "k&r"
|
|
* c-basic-offset: 4
|
|
* indent-tabs-mode: nil
|
|
* End:
|
|
*/
|