Package de.beimax.simplespleef.game

Source Code of de.beimax.simplespleef.game.GameStandard

/**
* This file is part of the SimpleSpleef bukkit plugin.
* Copyright (C) 2011 Maximilian Kalus
* See http://dev.bukkit.org/server-mods/simple-spleef/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>.
**/

package de.beimax.simplespleef.game;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import java.util.Set;

import net.milkbowl.vault.economy.EconomyResponse;

import org.bukkit.ChatColor;
import org.bukkit.GameMode;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.block.BlockState;
import org.bukkit.command.CommandSender;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.entity.Enderman;
import org.bukkit.entity.Player;
import org.bukkit.event.block.Action;
import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.event.entity.CreatureSpawnEvent;
import org.bukkit.event.entity.EntityChangeBlockEvent;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.event.entity.EntityDamageEvent;
import org.bukkit.event.entity.EntityExplodeEvent;
import org.bukkit.event.entity.FoodLevelChangeEvent;
import org.bukkit.event.player.PlayerGameModeChangeEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerKickEvent;
import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.event.player.PlayerTeleportEvent;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;

import de.beimax.simplespleef.SimpleSpleef;
import de.beimax.simplespleef.game.arenarestoring.*;
import de.beimax.simplespleef.game.trackers.*;
import de.beimax.simplespleef.gamehelpers.*;

/**
* @author mkalus
*
*/
public class GameStandard extends Game {
  /**
   * static reference to random generator
   */
  private static Random generator = new Random();
 
  /**
   * Reference to spleefer list
   */
  protected SpleeferList spleefers;
 
  /**
   * Reference to spectators
   */
  protected List<Player> spectators;
 
  /**
   * Reference to configuration
   */
  protected ConfigurationSection configuration;
 
  /**
   * list of trackers called in each tick
   */
  protected List<Tracker> trackers;

  /**
   * to make game faster:
   */
  protected Set<Material> loseOnTouchMaterial;

  /**
   * to make game faster:
   */
  protected Set<Material> winOnTouchMaterial;
 
  /**
   * list of players which may be teleported - used by teleportPlayer and playerMayTeleport
   */
  private Set<Player> teleportOkList;
 
  /**
   * list of players which may change their game mode - used by onGameModeChange
   */
  private Set<Player> gamemodeChangeOkList;
 
  /**
   * arena cuboid
   */
  private Cuboid arena;

  /**
   * floor cuboid
   */
  private Cuboid floor;

  /**
   * lose cuboid
   */
  private Cuboid lose;

  /**
   * win cuboid
   */
  private Cuboid win;
 
  /**
   * set to false, if disallowDigBlocks has been chosen in config
   */
  private boolean allowDigBlocks = true;
 
  /**
   * inventory keeper for this game
   */
  protected InventoryKeeper inventoryKeeper;
 
  /**
   * shortcuts for digging settings
   */
  protected static final int DIGGING_NONE = 0;
  protected static final int DIGGING_EVERYWHERE = 1;
  protected static final int DIGGING_FLOOR_ONLY = 2;
  protected static final int DIGGING_IN_ARENA = 3;
  protected static final int DIGGING_OUTSIDE_ARENA = 4;
 
  private int diggingIfArenaUndefined;
  private int diggingIfFloorUndefined;
 
  /**
   * blocks that can be dug or (if allowDigBlocks is false) cannot
   */
  private LinkedList<ItemStack> digBlocks;
 
  /**
   * start timestamp
   */
  protected long startTime = -1;

  /**
   * finish timestamp
   */
  protected long finishTime = -1;

  /**
   * Constructor
   * @param name
   */
  public GameStandard(String name) {
    super(name);
    this.spleefers = new SpleeferList();
    this.spectators = new LinkedList<Player>();
    this.teleportOkList = new HashSet<Player>();
    this.gamemodeChangeOkList = new HashSet<Player>();
    this.trackers = new LinkedList<Tracker>();
  }

  @Override
  public String getName() {
    // get name from config
    String name = configuration.getString("name");
    if (name != null) return name;
    // otherwise return non-fancy name
    return super.getName();
  }

  @Override
  public String getType() {
    return "standard";
  }
 
  @Override
  public SpleeferList getSpleefers() {
    return spleefers;
  }

  @Override
  public void tick() {
    if (!isEnabled() || !isActive()) return; //ignore disabled and inactive arenas
   
    // call trackers in each tick
    for (Iterator<Tracker> iterator = trackers.iterator(); iterator.hasNext();) {
      Tracker tracker = iterator.next();
      if (tracker.tick()) { // returned true - this means the tracker signalled its end - remove from list
        iterator.remove(); // remove element
      }
    }
   
    //System.out.println(trackers.size());

    // check game end and there are not open trackers any more
    if (this.status == Game.STATUS_FINISHED && trackers.size() == 0) {
      this.status = Game.STATUS_INACTIVE; // reset game to inactive status
      renewTrackers(); // renew the trackers for the next game
    }
  }

  @Override
  public void addTracker(Tracker tracker) {
    if (!trackers.contains(tracker)) { // only add the same tracker once
      trackers.add(tracker);
      tracker.initialize(this);
    }
  }

  @Override
  public void interruptTracker(Tracker tracker) {
    if (trackers.contains(tracker))
      tracker.interrupt();
  }

  @Override
  public void interruptAllTrackers() {
    for (Tracker tracker : trackers) {
      tracker.interrupt();
    }
  }

  @Override
  public void trackersUpdateBlock(Block block, BlockState oldState) {
    if (isEnabled() && isActive()) { // only call on active games
      for(Tracker tracker : trackers)
        tracker.updateBlock(block, oldState);
    }
  }
 
  @Override
  public void notifyChangedBlock(Block block, BlockState oldState, Tracker caller) {
    if (isEnabled() && isActive()) { // only call on active games
      for(Tracker tracker : trackers)
        if (tracker != caller) // caller is not updated - has to do this itself - this avoids endless callbacks
          tracker.updateBlock(block, oldState);
    }
  }

  @Override
  public void defineSettings(ConfigurationSection conf) {
    this.configuration = conf;
   
    // define defaults/shortcuts
    if (conf.getBoolean("loseOnTouchBlocks", true)) {
      // is loseBlocks a valid list?
      if (conf.isList("loseBlocks")) { //yes
        this.loseOnTouchMaterial = new HashSet<Material>();
        for (String material : conf.getStringList("loseBlocks")) {
          Material m = Material.getMaterial(material);
          // check existence of material
          if (m == null) SimpleSpleef.log.warning("[SimpleSpleef] " + getId() + " configuration warning! Unknown Material in loseBlocks: " + material);
          else this.loseOnTouchMaterial.add(m);
        }
        if (this.loseOnTouchMaterial.size() == 0) this.loseOnTouchMaterial = null; //no
      } else this.loseOnTouchMaterial = null; //no
    } else this.loseOnTouchMaterial = null; // reset
   
    if (conf.getBoolean("winOnTouchBlocks", false)) {
      // is winBlocks a valid list?
      if (conf.isList("winBlocks")) { //yes
        this.winOnTouchMaterial = new HashSet<Material>();
        for (String material : conf.getStringList("winBlocks")) {
          Material m = Material.getMaterial(material);
          // check existence of material
          if (m == null) SimpleSpleef.log.warning("[SimpleSpleef] " + getId() + " configuration warning! Unknown Material in winBlocks: " + material);
          else this.winOnTouchMaterial.add(m);
        }
        if (this.winOnTouchMaterial.size() == 0) this.winOnTouchMaterial = null; //no
      } else this.winOnTouchMaterial = null; //no
    } else this.winOnTouchMaterial = null; // reset

    // define arena, floor and lose cuboids
    arena = SimpleSpleef.getGameHandler().configToCuboid(getId(), "arena");
    floor = SimpleSpleef.getGameHandler().configToCuboid(getId(), "floor");
    lose = SimpleSpleef.getGameHandler().configToCuboid(getId(), "lose");
    win = SimpleSpleef.getGameHandler().configToCuboid(getId(), "win");
    // block destruction/keep hashes
    if (conf.isList("allowDigBlocks")) {
      allowDigBlocks = true;
      digBlocks = new LinkedList<ItemStack>();
      for (String line : conf.getStringList("allowDigBlocks")) {
        digBlocks.add(MaterialHelper.getItemStackFromString(line, true));
      }
    } else if (conf.isList("disallowDigBlocks")) {
      allowDigBlocks = false;
      digBlocks = new LinkedList<ItemStack>();
      for (String line : conf.getStringList("disallowDigBlocks")) {
        digBlocks.add(MaterialHelper.getItemStackFromString(line, true));
      }
    } else digBlocks = null; // delete previous settings

    // get dig settings
    String dig = conf.getString("diggingIfArenaUndefined", "floorOnly").toLowerCase();
    if (dig.equals("none")) diggingIfArenaUndefined = GameStandard.DIGGING_NONE;
    else if (dig.equals("everywhere")) diggingIfArenaUndefined = GameStandard.DIGGING_EVERYWHERE;
    else if (floor == null) diggingIfArenaUndefined = GameStandard.DIGGING_EVERYWHERE;
    else diggingIfArenaUndefined = GameStandard.DIGGING_FLOOR_ONLY;
   
    dig = conf.getString("diggingIfFloorUndefined", "inArena").toLowerCase();
    if (dig.equals("none")) diggingIfFloorUndefined = GameStandard.DIGGING_NONE;
    else if (dig.equals("everywhere")) diggingIfFloorUndefined = GameStandard.DIGGING_EVERYWHERE;
    else if (arena == null) diggingIfFloorUndefined = GameStandard.DIGGING_EVERYWHERE;
    else if (dig.equals("outsidearena")) diggingIfFloorUndefined = GameStandard.DIGGING_OUTSIDE_ARENA;
    else diggingIfFloorUndefined = GameStandard.DIGGING_IN_ARENA;

    // initialize inventory keeper if needed
    if (configuration.getBoolean("clearInventory", false))
      this.inventoryKeeper = new InventoryKeeper();
    else this.inventoryKeeper = null;
   
    // floor changes might be registered by the floor tracker
    renewTrackers();

    //TODO: more definitions/shortcuts
  }

