package com.nisovin.magicspells.spells.buff; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; 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.Listener; import org.bukkit.event.block.BlockBreakEvent; import org.bukkit.event.player.PlayerMoveEvent; import org.bukkit.event.player.PlayerPortalEvent; import org.bukkit.event.player.PlayerTeleportEvent; import org.bukkit.util.Vector; import com.nisovin.magicspells.MagicSpells; import com.nisovin.magicspells.spells.BuffSpell; import com.nisovin.magicspells.util.BlockUtils; import com.nisovin.magicspells.util.MagicConfig; public class WalkwaySpell extends BuffSpell { private Material material; private int size; private boolean cancelOnTeleport; private HashMap<String, Platform> platforms; private WalkwayListener listener; public WalkwaySpell(MagicConfig config, String spellName) { super(config, spellName); material = MagicSpells.getItemNameResolver().resolveBlock(getConfigString("platform-type", "wood")).getMaterial(); size = getConfigInt("size", 6); cancelOnTeleport = getConfigBoolean("cancel-on-teleport", true); platforms = new HashMap<String, Platform>(); } public void initialize() { super.initialize(); if (cancelOnTeleport) { registerEvents(new TeleportListener()); } } @Override public boolean castBuff(Player player, float power, String[] args) { platforms.put(player.getName(), new Platform(player, material, size)); registerListener(); return true; } private void registerListener() { if (listener == null) { listener = new WalkwayListener(); registerEvents(listener); } } private void unregisterListener() { if (listener != null && platforms.size() == 0) { unregisterEvents(listener); listener = null; } } public class WalkwayListener implements Listener { @EventHandler(priority=EventPriority.MONITOR) public void onPlayerMove(PlayerMoveEvent event) { Platform carpet = platforms.get(event.getPlayer().getName()); if (carpet != null) { boolean moved = carpet.move(); if (moved) { addUseAndChargeCost(event.getPlayer()); } } } @EventHandler(ignoreCancelled=true) public void onBlockBreak(BlockBreakEvent event) { for (Platform platform : platforms.values()) { if (platform.blockInPlatform(event.getBlock())) { event.setCancelled(true); return; } } } } public class TeleportListener implements Listener { @EventHandler(priority=EventPriority.MONITOR, ignoreCancelled=true) public void onPlayerTeleport(PlayerTeleportEvent event) { if (platforms.containsKey(event.getPlayer().getName())) { if (!event.getFrom().getWorld().getName().equals(event.getTo().getWorld().getName()) || event.getFrom().toVector().distanceSquared(event.getTo().toVector()) > 50*50) { turnOff(event.getPlayer()); } } } @EventHandler(priority=EventPriority.MONITOR, ignoreCancelled=true) public void onPlayerPortal(PlayerPortalEvent event) { if (platforms.containsKey(event.getPlayer().getName())) { turnOff(event.getPlayer()); } } } @Override public void turnOffBuff(Player player) { Platform platform = platforms.remove(player.getName()); if (platform != null) { platform.remove(); unregisterListener(); } } @Override protected void turnOff() { for (Platform platform : platforms.values()) { platform.remove(); } platforms.clear(); unregisterListener(); } private class Platform { private Player player; private Material material; private int size; private List<Block> platform; private int prevX; private int prevZ; private int prevDirX; private int prevDirY; private int prevDirZ; public Platform(Player player, Material material, int size) { this.player = player; this.material = material; this.size = size; this.platform = new ArrayList<Block>(); move(); } public boolean move() { Block origin = player.getLocation().subtract(0,1,0).getBlock(); int x = origin.getX(); int z = origin.getZ(); int dirX = 0; int dirY = 0; int dirZ = 0; Vector dir = player.getLocation().getDirection().setY(0).normalize(); if (dir.getX() > .7) { dirX = 1; } else if (dir.getX() < -.7) { dirX = -1; } else { dirX = 0; } if (dir.getZ() > .7) { dirZ = 1; } else if (dir.getZ() < -.7) { dirZ = -1; } else { dirZ = 0; } double pitch = player.getLocation().getPitch(); if (prevDirY == 0) { if (pitch < -40) { dirY = 1; } else if (pitch > 40) { dirY = -1; } else { dirY = prevDirY; } } else if (prevDirY == 1 && pitch > -10) { dirY = 0; } else if (prevDirY == -1 && pitch < 10) { dirY = 0; } else { dirY = prevDirY; } if (x != prevX || z != prevZ || dirX != prevDirX || dirY != prevDirY || dirZ != prevDirZ) { if (origin.getType() == Material.AIR) { // check for weird stair positioning Block up = origin.getRelative(0,1,0); if (up != null && ((material == Material.WOOD && up.getType() == Material.WOOD_STAIRS) || (material == Material.COBBLESTONE && up.getType() == Material.COBBLESTONE_STAIRS))) { origin = up; } else { // allow down movement when stepping out over an edge Block down = origin.getRelative(0,-1,0); if (down != null && down.getType() != Material.AIR) { origin = down; } } } drawCarpet(origin, dirX, dirY, dirZ); prevX = x; prevZ = z; prevDirX = dirX; prevDirY = dirY; prevDirZ = dirZ; return true; } return false; } public boolean blockInPlatform(Block block) { return platform.contains(block); } public void remove() { for (Block b : platform) { b.setType(Material.AIR); } } public void drawCarpet(Block origin, int dirX, int dirY, int dirZ) { // determine block type and maybe stair direction Material mat = material; byte data = 0; if ((material == Material.WOOD || material == Material.COBBLESTONE) && dirY != 0) { boolean changed = false; if (dirY == -1) { if (dirX == -1 && dirZ == 0) { data = 0; changed = true; } else if (dirX == 1 && dirZ == 0) { data = 1; changed = true; } else if (dirZ == -1 && dirX == 0) { data = 2; changed = true; } else if (dirZ == 1 && dirX == 0) { data = 3; changed = true; } } else if (dirY == 1) { if (dirX == -1 && dirZ == 0) { data = 1; changed = true; } else if (dirX == 1 && dirZ == 0) { data = 0; changed = true; } else if (dirZ == -1 && dirX == 0) { data = 3; changed = true; } else if (dirZ == 1 && dirX == 0) { data = 2; changed = true; } } if (changed) { if (material == Material.WOOD) { mat = Material.WOOD_STAIRS; } else if (material == Material.COBBLESTONE) { mat = Material.COBBLESTONE_STAIRS; } } } // get platform blocks List<Block> blocks = new ArrayList<Block>(); blocks.add(origin); // add standing block for (int i = 1; i < size; i++) { // add blocks ahead Block b = origin.getRelative(dirX*i, dirY*i, dirZ*i); if (b != null) { blocks.add(b); } } // remove old blocks Iterator<Block> iter = platform.iterator(); while (iter.hasNext()) { Block b = iter.next(); if (!blocks.contains(b)) { b.setType(Material.AIR); iter.remove(); } } // set new blocks for (Block b : blocks) { if (platform.contains(b) || b.getType() == Material.AIR) { BlockUtils.setTypeAndData(b, mat, data, false); platform.add(b); } } } } @Override public boolean isActive(Player player) { return platforms.containsKey(player.getName()); } }