diff --git a/assets/blocks.psd b/assets/blocks.psd index b66def3a9..cef221fd4 100644 Binary files a/assets/blocks.psd and b/assets/blocks.psd differ diff --git a/assets/items.psd b/assets/items.psd index 6856b63bd..b8354d978 100644 Binary files a/assets/items.psd and b/assets/items.psd differ diff --git a/src/main/resources/application.conf b/src/main/resources/application.conf index aa99d1c01..9d84c1c39 100644 --- a/src/main/resources/application.conf +++ b/src/main/resources/application.conf @@ -509,6 +509,10 @@ opencomputers { # controls the speed at which items are disassembled, basically. disassemblerTickAmount: 25 + # The amount of energy the printer can apply per tick. This controls + # the speed at which prints are completed, basically. + printerTickAmount: 1 + # If you don't want OpenComputers to accept power from one or more of the # supported power mods, for example because it doesn't suit the vision # of your mod pack, you can disable support for them here. To stop @@ -729,6 +733,9 @@ opencomputers { # Energy it costs to re-program an EEPROM. This is deliberately # expensive, to discourage frequent re-writing of EEPROMs. eepromWrite: 50 + + # How much energy is required for a single 3D print. + printerModel: 25 } # The rate at which different blocks accept external power. All of these @@ -1022,6 +1029,12 @@ opencomputers { # avoid issues with computers timing out, but can also lead to higher # server load. AGAIN, USE WITH CARE! threadPriority: -1 + + # The maximum number of shape for a state of a 3D print allowed. This is + # for the individual states (off and on), so it is possible to have up to + # this many shapes *per state* (the reasoning being that only one state + # will ever be visible at a time). + maxPrinterShapes: 16 } # Settings for mod integration (the mod previously known as OpenComponents). diff --git a/src/main/resources/assets/opencomputers/lang/en_US.lang b/src/main/resources/assets/opencomputers/lang/en_US.lang index 5de7bef65..8118232ca 100644 --- a/src/main/resources/assets/opencomputers/lang/en_US.lang +++ b/src/main/resources/assets/opencomputers/lang/en_US.lang @@ -23,6 +23,8 @@ tile.oc.microcontroller.name=Microcontroller tile.oc.motionSensor.name=Motion Sensor tile.oc.powerConverter.name=Power Converter tile.oc.powerDistributor.name=Power Distributor +tile.oc.print.name=3D Print +tile.oc.printer.name=3D Printer tile.oc.raid.name=Raid tile.oc.redstone.name=Redstone I/O tile.oc.robot.name=Robot @@ -42,6 +44,7 @@ item.oc.appengTunnel.name=P2P Tunnel - OpenComputers item.oc.ArrowKeys.name=Arrow Keys item.oc.ButtonGroup.name=Button Group item.oc.CardBase.name=Card Base +item.oc.Chamelium.name=Chamelium item.oc.CircuitBoard.name=Circuit Board item.oc.ControlUnit.name=Control Unit (CU) item.oc.ComponentBus0.name=Component Bus (Tier 1) @@ -66,6 +69,8 @@ item.oc.GraphicsCard2.name=Graphics Card (Tier 3) item.oc.HardDiskDrive0.name=Hard Disk Drive (Tier 1) item.oc.HardDiskDrive1.name=Hard Disk Drive (Tier 2) item.oc.HardDiskDrive2.name=Hard Disk Drive (Tier 3) +item.oc.InkCartridge.name=Ink Cartidge +item.oc.InkCartridgeEmpty.name=Ink Cartidge (Empty) item.oc.InternetCard.name=Internet Card item.oc.Interweb.name=Interweb item.oc.IronNugget.name=Iron Nugget @@ -98,6 +103,7 @@ item.oc.TabletCase0.name=Tablet Case (Tier 1) item.oc.TabletCase1.name=Tablet Case (Tier 2) item.oc.TabletCase3.name=Tablet Case (Creative) item.oc.Terminal.name=Remote Terminal +item.oc.TexturePicker.name=Texture Picker item.oc.Transistor.name=Transistor item.oc.UpgradeAngel.name=Angel Upgrade item.oc.UpgradeBattery0.name=Battery Upgrade (Tier 1) @@ -164,6 +170,7 @@ oc:gui.Chat.NewVersion=A new version is available: %s oc:gui.Chat.WarningFingerprint=§cWARNING§f - fingerprint mismatch! Expected '§a%s§f' but got '§e%s§f'. Unless you are a modder and are running the deobfuscated version of the mod, it is §lstrongly§f recommended to redownload OpenComputers, because the JAR you are using may have been tampered with. oc:gui.Chat.WarningLuaFallback=Native Lua libraries are not available, computers will not be able to persist their state. They will reboot on chunk reloads. oc:gui.Chat.WarningPower=No supported power providing mod available. Computers, screens and all other components will §lnot§f require energy. Install one of the following mods to enable power: BuildCraft, Electrical Age, IndustrialCraft2, Mekanism or Thermal Expansion. Disable power in the config to suppress this warning. +oc:gui.Chat.TextureName=§7Texture name is §a%s§f. oc:gui.Chat.WarningProjectRed=You are using a version of Project: Red that is incompatible with OpenComputers. Try updating your version of Project: Red. oc:gui.Error.ComponentOverflow=Too many components connected to the computer. oc:gui.Error.InternalError=Internal error, please see the log file. This is probably a bug. @@ -197,6 +204,7 @@ oc:container.Charger=Charger oc:container.Case=Computer oc:container.Disassembler=Disassembler oc:container.DiskDrive=Disk Drive +oc:container.Printer=Printer oc:container.Raid=Raid oc:container.Server=Server oc:container.ServerRack=Server Rack @@ -219,6 +227,7 @@ oc:tooltip.Cable=A cheap way of connecting blocks. oc:tooltip.Capacitor=Stores energy for later use. Can be filled and emptied very quickly. oc:tooltip.CardBase=As the name indicates, this is the basic building block for all expansion cards. oc:tooltip.Case=The Computer Case is the basic building block for computers and houses the computer's §fextension cards§7, §fRAM§7 and §fhard disks§7.[nl] Slots: §f%s§7 +oc:tooltip.Chamelium=Raw material for 3D prints. Do not swallow. Not that you could even if you wanted to. oc:tooltip.Charger=Transfers energy from capacitors into adjacent robots and drones. The transfer rate depends on the incoming §fredstone signal§7, where no signal means don't charge devices, and maximum strength means charge at full speed. Can also be used to charge tablets and access hard drives in tablets. oc:tooltip.CircuitBoard=Now we're getting somewhere. Can be etched to obtain a printed circuit board. oc:tooltip.ControlUnit=This is the unit that... controls... stuff. You need it to build a CPU. So yeah, totally important. @@ -237,6 +246,8 @@ oc:tooltip.DroneCase=This casing is used to build Drones in the assembler. It ha oc:tooltip.EEPROM=Small, programmable storage that contains the BIOS computers use to boot. oc:tooltip.Geolyzer=Allows scanning the surrounding area's blocks' hardness. This information can be useful for generating holograms of the area or for detecting ores. oc:tooltip.GraphicsCard=Used to change what's displayed on screens.[nl] Maximum resolution: §f%sx%s§7[nl] Maximum color depth: §f%s§7[nl] Operations/tick: §f%s§7 +oc:tooltip.InkCartridge=Used to refill ink in 3D printers. For mysterious reasons it does not have to remain in the printer. +oc:tooltip.InkCartridgeEmpty=This ink cartridge has been sucked dry. Refill it using dyes. Or throw it away. See if I care. oc:tooltip.InternetCard=This card allows making HTTP requests and using real TCP sockets. oc:tooltip.Interweb=Congratulations, you win one (1) interweb. You can connect to it using an Internet Card. Beware: don't feed the trolls. oc:tooltip.IronNugget=A nugget made of iron, that's why it's called an Iron Nugget, duh... @@ -262,6 +273,7 @@ oc:tooltip.PowerConverter.ThermalExpansion=§fThermal Expansion RF§7: §a%s:%s oc:tooltip.PowerConverter.ResonantEngine=§fResonant Engine Coulombs§7: §a%s:%s§7 oc:tooltip.PowerConverter=Converts power from other mods to the internal energy type. Conversion rates: oc:tooltip.PowerDistributor=Distributes energy among different networks. This is useful for sharing power fed into your system from one converter among different sub-networks that should remain separate. +oc:tooltip.Printer=Allows printing blocks of user-defined shapes using Chamelium and Ink Cartridges. Must be configured using a computer. Keep away from small children. Because reasons. oc:tooltip.PrintedCircuitBoard=The basic building block for expansion cards and memory and such. oc:tooltip.Present=... for your troubles. Open this present for a chance to receive some §kphat lewt§7![nl]§8Craft OpenComputers items when the time is right for a chance to receive a present.§7 oc:tooltip.Raid=Allows combining three hard drives into one larger file system that can be used by all connected computers. @@ -285,6 +297,7 @@ oc:tooltip.Switch=Allows connecting different networks to each other. Only netwo oc:tooltip.Tablet=A tablet computer, for fresh Lua on the go. Can be forced to shut down by sneak-activating it. oc:tooltip.TabletCase=Basic case for tablets. Place it into the assembler to add in components and create a tablet computer. oc:tooltip.Terminal=Allows controlling a server remotely, as long as you are in range of it. Acts like a portable screen and keyboard. Shift-right-click a server in a server rack to bind the terminal to it. +oc:tooltip.TexturePicker=This tool allows showing a string describing a block's surface, for use in 3D printer shape definitions. Totally not texture names, nope. No sir. oc:tooltip.Tier=§8Tier %s oc:tooltip.TooLong=Hold [§f%s§7] for a detailed tooltip. oc:tooltip.Transistor=A basic element in most other computer parts. It's a bit twisted, but it does the job. diff --git a/src/main/resources/assets/opencomputers/recipes/default.recipes b/src/main/resources/assets/opencomputers/recipes/default.recipes index 6cb743bcd..415e999c9 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, ""]] } +texturePicker { + input: [[dyeBlack, dyeRed, dyeGreen] + [dyeBlue, "oc:analyzer", dyePurple] + [dyeYellow, dyeMagenta, dyeWhite]] +} droneCase1 { input: [[{block="minecraft:end_stone"}, compass, {block="minecraft:end_stone"}] ["oc:circuitChip1", "oc:microcontrollerCase1", "oc:circuitChip1"] @@ -318,6 +323,21 @@ disk { [nuggetIron, "", nuggetIron] ["", nuggetIron, ""]] } +chamelium { + input: [[gravel, redstone, gravel], + [redstone, {item=coal, subID=1}, redstone], + [gravel, water_bucket, gravel]] + output: 16 +} +inkCartridgeEmpty { + input: [[nuggetIron, dispenser, nuggetIron], + ["oc:materialTransistor", bucket, "oc:materialTransistor"], + [nuggetIron, "oc:materialCircuitBoardPrinted", nuggetIron]] +} +inkCartridge { + type: shapeless + input: [dyeCyan, dyeMagenta, dyeYellow, dyeBlack, "oc:inkCartridgeEmpty"] +} buttonGroup { input: [[button, button, button] diff --git a/src/main/resources/assets/opencomputers/textures/blocks/PrinterSide.png b/src/main/resources/assets/opencomputers/textures/blocks/PrinterSide.png new file mode 100644 index 000000000..e90aba438 Binary files /dev/null and b/src/main/resources/assets/opencomputers/textures/blocks/PrinterSide.png differ diff --git a/src/main/resources/assets/opencomputers/textures/blocks/PrinterTop.png b/src/main/resources/assets/opencomputers/textures/blocks/PrinterTop.png new file mode 100644 index 000000000..afa1ef1de Binary files /dev/null and b/src/main/resources/assets/opencomputers/textures/blocks/PrinterTop.png differ diff --git a/src/main/resources/assets/opencomputers/textures/gui/printer.png b/src/main/resources/assets/opencomputers/textures/gui/printer.png new file mode 100644 index 000000000..f7e756e23 Binary files /dev/null and b/src/main/resources/assets/opencomputers/textures/gui/printer.png differ diff --git a/src/main/resources/assets/opencomputers/textures/gui/printer_ink.png b/src/main/resources/assets/opencomputers/textures/gui/printer_ink.png new file mode 100644 index 000000000..b125722a7 Binary files /dev/null and b/src/main/resources/assets/opencomputers/textures/gui/printer_ink.png differ diff --git a/src/main/resources/assets/opencomputers/textures/gui/printer_material.png b/src/main/resources/assets/opencomputers/textures/gui/printer_material.png new file mode 100644 index 000000000..3f67324d2 Binary files /dev/null and b/src/main/resources/assets/opencomputers/textures/gui/printer_material.png differ diff --git a/src/main/resources/assets/opencomputers/textures/gui/printer_progress.png b/src/main/resources/assets/opencomputers/textures/gui/printer_progress.png new file mode 100644 index 000000000..919b987d9 Binary files /dev/null and b/src/main/resources/assets/opencomputers/textures/gui/printer_progress.png differ diff --git a/src/main/resources/assets/opencomputers/textures/items/Chamelium.png b/src/main/resources/assets/opencomputers/textures/items/Chamelium.png new file mode 100644 index 000000000..eeb2e9552 Binary files /dev/null and b/src/main/resources/assets/opencomputers/textures/items/Chamelium.png differ diff --git a/src/main/resources/assets/opencomputers/textures/items/InkCartridge.png b/src/main/resources/assets/opencomputers/textures/items/InkCartridge.png new file mode 100644 index 000000000..a2b92d00b Binary files /dev/null and b/src/main/resources/assets/opencomputers/textures/items/InkCartridge.png differ diff --git a/src/main/resources/assets/opencomputers/textures/items/InkCartridgeEmpty.png b/src/main/resources/assets/opencomputers/textures/items/InkCartridgeEmpty.png new file mode 100644 index 000000000..007697d69 Binary files /dev/null and b/src/main/resources/assets/opencomputers/textures/items/InkCartridgeEmpty.png differ diff --git a/src/main/resources/assets/opencomputers/textures/items/TexturePicker.png b/src/main/resources/assets/opencomputers/textures/items/TexturePicker.png new file mode 100644 index 000000000..f8565ef03 Binary files /dev/null and b/src/main/resources/assets/opencomputers/textures/items/TexturePicker.png differ diff --git a/src/main/scala/li/cil/oc/Localization.scala b/src/main/scala/li/cil/oc/Localization.scala index 328848133..0100b5e96 100644 --- a/src/main/scala/li/cil/oc/Localization.scala +++ b/src/main/scala/li/cil/oc/Localization.scala @@ -84,6 +84,8 @@ object Localization { def WarningFingerprint(event: FMLFingerprintViolationEvent) = new ChatComponentText("§aOpenComputers§f: ").appendSibling(localizeLater("gui.Chat.WarningFingerprint", event.expectedFingerprint, event.fingerprints.toArray.mkString(", "))) def InfoNewVersion(version: String) = new ChatComponentText("§aOpenComputers§f: ").appendSibling(localizeLater("gui.Chat.NewVersion", version)) + + def TextureName(name: String) = new ChatComponentText("§aOpenComputers§f: ").appendSibling(localizeLater("gui.Chat.TextureName", name)) } object Computer { diff --git a/src/main/scala/li/cil/oc/Settings.scala b/src/main/scala/li/cil/oc/Settings.scala index 2b2374864..2b4f54715 100644 --- a/src/main/scala/li/cil/oc/Settings.scala +++ b/src/main/scala/li/cil/oc/Settings.scala @@ -139,6 +139,7 @@ class Settings(val config: Config) { val solarGeneratorEfficiency = config.getDouble("power.solarGeneratorEfficiency") val assemblerTickAmount = config.getDouble("power.assemblerTickAmount") max 1 val disassemblerTickAmount = config.getDouble("power.disassemblerTickAmount") max 1 + val printerTickAmount = config.getDouble("power.printerTickAmount") max 1 val powerModBlacklist = config.getStringList("power.modBlacklist") // power.buffer @@ -192,6 +193,7 @@ class Settings(val config: Config) { val chunkloaderCost = config.getDouble("power.cost.chunkloaderCost") max 0 val pistonCost = config.getDouble("power.cost.pistonPush") max 0 val eepromWriteCost = config.getDouble("power.cost.eepromWrite") max 0 + val printCost = config.getDouble("power.cost.printerModel") max 0 // power.rate val accessPointRate = config.getDouble("power.rate.accessPoint") max 0 @@ -291,6 +293,7 @@ class Settings(val config: Config) { val presentChance = config.getDouble("misc.presentChance") max 0 min 1 val assemblerBlacklist = config.getStringList("misc.assemblerBlacklist") val threadPriority = config.getInt("misc.threadPriority") + val maxPrintComplexity = config.getInt("misc.maxPrinterShapes") // ----------------------------------------------------------------------- // // integration diff --git a/src/main/scala/li/cil/oc/client/GuiHandler.scala b/src/main/scala/li/cil/oc/client/GuiHandler.scala index 6f1a078cf..944921e12 100644 --- a/src/main/scala/li/cil/oc/client/GuiHandler.scala +++ b/src/main/scala/li/cil/oc/client/GuiHandler.scala @@ -32,6 +32,8 @@ object GuiHandler extends CommonGuiHandler { new gui.Disassembler(player.inventory, t) case t: tileentity.DiskDrive if id == GuiType.DiskDrive.id => new gui.DiskDrive(player.inventory, t) + case t: tileentity.Printer if id == GuiType.Printer.id => + new gui.Printer(player.inventory, t) case t: tileentity.Raid if id == GuiType.Raid.id => new gui.Raid(player.inventory, t) case t: tileentity.RobotProxy if id == GuiType.Robot.id => diff --git a/src/main/scala/li/cil/oc/client/PacketHandler.scala b/src/main/scala/li/cil/oc/client/PacketHandler.scala index da0dc4f38..5ab795ed3 100644 --- a/src/main/scala/li/cil/oc/client/PacketHandler.scala +++ b/src/main/scala/li/cil/oc/client/PacketHandler.scala @@ -9,6 +9,7 @@ import li.cil.oc.api.component import li.cil.oc.api.event.FileSystemAccessEvent import li.cil.oc.client.renderer.PetRenderer import li.cil.oc.common.PacketType +import li.cil.oc.common.container import li.cil.oc.common.tileentity._ import li.cil.oc.common.tileentity.traits._ import li.cil.oc.common.{PacketHandler => CommonPacketHandler} @@ -41,6 +42,7 @@ object PacketHandler extends CommonPacketHandler { case PacketType.ColorChange => onColorChange(p) case PacketType.ComputerState => onComputerState(p) case PacketType.ComputerUserList => onComputerUserList(p) + case PacketType.ContainerUpdate => onContainerUpdate(p) case PacketType.DisassemblerActiveChange => onDisassemblerActiveChange(p) case PacketType.FileSystemActivity => onFileSystemActivity(p) case PacketType.FloppyChange => onFloppyChange(p) @@ -52,6 +54,7 @@ object PacketHandler extends CommonPacketHandler { case PacketType.HologramTranslation => onHologramPositionOffsetY(p) case PacketType.PetVisibility => onPetVisibility(p) case PacketType.PowerState => onPowerState(p) + case PacketType.PrinterState => onPrinterState(p) case PacketType.RaidStateChange => onRaidStateChange(p) case PacketType.RedstoneState => onRedstoneState(p) case PacketType.RobotAnimateSwing => onRobotAnimateSwing(p) @@ -134,6 +137,16 @@ object PacketHandler extends CommonPacketHandler { case _ => // Invalid packet. } + def onContainerUpdate(p: PacketParser) = { + val windowId = p.readUnsignedByte() + if (p.player.openContainer != null && p.player.openContainer.windowId == windowId) { + p.player.openContainer match { + case container: container.Player => container.updateCustomData(p.readNBT()) + case _ => // Invalid packet. + } + } + } + def onDisassemblerActiveChange(p: PacketParser) = p.readTileEntity[Disassembler]() match { case Some(t) => t.isActive = p.readBoolean() @@ -242,6 +255,14 @@ object PacketHandler extends CommonPacketHandler { case _ => // Invalid packet. } + def onPrinterState(p: PacketParser) = + p.readTileEntity[Printer]() match { + case Some(t) => + if (p.readBoolean()) t.requiredEnergy = 9001 + else t.requiredEnergy = 0 + case _ => // Invalid packet. + } + def onRaidStateChange(p: PacketParser) = p.readTileEntity[Raid]() match { case Some(t) => diff --git a/src/main/scala/li/cil/oc/client/Proxy.scala b/src/main/scala/li/cil/oc/client/Proxy.scala index 15c057756..67e9561c0 100644 --- a/src/main/scala/li/cil/oc/client/Proxy.scala +++ b/src/main/scala/li/cil/oc/client/Proxy.scala @@ -58,6 +58,7 @@ private[oc] class Proxy extends CommonProxy { ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.Hologram], HologramRendererFallback) ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.Microcontroller], MicrocontrollerRenderer) ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.PowerDistributor], PowerDistributorRenderer) + ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.Printer], PrinterRenderer) ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.Raid], RaidRenderer) ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.ServerRack], ServerRackRenderer) ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.Switch], SwitchRenderer) @@ -66,6 +67,7 @@ private[oc] class Proxy extends CommonProxy { ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.Screen], ScreenRenderer) MinecraftForgeClient.registerItemRenderer(Items.multi, ItemRenderer) + MinecraftForgeClient.registerItemRenderer(Items.get("print").createItemStack(1).getItem, ItemRenderer) ClientRegistry.registerKeyBinding(KeyBindings.materialCosts) ClientRegistry.registerKeyBinding(KeyBindings.clipboardPaste) diff --git a/src/main/scala/li/cil/oc/client/Textures.scala b/src/main/scala/li/cil/oc/client/Textures.scala index 324cab790..888a66207 100644 --- a/src/main/scala/li/cil/oc/client/Textures.scala +++ b/src/main/scala/li/cil/oc/client/Textures.scala @@ -25,6 +25,10 @@ object Textures { val guiDisassembler = new ResourceLocation(Settings.resourceDomain, "textures/gui/disassembler.png") val guiDrone = new ResourceLocation(Settings.resourceDomain, "textures/gui/drone.png") val guiKeyboardMissing = new ResourceLocation(Settings.resourceDomain, "textures/gui/keyboard_missing.png") + val guiPrinter = new ResourceLocation(Settings.resourceDomain, "textures/gui/printer.png") + val guiPrinterInk = new ResourceLocation(Settings.resourceDomain, "textures/gui/printer_ink.png") + val guiPrinterMaterial = new ResourceLocation(Settings.resourceDomain, "textures/gui/printer_material.png") + val guiPrinterProgress = new ResourceLocation(Settings.resourceDomain, "textures/gui/printer_progress.png") val guiRaid = new ResourceLocation(Settings.resourceDomain, "textures/gui/raid.png") val guiRange = new ResourceLocation(Settings.resourceDomain, "textures/gui/range.png") val guiRobot = new ResourceLocation(Settings.resourceDomain, "textures/gui/robot.png") diff --git a/src/main/scala/li/cil/oc/client/gui/Printer.scala b/src/main/scala/li/cil/oc/client/gui/Printer.scala new file mode 100644 index 000000000..a3ca975bc --- /dev/null +++ b/src/main/scala/li/cil/oc/client/gui/Printer.scala @@ -0,0 +1,78 @@ +package li.cil.oc.client.gui + +import li.cil.oc.Localization +import li.cil.oc.client.Textures +import li.cil.oc.client.gui.widget.ProgressBar +import li.cil.oc.common.container +import li.cil.oc.common.container.ComponentSlot +import li.cil.oc.common.tileentity +import net.minecraft.entity.player.InventoryPlayer +import org.lwjgl.opengl.GL11 + +class Printer(playerInventory: InventoryPlayer, val printer: tileentity.Printer) extends DynamicGuiContainer(new container.Printer(playerInventory, printer)) { + xSize = 176 + ySize = 166 + + private val materialBar = addWidget(new ProgressBar(40, 21) { + override def width = 62 + + override def height = 12 + + override def barTexture = Textures.guiPrinterMaterial + }) + private val inkBar = addWidget(new ProgressBar(40, 53) { + override def width = 62 + + override def height = 12 + + override def barTexture = Textures.guiPrinterInk + }) + private val progressBar = addWidget(new ProgressBar(105, 20) { + override def width = 46 + + override def height = 46 + + override def barTexture = Textures.guiPrinterProgress + }) + + private def printerContainer = inventorySlots.asInstanceOf[container.Printer] + + override def initGui() { + super.initGui() + } + + override def drawSecondaryForegroundLayer(mouseX: Int, mouseY: Int) = { + super.drawSecondaryForegroundLayer(mouseX, mouseY) + fontRendererObj.drawString( + Localization.localizeImmediately(printer.getInventoryName), + 8, 6, 0x404040) + GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS) // Me lazy... prevents NEI render glitch. + if (func_146978_c(materialBar.x, materialBar.y, materialBar.width, materialBar.height, mouseX, mouseY)) { + val tooltip = new java.util.ArrayList[String] + tooltip.add(printerContainer.amountMaterial + "/" + printer.maxAmountMaterial) + copiedDrawHoveringText(tooltip, mouseX - guiLeft, mouseY - guiTop, fontRendererObj) + } + if (func_146978_c(inkBar.x, inkBar.y, inkBar.width, inkBar.height, mouseX, mouseY)) { + val tooltip = new java.util.ArrayList[String] + tooltip.add(printerContainer.amountInk + "/" + printer.maxAmountInk) + copiedDrawHoveringText(tooltip, mouseX - guiLeft, mouseY - guiTop, fontRendererObj) + } + GL11.glPopAttrib() + } + + override def drawGuiContainerBackgroundLayer(dt: Float, mouseX: Int, mouseY: Int) { + GL11.glColor3f(1, 1, 1) // Required under Linux. + mc.renderEngine.bindTexture(Textures.guiPrinter) + drawTexturedModalRect(guiLeft, guiTop, 0, 0, xSize, ySize) + materialBar.level = printerContainer.amountMaterial / printer.maxAmountMaterial.toDouble + inkBar.level = printerContainer.amountInk / printer.maxAmountInk.toDouble + if (printerContainer.isPrinting) progressBar.level = (System.currentTimeMillis() % 3000) / 3000.0 + else progressBar.level = 0 + drawWidgets() + drawInventorySlots() + } + + override protected def drawDisabledSlot(slot: ComponentSlot) {} + + override def doesGuiPauseGame = false +} \ No newline at end of file diff --git a/src/main/scala/li/cil/oc/client/gui/widget/ProgressBar.scala b/src/main/scala/li/cil/oc/client/gui/widget/ProgressBar.scala index 7f9beb31e..4a049d221 100644 --- a/src/main/scala/li/cil/oc/client/gui/widget/ProgressBar.scala +++ b/src/main/scala/li/cil/oc/client/gui/widget/ProgressBar.scala @@ -9,19 +9,21 @@ class ProgressBar(val x: Int, val y: Int) extends Widget { override def height = 12 + def barTexture = Textures.guiBar + var level = 0.0 def draw() { if (level > 0) { val u0 = 0 - val u1 = width / 256.0 * level - val v0 = 1 - height / 256.0 + val u1 = level + val v0 = 0 val v1 = 1 val tx = owner.windowX + x val ty = owner.windowY + y val w = width * level - Minecraft.getMinecraft.renderEngine.bindTexture(Textures.guiBar) + Minecraft.getMinecraft.renderEngine.bindTexture(barTexture) val t = Tessellator.instance t.startDrawingQuads() t.addVertexWithUV(tx, ty, owner.windowZ, u0, v0) diff --git a/src/main/scala/li/cil/oc/client/gui/widget/Widget.scala b/src/main/scala/li/cil/oc/client/gui/widget/Widget.scala index c97facd41..db1552137 100644 --- a/src/main/scala/li/cil/oc/client/gui/widget/Widget.scala +++ b/src/main/scala/li/cil/oc/client/gui/widget/Widget.scala @@ -11,5 +11,5 @@ abstract class Widget { def height: Int - def draw() + def draw(): Unit } diff --git a/src/main/scala/li/cil/oc/client/renderer/HighlightRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/HighlightRenderer.scala index 6f0e8621a..55342a82a 100644 --- a/src/main/scala/li/cil/oc/client/renderer/HighlightRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/HighlightRenderer.scala @@ -4,12 +4,15 @@ import cpw.mods.fml.common.eventhandler.SubscribeEvent import li.cil.oc.Settings import li.cil.oc.api import li.cil.oc.client.Textures +import li.cil.oc.common import li.cil.oc.util.BlockPosition +import li.cil.oc.util.ExtendedAABB._ import li.cil.oc.util.ExtendedBlock._ import li.cil.oc.util.ExtendedWorld._ import li.cil.oc.util.RenderState import net.minecraft.client.Minecraft import net.minecraft.client.renderer.OpenGlHelper +import net.minecraft.client.renderer.RenderGlobal import net.minecraft.client.renderer.Tessellator import net.minecraft.util.MovingObjectPosition.MovingObjectType import net.minecraftforge.client.event.DrawBlockHighlightEvent @@ -95,5 +98,33 @@ object HighlightRenderer { GL11.glPopMatrix() } } + + if (hitInfo.typeOfHit == MovingObjectType.BLOCK) e.player.getEntityWorld.getTileEntity(hitInfo.blockX, hitInfo.blockY, hitInfo.blockZ) match { + case print: common.tileentity.Print => + val pos = e.player.getPosition(e.partialTicks) + val expansion = 0.002f + + // See RenderGlobal.drawSelectionBox. + GL11.glEnable(GL11.GL_BLEND) + OpenGlHelper.glBlendFunc(770, 771, 1, 0) + GL11.glColor4f(0, 0, 0, 0.4f) + GL11.glLineWidth(2) + GL11.glDisable(GL11.GL_TEXTURE_2D) + GL11.glDepthMask(false) + + for (shape <- if (print.state) print.data.stateOn else print.data.stateOff) { + val bounds = shape.bounds.rotateTowards(print.facing) + RenderGlobal.drawOutlinedBoundingBox(bounds.copy().expand(expansion, expansion, expansion) + .offset(e.target.blockX, e.target.blockY, e.target.blockZ) + .offset(-pos.xCoord, -pos.yCoord, -pos.zCoord), -1) + } + + GL11.glDepthMask(true) + GL11.glEnable(GL11.GL_TEXTURE_2D) + GL11.glDisable(GL11.GL_BLEND) + + e.setCanceled(true) + case _ => + } } } diff --git a/src/main/scala/li/cil/oc/client/renderer/block/BlockRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/block/BlockRenderer.scala index f198424d5..3d05d230b 100644 --- a/src/main/scala/li/cil/oc/client/renderer/block/BlockRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/block/BlockRenderer.scala @@ -1,9 +1,7 @@ package li.cil.oc.client.renderer.block import cpw.mods.fml.client.registry.ISimpleBlockRenderingHandler -import cpw.mods.fml.common.Loader import li.cil.oc.Settings -import li.cil.oc.client.Textures import li.cil.oc.client.renderer.tileentity.RobotRenderer import li.cil.oc.common.block._ import li.cil.oc.common.tileentity @@ -54,6 +52,13 @@ object BlockRenderer extends ISimpleBlockRenderingHandler { Tessellator.instance.draw() RenderState.checkError(getClass.getName + ".renderInventoryBlock: hologram") + case printer: Printer => + GL11.glTranslatef(-0.5f, -0.5f, -0.5f) + Tessellator.instance.startDrawingQuads() + Printer.render(block, metadata, renderer) + Tessellator.instance.draw() + + RenderState.checkError(getClass.getName + ".renderInventoryBlock: printer") case _ => block match { case simple: SimpleBlock => @@ -82,7 +87,7 @@ object BlockRenderer extends ISimpleBlockRenderingHandler { override def renderWorldBlock(world: IBlockAccess, x: Int, y: Int, z: Int, block: Block, modelId: Int, realRenderer: RenderBlocks) = { RenderState.checkError(getClass.getName + ".renderWorldBlock: entering (aka: wasntme)") - val renderer = patchedRenderer(realRenderer) + val renderer = patchedRenderer(realRenderer, block) world.getTileEntity(x, y, z) match { case cable: tileentity.Cable => Cable.render(world, x, y, z, block, renderer) @@ -91,85 +96,25 @@ object BlockRenderer extends ISimpleBlockRenderingHandler { true case keyboard: tileentity.Keyboard => - if (keyboard.facing == ForgeDirection.UP || keyboard.facing == ForgeDirection.DOWN) { - keyboard.yaw match { - case ForgeDirection.NORTH => - renderer.uvRotateTop = 0 - renderer.uvRotateBottom = 0 - case ForgeDirection.SOUTH => - renderer.uvRotateTop = 3 - renderer.uvRotateBottom = 3 - case ForgeDirection.WEST => - renderer.uvRotateTop = 2 - renderer.uvRotateBottom = 1 - case ForgeDirection.EAST => - renderer.uvRotateTop = 1 - renderer.uvRotateBottom = 2 - case _ => throw new AssertionError("Impossible yaw value on keyboard.") - } - if (keyboard.facing == ForgeDirection.DOWN) { - renderer.flipTexture = true - } - } - val result = renderer.renderStandardBlock(block, x, y, z) - renderer.uvRotateTop = 0 - renderer.uvRotateBottom = 0 - renderer.flipTexture = false + val result = Keyboard.render(keyboard, x, y, z, block, renderer) RenderState.checkError(getClass.getName + ".renderWorldBlock: keyboard") result + case print: tileentity.Print => + Print.render(print, x, y, z, block, renderer) + + RenderState.checkError(getClass.getName + ".renderWorldBlock: print") + + true + case printer: tileentity.Printer => + Printer.render(block, x, y, z, renderer) + + RenderState.checkError(getClass.getName + ".renderWorldBlock: printer") + + true case rack: tileentity.ServerRack => - val previousRenderAllFaces = renderer.renderAllFaces - val u1 = 1 / 16f - val u2 = 15 / 16f - val v1 = 2 / 16f - val v2 = 14 / 16f - val fs = 3 / 16f - - // Top and bottom. - renderer.renderAllFaces = true - renderer.setRenderBounds(0, 0, 0, 1, v1, 1) - renderer.renderStandardBlock(block, x, y, z) - renderer.setRenderBounds(0, v2, 0, 1, 1, 1) - renderer.renderStandardBlock(block, x, y, z) - - // Sides. - val front = rack.facing - def renderSide(side: ForgeDirection, lx: Double, lz: Double, hx: Double, hz: Double) { - if (side == front) { - for (i <- 0 until 4 if rack.isPresent(i).isDefined) { - side match { - case ForgeDirection.WEST => - renderer.setRenderBounds(lx, v2 - (i + 1) * fs, lz + u1, u2, v2 - i * fs, hz - u1) - case ForgeDirection.EAST => - renderer.setRenderBounds(u1, v2 - (i + 1) * fs, lz + u1, hx, v2 - i * fs, hz - u1) - case ForgeDirection.NORTH => - renderer.setRenderBounds(lx + u1, v2 - (i + 1) * fs, lz, hx - u1, v2 - i * fs, u2) - case ForgeDirection.SOUTH => - renderer.setRenderBounds(lx + u1, v2 - (i + 1) * fs, u1, hx - u1, v2 - i * fs, hz) - case _ => - } - renderer.renderStandardBlock(block, x, y, z) - } - } - else { - val isBack = front == side.getOpposite - if (isBack) { - renderer.setOverrideBlockTexture(Textures.ServerRack.icons(ForgeDirection.NORTH.ordinal)) - } - renderer.setRenderBounds(lx, v1, lz, hx, v2, hz) - renderer.renderStandardBlock(block, x, y, z) - renderer.clearOverrideBlockTexture() - } - } - - renderSide(ForgeDirection.WEST, 0, 0, u1, 1) - renderSide(ForgeDirection.EAST, u2, 0, 1, 1) - renderSide(ForgeDirection.NORTH, 0, 0, 1, u1) - renderSide(ForgeDirection.SOUTH, 0, u2, 1, 1) - - renderer.renderAllFaces = previousRenderAllFaces + ServerRack.render(rack, x, y, z, block, renderer) RenderState.checkError(getClass.getName + ".renderWorldBlock: rack") @@ -195,9 +140,8 @@ object BlockRenderer extends ISimpleBlockRenderingHandler { } } - val isOneSevenTwo = Loader.instance.getMinecraftModContainer.getVersion == "1.7.2" - - def patchedRenderer(renderer: RenderBlocks) = if (isOneSevenTwo) { + // The texture flip this works around only seems to occur for blocks with custom block renderers? + def patchedRenderer(renderer: RenderBlocks, block: Block) = if (block.isInstanceOf[Hologram] || block.isInstanceOf[Printer]) { PatchedRenderBlocks.blockAccess = renderer.blockAccess PatchedRenderBlocks.overrideBlockTexture = renderer.overrideBlockTexture PatchedRenderBlocks.flipTexture = renderer.flipTexture diff --git a/src/main/scala/li/cil/oc/client/renderer/block/Keyboard.scala b/src/main/scala/li/cil/oc/client/renderer/block/Keyboard.scala new file mode 100644 index 000000000..3fb92ff5d --- /dev/null +++ b/src/main/scala/li/cil/oc/client/renderer/block/Keyboard.scala @@ -0,0 +1,36 @@ +package li.cil.oc.client.renderer.block + +import li.cil.oc.common.tileentity +import net.minecraft.block.Block +import net.minecraft.client.renderer.RenderBlocks +import net.minecraftforge.common.util.ForgeDirection + +object Keyboard { + def render(keyboard: tileentity.Keyboard, x: Int, y: Int, z: Int, block: Block, renderer: RenderBlocks): Boolean = { + if (keyboard.facing == ForgeDirection.UP || keyboard.facing == ForgeDirection.DOWN) { + keyboard.yaw match { + case ForgeDirection.NORTH => + renderer.uvRotateTop = 0 + renderer.uvRotateBottom = 0 + case ForgeDirection.SOUTH => + renderer.uvRotateTop = 3 + renderer.uvRotateBottom = 3 + case ForgeDirection.WEST => + renderer.uvRotateTop = 2 + renderer.uvRotateBottom = 1 + case ForgeDirection.EAST => + renderer.uvRotateTop = 1 + renderer.uvRotateBottom = 2 + case _ => throw new AssertionError("Impossible yaw value on keyboard.") + } + if (keyboard.facing == ForgeDirection.DOWN) { + renderer.flipTexture = true + } + } + val result = renderer.renderStandardBlock(block, x, y, z) + renderer.uvRotateTop = 0 + renderer.uvRotateBottom = 0 + renderer.flipTexture = false + result + } +} diff --git a/src/main/scala/li/cil/oc/client/renderer/block/Print.scala b/src/main/scala/li/cil/oc/client/renderer/block/Print.scala new file mode 100644 index 000000000..fbbc79c20 --- /dev/null +++ b/src/main/scala/li/cil/oc/client/renderer/block/Print.scala @@ -0,0 +1,29 @@ +package li.cil.oc.client.renderer.block + +import li.cil.oc.common.tileentity +import li.cil.oc.util.ExtendedAABB._ +import net.minecraft.block.Block +import net.minecraft.client.Minecraft +import net.minecraft.client.renderer.RenderBlocks +import net.minecraft.client.renderer.texture.TextureMap +import net.minecraft.util.IIcon + +object Print { + def render(print: tileentity.Print, x: Int, y: Int, z: Int, block: Block, renderer: RenderBlocks): Unit = { + for (shape <- if (print.state) print.data.stateOn else print.data.stateOff) { + val bounds = shape.bounds.rotateTowards(print.facing) + renderer.setOverrideBlockTexture(resolveTexture(shape.texture)) + renderer.setRenderBounds( + bounds.minX, bounds.minY, bounds.minZ, + bounds.maxX, bounds.maxY, bounds.maxZ) + renderer.renderStandardBlock(block, x, y, z) + } + renderer.clearOverrideBlockTexture() + } + + def resolveTexture(name: String): IIcon = { + val icon = Minecraft.getMinecraft.getTextureMapBlocks.getTextureExtry(name) + if (icon == null) Minecraft.getMinecraft.getTextureManager.getTexture(TextureMap.locationBlocksTexture).asInstanceOf[TextureMap].getAtlasSprite("missingno") + else icon + } +} diff --git a/src/main/scala/li/cil/oc/client/renderer/block/Printer.scala b/src/main/scala/li/cil/oc/client/renderer/block/Printer.scala new file mode 100644 index 000000000..860ff381d --- /dev/null +++ b/src/main/scala/li/cil/oc/client/renderer/block/Printer.scala @@ -0,0 +1,68 @@ +package li.cil.oc.client.renderer.block + +import net.minecraft.block.Block +import net.minecraft.client.renderer.RenderBlocks + +object Printer { + def render(block: Block, x: Int, y: Int, z: Int, renderer: RenderBlocks) { + val previousRenderAllFaces = renderer.renderAllFaces + renderer.renderAllFaces = true + + // Bottom. + renderer.setRenderBounds(0, 0, 0, 1, 8 / 16f, 1) + renderer.renderStandardBlock(block, x, y, z) + // Corners. + renderer.setRenderBounds(0 / 16f, 8 / 16f, 0 / 16f, 3 / 16f, 16 / 16f, 3 / 16f) + renderer.renderStandardBlock(block, x, y, z) + renderer.setRenderBounds(13 / 16f, 8 / 16f, 0 / 16f, 16 / 16f, 16 / 16f, 3 / 16f) + renderer.renderStandardBlock(block, x, y, z) + renderer.setRenderBounds(0 / 16f, 8 / 16f, 13 / 16f, 3 / 16f, 16 / 16f, 16 / 16f) + renderer.renderStandardBlock(block, x, y, z) + renderer.setRenderBounds(13 / 16f, 8 / 16f, 13 / 16f, 16 / 16f, 16 / 16f, 16 / 16f) + renderer.renderStandardBlock(block, x, y, z) + // Top. + renderer.setRenderBounds(3 / 16f, 13 / 16f, 0 / 16f, 13 / 16f, 16 / 16f, 3 / 16f) + renderer.renderStandardBlock(block, x, y, z) + renderer.setRenderBounds(3 / 16f, 13 / 16f, 13 / 16f, 13 / 16f, 16 / 16f, 16 / 16f) + renderer.renderStandardBlock(block, x, y, z) + renderer.setRenderBounds(0 / 16f, 13 / 16f, 3 / 16f, 3 / 16f, 16 / 16f, 13 / 16f) + renderer.renderStandardBlock(block, x, y, z) + renderer.setRenderBounds(13 / 16f, 13 / 16f, 3 / 16f, 16 / 16f, 16 / 16f, 13 / 16f) + renderer.renderStandardBlock(block, x, y, z) + + renderer.renderAllFaces = previousRenderAllFaces + } + + def render(block: Block, metadata: Int, renderer: RenderBlocks) { + // Bottom. + renderer.setRenderBounds(0, 0, 0, 1, 8 / 16f, 1) + renderAllFaces(block, metadata, renderer) + // Corners. + renderer.setRenderBounds(0 / 16f, 8 / 16f, 0 / 16f, 3 / 16f, 16 / 16f, 3 / 16f) + renderAllFaces(block, metadata, renderer) + renderer.setRenderBounds(13 / 16f, 8 / 16f, 0 / 16f, 16 / 16f, 16 / 16f, 3 / 16f) + renderAllFaces(block, metadata, renderer) + renderer.setRenderBounds(0 / 16f, 8 / 16f, 13 / 16f, 3 / 16f, 16 / 16f, 16 / 16f) + renderAllFaces(block, metadata, renderer) + renderer.setRenderBounds(13 / 16f, 8 / 16f, 13 / 16f, 16 / 16f, 16 / 16f, 16 / 16f) + renderAllFaces(block, metadata, renderer) + // Top. + renderer.setRenderBounds(3 / 16f, 13 / 16f, 0 / 16f, 13 / 16f, 16 / 16f, 3 / 16f) + renderAllFaces(block, metadata, renderer) + renderer.setRenderBounds(3 / 16f, 13 / 16f, 13 / 16f, 13 / 16f, 16 / 16f, 16 / 16f) + renderAllFaces(block, metadata, renderer) + renderer.setRenderBounds(0 / 16f, 13 / 16f, 3 / 16f, 3 / 16f, 16 / 16f, 13 / 16f) + renderAllFaces(block, metadata, renderer) + renderer.setRenderBounds(13 / 16f, 13 / 16f, 3 / 16f, 16 / 16f, 16 / 16f, 13 / 16f) + renderAllFaces(block, metadata, renderer) + } + + private def renderAllFaces(block: Block, metadata: Int, renderer: RenderBlocks): Unit = { + BlockRenderer.renderFaceYPos(block, metadata, renderer) + BlockRenderer.renderFaceYNeg(block, metadata, renderer) + BlockRenderer.renderFaceXPos(block, metadata, renderer) + BlockRenderer.renderFaceXNeg(block, metadata, renderer) + BlockRenderer.renderFaceZPos(block, metadata, renderer) + BlockRenderer.renderFaceZNeg(block, metadata, renderer) + } +} diff --git a/src/main/scala/li/cil/oc/client/renderer/block/ServerRack.scala b/src/main/scala/li/cil/oc/client/renderer/block/ServerRack.scala new file mode 100644 index 000000000..fdf3fba32 --- /dev/null +++ b/src/main/scala/li/cil/oc/client/renderer/block/ServerRack.scala @@ -0,0 +1,62 @@ +package li.cil.oc.client.renderer.block + +import li.cil.oc.client.Textures +import li.cil.oc.common.tileentity +import net.minecraft.block.Block +import net.minecraft.client.renderer.RenderBlocks +import net.minecraftforge.common.util.ForgeDirection + +object ServerRack { + def render(rack: tileentity.ServerRack, x: Int, y: Int, z: Int, block: Block, renderer: RenderBlocks): Unit = { + val previousRenderAllFaces = renderer.renderAllFaces + val u1 = 1 / 16f + val u2 = 15 / 16f + val v1 = 2 / 16f + val v2 = 14 / 16f + val fs = 3 / 16f + + // Top and bottom. + renderer.renderAllFaces = true + renderer.setRenderBounds(0, 0, 0, 1, v1, 1) + renderer.renderStandardBlock(block, x, y, z) + renderer.setRenderBounds(0, v2, 0, 1, 1, 1) + renderer.renderStandardBlock(block, x, y, z) + + // Sides. + val front = rack.facing + def renderSide(side: ForgeDirection, lx: Double, lz: Double, hx: Double, hz: Double) { + if (side == front) { + for (i <- 0 until 4 if rack.isPresent(i).isDefined) { + side match { + case ForgeDirection.WEST => + renderer.setRenderBounds(lx, v2 - (i + 1) * fs, lz + u1, u2, v2 - i * fs, hz - u1) + case ForgeDirection.EAST => + renderer.setRenderBounds(u1, v2 - (i + 1) * fs, lz + u1, hx, v2 - i * fs, hz - u1) + case ForgeDirection.NORTH => + renderer.setRenderBounds(lx + u1, v2 - (i + 1) * fs, lz, hx - u1, v2 - i * fs, u2) + case ForgeDirection.SOUTH => + renderer.setRenderBounds(lx + u1, v2 - (i + 1) * fs, u1, hx - u1, v2 - i * fs, hz) + case _ => + } + renderer.renderStandardBlock(block, x, y, z) + } + } + else { + val isBack = front == side.getOpposite + if (isBack) { + renderer.setOverrideBlockTexture(Textures.ServerRack.icons(ForgeDirection.NORTH.ordinal)) + } + renderer.setRenderBounds(lx, v1, lz, hx, v2, hz) + renderer.renderStandardBlock(block, x, y, z) + renderer.clearOverrideBlockTexture() + } + } + + renderSide(ForgeDirection.WEST, 0, 0, u1, 1) + renderSide(ForgeDirection.EAST, u2, 0, 1, 1) + renderSide(ForgeDirection.NORTH, 0, 0, 1, u1) + renderSide(ForgeDirection.SOUTH, 0, u2, 1, 1) + + renderer.renderAllFaces = previousRenderAllFaces + } +} diff --git a/src/main/scala/li/cil/oc/client/renderer/item/ItemRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/item/ItemRenderer.scala index 59e8286de..4ed009a97 100644 --- a/src/main/scala/li/cil/oc/client/renderer/item/ItemRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/item/ItemRenderer.scala @@ -4,13 +4,16 @@ import li.cil.oc.Settings import li.cil.oc.api import li.cil.oc.api.detail.ItemInfo import li.cil.oc.client.Textures +import li.cil.oc.client.renderer.block.Print import li.cil.oc.client.renderer.entity.DroneRenderer +import li.cil.oc.common.item.data.PrintData import li.cil.oc.integration.opencomputers.Item import li.cil.oc.util.RenderState import net.minecraft.client.Minecraft import net.minecraft.client.gui.ScaledResolution import net.minecraft.client.renderer.entity.RenderItem import net.minecraft.client.renderer.entity.RenderManager +import net.minecraft.client.renderer.texture.TextureMap import net.minecraft.item.ItemStack import net.minecraft.util.AxisAlignedBB import net.minecraft.util.EnumChatFormatting @@ -32,6 +35,7 @@ object ItemRenderer extends IItemRenderer { lazy val floppy = api.Items.get("floppy") lazy val lootDisk = api.Items.get("lootDisk") + lazy val print = api.Items.get("print") def bounds = AxisAlignedBB.getBoundingBox(-0.1, -0.1, -0.1, 0.1, 0.1, 0.1) @@ -48,11 +52,13 @@ object ItemRenderer extends IItemRenderer { val descriptor = api.Items.get(stack) (renderType == ItemRenderType.EQUIPPED && isUpgrade(api.Items.get(stack))) || (renderType == ItemRenderType.INVENTORY && isFloppy(api.Items.get(stack))) || - ((renderType == ItemRenderType.INVENTORY || renderType == ItemRenderType.ENTITY || renderType == ItemRenderType.EQUIPPED || renderType == ItemRenderType.EQUIPPED_FIRST_PERSON) && descriptor == drone) + ((renderType == ItemRenderType.INVENTORY || renderType == ItemRenderType.ENTITY || renderType == ItemRenderType.EQUIPPED || renderType == ItemRenderType.EQUIPPED_FIRST_PERSON) && descriptor == drone) || + ((renderType == ItemRenderType.INVENTORY || renderType == ItemRenderType.ENTITY || renderType == ItemRenderType.EQUIPPED || renderType == ItemRenderType.EQUIPPED_FIRST_PERSON) && api.Items.get(stack) == print) } override def shouldUseRenderHelper(renderType: ItemRenderType, stack: ItemStack, helper: ItemRendererHelper) = if (renderType == ItemRenderType.ENTITY) true + else if (renderType == ItemRenderType.INVENTORY && api.Items.get(stack) == print) helper == ItemRendererHelper.INVENTORY_BLOCK // Note: it's easier to revert changes introduced by this "helper" than by // the code that applies if no helper is used... else helper == ItemRendererHelper.EQUIPPED_BLOCK @@ -116,6 +122,7 @@ object ItemRenderer extends IItemRenderer { RenderState.checkError("ItemRenderer.renderItem: floppy") } + else if (descriptor == drone) { GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS) GL11.glPushMatrix() @@ -144,6 +151,26 @@ object ItemRenderer extends IItemRenderer { RenderState.checkError("ItemRenderer.renderItem: drone") } + else if (descriptor == print) { + GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS) + GL11.glPushMatrix() + + if (renderType == ItemRenderType.ENTITY) { + GL11.glTranslatef(-0.5f, 0, -0.5f) + } + + val data = new PrintData(stack) + Minecraft.getMinecraft.renderEngine.bindTexture(TextureMap.locationBlocksTexture) + for (shape <- data.stateOff) { + drawShape(shape) + } + + GL11.glPopMatrix() + GL11.glPopAttrib() + + RenderState.checkError("ItemRenderer.renderItem: print") + } + RenderState.checkError("ItemRenderer.renderItem: leaving") } @@ -207,4 +234,79 @@ object ItemRenderer extends IItemRenderer { GL11.glEnd() } + + private def drawShape(shape: PrintData.Shape) { + val bounds = shape.bounds + val texture = Print.resolveTexture(shape.texture) + + GL11.glBegin(GL11.GL_QUADS) + + // Front. + GL11.glNormal3f(0, 0, 1) + GL11.glTexCoord2f(texture.getInterpolatedU(bounds.minX * 16), texture.getInterpolatedV(bounds.minY * 16)) + GL11.glVertex3d(bounds.minX, bounds.minY, bounds.maxZ) + GL11.glTexCoord2f(texture.getInterpolatedU(bounds.maxX * 16), texture.getInterpolatedV(bounds.minY * 16)) + GL11.glVertex3d(bounds.maxX, bounds.minY, bounds.maxZ) + GL11.glTexCoord2f(texture.getInterpolatedU(bounds.maxX * 16), texture.getInterpolatedV(bounds.maxY * 16)) + GL11.glVertex3d(bounds.maxX, bounds.maxY, bounds.maxZ) + GL11.glTexCoord2f(texture.getInterpolatedU(bounds.minX * 16), texture.getInterpolatedV(bounds.maxY * 16)) + GL11.glVertex3d(bounds.minX, bounds.maxY, bounds.maxZ) + + // Back. + GL11.glNormal3f(0, 0, -1) + GL11.glTexCoord2f(texture.getInterpolatedU(bounds.maxX * 16), texture.getInterpolatedV(bounds.minY * 16)) + GL11.glVertex3d(bounds.maxX, bounds.minY, bounds.minZ) + GL11.glTexCoord2f(texture.getInterpolatedU(bounds.minX * 16), texture.getInterpolatedV(bounds.minY * 16)) + GL11.glVertex3d(bounds.minX, bounds.minY, bounds.minZ) + GL11.glTexCoord2f(texture.getInterpolatedU(bounds.minX * 16), texture.getInterpolatedV(bounds.maxY * 16)) + GL11.glVertex3d(bounds.minX, bounds.maxY, bounds.minZ) + GL11.glTexCoord2f(texture.getInterpolatedU(bounds.maxX * 16), texture.getInterpolatedV(bounds.maxY * 16)) + GL11.glVertex3d(bounds.maxX, bounds.maxY, bounds.minZ) + + // Top. + GL11.glNormal3f(0, 1, 0) + GL11.glTexCoord2f(texture.getInterpolatedU(bounds.maxX * 16), texture.getInterpolatedV(bounds.maxZ * 16)) + GL11.glVertex3d(bounds.maxX, bounds.maxY, bounds.maxZ) + GL11.glTexCoord2f(texture.getInterpolatedU(bounds.maxX * 16), texture.getInterpolatedV(bounds.minZ * 16)) + GL11.glVertex3d(bounds.maxX, bounds.maxY, bounds.minZ) + GL11.glTexCoord2f(texture.getInterpolatedU(bounds.minX * 16), texture.getInterpolatedV(bounds.minZ * 16)) + GL11.glVertex3d(bounds.minX, bounds.maxY, bounds.minZ) + GL11.glTexCoord2f(texture.getInterpolatedU(bounds.minX * 16), texture.getInterpolatedV(bounds.maxZ * 16)) + GL11.glVertex3d(bounds.minX, bounds.maxY, bounds.maxZ) + + // Bottom. + GL11.glNormal3f(0, -1, 0) + GL11.glTexCoord2f(texture.getInterpolatedU(bounds.minX * 16), texture.getInterpolatedV(bounds.maxZ * 16)) + GL11.glVertex3d(bounds.minX, bounds.minY, bounds.maxZ) + GL11.glTexCoord2f(texture.getInterpolatedU(bounds.minX * 16), texture.getInterpolatedV(bounds.minZ * 16)) + GL11.glVertex3d(bounds.minX, bounds.minY, bounds.minZ) + GL11.glTexCoord2f(texture.getInterpolatedU(bounds.maxX * 16), texture.getInterpolatedV(bounds.minZ * 16)) + GL11.glVertex3d(bounds.maxX, bounds.minY, bounds.minZ) + GL11.glTexCoord2f(texture.getInterpolatedU(bounds.maxX * 16), texture.getInterpolatedV(bounds.maxZ * 16)) + GL11.glVertex3d(bounds.maxX, bounds.minY, bounds.maxZ) + + // Left. + GL11.glNormal3f(1, 0, 0) + GL11.glTexCoord2f(texture.getInterpolatedU(bounds.maxY * 16), texture.getInterpolatedV(bounds.maxZ * 16)) + GL11.glVertex3d(bounds.maxX, bounds.maxY, bounds.maxZ) + GL11.glTexCoord2f(texture.getInterpolatedU(bounds.minY * 16), texture.getInterpolatedV(bounds.maxZ * 16)) + GL11.glVertex3d(bounds.maxX, bounds.minY, bounds.maxZ) + GL11.glTexCoord2f(texture.getInterpolatedU(bounds.minY * 16), texture.getInterpolatedV(bounds.minZ * 16)) + GL11.glVertex3d(bounds.maxX, bounds.minY, bounds.minZ) + GL11.glTexCoord2f(texture.getInterpolatedU(bounds.maxY * 16), texture.getInterpolatedV(bounds.minZ * 16)) + GL11.glVertex3d(bounds.maxX, bounds.maxY, bounds.minZ) + + // Right. + GL11.glNormal3f(-1, 0, 0) + GL11.glTexCoord2f(texture.getInterpolatedU(bounds.minY * 16), texture.getInterpolatedV(bounds.maxZ * 16)) + GL11.glVertex3d(bounds.minX, bounds.minY, bounds.maxZ) + GL11.glTexCoord2f(texture.getInterpolatedU(bounds.maxY * 16), texture.getInterpolatedV(bounds.maxZ * 16)) + GL11.glVertex3d(bounds.minX, bounds.maxY, bounds.maxZ) + GL11.glTexCoord2f(texture.getInterpolatedU(bounds.maxY * 16), texture.getInterpolatedV(bounds.minZ * 16)) + GL11.glVertex3d(bounds.minX, bounds.maxY, bounds.minZ) + GL11.glTexCoord2f(texture.getInterpolatedU(bounds.minY * 16), texture.getInterpolatedV(bounds.minZ * 16)) + GL11.glVertex3d(bounds.minX, bounds.minY, bounds.minZ) + + GL11.glEnd() + } } diff --git a/src/main/scala/li/cil/oc/client/renderer/tileentity/PrinterRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/tileentity/PrinterRenderer.scala new file mode 100644 index 000000000..5e1c79f92 --- /dev/null +++ b/src/main/scala/li/cil/oc/client/renderer/tileentity/PrinterRenderer.scala @@ -0,0 +1,46 @@ +package li.cil.oc.client.renderer.tileentity + +import li.cil.oc.common.tileentity.Printer +import li.cil.oc.util.RenderState +import net.minecraft.client.renderer.OpenGlHelper +import net.minecraft.client.renderer.entity.RenderItem +import net.minecraft.client.renderer.entity.RenderManager +import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer +import net.minecraft.entity.item.EntityItem +import net.minecraft.tileentity.TileEntity +import org.lwjgl.opengl.GL11 + +object PrinterRenderer extends TileEntitySpecialRenderer { + override def renderTileEntityAt(tileEntity: TileEntity, x: Double, y: Double, z: Double, f: Float) { + RenderState.checkError(getClass.getName + ".renderTileEntityAt: entering (aka: wasntme)") + + val printer = tileEntity.asInstanceOf[Printer] + if (printer.data.stateOff.size > 0) { + val stack = printer.data.createItemStack() + + GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS) + GL11.glPushMatrix() + + GL11.glTranslated(x + 0.5, y + 0.5, z + 0.5) + +// GL11.glTranslated(-0.5, -0.5, -0.5) + GL11.glRotated((System.currentTimeMillis() % 20000) / 20000.0 * 360, 0, 1, 0) +// GL11.glTranslated(0.5, 0.5, 0.5) + + val brightness = printer.world.getLightBrightnessForSkyBlocks(printer.x, printer.y, printer.z, 0) + OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, brightness % 65536, brightness / 65536) + + // This is very 'meh', but item frames do it like this, too! + val entity = new EntityItem(printer.world, 0, 0, 0, stack) + entity.hoverStart = 0 + RenderItem.renderInFrame = true + RenderManager.instance.renderEntityWithPosYaw(entity, 0, -0.1, 0, 0, 0) + RenderItem.renderInFrame = false + + 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/GuiHandler.scala b/src/main/scala/li/cil/oc/common/GuiHandler.scala index 366963ebc..b6a42bfee 100644 --- a/src/main/scala/li/cil/oc/common/GuiHandler.scala +++ b/src/main/scala/li/cil/oc/common/GuiHandler.scala @@ -24,6 +24,8 @@ abstract class GuiHandler extends IGuiHandler { new container.Disassembler(player.inventory, t) case t: tileentity.DiskDrive if id == GuiType.DiskDrive.id => new container.DiskDrive(player.inventory, t) + case t: tileentity.Printer if id == GuiType.Printer.id => + new container.Printer(player.inventory, t) case t: tileentity.Raid if id == GuiType.Raid.id => new container.Raid(player.inventory, t) case t: tileentity.RobotProxy if id == GuiType.Robot.id => diff --git a/src/main/scala/li/cil/oc/common/GuiType.scala b/src/main/scala/li/cil/oc/common/GuiType.scala index 7628f8178..17927f40b 100644 --- a/src/main/scala/li/cil/oc/common/GuiType.scala +++ b/src/main/scala/li/cil/oc/common/GuiType.scala @@ -21,6 +21,7 @@ object GuiType extends ScalaEnum { val Disassembler = new EnumVal { def name = "Disassembler"; def subType = GuiType.Category.Block } val DiskDrive = new EnumVal { def name = "DiskDrive"; def subType = GuiType.Category.Block } val Drone = new EnumVal { def name = "Drone"; def subType = GuiType.Category.Entity } + val Printer = new EnumVal { def name = "Printer"; def subType = GuiType.Category.Block } val Rack = new EnumVal { def name = "Rack"; def subType = GuiType.Category.Block } val Raid = new EnumVal { def name = "Raid"; def subType = GuiType.Category.Block } val Robot = new EnumVal { def name = "Robot"; def subType = GuiType.Category.Block } diff --git a/src/main/scala/li/cil/oc/common/PacketType.scala b/src/main/scala/li/cil/oc/common/PacketType.scala index 6a0a0f201..d34b1d04f 100644 --- a/src/main/scala/li/cil/oc/common/PacketType.scala +++ b/src/main/scala/li/cil/oc/common/PacketType.scala @@ -9,6 +9,7 @@ object PacketType extends Enumeration { ColorChange, ComputerState, ComputerUserList, + ContainerUpdate, DisassemblerActiveChange, FileSystemActivity, FloppyChange, @@ -20,6 +21,7 @@ object PacketType extends Enumeration { HologramTranslation, PetVisibility, // Goes both ways. PowerState, + PrinterState, RaidStateChange, RedstoneState, RobotAnimateSwing, diff --git a/src/main/scala/li/cil/oc/common/Slot.scala b/src/main/scala/li/cil/oc/common/Slot.scala index 4cbb05bd6..fed8e914b 100644 --- a/src/main/scala/li/cil/oc/common/Slot.scala +++ b/src/main/scala/li/cil/oc/common/Slot.scala @@ -5,6 +5,7 @@ import li.cil.oc.api.driver object Slot { val None = driver.item.Slot.None val Any = driver.item.Slot.Any + val Filtered = "filtered" val Card = driver.item.Slot.Card val ComponentBus = driver.item.Slot.ComponentBus diff --git a/src/main/scala/li/cil/oc/common/block/Item.scala b/src/main/scala/li/cil/oc/common/block/Item.scala index 022012ada..42a121d6b 100644 --- a/src/main/scala/li/cil/oc/common/block/Item.scala +++ b/src/main/scala/li/cil/oc/common/block/Item.scala @@ -5,6 +5,7 @@ import java.util import li.cil.oc.Settings import li.cil.oc.api import li.cil.oc.client.KeyBindings +import li.cil.oc.common.item.data.PrintData import li.cil.oc.common.item.data.RobotData import li.cil.oc.common.tileentity import li.cil.oc.util.ItemCosts @@ -47,6 +48,14 @@ class Item(value: Block) extends ItemBlock(value) { override def getMetadata(itemDamage: Int) = itemDamage + override def getItemStackDisplayName(stack: ItemStack): String = { + if (api.Items.get(stack) == api.Items.get("print")) { + val data = new PrintData(stack) + data.label.getOrElse(super.getItemStackDisplayName(stack)) + } + else super.getItemStackDisplayName(stack) + } + override def getUnlocalizedName = block match { case simple: SimpleBlock => simple.getUnlocalizedName case _ => Settings.namespace + "tile" diff --git a/src/main/scala/li/cil/oc/common/block/Microcontroller.scala b/src/main/scala/li/cil/oc/common/block/Microcontroller.scala index c4c475bcd..de1ede9e4 100644 --- a/src/main/scala/li/cil/oc/common/block/Microcontroller.scala +++ b/src/main/scala/li/cil/oc/common/block/Microcontroller.scala @@ -102,8 +102,10 @@ class Microcontroller(protected implicit val tileTag: ClassTag[tileentity.Microc override protected def doCustomInit(tileEntity: tileentity.Microcontroller, player: EntityLivingBase, stack: ItemStack): Unit = { super.doCustomInit(tileEntity, player, stack) - tileEntity.info.load(stack) - tileEntity.snooperNode.changeBuffer(tileEntity.info.storedEnergy - tileEntity.snooperNode.localBuffer) + if (!tileEntity.world.isRemote) { + tileEntity.info.load(stack) + tileEntity.snooperNode.changeBuffer(tileEntity.info.storedEnergy - tileEntity.snooperNode.localBuffer) + } } override protected def doCustomDrops(tileEntity: tileentity.Microcontroller, player: EntityPlayer, willHarvest: Boolean): Unit = { diff --git a/src/main/scala/li/cil/oc/common/block/Print.scala b/src/main/scala/li/cil/oc/common/block/Print.scala new file mode 100644 index 000000000..cfcb70fa7 --- /dev/null +++ b/src/main/scala/li/cil/oc/common/block/Print.scala @@ -0,0 +1,160 @@ +package li.cil.oc.common.block + +import java.util +import java.util.Random + +import li.cil.oc.Settings +import li.cil.oc.common.item.data.PrintData +import li.cil.oc.common.tileentity +import li.cil.oc.integration.util.NEI +import li.cil.oc.util.ExtendedAABB +import li.cil.oc.util.ExtendedAABB._ +import net.minecraft.entity.Entity +import net.minecraft.entity.EntityLivingBase +import net.minecraft.entity.EnumCreatureType +import net.minecraft.entity.player.EntityPlayer +import net.minecraft.item.ItemStack +import net.minecraft.util.AxisAlignedBB +import net.minecraft.util.MovingObjectPosition +import net.minecraft.util.Vec3 +import net.minecraft.world.IBlockAccess +import net.minecraft.world.World +import net.minecraftforge.common.util.ForgeDirection + +import scala.collection.convert.WrapAsJava._ +import scala.reflect.ClassTag + +class Print(protected implicit val tileTag: ClassTag[tileentity.Print]) extends RedstoneAware with traits.SpecialBlock with traits.CustomDrops[tileentity.Print] { + setLightOpacity(0) + setHardness(1) + setCreativeTab(null) + NEI.hide(this) + setBlockTextureName(Settings.resourceDomain + "GenericTop") + + override protected def tooltipBody(metadata: Int, stack: ItemStack, player: EntityPlayer, tooltip: util.List[String], advanced: Boolean): Unit = { + super.tooltipBody(metadata, stack, player, tooltip, advanced) + val data = new PrintData(stack) + data.tooltip.foreach(s => tooltip.addAll(s.lines.toIterable)) + } + + override def shouldSideBeRendered(world: IBlockAccess, x: Int, y: Int, z: Int, side: ForgeDirection) = true + + override def isBlockSolid(world: IBlockAccess, x: Int, y: Int, z: Int, side: ForgeDirection) = isSideSolid(world, x, y, z, side) + + override def isSideSolid(world: IBlockAccess, x: Int, y: Int, z: Int, side: ForgeDirection): Boolean = { + world.getTileEntity(x, y, z) match { + case print: tileentity.Print => + val shapes = if (print.state) print.data.stateOn else print.data.stateOff + for (shape <- shapes) { + val bounds = shape.bounds + val fullX = bounds.minX == 0 && bounds.maxX == 1 + val fullY = bounds.minY == 0 && bounds.maxY == 1 + val fullZ = bounds.minZ == 0 && bounds.maxZ == 1 + if (side match { + case ForgeDirection.DOWN => bounds.minY == 0 && fullX && fullZ + case ForgeDirection.UP => bounds.maxY == 1 && fullX && fullZ + case ForgeDirection.NORTH => bounds.minZ == 0 && fullX && fullY + case ForgeDirection.SOUTH => bounds.maxZ == 1 && fullX && fullY + case ForgeDirection.WEST => bounds.minX == 0 && fullY && fullZ + case ForgeDirection.EAST => bounds.maxX == 1 && fullY && fullZ + case _ => false + }) return true + } + case _ => + } + false + } + + override def getPickBlock(target: MovingObjectPosition, world: World, x: Int, y: Int, z: Int, player: EntityPlayer): ItemStack = { + world.getTileEntity(x, y, z) match { + case print: tileentity.Print => print.data.createItemStack() + case _ => null + } + } + + override def addCollisionBoxesToList(world: World, x: Int, y: Int, z: Int, mask: AxisAlignedBB, list: util.List[_], entity: Entity): Unit = { + world.getTileEntity(x, y, z) match { + case print: tileentity.Print => + def add[T](list: util.List[T], value: Any) = list.add(value.asInstanceOf[T]) + val shapes = if (print.state) print.data.stateOn else print.data.stateOff + for (shape <- shapes) { + val bounds = shape.bounds.rotateTowards(print.facing).offset(x, y, z) + if (bounds.intersectsWith(mask)) { + add(list, bounds) + } + } + case _ => super.addCollisionBoxesToList(world, x, y, z, mask, list, entity) + } + } + + override protected def intersect(world: World, x: Int, y: Int, z: Int, origin: Vec3, direction: Vec3): MovingObjectPosition = { + world.getTileEntity(x, y, z) match { + case print: tileentity.Print => + var closestDistance = Double.PositiveInfinity + var closest: Option[MovingObjectPosition] = None + for (shape <- if (print.state) print.data.stateOn else print.data.stateOff) { + val bounds = shape.bounds.rotateTowards(print.facing).offset(x, y, z) + val hit = bounds.calculateIntercept(origin, direction) + if (hit != null) { + val distance = hit.hitVec.distanceTo(origin) + if (distance < closestDistance) { + closestDistance = distance + closest = Option(hit) + } + } + } + closest.map(hit => new MovingObjectPosition(x, y, z, hit.sideHit, hit.hitVec)).orNull + case _ => super.intersect(world, x, y, z, origin, direction) + } + } + + override protected def doSetBlockBoundsBasedOnState(world: IBlockAccess, x: Int, y: Int, z: Int): Unit = { + world.getTileEntity(x, y, z) match { + case print: tileentity.Print => setBlockBounds(if (print.state) print.boundsOn else print.boundsOff) + case _ => super.doSetBlockBoundsBasedOnState(world, x, y, z) + } + } + + override def setBlockBoundsForItemRender(metadata: Int): Unit = { + setBlockBounds(ExtendedAABB.unitBounds) + } + + override def canCreatureSpawn(creature: EnumCreatureType, world: IBlockAccess, x: Int, y: Int, z: Int): Boolean = true + + override def tickRate(world: World) = 20 + + override def updateTick(world: World, x: Int, y: Int, z: Int, rng: Random): Unit = { + if (!world.isRemote) world.getTileEntity(x, y, z) match { + case print: tileentity.Print => if (print.state) print.toggleState() + case _ => + } + } + + // ----------------------------------------------------------------------- // + + override def hasTileEntity(metadata: Int) = true + + override def createTileEntity(world: World, metadata: Int) = new tileentity.Print() + + // ----------------------------------------------------------------------- // + + override def onBlockActivated(world: World, x: Int, y: Int, z: Int, player: EntityPlayer, side: ForgeDirection, hitX: Float, hitY: Float, hitZ: Float): Boolean = { + world.getTileEntity(x, y, z) match { + case print: tileentity.Print => print.activate() + case _ => super.onBlockActivated(world, x, y, z, player, side, hitX, hitY, hitZ) + } + } + + override protected def doCustomInit(tileEntity: tileentity.Print, player: EntityLivingBase, stack: ItemStack): Unit = { + super.doCustomInit(tileEntity, player, stack) + tileEntity.data.load(stack) + tileEntity.updateBounds() + } + + override protected def doCustomDrops(tileEntity: tileentity.Print, player: EntityPlayer, willHarvest: Boolean): Unit = { + super.doCustomDrops(tileEntity, player, willHarvest) + if (!player.capabilities.isCreativeMode) { + dropBlockAsItem(tileEntity.world, tileEntity.x, tileEntity.y, tileEntity.z, tileEntity.data.createItemStack()) + } + } +} diff --git a/src/main/scala/li/cil/oc/common/block/Printer.scala b/src/main/scala/li/cil/oc/common/block/Printer.scala new file mode 100644 index 000000000..1590426fc --- /dev/null +++ b/src/main/scala/li/cil/oc/common/block/Printer.scala @@ -0,0 +1,40 @@ +package li.cil.oc.common.block + +import li.cil.oc.Settings +import li.cil.oc.client.Textures +import li.cil.oc.common.GuiType +import li.cil.oc.common.tileentity +import net.minecraft.client.renderer.texture.IIconRegister +import net.minecraft.world.IBlockAccess +import net.minecraft.world.World +import net.minecraftforge.common.util.ForgeDirection + +class Printer extends SimpleBlock with traits.SpecialBlock with traits.StateAware with traits.GUI { + override protected def customTextures = Array( + None, + Some("PrinterTop"), + Some("PrinterSide"), + Some("PrinterSide"), + Some("PrinterSide"), + Some("PrinterSide") + ) + + override def registerBlockIcons(iconRegister: IIconRegister) = { + super.registerBlockIcons(iconRegister) + Textures.Assembler.iconSideAssembling = iconRegister.registerIcon(Settings.resourceDomain + ":AssemblerSideAssembling") + Textures.Assembler.iconSideOn = iconRegister.registerIcon(Settings.resourceDomain + ":AssemblerSideOn") + Textures.Assembler.iconTopOn = iconRegister.registerIcon(Settings.resourceDomain + ":AssemblerTopOn") + } + + override def isBlockSolid(world: IBlockAccess, x: Int, y: Int, z: Int, side: ForgeDirection) = side == ForgeDirection.DOWN + + override def isSideSolid(world: IBlockAccess, x: Int, y: Int, z: Int, side: ForgeDirection) = side == ForgeDirection.DOWN + + // ----------------------------------------------------------------------- // + + override def guiType = GuiType.Printer + + override def hasTileEntity(metadata: Int) = true + + override def createTileEntity(world: World, metadata: Int) = new tileentity.Printer() +} diff --git a/src/main/scala/li/cil/oc/common/block/Raid.scala b/src/main/scala/li/cil/oc/common/block/Raid.scala index e23b1a959..db0c17f6d 100644 --- a/src/main/scala/li/cil/oc/common/block/Raid.scala +++ b/src/main/scala/li/cil/oc/common/block/Raid.scala @@ -55,14 +55,16 @@ class Raid(protected implicit val tileTag: ClassTag[tileentity.Raid]) extends Si override protected def doCustomInit(tileEntity: tileentity.Raid, player: EntityLivingBase, stack: ItemStack): Unit = { super.doCustomInit(tileEntity, player, stack) - val data = new RaidData(stack) - for (i <- 0 until math.min(data.disks.length, tileEntity.getSizeInventory)) { - tileEntity.setInventorySlotContents(i, data.disks(i)) - } - data.label.foreach(tileEntity.label.setLabel) - if (!data.filesystem.hasNoTags) { - tileEntity.tryCreateRaid(data.filesystem.getCompoundTag("node").getString("address")) - tileEntity.filesystem.foreach(_.load(data.filesystem)) + if (!tileEntity.world.isRemote) { + val data = new RaidData(stack) + for (i <- 0 until math.min(data.disks.length, tileEntity.getSizeInventory)) { + tileEntity.setInventorySlotContents(i, data.disks(i)) + } + data.label.foreach(tileEntity.label.setLabel) + if (!data.filesystem.hasNoTags) { + tileEntity.tryCreateRaid(data.filesystem.getCompoundTag("node").getString("address")) + tileEntity.filesystem.foreach(_.load(data.filesystem)) + } } } diff --git a/src/main/scala/li/cil/oc/common/block/RedstoneAware.scala b/src/main/scala/li/cil/oc/common/block/RedstoneAware.scala index e7f34c0fc..99c8c6175 100644 --- a/src/main/scala/li/cil/oc/common/block/RedstoneAware.scala +++ b/src/main/scala/li/cil/oc/common/block/RedstoneAware.scala @@ -51,7 +51,11 @@ abstract class RedstoneAware extends SimpleBlock with IRedNetOmniNode { } } world.getTileEntity(x, y, z) match { - case redstone: tileentity.traits.RedstoneAware => redstone.checkRedstoneInputChanged() + case redstone: tileentity.traits.RedstoneAware => + if (redstone.canUpdate) + redstone.checkRedstoneInputChanged() + else + ForgeDirection.VALID_DIRECTIONS.foreach(redstone.updateRedstoneInput) case _ => // Ignore. } } diff --git a/src/main/scala/li/cil/oc/common/block/SimpleBlock.scala b/src/main/scala/li/cil/oc/common/block/SimpleBlock.scala index 6d9ae64bc..02b30d99a 100644 --- a/src/main/scala/li/cil/oc/common/block/SimpleBlock.scala +++ b/src/main/scala/li/cil/oc/common/block/SimpleBlock.scala @@ -27,6 +27,7 @@ import net.minecraftforge.common.util.ForgeDirection class SimpleBlock(material: Material = Material.iron) extends Block(material) { setHardness(2f) + setResistance(5) setCreativeTab(CreativeTab) var showInItemList = true diff --git a/src/main/scala/li/cil/oc/common/block/traits/CustomDrops.scala b/src/main/scala/li/cil/oc/common/block/traits/CustomDrops.scala index 8a74a8897..40aafff03 100644 --- a/src/main/scala/li/cil/oc/common/block/traits/CustomDrops.scala +++ b/src/main/scala/li/cil/oc/common/block/traits/CustomDrops.scala @@ -31,12 +31,10 @@ trait CustomDrops[Tile <: TileEntity] extends SimpleBlock { override def onBlockPlacedBy(world: World, x: Int, y: Int, z: Int, player: EntityLivingBase, stack: ItemStack): Unit = { super.onBlockPlacedBy(world, x, y, z, player, stack) - if (!world.isRemote) { - val matcher = tileTag - world.getTileEntity(x, y, z) match { - case matcher(tileEntity) => doCustomInit(tileEntity, player, stack) - case _ => - } + val matcher = tileTag + world.getTileEntity(x, y, z) match { + case matcher(tileEntity) => doCustomInit(tileEntity, player, stack) + case _ => } } diff --git a/src/main/scala/li/cil/oc/common/container/Assembler.scala b/src/main/scala/li/cil/oc/common/container/Assembler.scala index 9829bbd4f..929e21aaf 100644 --- a/src/main/scala/li/cil/oc/common/container/Assembler.scala +++ b/src/main/scala/li/cil/oc/common/container/Assembler.scala @@ -7,8 +7,8 @@ import li.cil.oc.common 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 net.minecraft.entity.player.InventoryPlayer +import net.minecraft.nbt.NBTTagCompound class Assembler(playerInventory: InventoryPlayer, val assembler: tileentity.Assembler) extends Player(playerInventory, assembler) { // Computer case. @@ -67,40 +67,16 @@ class Assembler(playerInventory: InventoryPlayer, val assembler: tileentity.Asse // Show the player's inventory. addPlayerInventorySlots(8, 110) - var isAssembling = false - var assemblyProgress = 0.0 - var assemblyRemainingTime = 0 + def isAssembling = synchronizedData.getBoolean("isAssembling") - @SideOnly(Side.CLIENT) - override def updateProgressBar(id: Int, value: Int) { - super.updateProgressBar(id, value) - if (id == 0) { - isAssembling = value == 1 - } + def assemblyProgress = synchronizedData.getDouble("assemblyProgress") - if (id == 1) { - assemblyProgress = value / 5.0 - } + def assemblyRemainingTime = synchronizedData.getInteger("assemblyRemainingTime") - if (id == 2) { - assemblyRemainingTime = value - } + override protected def detectCustomDataChanges(nbt: NBTTagCompound): Unit = { + synchronizedData.setBoolean("isAssembling", assembler.isAssembling) + synchronizedData.setDouble("assemblyProgress", assembler.progress) + synchronizedData.setInteger("assemblyRemainingTime", assembler.timeRemaining) + super.detectCustomDataChanges(nbt) } - - override def detectAndSendChanges() { - super.detectAndSendChanges() - if (SideTracker.isServer) { - if (isAssembling != assembler.isAssembling) { - isAssembling = assembler.isAssembling - sendProgressBarUpdate(0, if (isAssembling) 1 else 0) - } - val timeRemaining = assembler.timeRemaining - if (math.abs(assembler.progress - assemblyProgress) > 0.2 || assemblyRemainingTime != timeRemaining) { - assemblyProgress = assembler.progress - assemblyRemainingTime = timeRemaining - sendProgressBarUpdate(1, (assemblyProgress * 5).toInt) - sendProgressBarUpdate(2, timeRemaining) - } - } - } -} \ No newline at end of file +} diff --git a/src/main/scala/li/cil/oc/common/container/Disassembler.scala b/src/main/scala/li/cil/oc/common/container/Disassembler.scala index 638de7f29..248e137b8 100644 --- a/src/main/scala/li/cil/oc/common/container/Disassembler.scala +++ b/src/main/scala/li/cil/oc/common/container/Disassembler.scala @@ -1,32 +1,17 @@ package li.cil.oc.common.container -import cpw.mods.fml.relauncher.Side -import cpw.mods.fml.relauncher.SideOnly import li.cil.oc.common.tileentity -import li.cil.oc.util.SideTracker import net.minecraft.entity.player.InventoryPlayer +import net.minecraft.nbt.NBTTagCompound class Disassembler(playerInventory: InventoryPlayer, val disassembler: tileentity.Disassembler) extends Player(playerInventory, disassembler) { addSlotToContainer(80, 35, "ocitem") addPlayerInventorySlots(8, 84) - var disassemblyProgress = 0.0 + def disassemblyProgress = synchronizedData.getDouble("disassemblyProgress") - @SideOnly(Side.CLIENT) - override def updateProgressBar(id: Int, value: Int) { - super.updateProgressBar(id, value) - if (id == 0) { - disassemblyProgress = value / 5.0 - } - } - - override def detectAndSendChanges() { - super.detectAndSendChanges() - if (SideTracker.isServer) { - if (math.abs(disassembler.progress - disassemblyProgress) > 0.2) { - disassemblyProgress = disassembler.progress - sendProgressBarUpdate(0, (disassemblyProgress * 5).toInt) - } - } + override protected def detectCustomDataChanges(nbt: NBTTagCompound): Unit = { + synchronizedData.setDouble("disassemblyProgress", disassembler.progress) + super.detectCustomDataChanges(nbt) } } 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 0d296d213..bc96137b4 100644 --- a/src/main/scala/li/cil/oc/common/container/DynamicComponentSlot.scala +++ b/src/main/scala/li/cil/oc/common/container/DynamicComponentSlot.scala @@ -28,7 +28,7 @@ class DynamicComponentSlot(val container: Player, inventory: IInventory, index: override def getSlotStackLimit = slot match { - case common.Slot.Tool | common.Slot.Any => super.getSlotStackLimit + case common.Slot.Tool | common.Slot.Any | common.Slot.Filtered => super.getSlotStackLimit case common.Slot.None => 0 case _ => 1 } 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 1c8d16aa6..3b9ead2ae 100644 --- a/src/main/scala/li/cil/oc/common/container/Player.scala +++ b/src/main/scala/li/cil/oc/common/container/Player.scala @@ -3,14 +3,18 @@ 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.server.{PacketSender => ServerPacketSender} import li.cil.oc.util.SideTracker import net.minecraft.entity.player.EntityPlayer +import net.minecraft.entity.player.EntityPlayerMP import net.minecraft.entity.player.InventoryPlayer import net.minecraft.inventory.Container import net.minecraft.inventory.ICrafting import net.minecraft.inventory.IInventory import net.minecraft.inventory.Slot import net.minecraft.item.ItemStack +import net.minecraft.nbt.NBTBase +import net.minecraft.nbt.NBTTagCompound import scala.collection.convert.WrapAsScala._ @@ -133,4 +137,104 @@ abstract class Player(val playerInventory: InventoryPlayer, val otherInventory: case _ => } } + + override def detectAndSendChanges(): Unit = { + super.detectAndSendChanges() + if (SideTracker.isServer) { + val nbt = new NBTTagCompound() + detectCustomDataChanges(nbt) + for (entry <- crafters) entry match { + case player: EntityPlayerMP => ServerPacketSender.sendContainerUpdate(this, nbt, player) + case _ => + } + } + } + + // Used for custom value synchronization, because shorts simply don't cut it most of the time. + protected def detectCustomDataChanges(nbt: NBTTagCompound): Unit = { + val delta = synchronizedData.getDelta + if (delta != null && !delta.hasNoTags) { + nbt.setTag("delta", delta) + } + } + + def updateCustomData(nbt: NBTTagCompound): Unit = { + if (nbt.hasKey("delta")) { + val delta = nbt.getCompoundTag("delta") + delta.func_150296_c().foreach { + case key: String => synchronizedData.setTag(key, delta.getTag(key)) + } + } + } + + protected class SynchronizedData extends NBTTagCompound { + private var delta = new NBTTagCompound() + + def getDelta: NBTTagCompound = this.synchronized { + if (delta.hasNoTags) null + else { + val result = delta + delta = new NBTTagCompound() + result + } + } + + override def setTag(key: String, value: NBTBase): Unit = this.synchronized { + if (!value.equals(getTag(key))) delta.setTag(key, value) + super.setTag(key, value) + } + + override def setByte(key: String, value: Byte): Unit = this.synchronized { + if (value != getByte(key)) delta.setByte(key, value) + super.setByte(key, value) + } + + override def setShort(key: String, value: Short): Unit = this.synchronized { + if (value != getShort(key)) delta.setShort(key, value) + super.setShort(key, value) + } + + override def setInteger(key: String, value: Int): Unit = this.synchronized { + if (value != getInteger(key)) delta.setInteger(key, value) + super.setInteger(key, value) + } + + override def setLong(key: String, value: Long): Unit = this.synchronized { + if (value != getLong(key)) delta.setLong(key, value) + super.setLong(key, value) + } + + override def setFloat(key: String, value: Float): Unit = this.synchronized { + if (value != getFloat(key)) delta.setFloat(key, value) + super.setFloat(key, value) + } + + override def setDouble(key: String, value: Double): Unit = this.synchronized { + if (value != getDouble(key)) delta.setDouble(key, value) + super.setDouble(key, value) + } + + override def setString(key: String, value: String): Unit = this.synchronized { + if (value != getString(key)) delta.setString(key, value) + super.setString(key, value) + } + + override def setByteArray(key: String, value: Array[Byte]): Unit = this.synchronized { + if (value.deep != getByteArray(key).deep) delta.setByteArray(key, value) + super.setByteArray(key, value) + } + + override def setIntArray(key: String, value: Array[Int]): Unit = this.synchronized { + if (value.deep != getIntArray(key).deep) delta.setIntArray(key, value) + super.setIntArray(key, value) + } + + override def setBoolean(key: String, value: Boolean): Unit = this.synchronized { + if (value != getBoolean(key)) delta.setBoolean(key, value) + super.setBoolean(key, value) + } + } + + protected val synchronizedData = new SynchronizedData() + } \ No newline at end of file diff --git a/src/main/scala/li/cil/oc/common/container/Printer.scala b/src/main/scala/li/cil/oc/common/container/Printer.scala new file mode 100644 index 000000000..11cf0691b --- /dev/null +++ b/src/main/scala/li/cil/oc/common/container/Printer.scala @@ -0,0 +1,28 @@ +package li.cil.oc.common.container + +import li.cil.oc.common.Slot +import li.cil.oc.common.tileentity +import net.minecraft.entity.player.InventoryPlayer +import net.minecraft.nbt.NBTTagCompound + +class Printer(playerInventory: InventoryPlayer, val printer: tileentity.Printer) extends Player(playerInventory, printer) { + addSlotToContainer(18, 19, Slot.Filtered) + addSlotToContainer(18, 51, Slot.Filtered) + addSlotToContainer(152, 35) + + // Show the player's inventory. + addPlayerInventorySlots(8, 84) + + def isPrinting = synchronizedData.getBoolean("isPrinting") + + def amountMaterial = synchronizedData.getInteger("amountMaterial") + + def amountInk = synchronizedData.getInteger("amountInk") + + override protected def detectCustomDataChanges(nbt: NBTTagCompound): Unit = { + synchronizedData.setBoolean("isPrinting", printer.isPrinting) + synchronizedData.setInteger("amountMaterial", printer.amountMaterial) + synchronizedData.setInteger("amountInk", printer.amountInk) + super.detectCustomDataChanges(nbt) + } +} diff --git a/src/main/scala/li/cil/oc/common/container/StaticComponentSlot.scala b/src/main/scala/li/cil/oc/common/container/StaticComponentSlot.scala index 0155308b4..ea7bae721 100644 --- a/src/main/scala/li/cil/oc/common/container/StaticComponentSlot.scala +++ b/src/main/scala/li/cil/oc/common/container/StaticComponentSlot.scala @@ -12,7 +12,7 @@ class StaticComponentSlot(val container: Player, inventory: IInventory, index: I override def getSlotStackLimit = slot match { - case common.Slot.Tool | common.Slot.Any => super.getSlotStackLimit + case common.Slot.Tool | common.Slot.Any | common.Slot.Filtered => super.getSlotStackLimit case common.Slot.None => 0 case _ => 1 } diff --git a/src/main/scala/li/cil/oc/common/container/Switch.scala b/src/main/scala/li/cil/oc/common/container/Switch.scala index c90cc8d39..ae6c16254 100644 --- a/src/main/scala/li/cil/oc/common/container/Switch.scala +++ b/src/main/scala/li/cil/oc/common/container/Switch.scala @@ -1,9 +1,9 @@ package li.cil.oc.common.container -import cpw.mods.fml.common.FMLCommonHandler import li.cil.oc.common.Slot import li.cil.oc.common.tileentity import net.minecraft.entity.player.InventoryPlayer +import net.minecraft.nbt.NBTTagCompound class Switch(playerInventory: InventoryPlayer, switch: tileentity.Switch) extends Player(playerInventory, switch) { addSlotToContainer(151, 15, Slot.CPU) @@ -11,58 +11,22 @@ class Switch(playerInventory: InventoryPlayer, switch: tileentity.Switch) extend addSlotToContainer(151, 53, Slot.HDD) addPlayerInventorySlots(8, 84) - var relayDelay = 0 + def relayDelay = synchronizedData.getInteger("relayDelay") - var relayAmount = 0 + def relayAmount = synchronizedData.getInteger("relayAmount") - var maxQueueSize = 0 + def maxQueueSize = synchronizedData.getInteger("maxQueueSize") - var packetsPerCycleAvg = 0 + def packetsPerCycleAvg = synchronizedData.getInteger("packetsPerCycleAvg") - var queueSize = 0 + def queueSize = synchronizedData.getInteger("queueSize") - override def updateProgressBar(id: Int, value: Int) { - super.updateProgressBar(id, value) - if (id == 0) { - relayDelay = value - } - else if (id == 1) { - relayAmount = value - } - else if (id == 2) { - maxQueueSize = value - } - else if (id == 3) { - packetsPerCycleAvg = value - } - else if (id == 4) { - queueSize = value - } - } - - override def detectAndSendChanges() { - super.detectAndSendChanges() - if (FMLCommonHandler.instance.getEffectiveSide.isServer) { - if (switch.relayDelay != relayDelay) { - relayDelay = switch.relayDelay - sendProgressBarUpdate(0, relayDelay) - } - if (switch.relayAmount != relayAmount) { - relayAmount = switch.relayAmount - sendProgressBarUpdate(1, relayAmount) - } - if (switch.maxQueueSize != maxQueueSize) { - maxQueueSize = switch.maxQueueSize - sendProgressBarUpdate(2, maxQueueSize) - } - if (switch.packetsPerCycleAvg() != packetsPerCycleAvg) { - packetsPerCycleAvg = switch.packetsPerCycleAvg() - sendProgressBarUpdate(3, packetsPerCycleAvg) - } - if (switch.queue.size != queueSize) { - queueSize = switch.queue.size - sendProgressBarUpdate(4, queueSize) - } - } + override protected def detectCustomDataChanges(nbt: NBTTagCompound): Unit = { + synchronizedData.setInteger("relayDelay", switch.relayDelay) + synchronizedData.setInteger("relayAmount", switch.relayAmount) + synchronizedData.setInteger("maxQueueSize", switch.maxQueueSize) + synchronizedData.setInteger("packetsPerCycleAvg", switch.packetsPerCycleAvg()) + synchronizedData.setInteger("queueSize", switch.queue.size) + super.detectCustomDataChanges(nbt) } } 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 c28554da4..396fc42b9 100644 --- a/src/main/scala/li/cil/oc/common/init/Blocks.scala +++ b/src/main/scala/li/cil/oc/common/init/Blocks.scala @@ -25,6 +25,8 @@ object Blocks { 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") + GameRegistry.registerTileEntity(classOf[tileentity.Print], Settings.namespace + "print") + GameRegistry.registerTileEntity(classOf[tileentity.Printer], Settings.namespace + "printer") GameRegistry.registerTileEntity(classOf[tileentity.Raid], Settings.namespace + "raid") GameRegistry.registerTileEntity(classOf[tileentity.Redstone], Settings.namespace + "redstone") GameRegistry.registerTileEntity(classOf[tileentity.RobotProxy], Settings.namespace + "robot") @@ -81,5 +83,9 @@ object Blocks { // v1.4.2 Recipes.addBlock(new Raid(), "raid", "oc:raid") Items.registerBlock(new Microcontroller(), "microcontroller") + + // v1.5.4 + Items.registerBlock(new Print(), "print") + Items.registerBlock(new Printer(), "printer") } } 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 88fb189e5..d656918a6 100644 --- a/src/main/scala/li/cil/oc/common/init/Items.scala +++ b/src/main/scala/li/cil/oc/common/init/Items.scala @@ -470,5 +470,11 @@ object Items extends ItemAPI { // 1.4.7 Recipes.addMultiItem(new item.TabletCase(multi, Tier.Two), "tabletCase2", "oc:tabletCase2") registerItem(new item.TabletCase(multi, Tier.Four), "tabletCaseCreative") + + // 1.5.4 + Recipes.addMultiItem(new item.InkCartridgeEmpty(multi), "inkCartridgeEmpty", "oc:inkCartridgeEmpty") + Recipes.addMultiItem(new item.InkCartridge(multi), "inkCartridge", "oc:inkCartridge") + Recipes.addMultiItem(new item.Chamelium(multi), "chamelium", "oc:chamelium") + Recipes.addMultiItem(new item.TexturePicker(multi), "texturePicker", "oc:texturePicker") } } diff --git a/src/main/scala/li/cil/oc/common/item/Chamelium.scala b/src/main/scala/li/cil/oc/common/item/Chamelium.scala new file mode 100644 index 000000000..0bcb54cbc --- /dev/null +++ b/src/main/scala/li/cil/oc/common/item/Chamelium.scala @@ -0,0 +1,3 @@ +package li.cil.oc.common.item + +class Chamelium(val parent: Delegator) extends Delegate diff --git a/src/main/scala/li/cil/oc/common/item/InkCartridge.scala b/src/main/scala/li/cil/oc/common/item/InkCartridge.scala new file mode 100644 index 000000000..363af10c2 --- /dev/null +++ b/src/main/scala/li/cil/oc/common/item/InkCartridge.scala @@ -0,0 +1,5 @@ +package li.cil.oc.common.item + +class InkCartridge(val parent: Delegator) extends Delegate { + override def maxStackSize = 1 +} diff --git a/src/main/scala/li/cil/oc/common/item/InkCartridgeEmpty.scala b/src/main/scala/li/cil/oc/common/item/InkCartridgeEmpty.scala new file mode 100644 index 000000000..913460404 --- /dev/null +++ b/src/main/scala/li/cil/oc/common/item/InkCartridgeEmpty.scala @@ -0,0 +1,5 @@ +package li.cil.oc.common.item + +class InkCartridgeEmpty(val parent: Delegator) extends Delegate { + override def maxStackSize = 1 +} diff --git a/src/main/scala/li/cil/oc/common/item/TexturePicker.scala b/src/main/scala/li/cil/oc/common/item/TexturePicker.scala new file mode 100644 index 000000000..fa69ecc10 --- /dev/null +++ b/src/main/scala/li/cil/oc/common/item/TexturePicker.scala @@ -0,0 +1,24 @@ +package li.cil.oc.common.item + +import li.cil.oc.Localization +import li.cil.oc.util.BlockPosition +import li.cil.oc.util.ExtendedWorld._ +import net.minecraft.block.Block +import net.minecraft.entity.player.EntityPlayer +import net.minecraft.item.ItemStack + +class TexturePicker(val parent: Delegator) extends Delegate { + override def onItemUse(stack: ItemStack, player: EntityPlayer, position: BlockPosition, side: Int, hitX: Float, hitY: Float, hitZ: Float): Boolean = { + player.getEntityWorld.getBlock(position) match { + case block: Block => + if (player.getEntityWorld.isRemote) { + val icon = block.getIcon(player.getEntityWorld, position.x, position.y, position.z, side) + if (icon != null) { + player.addChatMessage(Localization.Chat.TextureName(icon.getIconName)) + } + } + true + case _ => super.onItemUse(stack, player, position, side, hitX, hitY, hitZ) + } + } +} diff --git a/src/main/scala/li/cil/oc/common/item/data/ItemData.scala b/src/main/scala/li/cil/oc/common/item/data/ItemData.scala index 1df64ade7..c721c78bb 100644 --- a/src/main/scala/li/cil/oc/common/item/data/ItemData.scala +++ b/src/main/scala/li/cil/oc/common/item/data/ItemData.scala @@ -4,10 +4,6 @@ import li.cil.oc.api.Persistable import net.minecraft.item.ItemStack import net.minecraft.nbt.NBTTagCompound -object ItemData { - -} - abstract class ItemData extends Persistable { def load(stack: ItemStack) { if (stack.hasTagCompound) { diff --git a/src/main/scala/li/cil/oc/common/item/data/PrintData.scala b/src/main/scala/li/cil/oc/common/item/data/PrintData.scala new file mode 100644 index 000000000..ee9bad962 --- /dev/null +++ b/src/main/scala/li/cil/oc/common/item/data/PrintData.scala @@ -0,0 +1,81 @@ +package li.cil.oc.common.item.data + +import li.cil.oc.api +import li.cil.oc.util.ExtendedNBT._ +import net.minecraft.item.ItemStack +import net.minecraft.nbt.NBTTagCompound +import net.minecraft.util.AxisAlignedBB +import net.minecraftforge.common.util.Constants.NBT + +import scala.collection.mutable + +class PrintData extends ItemData { + def this(stack: ItemStack) { + this() + load(stack) + } + + var label: Option[String] = None + var tooltip: Option[String] = None + var isButtonMode = false + var emitRedstone = false + var pressurePlate = false + val stateOff = mutable.Set.empty[PrintData.Shape] + val stateOn = mutable.Set.empty[PrintData.Shape] + + override def load(nbt: NBTTagCompound): Unit = { + if (nbt.hasKey("label")) label = Option(nbt.getString("label")) else label = None + if (nbt.hasKey("tooltip")) tooltip = Option(nbt.getString("tooltip")) else tooltip = None + isButtonMode = nbt.getBoolean("isButtonMode") + emitRedstone = nbt.getBoolean("emitRedstone") + pressurePlate = nbt.getBoolean("pressurePlate") + stateOff.clear() + stateOff ++= nbt.getTagList("stateOff", NBT.TAG_COMPOUND).map(PrintData.nbtToShape) + stateOn.clear() + stateOn ++= nbt.getTagList("stateOn", NBT.TAG_COMPOUND).map(PrintData.nbtToShape) + } + + override def save(nbt: NBTTagCompound): Unit = { + label.foreach(nbt.setString("label", _)) + tooltip.foreach(nbt.setString("tooltip", _)) + nbt.setBoolean("isButtonMode", isButtonMode) + nbt.setBoolean("emitRedstone", emitRedstone) + nbt.setBoolean("pressurePlate", pressurePlate) + nbt.setNewTagList("stateOff", stateOff.map(PrintData.shapeToNBT)) + nbt.setNewTagList("stateOn", stateOn.map(PrintData.shapeToNBT)) + } + + def createItemStack() = { + val stack = api.Items.get("print").createItemStack(1) + save(stack) + stack + } +} + +object PrintData { + def nbtToShape(nbt: NBTTagCompound): Shape = { + val minX = nbt.getByte("minX") / 16f + val minY = nbt.getByte("minY") / 16f + val minZ = nbt.getByte("minZ") / 16f + val maxX = nbt.getByte("maxX") / 16f + val maxY = nbt.getByte("maxY") / 16f + val maxZ = nbt.getByte("maxZ") / 16f + val texture = nbt.getString("texture") + new Shape(AxisAlignedBB.getBoundingBox(minX, minY, minZ, maxX, maxY, maxZ), texture) + } + + def shapeToNBT(shape: Shape): NBTTagCompound = { + val nbt = new NBTTagCompound() + nbt.setByte("minX", (shape.bounds.minX * 16).round.toByte) + nbt.setByte("minY", (shape.bounds.minY * 16).round.toByte) + nbt.setByte("minZ", (shape.bounds.minZ * 16).round.toByte) + nbt.setByte("maxX", (shape.bounds.maxX * 16).round.toByte) + nbt.setByte("maxY", (shape.bounds.maxY * 16).round.toByte) + nbt.setByte("maxZ", (shape.bounds.maxZ * 16).round.toByte) + nbt.setString("texture", shape.texture) + nbt + } + + class Shape(val bounds: AxisAlignedBB, val texture: String) + +} \ No newline at end of file diff --git a/src/main/scala/li/cil/oc/common/tileentity/Print.scala b/src/main/scala/li/cil/oc/common/tileentity/Print.scala new file mode 100644 index 000000000..ac945183a --- /dev/null +++ b/src/main/scala/li/cil/oc/common/tileentity/Print.scala @@ -0,0 +1,95 @@ +package li.cil.oc.common.tileentity + +import cpw.mods.fml.relauncher.Side +import cpw.mods.fml.relauncher.SideOnly +import li.cil.oc.common.item.data.PrintData +import li.cil.oc.util.ExtendedAABB +import li.cil.oc.util.ExtendedAABB._ +import li.cil.oc.util.ExtendedNBT._ +import net.minecraft.nbt.NBTTagCompound +import net.minecraftforge.common.util.ForgeDirection + +class Print extends traits.TileEntity with traits.RedstoneAware with traits.Rotatable { + val data = new PrintData() + + var boundsOff = ExtendedAABB.unitBounds + var boundsOn = ExtendedAABB.unitBounds + var state = false + + _isOutputEnabled = true + + def activate(): Boolean = { + if (data.stateOn.size > 0) { + if (!state || !data.isButtonMode) { + toggleState() + return true + } + } + false + } + + def toggleState(): Unit = { + state = !state + world.playSoundEffect(x + 0.5, y + 0.5, z + 0.5, "random.click", 0.3F, if (state) 0.6F else 0.5F) + world.markBlockForUpdate(x, y, z) + if (data.emitRedstone) { + ForgeDirection.VALID_DIRECTIONS.foreach(output(_, if (state) 15 else 0)) + } + if (state && data.isButtonMode) { + world.scheduleBlockUpdate(x, y, z, block, block.tickRate(world)) + } + } + + override def canUpdate = false + + override protected def onRedstoneInputChanged(side: ForgeDirection, oldMaxValue: Int, newMaxValue: Int): Unit = { + super.onRedstoneInputChanged(side, oldMaxValue, newMaxValue) + if (!data.emitRedstone && data.stateOn.size > 0) { + state = newMaxValue > 0 + world.playSoundEffect(x + 0.5, y + 0.5, z + 0.5, "random.click", 0.3F, if (state) 0.6F else 0.5F) + world.markBlockForUpdate(x, y, z) + } + } + + override def readFromNBTForServer(nbt: NBTTagCompound): Unit = { + super.readFromNBTForServer(nbt) + data.load(nbt.getCompoundTag("data")) + state = nbt.getBoolean("state") + updateBounds() + } + + override def writeToNBTForServer(nbt: NBTTagCompound): Unit = { + super.writeToNBTForServer(nbt) + nbt.setNewCompoundTag("data", data.save) + nbt.setBoolean("state", state) + } + + @SideOnly(Side.CLIENT) + override def readFromNBTForClient(nbt: NBTTagCompound): Unit = { + super.readFromNBTForClient(nbt) + data.load(nbt.getCompoundTag("data")) + state = nbt.getBoolean("state") + updateBounds() + world.markBlockForUpdate(x, y, z) + } + + override def writeToNBTForClient(nbt: NBTTagCompound): Unit = { + super.writeToNBTForClient(nbt) + nbt.setNewCompoundTag("data", data.save) + nbt.setBoolean("state", state) + } + + def updateBounds(): Unit = { + boundsOff = data.stateOff.drop(1).foldLeft(data.stateOff.headOption.fold(ExtendedAABB.unitBounds)(_.bounds))((a, b) => a.func_111270_a(b.bounds)) + if (boundsOff.volume == 0) boundsOff = ExtendedAABB.unitBounds + else boundsOff = boundsOff.rotateTowards(facing) + boundsOn = data.stateOn.drop(1).foldLeft(data.stateOn.headOption.fold(ExtendedAABB.unitBounds)(_.bounds))((a, b) => a.func_111270_a(b.bounds)) + if (boundsOn.volume == 0) boundsOn = ExtendedAABB.unitBounds + else boundsOn = boundsOn.rotateTowards(facing) + } + + override protected def onRotationChanged(): Unit = { + super.onRotationChanged() + updateBounds() + } +} diff --git a/src/main/scala/li/cil/oc/common/tileentity/Printer.scala b/src/main/scala/li/cil/oc/common/tileentity/Printer.scala new file mode 100644 index 000000000..709cd0068 --- /dev/null +++ b/src/main/scala/li/cil/oc/common/tileentity/Printer.scala @@ -0,0 +1,307 @@ +package li.cil.oc.common.tileentity + +import java.util + +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.machine.Arguments +import li.cil.oc.api.machine.Callback +import li.cil.oc.api.machine.Context +import li.cil.oc.api.network._ +import li.cil.oc.common.item.data.PrintData +import li.cil.oc.server.{PacketSender => ServerPacketSender} +import li.cil.oc.util.ExtendedAABB._ +import li.cil.oc.util.ExtendedNBT._ +import li.cil.oc.util.ItemUtils +import net.minecraft.item.ItemStack +import net.minecraft.nbt.NBTTagCompound +import net.minecraft.util.AxisAlignedBB +import net.minecraftforge.common.util.ForgeDirection + +class Printer extends traits.Environment with traits.Inventory with traits.Rotatable with SidedEnvironment with traits.StateAware { + val node = api.Network.newNode(this, Visibility.Network). + withComponent("printer3d"). + withConnector(Settings.get.bufferConverter). + create() + + val maxAmountMaterial = 256000 + var amountMaterial = 0 + val maxAmountInk = 100000 + var amountInk = 0 + + var data = new PrintData() + var isActive = false + var output: Option[ItemStack] = None + var totalRequiredEnergy = 0.0 + var requiredEnergy = 0.0 + + val materialPerItem = 2000 + val inkPerCartridge = 50000 + + val slotMaterial = 0 + val slotInk = 1 + val slotOutput = 2 + + // ----------------------------------------------------------------------- // + + @SideOnly(Side.CLIENT) + override def canConnect(side: ForgeDirection) = side != ForgeDirection.UP + + override def sidedNode(side: ForgeDirection) = if (side != ForgeDirection.UP) node else null + + override def currentState = { + if (isPrinting) util.EnumSet.of(traits.State.IsWorking) + else if (canPrint) util.EnumSet.of(traits.State.CanWork) + else util.EnumSet.noneOf(classOf[traits.State]) + } + + // ----------------------------------------------------------------------- // + + def canPrint = data.stateOff.size > 0 && data.stateOff.size + data.stateOn.size <= Settings.get.maxPrintComplexity + + def isPrinting = (requiredEnergy > 0 || isActive) && Option(getStackInSlot(slotOutput)).fold(true)(stack => { + stack.stackSize < stack.getMaxStackSize && output.fold(true)(ItemStack.areItemStackTagsEqual(stack, _)) + }) + + def progress = (1 - requiredEnergy / totalRequiredEnergy) * 100 + + def timeRemaining = (requiredEnergy / Settings.get.assemblerTickAmount / 20).toInt + + // ----------------------------------------------------------------------- // + + @Callback(doc = """function() -- Resets the configuration of the printer and stop printing (current job will finish).""") + def reset(context: Context, args: Arguments): Array[Object] = { + data = new PrintData() + isActive = false // Needs committing. + null + } + + @Callback(doc = """function(value:string) -- Set a label for the block being printed.""") + def setLabel(context: Context, args: Arguments): Array[Object] = { + data.label = Option(args.optString(0, null)).map(_.take(24)) + if (data.label.fold(false)(_.isEmpty)) data.label = None + isActive = false // Needs committing. + null + } + + @Callback(doc = """function():string -- Get the current label for the block being printed.""") + def getLabel(context: Context, args: Arguments): Array[Object] = { + result(data.label.orNull) + } + + @Callback(doc = """function(value:string) -- Set a tooltip for the block being printed.""") + def setTooltip(context: Context, args: Arguments): Array[Object] = { + data.tooltip = Option(args.optString(0, null)).map(_.take(128)) + if (data.tooltip.fold(false)(_.isEmpty)) data.tooltip = None + isActive = false // Needs committing. + null + } + + @Callback(doc = """function():string -- Get the current tooltip for the block being printed.""") + def getTooltip(context: Context, args: Arguments): Array[Object] = { + result(data.tooltip.orNull) + } + + @Callback(doc = """function(value:boolean) -- Set whether the printed block should emit redstone when in its active state.""") + def setRedstoneEmitter(context: Context, args: Arguments): Array[Object] = { + data.emitRedstone = args.checkBoolean(0) + isActive = false // Needs committing. + null + } + + @Callback(doc = """function():boolean -- Get whether the printed block should emit redstone when in its active state.""") + def isRedstoneEmitter(context: Context, args: Arguments): Array[Object] = { + result(data.emitRedstone) + } + + @Callback(doc = """function(value:boolean) -- Set whether the printed block should automatically return to its off state.""") + def setButtonMode(context: Context, args: Arguments): Array[Object] = { + data.isButtonMode = args.checkBoolean(0) + isActive = false // Needs committing. + null + } + + @Callback(doc = """function():boolean -- Get whether the printed block should automatically return to its off state.""") + def isButtonMode(context: Context, args: Arguments): Array[Object] = { + result(data.isButtonMode) + } + + @Callback(doc = """function(minX:number, minY:number, minZ:number, maxX:number, maxY:number, maxZ:number, texture:string[, state:boolean=false]) -- Adds a shape to the printers configuration, optionally specifying whether it is for the off or on state.""") + def addShape(context: Context, args: Arguments): Array[Object] = { + if (data.stateOff.size + data.stateOn.size >= Settings.get.maxPrintComplexity) { + return result(null, "model too complex") + } + val minX = (args.checkInteger(0) max 0 min 16) / 16f + val minY = (args.checkInteger(1) max 0 min 16) / 16f + val minZ = (16 - (args.checkInteger(2) max 0 min 16)) / 16f + val maxX = (args.checkInteger(3) max 0 min 16) / 16f + val maxY = (args.checkInteger(4) max 0 min 16) / 16f + val maxZ = (16 - (args.checkInteger(5) max 0 min 16)) / 16f + val texture = args.checkString(6).take(64) + val state = args.checkAny(7) != null && args.optBoolean(7, false) + + if (minX == maxX) throw new IllegalArgumentException("empty block") + if (minY == maxY) throw new IllegalArgumentException("empty block") + if (minZ == maxZ) throw new IllegalArgumentException("empty block") + + val list = if (state) data.stateOn else data.stateOff + list += new PrintData.Shape(AxisAlignedBB.getBoundingBox( + math.min(minX, maxX), + math.min(minY, maxY), + math.min(minZ, maxZ), + math.max(maxX, minX), + math.max(maxY, minY), + math.max(maxZ, minZ)), texture) + isActive = false // Needs committing. + + world.markBlockForUpdate(x, y, z) + + result(true) + } + + @Callback(doc = """function():number -- Get the number of shapes in the current configuration.""") + def getShapeCount(context: Context, args: Arguments): Array[Object] = result(data.stateOff.size + data.stateOn.size) + + @Callback(doc = """function():number -- Get the maximum allowed number of shapes.""") + def getMaxShapeCount(context: Context, args: Arguments): Array[Object] = result(Settings.get.maxPrintComplexity) + + @Callback(doc = """function():boolean -- Commit and begin printing the current configuration.""") + def commit(context: Context, args: Arguments): Array[Object] = { + if (!canPrint) { + return result(null, "model invalid") + } + isActive = true + result(true) + } + + @Callback(doc = """function(): string, number or boolean -- The current state of the printer, `busy' or `idle', followed by the progress or model validity, respectively.""") + def status(context: Context, args: Arguments): Array[Object] = { + if (isPrinting) result("busy", progress) + else if (canPrint) result("idle", true) + else result("idle", false) + } + + // ----------------------------------------------------------------------- // + + override def canUpdate = isServer + + // ----------------------------------------------------------------------- // + + override def updateEntity() { + super.updateEntity() + + if (isActive && output.isEmpty && Option(getStackInSlot(slotOutput)).fold(true)(stack => stack.stackSize < stack.getMaxStackSize)) { + val totalVolume = data.stateOn.foldLeft(0)((acc, shape) => acc + shape.bounds.volume) + data.stateOff.foldLeft(0)((acc, shape) => acc + shape.bounds.volume) + val totalSurface = data.stateOn.foldLeft(0)((acc, shape) => acc + shape.bounds.surface) + data.stateOff.foldLeft(0)((acc, shape) => acc + shape.bounds.surface) + + if (totalVolume == 0) { + isActive = false + data = new PrintData() + } + else { + val materialRequired = (totalVolume / 2) max 1 + val inkRequired = (totalSurface / 6) max 1 + + totalRequiredEnergy = Settings.get.printCost + requiredEnergy = totalRequiredEnergy + + if (amountMaterial >= materialRequired && amountInk >= inkRequired) { + amountMaterial -= materialRequired + amountInk -= inkRequired + output = Option(data.createItemStack()) + ServerPacketSender.sendPrinting(this, printing = true) + } + } + } + + if (output.isDefined) { + val want = math.max(1, math.min(requiredEnergy, Settings.get.printerTickAmount)) + val success = Settings.get.ignorePower || node.tryChangeBuffer(-want) + if (success) { + requiredEnergy -= want + } + if (requiredEnergy <= 0) { + val result = getStackInSlot(slotOutput) + if (result == null) { + setInventorySlotContents(slotOutput, output.get) + } + else if (output.get.isItemEqual(result) && ItemStack.areItemStackTagsEqual(output.get, result) && result.stackSize < result.getMaxStackSize) { + result.stackSize += 1 + markDirty() + } + else { + return + } + requiredEnergy = 0 + output = None + } + ServerPacketSender.sendPrinting(this, success && output.isDefined) + } + + if (maxAmountMaterial - amountMaterial >= materialPerItem) { + val material = decrStackSize(slotMaterial, 1) + if (material != null) { + amountMaterial += materialPerItem + } + } + + if (maxAmountInk - amountInk >= inkPerCartridge) { + if (api.Items.get(getStackInSlot(slotInk)) == api.Items.get("inkCartridge")) { + setInventorySlotContents(slotInk, api.Items.get("inkCartridgeEmpty").createItemStack(1)) + amountInk += inkPerCartridge + } + } + } + + override def readFromNBTForServer(nbt: NBTTagCompound) { + super.readFromNBTForServer(nbt) + amountMaterial = nbt.getInteger(Settings.namespace + "amountMaterial") + amountInk = nbt.getInteger(Settings.namespace + "amountInk") + data.load(nbt.getCompoundTag(Settings.namespace + "data")) + isActive = nbt.getBoolean(Settings.namespace + "active") + if (nbt.hasKey(Settings.namespace + "output")) { + output = Option(ItemUtils.loadStack(nbt.getCompoundTag(Settings.namespace + "output"))) + } + totalRequiredEnergy = nbt.getDouble(Settings.namespace + "total") + requiredEnergy = nbt.getDouble(Settings.namespace + "remaining") + } + + override def writeToNBTForServer(nbt: NBTTagCompound) { + super.writeToNBTForServer(nbt) + nbt.setInteger(Settings.namespace + "amountMaterial", amountMaterial) + nbt.setInteger(Settings.namespace + "amountInk", amountInk) + nbt.setNewCompoundTag(Settings.namespace + "data", data.save) + nbt.setBoolean(Settings.namespace + "active", isActive) + output.foreach(stack => nbt.setNewCompoundTag(Settings.namespace + "output", stack.writeToNBT)) + nbt.setDouble(Settings.namespace + "total", totalRequiredEnergy) + nbt.setDouble(Settings.namespace + "remaining", requiredEnergy) + } + + @SideOnly(Side.CLIENT) override + def readFromNBTForClient(nbt: NBTTagCompound) { + super.readFromNBTForClient(nbt) + data.load(nbt.getCompoundTag(Settings.namespace + "data")) + requiredEnergy = nbt.getDouble("remaining") + } + + override def writeToNBTForClient(nbt: NBTTagCompound) { + super.writeToNBTForClient(nbt) + nbt.setNewCompoundTag(Settings.namespace + "data", data.save) + nbt.setDouble("remaining", requiredEnergy) + } + + // ----------------------------------------------------------------------- // + + override def getSizeInventory = 3 + + override def getInventoryStackLimit = 64 + + override def isItemValidForSlot(slot: Int, stack: ItemStack) = + if (slot == 0) + api.Items.get(stack) == api.Items.get("chamelium") + else if (slot == 1) + api.Items.get(stack) == api.Items.get("inkCartridge") + else false +} diff --git a/src/main/scala/li/cil/oc/common/tileentity/traits/RedstoneAware.scala b/src/main/scala/li/cil/oc/common/tileentity/traits/RedstoneAware.scala index f9e3c4d68..2f38a5651 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/traits/RedstoneAware.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/traits/RedstoneAware.scala @@ -75,7 +75,7 @@ trait RedstoneAware extends RotationAware with IConnectable with IRedstoneEmitte } } - protected def updateRedstoneInput(side: ForgeDirection) { + def updateRedstoneInput(side: ForgeDirection) { val oldInput = _input(side.ordinal()) val newInput = computeInput(side) _input(side.ordinal()) = newInput diff --git a/src/main/scala/li/cil/oc/server/PacketSender.scala b/src/main/scala/li/cil/oc/server/PacketSender.scala index 68e1df349..e9f652b86 100644 --- a/src/main/scala/li/cil/oc/server/PacketSender.scala +++ b/src/main/scala/li/cil/oc/server/PacketSender.scala @@ -9,6 +9,7 @@ import li.cil.oc.common.tileentity.traits._ import li.cil.oc.util.BlockPosition import li.cil.oc.util.PackedColor import net.minecraft.entity.player.EntityPlayerMP +import net.minecraft.inventory.Container import net.minecraft.item.ItemStack import net.minecraft.nbt.CompressedStreamTools import net.minecraft.nbt.NBTTagCompound @@ -74,6 +75,17 @@ object PacketSender { pb.sendToPlayersNearTileEntity(t) } + def sendContainerUpdate(c: Container, nbt: NBTTagCompound, player: EntityPlayerMP): Unit = { + if (!nbt.hasNoTags) { + val pb = new SimplePacketBuilder(PacketType.ContainerUpdate) + + pb.writeByte(c.windowId.toByte) + pb.writeNBT(nbt) + + pb.sendToPlayer(player) + } + } + def sendDisassemblerActive(t: tileentity.Disassembler, active: Boolean) { val pb = new SimplePacketBuilder(PacketType.DisassemblerActiveChange) @@ -226,6 +238,15 @@ object PacketSender { pb.sendToPlayersNearTileEntity(t) } + def sendPrinting(t: tileentity.Printer, printing: Boolean) { + val pb = new SimplePacketBuilder(PacketType.PrinterState) + + pb.writeTileEntity(t) + pb.writeBoolean(printing) + + pb.sendToPlayersNearHost(t) + } + def sendRaidChange(t: tileentity.Raid) { val pb = new SimplePacketBuilder(PacketType.RaidStateChange) diff --git a/src/main/scala/li/cil/oc/util/ExtendedAABB.scala b/src/main/scala/li/cil/oc/util/ExtendedAABB.scala new file mode 100644 index 000000000..ff28b246b --- /dev/null +++ b/src/main/scala/li/cil/oc/util/ExtendedAABB.scala @@ -0,0 +1,51 @@ +package li.cil.oc.util + +import net.minecraft.util.AxisAlignedBB +import net.minecraft.util.Vec3 +import net.minecraftforge.common.util.ForgeDirection + +import scala.language.implicitConversions + +object ExtendedAABB { + implicit def extendedAABB(bounds: AxisAlignedBB): ExtendedAABB = new ExtendedAABB(bounds) + + def unitBounds = AxisAlignedBB.getBoundingBox(0, 0, 0, 1, 1, 1) + + class ExtendedAABB(val bounds: AxisAlignedBB) { + def volume: Int = { + val sx = ((bounds.maxX - bounds.minX) * 16).round.toInt + val sy = ((bounds.maxY - bounds.minY) * 16).round.toInt + val sz = ((bounds.maxZ - bounds.minZ) * 16).round.toInt + sx * sy * sz + } + + def surface: Int = { + val sx = ((bounds.maxX - bounds.minX) * 16).round.toInt + val sy = ((bounds.maxY - bounds.minY) * 16).round.toInt + val sz = ((bounds.maxZ - bounds.minZ) * 16).round.toInt + sx * sy * 2 + sx * sz * 2 + sy * sz * 2 + } + + def rotateTowards(facing: ForgeDirection) = rotateY(facing match { + case ForgeDirection.WEST => 3 + case ForgeDirection.NORTH => 2 + case ForgeDirection.EAST => 1 + case _ => 0 + }) + + def rotateY(count: Int): AxisAlignedBB = { + val min = Vec3.createVectorHelper(bounds.minX - 0.5, bounds.minY - 0.5, bounds.minZ - 0.5) + val max = Vec3.createVectorHelper(bounds.maxX - 0.5, bounds.maxY - 0.5, bounds.maxZ - 0.5) + min.rotateAroundY(count * Math.PI.toFloat * 0.5f) + max.rotateAroundY(count * Math.PI.toFloat * 0.5f) + AxisAlignedBB.getBoundingBox( + (math.min(min.xCoord + 0.5, max.xCoord + 0.5) * 32).round / 32f, + (math.min(min.yCoord + 0.5, max.yCoord + 0.5) * 32).round / 32f, + (math.min(min.zCoord + 0.5, max.zCoord + 0.5) * 32).round / 32f, + (math.max(min.xCoord + 0.5, max.xCoord + 0.5) * 32).round / 32f, + (math.max(min.yCoord + 0.5, max.yCoord + 0.5) * 32).round / 32f, + (math.max(min.zCoord + 0.5, max.zCoord + 0.5) * 32).round / 32f) + } + } + +}