  /**
   * renew the trackers
   */
  protected void renewTrackers() {
    // renew the tracking list if needed
    if (trackers.size() > 0)
      trackers = new LinkedList<Tracker>();
   
    // floor trackers that are only used with working floor
    if (floor != null) {
      List<Block> diggableBlocks = floor.getDiggableBlocks(this);

      if (diggableBlocks != null) {
        int arenaFloorDissolvesAfter = configuration.getInt("arenaFloorDissolvesAfter", -1);
        if (arenaFloorDissolvesAfter > -1) {
          addTracker(new FloorDissolveWorker(arenaFloorDissolvesAfter, configuration.getInt("arenaFloorDissolveTick", 5), diggableBlocks));
        }
        int arenaFloorRepairsAfter = configuration.getInt("arenaFloorRepairsAfter", -1);
        if (arenaFloorRepairsAfter > -1) {
          addTracker(new FloorRepairWorker(arenaFloorRepairsAfter, configuration.getInt("arenaFloorRepairTick", 10), diggableBlocks));
        }
        int blockDegeneration = configuration.getInt("blockDegeneration", -1);
        if (blockDegeneration > -1) {
          addTracker(new PlayerOnBlockDegenerator(blockDegeneration, configuration.getStringList("degeneratingBlocks")));
        }
      }
    }
   
    // arena timeout
    int arenaTimeout = configuration.getInt("arenaTimeout", -1);
    if (arenaTimeout > 0) {
      addTracker(new ArenaTimeout(arenaTimeout));
    }
   
    if (configuration.getBoolean("useReady", false)) {
      int readyTimeout = configuration.getInt("readyTimeout", 0);
      if (readyTimeout > 0) {
        addTracker(new ReadyTimeout(readyTimeout));
      }
    }
   
    // determine type of arena
    String type;
    if (configuration.isBoolean("restoreArenaAfterGame")) type = "soft";
    else if (configuration.isString("restoreArenaAfterGame")) type = configuration.getString("restoreArenaAfterGame");
    else type = "soft"; // fall back
   
    ArenaRestorer arenaRestorer = null;
    if (type.equalsIgnoreCase("arenahard")) { // hard arena restorer
      arenaRestorer = new HardArenaRestorer(configuration.getInt("restoreArenaAfterGameTimer", 0));
      arenaRestorer.setArena(arena);
    } else if (type.equalsIgnoreCase("floorhard") || type.equalsIgnoreCase("hard")) { // hard floor restorer
      arenaRestorer = new HardArenaRestorer(configuration.getInt("restoreArenaAfterGameTimer", 0));
      arenaRestorer.setArena(floor);
    } else if (type.equalsIgnoreCase("simple")) { // simple floor restorer
      arenaRestorer = new SimpleRestorer(configuration.getInt("restoreArenaAfterGameTimer"), configuration.getString("simpleRestorerBlock", "GRASS"));
      arenaRestorer.setArena(floor);
    } else { // soft restorer
      Cuboid possibleFloor = floor==null?arena:floor;
      if (possibleFloor == null) {
        arenaRestorer = null;
        return; // freestyle spleefing does not implement any floor
        // TODO: we could relax this, though...
      }

      // create soft restorer
      arenaRestorer = new SoftRestorer(configuration.getInt("restoreArenaAfterGameTimer", 0));
      arenaRestorer.setArena(possibleFloor);
    }
    if (arenaRestorer != null)
      addTracker(arenaRestorer);
  }

  @Override
  public boolean announce(CommandSender sender) {
    // prechecks
    if (preCheckArenaActive(sender)) return false;
    if (preCheckArenaDisabled(sender)) return false;

    // activate game
    this.status = Game.STATUS_READY;
   
    // announce new game globally?
    if (SimpleSpleef.getPlugin().getConfig().getBoolean("settings.announceGame", true))
      broadcastMessage(ChatColor.GOLD + SimpleSpleef.ll("broadcasts.announce", "[PLAYER]", sender.getName(), "[ARENA]", getName()));
    else
      sender.sendMessage(ChatColor.GOLD + SimpleSpleef.ll("feedback.announce", "[ARENA]", getName()));

    return true;
  }

  @Override
  public boolean join(Player player) {
    // prechecks
    if (preCheckArenaDisabled(player)) return false;
    // announce before join activated for new game?
    if (!isActive()) {
      if (configuration.getBoolean("announceOnJoin", true))
        announce(player); // announce game, too
      else { // tell player that he/she may not announce game
        player.sendMessage(ChatColor.DARK_RED + SimpleSpleef.ll("errors.announceBeforeJoin", "[ARENA]", getName()));
        return false;
      }
    }
   
    // game is not joinable any more?
    if (!isJoinable()) {
      player.sendMessage(ChatColor.DARK_RED + SimpleSpleef.ll("errors.join", "[ARENA]", getName()));
      return false;
    }
   
    // has player already joined this game?
    if (hasPlayer(player)) {
      player.sendMessage(ChatColor.DARK_RED + SimpleSpleef.ll("errors.joinDouble", "[ARENA]", getName()));
      return false;
    }

    // max number of players?
    int maximumPlayers = configuration.getInt("maximumPlayers", 0);
    if (spleefers == null) spleefers = new SpleeferList(); //restore spleefers list if it was null for some reason - strange that this would happen...
    if (maximumPlayers > 0 && spleefers.size() >= maximumPlayers) {
      player.sendMessage(ChatColor.DARK_RED + SimpleSpleef.ll("errors.joinMax", "[ARENA]", getName(), "[NUMBER]", String.valueOf(maximumPlayers)));
      return false;
    }

    // player has join with empty inventory?
    if (configuration.getBoolean("emptyInventoryToJoin", false)) {
      if (!InventoryKeeper.inventoryIsEmpty(player)) {
        player.sendMessage(ChatColor.DARK_RED + SimpleSpleef.ll("errors.joinEmptyInventory"));
        return false;
      }
    }

    // check funds of player...
    if (SimpleSpleef.economy != null) {
      double entryFee = configuration.getDouble("entryFee", 0.0);
      if (entryFee > 0.0) {
        EconomyResponse response = SimpleSpleef.economy.withdrawPlayer(player.getName(), entryFee);
        if (response.type == EconomyResponse.ResponseType.SUCCESS) { // ok, tell the player about the amount charged
          player.sendMessage(SimpleSpleef.ll("feedback.joinFee", "[AMOUNT]", SimpleSpleef.economy.format(entryFee)));
        } else { //insufficient funds
          player.sendMessage(ChatColor.DARK_RED + SimpleSpleef.ll("errors.joinFee", "[AMOUNT]", SimpleSpleef.economy.format(entryFee)));
          return false;
        }
        // ok, is a part of the fee paid to a player?
        String playerNameGettingFees = configuration.getString("entryFeeGoesToPlayer", "none");
        if (playerNameGettingFees != null && !playerNameGettingFees.equalsIgnoreCase("none") &&  SimpleSpleef.economy.hasAccount(playerNameGettingFees))
          SimpleSpleef.economy.depositPlayer(playerNameGettingFees, configuration.getDouble("entryFeeAmountToPlayer", entryFee));
      }
    }
   
    // check gamemode and change it if needed
    boolean upgradeModeToCreative = false; // needed for OriginalPositionKeeper
    if (player.getGameMode() != GameMode.SURVIVAL) {
      player.setGameMode(GameMode.SURVIVAL);
      player.sendMessage(ChatColor.YELLOW + SimpleSpleef.ll("feedback.gamemodeChanged"));
      upgradeModeToCreative = true;
    }

    // actually add player
    if (!spleefers.addSpleefer(player)) { // some weird error
      player.sendMessage(ChatColor.DARK_RED + "Internal error while joining occured! Please tell the SimpleSpleef creator!");
      return false;
    }
   
    // unready game, if needed - this is needed because the game can have the status "ready", if all players are ready, but it has not started
    if (supportsReady()) {
      this.status = Game.STATUS_NEW;
    }

    // remember player's last position
    if (configuration.getBoolean("enableBackCommand", true))
      SimpleSpleef.getOriginalPositionKeeper().keepPosition(player, upgradeModeToCreative);
    // teleport player to lounge
    teleportPlayer(player, "lounge");

    // now we announce the joining of the player...
    String broadcastMessage = ChatColor.GREEN + SimpleSpleef.ll("broadcasts.join", "[PLAYER]", player.getName(), "[ARENA]", getName());
    if (SimpleSpleef.getPlugin().getConfig().getBoolean("settings.announceJoin", true)) { // broadcast
      broadcastMessage(broadcastMessage);
    } else { // player only
      player.sendMessage(ChatColor.GREEN + SimpleSpleef.ll("feedback.join", "[ARENA]", getName()));
      sendMessage(broadcastMessage, player); // notify players and spectators
    }
    return true;
  }

  @Override
  public boolean team(Player player, String team) {
    // no team games possible in this arena
    player.sendMessage(ChatColor.DARK_RED + SimpleSpleef.ll("errors.teamNotPossible", "[ARENA]", getName()));
    return false;
  }

