Improved shapeless recipe support
This commit is contained in:
@@ -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),
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
}
|
||||
|
||||
@@ -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]
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user