diff --git a/Compatibility/pom.xml b/Compatibility/pom.xml index b1ac25c1..4ab3993b 100644 --- a/Compatibility/pom.xml +++ b/Compatibility/pom.xml @@ -3,7 +3,7 @@ com.songoda SongodaCore-Modules - 2.5.4 + 2.5.5 ../ diff --git a/Compatibility/src/com/songoda/core/compatibility/ClassMapping.java b/Compatibility/src/com/songoda/core/compatibility/ClassMapping.java index 8022b6ab..1e190849 100644 --- a/Compatibility/src/com/songoda/core/compatibility/ClassMapping.java +++ b/Compatibility/src/com/songoda/core/compatibility/ClassMapping.java @@ -13,6 +13,7 @@ public enum ClassMapping { BLOCK_POSITION("core", "BlockPosition"), CHAT_MESSAGE_TYPE("network.chat", "ChatMessageType"), CHUNK("world.level.chunk", "Chunk"), + ENCHANTMENT_MANAGER("world.item.enchantment", "EnchantmentManager"), ENTITY("world.entity", "Entity"), ENTITY_INSENTIENT("world.entity", "EntityInsentient"), ENTITY_PLAYER("server.level", "EntityPlayer"), @@ -40,6 +41,7 @@ public enum ClassMapping { CRAFT_BLOCK_DATA("block.data", "CraftBlockData"), CRAFT_CHUNK("CraftChunk"), CRAFT_ENTITY("entity", "CraftEntity"), + CRAFT_ITEM_STACK("inventory", "CraftItemStack"), CRAFT_PLAYER("entity", "CraftPlayer"), CRAFT_WORLD("CraftWorld"); diff --git a/Core/pom.xml b/Core/pom.xml index 65f01789..4ab5715e 100644 --- a/Core/pom.xml +++ b/Core/pom.xml @@ -3,7 +3,7 @@ com.songoda SongodaCore-Modules - 2.5.4 + 2.5.5 ../ diff --git a/Core/src/main/java/com/songoda/core/SongodaCore.java b/Core/src/main/java/com/songoda/core/SongodaCore.java index 5523b53c..b365bcc5 100644 --- a/Core/src/main/java/com/songoda/core/SongodaCore.java +++ b/Core/src/main/java/com/songoda/core/SongodaCore.java @@ -56,7 +56,7 @@ public class SongodaCore { /** * This has been added as of Rev 6 */ - private final static String coreVersion = "2.5.4"; + private final static String coreVersion = "2.5.5"; /** * This is specific to the website api diff --git a/Core/src/main/java/com/songoda/core/lootables/Lootables.java b/Core/src/main/java/com/songoda/core/lootables/Lootables.java new file mode 100644 index 00000000..6813a822 --- /dev/null +++ b/Core/src/main/java/com/songoda/core/lootables/Lootables.java @@ -0,0 +1,23 @@ +package com.songoda.core.lootables; + +import com.songoda.core.lootables.loot.LootManager; + +public class Lootables { + + private final String lootablesDir; + + private final LootManager lootManager; + + public Lootables(String lootablesDir) { + this.lootablesDir = lootablesDir; + this.lootManager = new LootManager(this); + } + + public String getLootablesDir() { + return lootablesDir; + } + + public LootManager getLootManager() { + return lootManager; + } +} diff --git a/Core/src/main/java/com/songoda/core/lootables/Modify.java b/Core/src/main/java/com/songoda/core/lootables/Modify.java new file mode 100644 index 00000000..316c2f6a --- /dev/null +++ b/Core/src/main/java/com/songoda/core/lootables/Modify.java @@ -0,0 +1,8 @@ +package com.songoda.core.lootables; + +import com.songoda.core.lootables.loot.Loot; + +public interface Modify { + + Loot Modify(Loot loot); +} diff --git a/Core/src/main/java/com/songoda/core/lootables/gui/AbstractGuiListEditor.java b/Core/src/main/java/com/songoda/core/lootables/gui/AbstractGuiListEditor.java new file mode 100644 index 00000000..23b61d21 --- /dev/null +++ b/Core/src/main/java/com/songoda/core/lootables/gui/AbstractGuiListEditor.java @@ -0,0 +1,78 @@ +package com.songoda.core.lootables.gui; + +import com.songoda.core.compatibility.CompatibleMaterial; +import com.songoda.core.gui.AnvilGui; +import com.songoda.core.gui.Gui; +import com.songoda.core.gui.GuiUtils; +import com.songoda.core.utils.TextUtils; +import com.songoda.core.lootables.loot.Loot; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public abstract class AbstractGuiListEditor extends Gui { + + protected final Loot loot; + private final Gui returnGui; + + public AbstractGuiListEditor(Loot loot ,Gui returnGui) { + super(1, returnGui); + this.returnGui = returnGui; + this.loot = loot; + setDefaultItem(null); + paint(); + } + + public void paint() { + List lore = getData() == null ? new ArrayList<>() : getData(); + setButton(2, GuiUtils.createButtonItem(CompatibleMaterial.OAK_FENCE_GATE, + TextUtils.formatText("&cBack")), + (event) -> { + guiManager.showGUI(event.player, returnGui); + ((GuiLootEditor) returnGui).paint(); + }); + setButton(6, GuiUtils.createButtonItem(CompatibleMaterial.OAK_FENCE_GATE, + TextUtils.formatText("&cBack")), + (event) -> { + guiManager.showGUI(event.player, returnGui); + ((GuiLootEditor) returnGui).paint(); + }); + setButton(3, GuiUtils.createButtonItem(CompatibleMaterial.ARROW, + TextUtils.formatText("&aAdd new line")), + (event -> { + AnvilGui gui = new AnvilGui(event.player, this); + gui.setAction((e -> { + String validated = validate(gui.getInputText()); + if (validated != null) { + lore.add(validated); + updateData(lore.isEmpty() ? null : lore); + e.player.closeInventory(); + paint(); + } + })); + gui.setTitle("Enter a new line"); + guiManager.showGUI(event.player, gui); + })); + + setItem(4, GuiUtils.createButtonItem(CompatibleMaterial.WRITABLE_BOOK, + TextUtils.formatText("&9Lore:"), + lore.isEmpty() + ? TextUtils.formatText(Collections.singletonList("&cNo lore set...")) + : TextUtils.formatText(lore))); + + setButton(5, GuiUtils.createButtonItem(CompatibleMaterial.ARROW, + TextUtils.formatText("&cRemove the last line")), + (event -> { + lore.remove(lore.size() - 1); + updateData(lore); + paint(); + })); + } + + protected abstract List getData(); + + protected abstract void updateData(List list); + + protected abstract String validate(String line); +} diff --git a/Core/src/main/java/com/songoda/core/lootables/gui/GuiEditor.java b/Core/src/main/java/com/songoda/core/lootables/gui/GuiEditor.java new file mode 100644 index 00000000..d0fd0fbb --- /dev/null +++ b/Core/src/main/java/com/songoda/core/lootables/gui/GuiEditor.java @@ -0,0 +1,81 @@ +package com.songoda.core.lootables.gui; + +import com.songoda.core.compatibility.CompatibleMaterial; +import com.songoda.core.gui.Gui; +import com.songoda.core.gui.GuiUtils; +import com.songoda.core.lootables.loot.LootManager; +import com.songoda.core.lootables.loot.Lootable; +import org.bukkit.entity.EntityType; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; + +import java.util.ArrayList; +import java.util.List; + +public class GuiEditor extends Gui { + + private final LootManager lootManager; + + public GuiEditor(LootManager lootManager) { + super(6); + this.lootManager = lootManager; + setDefaultItem(null); + setTitle("Lootables Overview"); + + paint(); + } + + private void paint() { + if (inventory != null) + inventory.clear(); + setActionForRange(0, 0, 5, 9, null); + + List lootables = new ArrayList<>(lootManager.getRegisteredLootables().values()); + + double itemCount = lootables.size(); + this.pages = (int) Math.max(1, Math.ceil(itemCount / 36)); + + if (page != 1) + setButton(5, 2, GuiUtils.createButtonItem(CompatibleMaterial.ARROW, "Back"), + (event) -> { + page--; + paint(); + }); + + if (page != pages) + setButton(5, 6, GuiUtils.createButtonItem(CompatibleMaterial.ARROW, "Next"), + (event) -> { + page++; + paint(); + }); + + for (int i = 9; i < 45; i++) { + int current = ((page - 1) * 36) - 9; + if (current + i >= lootables.size()) { + setItem(i, null); + continue; + } + Lootable lootable = lootables.get(current + i); + if (lootable == null) continue; + + setButton(i, getIcon(lootable.getKey()), + (event) -> guiManager.showGUI(event.player, new GuiLootableEditor(lootManager, lootable, this))); + } + } + + public ItemStack getIcon(String key) { + ItemStack stack = null; + EntityType type = EntityType.fromName(key); + if (type != null) { + CompatibleMaterial material = CompatibleMaterial.getSpawnEgg(type); + if (material != null) + stack = material.getItem(); + } + if (stack == null) + stack = CompatibleMaterial.GHAST_SPAWN_EGG.getItem(); + ItemMeta meta = stack.getItemMeta(); + meta.setDisplayName(key); + stack.setItemMeta(meta); + return stack; + } +} diff --git a/Core/src/main/java/com/songoda/core/lootables/gui/GuiEnchantEditor.java b/Core/src/main/java/com/songoda/core/lootables/gui/GuiEnchantEditor.java new file mode 100644 index 00000000..aaae930b --- /dev/null +++ b/Core/src/main/java/com/songoda/core/lootables/gui/GuiEnchantEditor.java @@ -0,0 +1,96 @@ +package com.songoda.core.lootables.gui; + +import com.songoda.core.compatibility.CompatibleMaterial; +import com.songoda.core.gui.AnvilGui; +import com.songoda.core.gui.Gui; +import com.songoda.core.gui.GuiUtils; +import com.songoda.core.lootables.loot.Loot; +import com.songoda.core.utils.TextUtils; +import com.songoda.core.lootables.loot.Loot; +import org.bukkit.enchantments.Enchantment; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class GuiEnchantEditor extends Gui { + + private final Gui returnGui; + private final Loot loot; + + public GuiEnchantEditor(Loot loot, Gui returnGui) { + super(1, returnGui); + this.returnGui = returnGui; + this.loot = loot; + setDefaultItem(null); + setTitle("Enchantment Editor"); + paint(); + } + + public void paint() { + Map lore = loot.getEnchants() == null ? new HashMap<>() : new HashMap<>(loot.getEnchants()); + setButton(2, GuiUtils.createButtonItem(CompatibleMaterial.OAK_FENCE_GATE, + TextUtils.formatText("&cBack")), + (event) -> { + guiManager.showGUI(event.player, returnGui); + ((GuiLootEditor) returnGui).paint(); + }); + setButton(6, GuiUtils.createButtonItem(CompatibleMaterial.OAK_FENCE_GATE, + TextUtils.formatText("&cBack")), + (event) -> { + guiManager.showGUI(event.player, returnGui); + ((GuiLootEditor) returnGui).paint(); + }); + setButton(3, GuiUtils.createButtonItem(CompatibleMaterial.ARROW, + TextUtils.formatText("&aAdd new line")), + (event -> { + AnvilGui gui = new AnvilGui(event.player, this); + gui.setAction((e -> { + if (Enchantment.getByName(gui.getInputText().toUpperCase().trim()) == null) { + e.player.sendMessage("That is not a valid enchantment."); + e.player.closeInventory(); + return; + } + + AnvilGui gui1 = new AnvilGui(event.player, this); + gui1.setAction((ee -> { + lore.put(gui.getInputText().toUpperCase().trim(), Integer.parseInt(gui1.getInputText().trim())); + loot.setEnchants(lore.isEmpty() ? null : lore); + ee.player.closeInventory(); + paint(); + })); + gui1.setTitle("Enter a level"); + guiManager.showGUI(event.player, gui1); + })); + gui.setTitle("Enter an enchant"); + guiManager.showGUI(event.player, gui); + })); + + List enchantments = new ArrayList<>(); + + String last = null; + + if (!lore.isEmpty()) + for (Map.Entry entry : lore.entrySet()) { + last = entry.getKey(); + enchantments.add("&6" + entry.getKey() + " " + entry.getValue()); + } + + setItem(4, GuiUtils.createButtonItem(CompatibleMaterial.WRITABLE_BOOK, + TextUtils.formatText("&7Enchant Override:"), + lore.isEmpty() + ? TextUtils.formatText(Collections.singletonList("&cNo enchantments set...")) + : TextUtils.formatText(enchantments))); + + String lastFinal = last; + setButton(5, GuiUtils.createButtonItem(CompatibleMaterial.ARROW, + TextUtils.formatText("&cRemove the last line")), + (event -> { + lore.remove(lastFinal); + loot.setEnchants(lore); + paint(); + })); + } +} diff --git a/Core/src/main/java/com/songoda/core/lootables/gui/GuiEntityEditor.java b/Core/src/main/java/com/songoda/core/lootables/gui/GuiEntityEditor.java new file mode 100644 index 00000000..56e2faa2 --- /dev/null +++ b/Core/src/main/java/com/songoda/core/lootables/gui/GuiEntityEditor.java @@ -0,0 +1,36 @@ +package com.songoda.core.lootables.gui; + +import com.songoda.core.gui.Gui; +import com.songoda.core.lootables.loot.Loot; +import org.bukkit.entity.EntityType; + +import java.util.List; +import java.util.stream.Collectors; + +public class GuiEntityEditor extends AbstractGuiListEditor { + + public GuiEntityEditor(Loot loot, Gui returnGui) { + super(loot, returnGui); + } + + @Override + protected List getData() { + return loot.getOnlyDropFor().stream().map(Enum::name).collect(Collectors.toList()); + } + + @Override + protected void updateData(List list) { + loot.setOnlyDropFor(list.stream().map(EntityType::valueOf).collect(Collectors.toList())); + } + + @Override + protected String validate(String line) { + line = line.toUpperCase().trim(); + try { + EntityType.valueOf(line); + return line; + } catch (IllegalArgumentException e) { + return null; + } + } +} diff --git a/Core/src/main/java/com/songoda/core/lootables/gui/GuiLootEditor.java b/Core/src/main/java/com/songoda/core/lootables/gui/GuiLootEditor.java new file mode 100644 index 00000000..8e2d0228 --- /dev/null +++ b/Core/src/main/java/com/songoda/core/lootables/gui/GuiLootEditor.java @@ -0,0 +1,268 @@ +package com.songoda.core.lootables.gui; + +import com.songoda.core.compatibility.CompatibleMaterial; +import com.songoda.core.gui.AnvilGui; +import com.songoda.core.gui.Gui; +import com.songoda.core.gui.GuiUtils; +import com.songoda.core.utils.TextUtils; +import com.songoda.core.lootables.loot.Loot; +import com.songoda.core.lootables.loot.LootBuilder; +import com.songoda.core.lootables.loot.LootManager; +import org.bukkit.entity.EntityType; +import org.bukkit.event.inventory.ClickType; +import org.bukkit.inventory.ItemStack; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +public class GuiLootEditor extends Gui { + + private final LootManager lootManager; + private final Loot loot; + private final Gui returnGui; + + public GuiLootEditor(LootManager lootManager, Loot loot, Gui returnGui) { + super(6, returnGui); + this.lootManager = lootManager; + this.loot = loot; + this.returnGui = returnGui; + setDefaultItem(null); + setTitle("Loot Editor"); + paint(); + setOnClose((event) -> + lootManager.saveLootables(false)); + } + + public void paint() { + if (inventory != null) + inventory.clear(); + setActionForRange(0, 0, 5, 9, null); + + setButton(8, GuiUtils.createButtonItem(CompatibleMaterial.OAK_DOOR, + TextUtils.formatText("&cBack")), + (event) -> { + guiManager.showGUI(event.player, returnGui); + }); + + setButton(9, GuiUtils.createButtonItem(loot.getMaterial() == null ? CompatibleMaterial.BARRIER : loot.getMaterial(), + TextUtils.formatText("&7Current Material: &6" + (loot.getMaterial() != null + ? loot.getMaterial().name() : "None")), TextUtils.formatText( + Arrays.asList("", + "&8Click to set the material to", + "&8the material in your hand.") + )), (event) -> { + ItemStack stack = event.player.getInventory().getItemInMainHand(); + loot.setMaterial(CompatibleMaterial.getMaterial(stack)); + paint(); + }); + + setButton(10, GuiUtils.createButtonItem(CompatibleMaterial.PAPER, + TextUtils.formatText("&7Name Override: &6" + (loot.getName() == null ? "None set" : loot.getName()))), + (event) -> { + AnvilGui gui = new AnvilGui(event.player, this); + gui.setAction((e -> { + loot.setName(gui.getInputText().trim()); + paint(); + e.player.closeInventory(); + })); + guiManager.showGUI(event.player, gui); + gui.setInput(GuiUtils.createButtonItem(CompatibleMaterial.PAPER, loot.getName())); + }); + + setButton(11, GuiUtils.createButtonItem(CompatibleMaterial.WRITABLE_BOOK, + TextUtils.formatText("&7Lore Override:"), + TextUtils.formatText(loot.getLore() == null ? Collections.singletonList("&6None set") : loot.getLore())), + (event) -> guiManager.showGUI(event.player, new GuiLoreEditor(loot, this))); + + List enchantments = new ArrayList<>(); + + if (loot.getEnchants() != null) + for (Map.Entry entry : loot.getEnchants().entrySet()) + enchantments.add("&6" + entry.getKey() + " " + entry.getValue()); + + setButton(12, GuiUtils.createButtonItem(CompatibleMaterial.ENCHANTED_BOOK, + TextUtils.formatText("&7Enchantments:"), + TextUtils.formatText(enchantments.isEmpty() ? Collections.singletonList("&6None set") : enchantments)), + (event) -> guiManager.showGUI(event.player, new GuiEnchantEditor(loot, this))); + + setButton(13, GuiUtils.createButtonItem( + loot.getBurnedMaterial() == null + ? CompatibleMaterial.FIRE_CHARGE + : loot.getBurnedMaterial(), + TextUtils.formatText("&7Current Burned Material: &6" + + (loot.getBurnedMaterial() == null + ? "None" + : loot.getBurnedMaterial().name())), TextUtils.formatText( + Arrays.asList("", + "&8Click to set the burned material to", + "&8the material in your hand.") + )), + (event) -> { + ItemStack stack = event.player.getInventory().getItemInMainHand(); + loot.setBurnedMaterial(CompatibleMaterial.getMaterial(stack)); + paint(); + }); + + setButton(14, GuiUtils.createButtonItem(CompatibleMaterial.CLOCK, + TextUtils.formatText("&7Chance: &6" + loot.getChance()), + TextUtils.formatText( + Arrays.asList("", + "&8Click to edit this loots", + "&8drop chance.") + )), + (event) -> { + AnvilGui gui = new AnvilGui(event.player, this); + gui.setAction((e) -> { + loot.setChance(Double.parseDouble(gui.getInputText())); + paint(); + e.player.closeInventory(); + }); + gui.setInput(GuiUtils.createButtonItem(CompatibleMaterial.PAPER, + String.valueOf(loot.getChance()))); + guiManager.showGUI(event.player, gui); + }); + + setButton(15, GuiUtils.createButtonItem(CompatibleMaterial.REDSTONE, + TextUtils.formatText("&7Min Drop Amount: &6" + loot.getMin())), + (event) -> { + AnvilGui gui = new AnvilGui(event.player, this); + gui.setAction((e) -> { + loot.setMin(Integer.parseInt(gui.getInputText())); + paint(); + e.player.closeInventory(); + }); + gui.setInput(GuiUtils.createButtonItem(CompatibleMaterial.PAPER, + String.valueOf(loot.getMin()))); + guiManager.showGUI(event.player, gui); + }); + + setButton(16, GuiUtils.createButtonItem(CompatibleMaterial.GLOWSTONE_DUST, + TextUtils.formatText("&7Max Drop Amount: &6" + loot.getMax())), + (event) -> { + AnvilGui gui = new AnvilGui(event.player, this); + gui.setAction((e) -> { + loot.setMax(Integer.parseInt(gui.getInputText())); + paint(); + e.player.closeInventory(); + }); + gui.setInput(GuiUtils.createButtonItem(CompatibleMaterial.PAPER, + String.valueOf(loot.getMax()))); + guiManager.showGUI(event.player, gui); + }); + + setButton(17, GuiUtils.createButtonItem(CompatibleMaterial.REDSTONE, + TextUtils.formatText("&7Min Item Damage: &6" + loot.getDamageMin())), + (event) -> { + AnvilGui gui = new AnvilGui(event.player, this); + gui.setAction((e) -> { + loot.setDamageMin(Integer.parseInt(gui.getInputText())); + paint(); + e.player.closeInventory(); + }); + gui.setInput(GuiUtils.createButtonItem(CompatibleMaterial.PAPER, + String.valueOf(loot.getDamageMin()))); + guiManager.showGUI(event.player, gui); + }); + + setButton(18, GuiUtils.createButtonItem(CompatibleMaterial.GLOWSTONE_DUST, + TextUtils.formatText("&7Max Item Damage: &6" + loot.getDamageMax())), + (event) -> { + AnvilGui gui = new AnvilGui(event.player, this); + gui.setAction((e) -> { + loot.setDamageMax(Integer.parseInt(gui.getInputText())); + paint(); + e.player.closeInventory(); + }); + gui.setInput(GuiUtils.createButtonItem(CompatibleMaterial.PAPER, + String.valueOf(loot.getDamageMax()))); + guiManager.showGUI(event.player, gui); + }); + + setButton(19, GuiUtils.createButtonItem(CompatibleMaterial.CHEST, + TextUtils.formatText("&7Allow Looting Enchantment?: &6" + loot.isAllowLootingEnchant())), + (event) -> { + loot.setAllowLootingEnchant(!loot.isAllowLootingEnchant()); + paint(); + event.player.closeInventory(); + }); + + setButton(20, GuiUtils.createButtonItem(CompatibleMaterial.REDSTONE, + TextUtils.formatText("&7Min Child Loot Min: &6" + loot.getChildDropCountMin())), + (event) -> { + AnvilGui gui = new AnvilGui(event.player, this); + gui.setAction((e) -> { + loot.setChildDropCountMin(Integer.parseInt(gui.getInputText())); + paint(); + e.player.closeInventory(); + }); + gui.setInput(GuiUtils.createButtonItem(CompatibleMaterial.PAPER, + String.valueOf(loot.getChildDropCountMin()))); + guiManager.showGUI(event.player, gui); + }); + + setButton(21, GuiUtils.createButtonItem(CompatibleMaterial.GLOWSTONE_DUST, + TextUtils.formatText("&7Min Child Loot Max: &6" + loot.getChildDropCountMax())), + (event) -> { + AnvilGui gui = new AnvilGui(event.player, this); + gui.setAction((e) -> { + loot.setChildDropCountMax(Integer.parseInt(gui.getInputText())); + paint(); + e.player.closeInventory(); + }); + gui.setInput(GuiUtils.createButtonItem(CompatibleMaterial.PAPER, + String.valueOf(loot.getChildDropCountMax()))); + guiManager.showGUI(event.player, gui); + }); + + List entities = new ArrayList<>(); + + if (loot.getOnlyDropFor() != null) + for (EntityType entity : loot.getOnlyDropFor()) + entities.add("&6" + entity.name()); + + setButton(22, GuiUtils.createButtonItem(CompatibleMaterial.ENCHANTED_BOOK, + TextUtils.formatText("&7Only Drop For:"), + TextUtils.formatText(entities)), + (event) -> guiManager.showGUI(event.player, new GuiEntityEditor(loot, this))); + + setButton(4, 0, GuiUtils.createButtonItem(CompatibleMaterial.LIME_DYE, TextUtils.formatText("&aCreate new Child Loot")), + (event -> { + AnvilGui gui = new AnvilGui(event.player, this); + gui.setAction((event1 -> { + try { + loot.addChildLoots(new LootBuilder().setMaterial(CompatibleMaterial + .valueOf(gui.getInputText().trim())).build()); + } catch (IllegalArgumentException e) { + event.player.sendMessage("That is not a valid material."); + } + event.player.closeInventory(); + paint(); + })); + gui.setTitle("Enter a material"); + guiManager.showGUI(event.player, gui); + })); + + int i = 9 * 5; + for (Loot loot : loot.getChildLoot()) { + ItemStack item = loot.getMaterial() == null + ? CompatibleMaterial.BARRIER.getItem() + : GuiUtils.createButtonItem(loot.getMaterial(), null, + TextUtils.formatText("&6Left click &7to edit"), + TextUtils.formatText("&6Right click &7to destroy")); + + setButton(i, item, + (event) -> { + if (event.clickType == ClickType.RIGHT) { + this.loot.removeChildLoot(loot); + paint(); + } else if (event.clickType == ClickType.LEFT) { + guiManager.showGUI(event.player, new GuiLootEditor(lootManager, loot, this)); + } + }); + i++; + } + } +} diff --git a/Core/src/main/java/com/songoda/core/lootables/gui/GuiLootableEditor.java b/Core/src/main/java/com/songoda/core/lootables/gui/GuiLootableEditor.java new file mode 100644 index 00000000..69f44010 --- /dev/null +++ b/Core/src/main/java/com/songoda/core/lootables/gui/GuiLootableEditor.java @@ -0,0 +1,78 @@ +package com.songoda.core.lootables.gui; + +import com.songoda.core.compatibility.CompatibleMaterial; +import com.songoda.core.gui.AnvilGui; +import com.songoda.core.gui.Gui; +import com.songoda.core.gui.GuiUtils; +import com.songoda.core.utils.TextUtils; +import com.songoda.core.lootables.loot.Loot; +import com.songoda.core.lootables.loot.LootBuilder; +import com.songoda.core.lootables.loot.LootManager; +import com.songoda.core.lootables.loot.Lootable; +import org.bukkit.event.inventory.ClickType; +import org.bukkit.inventory.ItemStack; + +public class GuiLootableEditor extends Gui { + + private final LootManager lootManager; + private final Lootable lootable; + private final Gui returnGui; + + public GuiLootableEditor(LootManager lootManager, Lootable lootable, Gui returnGui) { + super(6); + this.lootManager = lootManager; + this.lootable = lootable; + this.returnGui = returnGui; + setOnClose((event) -> + lootManager.saveLootables(false)); + setDefaultItem(null); + setTitle("Lootables Editor"); + paint(); + } + + private void paint() { + if (inventory != null) + inventory.clear(); + setActionForRange(0, 0, 5, 9, null); + + setButton(0, GuiUtils.createButtonItem(CompatibleMaterial.LIME_DYE, TextUtils.formatText("&aCreate new Loot")), + (event -> { + AnvilGui gui = new AnvilGui(event.player, this); + gui.setAction((event1 -> { + try { + lootable.registerLoot(new LootBuilder().setMaterial(CompatibleMaterial + .valueOf(gui.getInputText().trim())).build()); + } catch (IllegalArgumentException e) { + event.player.sendMessage("That is not a valid material."); + } + event.player.closeInventory(); + paint(); + })); + gui.setTitle("Enter a material"); + guiManager.showGUI(event.player, gui); + })); + + setButton(8, GuiUtils.createButtonItem(CompatibleMaterial.OAK_DOOR, TextUtils.formatText("&cBack")), + (event -> guiManager.showGUI(event.player, returnGui))); + + int i = 9; + for (Loot loot : lootable.getRegisteredLoot()) { + ItemStack item = loot.getMaterial() == null + ? CompatibleMaterial.BARRIER.getItem() + : GuiUtils.createButtonItem(loot.getMaterial(), null, + TextUtils.formatText("&6Left click &7to edit"), + TextUtils.formatText("&6Right click &7to destroy")); + + setButton(i, item, + (event) -> { + if (event.clickType == ClickType.RIGHT) { + lootable.removeLoot(loot); + paint(); + } else if (event.clickType == ClickType.LEFT) { + guiManager.showGUI(event.player, new GuiLootEditor(lootManager, loot, this)); + } + }); + i++; + } + } +} diff --git a/Core/src/main/java/com/songoda/core/lootables/gui/GuiLoreEditor.java b/Core/src/main/java/com/songoda/core/lootables/gui/GuiLoreEditor.java new file mode 100644 index 00000000..3a938bb4 --- /dev/null +++ b/Core/src/main/java/com/songoda/core/lootables/gui/GuiLoreEditor.java @@ -0,0 +1,28 @@ +package com.songoda.core.lootables.gui; + +import com.songoda.core.gui.Gui; +import com.songoda.core.lootables.loot.Loot; + +import java.util.List; + +public class GuiLoreEditor extends AbstractGuiListEditor { + + public GuiLoreEditor(Loot loot, Gui returnGui) { + super(loot, returnGui); + } + + @Override + protected List getData() { + return loot.getLore(); + } + + @Override + protected void updateData(List list) { + loot.setLore(list); + } + + @Override + protected String validate(String line) { + return line.trim(); + } +} diff --git a/Core/src/main/java/com/songoda/core/lootables/loot/Drop.java b/Core/src/main/java/com/songoda/core/lootables/loot/Drop.java new file mode 100644 index 00000000..7a08f1fe --- /dev/null +++ b/Core/src/main/java/com/songoda/core/lootables/loot/Drop.java @@ -0,0 +1,48 @@ +package com.songoda.core.lootables.loot; + +import org.bukkit.inventory.ItemStack; + +public class Drop { + + private ItemStack itemStack; + + private String command; + + private int xp; + + public Drop(ItemStack itemStack) { + this.itemStack = itemStack; + } + + public Drop(String command) { + this.command = command; + } + + public Drop(int xp) { + this.xp = xp; + } + + public String getCommand() { + return command; + } + + public void setCommand(String command) { + this.command = command; + } + + public int getXp() { + return xp; + } + + public void setXp(int xp) { + this.xp = xp; + } + + public ItemStack getItemStack() { + return itemStack; + } + + public void setItemStack(ItemStack itemStack) { + this.itemStack = itemStack; + } +} diff --git a/Core/src/main/java/com/songoda/core/lootables/loot/DropUtils.java b/Core/src/main/java/com/songoda/core/lootables/loot/DropUtils.java new file mode 100644 index 00000000..7bda0ccd --- /dev/null +++ b/Core/src/main/java/com/songoda/core/lootables/loot/DropUtils.java @@ -0,0 +1,69 @@ +package com.songoda.core.lootables.loot; + +import org.bukkit.Bukkit; +import org.bukkit.entity.LivingEntity; +import org.bukkit.event.entity.EntityDeathEvent; +import org.bukkit.inventory.ItemStack; + +import java.util.ArrayList; +import java.util.List; + +public class DropUtils { + + public static void processStackedDrop(LivingEntity entity, List drops, EntityDeathEvent event) { + int xpToDrop = event.getDroppedExp(); + List items = new ArrayList<>(); + List commands = new ArrayList<>(); + List xp = new ArrayList<>(); + for (Drop drop : drops) { + if (drop == null) continue; + + ItemStack droppedItem = drop.getItemStack(); + if (droppedItem != null) { + droppedItem = droppedItem.clone(); + boolean success = false; + for (ItemStack item : items) { + if (item.getType() != droppedItem.getType() + || item.getDurability() != droppedItem.getDurability() + || item.getAmount() + droppedItem.getAmount() > droppedItem.getMaxStackSize()) continue; + item.setAmount(item.getAmount() + droppedItem.getAmount()); + success = true; + break; + } + if (!success) + items.add(droppedItem); + } + if (drop.getCommand() != null) + commands.add(drop.getCommand()); + + if (drop.getXp() != 0) + xp.add(drop.getXp()); + } + + event.getDrops().clear(); + + if (!items.isEmpty()) + dropItems(items, event); + else if (!commands.isEmpty()) + runCommands(entity, commands); + + for (int x : xp) + xpToDrop += x; + event.setDroppedExp(xpToDrop); + } + + private static void dropItems(List items, EntityDeathEvent event) { + for (ItemStack item : items) + event.getDrops().add(item); + } + + private static void runCommands(LivingEntity entity, List commands) { + for (String command : commands) { + if (entity.getKiller() != null) + command = command.replace("%player%", entity.getKiller().getName()); + if (!command.contains("%player%")) + Bukkit.dispatchCommand(Bukkit.getConsoleSender(), command); + } + } + +} diff --git a/Core/src/main/java/com/songoda/core/lootables/loot/Loot.java b/Core/src/main/java/com/songoda/core/lootables/loot/Loot.java new file mode 100644 index 00000000..21232cbc --- /dev/null +++ b/Core/src/main/java/com/songoda/core/lootables/loot/Loot.java @@ -0,0 +1,314 @@ +package com.songoda.core.lootables.loot; + +import com.google.gson.annotations.SerializedName; +import com.songoda.core.compatibility.CompatibleMaterial; +import com.songoda.core.utils.ItemUtils; +import com.songoda.core.utils.TextUtils; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.entity.EntityType; +import org.bukkit.inventory.ItemStack; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Random; + +public class Loot { + + // Command ran for this drop. + @SerializedName("Command") + private String command; + + // Xp for this drop. + @SerializedName("xp") + private int xp = 0; + + // Material used for this drop. + @SerializedName("Type") + private CompatibleMaterial material; + + // The override for the item name. + @SerializedName("Name") + private String name = null; + + // The override for the item lore. + @SerializedName("Lore") + private List lore = null; + + // The override for the item enchantments. + @SerializedName("Enchantments") + private Map enchants = null; + + // Material used if entity died on fire. + @SerializedName("Burned Type") + private CompatibleMaterial burnedMaterial = null; + + // Chance that this drop will take place. + @SerializedName("Chance") + private double chance = 100; + + // Minimum amount of this item. + @SerializedName("Min") + private int min = 1; + + // Maximum amount of this item. + @SerializedName("Max") + private int max = 1; + + // The override for chances applied by the wield item. + @SerializedName("Wielded Enchantment Chance Overrides") + private Map enchantChances = null; + + // Min amount of applied damage. + @SerializedName("Damage Min") + private Integer damageMin = null; + + // Max amount of applied damage. + @SerializedName("Damage Max") + private Integer damageMax = null; + + // Will the looting enchantment be usable for this loot? + @SerializedName("Looting") + private boolean allowLootingEnchant = true; + + // The looting chance increase. + @SerializedName("Looting Chance Increase") + private Double lootingIncrease; + + // Should this drop only be applicable for specific entities? + @SerializedName("Only Drop For") + private List onlyDropFor; + + // How many child loots should drop? + @SerializedName("Child Loot Drop Count Min") + private Integer childDropCountMin; + @SerializedName("Child Loot Drop Count Max") + private Integer childDropCountMax; + + // Should this drop house child drops? + @SerializedName("Child Loot") + private List childLoot; + + // Should the entity be charged? (Only works on creepers) + private boolean requireCharged = false; + + public CompatibleMaterial getMaterial() { + return material; + } + + public void setMaterial(CompatibleMaterial material) { + this.material = material; + } + + public String getCommand() { + return command; + } + + public void setCommand(String command) { + this.command = command; + } + + public int getXp() { + return xp; + } + + public void setXp(int xp) { + this.xp = xp; + } + + public String getName() { + return TextUtils.formatText(name); + } + + public void setName(String name) { + this.name = name; + } + + public List getLore() { + if (lore == null) return null; + List lore = new ArrayList<>(); + for (String line : this.lore) + lore.add(TextUtils.formatText(line)); + + return lore; + } + + public void setLore(List lore) { + this.lore = new ArrayList<>(lore); + } + + public ItemStack getEnchants(ItemStack item) { + if (enchants == null) return null; + Map enchants = new HashMap<>(); + for (Map.Entry entry : this.enchants.entrySet()) { + + if (entry.getValue() == null) continue; + + if (entry.getKey().equalsIgnoreCase("RANDOM")) { + item = ItemUtils.applyRandomEnchants(item, entry.getValue()); + continue; + } + enchants.put(Enchantment.getByName(entry.getKey()), entry.getValue()); + } + item.addEnchantments(enchants); + return item; + } + + public void setEnchants(Map enchants) { + this.enchants = enchants; + } + + + public void setEnchantChances(Map enchants) { + this.enchantChances = enchants; + } + + public Map getEnchants() { + return enchants == null ? null : Collections.unmodifiableMap(enchants); + } + + public CompatibleMaterial getBurnedMaterial() { + return burnedMaterial; + } + + public void setBurnedMaterial(CompatibleMaterial burnedMaterial) { + this.burnedMaterial = burnedMaterial; + } + + public double getChance() { + return chance; + } + + public void setChance(double chance) { + this.chance = chance; + } + + public boolean runChance(int looting, ItemStack murderWeapon) { + double chance = this.chance; + if (enchantChances != null && murderWeapon != null && enchants != null) { + for (Map.Entry entry : murderWeapon.getEnchantments().entrySet()) { + String key = entry.getKey().getName() + ":" + entry.getValue(); + if (!enchants.containsKey(key)) continue; + double ch = enchantChances.get(key); + if (ch > chance) + chance = enchantChances.get(key); + } + } + return (Math.random() * 100) - (chance + (lootingIncrease == null ? 1 + : lootingIncrease * looting)) < 0 || chance == 100; + } + + public int getMin() { + return min; + } + + public void setMin(int min) { + this.min = min; + } + + public int getMax() { + return max; + } + + public void setMax(int max) { + this.max = max; + } + + public int getDamageMax() { + return damageMax == null ? 0 : damageMax; + } + + public void setDamageMax(int damageMax) { + this.damageMax = damageMax; + } + + public int getDamageMin() { + return damageMin == null ? 0 : damageMin; + } + + public void setDamageMin(int damageMin) { + this.damageMin = damageMin; + } + + public int getAmountToDrop(int looting) { + return min == max ? (max + getLooting(looting)) : new Random().nextInt((max + getLooting(looting)) - min + 1) + min; + } + + public int getLooting(int looting) { + return allowLootingEnchant ? looting : 0; + } + + public boolean isAllowLootingEnchant() { + return allowLootingEnchant; + } + + public void setAllowLootingEnchant(boolean allowLootingEnchant) { + this.allowLootingEnchant = allowLootingEnchant; + } + + public void setLootingIncrease(double increase) { + this.lootingIncrease = increase; + } + + public void addChildLoots(Loot... loots) { + this.childDropCountMin = 1; + this.childDropCountMax = 1; + if (childLoot == null) + this.childLoot = new ArrayList<>(); + this.childLoot.addAll(Arrays.asList(loots)); + } + + public void removeChildLoot(Loot loot) { + if (childLoot == null) return; + this.childLoot.remove(loot); + } + + public List getChildLoot() { + return childLoot == null ? new ArrayList<>() : new ArrayList<>(childLoot); + } + + public List getOnlyDropFor() { + return onlyDropFor == null ? new ArrayList<>() : new ArrayList<>(onlyDropFor); + } + + public void addOnlyDropFor(EntityType... types) { + this.onlyDropFor = new ArrayList<>(); + this.onlyDropFor.addAll(Arrays.asList(types)); + } + + public void setOnlyDropFor(List types) { + this.onlyDropFor = types; + } + + public void setChildDropCountMin(int childDropCountMin) { + this.childDropCountMin = childDropCountMin; + } + + public void setChildDropCountMax(int childDropCountMax) { + this.childDropCountMax = childDropCountMax; + } + + public Integer getChildDropCountMin() { + return childDropCountMin; + } + + public Integer getChildDropCountMax() { + return childDropCountMax; + } + + public int getChildDropCount() { + if (childDropCountMin == null || childDropCountMax == null) return 0; + return new Random().nextInt(childDropCountMax - childDropCountMin + 1) + childDropCountMin; + } + + public boolean isRequireCharged() { + return requireCharged; + } + + public void setRequireCharged(boolean requireCharged) { + this.requireCharged = requireCharged; + } +} diff --git a/Core/src/main/java/com/songoda/core/lootables/loot/LootBuilder.java b/Core/src/main/java/com/songoda/core/lootables/loot/LootBuilder.java new file mode 100644 index 00000000..bfaf5053 --- /dev/null +++ b/Core/src/main/java/com/songoda/core/lootables/loot/LootBuilder.java @@ -0,0 +1,143 @@ +package com.songoda.core.lootables.loot; + + +import com.songoda.core.compatibility.CompatibleMaterial; +import com.songoda.core.lootables.loot.objects.EnchantChance; +import org.bukkit.entity.EntityType; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +public final class LootBuilder { + + private final Loot loot; + + public LootBuilder() { + this.loot = new Loot(); + } + + public LootBuilder setMaterial(CompatibleMaterial material) { + this.loot.setMaterial(material); + return this; + } + + public LootBuilder setName(String name) { + this.loot.setName(name); + return this; + } + + public LootBuilder addLore(String... lore) { + this.loot.setLore(Arrays.asList(lore)); + return this; + } + + public LootBuilder addEnchants(Tuple... tuples) { + Map enchants = new HashMap<>(); + for (Tuple tuple : tuples) + enchants.put((String)tuple.getKey(), (int)tuple.getValue()); + this.loot.setEnchants(enchants); + return this; + } + + public LootBuilder addEnchantChances(EnchantChance... enchantChances) { + Map enchants = new HashMap<>(); + for (EnchantChance chance : enchantChances) + enchants.put(chance.getEnchantment().getName() + ":" + chance.getLevel(), chance.getChanceOverride()); + this.loot.setEnchantChances(enchants); + return this; + } + + public LootBuilder setBurnedMaterial(CompatibleMaterial material) { + this.loot.setBurnedMaterial(material); + return this; + } + + public LootBuilder setChance(double chance) { + this.loot.setChance(chance); + return this; + } + + public LootBuilder setMin(int min) { + this.loot.setMin(min); + return this; + } + + public LootBuilder setMax(int max) { + this.loot.setMax(max); + return this; + } + + public LootBuilder setDamageMin(int min) { + this.loot.setDamageMin(min); + return this; + } + + public LootBuilder setDamageMax(int max) { + this.loot.setDamageMax(max); + return this; + } + + public LootBuilder setAllowLootingEnchant(boolean allow) { + this.loot.setAllowLootingEnchant(allow); + return this; + } + + public LootBuilder setLootingIncrease(double increase) { + this.loot.setLootingIncrease(increase); + return this; + } + + public LootBuilder addOnlyDropFors(EntityType... types) { + this.loot.addOnlyDropFor(types); + return this; + } + + public LootBuilder addChildLoot(Loot... loots) { + this.loot.addChildLoots(loots); + return this; + } + + public LootBuilder setChildDropCount(int count) { + this.loot.setChildDropCountMin(count); + this.loot.setChildDropCountMax(count); + return this; + } + + public LootBuilder setChildDropCounMin(int count) { + this.loot.setChildDropCountMin(count); + return this; + } + + public LootBuilder setChildDropCountMax(int count) { + this.loot.setChildDropCountMax(count); + return this; + } + + public LootBuilder setRequireCharged(boolean require) { + this.loot.setRequireCharged(require); + return this; + } + + public Loot build() { + return this.loot; + } + + public static class Tuple { + public final key x; + public final value y; + + public Tuple(key x, value y) { + this.x = x; + this.y = y; + } + + public key getKey() { + return this.x; + } + + public value getValue() { + return this.y; + } + } +} \ No newline at end of file diff --git a/Core/src/main/java/com/songoda/core/lootables/loot/LootManager.java b/Core/src/main/java/com/songoda/core/lootables/loot/LootManager.java new file mode 100644 index 00000000..eb5451a6 --- /dev/null +++ b/Core/src/main/java/com/songoda/core/lootables/loot/LootManager.java @@ -0,0 +1,180 @@ +package com.songoda.core.lootables.loot; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.stream.JsonReader; +import com.songoda.core.compatibility.CompatibleMaterial; +import com.songoda.core.lootables.Lootables; +import com.songoda.core.lootables.Modify; +import org.bukkit.Bukkit; +import org.bukkit.entity.EntityType; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; + +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.Writer; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Random; + +public class LootManager { + + private final Map registeredLootables = new HashMap<>(); + + private final Lootables lootables; + + public LootManager(Lootables lootables) { + this.lootables = lootables; + } + + public Lootable addLootable(Lootable lootable) { + return registeredLootables.put(lootable.getKey(), lootable); + } + + public void removeLootable(String key) { + registeredLootables.remove(key); + File file = new File(lootables.getLootablesDir() + + "/" + key.toLowerCase() + ".json"); + file.delete(); + } + + public List runLoot(Modify modify, boolean burning, boolean isCharged, ItemStack murderWeapon, EntityType looter, Loot loot, int rerollChance, int looting) { + List toDrop = new ArrayList<>(); + if (modify != null) + loot = modify.Modify(loot); + if (loot == null) return toDrop; + + if (loot.runChance(looting, murderWeapon) || ((Math.random() * 100) - rerollChance < 0 || rerollChance == 100) + && loot.runChance(looting, murderWeapon)) { + + if (loot.getOnlyDropFor().size() != 0 + && loot.getOnlyDropFor().stream().noneMatch(type -> looter != null && type == looter) + || !isCharged && loot.isRequireCharged()) + return toDrop; + + if (loot.getChildLoot().size() > 0) { + List childLoot = loot.getChildLoot(); + Collections.shuffle(childLoot); + + int amt = loot.getChildDropCount(); + int success = 0; + + top: + for (int i = 0; i < 100; i++) { + for (Loot value : childLoot) { + if (value == null) continue; + if (amt == success) break top; + List drops = runLoot(modify, burning, isCharged, murderWeapon, looter, value, rerollChance, looting); + if (!drops.isEmpty()) success++; + toDrop.addAll(drops); + } + } + } + + CompatibleMaterial material = loot.getMaterial(); + String command = loot.getCommand(); + int xp = loot.getXp(); + + if (material == null && command == null) return toDrop; + + int amount = loot.getAmountToDrop(looting); + if (amount == 0) return toDrop; + + if (material != null) { + ItemStack item = loot.getBurnedMaterial() != null && burning + ? loot.getBurnedMaterial().getItem() : material.getItem(); + item.setAmount(amount); + ItemMeta meta = item.getItemMeta() == null ? Bukkit.getItemFactory().getItemMeta(loot.getMaterial().getMaterial()) + : item.getItemMeta(); + + if (loot.getName() != null) + meta.setDisplayName(loot.getName()); + + if (loot.getLore() != null) + meta.setLore(loot.getLore()); + item.setItemMeta(meta); + + if (loot.getEnchants(item) != null) + item = loot.getEnchants(item); + + if (loot.getDamageMax() != 0 && loot.getDamageMin() != 0) { + short max = item.getType().getMaxDurability(); + short min = (short) (max * (10 / 100.0f)); + item.setDurability((short) (new Random().nextInt(max - min + 1) + min)); + } + + toDrop.add(new Drop(item)); + } + if (command != null) { + for (int i = 0; i < amount; i++) + toDrop.add(new Drop(command)); + } + if (xp != 0) { + for (int i = 0; i < amount; i++) + toDrop.add(new Drop(xp)); + } + } + return toDrop; + } + + public void loadLootables() { + registeredLootables.clear(); + File dir = new File(lootables.getLootablesDir()); + File[] directoryListing = dir.listFiles(); + if (directoryListing != null) { + for (File file : directoryListing) { + if (!file.getName().endsWith(".json")) continue; + try { + Gson gson = new Gson(); + JsonReader reader = new JsonReader(new FileReader(file.getPath())); + + Lootable lootable = gson.fromJson(reader, Lootable.class); + + if (lootable.getRegisteredLoot().size() != 0) + addLootable(lootable); + + reader.close(); + + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } + + public void saveLootables(boolean defaults) { + File dir = new File(lootables.getLootablesDir()); + dir.mkdir(); + + // Save to file + for (Lootable lootable : registeredLootables.values()) { + try { + + File file = new File(lootables.getLootablesDir() + + "/" + lootable.getKey().toLowerCase() + ".json"); + if (file.exists() && defaults) continue; + + try (Writer writer = new FileWriter(file.getPath())) { + Gson gson = new GsonBuilder().setPrettyPrinting().create(); + gson.toJson(lootable, writer); + } + + } catch (IOException e) { + e.printStackTrace(); + } + } + + if (defaults) + registeredLootables.clear(); + } + + public Map getRegisteredLootables() { + return Collections.unmodifiableMap(registeredLootables); + } +} diff --git a/Core/src/main/java/com/songoda/core/lootables/loot/Lootable.java b/Core/src/main/java/com/songoda/core/lootables/loot/Lootable.java new file mode 100644 index 00000000..9640b409 --- /dev/null +++ b/Core/src/main/java/com/songoda/core/lootables/loot/Lootable.java @@ -0,0 +1,43 @@ +package com.songoda.core.lootables.loot; + +import com.google.gson.annotations.SerializedName; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class Lootable { + + // The key applicable to this lootable. + @SerializedName("Type") + private final String type; + + // Registered loot. + @SerializedName("Loot") + private final List registeredLoot = new ArrayList<>(); + + public Lootable(String key) { + this.type = key; + } + + public Lootable(String key, Loot... loots) { + this.type = key; + registeredLoot.addAll(Arrays.asList(loots)); + } + + public List getRegisteredLoot() { + return new ArrayList<>(registeredLoot); + } + + public void registerLoot(Loot... loots) { + registeredLoot.addAll(Arrays.asList(loots)); + } + + public String getKey() { + return type; + } + + public void removeLoot(Loot loot) { + this.registeredLoot.remove(loot); + } +} diff --git a/Core/src/main/java/com/songoda/core/lootables/loot/objects/EnchantChance.java b/Core/src/main/java/com/songoda/core/lootables/loot/objects/EnchantChance.java new file mode 100644 index 00000000..915453ed --- /dev/null +++ b/Core/src/main/java/com/songoda/core/lootables/loot/objects/EnchantChance.java @@ -0,0 +1,28 @@ +package com.songoda.core.lootables.loot.objects; + +import org.bukkit.enchantments.Enchantment; + +public class EnchantChance { + + private final Enchantment enchantment; + private final int level; + private final double chanceOverride; + + public EnchantChance(Enchantment enchantment, int level, double chanceOverride) { + this.enchantment = enchantment; + this.level = level; + this.chanceOverride = chanceOverride; + } + + public Enchantment getEnchantment() { + return enchantment; + } + + public int getLevel() { + return level; + } + + public double getChanceOverride() { + return chanceOverride; + } +} diff --git a/Core/src/main/java/com/songoda/core/utils/ItemUtils.java b/Core/src/main/java/com/songoda/core/utils/ItemUtils.java index e42102f9..abde1616 100644 --- a/Core/src/main/java/com/songoda/core/utils/ItemUtils.java +++ b/Core/src/main/java/com/songoda/core/utils/ItemUtils.java @@ -34,6 +34,7 @@ import java.lang.reflect.Method; import java.util.Base64; import java.util.Iterator; import java.util.List; +import java.util.Random; import java.util.UUID; import java.util.logging.Level; import java.util.logging.Logger; @@ -77,6 +78,43 @@ public class ItemUtils { return titleCase.toString().trim(); } + private static Method methodAsBukkitCopy, methodAsNMSCopy, methodA; + + static { + try { + Class clazzEnchantmentManager = ClassMapping.ENCHANTMENT_MANAGER.getClazz(); + Class clazzItemStack = ClassMapping.ITEM_STACK.getClazz(); + Class clazzCraftItemStack = ClassMapping.CRAFT_ITEM_STACK.getClazz(); + + methodAsBukkitCopy = clazzCraftItemStack.getMethod("asBukkitCopy", clazzItemStack); + methodAsNMSCopy = clazzCraftItemStack.getMethod("asNMSCopy", ItemStack.class); + + if (ServerVersion.isServerVersion(ServerVersion.V1_8)) + methodA = clazzEnchantmentManager.getMethod("a", Random.class, clazzItemStack, int.class); + else + methodA = clazzEnchantmentManager.getMethod("a", Random.class, clazzItemStack, int.class, boolean.class); + + } catch (NoSuchMethodException e) { + e.printStackTrace(); + } + } + + public static ItemStack applyRandomEnchants(ItemStack item, int level) { + try { + Object nmsItemStack = methodAsNMSCopy.invoke(null, item); + + if (ServerVersion.isServerVersion(ServerVersion.V1_8)) + nmsItemStack = methodA.invoke(null, new Random(), nmsItemStack, level); + else + nmsItemStack = methodA.invoke(null, new Random(), nmsItemStack, level, false); + + item = (ItemStack) methodAsBukkitCopy.invoke(null, nmsItemStack); + } catch (IllegalAccessException | InvocationTargetException e) { + e.printStackTrace(); + } + return item; + } + public static String itemStackArrayToBase64(ItemStack[] items) { try { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); diff --git a/NMS/NMS-API/pom.xml b/NMS/NMS-API/pom.xml index 83655dfb..9c5ec0bb 100644 --- a/NMS/NMS-API/pom.xml +++ b/NMS/NMS-API/pom.xml @@ -3,7 +3,7 @@ com.songoda SongodaCore-Modules - 2.5.4 + 2.5.5 ../../ diff --git a/NMS/NMS-v1_10_R1/pom.xml b/NMS/NMS-v1_10_R1/pom.xml index 1b3ac3d3..26455a21 100644 --- a/NMS/NMS-v1_10_R1/pom.xml +++ b/NMS/NMS-v1_10_R1/pom.xml @@ -3,7 +3,7 @@ com.songoda SongodaCore-Modules - 2.5.4 + 2.5.5 ../../ diff --git a/NMS/NMS-v1_11_R1/pom.xml b/NMS/NMS-v1_11_R1/pom.xml index 31bedb09..cc675662 100644 --- a/NMS/NMS-v1_11_R1/pom.xml +++ b/NMS/NMS-v1_11_R1/pom.xml @@ -3,7 +3,7 @@ com.songoda SongodaCore-Modules - 2.5.4 + 2.5.5 ../../ diff --git a/NMS/NMS-v1_12_R1/pom.xml b/NMS/NMS-v1_12_R1/pom.xml index 6bbbfdb7..bbb4d824 100644 --- a/NMS/NMS-v1_12_R1/pom.xml +++ b/NMS/NMS-v1_12_R1/pom.xml @@ -3,7 +3,7 @@ com.songoda SongodaCore-Modules - 2.5.4 + 2.5.5 ../../ diff --git a/NMS/NMS-v1_13_R1/pom.xml b/NMS/NMS-v1_13_R1/pom.xml index 14ccb5ee..1d1808f8 100644 --- a/NMS/NMS-v1_13_R1/pom.xml +++ b/NMS/NMS-v1_13_R1/pom.xml @@ -3,7 +3,7 @@ com.songoda SongodaCore-Modules - 2.5.4 + 2.5.5 ../../ diff --git a/NMS/NMS-v1_13_R2/pom.xml b/NMS/NMS-v1_13_R2/pom.xml index 9fb30982..d04c7975 100644 --- a/NMS/NMS-v1_13_R2/pom.xml +++ b/NMS/NMS-v1_13_R2/pom.xml @@ -3,7 +3,7 @@ com.songoda SongodaCore-Modules - 2.5.4 + 2.5.5 ../../ diff --git a/NMS/NMS-v1_14_R1/pom.xml b/NMS/NMS-v1_14_R1/pom.xml index 690b4488..b74f3bc8 100644 --- a/NMS/NMS-v1_14_R1/pom.xml +++ b/NMS/NMS-v1_14_R1/pom.xml @@ -3,7 +3,7 @@ com.songoda SongodaCore-Modules - 2.5.4 + 2.5.5 ../../ diff --git a/NMS/NMS-v1_15_R1/pom.xml b/NMS/NMS-v1_15_R1/pom.xml index a0b7067a..3b988f23 100644 --- a/NMS/NMS-v1_15_R1/pom.xml +++ b/NMS/NMS-v1_15_R1/pom.xml @@ -3,7 +3,7 @@ com.songoda SongodaCore-Modules - 2.5.4 + 2.5.5 ../../ diff --git a/NMS/NMS-v1_16_R1/pom.xml b/NMS/NMS-v1_16_R1/pom.xml index eff6b877..aae8e284 100644 --- a/NMS/NMS-v1_16_R1/pom.xml +++ b/NMS/NMS-v1_16_R1/pom.xml @@ -3,7 +3,7 @@ com.songoda SongodaCore-Modules - 2.5.4 + 2.5.5 ../../ diff --git a/NMS/NMS-v1_16_R2/pom.xml b/NMS/NMS-v1_16_R2/pom.xml index 76b18bd4..903bb8ca 100644 --- a/NMS/NMS-v1_16_R2/pom.xml +++ b/NMS/NMS-v1_16_R2/pom.xml @@ -3,7 +3,7 @@ com.songoda SongodaCore-Modules - 2.5.4 + 2.5.5 ../../ diff --git a/NMS/NMS-v1_16_R3/pom.xml b/NMS/NMS-v1_16_R3/pom.xml index aa92167c..c935bd14 100644 --- a/NMS/NMS-v1_16_R3/pom.xml +++ b/NMS/NMS-v1_16_R3/pom.xml @@ -3,7 +3,7 @@ com.songoda SongodaCore-Modules - 2.5.4 + 2.5.5 ../../ diff --git a/NMS/NMS-v1_17_R1/pom.xml b/NMS/NMS-v1_17_R1/pom.xml index 324c002a..5623f4af 100644 --- a/NMS/NMS-v1_17_R1/pom.xml +++ b/NMS/NMS-v1_17_R1/pom.xml @@ -3,7 +3,7 @@ com.songoda SongodaCore-Modules - 2.5.4 + 2.5.5 ../../ diff --git a/NMS/NMS-v1_8_R1/pom.xml b/NMS/NMS-v1_8_R1/pom.xml index a892224a..94dfeb06 100644 --- a/NMS/NMS-v1_8_R1/pom.xml +++ b/NMS/NMS-v1_8_R1/pom.xml @@ -3,7 +3,7 @@ com.songoda SongodaCore-Modules - 2.5.4 + 2.5.5 ../../ diff --git a/NMS/NMS-v1_8_R2/pom.xml b/NMS/NMS-v1_8_R2/pom.xml index fd7227ea..cc427dc3 100644 --- a/NMS/NMS-v1_8_R2/pom.xml +++ b/NMS/NMS-v1_8_R2/pom.xml @@ -3,7 +3,7 @@ com.songoda SongodaCore-Modules - 2.5.4 + 2.5.5 ../../ diff --git a/NMS/NMS-v1_8_R3/pom.xml b/NMS/NMS-v1_8_R3/pom.xml index 4b35f42f..325d6254 100644 --- a/NMS/NMS-v1_8_R3/pom.xml +++ b/NMS/NMS-v1_8_R3/pom.xml @@ -3,7 +3,7 @@ com.songoda SongodaCore-Modules - 2.5.4 + 2.5.5 ../../ diff --git a/NMS/NMS-v1_9_R1/pom.xml b/NMS/NMS-v1_9_R1/pom.xml index d6275f16..f3219318 100644 --- a/NMS/NMS-v1_9_R1/pom.xml +++ b/NMS/NMS-v1_9_R1/pom.xml @@ -3,7 +3,7 @@ com.songoda SongodaCore-Modules - 2.5.4 + 2.5.5 ../../ diff --git a/NMS/NMS-v1_9_R2/pom.xml b/NMS/NMS-v1_9_R2/pom.xml index 60c775ed..bce6f919 100644 --- a/NMS/NMS-v1_9_R2/pom.xml +++ b/NMS/NMS-v1_9_R2/pom.xml @@ -3,7 +3,7 @@ com.songoda SongodaCore-Modules - 2.5.4 + 2.5.5 ../../ diff --git a/pom.xml b/pom.xml index ef0a0a6f..d9063a27 100644 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ com.songoda SongodaCore-Modules - 2.5.4 + 2.5.5 4.0.0 pom