com.wasteofplastic.beaconz.BeaconListeners.java Source code

Java tutorial

Introduction

Here is the source code for com.wasteofplastic.beaconz.BeaconListeners.java

Source

/*
 * Copyright (c) 2015 tastybento
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

package com.wasteofplastic.beaconz;

import java.awt.geom.Line2D;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import java.util.Random;
import java.util.UUID;

import org.apache.commons.lang.math.NumberUtils;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.Sound;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.entity.EntityType;
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.Action;
import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.event.block.BlockDamageEvent;
import org.bukkit.event.block.BlockFromToEvent;
import org.bukkit.event.block.BlockPistonExtendEvent;
import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.event.block.BlockSpreadEvent;
import org.bukkit.event.entity.EntityExplodeEvent;
import org.bukkit.event.player.PlayerBucketEmptyEvent;
import org.bukkit.event.player.PlayerChangedWorldEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.event.world.WorldInitEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.map.MapView;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import org.bukkit.scoreboard.Team;

import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;

public class BeaconListeners extends BeaconzPluginDependent implements Listener {

    /**
     * A bi-drectional hashmap to track players standing on beaconz
     */
    private BiMap<UUID, BeaconObj> standingOn = HashBiMap.create();
    private Beaconz plugin;

    public BeaconListeners(Beaconz plugin) {
        super(plugin);
        // Work out if players are on beacons or not
        standingOn.clear();
        for (Player player : getServer().getOnlinePlayers()) {
            if (player.getWorld().equals(getBeaconzWorld())) {
                BeaconObj beacon = getRegister().getBeaconAt(player.getLocation().getBlockX(),
                        player.getLocation().getBlockZ());
                if (beacon != null) {
                    // Add players to beacon standing
                    standingOn.put(player.getUniqueId(), beacon);
                }
            }
        }
    }

    @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
    public void onInit(WorldInitEvent event) {
        //Bukkit.getLogger().info("On World Init called");
        if (event.getWorld().equals(getBeaconzWorld())) {
            if (!getBeaconzWorld().getPopulators().contains(getBlockPopulator())) {
                event.getWorld().getPopulators().add(getBlockPopulator());
            }
        }
    }

    /**
     * Handles damage to, but not breakage of a beacon. Warns players to clear a beacon before
     * capture can occur. See block break event for capture.
     * @param event
     */
    @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
    public void onBeaconDamage(BlockDamageEvent event) {
        //getLogger().info("DEBUG: " + event.getEventName());
        World world = event.getBlock().getWorld();
        if (!world.equals(getBeaconzWorld())) {
            return;
        }
        //getLogger().info("DEBUG: This is a beacon");
        Player player = event.getPlayer();
        // Get the player's team
        Team team = getScorecard().getTeam(player);
        if (team == null) {
            // TODO: Probably should put the player in a team
            event.setCancelled(true);
            player.sendMessage(ChatColor.RED + "You must be in a team to play in this world");
            return;
        }
        // Apply triangle effects
        applyEffects(player,
                getRegister().getTriangle(player.getLocation().getBlockX(), player.getLocation().getBlockZ()),
                team);

        // Check if the block is a beacon or the surrounding pyramid
        Block block = event.getBlock();
        BeaconObj beacon = getRegister().getBeacon(block);
        if (beacon == null) {
            return;
        }
        // Check for obsidian/glass breakage - i.e., capture
        if (block.getRelative(BlockFace.DOWN).getType().equals(Material.BEACON)) {
            // Check if this is a real beacon
            if (getRegister().isBeacon(block.getRelative(BlockFace.DOWN))) {
                // It is a real beacon
                // Check that the beacon is clear of blocks
                if (!beacon.isClear()) {
                    // You can't capture an uncleared beacon
                    player.sendMessage(ChatColor.RED + "Clear around the beacon first!");
                    event.setCancelled(true);
                    return;
                }
            }
        } else {
            // Attempt to break another part of the beacon
        }
    }

    /**
     * Protects the underlying beacon from any damage
     * @param event
     */
    @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
    public void onExplode(EntityExplodeEvent event) {
        //getLogger().info("DEBUG: " + event.getEventName());
        World world = event.getLocation().getWorld();
        if (!world.equals(getBeaconzWorld())) {
            return;
        }
        // Check if the block is a beacon or the surrounding pyramid and remove it from the damaged blocks
        Iterator<Block> it = event.blockList().iterator();
        while (it.hasNext()) {
            if (getRegister().isBeacon(it.next())) {
                it.remove();
            }
        }
    }

    /**
     * Prevents trees from growing above the beacon
     * @param event
     */
    @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
    public void onBlockSpread(BlockSpreadEvent event) {
        World world = event.getBlock().getWorld();
        if (!world.equals(getBeaconzWorld())) {
            //getLogger().info("DEBUG: not right world");
            return;
        }
        BeaconObj beacon = getRegister().getBeaconAt(event.getBlock().getX(), event.getBlock().getZ());
        if (beacon != null && beacon.getY() < event.getBlock().getY()) {
            switch (event.getBlock().getType()) {
            // Allow leaves to grow over the beacon
            case LEAVES:
            case LEAVES_2:
                break;
            default:
                // For everything else, make sure there is air
                event.getBlock().setType(Material.AIR);
            }

        }
    }

    /**
     * Prevents blocks from being piston pushed above a beacon or a piston being used to remove beacon blocks
     * @param event
     */
    @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
    public void onPistonPush(BlockPistonExtendEvent event) {
        World world = event.getBlock().getWorld();
        if (!world.equals(getBeaconzWorld())) {
            //getLogger().info("DEBUG: not right world");
            return;
        }
        for (Block b : event.getBlocks()) {
            // If any block is part of a beacon cancel it
            if (getRegister().isBeacon(b)) {
                event.setCancelled(true);
                return;
            }
            Block testBlock = b.getRelative(event.getDirection());
            BeaconObj beacon = getRegister().getBeaconAt(testBlock.getX(), testBlock.getZ());
            if (beacon != null && beacon.getY() < testBlock.getY()) {
                event.setCancelled(true);
            }
        }
    }

    /**
     * Prevents the tipping of liquids over the beacon
     * @param event
     */
    @EventHandler(priority = EventPriority.LOW)
    public void onBucketEmpty(final PlayerBucketEmptyEvent event) {
        //getLogger().info("DEBUG: " + event.getEventName());
        World world = event.getBlockClicked().getWorld();
        if (!world.equals(getBeaconzWorld())) {
            //getLogger().info("DEBUG: not right world");
            return;
        }
        BeaconObj beacon = getRegister().getBeaconAt(event.getBlockClicked().getX(),
                event.getBlockClicked().getZ());
        if (beacon != null && beacon.getY() <= event.getBlockClicked().getY()) {
            event.setCancelled(true);
            event.getPlayer().sendMessage(ChatColor.RED + "You cannot place liquids above a beacon!");
        }
    }

    /**
     * Puts player into the Beaconz scoreboard if they are in the Beaconz world
     * @param event
     */
    @EventHandler(priority = EventPriority.LOW)
    public void onJoin(final PlayerJoinEvent event) {
        if (event.getPlayer().getWorld().equals(getBeaconzWorld())) {
            // Assign the player to the scoreboard
            final Player player = event.getPlayer();
            final UUID playerUUID = player.getUniqueId();
            player.setScoreboard(getScorecard().getScoreboard());
            // If the player is not in a team, assign one
            // TODO
            Team team = getScorecard().getTeam(player);
            // Teleport the player to the right spot
            player.teleport(getScorecard().getTeamSpawnPoint(team));
            // Check messages
            // Load any messages for the player
            // plugin.getLogger().info("DEBUG: Checking messages for " +
            // player.getName());
            final List<String> messages = getMessages().getMessages(playerUUID);
            if (messages != null) {
                // plugin.getLogger().info("DEBUG: Messages waiting!");
                getServer().getScheduler().runTaskLater(getBeaconzPlugin(), new Runnable() {
                    @Override
                    public void run() {
                        player.sendMessage(ChatColor.AQUA + "Beaconz News");
                        int i = 1;
                        for (String message : messages) {
                            player.sendMessage(i++ + ": " + message);
                        }
                        // Clear the messages
                        getMessages().clearMessages(playerUUID);
                    }
                }, 40L);
            } // else {
              // plugin.getLogger().info("no messages");
              // }
        }
    }

    @EventHandler(priority = EventPriority.HIGH)
    public void onWorldEnter(final PlayerChangedWorldEvent event) {
        if (event.getPlayer().getWorld().equals(getBeaconzWorld())) {
            event.getPlayer().setScoreboard(getScorecard().getScoreboard());
        }
    }

    @EventHandler(priority = EventPriority.LOW)
    public void onWorldExit(final PlayerChangedWorldEvent event) {
        // Exiting Beaconz world
        if (event.getFrom().equals((getBeaconzWorld()))) {
            // Remove player from map and remove his scoreboard
            standingOn.remove(event.getPlayer().getUniqueId());
            event.getPlayer().setScoreboard(Bukkit.getServer().getScoreboardManager().getNewScoreboard());
        }
    }

    /**
     * Prevents liquid flowing over the beacon beam
     * @param event
     */
    @EventHandler(priority = EventPriority.LOW)
    public void onLiquidFlow(final BlockFromToEvent event) {
        //getLogger().info("DEBUG: " + event.getEventName());
        World world = event.getBlock().getWorld();
        if (!world.equals(getBeaconzWorld())) {
            //getLogger().info("DEBUG: not right world");
            return;
        }
        // Only bother with horizontal flows
        if (event.getToBlock().getX() != event.getBlock().getX()
                || event.getToBlock().getZ() != event.getBlock().getZ()) {
            //getLogger().info("DEBUG: " + event.getEventName());
            BeaconObj beacon = getRegister().getBeaconAt(event.getToBlock().getX(), event.getToBlock().getZ());
            if (beacon != null && beacon.getY() < event.getToBlock().getY()) {
                event.setCancelled(true);
            }
        }
    }

    /**
     * Handles placing of blocks around a beacon
     * @param event
     */
    @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
    public void onBlockPlace(BlockPlaceEvent event) {
        //getLogger().info("DEBUG: " + event.getEventName());
        World world = event.getBlock().getWorld();
        if (!world.equals(getBeaconzWorld())) {
            //getLogger().info("DEBUG: not right world");
            return;
        }
        Player player = event.getPlayer();
        // Get the player's team
        Team team = getScorecard().getTeam(player);
        if (team == null) {
            // TODO: Probably should put the player in a team
            event.setCancelled(true);
            player.sendMessage(ChatColor.RED + "You must be in a team to play in this world");
            return;
        }
        // Apply triangle effects
        applyEffects(player,
                getRegister().getTriangle(player.getLocation().getBlockX(), player.getLocation().getBlockZ()),
                team);
        // Stop placing blocks on a beacon
        BeaconObj beacon = getRegister().getBeaconAt(event.getBlock().getX(), event.getBlock().getZ());
        if (beacon != null && beacon.getY() < event.getBlock().getY()) {
            event.setCancelled(true);
            event.getPlayer().sendMessage(ChatColor.RED + "You cannot build on top of a beacon!");
            return;
        }
    }

    /**
     * Handle breakage of the top part of a beacon
     * @param event
     */
    @SuppressWarnings("deprecation")
    @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
    public void onBeaconBreak(BlockBreakEvent event) {
        //getLogger().info("DEBUG: " + event.getEventName());
        World world = event.getBlock().getWorld();
        if (!world.equals(getBeaconzWorld())) {
            //getLogger().info("DEBUG: not right world");
            return;
        }
        //getLogger().info("DEBUG: This is a beacon");
        Player player = event.getPlayer();
        // Get the player's team
        Team team = getScorecard().getTeam(player);
        if (team == null) {
            // TODO: Probably should put the player in a team
            event.setCancelled(true);
            player.sendMessage(ChatColor.RED + "You must be in a team to play in this world");
            return;
        }
        // Apply triangle effects
        applyEffects(player,
                getRegister().getTriangle(player.getLocation().getBlockX(), player.getLocation().getBlockZ()),
                team);

        // Check if the block is a beacon or the surrounding pyramid
        Block block = event.getBlock();
        BeaconObj beacon = getRegister().getBeacon(block);
        if (beacon == null) {
            return;
        }
        // Cancel any breakage
        event.setCancelled(true);
        // Check for obsidian/glass breakage - i.e., capture
        if (block.getRelative(BlockFace.DOWN).getType().equals(Material.BEACON)) {
            //getLogger().info("DEBUG:beacon below");
            // Check if this is a real beacon
            if (getRegister().isBeacon(block.getRelative(BlockFace.DOWN))) {
                //getLogger().info("DEBUG: registered beacon");
                // It is a real beacon
                if (block.getType().equals(Material.OBSIDIAN)) {
                    // Check that the beacon is clear of blocks
                    if (!beacon.isClear()) {
                        // You can't capture an uncleared beacon
                        player.sendMessage(ChatColor.RED + "Clear around the beacon first!");
                        event.setCancelled(true);
                        return;
                    }
                    //getLogger().info("DEBUG: obsidian");
                    //Claiming unowned beacon
                    block.setType(getScorecard().getBlockID(team).getItemType());
                    block.setData(getScorecard().getBlockID(team).getData());
                    // Register the beacon to this team
                    getRegister().setBeaconOwner(beacon, team);
                    player.sendMessage(ChatColor.GREEN + "You captured a beacon!");
                } else {
                    //getLogger().info("DEBUG: another block");
                    Team beaconTeam = beacon.getOwnership();
                    if (beaconTeam != null) {
                        //getLogger().info("DEBUG: known team block");
                        if (team.equals(beaconTeam)) {
                            // You can't destroy your own beacon
                            player.sendMessage(ChatColor.RED + "You cannot destroy your own beacon");
                            event.setCancelled(true);
                            return;
                        }
                        // Check that the beacon is clear of blocks
                        if (!beacon.isClear()) {
                            // You can't capture an uncleared beacon
                            player.sendMessage(ChatColor.RED + "Clear around the beacon first!");
                            event.setCancelled(true);
                            return;
                        }
                        // Enemy team has lost a beacon!
                        // Taunt other teams
                        getMessages().tellOtherTeams(team, ChatColor.RED + team.getDisplayName()
                                + " team destroyed " + beaconTeam.getDisplayName() + "'s beacon!");
                        getMessages().tellTeam(player.getUniqueId(), player.getDisplayName() + " destroyed one of "
                                + beaconTeam.getDisplayName() + "'s beacons!");
                        player.sendMessage(ChatColor.GREEN + "You destroyed " + beaconTeam.getDisplayName()
                                + " team's beacon!");

                        getRegister().removeBeaconOwnership(beacon);
                        block.setType(Material.OBSIDIAN);
                        event.setCancelled(true);
                        // Remove any standers
                        if (standingOn.containsValue(beacon)) {
                            standingOn.inverse().remove(beacon);
                        }
                    } else {
                        getRegister().removeBeaconOwnership(beacon);
                        block.setType(Material.OBSIDIAN);
                        event.setCancelled(true);
                        getLogger().info("DEBUG: unknown team block");
                    }
                }
            }
        } else {
            // Attempt to break another part of the beacon
            // Only do on owned beacons
            if (beacon.getOwnership() != null) {
                // Check for cool down, if it's still cooling down, don't do anything
                if (beacon.isNewBeacon()
                        || System.currentTimeMillis() > beacon.getHackTimer() + Settings.mineCoolDown) {
                    // Give something to the player if they have enough experience
                    // Remove experience
                    //getLogger().info("DEBUG: player has " + player.getTotalExperience() + " and needs " + Settings.beaconMineExpRequired);
                    if (!testForExp(player, Settings.beaconMineExpRequired)) {
                        player.sendMessage(
                                ChatColor.RED + "You do not have enough experience to mine this beacon!");
                        return;
                    }
                    Random rand = new Random();
                    //getLogger().info("DEBUG: random number = " + value);
                    if (beacon.getOwnership().equals(getScorecard().getTeam(player))) {
                        // Own team
                        int value = rand.nextInt(Settings.teamGoodies.lastKey()) + 1;
                        Entry<Integer, ItemStack> en = Settings.teamGoodies.floorEntry(value);
                        if (en != null && en.getValue() != null) {
                            player.getWorld().dropItemNaturally(event.getBlock().getLocation(), en.getValue());
                            if (rand.nextInt(100) < Settings.beaconMineExhaustChance) {
                                beacon.resetHackTimer();
                                player.sendMessage(ChatColor.GREEN + "Success! Beacon is exhausted. Try again in "
                                        + (Settings.mineCoolDown / 60000) + " minute(s)");
                            } else {
                                player.sendMessage(ChatColor.GREEN + "Success!");
                            }
                        } else {
                            player.getWorld().spawnEntity(player.getLocation(), EntityType.ENDERMITE);
                            player.sendMessage(ChatColor.RED + "Failure!");
                        }
                    } else {
                        // Enemy
                        int value = rand.nextInt(Settings.enemyGoodies.lastKey()) + 1;
                        Entry<Integer, ItemStack> en = Settings.enemyGoodies.floorEntry(value);
                        if (en != null && en.getValue() != null) {
                            player.getWorld().dropItemNaturally(event.getBlock().getLocation(), en.getValue());
                            if (rand.nextInt(100) < Settings.beaconMineExhaustChance) {
                                beacon.resetHackTimer();
                                player.sendMessage(ChatColor.GREEN + "Success! Beacon is exhausted. Try again in "
                                        + (Settings.mineCoolDown / 60000) + " minute(s)");
                            } else {
                                player.sendMessage(ChatColor.GREEN + "Success!");
                            }
                        } else {
                            player.getWorld().spawnEntity(player.getLocation(), EntityType.ENDERMITE);
                            player.sendMessage(ChatColor.RED + "Failure!");
                        }
                    }
                } else {
                    // Damage player
                    //getLogger().info("DEBUG: hack cooldown " + Settings.overHackEffects);
                    int num = (int) (beacon.getHackTimer() + Settings.mineCoolDown - System.currentTimeMillis())
                            / 50;
                    for (String effect : Settings.minePenalty) {
                        String[] split = effect.split(":");
                        if (split.length == 2) {
                            int amplifier = 1;
                            if (NumberUtils.isNumber(split[1])) {
                                amplifier = Integer.valueOf(split[1]);
                                //getLogger().info("DEBUG: Amplifier is " + amplifier);
                            }
                            PotionEffectType potionEffectType = PotionEffectType.getByName(split[0]);
                            if (potionEffectType != null) {
                                player.addPotionEffect(new PotionEffect(potionEffectType, num, amplifier));
                                //getLogger().info("DEBUG: Applying " + potionEffectType.toString() + ":" + amplifier + " for " + num + " ticks");
                            }
                        } else {
                            getLogger().warning("Unknown hack cooldown effect" + effect);
                        }

                    }
                }
            }
        }
    }

    /**
     * Handles the event of hitting a beacon with paper or a map
     * @param event
     */
    @SuppressWarnings("deprecation")
    @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
    public void onPaperMapUse(final PlayerInteractEvent event) {
        //getLogger().info("DEBUG: paper map " + event.getEventName());
        if (Settings.pairLinking) {
            // Not used if pair linking is used
            return;
        }
        if (!event.hasItem()) {
            return;
        }
        if (!event.getItem().getType().equals(Material.PAPER) && !event.getItem().getType().equals(Material.MAP)) {
            return;
        }
        if (!event.getAction().equals(Action.RIGHT_CLICK_BLOCK)) {
            return;
        }
        World world = event.getClickedBlock().getWorld();
        if (!world.equals(getBeaconzWorld())) {
            //getLogger().info("DEBUG: not right world");
            return;
        }
        Player player = event.getPlayer();
        // Get the player's team
        Team team = getScorecard().getTeam(player);
        if (team == null) {
            // TODO: Probably should put the player in a team
            event.setCancelled(true);
            player.sendMessage(ChatColor.RED + "You must be in a team to play in this world");
            return;
        }
        // Apply triangle effects
        applyEffects(player,
                getRegister().getTriangle(player.getLocation().getBlockX(), player.getLocation().getBlockZ()),
                team);

        // Check if the block is a beacon or the surrounding pyramid
        Block b = event.getClickedBlock();
        final BeaconObj beacon = getRegister().getBeacon(b);
        if (beacon == null) {
            //getLogger().info("DEBUG: not a beacon");
            return;
        }
        // Check the team
        if (beacon.getOwnership() == null || !beacon.getOwnership().equals(team)) {
            player.sendMessage(ChatColor.RED + "You must capture this beacon first!");
            event.setCancelled(true);
            return;
        }
        if (event.getItem().getType().equals(Material.PAPER)) {
            // Make a map!
            player.sendMessage(
                    ChatColor.GREEN + "You made a beacon map! Take it to another beacon to link them up!");
            int amount = event.getItem().getAmount() - 1;
            MapView map = Bukkit.createMap(getBeaconzWorld());
            //map.setWorld(getBeaconzWorld());
            map.setCenterX(beacon.getX());
            map.setCenterZ(beacon.getZ());
            map.getRenderers().clear();
            map.addRenderer(new BeaconMap(getBeaconzPlugin()));
            event.getItem().setType(Material.MAP);
            event.getItem().setAmount(1);
            event.getItem().setDurability(map.getId());
            // Each map is unique and the durability defines the map ID, register it
            getRegister().addBeaconMap(map.getId(), beacon);
            //getLogger().info("DEBUG: beacon id = " + beacon.getId());
            if (amount > 0) {
                HashMap<Integer, ItemStack> leftOver = player.getInventory()
                        .addItem(new ItemStack(Material.PAPER, amount));
                if (!leftOver.isEmpty()) {
                    for (ItemStack stack : leftOver.values()) {
                        player.getLocation().getWorld().dropItemNaturally(player.getLocation(), stack);
                    }
                }
            }
            ItemMeta meta = event.getItem().getItemMeta();
            meta.setDisplayName("Beacon map for " + beacon.getName());
            event.getItem().setItemMeta(meta);
            // Stop the beacon inventory opening
            event.setCancelled(true);
            return;
        } else {
            // Map!
            BeaconObj mappedBeacon = getRegister().getBeaconMap(event.getItem().getDurability());
            if (mappedBeacon == null) {
                // This is not a beacon map
                return;
            }
            event.setCancelled(true);
            if (Settings.linkDistance >= 0 && Settings.expDistance > 0) {
                // Check if the player has sufficient experience to link the beacons
                double distance = beacon.getLocation().distance(mappedBeacon.getLocation());
                distance -= Settings.linkDistance;
                if (distance > 0) {
                    if (!testForExp(player, (int) (distance / Settings.expDistance))) {
                        player.sendMessage(
                                ChatColor.RED + "You do not have enough experience to link to this beacon!");
                        player.sendMessage(ChatColor.RED + "You can link up to "
                                + (int) (Settings.expDistance * player.getTotalExperience()) + " blocks away.");
                        player.sendMessage(ChatColor.RED + "This beacon is " + (int) distance + " blocks away.");
                        return;
                    }
                }
            }
            if (linkBeacons(player, team, beacon, mappedBeacon)) {
                player.sendMessage(ChatColor.GREEN + "The map disintegrates!");
                player.setItemInHand(null);
            }
        }
    }

    /**
     * Tries to link two beacons
     * @param player
     * @param team
     * @param beacon
     * @param otherBeacon
     * @return true if link is made successfully
     */
    private boolean linkBeacons(Player player, Team team, BeaconObj beacon, BeaconObj otherBeacon) {
        if (beacon.equals(otherBeacon)) {
            player.sendMessage(ChatColor.RED + "You cannot link a beacon to itself!");
            return false;
        }
        if (beacon.getNumberOfLinks() == 8) {
            player.sendMessage(ChatColor.RED + "This beacon already has 8 outbound links!");
            return false;
        }
        // Check if this link already exists
        if (beacon.getLinks().contains(otherBeacon)) {
            player.sendMessage(ChatColor.RED + "Link already exists!");
            return false;
        }
        // Proposed link
        Line2D proposedLink = new Line2D.Double(beacon.getLocation(), otherBeacon.getLocation());
        // Check if the link crosses opposition team's links
        //getLogger().info("DEBUG: Check if the link crosses opposition team's links");
        for (Line2D line : getRegister().getEnemyLinks(team)) {
            //getLogger().info("DEBUG: checking line " + line.getP1() + " to " + line.getP2());
            if (line.intersectsLine(proposedLink)) {
                player.sendMessage(ChatColor.RED + "Link cannot cross enemy link!");
                return false;
            }
        }

        // Link the two beacons!
        LinkResult result = beacon.addOutboundLink(otherBeacon);
        if (result.isSuccess()) {
            player.sendMessage(ChatColor.GREEN + "Link created!");
            player.sendMessage(ChatColor.GREEN + "This beacon now has " + beacon.getNumberOfLinks() + " links");
            player.getWorld().playSound(player.getLocation(), Sound.FIREWORK_LARGE_BLAST, 1F, 1F);
            player.getWorld().spawnEntity(player.getLocation(), EntityType.EXPERIENCE_ORB);
            if (Settings.pairLinking) {
                // Tell the other player if it was done via a pairing
                if (standingOn.containsValue(otherBeacon)) {
                    Player otherPlayer = getServer().getPlayer(standingOn.inverse().get(otherBeacon));
                    if (otherPlayer != null) {
                        otherPlayer.sendMessage(ChatColor.GREEN + "Link created!");

                        otherPlayer.getWorld().playSound(otherPlayer.getLocation(), Sound.FIREWORK_LARGE_BLAST, 1F,
                                1F);
                        otherPlayer.getWorld().spawnEntity(otherPlayer.getLocation(), EntityType.EXPERIENCE_ORB);
                    }
                    // Tell the team
                    getMessages().tellTeam(player.getUniqueId(), player.getDisplayName() + ChatColor.GREEN + " and "
                            + otherPlayer.getDisplayName() + ChatColor.GREEN + " created a link!");
                    // Taunt other teams
                    getMessages().tellOtherTeams(team,
                            ChatColor.GOLD + team.getDisplayName() + " team made a link!");
                }
            } else {
                // Tell the team
                getMessages().tellTeam(player.getUniqueId(),
                        player.getDisplayName() + ChatColor.GREEN + " created a link!");
            }
        } else {
            player.sendMessage(ChatColor.RED + "Link could not be created!");
            return false;
        }
        if (result.getFieldsMade() > 0) {
            if (result.getFieldsMade() == 1) {
                player.sendMessage(
                        ChatColor.GREEN + "Triangle created! New score = " + getScorecard().getScore(team, "area"));
                getMessages().tellTeam(player.getUniqueId(), player.getDisplayName() + ChatColor.GREEN
                        + " created a triangle! New team score = " + getScorecard().getScore(team, "area"));
                // Taunt other teams
                getMessages().tellOtherTeams(team, ChatColor.RED + team.getDisplayName() + " team made a tringle!");
            } else {
                player.sendMessage(ChatColor.GREEN + String.valueOf(result.getFieldsMade())
                        + " triangles created! New score = " + getScorecard().getScore(team, "area"));
                getMessages().tellTeam(player.getUniqueId(),
                        player.getDisplayName() + ChatColor.GREEN + " created "
                                + String.valueOf(result.getFieldsMade()) + " triangles! New team score = "
                                + getScorecard().getScore(team, "area"));
                // Taunt other teams
                getMessages().tellOtherTeams(team, ChatColor.RED + team.getDisplayName() + " team made "
                        + String.valueOf(result.getFieldsMade()) + " triangles!");
            }
            for (int i = 0; i < result.getFieldsMade(); i++) {
                player.getWorld().spawnEntity(player.getLocation(), EntityType.EXPERIENCE_ORB);
            }
        }
        if (result.getFieldsFailedToMake() > 0) {
            if (result.getFieldsFailedToMake() == 1) {
                player.sendMessage(
                        ChatColor.RED + "One triangle could not be created because of overlapping enemy elements!");
            } else {
                player.sendMessage(ChatColor.RED + String.valueOf(result.getFieldsFailedToMake())
                        + " triangle could not be created because of overlapping enemy elements!");
            }
        }
        return true;
    }

    @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
    public void onPlayerMove(PlayerMoveEvent event) {
        World world = event.getTo().getWorld();
        if (!world.equals(getBeaconzWorld())) {
            return;
        }
        // Only proceed if there's been a change in X or Z coords
        if (event.getFrom().getBlockX() == event.getTo().getBlockX()
                && event.getFrom().getBlockZ() == event.getTo().getBlockZ()) {
            return;
        }
        // Get the player's team
        Team team = getScorecard().getTeam(event.getPlayer());
        if (team == null) {
            return;
        }
        // Run the following if players can create links by standing on them in pairs (or more)
        if (Settings.pairLinking) {
            // Check if player is standing on a beacon
            BeaconObj beacon = getRegister().getBeaconAt(event.getTo().getBlockX(), event.getTo().getBlockZ());
            if (beacon != null) {
                // Check if the beacon is captured by this team
                if (beacon.getOwnership() != null && beacon.getOwnership().equals(team)) {
                    // Check if they are on the same beacon
                    BeaconObj currentBeacon = standingOn.get(event.getPlayer().getUniqueId());
                    if (currentBeacon == null || !currentBeacon.equals(beacon)) {
                        standingOn.remove(event.getPlayer().getUniqueId());
                        // Check if any other team players are on beacons and link to them
                        for (UUID others : standingOn.keySet()) {
                            Player player = getServer().getPlayer(others);
                            if (player != null) {
                                // Check team
                                if (getScorecard().getTeam(player).equals(team)) {
                                    // Only do if beacons are different locations
                                    if (player.getLocation().getBlockX() != event.getTo().getBlockX()
                                            && player.getLocation().getBlockZ() != event.getTo().getBlockZ()) {
                                        // Check if this link already exists
                                        if (!beacon.getLinks().contains(standingOn.get(others))) {
                                            // Try to link
                                            linkBeacons(event.getPlayer(), team, beacon, standingOn.get(others));
                                        }
                                    }
                                }
                            }
                        }
                        // Forceput removes any other player's on this beacon
                        standingOn.forcePut(event.getPlayer().getUniqueId(), beacon);
                    }
                }
            } else {
                // Remove player from map
                standingOn.remove(event.getPlayer().getUniqueId());
            }
        }
        // Check the From
        List<TriangleField> from = getRegister().getTriangle(event.getFrom().getBlockX(),
                event.getFrom().getBlockZ());
        // Check the To
        List<TriangleField> to = getRegister().getTriangle(event.getTo().getBlockX(), event.getTo().getBlockZ());
        // Outside any field
        if (from.isEmpty() && to.isEmpty()) {
            return;
        }
        // Check if to is not a triangle
        if (to.size() == 0) {
            // Leaving a control triangle
            event.getPlayer().sendMessage("Leaving " + from.get(0).getOwner().getDisplayName() + "'s control area");
            return;
        }
        // Apply triangle effects
        applyEffects(event.getPlayer(), to, team);

        // Entering a field, or moving to a stronger field
        if (from.size() < to.size()) {
            event.getPlayer().sendMessage("Now entering " + to.get(0).getOwner().getDisplayName()
                    + "'s area of control level " + to.size());
            return;
        }
        if (to.size() < from.size()) {
            event.getPlayer().sendMessage(
                    to.get(0).getOwner().getDisplayName() + "'s control level dropping to " + to.size());
            return;
        }
    }

    /**
     * Applies triangle effects to a player
     * @param player
     * @param to
     * @param team
     */
    private void applyEffects(Player player, List<TriangleField> to, Team team) {
        if (to.isEmpty() || team == null) {
            return;
        }
        // Apply bad stuff
        // Enemy team
        Team triangleOwner = to.get(0).getOwner();
        Collection<PotionEffect> effects = new ArrayList<PotionEffect>();
        if (triangleOwner != null && !triangleOwner.equals(team)) {
            for (int i = 0; i <= to.size(); i++) {
                if (Settings.enemyFieldEffects.containsKey(i)) {
                    effects.addAll(Settings.enemyFieldEffects.get(i));
                }
            }
            player.addPotionEffects(effects);
        }
        effects.clear();
        // Friendly triangle
        if (triangleOwner != null && triangleOwner.equals(team)) {
            for (int i = 0; i <= to.size(); i++) {
                if (Settings.friendlyFieldEffects.containsKey(i)) {
                    effects.addAll(Settings.friendlyFieldEffects.get(i));
                }
            }
            player.addPotionEffects(effects);
        }
    }

    /**
     * Tests if a player has the required experience to perform the action. If so, the experience
     * is deducted. This function updates the client's UI exp bar.
     * @param player
     * @param xpRequired
     * @return true if sufficient experience points otherwise false
     */
    public boolean testForExp(Player player, int xpRequired) {
        int xp = player.getTotalExperience();
        if (xp >= xpRequired) {
            int total = player.getTotalExperience() - xpRequired;
            player.setTotalExperience(total);
            player.setLevel(0);
            player.setExp(0);
            for (; total > player.getExpToLevel();) {
                total -= player.getExpToLevel();
                player.setLevel(player.getLevel() + 1);
            }
            float exp = (float) total / (float) player.getExpToLevel();
            player.setExp(exp);
            return true;
        }
        return false;
    }
}