com.wasteofplastic.askyblock.commands.Challenges.java Source code

Java tutorial

Introduction

Here is the source code for com.wasteofplastic.askyblock.commands.Challenges.java

Source

/*******************************************************************************
 * This file is part of ASkyBlock.
 *
 *     ASkyBlock is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU General Public License as published by
 *     the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 *
 *     ASkyBlock is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU General Public License for more details.
 *
 *     You should have received a copy of the GNU General Public License
 *     along with ASkyBlock.  If not, see <http://www.gnu.org/licenses/>.
 *******************************************************************************/
package com.wasteofplastic.askyblock.commands;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.UUID;

import net.milkbowl.vault.economy.EconomyResponse;

import org.apache.commons.lang.StringUtils;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.Sound;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabCompleter;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.inventory.meta.PotionMeta;
import org.bukkit.material.SpawnEgg;
import org.bukkit.potion.Potion;
import org.bukkit.potion.PotionData;
import org.bukkit.potion.PotionType;

import com.wasteofplastic.askyblock.ASkyBlock;
import com.wasteofplastic.askyblock.Settings;
import com.wasteofplastic.askyblock.events.ChallengeCompleteEvent;
import com.wasteofplastic.askyblock.events.ChallengeLevelCompleteEvent;
import com.wasteofplastic.askyblock.panels.CPItem;
//import com.wasteofplastic.askyblock.util.Potion1_9;
import com.wasteofplastic.askyblock.util.SpawnEgg1_9;
import com.wasteofplastic.askyblock.util.Util;
import com.wasteofplastic.askyblock.util.VaultHelper;

/**
 * Handles challenge commands and related methods
 */
@SuppressWarnings("deprecation")
public class Challenges implements CommandExecutor, TabCompleter {
    private ASkyBlock plugin;
    private static final boolean DEBUG = false;
    // Database of challenges
    private static LinkedHashMap<String, List<String>> challengeList = new LinkedHashMap<String, List<String>>();
    private HashMap<UUID, List<CPItem>> playerChallengeGUI = new HashMap<UUID, List<CPItem>>();
    // Where challenges are stored
    private static FileConfiguration challengeFile = null;
    private static File challengeConfigFile = null;

    public Challenges(ASkyBlock plugin) {
        this.plugin = plugin;
        saveDefaultChallengeConfig();
        reloadChallengeConfig();
    }

    /*
     * (non-Javadoc)
     * @see
     * org.bukkit.command.CommandExecutor#onCommand(org.bukkit.command.CommandSender
     * , org.bukkit.command.Command, java.lang.String, java.lang.String[])
     */
    public boolean onCommand(final CommandSender sender, final Command command, final String label,
            final String[] cmd) {
        if (!(sender instanceof Player)) {
            return false;
        }
        final Player player = (Player) sender;
        /*
         * if
         * (!player.getWorld().getName().equalsIgnoreCase(Settings.worldName)) {
         * player.sendMessage(ChatColor.RED + plugin.myLocale(player.getUniqueId()).errorWrongWorld);
         * return true;
         * }
         */
        // Check permissions
        if (!VaultHelper.checkPerm(player, Settings.PERMPREFIX + "island.challenges")) {
            player.sendMessage(ChatColor.RED + plugin.myLocale(player.getUniqueId()).errorNoPermission);
            return true;
        }
        // Check island
        if (plugin.getGrid().getIsland(player.getUniqueId(), player.getWorld().getEnvironment()) == null) {
            player.sendMessage(ChatColor.RED + plugin.myLocale(player.getUniqueId()).errorNoIsland);
            return true;
        }
        switch (cmd.length) {
        case 0:
            // Display panel
            player.openInventory(challengePanel(player));
            return true;
        case 1:
            if (cmd[0].equalsIgnoreCase("help") || cmd[0].equalsIgnoreCase("complete")
                    || cmd[0].equalsIgnoreCase("c")) {
                sender.sendMessage(ChatColor.GOLD + plugin.myLocale(player.getUniqueId()).challengeshelp1);
                sender.sendMessage(ChatColor.GOLD + plugin.myLocale(player.getUniqueId()).challengeshelp2);
            } else if (isLevelAvailable(player, getChallengeConfig()
                    .getString("challenges.challengeList." + cmd[0].toLowerCase() + ".level"))) {
                // Provide info on the challenge
                // Challenge Name
                // Description
                // Type
                // Items taken or not
                // island or not
                final String challenge = cmd[0].toLowerCase();
                sender.sendMessage(ChatColor.GOLD + plugin.myLocale(player.getUniqueId()).challengesname + ": "
                        + ChatColor.WHITE + challenge);
                sender.sendMessage(ChatColor.WHITE + plugin.myLocale(player.getUniqueId()).challengeslevel + ": "
                        + ChatColor.GOLD
                        + getChallengeConfig().getString("challenges.challengeList." + challenge + ".level", ""));
                String desc = ChatColor.translateAlternateColorCodes('&',
                        getChallengeConfig().getString("challenges.challengeList." + challenge + ".description", "")
                                .replace("[label]", Settings.ISLANDCOMMAND));
                List<String> result = new ArrayList<String>();
                if (desc.contains("|")) {
                    result.addAll(Arrays.asList(desc.split("\\|")));
                } else {
                    result.add(desc);
                }
                for (String line : result) {
                    sender.sendMessage(ChatColor.GOLD + line);
                }
                final String type = getChallengeConfig()
                        .getString("challenges.challengeList." + challenge + ".type", "").toLowerCase();
                if (type.equals("inventory")) {
                    if (getChallengeConfig()
                            .getBoolean("challenges.challengeList." + cmd[0].toLowerCase() + ".takeItems")) {
                        sender.sendMessage(
                                ChatColor.RED + plugin.myLocale(player.getUniqueId()).challengesitemTakeWarning);
                    }
                } else if (type.equals("island")) {
                    sender.sendMessage(
                            ChatColor.RED + plugin.myLocale(player.getUniqueId()).challengeserrorItemsNotThere);
                }
                if (plugin.getPlayers().checkChallenge(player.getUniqueId(), challenge)
                        && (!type.equals("inventory") || !getChallengeConfig()
                                .getBoolean("challenges.challengeList." + challenge + ".repeatable", false))) {
                    sender.sendMessage(
                            ChatColor.RED + plugin.myLocale(player.getUniqueId()).challengesnotRepeatable);
                    return true;
                }
                double moneyReward = 0;
                int expReward = 0;
                String rewardText = "";

                if (!plugin.getPlayers().checkChallenge(player.getUniqueId(), challenge)) {
                    // First time
                    moneyReward = getChallengeConfig()
                            .getDouble("challenges.challengeList." + challenge.toLowerCase() + ".moneyReward", 0D);
                    rewardText = ChatColor.translateAlternateColorCodes('&', getChallengeConfig().getString(
                            "challenges.challengeList." + challenge.toLowerCase() + ".rewardText", "Goodies!"));
                    expReward = getChallengeConfig().getInt("challenges.challengeList." + challenge + ".expReward",
                            0);
                    sender.sendMessage(
                            ChatColor.GOLD + plugin.myLocale(player.getUniqueId()).challengesfirstTimeRewards);
                } else {
                    // Repeat challenge
                    moneyReward = getChallengeConfig().getDouble(
                            "challenges.challengeList." + challenge.toLowerCase() + ".repeatMoneyReward", 0D);
                    rewardText = ChatColor.translateAlternateColorCodes('&',
                            getChallengeConfig().getString(
                                    "challenges.challengeList." + challenge.toLowerCase() + ".repeatRewardText",
                                    "Goodies!"));
                    expReward = getChallengeConfig()
                            .getInt("challenges.challengeList." + challenge + ".repeatExpReward", 0);
                    sender.sendMessage(
                            ChatColor.GOLD + plugin.myLocale(player.getUniqueId()).challengesrepeatRewards);

                }
                sender.sendMessage(ChatColor.WHITE + rewardText);
                if (expReward > 0) {
                    sender.sendMessage(ChatColor.GOLD + plugin.myLocale(player.getUniqueId()).challengesexpReward
                            + ": " + ChatColor.WHITE + expReward);
                }
                if (Settings.useEconomy && moneyReward > 0) {
                    sender.sendMessage(ChatColor.GOLD + plugin.myLocale(player.getUniqueId()).challengesmoneyReward
                            + ": " + ChatColor.WHITE + VaultHelper.econ.format(moneyReward));
                }
                sender.sendMessage(ChatColor.GOLD + plugin.myLocale(player.getUniqueId()).challengestoCompleteUse
                        + ChatColor.WHITE + " /" + label + " c " + challenge);
            } else {
                sender.sendMessage(
                        ChatColor.RED + plugin.myLocale(player.getUniqueId()).challengesinvalidChallengeName);
            }
            return true;
        case 2:
            if (cmd[0].equalsIgnoreCase("complete") || cmd[0].equalsIgnoreCase("c")) {
                if (!player.getWorld().equals(ASkyBlock.getIslandWorld())) {
                    // Check if in new nether
                    if (!Settings.createNether || !Settings.newNether || ASkyBlock.getNetherWorld() == null
                            || !player.getWorld().equals(ASkyBlock.getNetherWorld())) {
                        player.sendMessage(ChatColor.RED + plugin.myLocale(player.getUniqueId()).errorWrongWorld);
                        return true;
                    }
                }
                if (checkIfCanCompleteChallenge(player, cmd[1].toLowerCase())) {
                    int oldLevel = getLevelDone(player);
                    giveReward(player, cmd[1].toLowerCase());
                    int newLevel = getLevelDone(player);
                    // Fire an event if they are different
                    //plugin.getLogger().info("DEBUG: " + oldLevel + " " + newLevel);
                    if (oldLevel < newLevel) {
                        // Update chat
                        plugin.getChatListener().setPlayerChallengeLevel(player);
                        // Run commands and give rewards but only if they haven't done it below
                        //plugin.getLogger().info("DEBUG: old level = " + oldLevel + " new level = " + newLevel);
                        String level = Settings.challengeLevels.get(newLevel);
                        if (!level.isEmpty() && !plugin.getPlayers().checkChallenge(player.getUniqueId(), level)) {
                            //plugin.getLogger().info("DEBUG: level name = " + level);
                            plugin.getPlayers().completeChallenge(player.getUniqueId(), level);
                            String message = ChatColor.translateAlternateColorCodes('&', getChallengeConfig()
                                    .getString("challenges.levelUnlock." + level + ".message", ""));
                            if (!message.isEmpty()) {
                                player.sendMessage(ChatColor.GREEN + message);
                            }

                            String[] itemReward = getChallengeConfig()
                                    .getString("challenges.levelUnlock." + level + ".itemReward", "").split(" ");
                            String rewardDesc = getChallengeConfig()
                                    .getString("challenges.levelUnlock." + level + ".rewardDesc", "");
                            if (!rewardDesc.isEmpty()) {
                                player.sendMessage(
                                        ChatColor.GOLD + plugin.myLocale(player.getUniqueId()).challengesrewards
                                                + ": " + ChatColor.WHITE + rewardDesc);
                            }
                            List<ItemStack> rewardedItems = giveItems(player, itemReward);
                            double moneyReward = getChallengeConfig()
                                    .getDouble("challenges.levelUnlock." + level + ".moneyReward", 0D);
                            int expReward = getChallengeConfig()
                                    .getInt("challenges.levelUnlock." + level + ".expReward", 0);
                            if (expReward > 0) {
                                player.sendMessage(
                                        ChatColor.GOLD + plugin.myLocale(player.getUniqueId()).challengesexpReward
                                                + ": " + ChatColor.WHITE + expReward);
                                player.giveExp(expReward);
                            }
                            if (Settings.useEconomy && moneyReward > 0 && (VaultHelper.econ != null)) {
                                EconomyResponse e = VaultHelper.econ.depositPlayer(player, Settings.worldName,
                                        moneyReward);
                                if (e.transactionSuccess()) {
                                    player.sendMessage(ChatColor.GOLD
                                            + plugin.myLocale(player.getUniqueId()).challengesmoneyReward + ": "
                                            + ChatColor.WHITE + VaultHelper.econ.format(moneyReward));
                                } else {
                                    plugin.getLogger().severe("Error giving player " + player.getUniqueId()
                                            + " challenge money:" + e.errorMessage);
                                    plugin.getLogger().severe("Reward was $" + moneyReward);
                                }
                            }
                            String[] permList = getChallengeConfig()
                                    .getString("challenges.levelUnlock." + level + ".permissionReward", "")
                                    .split(" ");

                            for (final String s : permList) {
                                if (!s.isEmpty()) {
                                    VaultHelper.addPerm(player, s);
                                    plugin.getLogger()
                                            .info("Added permission " + s + " to " + player.getName() + "");
                                }
                            }
                            List<String> commands = getChallengeConfig()
                                    .getStringList("challenges.levelUnlock." + level + ".commands");
                            runCommands(player, commands);
                            // Fire event
                            ChallengeLevelCompleteEvent event = new ChallengeLevelCompleteEvent(player, oldLevel,
                                    newLevel, rewardedItems);
                            plugin.getServer().getPluginManager().callEvent(event);

                        }
                    }
                }
                return true;
            }
        default:
            return false;
        }
    }

