blob: b79fa5233ba0e02e1d4c2f621e78b0b2e890a674 [file] [log] [blame] [raw]
package li.cil.oc.util
import java.util
import li.cil.oc.{Settings, Items}
import net.minecraft.block.Block
import net.minecraft.item.crafting._
import net.minecraft.item.{Item, ItemStack}
import net.minecraft.util.StatCollector
import net.minecraftforge.oredict.{OreDictionary, ShapelessOreRecipe, ShapedOreRecipe}
import scala.collection.convert.WrapAsScala._
import scala.collection.mutable
object ItemCosts {
protected val cache = mutable.Map.empty[ItemStack, Iterable[(ItemStack, Double)]]
cache += Items.ironNugget.createItemStack() -> Iterable((new ItemStack(Item.ingotIron), 1.0 / 9.0))
def terminate(item: Item, meta: Int = 0) = cache += new ItemStack(item, 1, meta) -> mutable.Iterable((new ItemStack(item, 1, meta), 1))
def terminate(block: Block) = cache += new ItemStack(block) -> mutable.Iterable((new ItemStack(block), 1))
terminate(Block.blockClay)
terminate(Block.cobblestone)
terminate(Block.glass)
terminate(Block.planks)
terminate(Block.sand)
terminate(Block.stone)
terminate(Item.blazeRod)
terminate(Item.bucketEmpty)
terminate(Item.clay)
terminate(Item.coal)
terminate(Item.diamond)
for (i <- 0 to 15) terminate(Item.dyePowder, i)
terminate(Item.emerald)
terminate(Item.enderPearl)
terminate(Item.eyeOfEnder)
terminate(Item.ghastTear)
terminate(Item.glowstone)
terminate(Item.ingotGold)
terminate(Item.ingotIron)
terminate(Item.netherQuartz)
terminate(Item.netherStar)
terminate(Item.paper)
terminate(Item.redstone)
terminate(Item.silk)
terminate(Item.slimeBall)
terminate(Item.stick)
def hasCosts(stack: ItemStack) = {
val ingredients = computeIngredients(stack)
ingredients.size > 0 && (ingredients.size > 1 || !ItemStack.areItemStacksEqual(ingredients.head._1, stack))
}
def addTooltip(stack: ItemStack, tooltip: util.List[String]) {
tooltip.add(StatCollector.translateToLocal(Settings.namespace + "tooltip.Materials"))
for ((ingredient, count) <- computeIngredients(stack)) {
val line = math.ceil(count).toInt + "x " + ingredient.getDisplayName
tooltip.add(line)
}
}
protected def computeIngredients(what: ItemStack): Iterable[(ItemStack, Double)] = {
def deflate(list: Iterable[(ItemStack, Double)]): Iterable[(ItemStack, Double)] = {
val counts = mutable.Map.empty[ItemStack, Double]
for ((stack, count) <- list) {
counts.find {
case (key, value) => fuzzyEquals(key, stack)
} match {
case Some((key, value)) => counts.update(key, value + count)
case _ => counts += stack -> count
}
}
counts
}
def accumulate(input: Any, path: Seq[ItemStack] = Seq.empty): Iterable[(ItemStack, Double)] = input match {
case stack: ItemStack =>
cache.find {
case (key, value) => fuzzyEquals(key, stack)
} match {
case Some((_, value)) => value
case _ =>
if (path.exists(value => fuzzyEquals(value, stack))) {
Iterable((stack, 1.0))
}
else {
val recipes = CraftingManager.getInstance.getRecipeList.map(_.asInstanceOf[IRecipe])
val recipe = recipes.find(recipe => recipe.getRecipeOutput != null && fuzzyEquals(stack, recipe.getRecipeOutput))
val (ingredients, output) = recipe match {
case Some(recipe: ShapedRecipes) => (recipe.recipeItems.flatMap(accumulate(_, path :+ stack)).toIterable, recipe.getRecipeOutput.stackSize)
case Some(recipe: ShapelessRecipes) => (recipe.recipeItems.flatMap(accumulate(_, path :+ stack)).toIterable, recipe.getRecipeOutput.stackSize)
case Some(recipe: ShapedOreRecipe) => (recipe.getInput.flatMap(accumulate(_, path :+ stack)).toIterable, recipe.getRecipeOutput.stackSize)
case Some(recipe: ShapelessOreRecipe) => (recipe.getInput.flatMap(accumulate(_, path :+ stack)).toIterable, recipe.getRecipeOutput.stackSize)
case _ => FurnaceRecipes.smelting.getSmeltingList.asInstanceOf[util.Map[Integer, ItemStack]].find {
case (_, value) => fuzzyEquals(stack, value)
} match {
case Some((blockId, result)) => (accumulate(new ItemStack(blockId, 1, 0), path :+ stack), result.stackSize)
case _ => FurnaceRecipes.smelting.getMetaSmeltingList.find {
case (_, value) => fuzzyEquals(stack, value)
} match {
case Some((data, result)) =>
val (itemId, metadata) = (data.get(0), data.get(1))
(accumulate(new ItemStack(itemId, 1, metadata), path :+ stack), result.stackSize)
case _ => (Iterable((stack, 1.0)), 1)
}
}
}
val scaled = deflate(ingredients.map {
case (ingredient, count) => (ingredient.copy(), count / output)
}).toArray.sortBy(_._1.getUnlocalizedName)
cache += stack.copy() -> scaled
scaled
}
}
case list: util.ArrayList[ItemStack]@unchecked if !list.isEmpty =>
var result = Iterable.empty[(ItemStack, Double)]
for (stack <- list if result.isEmpty) {
cache.find {
case (key, value) => fuzzyEquals(key, stack)
} match {
case Some((_, value)) => result = value
case _ =>
}
}
if (result.isEmpty) {
result = accumulate(list.get(0), path)
}
result
case _ => Iterable.empty
}
accumulate(what)
}
// In case you'd like to use this class for your items and your items use
// NBT data in the item stack to differentiate them uncomment the last part.
// We don't use this in OC because the NBT of items can change dynamically,
// for example by components being assigned an address, which will break the
// equals check.
private def fuzzyEquals(stack1: ItemStack, stack2: ItemStack) =
stack1.itemID == stack2.itemID &&
(stack1.getItemDamage == stack2.getItemDamage ||
stack1.getItemDamage == OreDictionary.WILDCARD_VALUE ||
stack2.getItemDamage == OreDictionary.WILDCARD_VALUE ||
stack1.getItem.isDamageable) // && ItemStack.areItemStackTagsEqual(stack1, stack2)
}