  @Override
  public boolean ready(Player player, boolean hitBlock) {
    // readying is not used in this game
    if (!supportsReady()) {
      player.sendMessage(ChatColor.DARK_RED + SimpleSpleef.ll("errors.readyNotUsed", "[ARENA]", getName()));
      return false;
    }

    // game started already?
    if (!isJoinable()) { // avoid possible memory leak
      player.sendMessage(ChatColor.DARK_RED + SimpleSpleef.ll("errors.readyAlreadyStarted", "[ARENA]", getName()));
      return false;
    }

    // right command?
    if (!hitBlock && !supportsCommandReady()) {
      player.sendMessage(ChatColor.DARK_RED + SimpleSpleef.ll("errors.readyBlock", "[ARENA]", getName()));
      return false;
    }

    // get spleefer
    Spleefer spleefer = spleefers.getSpleefer(player);
    if (spleefer == null) { // internal error
      player.sendMessage(ChatColor.DARK_RED + "Internal error: Player " + player.getName() + " should be in spleefers list, but isn't!");
      return false;
    }

    // player already ready?
    if (spleefer.isReady()) {
      player.sendMessage(ChatColor.DARK_RED + SimpleSpleef.ll("errors.readyAlready", "[ARENA]", getName()));
      return false;
    }
   
    // ok, ready player now
    spleefer.setReady(true);
   
    // send messages
    player.sendMessage(ChatColor.GREEN + SimpleSpleef.ll("feedback.ready", "[ARENA]", getName(), "[PLAYER]", player.getDisplayName()));
    // broadcast message of somebody readying
    String broadcastMessage = ChatColor.DARK_PURPLE + SimpleSpleef.ll("broadcasts.ready", "[PLAYER]", player.getDisplayName(), "[ARENA]", getName());
    if (SimpleSpleef.getPlugin().getConfig().getBoolean("settings.announceReady", false)) {
      broadcastMessage(broadcastMessage); // broadcast message
    } else {
      // send message to all receivers
      sendMessage(broadcastMessage, player);
    }

    // is the game ready?
    checkReadyAndStartGame();
    return true;
  }

  @Override
  public boolean countdown(CommandSender sender) {
    // game started already?
    if (!isJoinable()) { // avoid possible memory leak
      sender.sendMessage(ChatColor.DARK_RED + SimpleSpleef.ll("errors.startDouble", "[ARENA]", getName()));
      return false;
    }
   
    // minimum number of players?
    int minimumPlayers = configuration.getInt("minimumPlayers", 0);
    if (minimumPlayers > 0 && spleefers.size() < minimumPlayers) {
      sender.sendMessage(ChatColor.DARK_RED + SimpleSpleef.ll("errors.startMin", "[ARENA]", getName(), "[NUMBER]", String.valueOf(minimumPlayers)));
      return false;
    }
   
    // game is not ready yet?
    if (!isReady()) {
      //get list of unready spleefers
      String unreadyList = getListOfUnreadySpleefers();
      if (unreadyList == null) unreadyList = "---"; // in any case...
      // send error message to player
      sender.sendMessage(ChatColor.DARK_RED + SimpleSpleef.ll("errors.startNotReady", "[ARENA]", getName(), "[PLAYERS]", unreadyList));
      return false;
    }

    // start game or countdown
    startGameOrCountdown();
   
    return true;
  }

  @Override
  public void checkReadyAndStartGame() {
    if (spleefers.countUnreadyPlayers() == 0) {
      // autostart game once all are ready
      if (configuration.getBoolean("readyAutoStart", false)) {
        // test for minumum players
        int minimumPlayers = configuration.getInt("minimumPlayers", 0);
        if (minimumPlayers > 0 && spleefers.size() < minimumPlayers) {
          // wait for more players to join and ready
          sendMessage(SimpleSpleef.ll("broadcasts.ready", "[NUMBER]", String.valueOf(spleefers.size() - minimumPlayers)), false);
          this.status = Game.STATUS_READY;
        }
        else startGameOrCountdown(); // start game right away
      }
      // otherwise, just set game as ready
      else this.status = Game.STATUS_READY;
    }
  }

  /**
   * helper to start game or countdown - used by ready and countdown methods
   */
  private void startGameOrCountdown() {
    // teleport players to arena
    teleportPlayersAtGameStart();
    // start countdown, if setting is 0 or higher
    int countdown = configuration.getInt("countdownFrom", 10);
    if (countdown == 0) {
      start(); // if countdown is null, start game right away
    } else {
      // initiate countdown - tick() will do the rest
      this.status = Game.STATUS_COUNTDOWN;
      addTracker(new Countdown(countdown)); // add countdown tracker
    }
  }
 
  /**
   * teleport players to arena
   */
  protected void teleportPlayersAtGameStart() {
    for (Spleefer spleefer : spleefers.get()) {
      teleportPlayer(spleefer.getPlayer(), "game");
    }
  }

  @Override
  public boolean start() {
    // change game status
    status = STATUS_STARTED;
    startTime = new Date().getTime();

    // notify statistics
    if (SimpleSpleef.getStatisticsModule() != null)
      SimpleSpleef.getStatisticsModule().gameStarted(this);

    // possibly clear inventory
    clearInventories();

    // optionally add to inventory
    addToInventories();

    // and/or give shovels
    addShovelItems();

    return true;
  }

  @Override
  public boolean leave(Player player) {
    // is the player a spectator?
    if (hasSpectator(player)) {
      return back(player); // redirect to back command instead
    }
   
    // check, if player is not a spleefer
    if (!spleefers.hasSpleefer(player)) {
      player.sendMessage(ChatColor.DARK_RED + "Internal error while leave occured! Please tell the SimpleSpleef creator!");
      return false;
    }
   
    // inform player
    player.sendMessage(ChatColor.GREEN + SimpleSpleef.ll("feedback.leave"));
    // broadcast message of somebody loosing
    String broadcastMessage = ChatColor.DARK_PURPLE + SimpleSpleef.ll("broadcasts.leave", "[PLAYER]", player.getDisplayName(), "[ARENA]", getName());
    if (SimpleSpleef.getPlugin().getConfig().getBoolean("settings.announceJoin", true)) {
      broadcastMessage(broadcastMessage); // broadcast message
    } else {
      // send message to all receivers
      sendMessage(broadcastMessage, player);
    }
   
    // teleport him/her back to original position, if supported
    if (configuration.getBoolean("enableBackCommand", true)) {
      // get original position
      boolean wasInCreativeBefore = SimpleSpleef.getOriginalPositionKeeper().wasInCreativeBefore(player);
      Location originalLocation = SimpleSpleef.getOriginalPositionKeeper().getOriginalPosition(player);
      if (originalLocation == null) { // no position
        player.sendMessage(ChatColor.DARK_RED + SimpleSpleef.ll("errors.backNoLocation"));
        return false;
      }
      // add player to teleport ok list
      this.teleportOkList.add(player);
      // teleport player to original position
      SimpleSpleef.simpleSpleefTeleport(player, originalLocation);
      player.sendMessage(ChatColor.GREEN + SimpleSpleef.ll("feedback.back"));
      // change game mode back, if needed
      if (wasInCreativeBefore)
        changeGameModeToCreative(player);
    }
   
    // check game status
    if (isJoinable() || isReady() || isFinished()) { //still joinable or ready or finished state - not so bad!
      // just remove spleefer
      spleefers.removeSpleefer(player);
      refundPlayer(player);
    } else if (status == Game.STATUS_COUNTDOWN) { // during countdown - end the game...
      // set player to lost, so that the player is not teleported twice - see endGame() for more info
      spleefers.setLost(player);
      endGame(); // actually end the game
    } else { // game is in progress - player loses - simple as that
      // player loses
      playerLoses(player, false); // do not teleport leaving players...
    }
    return true;
  }

  @Override
  public boolean stop(Player player) {
    // game is not in progress!
    if (!isInProgress()) {
      player.sendMessage(ChatColor.DARK_RED + SimpleSpleef.ll("errors.stop", "[ARENA]", getName()));
      return false;
    }

    // actually end the game
    if (!endGame()) return false;

    // send message
    sendMessage(SimpleSpleef.ll("feedback.stop", "[ARENA]", getName(), "[PLAYER]", player.getDisplayName()),
        SimpleSpleef.getPlugin().getConfig().getBoolean("settings.announceStop", true));
    return true;
  }

  @Override
  public boolean delete(CommandSender sender) {
    // refund players, if possible
    if (configuration.getDouble("refundMoney", 0.0) > 0) {
      for (Spleefer spleefer : spleefers.get())
        refundPlayer(spleefer.getPlayer());
    }
   
    // end the game first, if game is started
    if (!endGame()) return false;
    // send message
    sendMessage(SimpleSpleef.ll("feedback.delete", "[ARENA]", getName(), "[PLAYER]", sender.getName()),
        SimpleSpleef.getPlugin().getConfig().getBoolean("settings.announceStop", true));
    return true;
  }

  @Override
  public boolean watch(Player player) {
    if (!isEnabled() || !isActive()) { // game is not active
      player.sendMessage(ChatColor.DARK_RED + SimpleSpleef.ll("errors.watchNoGame", "[ARENA]", getName()));
      return false;
    }
   
    // check, if player is spleefer
    if (spleefers.hasSpleefer(player)) {
      player.sendMessage(ChatColor.DARK_RED + SimpleSpleef.ll("errors.watchSpleefer", "[ARENA]", getName()));
      return false;
    }

    // check, if player is in spectator list already
    if (spectators.contains(player)) {
      player.sendMessage(ChatColor.DARK_RED + SimpleSpleef.ll("errors.watchAlready", "[ARENA]", getName()));
      return false;
    }

    // check, if we have a spectator spawn defined
    if (!configuration.isConfigurationSection("spectatorSpawn") || !configuration.getBoolean("spectatorSpawn.enabled", false)) {
      player.sendMessage(ChatColor.DARK_RED + SimpleSpleef.ll("errors.watchNoSpawnDefined", "[ARENA]", getName()));
      return false;
    }

    // player has to watch with empty inventory?
    if (configuration.getBoolean("emptyInventoryToSpectate", false)) {
      if (!InventoryKeeper.inventoryIsEmpty(player)) {
        player.sendMessage(ChatColor.DARK_RED + SimpleSpleef.ll("errors.watchEmptyInventory"));
        return false;
      }
    }

    // save spectator's original position
    if (configuration.getBoolean("enableBackCommand", true))
      SimpleSpleef.getOriginalPositionKeeper().keepPosition(player, false); // never change game mode here

    // teleport spectator
    Location teleportTo = LocationHelper.configToExactLocation(configuration.getConfigurationSection("spectatorSpawn"));
    SimpleSpleef.simpleSpleefTeleport(player, teleportTo);

    // add to spectator list
    spectators.add(player);

    // send message to player
    player.sendMessage(ChatColor.GREEN + SimpleSpleef.ll("feedback.watch", "[ARENA]", getName()));
    return true;
  }

