// xsc: Copyright (c) 1993-2005 by Mark B. Hanson (mbh@panix.com). static const char *const file_id = "@(#)$Id: ship.C,v 3.20 2005/01/02 19:11:17 mark Exp $"; #include "global.h" #include "random.h" #include "timing.h" #include "trig.h" #include "util.h" #include "xsc.h" #include "ship.h" using namespace Trig; const coords ship_points[NUM_SHIP_POINTS] = { { 0.50, -0.10 }, { -0.50, -0.18 }, { -0.50, -0.18 }, { 0.30, -0.03 }, { 0.30, -0.03 }, { -0.45, -0.50 }, { -0.45, -0.50 }, { -0.05, 0.00 }, { -0.05, 0.00 }, { -0.45, 0.50 }, { -0.45, 0.50 }, { 0.30, 0.03 }, { 0.30, 0.03 }, { -0.50, 0.18 }, { -0.50, 0.18 }, { 0.50, 0.10 }, }; const float max_turn_rate = 540.0; Ship::Ship(void) { int i, j; //fprintf(stderr, "Ship::Ship()\n"); set_scale(25.0); user_rotating_cw = false; user_rotating_ccw = false; user_thrusting = false; theta = 180.0; points = ship_points; num_points = NUM_SHIP_POINTS; xpoints = new XPoint[num_points]; Tthing::set_xpoints(); x = initial_x(); y = initial_y(); max_shots = 3; lasers = new Laser[max_shots]; for (i = 0; i < max_shots; i++) { lasers[i].set_ship(this); } state = SHIP_LIVING; split_points = new coords[NUM_SHIP_POINTS * 4]; for (i = 0, j = 0; i < NUM_SHIP_POINTS; i += 2, j += 8) { coords mid1, mid2, mid3; mid2.x = (ship_points[i].x + ship_points[i + 1].x) / 2.0; mid2.y = (ship_points[i].y + ship_points[i + 1].y) / 2.0; mid1.x = (ship_points[i].x + mid2.x) / 2.0; mid1.y = (ship_points[i].y + mid2.y) / 2.0; mid3.x = (mid2.x + ship_points[i + 1].x) / 2.0; mid3.y = (mid2.y + ship_points[i + 1].y) / 2.0; split_points[j] = ship_points[i]; split_points[j + 1] = mid1; split_points[j + 2] = mid1; split_points[j + 3] = mid2; split_points[j + 4] = mid2; split_points[j + 5] = mid3; split_points[j + 6] = mid3; split_points[j + 7] = ship_points[i + 1]; } segments = new Ething[NUM_SHIP_POINTS * 2]; for (i = 0; i < NUM_SHIP_POINTS * 2; i++) { segments[i].set_points(split_points + (i * 2), 2); segments[i].set_gc(gc); segments[i].set_scale(scale); } } // Ship::Ship Ship::~Ship(void) { //fprintf(stderr, "Ship::~Ship()\n"); delete[] segments; delete[] split_points; delete[] lasers; } // Ship::~Ship float Ship::initial_x(void) { return wwidth * 0.9; } // Ship::initial_x float Ship::initial_y(void) { return (Random::get() % (int)(gwheight * 0.8)) + (gwheight * 0.1); } // Ship::initial_y void Ship::render(const bool ink) { int i; for (i = 0; i < max_shots; i++) { lasers[i].render(ink); } if (state == SHIP_EXPLODING) { for (i = 0; i < NUM_SHIP_POINTS * 2; i++) { segments[i].render(ink); } } if (!alive()) { return; } int nx = 0; int ny = 0; int nx0 = 0; int ny0 = 0; int nx1 = 0; int ny1 = 0; GC thisgc; if (ink) { Tthing::set_xpoints(); thisgc = gc; } else { thisgc = fetch_gc(GC_BLACK); } for (i = 0; i < num_points; i++) { if (xpoints[i].x < 0) { nx = wwidth; } else if (xpoints[i].x >= wwidth) { nx = -wwidth; } if (xpoints[i].y < 0) { ny = gwheight; } else if (xpoints[i].y >= gwheight) { ny = -gwheight; } } paint_points(ink); for (i = 1; i < num_points; i += 2) { if (nx) { nx0 = nx + xpoints[i - 1].x; nx1 = nx + xpoints[i].x; XDrawLine(display, window, thisgc, nx0, xpoints[i - 1].y, nx1, xpoints[i].y); } if (ny) { ny0 = ny + xpoints[i - 1].y; ny1 = ny + xpoints[i].y; XDrawLine(display, window, thisgc, xpoints[i - 1].x, ny0, xpoints[i].x, ny1); } if (nx && ny) { XDrawLine(display, window, thisgc, nx0, ny0, nx1, ny1); } } } // Ship::render inline void Ship::bounce(Castle *castle) { const float deg = get_angle(); const float dist = (castle->ring_size(CASTLE_RING_OUTER) / 2.0) + diag; x = (xcos(deg) * dist) + wwidth2; y = (-xsin(deg) * dist) + gwheight2; Xything::bounce(normalize(deg + 90.0)); // bounce speed penalty dx /= 4.0; dy /= 4.0; const float left = wedge(theta, deg - 90.0); const float right = wedge(theta, deg + 90.0); const float which_way = (fabs(left) < fabs(right)) ? left : right; dtheta = (max_turn_rate / args.fps) * (which_way / 90.0); } // Ship::bounce void Ship::move(Castle *castle, King *king, Minefield *minefield, Stats *stats) { int i; for (i = 0; i < max_shots; i++) { lasers[i].move(castle, king, minefield, stats); } if (state == SHIP_EXPLODING) { if ((time_now - time_of_death) - pause_sum > 2500000L) { state = SHIP_RESTING; } else { for (i = 0; i < NUM_SHIP_POINTS * 2; i++) { float sdx = segments[i].get_dx(); float sdy = segments[i].get_dy(); float sdt = segments[i].get_dtheta(); // motion decay sdx -= sdx / args.fps; sdy -= sdy / args.fps; sdt -= sdt / args.fps; segments[i].set_dx(sdx); segments[i].set_dy(sdy); segments[i].set_dtheta(sdt); // bounce off the castle if (king->alive()) { const XPoint *xp = segments[i].get_xpoints(); const float ring = castle->ring_size(CASTLE_RING_OUTER) / 2.0; const float d0 = hypot(wwidth2 - (xp[0].x + sdx), gwheight2 - (xp[0].y + sdy)); const float d1 = hypot(wwidth2 - (xp[1].x + sdx), gwheight2 - (xp[1].y + sdy)); if (d0 < ring || d1 < ring) { const float deg = segments[i].get_angle(); segments[i].bounce(normalize(deg + 90.0)); segments[i].set_dtheta(-sdt); } } segments[i].move(); segments[i].turn(); } } } if (state == SHIP_RESTING && (time_now - time_of_death) - pause_sum > 4000000L) { stats->lives--; if (stats->lives < 0) { stats->state = STATE_OVER; state = SHIP_DEAD; } else { reincarnate(); } } if (!alive()) { return; } if (user_thrusting) { const float acceleration = wwidth / 0.75; const float delta = acceleration / sq(args.fps); dx += xcos(theta) * delta; dy += -xsin(theta) * delta; const float speed = hypot(dx, dy); const float max_speed = (wwidth / 2.25) / args.fps; if (speed > max_speed) { const float limit_factor = max_speed / speed; dx *= limit_factor; dy *= limit_factor; } } else { const float acceleration = wwidth / 11.0; const float delta = -acceleration / sq(args.fps); float speed = hypot(dx, dy) + delta; if (speed < 0.0) { speed = 0.0; } const float drift_angle = xatan2(dy, -dx); dx = xcos(drift_angle) * speed; dy = -xsin(drift_angle) * speed; } if (king->alive()) { for (i = 0; i < num_points; i++) { if (hypot(wwidth2 - (xpoints[i].x + dx), gwheight2 - (xpoints[i].y + dy)) < castle->ring_size(CASTLE_RING_OUTER) / 2.0) { bounce(castle); break; } } } Xything::move(); } // Ship::move void Ship::turn(void) { if (!alive()) { return; } if ((user_rotating_cw || user_rotating_ccw) && !(user_rotating_cw && user_rotating_ccw)) { const float acceleration = 720.0; const float delta = acceleration / sq(args.fps); if (user_rotating_cw) { dtheta -= delta; } else if (user_rotating_ccw) { dtheta += delta; } const float max_rate = max_turn_rate / args.fps; if (fabs(dtheta) > max_rate) { dtheta = sign(dtheta) * max_rate; } } else { const float acceleration = 1440.0; const float delta = acceleration / sq(args.fps); const int direction = sign(dtheta); dtheta += -direction * delta; if (sign(dtheta) != direction) { dtheta = 0.0; } } Ething::turn(); } // Ship::turn void Ship::fire(void) const { if (!alive()) { return; } for (int i = 0; i < max_shots; i++) { if (!lasers[i].alive()) { lasers[i].launch(); return; } } } // Ship::fire void Ship::resize(const int nwidth, const int nheight) { int i; Xything::resize(nwidth, nheight); for (i = 0; i < max_shots; i++) { lasers[i].resize(nwidth, nheight); } for (i = 0; i < NUM_SHIP_POINTS * 2; i++) { segments[i].resize(nwidth, nheight); } } // Ship::resize bool Ship::hit(Xything *badguy) { if (!alive() || hypot(badguy->get_x() - x, badguy->get_y() - y) >= (diag + badguy->get_diag()) / 2.0) { return false; } state = SHIP_EXPLODING; // set up segments for (int i = 0; i < NUM_SHIP_POINTS * 2; i++) { segments[i].set_x(x); segments[i].set_y(y); segments[i].set_dx(dx + ((Random::get() % wwidth / 2) - wwidth / 4.0) / args.fps); segments[i].set_dy(dy + ((Random::get() % wwidth / 2) - wwidth / 4.0) / args.fps); segments[i].set_theta(theta); segments[i].set_dtheta(dtheta + ((Random::get() % 3000) - 1500.0) / args.fps); } x = wwidth2; // send the buzzers back home y = gwheight2; time_of_death = time_now; pause_sum = 0; XBell(display, 100); return true; } // Ship::hit void Ship::reincarnate(void) { theta = 180.0; x = initial_x(); y = initial_y(); dtheta = 0.0; dx = 0.0; dy = 0.0; state = SHIP_LIVING; } // Ship::reincarnate