   public enum Reason {
DISTANCE, GENERATOR, WORLD_NAME
   }; public class AdminCmd implements CommandExecutor, TabCompleter {
    private ASkyGrid plugin;

    public AdminCmd(ASkyGrid plugin) {
	this.plugin = plugin;
    }

    private void help(CommandSender sender, String label) {
	if (!(sender instanceof Player)) {
	    sender.sendMessage(ChatColor.YELLOW + "/" + label + " completechallenge <challengename> <player>:" + ChatColor.WHITE + " "
		    + plugin.myLocale().adminHelpcompleteChallenge);
	    sender.sendMessage(ChatColor.YELLOW + "/" + label + " info <player>:" + ChatColor.WHITE + " " + plugin.myLocale().adminHelpinfo);
	    sender.sendMessage(ChatColor.YELLOW + "/" + label + " reload:" + ChatColor.WHITE + " " + plugin.myLocale().adminHelpreload);
	    sender.sendMessage(ChatColor.YELLOW + "/" + label + " resetallchallenges <player>:" + ChatColor.WHITE + " " + plugin.myLocale().adminHelpresetAllChallenges);
	    sender.sendMessage(ChatColor.YELLOW + "/" + label + " resetchallenge <challengename> <player>:" + ChatColor.WHITE + " "
		    + plugin.myLocale().adminHelpresetChallenge);
	} else {
	    // Only give help if the player has permissions
	    // Permissions are split into admin permissions and mod permissions
	    // Listed in alphabetical order
	    Player player = (Player) sender;
	    player.sendMessage(plugin.myLocale(player.getUniqueId()).adminHelpHelp);
	    if (VaultHelper.checkPerm(player, Settings.PERMPREFIX + "mod.challenges") || player.isOp()) {
		player.sendMessage(ChatColor.YELLOW + "/" + label + " completechallenge <challengename> <player>:" + ChatColor.WHITE + " "
			+ plugin.myLocale(player.getUniqueId()).adminHelpcompleteChallenge);
	    }
	    if (VaultHelper.checkPerm(player, Settings.PERMPREFIX + "mod.info") || player.isOp()) {
		player.sendMessage(ChatColor.YELLOW + "/" + label + " info <player>:" + ChatColor.WHITE + " " + plugin.myLocale(player.getUniqueId()).adminHelpinfo);
	    }
	    if (VaultHelper.checkPerm(player, Settings.PERMPREFIX + "admin.reload") || player.isOp()) {
		player.sendMessage(ChatColor.YELLOW + "/" + label + " reload:" + ChatColor.WHITE + " " + plugin.myLocale(player.getUniqueId()).adminHelpreload);
	    }
	    if (VaultHelper.checkPerm(player, Settings.PERMPREFIX + "mod.challenges") || player.isOp()) {
		player.sendMessage(ChatColor.YELLOW + "/" + label + " resetchallenge <challengename> <player>:" + ChatColor.WHITE + " "
			+ plugin.myLocale(player.getUniqueId()).adminHelpresetChallenge);
	    }
	    if (VaultHelper.checkPerm(player, Settings.PERMPREFIX + "mod.challenges") || player.isOp()) {
		player.sendMessage(ChatColor.YELLOW + "/" + label + " resetallchallenges <player>:" + ChatColor.WHITE + " "
			+ plugin.myLocale(player.getUniqueId()).adminHelpresetAllChallenges);
	    }
	    if (VaultHelper.checkPerm(player, Settings.PERMPREFIX + "mod.sethome") || player.isOp()) {
		player.sendMessage(ChatColor.YELLOW + "/" + label + " sethome <player>:" + ChatColor.WHITE + " " + plugin.myLocale(player.getUniqueId()).adminHelpSetHome);
	    }
	    if (VaultHelper.checkPerm(player, Settings.PERMPREFIX + "mod.tp") || player.isOp()) {
		player.sendMessage(ChatColor.YELLOW + "/" + label + " tp <player>:" + ChatColor.WHITE + " " + plugin.myLocale(player.getUniqueId()).adminHelptp);
	    }
	}
    }

    /*
     * (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[] split) {
	// Console commands
	Player player;
	if (sender instanceof Player) {
	    player = (Player) sender;
	    if (split.length > 0) {
		// Admin-only commands
		if (split[0].equalsIgnoreCase("reload")) {
		    if (!checkAdminPerms(player, split)) {
			player.sendMessage(ChatColor.RED + plugin.myLocale(player.getUniqueId()).errorNoPermission);
			return true;
		    }
		} else {
		    // Mod commands
		    if (!checkModPerms(player, split)) {
			player.sendMessage(ChatColor.RED + plugin.myLocale(player.getUniqueId()).errorNoPermission);
			return true;
		    }
		}
	    }
	}
	// Check for zero parameters e.g., /asadmin
	switch (split.length) {
	case 0:
	    help(sender, label);
	    return true;
	case 1:
	    if (split[0].equalsIgnoreCase("reload")) {
		plugin.reloadConfig();
		plugin.loadPluginConfig();
		plugin.getChallenges().reloadChallengeConfig();
		plugin.startProtection();
		sender.sendMessage(ChatColor.YELLOW + plugin.myLocale().reloadconfigReloaded);
		return true;
	    } else {
		sender.sendMessage(ChatColor.RED + plugin.myLocale().errorUnknownCommand);
		return false;
	    }
	case 2:
	    if (split[0].equalsIgnoreCase("sethome")) { 
		if (!(sender instanceof Player)) {
		    sender.sendMessage(ChatColor.RED + plugin.myLocale().errorUseInGame);
		    return true;
		}
		player = (Player)sender;
		// Convert name to a UUID
		final UUID playerUUID = plugin.getPlayers().getUUID(split[1]);
		if (!plugin.getPlayers().isAKnownPlayer(playerUUID)) {
		    sender.sendMessage(ChatColor.RED + plugin.myLocale().errorUnknownPlayer);
		} else {
		    // Check that the location is safe
		    if (!GridManager.isSafeLocation(player.getLocation())) {
			// Not safe
			player.sendMessage(ChatColor.RED + plugin.myLocale(player.getUniqueId()).adminSetHomeNoneFound);
		    } else {
			// Success
			plugin.getPlayers().setHomeLocation(playerUUID, player.getLocation());
			player.sendMessage(ChatColor.GREEN + plugin.myLocale(player.getUniqueId()).adminSetHomeHomeSet.replace("[location]", player.getLocation().getBlockX() + ", " + player.getLocation().getBlockY() + "," + player.getLocation().getBlockZ()));
		    }
		}
		return true;
	    } else if (split[0].equalsIgnoreCase("tp")) {
		if (!(sender instanceof Player)) {
		    sender.sendMessage(ChatColor.RED + plugin.myLocale().errorUnknownCommand);
		    return true;
		}
		player = (Player)sender;
		// Convert name to a UUID
		final UUID targetUUID = plugin.getPlayers().getUUID(split[1]);
		if (!plugin.getPlayers().isAKnownPlayer(targetUUID)) {
		    player.sendMessage(ChatColor.RED + plugin.myLocale(player.getUniqueId()).errorUnknownPlayer);
		    return true;
		} else {
		    Location warpSpot = plugin.getPlayers().getHomeLocation(targetUUID).toVector().toLocation(ASkyGrid.getGridWorld());
		    String failureMessage = ChatColor.RED + plugin.myLocale(player.getUniqueId()).adminTpManualWarp.replace("[location]", warpSpot.getBlockX() + " " + warpSpot.getBlockY() + " "
			    + warpSpot.getBlockZ());
		    new SafeSpotTeleport(plugin, player, warpSpot, failureMessage);
		    return true;
		}
	    } else if (split[0].equalsIgnoreCase("info")) {
		// Convert name to a UUID
		final UUID playerUUID = plugin.getPlayers().getUUID(split[1]);
		// plugin.getLogger().info("DEBUG: console player info UUID = "
		// + playerUUID);
		if (!plugin.getPlayers().isAKnownPlayer(playerUUID)) {
		    sender.sendMessage(ChatColor.RED + plugin.myLocale().errorUnknownPlayer);
		    return true;
		} else {
		    showInfoChallenges(playerUUID, sender);
		    return true;
		}
	    } else if (split[0].equalsIgnoreCase("resetallchallenges")) {
		// Convert name to a UUID
		final UUID playerUUID = plugin.getPlayers().getUUID(split[1]);
		if (!plugin.getPlayers().isAKnownPlayer(playerUUID)) {
		    sender.sendMessage(ChatColor.RED + plugin.myLocale().errorUnknownPlayer);
		    return true;
		}
		plugin.getPlayers().resetAllChallenges(playerUUID);
		sender.sendMessage(ChatColor.YELLOW + plugin.myLocale().resetChallengessuccess.replace("[name]", split[1]));
		return true;
	    } else {
		return false;
	    }
	case 3:
	    if (split[0].equalsIgnoreCase("completechallenge")) {
		// Convert name to a UUID
		final UUID playerUUID = plugin.getPlayers().getUUID(split[2]);
		if (!plugin.getPlayers().isAKnownPlayer(playerUUID)) {
		    sender.sendMessage(ChatColor.RED + plugin.myLocale().errorUnknownPlayer);
		    return true;
		}
		if (plugin.getPlayers().checkChallenge(playerUUID, split[1].toLowerCase())
			|| !plugin.getPlayers().get(playerUUID).challengeExists(split[1].toLowerCase())) {
		    sender.sendMessage(ChatColor.RED + plugin.myLocale().completeChallengeerrorChallengeDoesNotExist);
		    return true;
		}
		plugin.getPlayers().get(playerUUID).completeChallenge(split[1].toLowerCase());
		sender.sendMessage(ChatColor.YELLOW
			+ plugin.myLocale().completeChallengechallangeCompleted.replace("[challengename]", split[1].toLowerCase()).replace("[name]", split[2]));
		return true;
	    } else if (split[0].equalsIgnoreCase("resetchallenge")) {
		// Convert name to a UUID
		final UUID playerUUID = plugin.getPlayers().getUUID(split[2]);
		if (!plugin.getPlayers().isAKnownPlayer(playerUUID)) {
		    sender.sendMessage(ChatColor.RED + plugin.myLocale().errorUnknownPlayer);
		    return true;
		}
		if (!plugin.getPlayers().checkChallenge(playerUUID, split[1].toLowerCase())
			|| !plugin.getPlayers().get(playerUUID).challengeExists(split[1].toLowerCase())) {
		    sender.sendMessage(ChatColor.RED + plugin.myLocale().resetChallengeerrorChallengeDoesNotExist);
		    return true;
		}
		plugin.getPlayers().resetChallenge(playerUUID, split[1].toLowerCase());
		sender.sendMessage(ChatColor.YELLOW
			+ plugin.myLocale().resetChallengechallengeReset.replace("[challengename]", split[1].toLowerCase()).replace("[name]", split[2]));
		return true;
	    }
	    return false;
	default:
	    return false;
	}
    }

    /**
     * Shows info on a player
     * 
     * @param playerUUID
     * @param sender
     */
    /*
    private void showInfo(UUID playerUUID, CommandSender sender) {
	sender.sendMessage(plugin.myLocale().adminInfoPlayer + ": " + ChatColor.GREEN + plugin.getPlayers().getName(playerUUID));
	sender.sendMessage(ChatColor.WHITE + "UUID: " + playerUUID.toString());
	// Last login
	try {
	    Date d = new Date(plugin.getServer().getOfflinePlayer(playerUUID).getLastPlayed());
	    sender.sendMessage(ChatColor.GOLD + plugin.myLocale().adminInfoLastLogin + ": " + d.toString());
	} catch (Exception e) {
	}
	Location homeLoc = null;
	homeLoc = plugin.getPlayers().getHomeLocation(playerUUID);
	if (homeLoc != null) {
	    sender.sendMessage(ChatColor.YELLOW + plugin.myLocale().adminInfoHomeLocation + ":" + ChatColor.WHITE + " (" + homeLoc.getBlockX() + ","
		    + homeLoc.getBlockY() + "," + homeLoc.getBlockZ() + ")");
	}
    }
*/
    /**
     * Shows info on the challenge situation for player
     * 
     * @param playerUUID
     * @param sender
     */
    private void showInfoChallenges(UUID playerUUID, CommandSender sender) {
	sender.sendMessage("Name:" + ChatColor.GREEN + plugin.getPlayers().getName(playerUUID));
	sender.sendMessage(ChatColor.WHITE + "UUID: " + playerUUID.toString());
	// Completed challenges
	sender.sendMessage(ChatColor.WHITE + plugin.myLocale().challengesguiTitle + ":");
	HashMap<String, Boolean> challenges = plugin.getPlayers().getChallengeStatus(playerUUID);
	HashMap<String, Integer> challengeTimes = plugin.getPlayers().getChallengeTimes(playerUUID);
	for (String c : challenges.keySet()) {
	    if (challengeTimes.containsKey(c)) {
		sender.sendMessage(c + ": "
			+ ((challenges.get(c)) ? ChatColor.GREEN + plugin.myLocale().challengescomplete : ChatColor.AQUA + plugin.myLocale().challengesincomplete) + "("
			+ plugin.getPlayers().checkChallengeTimes(playerUUID, c) + ")");

	    } else {
		sender.sendMessage(c + ": "
			+ ((challenges.get(c)) ? ChatColor.GREEN + plugin.myLocale().challengescomplete : ChatColor.AQUA + plugin.myLocale().challengesincomplete));
	    }
	}
    }

    private boolean checkAdminPerms(Player player2, String[] split) {
	// Check perms quickly for this command
	if (player2.isOp()) {
	    return true;
	}
	String check = split[0];
	if (check.equalsIgnoreCase("confirm"))
	    check = "purge";
	if (VaultHelper.checkPerm(player2, Settings.PERMPREFIX + "admin." + check.toLowerCase())) {
	    return true;
	}
	return false;
    }

    private boolean checkModPerms(Player player2, String[] split) {
	// Check perms quickly for this command
	if (player2.isOp()) {
	    return true;
	}
	String check = split[0];
	// Check special cases
	if (check.contains("challenge".toLowerCase())) {
	    check = "challenges";
	}
	if (VaultHelper.checkPerm(player2, Settings.PERMPREFIX + "mod." + check.toLowerCase())) {
	    return true;
	}
	return false;
    }

    @Override
    public List<String> onTabComplete(final CommandSender sender, final Command command, final String label, final String[] args) {
	final List<String> options = new ArrayList<String>();
	String lastArg = (args.length != 0 ? args[args.length - 1] : "");

	if (!(sender instanceof Player)) {
	    //Server console or something else; doesn't have
	    //permission checking.

	    switch (args.length) {
	    case 0: 
	    case 1:
		options.addAll(Arrays.asList("completechallenge", "info", "reload", "resetchallenge",
			"resetallchallenges"));
		break;
	    case 2:
		if (args[0].equalsIgnoreCase("completechallenge") || args[0].equalsIgnoreCase("resetchallenge")) {
		    options.addAll(plugin.getChallenges().getAllChallenges());
		}
		if (args[0].equalsIgnoreCase("resetallchallenges")) {
		    options.addAll(Util.getOnlinePlayerList());
		}
		if (args[0].equalsIgnoreCase("info")) {
		    options.addAll(Util.getOnlinePlayerList());
		}
		break;
	    case 3: 
		if (args[0].equalsIgnoreCase("completechallenge") || args[0].equalsIgnoreCase("resetchallenge")) {
		    options.addAll(Util.getOnlinePlayerList());
		}
		break;
	    }
	} else {
	    final Player player = (Player) sender;

	    switch (args.length) {
	    case 0: 
	    case 1: 
		if (VaultHelper.checkPerm(player, Settings.PERMPREFIX + "admin.reload") || player.isOp()) {
		    options.add("reload");
		}
		if (VaultHelper.checkPerm(player, Settings.PERMPREFIX + "mod.challenges") || player.isOp()) {
		    options.add("completechallenge");
		    options.add("resetchallenge");
		    options.add("resetallchallenges");
		}
		if (VaultHelper.checkPerm(player, Settings.PERMPREFIX + "mod.info") || player.isOp()) {
		    options.add("info");
		}
		if (VaultHelper.checkPerm(player, Settings.PERMPREFIX + "mod.tp") || player.isOp()) {
		    options.add("tp");
		}
		break;
	    case 2:
		if ((VaultHelper.checkPerm(player, Settings.PERMPREFIX + "mod.challenges") || player.isOp())
			&& (args[0].equalsIgnoreCase("completechallenge") || args[0].equalsIgnoreCase("resetchallenge"))) {
		    options.addAll(plugin.getChallenges().getAllChallenges());
		}
		if ((VaultHelper.checkPerm(player, Settings.PERMPREFIX + "mod.challenges") || player.isOp())
			&& args[0].equalsIgnoreCase("resetallchallenges")) {
		    options.addAll(Util.getOnlinePlayerList());
		}
		if ((VaultHelper.checkPerm(player, Settings.PERMPREFIX + "mod.info") || player.isOp())
			&& args[0].equalsIgnoreCase("info")) {
		    options.addAll(Util.getOnlinePlayerList());
		}
		if ((VaultHelper.checkPerm(player, Settings.PERMPREFIX + "mod.tp") || player.isOp())
			&& (args[0].equalsIgnoreCase("tp"))) {
		    options.addAll(Util.getOnlinePlayerList());
		}
		break;
	    case 3: 
		if ((VaultHelper.checkPerm(player, Settings.PERMPREFIX + "mod.challenges") || player.isOp())
			&& (args[0].equalsIgnoreCase("completechallenge") || args[0].equalsIgnoreCase("resetchallenge"))) {
		    options.addAll(Util.getOnlinePlayerList());
		}
		break;
	    }
	}

	return Util.tabLimit(options, lastArg);
    }
} @SuppressWarnings("deprecation")
public class Challenges implements CommandExecutor, TabCompleter {
    private ASkyGrid plugin;
    // Database of challenges
    private 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(ASkyGrid 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 + "challenges") && !VaultHelper.checkPerm(player, Settings.PERMPREFIX + "player.challenges") ) {
	    player.sendMessage(ChatColor.RED + plugin.myLocale(player.getUniqueId()).errorNoPermission);
	    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().toString().contains(Settings.worldName)) {
		    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) {
			// 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);
			}
		    }
		}
		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));
		}
	    }
	    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);
	// }
	return true;
    }

    private void runCommands(Player player, List<String> commands) {
	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 {
					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) {
	    //plugin.getLogger().info("DEBUG: level = " + Integer.valueOf(element[2]));
	    if (element[3].equalsIgnoreCase("EXTENDED")) {
		//plugin.getLogger().info("DEBUG: Extended");
		extended = true;
	    }
	}
	if (element.length > 4) {
	    if (element[4].equalsIgnoreCase("SPLASH")) {
		//plugin.getLogger().info("DEBUG: splash");
		splash = true;                
	    }
	    if (element[4].equalsIgnoreCase("LINGER")) {
		//plugin.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) {
	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("collect")) {
	    if (!hasRequired(player, challenge, "collect")) {
		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;
	}
	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 'collect' or 'inventory'");
	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 collect. 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").split(" ");
	// 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;
	    for (final String s : reqList) {
		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);
			// plugin.getLogger().info("DEBUG: required item = " +
			// reqItem.toString());
			// plugin.getLogger().info("DEBUG: item amount = " +
			// reqAmount);

			if (!player.getInventory().contains(reqItem)) {
			    return false;
			} else {
			    // check amount
			    int amount = 0;
			    // 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.hasItemMeta()) {
				    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();
					// plugin.getLogger().info("DEBUG: amount is <= req Remove "
					// + i.toString() + ":" +
					// i.getDurability() + " x " +
					// i.getAmount());
				    } else if ((amount + i.getAmount()) == reqAmount) {
					// 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
					// 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;
				    }
				}
			    }
			    // 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) {
		    // 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();
				// 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
				// 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;
			    }
			}
		    }
		    // plugin.getLogger().info("DEBUG: amount is " +
		    // amount);
		    // plugin.getLogger().info("DEBUG: req amount is " +
		    // reqAmount);
		    if (amount < reqAmount) {
			return false;
		    }

		    // plugin.getLogger().info("DEBUG: before set amount " +
		    // item.toString() + ":" + item.getDurability() + " x "
		    // + item.getAmount());
		    // item.setAmount(reqAmount);
		    // plugin.getLogger().info("DEBUG: after set amount " +
		    // item.toString() + ":" + item.getDurability() + " x "
		    // + item.getAmount());
		    // toBeRemoved.add(item);

		} 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]);
			//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;
				// 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 (!potionType.name().equalsIgnoreCase(part[1])) {
					    match = false;
					    // plugin.getLogger().info("DEBUG: name does not match");
					} else {
					    // 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 != potion.getLevel()) {
					    // plugin.getLogger().info("DEBUG: level does not match");
					    match = false;
					}                                     
				    }
				}
				// Extended check
				// plugin.getLogger().info("DEBUG: extended check " + part[3]);
				if (!part[3].isEmpty()) {
				    if (part[3].equalsIgnoreCase("EXTENDED") && !potion.hasExtendedDuration()) {
					match = false;
					// plugin.getLogger().info("DEBUG: extended does not match");
				    }
				    if (part[3].equalsIgnoreCase("NOTEXTENDED") && potion.hasExtendedDuration()) {
					match = false;
					// plugin.getLogger().info("DEBUG: extended does not match");
				    }
				}
				// Splash check
				// plugin.getLogger().info("DEBUG: splash/linger check " + part[4]);
				if (!part[4].isEmpty()) {
				    if (part[4].equalsIgnoreCase("SPLASH") && !potion.isSplash()) {
					match = false;
					// plugin.getLogger().info("DEBUG: not splash");
				    }
				    if (part[4].equalsIgnoreCase("NOSPLASH") && potion.isSplash()) {
					match = false;
					// plugin.getLogger().info("DEBUG: not no splash");
				    }                                    
				}
				// Quantity check
				if (match) {
				    // plugin.getLogger().info("DEBUG: potion matches!");
				    ItemStack removeItem = i.clone();
				    if (removeItem.getAmount() > reqAmount) {
					// plugin.getLogger().info("DEBUG: found " + removeItem.getAmount() + " qty in inv");
					removeItem.setAmount(reqAmount);
				    }
				    count = count - removeItem.getAmount();
				    // 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;
				// 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;
					    // plugin.getLogger().info("DEBUG: name does not match");
					} else {
					    // 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()) {
					    // plugin.getLogger().info("DEBUG: level does not match");
					    match = false;
					}
					if (level !=1 && !potionMeta.getBasePotionData().isUpgraded()) {
					    match = false;
					    // plugin.getLogger().info("DEBUG: level does not match");
					}
				    }
				}
				// Extended check
				// plugin.getLogger().info("DEBUG: extended check " + part[3]);
				if (!part[3].isEmpty()) {
				    if (part[3].equalsIgnoreCase("EXTENDED") && !potionMeta.getBasePotionData().isExtended()) {
					match = false;
					// plugin.getLogger().info("DEBUG: extended does not match");
				    }
				    if (part[3].equalsIgnoreCase("NOTEXTENDED") && potionMeta.getBasePotionData().isExtended()) {
					match = false;
					// plugin.getLogger().info("DEBUG: extended does not match");
				    }
				}
				// Splash or Linger check
				// 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;
					// plugin.getLogger().info("DEBUG: not splash");
				    }
				    if (part[4].equalsIgnoreCase("NOSPLASH") && i.getType().equals(Material.SPLASH_POTION)) {
					match = false;
					// plugin.getLogger().info("DEBUG: not no splash");
				    }
				    if (part[4].equalsIgnoreCase("LINGER") && !i.getType().equals(Material.LINGERING_POTION)) {
					match = false;
					// plugin.getLogger().info("DEBUG: not linger");
				    }
				    if (part[4].equalsIgnoreCase("NOLINGER") && i.getType().equals(Material.LINGERING_POTION)) {
					match = false;
					// plugin.getLogger().info("DEBUG: not no linger");
				    }
				}
				// Quantity check
				if (match) {
				    // plugin.getLogger().info("DEBUG: potion matches!");
				    ItemStack removeItem = i.clone();
				    if (removeItem.getAmount() > reqAmount) {
					// plugin.getLogger().info("DEBUG: found " + removeItem.getAmount() + " qty in inv");
					removeItem.setAmount(reqAmount);
				    }
				    count = count - removeItem.getAmount();
				    // plugin.getLogger().info("DEBUG: " + count + " left");
				    toBeRemoved.add(removeItem);
				}
			    }
			}
			if (count <= 0) {
			    // plugin.getLogger().info("DEBUG: Player has enough");
			    break;
			}
			// plugin.getLogger().info("DEBUG: still need " + count + " to complete");
		    }
		    if (count > 0) {
			// 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;
		// plugin.getLogger().info("DEBUG: Removing items");
		for (ItemStack i : toBeRemoved) {
		    // qty += i.getAmount();
		    // 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("collect")) {
	    final HashMap<Material, Integer> neededItem = new HashMap<Material, Integer>();
	    final HashMap<EntityType, Integer> neededEntities = new HashMap<EntityType, Integer>();
	    for (int i = 0; i < reqList.length; i++) {
		final String[] sPart = reqList[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;
	    }
	}
	// 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 {
			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("collect")) {
		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.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));
    }
} public class SkyGridCmd implements CommandExecutor, TabCompleter {
    public boolean levelCalcFreeFlag = true;
    private ASkyGrid plugin;
    /**
     * Constructor
     * 
     * @param plugin
     * @param players
     */
    public SkyGridCmd(ASkyGrid plugin) {
	// Plugin instance
	this.plugin = plugin;
    }

    /**
     * Makes the default home for the player
     * @param player
     */
    public void newHome(final Player player) {
	//long time = System.nanoTime();
	final UUID playerUUID = player.getUniqueId();
	Util.logger(2,"DEBUG: finding spawn location");
	Random random = new Random();
	long x = (random.nextInt(Settings.spawnDistance*2) - Settings.spawnDistance) + Settings.spawnCenterX;
	long z = (random.nextInt(Settings.spawnDistance*2) - Settings.spawnDistance) + Settings.spawnCenterZ;
	Location next = new Location(ASkyGrid.getGridWorld(), x, Settings.spawnHeight, z);
	// Check world Guard
	int tries = 0;
	if (plugin.getWorldGuard() != null && Settings.claim_protectionRange > 0) {
	    while (!plugin.getGguard().canBuild(player, next) && tries < 10) {
		x = (random.nextInt(Settings.spawnDistance*2) - Settings.spawnDistance) + Settings.spawnCenterX;
		z = (random.nextInt(Settings.spawnDistance*2) - Settings.spawnDistance) + Settings.spawnCenterZ;
		next = new Location(ASkyGrid.getGridWorld(), x, Settings.spawnHeight, z);
		tries++;
	    }
	    if (tries == 10) {
		plugin.getLogger().warning("Could not find an unclaimed spot for player to spawn. Try increasing spawn distance or move spawn center");
	    }
	}
	Util.logger(2,"DEBUG: found " + next);
	// Clear any old home locations (they should be clear, but just in case)
	plugin.getPlayers().clearHomeLocations(playerUUID);
	// Set the player's island location to this new spot
	plugin.getPlayers().setHomeLocation(playerUUID, next);
	// Save the player so that if the server is reset weird things won't happen
	plugin.getPlayers().save(playerUUID);
	// Set the biome
	//BiomesPanel.setIslandBiome(next, schematic.getBiome());
	// Teleport to the new home
	plugin.getGrid().homeTeleport(player);
	// Reset any inventory, etc. This is done AFTER the teleport because other plugins may switch out inventory based on world
	plugin.resetPlayer(player);
	// Reset money if required
	if (Settings.resetMoney) {
	    resetMoney(player);
	}
	// Show fancy titles!
	// Show fancy titles!
	if (!Bukkit.getServer().getVersion().contains("(MC: 1.7")) {
	    if (!plugin.myLocale(player.getUniqueId()).gridSubTitle.isEmpty()) {
		//plugin.getLogger().info("DEBUG: title " + player.getName() + " subtitle {\"text\":\"" + plugin.myLocale(player.getUniqueId()).islandSubTitle + "\", \"color\":\"" + plugin.myLocale(player.getUniqueId()).islandSubTitleColor + "\"}");
		plugin.getServer().dispatchCommand(plugin.getServer().getConsoleSender(),
			"title " + player.getName() + " subtitle {\"text\":\"" + plugin.myLocale(player.getUniqueId()).gridSubTitle + "\", \"color\":\"" + plugin.myLocale(player.getUniqueId()).gridSubTitleColor + "\"}");
	    }
	    if (!plugin.myLocale(player.getUniqueId()).gridTitle.isEmpty()) {
		//plugin.getLogger().info("DEBUG: title " + player.getName() + " title {\"text\":\"" + plugin.myLocale(player.getUniqueId()).islandTitle + "\", \"color\":\"" + plugin.myLocale(player.getUniqueId()).islandTitleColor + "\"}");
		plugin.getServer().dispatchCommand(plugin.getServer().getConsoleSender(),
			"title " + player.getName() + " title {\"text\":\"" + plugin.myLocale(player.getUniqueId()).gridTitle + "\", \"color\":\"" + plugin.myLocale(player.getUniqueId()).gridTitleColor + "\"}");
	    }
	    if (!plugin.myLocale(player.getUniqueId()).gridDonate.isEmpty() && !plugin.myLocale(player.getUniqueId()).gridURL.isEmpty()) {
		//plugin.getLogger().info("DEBUG: tellraw " + player.getName() + " {\"text\":\"" + plugin.myLocale(player.getUniqueId()).islandDonate + "\",\"color\":\"" + plugin.myLocale(player.getUniqueId()).islandDonateColor + "\",\"clickEvent\":{\"action\":\"open_url\",\"value\":\""
		//                + plugin.myLocale(player.getUniqueId()).islandURL + "\"}}");
		plugin.getServer().dispatchCommand(
			plugin.getServer().getConsoleSender(),
			"tellraw " + player.getName() + " {\"text\":\"" + plugin.myLocale(player.getUniqueId()).gridDonate + "\",\"color\":\"" + plugin.myLocale(player.getUniqueId()).gridDonateColor + "\",\"clickEvent\":{\"action\":\"open_url\",\"value\":\""
				+ plugin.myLocale(player.getUniqueId()).gridURL + "\"}}");
	    }
	}
	// Run any commands that need to be run at the start
	/*
	if (firstTime) {
	    runCommands(Settings.startCommands, player.getUniqueId());
	}*/
	// Done - fire event
	//final IslandNewEvent event = new IslandNewEvent(player,schematic, myIsland);
	//plugin.getServer().getPluginManager().callEvent(event);
	//Util.logger(2,"DEBUG: Done! " + (System.nanoTime()- time) * 0.000001);
    }

    private void resetMoney(Player player) {
	if (!Settings.useEconomy) {
	    return;
	}
	// Set player's balance in acid island to the starting balance
	try {
	    // Util.logger(2,"DEBUG: " + player.getName() + " " +
	    // Settings.general_worldName);
	    if (VaultHelper.econ == null) {
		// plugin.getLogger().warning("DEBUG: econ is null!");
		VaultHelper.setupEconomy();
	    }
	    Double playerBalance = VaultHelper.econ.getBalance(player, Settings.worldName);
	    // Util.logger(2,"DEBUG: playerbalance = " +
	    // playerBalance);
	    // Round the balance to 2 decimal places and slightly down to
	    // avoid issues when withdrawing the amount later
	    BigDecimal bd = new BigDecimal(playerBalance);
	    bd = bd.setScale(2, RoundingMode.HALF_DOWN);
	    playerBalance = bd.doubleValue();
	    // Util.logger(2,"DEBUG: playerbalance after rounding = "
	    // + playerBalance);
	    if (playerBalance != Settings.startingMoney) {
		if (playerBalance > Settings.startingMoney) {
		    Double difference = playerBalance - Settings.startingMoney;
		    EconomyResponse response = VaultHelper.econ.withdrawPlayer(player, Settings.worldName, difference);
		    // Util.logger(2,"DEBUG: withdrawn");
		    if (response.transactionSuccess()) {
			Util.logger(2,
				"FYI:" + player.getName() + " had " + VaultHelper.econ.format(playerBalance) + " when they typed /asg and it was set to "
					+ Settings.startingMoney);
		    } else {
			plugin.getLogger().warning(
				"Problem trying to withdraw " + playerBalance + " from " + player.getName() + "'s account when they typed /asg");
			plugin.getLogger().warning("Error from economy was: " + response.errorMessage);
		    }
		} else {
		    Double difference = Settings.startingMoney - playerBalance;
		    EconomyResponse response = VaultHelper.econ.depositPlayer(player, Settings.worldName, difference);
		    if (response.transactionSuccess()) {
			Util.logger(2,
				"FYI:" + player.getName() + " had " + VaultHelper.econ.format(playerBalance) + " when they typed /asg and it was set to "
					+ Settings.startingMoney);
		    } else {
			plugin.getLogger().warning(
				"Problem trying to deposit " + playerBalance + " from " + player.getName() + "'s account when they typed /asg!");
			plugin.getLogger().warning("Error from economy was: " + response.errorMessage);
		    }

		}
	    }
	} catch (final Exception e) {
	    plugin.getLogger().severe("Error trying to zero " + player.getName() + "'s account when they typed /asg!");
	    plugin.getLogger().severe(e.getMessage());
	}

    }

    /**
     * One-to-one relationship, you can return the first matched key
     * 
     * @param map
     * @param value
     * @return
     */
    public static <T, E> T getKeyByValue(Map<T, E> map, E value) {
	for (Entry<T, E> entry : map.entrySet()) {
	    if (value.equals(entry.getValue())) {
		return entry.getKey();
	    }
	}
	return null;
    }

    /*
     * (non-Javadoc)
     * @see
     * org.bukkit.command.CommandExecutor#onCommand(org.bukkit.command.CommandSender
     * , org.bukkit.command.Command, java.lang.String, java.lang.String[])
     */
    @Override
    public boolean onCommand(final CommandSender sender, final Command command, final String label, final String[] split) {
	if (!(sender instanceof Player)) {
	    return false;
	}
	final Player player = (Player) sender;
	// Basic permissions check to even use /island
	if (!VaultHelper.checkPerm(player, Settings.PERMPREFIX + "player.create")) {
	    player.sendMessage(ChatColor.RED + plugin.myLocale(player.getUniqueId()).errorYouDoNotHavePermission);
	    return true;
	}
	/*
	 * Grab data for this player - may be null or empty
	 * playerUUID is the unique ID of the player who issued the command
	 */
	final UUID playerUUID = player.getUniqueId();
	// Check if a player has an island or is in a team
	switch (split.length) {
	// /island command by itself
	case 0:
	    // New start
	    if (plugin.getPlayers().getHomeLocation(playerUUID) == null) {
		// Create new home for player
		player.sendMessage(ChatColor.GREEN + plugin.myLocale(player.getUniqueId()).newPlayer);
		newHome(player);
		if (Settings.removeMobs) {
		    plugin.getGrid().removeMobs(player.getLocation());
		}
		return true;
	    } else {
		if (!player.getWorld().getName().equalsIgnoreCase(Settings.worldName) || Settings.allowTeleportWhenFalling
			|| !PlayerEvents.isFalling(playerUUID) || player.isOp()) {
		    // Teleport home
		    plugin.getGrid().homeTeleport(player);
		    if (Settings.removeMobs) {
			plugin.getGrid().removeMobs(player.getLocation());
		    }
		} else {
		    player.sendMessage(ChatColor.RED + plugin.myLocale(player.getUniqueId()).errorCommandNotReady);
		}
		return true;
	    }
	case 1:
	    if (split[0].equalsIgnoreCase("go")) {
		if (plugin.getPlayers().getHomeLocation(playerUUID) == null) {
		    // Player has no island
		    player.sendMessage(ChatColor.RED + plugin.myLocale(playerUUID).newPlayer);
		    return true;
		}
		// Teleport home
		plugin.getGrid().homeTeleport(player);
		if (Settings.removeMobs) {
		    plugin.getGrid().removeMobs(player.getLocation());
		}
		return true;
	    } else if (split[0].equalsIgnoreCase("about")) {
		player.sendMessage(ChatColor.GOLD + "This plugin is free software: you can redistribute");
		player.sendMessage(ChatColor.GOLD + "it and/or modify it under the terms of the GNU");
		player.sendMessage(ChatColor.GOLD + "General Public License as published by the Free");
		player.sendMessage(ChatColor.GOLD + "Software Foundation, either version 3 of the License,");
		player.sendMessage(ChatColor.GOLD + "or (at your option) any later version.");
		player.sendMessage(ChatColor.GOLD + "This plugin is distributed in the hope that it");
		player.sendMessage(ChatColor.GOLD + "will be useful, but WITHOUT ANY WARRANTY; without");
		player.sendMessage(ChatColor.GOLD + "even the implied warranty of MERCHANTABILITY or");
		player.sendMessage(ChatColor.GOLD + "FITNESS FOR A PARTICULAR PURPOSE.  See the");
		player.sendMessage(ChatColor.GOLD + "GNU General Public License for more details.");
		player.sendMessage(ChatColor.GOLD + "You should have received a copy of the GNU");
		player.sendMessage(ChatColor.GOLD + "General Public License along with this plugin.");
		player.sendMessage(ChatColor.GOLD + "If not, see <http://www.gnu.org/licenses/>.");
		player.sendMessage(ChatColor.GOLD + "Souce code is available on GitHub.");
		player.sendMessage(ChatColor.GOLD + "(c) 2016 by tastybento");
		return true;
	    } else if (split[0].equalsIgnoreCase("warp")) {
		if (VaultHelper.checkPerm(player, Settings.PERMPREFIX + "player.warp")) {
		    player.sendMessage(ChatColor.YELLOW + "/island warp <player>: " + ChatColor.WHITE + plugin.myLocale(player.getUniqueId()).gridhelpWarp);
		    return true;
		}
	    } else if (split[0].equalsIgnoreCase("warps")) {
		if (VaultHelper.checkPerm(player, Settings.PERMPREFIX + "player.warp")) {
		    // Step through warp table
		    Collection<UUID> warpList = plugin.getWarpSignsListener().listWarps();
		    if (warpList.isEmpty()) {
			player.sendMessage(ChatColor.YELLOW + plugin.myLocale(player.getUniqueId()).warpserrorNoWarpsYet);
			if (VaultHelper.checkPerm(player, Settings.PERMPREFIX + "player.addwarp")) {
			    player.sendMessage(ChatColor.YELLOW + plugin.myLocale().warpswarpTip);
			}
			return true;
		    } else {
			if (Settings.useWarpPanel) {
			    // Try the warp panel
			    player.openInventory(plugin.getWarpPanel().getWarpPanel(0));
			} else {
			    Boolean hasWarp = false;
			    String wlist = "";
			    for (UUID w : warpList) {
				if (wlist.isEmpty()) {
				    wlist = plugin.getPlayers().getName(w);
				} else {
				    wlist += ", " + plugin.getPlayers().getName(w);
				}
				if (w.equals(playerUUID)) {
				    hasWarp = true;
				}
			    }
			    player.sendMessage(ChatColor.YELLOW + plugin.myLocale(player.getUniqueId()).warpswarpsAvailable + ": " + ChatColor.WHITE + wlist);
			    if (!hasWarp && (VaultHelper.checkPerm(player, Settings.PERMPREFIX + "player.addwarp"))) {
				player.sendMessage(ChatColor.YELLOW + plugin.myLocale().warpswarpTip);
			    }
			}
			return true;
		    }
		}
	    } else if (split[0].equalsIgnoreCase("sethome")) {
		if (VaultHelper.checkPerm(player, Settings.PERMPREFIX + "player.sethome")) {
		    // Check world
		    if (!player.getWorld().equals(ASkyGrid.getGridWorld())) {
			player.sendMessage(ChatColor.RED + plugin.myLocale(player.getUniqueId()).errorWrongWorld);
			return true;
		    }
		    plugin.getGrid().homeSet(player);
		    return true;
		}
		return false;
	    } else if (split[0].equalsIgnoreCase("help")) {
		player.sendMessage(ChatColor.GREEN + plugin.getName() + " " + plugin.getDescription().getVersion() + " help:");

		player.sendMessage(plugin.myLocale(player.getUniqueId()).helpColor + "/" + label + ": " + ChatColor.WHITE + plugin.myLocale(player.getUniqueId()).help);
		// Dynamic home sizes with permissions
		int maxHomes = Settings.maxHomes;
		for (PermissionAttachmentInfo perms : player.getEffectivePermissions()) {
		    if (perms.getPermission().startsWith(Settings.PERMPREFIX + "player.maxhomes.")) {
			maxHomes = Integer.valueOf(perms.getPermission().split(Settings.PERMPREFIX + "player.maxhomes.")[1]);
		    }
		    // Do some sanity checking
		    if (maxHomes < 1) {
			maxHomes = 1;
		    }
		}
		if (maxHomes > 1 && VaultHelper.checkPerm(player, Settings.PERMPREFIX + "player.sethome")) {
		    player.sendMessage(plugin.myLocale(player.getUniqueId()).helpColor + "/" + label + " go <1 - " + maxHomes + ">: " + ChatColor.WHITE + plugin.myLocale(player.getUniqueId()).gridhelpTeleport);
		} else {
		    player.sendMessage(plugin.myLocale(player.getUniqueId()).helpColor + "/" + label + " go: " + ChatColor.WHITE + plugin.myLocale(player.getUniqueId()).gridhelpTeleport);
		}
		if (VaultHelper.checkPerm(player, Settings.PERMPREFIX + "player.sethome")) {
		    if (maxHomes > 1) {
			player.sendMessage(plugin.myLocale(player.getUniqueId()).helpColor + "/" + label + " sethome <1 - " + maxHomes + ">: " + ChatColor.WHITE + plugin.myLocale(player.getUniqueId()).helpSetHome);
		    } else {
			player.sendMessage(plugin.myLocale(player.getUniqueId()).helpColor + "/" + label + " sethome: " + ChatColor.WHITE + plugin.myLocale(player.getUniqueId()).helpSetHome);
		    }
		}
		if (VaultHelper.checkPerm(player, Settings.PERMPREFIX + "player.warp")) {
		    player.sendMessage(plugin.myLocale(player.getUniqueId()).helpColor + "/" + label + " warps: " + ChatColor.WHITE + plugin.myLocale(player.getUniqueId()).gridhelpWarps);
		    player.sendMessage(plugin.myLocale(player.getUniqueId()).helpColor + "/" + label + " warp <player>: " + ChatColor.WHITE + plugin.myLocale(player.getUniqueId()).gridhelpWarp);
		}
		if (VaultHelper.checkPerm(player, Settings.PERMPREFIX + "player.challenges")) {
		    player.sendMessage(plugin.myLocale(player.getUniqueId()).helpColor + plugin.myLocale(player.getUniqueId()).gridHelpChallenges);
		}
		return true;
	    } 
	    /*
	     * Commands that have two parameters
	     */
	case 2:
	    if (split[0].equalsIgnoreCase("warps")) {
		if (Settings.useWarpPanel) {
		    if (VaultHelper.checkPerm(player, Settings.PERMPREFIX + "player.warp")) {
			// Step through warp table
			Set<UUID> warpList = plugin.getWarpSignsListener().listWarps();
			if (warpList.isEmpty()) {
			    player.sendMessage(ChatColor.YELLOW + plugin.myLocale(player.getUniqueId()).warpserrorNoWarpsYet);
			    if (VaultHelper.checkPerm(player, Settings.PERMPREFIX + "player.addwarp")) {
				player.sendMessage(ChatColor.YELLOW + plugin.myLocale().warpswarpTip);
			    }
			    return true;
			} else {
			    // Try the warp panel
			    int panelNum = 0;
			    try {
				panelNum = Integer.valueOf(split[1]) - 1;
			    } catch (Exception e) {
				panelNum = 0;
			    }
			    player.openInventory(plugin.getWarpPanel().getWarpPanel(panelNum));
			    return true;
			}
		    } else {
			player.sendMessage(ChatColor.RED + plugin.myLocale(playerUUID).errorNoPermission);
		    }
		} else {
		    return false;
		}
	    } else 
		// Multi home
		if (split[0].equalsIgnoreCase("go")) {
		    if (plugin.getPlayers().getHomeLocation(playerUUID) == null) {
			// Player has no island
			player.sendMessage(ChatColor.RED + plugin.myLocale(player.getUniqueId()).newPlayer);
			return true;
		    }
		    if (VaultHelper.checkPerm(player, Settings.PERMPREFIX + "player.sethome")) {
			int number = 1;
			try {
			    number = Integer.valueOf(split[1]);
			    //Util.logger(2,"DEBUG: number = " + number);
			    if (number < 1) {
				plugin.getGrid().homeTeleport(player,1);
			    } else {
				int maxHomes = Settings.maxHomes;
				// Dynamic home sizes with permissions
				for (PermissionAttachmentInfo perms : player.getEffectivePermissions()) {
				    if (perms.getPermission().startsWith(Settings.PERMPREFIX + "player.maxhomes.")) {
					if (perms.getPermission().contains(Settings.PERMPREFIX + "player.maxhomes.*")) {
					    maxHomes = Settings.maxHomes;
					} else {
					    maxHomes = Integer.valueOf(perms.getPermission().split(Settings.PERMPREFIX + "player.maxhomes.")[1]);
					}
				    }
				    // Do some sanity checking
				    if (maxHomes < 1) {
					maxHomes = 1;
				    }
				}
				if (number > maxHomes) {
				    if (maxHomes > 1) {
					player.sendMessage(ChatColor.RED + plugin.myLocale(player.getUniqueId()).setHomeerrorNumHomes.replace("[max]",String.valueOf(maxHomes)));
				    } else {
					plugin.getGrid().homeTeleport(player,1);
				    }
				} else {
				    // Teleport home
				    plugin.getGrid().homeTeleport(player,number);
				}
			    }
			} catch (Exception e) {
			    // Teleport home
			    plugin.getGrid().homeTeleport(player,1);
			}
			if (Settings.removeMobs) {
			    plugin.getGrid().removeMobs(player.getLocation());
			}
		    } else {
			player.sendMessage(ChatColor.RED + plugin.myLocale(player.getUniqueId()).errorNoPermission); 
		    }
		    return true;
		} else if (split[0].equalsIgnoreCase("sethome")) {
		    if (VaultHelper.checkPerm(player, Settings.PERMPREFIX + "player.sethome")) {
			if (!player.getWorld().equals(ASkyGrid.getGridWorld())) {
			    // Util.logger(2,"DEBUG: player has no island in grid");
			    // Player has no island in the grid
			    player.sendMessage(ChatColor.RED + plugin.myLocale(player.getUniqueId()).errorWrongWorld);
			    return true;
			}
			int maxHomes = Settings.maxHomes;
			// Dynamic home sizes with permissions
			for (PermissionAttachmentInfo perms : player.getEffectivePermissions()) {
			    if (perms.getPermission().startsWith(Settings.PERMPREFIX + "player.maxhomes.")) {
				if (perms.getPermission().contains(Settings.PERMPREFIX + "player.maxhomes.*")) {
				    maxHomes = Settings.maxHomes;
				} else {
				    maxHomes = Integer.valueOf(perms.getPermission().split(Settings.PERMPREFIX + "player.maxhomes.")[1]);
				}
			    }
			}
			if (maxHomes > 1) {
			    // Check the number given is a number
			    int number = 0;
			    try {
				number = Integer.valueOf(split[1]);
				if (number < 1 || number > maxHomes) {
				    player.sendMessage(ChatColor.RED + plugin.myLocale(player.getUniqueId()).setHomeerrorNumHomes.replace("[max]",String.valueOf(maxHomes)));
				} else {
				    plugin.getGrid().homeSet(player, number);
				}
			    } catch (Exception e) {
				player.sendMessage(ChatColor.RED + plugin.myLocale(player.getUniqueId()).setHomeerrorNumHomes.replace("[max]",String.valueOf(maxHomes)));
			    }
			} else {
			    player.sendMessage(ChatColor.RED + plugin.myLocale(player.getUniqueId()).errorNoPermission);
			}
			return true;
		    }
		    player.sendMessage(ChatColor.RED + plugin.myLocale(player.getUniqueId()).errorNoPermission);
		    return true;
		} else if (split[0].equalsIgnoreCase("warp")) {
		    // Warp somewhere command
		    if (VaultHelper.checkPerm(player, Settings.PERMPREFIX + "player.warp")) {
			final Set<UUID> warpList = plugin.getWarpSignsListener().listWarps();
			if (warpList.isEmpty()) {
			    player.sendMessage(ChatColor.YELLOW + plugin.myLocale(player.getUniqueId()).warpserrorNoWarpsYet);
			    if (VaultHelper.checkPerm(player, Settings.PERMPREFIX + "player.addwarp")) {
				player.sendMessage(ChatColor.YELLOW + plugin.myLocale().warpswarpTip);
			    } else {
				player.sendMessage(ChatColor.RED + plugin.myLocale(player.getUniqueId()).errorNoPermission);
			    }
			    return true;
			} else {
			    // Check if this is part of a name
			    UUID foundWarp = null;
			    for (UUID warp : warpList) {
				if (plugin.getPlayers().getName(warp).toLowerCase().startsWith(split[1].toLowerCase())) {
				    foundWarp = warp;
				    break;
				}
			    }
			    if (foundWarp == null) {
				player.sendMessage(ChatColor.RED + plugin.myLocale(player.getUniqueId()).warpserrorDoesNotExist);
				return true;
			    } else {
				// Warp exists!
				final Location warpSpot = plugin.getWarpSignsListener().getWarp(foundWarp);
				// Check if the warp spot is safe
				if (warpSpot == null) {
				    player.sendMessage(ChatColor.RED + plugin.myLocale(player.getUniqueId()).warpserrorNotReadyYet);
				    plugin.getLogger().warning("Null warp found, owned by " + plugin.getPlayers().getName(foundWarp));
				    return true;
				}
				// Find out which direction the warp is facing
				Block b = warpSpot.getBlock();
				if (b.getType().equals(Material.SIGN_POST) || b.getType().equals(Material.WALL_SIGN)) {
				    Sign sign = (Sign) b.getState();
				    org.bukkit.material.Sign s = (org.bukkit.material.Sign) sign.getData();
				    BlockFace directionFacing = s.getFacing();
				    Location inFront = b.getRelative(directionFacing).getLocation();
				    Location oneDown = b.getRelative(directionFacing).getRelative(BlockFace.DOWN).getLocation();
				    if ((GridManager.isSafeLocation(inFront))) {
					warpPlayer(player, inFront, foundWarp, directionFacing);
					return true;
				    } else if (b.getType().equals(Material.WALL_SIGN) && GridManager.isSafeLocation(oneDown)) {
					// Try one block down if this is a wall sign
					warpPlayer(player, oneDown, foundWarp, directionFacing);
					return true;
				    }
				} else {
				    // Warp has been removed
				    player.sendMessage(ChatColor.RED + plugin.myLocale(player.getUniqueId()).warpserrorDoesNotExist);
				    plugin.getWarpSignsListener().removeWarp(warpSpot);
				    return true;
				}
				if (!(GridManager.isSafeLocation(warpSpot))) {
				    player.sendMessage(ChatColor.RED + plugin.myLocale(player.getUniqueId()).warpserrorNotSafe);
				    // WALL_SIGN's will always be unsafe if the place in front is obscured.
				    if (b.getType().equals(Material.SIGN_POST)) {
					plugin.getLogger().warning(
						"Unsafe warp found at " + warpSpot.toString() + " owned by " + plugin.getPlayers().getName(foundWarp));

				    }
				    return true;
				} else {
				    final Location actualWarp = new Location(warpSpot.getWorld(), warpSpot.getBlockX() + 0.5D, warpSpot.getBlockY(),
					    warpSpot.getBlockZ() + 0.5D);
				    player.teleport(actualWarp);
				    if (Settings.warpSound != null)
					player.getWorld().playSound(player.getLocation(), Settings.warpSound, 1F, 1F);
				    return true;
				}
			    }
			}
		    } else {
			player.sendMessage(ChatColor.RED + plugin.myLocale(player.getUniqueId()).errorNoPermission);
			return false;
		    }
		} 
	}
	return false;
    }

    /**
     * Warps a player to a spot in front of a sign
     * @param player
     * @param inFront
     * @param foundWarp
     * @param directionFacing
     */
    private void warpPlayer(Player player, Location inFront, UUID foundWarp, BlockFace directionFacing) {
	// convert blockface to angle
	float yaw = Util.blockFaceToFloat(directionFacing);
	final Location actualWarp = new Location(inFront.getWorld(), inFront.getBlockX() + 0.5D, inFront.getBlockY(),
		inFront.getBlockZ() + 0.5D, yaw, 30F);
	player.teleport(actualWarp);
	if (Settings.warpSound != null)
	    player.getWorld().playSound(player.getLocation(), Settings.warpSound, 1F, 1F);
	Player warpOwner = plugin.getServer().getPlayer(foundWarp);
	if (warpOwner != null && !warpOwner.equals(player)) {
	    warpOwner.sendMessage(plugin.myLocale(foundWarp).warpsPlayerWarped.replace("[name]", player.getDisplayName()));
	}
    }

    @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>();
	}
	final Player player = (Player) sender;

	if (!VaultHelper.checkPerm(player, Settings.PERMPREFIX + "player.create")) {
	    return new ArrayList<String>();
	}

	final List<String> options = new ArrayList<String>();
	String lastArg = (args.length != 0 ? args[args.length - 1] : "");

	switch (args.length) {
	case 0: 
	case 1: 
	    options.add("help"); //No permission needed.
	    //options.add("make"); //Make is currently a private command never accessible to the player
	    if (VaultHelper.checkPerm(player, Settings.PERMPREFIX + "player.sethome")) {
		options.add("go");
	    }
	    options.add("about"); //No permission needed. :-) Indeed.
	    if (VaultHelper.checkPerm(player, Settings.PERMPREFIX + "player.warp")) {
		options.add("warp");
		options.add("warps");
	    }
	    if (VaultHelper.checkPerm(player, Settings.PERMPREFIX + "player.settings")) {
		options.add("settings");
	    }
	    break;
	case 2: 
	    if (VaultHelper.checkPerm(player, Settings.PERMPREFIX + "player.sethome")) {
		if (args[0].equalsIgnoreCase("go") || args[0].equalsIgnoreCase("sethome")) {
		    for (int i = 0; i < Settings.maxHomes; i++) {
			options.add(Integer.toString(i));
		    }
		}
	    }
	    if (VaultHelper.checkPerm(player, Settings.PERMPREFIX + "player.warp")
		    && args[0].equalsIgnoreCase("warp")) {
		final Set<UUID> warpList = plugin.getWarpSignsListener().listWarps();

		for (UUID warp : warpList) {
		    options.add(plugin.getPlayers().getName(warp));
		}
	    }
	    break;
	}

	return Util.tabLimit(options, lastArg);
    }
} public class SkyGridGen extends ChunkGenerator {
    // Blocks that need to be placed on dirt
    private final static List<Material> needDirt = Arrays.asList(Material.SAPLING, Material.RED_MUSHROOM, Material.BROWN_MUSHROOM,
	    Material.SUGAR_CANE_BLOCK, Material.LONG_GRASS, Material.DEAD_BUSH, Material.YELLOW_FLOWER, Material.RED_ROSE, Material.DOUBLE_PLANT);

    @Override
    public byte[][] generateBlockSections(World world, Random random, int chunkx, int chunkz, BiomeGrid biomeGrid) {
	BiomeGenerator biomeGenerator = new BiomeGenerator(world);
	// Default block
	Material blockMat = Material.AIR;
	// This gets all the blocks that can be picked and their probabilities
	BlockProbability prob = WorldStyles.get(world.getEnvironment()).getProb();
	// Biomes
	// Settings.createBiomes
	//int noise = (int)Math.floor(voronoi.noise(chunkx, chunkz, 1) * Biome.values().length);
	//Bukkit.getLogger().info("DEBUG: " + noise + " biome = " + Biome.values()[noise]);


	// The chunk we are making
	byte[][] chunk = new byte[world.getMaxHeight() / 16][];
	if (Settings.createBiomes && world.getEnvironment().equals(Environment.NORMAL)) {
	    for (int x=0; x<16; x++) { 
		for (int z=0; z<16; z++) {
		    int realX = x + chunkx * 16; //used so that the noise function gives us
		    int realZ = z + chunkz * 16; //different values each chunk
		    //We get the 3 closest biome's to the temperature and rainfall at this block
		    HashMap<Biomes, Double> biomes = biomeGenerator.getBiomes(realX, realZ);
		    //And tell bukkit (who tells the client) what the biggest biome here is
		    biomeGrid.setBiome(x, z, getDominantBiome(biomes));
		}
	    }

	}
	for (int x = 0; x < 16; x += 4) {
	    for (int z = 0; z < 16; z += 4) {
		for (int y = 0; y < Settings.gridHeight; y += 4) {
		    // Get a random block and feed in the last block (true if cactus or cane)
		    blockMat = prob.getBlock(random, y == 0, blockMat == Material.CACTUS || blockMat == Material.SUGAR_CANE_BLOCK);
		    // Check if the block needs dirt
		    if (needDirt.contains(blockMat)) {
			// Check biome
			if (biomeGrid.getBiome(x, z).equals(Biome.DESERT)) {
			    // No plants in desert except for cactus
			    if (y == 0) {
				blockMat = Material.SAND;
			    } else {
				blockMat = Material.CACTUS;
			    }
			} else {
			    // Add dirt
			    setBlock(chunk, x, y, z, Material.DIRT);
			    setBlock(chunk, x, y+1, z, blockMat);
			    if (blockMat.equals(Material.SUGAR_CANE_BLOCK)) {
				//Bukkit.getLogger().info("DEBUG: sugar cane - result of random selection = " + blockMat + " " + (chunkx*16+x) + " " + y + " " + (chunkz*16+z));
				setBlock(chunk, x+1, y, z, Material.STATIONARY_WATER);
			    } else if (blockMat.equals(Material.DOUBLE_PLANT)) {
				setBlock(chunk, x, y+2, z, blockMat);
			    }
			}
		    } else {
			switch (blockMat) {
			case LAVA:
			case STATIONARY_LAVA:
			    // Don't allow stationary lava in this biome because the swamp tree vines can cause it to drip and lag
			    if (biomeGrid.getBiome(x, z).equals(Biome.SWAMPLAND)) {
				setBlock(chunk, x, y, z, Material.GRASS);
			    } else {
				setBlock(chunk, x, y, z, Material.STATIONARY_LAVA);
			    }
			    break;
			case CACTUS:
			    //Bukkit.getLogger().info("DEBUG: cactus - result of random selection = " + blockMat + " " + (chunkx*16+x) + " " + y + " " + (chunkz*16+z));
			    setBlock(chunk, x, y, z, Material.SAND);
			    setBlock(chunk, x, y-1, z, Material.VINE);
			    setBlock(chunk, x, y+1, z, blockMat);
			    break;
			case DIRT:
			    if (biomeGrid.getBiome(x, z).equals(Biome.DESERT)) {
				// Desert
				setBlock(chunk, x, y, z, Material.SAND);
			    } else {
				setBlock(chunk, x, y, z, Material.DIRT);
			    }
			case WATER:
			case STATIONARY_WATER:
			    if (biomeGrid.getBiome(x, z).equals(Biome.DESERT)) {
				// Desert
				setBlock(chunk, x, y, z, Material.SAND);
			    } else {
				setBlock(chunk, x, y, z, Material.STATIONARY_WATER);
			    }
			    break;
			case NETHER_WARTS:
			    //Bukkit.getLogger().info("DEBUG: nether warts - result of random selection = " + blockMat + " " + (chunkx*16+x) + " " + y + " " + (chunkz*16+z));
			    setBlock(chunk, x, y, z, Material.SOUL_SAND);
			    setBlock(chunk, x, y+1, z, blockMat);
			    break;
			default:
			    // Check strings for backwards compatibility
			    if (blockMat.toString().equals("END_ROD")) {
				//Bukkit.getLogger().info("DEBUG: end rod - result of random selection = " + blockMat + " " + (chunkx*16+x) + " " + y + " " + (chunkz*16+z));
				setBlock(chunk, x, y, z, Material.ENDER_STONE);
				setBlock(chunk, x, y+1, z, blockMat);
			    } else if (blockMat.toString().equals("CHORUS_PLANT")) {
				//Bukkit.getLogger().info("DEBUG: Chorus plant - result of random selection = " + blockMat + " " + (chunkx*16+x) + " " + y + " " + (chunkz*16+z));
				setBlock(chunk, x, y, z, Material.ENDER_STONE);
				setBlock(chunk, x, y+1, z, blockMat);
			    } else {
				/*
				 * debug
			if (blockMat.equals(Material.CHEST)) {
			    int xLoc = (chunkx*16+x);
			    int zLoc = (chunkz*16+z);
			    Bukkit.getLogger().info("DEBUG: setting chest at (" + xLoc + " " + y + " " + zLoc + ")");
			}
				 */
				setBlock(chunk, x, y, z, blockMat);
			    }
			}
		    }
		}
	    }
	}
	return chunk;
    }

    @SuppressWarnings("deprecation")
    void setBlock(byte[][] result, int x, int y, int z, Material block) {
	// is this chunk part already initialized?
	if (result[y >> 4] == null) {
	    // Initialize the chunk part
	    result[y >> 4] = new byte[4096];
	}
	// set the block (look above, how this is done)
	result[y >> 4][((y & 0xF) << 8) | (z << 4) | x] = (byte)block.getId();
    }

    @Override
    public List<BlockPopulator> getDefaultPopulators(World world) {
	//return Arrays.asList(new BlockPopulator[0]);
	List<BlockPopulator> list = new ArrayList<BlockPopulator>(1);
	list.add(new SkyGridPop(Settings.gridHeight));
	return list;
    }



    @Override
    public Location getFixedSpawnLocation(World world, Random random) {
	//Bukkit.getLogger().info("DEBUG: fixed spawn loc requested");
	return new Location(world, 0, Settings.gridHeight + 2, 0);
    }

    //We get the closest biome to send to the client (using the biomegrid)
    private Biome getDominantBiome(HashMap<Biomes, Double> biomes) {
	double maxNoiz = 0.0;
	Biomes maxBiome = null;

	for (Biomes biome : biomes.keySet()) {
	    if (biomes.get(biome) >= maxNoiz) {
		maxNoiz = biomes.get(biome);
		maxBiome = biome;
	    }
	}
	return maxBiome.biome;
    }


} public class BlockEndDragon implements Listener {
    private final ASkyGrid plugin;

    public BlockEndDragon(ASkyGrid plugin) {
	this.plugin = plugin;
    }

    /**
     * This handles end dragon spawning
     * 
     * @param event
     */
    @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
    public void onDragonSpawn(EntitySpawnEvent event) {
	if (!Settings.createEnd) {
	    return;
	}
	if (!event.getLocation().getWorld().equals(ASkyGrid.getEndWorld())) {
	    return;
	}
	if (event.getEntityType().equals(EntityType.ENDER_DRAGON)) {
	    //plugin.getLogger().info("DEBUG: removing ender dragon");
	    LivingEntity dragon = (LivingEntity)event.getEntity();
	    dragon.setHealth(0);
	    event.getEntity().remove();
	    event.setCancelled(true);
	}
    }


} public class JoinLeaveEvents implements Listener {
    private ASkyGrid plugin;
    private PlayerCache players;

    public JoinLeaveEvents(ASkyGrid plugin) {
	this.plugin = plugin;
	this.players = plugin.getPlayers();
    }

    /**
     * @param event
     */
    @EventHandler(priority = EventPriority.NORMAL)
    public void onPlayerJoin(final PlayerJoinEvent event) {
	final Player player = event.getPlayer();
	final UUID playerUUID = player.getUniqueId();
	// Check language permission
	if (VaultHelper.checkPerm(player, Settings.PERMPREFIX + "island.lang")) {
	    // Get language
	    String language = getLanguage(player);
	    //plugin.getLogger().info("DEBUG: language = " + language);
	    // Check if we have this language
	    if (plugin.getResource("locale/" + language + ".yml") != null) {
		if (plugin.getPlayers().getLocale(playerUUID).isEmpty()) {
		    plugin.getPlayers().setLocale(playerUUID, language);
		}
	    }
	} else {
	    // Default locale
	    plugin.getPlayers().setLocale(playerUUID,"");
	}
	if (players == null) {
	    plugin.getLogger().severe("players is NULL");
	}
	// Load any messages for the player
	// plugin.getLogger().info("DEBUG: Checking messages for " +
	// player.getName());
	final List<String> messages = plugin.getMessages().getMessages(playerUUID);
	if (messages != null) {
	    // plugin.getLogger().info("DEBUG: Messages waiting!");
	    plugin.getServer().getScheduler().runTaskLater(plugin, new Runnable() {
		@Override
		public void run() {
		    player.sendMessage(ChatColor.AQUA + plugin.myLocale(playerUUID).newsHeadline);
		    int i = 1;
		    for (String message : messages) {
			player.sendMessage(i++ + ": " + message);
		    }
		    // Clear the messages
		    plugin.getMessages().clearMessages(playerUUID);
		}
	    }, 40L);
	} // else {
	// plugin.getLogger().info("no messages");
	// }

	// Set the player's name (it may have changed), but only if it isn't empty
	if (!player.getName().isEmpty()) {
	    players.setPlayerName(playerUUID, player.getName());
	    // Add to tinyDB
	    plugin.getTinyDB().savePlayerName(player.getName(), playerUUID);
	} else {
	    plugin.getLogger().warning("Player that just logged in has no name! " + playerUUID.toString());
	}
	players.save(playerUUID);
	if (Settings.logInRemoveMobs && player.getWorld().getName().startsWith(Settings.worldName)) {
	    plugin.getGrid().removeMobs(player.getLocation());
	}
   }

    @EventHandler(priority = EventPriority.NORMAL)
    public void onPlayerQuit(final PlayerQuitEvent event) {
	players.removeOnlinePlayer(event.getPlayer().getUniqueId());
    }

    /**
     * Attempts to get the player's language
     * @param p
     * @return language or empty string
     */
    public String getLanguage(Player p){
	Object ep;
	try {
	    ep = getMethod("getHandle", p.getClass()).invoke(p, (Object[]) null);
	    Field f = ep.getClass().getDeclaredField("locale");
	    f.setAccessible(true);
	    String language = (String) f.get(ep);
	    language.replace('_', '-');
	    return language;
	} catch (Exception e) {
	    //nothing
	}
	return "en-US";
    }

    private Method getMethod(String name, Class<?> clazz) {
	for (Method m : clazz.getDeclaredMethods()) {
	    if (m.getName().equals(name))
		return m;
	}
	return null;
    }

} public class NetherPortals implements Listener {
    private final ASkyGrid plugin;

    public NetherPortals(ASkyGrid plugin) {
	this.plugin = plugin;
    }

    /**
     * This handles non-player portal use
     * Currently disables portal use by entities
     * 
     * @param event
     */
    @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = false)
    public void onEntityPortal(EntityPortalEvent event) {
	// If the nether is disabled then quit immediately
	if (!Settings.createNether) {
	    return;
	}
	if (event.getEntity() == null) {
	    return;
	}
	// If this is not ASkyGrid then return
	if (!event.getFrom().getWorld().equals(ASkyGrid.getGridWorld()) && !event.getFrom().getWorld().equals(ASkyGrid.getNetherWorld())) {
	    return;
	}
	// Entities don't go through sky grid portals, sorry.
	event.setCancelled(true);
    }

    @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
    public void onPlayerPortal(PlayerPortalEvent event) {
	// If the nether is disabled then quit immediately
	if (!Settings.createNether) {
	    return;
	}
	Location currentLocation = event.getFrom().clone();
	String currentWorld = currentLocation.getWorld().getName();
	if (!currentWorld.equalsIgnoreCase(Settings.worldName) && !currentWorld.equalsIgnoreCase(Settings.worldName + "_nether")
		&& !currentWorld.equalsIgnoreCase(Settings.worldName + "_the_end")) {
	    return;
	}

	// Determine what portal it is
	switch (event.getCause()) {
	case END_PORTAL:
	    if (Settings.createEnd) {
		// Same action for all worlds except the end itself
		if (!event.getFrom().getWorld().getEnvironment().equals(Environment.THE_END)) {
		    if (ASkyGrid.getEndWorld() != null) {
			// The end exists
			event.setCancelled(true);
			// Convert current location to end world location
			Location end_place = event.getPlayer().getLocation().toVector().toLocation(ASkyGrid.getEndWorld());
			// Set the height to the top of the end grid
			end_place.setY(Settings.gridHeight);
			if (GridManager.isSafeLocation(end_place)) {
			    event.getPlayer().teleport(end_place);
			    return;
			} else {
			    end_place = plugin.getGrid().bigScan(end_place, 10);
			    if (end_place != null) {
				event.getPlayer().teleport(end_place);
			    } else {
				// Try the spawn
				end_place = ASkyGrid.getEndWorld().getSpawnLocation();
				if (GridManager.isSafeLocation(end_place)) {
				    event.getPlayer().teleport(end_place);
				    return;
				} else {
				    event.getPlayer().sendMessage(ChatColor.RED + plugin.myLocale(event.getPlayer().getUniqueId()).warpserrorNotSafe);
				}
			    }
			    return;
			}
		    }
		} else {
		    // Returning from the end - just go home
		    event.setCancelled(true);
		    plugin.getGrid().homeTeleport(event.getPlayer());
		}
	    }
	    break;
	case NETHER_PORTAL:
	    // Get the home world of this player
	    if (event.getFrom().getWorld().getEnvironment().equals(Environment.NORMAL)) {
		// Going to Nether
		event.setTo(event.getFrom().toVector().toLocation(ASkyGrid.getNetherWorld()));
		event.useTravelAgent(true); 
	    } else {
		// Returning
		event.setTo(event.getFrom().toVector().toLocation(ASkyGrid.getGridWorld()));
		event.useTravelAgent(true); 
	    }
	    break;
	default:
	    break;
	}
    }
} public class PlayerEvents implements Listener {
    private final ASkyGrid plugin;
    private final static boolean DEBUG = false;
    // A set of falling players
    private static HashSet<UUID> fallingPlayers = new HashSet<UUID>();
    private List<UUID> respawn;
    //private boolean spawnEggMeta = false;

    public PlayerEvents(final ASkyGrid plugin) {
	this.plugin = plugin;
	respawn = new ArrayList<UUID>();
	// Work out if SpawnEgg method is available
	/*
	if (getMethod("SpawnEggMeta", ItemMeta.class) != null) {
	    spawnEggMeta = true;
	}
	 */
	//plugin.getLogger().info("DEBUG: spawneggmeta = " + spawnEggMeta);
    }

    /**
     * Places player back at their home on the grid if the setting is true
     * @param e
     */
    @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
    public void onPlayerRespawn(final PlayerRespawnEvent e) {
	if (DEBUG) {
	    plugin.getLogger().info(e.getEventName());
	}
	if (!Settings.respawnAtHome) {
	    return;
	}
	if (respawn.contains(e.getPlayer().getUniqueId())) {
	    if (DEBUG)
		plugin.getLogger().info("DEBUG: found player to respawn");
	    respawn.remove(e.getPlayer().getUniqueId());
	    Location respawnLocation = plugin.getGrid().getSafeHomeLocation(e.getPlayer().getUniqueId(), 1);
	    if (respawnLocation != null) {
		if (DEBUG)
		    plugin.getLogger().info("DEBUG: Setting respawn location to " + respawnLocation);
		e.setRespawnLocation(respawnLocation);
	    }
	}
    }

    /**
     * Places the player on the home respawn list if they are eligible
     * @param e
     */
    @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
    public void onPlayerDeath(final PlayerDeathEvent e) {
	if (DEBUG) {
	    plugin.getLogger().info(e.getEventName());
	}
	if (!Settings.respawnAtHome) {
	    return;
	}
	// Died in island space?
	if (!inASkyGridWorld(e.getEntity().getWorld())) {
	    if (DEBUG)
		plugin.getLogger().info("DEBUG: player did not die in ASKyGrid space");
	    return;
	}
	UUID playerUUID = e.getEntity().getUniqueId();
	// Check if player has home
	if (plugin.getPlayers().getHomeLocation(playerUUID) != null) {
	    // Add them to the list to be respawned on their island
	    if (DEBUG)
		plugin.getLogger().info("DEBUG: player has a home - adding");
	    respawn.add(playerUUID);
	}
	// Mute death messages
	if (Settings.muteDeathMessages) {
	    e.setDeathMessage(null);
	}
    } 

    /*
     * Prevent typing /island if falling - hard core
     * Checked if player teleports
     */
    @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
    public void onPlayerFall(final PlayerMoveEvent e) {
	if (e.getPlayer().isDead()) {
	    return;
	}
	/*
	 * too spammy
	 * if (debug) {
	 * plugin.getLogger().info(e.getEventName());
	 * }
	 */
	if (!inASkyGridWorld(e.getPlayer().getWorld())) {
	    // If the player is not in the right world, then cancel any falling flags
	    unsetFalling(e.getPlayer().getUniqueId());
	    return;
	}
	if (Settings.allowTeleportWhenFalling) {
	    return;
	}
	if (!e.getPlayer().getGameMode().equals(GameMode.SURVIVAL) || e.getPlayer().isOp()) {
	    return;
	}
	// Check if air below player
	// plugin.getLogger().info("DEBUG:" +
	// Math.round(e.getPlayer().getVelocity().getY()));
	if ((Math.round(e.getPlayer().getVelocity().getY()) < 0L)
		&& e.getPlayer().getLocation().getBlock().getRelative(BlockFace.DOWN).getType() == Material.AIR
		&& e.getPlayer().getLocation().getBlock().getType() == Material.AIR) {
	    // plugin.getLogger().info("DEBUG: falling");
	    setFalling(e.getPlayer().getUniqueId());
	} else {
	    // plugin.getLogger().info("DEBUG: not falling");
	    unsetFalling(e.getPlayer().getUniqueId());
	}
    }

    /**
     * Prevents teleporting when falling based on setting by stopping commands
     * 
     * @param e
     */
    @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = false)
    public void onPlayerTeleport(final PlayerCommandPreprocessEvent e) {
	if (DEBUG) {
	    plugin.getLogger().info(e.getEventName());
	}
	if (Settings.allowTeleportWhenFalling || e.getPlayer().isOp() || !e.getPlayer().getGameMode().equals(GameMode.SURVIVAL)) {
	    return;
	}
	if (!inASkyGridWorld(e.getPlayer().getWorld())) {
	    return;
	}
	// Check commands
	// plugin.getLogger().info("DEBUG: falling command: '" +
	// e.getMessage().substring(1).toLowerCase() + "'");
	if (isFalling(e.getPlayer().getUniqueId()) && Settings.fallingCommandBlockList.contains(e.getMessage().substring(1).toLowerCase())) {
	    // Sorry you are going to die
	    e.getPlayer().sendMessage(plugin.myLocale(e.getPlayer().getUniqueId()).gridcannotTeleport);
	    e.setCancelled(true);
	}
    }

    /**
     * Prevents teleporting when falling based on setting
     * 
     * @param e
     */
    @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = false)
    public void onPlayerTeleport(final PlayerTeleportEvent e) {
	if (DEBUG) {
	    plugin.getLogger().info(e.getEventName());
	}
	// We only check if the player is teleporting from an Island world and to is not null
	if (e.getTo() == null || !inASkyGridWorld(e.getPlayer().getWorld())) {
	    return;
	}
	// Check if ready
	if (plugin.getGrid() == null) {
	    return;
	}
	// Teleporting while falling check
	if (!Settings.allowTeleportWhenFalling && e.getPlayer().getGameMode().equals(GameMode.SURVIVAL) && !e.getPlayer().isOp()) {
	    //plugin.getLogger().info("DEBUG: teleport when falling is not allowed - check if falling");
	    if (isFalling(e.getPlayer().getUniqueId())) {
		//plugin.getLogger().info("DEBUG: player is falling");
		// Sorry you are going to die
		e.getPlayer().sendMessage(plugin.myLocale(e.getPlayer().getUniqueId()).gridcannotTeleport);
		e.setCancelled(true);
		// Check if the player is in the void and kill them just in case
		if (e.getPlayer().getLocation().getBlockY() < 0) {
		    e.getPlayer().setHealth(0D);
		    unsetFalling(e.getPlayer().getUniqueId());
		}
		return;
	    }
	}
	//plugin.getLogger().info("DEBUG: From : " + e.getFrom());
	//plugin.getLogger().info("DEBUG: To : " + e.getTo());
    }




    /**
     * Used to prevent teleporting when falling
     * 
     * @param uniqueId
     * @return true or false
     */
    public static boolean isFalling(UUID uniqueId) {
	return fallingPlayers.contains(uniqueId);
    }

    /**
     * Used to prevent teleporting when falling
     * 
     * @param uniqueId
     */
    public static void setFalling(UUID uniqueId) {
	fallingPlayers.add(uniqueId);
    }

    /**
     * Unset the falling flag
     * 
     * @param uniqueId
     */
    public static void unsetFalling(UUID uniqueId) {
	// getLogger().info("DEBUG: unset falling");
	fallingPlayers.remove(uniqueId);
    }

    /**
     * Prevents players from using commands like /spawner
     * @param e
     */
    @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = false)
    public void onPlayerCommand(final PlayerCommandPreprocessEvent e) {
	if (DEBUG) {
	    plugin.getLogger().info("Player command " + e.getEventName() + ": " + e.getMessage());
	}
	if (e.getPlayer().isOp() || VaultHelper.checkPerm(e.getPlayer(), Settings.PERMPREFIX + "mod.bannedcommands")) {
	    return;
	}
	// Check world
	if (!inASkyGridWorld(e.getPlayer().getWorld())) {
	    return;
	}
	// Check banned commands
	//plugin.getLogger().info(Settings.visitorCommandBlockList.toString());
	String[] args = e.getMessage().substring(1).toLowerCase().split(" ");
	if (Settings.bannedCommandList.contains(args[0])) {
	    e.getPlayer().sendMessage(ChatColor.RED + plugin.myLocale(e.getPlayer().getUniqueId()).errorNoPermission);
	    e.setCancelled(true);
	}
    }

    /**
     * Prevents fall damage if required
     * @param e
     */
    @EventHandler(priority = EventPriority.LOW, ignoreCancelled = false)
    public void onPlayerFallDamage(final EntityDamageEvent e) {
	if (DEBUG) {
	    plugin.getLogger().info(e.getEventName());
	}
	if (Settings.allowFallDamage) {
	    return;
	}
	// Hurt in island space?
	if (!inASkyGridWorld(e.getEntity().getWorld())) {
	    return;
	}
	if (e.getCause() == null) {
	    return;
	}
	if (e.getCause().equals(DamageCause.FALL)) {
	    e.setCancelled(true);
	}
    }


    /**
     * Removes invalid spawn eggs
     * @param e
     */
    /*
    @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = false)
    public void onChestOpen(final InventoryOpenEvent e) {
	if (DEBUG) {
	    plugin.getLogger().info("Chest open " + e.getEventName());
	}
	// Check world
	if (!inASkyGridWorld(e.getPlayer().getWorld())) {
	    return;
	}

	Inventory chestInv = e.getInventory();
	if (chestInv.getType().equals(InventoryType.CHEST)) {
	    if (chestInv.contains(Material.MONSTER_EGG)) {
		for (ItemStack item: chestInv.getContents()) {
		    if (item != null && item.getType().equals(Material.MONSTER_EGG)) {
			if (spawnEggMeta) {
			    SpawnEggMeta me = (SpawnEggMeta)item.getItemMeta();
			    plugin.getLogger().info("DEBUG: monster egg me = " + me.getSpawnedType());
			    if (me.getSpawnedType() == null) {
				item.setType(Material.AIR);
				//plugin.getLogger().info("DEBUG: spawn egg type meta = null");
			    }
			} else {
			    @SuppressWarnings("deprecation")
			    SpawnEgg spawnEgg = (SpawnEgg)item.getData();
			    if (spawnEgg.getSpawnedType() == null) {
				//item.setType(Material.DIAMOND_BLOCK);
				//plugin.getLogger().info("DEBUG: spawn egg type = null ");
			    }
			}
		    }
		}
	    }
	}
    }
    private Method getMethod(String name, Class<?> clazz) {
	for (Method m : clazz.getDeclaredMethods()) {
	    if (m.getName().equals(name))
		return m;
	}
	return null;
    }*/

    /**
     * Checks if a world is an ASkyGrid world.
     * @param world
     * @return true if in an ASkyGrid world
     */
    private boolean inASkyGridWorld(World world) {
	if (world.equals(ASkyGrid.getGridWorld())) {
	    return true;
	}
	if (Settings.createNether && world.equals(ASkyGrid.getNetherWorld())) {
	    return true;
	}
	if (Settings.createEnd && world.equals(ASkyGrid.getEndWorld())) {
	    return true;
	}
	return false;
    }
} public class ChallengePanel implements Listener {

    private ASkyGrid plugin;

    /**
     * @param plugin
     */
    public ChallengePanel(ASkyGrid plugin) {
	this.plugin = plugin;
    }

    /**
     * Map of CP inventories by name
     */
    public static HashMap<String, Inventory> challengePanel = new HashMap<String, Inventory>();


    @EventHandler
    public void onInventoryClick(InventoryClickEvent event) {
	// TODO : this needs optimization
	Player player = (Player) event.getWhoClicked(); // The player that
							// clicked the item
	ItemStack clicked = event.getCurrentItem(); // The item that was clicked
	Inventory inventory = event.getInventory(); // The inventory that was
						    // clicked in
	int slot = event.getRawSlot();
	// Challenges
	if (inventory.getName().equals(plugin.myLocale(player.getUniqueId()).challengesguiTitle)) {
	    event.setCancelled(true);
	    if (event.getSlotType() == SlotType.OUTSIDE) {
		player.closeInventory();
		return;
	    }

	    // Get the list of items in this inventory
	    // plugin.getLogger().info("DEBUG: You clicked on slot " + slot);
	    List<CPItem> challenges = plugin.getChallenges().getCP(player);
	    if (challenges == null) {
		plugin.getLogger().warning("Player was accessing Challenge Inventory, but it had lost state - was server restarted?");
		player.closeInventory();
		player.performCommand(Settings.CHALLENGECOMMAND);
		return;
	    }
	    // plugin.getLogger().info("DEBUG: Challenges size = " +
	    // challenges.size());
	    if (slot >= 0 && slot < challenges.size()) {
		CPItem item = challenges.get(slot);
		// Check that it is the top items that are being clicked on
		// These two should be identical because it is made before
		if (clicked.equals(item.getItem())) {
		    // Next section indicates the level of panel to open
		    if (item.getNextSection() != null) {
			player.closeInventory();
			player.openInventory(plugin.getChallenges().challengePanel(player, item.getNextSection()));
		    } else if (item.getCommand() != null) {
			player.performCommand(item.getCommand());
			player.closeInventory();
			player.openInventory(plugin.getChallenges().challengePanel(player));
		    }
		}
	    }
	}
    }
} public class WarpPanel implements Listener {
    private ASkyGrid plugin;
    private List<Inventory> warpPanel;

    /**
     * @param plugin
     */
    public WarpPanel(ASkyGrid plugin) {
	this.plugin = plugin;
	warpPanel = new ArrayList<Inventory>();
	updatePanel();
    }

    /**
     * This needs to be called if a warp is added or deleted
     */
    public void updatePanel() {
	warpPanel.clear();
	int panelSize = 45; // Must be a multiple of 9
	// Create the warp panels
	Collection<UUID> warps = plugin.getWarpSignsListener().listSortedWarps();
	//plugin.getLogger().info("DEBUG: warps size = " + warps.size());
	int panelNumber = warps.size() / (panelSize-2);
	int remainder = (warps.size() % (panelSize-2)) + 8 + 2;
	remainder -= (remainder % 9);
	//plugin.getLogger().info("DEBUG: panel number = " + panelNumber + " remainder = " + remainder);
	int i = 0;
	// TODO: Make panel title a string
	for (i = 0; i < panelNumber; i++) {
	    //plugin.getLogger().info("DEBUG: created panel " + (i+1));
	    warpPanel.add(Bukkit.createInventory(null, panelSize, plugin.myLocale().warpsTitle + " #" + (i+1)));
	}
	// Make the last panel
	//plugin.getLogger().info("DEBUG: created panel " + (i+1));
	warpPanel.add(Bukkit.createInventory(null, remainder, plugin.myLocale().warpsTitle + " #" + (i+1)));
	panelNumber = 0;
	int slot = 0;
	int count = 0;
	// Add this buttons to each panel
	for (UUID playerUUID : warps) {
	    count++;
	    // Make a head if the player is known
	    String playerName = plugin.getServer().getOfflinePlayer(playerUUID).getName();
	    if (playerName != null) {
		ItemStack playerSkull = new ItemStack(Material.SKULL_ITEM, 1, (short) 3);
		SkullMeta meta = (SkullMeta) playerSkull.getItemMeta();
		meta.setOwner(playerName);
		meta.setDisplayName(playerName);
		//get the sign info
		Location signLocation = plugin.getWarpSignsListener().getWarp(playerUUID);
		//plugin.getLogger().info("DEBUG: " + playerName + ": block type = " + signLocation.getBlock().getType());
		if (signLocation.getBlock().getType().equals(Material.SIGN_POST) || signLocation.getBlock().getType().equals(Material.WALL_SIGN)) {
		    Sign sign = (Sign)signLocation.getBlock().getState();
		    List<String> lines = Arrays.asList(sign.getLines());
		    meta.setLore(lines);
		    //plugin.getLogger().info("DEBUG: " + playerName + ": lines = " + lines);
		} 
		/* else // TEST CODE
		    if (signLocation.getBlock().getType().equals(Material.AIR)) {
			signLocation.getBlock().getRelative(BlockFace.DOWN).setType(Material.BEDROCK);
			signLocation.getBlock().setType(Material.SIGN_POST);
			Sign sign = (Sign)signLocation.getBlock().getState();
			for (int line = 0; line < 4; line++) {
			    sign.setLine(line, "Line #" + line);
			}
			sign.update();
		    }*/
		playerSkull.setItemMeta(meta);
		// Add item to the panel
		//plugin.getLogger().info("DEBUG: adding item to panel number = " + panelNumber + " slot = " + slot);
		CPItem newButton = new CPItem(playerSkull, Settings.ISLANDCOMMAND + " warp " + playerName);
		warpPanel.get(panelNumber).setItem(slot++, newButton.getItem());
	    } else {
		// Just make a blank space
		//warpPanel.get(panelNumber).setItem(slot, new ItemStack(Material.AIR));
		// TEST CODE
		ItemStack playerSkull = new ItemStack(Material.SKULL_ITEM, 1, (short) 3);
		SkullMeta meta = (SkullMeta) playerSkull.getItemMeta();
		meta.setDisplayName("#" + count);
		playerSkull.setItemMeta(meta);
		warpPanel.get(panelNumber).setItem(slot++,playerSkull);
	    }
	    // Check if the panel is full
	    if (slot == panelSize-2) {
		// Add navigation buttons
		if (panelNumber > 0) {
		    warpPanel.get(panelNumber).setItem(slot++, new CPItem(Material.SIGN,plugin.myLocale().warpsPrevious,"warps " + (panelNumber-1),"").getItem());
		}
		warpPanel.get(panelNumber).setItem(slot, new CPItem(Material.SIGN,plugin.myLocale().warpsNext,"warps " + (panelNumber+1),"").getItem());
		// Move onto the next panel
		panelNumber++;
		slot = 0;
	    } 
	}
	if (remainder != 0 && panelNumber > 0) {
	    warpPanel.get(panelNumber).setItem(slot++, new CPItem(Material.SIGN,plugin.myLocale().warpsPrevious,"warps " + (panelNumber-1),"").getItem());
	}
    }

    public Inventory getWarpPanel(int panelNumber) {
	//makePanel();
	if (panelNumber < 0) {
	    panelNumber = 0;
	} else if (panelNumber > warpPanel.size()-1) {
	    panelNumber = warpPanel.size()-1;
	}
	return warpPanel.get(panelNumber);
    }

    @EventHandler
    public void onInventoryClick(InventoryClickEvent event) {
	Inventory inventory = event.getInventory(); // The inventory that was clicked in
	String title = inventory.getTitle();
	if (!inventory.getTitle().startsWith(plugin.myLocale().warpsTitle + " #")) {
	    return;
	}
	// The player that clicked the item
	Player player = (Player) event.getWhoClicked();
	event.setCancelled(true);
	if (event.getSlotType().equals(SlotType.OUTSIDE)) {
	    player.closeInventory();
	    return;
	}
	ItemStack clicked = event.getCurrentItem(); // The item that was clicked
	//plugin.getLogger().info("DEBUG: inventory size = " + inventory.getSize());
	//plugin.getLogger().info("DEBUG: clicked = " + clicked);
	//plugin.getLogger().info("DEBUG: rawslot = " + event.getRawSlot());
	if (event.getRawSlot() >= event.getInventory().getSize() || clicked.getType() == Material.AIR) {
	    return;
	}
	int panelNumber = 0;
	try {
	    panelNumber = Integer.valueOf(title.substring(title.indexOf('#')+ 1));
	} catch (Exception e) {
	    panelNumber = 0;
	}
	String command = clicked.getItemMeta().getDisplayName();
	//plugin.getLogger().info("DEBUG: command = " + command);
	if (command != null) {
	    if (command.equalsIgnoreCase(plugin.myLocale().warpsNext)) {
		player.closeInventory();
		player.performCommand(Settings.ISLANDCOMMAND + " warps " + (panelNumber+1));
	    } else if (command.equalsIgnoreCase(plugin.myLocale().warpsPrevious)) {
		player.closeInventory();
		player.performCommand(Settings.ISLANDCOMMAND + " warps " + (panelNumber-1));
	    } else {
		player.closeInventory();
		player.sendMessage(ChatColor.GREEN + plugin.myLocale(player.getUniqueId()).warpswarpToPlayersSign.replace("<player>", command));
		player.performCommand(Settings.ISLANDCOMMAND + " warp " + command);
	    }
	}
    }
} public class GGuard {

    RegionManager rm;
    RegionManager rmNether;
    ApplicableRegionSet set;
    ASkyGrid plugin;
    /**
     * @param plugin
     */
    public GGuard(ASkyGrid plugin) {
	this.plugin = plugin;
	rm = WGBukkit.getRegionManager(ASkyGrid.getGridWorld());
	if (Settings.createNether && ASkyGrid.getNetherWorld() != null) {
	    rmNether = WGBukkit.getRegionManager(ASkyGrid.getNetherWorld());
	}
    }

    /**
     * Returns true if a player can build at this location
     * @param player
     * @param loc
     * @return true or false
     */
    public boolean canBuild(Player player, Location loc) {
	if (loc.getWorld().equals(ASkyGrid.getGridWorld()) || (Settings.createNether && ASkyGrid.getNetherWorld() != null && loc.getWorld().equals(ASkyGrid.getNetherWorld()) ))
	{
	    return plugin.getWorldGuard().canBuild(player, loc);
	}
	return false;
    }
    
    /**
     * Tries to create a WG region for player at location. Size is determined by the global size variable or
     * the player's permission node.
     * @param player
     * @param loc
     * @return radius size of region created
     */
    public int createRegion(Player player, Location loc) {
	if (loc.getWorld().equals(ASkyGrid.getGridWorld()) || (Settings.createNether && ASkyGrid.getNetherWorld() != null && loc.getWorld().equals(ASkyGrid.getNetherWorld()) ))
	{
	    // Get the distance
	    int maxSize = Settings.claim_protectionRange;
	    for (PermissionAttachmentInfo perms : player.getEffectivePermissions()) {
		if (perms.getPermission().startsWith(Settings.PERMPREFIX + "protectionradius.")) {
		    if (perms.getPermission().contains(Settings.PERMPREFIX + "protectionradius.*")) {
			maxSize = Settings.claim_protectionRange;
			break;
		    } else {
			// Get the max value should there be more than one
			maxSize = Math.max(maxSize, Integer.valueOf(perms.getPermission().split(Settings.PERMPREFIX + "protectionradius.")[1]));
		    }
		}
		// Do some sanity checking
		if (maxSize < Settings.claim_protectionRange) {
		    maxSize = Settings.claim_protectionRange;
		}
	    }
	    double x = loc.getX() - maxSize;
	    double z = loc.getZ() - maxSize;
	    BlockVector l1 = new BlockVector(x,0,z);
	    BlockVector l2 = new BlockVector(x + maxSize*2, loc.getWorld().getMaxHeight(), z + maxSize *2);
	    ProtectedCuboidRegion pr = new ProtectedCuboidRegion("askygrid-" + player.getUniqueId().toString() , l1, l2);
	    LocalPlayer localPlayer = plugin.getWorldGuard().wrapPlayer(player);

	    if (loc.getWorld().equals(ASkyGrid.getGridWorld())) {
		if (rm.overlapsUnownedRegion(pr, localPlayer)) {
		    return 0;
		}
		// Remove any current regions
		rm.removeRegion("askygrid-" + player.getUniqueId().toString());
		if (rmNether != null) {
		    rmNether.removeRegion("askygrid-" + player.getUniqueId().toString());
		}
		// make region
		// Flags
		if (!plugin.myLocale(player.getUniqueId()).warpsentry.isEmpty()) { 
		    StringFlag flag = DefaultFlag.GREET_MESSAGE;
		    pr.setFlag(flag, plugin.myLocale(player.getUniqueId()).warpsentry.replace("[player]", player.getName()));
		}
		if (!plugin.myLocale(player.getUniqueId()).warpsexit.isEmpty()) { 
		    StringFlag flag = DefaultFlag.FAREWELL_MESSAGE;
		    pr.setFlag(flag, plugin.myLocale(player.getUniqueId()).warpsexit.replace("[player]", player.getName()));
		}
		//StateFlag stateFlag = DefaultFlag.BUILD;
		//pr.setFlag(stateFlag, State.DENY);
		//plugin.getLogger().info("DEBUG: adding region");
		pr.getOwners().addPlayer(localPlayer);
		rm.addRegion(pr);
	    } else {
		// Nether warp
		if (rmNether.overlapsUnownedRegion(pr, localPlayer)) {
		    return 0;
		}
		// Remove any current regions
		rm.removeRegion("askygrid-" + player.getUniqueId().toString());
		rmNether.removeRegion("askygrid-" + player.getUniqueId().toString());
		// make region
		// Flags
		if (!plugin.myLocale(player.getUniqueId()).warpsentry.isEmpty()) { 
		    StringFlag flag = DefaultFlag.GREET_MESSAGE;
		    pr.setFlag(flag, plugin.myLocale(player.getUniqueId()).warpsentry.replace("[player]", player.getName()));
		}
		if (!plugin.myLocale(player.getUniqueId()).warpsexit.isEmpty()) { 
		    StringFlag flag = DefaultFlag.FAREWELL_MESSAGE;
		    pr.setFlag(flag, plugin.myLocale(player.getUniqueId()).warpsexit.replace("[player]", player.getName()));
		}
		//StateFlag stateFlag = DefaultFlag.BUILD;
		//pr.setFlag(stateFlag, State.DENY);
		//plugin.getLogger().info("DEBUG: adding region");
		rmNether.addRegion(pr);
	    }
	    return maxSize;
	}
	return 0;
    }

    /**
     * Removes any region that a player has.
     * @param player
     */
    public void removeRegion(UUID player) {
	//plugin.getLogger().info("DEBUG: removing region");
	rm.removeRegion("askygrid-" + player.toString());
	if (rmNether != null) {
	    rmNether.removeRegion("askygrid-" + player.toString());
	}
    }

    /**
     * Removes all the ASkyGrid regions when the protection is set to zero
     * @return true if some regions were deleted
     */
    public boolean removeAllRegions() {
	boolean deleted = false;
	for (String region: rm.getRegions().keySet()) {
	    if (region.startsWith("askygrid-")) {
		rm.removeRegion(region);
		deleted = true;
	    }
	}
	if (rmNether != null) {
	    for (String region: rmNether.getRegions().keySet()) {
		if (region.startsWith("askygrid-")) {
		    rmNether.removeRegion(region);
		    deleted = true;
		}
	    }
	}
	return deleted;
    }
} public class VaultHelper {
    public static Economy econ = null;
    public static Permission permission = null;

    /**
     * Sets up the economy instance
     * 
     * @return
     */
    public static boolean setupEconomy() {
	RegisteredServiceProvider<Economy> economyProvider = ASkyGrid.getPlugin().getServer().getServicesManager()
		.getRegistration(net.milkbowl.vault.economy.Economy.class);
	if (economyProvider != null) {
	    econ = economyProvider.getProvider();
	}
	return econ != null;
    }

    /**
     * Sets up the permissions instance
     * 
     * @return
     */
    public static boolean setupPermissions() {
	RegisteredServiceProvider<Permission> permissionProvider = ASkyGrid.getPlugin().getServer().getServicesManager()
		.getRegistration(net.milkbowl.vault.permission.Permission.class);
	if (permissionProvider != null) {
	    permission = permissionProvider.getProvider();
	}
	return (permission != null);
    }

    /**
     * Checks permission of player in world or in any world
     * 
     * @param player
     * @param perm
     * @return
     */
    public static boolean checkPerm(final Player player, final String perm) {
	return permission.has(player, perm);
    }

    /**
     * Adds permission to player
     * 
     * @param player
     * @param perm
     */
    public static void addPerm(final Player player, final String perm) {
	permission.playerAdd(player, perm);
    }

    /**
     * Removes a player's permission
     * 
     * @param player
     * @param perm
     */
    public static void removePerm(final Player player, final String perm) {
	permission.playerRemove(player, perm);
    }

}
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.UUID;
import org.bukkit.Bukkit;
import org.bukkit.GameMode;
import org.bukkit.Sound;
import org.bukkit.World;
import org.bukkit.WorldCreator;
import org.bukkit.WorldType;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.event.HandlerList;
import org.bukkit.generator.ChunkGenerator;
import org.bukkit.inventory.ItemStack;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.potion.PotionEffect;
import com.sk89q.worldguard.bukkit.WorldGuardPlugin;
import com.wasteofplastic.askygrid.NotSetup.Reason;
import com.wasteofplastic.askygrid.commands.AdminCmd;
import com.wasteofplastic.askygrid.commands.Challenges;
import com.wasteofplastic.askygrid.commands.SkyGridCmd;
import com.wasteofplastic.askygrid.generators.SkyGridGen;
import com.wasteofplastic.askygrid.listeners.BlockEndDragon;
import com.wasteofplastic.askygrid.listeners.JoinLeaveEvents;
import com.wasteofplastic.askygrid.listeners.NetherPortals;
import com.wasteofplastic.askygrid.listeners.PlayerEvents;
import com.wasteofplastic.askygrid.panels.ChallengePanel;
import com.wasteofplastic.askygrid.panels.WarpPanel;
import com.wasteofplastic.askygrid.protection.GGuard;
import com.wasteofplastic.askygrid.util.VaultHelper;
	Settings.gridHeight = getConfig().getInt("general.gridheight", 128);
	Settings.spawnHeight = getConfig().getInt("general.spawnheight", 128);
	Settings.createNether = getConfig().getBoolean("general.createnether", true);
	Settings.createEnd = getConfig().getBoolean("general.createend", true);
	if (!Settings.createNether) {
	    getLogger().info("The Nether is disabled");
	}
	if (!Settings.createEnd) {
	    getLogger().info("The End is disabled");
	}
	Settings.spawnCenterX = 0;
	Settings.spawnCenterZ = 0;
	String spawnCenter = getConfig().getString("general.spawncenter","0,0");
	String[] split = spawnCenter.split(",");
	if (!spawnCenter.contains(",") || split.length != 2) {
	    getLogger().severe("spawncenter in config.yml must be in the format 'x,z' where x and z are numbers separated by a comma. Using 0,0.");
	} else {
	    try {
		Settings.spawnCenterX = Long.valueOf(split[0]);
		Settings.spawnCenterZ = Long.valueOf(split[1]);
	    } catch (Exception e) {
		getLogger().severe("spawncenter in config.yml must be in the format 'x,z' where x and z are numbers separated by a comma. Using 0,0.");
		Settings.spawnCenterX = 0;
		Settings.spawnCenterZ = 0;
	    }
	}
	Settings.spawnDistance = getConfig().getInt("general.distance", 1000);
	if (Settings.spawnDistance < 1) {
	    Settings.spawnDistance = 1;
	    getLogger().info("Setting minimum spawn distance to 1");
	}

	Settings.animalSpawnLimit = getConfig().getInt("general.animalspawnlimit", 15);
	if (Settings.animalSpawnLimit < -1) {
	    Settings.animalSpawnLimit = -1;
	}

	Settings.monsterSpawnLimit = getConfig().getInt("general.monsterspawnlimit", 70);
	if (Settings.monsterSpawnLimit < -1) {
	    Settings.monsterSpawnLimit = -1;
	}

	Settings.waterAnimalSpawnLimit = getConfig().getInt("general.wateranimalspawnlimit", 15);
	if (Settings.waterAnimalSpawnLimit < -1) {
	    Settings.waterAnimalSpawnLimit = -1;
	}

	Settings.claim_protectionRange = getConfig().getInt("general.protectionRange", 10);
	if (Settings.claim_protectionRange < 0) {
	    Settings.claim_protectionRange = 0;
	}
	Settings.resetMoney = getConfig().getBoolean("general.resetmoney", true);
	Settings.resetEnderChest = getConfig().getBoolean("general.resetenderchest", false);

	Settings.startingMoney = getConfig().getDouble("general.startingmoney", 0D);
	Settings.logInRemoveMobs = getConfig().getBoolean("general.loginremovemobs", true);
	Settings.removeMobs = getConfig().getBoolean("general.asgremovemobs", false);
	List<String> mobWhiteList = getConfig().getStringList("general.mobwhitelist");
	Settings.mobWhiteList.clear();
	String valid = "BLAZE, CREEPER, SKELETON, SPIDER, GIANT, ZOMBIE, GHAST, PIG_ZOMBIE, "
		+ "ENDERMAN, CAVE_SPIDER, SILVERFISH,  WITHER, WITCH, ENDERMITE,"
		+ " GUARDIAN";
	for (String mobName : mobWhiteList) {
	    if (valid.contains(mobName.toUpperCase())) {
		try {
		    Settings.mobWhiteList.add(EntityType.valueOf(mobName.toUpperCase()));
		} catch (Exception e) {
		    plugin.getLogger().severe("Error in config.yml, mobwhitelist value '" + mobName + "' is invalid.");
		    plugin.getLogger().severe("Possible values are : Blaze, Cave_Spider, Creeper, Enderman, Endermite, Giant, Guardian, "
			    + "Pig_Zombie, Silverfish, Skeleton, Spider, Witch, Wither, Zombie");
		}
	    } else {
		plugin.getLogger().severe("Error in config.yml, mobwhitelist value '" + mobName + "' is invalid.");
		plugin.getLogger().severe("Possible values are : Blaze, Cave_Spider, Creeper, Enderman, Endermite, Giant, Guardian, "
			+ "Pig_Zombie, Silverfish, Skeleton, Spider, Witch, Wither, Zombie");
	    }
	}
	// Assign settings
	// General settings that affect the world
	Settings.allowPvP = getConfig().getBoolean("general.allowPvP",true);
	Settings.allowEnderPearls = getConfig().getBoolean("general.allowenderpearls", true);
	Settings.allowFlowIn = getConfig().getBoolean("general.allowflowin", true);
	Settings.allowFlowOut = getConfig().getBoolean("general.allowflowout", true);
	Settings.allowTNTDamage = getConfig().getBoolean("general.allowTNTdamage", true);
	Settings.allowChestDamage = getConfig().getBoolean("general.allowchestdamage", true);
	// In-claim settings
	Settings.allowBreakBlocks = getConfig().getBoolean("general.allowbreakblocks", false);
	Settings.allowPlaceBlocks= getConfig().getBoolean("general.allowplaceblocks", false);
	Settings.allowBedUse= getConfig().getBoolean("general.allowbeduse", false);
	Settings.allowBucketUse = getConfig().getBoolean("general.allowbucketuse", false);
	Settings.allowShearing = getConfig().getBoolean("general.allowshearing", false);
	Settings.allowDoorUse = getConfig().getBoolean("general.allowdooruse", false);
	Settings.allowLeverButtonUse = getConfig().getBoolean("general.allowleverbuttonuse", false);
	Settings.allowCropTrample = getConfig().getBoolean("general.allowcroptrample", false);
	Settings.allowChestAccess = getConfig().getBoolean("general.allowchestaccess", false);
	Settings.allowFurnaceUse = getConfig().getBoolean("general.allowfurnaceuse", false);
	Settings.allowRedStone = getConfig().getBoolean("general.allowredstone", false);
	Settings.allowMusic = getConfig().getBoolean("general.allowmusic", false);
	Settings.allowCrafting = getConfig().getBoolean("general.allowcrafting", false);
	Settings.allowBrewing = getConfig().getBoolean("general.allowbrewing", false);
	Settings.allowGateUse = getConfig().getBoolean("general.allowgateuse", false);
	Settings.allowMobHarm = getConfig().getBoolean("general.allowmobharm", false);

	// Challenges
	getChallenges();
	// Challenge completion
	Settings.broadcastMessages = getConfig().getBoolean("general.broadcastmessages", true);
	Settings.removeCompleteOntimeChallenges = getConfig().getBoolean("general.removecompleteonetimechallenges", false);
	Settings.addCompletedGlow = getConfig().getBoolean("general.addcompletedglow", true);
	// All done
	return true;
    }


    /**
     * Registers events
     */
    public void registerEvents() {
	final PluginManager manager = getServer().getPluginManager();
	// Nether portal events
	manager.registerEvents(new NetherPortals(this), this);