JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
stop busy-waiting while paused
[vor.git] / main.c
diff --git a/main.c b/main.c
index 72f3cfd..14a486b 100644 (file)
--- a/main.c
+++ b/main.c
 #include "config.h"
 #include "dust.h"
 #include "file.h"
+#include "float.h"
 #include "globals.h"
 #include "mt.h"
 #include "rocks.h"
 #include "score.h"
 #include "sprite.h"
 #include "sound.h"
+#include "autopilot.h"
 
 // ************************************* VARS
 // SDL_Surface global variables
@@ -57,9 +59,17 @@ 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.
+       int heat;     // heat multiplier (color).
+};
+
+struct dot edot[MAXENGINEDOTS], *dotptr = edot;
+struct dot bdot[MAXBANGDOTS];
 
 // Other global variables
 char topline[1024];
@@ -82,7 +92,7 @@ int score;
 
 float fadetimer = 0;
 
-int pausedown = 0, paused = 0;
+int paused = 0;
 
 // bangdot start (bd1) and end (bd2) position:
 int bd1 = 0, bd2 = 0;
@@ -131,108 +141,11 @@ init_engine_dots() {
        }
 }
 
-void
-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;
-
-       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].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(img)) { SDL_UnlockSurface(img); }
-}
-
-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) {
+new_engine_dots(void) {
        int dir, i;
-       int n = time_span * ENGINE_DOTS_PER_TIC;
+       int n = t_frame * ENGINE_DOTS_PER_TIC;
        float a, r;  // angle, random length
        float dx, dy;
        float hx, hy; // half ship width/height.
@@ -253,10 +166,11 @@ new_engine_dots(float time_span) {
                                dx = r * cos(a);
                                dy = r * -sin(a);  // screen y is "backwards".
 
-                               dotptr->active = 1;
+                               dotptr->decay = 3;
+                               dotptr->heat = 6;
 
                                // dot was created at a random time during the time span
-                               time = frnd() * time_span; // this is how long ago
+                               time = frnd() * t_frame; // this is how long ago
 
                                // calculate how fast the ship was going when this engine dot was
                                // created (as if it had a smooth acceleration). This is used in
@@ -271,82 +185,140 @@ new_engine_dots(float time_span) {
 
                                // the starting position (not speed) of the dot is calculated as
                                // though the ship were traveling at a constant speed for this
-                               // time_span.
+                               // t_frame.
                                dotptr->x = (ship.x - (ship.dx - screendx) * time) + s[dir]*hx;
                                dotptr->y = (ship.y - (ship.dy - screendy) * time) + s[(dir+1)&3]*hy;
                                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
                                dotptr->x += (dotptr->dx - screendx) * time;
                                dotptr->y += (dotptr->dy - screendy) * time;
 
-                               if(dotptr - edot < MAXENGINEDOTS-1) dotptr++;
-                               else dotptr = edot;
+                               if(!fclip(dotptr->x, XSIZE) && !fclip(dotptr->y, YSIZE)) {
+                                       dotptr->active = 1;
+                                       if(dotptr - edot < MAXENGINEDOTS-1) {
+                                               dotptr++;
+                                       } else {
+                                               dotptr = edot;
+                                       }
+                               }
                        }
                }
        }
 }
 
+
 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;
-
-               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;
+       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].heat = 3;
+                                       bdot[bd2].active = 1;
+
+                                       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)
+{
+       Sprite *hit;
+       float mass;
+
+       if(d->active) {
+               d->x += (d->dx - screendx) * t_frame;
+               d->y += (d->dy - screendy) * t_frame;
+               d->mass -= t_frame * 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) if(hit->type != SHIP) {
+                               d->active = 0;
+                               mass = sprite_mass(hit);
+                               hit->dx += DOT_MASS_UNIT * d->mass * (d->dx - hit->dx) / mass;
+                               hit->dy += DOT_MASS_UNIT * d->mass * (d->dy - hit->dy) / mass;
                        }
                }
        }
 }
 
 void