    /**
     * Checks the highest level this player has achieved
     * @param player
     * @return level number
     */
    private int getLevelDone(Player player) {
        //plugin.getLogger().info("DEBUG: checking level completed");
        //plugin.getLogger().info("DEBUG: getting challenge level for " + player.getName());
        for (int result = 0; result < Settings.challengeLevels.size(); result++) {
            if (checkLevelCompletion(player, Settings.challengeLevels.get(result)) > 0) {
                return result;
            }
        }
        return (Math.max(0, Settings.challengeLevels.size() - 1));
    }

    /**
     * Gives the reward for completing the challenge 
     * 
     * @param player
     * @param challenge
     * @return ture if reward given successfully
     */
    private boolean giveReward(final Player player, final String challenge) {
        // Grab the rewards from the config.yml file
        String[] permList;
        String[] itemRewards;
        double moneyReward = 0;
        int expReward = 0;
        String rewardText = "";
        // If the friendly name is available use it
        String challengeName = ChatColor.translateAlternateColorCodes('&',
                getChallengeConfig().getString("challenges.challengeList." + challenge + ".friendlyname",
                        challenge.substring(0, 1).toUpperCase() + challenge.substring(1)));

        // Gather the rewards due
        // If player has done a challenge already, the rewards are different
        if (!plugin.getPlayers().checkChallenge(player.getUniqueId(), challenge)) {
            // First time
            player.sendMessage(ChatColor.GREEN + plugin.myLocale(player.getUniqueId()).challengesyouHaveCompleted
                    .replace("[challenge]", challengeName));
            if (Settings.broadcastMessages) {
                for (Player p : plugin.getServer().getOnlinePlayers()) {
                    p.sendMessage(ChatColor.GOLD + plugin.myLocale(p.getUniqueId()).challengesnameHasCompleted
                            .replace("[name]", player.getDisplayName()).replace("[challenge]", challengeName));
                }
            }
            plugin.getMessages().tellOfflineTeam(player.getUniqueId(),
                    ChatColor.GOLD + plugin.myLocale(player.getUniqueId()).challengesnameHasCompleted
                            .replace("[name]", player.getName()).replace("[challenge]", challengeName));
            itemRewards = getChallengeConfig()
                    .getString("challenges.challengeList." + challenge.toLowerCase() + ".itemReward", "")
                    .split(" ");
            moneyReward = getChallengeConfig()
                    .getDouble("challenges.challengeList." + challenge.toLowerCase() + ".moneyReward", 0D);
            rewardText = ChatColor.translateAlternateColorCodes('&', getChallengeConfig()
                    .getString("challenges.challengeList." + challenge.toLowerCase() + ".rewardText", "Goodies!"));
            expReward = getChallengeConfig().getInt("challenges.challengeList." + challenge + ".expReward", 0);
        } else {
            // Repeat challenge
            player.sendMessage(ChatColor.GREEN + plugin.myLocale(player.getUniqueId()).challengesyouRepeated
                    .replace("[challenge]", challengeName));
            itemRewards = getChallengeConfig()
                    .getString("challenges.challengeList." + challenge.toLowerCase() + ".repeatItemReward", "")
                    .split(" ");
            moneyReward = getChallengeConfig()
                    .getDouble("challenges.challengeList." + challenge.toLowerCase() + ".repeatMoneyReward", 0);
            rewardText = ChatColor.translateAlternateColorCodes('&', getChallengeConfig().getString(
                    "challenges.challengeList." + challenge.toLowerCase() + ".repeatRewardText", "Goodies!"));
            expReward = getChallengeConfig().getInt("challenges.challengeList." + challenge + ".repeatExpReward",
                    0);
        }
        // Report the rewards and give out exp, money and permissions if
        // appropriate
        player.sendMessage(ChatColor.GOLD + plugin.myLocale(player.getUniqueId()).challengesrewards + ": "
                + ChatColor.WHITE + rewardText);
        if (expReward > 0) {
            player.sendMessage(ChatColor.GOLD + plugin.myLocale(player.getUniqueId()).challengesexpReward + ": "
                    + ChatColor.WHITE + expReward);
            player.giveExp(expReward);
        }
        if (Settings.useEconomy && moneyReward > 0 && (VaultHelper.econ != null)) {
            EconomyResponse e = VaultHelper.econ.depositPlayer(player, Settings.worldName, moneyReward);
            if (e.transactionSuccess()) {
                player.sendMessage(ChatColor.GOLD + plugin.myLocale(player.getUniqueId()).challengesmoneyReward
                        + ": " + ChatColor.WHITE + VaultHelper.econ.format(moneyReward));
            } else {
                plugin.getLogger().severe(
                        "Error giving player " + player.getUniqueId() + " challenge money:" + e.errorMessage);
                plugin.getLogger().severe("Reward was $" + moneyReward);
            }
        }
        // Dole out permissions
        permList = getChallengeConfig()
                .getString("challenges.challengeList." + challenge.toLowerCase() + ".permissionReward", "")
                .split(" ");
        for (final String s : permList) {
            if (!s.isEmpty()) {
                if (!VaultHelper.checkPerm(player, s)) {
                    VaultHelper.addPerm(player, s);
                    plugin.getLogger().info("Added permission " + s + " to " + player.getName() + "");
                }
            }
        }
        // Give items
        List<ItemStack> rewardedItems = giveItems(player, itemRewards);
        if (rewardedItems == null) {
            return false;
        }

        // Run reward commands
        if (!plugin.getPlayers().checkChallenge(player.getUniqueId(), challenge)) {
            // First time
            List<String> commands = getChallengeConfig()
                    .getStringList("challenges.challengeList." + challenge.toLowerCase() + ".rewardcommands");
            runCommands(player, commands);
        } else {
            // Repeat challenge
            List<String> commands = getChallengeConfig()
                    .getStringList("challenges.challengeList." + challenge.toLowerCase() + ".repeatrewardcommands");
            runCommands(player, commands);
        }

        // Mark the challenge as complete
        // if (!plugin.getPlayers().checkChallenge(player.getUniqueId(),challenge)) {
        plugin.getPlayers().completeChallenge(player.getUniqueId(), challenge);
        // }
        // Call the Challenge Complete Event
        final ChallengeCompleteEvent event = new ChallengeCompleteEvent(player, challenge, permList, itemRewards,
                moneyReward, expReward, rewardText, rewardedItems);
        plugin.getServer().getPluginManager().callEvent(event);
        return true;
    }

    private void runCommands(Player player, List<String> commands) {
        // Ignore commands with this perm
        if (player.hasPermission(Settings.PERMPREFIX + "command.challengeexempt") && !player.isOp()) {
            return;
        }
        for (String cmd : commands) {
            if (cmd.startsWith("[SELF]")) {
                plugin.getLogger().info("Running command '" + cmd + "' as " + player.getName());
                cmd = cmd.substring(6, cmd.length()).replace("[player]", player.getName()).trim();
                try {
                    player.performCommand(cmd);
                } catch (Exception e) {
                    plugin.getLogger().severe("Problem executing island command executed by player - skipping!");
                    plugin.getLogger().severe("Command was : " + cmd);
                    plugin.getLogger().severe("Error was: " + e.getMessage());
                    e.printStackTrace();
                }

                continue;
            }
            // Substitute in any references to player
            try {
                if (!plugin.getServer().dispatchCommand(plugin.getServer().getConsoleSender(),
                        cmd.replace("[player]", player.getName()))) {
                    plugin.getLogger().severe("Problem executing challenge reward commands - skipping!");
                    plugin.getLogger().severe("Command was : " + cmd);
                }
            } catch (Exception e) {
                plugin.getLogger().severe("Problem executing challenge reward commands - skipping!");
                plugin.getLogger().severe("Command was : " + cmd);
                plugin.getLogger().severe("Error was: " + e.getMessage());
                e.printStackTrace();
            }
        }
    }

