|  | package li.cil.oc.common.recipe | 
|  |  | 
|  | import java.io.{File, FileReader} | 
|  | import java.util.logging.Level | 
|  |  | 
|  | import com.typesafe.config._ | 
|  | import cpw.mods.fml.common.Loader | 
|  | import cpw.mods.fml.common.registry.GameRegistry | 
|  | import li.cil.oc.util.mods.GregTech | 
|  | import li.cil.oc.{Items, OpenComputers, api, common} | 
|  | import net.minecraft.block.Block | 
|  | import net.minecraft.item.crafting.FurnaceRecipes | 
|  | import net.minecraft.item.{Item, ItemStack} | 
|  | import net.minecraftforge.oredict.OreDictionary | 
|  | import org.apache.commons.io.FileUtils | 
|  |  | 
|  | import scala.collection.convert.WrapAsScala._ | 
|  | import scala.collection.mutable | 
|  |  | 
|  | object Recipes { | 
|  | val list = mutable.LinkedHashMap.empty[ItemStack, String] | 
|  | val oreDictEntries = mutable.LinkedHashMap.empty[String, ItemStack] | 
|  |  | 
|  | def addBlock[T <: common.block.Delegate](delegate: T, name: String, oreDict: String = null) = { | 
|  | Items.registerBlock(delegate, name) | 
|  | list += delegate.createItemStack() -> name | 
|  | register(oreDict, delegate.createItemStack()) | 
|  | delegate | 
|  | } | 
|  |  | 
|  | def addItem[T <: common.item.Delegate](delegate: T, name: String, oreDict: String = null) = { | 
|  | Items.registerItem(delegate, name) | 
|  | list += delegate.createItemStack() -> name | 
|  | register(oreDict, delegate.createItemStack()) | 
|  | delegate | 
|  | } | 
|  |  | 
|  | def addItem(instance: Item, name: String) = { | 
|  | Items.registerItem(instance, name) | 
|  | list += new ItemStack(instance) -> name | 
|  | instance | 
|  | } | 
|  |  | 
|  | private def register(name: String, item: ItemStack) { | 
|  | if (name != null) { | 
|  | oreDictEntries += name -> item | 
|  | } | 
|  | } | 
|  |  | 
|  | def init() { | 
|  | for ((name, stack) <- oreDictEntries) { | 
|  | if (!OreDictionary.getOres(name).contains(stack)) { | 
|  | OreDictionary.registerOre(name, stack) | 
|  | } | 
|  | } | 
|  | oreDictEntries.clear() | 
|  |  | 
|  | try { | 
|  | val defaultRecipes = new File(Loader.instance.getConfigDir + File.separator + "opencomputers" + File.separator + "default.recipes") | 
|  | val hardmodeRecipes = new File(Loader.instance.getConfigDir + File.separator + "opencomputers" + File.separator + "hardmode.recipes") | 
|  | val gregTechRecipes = new File(Loader.instance.getConfigDir + File.separator + "opencomputers" + File.separator + "gregtech.recipes") | 
|  | val userRecipes = new File(Loader.instance.getConfigDir + File.separator + "opencomputers" + File.separator + "user.recipes") | 
|  |  | 
|  | defaultRecipes.getParentFile.mkdirs() | 
|  | FileUtils.copyURLToFile(getClass.getResource("/assets/opencomputers/recipes/default.recipes"), defaultRecipes) | 
|  | FileUtils.copyURLToFile(getClass.getResource("/assets/opencomputers/recipes/hardmode.recipes"), hardmodeRecipes) | 
|  | FileUtils.copyURLToFile(getClass.getResource("/assets/opencomputers/recipes/gregtech.recipes"), gregTechRecipes) | 
|  | if (!userRecipes.exists()) { | 
|  | FileUtils.copyURLToFile(getClass.getResource("/assets/opencomputers/recipes/user.recipes"), userRecipes) | 
|  | } | 
|  | val config = ConfigParseOptions.defaults. | 
|  | setSyntax(ConfigSyntax.CONF). | 
|  | setIncluder(new ConfigIncluder with ConfigIncluderFile { | 
|  | var fallback: ConfigIncluder = _ | 
|  |  | 
|  | override def withFallback(fallback: ConfigIncluder) = { | 
|  | this.fallback = fallback | 
|  | this | 
|  | } | 
|  |  | 
|  | override def include(context: ConfigIncludeContext, what: String) = fallback.include(context, what) | 
|  |  | 
|  | override def includeFile(context: ConfigIncludeContext, what: File) = { | 
|  | val in = if (what.isAbsolute) new FileReader(what) else new FileReader(new File(userRecipes.getParentFile, what.getPath)) | 
|  | val result = ConfigFactory.parseReader(in) | 
|  | in.close() | 
|  | result.root() | 
|  | } | 
|  | }) | 
|  | val recipes = ConfigFactory.parseFile(userRecipes, config) | 
|  |  | 
|  | // Register all known recipes. | 
|  | for ((stack, name) <- list) { | 
|  | addRecipe(stack, recipes, name) | 
|  | } | 
|  |  | 
|  | // Navigation upgrade recrafting. | 
|  | val navigationUpgrade = api.Items.get("navigationUpgrade").createItemStack(1) | 
|  | GameRegistry.addRecipe(new ExtendedShapelessOreRecipe(navigationUpgrade, navigationUpgrade, new ItemStack(Item.map, 1, OreDictionary.WILDCARD_VALUE))) | 
|  | } | 
|  | catch { | 
|  | case e: Throwable => OpenComputers.log.log(Level.SEVERE, "Error parsing recipes, you may not be able to craft any items from this mod!", e) | 
|  | } | 
|  | list.clear() | 
|  | } | 
|  |  | 
|  | private def addRecipe(output: ItemStack, list: Config, name: String) = try { | 
|  | if (list.hasPath(name)) { | 
|  | val recipe = list.getConfig(name) | 
|  | val recipeType = tryGetType(recipe) | 
|  | try { | 
|  | recipeType match { | 
|  | case "shaped" => addShapedRecipe(output, recipe) | 
|  | case "shapeless" => addShapelessRecipe(output, recipe) | 
|  | case "furnace" => addFurnaceRecipe(output, recipe) | 
|  | case "assembly" => addAssemblyRecipe(output, recipe) | 
|  | case other => | 
|  | OpenComputers.log.warning("Failed adding recipe for '" + name + "', you will not be able to craft this item! The error was: Invalid recipe type '" + other + "'.") | 
|  | hide(output) | 
|  | } | 
|  | } | 
|  | catch { | 
|  | case e: RecipeException => | 
|  | OpenComputers.log.warning("Failed adding " + recipeType + " recipe for '" + name + "', you will not be able to craft this item! The error was: " + e.getMessage) | 
|  | hide(output) | 
|  | } | 
|  | } | 
|  | else { | 
|  | OpenComputers.log.info("No recipe for '" + name + "', you will not be able to craft this item.") | 
|  | hide(output) | 
|  | } | 
|  | } | 
|  | catch { | 
|  | case e: Throwable => | 
|  | OpenComputers.log.log(Level.SEVERE, "Failed adding recipe for '" + name + "', you will not be able to craft this item!", e) | 
|  | hide(output) | 
|  | } | 
|  |  | 
|  | private def addShapedRecipe(output: ItemStack, recipe: Config) { | 
|  | val rows = recipe.getList("input").unwrapped().map { | 
|  | case row: java.util.List[AnyRef]@unchecked => row.map(parseIngredient) | 
|  | case other => throw new RecipeException("Invalid row entry for shaped recipe (not a list: " + other + ").") | 
|  | } | 
|  | output.stackSize = tryGetCount(recipe) | 
|  |  | 
|  | var number = -1 | 
|  | var shape = mutable.ArrayBuffer.empty[String] | 
|  | val input = mutable.ArrayBuffer.empty[AnyRef] | 
|  | for (row <- rows) { | 
|  | val (pattern, ingredients) = row.foldLeft((new StringBuilder, Seq.empty[AnyRef]))((acc, ingredient) => { | 
|  | val (pattern, ingredients) = acc | 
|  | ingredient match { | 
|  | case _@(_: ItemStack | _: String) => | 
|  | number += 1 | 
|  | (pattern.append(('a' + number).toChar), ingredients ++ Seq(Char.box(('a' + number).toChar), ingredient)) | 
|  | case _ => (pattern.append(' '), ingredients) | 
|  | } | 
|  | }) | 
|  | shape += pattern.toString | 
|  | input ++= ingredients | 
|  | } | 
|  | if (input.size > 0 && output.stackSize > 0) { | 
|  | GameRegistry.addRecipe(new ExtendedShapedOreRecipe(output, shape ++ input: _*)) | 
|  | } | 
|  | else hide(output) | 
|  | } | 
|  |  | 
|  | private def addShapelessRecipe(output: ItemStack, recipe: Config) { | 
|  | val input = recipe.getValue("input").unwrapped() match { | 
|  | case list: java.util.List[AnyRef]@unchecked => list.map(parseIngredient) | 
|  | case other => Seq(parseIngredient(other)) | 
|  | } | 
|  | output.stackSize = tryGetCount(recipe) | 
|  |  | 
|  | if (input.size > 0 && output.stackSize > 0) { | 
|  | GameRegistry.addRecipe(new ExtendedShapelessOreRecipe(output, input: _*)) | 
|  | } | 
|  | else hide(output) | 
|  | } | 
|  |  | 
|  | private def addAssemblyRecipe(output: ItemStack, recipe: Config) { | 
|  | val input = (recipe.getValue("input").unwrapped() match { | 
|  | case list: java.util.List[AnyRef]@unchecked => list.map(parseIngredient) | 
|  | case other => Seq(parseIngredient(other)) | 
|  | }) map { | 
|  | case stack: ItemStack => stack | 
|  | case null => null | 
|  | case name: String => throw new RecipeException("Invalid ingredient '" + name + "', OreDictionary not supported for assembly recipes.") | 
|  | case other => throw new RecipeException("Invalid ingredient type: " + other + ".") | 
|  | } | 
|  | output.stackSize = tryGetCount(recipe) | 
|  |  | 
|  | if (input.size < 1 || input.size > 2) { | 
|  | throw new RecipeException("Invalid recipe length: " + input.size + ", should be 1 or 2.") | 
|  | } | 
|  |  | 
|  | val inputCount = recipe.getIntList("count") | 
|  | if (inputCount.size() != input.size) { | 
|  | throw new RecipeException("Ingredient and input count mismatch: " + input.size + " != " + inputCount.size + ".") | 
|  | } | 
|  |  | 
|  | val eu = recipe.getInt("eu") | 
|  | val duration = recipe.getInt("time") | 
|  |  | 
|  | (input, inputCount).zipped.foreach((stack, count) => if (stack != null && count > 0) stack.stackSize = stack.getMaxStackSize min count) | 
|  | input.padTo(2, null) | 
|  |  | 
|  | if (input(0) != null) { | 
|  | GregTech.addAssemblerRecipe(input(0), input(1), output, duration, eu) | 
|  | } | 
|  | } | 
|  |  | 
|  | private def addFurnaceRecipe(output: ItemStack, recipe: Config) { | 
|  | val input = parseIngredient(recipe.getValue("input").unwrapped()) | 
|  | output.stackSize = tryGetCount(recipe) | 
|  |  | 
|  | input match { | 
|  | case stack: ItemStack => | 
|  | FurnaceRecipes.smelting().addSmelting(stack.itemID, stack.getItemDamage, output, 0) | 
|  | case name: String => | 
|  | for (stack <- OreDictionary.getOres(name)) { | 
|  | FurnaceRecipes.smelting().addSmelting(stack.itemID, stack.getItemDamage, output, 0) | 
|  | } | 
|  | case _ => | 
|  | } | 
|  | } | 
|  |  | 
|  | private def parseIngredient(entry: AnyRef) = entry match { | 
|  | case map: java.util.Map[AnyRef, AnyRef]@unchecked => | 
|  | if (map.contains("oreDict")) { | 
|  | map.get("oreDict") match { | 
|  | case value: String => value | 
|  | case other => throw new RecipeException("Invalid name in recipe (not a string: " + other + ").") | 
|  | } | 
|  | } | 
|  | else if (map.contains("item")) { | 
|  | map.get("item") match { | 
|  | case name: String => | 
|  | Item.itemsList.find(itemNameEquals(_, name)) match { | 
|  | case Some(item) => new ItemStack(item, 1, tryGetId(map)) | 
|  | case _ => throw new RecipeException("No item found with name '" + name + "'.") | 
|  | } | 
|  | case id: Number => new ItemStack(validateItemId(id), 1, tryGetId(map)) | 
|  | case other => throw new RecipeException("Invalid item name in recipe (not a string: " + other + ").") | 
|  | } | 
|  | } | 
|  | else if (map.contains("block")) { | 
|  | map.get("block") match { | 
|  | case name: String => | 
|  | Block.blocksList.find(blockNameEquals(_, name)) match { | 
|  | case Some(block) => new ItemStack(block, 1, tryGetId(map)) | 
|  | case _ => throw new RecipeException("No block found with name '" + name + "'.") | 
|  | } | 
|  | case id: Number => new ItemStack(validateBlockId(id), 1, tryGetId(map)) | 
|  | case other => throw new RecipeException("Invalid block name (not a string: " + other + ").") | 
|  | } | 
|  | } | 
|  | else throw new RecipeException("Invalid ingredient type (no oreDict, item or block entry).") | 
|  | case name: String => | 
|  | if (name == null || name.trim.isEmpty) null | 
|  | else if (OreDictionary.getOres(name) != null && !OreDictionary.getOres(name).isEmpty) name | 
|  | else { | 
|  | Item.itemsList.find(itemNameEquals(_, name)) match { | 
|  | case Some(item) => new ItemStack(item, 1, 0) | 
|  | case _ => Block.blocksList.find(blockNameEquals(_, name)) match { | 
|  | case Some(block) => new ItemStack(block, 1, 0) | 
|  | case _ => throw new RecipeException("No ore dictionary entry, item or block found for ingredient with name '" + name + "'.") | 
|  | } | 
|  | } | 
|  | } | 
|  | case other => throw new RecipeException("Invalid ingredient type (not a map or string): " + other) | 
|  | } | 
|  |  | 
|  | private def itemNameEquals(item: Item, name: String) = | 
|  | item != null && (item.getUnlocalizedName == name || item.getUnlocalizedName == "item." + name) | 
|  |  | 
|  | private def blockNameEquals(block: Block, name: String) = | 
|  | block != null && (block.getUnlocalizedName == name || block.getUnlocalizedName == "tile." + name) | 
|  |  | 
|  | private def tryGetType(recipe: Config) = if (recipe.hasPath("type")) recipe.getString("type") else "shaped" | 
|  |  | 
|  | private def tryGetCount(recipe: Config) = if (recipe.hasPath("output")) recipe.getInt("output") else 1 | 
|  |  | 
|  | private def tryGetId(ingredient: java.util.Map[AnyRef, AnyRef]): Int = | 
|  | if (ingredient.contains("subID")) ingredient.get("subID") match { | 
|  | case id: Number => id.intValue | 
|  | case "any" => OreDictionary.WILDCARD_VALUE | 
|  | case id: String => Integer.valueOf(id) | 
|  | case _ => 0 | 
|  | } else 0 | 
|  |  | 
|  | private def validateBlockId(id: Number) = { | 
|  | val index = id.intValue | 
|  | if (index < 1 || index >= Block.blocksList.length || Block.blocksList(index) == null) throw new RecipeException("Invalid block ID: " + index) | 
|  | Block.blocksList(index) | 
|  | } | 
|  |  | 
|  | private def validateItemId(id: Number) = { | 
|  | val index = id.intValue | 
|  | if (index < 0 || index >= Item.itemsList.length || Item.itemsList(index) == null) throw new RecipeException("Invalid item ID: " + index) | 
|  | Item.itemsList(index) | 
|  | } | 
|  |  | 
|  | private def hide(value: ItemStack) { | 
|  | Items.multi.subItem(value) match { | 
|  | case Some(stack) => stack.showInItemList = false | 
|  | case _ => common.block.Delegator.subBlock(value) match { | 
|  | case Some(block) => block.showInItemList = false | 
|  | case _ => | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | private class RecipeException(message: String) extends RuntimeException(message) | 
|  |  | 
|  | } |