Florian Nücke | 9cd5518 | 2014-05-18 19:53:34 +0200 | [diff] [blame] | 1 | package li.cil.oc.common.tileentity |
| 2 | |
| 3 | import cpw.mods.fml.relauncher.{SideOnly, Side} |
Florian Nücke | adb58e0 | 2014-06-10 00:02:53 +0200 | [diff] [blame] | 4 | import org.apache.logging.log4j.Level |
Florian Nücke | 02bdcce | 2014-05-19 11:25:25 +0200 | [diff] [blame] | 5 | import li.cil.oc.{OpenComputers, api, Settings} |
Florian Nücke | 9cd5518 | 2014-05-18 19:53:34 +0200 | [diff] [blame] | 6 | import li.cil.oc.api.network.Visibility |
Florian Nücke | 177edc9 | 2014-05-29 13:26:15 +0200 | [diff] [blame] | 7 | import li.cil.oc.common.inventory.ServerInventory |
Florian Nücke | 5f5a553 | 2014-05-19 10:10:15 +0200 | [diff] [blame] | 8 | import li.cil.oc.server.{PacketSender => ServerPacketSender} |
Florian Nücke | 9cd5518 | 2014-05-18 19:53:34 +0200 | [diff] [blame] | 9 | import li.cil.oc.util.ExtendedNBT._ |
| 10 | import li.cil.oc.util.{ItemUtils, InventoryUtils} |
Florian Nücke | 177edc9 | 2014-05-29 13:26:15 +0200 | [diff] [blame] | 11 | import net.minecraft.item.{ItemBucket, ItemStack} |
Florian Nücke | 9cd5518 | 2014-05-18 19:53:34 +0200 | [diff] [blame] | 12 | import net.minecraft.item.crafting.{ShapelessRecipes, ShapedRecipes, IRecipe, CraftingManager} |
| 13 | import net.minecraft.nbt.NBTTagCompound |
Florian Nücke | 177edc9 | 2014-05-29 13:26:15 +0200 | [diff] [blame] | 14 | import net.minecraftforge.common.util.ForgeDirection |
Florian Nücke | 9cd5518 | 2014-05-18 19:53:34 +0200 | [diff] [blame] | 15 | import net.minecraftforge.oredict.{ShapelessOreRecipe, ShapedOreRecipe} |
| 16 | import scala.collection.convert.WrapAsScala._ |
| 17 | import scala.collection.mutable |
Florian Nücke | 177edc9 | 2014-05-29 13:26:15 +0200 | [diff] [blame] | 18 | import net.minecraftforge.common.util.Constants.NBT |
Florian Nücke | 9cd5518 | 2014-05-18 19:53:34 +0200 | [diff] [blame] | 19 | |
Florian Nücke | 3459ff1 | 2014-05-29 16:16:04 +0200 | [diff] [blame] | 20 | class Disassembler extends traits.Environment with traits.PowerAcceptor with traits.Inventory { |
Florian Nücke | 9cd5518 | 2014-05-18 19:53:34 +0200 | [diff] [blame] | 21 | val node = api.Network.newNode(this, Visibility.None). |
Florian Nücke | 3459ff1 | 2014-05-29 16:16:04 +0200 | [diff] [blame] | 22 | withConnector(Settings.get.bufferConverter). |
Florian Nücke | 9cd5518 | 2014-05-18 19:53:34 +0200 | [diff] [blame] | 23 | create() |
| 24 | |
Florian Nücke | 5f5a553 | 2014-05-19 10:10:15 +0200 | [diff] [blame] | 25 | var isActive = false |
Florian Nücke | 9cd5518 | 2014-05-18 19:53:34 +0200 | [diff] [blame] | 26 | |
| 27 | val queue = mutable.ArrayBuffer.empty[ItemStack] |
| 28 | |
Florian Nücke | 3459ff1 | 2014-05-29 16:16:04 +0200 | [diff] [blame] | 29 | var totalRequiredEnergy = 0.0 |
| 30 | |
Florian Nücke | 9cd5518 | 2014-05-18 19:53:34 +0200 | [diff] [blame] | 31 | var buffer = 0.0 |
Florian Nücke | 3459ff1 | 2014-05-29 16:16:04 +0200 | [diff] [blame] | 32 | |
| 33 | def progress = if (queue.isEmpty) 0 else (1 - (queue.size * Settings.get.disassemblerItemCost - buffer) / totalRequiredEnergy) * 100 |
| 34 | |
| 35 | private def setActive(value: Boolean) = if (value != isActive) { |
| 36 | isActive = value |
| 37 | ServerPacketSender.sendDisassemblerActive(this, isActive) |
| 38 | } |
| 39 | |
| 40 | // ----------------------------------------------------------------------- // |
| 41 | |
| 42 | @SideOnly(Side.CLIENT) |
| 43 | override protected def hasConnector(side: ForgeDirection) = side != ForgeDirection.UP |
| 44 | |
| 45 | override protected def connector(side: ForgeDirection) = Option(if (side != ForgeDirection.UP) node else null) |
Florian Nücke | 9cd5518 | 2014-05-18 19:53:34 +0200 | [diff] [blame] | 46 | |
| 47 | // ----------------------------------------------------------------------- // |
| 48 | |
| 49 | override def canUpdate = isServer |
| 50 | |
| 51 | override def updateEntity() { |
| 52 | super.updateEntity() |
| 53 | if (world.getWorldTime % Settings.get.tickFrequency == 0) { |
| 54 | if (queue.isEmpty) { |
Florian Nücke | 3459ff1 | 2014-05-29 16:16:04 +0200 | [diff] [blame] | 55 | disassemble(decrStackSize(0, 1)) |
| 56 | setActive(!queue.isEmpty) |
Florian Nücke | 9cd5518 | 2014-05-18 19:53:34 +0200 | [diff] [blame] | 57 | } |
| 58 | else { |
| 59 | if (buffer < Settings.get.disassemblerItemCost) { |
| 60 | val want = Settings.get.disassemblerTickAmount |
Florian Nücke | 3459ff1 | 2014-05-29 16:16:04 +0200 | [diff] [blame] | 61 | val success = node.tryChangeBuffer(-want) |
| 62 | setActive(success) // If energy is insufficient indicate it visually. |
| 63 | if (success) { |
| 64 | buffer += want |
| 65 | } |
Florian Nücke | 9cd5518 | 2014-05-18 19:53:34 +0200 | [diff] [blame] | 66 | } |
| 67 | if (buffer >= Settings.get.disassemblerItemCost) { |
| 68 | buffer -= Settings.get.disassemblerItemCost |
| 69 | val stack = queue.remove(0) |
Florian Nücke | 5f5a553 | 2014-05-19 10:10:15 +0200 | [diff] [blame] | 70 | if (world.rand.nextDouble > Settings.get.disassemblerBreakChance) { |
| 71 | drop(stack) |
Florian Nücke | 9cd5518 | 2014-05-18 19:53:34 +0200 | [diff] [blame] | 72 | } |
| 73 | } |
| 74 | } |
| 75 | } |
| 76 | } |
| 77 | |
| 78 | def disassemble(stack: ItemStack) { |
| 79 | // Validate the item, never trust Minecraft / other Mods on anything! |
Florian Nücke | 3459ff1 | 2014-05-29 16:16:04 +0200 | [diff] [blame] | 80 | if (stack != null && isItemValidForSlot(0, stack)) { |
Florian Nücke | 5f5a553 | 2014-05-19 10:10:15 +0200 | [diff] [blame] | 81 | if (api.Items.get(stack) == api.Items.get("robot")) enqueueRobot(stack) |
| 82 | else if (api.Items.get(stack) == api.Items.get("server1")) enqueueServer(stack, 0) |
| 83 | else if (api.Items.get(stack) == api.Items.get("server2")) enqueueServer(stack, 1) |
| 84 | else if (api.Items.get(stack) == api.Items.get("server3")) enqueueServer(stack, 2) |
Florian Nücke | 9cd5518 | 2014-05-18 19:53:34 +0200 | [diff] [blame] | 85 | else if (api.Items.get(stack) == api.Items.get("navigationUpgrade")) { |
Florian Nücke | 5f5a553 | 2014-05-19 10:10:15 +0200 | [diff] [blame] | 86 | enqueueNavigationUpgrade(stack) |
Florian Nücke | 9cd5518 | 2014-05-18 19:53:34 +0200 | [diff] [blame] | 87 | } |
Florian Nücke | 5f5a553 | 2014-05-19 10:10:15 +0200 | [diff] [blame] | 88 | else queue ++= getIngredients(stack) |
Florian Nücke | 3459ff1 | 2014-05-29 16:16:04 +0200 | [diff] [blame] | 89 | totalRequiredEnergy = queue.size * Settings.get.disassemblerItemCost |
Florian Nücke | 9cd5518 | 2014-05-18 19:53:34 +0200 | [diff] [blame] | 90 | } |
| 91 | } |
| 92 | |
Florian Nücke | 5f5a553 | 2014-05-19 10:10:15 +0200 | [diff] [blame] | 93 | private def enqueueRobot(robot: ItemStack) { |
| 94 | val info = new ItemUtils.RobotData(robot) |
| 95 | queue += api.Items.get("case" + (info.tier + 1)).createItemStack(1) |
| 96 | queue ++= info.containers |
| 97 | queue ++= info.components |
Florian Nücke | ddd8e18 | 2014-05-23 18:55:19 +0200 | [diff] [blame] | 98 | node.changeBuffer(info.robotEnergy) |
Florian Nücke | 5f5a553 | 2014-05-19 10:10:15 +0200 | [diff] [blame] | 99 | } |
| 100 | |
| 101 | private def enqueueServer(server: ItemStack, serverTier: Int) { |
| 102 | val info = new ServerInventory { |
| 103 | override def tier = serverTier |
| 104 | |
| 105 | override def container = server |
| 106 | } |
| 107 | for (slot <- 0 until info.getSizeInventory) { |
| 108 | val stack = info.getStackInSlot(slot) |
| 109 | drop(stack) |
| 110 | } |
| 111 | queue ++= getIngredients(server) |
| 112 | } |
| 113 | |
| 114 | private def enqueueNavigationUpgrade(stack: ItemStack) { |
Florian Nücke | 3c355c2 | 2014-05-19 10:54:31 +0200 | [diff] [blame] | 115 | val info = new ItemUtils.NavigationUpgradeData(stack) |
Florian Nücke | 5f5a553 | 2014-05-19 10:10:15 +0200 | [diff] [blame] | 116 | val parts = getIngredients(stack) |
Florian Nücke | 3c355c2 | 2014-05-19 10:54:31 +0200 | [diff] [blame] | 117 | queue ++= parts.map { |
Florian Nücke | 177edc9 | 2014-05-29 13:26:15 +0200 | [diff] [blame] | 118 | case part if part.getItem == net.minecraft.init.Items.filled_map => info.map |
Florian Nücke | 3c355c2 | 2014-05-19 10:54:31 +0200 | [diff] [blame] | 119 | case part => part |
| 120 | } |
Florian Nücke | 5f5a553 | 2014-05-19 10:10:15 +0200 | [diff] [blame] | 121 | } |
| 122 | |
Florian Nücke | 02bdcce | 2014-05-19 11:25:25 +0200 | [diff] [blame] | 123 | private def getIngredients(stack: ItemStack): Iterable[ItemStack] = try { |
Florian Nücke | 5f5a553 | 2014-05-19 10:10:15 +0200 | [diff] [blame] | 124 | val recipes = CraftingManager.getInstance.getRecipeList.map(_.asInstanceOf[IRecipe]) |
| 125 | val recipe = recipes.find(recipe => recipe.getRecipeOutput != null && recipe.getRecipeOutput.isItemEqual(stack)) |
| 126 | val count = recipe.fold(0)(_.getRecipeOutput.stackSize) |
Florian Nücke | 02bdcce | 2014-05-19 11:25:25 +0200 | [diff] [blame] | 127 | val ingredients = (recipe match { |
Florian Nücke | 5f5a553 | 2014-05-19 10:10:15 +0200 | [diff] [blame] | 128 | case Some(recipe: ShapedRecipes) => recipe.recipeItems.toIterable |
| 129 | case Some(recipe: ShapelessRecipes) => recipe.recipeItems.map(_.asInstanceOf[ItemStack]) |
Florian Nücke | 9cd5518 | 2014-05-18 19:53:34 +0200 | [diff] [blame] | 130 | case Some(recipe: ShapedOreRecipe) => resolveOreDictEntries(recipe.getInput) |
| 131 | case Some(recipe: ShapelessOreRecipe) => resolveOreDictEntries(recipe.getInput) |
| 132 | case _ => Iterable.empty |
Florian Nücke | c28cfa6 | 2014-05-20 00:46:14 +0200 | [diff] [blame] | 133 | }).filter(ingredient => ingredient != null && |
| 134 | // Strip out buckets, because those are returned when crafting, and |
| 135 | // we have no way of returning the fluid only (and I can't be arsed |
| 136 | // to make it output fluids into fluiducts or such, sorry). |
| 137 | !ingredient.getItem.isInstanceOf[ItemBucket]).toArray |
Florian Nücke | 5f5a553 | 2014-05-19 10:10:15 +0200 | [diff] [blame] | 138 | // Avoid positive feedback loops. |
Florian Nücke | c28cfa6 | 2014-05-20 00:46:14 +0200 | [diff] [blame] | 139 | if (ingredients.exists(ingredient => ingredient.isItemEqual(stack))) { |
Florian Nücke | 02bdcce | 2014-05-19 11:25:25 +0200 | [diff] [blame] | 140 | return Iterable.empty |
| 141 | } |
Florian Nücke | 5f5a553 | 2014-05-19 10:10:15 +0200 | [diff] [blame] | 142 | // Merge equal items for size division by output size. |
| 143 | val merged = mutable.ArrayBuffer.empty[ItemStack] |
Florian Nücke | c28cfa6 | 2014-05-20 00:46:14 +0200 | [diff] [blame] | 144 | for (ingredient <- ingredients) { |
Florian Nücke | 5f5a553 | 2014-05-19 10:10:15 +0200 | [diff] [blame] | 145 | merged.find(_.isItemEqual(ingredient)) match { |
| 146 | case Some(entry) => entry.stackSize += ingredient.stackSize |
| 147 | case _ => merged += ingredient.copy() |
| 148 | } |
| 149 | } |
| 150 | merged.foreach(_.stackSize /= count) |
| 151 | // Split items up again to 'disassemble them individually'. |
| 152 | val distinct = mutable.ArrayBuffer.empty[ItemStack] |
| 153 | for (ingredient <- merged) { |
| 154 | val size = ingredient.stackSize |
| 155 | ingredient.stackSize = 1 |
| 156 | for (i <- 0 until size) { |
| 157 | distinct += ingredient.copy() |
| 158 | } |
| 159 | } |
| 160 | distinct |
Florian Nücke | 9cd5518 | 2014-05-18 19:53:34 +0200 | [diff] [blame] | 161 | } |
Florian Nücke | 02bdcce | 2014-05-19 11:25:25 +0200 | [diff] [blame] | 162 | catch { |
| 163 | case t: Throwable => |
Florian Nücke | adb58e0 | 2014-06-10 00:02:53 +0200 | [diff] [blame] | 164 | OpenComputers.log.log(Level.WARN, "Whoops, something went wrong when trying to figure out an item's parts.", t) |
Florian Nücke | 02bdcce | 2014-05-19 11:25:25 +0200 | [diff] [blame] | 165 | Iterable.empty |
| 166 | } |
Florian Nücke | 9cd5518 | 2014-05-18 19:53:34 +0200 | [diff] [blame] | 167 | |
| 168 | private def resolveOreDictEntries[T](entries: Iterable[T]) = entries.collect { |
| 169 | case stack: ItemStack => stack |
| 170 | case list: java.util.ArrayList[ItemStack]@unchecked if !list.isEmpty => list.get(world.rand.nextInt(list.size)) |
Florian Nücke | 5f5a553 | 2014-05-19 10:10:15 +0200 | [diff] [blame] | 171 | } |
Florian Nücke | 3c355c2 | 2014-05-19 10:54:31 +0200 | [diff] [blame] | 172 | |
Florian Nücke | 5f5a553 | 2014-05-19 10:10:15 +0200 | [diff] [blame] | 173 | private def drop(stack: ItemStack) { |
| 174 | if (stack != null) { |
| 175 | for (side <- ForgeDirection.VALID_DIRECTIONS if stack.stackSize > 0) { |
Florian Nücke | d7143f5 | 2014-05-20 16:00:04 +0200 | [diff] [blame] | 176 | InventoryUtils.insertIntoInventoryAt(stack, world, x + side.offsetX, y + side.offsetY, z + side.offsetZ, side.getOpposite) |
Florian Nücke | 5f5a553 | 2014-05-19 10:10:15 +0200 | [diff] [blame] | 177 | } |
| 178 | if (stack.stackSize > 0) { |
| 179 | spawnStackInWorld(stack, ForgeDirection.UP) |
| 180 | } |
| 181 | } |
| 182 | } |
Florian Nücke | 9cd5518 | 2014-05-18 19:53:34 +0200 | [diff] [blame] | 183 | |
| 184 | // ----------------------------------------------------------------------- // |
| 185 | |
| 186 | override def readFromNBT(nbt: NBTTagCompound) { |
| 187 | super.readFromNBT(nbt) |
| 188 | queue.clear() |
Florian Nücke | 177edc9 | 2014-05-29 13:26:15 +0200 | [diff] [blame] | 189 | queue ++= nbt.getTagList(Settings.namespace + "queue", NBT.TAG_COMPOUND).map((list, index) => { |
| 190 | ItemStack.loadItemStackFromNBT(list.getCompoundTagAt(index)) |
| 191 | }) |
Florian Nücke | 9cd5518 | 2014-05-18 19:53:34 +0200 | [diff] [blame] | 192 | buffer = nbt.getDouble(Settings.namespace + "buffer") |
Florian Nücke | 3459ff1 | 2014-05-29 16:16:04 +0200 | [diff] [blame] | 193 | totalRequiredEnergy = nbt.getDouble(Settings.namespace + "total") |
Florian Nücke | 5f5a553 | 2014-05-19 10:10:15 +0200 | [diff] [blame] | 194 | isActive = !queue.isEmpty |
Florian Nücke | 9cd5518 | 2014-05-18 19:53:34 +0200 | [diff] [blame] | 195 | } |
| 196 | |
| 197 | override def writeToNBT(nbt: NBTTagCompound) { |
| 198 | super.writeToNBT(nbt) |
| 199 | nbt.setNewTagList(Settings.namespace + "queue", queue) |
| 200 | nbt.setDouble(Settings.namespace + "buffer", buffer) |
Florian Nücke | 3459ff1 | 2014-05-29 16:16:04 +0200 | [diff] [blame] | 201 | nbt.setDouble(Settings.namespace + "total", totalRequiredEnergy) |
Florian Nücke | 9cd5518 | 2014-05-18 19:53:34 +0200 | [diff] [blame] | 202 | } |
| 203 | |
| 204 | @SideOnly(Side.CLIENT) |
| 205 | override def readFromNBTForClient(nbt: NBTTagCompound) { |
| 206 | super.readFromNBTForClient(nbt) |
| 207 | isActive = nbt.getBoolean("isActive") |
| 208 | } |
| 209 | |
| 210 | override def writeToNBTForClient(nbt: NBTTagCompound) { |
| 211 | super.writeToNBTForClient(nbt) |
Florian Nücke | 5f5a553 | 2014-05-19 10:10:15 +0200 | [diff] [blame] | 212 | nbt.setBoolean("isActive", isActive) |
Florian Nücke | 9cd5518 | 2014-05-18 19:53:34 +0200 | [diff] [blame] | 213 | } |
| 214 | |
| 215 | // ----------------------------------------------------------------------- // |
| 216 | |
| 217 | override def getSizeInventory = 1 |
| 218 | |
Florian Nücke | 5f5a553 | 2014-05-19 10:10:15 +0200 | [diff] [blame] | 219 | override def getInventoryStackLimit = 64 |
Florian Nücke | 9cd5518 | 2014-05-18 19:53:34 +0200 | [diff] [blame] | 220 | |
Florian Nücke | 177edc9 | 2014-05-29 13:26:15 +0200 | [diff] [blame] | 221 | override def getInventoryName = Settings.namespace + "container.Disassembler" |
Florian Nücke | 9cd5518 | 2014-05-18 19:53:34 +0200 | [diff] [blame] | 222 | |
| 223 | override def isItemValidForSlot(i: Int, stack: ItemStack) = |
| 224 | api.Items.get(stack) == api.Items.get("robot") || |
| 225 | ((Settings.get.disassembleAllTheThings || api.Items.get(stack) != null) && !getIngredients(stack).isEmpty) |
| 226 | } |