/*
* This file is part of jSpaceWar.
*
* jSpaceWar is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* jSpaceWar is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with jSpaceWar. If not, see <http://www.gnu.org/licenses/>.
*/
package com.mlarktar.spacewar.sprites;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.Timer;
import com.mlarktar.spacewar.Parameters;
import com.mlarktar.spacewar.SoundEvents;
import com.mlarktar.spacewar.Space;
/**
* @author malar
*
* This class is the main class for the spaceships. It controls all the aspects
* of its movements, velocity and angle.
*/
public class Ship extends SpaceItem implements ActionListener {
public static final int SHIPSIZE =
Integer.parseInt(
Parameters.getParameters().getProperty("ship.shipsize", "20"));
public static final int MAXLEVEL =
Integer.parseInt(
Parameters.getParameters().getProperty("ship.maxlevel", "200"));
public static final int SHIPHITPOINTS =
Integer.parseInt(
Parameters.getParameters().getProperty("ship.shiphitpoints", "20"));
public static final int HYPERCOST =
Integer.parseInt(
Parameters.getParameters().getProperty("ship.hypercost", "8"));
public static final int CLOAKCOST =
Integer.parseInt(
Parameters.getParameters().getProperty("ship.cloakcost", "1"));
public static final double TURNSHIP =
Double.parseDouble(
Parameters.getParameters().getProperty("ship.turnship", "0.2"));
public static final int PANICLEVEL =
Integer.parseInt(
Parameters.getParameters().getProperty("ship.paniclevel", "10"));
public static final int ENERGYBUFFER =
Integer.parseInt(
Parameters.getParameters().getProperty("ship.energybuffer", "4"));
public static final int ENERGYTOSHIELD =
Integer.parseInt(
Parameters.getParameters().getProperty(
"ship.energytoshield",
"50"));
public static final int SHIELDTOENERGY =
Integer.parseInt(
Parameters.getParameters().getProperty(
"ship.shieldtoenergy",
"50"));
public static final double BOOSTERSACCEL =
Double.parseDouble(
Parameters.getParameters().getProperty("ship.acceleration", "0.8"));
// command constants
public static final int CLOCKWISE = -1;
public static final int COUNTERCLOCKWISE = 1;
double angle;
protected Space parent;
private SoundEvents sounds;
protected Ship enemy;
private int shieldLevel = MAXLEVEL / 2;
private int energyLevel = MAXLEVEL / 2;
private int energyBuffer;
private int boosterBuffer;
private int misileBuffer;
protected Timer timer;
private boolean cloaking;
private boolean doHyper;
private boolean panic;
private ShipRepresentation representation;
/**
* @return
*/
public Space getParent() {
return parent;
}
/**
* @return true if the ship is running low on shields
*/
public boolean isPanic() {
/* cannot simply return panic because the ship AI or
* the user might have solved the situation by shifting
* energy
*/
return shieldLevel < MAXLEVEL / 100 * PANICLEVEL;
}
/**
* @return the current ship�s representation
*/
public ShipRepresentation getRepresentation() {
return representation;
}
/**
* @param representation a class derived from ShipRepresentation
*/
public void setRepresentation(ShipRepresentation representation) {
this.representation = representation;
}
/**
* @return current ship�s angle
*/
public double getAngle() {
return angle;
}
public Ship(int x, int y, Space parent) {
super(x, y, 0, 0);
this.angle = Math.atan2(vy, vx);
this.parent = parent;
this.timer = new Timer(500, this);
this.sounds = new SoundEvents();
this.representation = new ShipRepresentation();
}
/* Draws the spaceitem
* @see SpaceItem#draw(java.awt.Graphics)
*/
public void draw(Graphics g) {
if (doHyper) {
doHyper = false;
Rectangle bounds = g.getClipBounds();
x = Math.random() * bounds.width;
y = Math.random() * bounds.height;
sounds.playEvent(SoundEvents.HYPER);
}
if (!cloaking) {
representation.draw(this, g);
}
}
public void changeSpeed(double value) {
super.changeSpeed(
value * Math.cos(this.angle),
value * Math.sin(this.angle));
}
public void engageBoosters() {
if (energyLevel > 0) {
++boosterBuffer;
if (boosterBuffer >= 5) {
boosterBuffer = 0;
--energyLevel;
}
changeSpeed(BOOSTERSACCEL);
}
}
public void changeAngle(double value) {
this.angle += value;
final double TWOPI = Math.PI * 2;
if (this.angle > TWOPI) {
this.angle -= TWOPI;
}
if (this.angle < 0) {
this.angle += TWOPI;
}
}
public void turnShip(int direction) {
if (Math.abs(direction) == COUNTERCLOCKWISE) {
changeAngle(TURNSHIP * direction);
}
}
public Rectangle getBounds() {
return new Rectangle((int) x, (int) y, SHIPSIZE, SHIPSIZE);
}
/* Draw energy to create a misile if the energy depletes no misile can be fired
*
*/
public void fireMisile() {
if (energyLevel > Misile.MISILECOST
&& misileBuffer < Misile.MISILEREPEAT) {
--energyLevel;
++misileBuffer;
Rectangle bounds = getBounds();
parent.addSpaceItem(new Misile(this, parent));
sounds.playEvent(SoundEvents.MISILE);
}
}
/* Draw energy from the shield. If the shield energy depletes you are dead
* @see SpaceItem#hit(SpaceItem)
*/
public boolean hit(SpaceItem si) {
shieldLevel -= si.getHitPoints();
if (si.getHitPoints() >= SHIPHITPOINTS) {
changeSpeedDelay(-vx + si.getVx(), -vy + si.getVy());
sounds.playEvent(SoundEvents.BOUNCE);
}
if (shieldLevel <= 0) {
stop();
}
if (!panic && shieldLevel < MAXLEVEL / 100 * PANICLEVEL) {
panic = true;
sounds.loopEvent(SoundEvents.AMB_FIN);
}
return shieldLevel <= 0;
}
/* A collition against a ship produces 20 hitpoints
* @see SpaceItem#getHitPoints()
*/
public int getHitPoints() {
return SHIPHITPOINTS;
}
/**
* @return
*/
public int getEnergyLevel() {
return energyLevel;
}
/**
* @return
*/
public int getShieldLevel() {
return shieldLevel;
}
/* The ships energy is rechargeable. But it really does not matter much.
* @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
*/
public void actionPerformed(ActionEvent e) {
++energyBuffer;
if (misileBuffer > 0) {
--misileBuffer;
}
if (energyBuffer == ENERGYBUFFER) {
energyBuffer = 0;
++energyLevel;
}
if (cloaking && energyLevel >= CLOAKCOST) {
energyLevel -= CLOAKCOST;
} else {
stopCloak();
}
}
/** Fires a laser
*
*/
public void fireLaser() {
if (energyLevel > LaserBeam.LASERCOST) {
--energyLevel;
parent.addSpaceItem(new LaserBeam(this, parent));
sounds.playEvent(SoundEvents.LASER);
}
}
/**
*
*/
public void shiftToEnergy() {
if (shieldLevel > 1) {
int shift = (int) ((double) shieldLevel / 100 * SHIELDTOENERGY);
energyLevel += shift;
shieldLevel -= shift;
}
}
/**
*
*/
public void shiftToShield() {
if (energyLevel > 1) {
int shift = (int) ((double) energyLevel / 100 * ENERGYTOSHIELD);
shieldLevel += shift;
energyLevel -= shift;
}
}
/**
*
*/
public void hyperSpace() {
if (energyLevel > HYPERCOST) {
energyLevel -= HYPERCOST;
vx = 0;
vy = 0;
doHyper = true;
}
}
public void startCloak() {
if (energyLevel > CLOAKCOST)
cloaking = true;
}
public void stopCloak() {
cloaking = false;
}
public boolean isCloaked() {
return cloaking;
}
/**
* @return
*/
public SoundEvents getSounds() {
return sounds;
}
/**
* @param events
*/
public void setSounds(SoundEvents events) {
sounds = events;
}
public Ship getEnemy() {
return enemy;
}
public void setEnemy(Ship ship) {
enemy = ship;
}
public void start() {
timer.start();
}
public void stop() {
if (timer != null)
timer.stop();
}
public void dispose() {
if (timer != null) {
stop();
timer.removeActionListener(this);
timer = null;
}
parent = null;
sounds = null;
enemy = null;
}
}