rockbox/apps/plugins/spacerocks.c

2141 lines
57 KiB
C
Raw Normal View History

/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2006 by Mat Holton
*
* 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.
*
****************************************************************************/
#include "plugin.h"
#include "lib/display_text.h"
#include "lib/helper.h"
#include "lib/highscore.h"
#include "lib/playback_control.h"
/* variable button definitions */
#if (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
(CONFIG_KEYPAD == IRIVER_H300_PAD)
#define AST_PAUSE BUTTON_REC
#define AST_QUIT BUTTON_OFF
#define AST_THRUST BUTTON_UP
#define AST_HYPERSPACE BUTTON_DOWN
#define AST_LEFT BUTTON_LEFT
#define AST_RIGHT BUTTON_RIGHT
#define AST_FIRE BUTTON_SELECT
#define AST_RC_QUIT BUTTON_RC_STOP
#elif (CONFIG_KEYPAD == IAUDIO_X5M5_PAD)
#define AST_PAUSE BUTTON_PLAY
#define AST_QUIT BUTTON_POWER
#define AST_THRUST BUTTON_UP
#define AST_HYPERSPACE BUTTON_DOWN
#define AST_LEFT BUTTON_LEFT
#define AST_RIGHT BUTTON_RIGHT
#define AST_FIRE BUTTON_SELECT
#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || (CONFIG_KEYPAD == IPOD_3G_PAD) || \
(CONFIG_KEYPAD == IPOD_1G2G_PAD)
#define AST_PAUSE (BUTTON_SELECT | BUTTON_PLAY)
#define AST_QUIT (BUTTON_SELECT | BUTTON_MENU)
#define AST_THRUST BUTTON_MENU
#define AST_HYPERSPACE BUTTON_PLAY
#define AST_LEFT BUTTON_SCROLL_BACK
#define AST_RIGHT BUTTON_SCROLL_FWD
#define AST_FIRE BUTTON_SELECT
#elif (CONFIG_KEYPAD == GIGABEAT_PAD)
#define AST_PAUSE BUTTON_A
#define AST_QUIT BUTTON_POWER
#define AST_THRUST BUTTON_UP
#define AST_HYPERSPACE BUTTON_DOWN
#define AST_LEFT BUTTON_LEFT
#define AST_RIGHT BUTTON_RIGHT
#define AST_FIRE BUTTON_SELECT
#elif (CONFIG_KEYPAD == SANSA_E200_PAD)
#define AST_PAUSE BUTTON_REC
#define AST_QUIT BUTTON_POWER
#define AST_THRUST BUTTON_UP
#define AST_HYPERSPACE BUTTON_DOWN
#define AST_LEFT BUTTON_SCROLL_BACK
#define AST_RIGHT BUTTON_SCROLL_FWD
#define AST_FIRE BUTTON_SELECT
#elif (CONFIG_KEYPAD == SANSA_FUZE_PAD)
#define AST_PAUSE (BUTTON_SELECT | BUTTON_UP)
#define AST_QUIT (BUTTON_HOME|BUTTON_REPEAT)
#define AST_THRUST BUTTON_UP
#define AST_HYPERSPACE BUTTON_DOWN
#define AST_LEFT BUTTON_SCROLL_BACK
#define AST_RIGHT BUTTON_SCROLL_FWD
#define AST_FIRE BUTTON_SELECT
#elif (CONFIG_KEYPAD == SANSA_C200_PAD)
#define AST_PAUSE BUTTON_REC
#define AST_QUIT BUTTON_POWER
#define AST_THRUST BUTTON_UP
#define AST_HYPERSPACE BUTTON_DOWN
#define AST_LEFT BUTTON_LEFT
#define AST_RIGHT BUTTON_RIGHT
#define AST_FIRE BUTTON_SELECT
#elif (CONFIG_KEYPAD == SANSA_CLIP_PAD)
#define AST_PAUSE BUTTON_HOME
#define AST_QUIT BUTTON_POWER
#define AST_THRUST BUTTON_UP
#define AST_HYPERSPACE BUTTON_DOWN
#define AST_LEFT BUTTON_LEFT
#define AST_RIGHT BUTTON_RIGHT
#define AST_FIRE BUTTON_SELECT
#elif (CONFIG_KEYPAD == SANSA_M200_PAD)
#define AST_PAUSE (BUTTON_SELECT | BUTTON_UP)
#define AST_QUIT BUTTON_POWER
#define AST_THRUST BUTTON_UP
#define AST_HYPERSPACE BUTTON_DOWN
#define AST_LEFT BUTTON_LEFT
#define AST_RIGHT BUTTON_RIGHT
#define AST_FIRE (BUTTON_SELECT | BUTTON_REL)
#elif (CONFIG_KEYPAD == IRIVER_H10_PAD)
#define AST_PAUSE BUTTON_PLAY
#define AST_QUIT BUTTON_POWER
#define AST_THRUST BUTTON_SCROLL_UP
#define AST_HYPERSPACE BUTTON_SCROLL_DOWN
#define AST_LEFT BUTTON_LEFT
#define AST_RIGHT BUTTON_RIGHT
#define AST_FIRE BUTTON_REW
#elif (CONFIG_KEYPAD == GIGABEAT_S_PAD)
#define AST_PAUSE BUTTON_PLAY
#define AST_QUIT BUTTON_BACK
#define AST_THRUST BUTTON_UP
#define AST_HYPERSPACE BUTTON_DOWN
#define AST_LEFT BUTTON_LEFT
#define AST_RIGHT BUTTON_RIGHT
#define AST_FIRE BUTTON_SELECT
#elif (CONFIG_KEYPAD == MROBE100_PAD)
#define AST_PAUSE BUTTON_DISPLAY
#define AST_QUIT BUTTON_POWER
#define AST_THRUST BUTTON_UP
#define AST_HYPERSPACE BUTTON_DOWN
#define AST_LEFT BUTTON_LEFT
#define AST_RIGHT BUTTON_RIGHT
#define AST_FIRE BUTTON_SELECT
#elif CONFIG_KEYPAD == IAUDIO_M3_PAD
#define AST_PAUSE BUTTON_RC_PLAY
#define AST_QUIT BUTTON_RC_REC
#define AST_THRUST BUTTON_RC_VOL_UP
#define AST_HYPERSPACE BUTTON_RC_VOL_DOWN
#define AST_LEFT BUTTON_RC_REW
#define AST_RIGHT BUTTON_RC_FF
#define AST_FIRE BUTTON_RC_MODE
#elif (CONFIG_KEYPAD == COWON_D2_PAD)
#define AST_QUIT BUTTON_POWER
#elif CONFIG_KEYPAD == CREATIVEZVM_PAD
#define AST_PAUSE BUTTON_PLAY
#define AST_QUIT BUTTON_BACK
#define AST_THRUST BUTTON_UP
#define AST_HYPERSPACE BUTTON_DOWN
#define AST_LEFT BUTTON_LEFT
#define AST_RIGHT BUTTON_RIGHT
#define AST_FIRE BUTTON_SELECT
#elif CONFIG_KEYPAD == CREATIVE_ZENXFI3_PAD
#define AST_PAUSE (BUTTON_PLAY | BUTTON_REL)
#define AST_QUIT BUTTON_POWER
#define AST_THRUST BUTTON_UP
#define AST_HYPERSPACE BUTTON_DOWN
#define AST_LEFT BUTTON_BACK
#define AST_RIGHT BUTTON_MENU
#define AST_FIRE BUTTON_VOL_UP
#elif CONFIG_KEYPAD == PHILIPS_HDD1630_PAD
#define AST_PAUSE BUTTON_VIEW
#define AST_QUIT BUTTON_POWER
#define AST_THRUST BUTTON_UP
#define AST_HYPERSPACE BUTTON_DOWN
#define AST_LEFT BUTTON_LEFT
#define AST_RIGHT BUTTON_RIGHT
#define AST_FIRE BUTTON_PLAYLIST
#elif CONFIG_KEYPAD == PHILIPS_HDD6330_PAD
#define AST_PAUSE BUTTON_PLAY
#define AST_QUIT BUTTON_POWER
#define AST_THRUST BUTTON_UP
#define AST_HYPERSPACE BUTTON_DOWN
#define AST_LEFT BUTTON_LEFT
#define AST_RIGHT BUTTON_RIGHT
#define AST_FIRE BUTTON_VOL_DOWN
#elif CONFIG_KEYPAD == PHILIPS_SA9200_PAD
#define AST_PAUSE BUTTON_RIGHT
#define AST_QUIT BUTTON_POWER
#define AST_THRUST BUTTON_UP
#define AST_HYPERSPACE BUTTON_DOWN
#define AST_LEFT BUTTON_PREV
#define AST_RIGHT BUTTON_NEXT
#define AST_FIRE BUTTON_LEFT
#elif (CONFIG_KEYPAD == ONDAVX747_PAD) || \
(CONFIG_KEYPAD == ONDAVX777_PAD) || \
(CONFIG_KEYPAD == MROBE500_PAD)
#define AST_QUIT BUTTON_POWER
#elif (CONFIG_KEYPAD == SAMSUNG_YH820_PAD) || \
(CONFIG_KEYPAD == SAMSUNG_YH92X_PAD)
#define AST_PAUSE BUTTON_FFWD
#define AST_QUIT BUTTON_REW
#define AST_THRUST BUTTON_UP
#define AST_HYPERSPACE BUTTON_DOWN
#define AST_LEFT BUTTON_LEFT
#define AST_RIGHT BUTTON_RIGHT
#define AST_FIRE BUTTON_PLAY
#elif (CONFIG_KEYPAD == PBELL_VIBE500_PAD)
#define AST_PAUSE BUTTON_PLAY
#define AST_QUIT BUTTON_REC
#define AST_THRUST BUTTON_UP
#define AST_HYPERSPACE BUTTON_DOWN
#define AST_LEFT BUTTON_PREV
#define AST_RIGHT BUTTON_NEXT
#define AST_FIRE BUTTON_OK
#elif (CONFIG_KEYPAD == MPIO_HD200_PAD)
#define AST_PAUSE (BUTTON_PLAY|BUTTON_FUNC)
#define AST_QUIT (BUTTON_REC|BUTTON_PLAY)
#define AST_THRUST BUTTON_REC
#define AST_HYPERSPACE BUTTON_PLAY
#define AST_LEFT BUTTON_REW
#define AST_RIGHT BUTTON_FF
#define AST_FIRE BUTTON_FUNC
#elif (CONFIG_KEYPAD == MPIO_HD300_PAD)
#define AST_PAUSE (BUTTON_PLAY|BUTTON_REL)
#define AST_QUIT (BUTTON_MENU|BUTTON_REPEAT)
#define AST_THRUST BUTTON_REC
#define AST_HYPERSPACE (BUTTON_PLAY|BUTTON_REPEAT)
#define AST_LEFT BUTTON_UP
#define AST_RIGHT BUTTON_DOWN
#define AST_FIRE BUTTON_ENTER
#elif (CONFIG_KEYPAD == SANSA_FUZEPLUS_PAD)
#define AST_PAUSE BUTTON_PLAYPAUSE
#define AST_QUIT BUTTON_POWER
#define AST_THRUST BUTTON_UP
#define AST_HYPERSPACE BUTTON_BACK
#define AST_LEFT BUTTON_LEFT
#define AST_RIGHT BUTTON_RIGHT
#define AST_FIRE BUTTON_SELECT
#elif (CONFIG_KEYPAD == SANSA_CONNECT_PAD)
#define ALT_PAUSE BUTTON_VOL_DOWN
#define AST_QUIT BUTTON_POWER
#define AST_THRUST BUTTON_UP
#define AST_HYPERSPACE BUTTON_DOWN
#define AST_LEFT BUTTON_LEFT
#define AST_RIGHT BUTTON_RIGHT
#define AST_FIRE BUTTON_SELECT
#elif (CONFIG_KEYPAD == SAMSUNG_YPR0_PAD)
#define AST_PAUSE BUTTON_MENU
#define AST_QUIT BUTTON_BACK
#define AST_THRUST BUTTON_UP
#define AST_HYPERSPACE BUTTON_DOWN
#define AST_LEFT BUTTON_LEFT
#define AST_RIGHT BUTTON_RIGHT
#define AST_FIRE BUTTON_SELECT
#elif (CONFIG_KEYPAD == HM60X_PAD)
#define AST_PAUSE (BUTTON_SELECT|BUTTON_POWER)
#define AST_QUIT BUTTON_POWER
#define AST_THRUST BUTTON_UP
#define AST_HYPERSPACE BUTTON_DOWN
#define AST_LEFT BUTTON_LEFT
#define AST_RIGHT BUTTON_RIGHT
#define AST_FIRE BUTTON_SELECT
#elif (CONFIG_KEYPAD == HM801_PAD)
#define AST_PAUSE BUTTON_PLAY
#define AST_QUIT BUTTON_POWER
#define AST_THRUST BUTTON_UP
#define AST_HYPERSPACE BUTTON_DOWN
#define AST_LEFT BUTTON_LEFT
#define AST_RIGHT BUTTON_RIGHT
#define AST_FIRE BUTTON_SELECT
#elif CONFIG_KEYPAD == SONY_NWZ_PAD
#define AST_PAUSE BUTTON_POWER
#define AST_QUIT BUTTON_BACK
#define AST_THRUST BUTTON_UP
#define AST_HYPERSPACE BUTTON_DOWN
#define AST_LEFT BUTTON_LEFT
#define AST_RIGHT BUTTON_RIGHT
#define AST_FIRE BUTTON_PLAY
#elif CONFIG_KEYPAD == CREATIVE_ZEN_PAD
#define AST_PAUSE BUTTON_PLAYPAUSE
#define AST_QUIT BUTTON_BACK
#define AST_THRUST BUTTON_UP
#define AST_HYPERSPACE BUTTON_DOWN
#define AST_LEFT BUTTON_LEFT
#define AST_RIGHT BUTTON_RIGHT
#define AST_FIRE BUTTON_SELECT
Introducing Targets iBasso DX50 & iBasso DX90 The port to for this two targets has been entirely developped by Ilia Sergachev (alias Il or xzcc). His source can be found at https://bitbucket.org/isergachev/rockbox . The few necesary modifications for the DX90 port was done by headwhacker form head-fi.org. Unfortunately i could not try out the final state of the DX90 port. The port is hosted on android (without java) as standalone app. The official Firmware is required to run this port. Ilia did modify the source files for the "android" target in the rockbox source to make the DX port work. The work I did was to separate the code for DX50 (&DX90) from the android target. On this Target Ilia used source from tinyalsa from AOSP. I did not touch that part of the code because I do not understand it. What else I changed from Ilias sources besides the separation from the target "android": * removed a dirty hack to keep backlight off * changed value battery meter to voltage battery meter * made all plugins compile (named target as "standalone") and added keymaps * i added the graphics for the manual but did not do anything else for the manual yet * minor optimizations known bugs: * timers are slowed donw when playback is active (tinyalsa related?) * some minor bugs Things to do: * The main prolem will be how to install the app correctly. A guy called DOC2008 added a CWM (by androtab.info) to the official firmware and Ilia made a CWM installation script and a dualboot selector (rbutils/ibassoboot, build with ndk-build). We will have to find a way to install rockbox in a proper way without breaking any copyrights. Maybe ADB is an option but it is not enable with OF by default. Patching the OF is probably the way to go. * All the wiki and manual to build: needed: android ndk installed, android sdk installed with additional build-tools 19.1.0 installed ./tools/configure select iBasso DX50 or iBasso DX90 make -j apk the content of rockbox.zip/.rockbox needs to be copied to /system/rockbox/app_rockbox/rockbox/ (rockbox app not needed) the content of libs/armeabi to /system/rockbox/lib/ (rockbox app needed) The boot selector is needed as /system/bin/MangoPlayer and the iBasso app as /system/bin/MangoPlayer_original. There is also the "vold" file. The one from OF does not work with DX50 rockbox (DX90 works!?), the one from Ilia is necessary. Until we have found a proper way to install it, it can only be installed following the instructions of Ilia on his bitbucket page, using the CWM-OF and his installation script package. Change-Id: Ic4faaf84824c162aabcc08e492cee6e0068719d0 Reviewed-on: http://gerrit.rockbox.org/941 Tested: Chiwen Chang <rock1104.tw@yahoo.com.tw> Reviewed-by: Michael Giacomelli <giac2000@hotmail.com>
2014-08-30 11:15:53 +00:00
#elif (CONFIG_KEYPAD == DX50_PAD)
#define AST_QUIT BUTTON_POWER
#define AST_THRUST BUTTON_VOL_UP
#define AST_HYPERSPACE BUTTON_VOL_DOWN
#define AST_LEFT BUTTON_LEFT
#define AST_RIGHT BUTTON_PLAY
#define AST_FIRE BUTTON_RIGHT
#elif CONFIG_KEYPAD == CREATIVE_ZENXFI2_PAD
#define AST_QUIT BUTTON_POWER
#elif (CONFIG_KEYPAD == AGPTEK_ROCKER_PAD)
#define AST_QUIT BUTTON_POWER
#define AST_THRUST BUTTON_UP
#define AST_HYPERSPACE BUTTON_VOLUP
#define AST_LEFT BUTTON_LEFT
#define AST_RIGHT BUTTON_RIGHT
#define AST_FIRE BUTTON_SELECT
#elif (CONFIG_KEYPAD == XDUOO_X3_PAD) || (CONFIG_KEYPAD == XDUOO_X3II_PAD) || (CONFIG_KEYPAD == XDUOO_X20_PAD)
#define AST_PAUSE BUTTON_VOL_UP
#define AST_QUIT BUTTON_POWER
#define AST_THRUST BUTTON_HOME
#define AST_HYPERSPACE BUTTON_OPTION
#define AST_LEFT BUTTON_PREV
#define AST_RIGHT BUTTON_NEXT
#define AST_FIRE BUTTON_PLAY
#elif (CONFIG_KEYPAD == FIIO_M3K_LINUX_PAD)
#define AST_PAUSE BUTTON_VOL_UP
#define AST_QUIT BUTTON_POWER
#define AST_THRUST BUTTON_HOME
#define AST_HYPERSPACE BUTTON_OPTION
#define AST_LEFT BUTTON_PREV
#define AST_RIGHT BUTTON_NEXT
#define AST_FIRE BUTTON_PLAY
#elif (CONFIG_KEYPAD == IHIFI_770_PAD) || (CONFIG_KEYPAD == IHIFI_800_PAD)
#define AST_PAUSE BUTTON_PLAY
#define AST_QUIT BUTTON_POWER
#define AST_THRUST BUTTON_NEXT
#define AST_HYPERSPACE BUTTON_PREV
#define AST_LEFT BUTTON_HOME
#define AST_RIGHT BUTTON_VOL_DOWN
#define AST_FIRE BUTTON_VOL_UP
#elif (CONFIG_KEYPAD == EROSQ_PAD)
#define AST_PAUSE BUTTON_PREV
#define AST_QUIT BUTTON_POWER
#define AST_THRUST BUTTON_PLAY
#define AST_HYPERSPACE BUTTON_MENU
#define AST_LEFT BUTTON_SCROLL_BACK
#define AST_RIGHT BUTTON_SCROLL_FWD
#define AST_FIRE BUTTON_BACK
#else
#error No keymap defined!
#endif
#ifdef HAVE_TOUCHSCREEN
#ifndef AST_PAUSE
#define AST_PAUSE BUTTON_CENTER
#endif
#ifndef AST_QUIT
#define AST_QUIT BUTTON_TOPLEFT
#endif
#ifndef AST_THRUST
#define AST_THRUST BUTTON_TOPMIDDLE
#endif
#ifndef AST_HYPERSPACE
#define AST_HYPERSPACE BUTTON_TOPRIGHT
#endif
#ifndef AST_LEFT
#define AST_LEFT BUTTON_MIDLEFT
#endif
#ifndef AST_RIGHT
#define AST_RIGHT BUTTON_MIDRIGHT
#endif
#ifndef AST_FIRE
#define AST_FIRE BUTTON_BOTTOMMIDDLE
#endif
#endif
#define RES MAX(LCD_WIDTH, LCD_HEIGHT)
#define LARGE_LCD (RES >= 200)
#define CYCLETIME 30
#define SHOW_COL 0
#define SCALE 5000
#define WRAP_GAP (LARGE*SCALE*3)
#define POINT_SIZE 2
#define START_LEVEL 1
#define SHOW_LEVEL_TIME 50
#define EXPLOSION_LENGTH 20
#define MAX_NUM_ASTEROIDS 25
#define MAX_NUM_MISSILES 6
#define NUM_STARS 50
#define NUM_TRAIL_POINTS 70
#define MAX_LEVEL MAX_NUM_ASTEROIDS
#define NUM_ASTEROID_VERTICES 10
#define NUM_SHIP_VERTICES 4
#define NUM_ENEMY_VERTICES 8
#define INVULNERABLE_TIME 30
#define BLINK_TIME 10
#define EXTRA_LIFE 250
#define START_LIVES 3
#define MISSILE_LIFE_LENGTH 40
#define ASTEROID_SPEED (RES/20)
#define SPACE_CHECK_SIZE 30*SCALE
#if (LARGE_LCD)
#define SIZE_SHIP_COLLISION 8*SCALE
#else
#define SIZE_SHIP_COLLISION 6*SCALE
#endif
#define LITTLE_SHIP 1
#define BIG_SHIP 2
#define ENEMY_BIG_PROBABILITY_START 10
#define ENEMY_APPEAR_PROBABILITY_START 35
#define ENEMY_APPEAR_TIMING_START 600
#define ENEMY_SPEED 4
#define ENEMY_MISSILE_LIFE_LENGTH (RES/2)
#if (LARGE_LCD)
#define SIZE_ENEMY_COLLISION 7*SCALE
#else
#define SIZE_ENEMY_COLLISION 5*SCALE
#endif
#define SIN_COS_SCALE 10000
#define FAST_ROT_CW_SIN 873
#define FAST_ROT_CW_COS 9963
#define FAST_ROT_ACW_SIN -873
#define FAST_ROT_ACW_COS 9963
#define MEDIUM_ROT_CW_SIN 350
#define MEDIUM_ROT_CW_COS 9994
#define MEDIUM_ROT_ACW_SIN -350
#define MEDIUM_ROT_ACW_COS 9994
#define SLOW_ROT_CW_SIN 350
#define SLOW_ROT_CW_COS 9994
#define SLOW_ROT_ACW_SIN -350
#define SLOW_ROT_ACW_COS 9994
#ifdef HAVE_LCD_COLOR
#define SHIP_ROT_CW_SIN 2419
#define SHIP_ROT_CW_COS 9702
#define SHIP_ROT_ACW_SIN -2419
#define SHIP_ROT_ACW_COS 9702
#else
#define SHIP_ROT_CW_SIN 3827
#define SHIP_ROT_CW_COS 9239
#define SHIP_ROT_ACW_SIN -3827
#define SHIP_ROT_ACW_COS 9239
#endif
#define SCALED_WIDTH (LCD_WIDTH*SCALE)
#define SCALED_HEIGHT (LCD_HEIGHT*SCALE)
#define CENTER_LCD_X (LCD_WIDTH/2)
#define CENTER_LCD_Y (LCD_HEIGHT/2)
#ifdef HAVE_LCD_COLOR
#define ASTEROID_R 230
#define ASTEROID_G 200
#define ASTEROID_B 100
#define SHIP_R 255
#define SHIP_G 255
#define SHIP_B 255
#define ENEMY_R 50
#define ENEMY_G 220
#define ENEMY_B 50
#define THRUST_R 200
#define THRUST_G 200
#define THRUST_B 0
#define COL_MISSILE LCD_RGBPACK(200,0,0)
#define COL_PLAYER LCD_RGBPACK(200,200,200)
#define COL_INVULN LCD_RGBPACK(100,100,200)
#define COL_STARS LCD_WHITE
#define COL_ASTEROID LCD_RGBPACK(ASTEROID_R,ASTEROID_G,ASTEROID_B)
#define COL_TEXT LCD_RGBPACK(200,200,255)
#define COL_ENEMY LCD_RGBPACK(ENEMY_R,ENEMY_G,ENEMY_B)
#define SET_FG rb->lcd_set_foreground
#define SET_BG rb->lcd_set_background
#else
#define SET_FG(x)
#define SET_BG(x)
#endif
#define SCORE_FILE PLUGIN_GAMES_DATA_DIR "/spacerocks.score"
#define NUM_SCORES 5
static struct highscore highscores[NUM_SCORES];
/* The array of points that make up an asteroid */
static const short asteroid_one[NUM_ASTEROID_VERTICES*2] =
{
-2, -12,
4, -8,
8, -14,
16, -5,
14, 0,
20, 2,
12, 14,
-4, 14,
-10, 6,
-10, -8,
};
/* The array of points that make up an asteroid */
static const short asteroid_two[NUM_ASTEROID_VERTICES*2] =
{
-2, -12,
4, -16,
6, -14,
16, -8,
14, 0,
20, 2,
12, 14,
-4, 14,
-10, 6,
-10, -8,
};
/* The array of points that make up an asteroid */
static const short asteroid_three[NUM_ASTEROID_VERTICES*2] =
{
-2, -12,
4, -16,
6, -14,
2, -8,
14, 0,
20, 2,
12, 14,
-4, 14,
-16, 6,
-10, -8,
};
/* The array of points the make up the ship */
static const short ship_vertices[NUM_SHIP_VERTICES*2] =
{
#if (LARGE_LCD)
0, -6,
4, 6,
0, 2,
-4, 6,
#else
0, -4,
3, 4,
0, 1,
-3, 4,
#endif
};
/* The array of points the make up the bad spaceship */
static const short enemy_vertices[NUM_ENEMY_VERTICES*2] =
{
#if (LARGE_LCD)
-8, 0,
-4, 4,
4, 4,
8, 0,
-8, 0,
8, 0,
4, -4,
-4, -4,
#else
-5, 0,
-2, 2,
2, 2,
5, 0,
-5, 0,
5, 0,
2, -2,
-2, -2,
#endif
};
enum asteroid_type
{
#if (LARGE_LCD)
SMALL = 2,
MEDIUM = 4,
LARGE = 6,
#else
SMALL = 1,
MEDIUM = 2,
LARGE = 3,
#endif
};
enum explosion_type
{
EXPLOSION_SHIP,
EXPLOSION_ASTEROID,
EXPLOSION_ENEMY,
EXPLOSION_THRUST,
};
enum game_state
{
GAME_OVER,
SHOW_LEVEL,
PLAY_MODE,
PAUSE_MODE,
};
struct Point
{
int x;
int y;
int dx;
int dy;
};
struct TrailPoint
{
struct Point position;
int alive;
#ifdef HAVE_LCD_COLOR
short r;
short g;
short b;
short dec;
#endif
};
/* Asteroid structure, contains an array of points */
struct Asteroid
{
struct Point position;
struct Point rotation;
struct Point vertices[NUM_ASTEROID_VERTICES];
bool exists;
int explode_countdown;
enum asteroid_type type;
int radius;
long speed_cos;
long speed_sin;
};
struct Ship
{
struct Point position;
struct Point rotation;
struct Point vertices[NUM_SHIP_VERTICES];
bool exists;
int explode_countdown;
int invulnerable_time;
};
struct Enemy
{
struct Point position;
struct Point vertices[NUM_ENEMY_VERTICES];
bool exists;
int explode_countdown;
int appear_countdown;
short size_probability;
short appear_probability;
short appear_timing;
};
struct Missile
{
struct Point position;
struct Point oldpoint;
int alive;
};
static enum game_state game_state;
static int asteroid_count;
static int next_missile_count;
static int next_thrust_count;
static int num_lives;
static int extra_life;
static int show_level_timeout;
static int current_level;
static int current_score;
static struct Ship ship;
static struct Point stars[NUM_STARS];
static struct Asteroid asteroids_array[MAX_NUM_ASTEROIDS];
static struct Missile missiles_array[MAX_NUM_MISSILES];
static struct Missile enemy_missile;
static struct Enemy enemy;
static struct Point lives_points[NUM_SHIP_VERTICES];
static struct TrailPoint trail_points[NUM_TRAIL_POINTS];
/*************************************************
** Handle polygon and point
*************************************************/
/* Check if point is in a polygon */
static bool is_point_in_polygon(struct Point* vertices, int num_vertices,
int x, int y)
{
struct Point* pi;
struct Point* pj;
int n;
bool c = false;
if (x < -SCALED_WIDTH/2) x += SCALED_WIDTH;
else if (x > SCALED_WIDTH/2) x -= SCALED_WIDTH;
if (y < -SCALED_HEIGHT/2) y += SCALED_HEIGHT;
else if (y > SCALED_HEIGHT/2) y -= SCALED_HEIGHT;
pi = vertices;
pj = vertices + num_vertices-1;
n = num_vertices;
while (n--)
{
if ((((pi->y <= y) && (y < pj->y)) || ((pj->y <= y) && (y < pi->y))) &&
(x < (pj->x - pi->x) * (y - pi->y) / (pj->y - pi->y) + pi->x))
c = !c;
pj = pi;
pi++;
}
return c;
}
/* Check if point is within a rectangle */
static bool is_point_within_rectangle(struct Point* rect, struct Point* p,
int size)
{
int dx = p->x - rect->x;
int dy = p->y - rect->y;
#if SHOW_COL
rb->lcd_drawrect((rect->x - size)/SCALE, (rect->y - size)/SCALE,
(size*2+1)/SCALE, (size*2+1)/SCALE);
#endif
if (dx < -SCALED_WIDTH/2) dx += SCALED_WIDTH;
else if (dx > SCALED_WIDTH/2) dx -= SCALED_WIDTH;
if (dy < -SCALED_HEIGHT/2) dy += SCALED_HEIGHT;
else if (dy > SCALED_HEIGHT/2) dy -= SCALED_HEIGHT;
return (dx > -size && dx < size && dy > -size && dy < size);
}
/* Rotate polygon */
static void rotate_polygon(struct Point* vertices, int num_vertices,
struct Point* rotation, int cos, int sin)
{
struct Point* point;
int n;
long temp_x, temp_y;
temp_x = rotation->x;
temp_y = rotation->y;
rotation->x = (temp_x*cos - temp_y*sin)/SIN_COS_SCALE;
rotation->y = (temp_y*cos + temp_x*sin)/SIN_COS_SCALE;
#define MIN_SCALE (SIN_COS_SCALE-10)
#define MAX_SCALE (SIN_COS_SCALE+10)
/* normalize vector. this is not accurate but would be enough. */
temp_x = rotation->x*rotation->x + rotation->y*rotation->y;
if (temp_x <= MIN_SCALE*MIN_SCALE)
{
rotation->x = rotation->x*SIN_COS_SCALE/MIN_SCALE;
rotation->y = rotation->y*SIN_COS_SCALE/MIN_SCALE;
}
else if (temp_x >= MAX_SCALE*MAX_SCALE)
{
rotation->x = rotation->x*SIN_COS_SCALE/MAX_SCALE;
rotation->y = rotation->y*SIN_COS_SCALE/MAX_SCALE;
}
#undef MIN_SCALE
#undef MAX_SCALE
point = vertices;
n = num_vertices;
while (n--)
{
point->x = (point->dx*rotation->x - point->dy*rotation->y)/SIN_COS_SCALE;
point->y = (point->dy*rotation->x + point->dx*rotation->y)/SIN_COS_SCALE;
point++;
}
}
/* Draw polygon */
static void draw_polygon(struct Point* vertices, int num_vertices,
int px, int py)
{
int n, new_x, new_y, old_x, old_y;
struct Point *p;
bool draw_wrap;
if (px > SCALED_WIDTH - WRAP_GAP)
px -= SCALED_WIDTH;
if (py > SCALED_HEIGHT - WRAP_GAP)
py -= SCALED_HEIGHT;
draw_wrap = (px < WRAP_GAP || py < WRAP_GAP);
p = vertices + num_vertices - 1;
old_x = (p->x + px)/SCALE;
old_y = (p->y + py)/SCALE;
p = vertices;
n = num_vertices;
while (n--)
{
new_x = (p->x + px)/SCALE;
new_y = (p->y + py)/SCALE;
rb->lcd_drawline(old_x, old_y, new_x, new_y);
if (draw_wrap)
{
rb->lcd_drawline(old_x + LCD_WIDTH, old_y, new_x + LCD_WIDTH, new_y);
rb->lcd_drawline(old_x, old_y + LCD_HEIGHT, new_x, new_y + LCD_HEIGHT);
rb->lcd_drawline(old_x + LCD_WIDTH, old_y + LCD_HEIGHT,
new_x + LCD_WIDTH, new_y + LCD_HEIGHT);
}
old_x = new_x;
old_y = new_y;
p++;
}
}
static void move_point(struct Point* point)
{
point->x += point->dx;
point->y += point->dy;
/* Check bounds on the x-axis: */
point->x %= SCALED_WIDTH;
if (point->x < 0)
point->x += SCALED_WIDTH;
/* Check bounds on the y-axis: */
point->y %= SCALED_HEIGHT;
if (point->y < 0)
point->y += SCALED_HEIGHT;
}
/*************************************************
** Handle trail blaiz.
*************************************************/
static void create_ship_trail(struct TrailPoint* tpoint)
{
tpoint->position.x += ship.vertices[2].x;
tpoint->position.y += ship.vertices[2].y;
tpoint->position.dx = -( ship.vertices[0].x - ship.vertices[2].x )/10;
tpoint->position.dy = -( ship.vertices[0].y - ship.vertices[2].y )/10;
}
static void create_explosion_trail(struct TrailPoint* tpoint)
{
tpoint->position.dx = (rb->rand()%5001)-2500;
tpoint->position.dy = (rb->rand()%5001)-2500;
}
static void create_trail_blaze(int colour, struct Point* position)
{
int numtoadd;
struct TrailPoint* tpoint;
int n;
if (colour != EXPLOSION_SHIP)
{
numtoadd = NUM_TRAIL_POINTS/5;
}
else
{
numtoadd = NUM_TRAIL_POINTS/8;
}
/* give the point a random countdown timer, so they dissapears at different
times */
tpoint = trail_points;
n = NUM_TRAIL_POINTS;
while (n--)
{
/* find a space in the array of trail_points that is NULL or DEAD or
whatever and place this one here. */
if (tpoint->alive <= 0)
{
/* take a random point near the position. */
tpoint->position.x = (rb->rand()%18000)-9000 + position->x;
tpoint->position.y = (rb->rand()%18000)-9000 + position->y;
switch(colour)
{
case EXPLOSION_SHIP:
create_explosion_trail(tpoint);
tpoint->alive = 51;
#ifdef HAVE_LCD_COLOR
tpoint->r = SHIP_R;
tpoint->g = SHIP_G;
tpoint->b = SHIP_B;
tpoint->dec = 2;
#endif
break;
case EXPLOSION_ASTEROID:
create_explosion_trail(tpoint);
tpoint->alive = 51;
#ifdef HAVE_LCD_COLOR
tpoint->r = ASTEROID_R;
tpoint->g = ASTEROID_G;
tpoint->b = ASTEROID_B;
tpoint->dec = 2;
#endif
break;
case EXPLOSION_ENEMY:
create_explosion_trail(tpoint);
tpoint->alive = 51;
#ifdef HAVE_LCD_COLOR
tpoint->r = ENEMY_R;
tpoint->g = ENEMY_G;
tpoint->b = ENEMY_B;
tpoint->dec = 2;
#endif
break;
case EXPLOSION_THRUST:
create_ship_trail(tpoint);
tpoint->alive = 17;
#ifdef HAVE_LCD_COLOR
tpoint->r = THRUST_R;
tpoint->g = THRUST_G;
tpoint->b = THRUST_B;
tpoint->dec = 4;
#endif
break;
}
/* give the points a speed based on direction of travel
- i.e. opposite */
tpoint->position.dx += position->dx;
tpoint->position.dy += position->dy;
numtoadd--;
if (numtoadd <= 0)
break;
}
tpoint++;
}
}
static void draw_and_move_trail_blaze(void)
{
struct TrailPoint* tpoint;
int n;
/* loop through, if alive then move and draw.
when drawn, countdown it's timer.
if zero kill it! */
tpoint = trail_points;
n = NUM_TRAIL_POINTS;
while (n--)
{
if (tpoint->alive > 0)
{
if (game_state != PAUSE_MODE)
{
tpoint->alive--;
move_point(&(tpoint->position));
#ifdef HAVE_LCD_COLOR
/* intensity = tpoint->alive/2; */
if (tpoint->r >= tpoint->dec) tpoint->r -= tpoint->dec;
if (tpoint->g >= tpoint->dec) tpoint->g -= tpoint->dec;
if (tpoint->b >= tpoint->dec) tpoint->b -= tpoint->dec;
#endif
}
SET_FG(LCD_RGBPACK(tpoint->r, tpoint->g, tpoint->b));
rb->lcd_drawpixel(tpoint->position.x/SCALE, tpoint->position.y/SCALE);
}
tpoint++;
}
}
/*************************************************
** Handle asteroid.
*************************************************/
static void rotate_asteroid(struct Asteroid* asteroid)
{
rotate_polygon(asteroid->vertices, NUM_ASTEROID_VERTICES,
&asteroid->rotation,
asteroid->speed_cos, asteroid->speed_sin);
}
/* Initialise the passed Asteroid.
* if position is NULL, place it at the random loacation
* where ship doesn't exist
*/
static void initialise_asteroid(struct Asteroid* asteroid,
enum asteroid_type type, struct Point *position)
{
const short *asteroid_vertices;
struct Point* point;
int n;
asteroid->exists = true;
asteroid->explode_countdown = 0;
asteroid->type = type;
/* Set the radius of the asteroid: */
asteroid->radius = (int)type*SCALE*3;
/* shall we move Clockwise and Fast */
n = rb->rand()%100;
if (n < 25)
{
asteroid->speed_cos = FAST_ROT_CW_COS;
asteroid->speed_sin = FAST_ROT_CW_SIN;
}
else if (n < 50)
{
asteroid->speed_cos = FAST_ROT_ACW_COS;
asteroid->speed_sin = FAST_ROT_ACW_SIN;
}
else if (n < 75)
{
asteroid->speed_cos = SLOW_ROT_ACW_COS;
asteroid->speed_sin = SLOW_ROT_ACW_SIN;
}
else
{
asteroid->speed_cos = SLOW_ROT_CW_COS;
asteroid->speed_sin = SLOW_ROT_CW_SIN;
}
n = rb->rand()%99;
if (n < 33)
asteroid_vertices = asteroid_one;
else if (n < 66)
asteroid_vertices = asteroid_two;
else
asteroid_vertices = asteroid_three;
point = asteroid->vertices;
for(n = 0; n < NUM_ASTEROID_VERTICES*2; n += 2)
{
point->x = asteroid_vertices[n];
point->y = asteroid_vertices[n+1];
point->x *= asteroid->radius/20;
point->y *= asteroid->radius/20;
/* dx and dy are used when rotate polygon */
point->dx = point->x;
point->dy = point->y;
point++;
}
if (!position)
{
do {
/* Set the position randomly: */
asteroid->position.x = (rb->rand()%SCALED_WIDTH);
asteroid->position.y = (rb->rand()%SCALED_HEIGHT);
} while (is_point_within_rectangle(&ship.position, &asteroid->position,
SPACE_CHECK_SIZE));
}
else
{
asteroid->position.x = position->x;
asteroid->position.y = position->y;
}
do {
asteroid->position.dx = (rb->rand()%ASTEROID_SPEED)-ASTEROID_SPEED/2;
} while (asteroid->position.dx == 0);
do {
asteroid->position.dy = (rb->rand()%ASTEROID_SPEED)-ASTEROID_SPEED/2;
} while (asteroid->position.dy == 0);
asteroid->position.dx *= SCALE/10;
asteroid->position.dy *= SCALE/10;
asteroid->rotation.x = SIN_COS_SCALE;
asteroid->rotation.y = 0;
/* Now rotate the asteroid a bit, so they all look a bit different */
for(n = (rb->rand()%30)+2; n--; )
rotate_asteroid(asteroid);
/* great, we've created an asteroid, don't forget to increment the total: */
asteroid_count++;
}
/*
* Creates a new asteroid of the given 4type (size) and at the given location.
*/
static void create_asteroid(enum asteroid_type type, struct Point *position)
{
struct Asteroid* asteroid;
int n;
asteroid = asteroids_array;
n = MAX_NUM_ASTEROIDS;
while (n--)
{
if (!asteroid->exists && asteroid->explode_countdown <= 0)
{
initialise_asteroid(asteroid, type, position);
break;
}
asteroid++;
}
}
/* Draw and move all asteroids */
static void draw_and_move_asteroids(void)
{
struct Asteroid* asteroid;
int n;
SET_FG(COL_ASTEROID);
asteroid = asteroids_array;
n = MAX_NUM_ASTEROIDS;
while (n--)
{
if (asteroid->exists)
{
draw_polygon(asteroid->vertices, NUM_ASTEROID_VERTICES,
asteroid->position.x, asteroid->position.y);
}
if (game_state != PAUSE_MODE)
{
if (asteroid->exists)
{
move_point(&asteroid->position);
rotate_asteroid(asteroid);
}
else if (asteroid->explode_countdown > 0)
{
asteroid->explode_countdown--;
}
}
asteroid++;
}
}
static void explode_asteroid(struct Asteroid* asteroid)
{
struct Point p;
p.dx = asteroid->position.dx;
p.dy = asteroid->position.dy;
p.x = asteroid->position.x;
p.y = asteroid->position.y;
asteroid_count--;
asteroid->exists = false;
switch(asteroid->type)
{
case SMALL:
asteroid->explode_countdown = EXPLOSION_LENGTH;
create_trail_blaze(EXPLOSION_ASTEROID, &p);
break;
case MEDIUM:
create_asteroid(SMALL, &p);
create_asteroid(SMALL, &p);
break;
case LARGE:
create_asteroid(MEDIUM, &p);
create_asteroid(MEDIUM, &p);
break;
}
}
/*************************************************
** Handle ship.
*************************************************/
/* Initialise the ship */
static void initialise_ship(void)
{
struct Point* point;
struct Point* lives_point;
int n;
ship.position.x = CENTER_LCD_X * SCALE;
ship.position.y = CENTER_LCD_Y * SCALE;
ship.position.dx = 0;
ship.position.dy = 0;
ship.rotation.x = SIN_COS_SCALE;
ship.rotation.y = 0;
ship.exists = true;
ship.explode_countdown = 0;
ship.invulnerable_time = INVULNERABLE_TIME;
point = ship.vertices;
lives_point = lives_points;
for(n = 0; n < NUM_SHIP_VERTICES*2; n += 2)
{
point->x = ship_vertices[n];
point->y = ship_vertices[n+1];
point->x *= SCALE;
point->y *= SCALE;
/* dx and dy are used when rotate polygon */
point->dx = point->x;
point->dy = point->y;
/* grab a copy of the ships points for the lives display: */
lives_point->x = point->x;
lives_point->y = point->y;
point++;
lives_point++;
}
}
/*
* Draws the ship, moves the ship and creates a new
* one if it's finished exploding.
*/
static void draw_and_move_ship(void)
{
if (ship.invulnerable_time > BLINK_TIME || ship.invulnerable_time % 2 != 0)
{
SET_FG(COL_INVULN);
}
else
{
SET_FG(COL_PLAYER);
}
if (ship.exists)
{
draw_polygon(ship.vertices, NUM_SHIP_VERTICES,
ship.position.x, ship.position.y);
}
if (game_state != PAUSE_MODE)
{
if (ship.exists)
{
if (ship.invulnerable_time > 0)
ship.invulnerable_time--;
move_point(&ship.position);
}
else if (ship.explode_countdown > 0)
{
ship.explode_countdown--;
if (ship.explode_countdown <= 0)
{
num_lives--;
if (num_lives <= 0)
{
game_state = GAME_OVER;
}
else
{
initialise_ship();
}
}
}
}
}
static void explode_ship(void)
{
if (!ship.invulnerable_time)
{
/* if not invulnerable, blow up ship */
ship.explode_countdown = EXPLOSION_LENGTH;
ship.exists = false;
create_trail_blaze(EXPLOSION_SHIP, &ship.position);
}
}
/* Rotate the ship using the passed sin & cos values */
static void rotate_ship(int cos, int sin)
{
if (ship.exists)
{
rotate_polygon(ship.vertices, NUM_SHIP_VERTICES,
&ship.rotation, cos, sin);
}
}
static void thrust_ship(void)
{
if (ship.exists)
{
ship.position.dx += ( ship.vertices[0].x - ship.vertices[2].x )/20;
ship.position.dy += ( ship.vertices[0].y - ship.vertices[2].y )/20;
/* if dx and dy are below a certain threshold, then set 'em to 0
but to do this we need to ascertain if the spacehip as moved on
screen for more than a certain amount. */
create_trail_blaze(EXPLOSION_THRUST, &ship.position);
}
}
/* stop movement of ship, 'cos that's what happens when you go into hyperspace. */
static void hyperspace(void)
{
if (ship.exists)
{
ship.position.dx = ship.position.dy = 0;
ship.position.x = (rb->rand()%SCALED_WIDTH);
ship.position.y = (rb->rand()%SCALED_HEIGHT);
}
}
static void draw_lives(void)
{
int n;
#if (LARGE_LCD)
int px = (LCD_WIDTH-1 - 4)*SCALE;
int py = (LCD_HEIGHT-1 - 6)*SCALE;
#else
int px = (LCD_WIDTH-1 - 3)*SCALE;
int py = (LCD_HEIGHT-1 - 4)*SCALE;
#endif
SET_FG(COL_PLAYER);
n = num_lives-1;
while (n--)
{
draw_polygon(lives_points, NUM_SHIP_VERTICES, px, py);
#if (LARGE_LCD)
px -= 8*SCALE;
#else
px -= 6*SCALE;
#endif
}
}
/*
* missile
*/
/* Initialise a missile */
static void initialise_missile(struct Missile* missile)
{
missile->position.x = ship.position.x + ship.vertices[0].x;
missile->position.y = ship.position.y + ship.vertices[0].y;
missile->position.dx = (ship.vertices[0].x - ship.vertices[2].x)/2;
missile->position.dy = (ship.vertices[0].y - ship.vertices[2].y)/2;
missile->alive = MISSILE_LIFE_LENGTH;
missile->oldpoint.x = missile->position.x;
missile->oldpoint.y = missile->position.y;
}
/* Fire the next missile */
static void fire_missile(void)
{
struct Missile* missile;
int n;
if (ship.exists)
{
missile = missiles_array;
n = MAX_NUM_MISSILES;
while (n--)
{
if (missile->alive <= 0)
{
initialise_missile(missile);
break;
}
missile++;
}
}
}
/* Draw and Move all the missiles */
static void draw_and_move_missiles(void)
{
struct Missile* missile;
struct Point vertices[2];
int n;
SET_FG(COL_MISSILE);
missile = missiles_array;
n = MAX_NUM_MISSILES;
while (n--)
{
if (missile->alive > 0)
{
vertices[0].x = 0;
vertices[0].y = 0;
vertices[1].x = -missile->position.dx;
vertices[1].y = -missile->position.dy;
draw_polygon(vertices, 2, missile->position.x, missile->position.y);
if (game_state != PAUSE_MODE)
{
missile->oldpoint.x = missile->position.x;
missile->oldpoint.y = missile->position.y;
move_point(&missile->position);
missile->alive--;
}
}
missile++;
}
}
/*************************************************
** Handle enemy.
*************************************************/
static void initialise_enemy(void)
{
struct Point* point;
int n;
int size;
if (rb->rand()%100 > enemy.size_probability)
{
size = BIG_SHIP;
enemy.size_probability++;
if (enemy.size_probability > 90)
{
enemy.size_probability = ENEMY_BIG_PROBABILITY_START;
}
}
else
{
size = LITTLE_SHIP;
enemy.size_probability = ENEMY_BIG_PROBABILITY_START;
}
enemy.exists = true;
enemy.explode_countdown = 0;
enemy.appear_countdown = enemy.appear_timing;
point = enemy.vertices;
for(n = 0; n < NUM_ENEMY_VERTICES*2; n += 2)
{
point->x = enemy_vertices[n];
point->y = enemy_vertices[n+1];
point->x *= size*SCALE/2;
point->y *= size*SCALE/2;
point++;
}
if (ship.position.x >= SCALED_WIDTH/2)
{
enemy.position.dx = ENEMY_SPEED;
enemy.position.x = 0;
}
else
{
enemy.position.dx = -ENEMY_SPEED;
enemy.position.x = SCALED_WIDTH;
}
if (ship.position.y >= SCALED_HEIGHT/2)
{
enemy.position.dy = ENEMY_SPEED;
enemy.position.y = 0;
}
else
{
enemy.position.dy = -ENEMY_SPEED;
enemy.position.y = SCALED_HEIGHT;
}
enemy.position.dx *= SCALE/10;
enemy.position.dy *= SCALE/10;
}
static void draw_and_move_enemy(void)
{
SET_FG(COL_ENEMY);
if (enemy.exists)
{
draw_polygon(enemy.vertices, NUM_ENEMY_VERTICES,
enemy.position.x, enemy.position.y);
}
if (game_state != PAUSE_MODE)
{
if (enemy.exists)
{
enemy.position.x += enemy.position.dx;
enemy.position.y += enemy.position.dy;
if (enemy.position.x > SCALED_WIDTH || enemy.position.x < 0)
enemy.exists = false;
enemy.position.y %= SCALED_HEIGHT;
if (enemy.position.y < 0)
enemy.position.y += SCALED_HEIGHT;
if ((rb->rand()%1000) < 10)
enemy.position.dy = -enemy.position.dy;
}
else if (enemy.explode_countdown > 0)
{
enemy.explode_countdown--;
}
else
{
if (enemy.appear_countdown > 0)
enemy.appear_countdown--;
else if (rb->rand()%100 >= enemy.appear_probability)
initialise_enemy();
}
}
if (enemy_missile.alive <= 0)
{
/* if no missile and the enemy is here and not exploding..
then shoot baby! */
if (enemy.exists && ship.exists &&
game_state == PLAY_MODE && (rb->rand()%10) >= 5 )
{
int dx = ship.position.x - enemy.position.x;
int dy = ship.position.y - enemy.position.y;
if (dx < -SCALED_WIDTH/2) dx += SCALED_WIDTH;
else if (dx > SCALED_WIDTH/2) dx -= SCALED_WIDTH;
if (dy < -SCALED_HEIGHT/2) dy += SCALED_HEIGHT;
else if (dy > SCALED_HEIGHT/2) dy -= SCALED_HEIGHT;
enemy_missile.position.x = enemy.position.x;
enemy_missile.position.y = enemy.position.y;
/* lame, needs to be sorted - it's trying to shoot at the ship */
if (dx < -5*SCALE)
enemy_missile.position.dx = -1;
else if (dx > 5*SCALE)
enemy_missile.position.dx = 1;
else
enemy_missile.position.dx = 0;
if (dy < -5*SCALE)
enemy_missile.position.dy = -1;
else if (dy > 5*SCALE)
enemy_missile.position.dy = 1;
else
enemy_missile.position.dy = 0;
while (enemy_missile.position.dx == 0 &&
enemy_missile.position.dy == 0)
{
enemy_missile.position.dx = rb->rand()%2-1;
enemy_missile.position.dy = rb->rand()%2-1;
}
enemy_missile.position.dx *= SCALE;
enemy_missile.position.dy *= SCALE;
enemy_missile.alive = ENEMY_MISSILE_LIFE_LENGTH;
}
}
else
{
rb->lcd_fillrect( enemy_missile.position.x/SCALE,
enemy_missile.position.y/SCALE,
POINT_SIZE, POINT_SIZE );
if (game_state != PAUSE_MODE)
{
move_point(&enemy_missile.position);
enemy_missile.alive--;
}
}
}
/*************************************************
** Check collisions.
*************************************************/
/* Add score if missile hit asteroid or enemy */
static void add_score(int val)
{
current_score += val;
if (current_score >= extra_life)
{
num_lives++;
extra_life += EXTRA_LIFE;
}
}
static bool is_point_within_asteroid(struct Asteroid* asteroid,
struct Point* point)
{
if (is_point_within_rectangle(&asteroid->position, point, asteroid->radius)
&& is_point_in_polygon(asteroid->vertices, NUM_ASTEROID_VERTICES,
point->x - asteroid->position.x,
point->y - asteroid->position.y))
{
explode_asteroid(asteroid);
return true;
}
else
return false;
}
static bool is_point_within_ship(struct Point* point)
{
if (is_point_within_rectangle(&ship.position, point, SIZE_SHIP_COLLISION)
&& is_point_in_polygon(ship.vertices, NUM_SHIP_VERTICES,
point->x - ship.position.x,
point->y - ship.position.y))
{
return true;
}
else
return false;
}
static bool is_point_within_enemy(struct Point* point)
{
if (is_point_within_rectangle(&enemy.position, point, SIZE_ENEMY_COLLISION))
{
add_score(5);
enemy.explode_countdown = EXPLOSION_LENGTH;
enemy.exists = false;
create_trail_blaze(EXPLOSION_ENEMY, &enemy.position);
return true;
}
else
return false;
}
static bool is_ship_within_asteroid(struct Asteroid* asteroid)
{
struct Point p;
if (!is_point_within_rectangle(&asteroid->position, &ship.position,
asteroid->radius+SIZE_SHIP_COLLISION))
return false;
p.x = ship.position.x + ship.vertices[0].x;
p.y = ship.position.y + ship.vertices[0].y;
if (is_point_within_asteroid(asteroid, &p))
return true;
p.x = ship.position.x + ship.vertices[1].x;
p.y = ship.position.y + ship.vertices[1].y;
if (is_point_within_asteroid(asteroid, &p))
return true;
p.x = ship.position.x + ship.vertices[3].x;
p.y = ship.position.y + ship.vertices[3].y;
if (is_point_within_asteroid(asteroid, &p))
return true;
return false;
}
/* Check for collsions between the missiles and the asteroids and the ship */
static void check_collisions(void)
{
struct Missile* missile;
struct Asteroid* asteroid;
int m, n;
bool asteroids_onscreen = false;
asteroid = asteroids_array;
m = MAX_NUM_ASTEROIDS;
while (m--)
{
/* if the asteroids exists then test missile collision: */
if (asteroid->exists)
{
missile = missiles_array;
n = MAX_NUM_MISSILES;
while (n--)
{
/* if the missiles exists: */
if (missile->alive > 0)
{
/* has the missile hit the asteroid? */
if (is_point_within_asteroid(asteroid, &missile->position) ||
is_point_within_asteroid(asteroid, &missile->oldpoint))
{
add_score(1);
missile->alive = 0;
break;
}
}
missile++;
}
/* now check collision with ship: */
if (asteroid->exists && ship.exists)
{
if (is_ship_within_asteroid(asteroid))
{
add_score(1);
explode_ship();
}
}
/* has the enemy missile blown something up? */
if (asteroid->exists && enemy_missile.alive > 0)
{
if (is_point_within_asteroid(asteroid, &enemy_missile.position))
{
enemy_missile.alive = 0;
}
}
}
/* is an asteroid still exploding? */
if (asteroid->explode_countdown > 0)
asteroids_onscreen = true;
asteroid++;
}
/* now check collision between ship and enemy */
if (enemy.exists && ship.exists)
{
/* has the enemy collided with the ship? */
if (is_point_within_enemy(&ship.position))
{
explode_ship();
create_trail_blaze(EXPLOSION_ENEMY, &enemy.position);
}
if (enemy.exists)
{
/* Now see if the enemy has been shot at by the ships missiles: */
missile = missiles_array;
n = MAX_NUM_MISSILES;
while (n--)
{
if (missile->alive > 0 &&
is_point_within_enemy(&missile->position))
{
missile->alive = 0;
break;
}
missile++;
}
}
}
/* test collision with enemy missile and ship: */
if (enemy_missile.alive > 0 && is_point_within_ship(&enemy_missile.position))
{
explode_ship();
enemy_missile.alive = 0;
enemy_missile.position.x = enemy_missile.position.y = 0;
}
/* if all asteroids cleared then start again: */
if (asteroid_count == 0 && !asteroids_onscreen
&& !enemy.exists && enemy.explode_countdown <= 0)
{
current_level++;
if (current_level > MAX_LEVEL)
current_level = START_LEVEL;
enemy.appear_probability += 5;
if (enemy.appear_probability >= 100)
enemy.appear_probability = ENEMY_APPEAR_PROBABILITY_START;
enemy.appear_timing -= 30;
if (enemy.appear_timing < 30)
enemy.appear_timing = 30;
game_state = SHOW_LEVEL;
show_level_timeout = SHOW_LEVEL_TIME;
}
}
/*
* stars
*/
static void create_stars(void)
{
struct Point* p;
int n;
p = stars;
n = NUM_STARS;
while (n--)
{
p->x = (rb->rand()%LCD_WIDTH);
p->y = (rb->rand()%LCD_HEIGHT);
p++;
}
}
static void drawstars(void)
{
struct Point* p;
int n;
SET_FG(COL_STARS);
p = stars;
n = NUM_STARS;
while (n--)
{
rb->lcd_drawpixel(p->x , p->y);
p++;
}
}
/*************************************************
** Creates start_num number of new asteroids of
** full size.
**************************************************/
static void initialise_level(int start_num)
{
struct Asteroid* asteroid;
struct Missile* missile;
struct TrailPoint* tpoint;
int n;
asteroid_count = next_missile_count = next_thrust_count = 0;
/* no enemy */
enemy.exists = 0;
enemy.explode_countdown = 0;
enemy_missile.alive = 0;
/* clear asteroids */
asteroid = asteroids_array;
n = MAX_NUM_ASTEROIDS;
while (n--)
{
asteroid->exists = false;
asteroid++;
}
/* make some LARGE asteroids */
for(n = 0; n < start_num; n++)
initialise_asteroid(&asteroids_array[n], LARGE, NULL);
/* ensure all missiles are out of action: */
missile = missiles_array;
n = MAX_NUM_MISSILES;
while (n--)
{
missile->alive = 0;
missile++;
}
tpoint = trail_points;
n = NUM_TRAIL_POINTS;
while (n--)
{
tpoint->alive = 0;
tpoint++;
}
}
static void initialise_game(void)
{
enemy.appear_probability = ENEMY_APPEAR_PROBABILITY_START;
enemy.appear_timing = ENEMY_APPEAR_TIMING_START;
enemy.appear_countdown = enemy.appear_timing;
enemy.size_probability = ENEMY_BIG_PROBABILITY_START;
current_level = START_LEVEL;
num_lives = START_LIVES;
extra_life = EXTRA_LIFE;
current_score = 0;
initialise_ship();
initialise_level(0);
game_state = SHOW_LEVEL;
show_level_timeout = SHOW_LEVEL_TIME;
}
/* menu stuff */
static bool spacerocks_help(void)
{
static char *help_text[] = {
"Spacerocks", "", "Aim", "",
"The", "goal", "of", "the", "game", "is", "to", "blow", "up",
"the", "asteroids", "and", "avoid", "being", "hit", "by", "them.",
"Also", "you'd", "better", "watch", "out", "for", "the", "UFOs!"
};
static struct style_text formation[]={
{ 0, TEXT_CENTER|TEXT_UNDERLINE },
{ 2, C_RED },
LAST_STYLE_ITEM
};
rb->lcd_setfont(FONT_UI);
SET_BG(LCD_BLACK);
SET_FG(LCD_WHITE);
if (display_text(ARRAYLEN(help_text), help_text, formation, NULL, true))
return true;
rb->lcd_setfont(FONT_SYSFIXED);
return false;
}
#define PLUGIN_OTHER 10
static bool ingame;
static int spacerocks_menu_cb(int action,
const struct menu_item_ex *this_item,
struct gui_synclist *this_list)
{
(void)this_list;
if (action == ACTION_REQUEST_MENUITEM
&& !ingame && ((intptr_t)this_item)==0)
return ACTION_EXIT_MENUITEM;
return action;
}
static int spacerocks_menu(void)
{
int selection = 0;
MENUITEM_STRINGLIST(main_menu, "Spacerocks Menu", spacerocks_menu_cb,
"Resume Game", "Start New Game",
"Help", "High Scores",
"Playback Control", "Quit");
rb->button_clear_queue();
while (1)
{
switch (rb->do_menu(&main_menu, &selection, NULL, false))
{
case 0:
return PLUGIN_OTHER;
case 1:
initialise_game();
return PLUGIN_OTHER;
case 2:
if (spacerocks_help())
return PLUGIN_USB_CONNECTED;
break;
case 3:
highscore_show(-1, highscores, NUM_SCORES, true);
break;
case 4:
playback_control(NULL);
break;
case 5:
return PLUGIN_OK;
case MENU_ATTACHED_USB:
return PLUGIN_USB_CONNECTED;
default:
break;
}
}
}
static int spacerocks_game_loop(void)
{
int button;
int end;
int position;
int ret;
if ((ret = spacerocks_menu()) != PLUGIN_OTHER)
return ret;
SET_BG(LCD_BLACK);
ingame = true;
while (true)
{
end = *rb->current_tick + (CYCLETIME * HZ) / 1000;
rb->lcd_clear_display();
SET_FG(COL_TEXT);
switch(game_state)
{
case GAME_OVER:
ingame = false;
rb->splash (HZ * 2, "Game Over");
rb->lcd_clear_display();
position = highscore_update(current_score, current_level, "",
highscores, NUM_SCORES);
if (position != -1)
{
if (position == 0)
rb->splash(HZ*2, "New High Score");
highscore_show(position, highscores, NUM_SCORES, true);
}
return PLUGIN_OTHER;
break;
case PAUSE_MODE:
rb->lcd_putsxyf(1,LCD_HEIGHT-8, "score %d ", current_score);
rb->lcd_putsxy(CENTER_LCD_X - 15,
CENTER_LCD_Y + CENTER_LCD_Y/2 - 4, "pause");
draw_and_move_missiles();
draw_lives();
draw_and_move_ship();
break;
case PLAY_MODE:
rb->lcd_putsxyf(1, LCD_HEIGHT-8, "score %d ", current_score);
draw_and_move_missiles();
draw_lives();
check_collisions();
draw_and_move_ship();
break;
case SHOW_LEVEL:
rb->lcd_putsxyf(1, LCD_HEIGHT-8, "score %d ", current_score);
rb->lcd_putsxyf(CENTER_LCD_X - 20,
CENTER_LCD_Y + CENTER_LCD_Y/2 - 4,
"stage %d ", current_level);
draw_lives();
draw_and_move_ship();
show_level_timeout--;
if (show_level_timeout <= 0)
{
initialise_level(current_level);
game_state = PLAY_MODE;
}
break;
}
draw_and_move_trail_blaze();
drawstars();
draw_and_move_asteroids();
draw_and_move_enemy();
rb->lcd_update();
#ifdef HAS_BUTTON_HOLD
if (rb->button_hold() && game_state == PLAY_MODE)
game_state = PAUSE_MODE;
#endif
button = rb->button_get(false);
switch(button)
{
case(AST_QUIT):
return PLUGIN_OTHER;
break;
#ifdef AST_PAUSE
case(AST_PAUSE):
if (game_state == PAUSE_MODE)
game_state = PLAY_MODE;
else if (game_state == PLAY_MODE)
game_state = PAUSE_MODE;
break;
#endif
case (AST_LEFT):
case (AST_LEFT | BUTTON_REPEAT):
if (game_state == PLAY_MODE || game_state == SHOW_LEVEL)
rotate_ship(SHIP_ROT_ACW_COS, SHIP_ROT_ACW_SIN);
break;
case (AST_RIGHT):
case (AST_RIGHT | BUTTON_REPEAT):
if (game_state == PLAY_MODE || game_state == SHOW_LEVEL)
rotate_ship(SHIP_ROT_CW_COS, SHIP_ROT_CW_SIN);
break;
case (AST_THRUST):
case (AST_THRUST | BUTTON_REPEAT):
if (game_state == PLAY_MODE || game_state == SHOW_LEVEL)
{
if (next_thrust_count <= 0)
{
next_thrust_count = 5;
thrust_ship();
}
}
break;
case (AST_HYPERSPACE):
if (game_state == PLAY_MODE)
hyperspace();
/* maybe shield if it gets too hard */
break;
case (AST_FIRE):
case (AST_FIRE | BUTTON_REPEAT):
if (game_state == PLAY_MODE)
{
if (next_missile_count <= 0)
{
fire_missile();
next_missile_count = 10;
}
}
else if(game_state == PAUSE_MODE)
game_state = PLAY_MODE;
break;
default:
if (rb->default_event_handler(button)==SYS_USB_CONNECTED)
return PLUGIN_USB_CONNECTED;
break;
}
if (next_missile_count > 0)
next_missile_count--;
if (next_thrust_count > 0)
next_thrust_count--;
if (TIME_BEFORE(*rb->current_tick, end))
rb->sleep(end-*rb->current_tick);
else
rb->yield();
}
}
enum plugin_status plugin_start(const void* parameter)
{
(void)parameter;
int ret = PLUGIN_OTHER;
#if LCD_DEPTH > 1
rb->lcd_set_backdrop(NULL);
#endif
/* universal font */
rb->lcd_setfont(FONT_SYSFIXED);
#ifdef HAVE_BACKLIGHT
/* Turn off backlight timeout */
backlight_ignore_timeout();
#endif
highscore_load(SCORE_FILE, highscores, NUM_SCORES);
rb->srand(*rb->current_tick);
/* create stars once, and once only: */
create_stars();
while (ret == PLUGIN_OTHER)
ret = spacerocks_game_loop();
rb->lcd_setfont(FONT_UI);
highscore_save(SCORE_FILE, highscores, NUM_SCORES);
#ifdef HAVE_BACKLIGHT
/* Turn on backlight timeout (revert to settings) */
backlight_use_settings();
#endif
return ret;
}