blob: 27a1901dae1da5b942c465cc4608f5d39547e199 [file] [log] [blame] [raw]
package li.cil.oc
import com.typesafe.config.{Config, ConfigFactory}
import cpw.mods.fml.common.Loader
import cpw.mods.fml.common.registry.GameRegistry
import java.io.File
import java.net.URL
import java.util.logging.Level
import net.minecraft.block.Block
import net.minecraft.item.crafting.FurnaceRecipes
import net.minecraft.item.{ItemStack, Item}
import net.minecraftforge.oredict.{OreDictionary, ShapelessOreRecipe, ShapedOreRecipe}
import org.apache.commons.io.FileUtils
import scala.collection.convert.wrapAsScala._
import scala.collection.mutable.ArrayBuffer
object Recipes {
def init() {
val custom = ConfigFactory.parseResources("/assets/opencomputers/recipes/custom.conf")
try {
val defaultFile = new File(Loader.instance.getConfigDir + File.separator + "opencomputers" + File.separator + "default.conf")
val customFile = new File(Loader.instance.getConfigDir + File.separator + "opencomputers" + File.separator + "custom.conf")
defaultFile.getParentFile.mkdirs()
var in = Recipes.getClass.getResource("/assets/opencomputers/recipes/default.conf")
copyToFile(in, defaultFile)
if (!customFile.exists()) {
//debug
customFile.getParentFile.mkdirs()
in = Recipes.getClass.getResource("/assets/opencomputers/recipes/custom.conf")
copyToFile(in, customFile)
}
val recipes = ConfigFactory.parseFile(customFile).withFallback(custom)
// Try to keep this in the same order as the fields in the Items class
// to make it easier to match them and check if anything is missing.
addRecipe(Items.analyzer.createItemStack(), recipes, "analyzer")
addRecipe(Items.ram1.createItemStack(), recipes, "ram1")
addRecipe(Items.ram2.createItemStack(), recipes, "ram2")
addRecipe(Items.ram3.createItemStack(), recipes, "ram3")
addRecipe(Items.floppyDisk.createItemStack(), recipes, "floppy")
addRecipe(Items.hdd1.createItemStack(), recipes, "hdd1")
addRecipe(Items.hdd2.createItemStack(), recipes, "hdd2")
addRecipe(Items.hdd3.createItemStack(), recipes, "hdd3")
addRecipe(Items.gpu1.createItemStack(), recipes, "graphicsCard1")
addRecipe(Items.gpu2.createItemStack(), recipes, "graphicsCard2")
addRecipe(Items.gpu3.createItemStack(), recipes, "graphicsCard3")
addRecipe(Items.lan.createItemStack(), recipes, "lanCard")
addRecipe(Items.rs.createItemStack(), recipes, "redstoneCard")
addRecipe(Items.wlan.createItemStack(), recipes, "wlanCard")
addRecipe(Items.upgradeCrafting.createItemStack(), recipes, "craftingUpgrade")
addRecipe(Items.upgradeGenerator.createItemStack(), recipes, "generatorUpgrade")
addRecipe(Items.upgradeNavigation.createItemStack(), recipes, "navigationUpgrade")
addRecipe(Items.upgradeSign.createItemStack(), recipes, "signUpgrade")
addRecipe(Items.upgradeSolarGenerator.createItemStack(), recipes, "solarGeneratorUpgrade")
if (OreDictionary.getOres("nuggetIron").contains(Items.ironNugget.createItemStack())) {
addRecipe(Items.ironNugget.createItemStack(), recipes, "nuggetIron")
}
addRecipe(Items.cuttingWire.createItemStack(), recipes, "cuttingWire")
addRecipe(Items.acid.createItemStack(), recipes, "acid")
addRecipe(Items.disk.createItemStack(), recipes, "disk")
addRecipe(Items.buttonGroup.createItemStack(), recipes, "buttonGroup")
addRecipe(Items.arrowKeys.createItemStack(), recipes, "arrowKeys")
addRecipe(Items.numPad.createItemStack(), recipes, "numPad")
addRecipe(Items.transistor.createItemStack(), recipes, "transistor")
addRecipe(Items.chip1.createItemStack(), recipes, "chip1")
addRecipe(Items.chip2.createItemStack(), recipes, "chip2")
addRecipe(Items.chip3.createItemStack(), recipes, "chip3")
addRecipe(Items.alu.createItemStack(), recipes, "alu")
addRecipe(Items.cpu.createItemStack(), recipes, "cpu")
addRecipe(Items.cu.createItemStack(), recipes, "cu")
addRecipe(Items.rawCircuitBoard.createItemStack(), recipes, "rawCircuitBoard")
addRecipe(Items.circuitBoard.createItemStack(), recipes, "circuitBoard")
addRecipe(Items.pcb.createItemStack(), recipes, "printedCircuitBoard")
addRecipe(Items.card.createItemStack(), recipes, "card")
// Try to keep this in the same order as the fields in the Blocks class
// to make it easier to match them and check if anything is missing.
addRecipe(Blocks.adapter.createItemStack(), recipes, "adapter")
addRecipe(Blocks.cable.createItemStack(), recipes, "cable")
addRecipe(Blocks.capacitor.createItemStack(), recipes, "capacitor")
addRecipe(Blocks.charger.createItemStack(), recipes, "charger")
addRecipe(Blocks.case1.createItemStack(), recipes, "case1")
addRecipe(Blocks.case2.createItemStack(), recipes, "case2")
addRecipe(Blocks.case3.createItemStack(), recipes, "case3")
addRecipe(Blocks.diskDrive.createItemStack(), recipes, "diskDrive")
addRecipe(Blocks.keyboard.createItemStack(), recipes, "keyboard")
addRecipe(Blocks.powerConverter.createItemStack(), recipes, "powerConverter")
addRecipe(Blocks.powerDistributor.createItemStack(), recipes, "powerDistributor")
addRecipe(Blocks.redstone.createItemStack(), recipes, "redstone")
addRecipe(Blocks.robotProxy.createItemStack(), recipes, "robot")
addRecipe(Blocks.router.createItemStack(), recipes, "router")
addRecipe(Blocks.screen1.createItemStack(), recipes, "screen1")
addRecipe(Blocks.screen2.createItemStack(), recipes, "screen2")
addRecipe(Blocks.screen3.createItemStack(), recipes, "screen3")
// Navigation upgrade recrafting.
GameRegistry.addRecipe(new ShapelessOreRecipe(Items.upgradeNavigation.createItemStack(), Items.upgradeNavigation.createItemStack(), 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)
}
}
def addRecipe(output: ItemStack, list: Config, name: String) = try {
if (list.hasPath(name)) {
val recipe = list.getConfig(name)
tryGetType(recipe) match {
case "shaped" => addShapedRecipe(output, recipe)
case "shapeless" => addShapelessRecipe(output, recipe)
case "furnace" => addFurnaceRecipe(output, recipe)
case other => throw new RecipeException("Invalid recipe type '" + other + "'.")
}
}
}
catch {
case e: RecipeException => OpenComputers.log.warning("Failed adding recipe for '" + name + "', you will not be able to craft this item! The error was: " + e.getMessage)
case e: Throwable => OpenComputers.log.log(Level.SEVERE, "Failed adding recipe for '" + name + "', you will not be able to craft this item!", e)
}
private def addShapedRecipe(stack: ItemStack, recipe: Config) {
val rows = recipe.getList("input").unwrapped().map {
case row: java.util.List[Object] => row.map(parseIngredient).padTo(3, null)
case other => throw new RecipeException("Invalid row entry for shaped recipe (not a list: " + other + ").")
}
var number = -1
var shape = ArrayBuffer.empty[String]
val input = 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
}
val output = stack.copy()
output.stackSize = tryGetCount(recipe)
GameRegistry.addRecipe(new ShapedOreRecipe(output, shape ++ input: _*))
}
private def addShapelessRecipe(stack: ItemStack, recipe: Config) {
val input = recipe.getValue("input").unwrapped() match {
case list: java.util.List[Object] => list.map(parseIngredient)
case other => Seq(parseIngredient(other))
}
val output = stack.copy()
output.stackSize = tryGetCount(recipe)
if (input.size > 0 && output.stackSize > 0) {
GameRegistry.addRecipe(new ShapelessOreRecipe(output, input: _*))
}
}
private def addFurnaceRecipe(stack: ItemStack, recipe: Config) {
val input = parseIngredient(recipe.getValue("input").unwrapped())
val output = stack.copy()
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.HashMap[String, _] =>
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 => getOreNameOrItemStackFromName(name)
case _ => throw new RecipeException("Invalid ingredient type (not a map or string).")
}
private def getOreNameOrItemStackFromName(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 + "'.")
}
}
}
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 copyToFile(source: URL, target: java.io.File) {
FileUtils.copyURLToFile(source, target)
}
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.HashMap[String, _]): Int =
if (ingredient.contains("subID")) ingredient.get("subID") match {
case id: Number => id.intValue
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 cartesianProduct[T](xss: List[List[T]]): List[List[T]] = xss match {
case Nil => List(Nil)
case h :: t => for (xh <- h; xt <- cartesianProduct(t)) yield xh :: xt
}
private class RecipeException(message: String) extends RuntimeException(message)
}