/* ** ** XOIDS - main module ** ** v1.5 by Tim Ebling ** ** tebling@oce.orst.edu ** ** OIDS.C ** ** September, 1996 */ #include #include #include #include #include #include #include "oids.h" #include "bitmaps.h" main() { int i; Sprite *the_obj; /* Build the trig tables */ for (i=0;inext); /* PLAY LOOP */ while(game_over < 100 || !leave) { /* Draw stuff */ /* handle player(s) first */ the_obj = P; do { X_draw_object(the_obj); if (the_obj->thrust) X_draw_thrust(the_obj); switch(the_obj->state) { case WARPING: if (the_obj->state_ctr == HYPER_DELAY) warp_object(the_obj); break; case DEAD: if (!game_over) revive_player(the_obj); break; } } while (the_obj = the_obj->next); /* Handle end of one player game */ if (num_players == 1 && P->lives == 0) { game_over = 1; P->lives = -1; } /* Check for bonus life in the one player game */ if (num_players == 1 && (P->score - extra_man_scr) >= 10000) { if (++P->lives > 20) P->lives = 20; extra_man_scr += 10000; update_score(); make_burst(P, -1); } /* Handle end of two player game */ if (num_players > 1 && !game_over && (P->score >= goal_array[goal_score] || P->next->score >= goal_array[goal_score])) { game_over = 1; } /* Drawing to offscreen pixmap - could clean all this */ /* stuff up with some a list of all the game objects */ /* (C++ would come in handy here) */ X_draw_object(Big_O); X_draw_shots(P); X_draw_object(Slrb); X_draw_shots(Slrb); X_draw_object(Homer); X_draw_object(Resur); if (!Resur->state && Resur->rotation) { Flame->curr_map = ((int) random_num(0.0, 6.0)) % 2; Flame->x = Resur->x; Flame->y = Resur->y; X_draw_object(Flame); } X_draw_object(Power_Up); X_draw_object(Meta); X_update_status_bar(); /* MUST follow all drawing */ /* Copy from offscreen pixmap to window */ X_copy_object_to_window(P); X_copy_object_to_window(Slrb); X_copy_object_to_window(Homer); X_copy_object_to_window(Resur); X_copy_object_to_window(Power_Up); X_copy_object_to_window(Meta); X_copy_status_bar_to_window(); X_copy_object_to_window(Big_O); /* Leave this to end */ X_update(1); /* Drawing to the window */ /* Update bursts */ X_draw_bursts(0); update_bursts(); X_draw_bursts(1); X_draw_stars(WHITE, 0); if (coop) X_draw_link(P, GREEN); /* Get stuff from keyboard, mouse, etc... */ if (!game_over) check_input(); /* Update sprite positions */ update_object(P); if (coop) link_players(); update_object(Slrb); update_object(Homer); update_object(Resur); update_object(Big_O); update_object(Power_Up); update_object(Meta); /* Clear old positions */ X_clear_object(P); X_clear_object(Slrb); X_clear_object(Homer); X_clear_object(Resur); X_clear_object(Power_Up); X_clear_object(Meta); X_clear_object(Big_O); /* Update shot positions */ if (the_obj = update_shots(P)) { if (!(coop && (the_obj == P || the_obj == P->next))) (*the_obj->kill_func)(the_obj); } if (the_obj = update_shots(Slrb)) { (*the_obj->kill_func)(the_obj); } X_clear_shots(P); X_clear_shots(Slrb); if (shot_clock) shot_clock--; /* Check for collisions (ooh, aahh) */ /* Player collisions */ check_collision(P, Big_O, 1, 1); if (num_players > 1) simple_collision(P, P->next, 1, 1); /* Alien collisions */ if (Slrb->state <= DYING) { check_collision(Slrb, Big_O, 1, 1); simple_collision(P, Slrb, 1, 1); if (num_players > 1) simple_collision(P->next, Slrb, 1, 1); if (Slrb->wpn != GUN && Slrb->shots < Weapon[Slrb->wpn].max_shots && !shot_clock) create_new_shot(Slrb); } if (!Homer->state) { alien_lock_on(Homer); draw_homer_trail(0); check_collision(Homer, Big_O, 1, 1); simple_collision(P, Homer, 1, 1); if (!Slrb->state) simple_collision(Homer, Slrb, 1, 1); if (num_players > 1) simple_collision(P->next, Homer, 1, 1); } if (!Resur->state) { check_collision(Resur, Big_O, 1, 0); simple_collision(P, Resur, 1, 1); if (!Slrb->state) simple_collision(Resur, Slrb, 1, 1); if (!Homer->state) simple_collision(Resur, Homer, 1, 1); if (num_players > 1) simple_collision(P->next, Resur, 1, 1); } if (!Power_Up->state) { the_obj = NULL; check_collision(Power_Up, Big_O, 1, 1); if (simple_collision(P, Power_Up, 0, 0)) the_obj = P; if (simple_collision(Slrb, Power_Up, 0, 0)) the_obj = Slrb; if (num_players > 1 && simple_collision(P->next, Power_Up, 0, 0)) the_obj = P->next; if (the_obj) kill_power_up(Power_Up, the_obj); } if (!Meta->state) { the_obj = NULL; check_collision(Meta, Big_O, 1, 1); if (simple_collision(P, Meta, 0, 0)) the_obj = P; if (simple_collision(Slrb, Meta, 0, 0)) the_obj = Slrb; if (num_players > 1 && simple_collision(P->next, Meta, 0, 0)) the_obj = P->next; if (the_obj) kill_power_up(Meta, the_obj); } /* Update timer (used by oid rotation) */ if (timer) timer--; if (!timer) { test_for_aliens(); timer = TIMER_VAL; } /* Delay loop if running too fast */ if (delay) do_nothing_for_a_while(); /* Finished the level, so warp outta here */ if (warp_levels) { if (warp_levels == MAPS_PER_360) X_draw_stars(WHITE, 1); warp_levels--; draw_level_warp(); if (!warp_levels) { num_bursts = 0; X_flash_screen(2); } } /* If game is over, wait for the end */ if (game_over) { game_over++; if (!leave) { leave = X_checkkey(); draw_game_over(); } if (leave == (char) 'q') { X_exit(); exit(0); } } } /* Game has ended, so reset the state of everything */ X_backbuf(); X_clear(); X_frontbuf(); X_clear(); init_all_objects(1); level = 1; game_over = leave = 0; oids_shot = 0; extra_man_scr = 0; P->wpn = GUN; if (P->next) P->next->wpn = GUN; keyboard_state = 0; } } /* end OIDS.C */ /* ** CHECK_INPUT ** ** Handle keyboard state. */ void check_input() { Sprite *player; float diff_ang; X_check_keypress(); /* PLAYER ONE CONTROLS */ player = P; if (!player->state) { /* THRUST */ if (keyboard_state & KEY_THRUST1) { if (coop) { if (!player->next->state) { /* Here's where the neat link dynamics are */ diff_ang = link.ang - player->curr_map * DELTA_ANG; link.ang_vec += 0.01 * sin(diff_ang); link.length -= (int) (5.0 * cos(diff_ang)); link.x_vec -= fabs(cos(diff_ang)) * sin_table[player->curr_map]; link.y_vec -= fabs(cos(diff_ang)) * cos_table[player->curr_map]; } else { player->y_vec += -cos_table[player->curr_map]; player->x_vec += -sin_table[player->curr_map]; } } else { player->y_vec += -cos_table[player->curr_map]; player->x_vec += -sin_table[player->curr_map]; } player->thrust = 1; player->engine += 4; } else { player->thrust = 0; } if (keyboard_state & KEY_LEFT1) { /* ROTATE LEFT */ player->point_angle += player->delta_angle; player->curr_map = (player->curr_map + 1) % player->num_pixmaps; } if (keyboard_state & KEY_RIGHT1) { /* ROTATE RIGHT */ player->point_angle += player->delta_angle; player->curr_map = (player->curr_map - 1) % player->num_pixmaps; } /* FIRE */ if (keyboard_state & KEY_FIRE1 && player->shots < Weapon[player->wpn].max_shots && !shot_clock) { create_new_shot(player); shot_clock = Weapon[player->wpn].inhibit_time; } if (keyboard_state & KEY_HYPER1 && !coop) { /* HYPERSPACE */ player->state = NORMAL; X_clear_one_object(player); X_copy_object_to_window(player); player->state = WARPING; keyboard_state = keyboard_state ^ KEY_HYPER1; player->engine += 300; } } /* PLAYER TWO CONTROLS */ if (player = P->next) { if (!player->state) { if (keyboard_state & KEY_THRUST2) { /* Here's where the neat link dynamics are */ if (coop && !P->state) { diff_ang = link.ang - player->curr_map * DELTA_ANG; link.ang_vec -= 0.01 * sin(diff_ang); link.length += (int) (5.0 * cos(diff_ang)); link.x_vec -= fabs(cos(diff_ang)) * sin_table[player->curr_map]; link.y_vec -= fabs(cos(diff_ang)) * cos_table[player->curr_map]; } else { player->y_vec += -cos_table[player->curr_map]; player->x_vec += -sin_table[player->curr_map]; } player->thrust = 1; player->engine += 4; } else { player->thrust = 0; } if (keyboard_state & KEY_LEFT2) { /* ROTATE LEFT */ player->point_angle += player->delta_angle; player->curr_map = (player->curr_map + 1) % player->num_pixmaps; } if (keyboard_state & KEY_RIGHT2) { /* ROTATE RIGHT */ player->point_angle += player->delta_angle; player->curr_map = (player->curr_map - 1) % player->num_pixmaps; } /* FIRE */ if (keyboard_state & KEY_FIRE2 && player->shots < Weapon[player->wpn].max_shots && !shot_clock) { create_new_shot(player); shot_clock = Weapon[player->wpn].inhibit_time; } if (keyboard_state & KEY_HYPER2 && !coop) { /* HYPERSPACE */ player->state = NORMAL; X_clear_object(player); X_copy_object_to_window(player); player->state = WARPING; keyboard_state = keyboard_state ^ KEY_HYPER2; player->engine += 300; } } } if (keyboard_state & KEY_QUIT) { /* QUIT */ X_exit(); exit(0); } if (keyboard_state & KEY_PAUSE) { /* PAUSE */ pause_game(); keyboard_state = 0; } if (keyboard_state & KEY_ESC) { /* ESCAPE */ leave = 1; game_over = 100; } } /* end CHECK_INPUT */ /* ** UPDATE_OBJECT ** ** Called each game loop - should make this routine FAST ** */ void update_object(obj) Sprite *obj; { do { switch (obj->state) { case WARPING: obj->state_ctr++; break; case REVIVING: if (obj->death_sprite) { update_object(obj->death_sprite); if (fabs(obj->death_sprite->x - obj->x) < 2) { X_clear_object(obj->death_sprite); X_copy_object_to_window(obj->death_sprite); obj->state = NORMAL; obj->state_ctr = 0; } } break; case EXPLODING: if (++obj->state_ctr > obj->max_exploding) { X_clear_one_object(obj); obj->state = DEAD; /* Toasted */ obj->wpn = GUN; } if (obj->death_sprite) { update_object(obj->death_sprite); } break; case DEAD: break; case DYING: if (++obj->state_ctr > obj->max_dying) { (*obj->kill_func)(obj); } /* NOTE - fallthrough to default from DYING */ default: obj->ox = obj->x; obj->oy = obj->y; if (fabs(obj->x_vec) > obj->max_speed) obj->x_vec = fabs(obj->x_vec)/obj->x_vec*obj->max_speed; if (fabs(obj->y_vec) > obj->max_speed) obj->y_vec = fabs(obj->y_vec)/obj->y_vec*obj->max_speed; obj->x += obj->x_vec; obj->y += obj->y_vec; put_inside_window(obj); if (obj->rotation && (timer % obj->rotation) == 0) { obj->curr_map = (obj->curr_map + obj->rot_dir) % obj->num_pixmaps; } break; } if (obj->bounced) obj->bounced--; if (obj->engine) { if (obj->engine > 999 && obj->state != EXPLODING) { (*obj->kill_func)(obj); } else { obj->engine--; } } if (obj->state_ctr > 1000) obj->state_ctr = 1; } while (obj = obj->next); } /* end UPDATE_OBJECT */ /* ** LINK_PLAYERS ** ** Deal with the nasty physics of two players linked together ** via that elastic space-cable. Actually, most of that is found ** in check_input(). */ void link_players() { int stretch; stretch = (int) (LINK_LENGTH - link.length) / 15; /* Differential stretching */ if (link.length != LINK_LENGTH) link.length += stretch; if (!(P->state || P->next->state)) { /* Minimum length of link */ if (link.length < P->height) link.length = P->height; /* Maximum angular velocity of link */ if (fabs(link.ang_vec) > 0.0025 * link.length) link.ang_vec = 0.0025 * link.length * fabs(link.ang_vec) / link.ang_vec; link.ang += link.ang_vec; /* Maximum translational velocity of link */ if (fabs(link.x_vec) > P->max_speed) link.x_vec = fabs(link.x_vec)/link.x_vec*P->max_speed; if (fabs(link.y_vec) > P->max_speed) link.y_vec = fabs(link.y_vec)/link.y_vec*P->max_speed; link.y += link.y_vec; link.x += link.x_vec; /* Handle window borders */ if (link.x > vdevice.sizeSx) link.x = 0; if (link.x < 0) link.x = vdevice.sizeSx; if (link.y > vdevice.sizeSy) link.y = P->height + 3; if (link.y < P->height + 3) link.y = vdevice.sizeSy; /* Put the players at the ends of the link */ P->x = (int) link.length * sin(link.ang) + link.x; P->y = (int) link.length * cos(link.ang) + link.y; P->x_vec = P->x - P->ox; P->y_vec = P->y - P->oy; P->next->x = link.x - (int) link.length * sin(link.ang); P->next->y = link.y - (int) link.length * cos(link.ang); P->next->x_vec = P->next->x - P->next->ox; P->next->y_vec = P->next->y - P->next->oy; } if (!(P->state && P->next->state)) link.ang += link.ang_vec; } /* end LINK_PLAYERS */ /* ** TEST_FOR_ALIENS ** ** Check to see if an alien has appeared. Also handle some (most) ** of the alien logic. */ void test_for_aliens() { float z, ang; Sprite *lock_on; int s, i, j, fl, max_oids, map; static int resurr_state, res_map = 2, wait = 115; z = random_num(0.0, 20.0); max_oids = INIT_BIG_OIDS + floor(((float)level - 1.0) / 2.0); /* Slurb */ if (z < 1.0 && Slrb->state == DEAD) { revive_player(Slrb); } else if (Slrb->state == NORMAL && (z > 11.0 || Slrb->x_vec == 0.0)) { alien_lock_on(Slrb); } if (!Slrb->state && z < 17.0) { create_new_shot(Slrb); } /* Homer */ if (z > 10.0 && z < (9.0 + ((level < 10) ? level / 2.0 : 5.0)) && Homer->state == DEAD) { /* Want Homer to appear from the edge of the window */ s = (int) random_num(0.0, 4.0); switch (s) { case 0: Homer->x = 0; Homer->y = (int) random_num(P->height, (float) vdevice.sizeSy); break; case 1: Homer->x = vdevice.sizeSx - 1; Homer->y = (int) random_num(P->height, (float) vdevice.sizeSy); break; case 2: Homer->x = (int) random_num(0.0, (float) vdevice.sizeSx); Homer->y = 0; break; case 3: Homer->x = (int) random_num(0.0, (float) vdevice.sizeSx); Homer->y = vdevice.sizeSy - 1; break; default: Homer->x = (int) random_num(0.0, (float) vdevice.sizeSx); Homer->y = (int) random_num(0.0, (float) vdevice.sizeSy); break; } draw_homer_trail(1); Homer->state = NORMAL; } /* Resurrector */ if ((z = random_num(0.0, 20.0)) > 23.0 - level / 2.0 && oids_shot > 15 && Resur->state == DEAD) { revive_player(Resur); Resur->x_vec = random_num(-4.0, 4.0); Resur->y_vec = random_num(-4.0, 4.0); ang = atan2(Resur->x_vec, Resur->y_vec); map = (int) ((ang + PI) / (2*PI) * MAPS_PER_360); Resur->curr_map = map; resurr_state = 1; } /* Sit and spin */ if ((random_num(0.0, 20.0) < 10.0 && resurr_state == 1) || resurr_state == 3) { if (res_map == 2) { Resur->x_vec = 0.0; Resur->y_vec = 0.0; } if (!--res_map) { res_map = 2; resurr_state++; Resur->rotation = 0; } else { Resur->rotation = 1; } } /* Resurrector does his stuff - revive a dead oid! */ if (resurr_state == 5) resurr_state = 4; if (resurr_state == 2) { fl = 0; for (i=0;istate != DEAD) { lock_on = lock_on->next_draw; } if (lock_on && !fl && !Resur->state) { lock_on->x_vec = - (float) SO_MAX_SPEED*sin_table[Resur->curr_map]; lock_on->y_vec = - (float) SO_MAX_SPEED*cos_table[Resur->curr_map]; lock_on->state = NORMAL; lock_on->x = Resur->x + 3.0*lock_on->x_vec; lock_on->y = Resur->y + 3.0*lock_on->y_vec; oids_shot--; Resur->x_vec = 0.0; Resur->y_vec = 0.0; Resur->rotation = 0; fl = 1; }; } if (fl) { resurr_state = 5; } else { resurr_state = 4; } } if (resurr_state == 4) { Resur->x_vec = - (float) P_MAX_SPEED * sin_table[Resur->curr_map]; Resur->y_vec = - (float) P_MAX_SPEED * cos_table[Resur->curr_map]; resurr_state = 1; } } /* end TEST_FOR_ALIENS */ /* ** DRAW_HOMER_TRAIL ** ** It's a well known fact that Homers leave trails of red ** space-slime behind them. Duh. */ void draw_homer_trail(reset) int reset; { int i; static int homer_trail[20][2], c = 1; if (reset == 2) { X_color(BLACK); for (i=0;i<20;i++) X_point(homer_trail[i][0], homer_trail[i][1]); } else if (!(--c)) { c = 1; if (reset == 1) for (i=0;i<20;i++) { homer_trail[i][0] = (int) Homer->x; homer_trail[i][1] = (int) Homer->y; } X_color(BLACK); X_point(homer_trail[0][0], homer_trail[0][1]); X_color(RED); for (i=0;i<19;i++) { homer_trail[i][0] = homer_trail[i+1][0]; homer_trail[i][1] = homer_trail[i+1][1]; if (i) X_point(homer_trail[i][0], homer_trail[i][1]); } homer_trail[19][0] = (int) Homer->x; homer_trail[19][1] = (int) Homer->y; X_color(BLACK); } } /* ** ALIEN_LOCK_ON ** ** Give the alien a purpose in life. */ void alien_lock_on(alien) Sprite *alien; { Sprite *lock_on = NULL; int map; float ang, s; if (P->pixmaps == P->orig_pixmaps) lock_on = P; if (num_players > 1) { if (distance(alien, P->next) < distance(alien, P) && P->next->pixmaps == P->next->orig_pixmaps) lock_on = P->next; } if (!Slrb->state && alien == Homer) lock_on = Slrb; if (alien == Slrb && !Power_Up->state) lock_on = Power_Up; if (!lock_on) { alien->x_vec = (int) (random_num(-4.0, 4.0)); alien->y_vec = (int) (random_num(-4.0, 4.0)); } else if (!lock_on->state) { alien->x_vec = (lock_on->x - alien->x); alien->y_vec = (lock_on->y - alien->y); s = speed(alien); alien->x_vec = alien->max_speed * alien->x_vec / s; alien->y_vec = alien->max_speed * alien->y_vec / s; ang = atan2(alien->x_vec, alien->y_vec); map = (int) ((ang + PI) / (2*PI) * MAPS_PER_360); alien->curr_map = map; } } /* end ALIEN_LOCK_ON */ /* ** SIMPLE_COLLISION ** ** Collision between two objects only */ int simple_collision(obj1, obj2, bnce, kill) Sprite *obj1, *obj2; int bnce, kill; { float dx, dy, dist; int k = 0; if (obj1->state || obj2->state) return(0); dx = obj1->x - obj2->x; dy = obj1->y - obj2->y; dist = sqrt(dx * dx + dy * dy); if (dist < obj1->ho2 + obj2->ho2) { if (bnce) { if (speed(obj1) > speed(obj2)) { k = bounce_objects(obj1, obj2); } else { k = bounce_objects(obj2, obj1); } } if (k && kill) { obj1->state = DYING; obj1->rotation = 1; obj2->state = DYING; obj2->rotation = 1; } return(1); } return(0); } /* end SIMPLE_COLLISION */ /* ** CHECK_COLLISION ** ** If there is a collision, this routine returns a pointer to the ** member of obj2 which was hit. If bounce is set, then obj1 will ** appear to "bounce" off of obj2. ** ** This routine deals with an object AND all its children. */ Sprite *check_collision(obj1, ob2, bnce, kill) Sprite *obj1, *ob2; int bnce, kill; { float dx, dy, dist; int hit; int ds = 0; Sprite *obj2; Sprite *collide = NULL; /* Basic collision check - radius test, assumes objects have */ /* aspect ratio of ONE. */ do { ds = 0; if (obj1->state == EXPLODING && obj1->death_sprite) { check_collision(obj1->death_sprite, ob2, bnce, 0); ds = 1; } obj2 = ob2; do { if (obj2->state == EXPLODING && obj2->death_sprite) { check_collision(obj1, obj2->death_sprite, bnce, kill); } if (obj1->state <= DYING && obj2->state <= DYING && !obj1->bounced) { dx = obj1->x - obj2->x; dy = obj1->y - obj2->y; dist = sqrt(dx * dx + dy * dy); if (dist < obj1->ho2 + obj2->ho2) { if (bnce) hit = bounce_objects(obj1, obj2); if (hit && !ds) { if (kill) { if (coop) X_draw_link(P, BLACK); obj1->state = DYING; obj1->rotation = 1; (*obj2->kill_func)(obj2); } collide = obj2; } } } } while (obj2 = obj2->next_draw); } while (obj1 = obj1->next_draw); return(collide); } /* end CHECK_COLLISION */ /* ** BOUNCE_OBJECTS ** ** Bounces obj1 off of obj2, assuming obj2 has a circular shape (pretty ** good for most oids!) ** ** I thought this routine would be pretty easy, but the physics are ** not as trivial as I thought. There's certainly room for improvement ** as well. */ int bounce_objects(obj1, obj2) Sprite *obj1, *obj2; { float ang1, ang2, alpha, old_xv, old_yv; Sprite *fast, *slow; float x2t, y2t, rel_v, red_m; int flag = 0; red_m = (obj1->mass + obj2->mass); if (speed(obj1) > speed(obj2)) { fast = obj1; slow = obj2; } else { fast = obj2; slow = obj1; } old_xv = fast->x_vec; old_yv = fast->y_vec; fast->x_vec += fabs(slow->x_vec) * slow->mass / red_m; fast->y_vec += fabs(slow->y_vec) * slow->mass / red_m; slow->x_vec += old_xv * fast->mass / red_m; slow->y_vec += old_yv * fast->mass / red_m; x2t = fast->x - slow->x; y2t = fast->y - slow->y; ang1 = atan2(-y2t,x2t); /* Should make an ATAN table!! */ ang2 = atan2(-fast->y_vec,fast->x_vec); alpha = PI + ang2; alpha = 2 * ang1 - alpha; ang2 += PI; if (ang1 < 0.0) ang1 += 2*PI; /* If just "bumped", then we'll let you live - this time! */ rel_v = sqrt((obj1->x_vec - obj2->x_vec)*(obj1->x_vec - obj2->x_vec) + (obj1->y_vec - obj2->y_vec)*(obj1->y_vec - obj2->y_vec)); if (rel_v < DEATH_THRESH) { fast->x_vec = -old_xv; /* Pushed away */ fast->y_vec = -old_yv; if (coop) if (!(P->state || P->next->state)) { link.ang_vec = -link.ang_vec; } } else { fast->x_vec = fabs(fast->x_vec) * cos(alpha); fast->y_vec = -fabs(fast->y_vec) * sin(alpha); flag = 1; } obj1->bounced = 5; return(flag); } /* end BOUNCE_OBJECTS */ /* ** EXPLODE_OBJECT ** ** Blow something to bits, and make sure the bits behave as they should. */ void explode_object(obj) Sprite *obj; { int ang; Sprite *carnage; X_clear_one_object(obj); X_flash_screen(2); obj->state = EXPLODING; obj->thrust = 0; if (--obj->lives < 0) obj->lives = 0; if (num_players > 1 && obj->score > 500) obj->score -= 500; update_score(); make_burst(obj, 1); if (carnage = obj->death_sprite) { do { ang = (obj->curr_map + (int) (random_num(0.0, (float) obj->num_pixmaps))) % obj->num_pixmaps; carnage->state = DYING; carnage->x = obj->x; carnage->y = obj->y; carnage->x_vec = -CARNAGE_SPEED*sin_table[ang] + obj->x_vec; carnage->y_vec = -CARNAGE_SPEED*cos_table[ang] + obj->y_vec; } while (carnage = carnage->next); } if (!coop) { obj->x = random_num(0.0, (float) (vdevice.sizeSx)); obj->y = random_num((float) P->height, (float) (vdevice.sizeSy)); obj->ox = obj->x; obj->oy = obj->y; } } /* end EXPLODE_OBJECT */ /* ** WARP_OBJECT ** ** Send someone into hyperspace. */ void warp_object(obj) Sprite *obj; { do { obj->x = random_num(0.0, (float) vdevice.sizeSx); obj->y = random_num(0.0, (float) vdevice.sizeSy); } while (check_collision(obj, Big_O, 0, 0)); obj->state = NORMAL; obj->state_ctr = 0; obj->x_vec = 0.0; obj->y_vec = 0.0; obj->rotation = 0; } /* end WARP_OBJECT */ /* ** REVIVE_PLAYER ** ** You're lucky it's just a game, or this routine wouldn't exist. ** (That is, if you don't believe in reincarnation) */ void revive_player(player) Sprite *player; { Sprite *carnage; if (coop) X_draw_link(P, BLACK); player->state = NORMAL; player->state_ctr = 0; player->point_angle = 0.0; player->x_vec = 0.0; player->y_vec = 0.0; player->rotation = 0; player->thrust = 0; player->engine = 0; player->pixmaps = player->orig_pixmaps; player->clipmasks = player->orig_clipmasks; put_inside_window(player); if (carnage = player->death_sprite) { do { carnage->state = NORMAL; carnage->x = random_num(0.0, (float) (vdevice.sizeSx)); carnage->y = random_num(0.0, (float) (vdevice.sizeSy)); carnage->x_vec = (player->x - carnage->x) / 30; carnage->y_vec = (player->y - carnage->y) / 30; } while (carnage = carnage->next); } /* Here's where things are a bit tricky. If one of the linked */ /* players has died, the other one remains free to fly around */ /* unencumbered by the other */ if (coop && (player == P || player == P->next)) { if (player == P) { player->x = P->next->x + 2*link.length*sin(link.ang); player->y = P->next->y + 2*link.length*cos(link.ang); link.x_vec = P->next->x_vec; link.y_vec = P->next->y_vec; } else { player->x = P->x - 2*link.length*sin(link.ang); player->y = P->y - 2*link.length*cos(link.ang); link.x_vec = P->x_vec; link.y_vec = P->y_vec; } player->ox = player->x; player->oy = player->y; link.x = fabs(P->x + P->next->x) / 2; link.y = fabs(P->y + P->next->y) / 2; } else { player->state = REVIVING; } } /* end REVIVE_PLAYER */ /* ** UPDATE_BURSTS ** ** Keep track of all the various supernova explosions */ void update_bursts() { int i, j, flag = 0; struct explosion *b, *b1; for (i=0;i burst[i].max_radius || burst[i].radius < 1) { if (!flag) { X_draw_bursts(0); flag = 1; } for (j=i;jx = b1->x; b->y = b1->y; b->radius = b1->radius; b->color = b1->color; } num_bursts--; } } } /* end UPDATE_BURSTS */ /* ** MAKE_BURST ** ** Create a new supernova explosion */ void make_burst(obj, dir) Sprite *obj; int dir; { burst[num_bursts].x = obj->x; burst[num_bursts].y = obj->y; burst[num_bursts].color = obj->burst_color; burst[num_bursts].max_radius = obj->height; if (dir < 0) { burst[num_bursts].radius = obj->height; } else { burst[num_bursts].radius = 1; } burst[num_bursts].x_vec = (int) obj->x_vec; burst[num_bursts].y_vec = (int) obj->y_vec; burst[num_bursts++].dir = dir; } /* end MAKE_BURST */ /* ** UPDATE_SHOTS ** ** Keep track of everybody's fire */ Sprite *update_shots(obj) Sprite *obj; { int i; struct Shot *curr; Sprite *check; Sprite *hit_object = NULL; do { if (obj->shots) { for (i=0;ishots;i++) { curr = obj->S[i]; curr->ox = curr->x; curr->oy = curr->y; curr->x += curr->x_vec; curr->y += curr->y_vec; if (--curr->clock == 1) erase_shot(obj, i); if (curr->x > vdevice.sizeSx) curr->x = 0; if (curr->x < 0) curr->x = vdevice.sizeSx; if (curr->y > vdevice.sizeSy) curr->y = P->height + 3; if (curr->y < P->height + 3) curr->y = vdevice.sizeSy; /* Collision detection of shots */ check = shot_collide(curr, Big_O); if (check) hit_object = check; if (!hit_object) { check = shot_collide(curr, P); if (check) hit_object = check; } if (!Slrb->state && !hit_object) { check = shot_collide(curr, Slrb); if (check) hit_object = check; } if (!Homer->state && !hit_object) { check = shot_collide(curr, Homer); if (check) hit_object = check; } if (!Resur->state && !hit_object) { check = shot_collide(curr, Resur); if (check) hit_object = check; } if (hit_object == obj) hit_object = NULL; if (hit_object) { if (obj->wpn != LASER) erase_shot(obj, i); obj->score += hit_object->value; obj->hits++; update_score(); return(hit_object); } } } } while (obj = obj->next); return(hit_object); } /* end UPDATE_SHOTS */ /* ** SHOT_COLLIDE ** ** Test whether the input shot has hit a particular object. ** */ Sprite *shot_collide(this_shot, obj) struct Shot *this_shot; Sprite *obj; { float dx, dy, dist; Sprite *hit; int i; do { if (obj->state == EXPLODING && obj->death_sprite) { hit = shot_collide(this_shot, obj->death_sprite); if (hit) return(hit); } if (obj->state == NORMAL) { for (i=0;inum_kill_pts;i++) { dx = this_shot->x + this_shot->pts_x[this_shot->kill_pts[i]] - obj->x; dy = this_shot->y + this_shot->pts_y[this_shot->kill_pts[i]] - obj->y; dist = sqrt(dx * dx + dy * dy); if (dist < obj->ho2) { return(obj); } } } } while (obj = obj->next_draw); return(NULL); } /* end SHOT_COLLIDE */ /* ** ERASE_SHOT ** ** Shots are represented by a linked list, so when we erase a ** shot, we'll take all the shots to the right of it and shift them ** one to the left, overwriting the shot to be erased. */ void erase_shot(obj, n) Sprite *obj; int n; { int i,j; struct Shot *curr, *curr1; X_clear_shots(obj); X_copy_object_to_window(obj); for (i=n;ishots-1;i++) { curr = obj->S[i]; curr1 = obj->S[i+1]; curr->clock = curr1->clock; curr->x = curr1->x; curr->y = curr1->y; curr->ox = curr1->ox; curr->oy = curr1->oy; curr->x_vec = curr1->x_vec; curr->y_vec = curr1->y_vec; for (j=0;j<5;j++) { curr->pts_x[j] = curr1->pts_x[j]; curr->pts_y[j] = curr1->pts_y[j]; } } obj->shots--; } /* end ERASE_SHOT */ /* ** CREATE_NEW_SHOT ** ** Speaks for itself, don't it? */ void create_new_shot(obj) Sprite *obj; { int i, j; i=obj->shots++; obj->shots_fired++; obj->S[i]->clock = Weapon[obj->wpn].shot_life; obj->S[i]->num_kill_pts = Weapon[obj->wpn].num_kill_pts; for (j=0;jS[i]->num_kill_pts;j++) obj->S[i]->kill_pts[j] = Weapon[obj->wpn].kill_pts[j]; /* Define a graphical representation of the shot, based on the */ /* current weapon */ for (j=0;j<5;j++) { obj->S[i]->pts_x[j] = Weapon[obj->wpn].pts_x[j] * cos_table[obj->curr_map] - Weapon[obj->wpn].pts_y[j] * sin_table[obj->curr_map]; obj->S[i]->pts_y[j] = -Weapon[obj->wpn].pts_x[j] * sin_table[obj->curr_map] - Weapon[obj->wpn].pts_y[j] * cos_table[obj->curr_map]; } /* Place the shot according to how the shooter is oriented */ obj->S[i]->x = obj->S[i]->ox = hot_spot_x*cos_table[obj->curr_map] - (hot_spot_y + Weapon[obj->wpn].size / 2)*sin_table[obj->curr_map] + obj->x; obj->S[i]->y = obj->S[i]->oy = -hot_spot_x*sin_table[obj->curr_map] - (hot_spot_y + Weapon[obj->wpn].size / 2)*cos_table[obj->curr_map] + obj->y; /* Give it some kick */ obj->S[i]->x_vec = - (float) Weapon[obj->wpn].shot_speed*sin_table[obj->curr_map]; obj->S[i]->y_vec = - (float) Weapon[obj->wpn].shot_speed*cos_table[obj->curr_map]; /* The stock weapon has inertia */ if (obj->wpn != LASER) { obj->S[i]->x_vec += obj->x_vec; obj->S[i]->y_vec += obj->y_vec; } /* A little momentum conservation */ if (!coop) { obj->x_vec -= 0.01 * obj->S[i]->x_vec; obj->y_vec -= 0.01 * obj->S[i]->y_vec; } } /* end CREATE_NEW_SHOT */ /* ** UPDATE_SCORE ** */ void update_score() { X_draw_status_bar(); } /* end UPDATE_SCORE */ /* ** KILL_SHARD ** ** The remnants of a blown-up object finally ** bite the dust. */ void kill_shard(the_shard) Sprite *the_shard; { int fl = 0; X_clear_one_object(the_shard); X_copy_object_to_window(P); X_copy_object_to_window(Slrb); X_copy_object_to_window(Resur); the_shard->state = DEAD; the_shard->state_ctr = 0; make_burst(the_shard, 1); /* If you're lucky, a power up will appear */ if (random_num(0.0, 10.0) > 9.0 && Power_Up->state) { Power_Up->state = NORMAL; Power_Up->x = the_shard->x; Power_Up->y = the_shard->y; Power_Up->x_vec = the_shard->x_vec; Power_Up->y_vec = the_shard->y_vec; fl = 1; } if (random_num(0.0, 10.0) < 0.3 && num_players > 1 && Meta->state && !fl && level > 1) { Meta->state = NORMAL; Meta->x = the_shard->x; Meta->y = the_shard->y; Meta->x_vec = the_shard->x_vec; Meta->y_vec = the_shard->y_vec; } } /* end KILL_SHARD */ /* ** DESTROY_OBJECT ** ** Generic object destruction. */ void destroy_object(obj) Sprite *obj; { X_clear_one_object(obj); X_copy_object_to_window(obj); obj->state = DEAD; make_burst(obj, 1); } /* end DESTROY_OBJECT */ /* ** DESTROY_HOMER ** ** Doh! */ void destroy_homer(obj) Sprite *obj; { X_clear_one_object(obj); X_copy_object_to_window(obj); draw_homer_trail(2); obj->state = DEAD; make_burst(obj, 1); } /* end DESTROY_OBJECT */ /* ** KILL_POWER_UP ** ** Someone has run into a power up, and the lucky ship will ** now reap the benefits. */ void kill_power_up(the_pu, the_lucky) Sprite *the_pu, *the_lucky; { Pixmap *old_pixmap; X_clear_one_object(the_pu); X_copy_object_to_window(the_pu); the_pu->state = DEAD; make_burst(the_pu, 1); if (the_pu == Power_Up) the_lucky->wpn = LASER; /* Metamorphosize! (sp?) */ if (the_pu == Meta) { old_pixmap = the_lucky->pixmaps; do { switch ((int) random_num(0.0, 5.0)) { case 1: the_lucky->pixmaps = P->pixmaps; the_lucky->clipmasks = P->clipmasks; break; case 2: the_lucky->pixmaps = P->next->pixmaps; the_lucky->clipmasks = P->next->clipmasks; break; case 3: the_lucky->pixmaps = Slrb->pixmaps; the_lucky->clipmasks = Slrb->clipmasks; break; case 4: the_lucky->pixmaps = Resur->pixmaps; the_lucky->clipmasks = Resur->clipmasks; break; case 5: default: the_lucky->pixmaps = Med_O[0]->pixmaps; the_lucky->clipmasks = Med_O[0]->clipmasks; break; } } while (the_lucky->pixmaps == old_pixmap); } } /* end KILL_POWER_UP */ /* ** DESTROY_OID ** ** If you're a good player, this gets called quite a bit. */ void destroy_oid(the_oid) Sprite *the_oid; { Sprite *debris, *player; int i, max_oids; max_oids = INIT_BIG_OIDS + floor(((float)level - 1.0) / 2.0); X_clear_one_object(the_oid); X_copy_object_to_window(Big_O); the_oid->state = EXPLODING; make_burst(the_oid, 1); /* Handle the destruction of the last oid */ if (++oids_shot >= max_oids * (1 + OID_DIVIDE + OID_DIVIDE * OID_DIVIDE)) { oids_shot = 0; warp_levels = MAPS_PER_360; /* Don't ask */ level++; max_oids = INIT_BIG_OIDS + floor(((float)level - 1.0) / 2.0); if (max_oids > MAX_BIG_OIDS) max_oids = MAX_BIG_OIDS; for (i=0;ishots = 0; } while (player = player->next); Slrb->state = Power_Up->state = Homer->state = Resur->state = DEAD; Slrb->shots = 0; X_flash_screen(3); return; } /* Now that you've destroyed the oid, deal with its kids */ if (debris = the_oid->death_sprite) { do { debris->x = the_oid->x; debris->y = the_oid->y; } while (debris = debris->next); } } /* end DESTROY_OID */ /* ** REVIVE_OID ** ** Back from the dead, more oids! */ void revive_oid(obj, flag) Sprite *obj; int flag; { if (flag) { obj->state = NORMAL; obj->x = random_num(0.0, (float) vdevice.sizeSx); obj->y = random_num((float) P->height, (float) vdevice.sizeSy); if (obj->death_sprite) revive_oid(obj->death_sprite, 0); } else { do { if (obj->death_sprite) revive_oid(obj->death_sprite, 0); obj->state = NORMAL; obj->x = random_num(0.0, (float) vdevice.sizeSx); obj->y = random_num((float) P->height, (float) vdevice.sizeSy); } while (obj = obj->next); } } /* end REVIVE_OID */ /* ** PAUSE_GAME ** */ void pause_game() { int c; do { X_frontbuf(); X_color(YELLOW); X_string("G A M E P A U S E D", (int) vdevice.sizeSy / 2 - 30); X_color(CYAN); X_string("Hit any key to resume", (int) vdevice.sizeSy / 2); c = X_checkkey(); X_update(1); } while (!c); X_clear(); X_backbuf(); } /* end PAUSE_GAME */ /* ** DRAW_LEVEL_WARP ** */ void draw_level_warp() { char str1[50]; X_frontbuf(); X_color(YELLOW); sprintf(str1, "Warping to Level %d", level); X_string(str1, (int) vdevice.sizeSy / 2 - 70); X_backbuf(); } /* end DRAW_LEVEL_WARP */ /* ** DRAW_GAME_OVER ** */ void draw_game_over() { char str1[50]; Sprite *winner; int acc; X_frontbuf(); X_color(RED); X_string("G A M E O V E R", (int) vdevice.sizeSy / 2 - 70); if (num_players == 1) { X_color(YELLOW); sprintf(str1, "Final Score: %d", P->score); X_string(str1, (int) vdevice.sizeSy / 2); X_color(CYAN); sprintf(str1, "Shots fired: %d", P->shots_fired); X_string(str1, (int) vdevice.sizeSy / 2 + 50); sprintf(str1, "Number of hits: %d", P->hits); X_string(str1, (int) vdevice.sizeSy / 2 + 70); if (P->shots_fired == 0) { acc = 0; } else { acc = (int) ((float) P->hits / (float) P->shots_fired * 100.0); } sprintf(str1, "Accuracy: %d%%", acc); X_string(str1, (int) vdevice.sizeSy / 2 + 90); } else { if (P->score > P->next->score) { winner = P; X_color(CYAN); } else { winner = P->next; X_color(YELLOW); } sprintf(str1, "%s wins!", winner->name); X_string(str1, (int) vdevice.sizeSy / 2 - 40); X_color(CYAN); sprintf(str1, "%s:", P->name); X_string(str1, (int) vdevice.sizeSy / 2 + 10); sprintf(str1, "Shots fired: %d", P->shots_fired); X_string(str1, (int) vdevice.sizeSy / 2 + 30); sprintf(str1, "Number of hits: %d", P->hits); X_string(str1, (int) vdevice.sizeSy / 2 + 50); if (P->shots_fired == 0) { acc = 0; } else { acc = (int) ((float) P->hits / (float) P->shots_fired * 100.0); } sprintf(str1, "Accuracy: %d%%", acc); X_string(str1, (int) vdevice.sizeSy / 2 + 70); X_color(YELLOW); sprintf(str1, "%s:", P->next->name); X_string(str1, (int) vdevice.sizeSy / 2 + 120); sprintf(str1, "Shots fired: %d", P->next->shots_fired); X_string(str1, (int) vdevice.sizeSy / 2 + 140); sprintf(str1, "Number of hits: %d", P->next->hits); X_string(str1, (int) vdevice.sizeSy / 2 + 160); if (P->next->shots_fired == 0) { acc = 0; } else { acc = (int) ((float) P->next->hits / (float) P->next->shots_fired * 100.0); } sprintf(str1, "Accuracy: %d%%", acc); X_string(str1, (int) vdevice.sizeSy / 2 + 180); } X_backbuf(); X_color(BLACK); } /* end DRAW_GAME_OVER */ /* ** SPEED ** */ float speed(obj) Sprite *obj; { return(sqrt(obj->x_vec * obj->x_vec + obj->y_vec * obj->y_vec)); } /* end SPEED */ /* ** DISTANCE ** */ float distance(obj1, obj2) Sprite *obj1, *obj2; { return(fabs(obj1->x - obj2->x) + fabs(obj1->y - obj2->y)); } /* end DISTANCE */ /* ** RANDOM_NUM ** */ float random_num(low, high) float low, high; { float out; int num; srand(r_num); /* use prev # as seed for random number generator*/ num = rand(); r_num = num; out = ((float) num) / 2147483647.0 * (high - low) + low; return(out); } /* end RANDOM_NUM */ /* ** DO_NOTHING_FOR_A_WHILE ** */ void do_nothing_for_a_while() { double x; unsigned int d; d = delay * 1000; x = 0.37; do { x = cos(x); } while (d--); } /* end DO_NOTHING_FOR_A_WHILE */ /* ** PUT_INSIDE_WINDOW ** */ void put_inside_window(obj) Sprite *obj; { if (obj->x > vdevice.sizeSx) obj->x = 0; if (obj->x < 0) obj->x = vdevice.sizeSx; if (obj->y > vdevice.sizeSy) obj->y = P->height + 3; if (obj->y < P->height + 3) obj->y = vdevice.sizeSy; } /* end PUT_INSIDE_WINDOW */