From 3d5029caa022a4e61fbf0c535511e8c06fe28b0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20N=C3=BCcke?= Date: Fri, 29 Aug 2014 18:22:31 +0200 Subject: [PATCH 1/2] Implemented template system for assembler. --- .../assets/opencomputers/lang/de_DE.lang | 8 +- .../assets/opencomputers/lang/en_US.lang | 8 +- src/main/scala/li/cil/oc/Localization.scala | 22 +- src/main/scala/li/cil/oc/OpenComputers.scala | 16 +- .../li/cil/oc/client/gui/RobotAssembler.scala | 106 ++----- .../li/cil/oc/common/InventorySlots.scala | 102 ------- src/main/scala/li/cil/oc/common/Proxy.scala | 4 + .../container/DynamicComponentSlot.scala | 10 +- .../li/cil/oc/common/container/Player.scala | 10 +- .../oc/common/container/RobotAssembler.scala | 33 ++- .../common/template/AssemblerTemplates.scala | 93 ++++++ .../oc/common/template/RobotTemplate.scala | 275 ++++++++++++++++++ .../oc/common/tileentity/RobotAssembler.scala | 117 ++++---- .../scala/li/cil/oc/util/ExtendedNBT.scala | 18 ++ 14 files changed, 532 insertions(+), 290 deletions(-) create mode 100644 src/main/scala/li/cil/oc/common/template/AssemblerTemplates.scala create mode 100644 src/main/scala/li/cil/oc/common/template/RobotTemplate.scala diff --git a/src/main/resources/assets/opencomputers/lang/de_DE.lang b/src/main/resources/assets/opencomputers/lang/de_DE.lang index d4d6422a6..dba15a271 100644 --- a/src/main/resources/assets/opencomputers/lang/de_DE.lang +++ b/src/main/resources/assets/opencomputers/lang/de_DE.lang @@ -24,7 +24,7 @@ oc:tile.PowerDistributor.name=Stromverteiler oc:tile.Redstone.name=Redstone-I/O oc:tile.Robot.name=Roboter oc:tile.RobotAfterimage.name=Roboter -oc:tile.RobotAssembler.name=Roboter-Werkbank +oc:tile.RobotAssembler.name=Elektronik-Werkbank oc:tile.Screen0.name=Bildschirm (Stufe 1) oc:tile.Screen1.name=Bildschirm (Stufe 2) oc:tile.Screen2.name=Bildschirm (Stufe 3) @@ -132,9 +132,9 @@ oc:gui.Error.OutOfMemory=Nicht genug Arbeitsspeicher. oc:gui.Robot.Power=Energie oc:gui.Robot.TurnOff=Ausschalten oc:gui.Robot.TurnOn=Einschalten -oc:gui.RobotAssembler.CollectRobot=Roboter entnehmen +oc:gui.RobotAssembler.CollectRobot=Ergebnis entnehmen oc:gui.RobotAssembler.Complexity=Komplexität: %s/%s -oc:gui.RobotAssembler.InsertCase=Computergehäuse einlegen +oc:gui.RobotAssembler.InsertCase=Grundbaustein einlegen oc:gui.RobotAssembler.InsertCPU=CPU benötigt oc:gui.RobotAssembler.InsertRAM=RAM benötigt oc:gui.RobotAssembler.Progress=Fortschritt: %s%% (%s) @@ -232,7 +232,7 @@ oc:tooltip.Robot=Im Gegensatz zu normalen Computern können sich Roboter in der # The underscore makes sure this isn't hidden with the rest of the tooltip. oc:tooltip.Robot_Level=§fStufe§7: §a%s§7. oc:tooltip.Robot_StoredEnergy=§fGespeicherte Energie§7: §a%s§7. -oc:tooltip.RobotAssembler=Erlaubt die Fertigung von Robotern aus diversen anderen Computerteilen. +oc:tooltip.RobotAssembler=Erlaubt die Fertigung von Robotern und weiteren Geräten aus diversen anderen Computerteilen. oc:tooltip.Screen=Zeigt Text an, gesteuert von Grafikkarten in Computern.[nl] Höchstauflösung: §f%sx%s§7.[nl] Maximale Farbtiefe: §f%s§7. oc:tooltip.Server=Ein Server kann wie ein gewöhnliches Computergehäuse mit Komponenten verbessert werden. Um den Server zu starten, muss er in einem Servergehäuse installiert werden.[nl] Anzahl unterstützter Fernbedienungen: §f%s§7. oc:tooltip.Server.Components=Installierte Komponenten: diff --git a/src/main/resources/assets/opencomputers/lang/en_US.lang b/src/main/resources/assets/opencomputers/lang/en_US.lang index 41b2240b8..2af75a544 100644 --- a/src/main/resources/assets/opencomputers/lang/en_US.lang +++ b/src/main/resources/assets/opencomputers/lang/en_US.lang @@ -24,7 +24,7 @@ oc:tile.PowerDistributor.name=Power Distributor oc:tile.Redstone.name=Redstone I/O oc:tile.Robot.name=Robot oc:tile.RobotAfterimage.name=Robot -oc:tile.RobotAssembler.name=Robot Assembler +oc:tile.RobotAssembler.name=Electronics Assembler oc:tile.Screen0.name=Screen (Tier 1) oc:tile.Screen1.name=Screen (Tier 2) oc:tile.Screen2.name=Screen (Tier 3) @@ -133,9 +133,9 @@ oc:gui.Error.OutOfMemory=Out of memory. oc:gui.Robot.Power=Energy oc:gui.Robot.TurnOff=Turn off oc:gui.Robot.TurnOn=Turn on -oc:gui.RobotAssembler.CollectRobot=Collect robot +oc:gui.RobotAssembler.CollectRobot=Collect output oc:gui.RobotAssembler.Complexity=Complexity: %s/%s -oc:gui.RobotAssembler.InsertCase=Insert a Computer Case +oc:gui.RobotAssembler.InsertCase=Insert a base part oc:gui.RobotAssembler.InsertCPU=Insert a CPU oc:gui.RobotAssembler.InsertRAM=Insert some RAM oc:gui.RobotAssembler.Progress=Progress: %s%% (%s) @@ -233,7 +233,7 @@ oc:tooltip.Robot=Unlike computers, robots can move around and interact with the # The underscore makes sure this isn't hidden with the rest of the tooltip. oc:tooltip.Robot_Level=§fLevel§7: §a%s§7. oc:tooltip.Robot_StoredEnergy=§fStored energy§7: §a%s§7. -oc:tooltip.RobotAssembler=Allows constructing robots from a number of different computer parts. +oc:tooltip.RobotAssembler=Allows constructing robots and other devices from a number of different computer parts. oc:tooltip.Screen=Display text, controlled by a Graphics Card in a Case.[nl] Maximum resolution: §f%sx%s§7.[nl] Maximum color depth: §f%s§7. oc:tooltip.Server=This is a server, there are many like it, but this one can be upgraded with components much like a computer case can be. It can be run by inserting it into a server rack.[nl] Number of supported terminals: §f%s§7. oc:tooltip.Server.Components=Installed components: diff --git a/src/main/scala/li/cil/oc/Localization.scala b/src/main/scala/li/cil/oc/Localization.scala index d5799fb62..9381aa5a6 100644 --- a/src/main/scala/li/cil/oc/Localization.scala +++ b/src/main/scala/li/cil/oc/Localization.scala @@ -62,24 +62,28 @@ object Localization { def Power = localizeImmediately("gui.Robot.Power") } - object RobotAssembler { - def InsertCase = localizeImmediately("gui.RobotAssembler.InsertCase") + object Assembler { + def InsertTemplate = localizeImmediately("gui.RobotAssembler.InsertCase") - def InsertCPU = localizeImmediately("gui.RobotAssembler.InsertCPU") + def CollectResult = localizeImmediately("gui.RobotAssembler.CollectRobot") - def InsertRAM = localizeImmediately("gui.RobotAssembler.InsertRAM") + def InsertCPU = localizeLater("gui.RobotAssembler.InsertCPU") - def Complexity(complexity: Int, maxComplexity: Int) = localizeImmediately("gui.RobotAssembler.Complexity", complexity.toString, maxComplexity.toString) + def InsertRAM = localizeLater("gui.RobotAssembler.InsertRAM") + + def Complexity(complexity: Int, maxComplexity: Int) = { + val message = localizeLater("gui.RobotAssembler.Complexity", complexity.toString, maxComplexity.toString) + if (complexity > maxComplexity) ChatMessageComponent.createFromText("§4").appendComponent(message) + else message + } def Run = localizeImmediately("gui.RobotAssembler.Run") - def CollectRobot = localizeImmediately("gui.RobotAssembler.CollectRobot") - def Progress(progress: Double, timeRemaining: String) = localizeImmediately("gui.RobotAssembler.Progress", progress.toInt.toString, timeRemaining) - def Warning(name: String) = "§7- " + localizeImmediately("gui.RobotAssembler.Warning." + name) + def Warning(name: String) = ChatMessageComponent.createFromText("§7- ").appendComponent(localizeLater("gui.RobotAssembler.Warning." + name)) - def Warnings = localizeImmediately("gui.RobotAssembler.Warnings") + def Warnings = localizeLater("gui.RobotAssembler.Warnings") } object ServerRack { diff --git a/src/main/scala/li/cil/oc/OpenComputers.scala b/src/main/scala/li/cil/oc/OpenComputers.scala index 6ec973c0c..52767afff 100644 --- a/src/main/scala/li/cil/oc/OpenComputers.scala +++ b/src/main/scala/li/cil/oc/OpenComputers.scala @@ -3,14 +3,18 @@ package li.cil.oc import java.util.logging.Logger import cpw.mods.fml.common.Mod.EventHandler +import cpw.mods.fml.common.event.FMLInterModComms.IMCEvent import cpw.mods.fml.common.{Mod, SidedProxy} import cpw.mods.fml.common.event._ import cpw.mods.fml.common.network.NetworkMod import cpw.mods.fml.common.network.NetworkMod._ import li.cil.oc.client.{PacketHandler => ClientPacketHandler} import li.cil.oc.common.Proxy +import li.cil.oc.common.template.AssemblerTemplates import li.cil.oc.server.{CommandHandler, PacketHandler => ServerPacketHandler} +import scala.collection.convert.WrapAsScala._ + @Mod(modid = OpenComputers.ID, name = OpenComputers.Name, version = OpenComputers.Version, /* certificateFingerprint = OpenComputers.Fingerprint, */ modLanguage = "scala", useMetadata = true) @@ -52,4 +56,14 @@ object OpenComputers { @EventHandler def serverStart(e: FMLServerStartingEvent) = CommandHandler.register(e) -} \ No newline at end of file + + @EventHandler + def imc(e: IMCEvent) = { + for (message <- e.getMessages) { + if (message.key == "registerAssemblerTemplate" && message.isNBTMessage) { + log.fine(s"Registering new assembler template from mod ${message.getSender}.") + AssemblerTemplates.add(message.getNBTValue) + } + } + } +} diff --git a/src/main/scala/li/cil/oc/client/gui/RobotAssembler.scala b/src/main/scala/li/cil/oc/client/gui/RobotAssembler.scala index 4670a30eb..32b944ea4 100644 --- a/src/main/scala/li/cil/oc/client/gui/RobotAssembler.scala +++ b/src/main/scala/li/cil/oc/client/gui/RobotAssembler.scala @@ -2,17 +2,16 @@ package li.cil.oc.client.gui import java.util -import li.cil.oc.api.driver.{Inventory, Memory, Processor} +import li.cil.oc.Localization import li.cil.oc.client.gui.widget.ProgressBar import li.cil.oc.client.{Textures, PacketSender => ClientPacketSender} -import li.cil.oc.common.{Slot, container, tileentity} -import li.cil.oc.{Localization, api} +import li.cil.oc.common.template.AssemblerTemplates +import li.cil.oc.common.{container, tileentity} import net.minecraft.client.gui.GuiButton import net.minecraft.entity.player.InventoryPlayer import org.lwjgl.opengl.GL11 import scala.collection.convert.WrapAsJava._ -import scala.collection.mutable class RobotAssembler(playerInventory: InventoryPlayer, val assembler: tileentity.RobotAssembler) extends DynamicGuiContainer(new container.RobotAssembler(playerInventory, assembler)) { xSize = 176 @@ -24,60 +23,11 @@ class RobotAssembler(playerInventory: InventoryPlayer, val assembler: tileentity private val progress = addWidget(new ProgressBar(28, 92)) - val suggestedComponents = Array( - "Screen" -> (() => hasComponent("screen1")), - "Keyboard" -> (() => hasComponent("keyboard")), - "GraphicsCard" -> (() => Array("graphicsCard1", "graphicsCard2", "graphicsCard3").exists(hasComponent)), - "Inventory" -> (() => hasInventory), - "OS" -> (() => hasFileSystem)) - def add[T](list: util.List[T], value: Any) = list.add(value.asInstanceOf[T]) - private def hasCase = assembler.isItemValidForSlot(0, assembler.getStackInSlot(0)) + private def validate = AssemblerTemplates.select(assemblerContainer.getSlot(0).getStack).map(_.validate(assemblerContainer.otherInventory)) - private def hasCPU = assembler.items.exists { - case Some(stack) => api.Driver.driverFor(stack) match { - case _: Processor => true - case _ => false - } - case _ => false - } - - private def hasRAM = assembler.items.exists { - case Some(stack) => api.Driver.driverFor(stack) match { - case _: Memory => true - case _ => false - } - case _ => false - } - - private def hasComponent(name: String) = assembler.items.exists { - case Some(stack) => Option(api.Items.get(stack)) match { - case Some(descriptor) => descriptor.name == name - case _ => false - } - case _ => false - } - - private def hasInventory = assembler.items.exists { - case Some(stack) => api.Driver.driverFor(stack) match { - case _: Inventory => true - case _ => false - } - case _ => false - } - - private def hasFileSystem = assembler.items.exists { - case Some(stack) => Option(api.Driver.driverFor(stack)) match { - case Some(driver) => Slot.fromApi(driver.slot(stack)) == Slot.Floppy || Slot.fromApi(driver.slot(stack)) == Slot.HDD - case _ => false - } - case _ => false - } - - private def isCapacityValid = assembler.complexity <= assembler.maxComplexity - - private def canBuild = !assemblerContainer.isAssembling && hasCase && hasCPU && hasRAM && isCapacityValid + private def canBuild = !assemblerContainer.isAssembling && validate.exists(_._1) protected override def actionPerformed(button: GuiButton) { if (button.id == 0 && canBuild) { @@ -100,37 +50,23 @@ class RobotAssembler(playerInventory: InventoryPlayer, val assembler: tileentity override def drawGuiContainerForegroundLayer(mouseX: Int, mouseY: Int) = { GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS) // Me lazy... prevents NEI render glitch. if (!assemblerContainer.isAssembling) { - def drawMessage(message: String) { - fontRenderer.drawString(message, 30, 94, 0x404040) - } - if (!inventorySlots.getSlot(0).getHasStack) { - drawMessage(Localization.RobotAssembler.InsertCase) - } - else if (api.Items.get(inventorySlots.getSlot(0).getStack) == api.Items.get("robot")) { - drawMessage(Localization.RobotAssembler.CollectRobot) - } - else if (!hasCPU) { - drawMessage(Localization.RobotAssembler.InsertCPU) - } - else if (!hasRAM) { - drawMessage(Localization.RobotAssembler.InsertRAM) - } - else { - fontRenderer.drawString(Localization.RobotAssembler.Complexity(assembler.complexity, assembler.maxComplexity), 30, 94, if (isCapacityValid) 0x404040 else 0x804040) - } + val info = validate + val message = + if (!assemblerContainer.getSlot(0).getHasStack) { + Localization.Assembler.InsertTemplate + } + else info match { + case Some((_, value, _)) if value != null => value.toString + case _ if assemblerContainer.getSlot(0).getHasStack => Localization.Assembler.CollectResult + case _ => "" + } + fontRenderer.drawString(message, 30, 94, 0x404040) if (runButton.func_82252_a) { val tooltip = new java.util.ArrayList[String] - tooltip.add(Localization.RobotAssembler.Run) - if (canBuild) { - var warnings = mutable.ArrayBuffer.empty[String] - for ((name, check) <- suggestedComponents) { - if (!check()) { - warnings += Localization.RobotAssembler.Warning(name) - } - } - if (warnings.length > 0) { - tooltip.add(Localization.RobotAssembler.Warnings) - tooltip.addAll(warnings) + tooltip.add(Localization.Assembler.Run) + info.foreach { + case (valid, _, warnings) => if (valid && warnings.length > 0) { + tooltip.addAll(warnings.map(_.toString).toList) } } drawHoveringText(tooltip, mouseX - guiLeft, mouseY - guiTop, fontRenderer) @@ -139,7 +75,7 @@ class RobotAssembler(playerInventory: InventoryPlayer, val assembler: tileentity else if (isPointInRegion(progress.x, progress.y, progress.width, progress.height, mouseX, mouseY)) { val tooltip = new java.util.ArrayList[String] val timeRemaining = formatTime(assemblerContainer.assemblyRemainingTime) - tooltip.add(Localization.RobotAssembler.Progress(assemblerContainer.assemblyProgress, timeRemaining)) + tooltip.add(Localization.Assembler.Progress(assemblerContainer.assemblyProgress, timeRemaining)) copiedDrawHoveringText(tooltip, mouseX - guiLeft, mouseY - guiTop, fontRenderer) } GL11.glPopAttrib() diff --git a/src/main/scala/li/cil/oc/common/InventorySlots.scala b/src/main/scala/li/cil/oc/common/InventorySlots.scala index 7be868681..5098ba48b 100644 --- a/src/main/scala/li/cil/oc/common/InventorySlots.scala +++ b/src/main/scala/li/cil/oc/common/InventorySlots.scala @@ -46,108 +46,6 @@ object InventorySlots { ) ) - val assembler = Array( - Array( - InventorySlot(Slot.None, Tier.None), // Reserved for computer case. - InventorySlot(Slot.Container, Tier.Two), - InventorySlot(Slot.Container, Tier.One), - InventorySlot(Slot.Container, Tier.One), - InventorySlot(Slot.Upgrade, Tier.One), - InventorySlot(Slot.Upgrade, Tier.One), - InventorySlot(Slot.Upgrade, Tier.One), - InventorySlot(Slot.None, Tier.None), - InventorySlot(Slot.None, Tier.None), - InventorySlot(Slot.None, Tier.None), - InventorySlot(Slot.None, Tier.None), - InventorySlot(Slot.None, Tier.None), - InventorySlot(Slot.None, Tier.None), - InventorySlot(Slot.Card, Tier.One), - InventorySlot(Slot.None, Tier.None), - InventorySlot(Slot.None, Tier.None), - InventorySlot(Slot.CPU, Tier.One), - InventorySlot(Slot.Memory, Tier.One), - InventorySlot(Slot.Memory, Tier.One), - InventorySlot(Slot.Floppy, Tier.Any), - InventorySlot(Slot.HDD, Tier.One), - InventorySlot(Slot.None, Tier.None) - ), - - Array( - InventorySlot(Slot.None, Tier.None), - InventorySlot(Slot.Container, Tier.Three), - InventorySlot(Slot.Container, Tier.Two), - InventorySlot(Slot.Container, Tier.One), - InventorySlot(Slot.Upgrade, Tier.Two), - InventorySlot(Slot.Upgrade, Tier.Two), - InventorySlot(Slot.Upgrade, Tier.Two), - InventorySlot(Slot.Upgrade, Tier.One), - InventorySlot(Slot.Upgrade, Tier.One), - InventorySlot(Slot.Upgrade, Tier.One), - InventorySlot(Slot.None, Tier.None), - InventorySlot(Slot.None, Tier.None), - InventorySlot(Slot.None, Tier.None), - InventorySlot(Slot.Card, Tier.Two), - InventorySlot(Slot.Card, Tier.One), - InventorySlot(Slot.None, Tier.None), - InventorySlot(Slot.CPU, Tier.Two), - InventorySlot(Slot.Memory, Tier.Two), - InventorySlot(Slot.Memory, Tier.Two), - InventorySlot(Slot.Floppy, Tier.Any), - InventorySlot(Slot.HDD, Tier.Two), - InventorySlot(Slot.None, Tier.None) - ), - - Array( - InventorySlot(Slot.None, Tier.None), - InventorySlot(Slot.Container, Tier.Three), - InventorySlot(Slot.Container, Tier.Three), - InventorySlot(Slot.Container, Tier.Two), - InventorySlot(Slot.Upgrade, Tier.Three), - InventorySlot(Slot.Upgrade, Tier.Three), - InventorySlot(Slot.Upgrade, Tier.Three), - InventorySlot(Slot.Upgrade, Tier.Two), - InventorySlot(Slot.Upgrade, Tier.Two), - InventorySlot(Slot.Upgrade, Tier.Two), - InventorySlot(Slot.Upgrade, Tier.One), - InventorySlot(Slot.Upgrade, Tier.One), - InventorySlot(Slot.Upgrade, Tier.One), - InventorySlot(Slot.Card, Tier.Three), - InventorySlot(Slot.Card, Tier.Two), - InventorySlot(Slot.Card, Tier.Two), - InventorySlot(Slot.CPU, Tier.Three), - InventorySlot(Slot.Memory, Tier.Three), - InventorySlot(Slot.Memory, Tier.Three), - InventorySlot(Slot.Floppy, Tier.Any), - InventorySlot(Slot.HDD, Tier.Three), - InventorySlot(Slot.HDD, Tier.Two) - ), - - Array( - InventorySlot(Slot.None, Tier.None), - InventorySlot(Slot.Container, Tier.Three), - InventorySlot(Slot.Container, Tier.Three), - InventorySlot(Slot.Container, Tier.Three), - InventorySlot(Slot.Upgrade, Tier.Three), - InventorySlot(Slot.Upgrade, Tier.Three), - InventorySlot(Slot.Upgrade, Tier.Three), - InventorySlot(Slot.Upgrade, Tier.Three), - InventorySlot(Slot.Upgrade, Tier.Three), - InventorySlot(Slot.Upgrade, Tier.Three), - InventorySlot(Slot.Upgrade, Tier.Three), - InventorySlot(Slot.Upgrade, Tier.Three), - InventorySlot(Slot.Upgrade, Tier.Three), - InventorySlot(Slot.Card, Tier.Three), - InventorySlot(Slot.Card, Tier.Three), - InventorySlot(Slot.Card, Tier.Three), - InventorySlot(Slot.CPU, Tier.Three), - InventorySlot(Slot.Memory, Tier.Three), - InventorySlot(Slot.Memory, Tier.Three), - InventorySlot(Slot.Floppy, Tier.Any), - InventorySlot(Slot.HDD, Tier.Three), - InventorySlot(Slot.HDD, Tier.Three) - ) - ) - val server = Array( Array( InventorySlot(Slot.Card, Tier.Two), diff --git a/src/main/scala/li/cil/oc/common/Proxy.scala b/src/main/scala/li/cil/oc/common/Proxy.scala index 9cd18ce47..855502c53 100644 --- a/src/main/scala/li/cil/oc/common/Proxy.scala +++ b/src/main/scala/li/cil/oc/common/Proxy.scala @@ -10,6 +10,7 @@ import li.cil.oc.common.event._ import li.cil.oc.common.item.Tablet import li.cil.oc.common.multipart.MultiPart import li.cil.oc.common.recipe.Recipes +import li.cil.oc.common.template.RobotTemplate import li.cil.oc.server._ import li.cil.oc.server.component.machine.{LuaJLuaArchitecture, NativeLuaArchitecture} import li.cil.oc.server.component.{Keyboard, machine} @@ -116,6 +117,9 @@ class Proxy { api.Driver.add(driver.converter.FluidTankInfo) api.Driver.add(driver.converter.ItemStack) + OpenComputers.log.info("Initializing assembler templates.") + RobotTemplate.register() + OpenComputers.log.info("Initializing loot disks.") Loot.init() diff --git a/src/main/scala/li/cil/oc/common/container/DynamicComponentSlot.scala b/src/main/scala/li/cil/oc/common/container/DynamicComponentSlot.scala index 6f2f7d986..415a87766 100644 --- a/src/main/scala/li/cil/oc/common/container/DynamicComponentSlot.scala +++ b/src/main/scala/li/cil/oc/common/container/DynamicComponentSlot.scala @@ -7,18 +7,18 @@ import li.cil.oc.util.SideTracker import net.minecraft.entity.player.EntityPlayer import net.minecraft.inventory.{IInventory, Slot} -class DynamicComponentSlot(val container: Player, inventory: IInventory, index: Int, x: Int, y: Int, val info: Array[Array[InventorySlot]], val tierGetter: () => Int) extends Slot(inventory, index, x, y) with ComponentSlot { +class DynamicComponentSlot(val container: Player, inventory: IInventory, index: Int, x: Int, y: Int, val info: DynamicComponentSlot => InventorySlot, val containerTierGetter: () => Int) extends Slot(inventory, index, x, y) with ComponentSlot { override def tier = { - val mainTier = tierGetter() - if (mainTier >= 0) info(mainTier)(getSlotIndex).tier + val mainTier = containerTierGetter() + if (mainTier >= 0) info(this).tier else mainTier } def tierIcon = Icons.get(tier) def slot = { - val mainTier = tierGetter() - if (mainTier >= 0) info(tierGetter())(getSlotIndex).slot + val mainTier = containerTierGetter() + if (mainTier >= 0) info(this).slot else common.Slot.None } diff --git a/src/main/scala/li/cil/oc/common/container/Player.scala b/src/main/scala/li/cil/oc/common/container/Player.scala index f2c21d87b..d006ad416 100644 --- a/src/main/scala/li/cil/oc/common/container/Player.scala +++ b/src/main/scala/li/cil/oc/common/container/Player.scala @@ -2,6 +2,7 @@ package li.cil.oc.common.container import li.cil.oc.common import li.cil.oc.common.InventorySlots.InventorySlot +import li.cil.oc.common.Tier import li.cil.oc.util.SideTracker import net.minecraft.entity.player.{EntityPlayer, InventoryPlayer} import net.minecraft.inventory.{Container, ICrafting, IInventory, Slot} @@ -90,9 +91,14 @@ abstract class Player(val playerInventory: InventoryPlayer, val otherInventory: addSlotToContainer(new StaticComponentSlot(this, otherInventory, index, x, y, slot, tier)) } - def addSlotToContainer(x: Int, y: Int, info: Array[Array[InventorySlot]], tierGetter: () => Int) { + def addSlotToContainer(x: Int, y: Int, info: Array[Array[InventorySlot]], containerTierGetter: () => Int) { val index = inventorySlots.size - addSlotToContainer(new DynamicComponentSlot(this, otherInventory, index, x, y, info, tierGetter)) + addSlotToContainer(new DynamicComponentSlot(this, otherInventory, index, x, y, slot => info(slot.containerTierGetter())(slot.getSlotIndex), containerTierGetter)) + } + + def addSlotToContainer(x: Int, y: Int, info: DynamicComponentSlot => InventorySlot) { + val index = inventorySlots.size + addSlotToContainer(new DynamicComponentSlot(this, otherInventory, index, x, y, info, () => Tier.One)) } /** Render player inventory at the specified coordinates. */ diff --git a/src/main/scala/li/cil/oc/common/container/RobotAssembler.scala b/src/main/scala/li/cil/oc/common/container/RobotAssembler.scala index e5b865f77..aae69d5bd 100644 --- a/src/main/scala/li/cil/oc/common/container/RobotAssembler.scala +++ b/src/main/scala/li/cil/oc/common/container/RobotAssembler.scala @@ -2,11 +2,12 @@ package li.cil.oc.common.container import cpw.mods.fml.relauncher.{Side, SideOnly} import li.cil.oc.client.gui.Icons +import li.cil.oc.common.InventorySlots.InventorySlot +import li.cil.oc.common.template.AssemblerTemplates +import li.cil.oc.common.tileentity +import li.cil.oc.util.SideTracker import li.cil.oc.{Settings, common} -import li.cil.oc.common.{InventorySlots, tileentity} -import li.cil.oc.util.{ItemUtils, SideTracker} import net.minecraft.entity.player.InventoryPlayer -import net.minecraft.inventory.Slot class RobotAssembler(playerInventory: InventoryPlayer, val assembler: tileentity.RobotAssembler) extends Player(playerInventory, assembler) { // Computer case. @@ -20,34 +21,46 @@ class RobotAssembler(playerInventory: InventoryPlayer, val assembler: tileentity }) } - def caseTier = ItemUtils.caseTier(inventorySlots.get(0).asInstanceOf[Slot].getStack) + private def slotInfo(slot: DynamicComponentSlot) = { + AssemblerTemplates.select(getSlot(0).getStack) match { + case Some(template) => + val index = slot.getSlotIndex + val tplSlot = + if ((1 until 4) contains index) template.containerSlots(index - 1) + else if ((4 until 13) contains index) template.upgradeSlots(index - 4) + else if ((13 until 21) contains index) template.componentSlots(index - 13) + else AssemblerTemplates.NoSlot + new InventorySlot(tplSlot.kind, tplSlot.tier) + case _ => new InventorySlot(common.Slot.None, common.Tier.None) + } + } // Component containers. for (i <- 0 until 3) { - addSlotToContainer(34 + i * slotSize, 70, InventorySlots.assembler, () => caseTier) + addSlotToContainer(34 + i * slotSize, 70, slotInfo _) } // Components. for (i <- 0 until 9) { - addSlotToContainer(34 + (i % 3) * slotSize, 12 + (i / 3) * slotSize, InventorySlots.assembler, () => caseTier) + addSlotToContainer(34 + (i % 3) * slotSize, 12 + (i / 3) * slotSize, slotInfo _) } // Cards. for (i <- 0 until 3) { - addSlotToContainer(104, 12 + i * slotSize, InventorySlots.assembler, () => caseTier) + addSlotToContainer(104, 12 + i * slotSize, slotInfo _) } // CPU. - addSlotToContainer(126, 12, InventorySlots.assembler, () => caseTier) + addSlotToContainer(126, 12, slotInfo _) // RAM. for (i <- 0 until 2) { - addSlotToContainer(126, 30 + i * slotSize, InventorySlots.assembler, () => caseTier) + addSlotToContainer(126, 30 + i * slotSize, slotInfo _) } // Floppy + HDDs. for (i <- 0 until 3) { - addSlotToContainer(148, 12 + i * slotSize, InventorySlots.assembler, () => caseTier) + addSlotToContainer(148, 12 + i * slotSize, slotInfo _) } // Show the player's inventory. diff --git a/src/main/scala/li/cil/oc/common/template/AssemblerTemplates.scala b/src/main/scala/li/cil/oc/common/template/AssemblerTemplates.scala new file mode 100644 index 000000000..ee1445693 --- /dev/null +++ b/src/main/scala/li/cil/oc/common/template/AssemblerTemplates.scala @@ -0,0 +1,93 @@ +package li.cil.oc.common.template + +import java.lang.reflect.{Method, Modifier} +import java.util.logging.Level + +import li.cil.oc.common.{Slot, Tier} +import li.cil.oc.util.ExtendedNBT._ +import li.cil.oc.{OpenComputers, api} +import net.minecraft.inventory.IInventory +import net.minecraft.item.ItemStack +import net.minecraft.nbt.NBTTagCompound +import net.minecraft.util.ChatMessageComponent + +import scala.collection.mutable + +object AssemblerTemplates { + val NoSlot = new Slot(Slot.None, Tier.None, None) + + val templates = mutable.ArrayBuffer.empty[Template] + + def add(template: NBTTagCompound): Unit = try { + val selector = getStaticMethod(template.getString("select"), classOf[ItemStack]) + val validator = getStaticMethod(template.getString("validate"), classOf[IInventory]) + val assembler = getStaticMethod(template.getString("assemble"), classOf[IInventory]) + val containerSlots = template.getTagList("containerSlots").map(parseSlot(Some(Slot.Container))).take(3).padTo(3, NoSlot).toArray + val upgradeSlots = template.getTagList("upgradeSlots").map(parseSlot(Some(Slot.Upgrade))).take(9).padTo(9, NoSlot).toArray + val componentSlots = template.getTagList("componentSlots").map(parseSlot()).take(9).padTo(9, NoSlot).toArray + + templates += new Template(selector, validator, assembler, containerSlots, upgradeSlots, componentSlots) + } + catch { + case t: Throwable => OpenComputers.log.log(Level.WARNING, "Failed registering assembler template.", t) + } + + def select(stack: ItemStack) = templates.find(_.select(stack)) + + class Template(val selector: Method, + val validator: Method, + val assembler: Method, + val containerSlots: Array[Slot], + val upgradeSlots: Array[Slot], + val componentSlots: Array[Slot]) { + def select(stack: ItemStack) = tryInvokeStatic(selector, stack)(false) + + def validate(inventory: IInventory) = tryInvokeStatic(validator, inventory)(null: Array[AnyRef]) match { + case Array(valid: java.lang.Boolean, progress: ChatMessageComponent, warnings: Array[ChatMessageComponent]) => (valid: Boolean, progress, warnings) + case Array(valid: java.lang.Boolean, progress: ChatMessageComponent) => (valid: Boolean, progress, Array.empty[ChatMessageComponent]) + case Array(valid: java.lang.Boolean) => (valid: Boolean, null, Array.empty[ChatMessageComponent]) + case _ => (false, null, Array.empty[ChatMessageComponent]) + } + + def assemble(inventory: IInventory) = tryInvokeStatic(assembler, inventory)(null: Array[AnyRef]) match { + case Array(stack: ItemStack, energy: java.lang.Double) => (stack, energy: Double) + case Array(stack: ItemStack) => (stack, 0.0) + case _ => (null, 0.0) + } + } + + class Slot(val kind: String, val tier: Int, val validator: Option[Method]) { + def validate(inventory: IInventory, slot: Int, stack: ItemStack) = validator match { + case Some(method) => tryInvokeStatic(method, inventory, slot.underlying(), stack)(false) + case _ => Option(api.Driver.driverFor(stack)) match { + case Some(driver) => validateInternal(stack, driver) && Slot.fromApi(driver.slot(stack)) == kind + case _ => false + } + } + + protected def validateInternal(stack: ItemStack, driver: api.driver.Item) = driver.tier(stack) <= tier + } + + private def parseSlot(kindOverride: Option[String] = None)(nbt: NBTTagCompound) = { + val kind = kindOverride.getOrElse(if (nbt.hasKey("type")) nbt.getString("type") else Slot.None) + val tier = if (nbt.hasKey("tier")) nbt.getInteger("tier") else Tier.None + val validator = if (nbt.hasKey("validator")) Option(getStaticMethod(nbt.getString("validator"), classOf[ItemStack], classOf[Int], classOf[ItemStack])) else None + new Slot(kind, tier, validator) + } + + private def getStaticMethod(name: String, signature: Class[_]*) = { + val nameSplit = name.lastIndexOf('.') + val className = name.substring(0, nameSplit) + val methodName = name.substring(nameSplit + 1) + val clazz = Class.forName(className) + val method = clazz.getDeclaredMethod(methodName, signature: _*) + if (!Modifier.isStatic(method.getModifiers)) throw new IllegalArgumentException(s"Method $name is not static.") + method + } + + private def tryInvokeStatic[T](method: Method, args: AnyRef*)(default: T): T = try method.invoke(null, args: _*).asInstanceOf[T] catch { + case t: Throwable => + OpenComputers.log.log(Level.WARNING, s"Error invoking callback ${method.getDeclaringClass.getCanonicalName + "." + method.getName}.", t) + default + } +} diff --git a/src/main/scala/li/cil/oc/common/template/RobotTemplate.scala b/src/main/scala/li/cil/oc/common/template/RobotTemplate.scala new file mode 100644 index 000000000..b210e9495 --- /dev/null +++ b/src/main/scala/li/cil/oc/common/template/RobotTemplate.scala @@ -0,0 +1,275 @@ +package li.cil.oc.common.template + +import cpw.mods.fml.common.event.FMLInterModComms +import li.cil.oc.api.driver.{Inventory, Memory, Processor, UpgradeContainer} +import li.cil.oc.common.{Slot, Tier} +import li.cil.oc.util.ExtendedNBT._ +import li.cil.oc.util.ItemUtils +import li.cil.oc.{Localization, Settings, api} +import net.minecraft.inventory.IInventory +import net.minecraft.item.ItemStack +import net.minecraft.nbt.{NBTTagCompound, NBTTagList} +import net.minecraft.util.ChatMessageComponent + +import scala.collection.mutable + +object RobotTemplate { + def selectTier1(stack: ItemStack) = ItemUtils.caseTier(stack) == Tier.One + + def selectTier2(stack: ItemStack) = ItemUtils.caseTier(stack) == Tier.Two + + def selectTier3(stack: ItemStack) = ItemUtils.caseTier(stack) == Tier.Three + + def selectCreative(stack: ItemStack) = ItemUtils.caseTier(stack) == Tier.Four + + def validate(inventory: IInventory): Array[AnyRef] = { + val hasCase = ItemUtils.caseTier(inventory.getStackInSlot(0)) != Tier.None + val hasCPU = RobotTemplate.hasCPU(inventory) + val hasRAM = RobotTemplate.hasRAM(inventory) + val complexity = RobotTemplate.complexity(inventory) + val maxComplexity = RobotTemplate.maxComplexity(inventory) + + val valid = hasCase && hasCPU && hasRAM && complexity <= maxComplexity + + val progress = + if (!hasCPU) Localization.Assembler.InsertCPU + else if (!hasRAM) Localization.Assembler.InsertRAM + else Localization.Assembler.Complexity(complexity, maxComplexity) + + val warnings = mutable.ArrayBuffer.empty[ChatMessageComponent] + for ((name, check) <- suggestedComponents) { + if (!check(inventory)) { + warnings += Localization.Assembler.Warning(name) + } + } + if (warnings.length > 0) { + warnings.prepend(Localization.Assembler.Warnings) + } + + Array(valid: java.lang.Boolean, progress, warnings.toArray) + } + + def assemble(inventory: IInventory) = { + val items = (0 until inventory.getSizeInventory).map(inventory.getStackInSlot) + val data = new ItemUtils.RobotData() + data.tier = ItemUtils.caseTier(inventory.getStackInSlot(0)) + data.name = ItemUtils.RobotData.randomName + data.robotEnergy = 50000 + data.totalEnergy = data.robotEnergy + data.containers = items.slice(1, 4).filter(_ != null).toArray + data.components = items.drop(4).filter(_ != null).toArray + val stack = api.Items.get("robot").createItemStack(1) + data.save(stack) + val energy = Settings.get.robotBaseCost + complexity(inventory) * Settings.get.robotComplexityCost + + Array(stack, energy: java.lang.Double) + } + + def register() { + // Tier 1 + { + val nbt = new NBTTagCompound() + nbt.setString("select", "li.cil.oc.common.template.RobotTemplate.selectTier1") + nbt.setString("validate", "li.cil.oc.common.template.RobotTemplate.validate") + nbt.setString("assemble", "li.cil.oc.common.template.RobotTemplate.assemble") + + val containerSlots = new NBTTagList() + containerSlots.appendTag(Map("tier" -> Tier.Two)) + containerSlots.appendTag(Map("tier" -> Tier.One)) + containerSlots.appendTag(Map("tier" -> Tier.One)) + nbt.setTag("containerSlots", containerSlots) + + val upgradeSlots = new NBTTagList() + upgradeSlots.appendTag(Map("tier" -> Tier.One)) + upgradeSlots.appendTag(Map("tier" -> Tier.One)) + upgradeSlots.appendTag(Map("tier" -> Tier.One)) + nbt.setTag("upgradeSlots", upgradeSlots) + + val componentSlots = new NBTTagList() + componentSlots.appendTag(Map("type" -> Slot.Card, "tier" -> Tier.One)) + componentSlots.appendTag(new NBTTagCompound()) + componentSlots.appendTag(new NBTTagCompound()) + componentSlots.appendTag(Map("type" -> Slot.CPU, "tier" -> Tier.One)) + componentSlots.appendTag(Map("type" -> Slot.Memory, "tier" -> Tier.One)) + componentSlots.appendTag(Map("type" -> Slot.Memory, "tier" -> Tier.One)) + componentSlots.appendTag(Map("type" -> Slot.Floppy, "tier" -> Tier.Any)) + componentSlots.appendTag(Map("type" -> Slot.HDD, "tier" -> Tier.One)) + nbt.setTag("componentSlots", componentSlots) + + FMLInterModComms.sendMessage("OpenComputers", "registerAssemblerTemplate", nbt) + } + + // Tier 2 + { + val nbt = new NBTTagCompound() + nbt.setString("select", "li.cil.oc.common.template.RobotTemplate.selectTier2") + nbt.setString("validate", "li.cil.oc.common.template.RobotTemplate.validate") + nbt.setString("assemble", "li.cil.oc.common.template.RobotTemplate.assemble") + + val containerSlots = new NBTTagList() + containerSlots.appendTag(Map("tier" -> Tier.Three)) + containerSlots.appendTag(Map("tier" -> Tier.Two)) + containerSlots.appendTag(Map("tier" -> Tier.One)) + nbt.setTag("containerSlots", containerSlots) + + val upgradeSlots = new NBTTagList() + upgradeSlots.appendTag(Map("tier" -> Tier.Two)) + upgradeSlots.appendTag(Map("tier" -> Tier.Two)) + upgradeSlots.appendTag(Map("tier" -> Tier.Two)) + upgradeSlots.appendTag(Map("tier" -> Tier.One)) + upgradeSlots.appendTag(Map("tier" -> Tier.One)) + upgradeSlots.appendTag(Map("tier" -> Tier.One)) + nbt.setTag("upgradeSlots", upgradeSlots) + + val componentSlots = new NBTTagList() + componentSlots.appendTag(Map("type" -> Slot.Card, "tier" -> Tier.Two)) + componentSlots.appendTag(Map("type" -> Slot.Card, "tier" -> Tier.One)) + componentSlots.appendTag(new NBTTagCompound()) + componentSlots.appendTag(Map("type" -> Slot.CPU, "tier" -> Tier.Two)) + componentSlots.appendTag(Map("type" -> Slot.Memory, "tier" -> Tier.Two)) + componentSlots.appendTag(Map("type" -> Slot.Memory, "tier" -> Tier.Two)) + componentSlots.appendTag(Map("type" -> Slot.Floppy, "tier" -> Tier.Any)) + componentSlots.appendTag(Map("type" -> Slot.HDD, "tier" -> Tier.Two)) + nbt.setTag("componentSlots", componentSlots) + + FMLInterModComms.sendMessage("OpenComputers", "registerAssemblerTemplate", nbt) + } + + // Tier 3 + { + val nbt = new NBTTagCompound() + nbt.setString("select", "li.cil.oc.common.template.RobotTemplate.selectTier3") + nbt.setString("validate", "li.cil.oc.common.template.RobotTemplate.validate") + nbt.setString("assemble", "li.cil.oc.common.template.RobotTemplate.assemble") + + val containerSlots = new NBTTagList() + containerSlots.appendTag(Map("tier" -> Tier.Three)) + containerSlots.appendTag(Map("tier" -> Tier.Two)) + containerSlots.appendTag(Map("tier" -> Tier.Two)) + nbt.setTag("containerSlots", containerSlots) + + val upgradeSlots = new NBTTagList() + upgradeSlots.appendTag(Map("tier" -> Tier.Three)) + upgradeSlots.appendTag(Map("tier" -> Tier.Three)) + upgradeSlots.appendTag(Map("tier" -> Tier.Three)) + upgradeSlots.appendTag(Map("tier" -> Tier.Two)) + upgradeSlots.appendTag(Map("tier" -> Tier.Two)) + upgradeSlots.appendTag(Map("tier" -> Tier.Two)) + upgradeSlots.appendTag(Map("tier" -> Tier.One)) + upgradeSlots.appendTag(Map("tier" -> Tier.One)) + upgradeSlots.appendTag(Map("tier" -> Tier.One)) + nbt.setTag("upgradeSlots", upgradeSlots) + + val componentSlots = new NBTTagList() + componentSlots.appendTag(Map("type" -> Slot.Card, "tier" -> Tier.Three)) + componentSlots.appendTag(Map("type" -> Slot.Card, "tier" -> Tier.Two)) + componentSlots.appendTag(Map("type" -> Slot.Card, "tier" -> Tier.Two)) + componentSlots.appendTag(Map("type" -> Slot.CPU, "tier" -> Tier.Three)) + componentSlots.appendTag(Map("type" -> Slot.Memory, "tier" -> Tier.Three)) + componentSlots.appendTag(Map("type" -> Slot.Memory, "tier" -> Tier.Three)) + componentSlots.appendTag(Map("type" -> Slot.Floppy, "tier" -> Tier.Any)) + componentSlots.appendTag(Map("type" -> Slot.HDD, "tier" -> Tier.Three)) + componentSlots.appendTag(Map("type" -> Slot.HDD, "tier" -> Tier.Two)) + nbt.setTag("componentSlots", componentSlots) + + FMLInterModComms.sendMessage("OpenComputers", "registerAssemblerTemplate", nbt) + } + + // Creative + { + val nbt = new NBTTagCompound() + nbt.setString("select", "li.cil.oc.common.template.RobotTemplate.selectCreative") + nbt.setString("validate", "li.cil.oc.common.template.RobotTemplate.validate") + nbt.setString("assemble", "li.cil.oc.common.template.RobotTemplate.assemble") + + val containerSlots = new NBTTagList() + containerSlots.appendTag(Map("tier" -> Tier.Three)) + containerSlots.appendTag(Map("tier" -> Tier.Three)) + containerSlots.appendTag(Map("tier" -> Tier.Three)) + nbt.setTag("containerSlots", containerSlots) + + val upgradeSlots = new NBTTagList() + upgradeSlots.appendTag(Map("tier" -> Tier.Three)) + upgradeSlots.appendTag(Map("tier" -> Tier.Three)) + upgradeSlots.appendTag(Map("tier" -> Tier.Three)) + upgradeSlots.appendTag(Map("tier" -> Tier.Three)) + upgradeSlots.appendTag(Map("tier" -> Tier.Three)) + upgradeSlots.appendTag(Map("tier" -> Tier.Three)) + upgradeSlots.appendTag(Map("tier" -> Tier.Three)) + upgradeSlots.appendTag(Map("tier" -> Tier.Three)) + upgradeSlots.appendTag(Map("tier" -> Tier.Three)) + nbt.setTag("upgradeSlots", upgradeSlots) + + val componentSlots = new NBTTagList() + componentSlots.appendTag(Map("type" -> Slot.Card, "tier" -> Tier.Three)) + componentSlots.appendTag(Map("type" -> Slot.Card, "tier" -> Tier.Three)) + componentSlots.appendTag(Map("type" -> Slot.Card, "tier" -> Tier.Three)) + componentSlots.appendTag(Map("type" -> Slot.CPU, "tier" -> Tier.Three)) + componentSlots.appendTag(Map("type" -> Slot.Memory, "tier" -> Tier.Three)) + componentSlots.appendTag(Map("type" -> Slot.Memory, "tier" -> Tier.Three)) + componentSlots.appendTag(Map("type" -> Slot.Floppy, "tier" -> Tier.Any)) + componentSlots.appendTag(Map("type" -> Slot.HDD, "tier" -> Tier.Three)) + componentSlots.appendTag(Map("type" -> Slot.HDD, "tier" -> Tier.Three)) + nbt.setTag("componentSlots", componentSlots) + + FMLInterModComms.sendMessage("OpenComputers", "registerAssemblerTemplate", nbt) + } + } + + private val suggestedComponents = Array( + "Screen" -> hasComponent("screen1") _, + "Keyboard" -> hasComponent("keyboard") _, + "GraphicsCard" -> ((inventory: IInventory) => Array("graphicsCard1", "graphicsCard2", "graphicsCard3").exists(name => hasComponent(name)(inventory))), + "Inventory" -> hasInventory _, + "OS" -> hasFileSystem _) + + private def exists(inventory: IInventory, p: ItemStack => Boolean) = { + (0 until inventory.getSizeInventory).exists(slot => Option(inventory.getStackInSlot(slot)) match { + case Some(stack) => p(stack) + case _ => false + }) + } + + private def hasCPU(inventory: IInventory) = exists(inventory, api.Driver.driverFor(_) match { + case _: Processor => true + case _ => false + }) + + private def hasRAM(inventory: IInventory) = exists(inventory, api.Driver.driverFor(_) match { + case _: Memory => true + case _ => false + }) + + private def hasComponent(name: String)(inventory: IInventory) = exists(inventory, stack => Option(api.Items.get(stack)) match { + case Some(descriptor) => descriptor.name == name + case _ => false + }) + + private def hasInventory(inventory: IInventory) = exists(inventory, api.Driver.driverFor(_) match { + case _: Inventory => true + case _ => false + }) + + private def hasFileSystem(inventory: IInventory) = exists(inventory, stack => Option(api.Driver.driverFor(stack)) match { + case Some(driver) => Slot.fromApi(driver.slot(stack)) == Slot.Floppy || Slot.fromApi(driver.slot(stack)) == Slot.HDD + case _ => false + }) + + private def complexity(inventory: IInventory) = { + var acc = 0 + for (slot <- 1 until inventory.getSizeInventory) { + val stack = inventory.getStackInSlot(slot) + acc += (Option(api.Driver.driverFor(stack)) match { + case Some(driver: UpgradeContainer) => (1 + driver.tier(stack)) * 2 + case Some(driver) => 1 + driver.tier(stack) + case _ => 0 + }) + } + acc + } + + private def maxComplexity(inventory: IInventory) = { + val caseTier = ItemUtils.caseTier(inventory.getStackInSlot(0)) + if (caseTier >= 0) Settings.robotComplexityByTier(caseTier) else 0 + } +} diff --git a/src/main/scala/li/cil/oc/common/tileentity/RobotAssembler.scala b/src/main/scala/li/cil/oc/common/tileentity/RobotAssembler.scala index 43e7d89bb..4bff074ef 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/RobotAssembler.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/RobotAssembler.scala @@ -1,13 +1,10 @@ package li.cil.oc.common.tileentity import cpw.mods.fml.relauncher.{Side, SideOnly} -import li.cil.oc.api.Driver -import li.cil.oc.api.driver.UpgradeContainer import li.cil.oc.api.network._ -import li.cil.oc.common.{InventorySlots, Slot, Tier} +import li.cil.oc.common.template.AssemblerTemplates import li.cil.oc.server.{PacketSender => ServerPacketSender} import li.cil.oc.util.ExtendedNBT._ -import li.cil.oc.util.ItemUtils import li.cil.oc.{Settings, api} import net.minecraft.item.ItemStack import net.minecraft.nbt.NBTTagCompound @@ -19,7 +16,7 @@ class RobotAssembler extends traits.Environment with traits.PowerAcceptor with t withConnector(Settings.get.bufferConverter). create() - var robot: Option[ItemStack] = None + var output: Option[ItemStack] = None var totalRequiredEnergy = 0.0 @@ -45,61 +42,41 @@ class RobotAssembler extends traits.Environment with traits.PowerAcceptor with t // ----------------------------------------------------------------------- // - def complexity = items.drop(1).foldLeft(0)((acc, stack) => acc + (Option(api.Driver.driverFor(stack.orNull)) match { - case Some(driver: UpgradeContainer) => (1 + driver.tier(stack.get)) * 2 - case Some(driver) => 1 + driver.tier(stack.get) - case _ => 0 - })) - - def maxComplexity = { - val caseTier = ItemUtils.caseTier(items(0).orNull) - if (caseTier >= 0) Settings.robotComplexityByTier(caseTier) else 0 - } - def start(finishImmediately: Boolean = false): Boolean = this.synchronized { - if (!isAssembling && robot.isEmpty && complexity <= maxComplexity) { - for (slot <- 0 until getSizeInventory) { - val stack = getStackInSlot(slot) - if (stack != null && !isItemValidForSlot(slot, stack)) return false - } - val data = new ItemUtils.RobotData() - data.tier = ItemUtils.caseTier(items(0).get) - data.name = ItemUtils.RobotData.randomName - data.robotEnergy = 50000 - data.totalEnergy = data.robotEnergy - data.containers = items.take(4).drop(1).collect { - case Some(item) => item - } - data.components = items.drop(4).collect { - case Some(item) => item - } - val stack = api.Items.get("robot").createItemStack(1) - data.save(stack) - robot = Some(stack) - if (finishImmediately || data.tier == Tier.Four) { - // Creative tier, finish instantly. - totalRequiredEnergy = 0 - } - else { - totalRequiredEnergy = math.max(1, Settings.get.robotBaseCost + complexity * Settings.get.robotComplexityCost) - } - requiredEnergy = totalRequiredEnergy - ServerPacketSender.sendRobotAssembling(this, assembling = true) + AssemblerTemplates.select(getStackInSlot(0)) match { + case Some(template) if !isAssembling && output.isEmpty && template.validate(this)._1 => + for (slot <- 0 until getSizeInventory) { + val stack = getStackInSlot(slot) + if (stack != null && !isItemValidForSlot(slot, stack)) return false + } + val (stack, energy) = template.assemble(this) + output = Some(stack) + if (finishImmediately) { + totalRequiredEnergy = 0 + } + else { + totalRequiredEnergy = math.max(1, energy) + } + requiredEnergy = totalRequiredEnergy + ServerPacketSender.sendRobotAssembling(this, assembling = true) - for (slot <- 0 until getSizeInventory) items(slot) = None - onInventoryChanged() + for (slot <- 0 until getSizeInventory) items(slot) = None + onInventoryChanged() - true + true + case _ => false } - else false } // ----------------------------------------------------------------------- // - @Callback(doc = """function(): string, number[, number] -- The current state of the assember, `busy' or `idle', followed by the progress or complexity and maximum complexity, respectively.""") + @Callback(doc = """function(): string, number or boolean -- The current state of the assembler, `busy' or `idle', followed by the progress or template validity, respectively.""") def status(context: Context, args: Arguments): Array[Object] = { if (isAssembling) result("busy", progress) - else result("idle", complexity, maxComplexity) + else AssemblerTemplates.select(getStackInSlot(0)) match { + case Some(template) if template.validate(this)._1 => result("idle", true) + case _ => result("idle", false) + } } @Callback(doc = """function():boolean -- Start assembling, if possible. Returns whether assembly was started or not.""") @@ -111,25 +88,29 @@ class RobotAssembler extends traits.Environment with traits.PowerAcceptor with t override def updateEntity() { super.updateEntity() - if (robot.isDefined && world.getTotalWorldTime % Settings.get.tickFrequency == 0) { + if (output.isDefined && world.getTotalWorldTime % Settings.get.tickFrequency == 0) { val want = math.max(1, math.min(requiredEnergy, Settings.get.assemblerTickAmount * Settings.get.tickFrequency)) val success = Settings.get.ignorePower || node.tryChangeBuffer(-want) if (success) { requiredEnergy -= want } if (requiredEnergy <= 0) { - setInventorySlotContents(0, robot.get) - robot = None + setInventorySlotContents(0, output.get) + output = None requiredEnergy = 0 } - ServerPacketSender.sendRobotAssembling(this, success && robot.isDefined) + ServerPacketSender.sendRobotAssembling(this, success && output.isDefined) } } override def readFromNBT(nbt: NBTTagCompound) { super.readFromNBT(nbt) - if (nbt.hasKey(Settings.namespace + "robot")) { - robot = Option(ItemStack.loadItemStackFromNBT(nbt.getCompoundTag(Settings.namespace + "robot"))) + if (nbt.hasKey(Settings.namespace + "output")) { + output = Option(ItemStack.loadItemStackFromNBT(nbt.getCompoundTag(Settings.namespace + "output"))) + } + else if (nbt.hasKey(Settings.namespace + "robot")) { + // Backwards compatibility. + output = Option(ItemStack.loadItemStackFromNBT(nbt.getCompoundTag(Settings.namespace + "robot"))) } totalRequiredEnergy = nbt.getDouble(Settings.namespace + "total") requiredEnergy = nbt.getDouble(Settings.namespace + "remaining") @@ -137,7 +118,7 @@ class RobotAssembler extends traits.Environment with traits.PowerAcceptor with t override def writeToNBT(nbt: NBTTagCompound) { super.writeToNBT(nbt) - robot.foreach(stack => nbt.setNewCompoundTag(Settings.namespace + "robot", stack.writeToNBT)) + output.foreach(stack => nbt.setNewCompoundTag(Settings.namespace + "output", stack.writeToNBT)) nbt.setDouble(Settings.namespace + "total", totalRequiredEnergy) nbt.setDouble(Settings.namespace + "remaining", requiredEnergy) } @@ -155,22 +136,22 @@ class RobotAssembler extends traits.Environment with traits.PowerAcceptor with t // ----------------------------------------------------------------------- // - override def getSizeInventory = InventorySlots.assembler(0).length + override def getSizeInventory = 22 override def getInventoryStackLimit = 1 override def isItemValidForSlot(slot: Int, stack: ItemStack) = if (slot == 0) { - !isAssembling && ItemUtils.caseTier(stack) != Tier.None + !isAssembling && AssemblerTemplates.select(stack).isDefined } - else { - val caseTier = ItemUtils.caseTier(items(0).orNull) - caseTier != Tier.None && { - val info = InventorySlots.assembler(caseTier)(slot) - Option(Driver.driverFor(stack)) match { - case Some(driver) if info.slot != Slot.None && info.tier != Tier.None => Slot.fromApi(driver.slot(stack)) == info.slot && driver.tier(stack) <= info.tier - case _ => false - } - } + else AssemblerTemplates.select(getStackInSlot(0)) match { + case Some(template) => + val tplSlot = + if ((1 until 4) contains slot) template.containerSlots(slot - 1) + else if ((4 until 13) contains slot) template.upgradeSlots(slot - 4) + else if ((13 until 21) contains slot) template.componentSlots(slot - 13) + else AssemblerTemplates.NoSlot + tplSlot.validate(this, slot, stack) + case _ => false } } diff --git a/src/main/scala/li/cil/oc/util/ExtendedNBT.scala b/src/main/scala/li/cil/oc/util/ExtendedNBT.scala index 86e5f7a24..96e7161a4 100644 --- a/src/main/scala/li/cil/oc/util/ExtendedNBT.scala +++ b/src/main/scala/li/cil/oc/util/ExtendedNBT.scala @@ -34,6 +34,24 @@ object ExtendedNBT { nbt } + implicit def toNbt(value: Map[String, _]): NBTTagCompound = { + val nbt = new NBTTagCompound() + for ((key, value) <- value) value match { + case value: Byte => nbt.setTag(key, value) + case value: Short => nbt.setTag(key, value) + case value: Int => nbt.setTag(key, value) + case value: Array[Int] => nbt.setTag(key, value) + case value: Long => nbt.setTag(key, value) + case value: Float => nbt.setTag(key, value) + case value: Double => nbt.setTag(key, value) + case value: Array[Byte] => nbt.setTag(key, value) + case value: String => nbt.setTag(key, value) + case value: ItemStack => nbt.setTag(key, value) + case _ => + } + nbt + } + implicit def byteIterableToNbt(value: Iterable[Byte]) = value.map(toNbt) implicit def shortIterableToNbt(value: Iterable[Short]) = value.map(toNbt) From 1176e7b3f0d6020ef75981ad5adcde04c7599c0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20N=C3=BCcke?= Date: Fri, 29 Aug 2014 18:59:15 +0200 Subject: [PATCH 2/2] Reworked how complexity is computed when assembling robots. Cases now only provide a base level, CPUs control the max complexity in more detail (and don't contribute to complexity themselves anymore). Closes #522. --- .../li/cil/oc/common/template/RobotTemplate.scala | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/main/scala/li/cil/oc/common/template/RobotTemplate.scala b/src/main/scala/li/cil/oc/common/template/RobotTemplate.scala index b210e9495..9622397b4 100644 --- a/src/main/scala/li/cil/oc/common/template/RobotTemplate.scala +++ b/src/main/scala/li/cil/oc/common/template/RobotTemplate.scala @@ -260,6 +260,7 @@ object RobotTemplate { for (slot <- 1 until inventory.getSizeInventory) { val stack = inventory.getStackInSlot(slot) acc += (Option(api.Driver.driverFor(stack)) match { + case Some(driver: Processor) => 0 // CPUs are exempt, since they control the limit. case Some(driver: UpgradeContainer) => (1 + driver.tier(stack)) * 2 case Some(driver) => 1 + driver.tier(stack) case _ => 0 @@ -270,6 +271,16 @@ object RobotTemplate { private def maxComplexity(inventory: IInventory) = { val caseTier = ItemUtils.caseTier(inventory.getStackInSlot(0)) - if (caseTier >= 0) Settings.robotComplexityByTier(caseTier) else 0 + val cpuTier = (0 until inventory.getSizeInventory).foldRight(0)((slot, acc) => { + val stack = inventory.getStackInSlot(slot) + acc + (api.Driver.driverFor(stack) match { + case processor: Processor => processor.tier(stack) + case _ => 0 + }) + }) + if (caseTier >= Tier.One && cpuTier >= Tier.One) { + Settings.robotComplexityByTier(caseTier) - (caseTier - cpuTier) * 6 + } + else 0 } }