package org.mctourney.autoreferee; import java.lang.management.ManagementFactory; import java.util.List; import java.util.Map; import java.util.Set; import com.google.common.collect.Maps; import org.apache.commons.lang.StringUtils; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.entity.Player; import org.bukkit.event.entity.EntityDamageEvent; import org.bukkit.event.entity.PlayerDeathEvent; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.PlayerInventory; import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.potion.Potion; import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionType; import org.mctourney.autoreferee.AutoRefMatch.RespawnMode; import org.mctourney.autoreferee.AutoRefMatch.TranscriptEvent; import org.mctourney.autoreferee.goals.AutoRefGoal; import org.mctourney.autoreferee.goals.BlockGoal; import org.mctourney.autoreferee.listeners.GoalsInventorySnapshot; import org.mctourney.autoreferee.util.AchievementPoints; import org.mctourney.autoreferee.util.ArmorPoints; import org.mctourney.autoreferee.util.BlockData; import org.mctourney.autoreferee.util.LocationUtil; import org.mctourney.autoreferee.util.Metadatable; import org.mctourney.autoreferee.util.PlayerKit; import org.mctourney.autoreferee.util.PlayerUtil; import org.apache.commons.collections.map.DefaultedMap; import com.google.common.collect.Lists; import com.google.common.collect.MapDifference; import com.google.common.collect.Sets; /** * Represents a player participating in a match. * * @author authorblues */ public class AutoRefPlayer implements Metadatable, Comparable<AutoRefPlayer> { public class JSONPlayerData { public String name, team; public int kills, assists, deaths; public int streak; public int arrowfired, arrowhit; public int teamkills; } public static final EntityDamageEvent VOID_DEATH = new EntityDamageEvent(null, EntityDamageEvent.DamageCause.VOID, 0); private static final int MIN_KILLSTREAK = 5; private static final int MIN_DOMINATE = 3; private static final long DAMAGE_COOLDOWN_TICKS = 3 * 20L; public static final int SLOT_HELMET = 0; public static final int SLOT_CHEST = 1; public static final int SLOT_LEGS = 2; public static final int SLOT_FEET = 3; // stored references private String pname = null; private AutoRefTeam team; /** * Retrieves a Player object corresponding to this player. Player objects are not * cached, since they can log out and log in, so this method will always require * a lookup. * * @return a Player object */ public Player getPlayer() { return AutoReferee.getInstance().getServer().getPlayer(pname); } /** * Checks if the player is online. * * @return true if player is online, otherwise false */ public boolean isOnline() { return AutoReferee.getInstance().getServer().getPlayer(pname) != null && getPlayer().getWorld() == getMatch().getWorld(); } /** * Checks if the player is dead. * * @return true if player is dead, otherwise false */ public boolean isDead() { return currentHealth <= 0; } /** * Gets raw player name. * * @return player name */ public String getName() { return pname; } @Override public String toString() { return "AutoRefPlayer[" + getName() + ", team=" + getTeam() + "]"; } /** * Sets raw player name. */ public void setName(String name) { this.pname = name; } protected int nameSearch(String name) { if (!pname.toLowerCase().startsWith(name.toLowerCase())) return Integer.MAX_VALUE; return pname.length() - name.length(); } /** * Gets the team this player is on. * * @return team object */ public AutoRefTeam getTeam() { return team; } /** * Sets the team this player is on. */ public void setTeam(AutoRefTeam team) { this.team = team; } protected Map<String, Object> metadata = Maps.newHashMap(); public void addMetadata(String key, Object value) { this.metadata.put(key, value); } public Object getMetadata(String key) { return this.metadata.get(key); } public boolean hasMetadata(String key) { return this.metadata.containsKey(key); } public Object removeMetadata(String key) { return this.metadata.remove(key); } public void clearMetadata() { this.metadata.clear(); } /** * Gets the match this player is in. * * @return match object */ public AutoRefMatch getMatch() { return this.team == null ? null : this.team.getMatch(); } /** * Gets the URL for this player's cape. * * @return cape URL */ public String getCape() { String cape = getMatch().playerCapes.get(this.getName()); return cape == null ? "" : cape.replaceFirst("^https?://", ""); } private int shotsFired = 0, shotsHit = 0; /** * Resets arrow fire statistics. */ public void resetArrowFire() { this.shotsFired = this.shotsHit = 0; } /** * Increments the recorded number of arrows fired. */ public void incrementShotsFired() { ++this.shotsFired; this.sendAccuracyUpdate(); } /** * Increments the recorded number of this player's arrows that hit a target. */ public void incrementShotsHit() { ++this.shotsHit; this.sendAccuracyUpdate(); this.addPoints(AchievementPoints.ARROW_HIT); } // number of times this player has killed other players @SuppressWarnings("unchecked") private Map<AutoRefPlayer, Integer> kills = new DefaultedMap(0); private int totalKills = 0; private int teamKills = 0; // number of times this player has assisted in killing other players private int totalAssists = 0; private double furthestShot = 0.0; /** * Sets the furthest accurate bow shot. */ public void setFurthestShot(double distance) { if (distance > furthestShot) furthestShot = distance; } private int livesRemaining = -1; /** * Checks if this player has lives remaining. * * @return whether player has lives remaining, true if infinite lives */ public boolean hasLives() { return getMatch().getRespawnMode() != RespawnMode.DISALLOW && livesRemaining != 0; } /** * Sets the number of lives remaining for this player. * * @param lives number of lives remaining before player is eliminated */ public void setLivesRemaining(int lives) { this.livesRemaining = lives; } /** * Gets the number of times this player has killed a specific player. * * @param apl target player * @return number of kills */ public int getKills(AutoRefPlayer apl) { return this.kills.get(apl); } public int getAssists() { return this.totalAssists; } /** * Gets the total number of players killed by this player. * * @return number of kills */ public int getKills() { return totalKills - teamKills; } @SuppressWarnings("unchecked") private Map<AutoRefPlayer, Long> lastPlayerDamageMillis = new DefaultedMap((Long) 0L); private static final long KILLER_MS = 1000L * 3; private static final long ASSIST_MS = 1000L * 5; /** * Gets the player responsible for killing this player. * * @return killer */ public AutoRefPlayer getKiller() { // if the player is not dead, no killer if (this.isOnline() && !getPlayer().isDead()) return null; // the cutoff before which the player will not be credited with the kill long threshold = ManagementFactory.getRuntimeMXBean().getUptime() - KILLER_MS; AutoRefPlayer killer = null; for (Map.Entry<AutoRefPlayer, Long> e : lastPlayerDamageMillis.entrySet()) if (e.getValue() > threshold) { threshold = e.getValue(); killer = e.getKey(); } return killer; } /** * Gets the players responsible for killing this player. * * @return killers */ public Set<AutoRefPlayer> getKillAssists() { // if the player is not dead, no killer if (this.isOnline() && !getPlayer().isDead()) return Sets.newHashSet(); // the cutoff before which the player will not be credited with the kill long threshold = ManagementFactory.getRuntimeMXBean().getUptime() - ASSIST_MS; Set<AutoRefPlayer> killers = Sets.newHashSet(); for (Map.Entry<AutoRefPlayer, Long> e : lastPlayerDamageMillis.entrySet()) if (e.getValue() > threshold) killers.add(e.getKey()); return killers; } // number of times player has died and damage taken @SuppressWarnings("unchecked") private Map<AutoRefPlayer, Integer> deaths = new DefaultedMap((Integer) 0); private int totalDeaths = 0; /** * Gets the number of times this player has been killed by a specific player. * * @param apl target player * @return number of deaths */ public int getDeaths(AutoRefPlayer apl) { return this.deaths.get(apl); } /** * Gets the number of times this player has died. * * @return number of deaths */ public int getDeathCount() { return totalDeaths; } // tracking objective items private GoalsInventorySnapshot carrying; private int currentHealth = 20; private int currentArmor = 0; /** * Gets the objectives carried by this player. * * @return set of objectives */ public GoalsInventorySnapshot getCarrying() { Player p = getPlayer(); AutoRefTeam t = getTeam(); if (p == null || t == null) { carrying = new GoalsInventorySnapshot(); } else { carrying = new GoalsInventorySnapshot(p.getInventory(), t.getObjectives()); } return carrying; } private GoalsInventorySnapshot beforeOpeningInventory; private String inventoryDescription; private Location inventoryLocation; public GoalsInventorySnapshot getBeforeOpeningInventorySnapshot() { return beforeOpeningInventory; } public boolean hasActiveInventoryInfo() { return beforeOpeningInventory != null; } public String getInventoryDescription() { return ChatColor.GOLD + StringUtils.capitalize(inventoryDescription.toLowerCase()); } public Location getInventoryLocation() { return inventoryLocation; } public void setActiveInventoryInfo(GoalsInventorySnapshot mySnap, Location loc, String description) { beforeOpeningInventory = mySnap; inventoryDescription = description; inventoryLocation = loc; } public void clearActiveInventoryInfo() { beforeOpeningInventory = null; inventoryLocation = null; inventoryDescription = null; } // streak information - kill streak, domination, revenge private int totalStreak = 0; @SuppressWarnings("unchecked") private final Map<AutoRefPlayer, Integer> playerStreak = new DefaultedMap(0); /** * Gets the number of times this player has consecutively killed another. This * value will reset itself to zero once this player is killed by the target player. * * @param apl target player * @return number of consecutive deaths */ public int getStreak(AutoRefPlayer apl) { return this.playerStreak.get(apl); } /** * Gets the number of times this player has died in this life. * * @return number of consecutive deaths */ public int getStreak() { return totalStreak; } // last damage tick private long lastDamageMillis; /** * Gets the number of world ticks since this player was last damaged. * * @return ticks since last damage */ public long damageCooldownLength() { return ManagementFactory.getRuntimeMXBean().getUptime() - lastDamageMillis; } /** * Checks if the player has been damaged recently. * * @return true if player was recently damaged, otherwise false */ public boolean wasDamagedRecently() { return damageCooldownLength() < DAMAGE_COOLDOWN_TICKS; } // amount of time the saved inventory is valid private static final long SAVED_INVENTORY_LIFESPAN = 1000L * 60 * 3; private Inventory lastInventoryView = null; private long lastInventoryViewSavedMillis = -1L; private boolean isSavedInventoryStale() { return ManagementFactory.getRuntimeMXBean().getUptime() > lastInventoryViewSavedMillis + SAVED_INVENTORY_LIFESPAN; } // TODO never used // private int points = 0; /** * Adds achievement points for this player. * * TODO value is never read */ public void addPoints(AchievementPoints ach, int count) { // if (ach == null) return; // this.addPoints(ach.getValue() * count); } /** * Adds achievement points for this player. */ public void addPoints(AchievementPoints ach) // { this.addPoints(ach, 1); } { } /** * Adds achievement points for this player. This method can be used to add * custom point values, if necessary. */ public void addPoints(int points) // { this.points += points; } { } /** * Gets the number of achievement points this player has earned. * * @return achievement points */ // public int getPoints() // { return points; } /** * Gets the normalized number of achievement points this player has earned. * * @return normalized achievement points */ // public int getDisplayPoints() // { return AchievementPoints.ticksToPoints(points); } /** * Returns whether or not the player has the AutoReferee client mod installed. * * @return true if using client mod, otherwise false */ public boolean hasClientMod() { return PlayerUtil.hasClientMod(getPlayer()); } /** * Gets location of this player's bed. */ public Location getBedLocation() { Player pl = getPlayer(); return pl == null ? null : pl.getBedSpawnLocation(); } /** * Gets whether or not the player has a bed spawn set. */ public boolean hasBed() { return this.getBedLocation() != null; } private Location lastDeathLocation = null; /** * Gets location of this player's most recent death. */ public Location getLastDeathLocation() { return lastDeathLocation; } public void setLastDeathLocation(Location loc) { lastDeathLocation = loc; this.getMatch().setLastDeathLocation(loc); } private Location lastLogoutLocation = null; /** * Gets location of this player's most recent logout. */ public Location getLastLogoutLocation() { return lastLogoutLocation; } public void setLastLogoutLocation(Location loc) { lastLogoutLocation = loc; this.getMatch().setLastLogoutLocation(loc); } private Location lastTeleportLocation = null; /** * Gets location of this player's most recent teleport. */ public Location getLastTeleportLocation() { return lastTeleportLocation; } public void setLastTeleportLocation(Location loc) { lastTeleportLocation = loc; this.getMatch().setLastTeleportLocation(loc); } /** * Gets this player's current location. * * @return location of player if logged in, otherwise location of last logout */ public Location getLocation() { Player pl = getPlayer(); if (pl != null) return pl.getLocation(); return lastLogoutLocation; } /** * Creates a player object for the given player name. * * @param name player name * @param team player team, or null if no team */ @SuppressWarnings("unchecked") public AutoRefPlayer(String name, AutoRefTeam team) { // accuracy information this.resetArrowFire(); // save the player and team as references this.setName(name); this.setTeam(team); } /** * Creates a player object for the given player. * * @param player player * @param team player team, or null if no team */ public AutoRefPlayer(Player player, AutoRefTeam team) { this(player.getName(), team); // setup base health and armor level this.currentHealth = (int) player.getHealth(); this.currentArmor = ArmorPoints.fromPlayerInventory(player.getInventory()); } /** * Creates a player object for the given player. * * @param player player */ public AutoRefPlayer(Player player) { this(player, AutoReferee.getInstance().getTeam(player)); } @Override public int hashCode() { return getName().hashCode(); } @Override public boolean equals(Object o) { return o instanceof AutoRefPlayer && getName().equals(((AutoRefPlayer) o).getName()); } @Override public int compareTo(AutoRefPlayer other) { if (other == null) return 1; return this.pname.compareTo(other.pname); } /** * Gets name of this player, colored with team colors. * * @return colored name */ public String getDisplayName() { if (getTeam() == null) return getName(); return getTeam().getColor() + getName() + ChatColor.RESET; } /** * Kills the player. If the match has not yet started, the player * will be teleported back to spawn. * * @param cause reported cause of death, or null if no cause * @param cleardrops clear contents of inventory on death */ public void die(EntityDamageEvent cause, boolean cleardrops) { Player player = getPlayer(); if (player == null || player.isDead()) return; // if the inventory needs to be cleared, clear it if (cleardrops) this.clearInventory(); // "die" when the match isn't in progress just means a teleport if (!getMatch().getCurrentState().inProgress()) { player.teleport(getMatch().getPlayerSpawn(player)); player.setFallDistance(0.0f); } else { // if a cause of death is specified, set it if (cause != null) player.setLastDamageCause(cause); // kill the player player.setHealth(0); } } private boolean godmode = false; public void setGodMode(boolean godmode) { this.godmode = godmode; } public boolean isGodMode() { return this.godmode && getMatch().isPracticeMode(); } private boolean active = false; public boolean isActive() { return active; } public void setActive() { active = true; } /** * Clears the contents of this player's inventory. */ public void clearInventory() { Player p = getPlayer(); if (p != null) PlayerUtil.clearInventory(p); } public void respawn() { this.setExitLocation(null); this.active = false; } /** * Heals this player, including health, hunger, saturation, and exhaustion. */ public void heal() { Player p = getPlayer(); if (p != null) PlayerUtil.restore(p); } public void reset() { Player p = getPlayer(); if (p != null) PlayerUtil.reset(p); if (getTeam() != null) { PlayerKit kit = getTeam().getKit(); if (kit != null) kit.giveTo(this); } } public void registerDamage(EntityDamageEvent e, Player damager) { // sanity check... if (e.getEntity() != getPlayer()) return; // reset the damage tick lastDamageMillis = ManagementFactory.getRuntimeMXBean().getUptime(); // if the damage was caused by a player, set their last damage tick AutoRefPlayer apl = getMatch().getPlayer(damager); if (apl != null) lastPlayerDamageMillis.put(apl, lastDamageMillis); } // register that we just died public void registerDeath(PlayerDeathEvent e, Location from) { // sanity check... if (e.getEntity() != getPlayer()) return; try{ this.saveInventoryView(); } catch (IllegalArgumentException ex){ //Print a stack trace if inventory sizes change in future updates. ex.printStackTrace(); } // if this player has a number of lives, reduce by one if (hasLives()) --livesRemaining; AutoRefMatch match = getMatch(); AutoRefPlayer killer = match.getPlayer(e.getEntity().getKiller()); deaths.put(killer, getDeaths(killer) + 1); totalDeaths++; for (AutoRefPlayer apl : this.getKillAssists()) if (apl != killer && apl.getTeam() != match.getPlayerTeam(e.getEntity())) ++apl.totalAssists; Location loc = e.getEntity().getLocation(); if (getExitLocation() != null) loc = getExitLocation(); match.messageReferees("player", getName(), "deathpos", LocationUtil.toBlockCoords(loc)); match.messageReferees("player", getName(), "deaths", Integer.toString(totalDeaths)); TranscriptEvent entry = new TranscriptEvent(match, TranscriptEvent.EventType.PLAYER_DEATH, e.getDeathMessage(), loc, this, killer); match.addEvent(entry); String m = entry.getColoredMessage(); for (Player pl : match.getWorld().getPlayers()) pl.sendMessage(pl != e.getEntity().getKiller() || from == null ? m : String.format("%s " + ChatColor.DARK_GRAY + "(@ %.2f)", m, getLocation().distance(from))); this.setLastDeathLocation(loc); this.addPoints(AchievementPoints.DEATH); // reset total kill streak this.resetKillStreak(); match.messageReferees("player", getName(), "streak", Integer.toString(totalStreak)); } /** * Resets this player's killstreak. */ public void resetKillStreak() { // if it meets the requirements, report it if (totalStreak >= MIN_KILLSTREAK) getMatch().addEvent(new TranscriptEvent(getMatch(), TranscriptEvent.EventType.PLAYER_STREAK, String.format("%s had a %d-kill streak!", this.getName(), totalStreak), null, this)); // reset to zero this.totalStreak = 0; } // register that we killed the Player who fired this event public void registerKill(PlayerDeathEvent e) { // get the name of the player who died, record one kill against them AutoRefPlayer apl = getMatch().getPlayer(e.getEntity()); kills.put(apl, 1 + kills.get(apl)); ++totalKills; // if the player is on our team, register a team kill if (apl.getTeam() == this.getTeam()) ++teamKills; AutoRefMatch match = getMatch(); Location loc = e.getEntity().getLocation(); // one more kill for kill streak ++totalStreak; if (totalStreak >= MIN_KILLSTREAK) match.messageReferees("player", getName(), "streak", Integer.toString(totalStreak)); match.messageReferees("player", getName(), "kills", Integer.toString(totalKills)); if (getStreak(apl) + 1 == MIN_DOMINATE) { match.messageReferees("player", getName(), "dominate", apl.getName()); match.addEvent(new TranscriptEvent(match, TranscriptEvent.EventType.PLAYER_DOMINATE, String.format("%s is dominating %s", this.getName(), apl.getName()), apl.getLocation(), apl, this)); } if (apl.isDominating(this)) { match.messageReferees("player", getName(), "revenge", apl.getName()); match.addEvent(new TranscriptEvent(match, TranscriptEvent.EventType.PLAYER_REVENGE, String.format("%s got revenge on %s", this.getName(), apl.getName()), loc, this, apl)); this.addPoints(AchievementPoints.REVENGE); } // reset player streaks playerStreak.put(apl, getStreak(apl) + 1); apl.playerStreak.put(this, 0); } /** * Checks if this player is dominating a specified player. * * @return true if dominating, otherwise false */ public boolean isDominating(AutoRefPlayer apl) { return getStreak(apl) >= MIN_DOMINATE; } /** * Checks if this player is in the correct world. * * @return true if player is in correct world, otherwise false */ public boolean isPresent() { return getPlayer().getWorld() == getMatch().getWorld(); } /** * Gets the kill-death difference. * * @return difference of the kills and deaths */ public int getKDD() { return totalKills - totalDeaths; } public void sendAccuracyUpdate() { for (Player ref : getMatch().getReferees(false)) sendAccuracyUpdate(ref); } public void sendAccuracyUpdate(Player ref) { String acc = Integer.toString(shotsFired == 0 ? 0 : (100 * shotsHit / shotsFired)); AutoRefMatch.messageReferee(ref, "player", getName(), "accuracy", acc); } /** * Gets a string representation of this player's accuracy. Includes number of shots * fired and the number of hits. */ public String getExtendedAccuracyInfo() { return String.format("%s (%d/%d)", (shotsFired == 0 ? "N/A" : (Integer.toString(100 * shotsHit / shotsFired) + "%")), shotsHit, shotsFired); } private Location exitLocation = null; /** * Gets the location where this player left their lane. * * @return exit location */ public Location getExitLocation() { return exitLocation; } public void setExitLocation(Location loc) { this.exitLocation = loc; } public boolean isInsideLane() { return getExitLocation() == null; } public void updateCarrying() { Player player = getPlayer(); if (player == null) return; if (getTeam() != null) { GoalsInventorySnapshot oldCarrying = carrying; carrying = null; // invalidate old value carrying = getCarrying(); if (oldCarrying == null) { return; } MapDifference<BlockData, Integer> diff = oldCarrying.getDiff(carrying); if (!diff.areEqual()) { for (BlockGoal goal : getTeam().getTeamGoals(BlockGoal.class)) if (goal.getItemStatus() == AutoRefGoal.ItemStatus.NONE && carrying.containsKey(goal.getItem())) { // generate a transcript event for being the first String m = String.format("%s is carrying the first %s!", getDisplayName(), goal.getItem().getDisplayName()); getMatch().addEvent(new TranscriptEvent(getMatch(), TranscriptEvent.EventType.OBJECTIVE_FOUND, m, getLocation(), this, goal.getItem())); this.addPoints(AchievementPoints.OBJECTIVE_FOUND); // store the player's location as the last objective location getTeam().setLastObjectiveLocation(getLocation()); } getTeam().updateCarrying(this, oldCarrying, carrying); } } } public void updateHealthArmor() { Player player = this.getPlayer(); if (player == null) return; int newHealth = (int) Math.max(0, player.getHealth()); int newArmor = ArmorPoints.fromPlayerInventory(player.getInventory()); if (getTeam() != null) getTeam().updateHealthArmor(this, currentHealth, currentArmor, newHealth, newArmor); currentHealth = newHealth; currentArmor = newArmor; } private void saveInventoryView() { String invname = this.getDisplayName() + " @ " + getMatch().getTimestamp(); this.lastInventoryView = getInventoryView(invname); this.lastInventoryViewSavedMillis = ManagementFactory.getRuntimeMXBean().getUptime(); } /** * Gets the last saved copy of this player's inventory. If the inventory view * is older than a certain age, this method returns null * * @return inventory view, or null */ public Inventory getLastInventoryView() { return isSavedInventoryStale() ? null : this.lastInventoryView; } /** * Gets a copy of this player's current inventory. This includes an extra row * for armor and health/hunger information. * * @return inventory view */ public Inventory getInventoryView() { return getInventoryView(this.getDisplayName() + "'s Inventory"); } /** * Gets a copy of this player's current inventory. This includes an extra row * for armor and health/hunger information. * * @return inventory view */ public Inventory getInventoryView(String name) { Player player = this.getPlayer(); if (player == null) return null; PlayerInventory pInventory = player.getInventory(); String inventoryName = name.length() > 32 ? name.substring(0, 32) : name; // Player inventory grew by a slot in 1.9 // This code ensures that no matter how many slots we have, we're always a multiple of 9 int displayInventorySize = pInventory.getSize() + 9 + (9 - (pInventory.getSize() % 9)); Inventory inventoryView = Bukkit.getServer().createInventory(null, displayInventorySize, inventoryName); ItemStack[] oldContents = pInventory.getContents(); ItemStack[] newContents = inventoryView.getContents(); newContents[oldContents.length + SLOT_HELMET] = copyItem(pInventory.getHelmet()); newContents[oldContents.length + SLOT_CHEST] = copyItem(pInventory.getChestplate()); newContents[oldContents.length + SLOT_LEGS] = copyItem(pInventory.getLeggings()); newContents[oldContents.length + SLOT_FEET] = copyItem(pInventory.getBoots()); // SLOT 6: ACTIVE POTION EFFECTS if (player.getActivePotionEffects().size() > 0) { ItemStack potion = new Potion(PotionType.POISON).toItemStack(1); ItemMeta meta = potion.getItemMeta(); List<String> effects = Lists.newLinkedList(); for (PotionEffect effect : player.getActivePotionEffects()) effects.add(ChatColor.RESET + "" + ChatColor.GRAY + PlayerUtil.getStatusEffectName(effect)); meta.setDisplayName(ChatColor.BLUE + "" + ChatColor.ITALIC + "Status Effects"); meta.setLore(effects); potion.setItemMeta(meta); newContents[oldContents.length + 5] = potion; } // SLOT 7: CURRENT PLAYER LEVEL if (player.getLevel() > 0) { ItemStack level = new ItemStack(Material.EXP_BOTTLE, player.getLevel()); ItemMeta meta = level.getItemMeta(); meta.setDisplayName(ChatColor.GREEN + "" + ChatColor.ITALIC + "Player XP Level"); meta.setLore(Lists.newArrayList(ChatColor.GRAY + "" + ChatColor.ITALIC + String.format("%d %s", player.getLevel(), player.getLevel() == 1 ? "level" : "levels"))); level.setItemMeta(meta); newContents[oldContents.length + 6] = level; } // SLOT 8: PLAYER HEALTH { ItemStack health = new ItemStack(Material.APPLE, (int) player.getHealth()); ItemMeta meta = health.getItemMeta(); meta.setDisplayName(ChatColor.RED + "" + ChatColor.ITALIC + "Player Health"); meta.setLore(Lists.newArrayList(ChatColor.GRAY + "" + ChatColor.ITALIC + String.format("%2.1f hearts", player.getHealth() / 2.0))); health.setItemMeta(meta); newContents[oldContents.length + 7] = health; } // SLOT 9: PLAYER HUNGER { ItemStack hunger = new ItemStack(Material.COOKED_BEEF, player.getFoodLevel()); ItemMeta meta = hunger.getItemMeta(); meta.setDisplayName(ChatColor.GOLD + "" + ChatColor.ITALIC + "Player Hunger"); meta.setLore(Lists.newArrayList(ChatColor.GRAY + "" + ChatColor.ITALIC + String.format("%2.1f food", player.getFoodLevel() / 2.0))); hunger.setItemMeta(meta); newContents[oldContents.length + 8] = hunger; } for (int i = 0; i < oldContents.length; ++i) newContents[i] = copyItem(oldContents[i]); inventoryView.setContents(newContents); return inventoryView; } private ItemStack copyItem(ItemStack item) { return item == null ? null : item.clone(); } private boolean showInventory(Player pl, Inventory v) { AutoRefMatch match = AutoReferee.getInstance().getMatch(pl.getWorld()); if (match == null || !match.isSpectator(pl) || !match.getSpectator(pl).canViewInventory()) return false; // show the requested inventory view if (v != null) pl.openInventory(v); return v != null; } /** * Shows this player's inventory to the specified player. * * @param old show saved inventory if true, otherwise show current inventory * @return true if the inventory can be shown, otherwise false */ public boolean showInventory(Player player, boolean old) { Inventory which = old ? this.getLastInventoryView() : this.getInventoryView(); return this.showInventory(player, which); } /** * Shows this player's inventory to the specified player. * * @return true if the inventory can be shown, otherwise false */ public boolean showInventory(Player player) { return this.showInventory(player, this.getInventoryView()); } /** * Shows this player's last saved inventory to the specified player. * * @return true if the inventory can be shown, otherwise false */ public boolean showSavedInventory(Player player) { return this.showInventory(player, this.getLastInventoryView()); } public JSONPlayerData getJSONPlayer() { JSONPlayerData data = new JSONPlayerData(); data.name = this.getName(); data.team = this.getTeam().getDefaultName(); data.kills = this.getKills(); data.assists = this.getAssists(); data.deaths = this.getDeathCount(); data.teamkills = this.teamKills; data.assists = this.getAssists(); data.arrowfired = this.shotsFired; data.arrowhit = this.shotsHit; return data; } }