-draw_engine_dots(SDL_Surface *s) {
+move_dots(void)
+{
        int i;
-       uint16_t c;
-       uint16_t *pixels = (uint16_t *) s->pixels;
-       int row_inc = s->pitch/sizeof(uint16_t);
-       int heatindex;
 
-       for(i = 0; i<MAXENGINEDOTS; i++) {
-               if(!edot[i].active) continue;
+       for(i=0; i<MAXBANGDOTS; i++) move_dot(&bdot[i]);
+       for(i=0; i<MAXENGINEDOTS; i++) move_dot(&edot[i]);
+}
+
+
+void
+draw_dot(struct dot *d)
+{
+       uint16_t *pixels, *pixel;
+       int row_inc;
 
-               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;
+       if(d->active) {
+               pixels = (uint16_t *) surf_screen->pixels;
+               row_inc = surf_screen->pitch / sizeof(uint16_t);
+               pixel = pixels + (int)d->y * row_inc + (int)d->x;
+               *pixel = heatcolor[min(3*W-1, (int)(d->mass * d->heat))];
        }
 }
 
 void
-draw_dots(SDL_Surface *s) {
-       if(SDL_MUSTLOCK(s)) { SDL_LockSurface(s); }
-       draw_dust(s);
-       draw_engine_dots(s);
-       draw_bang_dots(s);
-       if(SDL_MUSTLOCK(s)) { SDL_UnlockSurface(s); }
+draw_dots(void) {
+       int i;
+
+       if(SDL_MUSTLOCK(surf_screen)) { SDL_LockSurface(surf_screen); }
+       draw_dust();
+       for(i=0; i<MAXBANGDOTS; i++) draw_dot(&bdot[i]);
+       for(i=0; i<MAXENGINEDOTS; i++) draw_dot(&edot[i]);
+       if(SDL_MUSTLOCK(surf_screen)) { SDL_UnlockSurface(surf_screen); }
 }
 
 SDL_Surface *
@@ -537,7 +509,7 @@ void
 draw(void)
 {
        SDL_FillRect(surf_screen,NULL,0);  // black background
-       draw_dots(surf_screen);            // background dots
+       draw_dots();            // background dots
        draw_sprite(SPRITE(&ship));
        draw_rocks();
 
@@ -545,16 +517,19 @@ draw(void)
        show_score();
 
        switch (state) {
-               case GAME_OVER: draw_game_over(); break;
+               case GAME_OVER:
+                       draw_game_over();
+                       break;
 
-               case TITLE_PAGE: draw_title_page(); break;
+               case TITLE_PAGE:
+                       draw_title_page();
+                       break;
 
-               case HIGH_SCORE_ENTRY: play_tune(TUNE_HIGH_SCORE_ENTRY);
-                       // and fall through to
+               case HIGH_SCORE_ENTRY:
                case HIGH_SCORE_DISPLAY:
-                       // Display de list o high scores mon.
-                       display_scores(surf_screen, 150,50);
+                       display_scores(150,50);
                        break;
+
                case GAMEPLAY:
                case DEAD_PAUSE:
                        ; // no action necessary
@@ -580,6 +555,7 @@ kill_ship(struct ship *ship)
                if(ship->dx < BARRIER_SPEED) ship->dx = BARRIER_SPEED;
        } else {
                state = GAME_OVER;
+               play_tune(TUNE_TITLE_PAGE);
                state_timeout = 200.0;
                fadetimer = 0.0;
                ship->flags = 0;
@@ -610,9 +586,9 @@ init_score_entry(void)
 
 // Count down the state timer, and change state when it gets to zero or less;
 void
-update_state(float ticks)
+update_state(void)
 {
-       state_timeout -= ticks*3;
+       state_timeout -= t_frame*3;
        if(state_timeout > 0) return;
 
        switch(state) {
@@ -621,7 +597,6 @@ update_state(float ticks)
                        // Restore the ship and continue playing
                        ship.flags = DRAW|MOVE|COLLIDE;
                        state = GAMEPLAY;
-                       play_tune(TUNE_GAMEPLAY);
                        break;
                case GAME_OVER:
                        if(new_high_score(score)) init_score_entry();
@@ -653,16 +628,25 @@ gameloop() {
        for(;;) {
                ms_frame = SDL_GetTicks() - ms_end;
                ms_end += ms_frame;
-               if(ms_frame > 1000) {
-                       ms_frame = 1000;
+               if(ms_frame > 50) {
+                       ms_frame = 50;
                }
                t_frame = gamespeed * ms_frame / 50;
                frames++;
 
-               while(SDL_PollEvent(&e)) {
+               if(opt_autopilot) {
+                       autopilot(t_frame);
+               }
+
+               while(paused ? SDL_WaitEvent(&e) : SDL_PollEvent(&e)) {
                        switch(e.type) {
                                case SDL_QUIT: return;
                                case SDL_KEYDOWN:
+                                       // even during high-score entry
+                                       if(e.key.keysym.sym == SDLK_ESCAPE) {
+                                               return;
+                                       }
+
                                        if(state == HIGH_SCORE_ENTRY) {
                                                if(!process_score_input(&e.key.keysym)) {
                                                        // Write the high score table to the file
@@ -670,44 +654,65 @@ gameloop() {
                                                        // continue to display the scores briefly
                                                        state = HIGH_SCORE_DISPLAY;
                                                        state_timeout = 200;
-                                                       play_tune(TUNE_TITLE_PAGE);
                                                }
-                                       } else if(e.key.keysym.sym == SDLK_q) {
-                                               return;
-                                       }
-
-                                       if(e.key.keysym.sym == SDLK_ESCAPE) {
-                                               return;
+                                       } else {
+                                               switch(e.key.keysym.sym) {
+                                                       case SDLK_q:
+                                                               return;
+                                                       case SDLK_3:
+                                                       case SDLK_PRINT:
+                                                               // FIXME make a unique filename like vor-screenshot-<pid>-<count>.bmp
+                                                               SDL_SaveBMP(surf_screen, "snapshot.bmp");
+                                                               break;
+                                                       case SDLK_p:
+                                                       case SDLK_PAUSE:
+                                                               paused = !paused;
+                                                               if(paused) {
+                                                                       pause_tune();
+                                                               } else {
+                                                                       resume_tune();
+                                                                       ms_end = SDL_GetTicks();
+                                                               }
+                                                               break;
+                                                       default:
+                                                               // other keys are handled by checking keystate each frame
+                                                               break;
+                                               }
                                        }
                                        break;
                        }
                }
                keystate = SDL_GetKeyState(NULL);
+               if(opt_autopilot) {
+                       autopilot_fix_keystates(keystate);
+               }
 
                if(state == GAMEPLAY) {
                        if(!paused) {
                                score += ms_frame;
                                
-                               if(keystate[SDLK_LEFT]  || keystate[SDLK_h]) { ship.dx -= THRUSTER_STRENGTH*t_frame; ship.jets |= 1<<0;}
-                               if(keystate[SDLK_DOWN]  || keystate[SDLK_t]) { ship.dy += THRUSTER_STRENGTH*t_frame; ship.jets |= 1<<1;}
-                               if(keystate[SDLK_RIGHT] || keystate[SDLK_n]) { ship.dx += THRUSTER_STRENGTH*t_frame; ship.jets |= 1<<2;}
-                               if(keystate[SDLK_UP]    || keystate[SDLK_c]) { ship.dy -= THRUSTER_STRENGTH*t_frame; ship.jets |= 1<<3;}
-                               if(keystate[SDLK_3])            { SDL_SaveBMP(surf_screen, "snapshot.bmp"); }
-                       }
-
-                       if(keystate[SDLK_p] | keystate[SDLK_s]) {
-                               if(!pausedown) {
-                                       paused = !paused;
-                                       pausedown = 1;
-                                       if(!paused) ms_end = SDL_GetTicks();
+                               if(keystate[SDLK_LEFT]  || keystate[SDLK_KP4]) {
+                                       ship.dx -= THRUSTER_STRENGTH*t_frame; ship.jets |= 1<<0;
+                               }
+                               if(keystate[SDLK_DOWN]  || keystate[SDLK_KP5] || keystate[SDLK_KP2]) {
+                                       ship.dy += THRUSTER_STRENGTH*t_frame; ship.jets |= 1<<1;
+                               }
+                               if(keystate[SDLK_RIGHT] || keystate[SDLK_KP6]) {
+                                       ship.dx += THRUSTER_STRENGTH*t_frame; ship.jets |= 1<<2;
+                               }
+                               if(keystate[SDLK_UP]    || keystate[SDLK_KP8]) {
+                                       ship.dy -= THRUSTER_STRENGTH*t_frame; ship.jets |= 1<<3;
+                               }
+                               if(ship.jets) {
+                                       ship.dx = fconstrain2(ship.dx, -50, 50);
+                                       ship.dy = fconstrain2(ship.dy, -50, 50);
                                }
-                       } else {
-                               pausedown = 0;
                        }
+
                }
 
                if(!paused) {
-                       update_state(t_frame);
+                       update_state();
 
                        // SCROLLING
                        tmp = (ship.y+ship.h/2 + ship.dy*t_frame - YSCROLLTO)/25 + (ship.dy-screendy);
@@ -721,25 +726,31 @@ gameloop() {
                        dist_ahead += (screendx - BARRIER_SPEED)*t_frame;
                        if(MAX_DIST_AHEAD >= 0) dist_ahead = min(dist_ahead, MAX_DIST_AHEAD);
 
-                       move_sprites(t_frame);  new_rocks(t_frame);
-                       move_engine_dots(t_frame); new_engine_dots(t_frame);
-                       move_bang_dots(t_frame);
-                       move_dust(t_frame);
+                       move_sprites();
+                       move_dots();
+                       move_dust();
 
-                       collisions();
+                       new_rocks();
 
                        // 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)*BOUNCINESS;
+                               ship.x = fconstrain(ship.x, XSIZE - ship.w);
                        }
 
                        // 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)*BOUNCINESS;
+                               ship.y = fconstrain(ship.y, YSIZE - ship.h);
                        }
 
+                       new_engine_dots();
+
+                       collisions(); // must happen after ship bouncing because it puts pixels where the ship is (thus the ship must be on the screen)
+
+
                        draw();
 
                        // new game