diff --git a/build.gradle b/build.gradle index 44553d4..ba085ee 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ plugins { } group = 'me.skymc' -version = '5.11' +version = '5.12' sourceCompatibility = 1.8 targetCompatibility = 1.8 diff --git a/src/main/scala/io/izzel/taboolib/Version.java b/src/main/scala/io/izzel/taboolib/Version.java index 2e7ff9c..e8f0802 100644 --- a/src/main/scala/io/izzel/taboolib/Version.java +++ b/src/main/scala/io/izzel/taboolib/Version.java @@ -56,4 +56,8 @@ public enum Version { return vNull; } } + + public static int getCurrentVersionInt() { + return getCurrentVersion().versionInt; + } } \ No newline at end of file diff --git a/src/main/scala/io/izzel/taboolib/util/TMap.java b/src/main/scala/io/izzel/taboolib/util/TMap.java index 22fcf62..2556b24 100644 --- a/src/main/scala/io/izzel/taboolib/util/TMap.java +++ b/src/main/scala/io/izzel/taboolib/util/TMap.java @@ -1,7 +1,11 @@ package io.izzel.taboolib.util; import com.google.common.collect.Maps; +import io.izzel.taboolib.util.item.ItemBuilder; +import io.izzel.taboolib.util.item.Items; import io.izzel.taboolib.util.lite.Numbers; +import org.bukkit.inventory.ItemFlag; +import org.bukkit.inventory.ItemStack; import org.bukkit.util.NumberConversions; import java.util.Arrays; @@ -82,6 +86,24 @@ public class TMap { return new String[] {v[0], r.toString().replace("`", "")}; } + public static ItemStack parseItem(String source) { + TMap map = TMap.parse(source); + ItemBuilder builder = new ItemBuilder(Items.asMaterial(map.get("material", "m", "type", "t"))) + .amount(map.getInt(new String[]{"amount", "a"}, 1)) + .damage(map.getInt("damage", "data", "d")) + .name(map.get("displayname", "display", "name", "n")); + if (map.getBoolean("shiny", "glowing", "glow")) { + builder.shiny(); + } + if (map.get("lore", "l") != null) { + builder.lore(map.get("lore", "l").split("\\n")); + } + if (map.get("flag", "f") != null) { + builder.flags(Arrays.stream(map.get("flag", "f").split("\\n")).map(Items::asItemFlag).filter(Objects::nonNull).toArray(ItemFlag[]::new)); + } + return builder.colored().build(); + } + @Override public boolean equals(Object o) { if (this == o) { diff --git a/src/main/scala/io/izzel/taboolib/util/item/ItemStacker.java b/src/main/scala/io/izzel/taboolib/util/item/ItemStacker.java new file mode 100644 index 0000000..678897a --- /dev/null +++ b/src/main/scala/io/izzel/taboolib/util/item/ItemStacker.java @@ -0,0 +1,165 @@ +package io.izzel.taboolib.util.item; + +import io.izzel.taboolib.util.ArrayUtil; +import org.bukkit.entity.Player; +import org.bukkit.inventory.CraftingInventory; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.PlayerInventory; + +/** + * @Author 坏黑 + * @Since 2019-02-07 23:53 + */ +public class ItemStacker { + + /** + * 从箱子里移动物品到玩家背包 + * 如果溢出则丢弃 + */ + public static void moveItemFromChest(ItemStack item, Player player) { + AddResult result = addItemAndMerge(item, player.getInventory(), new Integer[0]); + if (result.countOut > 0) { + item.setAmount(result.countOut); + if (!addItemAndSplit(item, player.getInventory(), 0, true)) { + player.getWorld().dropItem(player.getLocation(), item); + } + } + } + + public static boolean addItemAndSplit(ItemStack item, Inventory inventory, int start) { + return addItemAndSplit(item, inventory, start, false); + } + + /** + * 添加并拆分,但不合并 + * 返回值为是否添加完成 + * + * desc = 快捷栏逆向添加,用于工作台拟真,会忽略 start 参数 + */ + public static boolean addItemAndSplit(ItemStack item, Inventory inventory, int start, boolean desc) { + int size = inventory instanceof PlayerInventory || inventory instanceof CraftingInventory ? 36 : inventory.getSize(); + if (desc) { + // 8 ~ 0 + for (int i = 8; i >= 0; i--) { + if (check(item, inventory, i)) { + return true; + } + } + } + // 9 ~ 36 + for (int i = desc ? start + 9 : start; i < size; i++) { + if (check(item, inventory, i)) { + return true; + } + } + return false; + } + + public static boolean addItemFromChestToPlayer(ItemStack item, Inventory inventory) { + for (int i = 8; i >= 0; i--) { + if (Items.isNull(inventory.getItem(i))) { + if (item.getAmount() > item.getType().getMaxStackSize()) { + ItemStack itemClone = item.clone(); + itemClone.setAmount(item.getType().getMaxStackSize()); + inventory.setItem(i, itemClone); + item.setAmount(item.getAmount() - item.getType().getMaxStackSize()); + } else { + ItemStack itemClone = item.clone(); + itemClone.setAmount(item.getAmount()); + inventory.setItem(i, itemClone); + item.setAmount(0); + return true; + } + } + } + for (int i = 35; i >= 9; i--) { + if (Items.isNull(inventory.getItem(i))) { + if (item.getAmount() > item.getType().getMaxStackSize()) { + ItemStack itemClone = item.clone(); + itemClone.setAmount(item.getType().getMaxStackSize()); + inventory.setItem(i, itemClone); + item.setAmount(item.getAmount() - item.getType().getMaxStackSize()); + } else { + ItemStack itemClone = item.clone(); + itemClone.setAmount(item.getAmount()); + inventory.setItem(i, itemClone); + item.setAmount(0); + return true; + } + } + } + return false; + } + + /** + * 合并物品,不新增 + */ + public static AddResult addItemAndMerge(ItemStack item, Inventory inventory, Integer[] ignore) { + boolean changed = false; + int count = item.getAmount(); + int size = inventory instanceof PlayerInventory || inventory instanceof CraftingInventory ? 36 : inventory.getSize(); + for (int i = 0; i < size; i++) { + if (ArrayUtil.contains(ignore, i)) { + continue; + } + ItemStack inventoryItem = inventory.getItem(i); + if (!item.isSimilar(inventoryItem)) { + continue; + } + while (count > 0 && inventoryItem.getAmount() < item.getType().getMaxStackSize()) { + changed = true; + inventoryItem.setAmount(inventoryItem.getAmount() + 1); + count--; + } + if (count == 0) { + return new AddResult(count, changed); + } + } + return new AddResult(count, changed); + } + + private static boolean check(ItemStack item, Inventory inventory, int i) { + if (Items.isNull(inventory.getItem(i))) { + // 如果物品数量过多 + if (item.getAmount() > item.getType().getMaxStackSize()) { + ItemStack itemClone = item.clone(); + itemClone.setAmount(item.getType().getMaxStackSize()); + inventory.setItem(i, itemClone); + item.setAmount(item.getAmount() - item.getType().getMaxStackSize()); + } else { + inventory.setItem(i, item.clone()); + item.setAmount(0); + return true; + } + } + return false; + } + + public static class AddResult { + + private int countOut; + private boolean changed; + + public AddResult(int countOut, boolean changed) { + this.countOut = countOut; + this.changed = changed; + } + + public int getCountOut() { + return countOut; + } + + public boolean isChanged() { + return changed; + } + + @Override + public String toString() { + return "AddResult{" + + "countOut=" + countOut + + ", changed=" + changed + + '}'; + } + } +} diff --git a/src/main/scala/io/izzel/taboolib/util/item/inventory/BuildTask.java b/src/main/scala/io/izzel/taboolib/util/item/inventory/BuildTask.java new file mode 100644 index 0000000..b216f64 --- /dev/null +++ b/src/main/scala/io/izzel/taboolib/util/item/inventory/BuildTask.java @@ -0,0 +1,13 @@ +package io.izzel.taboolib.util.item.inventory; + +import org.bukkit.inventory.Inventory; + +/** + * @Author 坏黑 + * @Since 2019-05-21 18:14 + */ +public interface BuildTask { + + void run(Inventory event); + +} diff --git a/src/main/scala/io/izzel/taboolib/util/item/inventory/ClickEvent.java b/src/main/scala/io/izzel/taboolib/util/item/inventory/ClickEvent.java index d286939..cc15d4f 100644 --- a/src/main/scala/io/izzel/taboolib/util/item/inventory/ClickEvent.java +++ b/src/main/scala/io/izzel/taboolib/util/item/inventory/ClickEvent.java @@ -7,7 +7,9 @@ import org.bukkit.event.Cancellable; import org.bukkit.event.Event; import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.inventory.InventoryDragEvent; +import org.bukkit.event.inventory.InventoryEvent; import org.bukkit.event.inventory.InventoryInteractEvent; +import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; import java.util.List; @@ -28,6 +30,10 @@ public class ClickEvent { this.slot = slot; } + public List getAffectItems() { + return clickType == ClickType.CLICK ? Servers.getAffectItemInClickEvent((InventoryClickEvent) event) : Lists.newArrayList(); + } + public InventoryClickEvent castClick() { return (InventoryClickEvent) event; } @@ -52,8 +58,8 @@ public class ClickEvent { return (Player) ((InventoryInteractEvent) event).getWhoClicked(); } - public List getAffectItems() { - return clickType == ClickType.CLICK ? Servers.getAffectItemInClickEvent((InventoryClickEvent) event) : Lists.newArrayList(); + public Inventory getInventory() { + return ((InventoryEvent) event).getInventory(); } public void setCancelled(boolean c) { @@ -63,4 +69,14 @@ public class ClickEvent { public boolean isCancelled() { return ((Cancellable) event).isCancelled(); } + + public ItemStack getCurrentItem() { + return clickType == ClickType.CLICK ? castClick().getCurrentItem() : null; + } + + public void setCurrentItem(ItemStack item) { + if (clickType == ClickType.CLICK) { + castClick().setCurrentItem(item); + } + } } diff --git a/src/main/scala/io/izzel/taboolib/util/item/inventory/ClickListener.java b/src/main/scala/io/izzel/taboolib/util/item/inventory/ClickListener.java index 65b1aab..529bee5 100644 --- a/src/main/scala/io/izzel/taboolib/util/item/inventory/ClickListener.java +++ b/src/main/scala/io/izzel/taboolib/util/item/inventory/ClickListener.java @@ -13,6 +13,7 @@ import org.bukkit.event.Listener; import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.inventory.InventoryCloseEvent; import org.bukkit.event.inventory.InventoryDragEvent; +import org.bukkit.event.inventory.InventoryOpenEvent; import org.bukkit.event.player.PlayerDropItemEvent; import org.bukkit.event.player.PlayerItemHeldEvent; import org.bukkit.event.server.PluginDisableEvent; @@ -32,6 +33,14 @@ class ClickListener implements Listener { Bukkit.getOnlinePlayers().stream().filter(player -> player.getOpenInventory().getTopInventory().getHolder() instanceof MenuHolder && e.getPlugin().equals(((MenuHolder) player.getOpenInventory().getTopInventory().getHolder()).getBuilder().getPlugin())).forEach(HumanEntity::closeInventory); } + @EventHandler + public void e(InventoryOpenEvent e) { + if (e.getInventory().getHolder() instanceof MenuHolder) { + Bukkit.getScheduler().runTask(TabooLib.getPlugin(), () -> ((MenuHolder) e.getInventory().getHolder()).getBuilder().getBuildTask().run(e.getInventory())); + Bukkit.getScheduler().runTaskAsynchronously(TabooLib.getPlugin(), () -> ((MenuHolder) e.getInventory().getHolder()).getBuilder().getBuildTaskAsync().run(e.getInventory())); + } + } + @EventHandler public void e(InventoryClickEvent e) { if (e.getInventory().getHolder() instanceof MenuHolder) { diff --git a/src/main/scala/io/izzel/taboolib/util/item/inventory/MenuBuilder.java b/src/main/scala/io/izzel/taboolib/util/item/inventory/MenuBuilder.java index 240f2a6..9e6cd79 100644 --- a/src/main/scala/io/izzel/taboolib/util/item/inventory/MenuBuilder.java +++ b/src/main/scala/io/izzel/taboolib/util/item/inventory/MenuBuilder.java @@ -5,6 +5,7 @@ import io.izzel.taboolib.TabooLib; import io.izzel.taboolib.util.Ref; import org.bukkit.Bukkit; import org.bukkit.Material; +import org.bukkit.entity.Player; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; import org.bukkit.plugin.Plugin; @@ -24,6 +25,8 @@ public class MenuBuilder { private char[][] items = new char[0][0]; private ClickTask clickTask; private CloseTask closeTask; + private BuildTask buildTask; + private BuildTask buildTaskAsync; private boolean lockHand; public MenuBuilder(Plugin plugin) { @@ -43,6 +46,11 @@ public class MenuBuilder { return this; } + public MenuBuilder lockHand(boolean value) { + this.lockHand = value; + return this; + } + public MenuBuilder event(ClickTask clickTask) { this.clickTask = clickTask; return this; @@ -53,6 +61,16 @@ public class MenuBuilder { return this; } + public MenuBuilder build(BuildTask buildTask) { + this.buildTask = buildTask; + return this; + } + + public MenuBuilder buildAsync(BuildTask buildTask) { + this.buildTaskAsync = buildTask; + return this; + } + public MenuBuilder title(String title) { this.title = title; return this; @@ -76,6 +94,10 @@ public class MenuBuilder { return this; } + public void open(Player player) { + player.openInventory(build()); + } + public Inventory build() { Inventory inventory = Bukkit.createInventory(new MenuHolder(this), rows, String.valueOf(title)); for (int i = 0; i < items.length && i < rows; i++) { @@ -133,6 +155,14 @@ public class MenuBuilder { return closeTask; } + public BuildTask getBuildTask() { + return buildTask; + } + + public BuildTask getBuildTaskAsync() { + return buildTaskAsync; + } + public boolean isLockHand() { return lockHand; } diff --git a/src/main/scala/io/izzel/taboolib/util/item/inventory/stored/Action.java b/src/main/scala/io/izzel/taboolib/util/item/inventory/stored/Action.java new file mode 100644 index 0000000..75d8b57 --- /dev/null +++ b/src/main/scala/io/izzel/taboolib/util/item/inventory/stored/Action.java @@ -0,0 +1,13 @@ +package io.izzel.taboolib.util.item.inventory.stored; + +import io.izzel.taboolib.util.item.inventory.ClickEvent; +import org.bukkit.inventory.ItemStack; + +public abstract class Action { + + public abstract ItemStack getCurrent(ClickEvent e); + + public abstract void setCurrent(ClickEvent e, ItemStack item); + + public abstract int getCurrentSlot(ClickEvent e); +} diff --git a/src/main/scala/io/izzel/taboolib/util/item/inventory/stored/ActionClick.java b/src/main/scala/io/izzel/taboolib/util/item/inventory/stored/ActionClick.java new file mode 100644 index 0000000..d2062c6 --- /dev/null +++ b/src/main/scala/io/izzel/taboolib/util/item/inventory/stored/ActionClick.java @@ -0,0 +1,26 @@ +package io.izzel.taboolib.util.item.inventory.stored; + +import io.izzel.taboolib.util.item.inventory.ClickEvent; +import org.bukkit.inventory.ItemStack; + +/** + * @Author sky + * @Since 2019-12-03 19:17 + */ +public class ActionClick extends Action{ + + @Override + public ItemStack getCurrent(ClickEvent e) { + return e.getClicker().getItemOnCursor(); + } + + @Override + public void setCurrent(ClickEvent e, ItemStack item) { + e.getClicker().setItemOnCursor(item); + } + + @Override + public int getCurrentSlot(ClickEvent e) { + return e.getRawSlot(); + } +} diff --git a/src/main/scala/io/izzel/taboolib/util/item/inventory/stored/ActionKeyboard.java b/src/main/scala/io/izzel/taboolib/util/item/inventory/stored/ActionKeyboard.java new file mode 100644 index 0000000..0efa6a2 --- /dev/null +++ b/src/main/scala/io/izzel/taboolib/util/item/inventory/stored/ActionKeyboard.java @@ -0,0 +1,26 @@ +package io.izzel.taboolib.util.item.inventory.stored; + +import io.izzel.taboolib.util.item.inventory.ClickEvent; +import org.bukkit.inventory.ItemStack; + +/** + * @Author sky + * @Since 2019-12-03 19:20 + */ +public class ActionKeyboard extends Action { + + @Override + public ItemStack getCurrent(ClickEvent e) { + return e.getClicker().getInventory().getItem(e.castClick().getHotbarButton()); + } + + @Override + public void setCurrent(ClickEvent e, ItemStack item) { + e.getClicker().getInventory().setItem(e.castClick().getHotbarButton(), item); + } + + @Override + public int getCurrentSlot(ClickEvent e) { + return e.getRawSlot(); + } +} diff --git a/src/main/scala/io/izzel/taboolib/util/item/inventory/stored/ActionQuickTake.java b/src/main/scala/io/izzel/taboolib/util/item/inventory/stored/ActionQuickTake.java new file mode 100644 index 0000000..d0d2d98 --- /dev/null +++ b/src/main/scala/io/izzel/taboolib/util/item/inventory/stored/ActionQuickTake.java @@ -0,0 +1,30 @@ +package io.izzel.taboolib.util.item.inventory.stored; + +import io.izzel.taboolib.util.item.ItemStacker; +import io.izzel.taboolib.util.item.inventory.ClickEvent; +import org.bukkit.inventory.ItemStack; + +/** + * @Author sky + * @Since 2019-12-03 19:20 + */ +public class ActionQuickTake extends Action { + + @Override + public ItemStack getCurrent(ClickEvent e) { + return e.getClicker().getItemOnCursor(); + } + + @Override + public void setCurrent(ClickEvent e, ItemStack item) { + if (item != null) { + ItemStacker.moveItemFromChest(item, e.getClicker()); + } + e.getClicker().setItemOnCursor(null); + } + + @Override + public int getCurrentSlot(ClickEvent e) { + return e.getRawSlot(); + } +} diff --git a/src/main/scala/io/izzel/taboolib/util/item/inventory/stored/MenuStored.java b/src/main/scala/io/izzel/taboolib/util/item/inventory/stored/MenuStored.java new file mode 100644 index 0000000..1cdc954 --- /dev/null +++ b/src/main/scala/io/izzel/taboolib/util/item/inventory/stored/MenuStored.java @@ -0,0 +1,164 @@ +package io.izzel.taboolib.util.item.inventory.stored; + +import io.izzel.taboolib.util.item.Items; +import io.izzel.taboolib.util.item.inventory.ClickEvent; +import io.izzel.taboolib.util.item.inventory.ClickType; +import io.izzel.taboolib.util.item.inventory.MenuBuilder; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.InventoryCloseEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; + +/** + * @Author sky + * @Since 2019-12-03 13:24 + */ +public abstract class MenuStored { + + protected Player player; + + public MenuStored(Player player) { + this.player = player; + } + + public void open() { + MenuBuilder.builder() + .lockHand(isLockHand()) + .title(getTitle()) + .rows(getRows()) + .event(this::onClick) + .close(this::onClose) + .build(this::refresh) + .buildAsync(this::refreshAsync) + .open(player); + } + + public boolean isLockHand() { + return false; + } + + public String getTitle() { + return player.getName(); + } + + public int getRows() { + return 1; + } + + public void onClick(ClickEvent e) { + if (e.getClickType() == ClickType.CLICK) { + // 自动装填 + if (e.castClick().getClick().isShiftClick() && e.getRawSlot() >= e.getInventory().getSize() && Items.nonNull(e.getCurrentItem())) { + e.setCancelled(true); + // 获取有效位置 + int validSlot = getIntoSlot(e.getInventory(), e.getCurrentItem()); + if (validSlot >= 0) { + // 设置物品 + intoItem(e.getInventory(), e.getCurrentItem(), validSlot); + // 移除物品 + e.setCurrentItem(null); + onClicked(); + } + } + // 手动装填 + else { + Action action; + if (e.castClick().getClick().isShiftClick() && e.getRawSlot() >= 0 && e.getRawSlot() < e.getInventory().getSize()) { + action = new ActionQuickTake(); + } else if (e.castClick().getClick() == org.bukkit.event.inventory.ClickType.NUMBER_KEY) { + action = new ActionKeyboard(); + } else { + action = new ActionClick(); + } + // 点击有效位置 + if (isIntoSlot(e.getInventory(), action.getCurrent(e), action.getCurrentSlot(e))) { + e.setCancelled(true); + // 提取动作 + if (Items.isNull(action.getCurrent(e)) && existsItem(e.getInventory(), action.getCurrentSlot(e))) { + // 提取物品 + action.setCurrent(e, getItem(e.getInventory(), action.getCurrentSlot(e))); + // 删除物品 + intoItem(e.getInventory(), null, action.getCurrentSlot(e)); + onClicked(); + } + // 合法的位置 + else if (shouldIntoSlot(e.getInventory(), action.getCurrent(e), action.getCurrentSlot(e))) { + ItemStack current = action.getCurrent(e); + // 提取物品 + action.setCurrent(e, getItem(e.getInventory(), action.getCurrentSlot(e))); + // 写入物品 + intoItem(e.getInventory(), current, action.getCurrentSlot(e)); + onClicked(); + } + } + } + } + } + + /** + * 当点击动作完成时 + */ + public void onClicked() { + } + + /** + * 当界面关闭时 + */ + public void onClose(InventoryCloseEvent e) { + } + + /** + * 当界面刷新时 + */ + public void refresh(Inventory inventory) { + } + + /** + * 当界面刷新时(异步) + */ + public void refreshAsync(Inventory inventory) { + } + + /** + * 物品是否可以放入该位置 + */ + public boolean shouldIntoSlot(Inventory inventory, ItemStack item, int slot) { + return false; + } + + /** + * 是否为有效的位置 + */ + public boolean isIntoSlot(Inventory inventory, ItemStack item, int slot) { + return false; + } + + /** + * 获取界面中有效的位置 + * 用于 shift 点击时的自动装填 + */ + public int getIntoSlot(Inventory inventory, ItemStack item) { + return -1; + } + + /** + * 物品存入界面 + */ + public void intoItem(Inventory inventory, ItemStack item, int slot) { + inventory.setItem(slot, item); + } + + /** + * 是否存在物品 + */ + public boolean existsItem(Inventory inventory, int slot) { + return false; + } + + /** + * 获取物品 + */ + public ItemStack getItem(Inventory inventory, int slot) { + return inventory.getItem(slot); + } +} diff --git a/src/main/scala/io/izzel/taboolib/util/lite/Servers.java b/src/main/scala/io/izzel/taboolib/util/lite/Servers.java index a24c04f..9e5b49d 100644 --- a/src/main/scala/io/izzel/taboolib/util/lite/Servers.java +++ b/src/main/scala/io/izzel/taboolib/util/lite/Servers.java @@ -1,3 +1,4 @@ + package io.izzel.taboolib.util.lite; import com.google.common.collect.Lists;