Added Lootables to SongodaCore.

This commit is contained in:
Brianna 2021-06-13 18:04:16 -05:00
parent 7430fcc34a
commit c861aafe82
18 changed files with 1561 additions and 0 deletions

View File

@ -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");

View File

@ -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;
}
}

View File

@ -0,0 +1,8 @@
package com.songoda.core.lootables;
import com.songoda.core.lootables.loot.Loot;
public interface Modify {
Loot Modify(Loot loot);
}

View File

@ -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<String> 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<String> getData();
protected abstract void updateData(List<String> list);
protected abstract String validate(String line);
}

View File

@ -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<Lootable> 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;
}
}

View File

@ -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<String, Integer> 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<String> enchantments = new ArrayList<>();
String last = null;
if (!lore.isEmpty())
for (Map.Entry<String, Integer> 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();
}));
}
}

View File

@ -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<String> getData() {
return loot.getOnlyDropFor().stream().map(Enum::name).collect(Collectors.toList());
}
@Override
protected void updateData(List<String> 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;
}
}
}

View File

@ -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<String> enchantments = new ArrayList<>();
if (loot.getEnchants() != null)
for (Map.Entry<String, Integer> 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<String> 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++;
}
}
}

View File

@ -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++;
}
}
}

View File

@ -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<String> getData() {
return loot.getLore();
}
@Override
protected void updateData(List<String> list) {
loot.setLore(list);
}
@Override
protected String validate(String line) {
return line.trim();
}
}

View File

@ -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;
}
}

View File

@ -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<Drop> drops, EntityDeathEvent event) {
int xpToDrop = event.getDroppedExp();
List<ItemStack> items = new ArrayList<>();
List<String> commands = new ArrayList<>();
List<Integer> 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<ItemStack> items, EntityDeathEvent event) {
for (ItemStack item : items)
event.getDrops().add(item);
}
private static void runCommands(LivingEntity entity, List<String> 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);
}
}
}

View File

@ -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<String> lore = null;
// The override for the item enchantments.
@SerializedName("Enchantments")
private Map<String, Integer> 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<String, Double> 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<EntityType> 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<Loot> 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<String> getLore() {
if (lore == null) return null;
List<String> lore = new ArrayList<>();
for (String line : this.lore)
lore.add(TextUtils.formatText(line));
return lore;
}
public void setLore(List<String> lore) {
this.lore = new ArrayList<>(lore);
}
public ItemStack getEnchants(ItemStack item) {
if (enchants == null) return null;
Map<Enchantment, Integer> enchants = new HashMap<>();
for (Map.Entry<String, Integer> 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<String, Integer> enchants) {
this.enchants = enchants;
}
public void setEnchantChances(Map<String, Double> enchants) {
this.enchantChances = enchants;
}
public Map<String, Integer> 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<Enchantment, Integer> 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<Loot> getChildLoot() {
return childLoot == null ? new ArrayList<>() : new ArrayList<>(childLoot);
}
public List<EntityType> 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<EntityType> 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;
}
}

View File

@ -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<String, Integer> 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<String, Double> 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<key, value> {
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;
}
}
}

View File

@ -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<String, Lootable> 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<Drop> runLoot(Modify modify, boolean burning, boolean isCharged, ItemStack murderWeapon, EntityType looter, Loot loot, int rerollChance, int looting) {
List<Drop> 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<Loot> 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<Drop> 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<String, Lootable> getRegisteredLootables() {
return Collections.unmodifiableMap(registeredLootables);
}
}

View File

@ -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<Loot> 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<Loot> 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);
}
}

View File

@ -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;
}
}

View File

@ -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();