package com.projectkorra.projectkorra.waterbending.ice; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import java.util.Random; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; 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.GeneralMethods; import com.projectkorra.projectkorra.ability.AirAbility; import com.projectkorra.projectkorra.ability.ElementalAbility; import com.projectkorra.projectkorra.ability.IceAbility; import com.projectkorra.projectkorra.ability.WaterAbility; import com.projectkorra.projectkorra.attribute.Attribute; import com.projectkorra.projectkorra.util.DamageHandler; import com.projectkorra.projectkorra.util.TempBlock; import com.projectkorra.projectkorra.util.TempPotionEffect; public class IceSpikePillar extends IceAbility { /** The list of blocks IceSpike uses */ private final Map ice_blocks = new HashMap(); @Attribute(Attribute.HEIGHT) private int height; private int progress; @Attribute("SlowPotency") private int slowPower; @Attribute("Slow" + Attribute.DURATION) private int slowDuration; @Attribute(Attribute.COOLDOWN) private long cooldown; private long time; private long removeTimestamp; @Attribute(Attribute.DURATION) private long duration; private long interval; @Attribute("Slow" + Attribute.COOLDOWN) private long slowCooldown; @Attribute(Attribute.DAMAGE) private double damage; @Attribute(Attribute.RANGE) private double range; @Attribute(Attribute.SPEED) private double speed; private Block source_block; // The block clicked on. private Block base_block; // The block at the bottom of the pillar. private Location origin; private Location location; private Vector thrownForce; private Vector direction; private ArrayList damaged; protected boolean inField = false; // If it's part of a field or not. public IceSpikePillar(final Player player) { super(player); this.setFields(); if (this.bPlayer.isOnCooldown("IceSpikePillar")) { return; } try { double lowestDistance = this.range + 1; Entity closestEntity = null; for (final Entity entity : GeneralMethods.getEntitiesAroundPoint(player.getLocation(), this.range)) { if (GeneralMethods.getDistanceFromLine(player.getLocation().getDirection(), player.getLocation(), entity.getLocation()) <= 2 && (entity instanceof LivingEntity) && (entity.getEntityId() != player.getEntityId())) { double distance = 0; if (player.getWorld().equals(entity.getWorld())) { distance = player.getLocation().distance(entity.getLocation()); } if (distance < lowestDistance) { closestEntity = entity; lowestDistance = distance; } } } if (closestEntity != null) { final Block tempTestingBlock = closestEntity.getLocation().getBlock().getRelative(BlockFace.DOWN, 1); this.source_block = tempTestingBlock; } else { this.source_block = WaterAbility.getIceSourceBlock(player, this.range); if (this.source_block == null) { return; } } this.origin = this.source_block.getLocation(); this.location = this.origin.clone(); } catch (final IllegalStateException e) { return; } if (this.height != 0) { if (this.canInstantiate()) { this.start(); this.time = System.currentTimeMillis() - this.interval; this.bPlayer.addCooldown("IceSpikePillar", this.cooldown); } } } public IceSpikePillar(final Player player, final Location origin, final int damage, final Vector throwing, final long aoecooldown) { super(player); this.setFields(); this.cooldown = aoecooldown; this.player = player; this.origin = origin; this.damage = damage; this.thrownForce = throwing; this.location = origin.clone(); this.source_block = this.location.getBlock(); if (this.isIcebendable(this.source_block)) { if (this.canInstantiate()) { this.start(); this.time = System.currentTimeMillis() - this.interval; } } } private void setFields() { this.direction = new Vector(0, 1, 0); this.speed = getConfig().getDouble("Abilities.Water.IceSpike.Speed"); this.slowCooldown = getConfig().getLong("Abilities.Water.IceSpike.SlowCooldown"); this.slowPower = getConfig().getInt("Abilities.Water.IceSpike.SlowPower"); this.slowDuration = getConfig().getInt("Abilities.Water.IceSpike.SlowDuration"); this.damage = getConfig().getDouble("Abilities.Water.IceSpike.Damage"); this.range = getConfig().getDouble("Abilities.Water.IceSpike.Range"); this.cooldown = getConfig().getLong("Abilities.Water.IceSpike.Cooldown"); this.height = getConfig().getInt("Abilities.Water.IceSpike.Height"); this.thrownForce = new Vector(0, getConfig().getDouble("Abilities.Water.IceSpike.Push"), 0); this.damaged = new ArrayList<>(); this.interval = (long) (1000. / this.speed); if (this.bPlayer.isAvatarState()) { this.slowPower = getConfig().getInt("Abilities.Avatar.AvatarState.Water.IceSpike.SlowPower"); this.slowDuration = getConfig().getInt("Abilities.Avatar.AvatarState.Water.IceSpike.SlowDuration"); this.damage = getConfig().getDouble("Abilities.Avatar.AvatarState.Water.IceSpike.Damage"); this.range = getConfig().getDouble("Abilities.Avatar.AvatarState.Water.IceSpike.Range"); this.height = getConfig().getInt("Abilities.Avatar.AvatarState.Water.IceSpike.Height"); this.thrownForce = new Vector(0, getConfig().getDouble("Abilities.Avatar.AvatarState.Water.IceSpike.Push"), 0); } } /** * Reverts the block if it's part of IceSpike * * @param block The Block * @return If the block was removed or not */ public static boolean revertBlock(final Block block) { for (final IceSpikePillar iceSpike : getAbilities(IceSpikePillar.class)) { if (iceSpike.ice_blocks.containsKey(block)) { iceSpike.ice_blocks.get(block).revertBlock(); iceSpike.ice_blocks.remove(block); return true; } } return false; } /** * Checks to see if this move can start. Checks things like if there is * enough space to form, if the source isn't a TempBlock, etc. */ private boolean canInstantiate() { if (!this.isIcebendable(this.source_block.getType())) { return false; } Block b; for (int i = 1; i <= this.height; i++) { b = this.source_block.getWorld().getBlockAt(this.location.clone().add(this.direction.clone().multiply(i))); if (!ElementalAbility.isAir(b.getType())) { return false; } if (b.getX() == this.player.getEyeLocation().getBlock().getX() && b.getZ() == this.player.getEyeLocation().getBlock().getZ()) { return false; } } return true; } @Override public void progress() { if (System.currentTimeMillis() - this.time >= this.interval) { this.time = System.currentTimeMillis(); if (this.progress < this.height) { this.risePillar(); this.removeTimestamp = System.currentTimeMillis(); } else { // If it's time to remove. if (this.removeTimestamp != 0 && this.removeTimestamp + this.duration <= System.currentTimeMillis()) { if (!this.sinkPillar()) { this.remove(); return; } } } } } /** * Makes the pillar rise by 1 block. * * @return If the block was placed successfully. */ private boolean risePillar() { this.progress++; final Block affectedBlock = this.location.clone().add(this.direction).getBlock(); this.location = this.location.add(this.direction); if (GeneralMethods.isRegionProtectedFromBuild(this, this.location)) { return false; } for (final Entity en : GeneralMethods.getEntitiesAroundPoint(this.location, 1.4)) { if (en instanceof LivingEntity && en != this.player && !this.damaged.contains((en))) { final LivingEntity le = (LivingEntity) en; this.affect(le); } } final TempBlock b = new TempBlock(affectedBlock, Material.ICE); this.ice_blocks.put(affectedBlock, b); if (!this.inField || new Random().nextInt((int) ((this.height + 1) * 1.5)) == 0) { playIcebendingSound(this.source_block.getLocation()); } return true; } private void affect(final LivingEntity entity) { GeneralMethods.setVelocity(this, entity, this.thrownForce); DamageHandler.damageEntity(entity, this.damage, this); this.damaged.add(entity); if (entity instanceof Player) { if (this.bPlayer.canBeSlowed()) { final PotionEffect effect = new PotionEffect(PotionEffectType.SLOW, this.slowDuration, this.slowPower); new TempPotionEffect(entity, effect); this.bPlayer.slow(this.slowCooldown); } } else { final PotionEffect effect = new PotionEffect(PotionEffectType.SLOW, this.slowDuration, this.slowPower); new TempPotionEffect(entity, effect); } AirAbility.breakBreathbendingHold(entity); } /** * The reverse of risePillar(). Makes the pillar sink * * @return If the move should continue progressing. */ public boolean sinkPillar() { final Vector direction = this.direction.clone().multiply(-1); if (this.ice_blocks.containsKey(this.location.getBlock())) { this.ice_blocks.get(this.location.getBlock()).revertBlock(); this.ice_blocks.remove(this.location.getBlock()); this.location.add(direction); if (this.source_block.equals(this.location.getBlock())) { return false; } } return true; } @Override public String getName() { return "IceSpike"; } @Override public boolean isSneakAbility() { return true; } @Override public boolean isHarmlessAbility() { return false; } public int getHeight() { return this.height; } public void setHeight(final int height) { this.height = height; } public int getProgress() { return this.progress; } public void setProgress(final int progress) { this.progress = progress; } public int getSlowPower() { return this.slowPower; } public void setSlowPower(final int slowPower) { this.slowPower = slowPower; } public int getSlowDuration() { return this.slowDuration; } public void setSlowDuration(final int slowDuration) { this.slowDuration = slowDuration; } @Override public long getCooldown() { return this.cooldown; } public void setCooldown(final long cooldown) { this.cooldown = cooldown; } public long getTime() { return this.time; } public void setTime(final long time) { this.time = time; } public long getRemoveTimestamp() { return this.removeTimestamp; } public void setRemoveTimestamp(final long removeTimestamp) { this.removeTimestamp = removeTimestamp; } public long getDuration() { return this.duration; } public void setDuration(final long duration) { this.duration = duration; } public long getInterval() { return this.interval; } public void setInterval(final long interval) { this.interval = interval; } public long getSlowCooldown() { return this.slowCooldown; } public void setSlowCooldown(final long slowCooldown) { this.slowCooldown = slowCooldown; } public double getDamage() { return this.damage; } public void setDamage(final double damage) { this.damage = damage; } public double getRange() { return this.range; } public void setRange(final double range) { this.range = range; } public double getSpeed() { return this.speed; } public void setSpeed(final double speed) { this.speed = speed; } public Block getBlock() { return this.source_block; } public void setBlock(final Block block) { this.source_block = block; } public Location getOrigin() { return this.origin; } public void setOrigin(final Location origin) { this.origin = origin; } @Override public Location getLocation() { return this.location; } public void setLocation(final Location location) { this.location = location; } public Vector getThrownForce() { return this.thrownForce; } public void setThrownForce(final Vector thrownForce) { this.thrownForce = thrownForce; } public Vector getDirection() { return this.direction; } public void setDirection(final Vector direction) { this.direction = direction; } public Map getIceBlocks() { return this.ice_blocks; } public Block getBaseBlock() { return this.base_block; } }