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.
445 lines
13 KiB
Java
445 lines
13 KiB
Java
package com.projectkorra.projectkorra.waterbending.blood;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.concurrent.ConcurrentHashMap;
|
|
import java.util.concurrent.CopyOnWriteArrayList;
|
|
|
|
import org.bukkit.Location;
|
|
import org.bukkit.entity.Creature;
|
|
import org.bukkit.entity.Entity;
|
|
import org.bukkit.entity.LivingEntity;
|
|
import org.bukkit.entity.Player;
|
|
import org.bukkit.potion.PotionEffect;
|
|
import org.bukkit.potion.PotionEffectType;
|
|
import org.bukkit.util.Vector;
|
|
|
|
import com.projectkorra.projectkorra.BendingPlayer;
|
|
import com.projectkorra.projectkorra.GeneralMethods;
|
|
import com.projectkorra.projectkorra.ability.AirAbility;
|
|
import com.projectkorra.projectkorra.ability.BloodAbility;
|
|
import com.projectkorra.projectkorra.avatar.AvatarState;
|
|
import com.projectkorra.projectkorra.object.HorizontalVelocityTracker;
|
|
import com.projectkorra.projectkorra.util.DamageHandler;
|
|
import com.projectkorra.projectkorra.util.TempPotionEffect;
|
|
|
|
public class Bloodbending extends BloodAbility {
|
|
|
|
private static final Map<Entity, Player> TARGETED_ENTITIES = new ConcurrentHashMap<Entity, Player>();
|
|
|
|
private boolean canOnlyBeUsedAtNight;
|
|
private boolean canBeUsedOnUndeadMobs;
|
|
private boolean onlyUsableDuringMoon;
|
|
private boolean canBloodbendOtherBloodbenders;
|
|
private int range;
|
|
private long time;
|
|
private long holdTime;
|
|
private long cooldown;
|
|
private double throwFactor;
|
|
private Entity target;
|
|
private Vector vector;
|
|
|
|
public Bloodbending(Player player) {
|
|
super(player);
|
|
|
|
Bloodbending ability = getAbility(player, Bloodbending.class);
|
|
if (ability != null) {
|
|
ability.remove();
|
|
return;
|
|
}
|
|
|
|
this.canOnlyBeUsedAtNight = getConfig().getBoolean("Abilities.Water.Bloodbending.CanOnlyBeUsedAtNight");
|
|
this.canBeUsedOnUndeadMobs = getConfig().getBoolean("Abilities.Water.Bloodbending.CanBeUsedOnUndeadMobs");
|
|
this.onlyUsableDuringMoon = getConfig().getBoolean("Abilities.Water.Bloodbending.CanOnlyBeUsedDuringFullMoon");
|
|
this.canBloodbendOtherBloodbenders = getConfig().getBoolean("Abilities.Water.Bloodbending.CanBloodbendOtherBloodbenders");
|
|
this.range = getConfig().getInt("Abilities.Water.Bloodbending.Range");
|
|
this.holdTime = getConfig().getInt("Abilities.Water.Bloodbending.HoldTime");
|
|
this.cooldown = getConfig().getInt("Abilities.Water.Bloodbending.Cooldown");
|
|
this.throwFactor = getConfig().getDouble("Abilities.Water.Bloodbending.ThrowFactor");
|
|
vector = new Vector(0, 0, 0);
|
|
|
|
if (canOnlyBeUsedAtNight && !isNight(player.getWorld()) && !bPlayer.canBloodbendAtAnytime()) {
|
|
return;
|
|
} else if (onlyUsableDuringMoon && !isFullMoon(player.getWorld()) && !bPlayer.canBloodbendAtAnytime()) {
|
|
return;
|
|
} else if (!bPlayer.canBend(this) && !bPlayer.isAvatarState()) {
|
|
return;
|
|
}
|
|
|
|
range = (int) getNightFactor(range, player.getWorld());
|
|
if (bPlayer.isAvatarState()) {
|
|
range += AvatarState.getValue(1.5);
|
|
for (Entity entity : GeneralMethods.getEntitiesAroundPoint(player.getLocation(), range)) {
|
|
if (entity instanceof LivingEntity) {
|
|
if (entity instanceof Player) {
|
|
Player enemyPlayer = (Player) entity;
|
|
BendingPlayer enemyBPlayer = BendingPlayer.getBendingPlayer(enemyPlayer);
|
|
if (enemyBPlayer == null || GeneralMethods.isRegionProtectedFromBuild(this, entity.getLocation()) || enemyBPlayer.isAvatarState() || entity.getEntityId() == player.getEntityId() || enemyBPlayer.canBendIgnoreBindsCooldowns(this)) {
|
|
continue;
|
|
}
|
|
}
|
|
DamageHandler.damageEntity(entity, 0, this);
|
|
AirAbility.breakBreathbendingHold(entity);
|
|
TARGETED_ENTITIES.put(entity, player);
|
|
}
|
|
}
|
|
} else {
|
|
//Location location = GeneralMethods.getTargetedLocation(player, 6, getTransparentMaterial());
|
|
//List<Entity> entities = GeneralMethods.getEntitiesAroundPoint(location, 1.5);
|
|
List<Entity> entities = new CopyOnWriteArrayList<Entity>();
|
|
for (int i = 0; i < range; i++) {
|
|
Location location = GeneralMethods.getTargetedLocation(player, i, getTransparentMaterials());
|
|
entities = GeneralMethods.getEntitiesAroundPoint(location, 1.7);
|
|
if (entities.contains(player))
|
|
entities.remove(player);
|
|
for (Entity e : entities) {
|
|
if (!(e instanceof LivingEntity)) {
|
|
entities.remove(e);
|
|
}
|
|
}
|
|
if (entities != null && !entities.isEmpty() && !entities.contains(player)) {
|
|
break;
|
|
}
|
|
}
|
|
if (entities == null || entities.isEmpty()) {
|
|
return;
|
|
}
|
|
target = entities.get(0);
|
|
|
|
if (target == null || !(target instanceof LivingEntity) || GeneralMethods.isRegionProtectedFromBuild(this, target.getLocation()) || target.getEntityId() == player.getEntityId()) {
|
|
return;
|
|
} else if (target instanceof Player) {
|
|
BendingPlayer targetBPlayer = BendingPlayer.getBendingPlayer((Player) target);
|
|
if (targetBPlayer != null) {
|
|
if (targetBPlayer.canBloodbend() && !canBloodbendOtherBloodbenders) {
|
|
return;
|
|
} else if (targetBPlayer.isAvatarState()) {
|
|
return;
|
|
} else if (targetBPlayer.canBloodbendAtAnytime() && !canBloodbendOtherBloodbenders) {
|
|
return;
|
|
}
|
|
}
|
|
} else if (!canBeUsedOnUndeadMobs && isUndead(target)) {
|
|
return;
|
|
}
|
|
|
|
DamageHandler.damageEntity(target, 0, this);
|
|
HorizontalVelocityTracker.remove(target);
|
|
AirAbility.breakBreathbendingHold(target);
|
|
TARGETED_ENTITIES.put(target, player);
|
|
}
|
|
|
|
this.time = System.currentTimeMillis();
|
|
start();
|
|
}
|
|
|
|
public static void launch(Player player) {
|
|
Bloodbending bloodbending = getAbility(player, Bloodbending.class);
|
|
if (bloodbending != null) {
|
|
bloodbending.launch();
|
|
}
|
|
}
|
|
|
|
private void launch() {
|
|
Location location = player.getLocation();
|
|
for (Entity entity : TARGETED_ENTITIES.keySet()) {
|
|
Location target = entity.getLocation().clone();
|
|
Vector vector = new Vector(0, 0, 0);
|
|
if (location.getWorld().equals(target.getWorld())) {
|
|
vector = GeneralMethods.getDirection(location, GeneralMethods.getTargetedLocation(player, location.distance(target)));
|
|
}
|
|
vector.normalize();
|
|
entity.setVelocity(vector.multiply(throwFactor));
|
|
new HorizontalVelocityTracker(entity, player, 200, this);
|
|
}
|
|
remove();
|
|
bPlayer.addCooldown(this);
|
|
}
|
|
|
|
@Override
|
|
public void progress() {
|
|
PotionEffect effect = new PotionEffect(PotionEffectType.SLOW, 60, 1);
|
|
|
|
if (!player.isSneaking()) {
|
|
remove();
|
|
return;
|
|
} else if (holdTime > 0 && System.currentTimeMillis() - this.time > holdTime) {
|
|
remove();
|
|
bPlayer.addCooldown(this);
|
|
return;
|
|
}
|
|
|
|
if (!canBeUsedOnUndeadMobs) {
|
|
for (Entity entity : TARGETED_ENTITIES.keySet()) {
|
|
if (isUndead(entity)) {
|
|
TARGETED_ENTITIES.remove(entity);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (onlyUsableDuringMoon && !isFullMoon(player.getWorld()) && !bPlayer.canBloodbendAtAnytime()) {
|
|
remove();
|
|
return;
|
|
} else if (canOnlyBeUsedAtNight && !isNight(player.getWorld()) && !bPlayer.canBloodbendAtAnytime()) {
|
|
remove();
|
|
return;
|
|
} else if (!bPlayer.canBendIgnoreCooldowns(this)) {
|
|
remove();
|
|
return;
|
|
}
|
|
|
|
if (bPlayer.isAvatarState()) {
|
|
ArrayList<Entity> entities = new ArrayList<>();
|
|
|
|
for (Entity entity : GeneralMethods.getEntitiesAroundPoint(player.getLocation(), range)) {
|
|
if (GeneralMethods.isRegionProtectedFromBuild(this, entity.getLocation())) {
|
|
continue;
|
|
} else if (!(entity instanceof LivingEntity)) {
|
|
continue;
|
|
} else if (entity instanceof Player) {
|
|
BendingPlayer targetBPlayer = BendingPlayer.getBendingPlayer((Player) entity);
|
|
if (targetBPlayer != null) {
|
|
if (!targetBPlayer.canBeBloodbent() || entity.getEntityId() == player.getEntityId()) {
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
entities.add(entity);
|
|
if (!TARGETED_ENTITIES.containsKey(entity) && entity instanceof LivingEntity) {
|
|
DamageHandler.damageEntity(entity, 0, this);
|
|
TARGETED_ENTITIES.put(entity, player);
|
|
}
|
|
|
|
if (player.getWorld() != entity.getLocation().getWorld()) {
|
|
TARGETED_ENTITIES.remove(entity);
|
|
continue;
|
|
}
|
|
if (entity instanceof LivingEntity) {
|
|
entity.setVelocity(vector);
|
|
new TempPotionEffect((LivingEntity) entity, effect);
|
|
entity.setFallDistance(0);
|
|
if (entity instanceof Creature) {
|
|
((Creature) entity).setTarget(null);
|
|
}
|
|
AirAbility.breakBreathbendingHold(entity);
|
|
}
|
|
}
|
|
|
|
for (Entity entity : TARGETED_ENTITIES.keySet()) {
|
|
if (!entities.contains(entity) && TARGETED_ENTITIES.get(entity) == player) {
|
|
TARGETED_ENTITIES.remove(entity);
|
|
}
|
|
}
|
|
} else {
|
|
for (Entity entity : TARGETED_ENTITIES.keySet()) {
|
|
if (entity instanceof Player) {
|
|
BendingPlayer targetBPlayer = BendingPlayer.getBendingPlayer((Player) entity);
|
|
if (targetBPlayer != null && !targetBPlayer.canBeBloodbent()) {
|
|
TARGETED_ENTITIES.remove(entity);
|
|
continue;
|
|
} else if (targetBPlayer.isAvatarState()) {
|
|
TARGETED_ENTITIES.remove(entity);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
Location newLocation = entity.getLocation();
|
|
if (player.getWorld() != newLocation.getWorld()) {
|
|
TARGETED_ENTITIES.remove(entity);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (!TARGETED_ENTITIES.containsKey(target)) {
|
|
remove();
|
|
return;
|
|
}
|
|
|
|
if (TARGETED_ENTITIES.get(target) != player) {
|
|
remove();
|
|
return;
|
|
}
|
|
|
|
Location location = GeneralMethods.getTargetedLocation(player, 6, getTransparentMaterials());
|
|
double distance = 0;
|
|
if (location.getWorld().equals(target.getWorld())) {
|
|
distance = location.distance(target.getLocation());
|
|
}
|
|
double dx, dy, dz;
|
|
dx = location.getX() - target.getLocation().getX();
|
|
dy = location.getY() - target.getLocation().getY();
|
|
dz = location.getZ() - target.getLocation().getZ();
|
|
vector = new Vector(dx, dy, dz);
|
|
vector.normalize().multiply(.5);
|
|
|
|
if (distance < .6) {
|
|
vector = new Vector(0, 0, 0);
|
|
}
|
|
|
|
target.setVelocity(vector);
|
|
|
|
new TempPotionEffect((LivingEntity) target, effect);
|
|
target.setFallDistance(0);
|
|
if (target instanceof Creature) {
|
|
((Creature) target).setTarget(null);
|
|
}
|
|
AirAbility.breakBreathbendingHold(target);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void remove() {
|
|
if (!bPlayer.isAvatarState() && target != null) {
|
|
if (System.currentTimeMillis() < getStartTime() + 1200) {
|
|
bPlayer.addCooldown(this); //Prevents spamming
|
|
}
|
|
}
|
|
for (Entity e : TARGETED_ENTITIES.keySet()) {
|
|
if (TARGETED_ENTITIES.get(e) == player) {
|
|
TARGETED_ENTITIES.remove(e);
|
|
}
|
|
}
|
|
|
|
super.remove();
|
|
}
|
|
|
|
public static boolean isBloodbent(Entity entity) {
|
|
return entity != null ? TARGETED_ENTITIES.containsKey(entity) : null;
|
|
}
|
|
|
|
public static Location getBloodbendingLocation(Entity entity) {
|
|
return entity != null ? TARGETED_ENTITIES.get(entity).getLocation() : null;
|
|
}
|
|
|
|
public static Vector getBloodbendingVector(Entity entity) {
|
|
if (!TARGETED_ENTITIES.containsKey(entity)) {
|
|
return null;
|
|
}
|
|
|
|
Bloodbending bb = getAbility(TARGETED_ENTITIES.get(entity), Bloodbending.class);
|
|
return bb.vector;
|
|
}
|
|
|
|
public static BendingPlayer getBloodbender(Entity entity) {
|
|
if (!TARGETED_ENTITIES.containsKey(entity)) {
|
|
return null;
|
|
}
|
|
|
|
Bloodbending bb = getAbility(TARGETED_ENTITIES.get(entity), Bloodbending.class);
|
|
return bb.getBendingPlayer();
|
|
}
|
|
|
|
@Override
|
|
public String getName() {
|
|
return "Bloodbending";
|
|
}
|
|
|
|
@Override
|
|
public Location getLocation() {
|
|
if (target != null) {
|
|
return target.getLocation();
|
|
}
|
|
return player != null ? player.getLocation() : null;
|
|
}
|
|
|
|
@Override
|
|
public long getCooldown() {
|
|
return cooldown;
|
|
}
|
|
|
|
@Override
|
|
public boolean isSneakAbility() {
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public boolean isHarmlessAbility() {
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public List<Location> getLocations() {
|
|
// for collision purposes we only care about the player's location
|
|
ArrayList<Location> locations = new ArrayList<>();
|
|
if (player != null) {
|
|
locations.add(player.getLocation());
|
|
}
|
|
return locations;
|
|
}
|
|
|
|
public boolean isCanOnlyBeUsedAtNight() {
|
|
return canOnlyBeUsedAtNight;
|
|
}
|
|
|
|
public void setCanOnlyBeUsedAtNight(boolean canOnlyBeUsedAtNight) {
|
|
this.canOnlyBeUsedAtNight = canOnlyBeUsedAtNight;
|
|
}
|
|
|
|
public boolean isCanBeUsedOnUndeadMobs() {
|
|
return canBeUsedOnUndeadMobs;
|
|
}
|
|
|
|
public void setCanBeUsedOnUndeadMobs(boolean canBeUsedOnUndeadMobs) {
|
|
this.canBeUsedOnUndeadMobs = canBeUsedOnUndeadMobs;
|
|
}
|
|
|
|
public boolean isOnlyUsableDuringMoon() {
|
|
return onlyUsableDuringMoon;
|
|
}
|
|
|
|
public void setOnlyUsableDuringMoon(boolean onlyUsableDuringMoon) {
|
|
this.onlyUsableDuringMoon = onlyUsableDuringMoon;
|
|
}
|
|
|
|
public boolean isCanBloodbendOtherBloodbenders() {
|
|
return canBloodbendOtherBloodbenders;
|
|
}
|
|
|
|
public void setCanBloodbendOtherBloodbenders(boolean canBloodbendOtherBloodbenders) {
|
|
this.canBloodbendOtherBloodbenders = canBloodbendOtherBloodbenders;
|
|
}
|
|
|
|
public int getRange() {
|
|
return range;
|
|
}
|
|
|
|
public void setRange(int range) {
|
|
this.range = range;
|
|
}
|
|
|
|
public long getTime() {
|
|
return time;
|
|
}
|
|
|
|
public void setTime(long time) {
|
|
this.time = time;
|
|
}
|
|
|
|
public long getHoldTime() {
|
|
return holdTime;
|
|
}
|
|
|
|
public void setHoldTime(long holdTime) {
|
|
this.holdTime = holdTime;
|
|
}
|
|
|
|
public double getThrowFactor() {
|
|
return throwFactor;
|
|
}
|
|
|
|
public void setThrowFactor(double throwFactor) {
|
|
this.throwFactor = throwFactor;
|
|
}
|
|
|
|
public Entity getTarget() {
|
|
return target;
|
|
}
|
|
|
|
public void setCooldown(long cooldown) {
|
|
this.cooldown = cooldown;
|
|
}
|
|
|
|
}
|