    /**
     * Gives player the reward items. 
     * @param player
     * @param itemRewards
     * @return List of ItemStacks that were given to the player or null if there was an error in the interpretation of the rewards
     */
    private List<ItemStack> giveItems(Player player, String[] itemRewards) {
        List<ItemStack> rewardedItems = new ArrayList<ItemStack>();
        Material rewardItem;
        int rewardQty;
        // Build the item stack of rewards to give the player
        for (final String s : itemRewards) {
            final String[] element = s.split(":");
            if (element.length == 2) {
                try {
                    if (StringUtils.isNumeric(element[0])) {
                        rewardItem = Material.getMaterial(Integer.parseInt(element[0]));
                    } else {
                        rewardItem = Material.getMaterial(element[0].toUpperCase());
                    }
                    rewardQty = Integer.parseInt(element[1]);
                    ItemStack item = new ItemStack(rewardItem, rewardQty);
                    rewardedItems.add(item);
                    final HashMap<Integer, ItemStack> leftOvers = player.getInventory()
                            .addItem(new ItemStack[] { item });
                    if (!leftOvers.isEmpty()) {
                        player.getWorld().dropItemNaturally(player.getLocation(), leftOvers.get(0));
                    }
                    if (plugin.getServer().getVersion().contains("(MC: 1.8")
                            || plugin.getServer().getVersion().contains("(MC: 1.7")) {
                        player.getWorld().playSound(player.getLocation(), Sound.valueOf("ITEM_PICKUP"), 1F, 1F);
                    } else {
                        player.getWorld().playSound(player.getLocation(), Sound.ENTITY_ITEM_PICKUP, 1F, 1F);
                    }
                } catch (Exception e) {
                    player.sendMessage(
                            ChatColor.RED + plugin.myLocale(player.getUniqueId()).challengeserrorRewardProblem);
                    plugin.getLogger().severe("Could not give " + element[0] + ":" + element[1] + " to "
                            + player.getName() + " for challenge reward!");
                    String materialList = "";
                    boolean hint = false;
                    for (Material m : Material.values()) {
                        materialList += m.toString() + ",";
                        if (element[0].length() > 3) {
                            if (m.toString().startsWith(element[0].substring(0, 3))) {
                                plugin.getLogger().severe(
                                        "Did you mean " + m.toString() + "? If so, put that in challenges.yml.");
                                hint = true;
                            }
                        }
                    }
                    if (!hint) {
                        plugin.getLogger().severe(
                                "Sorry, I have no idea what " + element[0] + " is. Pick from one of these:");
                        plugin.getLogger().severe(materialList.substring(0, materialList.length() - 1));
                    }
                }
            } else if (element.length == 3) {
                try {
                    if (StringUtils.isNumeric(element[0])) {
                        rewardItem = Material.getMaterial(Integer.parseInt(element[0]));
                    } else {
                        rewardItem = Material.getMaterial(element[0].toUpperCase());
                    }
                    rewardQty = Integer.parseInt(element[2]);
                    // Check for POTION
                    if (rewardItem.equals(Material.POTION)) {
                        givePotion(player, rewardedItems, element, rewardQty);
                    } else {
                        ItemStack item = null;
                        // Normal item, not a potion, check if it is a Monster Egg
                        if (rewardItem.equals(Material.MONSTER_EGG)) {

                            try {
                                EntityType type = EntityType.valueOf(element[1].toUpperCase());
                                if (Bukkit.getServer().getVersion().contains("(MC: 1.8")
                                        || Bukkit.getServer().getVersion().contains("(MC: 1.7")) {
                                    item = new SpawnEgg(type).toItemStack(rewardQty);
                                } else {
                                    try {
                                        item = new SpawnEgg1_9(type).toItemStack(rewardQty);
                                    } catch (Exception ex) {
                                        item = new ItemStack(rewardItem);
                                        plugin.getLogger()
                                                .severe("Monster eggs not supported with this server version.");
                                    }
                                }
                            } catch (Exception e) {
                                Bukkit.getLogger().severe(
                                        "Spawn eggs must be described by name. Try one of these (not all are possible):");
                                for (EntityType type : EntityType.values()) {
                                    if (type.isSpawnable() && type.isAlive()) {
                                        plugin.getLogger().severe(type.toString());
                                    }
                                }
                            }
                        } else {
                            int rewMod = Integer.parseInt(element[1]);
                            item = new ItemStack(rewardItem, rewardQty, (short) rewMod);
                        }
                        if (item != null) {
                            rewardedItems.add(item);
                            final HashMap<Integer, ItemStack> leftOvers = player.getInventory()
                                    .addItem(new ItemStack[] { item });
                            if (!leftOvers.isEmpty()) {
                                player.getWorld().dropItemNaturally(player.getLocation(), leftOvers.get(0));
                            }
                        }
                    }
                    if (plugin.getServer().getVersion().contains("(MC: 1.8")
                            || plugin.getServer().getVersion().contains("(MC: 1.7")) {
                        player.getWorld().playSound(player.getLocation(), Sound.valueOf("ITEM_PICKUP"), 1F, 1F);
                    } else {
                        player.getWorld().playSound(player.getLocation(), Sound.ENTITY_ITEM_PICKUP, 1F, 1F);
                    }
                } catch (Exception e) {
                    player.sendMessage(
                            ChatColor.RED + "There was a problem giving your reward. Ask Admin to check log!");
                    plugin.getLogger().severe("Could not give " + element[0] + ":" + element[1] + " to "
                            + player.getName() + " for challenge reward!");
                    /*
                    if (element[0].equalsIgnoreCase("POTION")) {
                    String potionList = "";
                    boolean hint = false;
                    for (PotionEffectType m : PotionEffectType.values()) {
                        potionList += m.toString() + ",";
                        if (element[1].length() > 3) {
                            if (m.toString().startsWith(element[1].substring(0, 3))) {
                                plugin.getLogger().severe("Did you mean " + m.toString() + "?");
                                hint = true;
                            }
                        }
                    }
                    if (!hint) {
                        plugin.getLogger().severe("Sorry, I have no idea what potion type " + element[1] + " is. Pick from one of these:");
                        plugin.getLogger().severe(potionList.substring(0, potionList.length() - 1));
                    }
                        
                    } else {*/
                    String materialList = "";
                    boolean hint = false;
                    for (Material m : Material.values()) {
                        materialList += m.toString() + ",";
                        if (m.toString().startsWith(element[0].substring(0, 3))) {
                            plugin.getLogger().severe(
                                    "Did you mean " + m.toString() + "? If so, put that in challenges.yml.");
                            hint = true;
                        }
                    }
                    if (!hint) {
                        plugin.getLogger().severe(
                                "Sorry, I have no idea what " + element[0] + " is. Pick from one of these:");
                        plugin.getLogger().severe(materialList.substring(0, materialList.length() - 1));
                    }
                    //}
                    return null;
                }
            } else if (element.length == 6) {
                //plugin.getLogger().info("DEBUG: 6 element reward");
                // Potion format = POTION:name:level:extended:splash:qty
                try {
                    if (StringUtils.isNumeric(element[0])) {
                        rewardItem = Material.getMaterial(Integer.parseInt(element[0]));
                    } else {
                        rewardItem = Material.getMaterial(element[0].toUpperCase());
                    }
                    rewardQty = Integer.parseInt(element[5]);
                    // Check for POTION
                    if (rewardItem.equals(Material.POTION)) {
                        givePotion(player, rewardedItems, element, rewardQty);
                    }
                } catch (Exception e) {
                    player.sendMessage(
                            ChatColor.RED + "There was a problem giving your reward. Ask Admin to check log!");
                    plugin.getLogger().severe("Problem with reward potion: " + s);
                    plugin.getLogger().severe("Format POTION:NAME:<LEVEL>:<EXTENDED>:<SPLASH/LINGER>:QTY");
                    plugin.getLogger().severe("LEVEL, EXTENDED and SPLASH are optional");
                    plugin.getLogger().severe("LEVEL is a number");
                    plugin.getLogger().severe("Examples:");
                    plugin.getLogger().severe("POTION:STRENGTH:1:EXTENDED:SPLASH:1");
                    plugin.getLogger().severe("POTION:JUMP:2:NOTEXTENDED:NOSPLASH:1");
                    plugin.getLogger().severe("POTION:WEAKNESS:::::1   -  any weakness potion");
                    plugin.getLogger().severe("Available names are:");
                    String potionNames = "";
                    for (PotionType p : PotionType.values()) {
                        potionNames += p.toString() + ", ";
                    }
                    plugin.getLogger().severe(potionNames.substring(0, potionNames.length() - 2));
                    return null;
                }
            }
        }
        return rewardedItems;
    }

    /**
     * Converts a serialized potion to a ItemStack of that potion
     * @param element
     * @param rewardQty
     * @param configFile that is being used
     * @return ItemStack of the potion
     */
    public static ItemStack getPotion(String[] element, int rewardQty, String configFile) {
        // Check for potion aspects
        boolean splash = false;
        boolean extended = false;
        boolean linger = false;
        int level = 1;

        if (element.length > 2) {
            // Add level etc.
            if (!element[2].isEmpty()) {
                try {
                    level = Integer.valueOf(element[2]);
                } catch (Exception e) {
                    level = 1;
                }
            }
        }
        if (element.length > 3) {
            //Bukkit.getLogger().info("DEBUG: level = " + Integer.valueOf(element[2]));
            if (element[3].equalsIgnoreCase("EXTENDED")) {
                //Bukkit.getLogger().info("DEBUG: Extended");
                extended = true;
            }
        }
        if (element.length > 4) {
            if (element[4].equalsIgnoreCase("SPLASH")) {
                //Bukkit.getLogger().info("DEBUG: splash");
                splash = true;
            }
            if (element[4].equalsIgnoreCase("LINGER")) {
                //Bukkit.getLogger().info("DEBUG: linger");
                linger = true;
            }
        }

        if (Bukkit.getServer().getVersion().contains("(MC: 1.8")
                || Bukkit.getServer().getVersion().contains("(MC: 1.7")) {
            // Add the effect of the potion
            final PotionType potionType = PotionType.valueOf(element[1]);
            if (potionType == null) {
                Bukkit.getLogger()
                        .severe("Potion effect '" + element[1] + "' in " + configFile + " is unknown - skipping!");
                Bukkit.getLogger().severe("Use one of the following:");
                for (PotionType name : PotionType.values()) {
                    Bukkit.getLogger().severe(name.name());
                }
            } else {
                final Potion rewPotion = new Potion(potionType);
                if (potionType != PotionType.INSTANT_DAMAGE && potionType != PotionType.INSTANT_HEAL) {
                    // Instant potions cannot be extended
                    rewPotion.setHasExtendedDuration(extended);
                }
                rewPotion.setLevel(level);
                rewPotion.setSplash(splash);
                return rewPotion.toItemStack(rewardQty);
            }
        } else {
            // 1.9
            try {
                ItemStack result = new ItemStack(Material.POTION, rewardQty);
                if (splash) {
                    result = new ItemStack(Material.SPLASH_POTION, rewardQty);
                }
                if (linger) {
                    result = new ItemStack(Material.LINGERING_POTION, rewardQty);
                }
                PotionMeta potionMeta = (PotionMeta) result.getItemMeta();
                try {
                    PotionData potionData = new PotionData(PotionType.valueOf(element[1].toUpperCase()), extended,
                            level > 1 ? true : false);
                    potionMeta.setBasePotionData(potionData);
                } catch (IllegalArgumentException iae) {
                    Bukkit.getLogger()
                            .severe("Potion parsing problem with " + element[1] + ": " + iae.getMessage());
                    potionMeta.setBasePotionData(new PotionData(PotionType.WATER));
                }
                result.setItemMeta(potionMeta);
                return result;
                /*
                Potion1_9 rewPotion = new Potion1_9(Potion1_9.PotionType.valueOf(element[1].toUpperCase()));
                rewPotion.setHasExtendedDuration(extended);                
                rewPotion.setStrong(level > 1 ? true : false);
                rewPotion.setSplash(splash);
                rewPotion.setLinger(linger);
                return rewPotion.toItemStack(rewardQty); 
                 */
            } catch (Exception e) {
                e.printStackTrace();
                Bukkit.getLogger()
                        .severe("Potion effect '" + element[1] + "' in " + configFile + " is unknown - skipping!");
                Bukkit.getLogger().severe("Use one of the following:");
                for (PotionType name : PotionType.values()) {
                    Bukkit.getLogger().severe(name.name());
                }
                return new ItemStack(Material.POTION, rewardQty);
            }
        }
        return null;
    }

    private void givePotion(Player player, List<ItemStack> rewardedItems, String[] element, int rewardQty) {
        ItemStack item = getPotion(element, rewardQty, "challenges.yml");
        rewardedItems.add(item);
        final HashMap<Integer, ItemStack> leftOvers = player.getInventory().addItem(item);
        if (!leftOvers.isEmpty()) {
            player.getWorld().dropItemNaturally(player.getLocation(), leftOvers.get(0));
        }

    }

