#include "game.h" #include "data.h" #include "bullet.h" #include "expl.h" #include "star.h" #include "aster.h" #include "dirty.h" #include "display.h" #define BULLET_DELAY 20 /* the current score */ int score; static char score_buf[80]; int player_x_pos; int player_hit; int ship_state; int skip_count; /* info about the state of the game */ static int dead; static int xspeed, yspeed, ycounter; static int ship_burn; static int ship_count; static int skip_speed; static volatile int frame_count, fps; static volatile unsigned int game_time; static unsigned int prev_bullet_time; static int new_asteroid_time; static int score_pos; #define MAX_SPEED 32 /* handles fade effects and timing for the ready, steady, go messages */ static int fade_intro_item(int music_pos, int fade_speed) { while (midi_pos < music_pos) { if (keypressed()) return TRUE; poll_joystick(); if ((joy[0].button[0].b) || (joy[0].button[1].b)) return TRUE; } set_palette(data[GAME_PAL].dat); fade_out(fade_speed); return FALSE; } /* draws one of the ready, steady, go messages */ static void draw_intro_item(int item, int size) { BITMAP *b = (BITMAP *) data[item].dat; int w = MIN(SCREEN_W, (SCREEN_W * 2 / size)); int h = SCREEN_H / size; clear_bitmap(screen); stretch_blit(b, screen, 0, 0, b->w, b->h, (SCREEN_W - w) / 2, (SCREEN_H - h) / 2, w, h); } /* timer callback for controlling the game speed */ static void game_timer(void) { game_time++; } END_OF_STATIC_FUNCTION(game_timer); /* timer callback for measuring the frames per second */ static void fps_proc(void) { fps = frame_count; frame_count = 0; } END_OF_STATIC_FUNCTION(fps_proc); /* the main game update function */ static void move_everyone(void) { BULLET *bullet; score++; if (player_hit) { /* player dead */ if (skip_count <= 0) { if (ship_state >= EXPLODE_FLAG + EXPLODE_FRAMES - 1) { if (--ship_count <= 0) dead = TRUE; else player_hit = FALSE; } else ship_state++; if (yspeed) yspeed--; } } else { if (skip_count <= 0) { if ((key[KEY_LEFT]) || (joy[0].stick[0].axis[0].d1)) { /* moving left */ if (xspeed > -MAX_SPEED) xspeed -= 2; } else if ((key[KEY_RIGHT]) || (joy[0].stick[0].axis[0].d2)) { /* moving right */ if (xspeed < MAX_SPEED) xspeed += 2; } else { /* decelerate */ if (xspeed > 0) xspeed -= 2; else if (xspeed < 0) xspeed += 2; } } /* which ship sprite to use? */ if (xspeed < -24) ship_state = SHIP1; else if (xspeed < -2) ship_state = SHIP2; else if (xspeed <= 2) ship_state = SHIP3; else if (xspeed <= 24) ship_state = SHIP4; else ship_state = SHIP5; /* move player */ player_x_pos += xspeed; if (player_x_pos < (32 << SPEED_SHIFT)) { player_x_pos = (32 << SPEED_SHIFT); xspeed = 0; } else if (player_x_pos >= ((SCREEN_W - 32) << SPEED_SHIFT)) { player_x_pos = ((SCREEN_W - 32) << SPEED_SHIFT); xspeed = 0; } if (skip_count <= 0) { if ((key[KEY_UP]) || (joy[0].stick[0].axis[1].d1)) { /* firing thrusters */ if (yspeed < MAX_SPEED) { if (yspeed == 0) { play_sample(data[ENGINE_SPL].dat, 0, PAN(player_x_pos >> SPEED_SHIFT), 1000, TRUE); } else { /* fade in sample while speeding up */ adjust_sample(data[ENGINE_SPL].dat, yspeed * 64 / MAX_SPEED, PAN(player_x_pos >> SPEED_SHIFT), 1000, TRUE); } yspeed++; } else { /* adjust pan while the sample is looping */ adjust_sample(data[ENGINE_SPL].dat, 64, PAN(player_x_pos >> SPEED_SHIFT), 1000, TRUE); } ship_burn = TRUE; score++; } else { /* not firing thrusters */ if (yspeed) { yspeed--; if (yspeed == 0) { stop_sample(data[ENGINE_SPL].dat); } else { /* fade out and reduce frequency when slowing down */ adjust_sample(data[ENGINE_SPL].dat, yspeed * 64 / MAX_SPEED, PAN(player_x_pos >> SPEED_SHIFT), 500 + yspeed * 500 / MAX_SPEED, TRUE); } } ship_burn = FALSE; } } } /* if going fast, move everyone else down to compensate */ if (yspeed) { ycounter += yspeed; while (ycounter >= (1 << SPEED_SHIFT)) { for (bullet = bullet_list; bullet; bullet = bullet->next) bullet->y++; scroll_stars(); scroll_asteroids(); ycounter -= (1 << SPEED_SHIFT); } } move_bullets(); starfield_2d(); /* fire bullet? */ if (!player_hit) { if ((key[KEY_SPACE] || key[KEY_LCONTROL] || key[KEY_RCONTROL]) || (joy[0].button[0].b) || (joy[0].button[1].b)) { if (prev_bullet_time + BULLET_DELAY < game_time) { bullet = add_bullet((player_x_pos >> SPEED_SHIFT) - 2, SCREEN_H - 64); if (bullet) { play_sample(data[SHOOT_SPL].dat, 100, PAN(bullet->x), 1000, FALSE); prev_bullet_time = game_time; } } } } move_asteroids(); if (skip_count <= 0) { skip_count = skip_speed; /* make a new alien? */ new_asteroid_time++; if (new_asteroid_time > 600) { add_asteroid(); new_asteroid_time = 0; } } else skip_count--; } /* main screen update function */ static void draw_screen(BITMAP *bmp) { int x; RLE_SPRITE *spr; char *animation_type_str; if (animation_type == DOUBLE_BUFFER) { animation_type_str = "double buffered"; } else if (animation_type == PAGE_FLIP) { animation_type_str = "page flipping"; } else if (animation_type == TRIPLE_BUFFER) { animation_type_str = "triple buffered"; } else { animation_type_str = "dirty rectangles"; } acquire_bitmap(bmp); draw_starfield_2d(bmp); /* draw the player */ x = player_x_pos >> SPEED_SHIFT; if ((ship_burn) && (ship_state < EXPLODE_FLAG)) { spr = (RLE_SPRITE *) data[ENGINE1 + (retrace_count / 4) % 7].dat; draw_rle_sprite(bmp, spr, x - spr->w / 2, SCREEN_H - 24); if (animation_type == DIRTY_RECTANGLE) dirty_rectangle(x - spr->w / 2, SCREEN_H - 24, spr->w, spr->h); } if (ship_state >= EXPLODE_FLAG) spr = explosion[ship_state - EXPLODE_FLAG]; else spr = (RLE_SPRITE *) data[ship_state].dat; draw_rle_sprite(bmp, spr, x - spr->w / 2, SCREEN_H - 42 - spr->h / 2); if (animation_type == DIRTY_RECTANGLE) dirty_rectangle(x - spr->w / 2, SCREEN_H - 42 - spr->h / 2, spr->w, spr->h); /* draw the asteroids */ draw_asteroids(bmp); /* draw the bullets */ draw_bullets(bmp); /* draw the score and fps information */ if (fps) sprintf(score_buf, "Lives: %d - Score: %d - (%s, %d fps)", ship_count, score, animation_type_str, fps); else sprintf(score_buf, "Lives: %d - Score: %d", ship_count, score); textout_ex(bmp, font, score_buf, 0, 0, 7, 0); if (animation_type == DIRTY_RECTANGLE) dirty_rectangle(0, 0, text_length(font, score_buf), 8); release_bitmap(bmp); } static void move_score(void) { score_pos++; if (score_pos > SCREEN_H) score_pos = -160; } static void draw_score(BITMAP *bmp) { static BITMAP *b = NULL, *b2 = NULL; if (!b) b = create_bitmap(160, 160); if (!b2) b2 = create_bitmap(160, 160); blit(bmp, b2, score_pos, score_pos, 0, 0, 160, 160); clear_bitmap(b); textout_centre_ex(b, data[END_FONT].dat, "GAME OVER", 80, 50, 2, 0); sprintf(score_buf, "Score: %d", score); textout_centre_ex(b, data[END_FONT].dat, score_buf, 80, 82, 2, 0); rotate_sprite(b2, b, 0, 0, itofix(score_pos) / 4); blit(b2, bmp, 0, 0, score_pos, score_pos, 160, 160); if (animation_type == DIRTY_RECTANGLE) dirty_rectangle(score_pos, score_pos, 160, 160); } /* sets up and plays the game */ void play_game(void) { int esc = FALSE; unsigned int prev_update_time; stop_midi(); /* set up a load of globals */ dead = player_hit = FALSE; player_x_pos = (SCREEN_W / 2) << SPEED_SHIFT; xspeed = yspeed = ycounter = 0; ship_state = SHIP3; ship_burn = FALSE; ship_count = 3; /* 3 lives instead of one... */ frame_count = fps = 0; bullet_list = NULL; score = 0; score_pos = -160; skip_count = 0; if (SCREEN_W < 400) skip_speed = 0; else if (SCREEN_W < 700) skip_speed = 1; else if (SCREEN_W < 1000) skip_speed = 2; else skip_speed = 3; init_starfield_2d(); init_asteroids(); new_asteroid_time = 0; /* introduction synced to the music */ draw_intro_item(INTRO_BMP_1, 5); play_midi(data[GAME_MUSIC].dat, TRUE); clear_keybuf(); if (fade_intro_item(-1, 2)) goto skip; draw_intro_item(INTRO_BMP_2, 4); if (fade_intro_item(5, 2)) goto skip; draw_intro_item(INTRO_BMP_3, 3); if (fade_intro_item(9, 4)) goto skip; draw_intro_item(INTRO_BMP_4, 2); if (fade_intro_item(11, 4)) goto skip; draw_intro_item(GO_BMP, 1); if (fade_intro_item(13, 16)) goto skip; if (fade_intro_item(14, 16)) goto skip; if (fade_intro_item(15, 16)) goto skip; if (fade_intro_item(16, 16)) goto skip; skip: clear_display(); if ((!keypressed()) && (!joy[0].button[0].b) && (!joy[0].button[1].b)) { do { } while (midi_pos < 17); } clear_keybuf(); set_palette(data[GAME_PAL].dat); position_mouse(SCREEN_W / 2, SCREEN_H / 2); /* set up the interrupt routines... */ LOCK_VARIABLE(score); LOCK_VARIABLE(frame_count); LOCK_VARIABLE(fps); LOCK_FUNCTION(fps_proc); install_int(fps_proc, 1000); LOCK_VARIABLE(game_time); LOCK_FUNCTION(game_timer); install_int(game_timer, 6400 / SCREEN_W); game_time = 0; prev_bullet_time = 0; prev_update_time = 0; /* main game loop */ set_volume(-1, 90); while (!esc) { int updated = 0; poll_keyboard(); poll_joystick(); while (prev_update_time < game_time) { if (dead) { move_score(); } else { move_everyone(); if (dead) { set_volume(-1, 255); clear_keybuf(); } } prev_update_time++; updated = 1; } if (max_fps || updated) { BITMAP *bmp = prepare_display(); draw_screen(bmp); if (dead) { draw_score(bmp); } flip_display(); frame_count++; } /* rest for a short while if we're not in CPU-hog mode and too fast */ if (!max_fps && !updated) { rest(1); } if (key[KEY_ESC] || (dead && ( keypressed() || joy[0].button[0].b || joy[0].button[1].b))) esc = TRUE; } set_volume(-1, 255); /* cleanup */ remove_int(fps_proc); remove_int(game_timer); stop_sample(data[ENGINE_SPL].dat); while (bullet_list) delete_bullet(bullet_list); if (esc) { do { poll_keyboard(); } while (key[KEY_ESC]); fade_out(5); return; } clear_keybuf(); fade_out(5); }