  @Override
  public boolean back(Player player) {
    // not allowed
    if (!configuration.getBoolean("enableBackCommand", true)) {
      player.sendMessage(ChatColor.DARK_RED + SimpleSpleef.ll("errors.commandNotAllowed", "[ARENA]", getName()));
      return false;
    }

    // check, if player is spleefer
    if (spleefers.hasSpleefer(player)) {
      player.sendMessage(ChatColor.DARK_RED + SimpleSpleef.ll("errors.backSpleefer", "[ARENA]", getName()));
      return false;
    }

    // check, if player is not a spectator
    if (spectators == null || !spectators.contains(player)) {
      player.sendMessage(ChatColor.DARK_RED + "Internal error while back occured! Please tell the SimpleSpleef creator!");
      return false;
    }

    // get original position
    Location originalLocation = SimpleSpleef.getOriginalPositionKeeper().getOriginalPosition(player);
    if (originalLocation == null) { // no position
      player.sendMessage(ChatColor.DARK_RED + SimpleSpleef.ll("errors.backNoLocation"));
      return false;
    }

    // remove player from watch list
    removeSpectator(player);
    // teleport player to original position
    SimpleSpleef.simpleSpleefTeleport(player, originalLocation);

    player.sendMessage(ChatColor.GREEN + SimpleSpleef.ll("feedback.back"));
    return true;
  }

  @Override
  public boolean onPlayerQuit(PlayerQuitEvent event) {
    if (!isEnabled() || !hasPlayer(event.getPlayer())) return false; // ignore disabled arenas and if player not here
   
    // in game?
    if (hasPlayer(event.getPlayer())) {
      // call helper method
      loseOnQuitOrKick(event.getPlayer());
      return true;
    }
    return false;
  }

  @Override
  public boolean onPlayerKick(PlayerKickEvent event) {
    if (!isEnabled()) return false; // ignore disabled arenas
   
    // in game?
    if (hasPlayer(event.getPlayer())) {
      // call helper method
      loseOnQuitOrKick(event.getPlayer());
     
      // delete original position, because player is banned anyhow
      SimpleSpleef.getOriginalPositionKeeper().deleteOriginalPosition(event.getPlayer());
      return true;
    }
    // also delete position if spectator
    if (hasSpectator(event.getPlayer())) {
      // delete original position, because player is banned anyhow
      SimpleSpleef.getOriginalPositionKeeper().deleteOriginalPosition(event.getPlayer());
      return true;
    }

    return false;
  }
 
  /**
   * helper method for onPlayerQuit and onPlayerKick
   * @param player
   */
  protected void loseOnQuitOrKick(Player player) {
    if (configuration.getBoolean("loseOnLogout", true)) {
      // broadcast message of somebody loosing
      String broadcastMessage = ChatColor.GREEN + SimpleSpleef.ll("broadcasts.lostByQuitting", "[PLAYER]", player.getName(), "[ARENA]", getName());
      if (SimpleSpleef.getPlugin().getConfig().getBoolean("settings.announceLose", true)) {
        broadcastMessage(broadcastMessage); // broadcast message
      } else {
        // send message to all receivers
        sendMessage(broadcastMessage, player);
      }

      // update player's game mode
      if (SimpleSpleef.getOriginalPositionKeeper().wasInCreativeBefore(player))
        changeGameModeToCreative(player);

      // player loses, if set to true
      playerLoses(player, false); // do not teleport dead players...
    } // else - do nothing...
  }

  @Override
  public boolean onPlayerJoin(PlayerJoinEvent event) {
    if (!isEnabled()) return false; // ignore disabled arenas

    // right now, do nothing... later on maybe give shovel back or so (if it has been taken away before the game ended - that gets complicated...)
    return true; //change this to false once enabled
  }

  @Override
  public boolean onPlayerDeath(Player player) {
    if (!isEnabled()) return false; // ignore disabled arenas
   
    if (hasPlayer(player)) {
      // delete original position, because player spawns somewhere else anyhow
      boolean creativeMode = SimpleSpleef.getOriginalPositionKeeper().wasInCreativeBefore(player);
      SimpleSpleef.getOriginalPositionKeeper().deleteOriginalPosition(player);
     
      // add respawn location, if set
      Location teleportTo = LocationHelper.configToExactLocation(configuration.getConfigurationSection("loseSpawn"));
      if (teleportTo != null) {
        SimpleSpleef.getGameHandler().addPlayerRespawnLocation(player, teleportTo, creativeMode);
      }
 
      if (configuration.getBoolean("loseOnDeath", true)) {
        // broadcast message of somebody loosing
        String broadcastMessage = ChatColor.GREEN + SimpleSpleef.ll("broadcasts.lostByDeath", "[PLAYER]", player.getName(), "[ARENA]", getName());
        if (SimpleSpleef.getPlugin().getConfig().getBoolean("settings.announceLose", true)) {
          broadcastMessage(broadcastMessage); // broadcast message
        } else {
          // send message to all receivers
          sendMessage(broadcastMessage, player);
        }

        // change game mode
        // update player's game mode
        if (SimpleSpleef.getOriginalPositionKeeper().wasInCreativeBefore(player))
          changeGameModeToCreative(player);

        // player loses, if set to true
        playerLoses(player, false); // do not teleport dead players...
      } // else - do nothing...
      return true;
    }
    return false;
  }

  @Override
  public boolean onPlayerMove(PlayerMoveEvent event) {
    if (!isEnabled()) return false; // ignore disabled arenas
    Player player = event.getPlayer();
    if (!hasPlayer(player) || !isInGame() || spleefers.hasLost(player)) return false; // if not in game or game is not in progress or player has lost, return
   
    //player touched certain block (setting loseOnTouchBlocks)
    if (loseOnTouchMaterial != null) {
      Material touchedBlock = event.getTo().getBlock().getType();
      Material onBlock = player.getLocation().getBlock().getRelative(BlockFace.DOWN).getType();
      // what happened exactly?
      boolean lostByTouching = loseOnTouchMaterial.contains(touchedBlock);
      boolean lostByStandingOn = loseOnTouchMaterial.contains(onBlock);
      if (lostByTouching || lostByStandingOn) {
        // Block name
        String blockName;
        if (lostByTouching) blockName = touchedBlock.name();
        else blockName = onBlock.name();
        // translate block name
        String translatedBlockName = SimpleSpleef.ll("material." + blockName);
        if (translatedBlockName != null) blockName = translatedBlockName;
        // broadcast message of somebody loosing
        String broadcastMessage = ChatColor.GREEN + SimpleSpleef.ll("broadcasts.lostByTouching", "[PLAYER]", player.getName(), "[ARENA]", getName(), "[MATERIAL]", blockName);
        if (SimpleSpleef.getPlugin().getConfig().getBoolean("settings.announceLose", true)) {
          broadcastMessage(broadcastMessage); // broadcast message
        } else {
          // send message to all receivers
          sendMessage(broadcastMessage, player);
        }
        // Ha, lost!
        playerLoses(player, true);
        return true;
      }
    }
   
    //player touched certain block (setting winOnTouchBlocks)
    if (winOnTouchMaterial != null) {
      Material touchedBlock = event.getTo().getBlock().getType();
      Material onBlock = player.getLocation().getBlock().getRelative(BlockFace.DOWN).getType();
      // what happened exactly?
      boolean wonByTouching = winOnTouchMaterial.contains(touchedBlock);
      boolean wonByStandingOn = winOnTouchMaterial.contains(onBlock);
      if (wonByTouching || wonByStandingOn) {
        // broadcast message of somebody loosing
        String broadcastMessage = ChatColor.GOLD + SimpleSpleef.ll("broadcasts.winByTouching", "[PLAYER]", player.getName(), "[ARENA]", getName());
        if (SimpleSpleef.getPlugin().getConfig().getBoolean("settings.announceWin", true)) {
          broadcastMessage(broadcastMessage); // broadcast message
        } else {
          // send message to all receivers
          sendMessage(broadcastMessage, player);
        }
        //player wins
        winByTouching(player);
        return true;
      }
    }
   
    // check location within "lose" cuboid (setting lose)
    if (lose != null && lose.contains(player.getLocation())) {
      // broadcast message of somebody loosing
      String broadcastMessage = ChatColor.GREEN + SimpleSpleef.ll("broadcasts.lostByCuboid", "[PLAYER]", player.getName(), "[ARENA]", getName());
      if (SimpleSpleef.getPlugin().getConfig().getBoolean("settings.announceLose", true)) {
        broadcastMessage(broadcastMessage); // broadcast message
      } else {
        // send message to all receivers
        sendMessage(broadcastMessage, player);
      }
      // Ha, lost!
      playerLoses(player, true);
      //return true; //redundant right now   
    }
   
    // check location within "win" cuboid (setting win)
    if (win != null && win.contains(player.getLocation())) {
      // broadcast message of somebody loosing
      String broadcastMessage = ChatColor.GOLD + SimpleSpleef.ll("broadcasts.winByTouching", "[PLAYER]", player.getName(), "[ARENA]", getName());
      if (SimpleSpleef.getPlugin().getConfig().getBoolean("settings.announceWin", true)) {
        broadcastMessage(broadcastMessage); // broadcast message
      } else {
        // send message to all receivers
        sendMessage(broadcastMessage, player);
      }
      // Player wins
      winByTouching(player);
      //return true; //redundant right now   
    }
    return true;
  }

