Improved shapeless recipe support

This commit is contained in:
Auxilor
2022-03-07 12:27:21 +00:00
parent 595bc76294
commit 20f4bf4e78
5 changed files with 74 additions and 140 deletions

View File

@@ -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),

View File

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

View File

@@ -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<StackedRecipeHandler>()
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<out CraftingRecipe>
}

View File

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

View File

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