package com.nisovin.magicspells.spells.targeted; import java.io.File; import java.util.ArrayList; import java.util.List; import org.bukkit.Effect; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.block.Block; import org.bukkit.block.BlockState; import org.bukkit.entity.Player; import com.nisovin.magicspells.MagicSpells; import com.nisovin.magicspells.spelleffects.EffectPosition; import com.nisovin.magicspells.spells.TargetedLocationSpell; import com.nisovin.magicspells.spells.TargetedSpell; import com.nisovin.magicspells.util.MagicConfig; import com.sk89q.worldedit.CuboidClipboard; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.Vector; import com.sk89q.worldedit.blocks.BaseBlock; import com.sk89q.worldedit.bukkit.BukkitWorld; import com.sk89q.worldedit.schematic.SchematicFormat; public class PasteSpell extends TargetedSpell implements TargetedLocationSpell { File file; int yOffset; int maxBlocks; boolean pasteAir; boolean pasteEntities; boolean pasteAtCaster; boolean playBlockBreakEffect; int tickInterval; int blocksPerTick; public PasteSpell(MagicConfig config, String spellName) { super(config, spellName); File folder = new File(MagicSpells.plugin.getDataFolder(), "schematics"); if (!folder.exists()) { folder.mkdir(); } String schematic = getConfigString("schematic", "none"); file = new File(folder, schematic); if (!file.exists()) { MagicSpells.error("PasteSpell " + spellName + " has non-existant schematic: " + schematic); } yOffset = getConfigInt("y-offset", 0); maxBlocks = getConfigInt("max-blocks", 10000); pasteAir = getConfigBoolean("paste-air", false); pasteEntities = getConfigBoolean("paste-entities", true); pasteAtCaster = getConfigBoolean("paste-at-caster", false); playBlockBreakEffect = getConfigBoolean("play-block-break-effect", true); float blocksPerSecond = getConfigFloat("blocks-per-second", 0); if (blocksPerSecond == 0) { tickInterval = 0; blocksPerTick = 0; } else if (blocksPerSecond > 20) { tickInterval = 1; blocksPerTick = (int)Math.ceil(blocksPerSecond / 20); } else { tickInterval = Math.round(20 / blocksPerSecond); blocksPerTick = 1; } } @Override public PostCastAction castSpell(Player player, SpellCastState state, float power, String[] args) { if (state == SpellCastState.NORMAL) { Block target = pasteAtCaster ? player.getLocation().getBlock() : getTargetedBlock(player, power); if (target == null) { return noTarget(player); } Location loc = target.getLocation(); loc.add(0, yOffset, 0); boolean ok = castAtLocation(loc, power); if (!ok) { return noTarget(player); } } return PostCastAction.HANDLE_NORMALLY; } @Override public boolean castAtLocation(Player caster, Location target, float power) { boolean ok; if (tickInterval == 0) { ok = pasteInstant(target); } else { ok = pasteOverTime(target); } if (ok) { if (caster != null) { playSpellEffects(caster, target); } else { playSpellEffects(EffectPosition.TARGET, target); } } return ok; } @Override public boolean castAtLocation(Location target, float power) { return castAtLocation(null, target, power); } private boolean pasteInstant(Location target) { try { CuboidClipboard cuboid = SchematicFormat.MCEDIT.load(file); EditSession session = new EditSession(new BukkitWorld(target.getWorld()), maxBlocks); cuboid.paste(session, new Vector(target.getX(), target.getY(), target.getZ()), !pasteAir, pasteEntities); return true; } catch (Exception e) { e.printStackTrace(); return false; } } private boolean pasteOverTime(Location target) { try { Builder builder = new Builder(target); builder.build(); return true; } catch (Exception e) { e.printStackTrace(); return false; } } class Builder { Block center; List<BlockState> blocks = new ArrayList<BlockState>(); int current = 0; int taskId; public Builder(Location target) throws Exception { this.center = target.getBlock(); CuboidClipboard clipboard = SchematicFormat.MCEDIT.load(file); Vector size = clipboard.getSize(); Vector offset = clipboard.getOffset(); List<BlockState> air = new ArrayList<BlockState>(); List<BlockState> solids = new ArrayList<BlockState>(); List<BlockState> nonsolids = new ArrayList<BlockState>(); for (int y = 0; y < size.getBlockY(); y++) { for (int x = 0; x < size.getBlockX(); x++) { for (int z = 0; z < size.getBlockZ(); z++) { BaseBlock b = clipboard.getBlock(new Vector(x, y, z)); int blockX = target.getBlockX() + x + offset.getBlockX(); int blockY = target.getBlockY() + y + offset.getBlockY(); int blockZ = target.getBlockZ() + z + offset.getBlockZ(); Block block = target.getWorld().getBlockAt(blockX, blockY, blockZ); if (b.getId() != 0 || (pasteAir && block.getType() != Material.AIR)) { BlockState state = block.getState(); setBlockStateFromWorldEditBlock(state, b); if (state.getType() == Material.AIR) { air.add(state); } else if (state.getType().isSolid()) { solids.add(state); } else { nonsolids.add(state); } } } } } blocks.addAll(air); blocks.addAll(solids); blocks.addAll(nonsolids); } public void build() { taskId = MagicSpells.scheduleRepeatingTask(new Runnable() { public void run() { if (current >= blocks.size()) { MagicSpells.cancelTask(taskId); } else { for (int i = 0; i < blocksPerTick; i++) { BlockState state = blocks.get(current); state.update(true, false); if (playBlockBreakEffect && state.getType() != Material.AIR) { center.getWorld().playEffect(state.getLocation(), Effect.STEP_SOUND, state.getType()); } current++; if (current >= blocks.size()) { MagicSpells.cancelTask(taskId); break; } } } } }, 1, tickInterval); } @SuppressWarnings("deprecation") private void setBlockStateFromWorldEditBlock(BlockState state, BaseBlock block) { state.setTypeId(block.getId()); state.setRawData((byte)block.getData()); } } }