package vooga.rts.gamedesign.weapon; import vooga.rts.gamedesign.sprite.gamesprites.Projectile; import vooga.rts.gamedesign.sprite.gamesprites.interactive.InteractiveEntity; import vooga.rts.gamedesign.state.AttackingState; import vooga.rts.gamedesign.upgrades.*; import vooga.rts.util.DelayedTask; import vooga.rts.util.Location3D; import java.awt.geom.Ellipse2D; import java.util.ArrayList; import java.util.Iterator; import java.util.List; /** * This class represents a weapon. The CanAttack class the implements * AttackStrategy contains a list of weapons so that every interactive that can attack * will have an instance of a weapon. A weapon can do damage and also contains * a projectile that does damage. A weapon is used to attack by using the fire * method. When an upgrade of a pojectile is selected, the weapon will just * change the type of projectile that it contains to the specified projectile. * * @author Ryan Fishel * @author Kevin Oh * @author Francesco Agosti * @author Wenshun Liu * */ public class Weapon { public static int DEFAULT_RANGE = 500; public static int DEFAULT_COOLDOWN_TIME = 1; private Projectile myProjectile; private int myRange; private List<Projectile> myProjectiles; private double myCooldownTime; private Location3D myCenter; private AttackingState attackingState; private DelayedTask cooldownTime; /** * Creates a new weapon with default damage and projectile. * * @param damage * @param projectile */ public Weapon (Projectile projectile, int range, Location3D center, double cooldownTime) { this(range, cooldownTime); myProjectile = projectile; myCenter = center; } public Weapon (int range, double cooldownTime) { myRange = range; myCooldownTime = cooldownTime; myProjectiles = new ArrayList<Projectile>(); attackingState = AttackingState.NOT_ATTACKING; } /** * This method is used by the weapon to attack an InteractiveEntity. * */ public void fire (InteractiveEntity interactiveEntity) { final InteractiveEntity toBeShot = interactiveEntity; if (canFire(toBeShot)) { attackingState = AttackingState.WAITING; cooldownTime = new DelayedTask(myCooldownTime, new Runnable() { @Override public void run () { attackingState = AttackingState.ATTACKING; } }); } if (attackingState == AttackingState.ATTACKING) { Projectile firingProjectile = myProjectile.copy(myProjectile, myCenter); firingProjectile.setEnemy(toBeShot); firingProjectile.move(toBeShot.getWorldLocation()); myProjectiles.add(firingProjectile); attackingState = AttackingState.NOT_ATTACKING; } } /** * Determines whether a weapon can possibly fire based on whether or not it is already waiting * to attack (or attacking) and whether * its target is dead or not. This method is used to make sure that a delayed task (for weapon * cooldown) is not overwritten before it * is used. * * @param toBeShot is the enemy being targeted * @return whether or not the weapon can create a delayed task to fire */ private boolean canFire (final InteractiveEntity toBeShot) { return !toBeShot.isDead() && attackingState == AttackingState.NOT_ATTACKING; } /** * * NOTE: moving this method is gonna break DamageUpgradeNode. * * @param damage */ public void addDamage (int damage) { myProjectile.addDamage(damage); } /** * Returns the list of projectiles. * * @return the list of projectiles that this weapon has */ public List<Projectile> getProjectiles () { return myProjectiles; } /** * Returns the projectile that is currently in use. * * @return the projectile that is currently in use */ public Projectile getProjectile () { return myProjectile; } /** * This method is used to change the projectile for the weapon. * * @param projectile is the projectile that will be used by the weapon */ public void setProjectile (Projectile projectile) { myProjectile = projectile; } /** * Checks to see if the interactive passed in is in the range of the * weapon. * * @param interactive is checked to see if it is in the range of the weapon * @return true if the interactive is in the range of the weapon and false * if the interactive is out of the range of the weapon */ public boolean inRange (InteractiveEntity enemy, double distance) { return (distance < this.myRange); } /** * Returns the range of the weapon. * * @return the range of the weapon */ public int getRange () { return myRange; } /** * Increases the range of the weapon * * @param range the amount of range to be increased */ public void addRange (int range) { myRange += range; } /** * Updates the weapon so that the cooldown between attacks is decremented * and the projectiles are updated. * * @param elapsedTime is the time that has elapsed. */ public void update (double elapsedTime) { if (cooldownTime != null) { cooldownTime.update(elapsedTime); } Iterator<Projectile> it = myProjectiles.iterator(); while (it.hasNext()) { Projectile p = it.next(); if (!p.isDead()) { p.update(elapsedTime); } else { it.remove(); } } } /** * @param center the center to set */ public void setCenter (Location3D center) { myCenter = center; myProjectile.setWorldLocation(center); } /** * Returns a copy of the weapon so that units do not share the same weapon. * * @return a copy of the weapon */ public Weapon copy () { return new Weapon(myProjectile, myRange, myCenter, myCooldownTime); } }