blob: a7d8e6884ed477dbe09eef506c754de9f0743f10 [file] [log] [blame] [raw]
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.Color
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)
}
lazy val config: ConfigParseOptions = 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, config)
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)))
// Floppy disk coloring.
val floppy = api.Items.get("floppy").createItemStack(1)
for (dye <- Color.dyes) {
GameRegistry.addRecipe(new ExtendedShapelessOreRecipe(floppy, floppy, dye))
}
}
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)
}