    /**
     * Returns a color formatted string for all the challenges of a particular
     * level for a player Repeatable challenges are AQUA, completed are Dark
     * Green and yet to be done are green.
     * 
     * @param player
     * @param level
     * @return string of challenges
     */
    /*
     * private String getChallengesByLevel(final Player player, final String
     * level) {
     * List<String> levelChallengeList = challengeList.get(level);
     * String response = "";
     * for (String challenge : levelChallengeList) {
     * if (plugin.getPlayers().checkChallenge(player.getUniqueId(), challenge)) {
     * if (getChallengeConfig().getBoolean("challenges.challengeList." +
     * challenge + ".repeatable", false)) {
     * response += ChatColor.AQUA + challenge + ", ";
     * } else {
     * response += ChatColor.DARK_GREEN + challenge + ", ";
     * }
     * } else {
     * response += ChatColor.GREEN + challenge + ", ";
     * }
     * }
     * // Trim the final dash
     * if (response.length() > 3) {
     * response = response.substring(0, response.length() - 2);
     * }
     * return response;
     * }
     */
    /**
     * Returns the number of challenges that must still be completed to finish a
     * level Based on how many challenges there are in a level, how many have
     * been done and how many are okay to leave undone.
     * 
     * @param player
     * @param level
     * @return int of challenges that must still be completed to finish level.
     */
    public int checkLevelCompletion(final Player player, final String level) {
        if (Settings.freeLevels.contains(level)) {
            return 0;
        }
        int challengesCompleted = 0;
        List<String> levelChallengeList = challengeList.get(level);
        int waiver = Settings.waiverAmount;
        if (levelChallengeList != null) {
            for (String challenge : levelChallengeList) {
                if (plugin.getPlayers().checkChallenge(player.getUniqueId(), challenge)) {
                    challengesCompleted++;
                }
            }
            // If the number of challenges in a level is below the waiver amount, then they all need to be done
            if (levelChallengeList.size() <= Settings.waiverAmount) {
                waiver = 0;
            }
            return levelChallengeList.size() - waiver - challengesCompleted;
        }
        return 0;
    }

    /**
     * Checks if player can complete challenge
     * 
     * @param player
     * @param challenge
     * @return true if player can complete otherwise false
     */
    public boolean checkIfCanCompleteChallenge(final Player player, final String challenge) {
        // plugin.getLogger().info("DEBUG: " + player.getDisplayName() + " " +
        // challenge);
        // plugin.getLogger().info("DEBUG: 1");
        // Check if the challenge exists
        /*
        if (!isLevelAvailable(player, getChallengeConfig().getString("challenges.challengeList." + challenge.toLowerCase() + ".level"))) {
        player.sendMessage(ChatColor.RED + plugin.myLocale(player.getUniqueId()).challengesunknownChallenge + " '" + challenge + "'");
        return false;
        }*/
        // Check if this challenge level is available
        String level = getChallengeConfig().getString("challenges.challengeList." + challenge + ".level");
        if (level == null) {
            player.sendMessage(ChatColor.RED + plugin.myLocale(player.getUniqueId()).challengesunknownChallenge
                    + " '" + challenge + "'");
            return false;
        }
        // Only check if the challenge has a level, otherwise it's a free level
        if (!level.isEmpty()) {
            if (!isLevelAvailable(player, level)) {
                player.sendMessage(
                        ChatColor.RED + plugin.myLocale(player.getUniqueId()).challengesyouHaveNotUnlocked);
                return false;
            }
        }
        // Check if the player has maxed out the challenge
        if (getChallengeConfig().getBoolean("challenges.challengeList." + challenge + ".repeatable")) {
            int maxTimes = getChallengeConfig().getInt("challenges.challengeList." + challenge + ".maxtimes", 0);
            if (maxTimes > 0) {
                // There is a limit
                if (plugin.getPlayers().checkChallengeTimes(player.getUniqueId(), challenge) >= maxTimes) {
                    player.sendMessage(
                            ChatColor.RED + plugin.myLocale(player.getUniqueId()).challengesnotRepeatable);
                    return false;
                }
            }
        }
        // plugin.getLogger().info("DEBUG: 2");
        // Check if it is repeatable
        if (plugin.getPlayers().checkChallenge(player.getUniqueId(), challenge)
                && !getChallengeConfig().getBoolean("challenges.challengeList." + challenge + ".repeatable")) {
            player.sendMessage(ChatColor.RED + plugin.myLocale(player.getUniqueId()).challengesnotRepeatable);
            return false;
        }
        // plugin.getLogger().info("DEBUG: 3");
        // If the challenge is an island type and already done, then this too is
        // not repeatable
        if (plugin.getPlayers().checkChallenge(player.getUniqueId(), challenge) && getChallengeConfig()
                .getString("challenges.challengeList." + challenge + ".type").equalsIgnoreCase("island")) {
            player.sendMessage(ChatColor.RED + plugin.myLocale(player.getUniqueId()).challengesnotRepeatable);
            return false;
        }
        // plugin.getLogger().info("DEBUG: 4");
        // Check if this is an inventory challenge
        if (getChallengeConfig().getString("challenges.challengeList." + challenge + ".type")
                .equalsIgnoreCase("inventory")) {
            // Check if the player has the required items
            if (!hasRequired(player, challenge, "inventory")) {
                player.sendMessage(
                        ChatColor.RED + plugin.myLocale(player.getUniqueId()).challengeserrorNotEnoughItems);
                String desc = ChatColor.translateAlternateColorCodes('&',
                        getChallengeConfig().getString("challenges.challengeList." + challenge + ".description", "")
                                .replace("[label]", Settings.ISLANDCOMMAND));
                List<String> result = new ArrayList<String>();
                if (desc.contains("|")) {
                    result.addAll(Arrays.asList(desc.split("\\|")));
                } else {
                    result.add(desc);
                }
                for (String line : result) {
                    player.sendMessage(ChatColor.RED + line);
                }
                return false;
            }
            return true;
        }
        // plugin.getLogger().info("DEBUG: 5");
        // Check if this is an island-based challenge
        if (getChallengeConfig().getString("challenges.challengeList." + challenge + ".type")
                .equalsIgnoreCase("island")) {
            // plugin.getLogger().info("DEBUG: 6");
            // Don't count coop islands
            if (!plugin.getGrid().playerIsOnIsland(player, false)) {
                player.sendMessage(
                        ChatColor.RED + plugin.myLocale(player.getUniqueId()).challengeserrorNotOnIsland);
                return false;
            }
            if (!hasRequired(player, challenge, "island")) {
                int searchRadius = getChallengeConfig()
                        .getInt("challenges.challengeList." + challenge + ".searchRadius", 10);
                if (searchRadius < 10) {
                    searchRadius = 10;
                } else if (searchRadius > 50) {
                    searchRadius = 50;
                }
                player.sendMessage(
                        ChatColor.RED + plugin.myLocale(player.getUniqueId()).challengeserrorNotCloseEnough
                                .replace("[number]", String.valueOf(searchRadius)));
                String desc = ChatColor.translateAlternateColorCodes('&',
                        getChallengeConfig().getString("challenges.challengeList." + challenge + ".description")
                                .replace("[label]", Settings.ISLANDCOMMAND));
                List<String> result = new ArrayList<String>();
                if (desc.contains("|")) {
                    result.addAll(Arrays.asList(desc.split("\\|")));
                } else {
                    result.add(desc);
                }
                for (String line : result) {
                    player.sendMessage(ChatColor.RED + line);
                }
                return false;
            }
            // plugin.getLogger().info("DEBUG: 7");
            return true;
        }
        // Island level check
        if (getChallengeConfig().getString("challenges.challengeList." + challenge + ".type")
                .equalsIgnoreCase("level")) {
            if (plugin.getPlayers().getIslandLevel(player.getUniqueId()) >= getChallengeConfig()
                    .getInt("challenges.challengeList." + challenge + ".requiredItems")) {
                return true;
            }

            player.sendMessage(ChatColor.RED + plugin.myLocale(player.getUniqueId()).challengeserrorIslandLevel
                    .replace("[level]", String.valueOf(getChallengeConfig()
                            .getInt("challenges.challengeList." + challenge + ".requiredItems"))));
            return false;
        }
        player.sendMessage(ChatColor.RED + plugin.myLocale(player.getUniqueId()).errorCommandNotReady);
        plugin.getLogger().severe("The challenge " + challenge + " is of an unknown type "
                + getChallengeConfig().getString("challenges.challengeList." + challenge + ".type"));
        plugin.getLogger().severe("Types should be 'island', 'inventory' or 'level'");
        return false;
    }

    /**
     * Goes through all the challenges in the config.yml file and puts them into
     * the challenges list
     */
    public void populateChallengeList() {
        challengeList.clear();
        for (String s : Settings.challengeList) {
            String level = getChallengeConfig().getString("challenges.challengeList." + s + ".level", "");
            // Verify that this challenge's level is in the list of levels
            if (Settings.challengeLevels.contains(level) || level.isEmpty()) {
                if (challengeList.containsKey(level)) {
                    challengeList.get(level).add(s);
                } else {
                    List<String> t = new ArrayList<String>();
                    t.add(s);
                    challengeList.put(level, t);
                }
            } else {
                plugin.getServer().getLogger().severe(
                        "Level (" + level + ") for challenge " + s + " does not exist. Check challenges.yml.");
            }
        }
    }

    /**
     * Checks if a player has enough for a challenge. Supports two types of
     * checks, inventory and island. Removes items if required.
     * 
     * @param player
     * @param challenge
     * @param type
     * @return true if the player has everything required
     */

