package spacewar; class Ship extends SpaceObject { pointcut helmCommandsCut(Ship ship): target(ship) && ( call(void rotate(int)) || call(void thrust(boolean)) || call(void fire()) ); /** * Energy and Damage are key values in the state of a ship. Energy is * basically about fuel, and damage is about how bad a shape we are in. * * The energy related values are: * MAX_ENERGY * BULLET_ENERGY * ACCELERATION_ENERGY_FACTOR * energy * * The damage related values are: * MAX_DAMAGE * BULLET_DAMAGE * COLLISION_DAMAGE_FACTOR * damage * * Finally, REPAIR_RATE is the rate at which energy is consumed to fix * damage. * */ private static final int MAX_ENERGY = 100; private static final int BULLET_ENERGY= 2; private static final double ACCELERATION_COST_FACTOR = 0.05; //XXX was private static final int MAX_DAMAGE = 100; private static final int BULLET_DAMAGE = 15; private static final double COLLISION_DAMAGE_FACTOR = 0.1; private static final double REPAIR_RATE = 0.08; private static final int EXPLOSION_LENGTH = 10; static final int BULLET_SPEED = 10; static final int CLOCKWISE = 1; static final int STOP = 0; static final int COUNTERCLOCKWISE = (-1); static final double DEFAULT_ANGULAR_VELOCITY = 0.2; static final double DEFAULT_ACCELERATION = .4; static private final int SIZE = 30; //Can't be changed for now!!! private double energy; // range: 0 to MAX_ENERGY private double damage; // range: 0 to MAX_DAMAGE private double orientation; // in degrees private double angularVel; // in ??? private double xAcc, yAcc, rAcc; // private int countdown; // remaining explosion time private Pilot pilot; Ship(Game theGame, double xPos, double yPos, double orientation) { super(theGame, xPos, yPos, 0, 0); xAcc = 0; yAcc = 0; this.orientation = orientation; angularVel = 0; energy = MAX_ENERGY; damage = 0; countdown = EXPLOSION_LENGTH; } int getSize() { return SIZE; } double getEnergy() { return energy; } double getDamage() { return damage; } double getOrientation() { return orientation; } double getRAcc() { return rAcc; } Pilot getPilot() { return pilot; } void setPilot (Pilot p) { pilot = p; } float getEnergyLevel() { return (float)energy / (float)MAX_ENERGY; } float getDamageLevel() { return (float)damage / (float)MAX_DAMAGE; } // returns false if energy is out, otherwise decrements energy by amount // and returns true boolean expendEnergy(double amount) { if (amount <= energy) { energy -= amount; return true; } else return false; } // increments damage by amount and handles the destruction of a ship if // damage reaches MAX_DAMAGE. void inflictDamage(double amount) { if (amount < 0) // shouldn't happen return; damage = Math.min(MAX_DAMAGE, damage + amount); if (damage == MAX_DAMAGE) setIsAlive(false); } void repairDamage(double amount) { if (amount < 0) // shouldn't happen return; if (damage == 0) return; damage = Math.max(0, damage - amount); } public void clockTick() { if (! isAlive()) { // // If we aren't alive, but we are still in the registry, it means // we are exploding. countdown counts the length of the explosion. // if (--countdown == 0) die(); } else { if (angularVel != 0) { orientation += angularVel; xAcc = rAcc * Math.cos(orientation); yAcc = rAcc * Math.sin(orientation); } setXVel(getXVel() + xAcc); setYVel(getYVel() + yAcc); //expend energy if (!expendEnergy(rAcc * ACCELERATION_COST_FACTOR)) rAcc = xAcc = yAcc = 0; // fix damage if (energy > 10 && damage > REPAIR_RATE) { expendEnergy(REPAIR_RATE); repairDamage(REPAIR_RATE); } } super.clockTick(); } /** * First check to make sure we have enough energy to accelerate. If * we do, then go ahead and do so. Acceleration is in the direction * we are already facing (i.e. orientation). */ void setAcceleration(double acc) { if (acc * ACCELERATION_COST_FACTOR <= energy) { rAcc = acc; xAcc = rAcc * Math.cos(orientation); yAcc = rAcc * Math.sin(orientation); } } void setAngularVelocity(double omega) { // changing direction of rotation takes energy if (!expendEnergy(Math.abs(omega - angularVel) / 2)) return; //sets amount of degree rotation per clock tick, in radians; //clockwise is positive angularVel = omega; } void rotate(int direction) { setAngularVelocity( direction == CLOCKWISE ? DEFAULT_ANGULAR_VELOCITY : direction == COUNTERCLOCKWISE ? -DEFAULT_ANGULAR_VELOCITY : 0); } void thrust(boolean onOff) { setAcceleration(onOff ? DEFAULT_ACCELERATION : 0); } void fire() { // firing a shot takes energy if (!expendEnergy(BULLET_ENERGY)) return; //create a bullet object so it doesn't hit the ship that's firing it double xV = getXVel() + BULLET_SPEED * (Math.cos(orientation)); double yV = getYVel() + BULLET_SPEED * (Math.sin(orientation)); // create the actual bullet new Bullet( getGame(), (getXPos() + ((getSize()/2 + 2) * (Math.cos(orientation))) + xV), (getYPos() + ((getSize()/2 + 2) * (Math.sin(orientation))) + yV), xV, yV); } void handleCollision(SpaceObject obj) { if (obj instanceof Ship) { // should never be called. ship - ship collisions are handled in // Ship.bounce(Ship shipA, Ship shipB) } else if (obj instanceof Bullet) { inflictDamage(BULLET_DAMAGE); } else if (obj instanceof EnergyPacket) { double packetEnergy = ((EnergyPacket)obj).getEnergy(); energy = Math.max(0, Math.min(energy + packetEnergy, MAX_ENERGY)); } else { System.err.println("collision with UFO!"); } } static void bounce(Ship shipA, Ship shipB) { double dx, dy, denominator, xAccA, yAccA, xAccB, yAccB, damage, xComp, yComp, dvx, dvy; dx = Math.abs(shipA.getXPos() - shipB.getXPos()); dy = Math.abs(shipA.getYPos() - shipB.getYPos()); denominator = Math.sqrt(dx * dx + dy * dy); xComp = dx / denominator; yComp = dy / denominator; xAccA = shipB.getXVel() * xComp + shipA.getXVel() * (1 - xComp) - shipA.getXVel(); yAccA = shipB.getYVel() * yComp + shipA.getYVel() * (1 - yComp) - shipA.getYVel(); xAccB = shipA.getXVel() * xComp + shipB.getXVel() * (1 - xComp) - shipB.getXVel(); yAccB = shipA.getYVel() * yComp + shipB.getYVel() * (1 - yComp) - shipB.getYVel(); shipA.accelerate(xAccA, yAccA); shipB.accelerate(xAccB, yAccB); dvx = shipA.getXVel() - shipB.getXVel(); dvy = shipA.getYVel() - shipA.getYVel(); damage = COLLISION_DAMAGE_FACTOR * (dvx * dvx + dvy * dvy); shipA.inflictDamage(damage); shipB.inflictDamage(damage); // !!! // !!! poopers! this does a local time warp. this has to be a // !!! violation of the clockTick protocol // !!! while (Game.isCollision(shipA, shipB)) { shipA.clockTick(); shipB.clockTick(); } } }