  @Override
  public boolean onPlayerInteract(PlayerInteractEvent event) {
    if (!isEnabled()) return false; // ignore disabled arenas
    Block block = event.getClickedBlock();
    Player player = event.getPlayer();
    if (block == null || player == null || !hasPlayer(player) || spleefers.getSpleefer(player).hasLost()) return false; // ignore null blocks and null players and players not in game
   
    // check instant dig and block may be broken
    if (event.getAction() == Action.LEFT_CLICK_BLOCK && configuration.getBoolean("instantDig", true) && checkMayBreakBlock(block, player)) {
      // cancel event
      event.setCancelled(true);
      // get block data
      Block clickedBlock = event.getClickedBlock();
      BlockState state = clickedBlock.getState();
      // set block to air
      clickedBlock.setType(Material.AIR);
      // update chunk information
      clickedBlock.getWorld().refreshChunk(clickedBlock.getChunk().getX(), clickedBlock.getChunk().getZ());
      // notify trackers
      trackersUpdateBlock(clickedBlock, state);
    } else
    //check if player clicked on a "ready" block (e.g. iron block) and the game is readyable
      if (supportsBlockReady() && isJoinable()) {
        ItemStack readyBlockMaterial;
        try {
          readyBlockMaterial = MaterialHelper.getItemStackFromString(configuration.getString("readyBlockMaterial", null), true);
        } catch (Exception e) {
          return true; // ignore exceptions
        }
        if (readyBlockMaterial == null) return true; // ignore null materials
        // material has been checked, now test, if clicked block is of the same material
        if (readyBlockMaterial.getTypeId() == block.getTypeId() && MaterialHelper.isSameBlockType(block, readyBlockMaterial)) {
          ready(player, true);
        }
      }
    return true;
  }

  @Override
  public boolean onBlockBreak(BlockBreakEvent event) {
    if (!isEnabled()) return false; // ignore disabled arenas
    Player player = event.getPlayer();
    if (player == null) return false; // no NPEs
   
    // is player a spleefer?
    if (!isInProgress() || !hasPlayer(player) || spleefers.getSpleefer(player).hasLost()) {
      if (inProtectedArenaCube(event.getBlock())) { //check if block is in cube
        // cancel event
        event.setCancelled(true);
        player.sendMessage(ChatColor.DARK_RED + SimpleSpleef.ll("errors.noDig"));
        return true;
      }
    } else { // not in spleefer list, check arena area nevertheless
      blockBreakInGame(event);
      return true;     
    }
    return false;
  }

  /**
   * execute a block break within the game
   * @param event
   */
  protected void blockBreakInGame(BlockBreakEvent event) {
    Block block = event.getBlock();
    if (block == null) return; // sanity check
    // get block data
    BlockState state = block.getState();
    boolean floorBroken = false;
    // may the block be broken?
    if (!checkMayBreakBlock(block, event.getPlayer())) {
      // cancel event
      event.setCancelled(true);
      // message to player
      event.getPlayer().sendMessage(ChatColor.DARK_RED + SimpleSpleef.ll("errors.noDig"));
    } else if (!configuration.getBoolean("blockDropping", true)) { // otherwise: block dropping set to false => destroy blocks
      // cancel event - because we will handle the block destruction ourselves
      event.setCancelled(true);
      floorBroken = true;
      // set block to air
      block.setType(Material.AIR);
      // update chunk information
      block.getWorld().refreshChunk(block.getChunk().getX(), block.getChunk().getZ());
    } else floorBroken = true; // block was broken
   
    if (floorBroken) // update trackers
      trackersUpdateBlock(block, state);
  }
 
  /**
   * Check if a block is in protected arena
   * @param block
   * @return
   */
  protected boolean inProtectedArenaCube(Block block) {
    if (arena == null) return false;
   
    // check block position
    Location blockLoc = block.getLocation();
    return arena.contains(blockLoc);
  }

  @Override
  public boolean onBlockPlace(BlockPlaceEvent event) {
    if (!isEnabled()) return false; // ignore disabled arenas
    Player player = event.getPlayer();
    if (player == null) return true; // no NPEs
   
    // is player a spleefer?
    if (isInProgress() && hasPlayer(player) && !spleefers.getSpleefer(player).hasLost()) {
      blockPlaceInGame(event);
      return true;
    } else { // not in spleefer list, check arena area nevertheless
      if (inProtectedArenaCube(event.getBlock())) { //check if block is in cube
        // cancel event
        event.setCancelled(true);
        player.sendMessage(ChatColor.DARK_RED + SimpleSpleef.ll("errors.noPlacement"));
        return true;
      }
    }
    return false;
  }
 
  /**
   * execute a block place within the game
   * @param event
   */
  protected void blockPlaceInGame(BlockPlaceEvent event) {
    // joined players may not place blocks as long as game has not started
    // also, if allowBlockPlacing is false, disallow block placing during game
    if (!isInGame() || !configuration.getBoolean("allowBlockPlacing", false)) {
      // cancel event
      event.setCancelled(true);
      event.getPlayer().sendMessage(ChatColor.DARK_RED + SimpleSpleef.ll("errors.noPlacement"));
    }

    // if there is a floor tracker running, tell it about the change
    if (!event.isCancelled())
      trackersUpdateBlock(event.getBlock(), event.getBlockReplacedState());
  }

  @Override
  public boolean onFoodLevelChange(FoodLevelChangeEvent event) {
    if (!isEnabled()) return false; // ignore disabled arenas
    //if (event.getEntity() == null || !(event.getEntity() instanceof Player)) return;
    Player player = (Player) event.getEntity();

    if (hasPlayer(player) && configuration.getBoolean("noHunger", true)) {// if setting noHunger has been set for this arena, do not feel any hunger
      event.setCancelled(true);
      return true;
    }
    return false;
  }

  @Override
  public boolean onEntityDamage(EntityDamageEvent event) {
    //if (event.getEntity() == null || !(event.getEntity() instanceof Player)) return;
    Player player = (Player) event.getEntity();
   
    // player in game and in PVP world? Consider settings and only damage by other entities
    if (hasPlayer(player) && player.getWorld().getPVP() && configuration.getBoolean("noPvP", true) && event instanceof EntityDamageByEntityEvent) {
      EntityDamageByEntityEvent damageEvent = (EntityDamageByEntityEvent) event;
      // only consider player damage
      if (damageEvent.getDamager() instanceof Player) {
        event.setCancelled(true); // cancel damage event by caused by other players
        return true;
      }
    }
    return false;
  }

  @Override
  public boolean onEntityExplode(EntityExplodeEvent event) {
    if (!isEnabled() || !configuration.getBoolean("protectArena", true)
        || (arena == null && floor == null)) return false; // ignore disabled arenas, as well as unprotected ones or ones without blocks to protect

    // check if any exploded blocks were within the arena perimeter
    if (isInGame() && configuration.getBoolean("enableExplosionsDuringGame", false)) {
      // check for blocks exploded within the reach of a tracker...
      for (Block block : event.blockList()) {
        Cuboid checkCuboid = arena==null?floor:arena;
        if (checkCuboid.contains(block.getLocation())) {
          trackersUpdateBlock(block, block.getState());
        }
      }
      return true;
    } else { // not in game, cancel explosions close to the arena
      for (Block block : event.blockList()) {
        Cuboid checkCuboid = arena==null?floor:arena;
        if (checkCuboid.contains(block.getLocation())) {
          event.setCancelled(true);
          return true;
        }
      }
    }

    return false;
  }

  @Override
  public boolean onPlayerTeleport(PlayerTeleportEvent event) {
    if (!isEnabled()) return false; // ignore disabled arenas
    if (hasPlayer(event.getPlayer())) {
      // check, if arena allows the player's teleportation
      if (!playerMayTeleport(event.getPlayer())) {
        event.getPlayer().sendMessage(ChatColor.DARK_RED + SimpleSpleef.ll("errors.teleport", "[ARENA]", getName()));
        event.setCancelled(true); //cancel event
        return true;
      }
    }
    return false;
  }

  @Override
  public boolean onPlayerGameModeChange(PlayerGameModeChangeEvent event) {
    if (!isEnabled()) return false; // ignore disabled arenas
    Player player = event.getPlayer();
   
    // players and spectators may not change game mode
    if (hasPlayer(player) || hasSpectator(player)) {
      // ... if they are not in the game mode change ok list
      if (gamemodeChangeOkList.contains(player)) {
        gamemodeChangeOkList.remove(player);
      } else {
        event.getPlayer().sendMessage(ChatColor.DARK_RED + SimpleSpleef.ll("errors.gamemodeChange"));
        event.setCancelled(true); //cancel event
        return true;
      }
    }
    return false;
  }
 
  @Override
  public boolean onCreatureSpawn(CreatureSpawnEvent event) {
    if (!isEnabled() || arena == null || !configuration.getBoolean("disallowCreatureSpawns", true)) return false; // ignore disabled and undefined arenas, as well as those that allow spawning
   
    // check location of spawn event
    Location loc = event.getLocation();
    if (arena.contains(loc)) { // if within arena...
      event.setCancelled(true); //..cancel spawn event
    }
   
    return false;
  }
 
  @Override
  public boolean onEntityChangeBlock(EntityChangeBlockEvent event) {
    if (!isEnabled() || (arena == null && floor == null) || !configuration.getBoolean("protectArena", true)) return false; // ignore disabled and undifined arenas, as well as those that are not protected
    if (!(event.getEntity() instanceof Enderman)) return false; // only consider Endermen for now

    // check location of enderman event
    Location loc = event.getBlock().getLocation();
    if ((arena != null && arena.contains(loc)) || (floor != null && floor.contains(loc))) { // if within arena or floor...
      event.setCancelled(true); //..cancel spawn event
    }
   
    return false;
  }

  /**
   * Do end game maintenance
   * @return
   */
  public boolean endGame() {
    //check what the current status is and end game then
    boolean wasInProgress = isInProgress();
    //isJoinable()
    //isReady()
    // + other cases?
    int oldStatus = status;
   
    // change game status
    status = STATUS_FINISHED;
    finishTime = new Date().getTime();

    // notify statistics
    if (SimpleSpleef.getStatisticsModule() != null)
      SimpleSpleef.getStatisticsModule().gameFinished(this);

    // only do this when game was in progress
    if (wasInProgress) {
      // possibly take away shovel items
      removeShovelItems();
      // possibly restore inventories
      restoreAllInventories();
    }
   
    // do we need to teleport players?
    if (oldStatus == Game.STATUS_COUNTDOWN || oldStatus == Game.STATUS_INTERRUPTED) { // game ended during countdown - teleport players back to lounge
      for (Spleefer spleefer : spleefers.getLost()) {
        teleportPlayer(spleefer.getPlayer(), "lounge");
      }
    }
   
    // remove players from game
    spleefers = new SpleeferList();
   
    return true;
  }