    public boolean hasRequired(final Player player, final String challenge, final String type) {
        // Check money
        double moneyReq = 0D;
        if (Settings.useEconomy) {
            moneyReq = getChallengeConfig().getDouble("challenges.challengeList." + challenge + ".requiredMoney",
                    0D);
            if (moneyReq > 0D) {
                if (!VaultHelper.econ.has(player, Settings.worldName, moneyReq)) {
                    player.sendMessage(
                            ChatColor.RED + plugin.myLocale(player.getUniqueId()).challengeserrorNotEnoughItems);
                    String desc = ChatColor.translateAlternateColorCodes('&',
                            getChallengeConfig().getString("challenges.challengeList." + challenge + ".description")
                                    .replace("[label]", Settings.ISLANDCOMMAND));
                    List<String> result = new ArrayList<String>();
                    if (desc.contains("|")) {
                        result.addAll(Arrays.asList(desc.split("\\|")));
                    } else {
                        result.add(desc);
                    }
                    for (String line : result) {
                        player.sendMessage(ChatColor.RED + line);
                    }
                    return false;
                }
            }
        }
        final String reqList = getChallengeConfig()
                .getString("challenges.challengeList." + challenge + ".requiredItems");
        // The format of the requiredItems is as follows:
        // Material:Qty
        // or
        // Material:DamageModifier:Qty
        // This second one is so that items such as potions or variations on
        // standard items can be collected
        if (type.equalsIgnoreCase("inventory")) {
            List<ItemStack> toBeRemoved = new ArrayList<ItemStack>();
            Material reqItem;
            int reqAmount = 0;
            if (!reqList.isEmpty()) {
                for (final String s : reqList.split(" ")) {
                    final String[] part = s.split(":");
                    // Material:Qty
                    if (part.length == 2) {
                        try {
                            // Correct some common mistakes
                            if (part[0].equalsIgnoreCase("potato")) {
                                part[0] = "POTATO_ITEM";
                            } else if (part[0].equalsIgnoreCase("brewing_stand")) {
                                part[0] = "BREWING_STAND_ITEM";
                            } else if (part[0].equalsIgnoreCase("carrot")) {
                                part[0] = "CARROT_ITEM";
                            } else if (part[0].equalsIgnoreCase("cauldron")) {
                                part[0] = "CAULDRON_ITEM";
                            } else if (part[0].equalsIgnoreCase("skull")) {
                                part[0] = "SKULL_ITEM";
                            }
                            // TODO: add netherwart vs. netherstalk?
                            if (StringUtils.isNumeric(part[0])) {
                                reqItem = Material.getMaterial(Integer.parseInt(part[0]));
                            } else {
                                reqItem = Material.getMaterial(part[0].toUpperCase());
                            }
                            reqAmount = Integer.parseInt(part[1]);
                            ItemStack item = new ItemStack(reqItem);
                            if (DEBUG) {
                                plugin.getLogger().info("DEBUG: required item = " + reqItem.toString());
                                plugin.getLogger().info("DEBUG: item amount = " + reqAmount);
                            }
                            if (!player.getInventory().contains(reqItem)) {
                                if (DEBUG)
                                    plugin.getLogger().info("DEBUG: item not in inventory");
                                return false;
                            } else {
                                // check amount
                                int amount = 0;
                                if (DEBUG)
                                    plugin.getLogger().info("DEBUG: Amount in inventory = "
                                            + player.getInventory().all(reqItem).size());
                                // Go through all the inventory and try to find
                                // enough required items
                                for (Entry<Integer, ? extends ItemStack> en : player.getInventory().all(reqItem)
                                        .entrySet()) {
                                    // Get the item
                                    ItemStack i = en.getValue();
                                    // If the item is enchanted, skip - it doesn't count
                                    if (!i.getEnchantments().isEmpty()) {
                                        if (DEBUG)
                                            plugin.getLogger().info("DEBUG: item has enchantment - doesn't count");
                                        continue;
                                    }
                                    // Map needs special handling because the
                                    // durability increments every time a new one is
                                    // made by the player
                                    // TODO: if there are any other items that act
                                    // in the same way, they need adding too...
                                    if (i.getDurability() == 0
                                            || (reqItem == Material.MAP && i.getType() == Material.MAP)) {
                                        // Clear any naming, or lore etc.
                                        //i.setItemMeta(null);
                                        //player.getInventory().setItem(en.getKey(), i);
                                        // #1 item stack qty + amount is less than
                                        // required items - take all i
                                        // #2 item stack qty + amount = required
                                        // item -
                                        // take all
                                        // #3 item stack qty + amount > req items -
                                        // take
                                        // portion of i
                                        // amount += i.getAmount();
                                        if ((amount + i.getAmount()) < reqAmount) {
                                            // Remove all of this item stack - clone
                                            // otherwise it will keep a reference to
                                            // the
                                            // original
                                            toBeRemoved.add(i.clone());
                                            amount += i.getAmount();
                                            if (DEBUG)
                                                plugin.getLogger()
                                                        .info("DEBUG: amount is <= req Remove " + i.toString() + ":"
                                                                + i.getDurability() + " x " + i.getAmount());
                                        } else if ((amount + i.getAmount()) == reqAmount) {
                                            if (DEBUG)
                                                plugin.getLogger()
                                                        .info("DEBUG: amount is = req Remove " + i.toString() + ":"
                                                                + i.getDurability() + " x " + i.getAmount());
                                            toBeRemoved.add(i.clone());
                                            amount += i.getAmount();
                                            break;
                                        } else {
                                            // Remove a portion of this item
                                            if (DEBUG)
                                                plugin.getLogger()
                                                        .info("DEBUG: amount is > req Remove " + i.toString() + ":"
                                                                + i.getDurability() + " x " + i.getAmount());

                                            item.setAmount(reqAmount - amount);
                                            item.setDurability(i.getDurability());
                                            toBeRemoved.add(item);
                                            amount += i.getAmount();
                                            break;
                                        }
                                    }
                                }
                                if (DEBUG)
                                    plugin.getLogger().info("DEBUG: amount " + amount);
                                if (amount < reqAmount) {
                                    return false;
                                }
                            }
                        } catch (Exception e) {
                            plugin.getLogger().severe("Problem with " + s + " in challenges.yml!");
                            player.sendMessage(
                                    ChatColor.RED + plugin.myLocale(player.getUniqueId()).errorCommandNotReady);
                            String materialList = "";
                            boolean hint = false;
                            for (Material m : Material.values()) {
                                materialList += m.toString() + ",";
                                if (m.toString().contains(s.substring(0, 3).toUpperCase())) {
                                    plugin.getLogger().severe("Did you mean " + m.toString() + "?");
                                    hint = true;
                                }
                            }
                            if (!hint) {
                                plugin.getLogger()
                                        .severe("Sorry, I have no idea what " + s + " is. Pick from one of these:");
                                plugin.getLogger().severe(materialList.substring(0, materialList.length() - 1));
                            } else {
                                plugin.getLogger().severe("Correct challenges.yml with the correct material.");
                            }
                            return false;
                        }
                    } else if (part.length == 3) {
                        if (DEBUG)
                            plugin.getLogger().info("DEBUG: Item with durability");
                        // This handles items with durability
                        // Correct some common mistakes
                        if (part[0].equalsIgnoreCase("potato")) {
                            part[0] = "POTATO_ITEM";
                        } else if (part[0].equalsIgnoreCase("brewing_stand")) {
                            part[0] = "BREWING_STAND_ITEM";
                        } else if (part[0].equalsIgnoreCase("carrot")) {
                            part[0] = "CARROT_ITEM";
                        } else if (part[0].equalsIgnoreCase("cauldron")) {
                            part[0] = "CAULDRON_ITEM";
                        } else if (part[0].equalsIgnoreCase("skull")) {
                            part[0] = "SKULL_ITEM";
                        }
                        if (StringUtils.isNumeric(part[0])) {
                            reqItem = Material.getMaterial(Integer.parseInt(part[0]));
                        } else {
                            reqItem = Material.getMaterial(part[0].toUpperCase());
                        }
                        reqAmount = Integer.parseInt(part[2]);
                        int reqDurability = Integer.parseInt(part[1]);
                        ItemStack item = new ItemStack(reqItem);

                        // Item
                        item.setDurability((short) reqDurability);
                        // check amount
                        int amount = 0;
                        // Go through all the inventory and try to find
                        // enough required items
                        for (Entry<Integer, ? extends ItemStack> en : player.getInventory().all(reqItem)
                                .entrySet()) {
                            // Get the item
                            ItemStack i = en.getValue();
                            if (i.hasItemMeta()) {
                                continue;
                            }
                            if (i.getDurability() == reqDurability) {
                                // Clear any naming, or lore etc.
                                //i.setItemMeta(null);
                                // player.getInventory().setItem(en.getKey(), i);
                                // #1 item stack qty + amount is less than
                                // required items - take all i
                                // #2 item stack qty + amount = required
                                // item -
                                // take all
                                // #3 item stack qty + amount > req items -
                                // take
                                // portion of i
                                // amount += i.getAmount();
                                if ((amount + i.getAmount()) < reqAmount) {
                                    // Remove all of this item stack - clone
                                    // otherwise it will keep a reference to
                                    // the
                                    // original
                                    toBeRemoved.add(i.clone());
                                    amount += i.getAmount();
                                    if (DEBUG)
                                        plugin.getLogger().info("DEBUG: amount is <= req Remove " + i.toString()
                                                + ":" + i.getDurability() + " x " + i.getAmount());
                                } else if ((amount + i.getAmount()) == reqAmount) {
                                    toBeRemoved.add(i.clone());
                                    amount += i.getAmount();
                                    break;
                                } else {
                                    // Remove a portion of this item
                                    if (DEBUG)
                                        plugin.getLogger().info("DEBUG: amount is > req Remove " + i.toString()
                                                + ":" + i.getDurability() + " x " + i.getAmount());

                                    item.setAmount(reqAmount - amount);
                                    item.setDurability(i.getDurability());
                                    toBeRemoved.add(item);
                                    amount += i.getAmount();
                                    break;
                                }
                            }
                        }
                        if (DEBUG) {
                            plugin.getLogger().info("DEBUG: amount is " + amount);
                            plugin.getLogger().info("DEBUG: req amount is " + reqAmount);
                        }
                        if (amount < reqAmount) {
                            if (DEBUG)
                                plugin.getLogger().info("DEBUG: Failure! Insufficient amount of " + item.toString()
                                        + " required = " + reqAmount + " actual = " + amount);
                            return false;
                        }
                        if (DEBUG)
                            plugin.getLogger().info("DEBUG: before set amount " + item.toString() + ":"
                                    + item.getDurability() + " x " + item.getAmount());

                    } else if (part.length == 6 && part[0].contains("POTION")) {
                        // Run through player's inventory for the item
                        ItemStack[] playerInv = player.getInventory().getContents();
                        try {
                            reqAmount = Integer.parseInt(part[5]);
                            if (DEBUG)
                                plugin.getLogger().info("DEBUG: required amount is " + reqAmount);
                        } catch (Exception e) {
                            plugin.getLogger().severe("Could not parse the quantity of the potion item " + s);
                            return false;
                        }
                        int count = reqAmount;
                        for (ItemStack i : playerInv) {
                            // Catches all POTION, LINGERING_POTION and SPLASH_POTION
                            if (i != null && i.getType().toString().contains("POTION")) {
                                //plugin.getLogger().info("DEBUG:6 part potion check!");
                                // POTION:NAME:<LEVEL>:<EXTENDED>:<SPLASH/LINGER>:QTY
                                if (plugin.getServer().getVersion().contains("(MC: 1.8")
                                        || plugin.getServer().getVersion().contains("(MC: 1.7")) {
                                    // Test potion
                                    Potion potion = Potion.fromItemStack(i);
                                    PotionType potionType = potion.getType();
                                    boolean match = true;
                                    if (DEBUG)
                                        plugin.getLogger().info("DEBUG: name check " + part[1]);
                                    // Name check
                                    if (potion != null && potionType != null && !part[1].isEmpty()) {
                                        // There is a name
                                        // Custom potions may not have names
                                        if (potionType.name() != null) {
                                            if (!part[1].equalsIgnoreCase(potionType.name())) {
                                                match = false;
                                                if (DEBUG)
                                                    plugin.getLogger().info("DEBUG: name does not match");
                                            } else {
                                                if (DEBUG)
                                                    plugin.getLogger().info("DEBUG: name matches");
                                            }
                                        } else {
                                            plugin.getLogger().severe(
                                                    "Potion type is unknown. Please pick from the following:");
                                            for (PotionType pt : PotionType.values()) {
                                                plugin.getLogger().severe(pt.name());
                                            }
                                            match = false;
                                        }
                                    }
                                    // Level check (upgraded)
                                    if (DEBUG)
                                        plugin.getLogger().info("DEBUG: level check " + part[2]);
                                    if (!part[2].isEmpty()) {
                                        // There is a level declared - check it
                                        if (StringUtils.isNumeric(part[2])) {
                                            int level = Integer.valueOf(part[2]);
                                            if (level != potion.getLevel()) {
                                                if (DEBUG)
                                                    plugin.getLogger().info("DEBUG: level does not match");
                                                match = false;
                                            }
                                        }
                                    }
                                    // Extended check
                                    if (DEBUG)
                                        plugin.getLogger().info("DEBUG: extended check " + part[3]);
                                    if (!part[3].isEmpty()) {
                                        if (part[3].equalsIgnoreCase("EXTENDED") && !potion.hasExtendedDuration()) {
                                            match = false;
                                            if (DEBUG)
                                                plugin.getLogger().info("DEBUG: extended does not match");
                                        }
                                        if (part[3].equalsIgnoreCase("NOTEXTENDED")
                                                && potion.hasExtendedDuration()) {
                                            match = false;
                                            if (DEBUG)
                                                plugin.getLogger().info("DEBUG: extended does not match");
                                        }
                                    }
                                    // Splash check
                                    if (DEBUG)
                                        plugin.getLogger().info("DEBUG: splash/linger check " + part[4]);
                                    if (!part[4].isEmpty()) {
                                        if (part[4].equalsIgnoreCase("SPLASH") && !potion.isSplash()) {
                                            match = false;
                                            if (DEBUG)
                                                plugin.getLogger().info("DEBUG: not splash");
                                        }
                                        if (part[4].equalsIgnoreCase("NOSPLASH") && potion.isSplash()) {
                                            match = false;
                                            if (DEBUG)
                                                plugin.getLogger().info("DEBUG: not no splash");
                                        }
                                    }
                                    // Quantity check
                                    if (match) {
                                        if (DEBUG)
                                            plugin.getLogger().info("DEBUG: potion matches!");
                                        ItemStack removeItem = i.clone();
                                        if (removeItem.getAmount() > reqAmount) {
                                            if (DEBUG)
                                                plugin.getLogger().info(
                                                        "DEBUG: found " + removeItem.getAmount() + " qty in inv");
                                            removeItem.setAmount(reqAmount);
                                        }
                                        count = count - removeItem.getAmount();
                                        if (DEBUG)
                                            plugin.getLogger().info("DEBUG: " + count + " left");
                                        toBeRemoved.add(removeItem);
                                    }
                                } else {
                                    // V1.9 and above
                                    PotionMeta potionMeta = (PotionMeta) i.getItemMeta();
                                    // If any of the settings above are missing, then any is okay
                                    boolean match = true;
                                    if (DEBUG)
                                        plugin.getLogger().info("DEBUG: name check " + part[1]);
                                    // Name check
                                    if (!part[1].isEmpty()) {
                                        // There is a name
                                        if (PotionType.valueOf(part[1]) != null) {
                                            if (!potionMeta.getBasePotionData().getType().name()
                                                    .equalsIgnoreCase(part[1])) {
                                                match = false;
                                                if (DEBUG)
                                                    plugin.getLogger().info("DEBUG: name does not match");
                                            } else {
                                                if (DEBUG)
                                                    plugin.getLogger().info("DEBUG: name matches");
                                            }
                                        } else {
                                            plugin.getLogger().severe(
                                                    "Potion type is unknown. Please pick from the following:");
                                            for (PotionType pt : PotionType.values()) {
                                                plugin.getLogger().severe(pt.name());
                                            }
                                            match = false;
                                        }
                                    }
                                    // Level check (upgraded)
                                    // plugin.getLogger().info("DEBUG: level check " + part[2]);
                                    if (!part[2].isEmpty()) {
                                        // There is a level declared - check it
                                        if (StringUtils.isNumeric(part[2])) {
                                            int level = Integer.valueOf(part[2]);
                                            if (level == 1 && potionMeta.getBasePotionData().isUpgraded()) {
                                                if (DEBUG)
                                                    plugin.getLogger().info("DEBUG: level does not match");
                                                match = false;
                                            }
                                            if (level != 1 && !potionMeta.getBasePotionData().isUpgraded()) {
                                                match = false;
                                                if (DEBUG)
                                                    plugin.getLogger().info("DEBUG: level does not match");
                                            }
                                        }
                                    }
                                    // Extended check
                                    if (DEBUG)
                                        plugin.getLogger().info("DEBUG: extended check " + part[3]);
                                    if (!part[3].isEmpty()) {
                                        if (part[3].equalsIgnoreCase("EXTENDED")
                                                && !potionMeta.getBasePotionData().isExtended()) {
                                            match = false;
                                            if (DEBUG)
                                                plugin.getLogger().info("DEBUG: extended does not match");
                                        }
                                        if (part[3].equalsIgnoreCase("NOTEXTENDED")
                                                && potionMeta.getBasePotionData().isExtended()) {
                                            match = false;
                                            if (DEBUG)
                                                plugin.getLogger().info("DEBUG: extended does not match");
                                        }
                                    }
                                    // Splash or Linger check
                                    if (DEBUG)
                                        plugin.getLogger().info("DEBUG: splash/linger check " + part[4]);
                                    if (!part[4].isEmpty()) {
                                        if (part[4].equalsIgnoreCase("SPLASH")
                                                && !i.getType().equals(Material.SPLASH_POTION)) {
                                            match = false;
                                            if (DEBUG)
                                                plugin.getLogger().info("DEBUG: not splash");
                                        }
                                        if (part[4].equalsIgnoreCase("NOSPLASH")
                                                && i.getType().equals(Material.SPLASH_POTION)) {
                                            match = false;
                                            if (DEBUG)
                                                plugin.getLogger().info("DEBUG: not no splash");
                                        }
                                        if (part[4].equalsIgnoreCase("LINGER")
                                                && !i.getType().equals(Material.LINGERING_POTION)) {
                                            match = false;
                                            if (DEBUG)
                                                plugin.getLogger().info("DEBUG: not linger");
                                        }
                                        if (part[4].equalsIgnoreCase("NOLINGER")
                                                && i.getType().equals(Material.LINGERING_POTION)) {
                                            match = false;
                                            if (DEBUG)
                                                plugin.getLogger().info("DEBUG: not no linger");
                                        }
                                    }
                                    // Quantity check
                                    if (match) {
                                        if (DEBUG)
                                            plugin.getLogger().info("DEBUG: potion matches!");
                                        ItemStack removeItem = i.clone();
                                        if (removeItem.getAmount() > reqAmount) {
                                            if (DEBUG)
                                                plugin.getLogger().info(
                                                        "DEBUG: found " + removeItem.getAmount() + " qty in inv");
                                            removeItem.setAmount(reqAmount);
                                        }
                                        count = count - removeItem.getAmount();
                                        if (DEBUG)
                                            plugin.getLogger().info("DEBUG: " + count + " left");
                                        toBeRemoved.add(removeItem);
                                    }
                                }
                            }
                            if (count <= 0) {
                                if (DEBUG)
                                    plugin.getLogger().info("DEBUG: Player has enough");
                                break;
                            }
                            if (DEBUG)
                                plugin.getLogger().info("DEBUG: still need " + count + " to complete");
                        }
                        if (count > 0) {
                            if (DEBUG)
                                plugin.getLogger().info("DEBUG: Player does not have enough");
                            return false;
                        }

                    } else {
                        plugin.getLogger().severe("Problem with " + s + " in challenges.yml!");
                        return false;
                    }
                }
            }
            // Build up the items in the inventory and remove them if they are
            // all there.

            if (getChallengeConfig().getBoolean("challenges.challengeList." + challenge + ".takeItems")) {
                // checkChallengeItems(player, challenge);
                // int qty = 0;
                if (DEBUG)
                    plugin.getLogger().info("DEBUG: Removing items");
                for (ItemStack i : toBeRemoved) {
                    // qty += i.getAmount();
                    if (DEBUG)
                        plugin.getLogger().info(
                                "DEBUG: Remove " + i.toString() + "::" + i.getDurability() + " x " + i.getAmount());
                    HashMap<Integer, ItemStack> leftOver = player.getInventory().removeItem(i);
                    if (!leftOver.isEmpty()) {
                        plugin.getLogger().warning("Exploit? Could not remove the following in challenge "
                                + challenge + " for player " + player.getName() + ":");
                        for (ItemStack left : leftOver.values()) {
                            plugin.getLogger().info(left.toString());
                        }
                        return false;
                    }
                }
                // Remove money
                if (moneyReq > 0D) {
                    EconomyResponse er = VaultHelper.econ.withdrawPlayer(player, moneyReq);
                    if (!er.transactionSuccess()) {
                        plugin.getLogger().warning("Exploit? Could not remove " + VaultHelper.econ.format(moneyReq)
                                + " from " + player.getName() + " in challenge " + challenge);
                        plugin.getLogger().warning("Player's balance is "
                                + VaultHelper.econ.format(VaultHelper.econ.getBalance(player)));
                    }
                }
                // plugin.getLogger().info("DEBUG: total = " + qty);
            }
            return true;
        }
        if (type.equalsIgnoreCase("island")) {
            final HashMap<Material, Integer> neededItem = new HashMap<Material, Integer>();
            final HashMap<EntityType, Integer> neededEntities = new HashMap<EntityType, Integer>();
            if (!reqList.isEmpty()) {
                for (int i = 0; i < reqList.split(" ").length; i++) {
                    final String[] sPart = reqList.split(" ")[i].split(":");
                    // Parse the qty required first
                    try {
                        final int qty = Integer.parseInt(sPart[1]);
                        // Find out if the needed item is a Material or an Entity
                        boolean isEntity = false;
                        for (EntityType entityType : EntityType.values()) {
                            if (entityType.toString().equalsIgnoreCase(sPart[0])) {
                                isEntity = true;
                                break;
                            }
                        }
                        if (isEntity) {
                            // plugin.getLogger().info("DEBUG: Item " +
                            // sPart[0].toUpperCase() + " is an entity");
                            EntityType entityType = EntityType.valueOf(sPart[0].toUpperCase());
                            if (entityType != null) {
                                neededEntities.put(entityType, qty);
                                // plugin.getLogger().info("DEBUG: Needed entity is "
                                // + Integer.parseInt(sPart[1]) + " x " +
                                // EntityType.valueOf(sPart[0].toUpperCase()).toString());
                            }
                        } else {
                            Material item;
                            if (StringUtils.isNumeric(sPart[0])) {
                                item = Material.getMaterial(Integer.parseInt(sPart[0]));
                            } else {
                                item = Material.getMaterial(sPart[0].toUpperCase());
                            }
                            if (item != null) {
                                neededItem.put(item, qty);
                                // plugin.getLogger().info("DEBUG: Needed item is "
                                // + Integer.parseInt(sPart[1]) + " x " +
                                // Material.getMaterial(sPart[0]).toString());

                            } else {
                                plugin.getLogger().warning("Problem parsing required item for challenge "
                                        + challenge + " in challenges.yml!");
                                return false;
                            }
                        }
                    } catch (Exception intEx) {
                        plugin.getLogger().warning("Problem parsing required items for challenge " + challenge
                                + " in challenges.yml - skipping");
                        return false;
                    }
                }
            }
            // We now have two sets of required items or entities
            // Check the items first
            final Location l = player.getLocation();
            // if (!neededItem.isEmpty()) {
            final int px = l.getBlockX();
            final int py = l.getBlockY();
            final int pz = l.getBlockZ();
            // Get search radius - min is 10, max is 50
            int searchRadius = getChallengeConfig()
                    .getInt("challenges.challengeList." + challenge + ".searchRadius", 10);
            if (searchRadius < 10) {
                searchRadius = 10;
            } else if (searchRadius > 50) {
                searchRadius = 50;
            }
            for (int x = -searchRadius; x <= searchRadius; x++) {
                for (int y = -searchRadius; y <= searchRadius; y++) {
                    for (int z = -searchRadius; z <= searchRadius; z++) {
                        final Material b = new Location(l.getWorld(), px + x, py + y, pz + z).getBlock().getType();
                        if (neededItem.containsKey(b)) {
                            if (neededItem.get(b) == 1) {
                                neededItem.remove(b);
                            } else {
                                // Reduce the require amount by 1
                                neededItem.put(b, neededItem.get(b) - 1);
                            }
                        }
                    }
                }
            }
            // }
            // Check if all the needed items have been amassed
            if (!neededItem.isEmpty()) {
                // plugin.getLogger().info("DEBUG: Insufficient items around");
                for (Material missing : neededItem.keySet()) {
                    player.sendMessage(
                            ChatColor.RED + plugin.myLocale(player.getUniqueId()).challengeserrorYouAreMissing + " "
                                    + neededItem.get(missing) + " x " + Util.prettifyText(missing.toString()));
                }
                return false;
            } else {
                // plugin.getLogger().info("DEBUG: Items are there");
                // Check for needed entities
                for (Entity entity : player.getNearbyEntities(searchRadius, searchRadius, searchRadius)) {
                    // plugin.getLogger().info("DEBUG: Entity found:" +
                    // entity.getType().toString());
                    if (neededEntities.containsKey(entity.getType())) {
                        // plugin.getLogger().info("DEBUG: Entity in list");
                        if (neededEntities.get(entity.getType()) == 1) {
                            neededEntities.remove(entity.getType());
                            // plugin.getLogger().info("DEBUG: Entity qty satisfied");
                        } else {
                            neededEntities.put(entity.getType(), neededEntities.get(entity.getType()) - 1);
                            // plugin.getLogger().info("DEBUG: Entity qty reduced by 1");
                        }
                    } else {
                        // plugin.getLogger().info("DEBUG: Entity not in list");
                    }
                }
                if (neededEntities.isEmpty()) {
                    return true;
                } else {
                    for (EntityType missing : neededEntities.keySet()) {
                        player.sendMessage(ChatColor.RED
                                + plugin.myLocale(player.getUniqueId()).challengeserrorYouAreMissing + " "
                                + neededEntities.get(missing) + " x " + Util.prettifyText(missing.toString()));
                    }
                    return false;
                }
            }
        }

        return true;
    }

