public class SpaceShip extends MovingPolygon { /* This class represents a space ship. A space ship travels through space at a certain velocity (speed,direction). The shape of the ship is a triangle, oriented to point along the positive x-axis. Ship rotation is measured clockwise around it's center. (In Java, the positive x-axis runs left to right and the positive y-axis runs towards the bottom of the screen, as through you were counting lines & character position on a page.) public variables: center, radius, rotation, speed, direction public functions: translate, move, rotateCW, rotateCCW, fireThrusters, fireAt, rotate, inCollision, toPolygon */ // defines the smallest unit of rotation private static double DEFAULT_ROTATION_ANGLE = Math.PI / 16.0; // defines how fast a pursuer ship can go and how quickly it changes direction public static double MAX_PURSUER_SPEED = 2.0, MAX_PURSUER_THRUST = 1.0; public double rotation; // constructors ----------------------------------------------------------------------------- public SpaceShip() // make a new random ship { this(new DPoint(Math.random() * X_LIMIT, Math.random() * Y_LIMIT)); } public SpaceShip(DPoint c) // make a new ship at point c { // define the border of the ship - a DPolygon super(new double[] {c.x+6.0, c.x-4.0, c.x-4.0}, new double[] {c.y, c.y-4.0, c.y+4.0}, 6.0, c); rotation = 0.0; } public SpaceShip(SpaceShip ship) // make a copy of an existing ship { super(ship); rotation = ship.rotation; } //------------------------------------------------------------------------------------------- // functions for moving and rotating a SpaceShip public void rotateCW() { rotation += DEFAULT_ROTATION_ANGLE; rotate(DEFAULT_ROTATION_ANGLE); } public void rotateCCW() { rotation -= DEFAULT_ROTATION_ANGLE; rotate(-DEFAULT_ROTATION_ANGLE); } public void fireThrusters(double accel) { // calculate new speed & direction based on accleration double vx = speed * Math.cos(direction) + accel * Math.cos(rotation), vy = speed * Math.sin(direction) + accel * Math.sin(rotation); // special case: check if the ship is stopped if (Double.isNaN(vy/vx)) { direction = rotation; speed = 0.0; } else // ship is moving { direction = Math.atan(vy/vx); speed = vx / Math.cos(direction); } } // fireThrusters() //------------------------------------------------------------------------------------------- public DPoint fireAt(AsteroidField field) { /* short description: shoots into a field of asteroids First, we search through the field of asteroids to find the closest one, if any, that is hit. We record the point of impact and the distance to impact, since the firing range of the ship is limited to 25% of the horizontal flyable space for the ship. If we hit an asteroid, have the asteroid field register an impact and return the point of impact. Otherwise, we return null, for no impact. */ int a, v, w; // asteroid #, vertex #, next vertex # double distanceToImpact = Double.POSITIVE_INFINITY; DPoint intersection, pointOfImpact = null; Asteroid target = null; // find the closest point on any asteroid in the line of fire for (a = 0; a < field.numAsteroids; a++) for (v = 0; v < field.asteroids[a].vertices.length; v++) { w = (v + 1) % field.asteroids[a].vertices.length; // index of next vertex ccw // see where the line of fire and current asteroid edge intersect intersection = DPoint.intersection(center, vertices[0], field.asteroids[a].vertices[v], field.asteroids[a].vertices[w]); // if they intersect (1), at an asteroid edge (2), in front of the ship (3), // and if it's closer than any previously determined point of impact (4) ... if (intersection != null && intersection.isBetween(field.asteroids[a].vertices[v], field.asteroids[a].vertices[w]) && vertices[0].isBetween(center, intersection) && intersection.distanceFrom(vertices[0]) < distanceToImpact) { // make that intersection our new closest point of impact pointOfImpact = intersection; distanceToImpact = intersection.distanceFrom(vertices[0]); target = field.asteroids[a]; } } // if we hit anything (1), in range (2), reshape the asteroid & return the point of impact if (target != null && distanceToImpact < X_LIMIT/4.0) { field.impact(target); return pointOfImpact; } else return null; // oops, the shot missed } // fireAt() }