#include "config.h"
#include "dust.h"
#include "file.h"
+#include "float.h"
#include "globals.h"
#include "mt.h"
#include "rocks.h"
#include "sprite.h"
#include "sound.h"
-#ifdef WIN32
-#define SDL_SetAlpha(surf, flag, alpha)
-#endif
-
// ************************************* VARS
// SDL_Surface global variables
SDL_Surface
font *g_font;
-// Structure global variables
-struct enginedots edot[MAXENGINEDOTS], *dotptr = edot;
-struct bangdots bdot[MAXBANGDOTS], *bdotptr = bdot;
+struct dot {
+ int active;
+ float x, y;
+ float dx, dy;
+ float mass; // in DOT_MASS_UNITs
+ float decay; // rate at which to reduce mass.
+};
+
+struct dot edot[MAXENGINEDOTS], *dotptr = edot;
+struct dot bdot[MAXBANGDOTS];
// Other global variables
char topline[1024];
char *initerror = "";
-struct ship ship = { SHIP, 0, NULL, XSIZE/2, YSIZE/2, SCREENDXMIN, 0.0 };
+struct ship ship = { SHIP, 0, NULL, XSIZE/2, YSIZE/2, BARRIER_SPEED, 0.0 };
-float screendx = SCREENDXMIN, screendy = 0.0;
-float back_dist;
+float screendx = BARRIER_SPEED, screendy = 0.0;
+float dist_ahead;
// all movement is based on t_frame.
unsigned long frames, start, end;
int ms_frame; // length of this frame (milliseconds)
int ms_end; // end of this frame (milliseconds)
-int bang = false;
-float bangx, bangy, bangdx, bangdy;
+float gamespeed = 1.00;
int score;
-float fadetimer = 0, faderate;
+float fadetimer = 0;
int pausedown = 0, paused = 0;
extern char *optarg;
extern int optind, opterr, optopt;
-#define TO_TICKS(seconds) ((seconds)*20*opt_gamespeed)
+#define TO_TICKS(seconds) ((seconds)*20*gamespeed)
// ************************************* FUNCS
}
}
-void
-new_bang_dots(int xbang, int ybang, int dx, int dy, SDL_Surface *s)
-{
- int i, n, x, y;
- uint16_t *pixel, c;
- uint32_t colorkey;
- int row_inc;
- double theta, r;
-
- n = 20;
- pixel = s->pixels;
- row_inc = s->pitch/sizeof(uint16_t) - s->w;
- colorkey = s->format->colorkey;
-
- if(SDL_MUSTLOCK(s)) { SDL_LockSurface(s); }
-
- for(i=0; i<n; i++) {
- pixel = s->pixels;
- for(y=0; y<s->h; y++) {
- for(x = 0; x<s->w; x++) {
- c = *pixel++;
- if(c && c != colorkey) {
- theta = frnd()*M_PI*2;
- r = frnd(); r = 1 - r*r;
-
- bdot[bd2].dx = 45*r*cos(theta) + dx;
- bdot[bd2].dy = 45*r*sin(theta) + dy;
- bdot[bd2].x = x + xbang;
- bdot[bd2].y = y + ybang;
- //bdot[bd2].c = (i < n-3) ? 0 : c;
- bdot[bd2].life = frnd() * 99;
- bdot[bd2].decay = frnd()*3 + 1;
- bdot[bd2].active = 1;
-
- bd2 = (bd2+1) % MAXBANGDOTS;
- }
- pixel += row_inc;
- }
- }
- }
-
- if(SDL_MUSTLOCK(s)) { SDL_UnlockSurface(s); }
-}
-
-void
-move_bang_dots(float ticks)
-{
- int i;
- Sprite *hit;
-
- for(i=0; i<MAXBANGDOTS; i++) {
- if(!bdot[i].active) continue;
-
- // decrement life and maybe kill
- bdot[i].life -= bdot[i].decay * ticks/2.0;
- if(bdot[i].life < 0) { bdot[i].active = 0; continue; }
-
- // move and clip
- bdot[i].x += (bdot[i].dx - screendx)*ticks;
- bdot[i].y += (bdot[i].dy - screendy)*ticks;
- if(bdot[i].x < 0 || bdot[i].x >= (XSIZE - 0.000001) || bdot[i].y < 0 || bdot[i].y >= (YSIZE - 0.000001)) {
- bdot[i].active = 0;
- continue;
- }
-
- // check collisions
- if((hit = pixel_collides(bdot[i].x, bdot[i].y))) {
- if(hit->type != SHIP) { // they shouldn't hit the ship, but they do
- bdot[i].active = 0;
- hit->dx += ENGINE_DOT_WEIGHT * bdot[i].life * bdot[i].dx / sprite_mass(hit);
- hit->dy += ENGINE_DOT_WEIGHT * bdot[i].life * bdot[i].dy / sprite_mass(hit);
- continue;
- }
- }
- }
-}
-
-
-void
-draw_bang_dots(SDL_Surface *s)
-{
- int i;
- uint16_t *pixels, *pixel, c;
- int row_inc = s->pitch/sizeof(uint16_t);
-
- pixels = (uint16_t *) s->pixels;
-
- for(i=0; i<MAXBANGDOTS; i++) {
- if(!bdot[i].active) continue;
-
- pixel = pixels + row_inc*(int)(bdot[i].y) + (int)(bdot[i].x);
- if(bdot[i].c) c = bdot[i].c; else c = heatcolor[(int)(bdot[i].life)*3];
- *pixel = c;
- }
-}
-
void
new_engine_dots(float time_span) {
dy = r * -sin(a); // screen y is "backwards".
dotptr->active = 1;
+ dotptr->decay = 3;
// dot was created at a random time during the time span
time = frnd() * time_span; // this is how long ago
if(dir&1) {
dotptr->dx = past_ship_dx + 2*dx;
dotptr->dy = past_ship_dy + 20*dy;
- dotptr->life = 60 * fabs(dy);
+ dotptr->mass = 60 * fabs(dy);
} else {
dotptr->dx = past_ship_dx + 20*dx;
dotptr->dy = past_ship_dy + 2*dy;
- dotptr->life = 60 * fabs(dx);
+ dotptr->mass = 60 * fabs(dx);
}
// move the dot as though it were created in the past
}
}
+
void
-move_engine_dots(float ticks) {
- int i;
- Sprite *hit;
+new_bang_dots(struct sprite *s)
+{
+ int i, n, x, y;
+ uint16_t *pixel, c;
+ uint32_t colorkey;
+ int row_inc;
+ double theta, r;
+ SDL_Surface *img = s->image;
- for(i = 0; i<MAXENGINEDOTS; i++) {
- if(!edot[i].active) continue;
+ n = 20;
+ pixel = img->pixels;
+ row_inc = img->pitch/sizeof(uint16_t) - img->w;
+ colorkey = img->format->colorkey;
+
+ if(SDL_MUSTLOCK(img)) { SDL_LockSurface(img); }
+
+ for(i=0; i<n; i++) {
+ pixel = img->pixels;
+ for(y=0; y<img->h; y++) {
+ for(x = 0; x<img->w; x++) {
+ c = *pixel++;
+ if(c && c != colorkey) {
+ theta = frnd()*M_PI*2;
+ r = frnd(); r = 1 - r*r;
+
+ bdot[bd2].dx = 45*r*cos(theta) + s->dx;
+ bdot[bd2].dy = 45*r*sin(theta) + s->dy;
+ bdot[bd2].x = x + s->x;
+ bdot[bd2].y = y + s->y;
+ bdot[bd2].mass = frnd() * 99;
+ bdot[bd2].decay = frnd()*1.5 + 0.5;
+ bdot[bd2].active = 1;
- edot[i].x += (edot[i].dx - screendx)*ticks;
- edot[i].y += (edot[i].dy - screendy)*ticks;
- edot[i].life -= t_frame*3;
- if(edot[i].life < 0 || edot[i].x<0 || edot[i].x >= (XSIZE - 0.000001) || edot[i].y < 0 || edot[i].y >= (YSIZE - 0.000001)) {
- edot[i].active = 0;
- continue;
+ bd2 = (bd2+1) % MAXBANGDOTS;
+ }
+ pixel += row_inc;
+ }
}
+ }
+
+ if(SDL_MUSTLOCK(img)) { SDL_UnlockSurface(img); }
+}
- // check collisions
- if((hit = pixel_collides(edot[i].x, edot[i].y))) {
- if(hit->type != SHIP) { // they shouldn't hit the ship, but they do
- edot[i].active = 0;
- hit->dx += ENGINE_DOT_WEIGHT * edot[i].life * edot[i].dx / sprite_mass(hit);
- hit->dy += ENGINE_DOT_WEIGHT * edot[i].life * edot[i].dy / sprite_mass(hit);
- continue;
+
+void
+move_dot(struct dot *d, float ticks)
+{
+ Sprite *hit;
+ float mass;
+
+ if(d->active) {
+ d->x += (d->dx - screendx) * ticks;
+ d->y += (d->dy - screendy) * ticks;
+ d->mass -= ticks * d->decay;
+ if(d->mass < 0 || fclip(d->x, XSIZE) || fclip(d->y, YSIZE))
+ d->active = 0;
+ else {
+ hit = pixel_collides(d->x, d->y);
+ if(hit && hit->type != SHIP) {
+ d->active = 0;
+ mass = sprite_mass(hit);
+ hit->dx += DOT_MASS_UNIT * d->mass * d->dx / mass;
+ hit->dy += DOT_MASS_UNIT * d->mass * d->dy / mass;
}
}
}
}
void
-draw_engine_dots(SDL_Surface *s) {
+move_dots(float ticks)
+{
+ int i;
+
+ for(i=0; i<MAXBANGDOTS; i++) move_dot(&bdot[i], ticks);
+ for(i=0; i<MAXENGINEDOTS; i++) move_dot(&edot[i], ticks);
+}
+
+
+void
+draw_bang_dots(SDL_Surface *s)
+{
+ int i;
+ uint16_t *pixels, *pixel;
+ int row_inc = s->pitch/sizeof(uint16_t);
+
+ pixels = (uint16_t *) s->pixels;
+
+ for(i=0; i<MAXBANGDOTS; i++) {
+ if(!bdot[i].active) continue;
+ pixel = pixels + row_inc*(int)(bdot[i].y) + (int)(bdot[i].x);
+ *pixel = heatcolor[(int)(bdot[i].mass)*3];
+ }
+}
+
+void
+draw_engine_dots(SDL_Surface *s)
+{
int i;
- uint16_t c;
- uint16_t *pixels = (uint16_t *) s->pixels;
+ uint16_t *pixels, *pixel;
int row_inc = s->pitch/sizeof(uint16_t);
- int heatindex;
+
+ pixels = (uint16_t *) s->pixels;
for(i = 0; i<MAXENGINEDOTS; i++) {
if(!edot[i].active) continue;
-
- heatindex = edot[i].life * 6;
- c = heatindex>3*W ? heatcolor[3*W-1] : heatcolor[heatindex];
- pixels[row_inc*(int)(edot[i].y) + (int)(edot[i].x)] = c;
+ pixel = pixels + row_inc*(int)(edot[i].y) + (int)(edot[i].x);
+ *pixel = heatcolor[min(3*W-1, (int)(edot[i].mass)*6)];
}
}
int x;
char *text0, *text1;
SDL_Rect dest;
- float a_game = 0, a_over = 0;
-
- // fade in "GAME", then "OVER".
- a_game = min(1.0, faderate*fadetimer/3.0);
- if(a_game == 1.0) a_over = min(1.0, faderate*fadetimer/3.0 - 1);
fadetimer += t_frame;
dest.x = (XSIZE-surf_b_game->w)/2;
dest.y = (YSIZE-surf_b_game->h)/2-40;
- //SDL_SetAlpha(surf_b_game, SDL_SRCALPHA, (int)(a_game*(200 + 55*cos(fadetimer))));
SDL_BlitSurface(surf_b_game,NULL,surf_screen,&dest);
dest.x = (XSIZE-surf_b_over->w)/2;
dest.y = (YSIZE-surf_b_over->h)/2 + 40;
- //SDL_SetAlpha(surf_b_over, SDL_SRCALPHA, (int)(a_over*(200 + 55*sin(fadetimer))));
SDL_BlitSurface(surf_b_over,NULL,surf_screen,&dest);
if(new_high_score(score)) {
dest.x = (XSIZE-surf_b_variations->w)/2 + cos(fadetimer/6.5)*10;
dest.y = (YSIZE/2-surf_b_variations->h)/2 + sin(fadetimer/5.0)*10;
- //SDL_SetAlpha(surf_b_variations, SDL_SRCALPHA, (int)(200 + 55*sin(fadetimer)));
SDL_BlitSurface(surf_b_variations,NULL,surf_screen,&dest);
dest.x = (XSIZE-surf_b_on->w)/2 + cos((fadetimer + 1.0)/6.5)*10;
dest.y = (YSIZE/2-surf_b_on->h)/2 + surf_b_variations->h + 20 + sin((fadetimer + 1.0)/5.0)*10;
- //SDL_SetAlpha(surf_b_on, SDL_SRCALPHA, (int)(200 + 55*sin(fadetimer-1.0)));
SDL_BlitSurface(surf_b_on,NULL,surf_screen,&dest);
dest.x = (XSIZE-surf_b_rockdodger->w)/2 + cos((fadetimer + 2.0)/6.5)*10;
dest.y = (YSIZE/2-surf_b_rockdodger->h)/2 + surf_b_variations->h + surf_b_on->h + 40 + sin((fadetimer + 2.0)/5)*10;
- //SDL_SetAlpha(surf_b_rockdodger, SDL_SRCALPHA, (int)(200 + 55*sin(fadetimer-2.0)));
SDL_BlitSurface(surf_b_rockdodger,NULL,surf_screen,&dest);
text = msgs[g_easy][(int)(fadetimer/35)%NSEQUENCE];
}
void
-draw(void) {
-
+draw(void)
+{
SDL_FillRect(surf_screen,NULL,0); // black background
- draw_dots(surf_screen); // background dots
+ draw_dots(surf_screen); // background dots
draw_sprite(SPRITE(&ship));
draw_rocks();
show_lives();
show_score();
- // If it's game over, show the game over graphic in the dead centre
switch (state) {
case GAME_OVER: draw_game_over(); break;
case TITLE_PAGE: draw_title_page(); break;
- case HIGH_SCORE_ENTRY:
- play_tune(TUNE_HIGH_SCORE_ENTRY);
- // FALL THROUGH TO
+ case HIGH_SCORE_ENTRY: play_tune(TUNE_HIGH_SCORE_ENTRY);
+ // and fall through to
case HIGH_SCORE_DISPLAY:
// Display de list o high scores mon.
display_scores(surf_screen, 150,50);
; // no action necessary
}
- collisions();
-
// Update the surface
SDL_Flip(surf_screen);
}
static inline void
-kill_ship(Sprite *ship)
+kill_ship(struct ship *ship)
{
- ship->flags = MOVE;
- bang = true;
+ play_sound(SOUND_BANG);
+ new_bang_dots(SPRITE(ship));
+ if(--ship->lives) {
+ state = DEAD_PAUSE;
+ state_timeout = DEAD_PAUSE_LENGTH;
+ // want ship to be invisible, but keep drifting at sqrt(speed)
+ // to leave it in the middle of the space from the explosion.
+ ship->flags = MOVE;
+ ship->dx = (ship->dx < 0) ? -sqrt(-ship->dx) : sqrt(ship->dx);
+ ship->dy = (ship->dy < 0) ? -sqrt(-ship->dy) : sqrt(ship->dy);
+ if(ship->dx < BARRIER_SPEED) ship->dx = BARRIER_SPEED;
+ } else {
+ state = GAME_OVER;
+ state_timeout = 200.0;
+ fadetimer = 0.0;
+ ship->flags = 0;
+ // scrolling is based on the ship speed, so we need to reset it.
+ ship->dx = BARRIER_SPEED; ship->dy = 0;
+ }
}
void
do_collision(Sprite *a, Sprite *b)
{
- if(a->type == SHIP) kill_ship(a);
- else if (b->type == SHIP) kill_ship(b);
+ if(a->type == SHIP) kill_ship((struct ship *)a);
+ else if(b->type == SHIP) kill_ship((struct ship *)b);
else bounce(a, b);
}
if(state_timeout > 0) return;
switch(state) {
+ case GAMEPLAY: break; // no action necessary
case DEAD_PAUSE:
// Restore the ship and continue playing
ship.flags = DRAW|MOVE|COLLIDE;
state = HIGH_SCORE_DISPLAY;
state_timeout = 200.0;
break;
- case GAMEPLAY:
- ; // no action necessary
}
}
if(ms_frame > 1000) {
ms_frame = 1000;
}
- t_frame = opt_gamespeed * ms_frame / 50;
+ t_frame = gamespeed * ms_frame / 50;
frames++;
while(SDL_PollEvent(&e)) {
if(!paused) {
update_state(t_frame);
- if(state == DEAD_PAUSE && bangx < 60) bangx = 60;
-
// SCROLLING
- tmp = (ship.y+ship.h/2+ship.dy*t_frame-YSCROLLTO)/25 + (ship.dy-screendy);
+ tmp = (ship.y+ship.h/2 + ship.dy*t_frame - YSCROLLTO)/25 + (ship.dy-screendy);
screendy += tmp * t_frame/12;
- tmp = (ship.x+ship.w/2+ship.dx*t_frame-XSCROLLTO)/25 + (ship.dx-screendx);
+ tmp = (ship.x+ship.w/2 + ship.dx*t_frame - XSCROLLTO)/25 + (ship.dx-screendx);
screendx += tmp * t_frame/12;
// taper off so we don't hit the barrier abruptly.
// (if we would hit in < 2 seconds, adjust to 2 seconds).
- if(back_dist + (screendx - SCREENDXMIN)*TO_TICKS(2) < 0)
- screendx = SCREENDXMIN - (back_dist/TO_TICKS(2));
- back_dist += (screendx - SCREENDXMIN)*t_frame;
- if(opt_max_lead >= 0) back_dist = min(back_dist, opt_max_lead);
-
- // move bang center
- bangx += (bangdx - screendx)*t_frame;
- bangy += (bangdy - screendy)*t_frame;
+ if(dist_ahead + (screendx - BARRIER_SPEED)*TO_TICKS(2) < 0)
+ screendx = BARRIER_SPEED - (dist_ahead/TO_TICKS(2));
+ dist_ahead += (screendx - BARRIER_SPEED)*t_frame;
+ if(MAX_DIST_AHEAD >= 0) dist_ahead = min(dist_ahead, MAX_DIST_AHEAD);
move_sprites(t_frame);
- move_engine_dots(t_frame);
- move_bang_dots(t_frame);
+ move_dots(t_frame);
move_dust(t_frame);
+ new_rocks(t_frame);
+ new_engine_dots(t_frame);
+
+ collisions();
+
// BOUNCE off left or right edge of screen
if(ship.x < 0 || ship.x+ship.w > XSIZE) {
ship.x -= (ship.dx-screendx)*t_frame;
- ship.dx = screendx - (ship.dx-screendx)*opt_bounciness;
+ ship.dx = screendx - (ship.dx-screendx)*BOUNCINESS;
}
// BOUNCE off top or bottom of screen
if(ship.y < 0 || ship.y+ship.h > YSIZE) {
ship.y -= (ship.dy-screendy)*t_frame;
- ship.dy = screendy - (ship.dy-screendy)*opt_bounciness;
+ ship.dy = screendy - (ship.dy-screendy)*BOUNCINESS;
}
- new_rocks(t_frame);
- new_engine_dots(t_frame);
-
draw();
- if(state == GAMEPLAY && bang) {
- // Died
- bang = false;
- play_sound(SOUND_BANG); // Play the explosion sound
- bangx = ship.x; bangy = ship.y; bangdx = ship.dx; bangdy = ship.dy;
- new_bang_dots(ship.x,ship.y,ship.dx,ship.dy,ship.image);
-
- if(--ship.lives) {
- state = DEAD_PAUSE;
- state_timeout = DEAD_PAUSE_LENGTH;
- ship.dx = (ship.dx < 0) ? -sqrt(-ship.dx) : sqrt(ship.dx);
- ship.dy = (ship.dy < 0) ? -sqrt(-ship.dy) : sqrt(ship.dy);
- if(ship.dx < SCREENDXMIN) ship.dx = SCREENDXMIN;
- } else {
- state = GAME_OVER;
- ship.dx = SCREENDXMIN; ship.dy = 0;
- state_timeout = 200.0;
- fadetimer = 0.0;
- faderate = t_frame;
- }
- }
-
// new game
if((keystate[SDLK_SPACE] || keystate[SDLK_1] || keystate[SDLK_2])
&& (state == HIGH_SCORE_DISPLAY
g_easy = 0;
initial_rocks = NORMAL_I_ROCKS;
final_rocks = NORMAL_F_ROCKS;
- if(opt_gamespeed == EASY_GAMESPEED)
- opt_gamespeed = NORMAL_GAMESPEED;
+ if(gamespeed == EASY_GAMESPEED)
+ gamespeed = NORMAL_GAMESPEED;
} else if(keystate[SDLK_1]) {
g_easy = 1;
initial_rocks = EASY_I_ROCKS;
final_rocks = EASY_F_ROCKS;
- opt_gamespeed = EASY_GAMESPEED;
+ gamespeed = EASY_GAMESPEED;
}
reset_sprites();
reset_rocks();
- screendx = SCREENDXMIN; screendy = 0;
+ screendx = BARRIER_SPEED; screendy = 0;
ship.x = XSIZE/2.2; ship.y = YSIZE/2 - ship.w/2;
ship.dx = screendx; ship.dy = screendy;