    /**
     * Returns true if the level is unlocked and false if not
     * 
     * @param player
     * @param level
     * @return true/false
     */
    public boolean isLevelAvailable(final Player player, final String level) {
        if (challengeList.size() < 2) {
            return true;
        }
        for (int i = 0; i < Settings.challengeLevels.size(); i++) {
            if (Settings.challengeLevels.get(i).equalsIgnoreCase(level)) {
                if (i == 0) {
                    return true;
                }

                if (checkLevelCompletion(player, Settings.challengeLevels.get(i - 1)) <= 0) {
                    return true;
                }
            }

        }

        return false;
    }

    /**
     * Dynamically creates an inventory of challenges for the player
     * 
     * @param player
     * @return inventory
     */
    public Inventory challengePanel(Player player) {
        String maxLevel = "";
        for (String level : Settings.challengeLevels) {
            if (checkLevelCompletion(player, level) > 0) {
                maxLevel = level;
                break;
            }
        }
        return challengePanel(player, maxLevel);
    }

    /**
     * Dynamically creates an inventory of challenges for the player showing the
     * level
     * 
     * @param player
     * @param level
     * @return inventory
     */
    public Inventory challengePanel(Player player, String level) {
        //plugin.getLogger().info("DEBUG: level requested = " + level);
        // Create the challenges control panel
        // New panel map
        List<CPItem> cp = new ArrayList<CPItem>();

        // Do some checking
        // plugin.getLogger().severe("DEBUG: Opening level " + level);

        if (level.isEmpty() && !challengeList.containsKey("")) {
            if (!Settings.challengeLevels.isEmpty()) {
                level = Settings.challengeLevels.get(0);
            } else {
                // We have no challenges!
                plugin.getLogger().severe("There are no challenges to show!");
                Inventory error = Bukkit.createInventory(null, 9,
                        plugin.myLocale(player.getUniqueId()).challengesguiTitle);
                player.sendMessage(ChatColor.RED + plugin.myLocale(player.getUniqueId()).errorCommandNotReady);
                return error;
            }
        }
        if (challengeList.get(level) != null) {
            // Only show a control panel for the level requested.
            for (String challengeName : challengeList.get(level)) {
                CPItem item = createItem(challengeName, player);
                if (item != null) {
                    cp.add(item);
                }
            }
        }
        // Add the missing levels so player can navigate to them
        int levelDone = 0;
        for (; levelDone < Settings.challengeLevels.size(); levelDone++) {
            if (checkLevelCompletion(player, Settings.challengeLevels.get(levelDone)) > 0) {
                break;
            }
        }
        //plugin.getLogger().info("DEBUG: level done = " + levelDone);
        for (int i = 0; i < Settings.challengeLevels.size(); i++) {
            if (!level.equalsIgnoreCase(Settings.challengeLevels.get(i))) {
                // Add a navigation book
                List<String> lore = new ArrayList<String>();
                if (i <= levelDone) {
                    CPItem item = new CPItem(Material.BOOK_AND_QUILL,
                            ChatColor.GOLD + Settings.challengeLevels.get(i), null, null);
                    lore = Util.chop(ChatColor.WHITE, plugin.myLocale(player.getUniqueId()).challengesNavigation
                            .replace("[level]", Settings.challengeLevels.get(i)), 25);
                    item.setNextSection(Settings.challengeLevels.get(i));
                    item.setLore(lore);
                    cp.add(item);
                } else {
                    // Hint at what is to come
                    CPItem item = new CPItem(Material.BOOK, ChatColor.GOLD + Settings.challengeLevels.get(i), null,
                            null);
                    // Add the level
                    int toDo = checkLevelCompletion(player, Settings.challengeLevels.get(i - 1));
                    lore = Util.chop(ChatColor.WHITE,
                            plugin.myLocale(player.getUniqueId()).challengestoComplete
                                    .replace("[challengesToDo]", String.valueOf(toDo))
                                    .replace("[thisLevel]", Settings.challengeLevels.get(i - 1)),
                            25);
                    item.setLore(lore);
                    cp.add(item);
                }
            }
        }
        // Add the free challenges if not already shown (which can happen if all of the challenges are done!)
        if (!level.equals("") && challengeList.containsKey("")) {
            for (String freeChallenges : challengeList.get("")) {
                CPItem item = createItem(freeChallenges, player);
                if (item != null) {
                    cp.add(item);
                }
            }
        }
        // Create the panel
        if (cp.size() > 0) {
            // Make sure size is a multiple of 9
            int size = cp.size() + 8;
            size -= (size % 9);
            Inventory newPanel = Bukkit.createInventory(null, size,
                    plugin.myLocale(player.getUniqueId()).challengesguiTitle);
            // Store the panel details for retrieval later
            playerChallengeGUI.put(player.getUniqueId(), cp);
            // Fill the inventory and return
            for (CPItem i : cp) {
                newPanel.addItem(i.getItem());
            }
            return newPanel;
        }
        return null;
    }

