#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <time.h>
#include "font.h"
#include "args.h"
#include "common.h"
-#include "config.h"
+#include <config.h>
+#include "vorconfig.h"
#include "dust.h"
#include "file.h"
#include "float.h"
#include "score.h"
#include "sprite.h"
#include "sound.h"
+#include "autopilot.h"
// ************************************* VARS
// SDL_Surface global variables
font *g_font;
+#define ENGINEDOT 0
+#define BANGDOT 1
+
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).
+ uint8_t type; // BANGDOT or ENGINEDOT
};
+void draw(void);
+
struct dot edot[MAXENGINEDOTS], *dotptr = edot;
struct dot bdot[MAXBANGDOTS];
// Other global variables
char topline[1024];
char *initerror = "";
+int screenshot_number = 0;
struct ship ship = { SHIP, 0, NULL, XSIZE/2, YSIZE/2, BARRIER_SPEED, 0.0 };
float fadetimer = 0;
-int pausedown = 0, paused = 0;
+int paused = 0;
+
+int num_joysticks = 0;
+SDL_Joystick **joysticks = NULL;
// bangdot start (bd1) and end (bd2) position:
int bd1 = 0, bd2 = 0;
// ************************************* FUNCS
+#ifdef HAVE_NANOSLEEP
+void
+tiny_sleep() {
+ struct timespec t;
+ t.tv_sec = 0;
+ t.tv_nsec = 1;
+ nanosleep(&t, 0);
+}
+#else
+#define tiny_sleep()
+#endif
+
void
-init_engine_dots() {
+init_dots() {
int i;
for(i = 0; i<MAXENGINEDOTS; i++) {
edot[i].active = 0;
+ edot[i].type = ENGINEDOT;
+ }
+ for(i = 0; i<MAXBANGDOTS; i++) {
+ bdot[i].active = 0;
+ bdot[i].type = BANGDOT;
}
}
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.
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
// 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->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;
+ }
+ }
}
}
}
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;
if(SDL_MUSTLOCK(img)) { SDL_UnlockSurface(img); }
}
+void
+kill_rock(struct rock *r) {
+ r->x = -200;
+}
void
-move_dot(struct dot *d, float ticks)
+move_dot(struct dot *d)
{
Sprite *hit;
float mass;
if(d->active) {
- d->x += (d->dx - screendx) * ticks;
- d->y += (d->dy - screendy) * ticks;
- d->mass -= ticks * d->decay;
+ 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 && hit->type != SHIP) {
+ if(hit) if(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;
+ if(d->type == BANGDOT) {
+ struct rock *rock = (struct rock*)hit;
+ rock->life -= (d->dx - hit->dx) * (d->dx - hit->dx) + (d->dy - hit->dy) * (d->dy - hit->dy);
+ if(rock->life < 0) {
+ kill_rock(rock);
+ }
+ }
+ 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
-move_dots(float ticks)
+move_dots(void)
{
int i;
- for(i=0; i<MAXBANGDOTS; i++) move_dot(&bdot[i], ticks);
- for(i=0; i<MAXENGINEDOTS; i++) move_dot(&edot[i], ticks);
+ for(i=0; i<MAXBANGDOTS; i++) move_dot(&bdot[i]);
+ for(i=0; i<MAXENGINEDOTS; i++) move_dot(&edot[i]);
}
void
-draw_bang_dots(SDL_Surface *s)
+draw_dot(struct dot *d)
{
- int i;
uint16_t *pixels, *pixel;
- int row_inc = s->pitch/sizeof(uint16_t);
-
- pixels = (uint16_t *) s->pixels;
+ int row_inc;
- 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];
+ 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_engine_dots(SDL_Surface *s)
-{
+draw_dots(void) {
int i;
- uint16_t *pixels, *pixel;
- int row_inc = s->pitch/sizeof(uint16_t);
-
- pixels = (uint16_t *) s->pixels;
- for(i = 0; i<MAXENGINEDOTS; i++) {
- if(!edot[i].active) continue;
- pixel = pixels + row_inc*(int)(edot[i].y) + (int)(edot[i].x);
- *pixel = heatcolor[min(3*W-1, (int)(edot[i].mass)*6)];
- }
-}
-
-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); }
+ 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 *
font_free(g_font);
}
+void
+set_video_mode() {
+ Uint32 flag;
+
+ // Attempt to get the required video size
+ flag = SDL_DOUBLEBUF | SDL_HWSURFACE;
+ if(opt_fullscreen) flag |= SDL_FULLSCREEN;
+ surf_screen = SDL_SetVideoMode(XSIZE,YSIZE,16,flag);
+}
+
+void
+toggle_fullscreen() {
+ opt_fullscreen = 1 - opt_fullscreen;
+ set_video_mode();
+ if(paused) {
+ draw();
+ }
+}
+
+
int
init(void) {
int i;
char *s;
- Uint32 flag;
// Where are our data files?
if(!find_files()) exit(1);
if(opt_sound) {
// Initialize SDL with audio and video
- if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) != 0) {
+ if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK) != 0) {
opt_sound = 0;
fputs("Can't open sound, starting without it\n", stderr);
atexit(SDL_Quit);
}
} else {
// Initialize with video only
- CONDERROR(SDL_Init(SDL_INIT_VIDEO) != 0);
+ CONDERROR(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) != 0);
atexit(SDL_Quit);
}
play_tune(TUNE_TITLE_PAGE);
+
// Attempt to get the required video size
- flag = SDL_DOUBLEBUF | SDL_HWSURFACE;
- if(opt_fullscreen) flag |= SDL_FULLSCREEN;
- surf_screen = SDL_SetVideoMode(XSIZE,YSIZE,16,flag);
+ set_video_mode();
// Set the title bar text
SDL_WM_SetCaption("Variations on Rockdodger", "VoR");
exit(1);
}
- init_engine_dots();
+ num_joysticks = SDL_NumJoysticks();
+printf("num_joysticks: %i\n", num_joysticks);
+ if(num_joysticks)
+ {
+ joysticks = (SDL_Joystick **)malloc(num_joysticks * sizeof(SDL_Joystick *));
+ for(i = 0; i < num_joysticks; ++i) {
+ NULLERROR(joysticks[i] = SDL_JoystickOpen(i));
+ }
+ }
+
+ init_dots();
init_dust();
init_sprites();
x = (XSIZE-font_width(text))/2 + cos(fadetimer/4.5)*10;
font_write(x,YSIZE-100 + cos(fadetimer/3)*5,text);
- text = "Version " VERSION;
+ text = "Version " PACKAGE_VERSION;
x = (XSIZE-font_width(text))/2 + sin(fadetimer/4.5)*10;
font_write(x,YSIZE-50 + sin(fadetimer/2)*5,text);
}
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();
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
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;
// 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) {
// 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();
gameloop() {
SDL_Event e;
Uint8 *keystate;
+ Sint16 x_move, y_move;
+ char button_pressed = 0;
+ short i, j;
float tmp;
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
// 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:
+ {
+ FILE *screenshot_fp;
+ char tmp[30];
+ char *screenshot_filename = &(tmp[0]);
+ for(;;) {
+ snprintf(screenshot_filename, 30, "vor-screenshot-%02i.bmp", screenshot_number++);
+ screenshot_fp = fopen(screenshot_filename, "r");
+ if(screenshot_fp) {
+ fclose(screenshot_fp);
+ } else {
+ break;
+ }
+ }
+ SDL_SaveBMP(surf_screen, screenshot_filename);
+ }
+ break;
+ case SDLK_SPACE:
+ if(state != GAMEPLAY && state != DEAD_PAUSE) {
+ // don't conflict with space key to start a new game
+ break;
+ }
+ // else fall through
+ case SDLK_p:
+ case SDLK_PAUSE:
+ paused = !paused;
+ if(paused) {
+ pause_tune();
+ } else {
+ resume_tune();
+ ms_end = SDL_GetTicks();
+ }
+ break;
+ case SDLK_f:
+ case SDLK_F11:
+ toggle_fullscreen();
+ break;
+ default:
+ // other keys are handled by checking keystate each frame
+ break;
+ }
}
break;
}
}
keystate = SDL_GetKeyState(NULL);
+ if(num_joysticks) {
+ SDL_JoystickUpdate();
+ if(opt_joystick_enabled) {
+ x_move = SDL_JoystickGetAxis(joysticks[opt_joystick_number], opt_joystick_x_axis);
+ y_move = SDL_JoystickGetAxis(joysticks[opt_joystick_number], opt_joystick_y_axis);
+ button_pressed = 0;
+ for(i = 1; i <= SDL_JoystickNumButtons(joysticks[opt_joystick_number]); i++)
+ {
+ if(SDL_JoystickGetButton(joysticks[opt_joystick_number], i) == 1)
+ {
+ button_pressed = 1;
+ break;
+ }
+ }
+ } else { // there is at least one joystick, but it hasn't been enabled yet
+ // if any joystick has a button down, enable that joystick
+ for(j = 0; j <= num_joysticks; j++) {
+ for(i = 1; i <= SDL_JoystickNumButtons(joysticks[j]); i++)
+ {
+ if(SDL_JoystickGetButton(joysticks[j], i) == 1)
+ {
+ opt_joystick_enabled = 1;
+ opt_joystick_number = j;
+printf("enabled joystick #%i\n", opt_joystick_number);
+ if(state != GAMEPLAY) {
+ // first (enabling) press of the joystick
+ // button should start a game, but should
+ // not pause a running game
+ button_pressed = 1;
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ 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(opt_joystick_enabled) {
+ if(x_move < -3000) { ship.dx += x_move*THRUSTER_STRENGTH*t_frame/22768; ship.jets |= 1<<0;}
+ if(y_move > 3000) { ship.dy += y_move*THRUSTER_STRENGTH*t_frame/22768; ship.jets |= 1<<1;}
+ if(x_move > 3000) { ship.dx += x_move*THRUSTER_STRENGTH*t_frame/22768; ship.jets |= 1<<2;}
+ if(y_move < -3000) { ship.dy += y_move*THRUSTER_STRENGTH*t_frame/22768; 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);
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_dots(t_frame);
- move_dust(t_frame);
+ move_sprites();
+ move_dots();
+ move_dust();
- new_rocks(t_frame);
- new_engine_dots(t_frame);
-
- 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
- if((keystate[SDLK_SPACE] || keystate[SDLK_1] || keystate[SDLK_2])
+ if((keystate[SDLK_SPACE] || keystate[SDLK_1] || keystate[SDLK_2] || button_pressed)
&& (state == HIGH_SCORE_DISPLAY
|| state == TITLE_PAGE
|| state == GAME_OVER)) {
if(state == GAME_OVER && new_high_score(score))
init_score_entry();
else {
- if((keystate[SDLK_SPACE] && !initial_rocks) || keystate[SDLK_2]) {
+ if((keystate[SDLK_SPACE] && !initial_rocks) || keystate[SDLK_2] || button_pressed) {
g_easy = 0;
initial_rocks = NORMAL_I_ROCKS;
final_rocks = NORMAL_F_ROCKS;
state = HIGH_SCORE_DISPLAY;
state_timeout = 400;
}
+
+ tiny_sleep();
}
}
frames = 0;
gameloop();
end = SDL_GetTicks();
- // printf("%ld frames in %ld ms, %.2f fps.\n", frames, end-start, frames * 1000.0 / (end-start));
+ printf("%ld frames in %ld ms, %.2f fps.\n", frames, end-start, frames * 1000.0 / (end-start));
return 0;
}