Guest User

Untitled

a guest
Apr 6th, 2016
35
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 5 63.04 KB | None | 0 0
  1. package com.pgcraft.spectatorplus;
  2.  
  3. import java.util.ArrayList;
  4. import java.util.Arrays;
  5. import java.util.HashMap;
  6. import java.util.LinkedList;
  7. import java.util.List;
  8. import java.util.Set;
  9. import java.util.UUID;
  10.  
  11. import org.bukkit.Bukkit;
  12. import org.bukkit.ChatColor;
  13. import org.bukkit.GameMode;
  14. import org.bukkit.Location;
  15. import org.bukkit.Material;
  16. import org.bukkit.OfflinePlayer;
  17. import org.bukkit.block.Block;
  18. import org.bukkit.command.CommandSender;
  19. import org.bukkit.command.ConsoleCommandSender;
  20. import org.bukkit.configuration.serialization.ConfigurationSerialization;
  21. import org.bukkit.entity.Damageable;
  22. import org.bukkit.entity.Player;
  23. import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
  24. import org.bukkit.inventory.Inventory;
  25. import org.bukkit.inventory.ItemStack;
  26. import org.bukkit.inventory.PlayerInventory;
  27. import org.bukkit.inventory.meta.ItemMeta;
  28. import org.bukkit.inventory.meta.PotionMeta;
  29. import org.bukkit.inventory.meta.SkullMeta;
  30. import org.bukkit.plugin.java.JavaPlugin;
  31. import org.bukkit.potion.Potion;
  32. import org.bukkit.potion.PotionEffect;
  33. import org.bukkit.potion.PotionEffectType;
  34. import org.bukkit.potion.PotionType;
  35. import org.bukkit.scoreboard.DisplaySlot;
  36. import org.bukkit.scoreboard.Scoreboard;
  37. import org.bukkit.scoreboard.ScoreboardManager;
  38. import org.bukkit.scoreboard.Team;
  39. import org.bukkit.util.Vector;
  40.  
  41. @SuppressWarnings("deprecation")
  42. public class SpectatorPlus extends JavaPlugin {
  43.  
  44.     protected HashMap <String, PlayerObject> user = new HashMap<String, PlayerObject>();
  45.  
  46.     protected final static String basePrefix = ChatColor.BLUE + "Mineblockz " + ChatColor.WHITE + "KitPvP";
  47.     protected final static String prefix = ChatColor.DARK_GRAY + "[" + basePrefix + ChatColor.DARK_GRAY + "] " + ChatColor.DARK_GRAY;
  48.  
  49.     protected double version = 2.0; // Plugin version
  50.  
  51.     protected ConsoleCommandSender console;
  52.  
  53.     protected ConfigAccessor setup = null;
  54.     protected ConfigAccessor toggles = null;
  55.     protected ConfigAccessor specs = null;
  56.    
  57.     protected SpectateCommand commands = null;
  58.    
  59.     protected ArenasManager arenasManager = null;
  60.    
  61.     private SpectateAPI api = null;
  62.  
  63.     // Manage toggles
  64.     protected boolean compass;
  65.     protected Material compassItem;
  66.     protected boolean clock;
  67.     protected Material clockItem;
  68.     protected boolean spectatorsTools;
  69.     protected Material spectatorsToolsItem;
  70.     protected boolean inspector;
  71.     protected Material inspectorItem;
  72.     protected boolean tpToDeathTool, tpToDeathToolShowCause, inspectFromTPMenu, playersHealthInTeleportationMenu, playersLocationInTeleportationMenu, specChat, scoreboard, output, death, seeSpecs, blockCmds, adminBypass, newbieMode, teleportToSpawnOnSpecChangeWithoutLobby, useSpawnCommandToTeleport, enforceArenaBoundary;
  73.    
  74.     protected SpectatorMode mode = SpectatorMode.ANY;
  75.    
  76.     protected ScoreboardManager manager = null;
  77.     protected Scoreboard board = null;
  78.     protected Team team = null;
  79.    
  80.     // Constants for inventory title names
  81.     protected final static String TELEPORTER_ANY_TITLE = ChatColor.BLACK + "Teleporter";
  82.     protected final static String TELEPORTER_ARENA_TITLE = ChatColor.BLACK + "Arena "; // (Prefix only)
  83.     protected final static String ARENA_SELECTOR_TITLE = basePrefix;
  84.     protected final static String PLAYER_STATE_TITLE = ChatColor.RESET + "'s state"; // (Suffix only)
  85.     protected final static String SPEC_TOOLS_TITLE = ChatColor.BLACK + "Spectators' tools";
  86.    
  87.     // Constants used for identification of the spectators' tools in the listener
  88.     protected final static String TOOL_NORMAL_SPEED_NAME = ChatColor.DARK_AQUA + "Normal speed";
  89.     protected final static String TOOL_SPEED_I_NAME   = ChatColor.AQUA + "Speed I";
  90.     protected final static String TOOL_SPEED_II_NAME  = ChatColor.AQUA + "Speed II";
  91.     protected final static String TOOL_SPEED_III_NAME = ChatColor.AQUA + "Speed III";
  92.     protected final static String TOOL_SPEED_IV_NAME  = ChatColor.AQUA + "Speed IV";
  93.     protected final static String TOOL_NIGHT_VISION_INACTIVE_NAME = ChatColor.GRAY + "Enable night vision";
  94.     protected final static String TOOL_NIGHT_VISION_ACTIVE_NAME = ChatColor.DARK_PURPLE + "Disable night vision";
  95.     protected final static String TOOL_TP_TO_DEATH_POINT_NAME = ChatColor.YELLOW + "Go to your death point";
  96.  
  97.     /**
  98.      * This method is not meant for public use.
  99.      */
  100.     @Override
  101.     public void onLoad() {
  102.         // Registers the Arena class as a serializable one.
  103.         ConfigurationSerialization.registerClass(Arena.class);
  104.     }
  105.    
  106.     /**
  107.      * This method is not meant for public use.
  108.      */
  109.     @Override
  110.     public void onEnable() {
  111.         setup = new ConfigAccessor(this, "setup");
  112.         toggles = new ConfigAccessor(this, "toggles");
  113.         specs = new ConfigAccessor(this, "spectators");
  114.        
  115.         console = getServer().getConsoleSender();
  116.        
  117.         arenasManager = new ArenasManager(this);
  118.         api = new SpectateAPI(this);
  119.        
  120.         try {
  121.             mode = SpectatorMode.fromString(setup.getConfig().getString("mode"));
  122.         } catch(IllegalArgumentException e) {
  123.             getLogger().warning("The spectator mode set in the config (" + setup.getConfig().getString("mode") + ") is invalid; using the ANY mode instead!");
  124.             setSpectatorMode(SpectatorMode.ANY);
  125.         }
  126.        
  127.         // Add players already online to this plugin's database
  128.         for (Player player : getServer().getOnlinePlayers()) {
  129.             user.put(player.getName(), new PlayerObject());
  130.  
  131.             // Re-enable spectate mode if necessary
  132.             if (specs.getConfig().contains(player.getName())) {
  133.                 enableSpectate(player, (CommandSender) player, true);
  134.             }
  135.         }
  136.        
  137.         reloadConfig(true); // Load config values.
  138.  
  139.         // Register event listeners
  140.         getServer().getPluginManager().registerEvents(new SpectateListener(this), this);
  141.         new SpectatorManagerTask(this).runTaskTimer(this, 20, 20);
  142.  
  143.         if(output) {
  144.             console.sendMessage(prefix + "Version " + ChatColor.AQUA + version + ChatColor.GRAY + " is enabled!");
  145.         }
  146.  
  147.         this.commands = new SpectateCommand(this);
  148.         this.getCommand("spectate").setExecutor(commands);
  149.         this.getCommand("spec").setExecutor(commands);
  150.        
  151.         SpectateCompleter completer = new SpectateCompleter(this);
  152.         this.getCommand("spectate").setTabCompleter(completer);
  153.         this.getCommand("spec").setTabCompleter(completer);
  154.     }
  155.  
  156.     /**
  157.      * This method is not meant for public use.
  158.      */
  159.     @Override
  160.     public void onDisable() {
  161.         if(output) {
  162.             console.sendMessage(prefix + "Disabling...");
  163.         }
  164.         for (Player player : getServer().getOnlinePlayers()) {
  165.             for (Player target : getServer().getOnlinePlayers()) {
  166.                 target.showPlayer(player);
  167.             }
  168.             // Disable spectate mode temporarily.
  169.             if (getPlayerData(player).spectating) {
  170.                 disableSpectate(player, console, true, true);
  171.             }
  172.         }
  173.        
  174.         // Just in case
  175.         arenasManager.save();
  176.     }
  177.  
  178.     // ---------------
  179.     //     METHODS
  180.     // ---------------
  181.  
  182.     /**
  183.      * Teleports the player to the global lobby location.
  184.      *
  185.      * @param player
  186.      * @return true if the player was  teleported, false else.
  187.      */
  188.     protected boolean spawnPlayer(Player player) {
  189.         player.setFireTicks(0);
  190.         if (setup.getConfig().getBoolean("active")) {
  191.             Location where = new Location(getServer().getWorld(setup.getConfig().getString("world")), setup.getConfig().getDouble("xPos"), setup.getConfig().getDouble("yPos"), setup.getConfig().getDouble("zPos"));
  192.             Location aboveWhere = new Location(getServer().getWorld(setup.getConfig().getString("world")), setup.getConfig().getDouble("xPos"), setup.getConfig().getDouble("yPos") + 1, setup.getConfig().getDouble("zPos"));
  193.             Location belowWhere = new Location(getServer().getWorld(setup.getConfig().getString("world")), setup.getConfig().getDouble("xPos"), setup.getConfig().getDouble("yPos") - 1, setup.getConfig().getDouble("zPos"));
  194.             if (where.getBlock().getType() != Material.AIR || aboveWhere.getBlock().getType() != Material.AIR || (belowWhere.getBlock().getType() == Material.AIR || belowWhere.getBlock().getType() == Material.LAVA || belowWhere.getBlock().getType() == Material.WATER)) {
  195.                 while (where.getBlock().getType() != Material.AIR || aboveWhere.getBlock().getType() != Material.AIR || (belowWhere.getBlock().getType() == Material.AIR || belowWhere.getBlock().getType() == Material.LAVA || belowWhere.getBlock().getType() == Material.WATER)) {
  196.                     where.setY(where.getY()+1);
  197.                     aboveWhere.setY(aboveWhere.getY()+1);
  198.                     belowWhere.setY(belowWhere.getY()+1);
  199.                     if (where.getY() > getServer().getWorld(setup.getConfig().getString("world")).getHighestBlockYAt(where)) {
  200.                         where.setY(where.getY()-2);
  201.                         aboveWhere.setY(aboveWhere.getY()-2);
  202.                         belowWhere.setY(belowWhere.getY()-2);
  203.                     }
  204.                 }
  205.             }
  206.             getPlayerData(player).teleporting = true;
  207.             player.teleport(where);
  208.             getPlayerData(player).teleporting = false;
  209.             return true;
  210.         } else {
  211.             if(teleportToSpawnOnSpecChangeWithoutLobby) {
  212.                 if(useSpawnCommandToTeleport) {
  213.                     if(getServer().getPluginCommand("spawn") != null) {
  214.                         return player.performCommand("spawn");
  215.                     }
  216.                     return false;
  217.                 }
  218.                 else {
  219.                     return player.teleport(player.getWorld().getSpawnLocation(), TeleportCause.PLUGIN);
  220.                 }
  221.             }
  222.             return false;
  223.         }
  224.     }
  225.  
  226.  
  227.     /**
  228.      * Generates a player head ItemStack to be displayed in the teleportation GUI.
  229.      *
  230.      * @param player The player
  231.      * @param hidden True if the player is a displayed hidden player.
  232.      * @return The head.
  233.      */
  234.     protected ItemStack generatePlayerHeadItem(Player player, Player inventoryViewer, boolean hidden) {
  235.         ItemStack playerhead = new ItemStack(Material.SKULL_ITEM, 1, (short) 3);
  236.         SkullMeta meta = (SkullMeta)playerhead.getItemMeta();
  237.        
  238.         meta.setOwner(player.getName());
  239.        
  240.         if(hidden) meta.setDisplayName(ChatColor.DARK_GRAY + "[HIDDEN] " + ChatColor.RESET + player.getDisplayName());
  241.         else       meta.setDisplayName(ChatColor.RESET + player.getDisplayName());
  242.        
  243.        
  244.         List<String> lore = new ArrayList<String>();
  245.        
  246.         if(playersHealthInTeleportationMenu || playersLocationInTeleportationMenu) {
  247.             if(playersHealthInTeleportationMenu) {
  248.                 lore.add(ChatColor.GRAY + "" + ((int) ((Damageable) player).getHealth()) + " " + ChatColor.WHITE + "hearts" + ChatColor.GRAY + " out of 20");
  249.             }
  250.            
  251.             if(playersLocationInTeleportationMenu) {
  252.                 if(!player.getWorld().equals(inventoryViewer.getWorld())) {
  253.                     lore.add(ChatColor.GRAY + "You and " + player.getName() + " are not in the same world.");
  254.                 }
  255.                 else {
  256.                     int distance = (int) player.getLocation().distance(inventoryViewer.getLocation());
  257.                    
  258.                     String direction = null;
  259.                     // The angle between a vector pointing to the North and a vector pointing
  260.                     // from the spectator to the player, converted in degrees, -180 to have 0° for North.
  261.                     double angle = (new Vector(0, 0, -1).angle(player.getLocation().toVector().setY(0).subtract(inventoryViewer.getLocation().toVector().setY(0)).multiply(-1)) * 180/Math.PI - 180) % 360;
  262.                     if(angle < 0) angle += 360.0;
  263.                    
  264.                     // The calculated angle is the same for two positions symmetric of each other
  265.                     // relative to the N-S axis.
  266.                     // This lead to "west" displayed for both east and west.
  267.                     if(inventoryViewer.getLocation().getX() < player.getLocation().getX()
  268.                             && 202.5 <= angle && angle < 337.5) {
  269.                         angle -= 180.0;
  270.                     }
  271.                    
  272.                     if (0 <= angle && angle < 22.5) {
  273.                         direction = "North";
  274.                     } else if (22.5 <= angle && angle < 67.5) {
  275.                         direction =  "North-east";
  276.                     } else if (67.5 <= angle && angle < 112.5) {
  277.                         direction =  "East";
  278.                     } else if (112.5 <= angle && angle < 157.5) {
  279.                         direction =  "South-east";
  280.                     } else if (157.5 <= angle && angle < 202.5) {
  281.                         direction =  "South";
  282.                     } else if (202.5 <= angle && angle < 247.5) {
  283.                         direction =  "South-west";
  284.                     } else if (247.5 <= angle && angle < 292.5) {
  285.                         direction =  "West";
  286.                     } else if (292.5 <= angle && angle < 337.5) {
  287.                         direction =  "North-west";
  288.                     } else if (337.5 <= angle && angle <= 360.0) {
  289.                         direction =  "North";
  290.                     }
  291.                    
  292.                     if(direction != null) {
  293.                         lore.add(ChatColor.WHITE + direction + ", " + distance + " meters");
  294.                     }
  295.                     else {
  296.                         lore.add(ChatColor.WHITE + "" + distance + " meters");
  297.                     }
  298.                 }
  299.             }
  300.            
  301.             lore.add(" "); // separator
  302.         }
  303.        
  304.         lore.add(ChatColor.GRAY+""+ChatColor.ITALIC+"Left click"+ ChatColor.DARK_GRAY+ChatColor.ITALIC +" to be teleported");
  305.         if(this.inspectFromTPMenu) {
  306.             lore.add(ChatColor.GRAY+""+ChatColor.ITALIC+"Right click"+ ChatColor.DARK_GRAY+ChatColor.ITALIC +" to see inventory");
  307.         }
  308.        
  309.         meta.setLore(lore);
  310.        
  311.        
  312.         playerhead.setItemMeta(meta);
  313.         return playerhead;
  314.     }
  315.    
  316.     /**
  317.      * Opens the player head GUI, to allow spectators to choose a player to teleport to.
  318.      *
  319.      * @param spectator The GUI will be open for this spectator.
  320.      * @param region The UUID of the arena to use to choose the players to display on the GUI. Null if there isn't any arena set for this player.
  321.      */
  322.     protected void showGUI(Player spectator, UUID region) {
  323.        
  324.         if (mode == SpectatorMode.ARENA && region == null) {
  325.             if(output) {
  326.                 spectator.sendMessage(prefix + "Pick an arena first using the arena selector!");
  327.             }
  328.             return;
  329.         }
  330.        
  331.         Inventory gui = null;
  332.        
  333.         LinkedList<Player> displayedSpectators = new LinkedList<Player>();
  334.         LinkedList<Player> displayedSpectatorsHidden = new LinkedList<Player>();
  335.        
  336.         for (Player player : getServer().getOnlinePlayers()) {
  337.             if (mode == SpectatorMode.ARENA) {
  338.                 if (region == null) {
  339.                     if(output) {spectator.sendMessage(prefix + "Pick an arena first using the arena selector!");}
  340.                     return;
  341.                 } else {
  342.                     Location where = player.getLocation();
  343.                     Arena currentArena = arenasManager.getArena(region);
  344.                     int pos1y = currentArena.getCorner1().getBlockY();
  345.                     int pos2y = currentArena.getCorner2().getBlockY();
  346.                     int pos1x = currentArena.getCorner1().getBlockX();
  347.                     int pos2x = currentArena.getCorner2().getBlockX();
  348.                     int pos1z = currentArena.getCorner1().getBlockZ();
  349.                     int pos2z = currentArena.getCorner2().getBlockZ();
  350.                     // pos1 should have the highest co-ords of the arena, pos2 the lowest
  351.                     if (!getPlayerData(player).spectating) {
  352.                         if (Math.floor(where.getY()) < Math.floor(pos1y) && Math.floor(where.getY()) > Math.floor(pos2y)) {
  353.                             if (Math.floor(where.getX()) < pos1x && Math.floor(where.getX()) > pos2x) {
  354.                                 if (Math.floor(where.getZ()) < pos1z && Math.floor(where.getZ()) > pos2z) {
  355.                                     if(getPlayerData(player).hideFromTp) {
  356.                                         displayedSpectatorsHidden.add(player);
  357.                                     } else {
  358.                                         displayedSpectators.add(player);
  359.                                     }
  360.                                 }
  361.                             }
  362.                         }
  363.                     }
  364.                 }
  365.             }
  366.             else if(mode == SpectatorMode.ANY
  367.                     || (mode == SpectatorMode.WORLD && player.getWorld().equals(spectator.getWorld()))) {
  368.                
  369.                 if (!getPlayerData(player).hideFromTp && !getPlayerData(player).spectating) {
  370.                     displayedSpectators.add(player);
  371.                 }
  372.                
  373.                 // Admins will still be able to see players who have used '/spec hide':
  374.                 else if (spectator.hasPermission("spectate.admin.hide.see") && !getPlayerData(player).spectating) {
  375.                     displayedSpectatorsHidden.add(player);
  376.                 }
  377.                
  378.             }
  379.         }
  380.        
  381.         Integer inventorySize = (int) Math.ceil(Double.valueOf(displayedSpectators.size()+displayedSpectatorsHidden.size())/9) * 9;
  382.         if(inventorySize == 0) {
  383.             inventorySize = 9; // Avoids an empty inventory.
  384.         }
  385.        
  386.         if(mode == SpectatorMode.ARENA) {
  387.             gui = Bukkit.getServer().createInventory(spectator, inventorySize, TELEPORTER_ARENA_TITLE + ChatColor.ITALIC + arenasManager.getArena(region).getName());
  388.         }
  389.         else { // WORLD and ANY
  390.             gui = Bukkit.getServer().createInventory(spectator, inventorySize, TELEPORTER_ANY_TITLE);
  391.         }
  392.        
  393.         // Display hidden players first (people who have used '/spec hide')
  394.         for(Player displayedSpectatorHidden : displayedSpectatorsHidden) {
  395.             gui.addItem(generatePlayerHeadItem(displayedSpectatorHidden, spectator, true));
  396.         }
  397.        
  398.         // Display normal players
  399.         for(Player displayedSpectator : displayedSpectators) {
  400.             gui.addItem(generatePlayerHeadItem(displayedSpectator, spectator, false));
  401.         }
  402.  
  403.         spectator.openInventory(gui);
  404.     }
  405.  
  406.  
  407.     /**
  408.      * Shows the arena selection GUI, full of books with the name of valid arenas.
  409.      *
  410.      * @param spectator The GUI will be open for this spectator.
  411.      */
  412.     protected void showArenaGUI(Player spectator) {
  413.         Inventory gui = Bukkit.getServer().createInventory(spectator, 27, ARENA_SELECTOR_TITLE);
  414.  
  415.  
  416.         for (Arena arena : arenasManager.getArenas()) {
  417.             ItemStack arenaBook = new ItemStack(Material.BOOK, 1);
  418.  
  419.             ItemMeta meta = (ItemMeta)arenaBook.getItemMeta();
  420.             meta.setDisplayName(arena.getName());
  421.             arenaBook.setItemMeta(meta);
  422.  
  423.             gui.addItem(arenaBook);
  424.         }
  425.  
  426.         spectator.openInventory(gui);
  427.     }
  428.    
  429.     /**
  430.      * Shows a representation of the inventory, the armour, the health, the XP, the potion effects
  431.      * and the feed state of the player.
  432.      *
  433.      * @param spectator The GUI will be open for this spectator.
  434.      * @param inventoryOwner The analyzed player is this player.
  435.      */
  436.     protected void showPlayerInventoryGUI(Player spectator, Player inventoryOwner) {
  437.        
  438.         PlayerInventory inventory = inventoryOwner.getInventory();
  439.        
  440.         // Remove item name from the inventory separator.
  441.         ItemStack separator = new ItemStack(Material.STAINED_GLASS_PANE, 1, (short) 7);
  442.         ItemMeta separatorMeta = separator.getItemMeta();
  443.         separatorMeta.setDisplayName(ChatColor.RESET+"");
  444.         separator.setItemMeta(separatorMeta);
  445.        
  446.         // + 18: a separator row, and a row with armor, XP, potion effects, health and feed level.
  447.         Inventory gui = Bukkit.getServer().createInventory(spectator, inventory.getSize() + 18, (inventoryOwner.getDisplayName().length() > 22) ? inventoryOwner.getName() : inventoryOwner.getDisplayName() + PLAYER_STATE_TITLE);
  448.         ItemStack[] GUIContent = gui.getContents();
  449.        
  450.         // Player's inventory
  451.         // The hotbar is 0-8
  452.         // The inventory is 9-35
  453.         for(int i = 9; i < inventory.getSize(); i++) {
  454.             GUIContent[i - 9] = inventory.getItem(i);
  455.         }
  456.         for(int i = 0; i < 9; i++) {
  457.             GUIContent[i + 27] = inventory.getItem(i);
  458.         }
  459.        
  460.         // Separator
  461.         for(int i = inventory.getSize(); i < inventory.getSize() + 9; i++) {
  462.             GUIContent[i] = separator;
  463.         }
  464.        
  465.         // Armor
  466.         GUIContent[inventory.getSize() +  9] = inventory.getHelmet();
  467.         GUIContent[inventory.getSize() + 10] = inventory.getChestplate();
  468.         GUIContent[inventory.getSize() + 11] = inventory.getLeggings();
  469.         GUIContent[inventory.getSize() + 12] = inventory.getBoots();
  470.        
  471.         // Separator
  472.         GUIContent[inventory.getSize() + 13] = separator;
  473.        
  474.         // XP
  475.         if(inventoryOwner.getLevel() > 0) {
  476.             GUIContent[inventory.getSize() + 14] = new ItemStack(Material.EXP_BOTTLE, inventoryOwner.getLevel());
  477.         }
  478.         else {
  479.             GUIContent[inventory.getSize() + 14] = new ItemStack(Material.EXP_BOTTLE, 1);
  480.         }
  481.        
  482.         ItemMeta xpMeta = GUIContent[inventory.getSize() + 14].getItemMeta();
  483.             xpMeta.setDisplayName(ChatColor.GREEN +""+ ChatColor.BOLD + "Experience");
  484.             List<String> lore = new ArrayList<String>();
  485.                 lore.add(ChatColor.DARK_GRAY +""+ ChatColor.ITALIC + "Level " + ChatColor.GRAY + ChatColor.ITALIC + inventoryOwner.getLevel() + ChatColor.DARK_GRAY + ChatColor.ITALIC + " (" + ChatColor.GRAY + ChatColor.ITALIC + ((int) Math.floor(inventoryOwner.getExp()*100)) + ChatColor.DARK_GRAY + ChatColor.ITALIC + "% towards level " + ChatColor.GRAY + ChatColor.ITALIC + (inventoryOwner.getLevel()+1) + ChatColor.DARK_GRAY + ChatColor.ITALIC + ")");
  486.             xpMeta.setLore(lore);
  487.         GUIContent[inventory.getSize() + 14].setItemMeta(xpMeta);
  488.        
  489.         // Potion effects
  490.         if(inventoryOwner.getActivePotionEffects().size() == 0) {
  491.             GUIContent[inventory.getSize() + 15] = new ItemStack(Material.GLASS_BOTTLE, 1);
  492.             ItemMeta meta = GUIContent[inventory.getSize() + 15].getItemMeta();
  493.             meta.setLore(Arrays.asList(ChatColor.DARK_GRAY + "" + ChatColor.ITALIC + "No active effects"));
  494.             GUIContent[inventory.getSize() + 15].setItemMeta(meta);
  495.         }
  496.         else {
  497.             GUIContent[inventory.getSize() + 15] = new Potion(PotionType.FIRE_RESISTANCE).toItemStack(1);
  498.             PotionMeta effectsMeta = (PotionMeta) GUIContent[inventory.getSize() + 15].getItemMeta();
  499.                 effectsMeta.clearCustomEffects();
  500.                 lore = new ArrayList<String>();
  501.                     lore.add(ChatColor.GRAY +""+ ChatColor.ITALIC + inventoryOwner.getActivePotionEffects().size() + ChatColor.DARK_GRAY + ChatColor.ITALIC + " active effects");
  502.                 xpMeta.setLore(lore);
  503.                 for(PotionEffect effect : inventoryOwner.getActivePotionEffects()) {
  504.                     effectsMeta.addCustomEffect(effect, true);
  505.                 }
  506.             GUIContent[inventory.getSize() + 15].setItemMeta(effectsMeta);
  507.         }
  508.        
  509.         ItemMeta effectsMeta = GUIContent[inventory.getSize() + 15].getItemMeta();
  510.             effectsMeta.setDisplayName(ChatColor.DARK_PURPLE + "" + ChatColor.BOLD + "Potion effects  ");
  511.         GUIContent[inventory.getSize() + 15].setItemMeta(effectsMeta);
  512.        
  513.         // Health
  514.         if(((Damageable) inventoryOwner).getHealth() > 0) {
  515.             GUIContent[inventory.getSize() + 16] = new ItemStack(Material.GOLDEN_APPLE, (int) Math.ceil(((Damageable) inventoryOwner).getHealth()));
  516.             ItemMeta healthMeta = GUIContent[inventory.getSize() + 16].getItemMeta();
  517.                 healthMeta.setDisplayName(ChatColor.DARK_RED + "" + ChatColor.BOLD + "Health ");
  518.                 lore = new ArrayList<String>();
  519.                     lore.add(ChatColor.GRAY +"" + ChatColor.ITALIC + (int) Math.ceil(((Damageable) inventoryOwner).getHealth()) + ChatColor.DARK_GRAY + ChatColor.ITALIC + "/20");
  520.                 healthMeta.setLore(lore);
  521.             GUIContent[inventory.getSize() + 16].setItemMeta(healthMeta);
  522.         }
  523.        
  524.         // Food level
  525.         if(inventoryOwner.getFoodLevel() > 0) {
  526.             GUIContent[inventory.getSize() + 17] = new ItemStack(Material.COOKIE, inventoryOwner.getFoodLevel());
  527.             ItemMeta foodMeta = GUIContent[inventory.getSize() + 17].getItemMeta();
  528.                 foodMeta.setDisplayName(ChatColor.GRAY + "" + ChatColor.BOLD + "Food");
  529.                 lore = new ArrayList<String>();
  530.                     lore.add(ChatColor.DARK_GRAY +""+ ChatColor.ITALIC + "Food level: " + ChatColor.GRAY + ChatColor.ITALIC + inventoryOwner.getFoodLevel() + ChatColor.DARK_GRAY + ChatColor.ITALIC + "/20");
  531.                     lore.add(ChatColor.DARK_GRAY +""+ ChatColor.ITALIC + "Saturation: " + ChatColor.GRAY + ChatColor.ITALIC + inventoryOwner.getSaturation());
  532.                 foodMeta.setLore(lore);
  533.             GUIContent[inventory.getSize() + 17].setItemMeta(foodMeta);
  534.         }
  535.        
  536.         gui.setContents(GUIContent);
  537.        
  538.         spectator.openInventory(gui);
  539.     }
  540.    
  541.    
  542.     protected void showSpectatorsOptionsGUI(Player spectator) {
  543.         Inventory gui = Bukkit.getServer().createInventory(spectator, 9, SPEC_TOOLS_TITLE);
  544.         ItemStack[] GUIContent = gui.getContents();
  545.        
  546.         // Retrieves the current speed level, and the other enabled effects
  547.         // 0 = no speed; 1 = speed I, etc.
  548.         Integer speedLevel = 0;
  549.         Boolean nightVisionActive = false;
  550.         for(PotionEffect effect : spectator.getActivePotionEffects()) {
  551.             if(effect.getType().equals(PotionEffectType.SPEED)) {
  552.                 speedLevel = effect.getAmplifier() + 1; // +1 because Speed I = amplifier 0.
  553.             }
  554.             else if(effect.getType().equals(PotionEffectType.NIGHT_VISION)) {
  555.                 nightVisionActive = true;
  556.             }
  557.         }
  558.        
  559.         List<String> activeLore = new ArrayList<String>();
  560.         activeLore.add("" + ChatColor.GRAY + ChatColor.ITALIC + "Active");
  561.        
  562.         // If a death location is registered for this player, the position of the "night vision" tool
  563.         // is not the same (6 with death point registered; 8 without).
  564.         // That's why this is defined here, not below.
  565.         // If the "tp to death" tool is disabled, the death location is not set. So it's useless to
  566.         // check this here.
  567.         Location deathPoint = getPlayerData(spectator).deathLocation;
  568.        
  569.        
  570.         // Normal speed
  571.         ItemStack normalSpeed = new ItemStack(Material.STRING);
  572.         ItemMeta meta = normalSpeed.getItemMeta();
  573.         meta.setDisplayName(TOOL_NORMAL_SPEED_NAME);
  574.         if(speedLevel == 0) {
  575.             meta.setLore(activeLore);
  576.         }
  577.         normalSpeed.setItemMeta(meta);
  578.         GUIContent[0] = normalSpeed;
  579.        
  580.         // Speed I
  581.         ItemStack speedI = new ItemStack(Material.POTION);
  582.         meta = speedI.getItemMeta();
  583.         meta.setDisplayName(TOOL_SPEED_I_NAME);
  584.         if(speedLevel == 1) {
  585.             meta.setLore(activeLore);
  586.         }
  587.         speedI.setItemMeta(meta);
  588.         GUIContent[1] = speedI;
  589.        
  590.         // Speed II
  591.         ItemStack speedII = new ItemStack(Material.POTION, 2);
  592.         meta = speedII.getItemMeta();
  593.         meta.setDisplayName(TOOL_SPEED_II_NAME);
  594.         if(speedLevel == 2) {
  595.             meta.setLore(activeLore);
  596.         }
  597.         speedII.setItemMeta(meta);
  598.         GUIContent[2] = speedII;
  599.        
  600.         // Speed III
  601.         ItemStack speedIII = new ItemStack(Material.POTION, 3);
  602.         meta = speedIII.getItemMeta();
  603.         meta.setDisplayName(TOOL_SPEED_III_NAME);
  604.         if(speedLevel == 3) {
  605.             meta.setLore(activeLore);
  606.         }
  607.         speedIII.setItemMeta(meta);
  608.         GUIContent[3] = speedIII;
  609.        
  610.         // Speed IV
  611.         ItemStack speedIV = new ItemStack(Material.POTION, 4);
  612.         meta = speedIV.getItemMeta();
  613.         meta.setDisplayName(TOOL_SPEED_IV_NAME);
  614.         if(speedLevel == 4) {
  615.             meta.setLore(activeLore);
  616.         }
  617.         speedIV.setItemMeta(meta);
  618.         GUIContent[4] = speedIV;
  619.        
  620.        
  621.         // Night vision
  622.         ItemStack nightVision = new ItemStack(Material.EYE_OF_ENDER);
  623.         meta = nightVision.getItemMeta();
  624.         if(nightVisionActive) {
  625.             nightVision.setType(Material.ENDER_PEARL);
  626.             meta.setDisplayName(TOOL_NIGHT_VISION_ACTIVE_NAME);
  627.         }
  628.         else {
  629.             meta.setDisplayName(TOOL_NIGHT_VISION_INACTIVE_NAME);
  630.         }
  631.         nightVision.setItemMeta(meta);
  632.         if(deathPoint == null) { // No "TP to death point" tool: position #8.
  633.             GUIContent[8] = nightVision;
  634.         }
  635.         else {
  636.             GUIContent[6] = nightVision;
  637.         }
  638.        
  639.        
  640.         // Teleportation to the death point
  641.         if(deathPoint != null) {
  642.             ItemStack tpToDeathPoint = new ItemStack(Material.NETHER_STAR);
  643.             meta = tpToDeathPoint.getItemMeta();
  644.             meta.setDisplayName(TOOL_TP_TO_DEATH_POINT_NAME);
  645.             // The death message is never set if it is disabled: check useless (same as above).
  646.             if(getPlayerData(spectator).lastDeathMessage != null) {
  647.                 List<String> lore = new ArrayList<String>();
  648.                 lore.add("" + ChatColor.GRAY + getPlayerData(spectator).lastDeathMessage);
  649.                 meta.setLore(lore);
  650.             }
  651.             tpToDeathPoint.setItemMeta(meta);
  652.             GUIContent[8] = tpToDeathPoint;
  653.         }
  654.        
  655.        
  656.        
  657.         gui.setContents(GUIContent);
  658.         spectator.openInventory(gui);
  659.     }
  660.  
  661.  
  662.     /**
  663.      * Teleports the spectator to the player they have chosen using "/spec p &lt;target>"
  664.      *
  665.      * @param spectator The spectator to teleport.
  666.      * @param target The spectator will be teleported at the current location of this player.
  667.      */
  668.     protected void choosePlayer(Player spectator, Player target) {
  669.         spectator.teleport(target);
  670.  
  671.         if(output) {
  672.             spectator.sendMessage(prefix + "Teleported you to " + ChatColor.AQUA + target.getName());
  673.         }
  674.     }
  675.  
  676.     /**
  677.      * Checks for problems and enables spectator mode for spectator, on behalf of sender.
  678.      *
  679.      * @param spectator The player that will be a spectator.
  680.      * @param sender The sender of the /spec on [player] command.
  681.      */
  682.     protected void enableSpectate(Player spectator, CommandSender sender) {
  683.         enableSpectate(spectator, sender, false);
  684.     }
  685.    
  686.     /**
  687.      * Checks for problems and enables spectator mode for spectator, on behalf of sender.
  688.      *
  689.      * @param spectator The player that will be a spectator.
  690.      * @param sender The sender of the /spec on [player] command.
  691.      * @param silent Will not output any messages - useful when using the API or command blocks.
  692.      *
  693.      * @since 2.0
  694.      */
  695.     protected void enableSpectate(Player spectator, CommandSender sender, boolean silent) {
  696.         enableSpectate(spectator, sender, silent, false);
  697.     }
  698.    
  699.     /**
  700.      * Checks for problems and enables spectator mode for spectator, on behalf of sender.
  701.      *
  702.      * @param spectator The player that will be a spectator.
  703.      * @param sender The sender of the /spec on [player] command.
  704.      * @param silent Will not output any messages - useful when using the API or command blocks.
  705.      * @param worldChange Was the enable spectate caused by a world change?
  706.      *
  707.      * @since 2.0
  708.      */
  709.     protected void enableSpectate(Player spectator, CommandSender sender, boolean silent, boolean worldChange) {
  710.         if (user.get(spectator.getName()).spectating) {
  711.             if (!silent) {
  712.                 // Spectator mode was already on
  713.                 if (sender instanceof Player && spectator.getName().equals(sender.getName())) {
  714.                     spectator.sendMessage(prefix + ChatColor.GRAY + "You are already spectating!");
  715.                 }
  716.                 else {
  717.                     sender.sendMessage(prefix + ChatColor.AQUA + spectator.getDisplayName() + ChatColor.GRAY + " is already spectating!");
  718.                 }
  719.             }
  720.         }
  721.  
  722.         else {
  723.             // Hide them from everyone
  724.             for (Player target : getServer().getOnlinePlayers()) {
  725.                 if(seeSpecs && getPlayerData(target).spectating) {
  726.                     spectator.showPlayer(target);
  727.                 }
  728.                 else {
  729.                     target.hidePlayer(spectator); // Hide the spectator from non-specs: if seeSpecs mode is off and the target isn't spectating
  730.                 }
  731.             }
  732.  
  733.             // Gamemode, 'ghost' and inventory
  734.             getPlayerData(spectator).oldGameMode = spectator.getGameMode();
  735.             spectator.setGameMode(GameMode.ADVENTURE);
  736.            
  737.             savePlayerInv(spectator);
  738.             getPlayerData(spectator).effects = spectator.getActivePotionEffects();
  739.             for (PotionEffect pe : spectator.getActivePotionEffects()) {
  740.                 spectator.removePotionEffect(pe.getType());
  741.             }
  742.            
  743.             spectator.setAllowFlight(true);
  744.             spectator.setFoodLevel(20);
  745.             spectator.addPotionEffect(new PotionEffect(PotionEffectType.INVISIBILITY, Integer.MAX_VALUE, 15), true);
  746.  
  747.             // Disable interaction
  748.             getPlayerData(spectator).spectating = true;
  749.  
  750.             updateSpectatorInventory(spectator);
  751.  
  752.             // Set the prefix in the tab list if the toggle is on
  753.             if (scoreboard) {
  754.                 if (spectator.getScoreboard() != null) getPlayerData(spectator).oldScoreboard = spectator.getScoreboard(); else user.get(spectator.getName()).oldScoreboard = getServer().getScoreboardManager().getMainScoreboard();
  755.                 spectator.setScoreboard(board);
  756.                 team.addPlayer(spectator);
  757.             }
  758.  
  759.             // Teleport them to the global lobby (not if world change)
  760.             if (!worldChange) spawnPlayer(spectator);
  761.  
  762.             // Manage messages if spectator mode was enabled
  763.             if (!silent) {
  764.                 if (sender instanceof Player && spectator.getName().equals(sender.getName())) {
  765.                     if(output) {
  766.                         spectator.sendMessage(prefix + ChatColor.GRAY + "Spectator mode " + ChatColor.AQUA + "enabled");
  767.                     }
  768.                 }
  769.                 else if (sender instanceof Player && !spectator.getName().equals(sender.getName())) {
  770.                     if(output) {
  771.                         spectator.sendMessage(prefix + ChatColor.GRAY  + "Spectator mode " + ChatColor.AQUA + "enabled" + ChatColor.GRAY + " by " + ChatColor.AQUA + ((Player) sender).getDisplayName());
  772.                     }
  773.                     sender.sendMessage(prefix + ChatColor.GRAY + "Spectator mode " + ChatColor.AQUA + "enabled" + ChatColor.GRAY + " for " + ChatColor.AQUA + spectator.getDisplayName());
  774.                 }
  775.                 else {
  776.                     if(output) {
  777.                         spectator.sendMessage(prefix + "Spectator mode " + ChatColor.AQUA + "enabled" + ChatColor.GRAY + " by " + ChatColor.DARK_RED + "Console");
  778.                     }
  779.                     sender.sendMessage(prefix + "Spectator mode " + ChatColor.AQUA + "enabled" + ChatColor.GRAY + " for " + ChatColor.AQUA + spectator.getDisplayName());
  780.                 }
  781.             }
  782.  
  783.             specs.getConfig().set(spectator.getName(), true);
  784.             specs.saveConfig();
  785.         }
  786.     }
  787.    
  788.     /**
  789.      * Checks for problems and disables spectator mode for spectator, on behalf of sender.
  790.      * Convenience method for {@link #disableSpectate(Player spectator, CommandSender sender, boolean silent)}
  791.      *
  792.      * @param spectator The spectator that will be a normal player.
  793.      * @param sender The sender of the /spec off [player] command.
  794.      */
  795.     protected void disableSpectate(Player spectator, CommandSender sender) {
  796.         disableSpectate(spectator, sender, false);
  797.     }
  798.    
  799.     /**
  800.      * Checks for problems and disables spectator mode for spectator, on behalf of sender.
  801.      * Convenience method for {@link #disableSpectate(Player spectator, CommandSender sender, boolean silent, boolean temp)}
  802.      *
  803.      * @param spectator The spectator that will be a normal player.
  804.      * @param sender The sender of the /spec off [player] command.
  805.      * @param silent Will not output any messages - useful when using the API or command blocks.
  806.      *
  807.      * @since 2.0
  808.      */
  809.     protected void disableSpectate(Player spectator, CommandSender sender, boolean silent) {
  810.         disableSpectate(spectator, sender, silent, false);
  811.     }
  812.    
  813.     /**
  814.      * Checks for problems and disables spectator mode for spectator, on behalf of sender.
  815.      *
  816.      * @param spectator The spectator that will be a normal player.
  817.      * @param sender The sender of the /spec off [player] command.
  818.      * @param silent Will not output any messages - useful when using the API or command blocks.
  819.      * @param temp If true, the next time the player re-logs, spectator mode will be re-enabled.
  820.      *
  821.      * @since 2.0
  822.      */
  823.     protected void disableSpectate(Player spectator, CommandSender sender, boolean silent, boolean temp) {
  824.         disableSpectate(spectator, sender, silent, temp, false);
  825.     }
  826.    
  827.     /**
  828.      * Checks for problems and disables spectator mode for spectator, on behalf of sender.
  829.      *
  830.      * @param spectator The spectator that will be a normal player.
  831.      * @param sender The sender of the /spec off [player] command.
  832.      * @param silent Will not output any messages - useful when using the API or command blocks.
  833.      * @param temp If true, the next time the player re-logs, spectator mode will be re-enabled.
  834.      * @param worldChange Was the enable spectate caused by a world change?
  835.      *
  836.      * @since 2.0
  837.      */
  838.     protected void disableSpectate(Player spectator, CommandSender sender, boolean silent, boolean temp, boolean worldChange) {
  839.         if (getPlayerData(spectator).spectating) {
  840.             // Show them to everyone
  841.             for (Player target : getServer().getOnlinePlayers()) {
  842.                 if (seeSpecs && getPlayerData(target).spectating) {
  843.                     spectator.hidePlayer(target);
  844.                 }
  845.                 target.showPlayer(spectator);
  846.             }
  847.            
  848.             // Allow interaction
  849.             getPlayerData(spectator).spectating = false;
  850.             spectator.setAllowFlight(false);
  851.             spectator.setGameMode(getPlayerData(spectator).oldGameMode);
  852.            
  853.             loadPlayerInv(spectator);
  854.            
  855.             // Restore effects
  856.             spectator.removePotionEffect(PotionEffectType.INVISIBILITY);
  857.             spectator.removePotionEffect(PotionEffectType.SPEED);
  858.             spectator.removePotionEffect(PotionEffectType.WATER_BREATHING);
  859.             spectator.removePotionEffect(PotionEffectType.NIGHT_VISION);
  860.             spectator.addPotionEffects(getPlayerData(spectator).effects);
  861.            
  862.             spectator.setFlySpeed(0.1f);
  863.            
  864.             // Remove from spec team
  865.             if (scoreboard) {
  866.                 if(getPlayerData(spectator).oldScoreboard != null) spectator.setScoreboard(getPlayerData(spectator).oldScoreboard);
  867.                 team.removePlayer(spectator);
  868.             }
  869.            
  870.             if (!worldChange) {
  871.                 removePlayerFromArena(spectator, true); // Clear the arena they were spectating in
  872.                 spawnPlayer(spectator); // Teleport to spawn
  873.             }
  874.            
  875.             if (!silent) {
  876.                 if (sender instanceof Player && spectator.getName().equals(sender.getName())) {
  877.                     if(output) {
  878.                         spectator.sendMessage(prefix + ChatColor.GRAY + "Spectator mode " + ChatColor.AQUA + "disabled");
  879.                     }
  880.                 }
  881.                 else if (sender instanceof Player && !spectator.getName().equals(sender.getName())) {
  882.                     if(output) {
  883.                         spectator.sendMessage(prefix + ChatColor.GRAY + "Spectator mode " + ChatColor.AQUA + "disabled" + ChatColor.GRAY + " by " + ChatColor.AQUA + ((Player) sender).getDisplayName());
  884.                     }
  885.                     sender.sendMessage(prefix + ChatColor.GRAY + "Spectator mode " + ChatColor.AQUA + "disabled" + ChatColor.GRAY + " for " + ChatColor.AQUA + spectator.getDisplayName());
  886.                 }
  887.                 else {
  888.                     if(output) {
  889.                         spectator.sendMessage(prefix + "Spectator mode " + ChatColor.AQUA + "disabled" + ChatColor.GRAY + " by " + ChatColor.DARK_RED + "Console");
  890.                     }
  891.                     sender.sendMessage(prefix + "Spectator mode " + ChatColor.AQUA + "disabled" + ChatColor.GRAY + " for " + ChatColor.AQUA + spectator.getDisplayName());
  892.                 }
  893.             }
  894.  
  895.             if (!temp) {
  896.                 specs.getConfig().set(spectator.getName(), null);
  897.                 specs.saveConfig();
  898.             }
  899.         }
  900.         else {
  901.             // Spectate mode wasn't on
  902.             if (!silent) {
  903.                 if (sender instanceof Player && spectator.getName().equals(sender.getName())) {
  904.                     spectator.sendMessage(prefix + "You aren't spectating!");
  905.                 }
  906.                 else {
  907.                     sender.sendMessage(prefix + ChatColor.AQUA + spectator.getDisplayName() + ChatColor.GRAY + " isn't spectating!");
  908.                 }
  909.             }
  910.         }
  911.     }
  912.  
  913.  
  914.     protected void reloadConfig(boolean hardReload) {
  915.         // 'hardReload': true/false; a hard reload will reload the config values from file.
  916.         if (hardReload) {          
  917.             setup.saveDefaultConfig();
  918.             toggles.saveDefaultConfig();
  919.             specs.saveDefaultConfig();
  920.            
  921.             setup.reloadConfig();
  922.             toggles.reloadConfig();
  923.             specs.reloadConfig();
  924.            
  925.             Set<String> togglesND = toggles.getConfig().getKeys(true); // ND = no defaults
  926.            
  927.             // Update config & add default values in
  928.             if (togglesND.contains("version") && toggles.getConfig().getDouble("version")<version) {
  929.                 console.sendMessage(prefix+"Updating to version "+ChatColor.AQUA+version+ChatColor.GRAY+"...");
  930.             } else if (togglesND.contains("version") && toggles.getConfig().getDouble("version")>version) { // Placeholder for version checking in future...
  931.                 console.sendMessage(ChatColor.GRAY+"Version "+ChatColor.AQUA+toggles.getConfig().getDouble("version")+ChatColor.GRAY+" available!");
  932.             }
  933.  
  934.             // Compass: true/false
  935.             if (!togglesND.contains("compass")) {
  936.                 toggles.getConfig().set("compass", true);
  937.                 console.sendMessage(ChatColor.GRAY+"Added "+ChatColor.WHITE+"compass: true"+ChatColor.GRAY+" to "+ChatColor.WHITE+"toggles.yml"+ChatColor.GRAY+"...");
  938.             }
  939.             // -> Compass item: <item name>
  940.             if (!togglesND.contains("compassItem")) {
  941.                 toggles.getConfig().set("compassItem", "compass");
  942.                 console.sendMessage(ChatColor.GRAY+"Added "+ChatColor.WHITE+"compassItem: compass"+ChatColor.GRAY+" to "+ChatColor.WHITE+"toggles.yml"+ChatColor.GRAY+"...");
  943.             }
  944.  
  945.             // Arena Selector: true/false
  946.             if (!togglesND.contains("arenaclock")) {
  947.                 toggles.getConfig().set("arenaclock", true);
  948.                 console.sendMessage(ChatColor.GRAY+"Added "+ChatColor.WHITE+"arenaclock: true"+ChatColor.GRAY+" to "+ChatColor.WHITE+"toggles.yml"+ChatColor.GRAY+"...");
  949.             }
  950.             // -> Arena selector item: <item name>
  951.             if (!togglesND.contains("clockItem")) {
  952.                 toggles.getConfig().set("clockItem", "watch");
  953.                 console.sendMessage(ChatColor.GRAY+"Added "+ChatColor.WHITE+"clockItem: watch"+ChatColor.GRAY+" to "+ChatColor.WHITE+"toggles.yml"+ChatColor.GRAY+"...");
  954.             }
  955.            
  956.             // Spectators' tools: true/false
  957.             if (!togglesND.contains("spectatorsTools")) {
  958.                 toggles.getConfig().set("spectatorsTools", true);
  959.                 console.sendMessage(ChatColor.GRAY+"Added "+ChatColor.WHITE+"spectatorsTools: true"+ChatColor.GRAY+" to "+ChatColor.WHITE+"toggles.yml"+ChatColor.GRAY+"...");
  960.             }
  961.             // -> Spectators' tools item: <item name>
  962.             if (!togglesND.contains("spectatorsToolsItem")) {
  963.                 toggles.getConfig().set("spectatorsToolsItem", "magma_cream");
  964.                 console.sendMessage(ChatColor.GRAY+"Added "+ChatColor.WHITE+"spectatorsToolsItem: book"+ChatColor.GRAY+" to "+ChatColor.WHITE+"toggles.yml"+ChatColor.GRAY+"...");
  965.             }
  966.            
  967.             // Spectators' tool: TP to death: true/false
  968.             if (!togglesND.contains("tpToDeathTool")) {
  969.                 toggles.getConfig().set("tpToDeathTool", true);
  970.                 console.sendMessage(ChatColor.GRAY+"Added "+ChatColor.WHITE+"tpToDeathTool: true"+ChatColor.GRAY+" to "+ChatColor.WHITE+"toggles.yml"+ChatColor.GRAY+"...");
  971.             }
  972.             // Spectators' tool: TP to death: show death cause true/false
  973.             if (!togglesND.contains("tpToDeathToolShowCause")) {
  974.                 toggles.getConfig().set("tpToDeathToolShowCause", true);
  975.                 console.sendMessage(ChatColor.GRAY+"Added "+ChatColor.WHITE+"tpToDeathToolShowCause: true"+ChatColor.GRAY+" to "+ChatColor.WHITE+"toggles.yml"+ChatColor.GRAY+"...");
  976.             }
  977.            
  978.             // Inspector: true/false
  979.             if (!togglesND.contains("inspector")) {
  980.                 toggles.getConfig().set("inspector", true);
  981.                 console.sendMessage(ChatColor.GRAY+"Added "+ChatColor.WHITE+"inspector: true"+ChatColor.GRAY+" to "+ChatColor.WHITE+"toggles.yml"+ChatColor.GRAY+"...");
  982.             }
  983.             // -> Inspector item: <item name>
  984.             if (!togglesND.contains("inspectorItem")) {
  985.                 toggles.getConfig().set("inspectorItem", "book");
  986.                 console.sendMessage(ChatColor.GRAY+"Added "+ChatColor.WHITE+"inspectorItem: book"+ChatColor.GRAY+" to "+ChatColor.WHITE+"toggles.yml"+ChatColor.GRAY+"...");
  987.             }
  988.             // -> Inspect from teleportation menu
  989.             if (!togglesND.contains("inspectPlayerFromTeleportationMenu")) {
  990.                 toggles.getConfig().set("inspectPlayerFromTeleportationMenu", true);
  991.                 console.sendMessage(ChatColor.GRAY+"Added "+ChatColor.WHITE+"inspectPlayerFromTeleportationMenu: true"+ChatColor.GRAY+" to "+ChatColor.WHITE+"toggles.yml"+ChatColor.GRAY+"...");
  992.             }
  993.            
  994.             if (!togglesND.contains("playersHealthInTeleportationMenu")) {
  995.                 toggles.getConfig().set("playersHealthInTeleportationMenu", true);
  996.                 console.sendMessage(ChatColor.GRAY+"Added "+ChatColor.WHITE+"playersHealthInTeleportationMenu: true"+ChatColor.GRAY+" to "+ChatColor.WHITE+"toggles.yml"+ChatColor.GRAY+"...");
  997.             }
  998.             if (!togglesND.contains("playersLocationInTeleportationMenu")) {
  999.                 toggles.getConfig().set("playersLocationInTeleportationMenu", true);
  1000.                 console.sendMessage(ChatColor.GRAY+"Added "+ChatColor.WHITE+"playersLocationInTeleportationMenu: true"+ChatColor.GRAY+" to "+ChatColor.WHITE+"toggles.yml"+ChatColor.GRAY+"...");
  1001.             }
  1002.  
  1003.             // Spectator chat: true/false
  1004.             if (!togglesND.contains("specchat")) {
  1005.                 toggles.getConfig().set("specchat", true);
  1006.                 console.sendMessage(ChatColor.GRAY+"Added "+ChatColor.WHITE+"specchat: true"+ChatColor.GRAY+" to "+ChatColor.WHITE+"toggles.yml"+ChatColor.GRAY+"...");
  1007.             }
  1008.  
  1009.             // Output messages from plugin: true/false
  1010.             if (!togglesND.contains("outputmessages")) {
  1011.                 toggles.getConfig().set("outputmessages", true);
  1012.                 console.sendMessage(ChatColor.GRAY+"Added "+ChatColor.WHITE+"outputmessages: true"+ChatColor.GRAY+" to "+ChatColor.WHITE+"toggles.yml"+ChatColor.GRAY+"...");
  1013.             }
  1014.  
  1015.             // Spectate mode enable on death: true/false
  1016.             if (!togglesND.contains("deathspec")) {
  1017.                 toggles.getConfig().set("deathspec", false);
  1018.                 console.sendMessage(ChatColor.GRAY+"Added "+ChatColor.WHITE+"deathspec: false"+ChatColor.GRAY+" to "+ChatColor.WHITE+"toggles.yml"+ChatColor.GRAY+"...");
  1019.             }
  1020.  
  1021.             // Prefix spectator names in tab list: true/false
  1022.             if (!togglesND.contains("colouredtablist")) {
  1023.                 toggles.getConfig().set("colouredtablist", true);
  1024.                 console.sendMessage(ChatColor.GRAY+"Added "+ChatColor.WHITE+"colouredtablist: true"+ChatColor.GRAY+" to "+ChatColor.WHITE+"toggles.yml"+ChatColor.GRAY+"...");
  1025.             }
  1026.  
  1027.             // Can spectators see other spectators? true/false
  1028.             if (!togglesND.contains("seespecs")) {
  1029.                 toggles.getConfig().set("seespecs", false);
  1030.                 console.sendMessage(ChatColor.GRAY+"Added "+ChatColor.WHITE+"seespecs: false"+ChatColor.GRAY+" to "+ChatColor.WHITE+"toggles.yml"+ChatColor.GRAY+"...");
  1031.             }
  1032.  
  1033.             // Block spectators from executing non-SpectatorPlus commands? true/false
  1034.             if (!togglesND.contains("blockcmds")) {
  1035.                 toggles.getConfig().set("blockcmds", true);
  1036.                 console.sendMessage(ChatColor.GRAY+"Added "+ChatColor.WHITE+"blockcmds: true"+ChatColor.GRAY+" to "+ChatColor.WHITE+"toggles.yml"+ChatColor.GRAY+"...");
  1037.             }
  1038.  
  1039.             // Can admins bypass command blocking? true/false
  1040.             if (!togglesND.contains("adminbypass")) {
  1041.                 toggles.getConfig().set("adminbypass", true);
  1042.                 console.sendMessage(ChatColor.GRAY+"Added "+ChatColor.WHITE+"adminbypass: true"+ChatColor.GRAY+" to "+ChatColor.WHITE+"toggles.yml"+ChatColor.GRAY+"...");
  1043.             }
  1044.            
  1045.             // Display "(Right-click)" on the names of the misc tools (teleporter, etc.)? true/false
  1046.             if(!togglesND.contains("newbieMode")) {
  1047.                 toggles.getConfig().set("newbieMode", true);
  1048.                 console.sendMessage(ChatColor.GRAY+"Added "+ChatColor.WHITE+"newbieMode: true"+ChatColor.GRAY+" to "+ChatColor.WHITE+"toggles.yml"+ChatColor.GRAY+"...");
  1049.             }
  1050.            
  1051.             // Teleport the players to the spawn, if there isn't any main lobby set, when the spectator
  1052.             // mode is enabled/disabled? true/false
  1053.             if(!togglesND.contains("teleportToSpawnOnSpecChangeWithoutLobby")) {
  1054.                 toggles.getConfig().set("teleportToSpawnOnSpecChangeWithoutLobby", true);
  1055.                 console.sendMessage(ChatColor.GRAY+"Added "+ChatColor.WHITE+"teleportToSpawnOnSpecChangeWithoutLobby: true"+ChatColor.GRAY+" to "+ChatColor.WHITE+"toggles.yml"+ChatColor.GRAY+"...");
  1056.             }
  1057.            
  1058.             // When teleporting the players to the spawn (without main lobby), use the /spawn command, or
  1059.             // the spawn point of the current world?
  1060.             if(!togglesND.contains("useSpawnCommandToTeleport")) {
  1061.                 toggles.getConfig().set("useSpawnCommandToTeleport", true);
  1062.                 console.sendMessage(ChatColor.GRAY+"Added "+ChatColor.WHITE+"useSpawnCommandToTeleport: true"+ChatColor.GRAY+" to "+ChatColor.WHITE+"toggles.yml"+ChatColor.GRAY+"...");
  1063.             }
  1064.            
  1065.             // When teleporting the players to the spawn (without main lobby), use the /spawn command, or
  1066.             // the spawn point of the current world?
  1067.             if(!togglesND.contains("enforceArenaBoundary")) {
  1068.                 toggles.getConfig().set("enforceArenaBoundary", true);
  1069.                 console.sendMessage(ChatColor.GRAY+"Added "+ChatColor.WHITE+"enforceArenaBoundary: true"+ChatColor.GRAY+" to "+ChatColor.WHITE+"toggles.yml"+ChatColor.GRAY+"...");
  1070.             }
  1071.  
  1072.             // Config was updated, fix version number.
  1073.             toggles.getConfig().set("version",version);
  1074.             toggles.saveConfig();
  1075.  
  1076.             compass = toggles.getConfig().getBoolean("compass", true);
  1077.             clock = toggles.getConfig().getBoolean("arenaclock", true);
  1078.             spectatorsTools = toggles.getConfig().getBoolean("spectatorsTools", true);
  1079.             tpToDeathTool = toggles.getConfig().getBoolean("tpToDeathTool", true);
  1080.             tpToDeathToolShowCause = toggles.getConfig().getBoolean("tpToDeathToolShowCause", true);
  1081.             inspector = toggles.getConfig().getBoolean("inspector", true);
  1082.             inspectFromTPMenu = toggles.getConfig().getBoolean("inspectPlayerFromTeleportationMenu", true);
  1083.             playersHealthInTeleportationMenu = toggles.getConfig().getBoolean("playersHealthInTeleportationMenu", true);
  1084.             playersLocationInTeleportationMenu = toggles.getConfig().getBoolean("playersLocationInTeleportationMenu", true);
  1085.             specChat = toggles.getConfig().getBoolean("specchat", true);
  1086.             output = toggles.getConfig().getBoolean("outputmessages", true);
  1087.             death = toggles.getConfig().getBoolean("deathspec", false);
  1088.             scoreboard = toggles.getConfig().getBoolean("colouredtablist", true);
  1089.             seeSpecs = toggles.getConfig().getBoolean("seespecs", false);
  1090.             blockCmds = toggles.getConfig().getBoolean("blockcmds", true);
  1091.             adminBypass = toggles.getConfig().getBoolean("adminbypass", true);
  1092.             newbieMode = toggles.getConfig().getBoolean("newbieMode", true);
  1093.             teleportToSpawnOnSpecChangeWithoutLobby = toggles.getConfig().getBoolean("teleportToSpawnOnSpecChangeWithoutLobby", true);
  1094.             useSpawnCommandToTeleport = toggles.getConfig().getBoolean("useSpawnCommandToTeleport", true);
  1095.             enforceArenaBoundary = toggles.getConfig().getBoolean("enforceArenaBoundary", true);
  1096.            
  1097.             compassItem = Material.matchMaterial(toggles.getConfig().getString("compassItem", "COMPASS"));
  1098.             clockItem = Material.matchMaterial(toggles.getConfig().getString("clockItem", "WATCH"));
  1099.             spectatorsToolsItem = Material.matchMaterial(toggles.getConfig().getString("spectatorsToolsItem", "MAGMA_CREAM"));
  1100.             inspectorItem = Material.matchMaterial(toggles.getConfig().getString("inspectorItem", "BOOK"));
  1101.            
  1102.             if(compassItem == null) compassItem = Material.COMPASS;
  1103.             if(clockItem == null) clockItem = Material.WATCH;
  1104.             if(spectatorsToolsItem == null) spectatorsToolsItem = Material.MAGMA_CREAM;
  1105.             if(inspectorItem == null) inspectorItem = Material.BOOK;
  1106.            
  1107.             try {
  1108.                 setSpectatorMode(SpectatorMode.fromString(setup.getConfig().getString("mode")));
  1109.             } catch(IllegalArgumentException e) {
  1110.                 getLogger().warning("The SpectatorPlus' mode set in the config (" + setup.getConfig().getString("mode") + ") is invalid; using the ANY mode.");
  1111.                 setSpectatorMode(SpectatorMode.ANY);
  1112.             }
  1113.            
  1114.         } // ...end hardReload
  1115.  
  1116.         if (scoreboard) {
  1117.             if (manager==null) { // After a reload, if 'scoreboard' is kept on, the same scoreboard will be used.
  1118.                 manager = getServer().getScoreboardManager();
  1119.                 board = manager.getNewScoreboard();
  1120.                 board.registerNewObjective("health", "health").setDisplaySlot(DisplaySlot.PLAYER_LIST);
  1121.                 team = board.registerNewTeam("spec");
  1122.                 team.setPrefix(ChatColor.DARK_GRAY + "[" + ChatColor.GRAY + "Spec" + ChatColor.DARK_GRAY + "] " + ChatColor.GRAY);
  1123.                 for (Player target : getServer().getOnlinePlayers()) {
  1124.                     if (user.containsKey(target.getName()) && user.get(target.getName()).spectating) {
  1125.                     }
  1126.                 }
  1127.             }
  1128.            
  1129.             // Make sure the team is empty
  1130.             for (OfflinePlayer target : team.getPlayers()) {
  1131.                 team.removePlayer(target);
  1132.             }
  1133.            
  1134.             // Add players who are spectating & set their scoreboard
  1135.             for (Player target : getServer().getOnlinePlayers()) {
  1136.                 if (getPlayerData(target) != null && getPlayerData(target).spectating) {
  1137.                     target.setScoreboard(board);
  1138.                     team.addPlayer(target);
  1139.                 }
  1140.             }
  1141.             // Incase seeSpecs was previously disabled...
  1142.             seeSpecs = toggles.getConfig().getBoolean("seespecs", false);
  1143.         } else {
  1144.             // seeSpecs relies on using scoreboard teams. Force-disable seeSpecs if scoreboard is disabled.
  1145.             seeSpecs = false;
  1146.             // Do we need to worry about the scoreboard being previously enabled?
  1147.             if (manager != null) {
  1148.                 // Remove all players from spectator team
  1149.                 for (OfflinePlayer target : team.getPlayers()) {
  1150.                     team.removePlayer(target);
  1151.                 }
  1152.                 // Reset each spectator's scoreboard to default/previous
  1153.                 for (Player target : getServer().getOnlinePlayers()) {
  1154.                     if (getPlayerData(target) != null && getPlayerData(target).spectating) {
  1155.                         if (getPlayerData(target).oldScoreboard != null) {
  1156.                             target.setScoreboard(getPlayerData(target).oldScoreboard);
  1157.                         } else {
  1158.                             target.setScoreboard(getServer().getScoreboardManager().getMainScoreboard());
  1159.                         }
  1160.                     }
  1161.                 }
  1162.             }
  1163.         }
  1164.  
  1165.         if(team != null) team.setCanSeeFriendlyInvisibles(seeSpecs);
  1166.        
  1167.         // Update all spectators' inventories
  1168.         updateSpectatorInventories();
  1169.     }
  1170.  
  1171.     /**
  1172.      * Sets the current SpectatorPlus' mode.
  1173.      * <p>
  1174.      * <ul>
  1175.      *   <li>{@code ANY}: the spectators can teleports themselves to any player in the server.</li>
  1176.      *   <li>{@code ARENA}: the spectators will have to choose an arena; then they will be able
  1177.      *   to teleport themselves only to the players in this arena. An option is available to prevent
  1178.      *   the spectators from leaving the arena.</li>
  1179.      *   <li>{@code WORLD}: the spectators will be able to teleport themselves to the players in the same world.</li>
  1180.      * </ul>
  1181.      *
  1182.      * @param mode The mode.
  1183.      * @see SpectatorPlusMode
  1184.      *
  1185.      * @since 2.0
  1186.      */
  1187.     protected void setSpectatorMode(SpectatorMode mode) {
  1188.         this.mode = mode;
  1189.        
  1190.         setup.getConfig().set("mode", mode.toString());
  1191.         setup.saveConfig();
  1192.        
  1193.         // Needed if the mode is changed from/to the arena mode.
  1194.         updateSpectatorInventories();
  1195.     }
  1196.    
  1197.     /**
  1198.      * Lets a player select two points and set up an arena.
  1199.      *
  1200.      * @param player The player involved in the setup process.
  1201.      * @param block The block punched by the player.
  1202.      *
  1203.      * @return True if the player was setting up an arena; false else.
  1204.      */
  1205.     protected boolean arenaSetup(Player player, Block block) {
  1206.         if (getPlayerData(player).setup == 2) {
  1207.             getPlayerData(player).pos2 = block.getLocation();
  1208.             getPlayerData(player).setup = 0;
  1209.  
  1210.             Location lowPos, hiPos;
  1211.             lowPos = new Location(getPlayerData(player).pos1.getWorld(), 0, 0, 0);
  1212.             hiPos = new Location(getPlayerData(player).pos1.getWorld(), 0, 0, 0);
  1213.  
  1214.             // yPos
  1215.             if (Math.floor(getPlayerData(player).pos1.getY()) > Math.floor(getPlayerData(player).pos2.getY())) {
  1216.                 hiPos.setY(Math.floor(getPlayerData(player).pos1.getY()));
  1217.                 lowPos.setY(Math.floor(getPlayerData(player).pos2.getY()));
  1218.             } else {
  1219.                 lowPos.setY(Math.floor(getPlayerData(player).pos1.getY()));
  1220.                 hiPos.setY(Math.floor(getPlayerData(player).pos2.getY()));
  1221.             }
  1222.  
  1223.             // xPos
  1224.             if (Math.floor(getPlayerData(player).pos1.getX()) > Math.floor(getPlayerData(player).pos2.getX())) {
  1225.                 hiPos.setX(Math.floor(getPlayerData(player).pos1.getX()));
  1226.                 lowPos.setX(Math.floor(getPlayerData(player).pos2.getX()));
  1227.             } else {
  1228.                 lowPos.setX(Math.floor(getPlayerData(player).pos1.getX()));
  1229.                 hiPos.setX(Math.floor(getPlayerData(player).pos2.getX()));
  1230.             }
  1231.  
  1232.             // zPos
  1233.             if (Math.floor(getPlayerData(player).pos1.getZ()) > Math.floor(getPlayerData(player).pos2.getZ())) {
  1234.                 hiPos.setZ(Math.floor(getPlayerData(player).pos1.getZ()));
  1235.                 lowPos.setZ(Math.floor(getPlayerData(player).pos2.getZ()));
  1236.             } else {
  1237.                 lowPos.setZ(Math.floor(getPlayerData(player).pos1.getZ()));
  1238.                 hiPos.setZ(Math.floor(getPlayerData(player).pos2.getZ()));
  1239.             }
  1240.            
  1241.             arenasManager.registerArena(new Arena(getPlayerData(player).arenaName, hiPos, lowPos));
  1242.             player.sendMessage(prefix + "Arena " + ChatColor.AQUA + getPlayerData(player).arenaName + ChatColor.GRAY + " successfully set up!");
  1243.  
  1244.             // returns true: Cancels breaking of the block that was punched
  1245.             return true;
  1246.         }
  1247.         else {
  1248.             if (getPlayerData(player).setup == 1) {
  1249.                 getPlayerData(player).pos1 = block.getLocation();
  1250.  
  1251.                 player.sendMessage(prefix + "Punch point " + ChatColor.AQUA + "#2" + ChatColor.GRAY + " - the opposite corner of the arena");
  1252.  
  1253.                 getPlayerData(player).setup = 2;
  1254.  
  1255.                 // returns true: Cancels breaking of the block that was punched
  1256.                 return true;
  1257.             }
  1258.             else {
  1259.                 // returns false: The player was not setting up an arena.
  1260.                 return false;
  1261.             }
  1262.         }
  1263.     }
  1264.  
  1265.  
  1266.     /**
  1267.      * Removes an arena.
  1268.      *
  1269.      * @param arenaName
  1270.      * @return True if the arena was removed; false else (non-existant arena).
  1271.      */
  1272.     protected boolean removeArena(String arenaName) {
  1273.        
  1274.         Arena arenaToBeRemoved = arenasManager.getArena(arenaName);
  1275.         if(arenaToBeRemoved == null) {
  1276.             return false;
  1277.         }
  1278.  
  1279.         arenasManager.unregisterArena(arenaToBeRemoved);
  1280.        
  1281.         // The players in the deleted arena are removed to the arena
  1282.         for(Player player : this.getServer().getOnlinePlayers()) {
  1283.             if(getPlayerData(player).spectating) {
  1284.                 if(getPlayerData(player).arena != null && getPlayerData(player).arena.equals(arenaToBeRemoved.getUUID())) {
  1285.                     removePlayerFromArena(player);
  1286.                 }
  1287.             }
  1288.         }
  1289.        
  1290.         return true;
  1291.     }
  1292.  
  1293.  
  1294.     /**
  1295.      * Sets the arena for the given player.
  1296.      * Teleports the player to the lobby of that arena, if a lobby is available.
  1297.      *
  1298.      * @param player The player.
  1299.      * @param arenaName The name of the arena.
  1300.      * @param teleportToLobby If true the player will be teleported to the lobby (if a lobby is set).
  1301.      * @return True if the change was effective (i.e. the arena exists).
  1302.      */
  1303.     protected boolean setArenaForPlayer(Player player, String arenaName, boolean teleportToLobby) {
  1304.         Arena arena = arenasManager.getArena(arenaName);
  1305.        
  1306.         getPlayerData(player).arena = arena.getUUID();
  1307.         if(teleportToLobby) {
  1308.             Location lobbyLocation = arena.getLobby();
  1309.            
  1310.             if(lobbyLocation == null) { // No lobby set
  1311.                 player.sendMessage(prefix + "No lobby location set for " + ChatColor.AQUA + arenaName);
  1312.                 return true;
  1313.             }
  1314.  
  1315.             if(output) {
  1316.                 player.sendMessage(prefix + "Teleported you to " + ChatColor.AQUA + arenaName);
  1317.             }
  1318.  
  1319.             player.teleport(lobbyLocation);
  1320.         }
  1321.  
  1322.         return true;
  1323.     }
  1324.  
  1325.  
  1326.     /**
  1327.      * Sets the arena for the given player.
  1328.      * Teleports the player to the lobby of that arena, if a lobby is available.
  1329.      *
  1330.      * @param player The player.
  1331.      * @param arenaName The name of the arena.
  1332.      * @return True if the change was effective (i.e. the arena exists).
  1333.      */
  1334.     protected boolean setArenaForPlayer(Player player, String arenaName) {
  1335.         return setArenaForPlayer(player, arenaName, true);
  1336.     }
  1337.  
  1338.     /**
  1339.      * Removes a player from his arena.
  1340.      * The player is teleported to the main lobby, if such a lobby is set.
  1341.      *
  1342.      * @param player
  1343.      */
  1344.     protected void removePlayerFromArena(Player player) {
  1345.         removePlayerFromArena(player, false);
  1346.     }
  1347.    
  1348.     /**
  1349.      * Removes a player from his arena.
  1350.      * The player is teleported to the main lobby, if such a lobby is set.
  1351.      *
  1352.      * @param player
  1353.      * @param silent
  1354.      */
  1355.     protected void removePlayerFromArena(Player player, boolean silent) {
  1356.  
  1357.         getPlayerData(player).arena = null;
  1358.         boolean teleported = spawnPlayer(player);
  1359.  
  1360.         if(output && !silent) {
  1361.             if(teleported) {
  1362.                 player.sendMessage(prefix + "You were removed from your current arena and teleported to the main lobby.");
  1363.             }
  1364.             else {
  1365.                 player.sendMessage(prefix + "You were removed from your current arena.");
  1366.             }
  1367.         }
  1368.     }
  1369.  
  1370.     /**
  1371.      * Saves the player's inventory and clears it before enabling spectator mode.
  1372.      *
  1373.      * @param player The concerned player.
  1374.      */
  1375.     protected void savePlayerInv(Player player) {
  1376.         getPlayerData(player).inventory = player.getInventory().getContents();
  1377.         getPlayerData(player).armour = player.getInventory().getArmorContents();
  1378.  
  1379.         player.getInventory().clear();
  1380.         player.getInventory().setArmorContents(null);
  1381.     }
  1382.  
  1383.     /**
  1384.      * Loads the player's inventory after disabling the spectate mode.
  1385.      *
  1386.      * @param player The concerned player.
  1387.      */
  1388.     protected void loadPlayerInv(Player player) {
  1389.         player.getInventory().clear();
  1390.         player.getInventory().setContents(getPlayerData(player).inventory);
  1391.         player.getInventory().setArmorContents(getPlayerData(player).armour);
  1392.  
  1393.         getPlayerData(player).inventory = null;
  1394.         getPlayerData(player).armour = null;
  1395.  
  1396.         player.updateInventory(); // yes, it's deprecated. But it still works!
  1397.     }
  1398.  
  1399.     /**
  1400.      * Broadcasts a message to all players with spectator mode enabled, and the sender.
  1401.      *
  1402.      * @param sender The sender of the message to be broadcasted.
  1403.      * @param message The message to broadcast.
  1404.      */
  1405.     protected void broadcastToSpectators(CommandSender sender, String message) {
  1406.         String senderName = null;
  1407.         if(sender instanceof Player) {
  1408.             senderName = ChatColor.WHITE + ((Player) sender).getDisplayName();
  1409.         }
  1410.         else {
  1411.             senderName = ChatColor.DARK_RED + "CONSOLE";
  1412.         }
  1413.  
  1414.         String formattedMessage = ChatColor.GRAY + "[" + senderName + ChatColor.GRAY + " -> spectators] " + ChatColor.RESET + message;
  1415.  
  1416.         for (Player player : getServer().getOnlinePlayers()) {
  1417.             if(getPlayerData(player).spectating || player.getName().equals(sender.getName())) {
  1418.                 player.sendMessage(formattedMessage);
  1419.             }
  1420.         }
  1421.  
  1422.         console.sendMessage(formattedMessage);
  1423.     }
  1424.  
  1425.     /**
  1426.      * Sends a spectator chat message, from one spectator to all other spectators.
  1427.      * Includes "/me" actions
  1428.      *
  1429.      * @param sender The sender of the message.
  1430.      * @param message The text of the message.
  1431.      * @param isAction If true, the message will be displayed as an action message (like /me <message>).
  1432.      */
  1433.     protected void sendSpectatorMessage(CommandSender sender, String message, Boolean isAction) {
  1434.         String playerName = null;
  1435.         if(sender instanceof Player) {
  1436.             playerName = ChatColor.WHITE + ((Player) sender).getDisplayName();
  1437.         } else {
  1438.             playerName = ChatColor.DARK_RED + "CONSOLE";
  1439.         }
  1440.  
  1441.         String invite = null;
  1442.         if(isAction) {
  1443.             invite = "* " + playerName + " " + ChatColor.GRAY;
  1444.         } else {
  1445.             invite = playerName + ChatColor.GRAY + ": ";
  1446.         }
  1447.  
  1448.         for (Player player : getServer().getOnlinePlayers()) {
  1449.             if(getPlayerData(player).spectating) {
  1450.                 player.sendMessage(ChatColor.GRAY + "[SPEC] " + invite + message);
  1451.             }
  1452.         }
  1453.         console.sendMessage(ChatColor.GRAY + "[SPEC] " + invite + message);
  1454.     }
  1455.  
  1456.     /**
  1457.      * Updates the spectator inventory for a certain player.
  1458.      *
  1459.      * @param spectator The player whose inventory will be updated
  1460.      *
  1461.      * @since 2.0
  1462.      */
  1463.     protected void updateSpectatorInventory(Player spectator) {
  1464.         // Empty the inventory first...
  1465.         spectator.getInventory().clear();
  1466.  
  1467.         String rightClick = "", rightClickPlayer = "";
  1468.         if(newbieMode) {
  1469.             rightClick = ChatColor.GRAY + " (Right-click)";
  1470.             rightClickPlayer = ChatColor.GRAY + " (Right-click a player)";
  1471.         }
  1472.  
  1473.         // Give them compass if the toggle is on
  1474.         if (compass) {
  1475.             ItemStack compass = new ItemStack(compassItem, 1);
  1476.             ItemMeta compassMeta = (ItemMeta)compass.getItemMeta();
  1477.             compassMeta.setDisplayName(ChatColor.BLUE +""+ ChatColor.BOLD + "Teleporter" + rightClick);
  1478.             List<String> lore = new ArrayList<String>();
  1479.                 lore.add(ChatColor.AQUA +""+ ChatColor.ITALIC + "Right click" + ChatColor.GRAY + ChatColor.ITALIC + " to choose a player");
  1480.                 lore.add(ChatColor.GRAY +""+ ChatColor.ITALIC + "to teleport to");
  1481.             compassMeta.setLore(lore);
  1482.             compass.setItemMeta(compassMeta);
  1483.             spectator.getInventory().setItem(0, compass);
  1484.         }
  1485.  
  1486.         // Give them clock (only for arena mode and if the toggle is on)
  1487.         if (clock && mode == SpectatorMode.ARENA) {
  1488.             ItemStack watch = new ItemStack(clockItem, 1);
  1489.             ItemMeta watchMeta = (ItemMeta)watch.getItemMeta();
  1490.             watchMeta.setDisplayName(ChatColor.DARK_RED +""+ ChatColor.BOLD + "Arena selector" + rightClick);
  1491.             List<String> lore = new ArrayList<String>();
  1492.                 lore.add(ChatColor.AQUA +""+ ChatColor.ITALIC + "Right click" + ChatColor.GRAY + ChatColor.ITALIC + " to choose an arena");
  1493.                 lore.add(ChatColor.GRAY +""+ ChatColor.ITALIC + "to spectate in");
  1494.             watchMeta.setLore(lore);
  1495.             watch.setItemMeta(watchMeta);
  1496.             spectator.getInventory().setItem(1, watch);
  1497.         }
  1498.  
  1499.         // Give them magma cream (spectators tools) if the toggle is on
  1500.         if(spectatorsTools) {
  1501.             ItemStack tools = new ItemStack(spectatorsToolsItem, 1);
  1502.             ItemMeta toolsMeta = tools.getItemMeta();
  1503.             toolsMeta.setDisplayName(ChatColor.WHITE +""+ ChatColor.BOLD + "Spectators' tools" + rightClick);
  1504.             List<String> lore = new ArrayList<String>();
  1505.                 lore.add(ChatColor.AQUA +""+ ChatColor.ITALIC + "Right click" + ChatColor.GRAY + ChatColor.ITALIC + " to open the spectator");
  1506.                 lore.add(ChatColor.GRAY +""+ ChatColor.ITALIC + "tools menu");
  1507.             toolsMeta.setLore(lore);
  1508.             tools.setItemMeta(toolsMeta);
  1509.             spectator.getInventory().setItem(4, tools);
  1510.         }
  1511.  
  1512.         // Give them book if the toggle is on
  1513.         if(inspector) {
  1514.             ItemStack book = new ItemStack(inspectorItem, 1);
  1515.             ItemMeta bookMeta = (ItemMeta)book.getItemMeta();
  1516.             bookMeta.setDisplayName(ChatColor.BLUE +""+ ChatColor.BOLD + "Inspector" + rightClickPlayer);
  1517.             List<String> lore = new ArrayList<String>();
  1518.                 lore.add(ChatColor.AQUA +""+ ChatColor.ITALIC + "Right click" + ChatColor.GRAY + ChatColor.ITALIC + " a player to see their");
  1519.                 lore.add(ChatColor.GRAY +""+ ChatColor.ITALIC + "inventory, armour, health & more!");
  1520.             bookMeta.setLore(lore);
  1521.             book.setItemMeta(bookMeta);
  1522.             spectator.getInventory().setItem(8, book);
  1523.         }
  1524.  
  1525.         spectator.updateInventory();
  1526.     }
  1527.    
  1528.     /**
  1529.      * Updates the spectator inventories for all currently spectating players.
  1530.      *
  1531.      * @since 2.0
  1532.      */
  1533.     protected void updateSpectatorInventories() {
  1534.         for (Player target : getServer().getOnlinePlayers()) {
  1535.             if (getPlayerData(target).spectating) {
  1536.                 updateSpectatorInventory(target);
  1537.             }
  1538.         }
  1539.     }
  1540.    
  1541.     /**
  1542.      * Get the PlayerObject (data store) for the player.
  1543.      *
  1544.      * @param target The player to get the PlayerObject of.
  1545.      *
  1546.      * @since 2.0
  1547.      */
  1548.     protected PlayerObject getPlayerData(Player target) {
  1549.         return user.get(target.getName());
  1550.     }
  1551.  
  1552.     /**
  1553.      * Returns the API.
  1554.      *
  1555.      * @see SpectateAPI
  1556.      *
  1557.      * @return The API.
  1558.      */
  1559.     public final SpectateAPI getAPI() {
  1560.         return api;
  1561.     }
  1562.    
  1563.     protected Boolean parseBoolean(String input) {
  1564.         if (input.equalsIgnoreCase("on") || input.equalsIgnoreCase("yes") || input.equalsIgnoreCase("y") || input.equalsIgnoreCase("true")) {
  1565.             return true;
  1566.         } else if (input.equalsIgnoreCase("off") || input.equalsIgnoreCase("no") || input.equalsIgnoreCase("n") || input.equalsIgnoreCase("false")) {
  1567.             return false;
  1568.         } else {
  1569.             return null;
  1570.         }
  1571.     }
  1572. }
Add Comment
Please, Sign In to add comment