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
566 lines
15 KiB
Java
566 lines
15 KiB
Java
package com.projectkorra.projectkorra.waterbending.ice;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.HashMap;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Random;
|
|
import java.util.concurrent.CopyOnWriteArrayList;
|
|
|
|
import org.bukkit.Location;
|
|
import org.bukkit.Material;
|
|
import org.bukkit.block.Block;
|
|
import org.bukkit.block.BlockFace;
|
|
import org.bukkit.block.data.type.Snow;
|
|
import org.bukkit.entity.Player;
|
|
import org.bukkit.util.Vector;
|
|
|
|
import com.projectkorra.projectkorra.GeneralMethods;
|
|
import com.projectkorra.projectkorra.ability.ElementalAbility;
|
|
import com.projectkorra.projectkorra.ability.IceAbility;
|
|
import com.projectkorra.projectkorra.attribute.Attribute;
|
|
import com.projectkorra.projectkorra.util.TempBlock;
|
|
import com.projectkorra.projectkorra.waterbending.SurgeWall;
|
|
import com.projectkorra.projectkorra.waterbending.SurgeWave;
|
|
import com.projectkorra.projectkorra.waterbending.Torrent;
|
|
import com.projectkorra.projectkorra.waterbending.WaterSpoutWave;
|
|
import com.projectkorra.projectkorra.waterbending.multiabilities.WaterArmsSpear;
|
|
|
|
public class PhaseChange extends IceAbility {
|
|
|
|
public static enum PhaseChangeType {
|
|
FREEZE, MELT, CUSTOM;
|
|
|
|
@Override
|
|
public String toString() {
|
|
if (this == FREEZE) {
|
|
return "Freeze";
|
|
} else if (this == MELT) {
|
|
return "Melt";
|
|
} else if (this == CUSTOM) {
|
|
return "Custom";
|
|
}
|
|
return "";
|
|
}
|
|
}
|
|
|
|
private final List<PhaseChangeType> active_types = new ArrayList<>();
|
|
private static Map<TempBlock, Player> PLAYER_BY_BLOCK = new HashMap<>();
|
|
private final CopyOnWriteArrayList<TempBlock> blocks = new CopyOnWriteArrayList<>();
|
|
private final Random r = new Random();
|
|
|
|
@Attribute(Attribute.SELECT_RANGE)
|
|
private int sourceRange = 8;
|
|
|
|
// Freeze Variables.
|
|
@Attribute("Freeze" + Attribute.COOLDOWN)
|
|
private long freezeCooldown = 500;
|
|
@Attribute("Freeze" + Attribute.RADIUS)
|
|
private int freezeRadius = 3;
|
|
@Attribute("FreezeDepth")
|
|
private int depth = 1;
|
|
@Attribute("Control" + Attribute.RADIUS)
|
|
private double controlRadius = 25;
|
|
|
|
// Melt Variables.
|
|
private Location meltLoc;
|
|
@Attribute("Melt" + Attribute.COOLDOWN)
|
|
private long meltCooldown = 7000;
|
|
private int meltRadius;
|
|
@Attribute("Melt" + Attribute.RADIUS)
|
|
private int meltMaxRadius = 7;
|
|
@Attribute("Melt" + Attribute.SPEED)
|
|
private double meltSpeed = 8;
|
|
private double meltTicks = 0;
|
|
private boolean allowMeltFlow;
|
|
private final CopyOnWriteArrayList<Block> melted_blocks = new CopyOnWriteArrayList<>();
|
|
|
|
public PhaseChange(final Player player, final PhaseChangeType type) {
|
|
super(player);
|
|
this.startNewType(type);
|
|
this.start();
|
|
}
|
|
|
|
@Override
|
|
public void progress() {
|
|
if (!this.player.isOnline() || this.player.isDead()) {
|
|
this.remove();
|
|
return;
|
|
}
|
|
|
|
if (this.active_types.contains(PhaseChangeType.FREEZE)) {
|
|
if (this.blocks.isEmpty()) {
|
|
this.active_types.remove(PhaseChangeType.FREEZE);
|
|
return;
|
|
}
|
|
|
|
for (final TempBlock tb : this.blocks) {
|
|
if (tb.getLocation().getWorld() != this.player.getWorld()) {
|
|
tb.revertBlock();
|
|
this.blocks.remove(tb);
|
|
PLAYER_BY_BLOCK.remove(tb);
|
|
} else if (tb.getLocation().distanceSquared(this.player.getLocation()) > (this.controlRadius * this.controlRadius)) {
|
|
tb.revertBlock();
|
|
this.blocks.remove(tb);
|
|
PLAYER_BY_BLOCK.remove(tb);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (this.active_types.contains(PhaseChangeType.MELT)) {
|
|
if (!this.player.isSneaking() || !this.bPlayer.canBend(this)) {
|
|
this.active_types.remove(PhaseChangeType.MELT);
|
|
this.bPlayer.addCooldown("PhaseChangeMelt", this.meltCooldown);
|
|
this.meltRadius = 1;
|
|
this.meltTicks = 0;
|
|
return;
|
|
}
|
|
if (this.meltRadius >= this.meltMaxRadius) {
|
|
this.meltRadius = 1;
|
|
}
|
|
final Location l = GeneralMethods.getTargetedLocation(this.player, this.sourceRange);
|
|
this.resetMeltLocation(l);
|
|
this.meltArea(l, this.meltRadius);
|
|
}
|
|
|
|
if (this.active_types.contains(PhaseChangeType.CUSTOM)) {
|
|
for (final TempBlock tb : this.blocks) {
|
|
if (tb.getLocation().getWorld() != this.player.getWorld()) {
|
|
tb.revertBlock();
|
|
this.blocks.remove(tb);
|
|
PLAYER_BY_BLOCK.remove(tb);
|
|
} else if (tb.getLocation().distanceSquared(this.player.getLocation()) > (this.controlRadius * this.controlRadius)) {
|
|
tb.revertBlock();
|
|
this.blocks.remove(tb);
|
|
PLAYER_BY_BLOCK.remove(tb);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (this.active_types.isEmpty()) {
|
|
this.remove();
|
|
}
|
|
}
|
|
|
|
public void startNewType(final PhaseChangeType type) {
|
|
if (type == PhaseChangeType.MELT) {
|
|
if (this.bPlayer.isOnCooldown("PhaseChangeMelt")) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
this.active_types.add(type);
|
|
this.setFields(type);
|
|
}
|
|
|
|
public void setFields(final PhaseChangeType type) {
|
|
int night = 1;
|
|
if (isNight(this.player.getWorld())) {
|
|
night = (int) Math.round(getNightFactor());
|
|
}
|
|
this.sourceRange = night * getConfig().getInt("Abilities.Water.PhaseChange.SourceRange");
|
|
|
|
switch (type) {
|
|
case FREEZE:
|
|
this.depth = night * getConfig().getInt("Abilities.Water.PhaseChange.Freeze.Depth");
|
|
this.controlRadius = night * getConfig().getDouble("Abilities.Water.PhaseChange.Freeze.ControlRadius");
|
|
this.freezeCooldown = getConfig().getLong("Abilities.Water.PhaseChange.Freeze.Cooldown");
|
|
this.freezeRadius = night * getConfig().getInt("Abilities.Water.PhaseChange.Freeze.Radius");
|
|
|
|
this.freezeArea(GeneralMethods.getTargetedLocation(this.player, this.sourceRange));
|
|
case MELT:
|
|
this.meltRadius = 1;
|
|
this.meltCooldown = getConfig().getLong("Abilities.Water.PhaseChange.Melt.Cooldown");
|
|
this.meltSpeed = getConfig().getDouble("Abilities.Water.PhaseChange.Melt.Speed") * night;
|
|
this.meltMaxRadius = night * getConfig().getInt("Abilities.Water.PhaseChange.Melt.Radius");
|
|
this.allowMeltFlow = getConfig().getBoolean("Abilities.Water.PhaseChange.Melt.AllowFlow");
|
|
case CUSTOM:
|
|
this.depth = night * getConfig().getInt("Abilities.Water.PhaseChange.Freeze.Depth");
|
|
this.controlRadius = night * getConfig().getDouble("Abilities.Water.PhaseChange.Freeze.ControlRadius");
|
|
this.freezeCooldown = getConfig().getLong("Abilities.Water.PhaseChange.Freeze.Cooldown");
|
|
this.freezeRadius = night * getConfig().getInt("Abilities.Water.PhaseChange.Freeze.Radius");
|
|
|
|
this.meltRadius = 1;
|
|
this.meltCooldown = getConfig().getLong("Abilities.Water.PhaseChange.Melt.Cooldown");
|
|
this.meltSpeed = getConfig().getDouble("Abilities.Water.PhaseChange.Melt.Speed") * night;
|
|
this.meltMaxRadius = night * getConfig().getInt("Abilities.Water.PhaseChange.Melt.Radius");
|
|
this.allowMeltFlow = getConfig().getBoolean("Abilities.Water.PhaseChange.Melt.AllowFlow");
|
|
}
|
|
}
|
|
|
|
public void resetMeltLocation(final Location loc) {
|
|
if (this.meltLoc == null) {
|
|
this.meltLoc = loc;
|
|
return;
|
|
}
|
|
|
|
if (this.meltLoc.getWorld().equals(loc.getWorld()) && this.meltLoc.distance(loc) < 1) {
|
|
return;
|
|
}
|
|
|
|
if (!loc.equals(this.meltLoc)) {
|
|
this.meltLoc = loc;
|
|
this.meltRadius = 1;
|
|
}
|
|
}
|
|
|
|
public ArrayList<BlockFace> getBlockFacesTowardsPlayer(final Location center) {
|
|
final ArrayList<BlockFace> faces = new ArrayList<>();
|
|
final Vector toPlayer = GeneralMethods.getDirection(center, this.player.getEyeLocation());
|
|
final double[] vars = { toPlayer.getX(), toPlayer.getY(), toPlayer.getZ() };
|
|
for (int i = 0; i < 3; i++) {
|
|
if (vars[i] != 0) {
|
|
faces.add(GeneralMethods.getBlockFaceFromValue(i, vars[i]));
|
|
} else {
|
|
continue;
|
|
}
|
|
}
|
|
return faces;
|
|
}
|
|
|
|
public ArrayList<Block> getBlocksToFreeze(final Location center, final int radius) {
|
|
final ArrayList<Block> blocks = new ArrayList<>();
|
|
for (final Location l : GeneralMethods.getCircle(center, radius, this.depth, false, true, 0)) {
|
|
final Block b = l.getBlock();
|
|
loop: for (int i = 1; i <= this.depth; i++) {
|
|
for (final BlockFace face : this.getBlockFacesTowardsPlayer(center)) {
|
|
if (ElementalAbility.isAir(b.getRelative(face, i).getType())) {
|
|
blocks.add(b);
|
|
break loop;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return blocks;
|
|
}
|
|
|
|
public void freezeArea(final Location center, final int radius, final PhaseChangeType type) {
|
|
if (type == PhaseChangeType.FREEZE) {
|
|
if (this.bPlayer.isOnCooldown("PhaseChangeFreeze")) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (this.depth > 1) {
|
|
center.subtract(0, this.depth - 1, 0);
|
|
}
|
|
|
|
final ArrayList<Block> toFreeze = this.getBlocksToFreeze(center, radius);
|
|
for (final Block b : toFreeze) {
|
|
this.freeze(b);
|
|
}
|
|
|
|
if (!this.blocks.isEmpty()) {
|
|
if (type == PhaseChangeType.FREEZE) {
|
|
this.bPlayer.addCooldown("PhaseChangeFreeze", this.freezeCooldown);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void freezeArea(final Location center, final int radius) {
|
|
this.freezeArea(center, radius, PhaseChangeType.FREEZE);
|
|
}
|
|
|
|
public void freezeArea(final Location center, final PhaseChangeType type) {
|
|
this.freezeArea(center, this.freezeRadius, type);
|
|
}
|
|
|
|
public void freezeArea(final Location center) {
|
|
this.freezeArea(center, this.freezeRadius);
|
|
}
|
|
|
|
public void freeze(final Block b) {
|
|
if (b.getWorld() != this.player.getWorld()) {
|
|
return;
|
|
}
|
|
|
|
if (b.getLocation().distanceSquared(this.player.getLocation()) > this.controlRadius * this.controlRadius) {
|
|
return;
|
|
}
|
|
|
|
if (GeneralMethods.isRegionProtectedFromBuild(this.player, b.getLocation())) {
|
|
return;
|
|
}
|
|
|
|
if (!isWater(b)) {
|
|
return;
|
|
}
|
|
|
|
TempBlock tb = null;
|
|
|
|
if (TempBlock.isTempBlock(b)) {
|
|
tb = TempBlock.get(b);
|
|
if (this.melted_blocks.contains(tb.getBlock())) {
|
|
this.melted_blocks.remove(tb.getBlock());
|
|
tb.revertBlock();
|
|
tb.setType(Material.ICE);
|
|
}
|
|
}
|
|
if (tb == null) {
|
|
tb = new TempBlock(b, Material.ICE);
|
|
}
|
|
this.blocks.add(tb);
|
|
PLAYER_BY_BLOCK.put(tb, this.player);
|
|
playIcebendingSound(b.getLocation());
|
|
}
|
|
|
|
public void meltArea(final Location center, final int radius) {
|
|
final List<Block> ice = new ArrayList<Block>();
|
|
for (final Location l : GeneralMethods.getCircle(center, radius, 3, true, true, 0)) {
|
|
if (isIce(l.getBlock()) || isSnow(l.getBlock())) {
|
|
ice.add(l.getBlock());
|
|
|
|
}
|
|
}
|
|
|
|
this.meltTicks += this.meltSpeed / 20;
|
|
|
|
for (int i = 0; i < this.meltTicks % (this.meltSpeed); i++) {
|
|
if (ice.size() == 0) {
|
|
this.meltRadius++;
|
|
return;
|
|
}
|
|
|
|
final Block b = ice.get(this.r.nextInt(ice.size()));
|
|
this.melt(b);
|
|
ice.remove(b);
|
|
}
|
|
}
|
|
|
|
public void meltArea(final Location center) {
|
|
this.meltArea(center, this.meltRadius);
|
|
}
|
|
|
|
public void melt(final Block b) {
|
|
if (b.getWorld() != this.player.getWorld()) {
|
|
return;
|
|
}
|
|
if (b.getLocation().distanceSquared(this.player.getLocation()) > this.controlRadius * this.controlRadius) {
|
|
return;
|
|
}
|
|
if (GeneralMethods.isRegionProtectedFromBuild(this.player, b.getLocation())) {
|
|
return;
|
|
}
|
|
if (SurgeWall.getWallBlocks().containsKey(b)) {
|
|
return;
|
|
}
|
|
if (SurgeWave.isBlockWave(b)) {
|
|
return;
|
|
}
|
|
if (!SurgeWave.canThaw(b)) {
|
|
SurgeWave.thaw(b);
|
|
return;
|
|
}
|
|
if (!Torrent.canThaw(b)) {
|
|
Torrent.thaw(b);
|
|
return;
|
|
}
|
|
if (WaterArmsSpear.canThaw(b)) {
|
|
WaterArmsSpear.thaw(b);
|
|
return;
|
|
}
|
|
if (WaterSpoutWave.canThaw(b)) {
|
|
WaterSpoutWave.thaw(b);
|
|
return;
|
|
}
|
|
if (TempBlock.isTempBlock(b)) {
|
|
final TempBlock tb = TempBlock.get(b);
|
|
|
|
if (!isIce(tb.getBlock()) && !isSnow(tb.getBlock())) {
|
|
return;
|
|
}
|
|
|
|
if (PLAYER_BY_BLOCK.containsKey(tb)) {
|
|
thaw(tb);
|
|
}
|
|
|
|
if (b.getType() == Material.SNOW) {
|
|
if (b.getBlockData() instanceof Snow) {
|
|
final Snow snow = (Snow) b.getBlockData();
|
|
if (snow.getLayers() == snow.getMinimumLayers()) {
|
|
tb.revertBlock();
|
|
new TempBlock(b, Material.AIR.createBlockData(), 120 * 1000L);
|
|
} else {
|
|
tb.revertBlock();
|
|
new TempBlock(b, Material.SNOW.createBlockData(d -> ((Snow) d).setLayers(snow.getLayers() - 1)), 120 * 1000L);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (isIce(tb.getBlock()) && ElementalAbility.isWater(tb.getState().getBlockData().getMaterial())) {
|
|
tb.revertBlock();
|
|
}
|
|
} else if (isWater(b)) {
|
|
// Figure out what to do here also.
|
|
} else if (isIce(b)) {
|
|
if (this.allowMeltFlow) {
|
|
b.setType(Material.WATER);
|
|
b.setBlockData(GeneralMethods.getWaterData(0));
|
|
} else {
|
|
new TempBlock(b, Material.WATER);
|
|
}
|
|
this.melted_blocks.add(b);
|
|
} else if (b.getType() == Material.SNOW_BLOCK || b.getType() == Material.SNOW) {
|
|
if (b.getBlockData() instanceof Snow) {
|
|
final Snow snow = (Snow) b.getBlockData();
|
|
if (snow.getLayers() == snow.getMinimumLayers()) {
|
|
new TempBlock(b, Material.AIR.createBlockData(), 120 * 1000L);
|
|
} else {
|
|
snow.setLayers(snow.getLayers() - 1);
|
|
new TempBlock(b, Material.SNOW.createBlockData(d -> ((Snow) d).setLayers(snow.getLayers() - 1)), 120 * 1000L);
|
|
}
|
|
}
|
|
|
|
this.melted_blocks.add(b);
|
|
}
|
|
playWaterbendingSound(b.getLocation());
|
|
}
|
|
|
|
/**
|
|
* Only works with PhaseChange frozen blocks!
|
|
*
|
|
* @param tb TempBlock being thawed
|
|
* @return true when it is thawed successfully
|
|
*/
|
|
public static boolean thaw(final TempBlock tb) {
|
|
if (!PLAYER_BY_BLOCK.containsKey(tb)) {
|
|
return false;
|
|
} else {
|
|
final Player p = PLAYER_BY_BLOCK.get(tb);
|
|
final PhaseChange pc = getAbility(p, PhaseChange.class);
|
|
if (pc == null) {
|
|
return false;
|
|
}
|
|
PLAYER_BY_BLOCK.remove(tb);
|
|
if (pc.getFrozenBlocks() != null) {
|
|
pc.getFrozenBlocks().remove(tb);
|
|
tb.revertBlock();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Only works if the block is a {@link TempBlock} and PhaseChange frozen!
|
|
*
|
|
* @param b Block being thawed
|
|
* @return false if not a {@link TempBlock}
|
|
*/
|
|
public static boolean thaw(final Block b) {
|
|
if (!TempBlock.isTempBlock(b)) {
|
|
return false;
|
|
}
|
|
final TempBlock tb = TempBlock.get(b);
|
|
return thaw(tb);
|
|
}
|
|
|
|
public static Map<TempBlock, Player> getFrozenBlocksMap() {
|
|
return PLAYER_BY_BLOCK;
|
|
}
|
|
|
|
public static List<Block> getFrozenBlocksAsBlock() {
|
|
final List<Block> list = new ArrayList<>();
|
|
for (final TempBlock tb : PLAYER_BY_BLOCK.keySet()) {
|
|
final Block b = tb.getBlock();
|
|
list.add(b);
|
|
}
|
|
return list;
|
|
}
|
|
|
|
public void revertFrozenBlocks() {
|
|
if (this.active_types.contains(PhaseChangeType.FREEZE)) {
|
|
for (final TempBlock tb : this.blocks) {
|
|
PLAYER_BY_BLOCK.remove(tb);
|
|
tb.revertBlock();
|
|
}
|
|
this.blocks.clear();
|
|
}
|
|
}
|
|
|
|
public void revertMeltedBlocks() {
|
|
if (this.active_types.contains(PhaseChangeType.MELT)) {
|
|
for (final Block b : this.melted_blocks) {
|
|
if (TempBlock.isTempBlock(b)) {
|
|
TempBlock.get(b).revertBlock();
|
|
} else {
|
|
b.setType(Material.ICE);
|
|
}
|
|
}
|
|
this.melted_blocks.clear();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void remove() {
|
|
super.remove();
|
|
this.revertFrozenBlocks();
|
|
this.revertMeltedBlocks();
|
|
}
|
|
|
|
@Override
|
|
public boolean isSneakAbility() {
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public boolean isHarmlessAbility() {
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public long getCooldown() {
|
|
return 0;
|
|
}
|
|
|
|
@Override
|
|
public String getName() {
|
|
return "PhaseChange";
|
|
}
|
|
|
|
@Override
|
|
public Location getLocation() {
|
|
return null;
|
|
}
|
|
|
|
public int getDepth() {
|
|
return this.depth;
|
|
}
|
|
|
|
public void setDepth(final int value) {
|
|
this.depth = value;
|
|
}
|
|
|
|
public int getSourceRange() {
|
|
return this.sourceRange;
|
|
}
|
|
|
|
public void setSourceRange(final int value) {
|
|
this.sourceRange = value;
|
|
}
|
|
|
|
public double getFreezeControlRadius() {
|
|
return this.controlRadius;
|
|
}
|
|
|
|
public int getMeltRadius() {
|
|
return this.meltRadius;
|
|
}
|
|
|
|
public void setFreezeControlRadius(final int value) {
|
|
this.controlRadius = value;
|
|
}
|
|
|
|
public CopyOnWriteArrayList<TempBlock> getFrozenBlocks() {
|
|
return this.blocks;
|
|
}
|
|
|
|
public CopyOnWriteArrayList<Block> getMeltedBlocks() {
|
|
return this.melted_blocks;
|
|
}
|
|
|
|
public List<PhaseChangeType> getActiveTypes() {
|
|
return this.active_types;
|
|
}
|
|
}
|