diff --git a/eco-api/src/main/java/com/willfp/eco/core/gui/slot/CustomSlot.java b/eco-api/src/main/java/com/willfp/eco/core/gui/slot/CustomSlot.java index b646759f..00a1d6bf 100644 --- a/eco-api/src/main/java/com/willfp/eco/core/gui/slot/CustomSlot.java +++ b/eco-api/src/main/java/com/willfp/eco/core/gui/slot/CustomSlot.java @@ -32,7 +32,7 @@ public abstract class CustomSlot implements Slot { } @Override - public @NotNull ItemStack getItemStack(@NotNull final Player player) { + public final @NotNull ItemStack getItemStack(@NotNull final Player player) { if (delegate == null) { throw new IllegalStateException("Custom Slot was not initialized!"); } @@ -41,18 +41,28 @@ public abstract class CustomSlot implements Slot { } @Override - public boolean isCaptive(@NotNull final Player player, - @NotNull final Menu menu, - @Nullable final ItemStack item) { + public final boolean isCaptive(@NotNull final Player player, + @NotNull final Menu menu) { if (delegate == null) { throw new IllegalStateException("Custom Slot was not initialized!"); } - return delegate.isCaptive(player, menu, item); + return delegate.isCaptive(player, menu); } @Override - public boolean isCaptiveFromEmpty() { + public final boolean canCaptivateItem(@NotNull final Player player, + @NotNull final Menu menu, + @Nullable final ItemStack itemStack) { + if (delegate == null) { + throw new IllegalStateException("Custom Slot was not initialized!"); + } + + return delegate.canCaptivateItem(player, menu, itemStack); + } + + @Override + public final boolean isCaptiveFromEmpty() { if (delegate == null) { throw new IllegalStateException("Custom Slot was not initialized!"); } diff --git a/eco-api/src/main/java/com/willfp/eco/core/gui/slot/ReactiveSlot.java b/eco-api/src/main/java/com/willfp/eco/core/gui/slot/ReactiveSlot.java index 8bbb54cf..f4321817 100644 --- a/eco-api/src/main/java/com/willfp/eco/core/gui/slot/ReactiveSlot.java +++ b/eco-api/src/main/java/com/willfp/eco/core/gui/slot/ReactiveSlot.java @@ -35,10 +35,16 @@ public abstract class ReactiveSlot implements Slot { } @Override - public boolean isCaptive(@NotNull final Player player, - @NotNull final Menu menu, - @Nullable final ItemStack item) { - return getSlot(player, menu).isCaptive(player, menu, item); + public final boolean isCaptive(@NotNull final Player player, + @NotNull final Menu menu) { + return getSlot(player, menu).isCaptive(player, menu); + } + + @Override + public final boolean canCaptivateItem(@NotNull final Player player, + @NotNull final Menu menu, + @Nullable final ItemStack itemStack) { + return getSlot(player, menu).canCaptivateItem(player, menu, itemStack); } @Override diff --git a/eco-api/src/main/java/com/willfp/eco/core/gui/slot/Slot.java b/eco-api/src/main/java/com/willfp/eco/core/gui/slot/Slot.java index a68398c8..a9b2f4ba 100644 --- a/eco-api/src/main/java/com/willfp/eco/core/gui/slot/Slot.java +++ b/eco-api/src/main/java/com/willfp/eco/core/gui/slot/Slot.java @@ -51,14 +51,13 @@ public interface Slot extends GUIComponent { * * @param player The player. * @param menu The menu. - * @param itemStack The item. + * @param itemStack The item; use null if the item is unknown. * @return If captive. */ - default boolean isCaptive(@NotNull final Player player, - @NotNull final Menu menu, - @Nullable final ItemStack itemStack) { - // Delegate to no-item version for backwards compatibility. - return this.isCaptive(player, menu); + default boolean canCaptivateItem(@NotNull final Player player, + @NotNull final Menu menu, + @Nullable final ItemStack itemStack) { + return true; } /** diff --git a/eco-api/src/main/java/com/willfp/eco/core/gui/slot/SlotBuilder.java b/eco-api/src/main/java/com/willfp/eco/core/gui/slot/SlotBuilder.java index f726e760..1e86e7a2 100644 --- a/eco-api/src/main/java/com/willfp/eco/core/gui/slot/SlotBuilder.java +++ b/eco-api/src/main/java/com/willfp/eco/core/gui/slot/SlotBuilder.java @@ -1,17 +1,15 @@ package com.willfp.eco.core.gui.slot; +import com.willfp.eco.core.gui.slot.functional.CaptiveCondition; import com.willfp.eco.core.gui.slot.functional.SlotHandler; import com.willfp.eco.core.gui.slot.functional.SlotModifier; import com.willfp.eco.core.gui.slot.functional.SlotUpdater; import org.bukkit.entity.Player; import org.bukkit.event.inventory.ClickType; import org.bukkit.event.inventory.InventoryClickEvent; -import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import java.util.function.BiConsumer; -import java.util.function.BiPredicate; import java.util.function.Predicate; /** @@ -146,17 +144,15 @@ public interface SlotBuilder { * @param predicate The predicate. Returns true when the slot should not be captive. * @return The builder. */ - default SlotBuilder notCaptiveFor(@NotNull final Predicate predicate) { - return this.notCaptiveFor((player, itemStack) -> predicate.test(player)); - } + SlotBuilder notCaptiveFor(@NotNull final Predicate predicate); /** - * Prevent captive for players and items that match a predicate. + * Set a whitelist for allowed captive items. * - * @param predicate The predicate. Returns true when the slot should not be captive. + * @param condition The condition. Returns true when the slot should be captive. * @return The builder. */ - default SlotBuilder notCaptiveFor(@NotNull BiPredicate predicate) { + default SlotBuilder setCaptiveCondition(@NotNull final CaptiveCondition condition) { return this; } diff --git a/eco-api/src/main/java/com/willfp/eco/core/gui/slot/functional/CaptiveCondition.java b/eco-api/src/main/java/com/willfp/eco/core/gui/slot/functional/CaptiveCondition.java new file mode 100644 index 00000000..df4f4761 --- /dev/null +++ b/eco-api/src/main/java/com/willfp/eco/core/gui/slot/functional/CaptiveCondition.java @@ -0,0 +1,25 @@ +package com.willfp.eco.core.gui.slot.functional; + +import com.willfp.eco.core.gui.menu.Menu; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Interface to test if a captive slot is captive given a player, menu, and item. + */ +@FunctionalInterface +public interface CaptiveCondition { + /** + * Get if the slot is captive. + * + * @param player The player. + * @param menu The menu. + * @param itemStack The item. + * @return If captive. + */ + boolean isCaptive(@NotNull Player player, + @NotNull Menu menu, + @Nullable ItemStack itemStack); +} diff --git a/eco-api/src/main/kotlin/com/willfp/eco/core/gui/GUIHelpers.kt b/eco-api/src/main/kotlin/com/willfp/eco/core/gui/GUIHelpers.kt index a82429f0..a90f352e 100644 --- a/eco-api/src/main/kotlin/com/willfp/eco/core/gui/GUIHelpers.kt +++ b/eco-api/src/main/kotlin/com/willfp/eco/core/gui/GUIHelpers.kt @@ -72,11 +72,11 @@ fun SlotBuilder.onClick(clickType: ClickType, action: (Player, InventoryClickEve /** @see SlotBuilder.notCaptiveFor */ fun SlotBuilder.notCaptiveFor(test: (Player) -> Boolean): SlotBuilder = - this.notCaptiveFor { t, _ -> test(t) } + this.notCaptiveFor { test(it) } -/** @see SlotBuilder.notCaptiveFor */ -fun SlotBuilder.notCaptiveFor(test: (Player, ItemStack?) -> Boolean): SlotBuilder = - this.notCaptiveFor { player, item -> test(player, item) } +/** @see SlotBuilder.setCaptiveCondition */ +fun SlotBuilder.setCaptiveCondition(test: (Player, Menu, ItemStack?) -> Boolean): SlotBuilder = + this.setCaptiveCondition { a, b, c -> test(a, b, c) } /** * @see SlotBuilder.setModifier diff --git a/eco-core/core-backend/src/main/kotlin/com/willfp/eco/internal/gui/slot/EcoCaptiveSlot.kt b/eco-core/core-backend/src/main/kotlin/com/willfp/eco/internal/gui/slot/EcoCaptiveSlot.kt index a84468a2..c2f83eff 100644 --- a/eco-core/core-backend/src/main/kotlin/com/willfp/eco/internal/gui/slot/EcoCaptiveSlot.kt +++ b/eco-core/core-backend/src/main/kotlin/com/willfp/eco/internal/gui/slot/EcoCaptiveSlot.kt @@ -1,6 +1,7 @@ package com.willfp.eco.internal.gui.slot import com.willfp.eco.core.gui.menu.Menu +import com.willfp.eco.core.gui.slot.functional.CaptiveCondition import com.willfp.eco.core.gui.slot.functional.SlotHandler import com.willfp.eco.core.gui.slot.functional.SlotProvider import com.willfp.eco.util.toSingletonList @@ -11,16 +12,21 @@ import org.bukkit.inventory.ItemStack class EcoCaptiveSlot( provider: SlotProvider, private val captiveFromEmpty: Boolean, - private val notCaptiveFor: (Player, ItemStack?) -> Boolean + private val notCaptiveFor: (Player) -> Boolean, + private val condition: CaptiveCondition ) : EcoSlot( provider, ClickType.values().associateWith { - captiveWithTest(notCaptiveFor).toSingletonList() + captiveWithTest(notCaptiveFor, condition).toSingletonList() }, { _, _, prev -> prev } ) { - override fun isCaptive(player: Player, menu: Menu, itemStack: ItemStack?): Boolean { - return !notCaptiveFor(player, itemStack) + override fun isCaptive(player: Player, menu: Menu): Boolean { + return !notCaptiveFor(player) + } + + override fun canCaptivateItem(player: Player, menu: Menu, itemStack: ItemStack?): Boolean { + return condition.isCaptive(player, menu, itemStack) } override fun isCaptiveFromEmpty(): Boolean { @@ -28,6 +34,14 @@ class EcoCaptiveSlot( } } -private fun captiveWithTest(itemTest: (Player, ItemStack?) -> Boolean): SlotHandler = SlotHandler { event, _, _ -> - event.isCancelled = itemTest(event.whoClicked as Player, event.cursor) +private fun captiveWithTest( + playerTest: (Player) -> Boolean, + condition: CaptiveCondition +): SlotHandler = SlotHandler { event, _, menu -> + val player = event.whoClicked as Player + + val allowedForPlayer = !playerTest(player) + val allowedForCondition = condition.isCaptive(player, menu, event.currentItem) + + event.isCancelled = !(allowedForCondition && allowedForPlayer) } diff --git a/eco-core/core-backend/src/main/kotlin/com/willfp/eco/internal/gui/slot/EcoSlotBuilder.kt b/eco-core/core-backend/src/main/kotlin/com/willfp/eco/internal/gui/slot/EcoSlotBuilder.kt index 41e463e6..7703147a 100644 --- a/eco-core/core-backend/src/main/kotlin/com/willfp/eco/internal/gui/slot/EcoSlotBuilder.kt +++ b/eco-core/core-backend/src/main/kotlin/com/willfp/eco/internal/gui/slot/EcoSlotBuilder.kt @@ -2,13 +2,13 @@ package com.willfp.eco.internal.gui.slot import com.willfp.eco.core.gui.slot.Slot import com.willfp.eco.core.gui.slot.SlotBuilder +import com.willfp.eco.core.gui.slot.functional.CaptiveCondition import com.willfp.eco.core.gui.slot.functional.SlotHandler import com.willfp.eco.core.gui.slot.functional.SlotProvider import com.willfp.eco.core.gui.slot.functional.SlotUpdater import org.bukkit.entity.Player import org.bukkit.event.inventory.ClickType -import org.bukkit.inventory.ItemStack -import java.util.function.BiPredicate +import java.util.function.Predicate class EcoSlotBuilder(private val provider: SlotProvider) : SlotBuilder { private var captive = false @@ -17,15 +17,21 @@ class EcoSlotBuilder(private val provider: SlotProvider) : SlotBuilder { private val handlers = mutableMapOf>() - private var notCaptiveFor: (Player, ItemStack?) -> Boolean = { _, _ -> false} + private var captiveCondition = CaptiveCondition { _, _, _ -> true } + private var notCaptiveFor: (Player) -> Boolean = { _ -> false} override fun onClick(type: ClickType, action: SlotHandler): SlotBuilder { handlers.computeIfAbsent(type) { mutableListOf() } += action return this } - override fun notCaptiveFor(predicate: BiPredicate): SlotBuilder { - notCaptiveFor = { player, item -> predicate.test(player, item) } + override fun notCaptiveFor(predicate: Predicate): SlotBuilder { + notCaptiveFor = { player -> predicate.test(player) } + return this + } + + override fun setCaptiveCondition(condition: CaptiveCondition): SlotBuilder { + captiveCondition = condition return this } @@ -45,7 +51,8 @@ class EcoSlotBuilder(private val provider: SlotProvider) : SlotBuilder { EcoCaptiveSlot( provider, captiveFromEmpty, - notCaptiveFor + notCaptiveFor, + captiveCondition ) } else { EcoSlot( diff --git a/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/gui/GUIListener.kt b/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/gui/GUIListener.kt index 1e7b32ef..62b23f29 100644 --- a/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/gui/GUIListener.kt +++ b/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/gui/GUIListener.kt @@ -95,7 +95,7 @@ class GUIListener(private val plugin: EcoPlugin) : Listener { val slot = menu.getSlot(row, column, player) - if (!slot.isCaptive(player, menu, event.currentItem)) { + if (!slot.isCaptive(player, menu) && slot.canCaptivateItem(player, menu, event.currentItem)) { event.isCancelled = true } }