mirror of
https://github.com/TotalFreedomMC/TF-ProjectKorra.git
synced 2024-05-31 17:31:24 +00:00
10ac727eda
Update some deprecated calls ## Fixes * Fixes the server loading the legacy `Material` API when a player used `TremorSense` ## Changes * Added boundaries to `Levelled` block data in `GeneralMethods::getWaterData` and `GeneralMethods::getLavaData` to avoid future bugs
463 lines
13 KiB
Java
463 lines
13 KiB
Java
package com.projectkorra.projectkorra.waterbending;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Random;
|
|
import java.util.concurrent.ConcurrentHashMap;
|
|
|
|
import org.bukkit.Location;
|
|
import org.bukkit.Material;
|
|
import org.bukkit.block.Block;
|
|
import org.bukkit.entity.Player;
|
|
import org.bukkit.potion.PotionEffectType;
|
|
|
|
import com.projectkorra.projectkorra.GeneralMethods;
|
|
import com.projectkorra.projectkorra.ability.ElementalAbility;
|
|
import com.projectkorra.projectkorra.ability.WaterAbility;
|
|
import com.projectkorra.projectkorra.ability.util.Collision;
|
|
import com.projectkorra.projectkorra.attribute.Attribute;
|
|
import com.projectkorra.projectkorra.util.ParticleEffect;
|
|
import com.projectkorra.projectkorra.util.TempBlock;
|
|
|
|
public class WaterSpout extends WaterAbility {
|
|
|
|
private static final Map<Block, Block> AFFECTED_BLOCKS = new ConcurrentHashMap<Block, Block>();
|
|
private final List<TempBlock> blocks = new ArrayList<TempBlock>();
|
|
|
|
@Attribute("CanBendOnPackedIce")
|
|
private boolean canBendOnPackedIce;
|
|
private boolean useParticles;
|
|
private boolean useBlockSpiral;
|
|
private int angle;
|
|
private long time;
|
|
private long interval;
|
|
@Attribute(Attribute.COOLDOWN)
|
|
private long cooldown;
|
|
@Attribute(Attribute.DURATION)
|
|
private long duration;
|
|
private long startTime;
|
|
private double rotation;
|
|
private double height;
|
|
@Attribute(Attribute.HEIGHT)
|
|
private double maxHeight;
|
|
private Block base;
|
|
private TempBlock baseBlock;
|
|
|
|
public WaterSpout(final Player player) {
|
|
super(player);
|
|
|
|
final WaterSpout oldSpout = getAbility(player, WaterSpout.class);
|
|
if (oldSpout != null) {
|
|
oldSpout.remove();
|
|
return;
|
|
}
|
|
|
|
this.canBendOnPackedIce = getConfig().getStringList("Properties.Water.IceBlocks").contains(Material.PACKED_ICE.toString());
|
|
this.useParticles = getConfig().getBoolean("Abilities.Water.WaterSpout.Particles");
|
|
this.useBlockSpiral = getConfig().getBoolean("Abilities.Water.WaterSpout.BlockSpiral");
|
|
this.cooldown = getConfig().getLong("Abilities.Water.WaterSpout.Cooldown");
|
|
this.height = getConfig().getDouble("Abilities.Water.WaterSpout.Height");
|
|
this.interval = getConfig().getLong("Abilities.Water.WaterSpout.Interval");
|
|
this.duration = getConfig().getLong("Abilities.Water.WaterSpout.Duration");
|
|
this.startTime = System.currentTimeMillis();
|
|
|
|
this.maxHeight = this.getNightFactor(this.height);
|
|
final WaterSpoutWave spoutWave = new WaterSpoutWave(player, WaterSpoutWave.AbilityType.CLICK);
|
|
if (spoutWave.isStarted() && !spoutWave.isRemoved()) {
|
|
return;
|
|
}
|
|
|
|
Block topBlock = GeneralMethods.getTopBlock(player.getLocation(), (int) -this.getNightFactor(this.height), (int) -this.getNightFactor(this.height));
|
|
if (topBlock == null) {
|
|
topBlock = player.getLocation().getBlock();
|
|
}
|
|
|
|
if (!isWater(topBlock) && !this.isIcebendable(topBlock) && !isSnow(topBlock)) {
|
|
return;
|
|
} else if (topBlock.getType() == Material.PACKED_ICE && !this.canBendOnPackedIce) {
|
|
return;
|
|
}
|
|
|
|
final double heightRemoveThreshold = 2;
|
|
if (!this.isWithinMaxSpoutHeight(topBlock.getLocation(), heightRemoveThreshold)) {
|
|
return;
|
|
}
|
|
this.flightHandler.createInstance(player, this.getName());
|
|
player.setAllowFlight(true);
|
|
this.spoutableWaterHeight(player.getLocation()); // Sets base.
|
|
this.start();
|
|
}
|
|
|
|
private void displayWaterSpiral(final Location location) {
|
|
if (!this.useBlockSpiral) {
|
|
return;
|
|
}
|
|
|
|
final double maxHeight = this.player.getLocation().getY() - location.getY() - .5;
|
|
double height = 0;
|
|
this.rotation += .4;
|
|
int i = 0;
|
|
|
|
while (height < maxHeight) {
|
|
i += 20;
|
|
height += .4;
|
|
final double angle = (i * Math.PI / 180);
|
|
final double x = 1 * Math.cos(angle + this.rotation);
|
|
final double z = 1 * Math.sin(angle + this.rotation);
|
|
|
|
final Location loc = location.clone().getBlock().getLocation().add(.5, .5, .5);
|
|
loc.add(x, height, z);
|
|
|
|
final Block block = loc.getBlock();
|
|
if ((!TempBlock.isTempBlock(block)) && (ElementalAbility.isAir(block.getType()) || !GeneralMethods.isSolid(block))) {
|
|
this.blocks.add(new TempBlock(block, GeneralMethods.getWaterData(7)));
|
|
AFFECTED_BLOCKS.put(block, block);
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void progress() {
|
|
for (final TempBlock tb : this.blocks) {
|
|
AFFECTED_BLOCKS.remove(tb.getBlock());
|
|
tb.revertBlock();
|
|
}
|
|
if (this.player.isDead() || !this.player.isOnline() || !this.bPlayer.canBendIgnoreBindsCooldowns(this)) {
|
|
this.remove();
|
|
return;
|
|
} else if (this.duration != 0 && System.currentTimeMillis() > this.startTime + this.duration) {
|
|
this.bPlayer.addCooldown(this);
|
|
this.remove();
|
|
return;
|
|
} else {
|
|
this.blocks.clear();
|
|
this.player.setFallDistance(0);
|
|
this.player.setSprinting(false);
|
|
if ((new Random()).nextInt(10) == 0) {
|
|
playWaterbendingSound(this.player.getLocation());
|
|
}
|
|
|
|
this.player.removePotionEffect(PotionEffectType.SPEED);
|
|
|
|
Location location = this.player.getLocation().clone().add(0, .2, 0);
|
|
Block block = location.clone().getBlock();
|
|
final double height = this.spoutableWaterHeight(location);
|
|
|
|
if (height != -1) {
|
|
location = this.base.getLocation();
|
|
final double heightRemoveThreshold = 2;
|
|
if (!this.isWithinMaxSpoutHeight(location, heightRemoveThreshold)) {
|
|
this.bPlayer.addCooldown(this);
|
|
this.remove();
|
|
return;
|
|
}
|
|
for (int i = 1; i <= height; i++) {
|
|
|
|
block = location.clone().add(0, i, 0).getBlock();
|
|
|
|
if (!TempBlock.isTempBlock(block)) {
|
|
this.blocks.add(new TempBlock(block, Material.WATER));
|
|
AFFECTED_BLOCKS.put(block, block);
|
|
}
|
|
this.rotateParticles(block);
|
|
}
|
|
|
|
this.displayWaterSpiral(location.clone().add(.5, 0, .5));
|
|
if (this.player.getLocation().getBlockY() > block.getY()) {
|
|
if (this.player.isFlying()) {
|
|
this.player.setFlying(false);
|
|
}
|
|
} else {
|
|
if (!this.player.isFlying()) {
|
|
this.player.setAllowFlight(true);
|
|
this.player.setFlying(true);
|
|
}
|
|
}
|
|
} else {
|
|
this.bPlayer.addCooldown(this);
|
|
this.remove();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void remove() {
|
|
super.remove();
|
|
this.revertBaseBlock();
|
|
for (final TempBlock tb : this.blocks) {
|
|
AFFECTED_BLOCKS.remove(tb.getBlock());
|
|
tb.revertBlock();
|
|
}
|
|
this.flightHandler.removeInstance(this.player, this.getName());
|
|
}
|
|
|
|
public void revertBaseBlock() {
|
|
if (this.baseBlock != null) {
|
|
this.baseBlock.revertBlock();
|
|
this.baseBlock = null;
|
|
}
|
|
}
|
|
|
|
private boolean isWithinMaxSpoutHeight(final Location baseBlockLocation, final double threshold) {
|
|
if (baseBlockLocation == null) {
|
|
return false;
|
|
}
|
|
final double playerHeight = this.player.getLocation().getY();
|
|
if (playerHeight > baseBlockLocation.getY() + this.maxHeight + threshold) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public void rotateParticles(final Block block) {
|
|
if (!this.useParticles) {
|
|
return;
|
|
}
|
|
|
|
if (System.currentTimeMillis() >= this.time + this.interval) {
|
|
this.time = System.currentTimeMillis();
|
|
|
|
Location location = block.getLocation();
|
|
final Location playerLoc = this.player.getLocation();
|
|
|
|
location = new Location(location.getWorld(), playerLoc.getX(), location.getY(), playerLoc.getZ());
|
|
|
|
double dy = playerLoc.getY() - block.getY();
|
|
if (dy > this.height) {
|
|
dy = this.height;
|
|
}
|
|
|
|
final double[] directions = { -0.5, 0.325, 0.25, 0.125, 0.0, 0.125, 0.25, 0.325, 0.5 };
|
|
int index = this.angle;
|
|
this.angle++;
|
|
if (this.angle >= directions.length) {
|
|
this.angle = 0;
|
|
}
|
|
for (int i = 1; i <= dy; i++) {
|
|
index += 1;
|
|
if (index >= directions.length) {
|
|
index = 0;
|
|
}
|
|
|
|
final Location effectLoc2 = new Location(location.getWorld(), location.getX(), block.getY() + i, location.getZ());
|
|
ParticleEffect.WATER_SPLASH.display(effectLoc2, 5, directions[index], directions[index], directions[index]);
|
|
}
|
|
}
|
|
}
|
|
|
|
private double spoutableWaterHeight(final Location location) {
|
|
double newHeight = this.height;
|
|
if (isNight(this.player.getWorld())) {
|
|
newHeight = this.getNightFactor(newHeight);
|
|
}
|
|
|
|
this.maxHeight = newHeight + 5;
|
|
Block blocki;
|
|
|
|
for (int i = 0; i < this.maxHeight; i++) {
|
|
|
|
blocki = location.clone().add(0, -i, 0).getBlock();
|
|
if (GeneralMethods.isRegionProtectedFromBuild(this, blocki.getLocation())) {
|
|
return -1;
|
|
}
|
|
|
|
if (TempBlock.get(blocki) == null || !this.blocks.contains(TempBlock.get(blocki))) {
|
|
if (isWater(blocki)) {
|
|
if (!TempBlock.isTempBlock(blocki)) {
|
|
this.revertBaseBlock();
|
|
}
|
|
|
|
this.base = blocki;
|
|
if (i > newHeight) {
|
|
return newHeight;
|
|
}
|
|
return i;
|
|
}
|
|
|
|
if (this.isIcebendable(blocki) || isSnow(blocki)) {
|
|
if (this.isIcebendable(blocki)) {
|
|
if (blocki.getType() == Material.PACKED_ICE && !this.canBendOnPackedIce) {
|
|
this.remove();
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (!TempBlock.isTempBlock(blocki)) {
|
|
this.revertBaseBlock();
|
|
this.baseBlock = new TempBlock(blocki, Material.WATER);
|
|
}
|
|
|
|
this.base = blocki;
|
|
if (i > newHeight) {
|
|
return newHeight;
|
|
}
|
|
return i;
|
|
}
|
|
|
|
if ((!ElementalAbility.isAir(blocki.getType()) && (!isPlant(blocki) || !this.bPlayer.canPlantbend()))) {
|
|
this.revertBaseBlock();
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
* This method was used for the old collision detection system. Please see
|
|
* {@link Collision} for the new system.
|
|
*/
|
|
@Deprecated
|
|
public static boolean removeSpouts(final Location loc0, final double radius, final Player sourcePlayer) {
|
|
boolean removed = false;
|
|
for (final WaterSpout spout : getAbilities(WaterSpout.class)) {
|
|
if (!spout.player.equals(sourcePlayer)) {
|
|
final Location top = spout.getLocation();
|
|
final Location base = spout.getBase().getLocation();
|
|
final double dist = top.getBlockY() - base.getBlockY();
|
|
for (double d = 0; d <= dist; d += 0.5) {
|
|
final Location spoutLoc = base.clone().add(0, d, 0);
|
|
if (loc0.getWorld().equals(spoutLoc.getWorld()) && loc0.distance(spoutLoc) <= radius) {
|
|
removed = true;
|
|
spout.remove();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return removed;
|
|
}
|
|
|
|
@Override
|
|
public String getName() {
|
|
return "WaterSpout";
|
|
}
|
|
|
|
@Override
|
|
public Location getLocation() {
|
|
return this.player != null ? this.player.getLocation() : null;
|
|
}
|
|
|
|
@Override
|
|
public long getCooldown() {
|
|
return this.cooldown;
|
|
}
|
|
|
|
@Override
|
|
public boolean isSneakAbility() {
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public boolean isHarmlessAbility() {
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public boolean isCollidable() {
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public List<Location> getLocations() {
|
|
if (this.getBase() == null) {
|
|
return new ArrayList<>();
|
|
}
|
|
final ArrayList<Location> locations = new ArrayList<>();
|
|
final Location top = this.getLocation();
|
|
final Location iterLoc = this.getBase().getLocation();
|
|
final double ySpacing = 2;
|
|
while (iterLoc.getY() <= top.getY()) {
|
|
locations.add(iterLoc.clone());
|
|
iterLoc.add(0, ySpacing, 0);
|
|
}
|
|
return locations;
|
|
}
|
|
|
|
public boolean isCanBendOnPackedIce() {
|
|
return this.canBendOnPackedIce;
|
|
}
|
|
|
|
public void setCanBendOnPackedIce(final boolean canBendOnPackedIce) {
|
|
this.canBendOnPackedIce = canBendOnPackedIce;
|
|
}
|
|
|
|
public boolean isUseParticles() {
|
|
return this.useParticles;
|
|
}
|
|
|
|
public void setUseParticles(final boolean useParticles) {
|
|
this.useParticles = useParticles;
|
|
}
|
|
|
|
public boolean isUseBlockSpiral() {
|
|
return this.useBlockSpiral;
|
|
}
|
|
|
|
public void setUseBlockSpiral(final boolean useBlockSpiral) {
|
|
this.useBlockSpiral = useBlockSpiral;
|
|
}
|
|
|
|
public int getAngle() {
|
|
return this.angle;
|
|
}
|
|
|
|
public void setAngle(final int angle) {
|
|
this.angle = angle;
|
|
}
|
|
|
|
public long getTime() {
|
|
return this.time;
|
|
}
|
|
|
|
public void setTime(final long time) {
|
|
this.time = time;
|
|
}
|
|
|
|
public long getInterval() {
|
|
return this.interval;
|
|
}
|
|
|
|
public void setInterval(final long interval) {
|
|
this.interval = interval;
|
|
}
|
|
|
|
public double getRotation() {
|
|
return this.rotation;
|
|
}
|
|
|
|
public void setRotation(final double rotation) {
|
|
this.rotation = rotation;
|
|
}
|
|
|
|
public double getHeight() {
|
|
return this.height;
|
|
}
|
|
|
|
public void setHeight(final double height) {
|
|
this.height = height;
|
|
}
|
|
|
|
public Block getBase() {
|
|
return this.base;
|
|
}
|
|
|
|
public void setBase(final Block base) {
|
|
this.base = base;
|
|
}
|
|
|
|
public TempBlock getBaseBlock() {
|
|
return this.baseBlock;
|
|
}
|
|
|
|
public void setBaseBlock(final TempBlock baseBlock) {
|
|
this.baseBlock = baseBlock;
|
|
}
|
|
|
|
public static Map<Block, Block> getAffectedBlocks() {
|
|
return AFFECTED_BLOCKS;
|
|
}
|
|
|
|
}
|