mirror of
https://github.com/TotalFreedomMC/TF-ProjectKorra.git
synced 2024-05-15 17:31:26 +00:00
102112ffdd
## 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.
365 lines
9.9 KiB
Java
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");
|
|
}
|
|
|
|
}
|