TF-ProjectKorra/src/com/projectkorra/projectkorra/airbending/AirSuction.java
Christopher Martin 102112ffdd 1.8.6 (#825)
## Fixes
* Fixed Combos and possibly Passives appearing in `/pk b <Ability>` auto-tabbing.
* Fixed Combos not loading properly on certain servers.
* Fixed issue with `PreciousStones` by updating to the latest version to resolve API change issues.
* Fixed `RapidPunch` damage.
* Fixed incorrect summation of chiblocking chance.
* Fixed possible issue in PKListener#onPlayerInteraction()
* Fixed `Earth.LavaSound`.
* Fixed Chi attempting to chiblock targets with any move.
* Fixed hitting an entity with `TempArmor` not ignoring armor.
* Fixed `Immobilize` config path.

## Additions
* Added "Contributing" section to the `README` to help guide active community members.
* Added more detail to the `PULL_REQUEST_TEMPLATE` to allow for more uniform pull requests. 
* Added many new blocks to our ability block interaction.
* Added check to combo collisions to discard dead entities.
* Added functionality to allow chiblocking abilities to affect all entities.
* Added exception handling to the configurable `Sound` options to prevent `IllegalArgumentExcpetions`.
* Added sounds and `ActionBar` messages to being Bloodbent, Electrocuted, Immobilized, MetalClipped, and Paralyzed. (Abilities: `Bloodbending`, `Lightning`, `Immobilize`, `MetalClips`, and `Paralyze`)
* Added sound and `ActionBar` message for being Chiblocked.
* Added interval config option to `RapidPunch`. (time between each punch)

## API Changes
* Updated to `Spigot 1.12.1`.
    * Confirmed to be backward compatible with `Spigot 1.12` and `Spigot 1.11.2`.
* Renamed `ElementalAbility#getTransparentMaterial()` to `ElementalAbility#getTransparentMaterials()`.
* Converted most `byte`/`int` dependent `Material` logic to use `Material` instead. 
    * `ElementalAbility#getTransparentMaterialSet()` now returns a `HashSet<Material>` instead of a `HashSet<Byte>`.
    * `ElementalAbility#getTransparentMaterials()` and `GeneralMethods.NON_OPAQUE` now return `Material[]` instead of `Integer[]`.
    * `GeneralMethods#getTargetedLocation()` now takes a `varargs Material[]` instead of a `varargs Integer[]`.
* Removed `ElementalAbility.TRANSPARENT_MATERIAL`. It was outdated and became irrelevent after `GeneralMethods.NON_OPAQUE` was updated.
* Removed `Java 7` Travi-CI  JDK check.
* Updated `pom.xml` to build in `Java 8`.
* Added new `MovementHandler` utility class to control entity movement. (currently only capable of stopping movement.
2017-08-06 00:18:12 -07:00

365 lines
9.9 KiB
Java

package com.projectkorra.projectkorra.airbending;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import org.bukkit.Effect;
import org.bukkit.Location;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.util.Vector;
import com.projectkorra.projectkorra.BendingPlayer;
import com.projectkorra.projectkorra.GeneralMethods;
import com.projectkorra.projectkorra.ProjectKorra;
import com.projectkorra.projectkorra.ability.AirAbility;
import com.projectkorra.projectkorra.ability.util.Collision;
import com.projectkorra.projectkorra.attribute.Attribute;
import com.projectkorra.projectkorra.command.Commands;
import com.projectkorra.projectkorra.object.HorizontalVelocityTracker;
import com.projectkorra.projectkorra.util.Flight;
import com.projectkorra.projectkorra.waterbending.WaterSpout;
public class AirSuction extends AirAbility {
private static final int MAX_TICKS = 10000;
private static final Map<Player, Location> ORIGINS = new ConcurrentHashMap<>();
private boolean hasOtherOrigin;
private int ticks;
private int particleCount;
@Attribute(Attribute.COOLDOWN)
private long cooldown;
@Attribute(Attribute.SPEED)
private double speed;
@Attribute(Attribute.RANGE)
private double range;
@Attribute(Attribute.RADIUS)
private double radius;
@Attribute(Attribute.POWER)
private double pushFactor;
private Random random;
private Location location;
private Location origin;
private Vector direction;
public AirSuction(Player player) {
super(player);
if (bPlayer.isOnCooldown(this)) {
return;
} else if (player.getEyeLocation().getBlock().isLiquid()) {
return;
} else if (hasAbility(player, AirSpout.class) || hasAbility(player, WaterSpout.class)) {
return;
}
this.hasOtherOrigin = false;
this.ticks = 0;
this.particleCount = getConfig().getInt("Abilities.Air.AirSuction.Particles");
this.speed = getConfig().getDouble("Abilities.Air.AirSuction.Speed");
this.range = getConfig().getDouble("Abilities.Air.AirSuction.Range");
this.radius = getConfig().getDouble("Abilities.Air.AirSuction.Radius");
this.pushFactor = getConfig().getDouble("Abilities.Air.AirSuction.Push");
this.cooldown = getConfig().getLong("Abilities.Air.AirSuction.Cooldown");
this.random = new Random();
if (ORIGINS.containsKey(player)) {
origin = ORIGINS.get(player);
hasOtherOrigin = true;
ORIGINS.remove(player);
} else {
origin = player.getEyeLocation();
}
location = GeneralMethods.getTargetedLocation(player, range, getTransparentMaterials());
direction = GeneralMethods.getDirection(location, origin).normalize();
Entity entity = GeneralMethods.getTargetedEntity(player, range);
if (entity != null) {
direction = GeneralMethods.getDirection(entity.getLocation(), origin).normalize();
location = getLocation(origin, direction.clone().multiply(-1));
}
bPlayer.addCooldown(this);
if (bPlayer.isAvatarState()) {
this.pushFactor = getConfig().getDouble("Abilities.Avatar.AvatarState.Air.AirSuction.Push");
}
start();
}
private static void playOriginEffect(Player player) {
if (!ORIGINS.containsKey(player)) {
return;
}
Location origin = ORIGINS.get(player);
BendingPlayer bPlayer = BendingPlayer.getBendingPlayer(player);
if (bPlayer == null || player.isDead() || !player.isOnline()) {
return;
} else if (!origin.getWorld().equals(player.getWorld())) {
ORIGINS.remove(player);
return;
} else if (!bPlayer.canBendIgnoreCooldowns(getAbility("AirSuction"))) {
ORIGINS.remove(player);
return;
} else if (origin.distanceSquared(player.getEyeLocation()) > getSelectRange() * getSelectRange()) {
ORIGINS.remove(player);
return;
}
playAirbendingParticles(origin, getSelectParticles());
}
public static void progressOrigins() {
for (Player player : ORIGINS.keySet()) {
playOriginEffect(player);
}
}
public static void setOrigin(Player player) {
Location location = GeneralMethods.getTargetedLocation(player, getSelectRange(), getTransparentMaterials());
if (location.getBlock().isLiquid() || GeneralMethods.isSolid(location.getBlock())) {
return;
} else if (GeneralMethods.isRegionProtectedFromBuild(player, "AirSuction", location)) {
return;
} else {
ORIGINS.put(player, location);
}
}
private void advanceLocation() {
playAirbendingParticles(location, particleCount, 0.275F, 0.275F, 0.275F);
if (random.nextInt(4) == 0) {
playAirbendingSound(location);
}
double speedFactor = speed * (ProjectKorra.time_step / 1000.);
location = location.add(direction.clone().multiply(speedFactor));
}
private Location getLocation(Location origin, Vector direction) {
Location location = origin.clone();
for (double i = 1; i <= range; i++) {
location = origin.clone().add(direction.clone().multiply(i));
if (!isTransparent(location.getBlock()) || GeneralMethods.isRegionProtectedFromBuild(this, location)) {
return origin.clone().add(direction.clone().multiply(i - 1));
}
}
return location;
}
@Override
public void progress() {
if (player.isDead() || !player.isOnline()) {
remove();
return;
} else if (GeneralMethods.isRegionProtectedFromBuild(player, "AirSuction", location)) {
remove();
return;
}
ticks++;
if (ticks > MAX_TICKS) {
remove();
return;
} else if ((location.distanceSquared(origin) > range * range) || (location.distanceSquared(origin) <= 1)) {
remove();
return;
}
for (Entity entity : GeneralMethods.getEntitiesAroundPoint(location, radius)) {
if (entity.getEntityId() != player.getEntityId() || hasOtherOrigin) {
Vector velocity = entity.getVelocity();
double max = speed;
double factor = pushFactor;
Vector push = direction.clone();
if (Math.abs(push.getY()) > max && entity.getEntityId() != player.getEntityId()) {
if (push.getY() < 0) {
push.setY(-max);
} else {
push.setY(max);
}
}
if (location.getWorld().equals(origin.getWorld())) {
factor *= 1 - location.distance(origin) / (2 * range);
}
double comp = velocity.dot(push.clone().normalize());
if (comp > factor) {
velocity.multiply(.5);
velocity.add(push.clone().normalize().multiply(velocity.clone().dot(push.clone().normalize())));
} else if (comp + factor * .5 > factor) {
velocity.add(push.clone().multiply(factor - comp));
} else {
velocity.add(push.clone().multiply(factor * .5));
}
if (entity instanceof Player) {
if (Commands.invincible.contains(((Player) entity).getName())) {
continue;
}
}
GeneralMethods.setVelocity(entity, velocity);
new HorizontalVelocityTracker(entity, player, 200l, this);
entity.setFallDistance(0);
if (entity.getEntityId() != player.getEntityId() && entity instanceof Player) {
new Flight((Player) entity, player);
}
if (entity.getFireTicks() > 0) {
entity.getWorld().playEffect(entity.getLocation(), Effect.EXTINGUISH, 0);
}
entity.setFireTicks(0);
breakBreathbendingHold(entity);
}
}
advanceLocation();
}
/**
* This method was used for the old collision detection system. Please see
* {@link Collision} for the new system.
*/
@Deprecated
public static boolean removeAirSuctionsAroundPoint(Location location, double radius) {
boolean removed = false;
for (AirSuction airSuction : getAbilities(AirSuction.class)) {
Location airSuctionlocation = airSuction.location;
if (location.getWorld() == airSuctionlocation.getWorld()) {
if (location.distanceSquared(airSuctionlocation) <= radius * radius) {
airSuction.remove();
}
removed = true;
}
}
return removed;
}
@Override
public String getName() {
return "AirSuction";
}
@Override
public Location getLocation() {
return location;
}
@Override
public long getCooldown() {
return cooldown;
}
@Override
public boolean isSneakAbility() {
return true;
}
@Override
public boolean isHarmlessAbility() {
return false;
}
@Override
public double getCollisionRadius() {
return getRadius();
}
public Location getOrigin() {
return origin;
}
public void setOrigin(Location origin) {
this.origin = origin;
}
public Vector getDirection() {
return direction;
}
public void setDirection(Vector direction) {
this.direction = direction;
}
public boolean isHasOtherOrigin() {
return hasOtherOrigin;
}
public void setHasOtherOrigin(boolean hasOtherOrigin) {
this.hasOtherOrigin = hasOtherOrigin;
}
public int getTicks() {
return ticks;
}
public void setTicks(int ticks) {
this.ticks = ticks;
}
public int getParticleCount() {
return particleCount;
}
public void setParticleCount(int particleCount) {
this.particleCount = particleCount;
}
public double getSpeed() {
return speed;
}
public void setSpeed(double speed) {
this.speed = speed;
}
public double getRange() {
return range;
}
public void setRange(double range) {
this.range = range;
}
public double getRadius() {
return radius;
}
public void setRadius(double radius) {
this.radius = radius;
}
public double getPushFactor() {
return pushFactor;
}
public void setPushFactor(double pushFactor) {
this.pushFactor = pushFactor;
}
public void setLocation(Location location) {
this.location = location;
}
public void setCooldown(long cooldown) {
this.cooldown = cooldown;
}
public static Map<Player, Location> getOrigins() {
return ORIGINS;
}
public static int getSelectParticles() {
return getConfig().getInt("Abilities.Air.AirSuction.SelectParticles");
}
public static double getSelectRange() {
return getConfig().getDouble("Abilities.Air.AirSuction.SelectRange");
}
}