    /**
     * Creates an inventory item for the challenge
     * @param challengeName
     * @param player
     * @return Control Panel item
     */
    private CPItem createItem(String challengeName, Player player) {
        CPItem item = null;
        // Get the icon
        ItemStack icon = null;
        String iconName = getChallengeConfig().getString("challenges.challengeList." + challengeName + ".icon", "");
        if (!iconName.isEmpty()) {
            try {
                // Split if required
                String[] split = iconName.split(":");
                if (split.length == 1) {
                    // Some material does not show in the inventory
                    if (iconName.equalsIgnoreCase("potato")) {
                        iconName = "POTATO_ITEM";
                    } else if (iconName.equalsIgnoreCase("brewing_stand")) {
                        iconName = "BREWING_STAND_ITEM";
                    } else if (iconName.equalsIgnoreCase("carrot")) {
                        iconName = "CARROT_ITEM";
                    } else if (iconName.equalsIgnoreCase("cauldron")) {
                        iconName = "CAULDRON_ITEM";
                    } else if (iconName.equalsIgnoreCase("lava") || iconName.equalsIgnoreCase("stationary_lava")) {
                        iconName = "LAVA_BUCKET";
                    } else if (iconName.equalsIgnoreCase("water")
                            || iconName.equalsIgnoreCase("stationary_water")) {
                        iconName = "WATER_BUCKET";
                    } else if (iconName.equalsIgnoreCase("portal")) {
                        iconName = "OBSIDIAN";
                    } else if (iconName.equalsIgnoreCase("PUMPKIN_STEM")) {
                        iconName = "PUMPKIN";
                    } else if (iconName.equalsIgnoreCase("skull")) {
                        iconName = "SKULL_ITEM";
                    } else if (iconName.equalsIgnoreCase("COCOA")) {
                        iconName = "INK_SACK:3";
                    } else if (iconName.equalsIgnoreCase("NETHER_WARTS")) {
                        iconName = "NETHER_STALK";
                    }
                    if (StringUtils.isNumeric(iconName)) {
                        icon = new ItemStack(Integer.parseInt(iconName));
                    } else {
                        icon = new ItemStack(Material.valueOf(iconName));
                    }
                    // Check POTION for V1.9 - for some reason, it must be declared as WATER otherwise comparison later causes an NPE
                    if (icon.getType().name().contains("POTION")) {
                        if (!plugin.getServer().getVersion().contains("(MC: 1.8")
                                && !plugin.getServer().getVersion().contains("(MC: 1.7")) {
                            PotionMeta potionMeta = (PotionMeta) icon.getItemMeta();
                            potionMeta.setBasePotionData(new PotionData(PotionType.WATER));
                            icon.setItemMeta(potionMeta);
                        }
                    }
                } else if (split.length == 2) {
                    if (StringUtils.isNumeric(split[0])) {
                        icon = new ItemStack(Integer.parseInt(split[0]));
                    } else {
                        icon = new ItemStack(Material.valueOf(split[0]));
                    }
                    // Check POTION for V1.9 - for some reason, it must be declared as WATER otherwise comparison later causes an NPE
                    if (icon.getType().name().contains("POTION")) {
                        if (!plugin.getServer().getVersion().contains("(MC: 1.8")
                                && !plugin.getServer().getVersion().contains("(MC: 1.7")) {
                            PotionMeta potionMeta = (PotionMeta) icon.getItemMeta();
                            try {
                                potionMeta.setBasePotionData(
                                        new PotionData(PotionType.valueOf(split[1].toUpperCase())));
                            } catch (Exception e) {
                                plugin.getLogger().severe("Challenges icon: Potion type of " + split[1]
                                        + " is unknown, setting to WATER. Valid types are:");
                                for (PotionType type : PotionType.values()) {
                                    plugin.getLogger().severe(type.name());
                                }
                                potionMeta.setBasePotionData(new PotionData(PotionType.WATER));
                            }
                            icon.setItemMeta(potionMeta);
                        }
                    } else if (icon.getType().equals(Material.MONSTER_EGG)) {
                        // Handle monster egg icons
                        try {
                            EntityType type = EntityType.valueOf(split[1].toUpperCase());
                            if (Bukkit.getServer().getVersion().contains("(MC: 1.8")
                                    || Bukkit.getServer().getVersion().contains("(MC: 1.7")) {
                                icon = new SpawnEgg(type).toItemStack();
                            } else {
                                try {
                                    icon = new SpawnEgg1_9(type).toItemStack();
                                } catch (Exception ex) {
                                    plugin.getLogger()
                                            .severe("Monster eggs not supported with this server version.");
                                }
                            }
                        } catch (Exception e) {
                            Bukkit.getLogger().severe(
                                    "Spawn eggs must be described by name. Try one of these (not all are possible):");
                            for (EntityType type : EntityType.values()) {
                                if (type.isSpawnable() && type.isAlive()) {
                                    plugin.getLogger().severe(type.toString());
                                }
                            }
                        }
                    } else {
                        icon.setDurability(Integer.valueOf(split[1]).shortValue());
                    }
                }
            } catch (Exception e) {
                // Icon was not well formatted
                plugin.getLogger().warning(
                        "Error in challenges.yml - icon format is incorrect for " + challengeName + ":" + iconName);
                plugin.getLogger().warning("Format should be 'icon: MaterialType:Damage' where Damage is optional");
            }
        }
        if (icon == null || icon.getType() == Material.AIR) {
            icon = new ItemStack(Material.PAPER);
        }
        String description = ChatColor.GREEN + ChatColor.translateAlternateColorCodes('&',
                getChallengeConfig().getString("challenges.challengeList." + challengeName + ".friendlyname",
                        challengeName.substring(0, 1).toUpperCase() + challengeName.substring(1)));

        // Check if completed or not
        boolean complete = false;
        if (Settings.addCompletedGlow && plugin.getPlayers().checkChallenge(player.getUniqueId(), challengeName)) {
            // Complete! Make the icon glow
            ItemMeta im = icon.getItemMeta();
            im.addEnchant(Enchantment.ARROW_DAMAGE, 0, true);
            icon.setItemMeta(im);
            icon.removeEnchantment(Enchantment.ARROW_DAMAGE);
            complete = true;
        }
        boolean repeatable = false;
        if (getChallengeConfig().getBoolean("challenges.challengeList." + challengeName + ".repeatable", false)) {
            // Repeatable
            repeatable = true;
        }
        // Only show this challenge if it is not done or repeatable if the
        // setting Settings.removeCompleteOntimeChallenges
        if (!complete || ((complete && repeatable) || !Settings.removeCompleteOntimeChallenges)) {
            // Store the challenge panel item and the command that will be
            // called if it is activated.
            item = new CPItem(icon, description, Settings.CHALLENGECOMMAND + " c " + challengeName, null);
            // Get the challenge description, that changes depending on
            // whether the challenge is complete or not.
            List<String> lore = challengeDescription(challengeName, player);
            item.setLore(lore);
        }
        return item;
    }

