TF-ProjectKorra/src/com/projectkorra/projectkorra/waterbending/SurgeWall.java
Vahagn Tovmasian 3c1d6b7b85
Blue Fire Update & Firebending Refactor (#1062)
## Additions
* Adds Blue Fire SubElement.
    > *Adds related damage, cooldown, and range modifiers for configuration
* Adds Sticks, Sponges, and Chorus Fruit to cookable HeatControl items.
* Adds Smoker, BlastFurnace, and extinguished Campfires to blocks which FireBlast can light.
* Adds new TempBlock constructor which takes in a `long revertTime` parameter
* Adds new blocks to block lists in configuration
  >* Adds new nether plants to plantBlocks list
  >* Adds new earth blocks to earthBlocks list

## Fixes
* Fixes AvatarState buffs overriding day related buffs for firebending.
* Fixes Blaze not going up hills, going through walls (mostly), jumping gaps.
* Fixes Furnaces and related blocks not smelting after being activated by FireBlast

## Removals
* Removes BlazeArc dependencies for Fire Abilities which ignite the ground.
* Removes smoke particles from Fire bending to increase visibility and better emulate the show.

## Misc. Changes
* Changes API versioning to 1.16.1
* Fire from Firebending no longer reverts all at once.
* Changes Combustion animation to be more beam-like rather than a rehash of FireBlast.
* Changes Add, Remove, Display command to properly display space for Blue Fire.
* Changes `ElementalAbility#isFire()` to check for SOUL_FIRE_FLAME.
* Changes isIgnitable to check whether fire can be placed at that location rather than solely based on flammability.
* Changes firebending abilities to use `FireAbility#playFirebendingParticles()` & `FireAbility#createTempFire()` where applicable.
* Changes `FireAbility#playFirebendingParticles()` to play blue fire particles when player has the BlueFire subelement.
2020-07-11 22:05:45 -07:00

673 lines
18 KiB
Java

package com.projectkorra.projectkorra.waterbending;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
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.util.Vector;
import com.projectkorra.projectkorra.BendingPlayer;
import com.projectkorra.projectkorra.GeneralMethods;
import com.projectkorra.projectkorra.ability.ElementalAbility;
import com.projectkorra.projectkorra.ability.FireAbility;
import com.projectkorra.projectkorra.ability.WaterAbility;
import com.projectkorra.projectkorra.attribute.Attribute;
import com.projectkorra.projectkorra.firebending.FireBlast;
import com.projectkorra.projectkorra.util.BlockSource;
import com.projectkorra.projectkorra.util.ClickType;
import com.projectkorra.projectkorra.util.ParticleEffect;
import com.projectkorra.projectkorra.util.TempBlock;
import com.projectkorra.projectkorra.waterbending.plant.PlantRegrowth;
import com.projectkorra.projectkorra.waterbending.util.WaterReturn;
public class SurgeWall extends WaterAbility {
private static final String RANGE_CONFIG = "Abilities.Water.Surge.Wall.Range";
private static final Map<Block, Block> AFFECTED_BLOCKS = new ConcurrentHashMap<>();
private static final Map<Block, Player> WALL_BLOCKS = new ConcurrentHashMap<>();
public static final List<TempBlock> SOURCE_BLOCKS = new ArrayList<>();
private boolean progressing;
private boolean settingUp;
private boolean forming;
private boolean frozen;
private long time;
private long interval;
@Attribute(Attribute.COOLDOWN)
private long cooldown;
@Attribute(Attribute.DURATION)
private long duration;
@Attribute(Attribute.RADIUS)
private double radius;
@Attribute(Attribute.RANGE)
private double range;
private Block sourceBlock;
private Location location;
private Location firstDestination;
private Location targetDestination;
private ArrayList<Location> locations;
private Vector firstDirection;
private Vector targetDirection;
private Map<Block, Material> oldTemps;
public SurgeWall(final Player player) {
super(player);
this.interval = getConfig().getLong("Abilities.Water.Surge.Wall.Interval");
this.cooldown = getConfig().getLong("Abilities.Water.Surge.Wall.Cooldown");
this.duration = getConfig().getLong("Abilities.Water.Surge.Wall.Duration");
this.range = getConfig().getDouble(RANGE_CONFIG);
this.radius = getConfig().getDouble("Abilities.Water.Surge.Wall.Radius");
this.locations = new ArrayList<>();
this.oldTemps = new HashMap<>();
SurgeWave wave = getAbility(player, SurgeWave.class);
if (wave != null && !wave.isProgressing() && !this.bPlayer.isOnCooldown("SurgeWave")) {
wave.moveWater();
return;
}
if (this.bPlayer.isAvatarState()) {
this.radius = getConfig().getDouble("Abilities.Avatar.AvatarState.Water.Surge.Wall.Radius");
}
final SurgeWall wall = getAbility(player, SurgeWall.class);
if (wall != null) {
if (wall.progressing) {
wall.freezeThaw();
return;
} else if (this.prepare()) {
wall.remove();
this.start();
this.time = System.currentTimeMillis();
}
} else if (!this.bPlayer.isOnCooldown("SurgeWall") && this.prepare()) {
this.start();
this.time = System.currentTimeMillis();
return;
}
if (this.bPlayer.isOnCooldown("SurgeWave") || player.isSneaking()) {
return;
} else if (wall == null && WaterReturn.hasWaterBottle(player)) {
final Location eyeLoc = player.getEyeLocation();
final Block block = eyeLoc.add(eyeLoc.getDirection().normalize()).getBlock();
if (isTransparent(player, block) && isTransparent(player, eyeLoc.getBlock())) {
final TempBlock tempBlock = new TempBlock(block, Material.WATER, GeneralMethods.getWaterData(0));
SOURCE_BLOCKS.add(tempBlock);
wave = new SurgeWave(player);
wave.setCanHitSelf(false);
wave.moveWater();
if (!wave.isProgressing()) {
wave.remove();
} else {
WaterReturn.emptyWaterBottle(player);
}
SOURCE_BLOCKS.remove(tempBlock);
tempBlock.revertBlock();
}
}
}
private void freezeThaw() {
if (!this.bPlayer.canIcebend()) {
return;
} else if (this.frozen) {
this.thaw();
} else {
this.freeze();
}
}
private void freeze() {
this.frozen = true;
for (final Block block : WALL_BLOCKS.keySet()) {
if (WALL_BLOCKS.get(block) == this.player) {
new TempBlock(block, Material.ICE);
playIcebendingSound(block.getLocation());
}
}
}
private void thaw() {
this.frozen = false;
for (final Block block : WALL_BLOCKS.keySet()) {
if (WALL_BLOCKS.get(block) == this.player) {
new TempBlock(block, Material.WATER, GeneralMethods.getWaterData(0));
}
}
}
public boolean prepare() {
this.cancelPrevious();
final Block block = BlockSource.getWaterSourceBlock(this.player, this.range, ClickType.LEFT_CLICK, true, true, this.bPlayer.canPlantbend());
if (block != null && !GeneralMethods.isRegionProtectedFromBuild(this, block.getLocation())) {
this.sourceBlock = block;
this.focusBlock();
return true;
}
return false;
}
private void cancelPrevious() {
final SurgeWall oldWave = getAbility(this.player, SurgeWall.class);
if (oldWave != null) {
if (oldWave.progressing) {
oldWave.removeWater(oldWave.sourceBlock);
} else {
oldWave.remove();
}
}
}
private void focusBlock() {
this.location = this.sourceBlock.getLocation();
}
public void moveWater() {
if (this.sourceBlock != null) {
this.targetDestination = this.player.getTargetBlock(getTransparentMaterialSet(), (int) this.range).getLocation();
if (this.targetDestination.distanceSquared(this.location) <= 1) {
this.progressing = false;
this.targetDestination = null;
} else {
this.bPlayer.addCooldown("SurgeWall", this.cooldown);
this.progressing = true;
this.settingUp = true;
this.firstDestination = this.getToEyeLevel();
this.firstDirection = this.getDirection(this.sourceBlock.getLocation(), this.firstDestination);
this.targetDirection = this.getDirection(this.firstDestination, this.targetDestination);
if (isPlant(this.sourceBlock) || isSnow(this.sourceBlock)) {
new PlantRegrowth(this.player, this.sourceBlock);
this.sourceBlock.setType(Material.AIR, false);
}
this.addWater(this.sourceBlock);
}
}
}
private Location getToEyeLevel() {
final Location loc = this.sourceBlock.getLocation().clone();
loc.setY(this.targetDestination.getY());
return loc;
}
private Vector getDirection(final Location location, final Location destination) {
double x1, y1, z1;
double x0, y0, z0;
x1 = destination.getX();
y1 = destination.getY();
z1 = destination.getZ();
x0 = location.getX();
y0 = location.getY();
z0 = location.getZ();
return new Vector(x1 - x0, y1 - y0, z1 - z0);
}
@Override
public void progress() {
if (!this.bPlayer.canBendIgnoreBindsCooldowns(this)) {
this.remove();
return;
} else if (this.duration != 0 && System.currentTimeMillis() > this.getStartTime() + this.duration) {
this.bPlayer.addCooldown(this);
this.remove();
return;
}
this.locations.clear();
if (System.currentTimeMillis() - this.time >= this.interval) {
this.time = System.currentTimeMillis();
final boolean matchesName = this.bPlayer.getBoundAbilityName().equals(this.getName());
if (!this.progressing && !matchesName) {
this.remove();
return;
} else if (this.progressing && (!this.player.isSneaking() || !matchesName)) {
this.remove();
return;
} else if (!this.progressing) {
ParticleEffect.SMOKE_NORMAL.display(this.sourceBlock.getLocation().add(0.5, 0.5, 0.5), 1);
return;
}
if (this.forming) {
if ((new Random()).nextInt(7) == 0) {
playWaterbendingSound(this.location);
}
final ArrayList<Block> blocks = new ArrayList<Block>();
final Location targetLoc = GeneralMethods.getTargetedLocation(this.player, (int) this.range, false, false, Material.WATER, Material.ICE);
this.location = targetLoc.clone();
final Vector eyeDir = this.player.getEyeLocation().getDirection();
Vector vector;
Block block;
for (double i = 0; i <= this.getNightFactor(this.radius); i += 0.5) {
for (double angle = 0; angle < 360; angle += 10) {
vector = GeneralMethods.getOrthogonalVector(eyeDir.clone(), angle, i);
block = targetLoc.clone().add(vector).getBlock();
if (GeneralMethods.isRegionProtectedFromBuild(this, block.getLocation())) {
continue;
} else if (WALL_BLOCKS.containsKey(block)) {
blocks.add(block);
} else if (!blocks.contains(block) && (ElementalAbility.isAir(block.getType()) || FireAbility.isFire(block.getType()) || this.isWaterbendable(block)) && this.isTransparent(block)) {
WALL_BLOCKS.put(block, this.player);
this.addWallBlock(block);
blocks.add(block);
this.locations.add(block.getLocation());
FireBlast.removeFireBlastsAroundPoint(block.getLocation(), 2);
}
}
}
for (final Block blocki : WALL_BLOCKS.keySet()) {
if (WALL_BLOCKS.get(blocki) == this.player && !blocks.contains(blocki)) {
this.finalRemoveWater(blocki);
}
}
return;
}
if (this.sourceBlock.getLocation().distanceSquared(this.firstDestination) < 0.5 * 0.5 && this.settingUp) {
this.settingUp = false;
}
Vector direction;
if (this.settingUp) {
direction = this.firstDirection;
} else {
direction = this.targetDirection;
}
this.location = this.location.clone().add(direction);
Block block = this.location.getBlock();
if (block.getLocation().equals(this.sourceBlock.getLocation())) {
this.location = this.location.clone().add(direction);
block = this.location.getBlock();
}
if (!ElementalAbility.isAir(block.getType())) {
this.remove();
return;
} else if (!this.progressing) {
this.remove();
return;
}
this.addWater(block);
this.removeWater(this.sourceBlock);
this.sourceBlock = block;
if (this.location.distanceSquared(this.targetDestination) < 1) {
this.removeWater(this.sourceBlock);
this.forming = true;
}
}
}
private void addWallBlock(final Block block) {
if (TempBlock.isTempBlock(block)) {
this.oldTemps.put(block, block.getType());
}
if (this.frozen) {
new TempBlock(block, Material.ICE);
} else {
new TempBlock(block, Material.WATER, GeneralMethods.getWaterData(0));
}
}
@Override
public void remove() {
super.remove();
this.returnWater();
this.finalRemoveWater(this.sourceBlock);
for (final Block block : WALL_BLOCKS.keySet()) {
if (WALL_BLOCKS.get(block) == this.player) {
this.finalRemoveWater(block);
}
}
}
private void removeWater(final Block block) {
if (block != null) {
if (AFFECTED_BLOCKS.containsKey(block)) {
if (!GeneralMethods.isAdjacentToThreeOrMoreSources(block)) {
if (this.oldTemps.containsKey(block)) {
final TempBlock tb = TempBlock.get(block);
if (tb != null) {
tb.setType(this.oldTemps.get(block));
}
} else {
TempBlock.revertBlock(block, Material.AIR);
}
}
AFFECTED_BLOCKS.remove(block);
}
}
}
private void finalRemoveWater(final Block block) {
if (block != null) {
if (AFFECTED_BLOCKS.containsKey(block)) {
if (this.oldTemps.containsKey(block)) {
final TempBlock tb = TempBlock.get(block);
if (tb != null) {
tb.setType(this.oldTemps.get(block));
}
} else {
TempBlock.revertBlock(block, Material.AIR);
}
AFFECTED_BLOCKS.remove(block);
}
if (WALL_BLOCKS.containsKey(block)) {
if (this.oldTemps.containsKey(block)) {
final TempBlock tb = TempBlock.get(block);
if (tb != null) {
tb.setType(this.oldTemps.get(block));
}
} else {
TempBlock.revertBlock(block, Material.AIR);
}
WALL_BLOCKS.remove(block);
}
}
}
private void addWater(final Block block) {
if (GeneralMethods.isRegionProtectedFromBuild(this, block.getLocation())) {
return;
} else if (!TempBlock.isTempBlock(block)) {
new TempBlock(block, Material.WATER, GeneralMethods.getWaterData(0));
AFFECTED_BLOCKS.put(block, block);
}
}
@Override
public boolean allowBreakPlants() {
return false;
}
public static void form(final Player player) {
final BendingPlayer bPlayer = BendingPlayer.getBendingPlayer(player);
if (bPlayer == null) {
return;
}
final int range = getConfig().getInt(RANGE_CONFIG);
SurgeWall wall = getAbility(player, SurgeWall.class);
SurgeWave wave = getAbility(player, SurgeWave.class);
if (wave != null) {
if (wave.isProgressing() && !wave.isFreezing()) {
// Freeze the wave.
new SurgeWave(player);
} else if (wave.isActivateFreeze()) {
wave.remove();
return;
}
}
if (wall == null) {
final Block source = BlockSource.getWaterSourceBlock(player, range, ClickType.SHIFT_DOWN, true, true, bPlayer.canPlantbend());
if (wave == null && source == null && WaterReturn.hasWaterBottle(player)) {
if (bPlayer.isOnCooldown("SurgeWall")) {
return;
}
final Location eyeLoc = player.getEyeLocation();
final Block block = eyeLoc.add(eyeLoc.getDirection().normalize()).getBlock();
if (isTransparent(player, block) && isTransparent(player, eyeLoc.getBlock())) {
final TempBlock tempBlock = new TempBlock(block, Material.WATER, GeneralMethods.getWaterData(0));
SOURCE_BLOCKS.add(tempBlock);
wall = new SurgeWall(player);
wall.moveWater();
if (!wall.progressing) {
SOURCE_BLOCKS.remove(tempBlock);
tempBlock.revertBlock();
wall.remove();
} else {
WaterReturn.emptyWaterBottle(player);
}
SOURCE_BLOCKS.remove(tempBlock);
tempBlock.revertBlock();
return;
}
}
// If SurgeWall isn't being created, then try to source SurgeWave.
if (!bPlayer.isOnCooldown("SurgeWave")) {
wave = new SurgeWave(player);
}
return;
} else {
if (isWaterbendable(player, null, player.getTargetBlock((HashSet<Material>) null, range))) {
wave = new SurgeWave(player);
return;
}
}
if (wall != null) {
wall.moveWater();
}
}
public static void removeAllCleanup() {
for (final Block block : AFFECTED_BLOCKS.keySet()) {
TempBlock.revertBlock(block, Material.AIR);
AFFECTED_BLOCKS.remove(block);
WALL_BLOCKS.remove(block);
}
for (final Block block : WALL_BLOCKS.keySet()) {
TempBlock.revertBlock(block, Material.AIR);
AFFECTED_BLOCKS.remove(block);
WALL_BLOCKS.remove(block);
}
}
public static boolean wasBrokenFor(final Player player, final Block block) {
final SurgeWall wall = getAbility(player, SurgeWall.class);
if (wall != null) {
if (wall.sourceBlock == null) {
return false;
} else if (wall.sourceBlock.equals(block)) {
return true;
}
}
return false;
}
private void returnWater() {
if (this.location != null) {
if (this.frozen) {
this.thaw();
}
new WaterReturn(this.player, this.location.getBlock());
}
}
@Override
public String getName() {
return "Surge";
}
@Override
public Location getLocation() {
if (this.location != null) {
return this.location;
} else if (this.sourceBlock != null) {
return this.sourceBlock.getLocation();
}
return this.player != null ? this.player.getLocation() : null;
}
@Override
public long getCooldown() {
return this.cooldown;
}
@Override
public boolean isSneakAbility() {
return true;
}
@Override
public boolean isHarmlessAbility() {
return false;
}
@Override
public List<Location> getLocations() {
return this.locations;
}
public boolean isProgressing() {
return this.progressing;
}
public void setProgressing(final boolean progressing) {
this.progressing = progressing;
}
public boolean isSettingUp() {
return this.settingUp;
}
public void setSettingUp(final boolean settingUp) {
this.settingUp = settingUp;
}
public boolean isForming() {
return this.forming;
}
public void setForming(final boolean forming) {
this.forming = forming;
}
public boolean isFrozen() {
return this.frozen;
}
public void setFrozen(final boolean frozen) {
this.frozen = frozen;
}
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 getRadius() {
return this.radius;
}
public void setRadius(final double radius) {
this.radius = radius;
}
public double getRange() {
return this.range;
}
public void setRange(final double range) {
this.range = range;
}
public Block getSourceBlock() {
return this.sourceBlock;
}
public void setSourceBlock(final Block sourceBlock) {
this.sourceBlock = sourceBlock;
}
public Location getFirstDestination() {
return this.firstDestination;
}
public void setFirstDestination(final Location firstDestination) {
this.firstDestination = firstDestination;
}
public Location getTargetDestination() {
return this.targetDestination;
}
public void setTargetDestination(final Location targetDestination) {
this.targetDestination = targetDestination;
}
public Vector getFirstDirection() {
return this.firstDirection;
}
public void setFirstDirection(final Vector firstDirection) {
this.firstDirection = firstDirection;
}
public Vector getTargetDirection() {
return this.targetDirection;
}
public void setTargetDirection(final Vector targetDirection) {
this.targetDirection = targetDirection;
}
public static Map<Block, Block> getAffectedBlocks() {
return AFFECTED_BLOCKS;
}
public static Map<Block, Player> getWallBlocks() {
return WALL_BLOCKS;
}
public void setCooldown(final long cooldown) {
this.cooldown = cooldown;
}
public void setLocation(final Location location) {
this.location = location;
}
}