diff --git a/assets/blocks.psd b/assets/blocks.psd index 7d9ae961b..02978682d 100644 Binary files a/assets/blocks.psd and b/assets/blocks.psd differ diff --git a/assets/items.psd b/assets/items.psd index f2eaafd96..ccd0d15d0 100644 Binary files a/assets/items.psd and b/assets/items.psd differ diff --git a/src/main/java/li/cil/oc/api/internal/Microcontroller.java b/src/main/java/li/cil/oc/api/internal/Microcontroller.java new file mode 100644 index 000000000..91477ac55 --- /dev/null +++ b/src/main/java/li/cil/oc/api/internal/Microcontroller.java @@ -0,0 +1,25 @@ +package li.cil.oc.api.internal; + +import li.cil.oc.api.driver.EnvironmentHost; +import li.cil.oc.api.machine.Machine; +import li.cil.oc.api.network.Environment; + +/** + * This interface is implemented as a marker by microcontrollers. + *

+ * This is implemented by microcontroller tile entities. That means you can + * use this to check for microcontrollers by using: + *

+ *     if (tileEntity instanceof Microcontroller) {
+ * 
+ *

+ * The only purpose is to allow identifying tile entities as microcontrollers + * via the API, i.e. without having to link against internal classes. This + * also means that you should not implement this. + */ +public interface Microcontroller extends Environment, EnvironmentHost, Rotatable { + /** + * The machine currently hosted by this computer case. + */ + Machine machine(); +} diff --git a/src/main/resources/application.conf b/src/main/resources/application.conf index fa2f1b7df..3986610f7 100644 --- a/src/main/resources/application.conf +++ b/src/main/resources/application.conf @@ -667,6 +667,20 @@ opencomputers { # The amount of energy pushing blocks with the piston upgrade costs. pistonPush: 20 + + # Energy it costs to re-program an EEPROM. This is deliberately + # expensive, to discourage frequent re-writing of EEPROMs. + eepromWrite: 50 + + # Amount of energy a microcontroller consumes per tick while running. + microcontroller: 0.1 + + # The base energy cost for assembling a microcontroller. + microcontrollerAssemblyBase: 10000 + + # The additional amount of energy required to assemble a + # microcontroller for each point of complexity. + microcontrollerAssemblyComplexity: 10000 } # The rate at which different blocks accept external power. All of these diff --git a/src/main/resources/assets/opencomputers/lang/en_US.lang b/src/main/resources/assets/opencomputers/lang/en_US.lang index 351f48fe2..cac44af0d 100644 --- a/src/main/resources/assets/opencomputers/lang/en_US.lang +++ b/src/main/resources/assets/opencomputers/lang/en_US.lang @@ -19,6 +19,7 @@ tile.oc.geolyzer.name=Geolyzer tile.oc.keyboard.name=Keyboard tile.oc.hologram1.name=Hologram Projector (Tier 1) tile.oc.hologram2.name=Hologram Projector (Tier 2) +tile.oc.microcontroller.name=Microcontroller tile.oc.motionSensor.name=Motion Sensor tile.oc.powerConverter.name=Power Converter tile.oc.powerDistributor.name=Power Distributor @@ -74,6 +75,7 @@ item.oc.Memory5.name=Memory (Tier 3.5) item.oc.Microchip0.name=Microchip (Tier 1) item.oc.Microchip1.name=Microchip (Tier 2) item.oc.Microchip2.name=Microchip (Tier 3) +item.oc.MicrocontrollerCase.name=Microcontroller Case item.oc.NetworkCard.name=Network Card item.oc.NumPad.name=Numeric Keypad item.oc.PrintedCircuitBoard.name=Printed Circuit Board (PCB) @@ -230,6 +232,8 @@ oc:tooltip.MaterialCosts=Hold [§f%s§7] for material costs. oc:tooltip.Materials=Materials: oc:tooltip.Memory=Required to get computers to run. The more you have, the more complex the programs you can run. oc:tooltip.Microchip=The chip formerly known as Integrated Circuit. I have no idea why this works with redstone, but it does. +oc:tooltip.Microcontroller=Microcontrollers are computers boiled down to the essentials. They are intended to take care of very specific tasks, running only a single program that is provided on the EEPROM built into them. +oc:tooltip.MicrocontrollerCase=Base component for building microcontrollers. Place it into an assembler to add further components and assemble a microcontroller. oc:tooltip.MotionSensor=Can detect movement of nearby living beings. Requires clear line-of-sight. oc:tooltip.NetworkCard=Allows distant computers connected by other blocks (such as cable) to communicate by sending messages to each other. oc:tooltip.PowerAcceptor=Energy conversion speed: §f%s/t§7 @@ -299,6 +303,7 @@ item.oc.HardDiskDrive.usage=The §oHard Disk Drives§r are the higher tier stora item.oc.InternetCard.usage=The §oInternet Card§r grants computers access to the internet. It provides ways to perform simple HTTP requests, as well as to open plain TCP client sockets that can be read and written to.[nl][nl]Installing an internet card in a computer will also attach a custom file system that contains a few internet related applications, such as one for downloading and uploading snippets from and to pastebin as well as a wannabe wget clone that allows downloading data from arbitrary HTTP URLs. item.oc.LinkedCard.usage=The §oLinked Card§r is a specialized but advanced version of a network card. It can only operate in pairs, providing a point-to-point communication between the paired cards. In return the distance the cards can communicate over is unlimited. They can even communicate when in different dimensions. item.oc.Memory.usage=§oMemory§r is, like a §oCPU§r, an essential part in all computers. Depending on the CPU's architecture, the memory has a very essential effect on what a computer can and cannot do. For the standard Lua architecture, for example, it controls the actual amount of memory Lua scripts can use. This means that to run larger and more memory-intensive programs, you'll need more RAM. +item.oc.MicrocontrollerCase.usage=The §oMicrocontroller Case§r is the base part when building microcontrollers in the §oAssembler§r. Microcontrollers are very primitive computers. They may only contain a very limited number of components, and are intended to be used in very specific use-cases, such as transforming or reacting to redstone signals, or processing network messages.[nl][nl]They do not have an actual file system. All programming must be done using the EEPROM chip built into them. This chip can be swapped for another one by crafting a microcontroller with the chip to insert. The old EEPROM will be returned to your inventory.[nl][nl]While they also require power to run, they consume very little energy. item.oc.NetworkCard.usage=The §oNetwork Card§r allows computers to send and receive network messages. Such messages (or packets) can be either sent as a broadcast, in which case they will be sent to all nodes in the same subnetwork, or sent to specific target, in which case they will only be received by the node with the specified target address. §oSwitches§r and §oAccess Points§r can be used to bridge multiple subnetworks by relaying messages between the subnetworks they are connected to. It is also possible to send a targeted message if the receiver is in another subnetwork, if the networks are connected via one or more switches. item.oc.RedstoneCard.usage=The §oRedstone Card§r allows computers to read and emit analog redstone signal in adjacent blocks. When an ingoing signal strength changes, a signal is injected into the computer.[nl][nl]If there are any supported mods present that provide bundled redstone facilities, such as RedLogic, Project Red or MineFactory Reloaded, or mods that provide wireless redstone facilities such as WR-CBE and Slimevoid's Wireless mod, a second tier card is available that allows interacting with these systems.[nl][nl]The side provided to the several methods are relative to the orientation of the computer case / robot / server rack. That means when looking at the front of the computer, right is at your left and vice versa. item.oc.Server.usage=§oServers§r are a form of higher tier computer. They can be configured by holding them in the hand and rightclicking - like opening a backpack or ender pouch, for example. After inserting CPU, memory and cards, the server has to be placed inside a §oServer Rack§r. For more information see the server rack entry. diff --git a/src/main/resources/assets/opencomputers/recipes/default.recipes b/src/main/resources/assets/opencomputers/recipes/default.recipes index e6f6c0e8a..ee2809b58 100644 --- a/src/main/resources/assets/opencomputers/recipes/default.recipes +++ b/src/main/resources/assets/opencomputers/recipes/default.recipes @@ -6,6 +6,11 @@ analyzer { ["oc:materialTransistor", nuggetGold, ""] ["oc:materialCircuitBoardPrinted", nuggetGold, ""]] } +microcontrollerCase { + input: [[nuggetIron, "oc:circuitChip1", nuggetIron] + [redstone, chest, redstone] + [nuggetIron, "oc:materialCircuitBoardPrinted", nuggetIron]] +} terminal { input: [[nuggetIron, "oc:solarGeneratorUpgrade", nuggetIron] ["oc:circuitChip3", "oc:screen2", "oc:wlanCard"] diff --git a/src/main/resources/assets/opencomputers/textures/blocks/MicrocontrollerFront.png b/src/main/resources/assets/opencomputers/textures/blocks/MicrocontrollerFront.png new file mode 100644 index 000000000..6814b1946 Binary files /dev/null and b/src/main/resources/assets/opencomputers/textures/blocks/MicrocontrollerFront.png differ diff --git a/src/main/resources/assets/opencomputers/textures/blocks/MicrocontrollerFrontLight.png b/src/main/resources/assets/opencomputers/textures/blocks/MicrocontrollerFrontLight.png new file mode 100644 index 000000000..7a3eb9c28 Binary files /dev/null and b/src/main/resources/assets/opencomputers/textures/blocks/MicrocontrollerFrontLight.png differ diff --git a/src/main/resources/assets/opencomputers/textures/blocks/MicrocontrollerFrontOn.png b/src/main/resources/assets/opencomputers/textures/blocks/MicrocontrollerFrontOn.png new file mode 100644 index 000000000..37e5c6a4a Binary files /dev/null and b/src/main/resources/assets/opencomputers/textures/blocks/MicrocontrollerFrontOn.png differ diff --git a/src/main/resources/assets/opencomputers/textures/blocks/MicrocontrollerSide.png b/src/main/resources/assets/opencomputers/textures/blocks/MicrocontrollerSide.png new file mode 100644 index 000000000..49f24a1d9 Binary files /dev/null and b/src/main/resources/assets/opencomputers/textures/blocks/MicrocontrollerSide.png differ diff --git a/src/main/resources/assets/opencomputers/textures/blocks/MicrocontrollerTop.png b/src/main/resources/assets/opencomputers/textures/blocks/MicrocontrollerTop.png new file mode 100644 index 000000000..5773ae15e Binary files /dev/null and b/src/main/resources/assets/opencomputers/textures/blocks/MicrocontrollerTop.png differ diff --git a/src/main/resources/assets/opencomputers/textures/items/MicrocontrollerCase.png b/src/main/resources/assets/opencomputers/textures/items/MicrocontrollerCase.png new file mode 100644 index 000000000..cda22607c Binary files /dev/null and b/src/main/resources/assets/opencomputers/textures/items/MicrocontrollerCase.png differ diff --git a/src/main/scala/li/cil/oc/Settings.scala b/src/main/scala/li/cil/oc/Settings.scala index 3dd173930..40d35ff3d 100644 --- a/src/main/scala/li/cil/oc/Settings.scala +++ b/src/main/scala/li/cil/oc/Settings.scala @@ -178,6 +178,10 @@ class Settings(val config: Config) { val disassemblerItemCost = config.getDouble("power.cost.disassemblerPerItem") max 0 val chunkloaderCost = config.getDouble("power.cost.chunkloaderCost") max 0 val pistonCost = config.getDouble("power.cost.pistonPush") max 0 + val microcontrollerCost = config.getDouble("power.cost.microcontroller") max 0 + val eepromWriteCost = config.getDouble("power.cost.eepromWrite") max 0 + val microcontrollerBaseCost = config.getDouble("power.cost.microcontrollerAssemblyBase") max 0 + val microcontrollerComplexityCost = config.getDouble("power.cost.microcontrollerAssemblyComplexity") max 0 // power.rate val accessPointRate = config.getDouble("power.rate.accessPoint") max 0 diff --git a/src/main/scala/li/cil/oc/client/Proxy.scala b/src/main/scala/li/cil/oc/client/Proxy.scala index fbd63e85a..5d82aa38a 100644 --- a/src/main/scala/li/cil/oc/client/Proxy.scala +++ b/src/main/scala/li/cil/oc/client/Proxy.scala @@ -55,6 +55,7 @@ private[oc] class Proxy extends CommonProxy { ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.DiskDrive], DiskDriveRenderer) ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.Geolyzer], GeolyzerRenderer) ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.Hologram], HologramRenderer) + ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.Microcontroller], MicrocontrollerRenderer) ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.PowerDistributor], PowerDistributorRenderer) ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.Raid], RaidRenderer) ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.ServerRack], ServerRackRenderer) diff --git a/src/main/scala/li/cil/oc/client/Textures.scala b/src/main/scala/li/cil/oc/client/Textures.scala index 9de6c64f7..fd951446a 100644 --- a/src/main/scala/li/cil/oc/client/Textures.scala +++ b/src/main/scala/li/cil/oc/client/Textures.scala @@ -37,6 +37,8 @@ object Textures { val blockCaseFrontActivity = new ResourceLocation(Settings.resourceDomain, "textures/blocks/CaseFrontActivity.png") val blockDiskDriveFrontActivity = new ResourceLocation(Settings.resourceDomain, "textures/blocks/DiskDriveFrontActivity.png") val blockHologram = new ResourceLocation(Settings.resourceDomain, "textures/blocks/HologramEffect.png") + val blockMicrocontrollerFrontLight = new ResourceLocation(Settings.resourceDomain, "textures/blocks/MicrocontrollerFrontLight.png") + val blockMicrocontrollerFrontOn = new ResourceLocation(Settings.resourceDomain, "textures/blocks/MicrocontrollerFrontOn.png") val blockRackFrontOn = new ResourceLocation(Settings.resourceDomain, "textures/blocks/ServerRackFrontOn.png") val blockRackFrontActivity = new ResourceLocation(Settings.resourceDomain, "textures/blocks/ServerRackFrontActivity.png") val blockRaidFrontError = new ResourceLocation(Settings.resourceDomain, "textures/blocks/RaidFrontError.png") @@ -105,7 +107,10 @@ object Textures { tm.bindTexture(guiSlot) tm.bindTexture(blockCaseFrontOn) + tm.bindTexture(blockCaseFrontActivity) tm.bindTexture(blockHologram) + tm.bindTexture(blockMicrocontrollerFrontLight) + tm.bindTexture(blockMicrocontrollerFrontOn) tm.bindTexture(blockRackFrontOn) tm.bindTexture(blockRobot) tm.bindTexture(blockScreenUpIndicator) diff --git a/src/main/scala/li/cil/oc/client/renderer/tileentity/MicrocontrollerRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/tileentity/MicrocontrollerRenderer.scala new file mode 100644 index 000000000..2323f8ac3 --- /dev/null +++ b/src/main/scala/li/cil/oc/client/renderer/tileentity/MicrocontrollerRenderer.scala @@ -0,0 +1,62 @@ +package li.cil.oc.client.renderer.tileentity + +import li.cil.oc.client.Textures +import li.cil.oc.common.tileentity.Microcontroller +import li.cil.oc.util.RenderState +import net.minecraft.client.renderer.Tessellator +import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer +import net.minecraft.tileentity.TileEntity +import net.minecraftforge.common.util.ForgeDirection +import org.lwjgl.opengl.GL11 + +object MicrocontrollerRenderer extends TileEntitySpecialRenderer { + override def renderTileEntityAt(tileEntity: TileEntity, x: Double, y: Double, z: Double, f: Float) { + RenderState.checkError(getClass.getName + ".renderTileEntityAt: entering (aka: wasntme)") + + val mcu = tileEntity.asInstanceOf[Microcontroller] + GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS) + + RenderState.disableLighting() + RenderState.makeItBlend() + RenderState.setBlendAlpha(1) + + GL11.glPushMatrix() + + GL11.glTranslated(x + 0.5, y + 0.5, z + 0.5) + + mcu.yaw match { + case ForgeDirection.WEST => GL11.glRotatef(-90, 0, 1, 0) + case ForgeDirection.NORTH => GL11.glRotatef(180, 0, 1, 0) + case ForgeDirection.EAST => GL11.glRotatef(90, 0, 1, 0) + case _ => // No yaw. + } + + GL11.glTranslated(-0.5, 0.5, 0.505) + GL11.glScalef(1, -1, 1) + + bindTexture(Textures.blockMicrocontrollerFrontLight) + val t = Tessellator.instance + t.startDrawingQuads() + t.addVertexWithUV(0, 1, 0, 0, 1) + t.addVertexWithUV(1, 1, 0, 1, 1) + t.addVertexWithUV(1, 0, 0, 1, 0) + t.addVertexWithUV(0, 0, 0, 0, 0) + t.draw() + + if (mcu.isRunning) { + bindTexture(Textures.blockMicrocontrollerFrontOn) + val t = Tessellator.instance + t.startDrawingQuads() + t.addVertexWithUV(0, 1, 0, 0, 1) + t.addVertexWithUV(1, 1, 0, 1, 1) + t.addVertexWithUV(1, 0, 0, 1, 0) + t.addVertexWithUV(0, 0, 0, 0, 0) + t.draw() + } + + GL11.glPopMatrix() + GL11.glPopAttrib() + + RenderState.checkError(getClass.getName + ".renderTileEntityAt: leaving") + } +} \ No newline at end of file diff --git a/src/main/scala/li/cil/oc/common/EventHandler.scala b/src/main/scala/li/cil/oc/common/EventHandler.scala index e8bb02479..c4485169d 100644 --- a/src/main/scala/li/cil/oc/common/EventHandler.scala +++ b/src/main/scala/li/cil/oc/common/EventHandler.scala @@ -8,6 +8,7 @@ import cpw.mods.fml.common.gameevent.TickEvent.ServerTickEvent import cpw.mods.fml.common.network.FMLNetworkEvent.ClientConnectedToServerEvent import li.cil.oc._ import li.cil.oc.api.Network +import li.cil.oc.api.detail.ItemInfo import li.cil.oc.client.renderer.PetRenderer import li.cil.oc.client.{PacketSender => ClientPacketSender} import li.cil.oc.common.tileentity.traits.power @@ -20,6 +21,7 @@ import li.cil.oc.util.SideTracker import li.cil.oc.util.UpdateCheck import net.minecraft.client.Minecraft import net.minecraft.entity.player.EntityPlayerMP +import net.minecraft.item.ItemStack import net.minecraft.server.MinecraftServer import net.minecraft.tileentity.TileEntity import net.minecraftforge.common.MinecraftForge @@ -145,23 +147,36 @@ object EventHandler { ClientPacketSender.sendPetVisibility() } + lazy val eeprom = api.Items.get("eeprom") + lazy val mcu = api.Items.get("microcontroller") lazy val navigationUpgrade = api.Items.get("navigationUpgrade") @SubscribeEvent def onCrafting(e: ItemCraftedEvent) = { - if (api.Items.get(e.crafting) == navigationUpgrade) { - Option(api.Driver.driverFor(e.crafting)).foreach(driver => - for (i <- 0 until e.craftMatrix.getSizeInventory) { - val stack = e.craftMatrix.getStackInSlot(i) - if (stack != null && api.Items.get(stack) == navigationUpgrade) { - // Restore the map currently used in the upgrade. - val nbt = driver.dataTag(stack) - val map = ItemUtils.loadStack(nbt.getCompoundTag(Settings.namespace + "map")) - if (map != null && !e.player.inventory.addItemStackToInventory(map)) { - e.player.dropPlayerItemWithRandomChoice(map, false) - } - } - }) + recraft(e, navigationUpgrade, stack => { + // Restore the map currently used in the upgrade. + Option(api.Driver.driverFor(e.crafting)) match { + case Some(driver) => Option(ItemUtils.loadStack(driver.dataTag(stack).getCompoundTag(Settings.namespace + "map"))) + case _ => None + } + }) + + recraft(e, mcu, stack => { + // Restore EEPROM currently used in microcontroller. + new ItemUtils.MicrocontrollerData(stack).components.find(api.Items.get(_) == eeprom) + }) + } + + private def recraft(e: ItemCraftedEvent, item: ItemInfo, callback: ItemStack => Option[ItemStack]): Unit = { + if (api.Items.get(e.crafting) == item) { + for (slot <- 0 until e.craftMatrix.getSizeInventory) { + val stack = e.craftMatrix.getStackInSlot(slot) + if (api.Items.get(stack) == item) { + callback(stack).foreach(extra => if (!e.player.inventory.addItemStackToInventory(extra)) { + e.player.dropPlayerItemWithRandomChoice(extra, false) + }) + } + } } } diff --git a/src/main/scala/li/cil/oc/common/block/Microcontroller.scala b/src/main/scala/li/cil/oc/common/block/Microcontroller.scala new file mode 100644 index 000000000..3de72e72d --- /dev/null +++ b/src/main/scala/li/cil/oc/common/block/Microcontroller.scala @@ -0,0 +1,99 @@ +package li.cil.oc.common.block + +import java.util + +import li.cil.oc.OpenComputers +import li.cil.oc.Settings +import li.cil.oc.client.KeyBindings +import li.cil.oc.common.GuiType +import li.cil.oc.common.Tier +import li.cil.oc.common.tileentity +import li.cil.oc.integration.util.Wrench +import li.cil.oc.util.ItemUtils +import net.minecraft.entity.EntityLivingBase +import net.minecraft.entity.player.EntityPlayer +import net.minecraft.item.ItemStack +import net.minecraft.util.MovingObjectPosition +import net.minecraft.world.World +import net.minecraftforge.common.util.ForgeDirection + +class Microcontroller extends RedstoneAware with traits.PowerAcceptor { + override protected def customTextures = Array( + Some("MicrocontrollerTop"), + Some("MicrocontrollerTop"), + Some("MicrocontrollerSide"), + Some("MicrocontrollerFront"), + Some("MicrocontrollerSide"), + Some("MicrocontrollerSide") + ) + + // ----------------------------------------------------------------------- // + + override def getPickBlock(target: MovingObjectPosition, world: World, x: Int, y: Int, z: Int) = + world.getTileEntity(x, y, z) match { + case mcu: tileentity.Microcontroller => mcu.info.copyItemStack() + case _ => null + } + + // Custom drop logic for NBT tagged item stack. + override def getDrops(world: World, x: Int, y: Int, z: Int, metadata: Int, fortune: Int) = new java.util.ArrayList[ItemStack]() + + override def onBlockPreDestroy(world: World, x: Int, y: Int, z: Int, metadata: Int) {} + + // ----------------------------------------------------------------------- // + + override protected def tooltipTail(metadata: Int, stack: ItemStack, player: EntityPlayer, tooltip: util.List[String], advanced: Boolean) { + super.tooltipTail(metadata, stack, player, tooltip, advanced) + if (KeyBindings.showExtendedTooltips) { + val info = new ItemUtils.MicrocontrollerData(stack) + for (component <- info.components) { + tooltip.add("- " + component.getDisplayName) + } + } + } + + // ----------------------------------------------------------------------- // + + override def energyThroughput = Settings.get.caseRate(Tier.One) + + override def createTileEntity(world: World, metadata: Int) = new tileentity.Microcontroller() + + // ----------------------------------------------------------------------- // + + override def onBlockActivated(world: World, x: Int, y: Int, z: Int, player: EntityPlayer, + side: ForgeDirection, hitX: Float, hitY: Float, hitZ: Float) = { + if (!player.isSneaking && !Wrench.holdsApplicableWrench(player, x, y, z)) { + if (!world.isRemote) { + world.getTileEntity(x, y, z) match { + case mcu: tileentity.Microcontroller => + if (mcu.machine.isRunning) mcu.machine.stop() + else mcu.machine.start() + case _ => + } + } + true + } + else false + } + + override def onBlockPlacedBy(world: World, x: Int, y: Int, z: Int, entity: EntityLivingBase, stack: ItemStack) { + super.onBlockPlacedBy(world, x, y, z, entity, stack) + if (!world.isRemote) world.getTileEntity(x, y, z) match { + case mcu: tileentity.Microcontroller => + mcu.info.load(stack) + case _ => + } + } + + override def removedByPlayer(world: World, player: EntityPlayer, x: Int, y: Int, z: Int, willHarvest: Boolean): Boolean = { + if (!world.isRemote) { + world.getTileEntity(x, y, z) match { + case mcu: tileentity.Microcontroller => + mcu.saveComponents() + dropBlockAsItem(world, x, y, z, mcu.info.createItemStack()) + case _ => + } + } + super.removedByPlayer(world, player, x, y, z, willHarvest) + } +} diff --git a/src/main/scala/li/cil/oc/common/init/Blocks.scala b/src/main/scala/li/cil/oc/common/init/Blocks.scala index 096b92809..c3e57d629 100644 --- a/src/main/scala/li/cil/oc/common/init/Blocks.scala +++ b/src/main/scala/li/cil/oc/common/init/Blocks.scala @@ -21,6 +21,7 @@ object Blocks { GameRegistry.registerTileEntity(classOf[tileentity.Keyboard], Settings.namespace + "keyboard") GameRegistry.registerTileEntity(classOf[tileentity.Hologram], Settings.namespace + "hologram") GameRegistry.registerTileEntity(classOf[tileentity.Geolyzer], Settings.namespace + "geolyzer") + GameRegistry.registerTileEntity(classOf[tileentity.Microcontroller], Settings.namespace + "microcontroller") GameRegistry.registerTileEntity(classOf[tileentity.MotionSensor], Settings.namespace + "motion_sensor") GameRegistry.registerTileEntity(classOf[tileentity.PowerConverter], Settings.namespace + "power_converter") GameRegistry.registerTileEntity(classOf[tileentity.PowerDistributor], Settings.namespace + "power_distributor") @@ -33,6 +34,7 @@ object Blocks { // These are purely for converting existing blocks in delegator format to the new, // one block type per block format. + // TODO Remove in 1.5 GameRegistry.registerBlock(new DelegatorConverter(), classOf[DelegatorConverter.Item], "simple") GameRegistry.registerBlock(new DelegatorConverter(), classOf[DelegatorConverter.Item], "simple_redstone") GameRegistry.registerBlock(new DelegatorConverter(), classOf[DelegatorConverter.Item], "special") @@ -86,5 +88,6 @@ object Blocks { // v1.4.2 Recipes.addBlock(new Raid(), "raid", "oc:raid") + Items.registerBlock(new Microcontroller(), "microcontroller") } } diff --git a/src/main/scala/li/cil/oc/common/init/Items.scala b/src/main/scala/li/cil/oc/common/init/Items.scala index afa1c6684..1317caff7 100644 --- a/src/main/scala/li/cil/oc/common/init/Items.scala +++ b/src/main/scala/li/cil/oc/common/init/Items.scala @@ -307,9 +307,9 @@ object Items extends ItemAPI { if (Mods.AppliedEnergistics2.isAvailable) { Recipes.addItem(new item.AppliedEnergisticsP2PTunnel(), "appengTunnel") } - val eeprom = new item.EEPROM() Recipes.addItem(eeprom, "eeprom", "oc:eeprom") Recipes.addRecipe(createLuaBios(), "luaBios") + Recipes.addMultiItem(new item.MicrocontrollerCase(multi), "microcontrollerCase", "oc:microcontrollerCase") } } diff --git a/src/main/scala/li/cil/oc/common/item/MicrocontrollerCase.scala b/src/main/scala/li/cil/oc/common/item/MicrocontrollerCase.scala new file mode 100644 index 000000000..480879db7 --- /dev/null +++ b/src/main/scala/li/cil/oc/common/item/MicrocontrollerCase.scala @@ -0,0 +1,3 @@ +package li.cil.oc.common.item + +class MicrocontrollerCase(val parent: Delegator) extends Delegate diff --git a/src/main/scala/li/cil/oc/common/recipe/ExtendedRecipe.scala b/src/main/scala/li/cil/oc/common/recipe/ExtendedRecipe.scala index 62bb84589..fdd5698ba 100644 --- a/src/main/scala/li/cil/oc/common/recipe/ExtendedRecipe.scala +++ b/src/main/scala/li/cil/oc/common/recipe/ExtendedRecipe.scala @@ -7,6 +7,7 @@ import li.cil.oc.api import li.cil.oc.integration.Mods import li.cil.oc.util.Color import li.cil.oc.util.ExtendedNBT._ +import li.cil.oc.util.ItemUtils import li.cil.oc.util.SideTracker import net.minecraft.init.Blocks import net.minecraft.inventory.InventoryCrafting @@ -16,6 +17,8 @@ import net.minecraft.nbt.NBTTagCompound import scala.collection.convert.WrapAsScala._ object ExtendedRecipe { + private lazy val eeprom = api.Items.get("eeprom") + private lazy val mcu = api.Items.get("microcontroller") private lazy val navigationUpgrade = api.Items.get("navigationUpgrade") private lazy val linkedCard = api.Items.get("linkedCard") private lazy val floppy = api.Items.get("floppy") @@ -30,8 +33,8 @@ object ExtendedRecipe { def addNBTToResult(craftedStack: ItemStack, inventory: InventoryCrafting): ItemStack = { if (api.Items.get(craftedStack) == navigationUpgrade) { Option(api.Driver.driverFor(craftedStack)).foreach(driver => - for (i <- 0 until inventory.getSizeInventory) { - val stack = inventory.getStackInSlot(i) + for (slot <- 0 until inventory.getSizeInventory) { + val stack = inventory.getStackInSlot(slot) if (stack != null && stack.getItem == net.minecraft.init.Items.filled_map) { // Store information of the map used for crafting in the result. val nbt = driver.dataTag(craftedStack) @@ -55,8 +58,8 @@ object ExtendedRecipe { craftedStack.setTagCompound(new NBTTagCompound()) } val nbt = craftedStack.getTagCompound - for (i <- 0 until inventory.getSizeInventory) { - val stack = inventory.getStackInSlot(i) + for (slot <- 0 until inventory.getSizeInventory) { + val stack = inventory.getStackInSlot(slot) if (stack != null) { if (api.Items.get(stack) == floppy && stack.hasTagCompound) { val oldData = stack.getTagCompound @@ -68,6 +71,29 @@ object ExtendedRecipe { } } + if (api.Items.get(craftedStack) == mcu) { + // Find old Microcontroller. + (0 until inventory.getSizeInventory).map(inventory.getStackInSlot).find(api.Items.get(_) == mcu) match { + case Some(oldMcu) => + val data = new ItemUtils.MicrocontrollerData(oldMcu) + + // Remove old EEPROM. + val oldRom = data.components.filter(api.Items.get(_) == eeprom) + data.components = data.components.diff(oldRom) + + // Insert new EEPROM. + for (slot <- 0 until inventory.getSizeInventory) { + val stack = inventory.getStackInSlot(slot) + if (api.Items.get(stack) == eeprom) { + data.components :+= stack + } + } + + data.save(craftedStack) + case _ => + } + } + craftedStack } diff --git a/src/main/scala/li/cil/oc/common/recipe/Recipes.scala b/src/main/scala/li/cil/oc/common/recipe/Recipes.scala index 6e0030965..dcbc504b9 100644 --- a/src/main/scala/li/cil/oc/common/recipe/Recipes.scala +++ b/src/main/scala/li/cil/oc/common/recipe/Recipes.scala @@ -132,6 +132,10 @@ object Recipes { result.setTagCompound(tag) GameRegistry.addRecipe(new ExtendedShapelessOreRecipe(result, floppy, dye)) } + + // Microcontroller recrafting. + val mcu = api.Items.get("microcontroller").createItemStack(1) + GameRegistry.addRecipe(new ExtendedShapelessOreRecipe(mcu, mcu, api.Items.get("eeprom").createItemStack(1))) } catch { case e: Throwable => OpenComputers.log.error("Error parsing recipes, you may not be able to craft any items from this mod!", e) diff --git a/src/main/scala/li/cil/oc/common/template/MicrocontrollerTemplate.scala b/src/main/scala/li/cil/oc/common/template/MicrocontrollerTemplate.scala new file mode 100644 index 000000000..c604e88f5 --- /dev/null +++ b/src/main/scala/li/cil/oc/common/template/MicrocontrollerTemplate.scala @@ -0,0 +1,67 @@ +package li.cil.oc.common.template + +import cpw.mods.fml.common.event.FMLInterModComms +import li.cil.oc.Settings +import li.cil.oc.api +import li.cil.oc.api.internal +import li.cil.oc.common.Slot +import li.cil.oc.common.Tier +import li.cil.oc.common.template.TabletTemplate.hasComponent +import li.cil.oc.common.template.TabletTemplate.hasFileSystem +import li.cil.oc.util.ExtendedNBT._ +import li.cil.oc.util.ItemUtils +import net.minecraft.inventory.IInventory +import net.minecraft.item.ItemStack +import net.minecraft.nbt.NBTTagCompound +import net.minecraft.nbt.NBTTagList + +object MicrocontrollerTemplate extends Template { + override protected val suggestedComponents = Array( + "BIOS" -> hasComponent("eeprom") _) + + override protected def hostClass = classOf[internal.Microcontroller] + + def select(stack: ItemStack) = api.Items.get(stack) == api.Items.get("microcontrollerCase") + + def validate(inventory: IInventory): Array[AnyRef] = validateComputer(inventory) + + def assemble(inventory: IInventory) = { + val items = (0 until inventory.getSizeInventory).map(inventory.getStackInSlot) + val data = new ItemUtils.MicrocontrollerData() + data.components = items.drop(1).filter(_ != null).toArray + val stack = api.Items.get("microcontroller").createItemStack(1) + data.save(stack) + val energy = Settings.get.microcontrollerBaseCost + complexity(inventory) * Settings.get.microcontrollerComplexityCost + + Array(stack, double2Double(energy)) + } + + def register() { + val nbt = new NBTTagCompound() + nbt.setString("name", "Microcontroller") + nbt.setString("select", "li.cil.oc.common.template.MicrocontrollerTemplate.select") + nbt.setString("validate", "li.cil.oc.common.template.MicrocontrollerTemplate.validate") + nbt.setString("assemble", "li.cil.oc.common.template.MicrocontrollerTemplate.assemble") + nbt.setString("hostClass", "li.cil.oc.api.internal.Microcontroller") + + val upgradeSlots = new NBTTagList() + upgradeSlots.appendTag(Map("tier" -> Tier.Any)) + nbt.setTag("upgradeSlots", upgradeSlots) + + val componentSlots = new NBTTagList() + componentSlots.appendTag(Map("type" -> Slot.Card, "tier" -> Tier.One)) + componentSlots.appendTag(Map("type" -> Slot.Card, "tier" -> Tier.One)) + componentSlots.appendTag(new NBTTagCompound()) + componentSlots.appendTag(Map("type" -> Slot.CPU, "tier" -> Tier.One)) + componentSlots.appendTag(Map("type" -> Slot.Memory, "tier" -> Tier.One)) + componentSlots.appendTag(new NBTTagCompound()) + componentSlots.appendTag(Map("type" -> Slot.EEPROM, "tier" -> Tier.Any)) + nbt.setTag("componentSlots", componentSlots) + + FMLInterModComms.sendMessage("OpenComputers", "registerAssemblerTemplate", nbt) + } + + override protected def maxComplexity(inventory: IInventory) = 4 + + override protected def caseTier(inventory: IInventory) = if (select(inventory.getStackInSlot(0))) Tier.One else Tier.None +} diff --git a/src/main/scala/li/cil/oc/common/template/Template.scala b/src/main/scala/li/cil/oc/common/template/Template.scala index f9ac618a4..eaa2f4680 100644 --- a/src/main/scala/li/cil/oc/common/template/Template.scala +++ b/src/main/scala/li/cil/oc/common/template/Template.scala @@ -93,7 +93,7 @@ abstract class Template { acc += (Option(api.Driver.driverFor(stack, hostClass)) match { case Some(driver: Processor) => 0 // CPUs are exempt, since they control the limit. case Some(driver: Container) => (1 + driver.tier(stack)) * 2 - case Some(driver) => 1 + driver.tier(stack) + case Some(driver) if driver.slot(stack) != Slot.EEPROM => 1 + driver.tier(stack) case _ => 0 }) } diff --git a/src/main/scala/li/cil/oc/common/tileentity/Disassembler.scala b/src/main/scala/li/cil/oc/common/tileentity/Disassembler.scala index 5024bc20d..07a0a63f3 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Disassembler.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Disassembler.scala @@ -95,9 +95,8 @@ class Disassembler extends traits.Environment with traits.PowerAcceptor with tra else if (api.Items.get(stack) == api.Items.get("server2")) enqueueServer(stack, 1) else if (api.Items.get(stack) == api.Items.get("server3")) enqueueServer(stack, 2) else if (api.Items.get(stack) == api.Items.get("tablet")) enqueueTablet(stack) - else if (api.Items.get(stack) == api.Items.get("navigationUpgrade")) { - enqueueNavigationUpgrade(stack) - } + else if (api.Items.get(stack) == api.Items.get("microcontroller")) enqueueMicrocontroller(stack) + else if (api.Items.get(stack) == api.Items.get("navigationUpgrade")) enqueueNavigationUpgrade(stack) else queue ++= getIngredients(stack) totalRequiredEnergy = queue.size * Settings.get.disassemblerItemCost } @@ -136,6 +135,12 @@ class Disassembler extends traits.Environment with traits.PowerAcceptor with tra node.changeBuffer(info.energy) } + private def enqueueMicrocontroller(mcu: ItemStack) { + val info = new ItemUtils.MicrocontrollerData(mcu) + queue += api.Items.get("microcontrollerCase").createItemStack(1) + queue ++= info.components + } + private def enqueueNavigationUpgrade(stack: ItemStack) { val info = new ItemUtils.NavigationUpgradeData(stack) val parts = getIngredients(stack) @@ -243,7 +248,8 @@ class Disassembler extends traits.Environment with traits.PowerAcceptor with tra override def getInventoryStackLimit = 64 override def isItemValidForSlot(i: Int, stack: ItemStack) = - api.Items.get(stack) == api.Items.get("robot") || + ((Settings.get.disassembleAllTheThings || api.Items.get(stack) != null) && getIngredients(stack).nonEmpty) || + api.Items.get(stack) == api.Items.get("robot") || api.Items.get(stack) == api.Items.get("tablet") || - ((Settings.get.disassembleAllTheThings || api.Items.get(stack) != null) && getIngredients(stack).nonEmpty) + api.Items.get(stack) == api.Items.get("microcontroller") } diff --git a/src/main/scala/li/cil/oc/common/tileentity/Microcontroller.scala b/src/main/scala/li/cil/oc/common/tileentity/Microcontroller.scala new file mode 100644 index 000000000..37dfe4ca8 --- /dev/null +++ b/src/main/scala/li/cil/oc/common/tileentity/Microcontroller.scala @@ -0,0 +1,176 @@ +package li.cil.oc.common.tileentity + +import cpw.mods.fml.relauncher.Side +import cpw.mods.fml.relauncher.SideOnly +import li.cil.oc.Settings +import li.cil.oc.api +import li.cil.oc.api.Driver +import li.cil.oc.api.driver.item.Memory +import li.cil.oc.api.driver.item.Processor +import li.cil.oc.api.internal +import li.cil.oc.api.machine.Architecture +import li.cil.oc.api.machine.Arguments +import li.cil.oc.api.machine.Callback +import li.cil.oc.api.machine.Context +import li.cil.oc.api.network.Connector +import li.cil.oc.api.network.Message +import li.cil.oc.api.network.Node +import li.cil.oc.api.network.Visibility +import li.cil.oc.common.Slot +import li.cil.oc.common.Tier +import li.cil.oc.util.ExtendedNBT._ +import li.cil.oc.util.ItemUtils +import net.minecraft.item.ItemStack +import net.minecraft.nbt.NBTTagCompound +import net.minecraftforge.common.util.ForgeDirection + +class Microcontroller extends traits.PowerAcceptor with traits.Computer with internal.Microcontroller { + val info = new ItemUtils.MicrocontrollerData() + + override val node = api.Network.newNode(this, Visibility.Network). + withComponent("microcontroller"). + withConnector(). + create() + + private val snooperNode = api.Network.newNode(this, Visibility.Network).create() + + override protected def runSound = None // Microcontrollers are silent. + + // ----------------------------------------------------------------------- // + + @SideOnly(Side.CLIENT) + override protected def hasConnector(side: ForgeDirection) = side != facing + + override protected def connector(side: ForgeDirection) = Option(if (side != facing && machine != null) machine.node.asInstanceOf[Connector] else null) + + override protected def energyThroughput = Settings.get.caseRate(Tier.One) + + override def getWorld = world + + // ----------------------------------------------------------------------- // + + override def cpuArchitecture: Class[_ <: Architecture] = info.components.map(stack => (stack, Driver.driverFor(stack, getClass))).collectFirst { + case (stack, driver: Processor) if driver.slot(stack) == Slot.CPU => driver.architecture(stack) + }.orNull + + override def callBudget = info.components.foldLeft(0.0)((sum, item) => sum + (Option(item) match { + case Some(stack) => Option(Driver.driverFor(stack, getClass)) match { + case Some(driver: Processor) if driver.slot(stack) == Slot.CPU => Settings.get.callBudgets(driver.tier(stack)) + case _ => 0 + } + case _ => 0 + })) + + override def installedMemory = info.components.foldLeft(0)((sum, item) => sum + (Option(item) match { + case Some(stack) => Option(Driver.driverFor(stack, getClass)) match { + case Some(driver: Memory) => driver.amount(stack) + case _ => 0 + } + case _ => 0 + })) + + override def componentSlot(address: String) = components.indexWhere(_.exists(env => env.node != null && env.node.address == address)) + + def maxComponents = 32 + + // ----------------------------------------------------------------------- // + + @Callback(doc = """function():boolean -- Starts the microcontroller. Returns true if the state changed.""") + def start(context: Context, args: Arguments): Array[AnyRef] = + result(!machine.isPaused && machine.start()) + + @Callback(doc = """function():boolean -- Stops the microcontroller. Returns true if the state changed.""") + def stop(context: Context, args: Arguments): Array[AnyRef] = + result(machine.stop()) + + @Callback(direct = true, doc = """function():boolean -- Returns whether the microcontroller is running.""") + def isRunning(context: Context, args: Arguments): Array[AnyRef] = + result(machine.isRunning) + + @Callback(direct = true, doc = """function():string -- Returns the reason the microcontroller crashed, if applicable.""") + def lastError(context: Context, args: Arguments): Array[AnyRef] = + result(machine.lastError) + + // ----------------------------------------------------------------------- // + + override def canUpdate = isServer + + override def updateEntity() { + super.updateEntity() + + // Pump energy into the internal network. + if (world.getTotalWorldTime % Settings.get.tickFrequency == 0) { + machine.node match { + case connector: Connector => + val demand = connector.globalBufferSize - connector.globalBuffer + val available = demand + node.changeBuffer(-demand) + connector.changeBuffer(available) + case _ => + } + } + } + + // ----------------------------------------------------------------------- // + + override def onConnect(node: Node) { + if (node == this.node) { + api.Network.joinNewNetwork(machine.node) + machine.node.connect(snooperNode) + machine.setCostPerTick(Settings.get.microcontrollerCost) + } + super.onConnect(node) + } + + override def onMessage(message: Message) { + super.onMessage(message) + if (message.name == "network.message") { + if (message.source.network == snooperNode.network) + node.sendToReachable(message.name, message.data: _*) + else + snooperNode.sendToReachable(message.name, message.data: _*) + } + } + + override protected def connectItemNode(node: Node) { + if (machine.node != null && node != null) { + machine.node.connect(node) + } + } + + override def readFromNBT(nbt: NBTTagCompound) { + // Load info before inventory and such, to avoid initializing components + // to empty inventory. + info.load(nbt.getCompoundTag(Settings.namespace + "info")) + super.readFromNBT(nbt) + } + + override def writeToNBT(nbt: NBTTagCompound) { + super.writeToNBT(nbt) + nbt.setNewCompoundTag(Settings.namespace + "info", info.save) + } + + // ----------------------------------------------------------------------- // + + @SideOnly(Side.CLIENT) override + def readFromNBTForClient(nbt: NBTTagCompound) { + info.load(nbt.getCompoundTag("info")) + super.readFromNBTForClient(nbt) + } + + override def writeToNBTForClient(nbt: NBTTagCompound) { + super.writeToNBTForClient(nbt) + nbt.setNewCompoundTag("info", info.save) + } + + override lazy val items = info.components.map(Option(_)) + + override def getSizeInventory = info.components.length + + override def isItemValidForSlot(slot: Int, stack: ItemStack) = false + + // Nope. + override def setInventorySlotContents(slot: Int, stack: ItemStack) {} + + // Nope. + override def decrStackSize(slot: Int, amount: Int) = null +} diff --git a/src/main/scala/li/cil/oc/common/tileentity/traits/Computer.scala b/src/main/scala/li/cil/oc/common/tileentity/traits/Computer.scala index 03b009f31..00d737428 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/traits/Computer.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/traits/Computer.scala @@ -41,6 +41,8 @@ trait Computer extends Environment with ComponentInventory with Rotatable with B private val _users = mutable.Set.empty[String] + protected def runSound = Option("computer_running") + // ----------------------------------------------------------------------- // def canInteract(player: String) = @@ -53,8 +55,10 @@ trait Computer extends Environment with ComponentInventory with Rotatable with B def setRunning(value: Boolean): Unit = if (value != _isRunning) { _isRunning = value world.markBlockForUpdate(x, y, z) - if (_isRunning) Sound.startLoop(this, "computer_running", 0.5f, 50 + world.rand.nextInt(50)) - else Sound.stopLoop(this) + runSound.foreach(sound => + if (_isRunning) Sound.startLoop(this, sound, 0.5f, 50 + world.rand.nextInt(50)) + else Sound.stopLoop(this) + ) } @SideOnly(Side.CLIENT) @@ -158,7 +162,7 @@ trait Computer extends Environment with ComponentInventory with Rotatable with B _isRunning = nbt.getBoolean("isRunning") _users.clear() _users ++= nbt.getTagList("users", NBT.TAG_STRING).map((tag: NBTTagString) => tag.func_150285_a_()) - if (_isRunning) Sound.startLoop(this, "computer_running", 0.5f, 1000 + world.rand.nextInt(2000)) + if (_isRunning) runSound.foreach(sound => Sound.startLoop(this, sound, 0.5f, 1000 + world.rand.nextInt(2000))) } override def writeToNBTForClient(nbt: NBTTagCompound) { diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/DriverCPU.scala b/src/main/scala/li/cil/oc/integration/opencomputers/DriverCPU.scala index 8d053b1ba..19ac79f0a 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/DriverCPU.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverCPU.scala @@ -1,11 +1,15 @@ package li.cil.oc.integration.opencomputers -import li.cil.oc.{OpenComputers, Settings, api} +import li.cil.oc.OpenComputers +import li.cil.oc.Settings +import li.cil.oc.api import li.cil.oc.api.driver.EnvironmentHost import li.cil.oc.api.driver.item.Processor import li.cil.oc.api.machine.Architecture +import li.cil.oc.common.Slot +import li.cil.oc.common.Tier import li.cil.oc.common.init.Items -import li.cil.oc.common.{Slot, Tier, item} +import li.cil.oc.common.item import net.minecraft.item.ItemStack import scala.collection.convert.WrapAsScala._ diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/DriverComponentBus.scala b/src/main/scala/li/cil/oc/integration/opencomputers/DriverComponentBus.scala index 2e633b2f0..2e358b76c 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/DriverComponentBus.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverComponentBus.scala @@ -1,10 +1,13 @@ package li.cil.oc.integration.opencomputers -import li.cil.oc.{Settings, api} +import li.cil.oc.Settings +import li.cil.oc.api import li.cil.oc.api.driver.EnvironmentHost import li.cil.oc.api.driver.item.Processor +import li.cil.oc.common.Slot +import li.cil.oc.common.Tier import li.cil.oc.common.init.Items -import li.cil.oc.common.{Slot, Tier, item} +import li.cil.oc.common.item import net.minecraft.item.ItemStack object DriverComponentBus extends Item with Processor { diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/DriverContainerCard.scala b/src/main/scala/li/cil/oc/integration/opencomputers/DriverContainerCard.scala index 5de89174f..3ff32b1b7 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/DriverContainerCard.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverContainerCard.scala @@ -3,8 +3,10 @@ package li.cil.oc.integration.opencomputers import li.cil.oc.api import li.cil.oc.api.driver.EnvironmentHost import li.cil.oc.api.driver.item.Container +import li.cil.oc.common.Slot +import li.cil.oc.common.Tier import li.cil.oc.common.init.Items -import li.cil.oc.common.{Slot, Tier, item} +import li.cil.oc.common.item import net.minecraft.item.ItemStack object DriverContainerCard extends Item with Container { diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/DriverContainerUpgrade.scala b/src/main/scala/li/cil/oc/integration/opencomputers/DriverContainerUpgrade.scala index 3b879fea5..f666eb849 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/DriverContainerUpgrade.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverContainerUpgrade.scala @@ -3,8 +3,10 @@ package li.cil.oc.integration.opencomputers import li.cil.oc.api import li.cil.oc.api.driver.EnvironmentHost import li.cil.oc.api.driver.item.Container +import li.cil.oc.common.Slot +import li.cil.oc.common.Tier import li.cil.oc.common.init.Items -import li.cil.oc.common.{Slot, Tier, item} +import li.cil.oc.common.item import net.minecraft.item.ItemStack object DriverContainerUpgrade extends Item with Container { diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/DriverEEPROM.scala b/src/main/scala/li/cil/oc/integration/opencomputers/DriverEEPROM.scala index 9f562a7b8..5f333d074 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/DriverEEPROM.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverEEPROM.scala @@ -14,7 +14,7 @@ object DriverEEPROM extends Item with HostAware with EnvironmentAware { isOneOf(stack, api.Items.get("eeprom")) override def worksWith(stack: ItemStack, host: Class[_ <: EnvironmentHost]) = - worksWith(stack) && (isComputer(host) || isRobot(host) || isServer(host) || isTablet(host)) + worksWith(stack) && (isComputer(host) || isRobot(host) || isServer(host) || isTablet(host) || isMicrocontroller(host)) override def createEnvironment(stack: ItemStack, host: EnvironmentHost) = new component.EEPROM() diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/DriverGraphicsCard.scala b/src/main/scala/li/cil/oc/integration/opencomputers/DriverGraphicsCard.scala index 7e2643765..670e25578 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/DriverGraphicsCard.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverGraphicsCard.scala @@ -1,16 +1,23 @@ package li.cil.oc.integration.opencomputers -import li.cil.oc.api.driver.{EnvironmentAware, EnvironmentHost} -import li.cil.oc.{api, common} +import li.cil.oc.api +import li.cil.oc.api.driver.EnvironmentAware +import li.cil.oc.api.driver.EnvironmentHost +import li.cil.oc.api.driver.item.HostAware +import li.cil.oc.common +import li.cil.oc.common.Slot +import li.cil.oc.common.Tier import li.cil.oc.common.init.Items -import li.cil.oc.common.{Slot, Tier} import li.cil.oc.server.component import net.minecraft.item.ItemStack -object DriverGraphicsCard extends Item with EnvironmentAware { +object DriverGraphicsCard extends Item with HostAware with EnvironmentAware { override def worksWith(stack: ItemStack) = isOneOf(stack, api.Items.get("graphicsCard1"), api.Items.get("graphicsCard2"), api.Items.get("graphicsCard3")) + override def worksWith(stack: ItemStack, host: Class[_ <: EnvironmentHost]) = + worksWith(stack) && !isMicrocontroller(host) + override def createEnvironment(stack: ItemStack, host: EnvironmentHost) = tier(stack) match { case Tier.One => new component.GraphicsCard.Tier1() diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/DriverKeyboard.scala b/src/main/scala/li/cil/oc/integration/opencomputers/DriverKeyboard.scala index fcab537be..2bcae0810 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/DriverKeyboard.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverKeyboard.scala @@ -12,7 +12,7 @@ object DriverKeyboard extends Item with HostAware { isOneOf(stack, api.Items.get("keyboard")) override def worksWith(stack: ItemStack, host: Class[_ <: EnvironmentHost]) = - worksWith(stack) && !isAdapter(host) + worksWith(stack) && !isAdapter(host) && !isMicrocontroller(host) override def createEnvironment(stack: ItemStack, host: EnvironmentHost) = new component.Keyboard(host) diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/DriverRedstoneCard.scala b/src/main/scala/li/cil/oc/integration/opencomputers/DriverRedstoneCard.scala index 9091e32ff..9a229690d 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/DriverRedstoneCard.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverRedstoneCard.scala @@ -20,7 +20,7 @@ object DriverRedstoneCard extends Item with HostAware with EnvironmentAware { override def worksWith(stack: ItemStack) = isOneOf(stack, api.Items.get("redstoneCard1"), api.Items.get("redstoneCard2")) override def worksWith(stack: ItemStack, host: Class[_ <: EnvironmentHost]) = - worksWith(stack) && (isComputer(host) || isRobot(host) || isServer(host)) + worksWith(stack) && (isComputer(host) || isRobot(host) || isServer(host) || isMicrocontroller(host)) override def createEnvironment(stack: ItemStack, host: EnvironmentHost) = host match { diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/DriverScreen.scala b/src/main/scala/li/cil/oc/integration/opencomputers/DriverScreen.scala index dd229c558..a93be4ee1 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/DriverScreen.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverScreen.scala @@ -14,7 +14,7 @@ object DriverScreen extends Item with HostAware with EnvironmentAware { isOneOf(stack, api.Items.get("screen1")) override def worksWith(stack: ItemStack, host: Class[_ <: EnvironmentHost]) = - worksWith(stack) && !isTablet(host) && !isAdapter(host) + worksWith(stack) && !isTablet(host) && !isAdapter(host) && !isMicrocontroller(host) override def createEnvironment(stack: ItemStack, host: EnvironmentHost) = host match { case screen: tileentity.Screen if screen.tier > 0 => new component.Screen(screen) diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeBattery.scala b/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeBattery.scala index d16bdfb36..3f0f316f1 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeBattery.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeBattery.scala @@ -3,8 +3,10 @@ package li.cil.oc.integration.opencomputers import li.cil.oc.api import li.cil.oc.api.driver.EnvironmentHost import li.cil.oc.api.driver.item.HostAware +import li.cil.oc.common.Slot +import li.cil.oc.common.Tier import li.cil.oc.common.init.Items -import li.cil.oc.common.{Slot, Tier, item} +import li.cil.oc.common.item import li.cil.oc.server.component import net.minecraft.item.ItemStack diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeDatabase.scala b/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeDatabase.scala index 15a584212..4a6d18281 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeDatabase.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeDatabase.scala @@ -3,17 +3,24 @@ package li.cil.oc.integration.opencomputers import li.cil.oc.api import li.cil.oc.api.driver import li.cil.oc.api.driver.EnvironmentAware +import li.cil.oc.api.driver.EnvironmentHost +import li.cil.oc.api.driver.item.HostAware +import li.cil.oc.common.Slot +import li.cil.oc.common.Tier import li.cil.oc.common.init.Items import li.cil.oc.common.inventory.DatabaseInventory -import li.cil.oc.common.{Slot, Tier, item} +import li.cil.oc.common.item import li.cil.oc.server.component import net.minecraft.entity.player.EntityPlayer import net.minecraft.item.ItemStack -object DriverUpgradeDatabase extends Item with EnvironmentAware { +object DriverUpgradeDatabase extends Item with HostAware with EnvironmentAware { override def worksWith(stack: ItemStack) = isOneOf(stack, api.Items.get("databaseUpgrade1"), api.Items.get("databaseUpgrade2"), api.Items.get("databaseUpgrade3")) + override def worksWith(stack: ItemStack, host: Class[_ <: EnvironmentHost]) = + worksWith(stack) && !isMicrocontroller(host) + override def createEnvironment(stack: ItemStack, host: driver.EnvironmentHost) = new component.UpgradeDatabase(new DatabaseInventory { override def tier = DriverUpgradeDatabase.tier(stack) diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeNavigation.scala b/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeNavigation.scala index 9e25a5c6c..5d2b0ce98 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeNavigation.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeNavigation.scala @@ -15,7 +15,7 @@ object DriverUpgradeNavigation extends Item with HostAware with EnvironmentAware isOneOf(stack, api.Items.get("navigationUpgrade")) override def worksWith(stack: ItemStack, host: Class[_ <: EnvironmentHost]) = - worksWith(stack) && isRotatable(host) + worksWith(stack) && (isRotatable(host) && !isMicrocontroller(host)) override def createEnvironment(stack: ItemStack, host: EnvironmentHost) = host match { diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeSign.scala b/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeSign.scala index 78e1fea57..12b123edf 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeSign.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeSign.scala @@ -1,12 +1,15 @@ package li.cil.oc.integration.opencomputers import li.cil.oc.api -import li.cil.oc.api.driver.{EnvironmentAware, EnvironmentHost} +import li.cil.oc.api.driver.EnvironmentAware +import li.cil.oc.api.driver.EnvironmentHost import li.cil.oc.api.driver.item.HostAware -import li.cil.oc.api.internal.{Adapter, Rotatable} +import li.cil.oc.api.internal.Adapter +import li.cil.oc.api.internal.Rotatable import li.cil.oc.common.Slot import li.cil.oc.server.component -import li.cil.oc.server.component.{UpgradeSignInAdapter, UpgradeSignInRotatable} +import li.cil.oc.server.component.UpgradeSignInAdapter +import li.cil.oc.server.component.UpgradeSignInRotatable import net.minecraft.item.ItemStack object DriverUpgradeSign extends Item with HostAware with EnvironmentAware { @@ -14,7 +17,7 @@ object DriverUpgradeSign extends Item with HostAware with EnvironmentAware { isOneOf(stack, api.Items.get("signUpgrade")) override def worksWith(stack: ItemStack, host: Class[_ <: EnvironmentHost]) = - worksWith(stack) && (isRotatable(host) || isAdapter(host)) + worksWith(stack) && (isAdapter(host) || isRotatable(host)) override def createEnvironment(stack: ItemStack, host: EnvironmentHost) = host match { diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeSolarGenerator.scala b/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeSolarGenerator.scala index 619259a99..f04425a70 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeSolarGenerator.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeSolarGenerator.scala @@ -19,5 +19,5 @@ object DriverUpgradeSolarGenerator extends Item with HostAware { override def slot(stack: ItemStack) = Slot.Upgrade - override def tier(stack: ItemStack) = Tier.Three + override def tier(stack: ItemStack) = Tier.Two } diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/Item.scala b/src/main/scala/li/cil/oc/integration/opencomputers/Item.scala index 590f504a3..29c3da899 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/Item.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/Item.scala @@ -27,6 +27,8 @@ trait Item extends driver.Item { protected def isServer(host: Class[_ <: EnvironmentHost]) = classOf[internal.Server].isAssignableFrom(host) protected def isTablet(host: Class[_ <: EnvironmentHost]) = classOf[internal.Tablet].isAssignableFrom(host) + + protected def isMicrocontroller(host: Class[_ <: EnvironmentHost]) = classOf[internal.Microcontroller].isAssignableFrom(host) } object Item { diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/ModOpenComputers.scala b/src/main/scala/li/cil/oc/integration/opencomputers/ModOpenComputers.scala index 66e7c9dee..b3cefc470 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/ModOpenComputers.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/ModOpenComputers.scala @@ -10,6 +10,7 @@ import li.cil.oc.common.asm.SimpleComponentTickHandler import li.cil.oc.common.event._ import li.cil.oc.common.item.Tablet import li.cil.oc.common.recipe.Recipes +import li.cil.oc.common.template.MicrocontrollerTemplate import li.cil.oc.common.template.RobotTemplate import li.cil.oc.common.template.TabletTemplate import li.cil.oc.integration.ModProxy @@ -22,6 +23,7 @@ object ModOpenComputers extends ModProxy { override def getMod = Mods.OpenComputers override def initialize() { + MicrocontrollerTemplate.register() RobotTemplate.register() TabletTemplate.register() diff --git a/src/main/scala/li/cil/oc/integration/stargatetech2/DriverAbstractBusCard.scala b/src/main/scala/li/cil/oc/integration/stargatetech2/DriverAbstractBusCard.scala index d242f2510..35dcd8332 100644 --- a/src/main/scala/li/cil/oc/integration/stargatetech2/DriverAbstractBusCard.scala +++ b/src/main/scala/li/cil/oc/integration/stargatetech2/DriverAbstractBusCard.scala @@ -16,7 +16,7 @@ object DriverAbstractBusCard extends Item with HostAware with EnvironmentAware { isOneOf(stack, api.Items.get("abstractBusCard")) override def worksWith(stack: ItemStack, host: Class[_ <: EnvironmentHost]) = - worksWith(stack) && (isComputer(host) || isRobot(host) || isServer(host)) + worksWith(stack) && (isComputer(host) || isRobot(host) || isServer(host) || isMicrocontroller(host)) override def createEnvironment(stack: ItemStack, host: EnvironmentHost) = if (Mods.StargateTech2.isAvailable) host match { case device: IBusDevice => new component.AbstractBusCard(device) diff --git a/src/main/scala/li/cil/oc/server/component/EEPROM.scala b/src/main/scala/li/cil/oc/server/component/EEPROM.scala index fe7225049..adb30e230 100644 --- a/src/main/scala/li/cil/oc/server/component/EEPROM.scala +++ b/src/main/scala/li/cil/oc/server/component/EEPROM.scala @@ -26,6 +26,9 @@ class EEPROM extends prefab.ManagedEnvironment { @Callback(doc = """function(data:string) -- Overwrite the currently stored byte array.""") def set(context: Context, args: Arguments): Array[AnyRef] = { + if (!node.tryChangeBuffer(-Settings.get.eepromWriteCost)) { + return result(Unit, "not enough energy") + } val newData = args.checkByteArray(0) if (newData.length > 4 * 1024) throw new IllegalArgumentException("not enough space") data = newData diff --git a/src/main/scala/li/cil/oc/server/component/RedstoneWireless.scala b/src/main/scala/li/cil/oc/server/component/RedstoneWireless.scala index 347e1756b..f9c5c68df 100644 --- a/src/main/scala/li/cil/oc/server/component/RedstoneWireless.scala +++ b/src/main/scala/li/cil/oc/server/component/RedstoneWireless.scala @@ -13,6 +13,7 @@ import li.cil.oc.common.tileentity.traits.RedstoneAware import li.cil.oc.integration.Mods import li.cil.oc.integration.util import net.minecraft.nbt.NBTTagCompound +import net.minecraftforge.common.util.ForgeDirection @Optional.InterfaceList(Array( new Optional.Interface(iface = "codechicken.wirelessredstone.core.WirelessReceivingDevice", modid = Mods.IDs.WirelessRedstoneCBE), @@ -74,7 +75,7 @@ trait RedstoneWireless extends Redstone[RedstoneAware] with WirelessReceivingDev override def updateDevice(frequency: Int, on: Boolean) { if (frequency == wirelessFrequency && on != wirelessInput) { wirelessInput = on - // TODO signal to computer + node.sendToReachable("computer.signal", "redstone_changed", "wireless") } } diff --git a/src/main/scala/li/cil/oc/server/component/UpgradePiston.scala b/src/main/scala/li/cil/oc/server/component/UpgradePiston.scala index c0b14a45c..e7152ef28 100644 --- a/src/main/scala/li/cil/oc/server/component/UpgradePiston.scala +++ b/src/main/scala/li/cil/oc/server/component/UpgradePiston.scala @@ -24,7 +24,7 @@ class UpgradePiston(val host: Rotatable with EnvironmentHost) extends prefab.Man private lazy val tryExtend = ReflectionHelper.findMethod(classOf[BlockPistonBase], null, Array("tryExtend", "func_150079_i", "i"), classOf[World], classOf[Int], classOf[Int], classOf[Int], classOf[Int]) - @Callback(doc = """function(side:number):boolean -- Tries to push the block in front of the container of the upgrade.""") + @Callback(doc = """function():boolean -- Tries to push the block in front of the container of the upgrade.""") def push(context: Context, args: Arguments): Array[AnyRef] = { val hostPos = BlockPosition(host) val blockPos = hostPos.offset(host.facing) diff --git a/src/main/scala/li/cil/oc/util/ItemUtils.scala b/src/main/scala/li/cil/oc/util/ItemUtils.scala index 2acacb50e..e528900a1 100644 --- a/src/main/scala/li/cil/oc/util/ItemUtils.scala +++ b/src/main/scala/li/cil/oc/util/ItemUtils.scala @@ -10,7 +10,6 @@ import li.cil.oc.common.Tier import li.cil.oc.common.block.DelegatorConverter import li.cil.oc.common.init.Items import li.cil.oc.integration.opencomputers.DriverScreen -import li.cil.oc.integration.opencomputers.DriverUpgradeExperience import li.cil.oc.util.ExtendedNBT._ import net.minecraft.item.ItemMap import net.minecraft.item.ItemStack @@ -49,6 +48,85 @@ object ItemUtils { } } + class MicrocontrollerData extends ItemData { + def this(stack: ItemStack) { + this() + load(stack) + } + + var components = Array.empty[ItemStack] + + override def load(nbt: NBTTagCompound) { + components = nbt.getTagList(Settings.namespace + "components", NBT.TAG_COMPOUND). + toArray[NBTTagCompound].map(loadStack) + } + + override def save(nbt: NBTTagCompound) { + nbt.setNewTagList(Settings.namespace + "components", components.toIterable) + } + + def createItemStack() = { + val stack = api.Items.get("microcontroller").createItemStack(1) + save(stack) + stack + } + + def copyItemStack() = { + val stack = createItemStack() + // Forget all node addresses and so on. This is used when 'picking' a + // microcontroller in creative mode. + val newInfo = new MicrocontrollerData(stack) + newInfo.components.foreach(cs => Option(api.Driver.driverFor(cs)) match { + case Some(driver) if driver == DriverScreen => + val nbt = driver.dataTag(cs) + for (tagName <- nbt.func_150296_c().toArray) { + nbt.removeTag(tagName.asInstanceOf[String]) + } + case _ => + }) + newInfo.save(stack) + stack + } + } + + class NavigationUpgradeData extends ItemData { + def this(stack: ItemStack) { + this() + load(stack) + } + + var map = new ItemStack(net.minecraft.init.Items.filled_map) + + def mapData(world: World) = try map.getItem.asInstanceOf[ItemMap].getMapData(map, world) catch { + case _: Throwable => throw new Exception("invalid map") + } + + override def load(stack: ItemStack) { + if (stack.hasTagCompound) { + load(stack.getTagCompound.getCompoundTag(Settings.namespace + "data")) + } + } + + override def save(stack: ItemStack) { + if (!stack.hasTagCompound) { + stack.setTagCompound(new NBTTagCompound()) + } + save(stack.getCompoundTag(Settings.namespace + "data")) + } + + override def load(nbt: NBTTagCompound) { + if (nbt.hasKey(Settings.namespace + "map")) { + map = loadStack(nbt.getCompoundTag(Settings.namespace + "map")) + } + } + + override def save(nbt: NBTTagCompound) { + if (map != null) { + nbt.setNewCompoundTag(Settings.namespace + "map", map.writeToNBT) + } + } + } + class RobotData extends ItemData { def this(stack: ItemStack) { this() @@ -151,44 +229,6 @@ object ItemUtils { def randomName = if (names.length > 0) names((math.random * names.length).toInt) else "Robot" } - class NavigationUpgradeData extends ItemData { - def this(stack: ItemStack) { - this() - load(stack) - } - - var map = new ItemStack(net.minecraft.init.Items.filled_map) - - def mapData(world: World) = try map.getItem.asInstanceOf[ItemMap].getMapData(map, world) catch { - case _: Throwable => throw new Exception("invalid map") - } - - override def load(stack: ItemStack) { - if (stack.hasTagCompound) { - load(stack.getTagCompound.getCompoundTag(Settings.namespace + "data")) - } - } - - override def save(stack: ItemStack) { - if (!stack.hasTagCompound) { - stack.setTagCompound(new NBTTagCompound()) - } - save(stack.getCompoundTag(Settings.namespace + "data")) - } - - override def load(nbt: NBTTagCompound) { - if (nbt.hasKey(Settings.namespace + "map")) { - map = loadStack(nbt.getCompoundTag(Settings.namespace + "map")) - } - } - - override def save(nbt: NBTTagCompound) { - if (map != null) { - nbt.setNewCompoundTag(Settings.namespace + "map", map.writeToNBT) - } - } - } - class TabletData extends ItemData { def this(stack: ItemStack) { this()