From 20f4bf4e78558444df51d7a50a68f08d8b8eba6b Mon Sep 17 00:00:00 2001 From: Auxilor Date: Mon, 7 Mar 2022 12:27:21 +0000 Subject: [PATCH] Improved shapeless recipe support --- .../eco/internal/spigot/EcoSpigotPlugin.kt | 7 +- .../recipes/ShapelessStackedRecipeListener.kt | 136 ------------------ .../spigot/recipes/StackedRecipeListener.kt | 28 +++- .../ShapedCraftingRecipeStackHandler.kt | 15 ++ .../ShapelessCraftingRecipeStackHandler.kt | 28 ++++ 5 files changed, 74 insertions(+), 140 deletions(-) delete mode 100644 eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/recipes/ShapelessStackedRecipeListener.kt create mode 100644 eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/recipes/stackhandlers/ShapedCraftingRecipeStackHandler.kt create mode 100644 eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/recipes/stackhandlers/ShapelessCraftingRecipeStackHandler.kt diff --git a/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/EcoSpigotPlugin.kt b/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/EcoSpigotPlugin.kt index 11b637d1..4a79684d 100644 --- a/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/EcoSpigotPlugin.kt +++ b/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/EcoSpigotPlugin.kt @@ -111,10 +111,11 @@ import com.willfp.eco.internal.spigot.proxy.FastItemStackFactoryProxy import com.willfp.eco.internal.spigot.proxy.SkullProxy import com.willfp.eco.internal.spigot.proxy.TPSProxy import com.willfp.eco.internal.spigot.recipes.CraftingRecipeListener -import com.willfp.eco.internal.spigot.recipes.ShapelessStackedRecipeListener import com.willfp.eco.internal.spigot.recipes.StackedRecipeListener import com.willfp.eco.internal.spigot.recipes.listeners.ComplexInComplex import com.willfp.eco.internal.spigot.recipes.listeners.ComplexInVanilla +import com.willfp.eco.internal.spigot.recipes.stackhandlers.ShapedCraftingRecipeStackHandler +import com.willfp.eco.internal.spigot.recipes.stackhandlers.ShapelessCraftingRecipeStackHandler import com.willfp.eco.util.NumberUtils import com.willfp.eco.util.ServerUtils import com.willfp.eco.util.SkullUtils @@ -160,6 +161,9 @@ abstract class EcoSpigotPlugin : EcoPlugin() { CraftingRecipeListener.registerListener(ComplexInComplex()) CraftingRecipeListener.registerListener(ComplexInVanilla()) + StackedRecipeListener.registerHandler(ShapedCraftingRecipeStackHandler()) + StackedRecipeListener.registerHandler(ShapelessCraftingRecipeStackHandler()) + SegmentParserGroup().register() SegmentParserUseIfPresent().register() @@ -319,7 +323,6 @@ abstract class EcoSpigotPlugin : EcoPlugin() { EntityDeathByEntityListeners(this), CraftingRecipeListener(), StackedRecipeListener(this), - ShapelessStackedRecipeListener(this), GUIListener(this), ArrowDataListener(this), ArmorChangeEventListeners(this), diff --git a/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/recipes/ShapelessStackedRecipeListener.kt b/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/recipes/ShapelessStackedRecipeListener.kt deleted file mode 100644 index 7c60e2e3..00000000 --- a/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/recipes/ShapelessStackedRecipeListener.kt +++ /dev/null @@ -1,136 +0,0 @@ -package com.willfp.eco.internal.spigot.recipes - -import com.willfp.eco.core.EcoPlugin -import com.willfp.eco.core.recipe.Recipes -import com.willfp.eco.core.recipe.parts.EmptyTestableItem -import com.willfp.eco.core.recipe.parts.GroupedTestableItems -import com.willfp.eco.core.recipe.parts.TestableStack -import com.willfp.eco.core.recipe.recipes.ShapelessCraftingRecipe -import org.bukkit.Material -import org.bukkit.event.EventHandler -import org.bukkit.event.EventPriority -import org.bukkit.event.Listener -import org.bukkit.event.inventory.InventoryClickEvent -import org.bukkit.inventory.CraftingInventory -import kotlin.math.max -import kotlin.math.min - -class ShapelessStackedRecipeListener( - private val plugin: EcoPlugin -) : Listener { - /* - If you think you can fix this code, you're wrong. - Or, pray to whatever god you have that you can figure it out. - Best of luck, you're going to need it. - */ - @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) - fun handleStacks(event: InventoryClickEvent) { - val inventory = event.clickedInventory as? CraftingInventory ?: return - if (event.slot != 0) { - return - } - - // Just in case - if (EmptyTestableItem().matches(inventory.getItem(event.slot))) { - return - } - - val matrix = inventory.matrix - - val recipe = Recipes.getMatch(matrix) as? ShapelessCraftingRecipe ?: return - - var isStackedRecipe = false - var maxCraftable = Int.MAX_VALUE - - val test = recipe.newTest() - - // Start by calculating the maximum number of items to craft - for (i in 0..8) { - val item = inventory.matrix.getOrNull(i) ?: continue - val part = test.matchAndRemove(item).let { - if (it is GroupedTestableItems) { - it.getMatchingChild(item) - } else it - } ?: continue - - if (part is TestableStack) { - isStackedRecipe = true - } - - maxCraftable = min(maxCraftable, Math.floorDiv(item.amount, part.item.amount)) - } - - if (!isStackedRecipe) { - return - } - - // Don't allow crafting above the max stack size of the output - maxCraftable = min(maxCraftable, Math.floorDiv(recipe.output.maxStackSize, recipe.output.amount)) - - // Run this first before the deduction or shift-clicking breaks - val existingResult = inventory.result - - val test2 = recipe.newTest(); - - // Deduct the correct number of items from the inventory - for (i in 0..8) { - val item = inventory.matrix.getOrNull(i) ?: continue - val part = test2.matchAndRemove(item).let { - if (it is GroupedTestableItems) { - it.getMatchingChild(item) - } else it - } ?: continue - - val amount = max( - if (event.isShiftClick) { - item.amount - (part.item.amount * maxCraftable) - } else { - item.amount - part.item.amount - }, 0 - ) - - // Anti-Underflow - if (amount == 0) { - item.type = Material.AIR - } - item.amount = amount - - /* - Everything below this point is unreadable garbage - If you want to modify the behaviour of stacked recipes, then - change the code above. The code after this just sets the items - in the inventory, despite spigot trying to stop me. - */ - - // Do it twice because spigot hates me - // Everything has to be cloned because the inventory changes the item - inventory.matrix[i] = item.clone() // Use un-cloned version first - // This isn't even funny anymore - runTwice { - val newItem = item.clone() - // Just use every method possible to set the item - inventory.matrix[i] = newItem - inventory.setItem(i + 1, newItem) - // Just to be safe, modify the instance (safe check) Using ?. causes a warning. - @Suppress("SENSELESS_COMPARISON") // I hate compiler warnings - if (inventory.matrix[i] != null) { - inventory.matrix[i].amount = amount - } - } - } - - // Multiply the result by the amount to craft if shift-clicking - existingResult ?: return - - // Modify the item and then set it - if (event.isShiftClick) { - existingResult.amount *= maxCraftable - } - inventory.result = existingResult - } - - private fun runTwice(block: () -> Unit) { - block() - plugin.scheduler.run(block) - } -} diff --git a/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/recipes/StackedRecipeListener.kt b/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/recipes/StackedRecipeListener.kt index e3842e40..1eb19d24 100644 --- a/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/recipes/StackedRecipeListener.kt +++ b/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/recipes/StackedRecipeListener.kt @@ -1,16 +1,19 @@ package com.willfp.eco.internal.spigot.recipes import com.willfp.eco.core.EcoPlugin +import com.willfp.eco.core.items.TestableItem import com.willfp.eco.core.recipe.Recipes import com.willfp.eco.core.recipe.parts.EmptyTestableItem import com.willfp.eco.core.recipe.parts.GroupedTestableItems import com.willfp.eco.core.recipe.parts.TestableStack +import com.willfp.eco.core.recipe.recipes.CraftingRecipe import org.bukkit.Material import org.bukkit.event.EventHandler import org.bukkit.event.EventPriority import org.bukkit.event.Listener import org.bukkit.event.inventory.InventoryClickEvent import org.bukkit.inventory.CraftingInventory +import org.bukkit.inventory.ItemStack import kotlin.math.max import kotlin.math.min @@ -38,13 +41,18 @@ class StackedRecipeListener( val recipe = Recipes.getMatch(matrix) ?: return + // Get the handler for the type of recipe + @Suppress("UNCHECKED_CAST") + val handler = handlers.firstOrNull { recipe::class.java.isAssignableFrom(it.recipeType) } ?: return + var isStackedRecipe = false var maxCraftable = Int.MAX_VALUE // Start by calculating the maximum number of items to craft + val maxToCraftData = handler.makeData(recipe) for (i in 0..8) { val item = inventory.matrix.getOrNull(i) ?: continue - val part = recipe.parts[i].let { + val part = handler.getPart(recipe, i, item, maxToCraftData).let { if (it is GroupedTestableItems) { it.getMatchingChild(item) } else it @@ -68,9 +76,11 @@ class StackedRecipeListener( val existingResult = inventory.result // Deduct the correct number of items from the inventory + + val deductionData = handler.makeData(recipe) for (i in 0..8) { val item = inventory.matrix.getOrNull(i) ?: continue - val part = recipe.parts[i].let { + val part = handler.getPart(recipe, i, item, deductionData).let { if (it is GroupedTestableItems) { it.getMatchingChild(item) } else it @@ -128,4 +138,18 @@ class StackedRecipeListener( block() plugin.scheduler.run(block) } + + companion object { + private val handlers = mutableListOf() + + fun registerHandler(handler: StackedRecipeHandler) { + handlers.add(handler) + } + } +} + +interface StackedRecipeHandler { + fun makeData(recipe: CraftingRecipe): Any? + fun getPart(recipe: CraftingRecipe, position: Int, item: ItemStack, data: Any?): TestableItem? + val recipeType: Class } diff --git a/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/recipes/stackhandlers/ShapedCraftingRecipeStackHandler.kt b/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/recipes/stackhandlers/ShapedCraftingRecipeStackHandler.kt new file mode 100644 index 00000000..8d69da53 --- /dev/null +++ b/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/recipes/stackhandlers/ShapedCraftingRecipeStackHandler.kt @@ -0,0 +1,15 @@ +package com.willfp.eco.internal.spigot.recipes.stackhandlers + +import com.willfp.eco.core.items.TestableItem +import com.willfp.eco.core.recipe.recipes.CraftingRecipe +import com.willfp.eco.core.recipe.recipes.ShapedCraftingRecipe +import com.willfp.eco.internal.spigot.recipes.StackedRecipeHandler +import org.bukkit.inventory.ItemStack + +class ShapedCraftingRecipeStackHandler : StackedRecipeHandler { + override val recipeType = ShapedCraftingRecipe::class.java + override fun makeData(recipe: CraftingRecipe): Any? = null + + override fun getPart(recipe: CraftingRecipe, position: Int, item: ItemStack, data: Any?): TestableItem? = + recipe.parts[position] +} \ No newline at end of file diff --git a/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/recipes/stackhandlers/ShapelessCraftingRecipeStackHandler.kt b/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/recipes/stackhandlers/ShapelessCraftingRecipeStackHandler.kt new file mode 100644 index 00000000..42c8f179 --- /dev/null +++ b/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/recipes/stackhandlers/ShapelessCraftingRecipeStackHandler.kt @@ -0,0 +1,28 @@ +package com.willfp.eco.internal.spigot.recipes.stackhandlers + +import com.willfp.eco.core.items.TestableItem +import com.willfp.eco.core.recipe.recipes.CraftingRecipe +import com.willfp.eco.core.recipe.recipes.ShapelessCraftingRecipe +import com.willfp.eco.internal.spigot.recipes.StackedRecipeHandler +import org.bukkit.inventory.ItemStack + +class ShapelessCraftingRecipeStackHandler : + StackedRecipeHandler { + override val recipeType = ShapelessCraftingRecipe::class.java + override fun makeData(recipe: CraftingRecipe): Any { + recipe as ShapelessCraftingRecipe + return recipe.newTest() + } + + override fun getPart( + recipe: CraftingRecipe, + position: Int, + item: ItemStack, + data: Any? + ): TestableItem? { + recipe as ShapelessCraftingRecipe + data as ShapelessCraftingRecipe.RecipeTest + + return data.matchAndRemove(item) + } +}