  /**
   * check whether a certain block may be broken
   * => player has been checked before this, so this does only concern block breaks
   * and interactions by spleefers
   * @param block broken/interacted (on instant-break) by spleefer
   * @param player - player or null for automatic changes (e.g. trackers)
   * @return true, if block may be destroyed
   */
  @Override
  public boolean checkMayBreakBlock(Block block, Player player) {
    // sanity check
    if (block == null) return true;
    // joined players may not break blocks as long as game has not started
    if (player != null && !isInGame())
      return false;
    // joined players may not break blocks as long as game has not started
    return checkMayBreakBlockMaterial(block) && checkMayBreakBlockLocation(block);
  }
 
  /**
   * helper function for the above checkMayBreakBlock method to check the material of a block
   * @param block
   * @return
   */
  protected boolean checkMayBreakBlockMaterial(Block block) {
    // allowed blocks? => allowDigBlocks
    // alternatively: disallowDigBlocks
    if (digBlocks != null) {
      if (MaterialHelper.isSameBlockType(block, digBlocks)) return allowDigBlocks; // found -> return state
      else // not found
        return !allowDigBlocks; // negate
    }
    return true;
  }
 
  /**
   * helper function for the above checkMayBreakBlock method to check the location of a block
   * @param block
   * @return
   */
  protected boolean checkMayBreakBlockLocation(Block block) {
    Location blockLocation = block.getLocation();
    // is arena undefined?
    if (arena == null) {
      switch (diggingIfArenaUndefined) {
      case GameStandard.DIGGING_NONE: return false; // digging is not allowed
      case GameStandard.DIGGING_EVERYWHERE: return true; // digging is allowed everywhere
      default: // allowed in the arena floor in all other cases
        return this.floor.contains(blockLocation);
      }
    } else { // arena defined
      // is floor undefined?
      if (floor == null) {
        switch (diggingIfFloorUndefined) {
        case GameStandard.DIGGING_NONE: return false; // digging is not allowed
        case GameStandard.DIGGING_EVERYWHERE: return true; // digging is allowed everywhere
        case GameStandard.DIGGING_OUTSIDE_ARENA: return !this.arena.contains(blockLocation); // digging is allowed outside of arena
        default: // allowed within the arena in other cases
          return this.arena.contains(blockLocation);
        }
      } else { // floor and arena defined
        return this.floor.contains(blockLocation); // only arena floor can be broken during game
      }
    }
  }

  @Override
  public boolean hasPlayer(Player player) {
    if (spleefers == null || player == null) return false;
    return spleefers.hasSpleefer(player);
  }
 
  @Override
  public boolean hasSpectator(Player player) {
    if (spectators == null) return false;
    return spectators.contains(player);
  }
 
  @Override
  public boolean removeSpectator(Player player) {
    return spectators.remove(player);
  }

  @Override
  public String getNumberOfPlayers() {
    // no spleefers - return empty string
    if (spleefers == null || spleefers.size() == 0) return "";

    int active;
    int max;
    // shows information depending on state
    if (isInProgress()) {
      active = spleefers.inGame();
      max = spleefers.size();
    } else { // joinable -> show number of players joined, open places
      active = spleefers.size();
      max = configuration.getInt("maximumPlayers", 0);
    }
    return "(" + active + "/" + (max>0?max:"-") + ")";
  }

  @Override
  public String getListOfSpleefers() {
    // no spleefers - return null
    if (spleefers == null || spleefers.size() == 0) return null;
    // create list of spleefers
    String comma = SimpleSpleef.ll("feedback.infoComma");
    StringBuilder builder = new StringBuilder();
    int i = 0;
    for (Spleefer spleefer : spleefers.get()) {
      if (i > 0 && i == spleefers.size() - 1) builder.append(SimpleSpleef.ll("feedback.infoAnd")); // last element with end
      else if (i > 0) builder.append(comma)// other elements with ,
      // lost or in game?
      if (spleefer.hasLost()) builder.append(ChatColor.RED);
      else builder.append(ChatColor.GREEN);
      builder.append(spleefer.getPlayer().getDisplayName());
      builder.append(ChatColor.GRAY);
      i++;
    }
    return builder.toString();
  }

  @Override
  public String getListOfUnreadySpleefers() {
    // no spleefers - return null
    if (spleefers == null || spleefers.size() == 0) return null;
    // get unready spleefers
    List<Spleefer> list = spleefers.getUnready();
    // is the list empty?
    if (list.size() == 0) return null; // no unready spleefes
    // compile list
    return SpleeferList.getPrintablePlayerList(list);
  }

  @Override
  public String getListOfSpectators() {
    // no spectators - return null
    if (spectators == null || spectators.size() == 0) return null;
    // create list of spectators
    String comma = SimpleSpleef.ll("feedback.infoComma");
    StringBuilder builder = new StringBuilder();
    for (int i = 0; i < spectators.size(); i++) {
      if (i > 0 && i == spectators.size() - 1) builder.append(SimpleSpleef.ll("feedback.infoAnd")); // last element with end
      else if (i > 0) builder.append(comma)// other elements with ,
      builder.append(spectators.get(i).getDisplayName());
    }
    return builder.toString();
  }


  @Override
  public boolean isEnabled() {
    return configuration.getBoolean("enabled", true);
  }

  @Override
  public boolean supportsReady() {
    return supportsReady(false, false);
  }

  @Override
  public boolean supportsCommandReady() {
    return supportsReady(false, true);
  }

  @Override
  public boolean supportsBlockReady() {
    return supportsReady(true, false);
  }

