/*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ * $Id$ * * Copyright (C) 2006 by Mat Holton * * All files in this archive are subject to the GNU General Public License. * See the file COPYING in the source tree root for full license agreement. * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * ****************************************************************************/ #include "plugin.h" #include "math.h" #include "stdio.h" PLUGIN_HEADER /******************************* Globals ***********************************/ static struct plugin_api* rb; /* global api struct pointer */ /* variable button definitions */ #if CONFIG_KEYPAD == RECORDER_PAD #define AST_PAUSE BUTTON_ON #define AST_QUIT BUTTON_OFF #define AST_THRUST_REP BUTTON_UP | BUTTON_REPEAT #define AST_THRUST BUTTON_UP #define AST_HYPERSPACE BUTTON_DOWN #define AST_LEFT BUTTON_LEFT #define AST_LEFT_REP BUTTON_LEFT | BUTTON_REPEAT #define AST_RIGHT BUTTON_RIGHT #define AST_RIGHT_REP (BUTTON_RIGHT | BUTTON_REPEAT) #define AST_FIRE BUTTON_PLAY #define AST_FIRE_REP BUTTON_PLAY | BUTTON_REPEAT #elif CONFIG_KEYPAD == ONDIO_PAD #define AST_PAUSE (BUTTON_MENU | BUTTON_OFF) #define AST_QUIT BUTTON_OFF #define AST_THRUST BUTTON_UP #define AST_THRUST_REP BUTTON_UP | BUTTON_REPEAT #define AST_HYPERSPACE BUTTON_DOWN #define AST_LEFT BUTTON_LEFT #define AST_LEFT_REP BUTTON_LEFT | BUTTON_REPEAT #define AST_RIGHT BUTTON_RIGHT #define AST_RIGHT_REP (BUTTON_RIGHT | BUTTON_REPEAT) #define AST_FIRE BUTTON_MENU #define AST_FIRE_REP BUTTON_MENU | BUTTON_REPEAT #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \ (CONFIG_KEYPAD == IRIVER_H300_PAD) #define AST_PAUSE BUTTON_REC #define AST_QUIT BUTTON_OFF #define AST_THRUST_REP BUTTON_UP | BUTTON_REPEAT #define AST_THRUST BUTTON_UP #define AST_HYPERSPACE BUTTON_DOWN #define AST_LEFT BUTTON_LEFT #define AST_LEFT_REP BUTTON_LEFT | BUTTON_REPEAT #define AST_RIGHT BUTTON_RIGHT #define AST_RIGHT_REP (BUTTON_RIGHT | BUTTON_REPEAT) #define AST_FIRE BUTTON_SELECT #define AST_FIRE_REP BUTTON_SELECT | BUTTON_REPEAT #define AST_RC_QUIT BUTTON_RC_STOP #elif (CONFIG_KEYPAD == IAUDIO_X5_PAD) #define AST_PAUSE BUTTON_PLAY #define AST_QUIT BUTTON_POWER #define AST_THRUST_REP BUTTON_UP | BUTTON_REPEAT #define AST_THRUST BUTTON_UP #define AST_HYPERSPACE BUTTON_DOWN #define AST_LEFT BUTTON_LEFT #define AST_LEFT_REP BUTTON_LEFT | BUTTON_REPEAT #define AST_RIGHT BUTTON_RIGHT #define AST_RIGHT_REP (BUTTON_RIGHT | BUTTON_REPEAT) #define AST_FIRE BUTTON_SELECT #define AST_FIRE_REP BUTTON_SELECT | BUTTON_REPEAT #elif (CONFIG_KEYPAD == IPOD_3G_PAD) || (CONFIG_KEYPAD == IPOD_4G_PAD) #define AST_PAUSE (BUTTON_SELECT | BUTTON_PLAY) #define AST_QUIT (BUTTON_SELECT | BUTTON_MENU) #define AST_THRUST BUTTON_MENU #define AST_THRUST_REP (BUTTON_MENU | BUTTON_REPEAT) #define AST_HYPERSPACE BUTTON_PLAY #define AST_LEFT BUTTON_SCROLL_BACK #define AST_LEFT_REP (BUTTON_SCROLL_BACK | BUTTON_REPEAT) #define AST_RIGHT BUTTON_SCROLL_FWD #define AST_RIGHT_REP (BUTTON_SCROLL_FWD | BUTTON_REPEAT) #define AST_FIRE BUTTON_SELECT #define AST_FIRE_REP (BUTTON_SELECT | BUTTON_REPEAT) #elif (CONFIG_KEYPAD == GIGABEAT_PAD) #define AST_PAUSE BUTTON_POWER #define AST_QUIT BUTTON_A #define AST_THRUST_REP BUTTON_UP | BUTTON_REPEAT #define AST_THRUST BUTTON_UP #define AST_HYPERSPACE BUTTON_DOWN #define AST_LEFT BUTTON_LEFT #define AST_LEFT_REP BUTTON_LEFT | BUTTON_REPEAT #define AST_RIGHT BUTTON_RIGHT #define AST_RIGHT_REP (BUTTON_RIGHT | BUTTON_REPEAT) #define AST_FIRE BUTTON_SELECT #define AST_FIRE_REP BUTTON_SELECT | BUTTON_REPEAT #elif (CONFIG_KEYPAD == IRIVER_H10_PAD) #define AST_PAUSE BUTTON_PLAY #define AST_QUIT BUTTON_POWER #define AST_THRUST_REP BUTTON_SCROLL_UP | BUTTON_REPEAT #define AST_THRUST BUTTON_SCROLL_UP #define AST_HYPERSPACE BUTTON_SCROLL_DOWN #define AST_LEFT BUTTON_LEFT #define AST_LEFT_REP BUTTON_LEFT | BUTTON_REPEAT #define AST_RIGHT BUTTON_RIGHT #define AST_RIGHT_REP (BUTTON_RIGHT | BUTTON_REPEAT) #define AST_FIRE BUTTON_REW #define AST_FIRE_REP BUTTON_REW | BUTTON_REPEAT #endif #define SHOW_COL 0 #define HISCORE_FILE PLUGIN_DIR "/astrorocks.hs" #define POINT_SIZE 2 #define MAX_NUM_ASTEROIDS 25 #define MAX_NUM_MISSILES 6 #define ABS(x) ((x)>0?(x):-(x)) #define SCALE 5000 #define MISSILE_SCALE 5000 #define WRAP_GAP 12 #define EXPLOSION_LENGTH 35 #if CONFIG_KEYPAD == RECORDER_PAD || CONFIG_KEYPAD == ONDIO_PAD #define ENEMY_MISSILE_SURVIVAL_LENGTH 65 #define MISSILE_SURVIVAL_LENGTH 40 #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \ (CONFIG_KEYPAD == IRIVER_H300_PAD) || \ (CONFIG_KEYPAD == IAUDIO_X5_PAD) || \ (CONFIG_KEYPAD == IPOD_3G_PAD) || \ (CONFIG_KEYPAD == IPOD_4G_PAD) || \ (CONFIG_KEYPAD == GIGABEAT_PAD)|| \ (CONFIG_KEYPAD == IRIVER_H10_PAD) #define ENEMY_MISSILE_SURVIVAL_LENGTH 120 #define MISSILE_SURVIVAL_LENGTH 80 #endif #define SHOW_GAME_OVER_TIME 100 #define SHOW_LEVEL_TIME 50 #define START_LIVES 3 #define START_LEVEL 1 #define NUM_ASTEROID_VERTICES 10 #define NUM_SHIP_VERTICES 4 #define NUM_ENEMY_VERTICES 6 #define MAX_LEVEL MAX_NUM_ASTEROIDS #define ENEMY_SPEED 4 #define ENEMY_START_X 0 #define ENEMY_START_Y 0 #define SIZE_ENEMY_COLLISION 5*SCALE #define ATTRACT_FLIP_TIME 100 #define NUM_STARS 50 #define NUM_ROTATIONS 16 #define SIN_COS_SCALE 10000 #define SHIP_ROT_CW_SIN 3827 #define SHIP_ROT_CW_COS 9239 #define SHIP_ROT_ACW_SIN -3827 #define SHIP_ROT_ACW_COS 9239 #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 #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 COL_MISSILE LCD_RGBPACK(200,0,0) #define COL_PLAYER LCD_RGBPACK(200,200,200) #define COL_STARS LCD_WHITE #define COL_ASTEROID LCD_RGBPACK(150,95,0) #define COL_TEXT LCD_RGBPACK(200,200,255) #define COL_ENEMY LCD_RGBPACK(50,220,50) #define SET_FG rb->lcd_set_foreground #define SET_BG rb->lcd_set_background #else #define SET_FG(x) #define SET_BG(x) #endif enum asteroid_type { SMALL = 1, MEDIUM = 2, LARGE = 3, }; enum game_state { GAME_OVER, ATTRACT_MODE, SHOW_LEVEL, PLAY_MODE, PAUSE_MODE }; struct Point { int x; int y; int dx; int dy; }; /* Asteroid structure, contains an array of points */ struct Asteroid { enum asteroid_type type; bool exists; struct Point position; struct Point vertices[NUM_ASTEROID_VERTICES]; int radius; long speed_cos; long speed_sin; int explode_countdown; }; struct Ship { struct Point vertices[NUM_SHIP_VERTICES]; struct Point position; bool waiting_for_space; int explode_countdown; }; struct Enemy { struct Point vertices[NUM_ENEMY_VERTICES]; struct Point position; int explode_countdown; }; struct Missile { struct Point position; struct Point oldpoint; int survived; }; 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 show_level_timeout; static int attract_flip_timeout; static int show_game_over; static int current_level; static int current_score; static int high_score; static int space_check_size = 20*SCALE; static bool enemy_on_screen; static char phscore[30]; 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]; void draw_and_move_asteroids(void); void initialise_game(int nStartNum); bool is_asteroid_near_ship(struct Asteroid* asteroid); bool is_point_within_asteroid(struct Asteroid* asteroid, struct Point* point); void initialise_asteroid(struct Asteroid* asteroid, enum asteroid_type eType); void draw_polygon(struct Point* vertices, int px, int py, int num_vertices); void rotate_asteroid(struct Asteroid* asteroid); void create_asteroid(enum asteroid_type type, int x, int y); void create_stars(void); void initialise_ship(void); void draw_and_move_ship(void); void rotate_ship(int s, int c); void thrust_ship(void); void initialise_missile(struct Missile* missile); void draw_and_move_missiles(void); void fire_missile(void); void animate_and_draw_explosion(struct Point* point, int num_points, int xoffset, int yoffset); void initialise_explosion(struct Point* point, int num_points); void move_point(struct Point* point); void hyperspace(void); void check_collisions(void); void initialise_enemy(void); void draw_and_move_enemy(void); void draw_lives(void); void drawstars(void); bool is_ship_within_asteroid(struct Asteroid* asteroid); /* The array of points that make up an asteroid */ static const short asteroid_one[NUM_ASTEROID_VERTICES*2] = { -1, -6, 2, -4, 4, -7, 8, -4, 7, 0, 10, 1, 6, 7, -2, 7, -5, 3, -5, -4 }; /* The array of points that make up an asteroid */ static const short asteroid_two[NUM_ASTEROID_VERTICES*2] = { -1, -6, 2, -8, 3, -7, 8, -4, 7, 0, 10, 1, 6, 7, -2, 7, -5, 3, -5, -4 }; /* The array of points that make up an asteroid */ static const short asteroid_three[NUM_ASTEROID_VERTICES*2] = { -1, -6, 2, -8, 3, -7, 1, -4, 7, 0, 10, 1, 6, 7, -2, 7, -8, 3, -5, -4 }; /* The array od points the make up the ship */ static const short ship_vertices[NUM_SHIP_VERTICES*2] = { 0,-4, 3, 4, 0, 1, -3, 4 }; /* The array of points the make up the bad spaceship */ static const short enemy_vertices[NUM_ENEMY_VERTICES*2] = { -5, 0, -2, 2, 2, 2, 5, 0, 2, -2, -2, -2 }; /*Hi-Score reading and writing to file - this needs moving to the hi-score plugin lib as a 3rd function */ void iohiscore(void) { int fd; int compare; /* clear the buffer we're about to load the highscore data into */ rb->memset(phscore, 0, sizeof(phscore)); fd = rb->open(HISCORE_FILE,O_RDWR | O_CREAT); /* highscore used to %d, is now %d\n Deal with no file or bad file */ rb->read(fd,phscore, sizeof(phscore)); compare = rb->atoi(phscore); if(high_score > compare){ rb->lseek(fd,0,SEEK_SET); rb->fdprintf(fd, "%d\n", high_score); } else high_score = compare; rb->close(fd); } bool point_in_poly(struct Point* _point, int num_vertices, int x, int y) { struct Point* pi; struct Point* pj; int n; bool c = false; pi = _point; pj = _point; pj += 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; if(n == num_vertices - 1) pj = _point; else pj++; pi++; } return c; } void move_point(struct Point* point) { point->x += point->dx; point->y += point->dy; /*check bounds on the x-axis:*/ if(point->x >= SCALED_WIDTH) point->x = 0; else if(point->x <= 0) point->x = SCALED_WIDTH; /*Check bounds on the y-axis:*/ if(point->y >= SCALED_HEIGHT) point->y = 0; else if(point->y <= 0) point->y = SCALED_HEIGHT; } /*Check if point is within a rectangle*/ bool is_point_within_rectangle(struct Point* rect, struct Point* p, int size) { #if SHOW_COL int aTLx = rect->x - size; int aTLy = rect->y - size; int aBRx = rect->x + size; int aBRy = rect->y + size; rb->lcd_drawline( aTLx/SCALE, aTLy/SCALE, aBRx/SCALE, aTLy/SCALE); rb->lcd_drawline( aTLx/SCALE, aTLy/SCALE, aTLx/SCALE, aBRy/SCALE); rb->lcd_drawline( aTLx/SCALE, aBRy/SCALE, aBRx/SCALE, aBRy/SCALE); rb->lcd_drawline( aBRx/SCALE, aBRy/SCALE, aBRx/SCALE, aTLy/SCALE); return (p->x > aTLx && p->x < aBRx && p->y > aTLy && p->y < aBRy); #else return (p->x > rect->x - size && p->x < rect->x + size && p->y > rect->y - size && p->y < rect->y + size); #endif } /* Draw polygon */ void draw_polygon(struct Point* vertices, int px, int py, int num_vertices) { int n, t1, t2, oldX, oldY; struct Point *p; bool bDrawAll = px < WRAP_GAP || LCD_WIDTH - px < WRAP_GAP || py < WRAP_GAP || LCD_HEIGHT - py < WRAP_GAP; p = vertices; p += num_vertices-1; oldX = p->x/SCALE + px; oldY = p->y/SCALE + py; p = vertices; for(n = num_vertices+1; --n;) { t1 = p->x/SCALE + px; t2 = p->y/SCALE + py; rb->lcd_drawline(oldX, oldY, t1, t2); if(bDrawAll) { rb->lcd_drawline(oldX - LCD_WIDTH, oldY, t1 - LCD_WIDTH, t2); rb->lcd_drawline(oldX + LCD_WIDTH, oldY, t1 + LCD_WIDTH, t2); rb->lcd_drawline(oldX - LCD_WIDTH, oldY + LCD_HEIGHT, t1 - LCD_WIDTH, t2 + LCD_HEIGHT); rb->lcd_drawline(oldX + LCD_WIDTH, oldY + LCD_HEIGHT, t1 + LCD_WIDTH, t2 + LCD_HEIGHT); rb->lcd_drawline(oldX, oldY - LCD_HEIGHT, t1, t2 - LCD_HEIGHT); rb->lcd_drawline(oldX, oldY + LCD_HEIGHT, t1, t2 + LCD_HEIGHT); rb->lcd_drawline(oldX - LCD_WIDTH, oldY - LCD_HEIGHT, t1 - LCD_WIDTH, t2 - LCD_HEIGHT); rb->lcd_drawline(oldX + LCD_WIDTH, oldY - LCD_HEIGHT, t1 + LCD_WIDTH, t2 - LCD_HEIGHT); } oldX = t1; oldY = t2; p++; } } void animate_and_draw_explosion(struct Point* point, int num_points, int xoffset, int yoffset) { int n; for(n = num_points; --n;) { if(game_state != PAUSE_MODE) { point->x += point->dx; point->y += point->dy; } rb->lcd_fillrect( point->x/SCALE + xoffset, point->y/SCALE + yoffset, POINT_SIZE, POINT_SIZE); point++; } } /*stop movement of ship, 'cos that's what happens when you go into hyperspace.*/ void hyperspace(void) { ship.position.dx = ship.position.dy = 0; ship.position.x = (rb->rand()%SCALED_WIDTH); ship.position.y = (rb->rand()%SCALED_HEIGHT); } void initialise_enemy(void) { struct Point* point; int n; enemy_missile.survived = 0; enemy_on_screen = true; enemy.explode_countdown = 0; point = enemy.vertices; for(n = 0; n < NUM_ENEMY_VERTICES+NUM_ENEMY_VERTICES; n+=2) { point->x = enemy_vertices[n]; point->y = enemy_vertices[n+1]; point->x *= SCALE; point->y *= SCALE; 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; } void draw_and_move_enemy(void) { int enemy_x, enemy_y; struct Point *point; SET_FG(COL_ENEMY); if(enemy_on_screen) { enemy_x = enemy.position.x/SCALE; enemy_y = enemy.position.y/SCALE; if(!enemy.explode_countdown) { point = enemy.vertices; draw_polygon(enemy.vertices, enemy_x, enemy_y, NUM_ENEMY_VERTICES); rb->lcd_drawline(enemy.vertices[0].x/SCALE + enemy_x, enemy.vertices[0].y/SCALE + enemy_y, enemy.vertices[3].x/SCALE + enemy_x, enemy.vertices[3].y/SCALE + enemy_y); if(game_state != PAUSE_MODE) { enemy.position.x += enemy.position.dx; enemy.position.y += enemy.position.dy; } if(enemy.position.x > SCALED_WIDTH || enemy.position.x < 0) enemy_on_screen = false; if(enemy.position.y > SCALED_HEIGHT) enemy.position.y = 0; else if(enemy.position.y < 0) enemy.position.y = SCALED_HEIGHT; if( (rb->rand()%1000) < 10) enemy.position.dy = -enemy.position.dy; } else { animate_and_draw_explosion(enemy.vertices, NUM_ENEMY_VERTICES, enemy_x, enemy.position.y/SCALE); if(game_state != PAUSE_MODE) { enemy.explode_countdown--; if(!enemy.explode_countdown) enemy_on_screen = false; } } } else { if( (rb->rand()%1000) < 2 ) initialise_enemy(); } if(!enemy_missile.survived && game_state != GAME_OVER) { /*if no missile and the enemy is here and not exploding..then shoot baby!*/ if( !enemy.explode_countdown && enemy_on_screen && !ship.waiting_for_space && (rb->rand()%10) > 5 ) { 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(ABS(enemy.position.y - ship.position.y) <= 5*SCALE) { enemy_missile.position.dy = 0; } else { if( enemy.position.y < ship.position.y) enemy_missile.position.dy = 1; else enemy_missile.position.dy = -1; } if(ABS(enemy.position.x - ship.position.x) <= 5*SCALE) enemy_missile.position.dx = 0; else { if( enemy.position.x < ship.position.x) enemy_missile.position.dx = 1; else enemy_missile.position.dx = -1; } if(enemy_missile.position.dx == 0 && enemy_missile.position.dy == 0) enemy_missile.position.dx = enemy_missile.position.dy = -1; enemy_missile.position.dx *= SCALE; enemy_missile.position.dy *= SCALE; enemy_missile.survived = ENEMY_MISSILE_SURVIVAL_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.survived--; } } } /****************** * Lame method of collision * detection. It's checking for collision * between point and a big rectangle around the asteroid... *******************/ bool is_point_within_asteroid(struct Asteroid* asteroid, struct Point* point) { if( !is_point_within_rectangle(&asteroid->position, point, asteroid->radius+4*SCALE) ) return false; if(point_in_poly(asteroid->vertices, NUM_ASTEROID_VERTICES, point->x - asteroid->position.x, point->y - asteroid->position.y)) { switch(asteroid->type) { case(SMALL): asteroid->explode_countdown = EXPLOSION_LENGTH; initialise_explosion(asteroid->vertices, NUM_ASTEROID_VERTICES); break; case(LARGE): create_asteroid(MEDIUM, asteroid->position.x, asteroid->position.y); create_asteroid(MEDIUM, asteroid->position.x, asteroid->position.y); break; case(MEDIUM): create_asteroid(SMALL, asteroid->position.x, asteroid->position.y); create_asteroid(SMALL, asteroid->position.x, asteroid->position.y); break; } current_score++; asteroid_count--; asteroid->exists = false; return true; } else return false; } bool is_point_within_enemy(struct Point* point) { if( is_point_within_rectangle(&enemy.position, point, 5*SCALE) ) { current_score += 5; /*enemy_missile.survived = 0;*/ enemy.explode_countdown = EXPLOSION_LENGTH; initialise_explosion(enemy.vertices, NUM_ENEMY_VERTICES); return true; } else return false; } bool is_ship_within_asteroid(struct Asteroid* asteroid) { bool hit = false; struct Point p; p.x = ship.position.x + ship.vertices[0].x; p.y = ship.position.y + ship.vertices[0].y; hit |= is_point_within_asteroid(asteroid, &p); if(!hit) { p.x = ship.position.x + ship.vertices[1].x; p.y = ship.position.y + ship.vertices[1].y; hit |= is_point_within_asteroid(asteroid, &p); if(!hit) { p.x = ship.position.x + ship.vertices[3].x; p.y = ship.position.y + ship.vertices[3].y; hit |= is_point_within_asteroid(asteroid, &p); } } return hit; } void initialise_explosion(struct Point* point, int num_points) { int n; point->x += point->dx; point->y += point->dy; for(n = num_points; --n;) { point->dx = point->x; point->dy = point->y; point++; } } /* Check for collsions between the missiles and the asteroids and the ship */ void check_collisions(void) { int m, n; bool asteroids_onscreen = false; struct Missile* missile; struct Asteroid* asteroid; bool ship_cant_be_placed = 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->survived > 0) { /*has the missile hit the asteroid?*/ if(is_point_within_asteroid(asteroid, &missile->position) || is_point_within_asteroid(asteroid, &missile->oldpoint)) { missile->survived = 0; break; } } missile++; } } /*If it exists now, check ship collision:*/ if(asteroid->exists) { /*now check collision with ship:*/ if(!ship.waiting_for_space && !ship.explode_countdown) { if(is_ship_within_asteroid(asteroid)) { /*blow up ship*/ ship.explode_countdown = EXPLOSION_LENGTH; initialise_explosion(ship.vertices, NUM_SHIP_VERTICES); } } } /*has the enemy missile blown something up?*/ if(asteroid->exists && enemy_missile.survived) { if(is_point_within_asteroid(asteroid, &enemy_missile.position)) { /*take that score back then:*/ if(current_score > 0) current_score--; enemy_missile.survived = 0; } } /*if it still exists, check if ship is waiting for space:*/ if(asteroid->exists && ship.waiting_for_space) ship_cant_be_placed |= is_point_within_rectangle(&ship.position, &asteroid->position, space_check_size); /*is an asteroid still exploding?*/ if(asteroid->explode_countdown) asteroids_onscreen = true; asteroid++; } /*now check collision between ship and enemy*/ if(enemy_on_screen && !ship.waiting_for_space && !ship.explode_countdown && !enemy.explode_countdown) { /*has the enemy collided with the ship?*/ if(is_point_within_enemy(&ship.position)) { ship.explode_countdown = EXPLOSION_LENGTH; initialise_explosion(ship.vertices, NUM_SHIP_VERTICES); } /*Now see if the enemy has been shot at by the ships missiles:*/ missile = missiles_array; n = MAX_NUM_MISSILES; while(--n) { if(missile->survived > 0 && is_point_within_enemy(&missile->position)) { missile->survived = 0; break; } } } /*test collision with enemy missile and ship:*/ if(!ship_cant_be_placed && enemy_missile.survived > 0 && point_in_poly(ship.vertices, NUM_SHIP_VERTICES, enemy_missile.position.x - ship.position.x, enemy_missile.position.y - ship.position.y)) { ship.explode_countdown = EXPLOSION_LENGTH; initialise_explosion(ship.vertices, NUM_SHIP_VERTICES); enemy_missile.survived = 0; enemy_missile.position.x = enemy_missile.position.y = 0; } if(!ship_cant_be_placed) ship.waiting_for_space = false; /*if all asteroids cleared then start again:*/ if(asteroid_count == 0 && !enemy_on_screen && !asteroids_onscreen) { current_level++; game_state = SHOW_LEVEL; show_level_timeout = SHOW_LEVEL_TIME; } } /************************************************* ** Creates a new asteroid of the given 4type (size) ** and at the given location. *************************************************/ void create_asteroid(enum asteroid_type type, int x, int y) { struct Asteroid* asteroid; int n; asteroid = asteroids_array; n = MAX_NUM_ASTEROIDS; while(--n) { if(!asteroid->exists && !asteroid->explode_countdown) { initialise_asteroid(asteroid, type); asteroid->position.x = x; asteroid->position.y = y; break; } asteroid++; } } /* Initialise a missile */ void initialise_missile(struct Missile* missile) { missile->position.x = ship.position.x; missile->position.y = ship.position.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->survived = MISSILE_SURVIVAL_LENGTH; missile->oldpoint.x = missile->position.x; missile->oldpoint.y = missile->position.y; } /* Draw and Move all the missiles */ void draw_and_move_missiles(void) { int n; int p1x, p1y; int p2x, p2y; struct Missile* missile; missile = missiles_array; SET_FG(COL_MISSILE); n = MAX_NUM_MISSILES; while(--n) { if(missile->survived) { if(missile->position.dx > 0) { if(missile->position.x >= missile->oldpoint.x) { p1x = missile->oldpoint.x; p2x = missile->position.x; } else { p1x = 0; p2x = missile->position.x; } } else { if(missile->oldpoint.x >= missile->position.x) { p1x = missile->oldpoint.x; p2x = missile->position.x; } else { p1x = missile->oldpoint.x; p2x = LCD_WIDTH; } } if(missile->position.dy > 0) { if(missile->position.y >= missile->oldpoint.y) { p1y = missile->oldpoint.y; p2y = missile->position.y; } else { p1y = 0; p2y = missile->position.y; } } else { if(missile->oldpoint.y >= missile->position.y) { p1y = missile->oldpoint.y; p2y = missile->position.y; } else { p1y = missile->oldpoint.y; p2y = LCD_HEIGHT; } } rb->lcd_drawline( p1x/SCALE, p1y/SCALE, p2x/SCALE, p2y/SCALE); if(game_state != PAUSE_MODE) { missile->oldpoint.x = missile->position.x; missile->oldpoint.y = missile->position.y; move_point(&missile->position); missile->survived--; } } missile++; } } void draw_lives(void) { int n; int px = (LCD_WIDTH - num_lives*4 - 1); int py = (LCD_HEIGHT-4); SET_FG(COL_PLAYER); n = num_lives; while(--n) { draw_polygon(lives_points, px, py, NUM_SHIP_VERTICES); px += 6; } } /*Fire the next missile*/ void fire_missile(void) { int n; struct Missile* missile; if(!ship.explode_countdown && !ship.waiting_for_space) { missile = missiles_array; n = MAX_NUM_MISSILES; while(--n) { if(!missile->survived) { initialise_missile(missile); break; } missile++; } } } /* Initialise the passed Asteroid */ void initialise_asteroid(struct Asteroid* asteroid, enum asteroid_type type) { int n; bool b,b2; struct Point* point; asteroid->exists = true; asteroid->type = type; asteroid->explode_countdown = 0; /*Set the radius of the asteroid:*/ asteroid->radius = (int)type; /*shall we move Clockwise and Fast*/ if((rb->rand()%100)>75) { asteroid->speed_cos = FAST_ROT_CW_COS; asteroid->speed_sin = FAST_ROT_CW_SIN; } else if((rb->rand()%100)>75) { asteroid->speed_cos = FAST_ROT_ACW_COS; asteroid->speed_sin = FAST_ROT_ACW_SIN; } else if((rb->rand()%100)>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; } b = (rb->rand()%100)>66; b2 = (rb->rand()%100)>66; point = asteroid->vertices; for(n = 0; n < NUM_ASTEROID_VERTICES*2; n+=2) { if(b) { point->x = asteroid_one[n]*asteroid->radius/2; point->y = asteroid_one[n+1]*asteroid->radius/2; } else if( b2 ) { point->x = asteroid_two[n]*asteroid->radius/2; point->y = asteroid_two[n+1]*asteroid->radius/2; } else { point->x = asteroid_three[n]*asteroid->radius/2; point->y = asteroid_three[n+1]*asteroid->radius/2; } point->x *= SCALE; point->y *= SCALE; point++; } asteroid->radius *= SCALE/2; if(asteroid->type == LARGE) asteroid->radius *= 7; else if(asteroid->type == MEDIUM) asteroid->radius *= 4; else if(asteroid->type == SMALL) asteroid->radius /= 2; b = true; while(b) { /*Set the position randomly:*/ asteroid->position.x = (rb->rand()%SCALED_WIDTH); asteroid->position.y = (rb->rand()%SCALED_HEIGHT); asteroid->position.dx = 0; while(asteroid->position.dx == 0) asteroid->position.dx = (rb->rand()%10)-5; asteroid->position.dy = 0; while(asteroid->position.dy == 0) asteroid->position.dy = (rb->rand()%10)-5; asteroid->position.dx *= SCALE/10; asteroid->position.dy *= SCALE/10; b = is_point_within_rectangle(&ship.position, &asteroid->position, space_check_size); } /*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++; } /*Initialise the ship*/ void initialise_ship(void) { struct Point* point; struct Point* lives_point; int n; ship.position.x = CENTER_LCD_X; ship.position.y = CENTER_LCD_Y; ship.position.x *= SCALE; ship.position.y *= SCALE; ship.position.dx = ship.position.dy = 0; 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; point++; lives_point++; } ship.position.dx = 0; ship.position.dy = 0; ship.explode_countdown = 0; /*hack-o-rama-city-arizona, take it out to see what happens:*/ for(n=17;--n;) rotate_ship(SHIP_ROT_ACW_COS, SHIP_ROT_ACW_SIN); /*grab a copy of the ships points for the lives display:*/ point = ship.vertices; lives_point = lives_points; for(n = 0; n < NUM_SHIP_VERTICES*2; n+=2) { lives_point->x = point->x; lives_point->y = point->y; lives_point++; point++; } } void rotate_asteroid(struct Asteroid* asteroid) { struct Point* point; int n; long xtemp; point = asteroid->vertices; for(n = NUM_ASTEROID_VERTICES+1; --n;) { xtemp = point->x; point->x = xtemp*asteroid->speed_cos/SIN_COS_SCALE - point->y*asteroid->speed_sin/SIN_COS_SCALE; point->y = point->y*asteroid->speed_cos/SIN_COS_SCALE + xtemp*asteroid->speed_sin/SIN_COS_SCALE; point++; } } /************************************************* ** Draws the ship, moves the ship and creates a new ** one if it's finished exploding. **************************************************/ void draw_and_move_ship(void) { int nxoffset = ship.position.x/SCALE; int nyoffset = ship.position.y/SCALE; SET_FG(COL_PLAYER); if(!ship.explode_countdown) { if(!ship.waiting_for_space) draw_polygon(ship.vertices, nxoffset, nyoffset, NUM_SHIP_VERTICES); } else { animate_and_draw_explosion(ship.vertices, NUM_SHIP_VERTICES, ship.position.x/SCALE, ship.position.y/SCALE); if(game_state != PAUSE_MODE) { ship.explode_countdown--; if(!ship.explode_countdown) { num_lives--; if(!num_lives) { show_game_over = SHOW_GAME_OVER_TIME; game_state = GAME_OVER; } else { initialise_ship(); ship.waiting_for_space = true; } } } } if(game_state != PAUSE_MODE && game_state != GAME_OVER) move_point(&ship.position); } void thrust_ship(void) { if(!ship.waiting_for_space) { ship.position.dx += ( ship.vertices[0].x - ship.vertices[2].x )/10; ship.position.dy += ( ship.vertices[0].y - ship.vertices[2].y )/10; /*if dx and dy are below a certain threshold, then set 'em to 0*/ } } /************************************************** ** Rotate the ship using the passed sin & cos values ***************************************************/ void rotate_ship(int c, int s) { struct Point* point; int n; double xtemp; if(!ship.waiting_for_space && !ship.explode_countdown) { point = ship.vertices; for(n=NUM_SHIP_VERTICES+1;--n;) { xtemp = point->x; point->x = xtemp*c/SIN_COS_SCALE - point->y*s/SIN_COS_SCALE; point->y = point->y*c/SIN_COS_SCALE + xtemp*s/SIN_COS_SCALE; point++; } } } void drawstars() { struct Point* p; int n = NUM_STARS; p = stars; SET_FG(COL_STARS); while(--n) { rb->lcd_drawpixel(p->x , p->y); p++; } } /************************************************* ** Draw And Move all Asteroids *************************************************/ void draw_and_move_asteroids(void) { int n; struct Asteroid* asteroid; asteroid = asteroids_array; SET_FG(COL_ASTEROID); n = MAX_NUM_ASTEROIDS; while(--n) { if(game_state != PAUSE_MODE) { if(asteroid->exists) { move_point(&asteroid->position); rotate_asteroid(asteroid); draw_polygon(asteroid->vertices, asteroid->position.x/SCALE, asteroid->position.y/SCALE, NUM_ASTEROID_VERTICES); } else if(asteroid->explode_countdown) { animate_and_draw_explosion(asteroid->vertices, NUM_ASTEROID_VERTICES, asteroid->position.x/SCALE, asteroid->position.y/SCALE); if(game_state != PAUSE_MODE) asteroid->explode_countdown--; } } else { if(asteroid->exists) draw_polygon(asteroid->vertices, asteroid->position.x/SCALE, asteroid->position.y/SCALE, NUM_ASTEROID_VERTICES); } asteroid++; } } 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++; } } /************************************************* ** Creates start_num number of new asteroids of ** full size. **************************************************/ void initialise_game(int start_num) { int n; asteroid_count = next_missile_count = next_thrust_count = 0; struct Asteroid* asteroid; struct Missile* missile; /*no enemy*/ enemy_on_screen = 0; enemy_missile.survived = 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); /*ensure all missiles are out of action: */ missile = missiles_array; n = MAX_NUM_MISSILES; while(--n) { missile->survived=0; missile++; } } void start_attract_mode(void) { current_level = 5; num_lives = START_LIVES; current_score = 0; attract_flip_timeout = ATTRACT_FLIP_TIME; game_state = ATTRACT_MODE; if(asteroid_count < 3) initialise_game(current_level); } enum plugin_status start_game(void) { char s[20]; char level[10]; int button; int end; int CYCLETIME = 30; /*create stars once, and once only:*/ create_stars(); SET_BG(LCD_BLACK); while(true) { /*game starts with at level 1 with 1 asteroid.*/ start_attract_mode(); /*Main loop*/ while(true) { end = *rb->current_tick + (CYCLETIME * HZ) / 1000; rb->lcd_clear_display(); SET_FG(COL_TEXT); switch(game_state) { case(ATTRACT_MODE): if(attract_flip_timeout < ATTRACT_FLIP_TIME/2) { rb->lcd_putsxy(CENTER_LCD_X - 39, CENTER_LCD_Y + CENTER_LCD_Y/2 - 4, "Fire to Start"); if(!attract_flip_timeout) attract_flip_timeout = ATTRACT_FLIP_TIME; } else { rb->snprintf(s, sizeof(s), "Hi Score %d ", high_score); rb->lcd_putsxy(CENTER_LCD_X - 30, CENTER_LCD_Y + CENTER_LCD_Y/2 - 4, s); } attract_flip_timeout--; break; case(GAME_OVER): rb->lcd_putsxy(CENTER_LCD_X - 25, CENTER_LCD_Y + CENTER_LCD_Y/2 - 4, "Game Over"); rb->snprintf(s, sizeof(s), "score %d ", current_score); rb->lcd_putsxy(1,LCD_HEIGHT-8, s); show_game_over--; if(!show_game_over) start_attract_mode(); break; case(PAUSE_MODE): rb->snprintf(s, sizeof(s), "score %d ", current_score); rb->lcd_putsxy(1,LCD_HEIGHT-8, s); 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->snprintf(s, sizeof(s), "score %d ", current_score); rb->lcd_putsxy(1,LCD_HEIGHT-8, s); draw_and_move_missiles(); draw_lives(); check_collisions(); draw_and_move_ship(); break; case(SHOW_LEVEL): show_level_timeout--; rb->snprintf(s, sizeof(s), "score %d ", current_score); rb->lcd_putsxy(1,LCD_HEIGHT-8, s); rb->snprintf(level, sizeof(level), "stage %d ", current_level); rb->lcd_putsxy(CENTER_LCD_X - 20, CENTER_LCD_Y + CENTER_LCD_Y/2 - 4, level); draw_and_move_ship(); draw_lives(); if(!show_level_timeout) { initialise_game(current_level); game_state = PLAY_MODE; draw_lives(); } break; } drawstars(); draw_and_move_asteroids(); draw_and_move_enemy(); rb->lcd_update(); button = rb->button_get(false); #ifdef HAS_BUTTON_HOLD if (rb->button_hold()) game_state = PAUSE_MODE; #endif switch(button) { case(AST_PAUSE): if(game_state == PLAY_MODE) game_state = PAUSE_MODE; else if(game_state == PAUSE_MODE) game_state = PLAY_MODE; break; #ifdef AST_RC_QUIT case AST_RC_QUIT: #endif case(AST_QUIT): if(game_state == ATTRACT_MODE) return PLUGIN_OK; else if(game_state == GAME_OVER) { start_attract_mode(); } else { show_game_over = SHOW_GAME_OVER_TIME; game_state = GAME_OVER; } break; case (AST_LEFT_REP): case (AST_LEFT): if(game_state == PLAY_MODE || game_state == SHOW_LEVEL) rotate_ship(SHIP_ROT_ACW_COS, SHIP_ROT_ACW_SIN); break; case (AST_RIGHT_REP): case (AST_RIGHT): if(game_state == PLAY_MODE || game_state == SHOW_LEVEL) rotate_ship(SHIP_ROT_CW_COS, SHIP_ROT_CW_SIN); break; case (AST_THRUST_REP): case (AST_THRUST): if((game_state == PLAY_MODE || game_state == SHOW_LEVEL) && !next_thrust_count) { thrust_ship(); next_thrust_count = 5; } break; case (AST_HYPERSPACE): if(game_state == PLAY_MODE) hyperspace(); /*maybe shield if it gets too hard */ break; case (AST_FIRE_REP): case (AST_FIRE): if(game_state == ATTRACT_MODE) { current_level = START_LEVEL; initialise_ship(); initialise_game(current_level); show_level_timeout = SHOW_LEVEL_TIME; game_state = PLAY_MODE; } else if(game_state == PLAY_MODE) { if(!next_missile_count) { 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(!num_lives) { if(high_score < current_score) high_score = current_score; if(!show_game_over) break; } if(next_missile_count) next_missile_count--; if(next_thrust_count) next_thrust_count--; if (end > *rb->current_tick) rb->sleep(end-*rb->current_tick); else rb->yield(); } } } enum plugin_status plugin_start(struct plugin_api* api, void* parameter) { enum plugin_status retval; (void)(parameter); rb = api; game_state = ATTRACT_MODE; /* universal font */ rb->lcd_setfont(FONT_SYSFIXED); rb->backlight_set_timeout(1); iohiscore(); retval = start_game(); iohiscore(); rb->lcd_setfont(FONT_UI); /* restore normal backlight setting*/ rb->backlight_set_timeout(rb->global_settings->backlight_timeout); return retval; }