package com.nisovin.magicspells.spells.targeted; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import org.bukkit.BlockChangeDelegate; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.TreeType; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; import org.bukkit.block.BlockState; import org.bukkit.entity.Player; import com.nisovin.magicspells.events.SpellTargetLocationEvent; import com.nisovin.magicspells.spells.TargetedLocationSpell; import com.nisovin.magicspells.spells.TargetedSpell; import com.nisovin.magicspells.util.MagicConfig; import com.nisovin.magicspells.util.SpellAnimation; public class TreeSpell extends TargetedSpell implements TargetedLocationSpell { private TreeType treeType; private int speed; public TreeSpell(MagicConfig config, String spellName) { super(config, spellName); treeType = TreeType.valueOf(getConfigString("tree-type", "tree").toUpperCase().replace(" ", "_")); if (treeType == null) treeType = TreeType.TREE; speed = getConfigInt("animation-speed", 20); } @Override public PostCastAction castSpell(Player player, SpellCastState state, float power, String[] args) { if (state == SpellCastState.NORMAL) { // get target block Block target = getTargetedBlock(player, power); if (target != null && target.getType() != Material.AIR) { SpellTargetLocationEvent event = new SpellTargetLocationEvent(this, player, target.getLocation(), power); Bukkit.getPluginManager().callEvent(event); if (event.isCancelled()) { target = null; } else { target = event.getTargetLocation().getBlock(); } } if (target == null || target.getType() == Material.AIR) { return noTarget(player); } // grow tree boolean grown = growTree(target); // check if failed if (!grown) { return noTarget(player); } else { playSpellEffects(player, target.getLocation()); } } return PostCastAction.HANDLE_NORMALLY; } private boolean growTree(Block target) { // switch to block above target = target.getRelative(BlockFace.UP); if (target.getType() != Material.AIR) { return false; } // grow tree Location loc = target.getLocation(); if (speed > 0) { List<BlockState> blockStates = new ArrayList<BlockState>(); target.getWorld().generateTree(loc, treeType, new TreeWatch(loc, blockStates)); if (blockStates.size() > 0) { new GrowAnimation(loc.getBlockX(), loc.getBlockZ(), blockStates, speed); return true; } else { return false; } } else { return target.getWorld().generateTree(loc, treeType); } } @Override public boolean castAtLocation(Player caster, Location target, float power) { boolean ret = growTree(target.getBlock()); if (ret) { playSpellEffects(caster, target); } return ret; } @Override public boolean castAtLocation(Location target, float power) { return false; } private class GrowAnimation extends SpellAnimation { List<BlockState> blockStates; int blocksPerTick; public GrowAnimation(final int centerX, final int centerZ, final List<BlockState> blocks, int speed) { super(speed < 20 ? 20 / speed : 1, true); this.blockStates = blocks; this.blocksPerTick = speed/20 + 1; Collections.sort(blockStates, new Comparator<BlockState>() { @Override public int compare(BlockState o1, BlockState o2) { if (o1.getY() < o2.getY()) { return -1; } else if (o1.getY() > o2.getY()) { return 1; } else { int dist1 = Math.abs(o1.getX() - centerX) + Math.abs(o1.getZ() - centerZ); int dist2 = Math.abs(o2.getX() - centerX) + Math.abs(o2.getZ() - centerZ); if (dist1 > dist2) { return 1; } else if (dist1 < dist2) { return -1; } else { return 0; } } } }); } @Override protected void onTick(int tick) { for (int i = 0; i < blocksPerTick; i++) { BlockState state = blockStates.remove(0); state.update(true); if (blockStates.size() == 0) { stop(); break; } } } } private class TreeWatch implements BlockChangeDelegate { private Location loc; private List<BlockState> blockStates; public TreeWatch(Location loc, List<BlockState> blockStates) { this.loc = loc; this.blockStates = blockStates; } @Override public int getHeight() { return loc.getWorld().getMaxHeight(); } @Override public int getTypeId(int x, int y, int z) { return loc.getWorld().getBlockTypeIdAt(x, y, z); } @Override public boolean isEmpty(int x, int y, int z) { return getTypeId(x,y,z) == 0; } @Override public boolean setRawTypeId(int x, int y, int z, int id) { BlockState state = loc.getWorld().getBlockAt(x, y, z).getState(); state.setTypeId(id); blockStates.add(state); return true; } @Override public boolean setRawTypeIdAndData(int x, int y, int z, int id, int data) { BlockState state = loc.getWorld().getBlockAt(x, y, z).getState(); state.setTypeId(id); state.setRawData((byte)data); blockStates.add(state); return true; } @Override public boolean setTypeId(int x, int y, int z, int id) { return setRawTypeId(x, y, z, id); } @Override public boolean setTypeIdAndData(int x, int y, int z, int id, int data) { return setRawTypeIdAndData(x, y, z, id, data); } } }