mirror of
https://gitlab.com/phoenix-dvpmt/mmoitems.git
synced 2024-12-22 04:37:42 +01:00
Improved soulbound.can-drop
option to support moving items around in chests, dispensers...
This commit is contained in:
parent
cef7d0163a
commit
c71f673bc4
@ -192,22 +192,16 @@ public class MMOItemTemplate implements ItemReference, PreloadedObject {
|
||||
return options.contains(option);
|
||||
}
|
||||
|
||||
public MMOItemBuilder newBuilder(@Nullable Player player) {
|
||||
if (player != null) {
|
||||
return newBuilder(PlayerData.get(player).getRPG());
|
||||
}
|
||||
return newBuilder((RPGPlayer) null);
|
||||
}
|
||||
|
||||
public MMOItemBuilder newBuilder() {
|
||||
return newBuilder((RPGPlayer) null);
|
||||
}
|
||||
|
||||
public MMOItemBuilder newBuilder(@Nullable Player player) {
|
||||
return newBuilder(player == null ? null : PlayerData.get(player).getRPG());
|
||||
}
|
||||
|
||||
public MMOItemBuilder newBuilder(@Nullable PlayerData player) {
|
||||
if (player != null) {
|
||||
return newBuilder(player.getRPG());
|
||||
}
|
||||
return newBuilder((RPGPlayer) null);
|
||||
return newBuilder(player == null ? null : player.getRPG());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -233,12 +227,11 @@ public class MMOItemTemplate implements ItemReference, PreloadedObject {
|
||||
* @param forDisplay Should it take modifiers into account
|
||||
* @return Item builder with random level and tier?
|
||||
*/
|
||||
@NotNull
|
||||
public MMOItemBuilder newBuilder(@Nullable RPGPlayer player, boolean forDisplay) {
|
||||
|
||||
// No player ~ default settings
|
||||
if (player == null) {
|
||||
return newBuilder(0, null);
|
||||
}
|
||||
if (player == null) return newBuilder(0, null);
|
||||
|
||||
// Read from player
|
||||
int itemLevel = hasOption(TemplateOption.LEVEL_ITEM) ? MMOItems.plugin.getTemplates().rollLevel(player.getLevel()) : 0;
|
||||
@ -251,6 +244,7 @@ public class MMOItemTemplate implements ItemReference, PreloadedObject {
|
||||
* @param itemTier The desired item tier, can be null
|
||||
* @return Item builder with specific item level and tier
|
||||
*/
|
||||
@NotNull
|
||||
public MMOItemBuilder newBuilder(int itemLevel, @Nullable ItemTier itemTier) {
|
||||
return new MMOItemBuilder(this, itemLevel, itemTier);
|
||||
}
|
||||
|
@ -36,6 +36,11 @@ public class MMOUtils {
|
||||
return particle.getDataType() == Particle.DustOptions.class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Optimized Soulbound check based on the fact that the
|
||||
* compressed item Soulbound data contains only one UUID,
|
||||
* the target player's UUID, sparing one Json parse pass.
|
||||
*/
|
||||
public static boolean isSoulboundTo(@NotNull NBTItem item, @NotNull Player player) {
|
||||
final @Nullable String foundNbt = item.getString("MMOITEMS_SOULBOUND");
|
||||
return foundNbt != null && foundNbt.contains(player.getUniqueId().toString());
|
||||
|
@ -6,6 +6,7 @@ import net.Indyuce.mmoitems.comp.PhatLootsHook;
|
||||
import net.Indyuce.mmoitems.gui.listener.GuiListener;
|
||||
import net.Indyuce.mmoitems.listener.*;
|
||||
import net.Indyuce.mmoitems.listener.option.DroppedItems;
|
||||
import net.Indyuce.mmoitems.listener.option.SoulboundNoDrop;
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
public class MMOItemsBukkit {
|
||||
@ -33,6 +34,9 @@ public class MMOItemsBukkit {
|
||||
if (plugin.getLanguage().disableRemovedItems)
|
||||
Bukkit.getPluginManager().registerEvents(new DisabledItemsListener(plugin), plugin);
|
||||
|
||||
if (!plugin.getConfig().getBoolean("soulbound.can-drop"))
|
||||
Bukkit.getPluginManager().registerEvents(new SoulboundNoDrop(), plugin);
|
||||
|
||||
// Profile support
|
||||
if (MythicLib.plugin.hasProfiles())
|
||||
Bukkit.getPluginManager().registerEvents(new ProfileSupportListener(), plugin);
|
||||
|
@ -21,7 +21,6 @@ import org.bukkit.event.inventory.CraftItemEvent;
|
||||
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
import org.bukkit.event.inventory.PrepareItemCraftEvent;
|
||||
import org.bukkit.event.player.PlayerDropItemEvent;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
import org.bukkit.inventory.CraftingInventory;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
@ -137,13 +136,6 @@ public class ItemListener implements Listener {
|
||||
if (newItem != null) event.setCurrentItem(newItem);
|
||||
}
|
||||
|
||||
@EventHandler(ignoreCancelled = true)
|
||||
private void dropItem(PlayerDropItemEvent event) {
|
||||
NBTItem nbt = NBTItem.get(event.getItemDrop().getItemStack());
|
||||
if (!MMOItems.plugin.getConfig().getBoolean("soulbound.can-drop") && nbt.hasTag("MMOITEMS_SOULBOUND"))
|
||||
event.setCancelled(true);
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
public void playerJoin(PlayerJoinEvent event) {
|
||||
if (!MythicLib.plugin.hasProfiles()) updateInventory(event.getPlayer());
|
||||
|
@ -81,11 +81,6 @@ public class PlayerListener implements Listener {
|
||||
final ItemStack item = iterator.next();
|
||||
final NBTItem nbt = NBTItem.get(item);
|
||||
|
||||
/*
|
||||
* Not a perfect check but it's very sufficient and so we avoid
|
||||
* using a JsonParser followed by map checkups in the SoulboundData
|
||||
* constructor
|
||||
*/
|
||||
if (nbt.getBoolean("MMOITEMS_DISABLE_DEATH_DROP") || (MMOItems.plugin.getLanguage().keepSoulboundOnDeath && MMOUtils.isSoulboundTo(nbt, player))) {
|
||||
iterator.remove();
|
||||
soulboundInfo.registerItem(item);
|
||||
|
@ -0,0 +1,154 @@
|
||||
package net.Indyuce.mmoitems.listener.option;
|
||||
|
||||
import io.lumine.mythic.lib.api.item.NBTItem;
|
||||
import net.Indyuce.mmoitems.util.MMOUtils;
|
||||
import org.bukkit.entity.HumanEntity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||
import org.bukkit.event.inventory.InventoryDragEvent;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
import org.bukkit.event.player.PlayerDropItemEvent;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Useful Resources:
|
||||
* - <a href="https://hub.spigotmc.org/javadocs/bukkit/org/bukkit/event/inventory/InventoryAction.html">...</a>
|
||||
* - <a href="https://hub.spigotmc.org/javadocs/bukkit/org/bukkit/event/inventory/InventoryType.html">...</a>
|
||||
*
|
||||
* @author Jules
|
||||
*/
|
||||
public class SoulboundNoDrop implements Listener {
|
||||
|
||||
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
|
||||
public void cannotDrop(PlayerDropItemEvent event) {
|
||||
if (isBound(event.getItemDrop().getItemStack(), event.getPlayer())) event.setCancelled(true);
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
|
||||
public void cannotDragAround(InventoryDragEvent event) {
|
||||
|
||||
if (event.getView().getType() == InventoryType.CRAFTING) return;
|
||||
|
||||
// This easily allows to check if the item was dragged in or out of the player's inventory
|
||||
final int topInventorySize = event.getView().getTopInventory().getContents().length;
|
||||
for (Map.Entry<Integer, ItemStack> entry : event.getNewItems().entrySet())
|
||||
if (entry.getKey() < topInventorySize && isBound(entry.getValue(), event.getWhoClicked())) {
|
||||
event.setCancelled(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
|
||||
public void cannotMoveAround(InventoryClickEvent event) {
|
||||
|
||||
// Can only move around in
|
||||
if (event.getView().getType() == InventoryType.CRAFTING) return;
|
||||
|
||||
try {
|
||||
|
||||
// Depends on click and inventory type.
|
||||
final boolean result = isSafe(event);
|
||||
if (!result) event.setCancelled(true);
|
||||
} catch (RuntimeException exception) {
|
||||
|
||||
// Safe check...
|
||||
if (isBound(event.getCurrentItem(), event.getWhoClicked()) || isBound(event.getCursor(), event.getWhoClicked()))
|
||||
event.setCancelled(true);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isSafe(@NotNull InventoryClickEvent event) {
|
||||
switch (event.getAction()) {
|
||||
|
||||
/*
|
||||
* Pickups
|
||||
*/
|
||||
case NOTHING: // 'Nothing happens' is safe enough
|
||||
case PICKUP_ALL: // Can pickup any item
|
||||
case PICKUP_SOME:
|
||||
case PICKUP_HALF:
|
||||
case PICKUP_ONE:
|
||||
case COLLECT_TO_CURSOR: // Considered a pickup
|
||||
case CLONE_STACK: // (Creative) Clones currentItem into cursor. Considered a pickup
|
||||
case HOTBAR_MOVE_AND_READD: // Some is given to the player, but not the target inventory, hence safe!
|
||||
return true;
|
||||
|
||||
/*
|
||||
* Drop cursor. Check cursor
|
||||
*/
|
||||
case DROP_ONE_CURSOR: // Check cursor (dropped)
|
||||
case DROP_ALL_CURSOR:
|
||||
return !isBound(event.getCursor(), event.getWhoClicked());
|
||||
|
||||
/*
|
||||
* Drop current item. Check current item
|
||||
*/
|
||||
case DROP_ALL_SLOT: // Check current item (dropped)
|
||||
case DROP_ONE_SLOT:
|
||||
return !isBound(event.getCurrentItem(), event.getWhoClicked());
|
||||
|
||||
/*
|
||||
* Places. Check cursor only if place is in remove inventory
|
||||
*/
|
||||
case SWAP_WITH_CURSOR:
|
||||
case PLACE_ALL:
|
||||
case PLACE_SOME:
|
||||
case PLACE_ONE: {
|
||||
|
||||
// Can place any item in player's inventory
|
||||
if (event.getClickedInventory().getType() == InventoryType.PLAYER) return true;
|
||||
|
||||
// Only accepted if the item is not soulbound
|
||||
return !isBound(event.getCursor(), event.getWhoClicked());
|
||||
}
|
||||
|
||||
/*
|
||||
* Swap with hotbar. Check hotbar item only if
|
||||
* swap is done with remote inventory
|
||||
*/
|
||||
case HOTBAR_SWAP: {
|
||||
|
||||
// Can place any item in player's inventory
|
||||
if (event.getClickedInventory().getType() == InventoryType.PLAYER) return true;
|
||||
|
||||
// Check hotbar
|
||||
final ItemStack hotbarItem = event.getWhoClicked().getInventory().getItem(event.getHotbarButton());
|
||||
return !isBound(hotbarItem, event.getWhoClicked());
|
||||
}
|
||||
|
||||
/*
|
||||
* Shift click item move. Check current item only if
|
||||
* being placed in remove inventory
|
||||
*/
|
||||
case MOVE_TO_OTHER_INVENTORY: {
|
||||
|
||||
// Can move anything to player's inventory
|
||||
if (event.getClickedInventory().getType() != InventoryType.PLAYER) return true;
|
||||
|
||||
// Check current item
|
||||
return !isBound(event.getCurrentItem(), event.getWhoClicked());
|
||||
}
|
||||
|
||||
/*
|
||||
* For anything else, check both current item and cursor for safeguard.
|
||||
* Maybe caused by 1.20.6+ inventory actions and other plugins.
|
||||
*/
|
||||
case UNKNOWN:
|
||||
default:
|
||||
throw new RuntimeException("Not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isBound(@Nullable ItemStack item, @NotNull HumanEntity player) {
|
||||
return item != null && item.hasItemMeta() && MMOUtils.isSoulboundTo(NBTItem.get(item), (Player) player);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -125,12 +125,15 @@ soulbound:
|
||||
base: 1
|
||||
per-lvl: 1
|
||||
|
||||
# Whether or not soulbound items should be
|
||||
# Whether soulbound items should be
|
||||
# kept when a player dies.
|
||||
keep-on-death: true
|
||||
|
||||
# Whether or not soulbound item can be
|
||||
# dropped by the player
|
||||
# [Experimental feature]
|
||||
# When toggled off, players cannot drop or take
|
||||
# Soulbound items away from their inventory.
|
||||
# Requires `keep-on-death` enabled.
|
||||
# Changes apply on server restart.
|
||||
can-drop: true
|
||||
|
||||
# Enable, disable, and customize the weapon effects here.
|
||||
|
Loading…
Reference in New Issue
Block a user