diff --git a/plugin/src/main/java/com/alttd/cometskyblock/configuration/MessageConfiguration.java b/plugin/src/main/java/com/alttd/cometskyblock/configuration/MessageConfiguration.java index c110cf3..08fe264 100644 --- a/plugin/src/main/java/com/alttd/cometskyblock/configuration/MessageConfiguration.java +++ b/plugin/src/main/java/com/alttd/cometskyblock/configuration/MessageConfiguration.java @@ -20,7 +20,21 @@ public class MessageConfiguration implements Configuration { SetHome setHome = new SetHome(); @ConfigSerializable @Getter public static class SetHome { + String homeSet = "You have set your island home to your current location."; + } + CobbeGen cobbeGen = new CobbeGen(); + @ConfigSerializable @Getter + public static class CobbeGen { + String requiredLevel = "You must reach before upgrading to the next cobblestone generation level."; + String upgraded = "Your cobblestone generation is now level ."; + } + + Level level = new Level(); + @ConfigSerializable @Getter + public static class Level { + String requiredXp = "You need to level up your island."; + String upgraded = "Your island has leveled up!"; } } diff --git a/plugin/src/main/java/com/alttd/cometskyblock/gui/GUIInventory.java b/plugin/src/main/java/com/alttd/cometskyblock/gui/GUIInventory.java index 8063f9f..055ca91 100644 --- a/plugin/src/main/java/com/alttd/cometskyblock/gui/GUIInventory.java +++ b/plugin/src/main/java/com/alttd/cometskyblock/gui/GUIInventory.java @@ -3,6 +3,8 @@ package com.alttd.cometskyblock.gui; import com.alttd.cometskyblock.island.Island; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.minimessage.MiniMessage; +import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; +import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.OfflinePlayer; @@ -106,7 +108,7 @@ public abstract class GUIInventory implements GUI, InventoryHolder { for (int i = 0; i < 9; ++i) { addButton(i, createMenuButton(Material.BLACK_STAINED_GLASS_PANE, "", new ArrayList<>(), event -> {})); } - addButton(5, createIslandMenuButton(island.owner(), event -> {})); + addButton(4, createIslandMenuButton(island.owner(), event -> {})); } public void open(Player player) { @@ -173,10 +175,13 @@ public abstract class GUIInventory implements GUI, InventoryHolder { SkullMeta meta = (SkullMeta) skull.getItemMeta(); meta.setPlayerProfile(offlinePlayer.getPlayerProfile()); - + var placeholders = TagResolver.resolver( + Placeholder.unparsed("islandlevel", island.level() + ""), + Placeholder.unparsed("islandmembers", island.members().size() + "") + ); List metaLore = new ArrayList<>(); - for (String loreString : List.of("Island Level : ", "Members: ", "")) { - metaLore.add(MiniMessage.miniMessage().deserialize(loreString)); + for (String loreString : List.of("Island Level : ", "Members: ", "")) { + metaLore.add(MiniMessage.miniMessage().deserialize(loreString, placeholders)); } meta.lore(metaLore); diff --git a/plugin/src/main/java/com/alttd/cometskyblock/island/gui/MembersGUI.java b/plugin/src/main/java/com/alttd/cometskyblock/island/gui/MembersGUI.java index ca444d2..08eaafe 100644 --- a/plugin/src/main/java/com/alttd/cometskyblock/island/gui/MembersGUI.java +++ b/plugin/src/main/java/com/alttd/cometskyblock/island/gui/MembersGUI.java @@ -37,7 +37,10 @@ public class MembersGUI extends GUIInventory { int startIndex = pageIndex * 45; for (int i = startIndex; i < island.members().size(); i++) { - GUIButton guiButton = createPlayerHeadMenuButton(island.members().get(i), event -> {}); + GUIButton guiButton = createPlayerHeadMenuButton(island.members().get(i), event -> { + // TODO - show last online, some player info + // Island owner can kick from gui + }); if (!addItem(guiButton)) { createNextPageButton(player); break; diff --git a/plugin/src/main/java/com/alttd/cometskyblock/island/gui/SettingsGUI.java b/plugin/src/main/java/com/alttd/cometskyblock/island/gui/SettingsGUI.java index d5025fa..a0733e3 100644 --- a/plugin/src/main/java/com/alttd/cometskyblock/island/gui/SettingsGUI.java +++ b/plugin/src/main/java/com/alttd/cometskyblock/island/gui/SettingsGUI.java @@ -47,6 +47,7 @@ public class SettingsGUI extends GUIInventory { return; } player.getWorld().setSpawnLocation(player.getLocation()); + player.sendRichMessage(islandMessages.setHome().homeSet()); })); super.decorate(player); } diff --git a/plugin/src/main/java/com/alttd/cometskyblock/island/gui/UpgradesGUI.java b/plugin/src/main/java/com/alttd/cometskyblock/island/gui/UpgradesGUI.java index 015812d..cf1fc40 100644 --- a/plugin/src/main/java/com/alttd/cometskyblock/island/gui/UpgradesGUI.java +++ b/plugin/src/main/java/com/alttd/cometskyblock/island/gui/UpgradesGUI.java @@ -1,9 +1,13 @@ package com.alttd.cometskyblock.island.gui; import com.alttd.cometskyblock.CometSkyBlockPlugin; +import com.alttd.cometskyblock.configuration.MessageConfiguration; import com.alttd.cometskyblock.gui.GUIInventory; +import com.alttd.cometskyblock.island.CobblestoneGeneratorLevel; import com.alttd.cometskyblock.island.Island; +import com.alttd.cometskyblock.util.Experience; import net.kyori.adventure.text.minimessage.MiniMessage; +import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.World; @@ -12,6 +16,7 @@ import org.bukkit.entity.Player; import org.bukkit.inventory.Inventory; import java.util.ArrayList; +import java.util.List; public class UpgradesGUI extends GUIInventory { @@ -27,6 +32,7 @@ public class UpgradesGUI extends GUIInventory { @Override public void decorate(Player player) { makeMenuBar(); + MessageConfiguration.Island islandMessages = CometSkyBlockPlugin.instance().messagesConfiguration().get().island(); // Todo - move to handlers, add costs, validation ... // WorldBorder addButton(10, createMenuButton(Material.GRASS_BLOCK, "Expand The world border", new ArrayList<>(), event -> { @@ -40,12 +46,50 @@ public class UpgradesGUI extends GUIInventory { })); // Cobble Gen addButton(11, createMenuButton(Material.COBBLESTONE, "Upgrade your cobble stone generator", new ArrayList<>(), event -> { + int requiredIslandLevel = getRequiredIslandLevel(island.cobblegenLevel() + 1); + if (island.level() < requiredIslandLevel) { + player.sendRichMessage(islandMessages.cobbeGen().requiredLevel(), Placeholder.parsed("islandlevel", "" + requiredIslandLevel + "")); + return; + } island.cobblegenLevel(island.cobblegenLevel() + 1); + player.sendRichMessage(islandMessages.cobbeGen().upgraded(), Placeholder.parsed("level", "" + island.cobblegenLevel() + "")); })); // Level - addButton(12, createMenuButton(Material.ENCHANTING_TABLE, "Increase your island level", new ArrayList<>(), event -> {})); + addButton(12, createMenuButton(Material.EXPERIENCE_BOTTLE, "Increase your island level", List.of( + "Use your experience to upgrade your island level.", + "", + "" + Experience.getExpToNextIslandLevel(island.level()) + " experience required to level up!" + ),event -> { + int xp = Experience.getExpToNextIslandLevel(island.level()); + if (player.getTotalExperience() < xp) { + player.sendRichMessage(islandMessages.level().requiredXp(), Placeholder.parsed("requiredxp", "" + xp + "")); + return; + } + Experience.changeExp(player, player.getTotalExperience() - xp); + })); // Difficulty super.decorate(player); } + Integer getRequiredIslandLevel(int generatorLevel) { + CobblestoneGeneratorLevel level = getLevel(generatorLevel); + return level != null ? level.islandLevel() : 0; + } + + CobblestoneGeneratorLevel getLevel(int generatorLevel) { + if (generatorLevel == 0) { + return null; + } + CometSkyBlockPlugin plugin = CometSkyBlockPlugin.instance(); + if (plugin.cobblestoneGeneratorConfiguration().get().levels().size() < generatorLevel) { + return getLevel(generatorLevel - 1); + } + for (CobblestoneGeneratorLevel level : plugin.cobblestoneGeneratorConfiguration().get().levels()) { + if (level.level() == generatorLevel) { + return level; + } + } + return null; + } + } diff --git a/plugin/src/main/java/com/alttd/cometskyblock/listeners/CobbestoneGeneratorListener.java b/plugin/src/main/java/com/alttd/cometskyblock/listeners/CobbestoneGeneratorListener.java index 12010da..e612045 100644 --- a/plugin/src/main/java/com/alttd/cometskyblock/listeners/CobbestoneGeneratorListener.java +++ b/plugin/src/main/java/com/alttd/cometskyblock/listeners/CobbestoneGeneratorListener.java @@ -69,26 +69,6 @@ public class CobbestoneGeneratorListener implements Listener { return Material.COBBLESTONE; } - Integer getRequiredIslandLevel(int generatorLevel) { - CobblestoneGeneratorLevel level = getLevel(generatorLevel); - return level != null ? level.islandLevel() : 0; - } - - CobblestoneGeneratorLevel getLevel(int generatorLevel) { - if (generatorLevel == 0) { - return null; - } - if (plugin.cobblestoneGeneratorConfiguration().get().levels().size() < generatorLevel) { - return getLevel(generatorLevel - 1); - } - for (CobblestoneGeneratorLevel level : plugin.cobblestoneGeneratorConfiguration().get().levels()) { - if (level.level() == generatorLevel) { - return level; - } - } - return null; - } - boolean relativeEqualsMaterial(Block block, Material material) { return block.getRelative(BlockFace.NORTH).getType().equals(material) || block.getRelative(BlockFace.EAST).getType().equals(material) diff --git a/plugin/src/main/java/com/alttd/cometskyblock/util/Experience.java b/plugin/src/main/java/com/alttd/cometskyblock/util/Experience.java new file mode 100644 index 0000000..f0438f6 --- /dev/null +++ b/plugin/src/main/java/com/alttd/cometskyblock/util/Experience.java @@ -0,0 +1,136 @@ +package com.alttd.cometskyblock.util; + +import org.bukkit.entity.Player; + +/** + * Based on gist + * A utility for managing player experience. + */ +public final class Experience { + + /** + * Calculate a player's total experience based on level and progress to next. + * + * @param player the Player + * @return the amount of experience the Player has + * + * @see Experience#Leveling_up + */ + public static int getExp(Player player) { + return getExpFromLevel(player.getLevel()) + + Math.round(getExpToNext(player.getLevel()) * player.getExp()); + } + + /** + * Calculate total experience based on level. + * + * @param level the level + * @return the total experience calculated + * + * @see Experience#Leveling_up + */ + public static int getExpFromLevel(int level) { + if (level > 30) { + return (int) (4.5 * level * level - 162.5 * level + 2220); + } + if (level > 15) { + return (int) (2.5 * level * level - 40.5 * level + 360); + } + return level * level + 6 * level; + } + + /** + * Calculate level (including progress to next level) based on total experience. + * + * @param exp the total experience + * @return the level calculated + */ + public static double getLevelFromExp(long exp) { + int level = getIntLevelFromExp(exp); + + // Get remaining exp progressing towards next level. Cast to float for next bit of math. + float remainder = exp - (float) getExpFromLevel(level); + + // Get level progress with float precision. + float progress = remainder / getExpToNext(level); + + // Slap both numbers together and call it a day. While it shouldn't be possible for progress + // to be an invalid value (value < 0 || 1 <= value) + return ((double) level) + progress; + } + + /** + * Calculate level based on total experience. + * + * @param exp the total experience + * @return the level calculated + */ + public static int getIntLevelFromExp(long exp) { + if (exp > 1395) { + return (int) ((Math.sqrt(72 * exp - 54215D) + 325) / 18); + } + if (exp > 315) { + return (int) (Math.sqrt(40 * exp - 7839D) / 10 + 8.1); + } + if (exp > 0) { + return (int) (Math.sqrt(exp + 9D) - 3); + } + return 0; + } + + /** + * Get the total amount of experience required to progress to the next level. + * + * @param level the current level + * + * @see Experience#Leveling_up + */ + private static int getExpToNext(int level) { + if (level >= 30) { + // Simplified formula. Internal: 112 + (level - 30) * 9 + return level * 9 - 158; + } + if (level >= 15) { + // Simplified formula. Internal: 37 + (level - 15) * 5 + return level * 5 - 38; + } + // Internal: 7 + level * 2 + return level * 2 + 7; + } + + /** + * Change a Player's experience. + * + *

This method is preferred over {@link Player#giveExp(int)}. + *
In older versions the method does not take differences in exp per level into account. + * This leads to overlevelling when granting players large amounts of experience. + *
In modern versions, while differing amounts of experience per level are accounted for, the + * approach used is loop-heavy and requires an excessive number of calculations, which makes it + * quite slow. + * + * @param player the Player affected + * @param exp the amount of experience to add or remove + */ + public static void changeExp(Player player, int exp) { + exp += getExp(player); + + if (exp < 0) { + exp = 0; + } + + double levelAndExp = getLevelFromExp(exp); + int level = (int) levelAndExp; + player.setLevel(level); + player.setExp((float) (levelAndExp - level)); + } + + public static int getExpToNextIslandLevel(int level) { + if (level <= 15) { + return 2 * level + 7; + } else if (level <= 30) { + return 5 * level - 38; + } else { + return 9 * level - 158; + } + } +} \ No newline at end of file