package worms.model; import static java.lang.Math.*; import worms.util.Coordinate; import java.util.List; public abstract class Projectile extends GameObject implements IJumpable { // region constructor //=================================================================================== public static final double G = 5.0; public static final double FORCE_TIME = 0.5; private static final int rho = 7800; protected Projectile (Worm worm, double mass, double force) { super(worm.getWorld(), calcLocation(worm.getLocation(), worm.getOrientation(), worm.getRadius(), mass), calcRadius(mass)); super.mass = mass; this.force = force; this.orientation = worm.getOrientation(); this.hitPoints = getRandomHitPoints(); } // =================================================================================== // endregion // region radius / orientation / force //=================================================================================== public double getOrientation() { return orientation; } private final double orientation; public static double calcRadius(double mass) { return pow(((mass / 1000.0 / rho) * (3.0 / (4.0 * PI))), 1.0 / 3.0); } public double getForce() { return this.force; } protected final double force; // =================================================================================== // endregion // region hitpoints //=================================================================================== public int getHitPoints() { return this.hitPoints; } public void incrementHitPoints(int value) { setHitPoints(this.hitPoints + value); } public void hit(Worm worm){ worm.decreaseHitPoints(getImpactHitPoints()); terminate(); } protected abstract void setHitPoints(int value) throws IllegalArgumentException; protected abstract int getImpactHitPoints(); protected abstract int getRandomHitPoints(); protected int hitPoints; // =================================================================================== // endregion // region jump //=================================================================================== public void jump(double jumpTimeStep) throws IllegalStateException { if (!canJump()) throw new IllegalStateException(); double v = jumpVelocity(); double t = jumpTime(jumpTimeStep); double a = getOrientation(); Coordinate newLocation = this.location; List worms = getWorld().getGameObjectsByClass(Worm.class); if (!getWorld().isAdjacent(getLocation(),getRadius())) { newLocation = Coordinate.create(getLocation().getX() + v * t * cos(a), getLocation().getY() + v * t * sin(a) - (G * t * t) / 2.0); } if (!isValidLocation(newLocation)) { terminate(); return; } setLocation(newLocation); for (Worm worm: worms) { if (this.getDistance(worm) < 0) { setLocation(newLocation); hit(worm); } } } public double jumpTime(double timeStep) { World world = getWorld(); if (world == null) { throw new IllegalStateException("World cannot be null"); } double v = jumpVelocity(); double time = 0; double a = getOrientation(); double radius = getRadius(); List worms = world.getGameObjectsByClass(Worm.class); if (!world.isPassable(this.location) || worms.stream().anyMatch(w -> w.getDistance(this) < 0)) { return 0.0; } while(true) { time += timeStep; Coordinate newLoc = Coordinate.create(this.location.getX() + v * time * cos(a), this.location.getY() + v * time * sin(a) - (G * time * time) / 2.0); if (newLoc.getX() - radius < 0 || newLoc.getY() - radius < 0 || newLoc.getX() + radius > world.getWidth() || newLoc.getY() + radius > world.getHeight() || !world.isPassable(newLoc) || worms.stream().anyMatch(w -> w.getDistance(newLoc, this.radius) < 0)) { return time; } } } public Coordinate jumpStep(double elapsedTime) { if (Double.isNaN(elapsedTime) || elapsedTime < 0) throw new IllegalArgumentException(); double velocity = this.jumpVelocity(); return Coordinate.create(getLocation().getX() + velocity * cos(getOrientation()) * elapsedTime, getLocation().getY() + velocity * sin(getOrientation()) * elapsedTime - 0.5 * G * pow(elapsedTime, 2)); } private boolean canJump() { return getOrientation() < PI; } private double jumpVelocity() { return (getForce() / (getMass() / 1000)) * FORCE_TIME; } // =================================================================================== // endregion // region location //=================================================================================== @Override protected boolean isValidLocation(Coordinate location) { double radius = getRadius(); if (getWorld() == null) { return !Double.isNaN(location.getX()) && !Double.isNaN(location.getY()); } return !Double.isNaN(location.getX()) && !Double.isNaN(location.getY()) && !(location.getX() - radius < 0) && !(location.getX() + radius > getWorld().getWidth()) && !(location.getY() + radius > getWorld().getHeight()) && !(location.getY() - radius < 0); } public static Coordinate calcLocation(Coordinate wormLocation, double wormOrientation, double wormRadius, double mass) { double radius = calcRadius(mass); return Coordinate.create(wormLocation.getX() + cos(wormOrientation) * (wormRadius + radius), wormLocation.getY() + sin(wormOrientation) * (wormRadius + radius)); } // =================================================================================== // endregion }