/*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ * $Id: asteroids.c,v 1.1 2004/11/03 20:56:59 Mat Holton$ * * asteroids plugin * * My crack at making an Asteroids game on the Rockbox. * * 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" #ifdef HAVE_LCD_BITMAP /******************************* 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_ON #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_MENU #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 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) #define AST_PAUSE BUTTON_REC #define AST_QUIT (BUTTON_SELECT | BUTTON_MENU) #define AST_THRUST BUTTON_MENU #define AST_HYPERSPACE BUTTON_PLAY #define AST_LEFT BUTTON_LEFT #define AST_RIGHT BUTTON_RIGHT #define AST_FIRE (BUTTON_SELECT | BUTTON_PLAY) #endif #define SHOW_COL 0 #define HISCORE_FILE PLUGIN_DIR "/asteroids.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 10000 #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 == IPOD_4G_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 9 #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 6*SCALE #define ATTRACT_FLIP_TIME 100 #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 872 #define FAST_ROT_CW_COS 9962 #define FAST_ROT_ACW_SIN -872 #define FAST_ROT_ACW_COS 9962 #define MEDIUM_ROT_CW_SIN 349 #define MEDIUM_ROT_CW_COS 9993 #define MEDIUM_ROT_ACW_SIN -349 #define MEDIUM_ROT_ACW_COS 9993 #define SLOW_ROT_CW_SIN 349 #define SLOW_ROT_CW_COS 9993 #define SLOW_ROT_ACW_SIN - 349 #define SLOW_ROT_ACW_COS 9993 enum asteroid_type { SMALL = 1, MEDIUM = 2, LARGE = 3, }; enum GameState { 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; int survived; }; static int LCD_WIDTH_X_SCALE = LCD_WIDTH*SCALE; static int LCD_HEIGHT_X_SCALE = LCD_HEIGHT*SCALE; static enum GameState game_state; static int asteroid_count; static int next_missile_count; static int next_thrust_count; static int nLives; static int nShowLevelTimeout; static int attract_flip_timeout; static int nShowGameOver; static bool bEnemyOnScreen; static char phscore[30]; static struct Ship ship; static struct Asteroid aAsteroids[MAX_NUM_ASTEROIDS]; static struct Missile aMissiles[MAX_NUM_MISSILES]; static struct Missile enemy_missile; static struct Enemy enemy; static int nLevel; static int nScore; static int nHighScore; static struct Point lives_points[NUM_SHIP_VERTICES]; int center_lcd_x = LCD_WIDTH/2; int center_lcd_y = LCD_HEIGHT/2; int space_check_size = 20*SCALE; enum plugin_status startGame(void); void draw_and_move_asteroids(void); void initilise_game(int nStartNum); bool is_asteroid_near_ship(struct Asteroid* asteroid); bool is_point_within_asteroid(struct Asteroid* asteroid, struct Point* point); double dSQRT_mod( int dwN ); void initilise_asteroid(struct Asteroid* asteroid, enum asteroid_type eType); void draw_polygon(struct Point* vertices, int px, int py, int num_vertices); void moveAsteroid(struct Asteroid* asteroid); void rotate_asteroid(struct Asteroid* asteroid); void create_asteroid(enum asteroid_type type, long x, long y); void initilise_ship(void); void draw_and_move_ship(void); void rotate_ship(int s, int c); void thrust_ship(void); void initilise_missile(struct Missile* missile); void draw_and_move_missiles(void); void fire_missile(void); void animate_and_draw_explosion(struct Point* point, int nNumPoints, int xOffset, int yOffset); void initilise_explosion(struct Point* point, int nNumPoints); void move_point(struct Point* point); void hyperspace(void); void check_collisions(void); void initilise_enemy(void); void draw_and_move_enemy(void); void draw_lives(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] = { -2, 4, 1, 3, 3, 3, 3, -2, 2, -3, -2, -4, -2, -2, -4, -2, -5, 1, }; /* 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] = { -4, 0, -2, 2, 2, 2, 4, 0, 2, -2, -2, -2 }; double dSQRT_mod( int dwN ) { double dX = 1; /* depending on precision */ dX = ( dX + ( dwN / dX ) ) / 2.0; dX = ( dX + ( dwN / dX ) ) / 2.0; dX = ( dX + ( dwN / dX ) ) / 2.0; dX = ( dX + ( dwN / dX ) ) / 2.0; dX = ( dX + ( dwN / dX ) ) / 2.0; dX = ( dX + ( dwN / dX ) ) / 2.0; return( dX ); } /*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(nHighScore > compare){ rb->lseek(fd,0,SEEK_SET); rb->fdprintf(fd, "%d\n", nHighScore); } else nHighScore = compare; rb->close(fd); } void move_point(struct Point* point) { point->x += point->dx; point->y += point->dy; //check bounds on the x-axis: if(point->x >= LCD_WIDTH_X_SCALE) point->x = 0; else if(point->x <= 0) point->x = LCD_WIDTH_X_SCALE; //Check bounds on the y-axis: if(point->y >= LCD_HEIGHT*SCALE) point->y = 0; else if(point->y <= 0) point->y = LCD_HEIGHT*SCALE; } /*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; long oldX, oldY; struct Point *point; point = vertices; for(n = num_vertices; --n;) { //keep copy oldX = point->x/SCALE + px; oldY = point->y/SCALE + py; point++; rb->lcd_drawline( point->x/SCALE + px, point->y/SCALE + py, oldX, oldY); } //lame code Join up with the last one: oldX = point->x/SCALE + px; oldY = point->y/SCALE + py; point = vertices; rb->lcd_drawline( point->x/SCALE + px, point->y/SCALE + py, oldX, oldY); } void animate_and_draw_explosion(struct Point* point, int nNumPoints, int xOffset, int yOffset) { int n; for(n = nNumPoints; --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()%LCD_WIDTH_X_SCALE); ship.position.y = (rb->rand()%LCD_HEIGHT*SCALE); } void initilise_enemy(void) { struct Point* point; int n; enemy_missile.survived = 0; bEnemyOnScreen = true; enemy.explode_countdown = 0; 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 *= SCALE; point->y *= SCALE; point++; } if(ship.position.x >= LCD_WIDTH_X_SCALE/2) { enemy.position.dx = ENEMY_SPEED; enemy.position.x = 0; } else { enemy.position.dx = -ENEMY_SPEED; enemy.position.x = LCD_WIDTH_X_SCALE; } if(ship.position.y >= LCD_HEIGHT_X_SCALE/2) { enemy.position.dy = ENEMY_SPEED; enemy.position.y = 0; } else { enemy.position.dy = -ENEMY_SPEED; enemy.position.y = LCD_HEIGHT_X_SCALE; } //enemy.position.dx = ENEMY_SPEED; //enemy.position.dy = ENEMY_SPEED; enemy.position.dx *= SCALE/10; enemy.position.dy *= SCALE/10; } void draw_and_move_enemy(void) { int n, oldX, oldY, enemy_x, enemy_y; struct Point *point; if(bEnemyOnScreen) { enemy_x = enemy.position.x/SCALE; enemy_y = enemy.position.y/SCALE; if(!enemy.explode_countdown) { point = enemy.vertices; for(n = NUM_ENEMY_VERTICES; --n;) { //keep copy oldX = point->x/SCALE; oldY = point->y/SCALE; point++; rb->lcd_drawline( point->x/SCALE + enemy_x, point->y/SCALE + enemy_y, oldX + enemy_x, oldY + enemy_y); } //Join up with the last one: oldX = point->x/SCALE; oldY = point->y/SCALE; point = enemy.vertices; rb->lcd_drawline( point->x/SCALE + enemy_x, point->y/SCALE + enemy_y, oldX + enemy_x, oldY + enemy_y); oldX = enemy.vertices[3].x/SCALE; oldY = enemy.vertices[3].y/SCALE; //draw a line too: rb->lcd_drawline( point->x/SCALE + enemy_x, point->y/SCALE + enemy_y, oldX + enemy_x, oldY + enemy_y); if(game_state != PAUSE_MODE) { enemy.position.x += enemy.position.dx; enemy.position.y += enemy.position.dy; } if(enemy.position.x > LCD_WIDTH_X_SCALE || enemy.position.x < 0) bEnemyOnScreen = false; if(enemy.position.y > LCD_HEIGHT*SCALE) enemy.position.y = 0; else if(enemy.position.y < 0) enemy.position.y = LCD_HEIGHT*SCALE; if( (rb->rand()%1000) < 10) enemy.position.dy = -enemy.position.dy; if(!enemy_missile.survived) { if( (rb->rand()%10) > 5) { enemy_missile.position.x = enemy.position.x; enemy_missile.position.y = enemy.position.y; //lame, needs to be sorted 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--; } } } 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) bEnemyOnScreen = false; } } } else { if( (rb->rand()%1000) < 3) initilise_enemy(); } } /****************** * 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) ) { switch(asteroid->type) { case(SMALL): asteroid->explode_countdown = EXPLOSION_LENGTH; initilise_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; } nScore++; 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, SIZE_ENEMY_COLLISION)) { nScore += 5; enemy.explode_countdown = EXPLOSION_LENGTH; initilise_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 initilise_explosion(struct Point* point, int nNumPoints) { int n; point->x += point->dx; point->y += point->dy; for(n = nNumPoints; --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 bShipCannotBePlaced = false; asteroid = aAsteroids; for(m = MAX_NUM_ASTEROIDS;--m;) { //if the asteroids exists then test missile collision: if(asteroid->exists) { missile = aMissiles; for(n = MAX_NUM_MISSILES;--n;) { //if the missiles exists: if(missile->survived > 0) { //has the missile hit the asteroid? if(is_point_within_asteroid(asteroid, &missile->position)) { 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; initilise_explosion(ship.vertices, NUM_SHIP_VERTICES); } } } //has the enemy missile blown something up? if(asteroid->exists && enemy_missile.survived > 0) if(is_point_within_asteroid(asteroid, &enemy_missile.position)) { //take that score back then: if(nScore > 0) nScore--; enemy_missile.survived = 0; } //if it still exists, check if ship is waiting for space: if(asteroid->exists && ship.waiting_for_space) bShipCannotBePlaced |= 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(bEnemyOnScreen && !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; initilise_explosion(ship.vertices, NUM_SHIP_VERTICES); } //Now see if the enemy has been shot at by the ships missiles: missile = aMissiles; for(n = MAX_NUM_MISSILES;--n;) { if(missile->survived > 0 && is_point_within_enemy(&missile->position)) { missile->survived = 0; break; } } } //test collision with enemy missile and ship: if(!bShipCannotBePlaced && enemy_missile.survived > 0 && is_point_within_rectangle(&ship.position, &enemy_missile.position, 4*SCALE)) { ship.explode_countdown = EXPLOSION_LENGTH; initilise_explosion(ship.vertices, NUM_SHIP_VERTICES); enemy_missile.survived = 0; } if(!bShipCannotBePlaced) ship.waiting_for_space = false; //if all asteroids cleared then start again: if(asteroid_count == 0 && !bEnemyOnScreen && !asteroids_onscreen) { nLevel++; game_state = SHOW_LEVEL; nShowLevelTimeout = SHOW_LEVEL_TIME; } } /************************************************* ** Creates a new asteroid of the given 4type (size) ** and at the given location. *************************************************/ void create_asteroid(enum asteroid_type type, long x, long y) { struct Asteroid* asteroid; int n; asteroid = aAsteroids; for(n = MAX_NUM_ASTEROIDS;--n;) { if(!asteroid->exists && !asteroid->explode_countdown) { initilise_asteroid(asteroid, type); asteroid->position.x = x; asteroid->position.y = y; break; } asteroid++; } } /* Initilise a missile */ void initilise_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)/3; missile->position.dy = (ship.vertices[0].y - ship.vertices[2].y)/3; missile->survived = MISSILE_SURVIVAL_LENGTH; } /* Draw and Move all the missiles */ void draw_and_move_missiles(void) { int n = MAX_NUM_MISSILES; struct Missile* missile; missile = aMissiles; while(--n) { if(missile->survived) { rb->lcd_fillrect( missile->position.x/SCALE, missile->position.y/SCALE, POINT_SIZE, POINT_SIZE); if(game_state != PAUSE_MODE) { move_point(&missile->position); missile->survived--; } } missile++; } } void draw_lives(void) { int n; int px = (LCD_WIDTH - nLives*4 - 1); int py = (LCD_HEIGHT-4); for(n=nLives;n != 0 && --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 = aMissiles; for(n = MAX_NUM_MISSILES;--n;) { if(!missile->survived) { initilise_missile(missile); break; } missile++; } } } /* Initilise the passed Asteroid */ void initilise_asteroid(struct Asteroid* asteroid, enum asteroid_type eType) { int n; bool b; struct Point* point; asteroid->exists = true; asteroid->type = eType; asteroid->explode_countdown = 0; //Set the radius of the asteroid: asteroid->radius = (int)eType; //shall we move Clockwise and Fast if((rb->rand()%100)>50) { asteroid->speed_cos = FAST_ROT_CW_COS; asteroid->speed_sin = FAST_ROT_CW_SIN; } else if((rb->rand()%100)>50) { asteroid->speed_cos = FAST_ROT_ACW_COS; asteroid->speed_sin = FAST_ROT_ACW_SIN; } else if((rb->rand()%100)>50) { 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; } point = asteroid->vertices; for(n = 0; n < NUM_ASTEROID_VERTICES*2; n+=2) { point->x = asteroid_one[n]*asteroid->radius; point->y = asteroid_one[n+1]*asteroid->radius; point->x *= SCALE; point->y *= SCALE; point++; } asteroid->radius *= SCALE; //this is a hack to get the crap collision detection to work (at all) if(eType == LARGE) asteroid->radius *= 3.6; else asteroid->radius *= 3; b = true; while(b) { //Set the position randomly: asteroid->position.x = (rb->rand()%LCD_WIDTH_X_SCALE); asteroid->position.y = (rb->rand()%LCD_HEIGHT*SCALE); 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); } //great, we've created an asteroid, don't forget to increment the total: asteroid_count++; } /*Initilise the ship*/ void initilise_ship(void) { struct Point* point; struct Point* livesPoint; 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; livesPoint = 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++; livesPoint++; } ship.position.dx = 0; ship.position.dy = 0; ship.explode_countdown = 0; //ship.waiting_for_space = false; //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; livesPoint = lives_points; for(n = 0; n < NUM_SHIP_VERTICES*2; n+=2) { livesPoint->x = point->x; livesPoint->y = point->y; livesPoint++; point++; } //ship.waiting_for_space = true; } 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++; } } void draw_and_move_ship(void) { if(!ship.explode_countdown) { if(!ship.waiting_for_space) draw_polygon(ship.vertices, ship.position.x/SCALE, ship.position.y/SCALE, 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) { initilise_ship(); ship.waiting_for_space = true; nLives--; if(!nLives) { nShowGameOver = SHOW_GAME_OVER_TIME; game_state = GAME_OVER; } } } } move_point(&ship.position); } void thrust_ship(void) { 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++; } } } /************************************************* ** Draw And Move all Asteroids *************************************************/ void draw_and_move_asteroids(void) { int nArrayPos = MAX_NUM_ASTEROIDS; int offsetx, offsety; struct Asteroid* asteroid = aAsteroids; while(--nArrayPos) { 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) { if(game_state != PAUSE_MODE) { move_point(&asteroid->position); rotate_asteroid(asteroid); } draw_polygon(asteroid->vertices, asteroid->position.x/SCALE, asteroid->position.y/SCALE, NUM_ASTEROID_VERTICES); //is it half-off the screen? offsetx = LCD_WIDTH_X_SCALE - asteroid->position.x; offsety = LCD_HEIGHT_X_SCALE - asteroid->position.y; if(offsetx < asteroid->radius) { //the gap along the width is tiny so draw the asteroid back if(offsety < asteroid->radius) draw_polygon(asteroid->vertices, (asteroid->position.x - LCD_WIDTH_X_SCALE)/SCALE, (asteroid->position.y - LCD_HEIGHT_X_SCALE)/SCALE, NUM_ASTEROID_VERTICES); else draw_polygon(asteroid->vertices, (asteroid->position.x - LCD_WIDTH_X_SCALE)/SCALE, (offsety + LCD_HEIGHT_X_SCALE)/SCALE, NUM_ASTEROID_VERTICES); } else { //the gap along the width is big so draw the asteroid back if(offsety < asteroid->radius) draw_polygon(asteroid->vertices, (offsetx + LCD_WIDTH_X_SCALE)/SCALE, (asteroid->position.y - LCD_HEIGHT_X_SCALE)/SCALE, NUM_ASTEROID_VERTICES); else draw_polygon(asteroid->vertices, (offsetx + LCD_WIDTH_X_SCALE)/SCALE, (offsety + LCD_HEIGHT_X_SCALE)/SCALE, NUM_ASTEROID_VERTICES); } } asteroid++; } } /************************************************* ** Creates nStartNum number of new asteroids of ** full size. **************************************************/ void initilise_game(int nStartNum) { int n; asteroid_count = 0; struct Missile* missile; bEnemyOnScreen = 0; //clear asteroids for(n = 0; n < MAX_NUM_ASTEROIDS; n++) aAsteroids[n].exists = false; //make some LARGE asteroids for(n = 0; n < nStartNum; n++) initilise_asteroid(&aAsteroids[n], LARGE); //ensure all missiles are out of action: missile = aMissiles; for(n = MAX_NUM_MISSILES;--n;) { missile->survived=0; missile++; } } void start_attract_mode(void) { nLevel = 5; nLives = START_LIVES; nScore = 0; attract_flip_timeout = ATTRACT_FLIP_TIME; game_state = ATTRACT_MODE; if(asteroid_count < 3) initilise_game(nLevel); } enum plugin_status startGame() { char s[20]; char aLevel[10]; int button; while(true) { //game starts with at level 1 //with 1 asteroid. start_attract_mode(); //Main loop while(true) { switch(game_state) { case(ATTRACT_MODE): if(attract_flip_timeout < ATTRACT_FLIP_TIME/2) { rb->lcd_putsxy(center_lcd_x - 39, center_lcd_y - 4, "Fire to Start"); if(!attract_flip_timeout) attract_flip_timeout = ATTRACT_FLIP_TIME; } else { rb->snprintf(s, sizeof(s), "Hi Score %d ", nHighScore); rb->lcd_putsxy(center_lcd_x - 30, center_lcd_y - 4, s); } attract_flip_timeout--; break; case(GAME_OVER): rb->lcd_putsxy(center_lcd_x - 25, center_lcd_y - 4, "Game Over"); rb->snprintf(s, sizeof(s), "Score %d ", nScore); rb->lcd_putsxy(1,LCD_HEIGHT-8, s); nShowGameOver--; if(!nShowGameOver) start_attract_mode(); break; case(PAUSE_MODE): rb->snprintf(s, sizeof(s), "Score %d ", nScore); rb->lcd_putsxy(1,LCD_HEIGHT-8, s); rb->lcd_putsxy(center_lcd_x - 15, center_lcd_y - 4, "pause"); draw_and_move_missiles(); draw_lives(); break; case(PLAY_MODE): rb->snprintf(s, sizeof(s), "Score %d ", nScore); rb->lcd_putsxy(1,LCD_HEIGHT-8, s); check_collisions(); draw_and_move_ship(); draw_and_move_missiles(); draw_lives(); break; case(SHOW_LEVEL): nShowLevelTimeout--; rb->snprintf(s, sizeof(s), "Score %d ", nScore); rb->lcd_putsxy(1,LCD_HEIGHT-8, s); rb->snprintf(aLevel, sizeof(aLevel), "stage %d ", nLevel); rb->lcd_putsxy(center_lcd_x - 20, center_lcd_y - 4, aLevel); draw_and_move_ship(); draw_lives(); if(!nShowLevelTimeout) { initilise_game(nLevel); game_state = PLAY_MODE; draw_lives(); } break; } draw_and_move_asteroids(); draw_and_move_enemy(); rb->lcd_update(); rb->lcd_clear_display(); button = rb->button_get_w_tmo(4/*HZ/10*/); 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; case(AST_QUIT): if(game_state == ATTRACT_MODE) return PLUGIN_OK; else { nShowGameOver = 25; game_state = GAME_OVER; } break; case (AST_LEFT_REP): case (AST_LEFT): rotate_ship(SHIP_ROT_ACW_COS, SHIP_ROT_ACW_SIN); break; case (AST_RIGHT_REP): case (AST_RIGHT): 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): hyperspace(); //maybe shield if it gets too hard break; case (AST_FIRE_REP): case (AST_FIRE): if(game_state == ATTRACT_MODE) { nLevel = START_LEVEL; initilise_ship(); initilise_game(nLevel); nShowLevelTimeout = SHOW_LEVEL_TIME; //game_state = SHOW_LEVEL; 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(!nLives) { if(nHighScore < nScore) nHighScore = nScore; if(!nShowGameOver) break; } if(next_missile_count) next_missile_count--; if(next_thrust_count) next_thrust_count--; } } } enum plugin_status plugin_start(struct plugin_api* api, void* parameter) { enum plugin_status returnValue; TEST_PLUGIN_API(api); (void)(parameter); rb = api; game_state = ATTRACT_MODE; /* universal font */ rb->lcd_setfont(FONT_SYSFIXED); rb->backlight_set_timeout(1); iohiscore(); returnValue = startGame(); iohiscore(); rb->lcd_setfont(FONT_UI); // restore normal backlight setting rb->backlight_set_timeout(rb->global_settings->backlight_timeout); return returnValue; } #endif // #ifdef HAVE_LCD_BITMAP