| package li.cil.oc.util |
| |
| import java.util |
| |
| import li.cil.oc.{Items, Settings} |
| 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, ShapedOreRecipe, ShapelessOreRecipe} |
| |
| 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 || !ingredients.head._1.isItemEqual(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) |
| } |