  /**
   * Helper method to actually check useReady element
   * @param noCommand
   * @param noBlock
   * @return
   */
  protected boolean supportsReady(boolean noCommand, boolean noBlock) {
    if (configuration.isBoolean("useReady")) {
      return configuration.getBoolean("useReady", false);
    }
    if (configuration.isString("useReady")) {
      String ready = configuration.getString("useReady");
      if (noCommand == false && ready.equalsIgnoreCase("command")) return true;
      if (noBlock == false && ready.equalsIgnoreCase("block")) return true;
    }
    return false
 


  /**
   * Send a message to broadcast, or to players and spectators
   * @param message
   * @param broadcast
   */
  @Override
  public void sendMessage(String message, boolean broadcast) {
    if (message == null) {
      SimpleSpleef.log.warning("[SimpleSpleef] Message was null and could not be broadcasted!");
      return;
    }
    // global broadcast
    if (broadcast)
      broadcastMessage(message);
    else { // only players and specators
      // players
      for (Spleefer spleefer : spleefers.get()) {
        spleefer.getPlayer().sendMessage(message);
      }
      // spectators
      for (Player player : this.spectators) {
        player.sendMessage(message);
      }
      // send to console, too?
      if (SimpleSpleef.getPlugin().getConfig().getBoolean("settings.sendMessagesToConsole", true)) {
        SimpleSpleef.getPlugin().getServer().getConsoleSender().sendMessage(message);
      }
    }
  }

  /**
   * Send a message to broadcast, or to players and spectators
   * @param message
   * @param exception exception - this player does not receive message
   */
  @Override
  public void sendMessage(String message, Player exception) {
    // players
    for (Spleefer spleefer : spleefers.get()) {
      if (exception != spleefer.getPlayer())
        spleefer.getPlayer().sendMessage(message);
    }
    // spectators
    for (Player player : this.spectators) {
      if (exception != player)
        player.sendMessage(message);
    }
    // send to console, too
    SimpleSpleef.log.info(message);
  }
 
  /**
   * broadcast message considering announcementRadius
   * @param message
   */
  protected void broadcastMessage(String message) {
    // get announcement radius
    int announcementRadius = SimpleSpleef.getPlugin().getConfig().getInt("settings.announcementRadius", -1);
    if (announcementRadius <= 0 || arena == null) {
      SimpleSpleef.getPlugin().getServer().broadcastMessage(message); // broadcast globally, of radius set to -1 or arena undefined
      return; // do not send to console twice
    } else {
      // get (rough) center of arena
      Location center = arena.getCenter();

      //get players and calculate distance - only players in distance or in the game receive a message
      if (center != null)
        for (Player player : SimpleSpleef.getPlugin().getServer().getOnlinePlayers()) {
          if ((player.getWorld() == center.getWorld() && player.getLocation().distance(center) <= announcementRadius) || spleefers.hasSpleefer(player) || spectators.contains(player))
            player.sendMessage(message);
        }
    }
  }

  @Override
  public boolean playerMayTeleport(Player player) {
    // at the start of game, teleportation is ok
    if (isJoinable() && (spleefers == null || spleefers.size() == 0)) return true;
    // check if the player is on the teleport-ok-list, delete him/her from list and return true
    if (this.teleportOkList.contains(player)) {
      this.teleportOkList.remove(player);
      player.setFallDistance(0.0f); // set fall distance to 0 to negate damager
      return true;
    }
    // otherwise return preventTeleportingDuringGames for this arena
    return !configuration.getBoolean("preventTeleportingDuringGames", true);
  }

  /**
   * teleport a player to named spawn, if it exists and it is enabled
   * @param player
   * @param string
   */
  protected void teleportPlayer(Player player, String spawn) {
    if (!configuration.isConfigurationSection(spawn + "Spawn") || !configuration.getBoolean(spawn + "Spawn.enabled", false))
      return; // just ignore, if not set or not enabled
    // everything ok -> teleport player
    Location teleportTo = LocationHelper.configToExactLocation(configuration.getConfigurationSection(spawn + "Spawn"));
    if (teleportTo == null) SimpleSpleef.log.warning("[SimpleSpleef] Teleport error - location was null!");
    // load chunk, if needed
    teleportTo.getWorld().loadChunk(teleportTo.getChunk());
    // add player to teleport ok list
    this.teleportOkList.add(player);
    SimpleSpleef.simpleSpleefTeleport(player, teleportTo);
  }


  /**
   * called when player loses a game
   * (broadcast message has to be sent by calling method)
   * @param player
   * @param teleport true, if player should be teleported to lose spawn, if possible
   */
  protected void playerLoses(Player player, boolean teleport) {
    // set player to lost
    spleefers.setLost(player);
    // notify statistics
    if (SimpleSpleef.getStatisticsModule() != null)
      SimpleSpleef.getStatisticsModule().playerLostGame(player, this);
    // message to player
    player.sendMessage(ChatColor.RED + SimpleSpleef.ll("feedback.lost"));
    // broadcast message has to be sent by calling method
    // shovel lost, too
    removeShovelItem(player, true);
    // restore inventory
    restoreInventory(player);
    // teleport player to lose spawn
    if (teleport) teleportPlayer(player, "lose");
    // update player's game mode
    if (SimpleSpleef.getOriginalPositionKeeper().wasInCreativeBefore(player))
      changeGameModeToCreative(player);
    // determine if game is over...
    if (checkGameOver()) gameOver();
  }
 
  /**
   * checks whether the game is over
   * @return true for game over conditions are all met
   */
  protected boolean checkGameOver() {
    // check for number of players that have to remain on the field to tell it a win
    if (spleefers.inGame() <= configuration.getInt("remainingPlayersWin", 1)) return true;
    return false;
  }
 
  /**
   * Player wins by touching or reaching a certain part of the arena
   * @param player
   */
  protected void winByTouching(Player player) {
    for (Spleefer spleefer : spleefers.getNotLost()) {
      // if not the winning player and not already lost
      if (spleefer.getPlayer() != player)
        playerLoses(spleefer.getPlayer(), true);
    }
    // check game over manually, if there was no winner yet
    if (!isFinished() && checkGameOver()) gameOver();
  }
 
  /**
   * called when game is over - get winners, etc.
   */
  protected void gameOver() {
    // possibly restore inventories
    restoreAllInventories(); // done before paying prizes...

    // determine winners
    LinkedList<Spleefer> winners = new LinkedList<Spleefer>();
    for (Spleefer spleefer : spleefers.get()) {
      Player player = spleefer.getPlayer();
      if (!spleefer.hasLost()) { // not lost?
        //this guy is a winner - send a message
        player.sendMessage(ChatColor.DARK_GREEN + SimpleSpleef.ll("feedback.won"));
        winners.add(spleefer); // aggregate the winners to broadcast them later on
        // pay prizes
        payPrizeMoney(player);
        payPrizeExperience(player);
        payPrizeItems(player);

        // notify statistics
        if (SimpleSpleef.getStatisticsModule() != null)
          SimpleSpleef.getStatisticsModule().playerWonGame(player, this);
       
        // change player's game mode, if it was creative before
        if (SimpleSpleef.getOriginalPositionKeeper().wasInCreativeBefore(player))
          changeGameModeToCreative(player);

        // teleport winners back to winner's point or to lounge
        if (configuration.isConfigurationSection("winnerSpawn") && configuration.getBoolean("winnerSpawn.enabled", false))
          teleportPlayer(player, "winner");
        else teleportPlayer(player, "lounge");
      }
      // update original positions
      if (configuration.getBoolean("enableBackCommand", true))
        SimpleSpleef.getOriginalPositionKeeper().updateOriginalLocationTimestamp(player);
    }
    broadcastWinners(winners);
   
    // clean up game and end it
    endGame();
  }
 
  /**
   * broadcast the winners
   * @param winners
   */
  protected void broadcastWinners(LinkedList<Spleefer> winners) {
    // get the total winners from winners list
    String broadcastKey;
    String replacePlayer = "";
    // no winner?
    if (winners.size() == 0) broadcastKey = "None";
    else if (winners.size() == 1) { // one winner?
      broadcastKey = "One";
      replacePlayer = winners.getFirst().getPlayer().getDisplayName();
    } else { // multiple winners
      broadcastKey = "Multi";
      // build list of winners
      replacePlayer = SpleeferList.getPrintablePlayerList(winners);
    }
    // broadcast message
    String broadcastMessage = ChatColor.GOLD + SimpleSpleef.ll("broadcasts.win" + broadcastKey, "[PLAYER]", replacePlayer, "[ARENA]", getName());
    sendMessage(broadcastMessage, SimpleSpleef.getPlugin().getConfig().getBoolean("settings.announceWin", true));   
  }

  /**
   * Pay a prize in money
   * @param player
   */
  protected void payPrizeMoney(Player player) {
    // get prizes
    double prizeMoneyFixed = configuration.getDouble("prizeMoneyFixed", 0.0);
    double prizeMoneyPerPlayer = configuration.getDouble("prizeMoneyPerPlayer", 5.0);
    double win = prizeMoneyFixed + prizeMoneyPerPlayer * spleefers.size();
    if (win == 0) return; // if no prize money is payed, return without telling anybody
    if (SimpleSpleef.economy != null) {
      String formated = SimpleSpleef.economy.format(win);
      // give money to player
      SimpleSpleef.economy.depositPlayer(player.getName(), win);
      // player gets message
      player.sendMessage(ChatColor.AQUA + SimpleSpleef.ll("feedback.prizeMoney", "[ARENA]", getName(), "[MONEY]", formated));
      // broadcast prize?
      String broadcastMessage = ChatColor.AQUA + SimpleSpleef.ll("broadcasts.prizeMoney", "[PLAYER]", player.getName(), "[ARENA]", getName(), "[MONEY]", formated);
      if (SimpleSpleef.getPlugin().getConfig().getBoolean("settings.announcePrize", true)) {
        broadcastMessage(broadcastMessage); // broadcast message
      } else {
        sendMessage(broadcastMessage, player); // send message to all receivers
      }
    }
  }
 
  /**
   * Pay a prize in experience
   * @param player
   */
  protected void payPrizeExperience(Player player) {
    // get configuration and look, if we actually add experience
    int prizeExperienceFixed = configuration.getInt("prizeExperienceFixed", 0);
    int prizeExperiencePerPlayer = configuration.getInt("prizeExperiencePerPlayer", 0);
    int win = prizeExperienceFixed + prizeExperiencePerPlayer * spleefers.size();
    if (win == 0) return; // if no prize xps is payed, return without telling anybody
    // add player experience
    player.giveExp(win);
    // player gets message
    player.sendMessage(ChatColor.AQUA + SimpleSpleef.ll("feedback.prizeExperience", "[ARENA]", getName(), "[EXPERIENCE]", String.valueOf(win)));
    // broadcast prize?
    String broadcastMessage = ChatColor.AQUA + SimpleSpleef.ll("broadcasts.prizeExperience", "[PLAYER]", player.getName(), "[ARENA]", getName(), "[EXPERIENCE]", String.valueOf(win));
    if (SimpleSpleef.getPlugin().getConfig().getBoolean("settings.announcePrize", true)) {
      broadcastMessage(broadcastMessage); // broadcast message
    } else {
      sendMessage(broadcastMessage, player); // send message to all receivers
    }
  }

  /**
   * Pay a prize in items
   * @param player
   */
  protected void payPrizeItems(Player player) {
    if (!configuration.getBoolean("giveItemPrizes", true)) return; // disabled?
    if (!configuration.isList("itemPrizes")) { // check if this is a list
      SimpleSpleef.log.warning("[SimpleSpleef] Item winning list is arena " + getId() + " is not a valid list!");
      return;
    }
    // get list
    List<String> prizes = configuration.getStringList("itemPrizes");
    // random entry...
    String prize;
    if (prizes.size() == 1) { // if there is only one entry...
      prize = prizes.get(0);
    } else prize = prizes.get(generator.nextInt(prizes.size() - 1));
    // interpret line to Item Stack
    ItemStack itemStack = MaterialHelper.getItemStackFromString(prize, false);
    if (itemStack == null) { // fix NPE
      SimpleSpleef.log.warning("[SimpleSpleef] Item prize error: NPE for prize index #" + prize + "! Ignoring prize.");
      return;
    }
    // give prizes to player
    player.getInventory().addItem(itemStack);
    // player gets message
    player.sendMessage(ChatColor.AQUA + SimpleSpleef.ll("feedback.prizeItems", "[ARENA]", getName(), "[ITEM]", itemStack.getType().toString(), "[AMOUNT]", String.valueOf(itemStack.getAmount())));
    // broadcast prize?
    String broadcastMessage = ChatColor.AQUA + SimpleSpleef.ll("broadcasts.prizeItems", "[PLAYER]", player.getName(), "[ARENA]", getName(), "[ITEM]", itemStack.getType().toString(), "[AMOUNT]", String.valueOf(itemStack.getAmount()));
    if (SimpleSpleef.getPlugin().getConfig().getBoolean("settings.announcePrize", true)) {
      broadcastMessage(broadcastMessage); // broadcast message
    } else {
      sendMessage(broadcastMessage, player); // send message to all receivers
    }
  }

  /**
   * if "clearInventory" setting of area is true, clear inventory of player and remember it
   * @return
   */
  protected boolean clearInventories() {
    // check setting in configuration
    if (!configuration.getBoolean("clearInventory", false)) return false;
    if (spleefers == null || inventoryKeeper == null) return false; // no NPE

    for (Spleefer spleefer : spleefers.get()) {
      Player player = spleefer.getPlayer();
      if (!inventoryKeeper.saveInventory(player)) {
        // log error
        SimpleSpleef.log.severe("[SimpleSpleef] Could not clear inventory of " + player.getName() + ". Keeping it.");
      }
    }
    return true;
  }
 
  /**
   * if "clearInventory" setting of area is true, restore saved inventory of all players
   * @return
   */
  protected boolean restoreAllInventories() {
    // check setting in configuration
    if (!configuration.getBoolean("clearInventory", false)) return false;
    if (spleefers == null) return false; // no NPE

    for (Spleefer spleefer : spleefers.get()) {
      restoreInventory(spleefer.getPlayer());
    }
    return true;
  }
 
  /**
   * if "clearInventory" setting of area is true, restore saved inventory of single player
   * @return
   */
  protected boolean restoreInventory(Player player) {
    // check setting in configuration
    if (!configuration.getBoolean("clearInventory", false)) return false;
    if (player == null || inventoryKeeper == null) return false; // no NPE

    return inventoryKeeper.restoreInventory(player);
  }
 
  /**
   * if "addToInventory" setting of area is true, add items to inventory of player
   * @return
   */
  protected boolean addToInventories() {
    // check setting in configuration
    if (!configuration.getBoolean("addToInventory", false) || !configuration.isList("addInventoryItems")) return false;
   
    // create items for the inventory
    List<String> addInventoryItems = configuration.getStringList("addInventoryItems");
    LinkedList<ItemStack> itemStack = new LinkedList<ItemStack>();
    for (String item : addInventoryItems) {
      ItemStack stack = MaterialHelper.getItemStackFromString(item, false);
      if (stack == null)
        SimpleSpleef.log.warning("[SimpleSpleef] Could not parse addInventoryItems to item stack in arena " + getId() + ", line: " + item);
      else itemStack.add(stack);
    }
    // only continue if stack is greater 0
    if (itemStack.size() == 0) return false;
    addInventoryItems = null; // save memory
   
    // now give spleefers stack items
    for (Spleefer spleefer : spleefers.get()) {
      Player player = spleefer.getPlayer();
      for (ItemStack item : itemStack) {
        player.getInventory().addItem(item.clone());
      }
    }

    return true;
  }
 
  /**
   * if "playersReceiveShovelAtGameStart" setting of area is true, add shovel to inventory of player
   * @return
   */
  protected boolean addShovelItems() {
    // check setting first
    if (!configuration.getBoolean("playersReceiveShovelAtGameStart", true)) return false; // no shovels lost
    // if yes, add shovels to all players
    for (Spleefer spleefer : spleefers.get()) {
      addShovelItem(spleefer.getPlayer(), false); // add shovel without checking setting again
    }
    return true;
  }
 
  /**
   * remove shovel item from single player
   * @param player
   * @param checkSetting - if true, setting playersReceiveShovelAtGameStart is checked before addition
   * @return
   */
  protected boolean addShovelItem(Player player, boolean checkSetting) {
    // should setting be checked first?
    if (checkSetting && !configuration.getBoolean("playersReceiveShovelAtGameStart", true)) return false; // no shovel added
    // get material
    ItemStack shovelItem = MaterialHelper.getItemStackFromString(configuration.getString("shovelItem", "DIAMOND_SPADE"), false);
    if (shovelItem == null) {
      SimpleSpleef.log.warning("[SimpleSpleef] shovelItem of arena " + getId() + " is not a correct item id/name!");
      return false;
    }
    // give it to the player
    player.getInventory().addItem(shovelItem);
   
    return true;
  }
 
  /**
   * if "playersLoseShovelAtGameEnd" setting of area is true, remove shovel from inventory of player
   * @return
   */
  protected boolean removeShovelItems() {
    // check setting first
    if (!configuration.getBoolean("playersLoseShovelAtGameEnd", true)) return false; // no shovels lost
    // if yes, remove shovels from remaining players
    for (Spleefer spleefer : spleefers.getNotLost()) {
      removeShovelItem(spleefer.getPlayer(), false); // remove shovel without checking setting again
    }
    return true;
  }
 
  /**
   * remove shovel item from single player
   * @param player
   * @param checkSetting - if true, setting playersLoseShovelAtGameEnd is checked before removal
   * @return
   */
  protected boolean removeShovelItem(Player player, boolean checkSetting) {
    // ignore offline players
    if (player == null || !player.isOnline()) return false;

    // should setting be checked first?
    if (checkSetting && !configuration.getBoolean("playersLoseShovelAtGameEnd", true)) return false; // no shovel lost
    // get material
    ItemStack shovelItem = MaterialHelper.getItemStackFromString(configuration.getString("shovelItem", "DIAMOND_SPADE"), false);
    if (shovelItem == null) {
      SimpleSpleef.log.warning("[SimpleSpleef] shovelItem of arena " + getId() + " is not a correct item id/name!");
      return false;
    }
    // take if, if player still has it - yes, there are exploits, but I do
    // not want to handle them ;-)
    // We have to find the shovel item - it might have been used and its damage number be lowered due to use... So we have to
    // find the shovel that has been used most and delete that one...
    Inventory inventory = player.getInventory();
    Material shovelMaterial = MaterialHelper.getMaterialFromString(configuration.getString("shovelItem", "DIAMOND_SPADE"));
    // find all shovel items, if there are more than one to be removed
    for (int i = 0; i < shovelItem.getAmount(); i++) {
      int indexFound = -1; // keeps found shovels
      short durability = Short.MIN_VALUE; // keeps status of worst shovel found
      // get all possible shovel items
      for (Integer index : inventory.all(shovelMaterial).keySet()) {
        ItemStack current = inventory.getItem(index);
        short currentDurability = current.getDurability();
        if (currentDurability > durability) { // is this the worst shovel found?
          indexFound = index;
          durability = currentDurability;
        }
      }
     
      // worst shovel found?
      if (indexFound > -1) {
        // get item
        ItemStack stack = inventory.getItem(indexFound);
        if (stack.getAmount() > 1) { // shovels should only come in packs of one, but who knows?
          stack.setAmount(stack.getAmount() - 1); // reduce amount by one
          inventory.setItem(indexFound, stack);
        } else
          inventory.setItem(indexFound, null); // remove item completely
      }
    }
   
    return true;
  }
 
  /**
   * Check if arena is disabled
   * @param sender
   * @return true, if arena disabled
   */
  private boolean preCheckArenaDisabled(CommandSender sender) {
    if (!isEnabled()) {
      sender.sendMessage(ChatColor.DARK_RED + SimpleSpleef.ll("errors.arenaDisabled", "[ARENA]", getName()));
      return true;
    }
    return false;
  }

  /**
   * Check if game is already active
   * @param sender
   * @return true, if game is active
   */
  private boolean preCheckArenaActive(CommandSender sender) {
    // already running?
    if (isActive()) {
      sender.sendMessage(ChatColor.DARK_RED + SimpleSpleef.ll("errors.arenaExistsAlready", "[ARENA]", getName()));
      return true;
    }
    return false;
  }

  /**
   * refund player, if possible
   * @param player
   */
  private void refundPlayer(Player player) {
    // refund player?
    if (configuration.getDouble("refundMoney", 0.0) > 0 && SimpleSpleef.economy != null) {
      double money = configuration.getDouble("refundMoney", 0.0);
      String formated = SimpleSpleef.economy.format(money);
      // give money to player
      SimpleSpleef.economy.depositPlayer(player.getName(), money);
      // player gets message
      player.sendMessage(ChatColor.AQUA + SimpleSpleef.ll("feedback.refund", "[ARENA]", getName(), "[MONEY]", formated));
    }
  }

  /**
   * change game mode to creative and add player to gamemodeChangeOkList
   * @param player
   */
  private void changeGameModeToCreative(Player player) {
    // change only if setting has been set to do so
    if (configuration.getBoolean("changeGameModeBackToCreative", true)) {
      gamemodeChangeOkList.add(player); // may change game mode
      player.setGameMode(GameMode.CREATIVE);
    }
  }

  @Override
  public long getStartTime() {
    return startTime;
  }

  @Override
  public  long getFinishTime() {
    return finishTime;
  }

  @Override
  public void saveArena(CommandSender sender, boolean temporaryStore) {
    if (sender == null) sender = SimpleSpleef.getPlugin().getServer().getConsoleSender();

    if (arena == null) {
      sender.sendMessage(ChatColor.DARK_RED + SimpleSpleef.ll("adminerrors.saverestore", "[ARENA]", getName()));
      return;
    }
   
    SerializableBlockData[][][] blockData = arena.getSerializedBlocks();
   
    // output file
    File file = new File(SimpleSpleef.getPlugin().getDataFolder(), "arena_" + getId() + (temporaryStore?"_temporary":"") + ".save");
    // delete old file
    if (file.exists() && !file.delete()) {
      sender.sendMessage("[SimpleSpleef] Could not delete file " + file.getName());
      return;
    }
   
    try {
      // serialize objects
      FileOutputStream fileStream = new FileOutputStream(file);
      ObjectOutputStream os = new ObjectOutputStream(fileStream);
      // write array itself
      os.writeObject(blockData);
      os.close();
    } catch (Exception e) {
      sender.sendMessage("[SimpleSpleef] Could not save arena file " + file.getName() + ". Reason: " + e.getMessage());
      e.printStackTrace();
    }
   
    //feedback, if not temporary
    if (!temporaryStore) {
      sender.sendMessage(ChatColor.GREEN + SimpleSpleef.ll("adminfeedback.savearena", "[ARENA]", getName()));
    }
  }

  @Override
  public void restoreArena(CommandSender sender, boolean temporaryStore) {
    if (sender == null) sender = SimpleSpleef.getPlugin().getServer().getConsoleSender();

    if (arena == null) {
      sender.sendMessage(ChatColor.DARK_RED + SimpleSpleef.ll("adminerrors.saverestore", "[ARENA]", getName()));
      return;
    }
   
    // input file
    File file = new File(SimpleSpleef.getPlugin().getDataFolder(), "arena_" + getId() + (temporaryStore?"_temporary":"") + ".save");
    if (!file.exists()) {
      sender.sendMessage("[SimpleSpleef] Could not find arena file " + file.getName());
      return;
    }
    SerializableBlockData[][][] blockData;
    try {
      // deserialize objects
      FileInputStream fileInputStream = new FileInputStream(file);
      ObjectInputStream oInputStream = new ObjectInputStream(fileInputStream);
      blockData = (SerializableBlockData[][][]) oInputStream.readObject();
      oInputStream.close();
    } catch (Exception e) {
      sender.sendMessage("[SimpleSpleef] Could not restore arena file " + file.getName() + ". Reason: " + e.getMessage());
      return;
    }
    // restore arena - this is quite heavy on the server...
    arena.setSerializedBlocks(blockData);
    // delete file at the end in teporary mode - cleanup work...
    if (temporaryStore && !file.delete()) sender.sendMessage("[SimpleSpleef] Could not delete file " + file.getName());

    //feedback, if not temporary
    if (!temporaryStore) {
      sender.sendMessage(ChatColor.GREEN + SimpleSpleef.ll("adminfeedback.restorearena", "[ARENA]", getName()));
    }
  }
}
TOP

Related Classes of de.beimax.simplespleef.game.GameStandard

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.