diff --git a/Level/config.yml b/Level/config.yml
new file mode 100644
index 0000000..15b3707
--- /dev/null
+++ b/Level/config.yml
@@ -0,0 +1,305 @@
+# This file lists the values for various blocks that are used to calculate the
+# island level. Level = total of all blocks in island boundary / 100.
+# Players with the permission askyblock.island.multiplier.# will have their blocks
+# multiplied in value by that amount.
+
+# Underwater block multiplier
+# If blocks are below sea-level, they can have a higher value. e.g. 2x
+# Promotes under-water development if there is a sea. Value can be fractional.
+underwater: 1.0
+
+# Value of one island level. Default 100. Minimum value is 1.
+levelcost: 100
+
+# Cooldown between level requests in seconds
+levelwait: 60
+
+# Death penalty
+# How many block values a player will lose per death.
+# Default value of 100 means that for every death, the player will lose 1 level (if levelcost is 100)
+# Set to zero to not use this feature
+deathpenalty: 100
+# Sum team deaths - if true, all the teams deaths are summed
+# If false, only the leader's deaths counts
+sumteamdeaths: false
+# Max deaths
+# If player dies more than this, it doesn't count anymore
+# Stops players from getting into an impossible situation
+maxdeaths: 10
+# Reset deaths on island reset
+islandresetdeathreset: true
+# Reset deaths on team join
+teamjoindeathreset: true
+
+
+# This section lists the limits for any particular block. Blocks over this amount
+# are not counted.
+# Format:
+# MATERIAL: limit or MATERIAL:DATA: limit.
+# If DATA is used, there MUST be a corresponding block:data value in the blocks list.
+# For example, if you limit Jungle Logs LOG:3, then there must be a value for LOG:3
+# in the blocks section. If there is not, then LOG:3 would have no value.
+limits:
+ COBBLESTONE: 10000
+ NETHERRACK: 1000
+ #LOG:3: 10
+
+# This section lists the value of a block. Value must be an integer.
+# Any blocks not listed will have a value of zero.
+# Format is MATERIAL: value or MATERIAL:DATA: value.
+
+blocks:
+ ACACIA_DOOR: 1
+ ACACIA_STAIRS: 1
+ ACACIA_FENCE: 1
+ ACACIA_FENCE_GATE: 1
+ ACACIA_STAIRS: 2
+ ACTIVATOR_RAIL: 10
+ AIR: 0
+ ANVIL: 10
+ ARMOR_STAND: 2
+ BANNER: 2
+ BEACON: 100
+ BED_BLOCK: 1
+ BEDROCK: 0
+ BEETROOT_BLOCK: 1
+ BIRCH_DOOR: 1
+ BIRCH_FENCE: 1
+ BIRCH_FENCE_GATE: 1
+ BIRCH_WOOD_STAIRS: 1
+ BLACK_GLAZED_TERRACOTTA: 1
+ BLACK_SHULKER_BOX: 1
+ BLUE_GLAZED_TERRACOTTA: 1
+ BLUE_SHULKER_BOX: 1
+ BOAT: 2
+ BOAT_ACACIA: 2
+ BOAT_BIRCH: 2
+ BOAT_DARK_OAK: 2
+ BOAT_JUNGLE: 2
+ BOAT_SPRUCE: 2
+ BONE_BLOCK: 1
+ BOOKSHELF: 5
+ BREWING_STAND: 20
+ BRICK: 5
+ BRICK_STAIRS: 5
+ BROWN_GLAZED_TERRACOTTA: 1
+ BROWN_SHULKER_BOX: 1
+ BURNING_FURNACE: 10
+ CACTUS: 1
+ CAKE_BLOCK: 1
+ CARPET: 1
+ CAULDRON: 10
+ CHEST: 2
+ CHORUS_FLOWER: 1
+ CHORUS_PLANT: 1
+ CLAY: 2
+ COAL_BLOCK: 9
+ COAL_ORE: 0
+ COBBLE_WALL: 1
+ COBBLESTONE: 1
+ COBBLESTONE_STAIRS: 1
+ COCOA: 1
+ CONCRETE: 1
+ CONCRETE_POWDER: 1
+ CYAN_GLAZED_TERRACOTTA: 1
+ CYAN_SHULKER_BOX: 1
+ DARK_OAK_DOOR: 1
+ DARK_OAK_FENCE: 1
+ DARK_OAK_FENCE_GATE: 1
+ DARK_OAK_STAIRS: 1
+ DAYLIGHT_DETECTOR: 10
+ DAYLIGHT_DETECTOR_INVERTED: 10
+ DEAD_BUSH: 1
+ DETECTOR_RAIL: 10
+ DIAMOND_BLOCK: 300
+ DIODE: 5
+ DIODE_BLOCK_OFF: 5
+ DIODE_BLOCK_ON: 5
+ DIRT: 2
+ DISPENSER: 5
+ DOUBLE_PLANT: 2
+ DOUBLE_STEP: 1
+ DOUBLE_STONE_SLAB2: 1
+ DRAGON_EGG: 150
+ DROPPER: 5
+ EMERALD_BLOCK: 150
+ EMERALD_ORE: 0
+ ENCHANTMENT_TABLE: 150
+ END_BRICKS: 2
+ ENDER_CHEST: 150
+ ENDER_PORTAL_FRAME: 0
+ ENDER_PORTAL: 0
+ ENDER_STONE: 2
+ EXPLOSIVE_MINECART: 10
+ FENCE: 1
+ FENCE_GATE: 1
+ FIRE: 0
+ FLOWER_POT: 5
+ FROSTED_ICE: 1
+ FURNACE: 10
+ GLASS: 2
+ GLOWSTONE: 1
+ GOLD_BLOCK: 150
+ GOLD_ORE: 0
+ GRASS: 5
+ GRASS_PATH: 5
+ GRAY_GLAZED_TERRACOTTA: 1
+ GRAY_SHULKER_BOX: 1
+ GRAVEL: 1
+ GREEN_GLAZED_TERRACOTTA: 1
+ GREEN_SHULKER_BOX: 1
+ HARD_CLAY: 2
+ HAY_BLOCK: 2
+ HOPPER: 10
+ HOPPER_MINECART: 20
+ HUGE_MUSHROOM_1: 1
+ HUGE_MUSHROOM_2: 1
+ ICE: 5
+ IRON_BLOCK: 10
+ IRON_DOOR_BLOCK: 5
+ IRON_FENCE: 5
+ IRON_ORE: 0
+ IRON_PLATE: 5
+ IRON_TRAPDOOR: 1
+ ITEM_FRAME: 2
+ JACK_O_LANTERN: 1
+ JUKEBOX: 10
+ JUNGLE_DOOR: 1
+ JUNGLE_FENCE: 1
+ JUNGLE_FENCE_GATE: 1
+ JUNGLE_WOOD_STAIRS: 1
+ LADDER: 1
+ LAPIS_BLOCK: 10
+ LAPIS_ORE: 0
+ LAVA: 0
+ LEAVES_2: 1
+ LEAVES: 1
+ LEVER: 1
+ LIGHT_BLUE_GLAZED_TERRACOTTA: 1
+ LIGHT_BLUE_SHULKER_BOX: 1
+ LIME_GLAZED_TERRACOTTA: 1
+ LIME_SHULKER_BOX: 1
+ LOG: 1
+ #Other log types - examples
+ #LOG:3: 2
+ LOG_2: 1
+ LONG_GRASS: 1
+ MAGENTA_GLAZED_TERRACOTTA: 1
+ MAGENTA_SHULKER_BOX: 1
+ MAGMA: 1
+ MELON_BLOCK: 1
+ MELON_STEM: 1
+ MINECART: 10
+ MOB_SPAWNER: 0
+ MOSSY_COBBLESTONE: 2
+ MYCEL: 5
+ NETHER_BRICK: 2
+ NETHER_BRICK_STAIRS: 2
+ NETHER_FENCE: 2
+ NETHER_STALK: 1
+ NETHER_WART_BLOCK: 2
+ NETHERRACK: 1
+ NOTE_BLOCK: 10
+ OBSERVER: 1
+ OBSIDIAN: 10
+ ORANGE_GLAZED_TERRACOTTA: 1
+ ORANGE_SHULKER_BOX: 1
+ PACKED_ICE: 5
+ PAINTING: 2
+ PINK_GLAZED_TERRACOTTA: 1
+ PINK_SHULKER_BOX: 1
+ PISTON_BASE: 2
+ PISTON_STICKY_BASE: 2
+ PORTAL: 0
+ POWERED_MINECART: 10
+ POWERED_RAIL: 10
+ PRISMARINE: 10
+ PUMPKIN_STEM: 1
+ PUMPKIN: 1
+ PURPLE_GLAZED_TERRACOTTA: 1
+ PURPLE_SHULKER_BOX: 1
+ PURPUR_BLOCK: 1
+ PURPUR_DOUBLE_SLAB: 1
+ PURPUR_PILLAR: 1
+ PURPUR_SLAB: 1
+ PURPUR_STAIRS: 1
+ QUARTZ_BLOCK: 1
+ QUARTZ_ORE: 0
+ QUARTZ_STAIRS: 1
+ QUARTZ: 1
+ RAILS: 1
+ RED_GLAZED_TERRACOTTA: 1
+ RED_MUSHROOM: 1
+ RED_NETHER_BRICK: 2
+ RED_ROSE: 1
+ RED_SANDSTONE: 1
+ RED_SANDSTONE_STAIRS: 1
+ RED_SHULKER_BOX: 1
+ REDSTONE_BLOCK: 10
+ REDSTONE_COMPARATOR_OFF: 10
+ REDSTONE_COMPARATOR_ON: 10
+ REDSTONE_COMPARATOR: 10
+ REDSTONE_LAMP_OFF: 10
+ REDSTONE_LAMP_ON: 10
+ REDSTONE_ORE: 0
+ REDSTONE_TORCH_OFF: 5
+ REDSTONE_TORCH_ON: 5
+ REDSTONE_WIRE: 1
+ SAND: 1
+ SANDSTONE: 1
+ SANDSTONE_STAIRS: 1
+ SEA_LANTERN: 1
+ SIGN_POST: 1
+ SILVER_GLAZED_TERRACOTTA: 1
+ SILVER_SHULKER_BOX: 1
+ SKULL: 10
+ SLIME_BLOCK: 10
+ SMOOTH_BRICK: 2
+ SMOOTH_STAIRS: 2
+ SNOW_BLOCK: 1
+ SOIL: 2
+ SOUL_SAND: 2
+ SPONGE: 10
+ SPRUCE_DOOR: 1
+ SPRUCE_FENCE: 1
+ SPRUCE_FENCE_GATE: 1
+ SPRUCE_WOOD_STAIRS: 1
+ STAINED_CLAY: 2
+ STAINED_GLASS: 2
+ STAINED_GLASS_PANE: 1
+ STATIONARY_LAVA: 0
+ STATIONARY_WATER: 0
+ STEP: 1
+ STONE: 1
+ STONE_BUTTON: 1
+ STONE_PLATE: 2
+ STORAGE_MINECART: 10
+ SUGAR_CANE_BLOCK: 1
+ THIN_GLASS: 1
+ TNT: 5
+ TORCH: 2
+ TRAP_DOOR: 5
+ TRAPPED_CHEST: 10
+ TRIPWIRE_HOOK: 2
+ TRIPWIRE: 2
+ VINE: 1
+ WALL_SIGN: 1
+ WATER_LILY: 5
+ WEB: 10
+ WHEAT: 1
+ WHITE_GLAZED_TERRACOTTA: 1
+ WHITE_SHULKER_BOX: 1
+ WOOD: 1
+ WOOD_BUTTON: 1
+ WOOD_DOOR: 1
+ WOOD_DOUBLE_STEP: 1
+ WOOD_PLATE: 1
+ WOOD_STAIRS: 1
+ WOOD_STEP: 1
+ WOODEN_DOOR: 1
+ WOOL: 1
+ WORKBENCH: 1
+ YELLOW_FLOWER: 1
+ YELLOW_GLAZED_TERRACOTTA: 1
+ YELLOW_SHULKER_BOX: 1
+
\ No newline at end of file
diff --git a/Level/locales/bsb_en_US.yml b/Level/locales/bsb_en_US.yml
new file mode 100755
index 0000000..8c3e1f0
--- /dev/null
+++ b/Level/locales/bsb_en_US.yml
@@ -0,0 +1,15 @@
+###########################################################################################
+# This is a YML file. Be careful when editing. Check your edits in a YAML checker like #
+# the one at http://yaml-online-parser.appspot.com #
+###########################################################################################
+
+### Credits ###
+# Tastybento: maintainer
+# Poslovitch: maintainer
+#
+# This translation is adapted to version : [alpha-0.0.1]
+
+island:
+ islandLevelIs: "Island level is"
+ requiredPointsToNextLevel: "[points] points required until the next level"
+levelDeaths: "([number] deaths)"
\ No newline at end of file
diff --git a/Level/pom.xml b/Level/pom.xml
index da19790..e96246d 100644
--- a/Level/pom.xml
+++ b/Level/pom.xml
@@ -20,6 +20,14 @@
*.yml
+
+ locales
+ false
+ ${basedir}/locales
+
+ *.yml
+
+
diff --git a/Level/src/bskyblock/addin/level/Level.java b/Level/src/bskyblock/addin/level/Level.java
index 6a5fe19..075f604 100644
--- a/Level/src/bskyblock/addin/level/Level.java
+++ b/Level/src/bskyblock/addin/level/Level.java
@@ -1,20 +1,52 @@
package bskyblock.addin.level;
+import java.util.Calendar;
+import java.util.HashMap;
import java.util.Set;
+import java.util.UUID;
+import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;
+import org.bukkit.scheduler.BukkitTask;
+import bskyblock.addin.level.config.LocaleManager;
+import bskyblock.addin.level.config.PluginConfig;
import us.tastybento.bskyblock.BSkyBlock;
import us.tastybento.bskyblock.api.commands.ArgumentHandler;
import us.tastybento.bskyblock.api.commands.CanUseResp;
+import us.tastybento.bskyblock.config.BSBLocale;
+import us.tastybento.bskyblock.config.Settings;
+import us.tastybento.bskyblock.util.Util;
+import us.tastybento.bskyblock.util.VaultHelper;
public class Level extends JavaPlugin {
+
+ private BSkyBlock bSkyBlock;
+
+ // Level calc cool down
+ private HashMap levelWaitTime = new HashMap();
+
+ // Level calc checker
+ BukkitTask checker = null;
+
+ private int levelWait;
+
+ private LocaleManager localeManager;
+
+ private HashMap islandLevel;
+
@Override
- public void onEnable(){
- BSkyBlock plugin = BSkyBlock.getPlugin();
- plugin.getIslandCommand().addSubCommand(new ArgumentHandler("island") {
+ public void onEnable() {
+ new PluginConfig(this);
+ bSkyBlock = BSkyBlock.getPlugin();
+ islandLevel = new HashMap<>();
+ new TopTen(this);
+ // Local locales
+ localeManager = new LocaleManager(this);
+ bSkyBlock.getIslandCommand().addSubCommand(new ArgumentHandler("island") {
@Override
public CanUseResp canUse(CommandSender sender) {
@@ -23,7 +55,23 @@ public class Level extends JavaPlugin {
@Override
public void execute(CommandSender sender, String[] args) {
- sender.sendMessage("Your island level is XXX");
+ if (sender instanceof Player) {
+ Player player = (Player)sender;
+ UUID playerUUID = player.getUniqueId();
+
+ if (VaultHelper.hasPerm(player, Settings.PERMPREFIX + "island.info")) {
+ if (!bSkyBlock.getPlayers().inTeam(playerUUID) && !bSkyBlock.getPlayers().hasIsland(playerUUID)) {
+ Util.sendMessage(player, ChatColor.RED + bSkyBlock.getLocale(sender).get("error.noisland"));
+ return;
+ } else {
+ calculateIslandLevel(player, playerUUID);
+ return;
+ }
+ } else {
+ //Util.sendMessage(player, ChatColor.RED + bSkyBlock.myLocale(playerUUID).errorNoPermission);
+ return;
+ }
+ }
}
@Override
@@ -45,4 +93,112 @@ public class Level extends JavaPlugin {
}
+ /**
+ * Calculates the island level
+ *
+ * @param sender
+ * - Player object of player who is asking
+ * @param targetPlayer
+ * - UUID of the player's island that is being requested
+ * @return - true if successful.
+ */
+ public boolean calculateIslandLevel(final CommandSender sender, final UUID targetPlayer) {
+ return calculateIslandLevel(sender, targetPlayer, false);
+ }
+
+ /**
+ * Calculates the island level
+ * @param sender - asker of the level info
+ * @param targetPlayer
+ * @param report - if true, a detailed report will be provided
+ * @return - false if this is cannot be done
+ */
+ public boolean calculateIslandLevel(final CommandSender sender, final UUID targetPlayer, boolean report) {
+ if (sender instanceof Player) {
+ Player asker = (Player)sender;
+ // Player asking for their own island calc
+ if (asker.getUniqueId().equals(targetPlayer) || asker.isOp() || VaultHelper.hasPerm(asker, Settings.PERMPREFIX + "mod.info")) {
+ // Newer better system - uses chunks
+ if (!onLevelWaitTime(asker) || levelWait <= 0 || asker.isOp() || VaultHelper.hasPerm(asker, Settings.PERMPREFIX + "mod.info")) {
+ Util.sendMessage(asker, ChatColor.GREEN + "Calculating level, please wait...");
+ setLevelWaitTime(asker);
+ new LevelCalcByChunk(this, bSkyBlock, targetPlayer, asker, report);
+ } else {
+ Util.sendMessage(asker, ChatColor.YELLOW + String.valueOf(getLevelWaitTime(asker)));
+ }
+
+ } else {
+ // Asking for the level of another player
+ //Util.sendMessage(asker, ChatColor.GREEN + bSkyBlock.myLocale(asker.getUniqueId()).islandislandLevelis.replace("[level]", String.valueOf(bSkyBlock.getIslands().getIslandLevel(targetPlayer))));
+ }
+ } else {
+ // Console request
+ //Util.sendMessage(sender, ChatColor.GREEN + bSkyBlock.myLocale().levelCalculating);
+ new LevelCalcByChunk(this, bSkyBlock, targetPlayer, sender, report);
+ }
+ return true;
+ }
+
+ /**
+ * Sets cool down for the level command
+ *
+ * @param player
+ */
+ private void setLevelWaitTime(final Player player) {
+ levelWaitTime.put(player.getUniqueId(), Long.valueOf(Calendar.getInstance().getTimeInMillis() + levelWait * 1000));
+ }
+
+ public boolean onLevelWaitTime(final Player player) {
+ if (levelWaitTime.containsKey(player.getUniqueId())) {
+ if (levelWaitTime.get(player.getUniqueId()).longValue() > Calendar.getInstance().getTimeInMillis()) {
+ return true;
+ }
+
+ return false;
+ }
+
+ return false;
+ }
+
+ private long getLevelWaitTime(final Player player) {
+ if (levelWaitTime.containsKey(player.getUniqueId())) {
+ if (levelWaitTime.get(player.getUniqueId()).longValue() > Calendar.getInstance().getTimeInMillis()) {
+ return (levelWaitTime.get(player.getUniqueId()).longValue() - Calendar.getInstance().getTimeInMillis()) / 1000;
+ }
+
+ return 0L;
+ }
+
+ return 0L;
+ }
+
+ public long getIslandLevel(UUID targetPlayer) {
+ //getLogger().info("DEBUG: getting island level for " + bSkyBlock.getPlayers().getName(targetPlayer));
+ if (islandLevel.containsKey(targetPlayer))
+ return islandLevel.get(targetPlayer);
+ return 0;
+ }
+
+ public void setIslandLevel(UUID targetPlayer, long level) {
+ //getLogger().info("DEBUG: set island level to " + level + " for " + bSkyBlock.getPlayers().getName(targetPlayer));
+ islandLevel.put(targetPlayer, level);
+
+ }
+
+ /**
+ * @param sender
+ * @return Locale object for sender
+ */
+ public BSBLocale getLocale(CommandSender sender) {
+ return localeManager.getLocale(sender);
+ }
+
+ /**
+ * @param uuid
+ * @return Locale object for UUID
+ */
+ public BSBLocale getLocale(UUID uuid) {
+ return localeManager.getLocale(uuid);
+ }
+
}
diff --git a/Level/src/bskyblock/addin/level/LevelCalcByChunk.java b/Level/src/bskyblock/addin/level/LevelCalcByChunk.java
new file mode 100644
index 0000000..f617dbf
--- /dev/null
+++ b/Level/src/bskyblock/addin/level/LevelCalcByChunk.java
@@ -0,0 +1,496 @@
+package bskyblock.addin.level;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.UUID;
+
+import org.apache.commons.lang.math.NumberUtils;
+import org.bukkit.ChatColor;
+import org.bukkit.Chunk;
+import org.bukkit.ChunkSnapshot;
+import org.bukkit.World;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+import org.bukkit.material.MaterialData;
+import org.bukkit.permissions.PermissionAttachmentInfo;
+
+import com.google.common.collect.HashMultiset;
+import com.google.common.collect.Multiset;
+import com.google.common.collect.Multiset.Entry;
+import com.google.common.collect.Multisets;
+
+import bskyblock.addin.level.config.Settings;
+import bskyblock.addin.level.event.IslandPostLevelEvent;
+import bskyblock.addin.level.event.IslandPreLevelEvent;
+import us.tastybento.bskyblock.BSkyBlock;
+import us.tastybento.bskyblock.database.objects.Island;
+import us.tastybento.bskyblock.util.Util;
+
+/**
+ * A class that calculates the level of an island very quickly by copying island
+ * chunks to a list and then processing asynchronously.
+ *
+ * @author tastybento
+ *
+ */
+public class LevelCalcByChunk {
+ private static final boolean DEBUG = false;
+ protected static final boolean LEVEL_LOGGING = false;
+ private List reportLines = new ArrayList();
+
+ public LevelCalcByChunk(Level plugin, BSkyBlock bSkyBlock, UUID targetPlayer, CommandSender asker) {
+ this(plugin, bSkyBlock, targetPlayer, asker, false);
+ }
+
+ /**
+ * Calculates the level of an island
+ * @param bSkyBlock
+ * @param targetPlayer - UUID of island owner or team member
+ * @param sender - requester of the level calculation, if anyone
+ * @param report - provide a report to the asker
+ */
+ public LevelCalcByChunk(final Level plugin, final BSkyBlock bSkyBlock, final UUID targetPlayer, final CommandSender sender, final boolean report) {
+ // Get player's island
+ final Island island = bSkyBlock.getIslands().getIsland(targetPlayer);
+ if (DEBUG)
+ plugin.getLogger().info("DEBUG: " + island);
+ if (island != null) {
+ // Get the permission multiplier if it is available
+ Player player = plugin.getServer().getPlayer(targetPlayer);
+ int multiplier = 1;
+ if (player != null) {
+ if (DEBUG)
+ plugin.getLogger().info("DEBUG: player is online");
+ // Get permission multiplier
+ for (PermissionAttachmentInfo perms : player.getEffectivePermissions()) {
+ if (perms.getPermission().startsWith(Settings.PERMPREFIX + "island.multiplier.")) {
+ String spl[] = perms.getPermission().split(Settings.PERMPREFIX + "island.multiplier.");
+ if (spl.length > 1) {
+ if (!NumberUtils.isDigits(spl[1])) {
+ bSkyBlock.getLogger().severe("Player " + player.getName() + " has permission: " + perms.getPermission() + " <-- the last part MUST be a number! Ignoring...");
+ } else {
+ // Get the max value should there be more than one
+ multiplier = Math.max(multiplier, Integer.valueOf(spl[1]));
+ }
+ }
+ }
+ // Do some sanity checking
+ if (multiplier < 1) {
+ multiplier = 1;
+ }
+ }
+ if (DEBUG)
+ plugin.getLogger().info("DEBUG: multiplier = " + multiplier);
+ }
+ final int levelMultiplier = multiplier;
+ // Get the handicap
+ final int levelHandicap = island.getLevelHandicap();
+ if (DEBUG)
+ plugin.getLogger().info("DEBUG: island level handicap = " + levelHandicap);
+ // Get the death handicap
+ int deaths = bSkyBlock.getPlayers().getDeaths(targetPlayer);
+ if (DEBUG)
+ plugin.getLogger().info("DEBUG: deaths = " + deaths);
+ if (bSkyBlock.getPlayers().inTeam(targetPlayer)) {
+ if (DEBUG)
+ plugin.getLogger().info("DEBUG: player is in a team");
+ // Get the team leader's deaths
+ deaths = bSkyBlock.getPlayers().getDeaths(bSkyBlock.getIslands().getTeamLeader(targetPlayer));
+ if (Settings.sumTeamDeaths) {
+ deaths = 0;
+ //plugin.getLogger().info("DEBUG: player is in team");
+ for (UUID member : bSkyBlock.getIslands().getMembers(targetPlayer)) {
+ deaths += bSkyBlock.getPlayers().getDeaths(member);
+ }
+ }
+ if (DEBUG)
+ plugin.getLogger().info("DEBUG: deaths is now = " + deaths);
+ }
+ final int deathHandicap = deaths;
+ // Check if player's island world is the nether or overworld and adjust accordingly
+ final World world = bSkyBlock.getIslands().getIslandLocation(targetPlayer).getWorld();
+ // Get the chunks
+ if (DEBUG)
+ plugin.getLogger().info("DEBUG: Getting chunks. Protection range = " + island.getProtectionRange());
+ //long nano = System.nanoTime();
+ Set chunkSnapshot = new HashSet();
+ for (int x = island.getMinProtectedX(); x < (island.getMinProtectedX() + (island.getProtectionRange() *2) + 16); x += 16) {
+ for (int z = island.getMinProtectedZ(); z < (island.getMinProtectedZ() + (island.getProtectionRange() * 2) + 16); z += 16) {
+ if (!world.isChunkLoaded((int)((double)x/16), (int)((double)z/16))) {
+ //plugin.getLogger().info("DEBUG: chunk is not loaded");
+ // If the chunk isn't already generated, don't try and generate it
+ if (world.loadChunk((int)((double)x/16), (int)((double)z/16), false)) {
+ //plugin.getLogger().info("DEBUG: chunk loaded");
+ Chunk chunk = world.getChunkAt((int)((double)x/16), (int)((double)z/16));
+ chunkSnapshot.add(chunk.getChunkSnapshot());
+ //plugin.getLogger().info("DEBUG: unload = " + chunk.unload(false));
+ }
+ } else {
+ //plugin.getLogger().info("DEBUG: chunk is loaded");
+ chunkSnapshot.add(world.getBlockAt(x, 0, z).getChunk().getChunkSnapshot());
+ }
+ if (DEBUG)
+ plugin.getLogger().info("DEBUG: getting chunk at " + x + ", " + z);
+ }
+ }
+ //plugin.getLogger().info("DEBUG: time = " + (System.nanoTime() - nano) / 1000000 + " ms");
+ if (DEBUG)
+ plugin.getLogger().info("DEBUG: size of chunk snapshot = " + chunkSnapshot.size());
+ final Set finalChunk = chunkSnapshot;
+ final int worldHeight = world.getMaxHeight();
+ //plugin.getLogger().info("DEBUG:world height = " +worldHeight);
+ plugin.getServer().getScheduler().runTaskAsynchronously(plugin, new Runnable() {
+
+ @Override
+ public void run() {
+ // Logging
+ List mdLog = null;
+ List uwLog = null;
+ List noCountLog = null;
+ List overflowLog = null;
+ if (LEVEL_LOGGING || report) {
+ mdLog = new ArrayList();
+ uwLog = new ArrayList();
+ noCountLog = new ArrayList();
+ overflowLog = new ArrayList();
+ }
+ // Copy the limits hashmap
+ HashMap limitCount = new HashMap(Settings.blockLimits);
+ // Calculate the island score
+ long blockCount = 0;
+ long underWaterBlockCount = 0;
+ for (ChunkSnapshot chunk: finalChunk) {
+ for (int x = 0; x< 16; x++) {
+ // Check if the block coord is inside the protection zone and if not, don't count it
+ if (chunk.getX() * 16 + x < island.getMinProtectedX() || chunk.getX() * 16 + x >= island.getMinProtectedX() + (island.getProtectionRange() * 2)) {
+ if (DEBUG)
+ plugin.getLogger().info("Block is outside protected area - x = " + (chunk.getX() * 16 + x));
+ continue;
+ }
+ for (int z = 0; z < 16; z++) {
+ // Check if the block coord is inside the protection zone and if not, don't count it
+ if (chunk.getZ() * 16 + z < island.getMinProtectedZ() || chunk.getZ() * 16 + z >= island.getMinProtectedZ() + (island.getProtectionRange() * 2)) {
+ if (DEBUG)
+ plugin.getLogger().info("Block is outside protected area - z = " + (chunk.getZ() * 16 + z));
+ continue;
+ }
+
+ for (int y = 0; y < worldHeight; y++) {
+ int type = chunk.getBlockTypeId(x, y, z);
+ int data = chunk.getBlockData(x, y, z);
+ MaterialData md = new MaterialData(type,(byte) data);
+ MaterialData generic = new MaterialData(type);
+ if (type != 0) { // AIR
+ if (DEBUG)
+ plugin.getLogger().info("Block is inside protected area " + (chunk.getX() * 16) + "," + (chunk.getZ() * 16 + z));
+ if (DEBUG)
+ plugin.getLogger().info("Block is " + md + "[" + generic +"]");
+ if (limitCount.containsKey(md) && Settings.blockValues.containsKey(md)) {
+ int count = limitCount.get(md);
+ if (DEBUG)
+ plugin.getLogger().info("DEBUG: Count for non-generic " + md + " is " + count);
+ if (count > 0) {
+ limitCount.put(md, --count);
+ if (Settings.seaHeight > 0 && y<=Settings.seaHeight) {
+ underWaterBlockCount += Settings.blockValues.get(md);
+ if (LEVEL_LOGGING || report) {
+ uwLog.add(md);
+ }
+ } else {
+ blockCount += Settings.blockValues.get(md);
+ if (LEVEL_LOGGING || report) {
+ mdLog.add(md);
+ }
+ }
+ } else if (LEVEL_LOGGING || report) {
+ overflowLog.add(md);
+ }
+ } else if (limitCount.containsKey(generic) && Settings.blockValues.containsKey(generic)) {
+ int count = limitCount.get(generic);
+ if (DEBUG)
+ plugin.getLogger().info("DEBUG: Count for generic " + generic + " is " + count);
+ if (count > 0) {
+ limitCount.put(generic, --count);
+ if (Settings.seaHeight > 0 && y<=Settings.seaHeight) {
+ underWaterBlockCount += Settings.blockValues.get(generic);
+ if (LEVEL_LOGGING || report) {
+ uwLog.add(md);
+ }
+ } else {
+ blockCount += Settings.blockValues.get(generic);
+ if (LEVEL_LOGGING || report) {
+ mdLog.add(md);
+ }
+ }
+ } else if (LEVEL_LOGGING || report) {
+ overflowLog.add(md);
+ }
+ } else if (Settings.blockValues.containsKey(md)) {
+ if (DEBUG)
+ plugin.getLogger().info("DEBUG: Adding " + md + " = " + Settings.blockValues.get(md));
+ if (Settings.seaHeight > 0 && y<=Settings.seaHeight) {
+ underWaterBlockCount += Settings.blockValues.get(md);
+ if (LEVEL_LOGGING || report) {
+ uwLog.add(md);
+ }
+ } else {
+ blockCount += Settings.blockValues.get(md);
+ if (LEVEL_LOGGING || report) {
+ mdLog.add(md);
+ }
+ }
+ } else if (Settings.blockValues.containsKey(generic)) {
+ if (DEBUG)
+ plugin.getLogger().info("DEBUG: Adding " + generic + " = " + Settings.blockValues.get(generic));
+ if (Settings.seaHeight > 0 && y<=Settings.seaHeight) {
+ underWaterBlockCount += Settings.blockValues.get(generic);
+ if (LEVEL_LOGGING || report) {
+ uwLog.add(md);
+ }
+ } else {
+ blockCount += Settings.blockValues.get(generic);
+ if (LEVEL_LOGGING || report) {
+ mdLog.add(md);
+ }
+ }
+ } else if (LEVEL_LOGGING || report) {
+ noCountLog.add(md);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ blockCount += (long)((double)underWaterBlockCount * Settings.underWaterMultiplier);
+ if (DEBUG)
+ plugin.getLogger().info("DEBUG: block count = "+blockCount);
+
+ final long score = (((blockCount * levelMultiplier) - (deathHandicap * Settings.deathpenalty)) / Settings.levelCost) - levelHandicap;
+ // Logging or report
+ if (LEVEL_LOGGING || report) {
+ // provide counts
+ Multiset uwCount = HashMultiset.create(uwLog);
+ Multiset mdCount = HashMultiset.create(mdLog);
+ Multiset ncCount = HashMultiset.create(noCountLog);
+ Multiset ofCount = HashMultiset.create(overflowLog);
+ reportLines.add("Level Log for island at " + island.getCenter());
+ if (sender instanceof Player) {
+ reportLines.add("Asker is " + sender.getName() + " (" + ((Player)sender).getUniqueId().toString() + ")");
+ } else {
+ reportLines.add("Asker is console");
+ }
+ reportLines.add("Target player UUID = " + targetPlayer.toString());
+ reportLines.add("Total block value count = " + String.format("%,d",blockCount));
+ reportLines.add("Level cost = " + Settings.levelCost);
+ reportLines.add("Level multiplier = " + levelMultiplier + " (Player must be online to get a permission multiplier)");
+ reportLines.add("Schematic level handicap = " + levelHandicap + " (level is reduced by this amount)");
+ reportLines.add("Deaths handicap = " + (deathHandicap * Settings.deathpenalty) + " (" + deathHandicap + " deaths)");
+ reportLines.add("Level calculated = " + score);
+ reportLines.add("==================================");
+ int total = 0;
+ if (!uwCount.isEmpty()) {
+ reportLines.add("Underwater block count (Multiplier = x" + Settings.underWaterMultiplier + ") value");
+ reportLines.add("Total number of underwater blocks = " + String.format("%,d",uwCount.size()));
+ Iterable> entriesSortedByCount =
+ Multisets.copyHighestCountFirst(uwCount).entrySet();
+ Iterator> it = entriesSortedByCount.iterator();
+ while (it.hasNext()) {
+ Entry type = it.next();
+ int value = 0;
+ if (Settings.blockValues.containsKey(type)) {
+ // Specific
+ value = Settings.blockValues.get(type);
+ } else if (Settings.blockValues.containsKey(new MaterialData(type.getElement().getItemType()))) {
+ // Generic
+ value = Settings.blockValues.get(new MaterialData(type.getElement().getItemType()));
+ }
+ if (value > 0) {
+ reportLines.add(type.getElement().toString() + ":"
+ + String.format("%,d",type.getCount()) + " blocks x " + value + " = " + (value * type.getCount()));
+ total += (value * type.getCount());
+ }
+ }
+ reportLines.add("Subtotal = " + total);
+ reportLines.add("==================================");
+ }
+ reportLines.add("Regular block count");
+ reportLines.add("Total number of blocks = " + String.format("%,d",mdCount.size()));
+ //Iterable> entriesSortedByCount =
+ // Multisets.copyHighestCountFirst(mdCount).entrySet();
+ Iterable> entriesSortedByCount =
+ mdCount.entrySet();
+ Iterator> it = entriesSortedByCount.iterator();
+ while (it.hasNext()) {
+ Entry type = it.next();
+ int value = 0;
+ if (Settings.blockValues.containsKey(type)) {
+ // Specific
+ value = Settings.blockValues.get(type);
+ } else if (Settings.blockValues.containsKey(new MaterialData(type.getElement().getItemType()))) {
+ // Generic
+ value = Settings.blockValues.get(new MaterialData(type.getElement().getItemType()));
+ }
+ if (value > 0) {
+ reportLines.add(type.getElement().toString() + ":"
+ + String.format("%,d",type.getCount()) + " blocks x " + value + " = " + (value * type.getCount()));
+ total += (value * type.getCount());
+ }
+ }
+ reportLines.add("Total = " + total);
+ reportLines.add("==================================");
+ reportLines.add("Blocks not counted because they exceeded limits: " + String.format("%,d",ofCount.size()));
+ //entriesSortedByCount = Multisets.copyHighestCountFirst(ofCount).entrySet();
+ entriesSortedByCount = ofCount.entrySet();
+ it = entriesSortedByCount.iterator();
+ while (it.hasNext()) {
+ Entry type = it.next();
+ Integer limit = Settings.blockLimits.get(type.getElement());
+ String explain = ")";
+ if (limit == null) {
+ MaterialData generic = new MaterialData(type.getElement().getItemType());
+ limit = Settings.blockLimits.get(generic);
+ explain = " - All types)";
+ }
+ reportLines.add(type.getElement().toString() + ": " + String.format("%,d",type.getCount()) + " blocks (max " + limit + explain);
+ }
+ reportLines.add("==================================");
+ reportLines.add("Blocks on island that are not in blockvalues.yml");
+ reportLines.add("Total number = " + String.format("%,d",ncCount.size()));
+ //entriesSortedByCount = Multisets.copyHighestCountFirst(ncCount).entrySet();
+ entriesSortedByCount = ncCount.entrySet();
+ it = entriesSortedByCount.iterator();
+ while (it.hasNext()) {
+ Entry type = it.next();
+ reportLines.add(type.getElement().toString() + ": " + String.format("%,d",type.getCount()) + " blocks");
+ }
+ reportLines.add("=================================");
+ }
+
+ // Calculate how many points are required to get to the next level
+ long calculatePointsToNextLevel = (Settings.levelCost * (score + 1 + levelHandicap)) - ((blockCount * levelMultiplier) - (deathHandicap * Settings.deathpenalty));
+ // Sometimes it will return 0, so calculate again to make sure it will display a good value
+ if(calculatePointsToNextLevel == 0) calculatePointsToNextLevel = (Settings.levelCost * (score + 2 + levelHandicap)) - ((blockCount * levelMultiplier) - (deathHandicap * Settings.deathpenalty));
+
+ final long pointsToNextLevel = calculatePointsToNextLevel;
+
+ // Return to main thread
+ plugin.getServer().getScheduler().runTask(plugin, new Runnable() {
+
+ @Override
+ public void run() {
+ // Fire the pre-level event
+ Island island = bSkyBlock.getIslands().getIsland(targetPlayer);
+ final IslandPreLevelEvent event = new IslandPreLevelEvent(targetPlayer, island, score);
+ event.setPointsToNextLevel(pointsToNextLevel);
+ plugin.getServer().getPluginManager().callEvent(event);
+ long oldLevel = plugin.getIslandLevel(targetPlayer);
+ if (!event.isCancelled()) {
+ if (DEBUG)
+ plugin.getLogger().info("DEBUG: updating player");
+
+ if (oldLevel != event.getLevel()) {
+ // Update player and team mates
+ plugin.setIslandLevel(targetPlayer, event.getLevel());
+ if (DEBUG)
+ plugin.getLogger().info("DEBUG: set island level, now trying to save player");
+ bSkyBlock.getPlayers().save(targetPlayer);
+ }
+ if (DEBUG)
+ plugin.getLogger().info("DEBUG: save player, now looking at team members");
+ // Update any team members too
+ if (bSkyBlock.getPlayers().inTeam(targetPlayer)) {
+ //plugin.getLogger().info("DEBUG: player is in team");
+ for (UUID member : bSkyBlock.getIslands().getMembers(targetPlayer)) {
+ //plugin.getLogger().info("DEBUG: updating team member level too");
+ if (plugin.getIslandLevel(member) != event.getLevel()) {
+ plugin.setIslandLevel(member, event.getLevel());
+ bSkyBlock.getPlayers().save(member);
+ }
+ }
+ }
+ if (DEBUG) {
+ plugin.getLogger().info("DEBUG: finished team member saving");
+ plugin.getLogger().info("DEBUG: updating top ten");
+ }
+ if (bSkyBlock.getPlayers().inTeam(targetPlayer)) {
+ UUID leader = bSkyBlock.getIslands().getTeamLeader(targetPlayer);
+ if (leader != null) {
+ TopTen.topTenAddEntry(leader, event.getLevel());
+ }
+ } else {
+ TopTen.topTenAddEntry(targetPlayer, event.getLevel());
+ }
+ }
+
+ // Fire the island post level calculation event
+ final IslandPostLevelEvent event3 = new IslandPostLevelEvent(targetPlayer, island, event.getLevel(), event.getPointsToNextLevel());
+ bSkyBlock.getServer().getPluginManager().callEvent(event3);
+
+ if(!event3.isCancelled()){
+ // Check that sender still is online
+ if (sender != null) {
+ // Check if console
+ if (!(sender instanceof Player)) {
+ // Console
+ if (!report) {
+ Util.sendMessage(sender, ChatColor.GREEN + plugin.getLocale(sender).get("island.islandLevelIs") + " " + ChatColor.WHITE + plugin.getIslandLevel(targetPlayer));
+ } else {
+ for (String line: reportLines) {
+ Util.sendMessage(sender, line);
+ }
+ Util.sendMessage(sender, ChatColor.GREEN + plugin.getLocale(sender).get("island.islandLevelIs") + " " + ChatColor.WHITE + plugin.getIslandLevel(targetPlayer));
+ if (event.getPointsToNextLevel() >= 0) {
+ String toNextLevel = ChatColor.GREEN + plugin.getLocale(sender).get("island.requiredPointsToNextLevel").replace("[points]", String.valueOf(event.getPointsToNextLevel()));
+ toNextLevel = toNextLevel.replace("[next]", String.valueOf(plugin.getIslandLevel(targetPlayer) + 1));
+ Util.sendMessage(sender, toNextLevel);
+ }
+ }
+ } else {
+ // Player
+ if (!report) {
+ // Tell offline team members the island level changed
+ if (plugin.getIslandLevel(targetPlayer) != oldLevel) {
+ //plugin.getLogger().info("DEBUG: telling offline players");
+ //bSkyBlock.getMessages().tellOfflineTeam(targetPlayer, ChatColor.GREEN + bSkyBlock.myLocale(targetPlayer).islandislandLevelis + " " + ChatColor.WHITE
+ // + plugin.getIslandLevel(targetPlayer));
+ }
+ if (sender instanceof Player && ((Player)sender).isOnline()) {
+ String message = ChatColor.GREEN + plugin.getLocale(sender).get("island.islandLevelIs") + " " + ChatColor.WHITE + plugin.getIslandLevel(targetPlayer);
+ if (Settings.deathpenalty != 0) {
+ message += " " + plugin.getLocale(sender).get("levelDeaths").replace("[number]", String.valueOf(deathHandicap));
+ }
+ Util.sendMessage(sender, message);
+ //Send player how many points are required to reach next island level
+ if (event.getPointsToNextLevel() >= 0) {
+ String toNextLevel = ChatColor.GREEN + plugin.getLocale(sender).get("island.requiredPointsToNextLevel").replace("[points]", String.valueOf(event.getPointsToNextLevel()));
+ toNextLevel = toNextLevel.replace("[next]", String.valueOf(plugin.getIslandLevel(targetPlayer) + 1));
+ Util.sendMessage(sender, toNextLevel);
+ }
+ }
+ } else {
+ if (((Player)sender).isOnline()) {
+ for (String line: reportLines) {
+ Util.sendMessage(sender, line);
+ }
+ }
+ Util.sendMessage(sender, ChatColor.GREEN + plugin.getLocale(sender).get("island.islandLevelIs") + " " + ChatColor.WHITE + plugin.getIslandLevel(targetPlayer));
+ if (event.getPointsToNextLevel() >= 0) {
+ String toNextLevel = ChatColor.GREEN + plugin.getLocale(sender).get("island.requiredPointsToNextLevel").replace("[points]", String.valueOf(event.getPointsToNextLevel()));
+ toNextLevel = toNextLevel.replace("[next]", String.valueOf(plugin.getIslandLevel(targetPlayer) + 1));
+ Util.sendMessage(sender, toNextLevel);
+ }
+ }
+ }
+ }
+ }
+ }});
+ }});
+ }
+ }
+
+}
diff --git a/Level/src/bskyblock/addin/level/MapUtil.java b/Level/src/bskyblock/addin/level/MapUtil.java
new file mode 100644
index 0000000..0116a77
--- /dev/null
+++ b/Level/src/bskyblock/addin/level/MapUtil.java
@@ -0,0 +1,53 @@
+/*******************************************************************************
+ * This file is part of ASkyBlock.
+ *
+ * ASkyBlock is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * ASkyBlock is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with ASkyBlock. If not, see .
+ *******************************************************************************/
+package bskyblock.addin.level;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author tastybento
+ */
+public class MapUtil {
+ /**
+ * Sorts map in descending order
+ *
+ * @param map
+ * @return
+ */
+ public static > LinkedHashMap sortByValue(Map map) {
+ List> list = new LinkedList>(map.entrySet());
+ Collections.sort(list, new Comparator>() {
+ public int compare(Map.Entry o1, Map.Entry o2) {
+ // Switch these two if you want ascending
+ return (o2.getValue()).compareTo(o1.getValue());
+ }
+ });
+
+ LinkedHashMap result = new LinkedHashMap();
+ for (Map.Entry entry : list) {
+ result.put(entry.getKey(), entry.getValue());
+ if (result.size() > 20)
+ break;
+ }
+ return result;
+ }
+}
diff --git a/Level/src/bskyblock/addin/level/TopTen.java b/Level/src/bskyblock/addin/level/TopTen.java
new file mode 100644
index 0000000..597019f
--- /dev/null
+++ b/Level/src/bskyblock/addin/level/TopTen.java
@@ -0,0 +1,284 @@
+/*******************************************************************************
+ * This file is part of ASkyBlock.
+ *
+ * ASkyBlock is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * ASkyBlock is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with ASkyBlock. If not, see .
+ *******************************************************************************/
+
+package bskyblock.addin.level;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.UUID;
+
+import org.bukkit.Bukkit;
+import org.bukkit.ChatColor;
+import org.bukkit.Material;
+import org.bukkit.command.CommandSender;
+import org.bukkit.configuration.file.YamlConfiguration;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.EventPriority;
+import org.bukkit.event.Listener;
+import org.bukkit.event.inventory.ClickType;
+import org.bukkit.event.inventory.InventoryClickEvent;
+import org.bukkit.event.inventory.InventoryType.SlotType;
+import org.bukkit.inventory.Inventory;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.meta.SkullMeta;
+
+import bskyblock.addin.level.config.Settings;
+import us.tastybento.bskyblock.BSkyBlock;
+
+/**
+ * Handles all Top Ten List functions
+ *
+ * @author tastybento
+ *
+ */
+public class TopTen implements Listener{
+ private static Level plugin;
+ // Top ten list of players
+ private static Map topTenList = new HashMap();
+ private static final int GUISIZE = 27; // Must be a multiple of 9
+ private static final int[] SLOTS = new int[] {4, 12, 14, 19, 20, 21, 22, 23, 24, 25};
+ private static final boolean DEBUG = false;
+ // Store this as a static because it's the same for everyone and saves memory cleanup
+ private static Inventory gui;
+
+ public TopTen(Level plugin) {
+ TopTen.plugin = plugin;
+ }
+
+ /**
+ * Adds a player to the top ten, if the level is good enough
+ *
+ * @param ownerUUID
+ * @param l
+ */
+ public static void topTenAddEntry(UUID ownerUUID, long l) {
+ // Special case for removals. If a level of zero is given the player
+ // needs to be removed from the list
+ if (l < 1) {
+ if (topTenList.containsKey(ownerUUID)) {
+ topTenList.remove(ownerUUID);
+ }
+ return;
+ }
+ // Try and see if the player is online
+ Player player = plugin.getServer().getPlayer(ownerUUID);
+ if (player != null) {
+ // Online
+ if (!player.hasPermission(Settings.PERMPREFIX + "intopten")) {
+ topTenList.remove(ownerUUID);
+ return;
+ }
+ }
+ topTenList.put(ownerUUID, l);
+ topTenList = MapUtil.sortByValue(topTenList);
+ }
+
+ /**
+ * Removes ownerUUID from the top ten list
+ *
+ * @param ownerUUID
+ */
+ public static void topTenRemoveEntry(UUID ownerUUID) {
+ topTenList.remove(ownerUUID);
+ }
+
+ /**
+ * Generates a sorted map of islands for the Top Ten list from all player
+ * files
+ */
+ public static void topTenCreate() {
+ topTenCreate(null);
+ }
+
+ /**
+ * Creates the top ten list from scratch. Does not get the level of each island. Just
+ * takes the level from the player's file.
+ * Runs asynchronously from the main thread.
+ * @param sender
+ */
+ public static void topTenCreate(final CommandSender sender) {
+ // TODO
+ }
+
+ public static void topTenSave() {
+ if (topTenList == null) {
+ return;
+ }
+ plugin.getLogger().info("Saving top ten list");
+ // Make file
+ File topTenFile = new File(plugin.getDataFolder(), "topten.yml");
+ // Make configuration
+ YamlConfiguration config = new YamlConfiguration();
+ // Save config
+
+ int rank = 0;
+ for (Map.Entry m : topTenList.entrySet()) {
+ if (rank++ == 10) {
+ break;
+ }
+ config.set("topten." + m.getKey().toString(), m.getValue());
+ }
+ try {
+ config.save(topTenFile);
+ plugin.getLogger().info("Saved top ten list");
+ } catch (Exception e) {
+ plugin.getLogger().severe("Could not save top ten list!");
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Loads the top ten from the file system topten.yml. If it does not exist
+ * then the top ten is created
+ */
+ public static void topTenLoad() {
+ topTenList.clear();
+ // TODO
+ }
+
+ /**
+ * Displays the Top Ten list if it exists in chat
+ *
+ * @param player
+ * - the requesting player
+ * @return - true if successful, false if no Top Ten list exists
+ */
+ public static boolean topTenShow(final Player player) {
+
+ if (DEBUG)
+ plugin.getLogger().info("DEBUG: new GUI display");
+ // New GUI display (shown by default)
+ if (topTenList == null) topTenCreate();
+ topTenList = MapUtil.sortByValue(topTenList);
+ // Create the top ten GUI if it does not exist
+ if (gui == null) {
+ gui = Bukkit.createInventory(null, GUISIZE, plugin.getLocale(player.getUniqueId()).get("topTenGuiTitle"));
+ if (DEBUG)
+ plugin.getLogger().info("DEBUG: creating GUI for the first time");
+ }
+ // Reset
+ gui.clear();
+ int i = 1;
+ Iterator> it = topTenList.entrySet().iterator();
+ while (it.hasNext()) {
+ Map.Entry m = it.next();
+ UUID playerUUID = m.getKey();
+ if (DEBUG)
+ plugin.getLogger().info("DEBUG: " + i + ": " + playerUUID);
+ // Remove from TopTen if the player is online and has the permission
+ Player entry = plugin.getServer().getPlayer(playerUUID);
+ boolean show = true;
+ if (entry != null) {
+ if (!entry.hasPermission(Settings.PERMPREFIX + "intopten")) {
+ it.remove();
+ show = false;
+ }
+ } else {
+ if (DEBUG)
+ plugin.getLogger().info("DEBUG: player not online, so no per check");
+
+ }
+ if (show) {
+ gui.setItem(SLOTS[i-1], getSkull(i, m.getValue(), playerUUID));
+ if (i++ == 10) break;
+ }
+ }
+
+ player.openInventory(gui);
+
+ return true;
+ }
+
+ static ItemStack getSkull(int rank, Long long1, UUID player){
+ if (DEBUG)
+ plugin.getLogger().info("DEBUG: Getting the skull");
+ String playerName = BSkyBlock.getPlugin().getPlayers().getName(player);
+ if (DEBUG) {
+ plugin.getLogger().info("DEBUG: playername = " + playerName);
+
+ plugin.getLogger().info("DEBUG: second chance = " + BSkyBlock.getPlugin().getPlayers().getName(player));
+ }
+ ItemStack playerSkull = new ItemStack(Material.SKULL_ITEM, 1, (short) 3);
+ if (playerName == null) return null;
+ SkullMeta meta = (SkullMeta) playerSkull.getItemMeta();
+ meta.setOwner(playerName);
+ meta.setDisplayName((plugin.getLocale(player).get("topTenGuiHeading").replace("[name]", BSkyBlock.getPlugin().getIslands().getIslandName(player))).replace("[rank]", String.valueOf(rank)));
+ //meta.setDisplayName(ChatColor.YELLOW + "" + ChatColor.BOLD + " " + ChatColor.YELLOW + "Island: " + ChatColor.GOLD + ChatColor.UNDERLINE + plugin.getGrid().getIslandName(player) + ChatColor.GRAY + " (#" + rank + ")");
+ List lore = new ArrayList();
+ lore.add(ChatColor.YELLOW + plugin.getLocale(player).get("levelislandLevel") + " " + long1);
+ if (BSkyBlock.getPlugin().getPlayers().inTeam(player)) {
+ List memberList = new ArrayList<>();
+ for (UUID members : BSkyBlock.getPlugin().getIslands().getMembers(player)) {
+ memberList.add(ChatColor.AQUA + BSkyBlock.getPlugin().getPlayers().getName(members));
+ }
+ lore.addAll(memberList);
+ }
+ //else lore.add(ChatColor.AQUA + playerName);
+
+ meta.setLore(lore);
+ playerSkull.setItemMeta(meta);
+ return playerSkull;
+ }
+
+ static void remove(UUID owner) {
+ topTenList.remove(owner);
+ }
+
+ @SuppressWarnings("deprecation")
+ @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled=true)
+ public void onInventoryClick(InventoryClickEvent event) {
+ Inventory inventory = event.getInventory(); // The inventory that was clicked in
+ if (inventory.getName() == null) {
+ return;
+ }
+ // The player that clicked the item
+ Player player = (Player) event.getWhoClicked();
+ if (!inventory.getTitle().equals(plugin.getLocale(player).get("topTenGuiTitle"))) {
+ return;
+ }
+ event.setCancelled(true);
+ player.updateInventory();
+ if(event.getCurrentItem() != null && event.getCurrentItem().getType().equals(Material.SKULL_ITEM) && event.getCurrentItem().hasItemMeta()){
+ // TODO warp
+ //Util.runCommand(player, "is warp " + ((SkullMeta)event.getCurrentItem().getItemMeta()).getOwner());
+ player.closeInventory();
+ return;
+ }
+ if (event.getSlotType().equals(SlotType.OUTSIDE)) {
+ player.closeInventory();
+ return;
+ }
+ if (event.getClick().equals(ClickType.SHIFT_RIGHT)) {
+ player.closeInventory();
+ return;
+ }
+ }
+
+ /**
+ * Get a sorted descending map of the top players
+ * @return the topTenList - may be more or less than ten
+ */
+ public static Map getTopTenList() {
+ return topTenList;
+ }
+}
diff --git a/Level/src/bskyblock/addin/level/config/LocaleManager.java b/Level/src/bskyblock/addin/level/config/LocaleManager.java
new file mode 100644
index 0000000..c148e7b
--- /dev/null
+++ b/Level/src/bskyblock/addin/level/config/LocaleManager.java
@@ -0,0 +1,28 @@
+package bskyblock.addin.level.config;
+
+import java.util.UUID;
+
+import bskyblock.addin.level.Level;
+import us.tastybento.bskyblock.BSkyBlock;
+import us.tastybento.bskyblock.config.AbstractLocaleManager;
+import us.tastybento.bskyblock.config.BSBLocale;
+import us.tastybento.bskyblock.config.Settings;
+
+public class LocaleManager extends AbstractLocaleManager {
+
+ public LocaleManager(Level plugin) {
+ super(plugin);
+ }
+
+ @Override
+ public BSBLocale getLocale(UUID player) {
+ //getLogger().info("DEBUG: " + player);
+ //getLogger().info("DEBUG: " + getPlayers() == null ? "Players is null":"Players in not null");
+ //getLogger().info("DEBUG: " + getPlayers().getPlayer(player));
+ //getLogger().info("DEBUG: " + getPlayers().getPlayer(player).getLocale());
+ String locale = BSkyBlock.getPlugin().getPlayers().getPlayer(player).getLocale();
+ if(locale.isEmpty() || !getLocales().containsKey(locale)) return getLocales().get(Settings.defaultLanguage);
+
+ return getLocales().get(locale);
+ }
+}
diff --git a/Level/src/bskyblock/addin/level/config/PluginConfig.java b/Level/src/bskyblock/addin/level/config/PluginConfig.java
new file mode 100644
index 0000000..ae70d30
--- /dev/null
+++ b/Level/src/bskyblock/addin/level/config/PluginConfig.java
@@ -0,0 +1,95 @@
+package bskyblock.addin.level.config;
+
+import java.util.HashMap;
+
+import org.apache.commons.lang.StringUtils;
+import org.bukkit.Material;
+import org.bukkit.material.MaterialData;
+
+import bskyblock.addin.level.Level;
+
+public class PluginConfig {
+ private static final boolean DEBUG = false;
+
+ /**
+ * Loads the various settings from the config.yml file into the plugin
+ */
+ public PluginConfig(Level plugin) {
+ plugin.saveDefaultConfig();
+
+ // Island level cool down time
+ Settings.levelWait = plugin.getConfig().getInt("levelwait", 60);
+ if (Settings.levelWait < 0) {
+ Settings.levelWait = 0;
+ }
+
+ // Get the under water multiplier
+ Settings.deathpenalty = plugin.getConfig().getInt("deathpenalty", 0);
+ Settings.sumTeamDeaths = plugin.getConfig().getBoolean("sumteamdeaths");
+ Settings.maxDeaths = plugin.getConfig().getInt("maxdeaths", 10);
+ Settings.islandResetDeathReset = plugin.getConfig().getBoolean("islandresetdeathreset", true);
+ Settings.teamJoinDeathReset = plugin.getConfig().getBoolean("teamjoindeathreset", true);
+ Settings.underWaterMultiplier = plugin.getConfig().getDouble("underwater", 1D);
+ Settings.levelCost = plugin.getConfig().getInt("levelcost", 100);
+ if (Settings.levelCost < 1) {
+ Settings.levelCost = 1;
+ plugin.getLogger().warning("levelcost in blockvalues.yml cannot be less than 1. Setting to 1.");
+ }
+ Settings.blockLimits = new HashMap();
+ if (plugin.getConfig().isSet("limits")) {
+ for (String material : plugin.getConfig().getConfigurationSection("limits").getKeys(false)) {
+ try {
+ String[] split = material.split(":");
+ byte data = 0;
+ if (split.length>1) {
+ data = Byte.valueOf(split[1]);
+ }
+ Material mat;
+ if (StringUtils.isNumeric(split[0])) {
+ mat = Material.getMaterial(Integer.parseInt(split[0]));
+ } else {
+ mat = Material.valueOf(split[0].toUpperCase());
+ }
+ MaterialData materialData = new MaterialData(mat);
+ materialData.setData(data);
+ Settings.blockLimits.put(materialData, plugin.getConfig().getInt("limits." + material, 0));
+ if (DEBUG) {
+ plugin.getLogger().info("Maximum number of " + materialData + " will be " + Settings.blockLimits.get(materialData));
+ }
+ } catch (Exception e) {
+ plugin.getLogger().warning("Unknown material (" + material + ") in blockvalues.yml Limits section. Skipping...");
+ }
+ }
+ }
+ Settings.blockValues = new HashMap();
+ if (plugin.getConfig().isSet("blocks")) {
+ for (String material : plugin.getConfig().getConfigurationSection("blocks").getKeys(false)) {
+ try {
+ String[] split = material.split(":");
+ byte data = 0;
+ if (split.length>1) {
+ data = Byte.valueOf(split[1]);
+ }
+ MaterialData materialData = null;
+ if (StringUtils.isNumeric(split[0])) {
+ materialData = new MaterialData(Integer.parseInt(split[0]));
+ } else {
+ materialData = new MaterialData(Material.valueOf(split[0].toUpperCase()));
+ }
+
+ materialData.setData(data);
+ Settings.blockValues.put(materialData, plugin.getConfig().getInt("blocks." + material, 0));
+ if (DEBUG) {
+ plugin.getLogger().info(materialData.toString() + " value = " + Settings.blockValues.get(materialData));
+ }
+ } catch (Exception e) {
+ // e.printStackTrace();
+ plugin.getLogger().warning("Unknown material (" + material + ") in blockvalues.yml blocks section. Skipping...");
+ }
+ }
+ } else {
+ plugin.getLogger().severe("No block values in blockvalues.yml! All island levels will be zero!");
+ }
+ // All done
+ }
+}
diff --git a/Level/src/bskyblock/addin/level/config/Settings.java b/Level/src/bskyblock/addin/level/config/Settings.java
new file mode 100644
index 0000000..6d36216
--- /dev/null
+++ b/Level/src/bskyblock/addin/level/config/Settings.java
@@ -0,0 +1,23 @@
+package bskyblock.addin.level.config;
+
+import java.util.HashMap;
+
+import org.bukkit.material.MaterialData;
+
+public class Settings {
+
+ public static String PERMPREFIX;
+ public static boolean sumTeamDeaths;
+ public static int seaHeight;
+ public static HashMap blockLimits;
+ public static HashMap blockValues;
+ public static double underWaterMultiplier;
+ public static int deathpenalty;
+ public static long levelCost;
+ public static Object defaultLanguage;
+ public static int levelWait;
+ public static int maxDeaths;
+ public static boolean islandResetDeathReset;
+ public static boolean teamJoinDeathReset;
+
+}
diff --git a/Level/src/bskyblock/addin/level/event/IslandPostLevelEvent.java b/Level/src/bskyblock/addin/level/event/IslandPostLevelEvent.java
new file mode 100644
index 0000000..6182cdb
--- /dev/null
+++ b/Level/src/bskyblock/addin/level/event/IslandPostLevelEvent.java
@@ -0,0 +1,47 @@
+package bskyblock.addin.level.event;
+
+import java.util.UUID;
+
+import us.tastybento.bskyblock.api.events.IslandEvent;
+import us.tastybento.bskyblock.database.objects.Island;
+
+/**
+ * This event is fired after ASkyBlock calculates an island level and when it sends notification to the player.
+ * Use getLevel() to see the level calculated and getPointsToNextLevel() to see how much points are needed to reach next level.
+ * Canceling this event will result in no notifications to the player.
+ *
+ * @author Poslovitch, tastybento
+ */
+public class IslandPostLevelEvent extends IslandEvent {
+ private long level;
+ private long pointsToNextLevel;
+
+ /**
+ * @param player
+ * @param island
+ * @param l
+ */
+ public IslandPostLevelEvent(UUID player, Island island, long l, long m) {
+ super(island);
+ this.level = l;
+ this.pointsToNextLevel = m;
+ }
+
+ public long getLevel() {
+ return level;
+ }
+
+ public void setLevel(long level) {
+ this.level = level;
+ }
+
+ public long getPointsToNextLevel() {
+ return pointsToNextLevel;
+ }
+
+ public void setPointsToNextLevel(long pointsToNextLevel) {
+ this.pointsToNextLevel = pointsToNextLevel;
+ }
+
+
+}
diff --git a/Level/src/bskyblock/addin/level/event/IslandPreLevelEvent.java b/Level/src/bskyblock/addin/level/event/IslandPreLevelEvent.java
new file mode 100644
index 0000000..c818cf9
--- /dev/null
+++ b/Level/src/bskyblock/addin/level/event/IslandPreLevelEvent.java
@@ -0,0 +1,45 @@
+package bskyblock.addin.level.event;
+
+import java.util.UUID;
+
+import us.tastybento.bskyblock.api.events.IslandEvent;
+import us.tastybento.bskyblock.database.objects.Island;
+
+public class IslandPreLevelEvent extends IslandEvent {
+
+ private UUID targetPlayer;
+ private long level;
+ private long pointsToNextLevel;
+
+
+ public IslandPreLevelEvent(UUID targetPlayer, Island island, long level) {
+ super(island);
+ this.targetPlayer = targetPlayer;
+ this.level = level;
+ }
+
+ public long getPointsToNextLevel() {
+ return pointsToNextLevel;
+ }
+
+ public void setPointsToNextLevel(long pointsToNextLevel) {
+ this.pointsToNextLevel = pointsToNextLevel;
+ }
+
+ public UUID getTargetPlayer() {
+ return targetPlayer;
+ }
+
+ public void setTargetPlayer(UUID targetPlayer) {
+ this.targetPlayer = targetPlayer;
+ }
+
+ public long getLevel() {
+ return level;
+ }
+
+ public void setLevel(long level) {
+ this.level = level;
+ }
+
+}