package com.nisovin.magicspells.spells.buff; import java.util.ArrayList; import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Set; import org.bukkit.Bukkit; import org.bukkit.Chunk; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.block.Block; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.player.PlayerMoveEvent; import org.bukkit.material.MaterialData; import com.nisovin.magicspells.MagicSpells; import com.nisovin.magicspells.materials.MagicBlockMaterial; import com.nisovin.magicspells.materials.MagicMaterial; import com.nisovin.magicspells.spells.BuffSpell; import com.nisovin.magicspells.util.MagicConfig; import com.nisovin.magicspells.util.Util; public class StonevisionSpell extends BuffSpell { private int range; private Set<Material> transparentTypes; private boolean unobfuscate; private MagicMaterial glass = new MagicBlockMaterial(new MaterialData(Material.GLASS)); private HashMap<String, TransparentBlockSet> seers; public StonevisionSpell(MagicConfig config, String spellName) { super(config, spellName); range = getConfigInt("range", 4); unobfuscate = getConfigBoolean("unobfuscate", false); transparentTypes = EnumSet.noneOf(Material.class); MagicMaterial type = MagicSpells.getItemNameResolver().resolveBlock(getConfigString("transparent-type", "stone")); if (type != null) { transparentTypes.add(type.getMaterial()); } List<String> types = getConfigStringList("transparent-types", null); if (types != null) { for (int i = 0; i < types.size(); i++) { type = MagicSpells.getItemNameResolver().resolveBlock(types.get(i)); if (type != null) { transparentTypes.add(type.getMaterial()); } } } if (transparentTypes.size() == 0) { MagicSpells.error("Spell '" + internalName + "' does not define any transparent types"); } String s = getConfigString("glass", ""); if (!s.isEmpty()) { glass = MagicSpells.getItemNameResolver().resolveBlock(s); } if (glass == null) { glass = new MagicBlockMaterial(new MaterialData(Material.GLASS)); } seers = new HashMap<String, TransparentBlockSet>(); } @Override public boolean castBuff(Player player, float power, String[] args) { seers.put(player.getName(), new TransparentBlockSet(player, range, transparentTypes)); return true; } @EventHandler(priority=EventPriority.MONITOR) public void onPlayerMove(PlayerMoveEvent event) { Player p = event.getPlayer(); if (seers.containsKey(p.getName())) { if (isExpired(p)) { turnOff(p); } else { boolean moved = seers.get(p.getName()).moveTransparency(); if (moved) { addUse(p); chargeUseCost(p); } } } } @Override public void turnOffBuff(Player player) { TransparentBlockSet t = seers.remove(player.getName()); if (t != null) { t.removeTransparency(); } } @Override protected void turnOff() { for (TransparentBlockSet tbs : seers.values()) { tbs.removeTransparency(); } seers.clear(); } private class TransparentBlockSet { Player player; Block center; int range; Set<Material> types; List<Block> blocks; Set<Chunk> chunks; public TransparentBlockSet(Player player, int range, Set<Material> types) { this.player = player; this.center = player.getLocation().getBlock(); this.range = range; this.types = types; blocks = new ArrayList<Block>(); if (unobfuscate) { chunks = new HashSet<Chunk>(); } setTransparency(); } public void setTransparency() { List<Block> newBlocks = new ArrayList<Block>(); // get blocks to set to transparent int px = center.getX(); int py = center.getY(); int pz = center.getZ(); Block block; if (!unobfuscate) { // handle normally for (int x = px - range; x <= px + range; x++) { for (int y = py - range; y <= py + range; y++) { for (int z = pz - range; z <= pz + range; z++) { block = center.getWorld().getBlockAt(x,y,z); if (types.contains(block.getType())) { Util.sendFakeBlockChange(player, block, glass); newBlocks.add(block); } } } } } else { // unobfuscate everything int dx, dy, dz; for (int x = px - range - 1; x <= px + range + 1; x++) { for (int y = py - range - 1; y <= py + range + 1; y++) { for (int z = pz - range - 1; z <= pz + range + 1; z++) { dx = Math.abs(x - px); dy = Math.abs(y - py); dz = Math.abs(z - pz); block = center.getWorld().getBlockAt(x,y,z); if (types.contains(block.getType()) && dx <= range && dy <= range && dz <= range) { Util.sendFakeBlockChange(player, block, glass); newBlocks.add(block); } else if (block.getType() != Material.AIR) { Util.restoreFakeBlockChange(player, block); } // save chunk for resending after spell ends Chunk c = block.getChunk(); chunks.add(c); } } } } // remove old transparent blocks for (Block b : blocks) { if (!newBlocks.contains(b)) { Util.restoreFakeBlockChange(player, b); } } // update block set blocks = newBlocks; } public boolean moveTransparency() { if (player.isDead()) { player = Bukkit.getServer().getPlayer(player.getName()); } Location loc = player.getLocation(); if (!center.getWorld().equals(loc.getWorld()) || center.getX() != loc.getBlockX() || center.getY() != loc.getBlockY() || center.getZ() != loc.getBlockZ()) { // moved this.center = loc.getBlock(); setTransparency(); return true; } return false; } public void removeTransparency() { for (Block b : blocks) { Util.restoreFakeBlockChange(player, b); } blocks = null; } } @Override public boolean isActive(Player player) { return seers.containsKey(player.getName()); } }