package com.garbagemule.MobArena;
import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.PriorityBlockingQueue;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Chunk;
import org.bukkit.GameMode;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.block.BlockState;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.entity.*;
import org.bukkit.inventory.*;
import org.bukkit.permissions.PermissionAttachment;
import org.bukkit.potion.PotionEffect;
import static com.garbagemule.MobArena.util.config.ConfigUtils.makeSection;
import com.garbagemule.MobArena.ArenaClass.ArmorType;
import com.garbagemule.MobArena.events.*;
import com.garbagemule.MobArena.framework.Arena;
import com.garbagemule.MobArena.leaderboards.Leaderboard;
import com.garbagemule.MobArena.region.ArenaRegion;
import com.garbagemule.MobArena.repairable.*;
import com.garbagemule.MobArena.time.Time;
import com.garbagemule.MobArena.time.TimeStrategy;
import com.garbagemule.MobArena.time.TimeStrategyLocked;
import com.garbagemule.MobArena.time.TimeStrategyNull;
import com.garbagemule.MobArena.util.*;
import com.garbagemule.MobArena.util.inventory.InventoryManager;
import com.garbagemule.MobArena.util.inventory.InventoryUtils;
import com.garbagemule.MobArena.util.timer.AutoStartTimer;
import com.garbagemule.MobArena.util.timer.StartDelayTimer;
import com.garbagemule.MobArena.waves.*;
import com.garbagemule.MobArena.ScoreboardManager.NullScoreboardManager;
public class ArenaImpl implements Arena
{
// General stuff
private MobArena plugin;
private String name;
private World world;
// Settings section of the config-file for this arena.
private ConfigurationSection settings;
// Run-time settings and critical config settings
private boolean enabled, protect, running, edit;
// World stuff
private boolean allowMonsters, allowAnimals;
//private Difficulty spawnMonsters;
// Warps, points and locations
private ArenaRegion region;
private Leaderboard leaderboard;
// Player stuff
private InventoryManager inventoryManager;
private RewardManager rewardManager;
private ClassLimitManager limitManager;
private Map<Player,ArenaPlayer> arenaPlayerMap;
private Map<Player,PlayerData> playerData = new HashMap<Player,PlayerData>();
private Set<Player> arenaPlayers, lobbyPlayers, readyPlayers, specPlayers, deadPlayers;
private Set<Player> randoms;
// Classes stuff
private Map<String,ArenaClass> classes;
private Map<Player,PermissionAttachment> attachments;
// Blocks and pets
private PriorityBlockingQueue<Repairable> repairQueue;
private Set<Block> blocks;
private LinkedList<Repairable> repairables, containables;
// Monster stuff
private MonsterManager monsterManager;
// Wave stuff
private WaveManager waveManager;
private MASpawnThread spawnThread;
private SheepBouncer sheepBouncer;
private Map<Integer,List<ItemStack>> everyWaveMap, afterWaveMap;
// Misc
private ArenaListener eventListener;
private List<ItemStack> entryFee;
private TimeStrategy timeStrategy;
private AutoStartTimer autoStartTimer;
private StartDelayTimer startDelayTimer;
private boolean isolatedChat;
// Scoreboards
private ScoreboardManager scoreboard;
// Last player standing
private Player lastStanding;
/**
* Primary constructor. Requires a name and a world.
*/
public ArenaImpl(MobArena plugin, ConfigurationSection section, String name, World world) {
if (world == null)
throw new NullPointerException("[MobArena] ERROR! World for arena '" + name + "' does not exist!");
this.name = name;
this.world = world;
this.plugin = plugin;
this.settings = makeSection(section, "settings");
this.region = new ArenaRegion(section, this);
this.enabled = settings.getBoolean("enabled", false);
this.protect = settings.getBoolean("protect", true);
this.running = false;
this.edit = false;
this.inventoryManager = new InventoryManager(this);
this.rewardManager = new RewardManager(this);
// Warps, points and locations
this.leaderboard = new Leaderboard(plugin, this, region.getLeaderboard());
// Player stuff
this.arenaPlayerMap = new HashMap<Player,ArenaPlayer>();
this.arenaPlayers = new HashSet<Player>();
this.lobbyPlayers = new HashSet<Player>();
this.readyPlayers = new HashSet<Player>();
this.specPlayers = new HashSet<Player>();
this.deadPlayers = new HashSet<Player>();
this.randoms = new HashSet<Player>();
// Classes, items and permissions
this.classes = plugin.getArenaMaster().getClasses();
this.attachments = new HashMap<Player,PermissionAttachment>();
this.limitManager = new ClassLimitManager(this, classes, makeSection(section, "class-limits"));
// Blocks and pets
this.repairQueue = new PriorityBlockingQueue<Repairable>(100, new RepairableComparator());
this.blocks = new HashSet<Block>();
this.repairables = new LinkedList<Repairable>();
this.containables = new LinkedList<Repairable>();
// Monster stuff
this.monsterManager = new MonsterManager();
// Wave stuff
this.waveManager = new WaveManager(this, section.getConfigurationSection("waves"));
this.everyWaveMap = MAUtils.getArenaRewardMap(plugin, section, name, "every");
this.afterWaveMap = MAUtils.getArenaRewardMap(plugin, section, name, "after");
// Misc
this.eventListener = new ArenaListener(this, plugin);
this.entryFee = ItemParser.parseItems(settings.getString("entry-fee", ""));
this.allowMonsters = world.getAllowMonsters();
this.allowAnimals = world.getAllowAnimals();
this.autoStartTimer = new AutoStartTimer(this);
this.startDelayTimer = new StartDelayTimer(this, autoStartTimer);
this.isolatedChat = settings.getBoolean("isolated-chat", false);
String timeString = settings.getString("player-time-in-arena", "world");
Time time = Enums.getEnumFromString(Time.class, timeString);
this.timeStrategy = (time != null ? new TimeStrategyLocked(time) : new TimeStrategyNull());
// Scoreboards
this.scoreboard = (settings.getBoolean("use-scoreboards", true) ? new ScoreboardManager(this) : new NullScoreboardManager(this));
}
/*/////////////////////////////////////////////////////////////////////////
//
// NEW METHODS IN REFACTORING
//
/////////////////////////////////////////////////////////////////////////*/
@Override
public ConfigurationSection getSettings() {
return settings;
}
@Override
public World getWorld() {
return world;
}
@Override
public void setWorld(World world) {
this.world = world;
settings.set("world", world.getName());
plugin.saveConfig();
if (region != null) region.refreshWorld();
}
@Override
public boolean isEnabled() {
return enabled;
}
@Override
public void setEnabled(boolean value) {
enabled = value;
settings.set("enabled", enabled);
}
@Override
public boolean isProtected() {
return protect;
}
@Override
public void setProtected(boolean value) {
protect = value;
settings.set("protect", protect);
}
@Override
public boolean isRunning() {
return running;
}
@Override
public boolean inEditMode() {
return edit;
}
@Override
public void setEditMode(boolean value) {
edit = value;
}
@Override
public int getMinPlayers() {
return settings.getInt("min-players");
}
@Override
public int getMaxPlayers() {
return settings.getInt("max-players");
}
private int getJoinDistance() {
return settings.getInt("max-join-distance");
}
@Override
public List<ItemStack> getEntryFee() {
return entryFee;
}
@Override
public Set<Map.Entry<Integer,List<ItemStack>>> getEveryWaveEntrySet() {
return everyWaveMap.entrySet();
}
@Override
public List<ItemStack> getAfterWaveReward(int wave) {
return afterWaveMap.get(wave);
}
@Override
public Set<Player> getPlayersInArena() {
return Collections.unmodifiableSet(arenaPlayers);
}
@Override
public Set<Player> getPlayersInLobby() {
return Collections.unmodifiableSet(lobbyPlayers);
}
@Override
public Set<Player> getReadyPlayersInLobby() {
return Collections.unmodifiableSet(readyPlayers);
}
@Override
public Set<Player> getSpectators() {
return Collections.unmodifiableSet(specPlayers);
}
@Override
public MASpawnThread getSpawnThread() {
return spawnThread;
}
@Override
public WaveManager getWaveManager() {
return waveManager;
}
@Override
public Location getPlayerEntry(Player p) {
PlayerData mp = playerData.get(p);
return (mp != null ? mp.entry() : null);
}
@Override
public ArenaListener getEventListener() {
return eventListener;
}
@Override
public void setLeaderboard(Leaderboard leaderboard) {
this.leaderboard = leaderboard;
}
@Override
public ArenaPlayer getArenaPlayer(Player p) {
return arenaPlayerMap.get(p);
}
@Override
public Set<Block> getBlocks() {
return blocks;
}
@Override
public void addBlock(Block b) {
blocks.add(b);
}
@Override
public boolean removeBlock(Block b) {
return blocks.remove(b);
}
@Override
public boolean hasPet(Entity e) {
return monsterManager.hasPet(e);
}
@Override
public void addRepairable(Repairable r) {
repairables.add(r);
}
@Override
public ArenaRegion getRegion() {
return region;
}
@Override
public InventoryManager getInventoryManager() {
return inventoryManager;
}
@Override
public RewardManager getRewardManager() {
return rewardManager;
}
@Override
public MonsterManager getMonsterManager() {
return monsterManager;
}
@Override
public ClassLimitManager getClassLimitManager() {
return limitManager;
}
@Override
public ScoreboardManager getScoreboard() {
return scoreboard;
}
@Override
public boolean startArena() {
// Sanity-checks
if (running || lobbyPlayers.isEmpty() || !readyPlayers.containsAll(lobbyPlayers)) {
return false;
}
// Check if start-delay is over
if (startDelayTimer.isRunning()) {
return false;
}
// Stop the auto-start-timer regardless
autoStartTimer.stop();
// Fire the event and check if it's been cancelled.
ArenaStartEvent event = new ArenaStartEvent(this);
plugin.getServer().getPluginManager().callEvent(event);
if (event.isCancelled()) {
return false;
}
// Store all chest contents.
storeContainerContents();
// Populate arenaPlayers and clear the lobby.
arenaPlayers.addAll(lobbyPlayers);
lobbyPlayers.clear();
readyPlayers.clear();
// Assign random classes.
for (Player p : randoms) {
assignRandomClass(p);
}
randoms.clear();
// Then check if there are still players left.
if (arenaPlayers.isEmpty()) {
return false;
}
// Initialize scoreboards
scoreboard.initialize();
// Teleport players, give full health, initialize map
for (Player p : arenaPlayers) {
// TODO figure out how people die in lobby and get sent to spectator area early
// Remove player from spec list to avoid invincibility issues
if (inSpec(p)) {
specPlayers.remove(p);
System.out.println("[MobArena] Player " + p.getName() + " joined the arena from the spec area!");
System.out.println("[MobArena] Invincibility glitch attempt stopped!");
}
p.teleport(region.getArenaWarp());
p.setAllowFlight(false);
p.setFlying(false);
//movePlayerToLocation(p, region.getArenaWarp());
setHealth(p, p.getMaxHealth());
p.setFoodLevel(20);
if (settings.getBoolean("display-waves-as-level", false)) {
p.setLevel(0);
p.setExp(0.0f);
}
assignClassPermissions(p);
arenaPlayerMap.get(p).resetStats();
double price = arenaPlayerMap.get(p).getArenaClass().getPrice();
if (price > 0D) {
plugin.takeMoney(p, price);
}
scoreboard.addPlayer(p);
}
// Start spawning monsters (must happen before 'running = true;')
startSpawner();
startBouncingSheep();
// Set the boolean.
running = true;
// Spawn pets (must happen after 'running = true;')
spawnPets();
// Spawn mounts
spawnMounts();
// Clear the classes in use map, as they're no longer needed
limitManager.clearClassesInUse();
// Reset rewards
rewardManager.reset();
// Initialize leaderboards and start displaying info.
leaderboard.initialize();
leaderboard.startTracking();
Messenger.announce(this, Msg.ARENA_START);
return true;
}
@Override
public boolean endArena() {
// Sanity-checks.
if (!running || !arenaPlayers.isEmpty()) {
return false;
}
// Fire the event and check if it's been cancelled.
ArenaEndEvent event = new ArenaEndEvent(this);
plugin.getServer().getPluginManager().callEvent(event);
if (event.isCancelled()) {
return false;
}
// Reset last standing
lastStanding = null;
// Set the running boolean and disable arena if not disabled.
boolean en = enabled;
enabled = false;
running = false;
// Stop tracking leaderboards
leaderboard.stopTracking();
leaderboard.update();
// Stop spawning.
stopSpawner();
// Announce and clean arena floor, etc.
if (settings.getBoolean("global-end-announce", false)) {
for (Player p : Bukkit.getOnlinePlayers()) {
Messenger.tell(p, Msg.ARENA_END_GLOBAL, configName());
}
} else {
Messenger.announce(this, Msg.ARENA_END);
}
cleanup();
// Restore region.
if (settings.getBoolean("soft-restore", false)) {
restoreRegion();
}
// Restore chests
restoreContainerContents();
// Restore enabled status.
enabled = en;
return true;
}
@Override
public void forceStart()
{
if (running)
return;
// Set operations.
Set<Player> tmp = new HashSet<Player>();
tmp.addAll(lobbyPlayers);
tmp.removeAll(readyPlayers);
// Force leave.
for (Player p : tmp) {
playerLeave(p);
Messenger.tell(p, Msg.LEAVE_NOT_READY);
}
// Stop start-delay-timer and start arena
startDelayTimer.stop();
startArena();
}
@Override
public void forceEnd() {
for (Player p : getAllPlayers()) {
playerLeave(p);
}
cleanup();
}
@Override
public boolean playerJoin(Player p, Location loc)
{
// Fire the event and check if it's been cancelled.
ArenaPlayerJoinEvent event = new ArenaPlayerJoinEvent(p, this);
plugin.getServer().getPluginManager().callEvent(event);
if (event.isCancelled()) {
return false;
}
// Announce globally (must happen before moving player)
if (settings.getBoolean("global-join-announce", false)) {
if (lobbyPlayers.isEmpty()) {
for (Player q : Bukkit.getOnlinePlayers()) {
Messenger.tell(q, Msg.ARENA_JOIN_GLOBAL, configName());
}
}
}
movePlayerToLobby(p);
takeFee(p);
storePlayerData(p, loc);
removePotionEffects(p);
MAUtils.sitPets(p);
setHealth(p, p.getMaxHealth());
p.setFoodLevel(20);
if (settings.getBoolean("display-timer-as-level", false)) {
p.setLevel(0);
p.setExp(0.0f);
}
p.setGameMode(GameMode.SURVIVAL);
arenaPlayerMap.put(p, new ArenaPlayer(p, this, plugin));
// Start the start-delay-timer if applicable
if (!autoStartTimer.isRunning()) {
startDelayTimer.start();
}
// Notify player of joining
Messenger.tell(p, Msg.JOIN_PLAYER_JOINED);
// Notify player of time left
if (startDelayTimer.isRunning()) {
Messenger.tell(p, Msg.ARENA_START_DELAY, "" + startDelayTimer.getRemaining() / 20l);
} else if (autoStartTimer.isRunning()) {
Messenger.tell(p, Msg.ARENA_AUTO_START, "" + autoStartTimer.getRemaining() / 20l);
}
return true;
}
@Override
public void playerReady(Player p)
{
ArenaPlayerReadyEvent event = new ArenaPlayerReadyEvent(p, this);
plugin.getServer().getPluginManager().callEvent(event);
if (event.isCancelled()) {
return;
}
readyPlayers.add(p);
int minPlayers = getMinPlayers();
if (minPlayers > 0 && lobbyPlayers.size() < minPlayers)
{
Messenger.tell(p, Msg.LOBBY_NOT_ENOUGH_PLAYERS, "" + minPlayers);
return;
}
startArena();
}
@Override
public boolean playerLeave(Player p)
{
// Fire the event and check if it's been cancelled.
ArenaPlayerLeaveEvent event = new ArenaPlayerLeaveEvent(p, this);
plugin.getServer().getPluginManager().callEvent(event);
if (event.isCancelled()) {
return false;
}
// Clear inventory if player is an arena player, and unmount
if (arenaPlayers.contains(p)) {
unmount(p);
clearInv(p);
}
removeClassPermissions(p);
removePotionEffects(p);
restoreInvAndExp(p);
if (inLobby(p) || inArena(p)) {
refund(p);
}
if (inLobby(p)) {
ArenaPlayer ap = arenaPlayerMap.get(p);
if (ap.getArenaClass() != null) {
limitManager.playerLeftClass(ap.getArenaClass(), ap.getPlayer());
}
// Last lobby player leaving? Stop the timer
if (lobbyPlayers.size() == 1) {
startDelayTimer.stop();
}
}
movePlayerToEntry(p);
discardPlayer(p);
endArena();
return true;
}
@Override
public void playerDeath(Player p)
{
// Check if we're the last player standing
boolean last = arenaPlayers.size() == 1;
if (last) lastStanding = p;
// Fire the event
ArenaPlayerDeathEvent event = new ArenaPlayerDeathEvent(p, this, last);
plugin.getServer().getPluginManager().callEvent(event);
// Clear the player's inventory, and unmount
if (arenaPlayers.remove(p)) {
unmount(p);
clearInv(p);
}
if (!settings.getBoolean("auto-respawn", true)) {
deadPlayers.add(p);
endArena();
return;
}
setHealth(p, p.getMaxHealth());
Delays.revivePlayer(plugin, this, p);
endArena();
}
private void clearInv(Player p) {
InventoryView view = p.getOpenInventory();
if (view != null) {
view.setCursor(new ItemStack(0));
view.getBottomInventory().clear();
view.close();
}
}
private void unmount(Player p) {
Entity v = p.getVehicle();
if (v != null) {
monsterManager.removeMount(v);
v.eject();
v.remove();
}
}
@Override
public void playerRespawn(Player p) {
if (settings.getBoolean("auto-respawn", true)) {
return;
}
deadPlayers.remove(p);
revivePlayer(p);
}
@Override
@SuppressWarnings("deprecation")
public void revivePlayer(Player p) {
Delays.douse(plugin, p, 1);
removeClassPermissions(p);
removePotionEffects(p);
if (settings.getBoolean("spectate-on-death", true)) {
movePlayerToSpec(p);
Messenger.tell(p, Msg.SPEC_FROM_ARENA);
Messenger.tell(p, Msg.MISC_MA_LEAVE_REMINDER);
} else {
restoreInvAndExp(p);
movePlayerToEntry(p);
discardPlayer(p);
}
p.updateInventory();
}
@Override
public Location getRespawnLocation(Player p) {
Location l = null;
if (settings.getBoolean("spectate-on-death", true)) {
l = region.getSpecWarp();
} else {
l = playerData.get(p).entry();
}
return l;
}
@Override
public void playerSpec(Player p, Location loc) {
storePlayerData(p, loc);
MAUtils.sitPets(p);
movePlayerToSpec(p);
Messenger.tell(p, Msg.SPEC_PLAYER_SPECTATE);
}
private void spawnPets() {
for (Player p : arenaPlayers) {
// Skip players who are either null or offline
if (p == null || !p.isOnline()) continue;
// Grab the inventory
PlayerInventory inv = p.getInventory();
if (inv == null) continue;
// Find the first slot containing bones
int bone = inv.first(Material.BONE);
if (bone == -1) continue;
// Get the amount of pets to spawn
int amount = inv.getItem(bone).getAmount();
// Spawn each pet
for (int i = 0; i < amount; i++) {
Wolf wolf = (Wolf) world.spawnEntity(p.getLocation(), EntityType.WOLF);
wolf.setTamed(true);
wolf.setOwner(p);
wolf.setHealth(wolf.getMaxHealth());
if (settings.getBoolean("hellhounds"))
wolf.setFireTicks(32768);
monsterManager.addPet(wolf);
}
// Remove the bones
inv.setItem(bone, null);
}
}
private void spawnMounts() {
for (Player p : arenaPlayers) {
// Skip players who are either null or offline
if (p == null || !p.isOnline()) continue;
// Grab the inventory
PlayerInventory inv = p.getInventory();
if (inv == null) continue;
// Find the first slot containing a haybale
int hay = inv.first(Material.HAY_BLOCK);
if (hay == -1) continue;
// Grab the amount and calculate the configuration
int amount = inv.getItem(hay).getAmount();
// Variant
Horse.Variant variant = Horse.Variant.HORSE;
switch (amount % 8) {
case 2: variant = Horse.Variant.DONKEY; break;
case 3: variant = Horse.Variant.MULE; break;
case 4: variant = Horse.Variant.SKELETON_HORSE; break;
case 5: variant = Horse.Variant.UNDEAD_HORSE; break;
default: break;
}
// Barding
Material barding = null;
switch ((amount >> 3) % 4) {
case 1: barding = Material.IRON_BARDING; break;
case 2: barding = Material.GOLD_BARDING; break;
case 3: barding = Material.DIAMOND_BARDING; break;
default: break;
}
// Spawn the horse, set its variant, tame it, etc.
Horse horse = (Horse) world.spawnEntity(p.getLocation(), EntityType.HORSE);
if (MobArena.random.nextInt(20) == 0) {
horse.setBaby();
} else {
horse.setAdult();
}
horse.setVariant(variant);
horse.setTamed(true);
horse.setOwner(p);
horse.setPassenger(p);
horse.setHealth(horse.getMaxHealth());
// Give it a saddle and possibly barding
horse.getInventory().setSaddle(new ItemStack(Material.SADDLE));
if (barding != null) {
horse.getInventory().setArmor(new ItemStack(barding));
}
// Add to monster manager
monsterManager.addMount(horse);
// Remove the hay
inv.setItem(hay, null);
}
}
private void removePotionEffects(Player p) {
for (PotionEffect effect : p.getActivePotionEffects()) {
p.removePotionEffect(effect.getType());
}
}
private void startSpawner() {
// Set the spawn flags to enable monster spawning.
world.setSpawnFlags(true, true);
//world.setDifficulty(Difficulty.NORMAL);
// Create a spawner if one doesn't exist, otherwise reset it
if (spawnThread == null) {
spawnThread = new MASpawnThread(plugin, this);
} else {
spawnThread.reset();
}
// Schedule it for the initial first wave delay.
scheduleTask(spawnThread, settings.getInt("first-wave-delay", 5) * 20);
// Schedule to enable PvP if pvp-enabled: true
scheduleTask(new Runnable() {
public void run() {
eventListener.pvpActivate();
}
}, settings.getInt("first-wave-delay", 5) * 20);
}
/**
* Schedule a Runnable to be executed after the given delay in
* server ticks. The method is used by the MASpawnThread to
* repeatedly spawn new mobs instead of a scheduled repeating
* tasks, as well as the sheep bouncer.
*/
@Override
public void scheduleTask(Runnable r, int delay) {
Bukkit.getScheduler().runTaskLater(plugin, r, delay);
}
private void stopSpawner() {
world.setSpawnFlags(allowMonsters, allowAnimals);
eventListener.pvpDeactivate();
//world.setDifficulty(spawnMonsters);
}
private void startBouncingSheep()
{
// Create a new bouncer if necessary.
if (sheepBouncer == null) {
sheepBouncer = new SheepBouncer(this);
}
// Start bouncing!
scheduleTask(sheepBouncer, settings.getInt("first-wave-delay", 5) * 20);
}
@Override
public void storePlayerData(Player p, Location loc)
{
plugin.getArenaMaster().addPlayer(p, this);
PlayerData mp = playerData.get(p);
// If there's no player stored, create a new one!
if (mp == null) {
if (region.getExitWarp() != null) loc = region.getExitWarp();
mp = new PlayerData(p, loc);
playerData.put(p, mp);
}
// At any rate, update the data.
mp.update();
// And update the inventory as well.
try {
inventoryManager.storeInv(p);
} catch (Exception e) {
e.printStackTrace();
Messenger.severe("Failed to store inventory for player " + p.getName() + "!");
}
}
@Override
public void storeContainerContents()
{
for (Location loc : region.getContainers()) {
BlockState state = world.getBlockAt(loc).getState();
if (state instanceof InventoryHolder) {
containables.add(new RepairableContainer(state, false));
}
}
}
@Override
public void restoreContainerContents()
{
for (Repairable r : containables) {
r.repair();
}
}
@Override
public void movePlayerToLobby(Player p)
{
specPlayers.remove(p); // If joining from spec area
lobbyPlayers.add(p);
p.teleport(region.getLobbyWarp());
p.setAllowFlight(false);
p.setFlying(false);
timeStrategy.setPlayerTime(p);
}
@Override
public void movePlayerToSpec(Player p)
{
specPlayers.add(p);
p.teleport(region.getSpecWarp());
timeStrategy.setPlayerTime(p);
}
@Override
public void movePlayerToEntry(Player p)
{
Location entry = playerData.get(p).entry();
if (entry == null || p.isDead()) return;
p.teleport(entry);
timeStrategy.resetPlayerTime(p);
p.setGameMode(playerData.get(p).getMode());
p.addPotionEffects(playerData.get(p).getPotionEffects());
}
private void restoreInvAndExp(Player p) {
inventoryManager.clearInventory(p);
try {
inventoryManager.restoreInv(p);
} catch (Exception e) {
e.printStackTrace();
Messenger.severe("Failed to restore inventory for player " + p.getName() + "!");
}
rewardManager.grantRewards(p);
// Try to prevent XP issues
if (lobbyPlayers.contains(p)
|| !settings.getBoolean("keep-exp")
|| settings.getBoolean("display-waves-as-level", false)
|| settings.getBoolean("display-timer-as-level", false)) {
playerData.get(p).restoreData();
}
else {
p.setFoodLevel(playerData.get(p).food());
}
}
@Override
public void discardPlayer(Player p)
{
plugin.getArenaMaster().removePlayer(p);
clearPlayer(p);
}
private void clearPlayer(Player p)
{
// Remove the player data completely.
PlayerData mp = playerData.remove(p);
// Health must be handled in a certain way because of Heroes
// Math.min to guard for ItemLoreStats weirdness
setHealth(p, Math.min(p.getMaxHealth(), mp.health()));
// Put out fire.
Delays.douse(plugin, p, 3);
// Remove pets.
monsterManager.removePets(p);
// readyPlayers before lobbyPlayers because of startArena sanity-checks
readyPlayers.remove(p);
specPlayers.remove(p);
arenaPlayers.remove(p);
lobbyPlayers.remove(p);
arenaPlayerMap.remove(p);
scoreboard.removePlayer(p);
}
private void setHealth(Player p, double health) {
p.setHealth(health);
}
@Override
public void repairBlocks()
{
while (!repairQueue.isEmpty())
repairQueue.poll().repair();
}
@Override
public void queueRepairable(Repairable r)
{
repairQueue.add(r);
}
/*////////////////////////////////////////////////////////////////////
//
// Items & Cleanup
//
////////////////////////////////////////////////////////////////////*/
@Override
public void assignClass(Player p, String className) {
ArenaPlayer arenaPlayer = arenaPlayerMap.get(p);
ArenaClass arenaClass = classes.get(className);
if (arenaPlayer == null || arenaClass == null) {
return;
}
inventoryManager.clearInventory(p);
arenaPlayer.setArenaClass(arenaClass);
arenaClass.grantItems(p);
PermissionAttachment pa = arenaClass.grantLobbyPermissions(plugin, p);
replacePermissions(p, pa);
autoReady(p);
}
@Override
public void assignClassGiveInv(Player p, String className, ItemStack[] contents) {
ArenaPlayer arenaPlayer = arenaPlayerMap.get(p);
ArenaClass arenaClass = classes.get(className);
if (arenaPlayer == null || arenaClass == null) {
return;
}
inventoryManager.clearInventory(p);
arenaPlayer.setArenaClass(arenaClass);
PlayerInventory inv = p.getInventory();
// Check the very last slot to see if it'll work as a helmet
int last = contents.length-1;
if (contents[last] != null) {
inv.setHelmet(contents[last]);
contents[last] = null;
}
// Check the remaining three of the four last slots for armor
for (int i = contents.length-1; i > contents.length-5; i--) {
if (contents[i] == null) continue;
ArmorType type = ArmorType.getType(contents[i]);
if (type == null || type == ArmorType.HELMET) continue;
switch (type) {
case CHESTPLATE: inv.setChestplate(contents[i]); break;
case LEGGINGS: inv.setLeggings(contents[i]); break;
case BOOTS: inv.setBoots(contents[i]); break;
default: break;
}
contents[i] = null;
}
// Check the remaining slots for weapons
if (arenaClass.hasUnbreakableWeapons()) {
for (ItemStack stack : contents) {
if (stack != null && arenaClass.isWeapon(stack)) {
stack.setDurability(Short.MIN_VALUE);
}
}
}
p.getInventory().setContents(contents);
PermissionAttachment pa = arenaClass.grantLobbyPermissions(plugin, p);
replacePermissions(p, pa);
autoReady(p);
}
private void replacePermissions(Player p, PermissionAttachment rep) {
PermissionAttachment old = attachments.get(p);
if (old != null) {
old.remove();
p.recalculatePermissions();
}
if (rep != null) {
attachments.put(p, rep);
p.recalculatePermissions();
}
}
private void autoReady(Player p) {
if (settings.getBoolean("auto-ready", false)) {
if (autoStartTimer.getRemaining() <= 0) {
playerReady(p);
} else {
readyPlayers.add(p);
}
}
}
@Override
public void addRandomPlayer(Player p) {
randoms.add(p);
}
@Override
public void assignRandomClass(Player p)
{
Random r = new Random();
List<String> classes = new LinkedList<String>(this.classes.keySet());
String className = classes.remove(r.nextInt(classes.size()));
while (!plugin.has(p, "mobarena.classes." + className))
{
if (classes.isEmpty())
{
Messenger.info("Player '" + p.getName() + "' has no class permissions!");
playerLeave(p);
return;
}
className = classes.remove(r.nextInt(classes.size()));
}
assignClass(p, className);
Messenger.tell(p, Msg.LOBBY_CLASS_PICKED, TextUtils.camelCase(className));
}
@Override
public void assignClassPermissions(Player p)
{
PermissionAttachment pa = arenaPlayerMap.get(p).getArenaClass().grantPermissions(plugin, p);
replacePermissions(p, pa);
}
@Override
public void removeClassPermissions(Player p)
{
PermissionAttachment pa = attachments.remove(p);
if (pa == null) return;
try {
p.removeAttachment(pa);
}
catch (Exception e) {
for (Entry<String,Boolean> entry : pa.getPermissions().entrySet()) {
String perm = entry.getKey() + ":" + entry.getValue();
String name = p.getName();
Messenger.warning("[PERM01] Failed to remove permission attachment '" + perm + "' from player '" + name
+ "'.\nThis should not be a big issue, but please verify that the player doesn't have any permissions they shouldn't have.");
}
}
p.recalculatePermissions();
}
@Override
public void addPermission(Player p, String perm, boolean value) {
PermissionAttachment pa = attachments.get(p);
if (pa == null) {
pa = p.addAttachment(plugin);
attachments.put(p, pa);
}
pa.setPermission(perm, value);
p.recalculatePermissions();
}
private void cleanup() {
removeMonsters();
removeBlocks();
removeEntities();
clearPlayers();
}
private void removeMonsters() {
monsterManager.clear();
}
private void removeBlocks() {
for (Block b : blocks) {
b.setTypeId(0);
}
blocks.clear();
}
private void removeEntities() {
List<Chunk> chunks = region.getChunks();
for (Chunk c : chunks) {
for (Entity e : c.getEntities()) {
if (e == null) {
continue;
}
switch (e.getType()) {
case DROPPED_ITEM:
case EXPERIENCE_ORB:
case ARROW:
case MINECART:
case BOAT:
e.remove();
}
}
}
}
private void clearPlayers() {
arenaPlayers.clear();
arenaPlayerMap.clear();
lobbyPlayers.clear();
readyPlayers.clear();
}
/*////////////////////////////////////////////////////////////////////
//
// Initialization & Checks
//
////////////////////////////////////////////////////////////////////*/
@Override
public void restoreRegion()
{
Collections.sort(repairables, new RepairableComparator());
for (Repairable r : repairables)
r.repair();
}
/*////////////////////////////////////////////////////////////////////
//
// Getters & Misc
//
////////////////////////////////////////////////////////////////////*/
@Override
public boolean inArena(Player p) {
return arenaPlayers.contains(p);
}
@Override
public boolean inLobby(Player p) {
return lobbyPlayers.contains(p);
}
@Override
public boolean inSpec(Player p) {
return specPlayers.contains(p);
}
@Override
public boolean isDead(Player p) {
return deadPlayers.contains(p);
}
@Override
public String configName()
{
return name;
}
@Override
public String arenaName()
{
return MAUtils.nameConfigToArena(name);
}
@Override
public MobArena getPlugin()
{
return plugin;
}
@Override
public Map<String,ArenaClass> getClasses()
{
return classes;
}
@Override
public int getPlayerCount()
{
return spawnThread.getPlayerCount();
}
@Override
public List<Player> getAllPlayers()
{
List<Player> result = new LinkedList<Player>();
result.addAll(arenaPlayers);
result.addAll(lobbyPlayers);
result.addAll(specPlayers);
return result;
}
@Override
public Collection<ArenaPlayer> getArenaPlayerSet()
{
return arenaPlayerMap.values();
}
@Override
public AutoStartTimer getAutoStartTimer() {
return autoStartTimer;
}
/*@Override
public List<ArenaPlayerStatistics> getArenaPlayerStatistics(Comparator<ArenaPlayerStatistics> comparator)
{
List<ArenaPlayerStatistics> list = new ArrayList<ArenaPlayerStatistics>();
for (ArenaPlayer ap : arenaPlayerMap.values())
list.add(ap.getStats());
Collections.sort(list, comparator);
return list;
}*/
@Override
public List<Player> getNonreadyPlayers()
{
List<Player> result = new LinkedList<Player>();
result.addAll(lobbyPlayers);
result.removeAll(readyPlayers);
return result;
}
@Override
public boolean canAfford(Player p) {
if (entryFee.isEmpty()) return true;
PlayerInventory inv = p.getInventory();
for (ItemStack stack : entryFee) {
// Economy money
if (stack.getTypeId() == MobArena.ECONOMY_MONEY_ID) {
if (!plugin.hasEnough(p, stack)) {
return false;
}
}
// Normal stack
else {
if (!inv.contains(stack.getType(), stack.getAmount())) {
return false;
}
}
}
return true;
}
@Override
public boolean takeFee(Player p) {
if (entryFee.isEmpty()) return true;
PlayerInventory inv = p.getInventory();
// Take some economy money
for (ItemStack stack : InventoryUtils.extractAll(MobArena.ECONOMY_MONEY_ID, entryFee)) {
plugin.takeMoney(p, stack);
}
// Take any other items
for (ItemStack fee : entryFee) {
if (fee.getTypeId() < 0) continue;
int remaining = fee.getAmount();
while (remaining > 0) {
int slot = inv.first(fee.getType());
if (slot < 0) break;
ItemStack item = inv.getItem(slot);
remaining -= item.getAmount();
if (remaining >= 0) {
inv.setItem(slot, null);
} else {
item.setAmount(-remaining);
inv.setItem(slot, item);
}
}
}
Messenger.tell(p, Msg.JOIN_FEE_PAID.format(MAUtils.listToString(entryFee, plugin)));
return true;
}
@Override
public boolean refund(Player p) {
if (entryFee.isEmpty()) return true;
if (!inLobby(p)) return false;
// Refund economy money
for (ItemStack stack : InventoryUtils.extractAll(MobArena.ECONOMY_MONEY_ID, entryFee)) {
plugin.giveMoney(p, stack);
}
// Refund other items.
for (ItemStack stack : entryFee) {
if (stack.getTypeId() > 0) {
p.getInventory().addItem(stack);
}
}
return true;
}
@Override
public boolean canJoin(Player p) {
if (!enabled)
Messenger.tell(p, Msg.JOIN_ARENA_NOT_ENABLED);
else if (!region.isSetup() || waveManager.getRecurrentWaves().isEmpty())
Messenger.tell(p, Msg.JOIN_ARENA_NOT_SETUP);
else if (edit)
Messenger.tell(p, Msg.JOIN_ARENA_EDIT_MODE);
else if (arenaPlayers.contains(p) || lobbyPlayers.contains(p))
Messenger.tell(p, Msg.JOIN_ALREADY_PLAYING);
else if (running)
Messenger.tell(p, Msg.JOIN_ARENA_IS_RUNNING);
else if (!plugin.has(p, "mobarena.arenas." + configName()))
Messenger.tell(p, Msg.JOIN_ARENA_PERMISSION);
else if (getMaxPlayers() > 0 && lobbyPlayers.size() >= getMaxPlayers())
Messenger.tell(p, Msg.JOIN_PLAYER_LIMIT_REACHED);
else if (getJoinDistance() > 0 && !region.contains(p.getLocation(), getJoinDistance()))
Messenger.tell(p, Msg.JOIN_TOO_FAR);
else if (settings.getBoolean("require-empty-inv-join", true) && !InventoryManager.hasEmptyInventory(p))
Messenger.tell(p, Msg.JOIN_EMPTY_INV);
else if (!canAfford(p))
Messenger.tell(p, Msg.JOIN_FEE_REQUIRED, MAUtils.listToString(entryFee, plugin));
else return true;
return false;
}
@Override
public boolean canSpec(Player p) {
if (!enabled)
Messenger.tell(p, Msg.JOIN_ARENA_NOT_ENABLED);
else if (!region.isSetup())
Messenger.tell(p, Msg.JOIN_ARENA_NOT_SETUP);
else if (edit)
Messenger.tell(p, Msg.JOIN_ARENA_EDIT_MODE);
else if (arenaPlayers.contains(p) || lobbyPlayers.contains(p))
Messenger.tell(p, Msg.SPEC_ALREADY_PLAYING);
else if (settings.getBoolean("require-empty-inv-spec", true) && !InventoryManager.hasEmptyInventory(p))
Messenger.tell(p, Msg.SPEC_EMPTY_INV);
else if (getJoinDistance() > 0 && !region.contains(p.getLocation(), getJoinDistance()))
Messenger.tell(p, Msg.JOIN_TOO_FAR);
else return true;
return false;
}
@Override
public boolean hasIsolatedChat() {
return isolatedChat;
}
@Override
public Player getLastPlayerStanding() {
return lastStanding;
}
/**
* The "perfect equals method" cf. "Object-Oriented Design and Patterns"
* by Cay S. Horstmann.
*/
@Override
public boolean equals(Object other) {
if (this == other) return true;
if (other == null) return false;
if (getClass() != other.getClass()) return false;
// Arenas must have different names.
if (other instanceof ArenaImpl && ((ArenaImpl)other).name.equals(name))
return true;
return false;
}
@Override
public String toString() {
return ((enabled && region.isSetup()) ? ChatColor.GREEN : ChatColor.GRAY) + configName();
}
}