    public List<CPItem> getCP(Player player) {
        return playerChallengeGUI.get(player.getUniqueId());
    }

    /**
     * Creates the challenge description for the "item" in the inventory
     * 
     * @param challenge
     * @param player
     * @return List of strings splitting challenge string into 25 chars long 
     */
    private List<String> challengeDescription(String challenge, Player player) {
        List<String> result = new ArrayList<String>();
        final int length = 25;
        // plugin.getLogger().info("DEBUG: challenge is '"+challenge+"'");
        // plugin.getLogger().info("challenges.challengeList." + challenge +
        // ".level");
        // plugin.getLogger().info(getChallengeConfig().getString("challenges.challengeList."
        // + challenge + ".level"));
        String level = getChallengeConfig().getString("challenges.challengeList." + challenge + ".level", "");
        if (!level.isEmpty()) {
            result.addAll(Util.chop(ChatColor.WHITE,
                    plugin.myLocale(player.getUniqueId()).challengeslevel + ": " + level, length));
        }
        // Check if completed or not
        boolean complete = false;
        int maxTimes = getChallengeConfig().getInt("challenges.challengeList." + challenge + ".maxtimes", 0);
        int doneTimes = plugin.getPlayers().checkChallengeTimes(player.getUniqueId(), challenge);
        if (plugin.getPlayers().checkChallenge(player.getUniqueId(), challenge)) {
            // Complete!
            // result.add(ChatColor.AQUA + plugin.myLocale(player.getUniqueId()).challengescomplete);
            complete = true;
        }
        boolean repeatable = false;
        if (getChallengeConfig().getBoolean("challenges.challengeList." + challenge + ".repeatable", false)) {
            repeatable = true;
        }

        if (repeatable) {
            if (maxTimes == 0) {
                if (complete) {
                    result.add(ChatColor.AQUA + plugin.myLocale(player.getUniqueId()).challengescomplete);
                }
            } else {
                // Check if the player has maxed out the challenge
                if (doneTimes < maxTimes) {
                    result.add(plugin.myLocale(player.getUniqueId()).challengescompletedtimes
                            .replace("[donetimes]", String.valueOf(doneTimes))
                            .replace("[maxtimes]", String.valueOf(maxTimes)));
                } else {
                    repeatable = false;
                    result.add(plugin.myLocale(player.getUniqueId()).challengesmaxreached
                            .replace("[donetimes]", String.valueOf(doneTimes))
                            .replace("[maxtimes]", String.valueOf(maxTimes)));
                }
            }
        } else if (complete) {
            result.add(ChatColor.AQUA + plugin.myLocale(player.getUniqueId()).challengescomplete);
        }

        final String type = getChallengeConfig().getString("challenges.challengeList." + challenge + ".type", "")
                .toLowerCase();
        if (!complete || (complete && repeatable)) {
            String desc = ChatColor.translateAlternateColorCodes('&',
                    getChallengeConfig().getString("challenges.challengeList." + challenge + ".description", "")
                            .replace("[label]", Settings.ISLANDCOMMAND));
            if (desc.contains("|")) {
                result.addAll(Arrays.asList(desc.split("\\|")));
            } else {
                result.addAll(Util.chop(ChatColor.GOLD, desc, length));
            }
            if (type.equals("inventory")) {
                if (getChallengeConfig()
                        .getBoolean("challenges.challengeList." + challenge.toLowerCase() + ".takeItems")) {
                    result.addAll(Util.chop(ChatColor.RED,
                            plugin.myLocale(player.getUniqueId()).challengesitemTakeWarning, length));
                }
            } else if (type.equals("island")) {
                result.addAll(Util.chop(ChatColor.RED,
                        plugin.myLocale(player.getUniqueId()).challengeserrorItemsNotThere, length));
            }
        }
        if (complete && (!type.equals("inventory") || !repeatable)) {
            result.addAll(Util.chop(ChatColor.RED, plugin.myLocale(player.getUniqueId()).challengesnotRepeatable,
                    length));
            return result;
        }
        double moneyReward = 0;
        int expReward = 0;
        String rewardText = "";
        if (!plugin.getPlayers().checkChallenge(player.getUniqueId(), challenge)) {
            // First time
            moneyReward = getChallengeConfig()
                    .getDouble("challenges.challengeList." + challenge.toLowerCase() + ".moneyReward", 0);
            rewardText = ChatColor.translateAlternateColorCodes('&', getChallengeConfig()
                    .getString("challenges.challengeList." + challenge.toLowerCase() + ".rewardText", "Goodies!"));
            expReward = getChallengeConfig().getInt("challenges.challengeList." + challenge + ".expReward", 0);
            result.addAll(Util.chop(ChatColor.GOLD,
                    plugin.myLocale(player.getUniqueId()).challengesfirstTimeRewards, length));
        } else {
            // Repeat challenge
            moneyReward = getChallengeConfig()
                    .getDouble("challenges.challengeList." + challenge.toLowerCase() + ".repeatMoneyReward", 0);
            rewardText = ChatColor.translateAlternateColorCodes('&', getChallengeConfig().getString(
                    "challenges.challengeList." + challenge.toLowerCase() + ".repeatRewardText", "Goodies!"));
            expReward = getChallengeConfig().getInt("challenges.challengeList." + challenge + ".repeatExpReward",
                    0);
            result.addAll(Util.chop(ChatColor.GOLD, plugin.myLocale(player.getUniqueId()).challengesrepeatRewards,
                    length));

        }
        result.addAll(Util.chop(ChatColor.WHITE, rewardText, length));
        if (expReward > 0) {
            result.addAll(Util.chop(ChatColor.GOLD,
                    plugin.myLocale(player.getUniqueId()).challengesexpReward + ": " + ChatColor.WHITE + expReward,
                    length));
        }
        if (Settings.useEconomy && moneyReward > 0) {
            result.addAll(Util.chop(ChatColor.GOLD, plugin.myLocale(player.getUniqueId()).challengesmoneyReward
                    + ": " + ChatColor.WHITE + VaultHelper.econ.format(moneyReward), length));
        }
        return result;
    }

    /**
     * Saves the challenge.yml file if it does not exist
     */
    public void saveDefaultChallengeConfig() {
        if (challengeConfigFile == null) {
            challengeConfigFile = new File(plugin.getDataFolder(), "challenges.yml");
        }
        if (!challengeConfigFile.exists()) {
            plugin.saveResource("challenges.yml", false);
        }
    }

    /**
     * Reloads the challenge config file
     */
    public void reloadChallengeConfig() {
        if (challengeConfigFile == null) {
            challengeConfigFile = new File(plugin.getDataFolder(), "challenges.yml");
        }
        challengeFile = YamlConfiguration.loadConfiguration(challengeConfigFile);

        // Look for defaults in the jar
        /*
        if (plugin.getResource("challenges.yml") != null) {
        YamlConfiguration defConfig = YamlConfiguration.loadConfiguration(defConfigStream);
        challengeFile.setDefaults(defConfig);
        }*/
        Settings.challengeList = getChallengeConfig().getConfigurationSection("challenges.challengeList")
                .getKeys(false);
        Settings.challengeLevels = Arrays
                .asList(getChallengeConfig().getString("challenges.levels", "").split(" "));
        Settings.freeLevels = Arrays.asList(getChallengeConfig().getString("challenges.freelevels", "").split(" "));
        Settings.waiverAmount = getChallengeConfig().getInt("challenges.waiveramount", 1);
        if (Settings.waiverAmount < 0) {
            Settings.waiverAmount = 0;
        }
        populateChallengeList();
    }

    /**
     * @return challenges FileConfiguration object
     */
    public FileConfiguration getChallengeConfig() {
        if (challengeFile == null) {
            reloadChallengeConfig();
        }
        return challengeFile;
    }

    /**
     * Saves challenges.yml
     */
    public void saveChallengeConfig() {
        if (challengeFile == null || challengeConfigFile == null) {
            return;
        }
        try {
            getChallengeConfig().save(challengeConfigFile);
        } catch (IOException ex) {
            plugin.getLogger().severe("Could not save config to " + challengeConfigFile);
        }
    }

    /**
     * Gets a list of all challenges in existence.
     */
    public List<String> getAllChallenges() {
        List<String> returned = new ArrayList<String>();
        for (List<String> challenges : challengeList.values()) {
            returned.addAll(challenges);
        }
        // Add levels
        for (String level : Settings.challengeLevels) {
            returned.add(level.toLowerCase());
        }
        return returned;
    }

    /**
     * Creates a list of challenges that the specified player is able to complete.
     * @param player
     * @return List of challenges
     */
    public List<String> getAvailableChallenges(Player player) {
        List<String> returned = new ArrayList<String>();
        for (Map.Entry<String, List<String>> e : challengeList.entrySet())
            if (isLevelAvailable(player, e.getKey())) {
                returned.addAll(e.getValue());
            }
        return returned;
    }

    @Override
    public List<String> onTabComplete(final CommandSender sender, final Command command, final String label,
            final String[] args) {
        if (!(sender instanceof Player)) {
            return new ArrayList<String>();
        }
        Player player = (Player) sender;

        List<String> options = new ArrayList<String>();

        switch (args.length) {
        case 0:
        case 1:
            options.add("complete");
            //Fall through
        case 2:
            options.addAll(getAvailableChallenges(player));
            break;
        }

        return Util.tabLimit(options, args.length != 0 ? args[args.length - 1] : "");
    }

    /**
     * Check if challenge can be reset according to challenges.yml file
     * @param challenge
     * @return true if this challenge can be reset, false if not
     */
    public boolean resetable(String challenge) {
        return getChallengeConfig().getBoolean("challenges.challengeList." + challenge + ".resetallowed", true);
    }

    /**
     * Gets the name of the highest challenge level the player has completed
     * @param player
     * @return challenge level
     */
    public String getChallengeLevel(Player player) {
        //plugin.getLogger().info("DEBUG: getting challenge level for " + player.getName());
        if (Settings.challengeLevels.isEmpty()) {
            return "";
        }
        return Settings.challengeLevels.get(getLevelDone(player));
    }

    /**
    * Get the challenge list
    * @return challenge list
    */
    public static LinkedHashMap<String, List<String>> getChallengeList() {
        return challengeList;
    }
}