diff --git a/LICENSE b/LICENSE index e4a728039..730f0b8c4 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2013-2014 Florian "Sangar" Nücke +Copyright (c) 2013-2015 Florian "Sangar" Nücke Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 1daa8f8da..fb9f75438 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ A few useful links: You can find experimental builds [on the build server][jenkins]. Expect these to be generally more unstable than builds marked as releases. Use these **at your own risk**, but - when using the latest one - please *do* report bugs you encounter using them. Thanks! ## License / Use in Modpacks -This mod is [licensed under the **MIT license**](https://github.com/MightyPirates/OpenComputers/blob/master/LICENSE). All **assets are public domain**, unless otherwise stated; all are free to be distributed as long as the license / source credits are kept. This means you can use this mod in any mod pack **as you please**. I'd be happy to hear about you using it, though, just out of curiosity. +This mod is [licensed under the **MIT license**](https://github.com/MightyPirates/OpenComputers/blob/master-MC1.7.10/LICENSE). All **assets are public domain**, unless otherwise stated; all are free to be distributed as long as the license / source credits are kept. This means you can use this mod in any mod pack **as you please**. I'd be happy to hear about you using it, though, just out of curiosity. ## Contributing ###Assets and Localizations diff --git a/build.gradle b/build.gradle index 01568b464..8409c5c16 100644 --- a/build.gradle +++ b/build.gradle @@ -17,6 +17,7 @@ buildscript { apply plugin: 'scala' apply plugin: 'forge' +apply plugin: 'idea' apply plugin: 'maven-publish' file "build.properties" withReader { @@ -136,6 +137,10 @@ repositories { name 'Railcraft' artifactPattern "http://addons.cursecdn.com/files/${config.rc.cf}/[module]_[revision].[ext]" } + ivy { + name 'BloodMagic' + artifactPattern "http://addons.cursecdn.com/files/${config.bloodmagic.cf}/[module]-${config.minecraft.version}-[revision].[ext]" + } */ } @@ -173,6 +178,8 @@ dependencies { provided name: 'ComputerCraft', version: config.cc.version, ext: 'jar' provided name: 'EnderIO', version: config.eio.version, ext: 'jar' provided name: 'Railcraft', version: config.rc.version, ext: 'jar' + provided name: 'BloodMagic', version: config.bloodmagic.version, ext: 'jar' + */ compile 'com.google.code.findbugs:jsr305:1.3.9' // Annotations used by google libs. @@ -317,8 +324,8 @@ publishing { } // this is needed for IntelliJ so we don't have to copy over the assets manually every time -sourceSets { - main { - output.resourcesDir = output.classesDir +idea { + module { + outputDir = file('build/classes/main') } } diff --git a/build.properties b/build.properties index c03c07a4c..5232bceff 100644 --- a/build.properties +++ b/build.properties @@ -1,17 +1,19 @@ minecraft.version=1.8 forge.version=11.14.0.1280-1.8 -oc.version=2.0.0 +oc.version=1.4.6 oc.subversion=dev ae2.version=rv1-stable-1 bc.version=6.2.6 +bloodmagic.cf=2223/203 +bloodmagic.version=1.3.0a-1 cc.cf=2216/236 cc.version=1.65 ccc.version=1.0.5.34 ccl.version=1.1.2.115 -cofhlib.cf=2212/893 -cofhlib.version=[1.7.10]1.0.0B6-dev-26 +cofhlib.cf=2218/257 +cofhlib.version=[1.7.10]1.0.0B7-dev-29 eio.cf=2219/296 eio.version=1.7.10-2.2.1.276 es.version=1.4.5.24 diff --git a/src/main/java/li/cil/oc/api/component/TextBuffer.java b/src/main/java/li/cil/oc/api/component/TextBuffer.java index e45c40af4..774255ded 100644 --- a/src/main/java/li/cil/oc/api/component/TextBuffer.java +++ b/src/main/java/li/cil/oc/api/component/TextBuffer.java @@ -341,6 +341,71 @@ public interface TextBuffer extends ManagedEnvironment, Persistable { */ boolean isBackgroundFromPalette(int column, int row); + /** + * Overwrites a portion of the text in raw mode. + *

+ * This will copy the given char array into the buffer, starting at the + * specified column and row. The array is expected to be indexed row- + * first, i.e. the first dimension is the vertical axis, the second + * the horizontal. + *

+ * Important: this performs no checks as to whether something + * actually changed. It will always send the changed patch to clients. + * It will also not crop the specified array to the actually used range. + * In other words, this is not intended to be exposed as-is to user code, + * it should always be called with validated, and, as necessary, cropped + * values. + * + * @param column the horizontal index. + * @param row the vertical index. + * @param text the text to write. + */ + void rawSetText(int column, int row, char[][] text); + + /** + * Overwrites a portion of the foreground color information in raw mode. + *

+ * This will convert the specified RGB data (in 0xRRGGBB format) + * to the internal, packed representation and copy it into the buffer, + * starting at the specified column and row. The array is expected to be + * indexed row-first, i.e. the first dimension is the vertical axis, the + * second the horizontal. + *

+ * Important: this performs no checks as to whether something + * actually changed. It will always send the changed patch to clients. + * It will also not crop the specified array to the actually used range. + * In other words, this is not intended to be exposed as-is to user code, + * it should always be called with validated, and, as necessary, cropped + * values. + * + * @param column the horizontal index. + * @param row the vertical index. + * @param color the foreground color data to write. + */ + void rawSetForeground(int column, int row, int[][] color); + + /** + * Overwrites a portion of the background color information in raw mode. + *

+ * This will convert the specified RGB data (in 0xRRGGBB format) + * to the internal, packed representation and copy it into the buffer, + * starting at the specified column and row. The array is expected to be + * indexed row-first, i.e. the first dimension is the vertical axis, the + * second the horizontal. + *

+ * Important: this performs no checks as to whether something + * actually changed. It will always send the changed patch to clients. + * It will also not crop the specified array to the actually used range. + * In other words, this is not intended to be exposed as-is to user code, + * it should always be called with validated, and, as necessary, cropped + * values. + * + * @param column the horizontal index. + * @param row the vertical index. + * @param color the background color data to write. + */ + void rawSetBackground(int column, int row, int[][] color); + // ----------------------------------------------------------------------- // /** @@ -452,7 +517,7 @@ public interface TextBuffer extends ManagedEnvironment, Persistable { * @param button the button of the mouse that was pressed. * @param player the player that pressed the mouse button. Pass null on the client side. */ - void mouseDown(int x, int y, int button, EntityPlayer player); + void mouseDown(double x, double y, int button, EntityPlayer player); /** * Signals a mouse drag event for the buffer. @@ -465,7 +530,7 @@ public interface TextBuffer extends ManagedEnvironment, Persistable { * @param button the button of the mouse that is pressed. * @param player the player that moved the mouse. Pass null on the client side. */ - void mouseDrag(int x, int y, int button, EntityPlayer player); + void mouseDrag(double x, double y, int button, EntityPlayer player); /** * Signals a mouse button release event for the buffer. @@ -478,7 +543,7 @@ public interface TextBuffer extends ManagedEnvironment, Persistable { * @param button the button of the mouse that was released. * @param player the player that released the mouse button. Pass null on the client side. */ - void mouseUp(int x, int y, int button, EntityPlayer player); + void mouseUp(double x, double y, int button, EntityPlayer player); /** * Signals a mouse wheel scroll event for the buffer. @@ -491,7 +556,7 @@ public interface TextBuffer extends ManagedEnvironment, Persistable { * @param delta indicates the direction of the mouse scroll. * @param player the player that scrolled the mouse wheel. Pass null on the client side. */ - void mouseScroll(int x, int y, int delta, EntityPlayer player); + void mouseScroll(double x, double y, int delta, EntityPlayer player); // ----------------------------------------------------------------------- // diff --git a/src/main/java/li/cil/oc/api/network/ManagedPeripheral.java b/src/main/java/li/cil/oc/api/network/ManagedPeripheral.java index b12c8ff9e..3e69def05 100644 --- a/src/main/java/li/cil/oc/api/network/ManagedPeripheral.java +++ b/src/main/java/li/cil/oc/api/network/ManagedPeripheral.java @@ -6,7 +6,7 @@ import li.cil.oc.api.machine.Context; /** * This interface can be used with an {@link li.cil.oc.api.network.Environment} * and is intended to be used for environments wrapping a ComputerCraft - * peripheral. Tt could be used for other purposes as well, though. It allows + * peripheral. It could be used for other purposes as well, though. It allows * providing method names in addition to those defined via the * {@link li.cil.oc.api.machine.Callback} annotation, and invoking said methods. */ diff --git a/src/main/java/li/cil/oc/api/network/SidedComponent.java b/src/main/java/li/cil/oc/api/network/SidedComponent.java new file mode 100644 index 000000000..bc5eea0dd --- /dev/null +++ b/src/main/java/li/cil/oc/api/network/SidedComponent.java @@ -0,0 +1,26 @@ +package li.cil.oc.api.network; + +import net.minecraft.util.EnumFacing; + +/** + * This is an extended version of {@link li.cil.oc.api.network.SimpleComponent} + * which allows controlling connectivity on a side-by-side basis. + *

+ * Like the SimpleComponent interface, this is intended to be used + * with tile entities that should act as OC components. Please see the + * SimpleComponent interface for more information. + */ +public interface SidedComponent { + /** + * Whether this component can connect to a node on the specified side. + *

+ * The provided side is relative to the component, i.e. when the tile + * entity sits at (0, 0, 0) and is asked for its southern node (positive + * Z axis) it has to return the connectivity for the face between it and + * the block at (0, 0, 1). + * + * @param side the side to check for. + * @return whether the component may be connected to from the specified side. + */ + boolean canConnectNode(EnumFacing side); +} diff --git a/src/main/resources/LICENSE b/src/main/resources/LICENSE index 68dd6e055..8e9ad6ecb 100644 --- a/src/main/resources/LICENSE +++ b/src/main/resources/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2013-2014 Florian "Sangar" Nücke +Copyright (c) 2013-2015 Florian "Sangar" Nücke Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/main/resources/application.conf b/src/main/resources/application.conf index c79466de2..ec5211626 100644 --- a/src/main/resources/application.conf +++ b/src/main/resources/application.conf @@ -105,6 +105,18 @@ opencomputers { # importantly, can only render code page 437 (as opposed to... # a *lot* of unicode). fontRenderer: "unifont" + + # The sample rate used for generating beeps of computers' internal + # speakers. Use custom values at your own responsibility here; if it + # breaks OC you'll get no support. Some potentially reasonable + # lower values are 16000 or even 8000 (which was the old default, but + # leads to artifacting on certain frequencies). + beepSampleRate: 44100 + + # The base volume of beeps generated by computers. This may be in a + # range of [0, 127], where 0 means mute (the sound will not even be + # generated), and 127 means maximum amplitude / volume. + beepVolume: 32 } # Computer related settings, concerns server performance and security. @@ -675,11 +687,11 @@ opencomputers { # each point of complexity. tabletAssemblyComplexity: 5000 - # The base energy cost for assembling a microcontroller. + # The base energy cost for assembling a drone. droneAssemblyBase: 25000 # The additional amount of energy required to assemble a - # microcontroller for each point of complexity. + # drone for each point of complexity. droneAssemblyComplexity: 15000 # The amount of energy it takes to extract one ingredient from an diff --git a/src/main/resources/assets/opencomputers/lang/en_US.lang b/src/main/resources/assets/opencomputers/lang/en_US.lang index b962ef1bd..c2854fe7b 100644 --- a/src/main/resources/assets/opencomputers/lang/en_US.lang +++ b/src/main/resources/assets/opencomputers/lang/en_US.lang @@ -298,7 +298,7 @@ oc:tooltip.UpgradePiston=This upgrade is very pushy. It allows moving blocks, si oc:tooltip.UpgradeSign=Allows reading text on and writing text to signs. oc:tooltip.UpgradeSolarGenerator=Can be used to generate energy from sunlight on the go. Requires a clear line of sight to the sky above the device. Generates energy at %s%% of the speed of a Stirling Engine. oc:tooltip.UpgradeTank=This upgrade provides a tank for fluid storage for robots and drones. Without one of these, they will not be able to store fluids internally. -oc:tooltip.UpgradeTankController=This upgrade allows the robots and drones more control in how they interacts with external tanks, and allows them to transfer fluids into and out of fluid tank items in their inventory. +oc:tooltip.UpgradeTankController=This upgrade allows robots and drones more control in how they interacts with external tanks, and allows them to transfer fluids into and out of fluid tank items in their inventory. oc:tooltip.UpgradeTractorBeam=Equips a device with extremely advanced technology, nicknamed the "Item Magnet". Allows the device to pick up items anywhere within 3 blocks of its location. oc:tooltip.WirelessNetworkCard=Allows wireless sending of network messages in addition to normal ones. You can adjust the §fsignal strength§7 to control how far messages are sent. Higher signal strength results in higher energy consumption. oc:tooltip.WorldSensorCard=Allows reading out information about the world, such as its gravity and whether it has a breathable atmosphere. Use results at own risk. The manufacturer takes no responsibility for bodily or material harm caused by decisions made upon the cards' outputs. We have lawyers. And money. Don't even try. @@ -308,7 +308,7 @@ item.oc.AbstractBusCard.usage=This card allows computers, servers and robots to item.oc.Analyzer.usage=The §oAnalyzer§r is a handy tool for getting some information about OpenComputers-related blocks in the world. Simply (sneak-)activate a block to get some information printed to the chat. This ranges from basic things like the address of components, to power levels in the subnetwork the block is in, and information on the error lead to a computer to crash, for example.[nl][nl]Another useful functionality is that when using the using the analyzer on a block while holding down [Ctrl] the address of the block component will be copied to the clipboard. item.oc.ComponentBus.usage=A §oComponent Bus§r is a server-specific upgrade that allows the server to communicate with more components at the same time, without shutting down. Like with CPUs, higher tier buses provide higher component limits. item.oc.CPU.usage=The §oCentral Processing Unit§r is a core part for each computer. It defines the architecture of the computer, and the number of components that can be connected to the computer before it stops working. Higher tier CPUs also provide a higher per-tick direct call limit to the computer - in simpler terms: better CPUs run faster. -item.oc.DebugCard.usage=The §oDebug Card§r is a non-craftable item that was originally only intended to make debugging things easier, by automating some processes. It has since gotten a bunch more functionality, making it quite useful for custom map-making. +item.oc.DebugCard.usage=The §oDebug Card§r is a non-craftable item that was originally only intended to make debugging things easier, by automating some processes. It has since gotten a bunch more functionality, making it quite useful for custom map-making.[nl][nl]Note that you can use shift-rightclick while holding the card to bind it to you / unbind it, meaning §orunCommand§r will be performed using your permission levels instead of the default OpenComputers ones. item.oc.DroneCase.usage=The §oDrone Case§r is used to build Drones in the Assembler. Drones are light-weight, fast and very mobile machines with limited functionality. Unlike Robots they cannot use tools, and can interact with the world only in a relatively limited manner.[nl][nl]They make up for their limitations with speed and lower running energy costs. They are well suited for transport of small amounts of items, and ideal for reconnaissance. Pairing a Drone with a Robot can be quite powerful, with the Robot doing the "hard work", and the Drone providing information about the environment and transporting items to and from a central hub, for example.[nl][nl]Like microcontrollers, Drones can only be programmed using their EEPROM. Accordingly, the EEPROM can be changed by recrafting the Drone with another EEPROM. item.oc.eeprom.usage=The §oEEPROM§r is what contains the code used to initialize a computer when it is being booted. This data is stored as a plain byte array, and may mean different things to different CPU architectures. For example, for Lua it is usually a small script that searches for file systems with an init script, for other architectures it may be actual machine code. item.oc.FloppyDisk.usage=The §oFloppy Disk§r is the cheapest and smallest type of storage medium in OpenComputers. It is a handy early game way of storing data and transferring it between computers and robots. You may also find floppy disks with useful programs on them in dungeon chests.[nl][nl]Beware: shift-rightclicking while holding a floppy disk in your hand will wipe the floppy disk! diff --git a/src/main/resources/assets/opencomputers/loot/OpenIRC/irc.lua b/src/main/resources/assets/opencomputers/loot/OpenIRC/irc.lua index 9e0d7bee3..bd3018aa5 100644 --- a/src/main/resources/assets/opencomputers/loot/OpenIRC/irc.lua +++ b/src/main/resources/assets/opencomputers/loot/OpenIRC/irc.lua @@ -17,13 +17,17 @@ local text = require("text") local args, options = shell.parse(...) if #args < 1 then - print("Usage: irc [server:port]") + print("Usage: irc [server[:port]]") return end local nick = args[1] local host = args[2] or "irc.esper.net:6667" +if not host:find(":") then + host = host .. ":6667" +end + -- try to connect to server. local sock, reason = internet.open(host) if not sock then diff --git a/src/main/resources/assets/opencomputers/loot/OpenOS/boot/04_component.lua b/src/main/resources/assets/opencomputers/loot/OpenOS/boot/04_component.lua index 287f27862..c58ca54c0 100644 --- a/src/main/resources/assets/opencomputers/loot/OpenOS/boot/04_component.lua +++ b/src/main/resources/assets/opencomputers/loot/OpenOS/boot/04_component.lua @@ -27,7 +27,7 @@ end function component.isAvailable(componentType) checkArg(1, componentType, "string") - if not primaries[componentType] then + if not primaries[componentType] and not adding[componentType] then -- This is mostly to avoid out of memory errors preventing proxy -- creation cause confusion by trying to create the proxy again, -- causing the oom error to be thrown again. diff --git a/src/main/resources/assets/opencomputers/loot/OpenOS/lib/filesystem.lua b/src/main/resources/assets/opencomputers/loot/OpenOS/lib/filesystem.lua index 0ab4e4e63..9904ed9d8 100644 --- a/src/main/resources/assets/opencomputers/loot/OpenOS/lib/filesystem.lua +++ b/src/main/resources/assets/opencomputers/loot/OpenOS/lib/filesystem.lua @@ -351,8 +351,8 @@ function filesystem.list(path) table.remove(result, i) else f = result[i] + i = i + 1 end - i = i + 1 end local i = 0 return function() @@ -376,22 +376,31 @@ function filesystem.makeDirectory(path) end function filesystem.remove(path) - local node, rest, vnode, vrest = findNode(filesystem.path(path)) - local name = filesystem.name(path) - if vnode.children[name] then - vnode.children[name] = nil - removeEmptyNodes(vnode) - return true - elseif vnode.links[name] then - vnode.links[name] = nil - removeEmptyNodes(vnode) - return true - else + local function removeVirtual() + local node, rest, vnode, vrest = findNode(filesystem.path(path)) + local name = filesystem.name(path) + if vnode.children[name] then + vnode.children[name] = nil + removeEmptyNodes(vnode) + return true + elseif vnode.links[name] then + vnode.links[name] = nil + removeEmptyNodes(vnode) + return true + end + return false + end + local function removePhysical() node, rest = findNode(path) if node.fs and rest then return component.proxy(node.fs).remove(rest) end - return nil, "no such file or directory" + return false + end + local success = removeVirtual() + success = removePhysical() or success -- Always run. + if success then return true + else return nil, "no such file or directory" end end diff --git a/src/main/resources/assets/opencomputers/loot/OpenOS/lib/term.lua b/src/main/resources/assets/opencomputers/loot/OpenOS/lib/term.lua index 03a53556e..617441312 100644 --- a/src/main/resources/assets/opencomputers/loot/OpenOS/lib/term.lua +++ b/src/main/resources/assets/opencomputers/loot/OpenOS/lib/term.lua @@ -108,10 +108,11 @@ function term.isAvailable() return component.isAvailable("gpu") and component.isAvailable("screen") end -function term.read(history, dobreak, hint, pwchar) +function term.read(history, dobreak, hint, pwchar, filter) checkArg(1, history, "table", "nil") checkArg(3, hint, "function", "table", "nil") checkArg(4, pwchar, "string", "nil") + checkArg(5, filter, "string", "function", "nil") history = history or {} table.insert(history, "") local offset = term.getCursor() - 1 @@ -130,6 +131,13 @@ function term.read(history, dobreak, hint, pwchar) pwchar = unicode.sub(pwchar, 1, 1) end + if type(filter) == "string" then + local pattern = filter + filter = function(line) + return line:match(pattern) + end + end + local function masktext(str) return pwchar and pwchar:rep(unicode.len(str)) or str end @@ -327,12 +335,16 @@ function term.read(history, dobreak, hint, pwchar) elseif code == keyboard.keys.tab and hint then tab(keyboard.isShiftDown() and -1 or 1) elseif code == keyboard.keys.enter then - local cbx, cby = getCursor() - if cby ~= #history then -- bring entry to front - history[#history] = line() - table.remove(history, cby) + if not filter or filter(line() or "") then + local cbx, cby = getCursor() + if cby ~= #history then -- bring entry to front + history[#history] = line() + table.remove(history, cby) + end + return true, history[#history] .. "\n" + else + computer.beep(2000, 0.1) end - return true, history[#history] .. "\n" elseif keyboard.isControlDown() and code == keyboard.keys.d then if line() == "" then history[#history] = "" diff --git a/src/main/resources/assets/opencomputers/loot/OpenOS/usr/man/echo b/src/main/resources/assets/opencomputers/loot/OpenOS/usr/man/echo index b6337a2b1..d867b58e6 100644 --- a/src/main/resources/assets/opencomputers/loot/OpenOS/usr/man/echo +++ b/src/main/resources/assets/opencomputers/loot/OpenOS/usr/man/echo @@ -9,7 +9,7 @@ DESCRIPTION EXAMPLES echo test - Print `test` to the terminal. + Print `test` to the standard output (which is usually the terminal). - echo "a b" > test - Writes the string `a b` to the standard output, which is redirected into file `test`. \ No newline at end of file + echo "a b" + Writes the string `a b`. \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/sounds/preload.cfg b/src/main/resources/assets/opencomputers/sounds/preload.cfg new file mode 100644 index 000000000..e7334d6c8 --- /dev/null +++ b/src/main/resources/assets/opencomputers/sounds/preload.cfg @@ -0,0 +1,16 @@ +assets/opencomputers/sounds/computer_running.ogg +assets/opencomputers/sounds/floppy_access1.ogg +assets/opencomputers/sounds/floppy_access2.ogg +assets/opencomputers/sounds/floppy_access3.ogg +assets/opencomputers/sounds/floppy_access4.ogg +assets/opencomputers/sounds/floppy_access5.ogg +assets/opencomputers/sounds/floppy_access6.ogg +assets/opencomputers/sounds/floppy_eject.ogg +assets/opencomputers/sounds/floppy_insert.ogg +assets/opencomputers/sounds/hdd_access1.ogg +assets/opencomputers/sounds/hdd_access2.ogg +assets/opencomputers/sounds/hdd_access3.ogg +assets/opencomputers/sounds/hdd_access4.ogg +assets/opencomputers/sounds/hdd_access5.ogg +assets/opencomputers/sounds/hdd_access6.ogg +assets/opencomputers/sounds/hdd_access7.ogg \ No newline at end of file diff --git a/src/main/scala/li/cil/oc/Settings.scala b/src/main/scala/li/cil/oc/Settings.scala index c00f41c4d..6d88581cc 100644 --- a/src/main/scala/li/cil/oc/Settings.scala +++ b/src/main/scala/li/cil/oc/Settings.scala @@ -49,6 +49,8 @@ class Settings(val config: Config) { } val monochromeColor = Integer.decode(config.getString("client.monochromeColor")) val fontRenderer = config.getString("client.fontRenderer") + val beepSampleRate = config.getInt("client.beepSampleRate") + val beepAmplitude = config.getInt("client.beepVolume") max 0 min Byte.MaxValue // ----------------------------------------------------------------------- // // computer diff --git a/src/main/scala/li/cil/oc/client/KeyBindings.scala b/src/main/scala/li/cil/oc/client/KeyBindings.scala index 55dbb5c21..154749ba8 100644 --- a/src/main/scala/li/cil/oc/client/KeyBindings.scala +++ b/src/main/scala/li/cil/oc/client/KeyBindings.scala @@ -5,9 +5,13 @@ import net.minecraftforge.fml.client.FMLClientHandler import org.lwjgl.input.Keyboard object KeyBindings { - def showExtendedTooltips = Keyboard.isCreated && Keyboard.isKeyDown(extendedTooltip.getKeyCode) + def showExtendedTooltips = Keyboard.isCreated && (try Keyboard.isKeyDown(extendedTooltip.getKeyCode) catch { + case _: Throwable => false // Don't ask me, sometimes things can apparently screw up LWJGL's keyboard handling. + }) - def showMaterialCosts = Keyboard.isCreated && Keyboard.isKeyDown(materialCosts.getKeyCode) + def showMaterialCosts = Keyboard.isCreated && (try Keyboard.isKeyDown(materialCosts.getKeyCode) catch { + case _: Throwable => false // Don't ask me, sometimes things can apparently screw up LWJGL's keyboard handling. + }) def extendedTooltip = FMLClientHandler.instance.getClient.gameSettings.keyBindSneak diff --git a/src/main/scala/li/cil/oc/client/PacketHandler.scala b/src/main/scala/li/cil/oc/client/PacketHandler.scala index cdb79a1d7..2ff1f5f27 100644 --- a/src/main/scala/li/cil/oc/client/PacketHandler.scala +++ b/src/main/scala/li/cil/oc/client/PacketHandler.scala @@ -355,6 +355,9 @@ object PacketHandler extends CommonPacketHandler { case PacketType.TextBufferMultiResolutionChange => onTextBufferMultiResolutionChange(p, buffer) case PacketType.TextBufferMultiMaxResolutionChange => onTextBufferMultiMaxResolutionChange(p, buffer) case PacketType.TextBufferMultiSet => onTextBufferMultiSet(p, buffer) + case PacketType.TextBufferMultiRawSetText => onTextBufferMultiRawSetText(p, buffer) + case PacketType.TextBufferMultiRawSetBackground => onTextBufferMultiRawSetBackground(p, buffer) + case PacketType.TextBufferMultiRawSetForeground => onTextBufferMultiRawSetForeground(p, buffer) case _ => // Invalid packet. } } @@ -426,6 +429,60 @@ object PacketHandler extends CommonPacketHandler { buffer.set(col, row, s, vertical) } + def onTextBufferMultiRawSetText(p: PacketParser, buffer: component.TextBuffer) { + val col = p.readInt() + val row = p.readInt() + + val rows = p.readShort() + val text = new Array[Array[Char]](rows) + for (y <- 0 until rows) { + val cols = p.readShort() + val line = new Array[Char](cols) + for (x <- 0 until cols) { + line(x) = p.readChar() + } + text(y) = line + } + + buffer.rawSetText(col, row, text) + } + + def onTextBufferMultiRawSetBackground(p: PacketParser, buffer: component.TextBuffer) { + val col = p.readInt() + val row = p.readInt() + + val rows = p.readShort() + val color = new Array[Array[Int]](rows) + for (y <- 0 until rows) { + val cols = p.readShort() + val line = new Array[Int](cols) + for (x <- 0 until cols) { + line(x) = p.readInt() + } + color(y) = line + } + + buffer.rawSetBackground(col, row, color) + } + + def onTextBufferMultiRawSetForeground(p: PacketParser, buffer: component.TextBuffer) { + val col = p.readInt() + val row = p.readInt() + + val rows = p.readShort() + val color = new Array[Array[Int]](rows) + for (y <- 0 until rows) { + val cols = p.readShort() + val line = new Array[Int](cols) + for (x <- 0 until cols) { + line(x) = p.readInt() + } + color(y) = line + } + + buffer.rawSetForeground(col, row, color) + } + def onScreenTouchMode(p: PacketParser) = p.readTileEntity[Screen]() match { case Some(t) => t.invertTouchMode = p.readBoolean() diff --git a/src/main/scala/li/cil/oc/client/PacketSender.scala b/src/main/scala/li/cil/oc/client/PacketSender.scala index 4e18b9378..3182a73aa 100644 --- a/src/main/scala/li/cil/oc/client/PacketSender.scala +++ b/src/main/scala/li/cil/oc/client/PacketSender.scala @@ -76,35 +76,35 @@ object PacketSender { } } - def sendMouseClick(address: String, x: Int, y: Int, drag: Boolean, button: Int) { + def sendMouseClick(address: String, x: Double, y: Double, drag: Boolean, button: Int) { val pb = new SimplePacketBuilder(PacketType.MouseClickOrDrag) pb.writeUTF(address) - pb.writeShort(x) - pb.writeShort(y) + pb.writeFloat(x.toFloat) + pb.writeFloat(y.toFloat) pb.writeBoolean(drag) pb.writeByte(button.toByte) pb.sendToServer() } - def sendMouseScroll(address: String, x: Int, y: Int, scroll: Int) { + def sendMouseScroll(address: String, x: Double, y: Double, scroll: Int) { val pb = new SimplePacketBuilder(PacketType.MouseScroll) pb.writeUTF(address) - pb.writeShort(x) - pb.writeShort(y) + pb.writeFloat(x.toFloat) + pb.writeFloat(y.toFloat) pb.writeByte(scroll) pb.sendToServer() } - def sendMouseUp(address: String, x: Int, y: Int, button: Int) { + def sendMouseUp(address: String, x: Double, y: Double, button: Int) { val pb = new SimplePacketBuilder(PacketType.MouseUp) pb.writeUTF(address) - pb.writeShort(x) - pb.writeShort(y) + pb.writeFloat(x.toFloat) + pb.writeFloat(y.toFloat) pb.writeByte(button.toByte) pb.sendToServer() diff --git a/src/main/scala/li/cil/oc/client/Sound.scala b/src/main/scala/li/cil/oc/client/Sound.scala index d9a24fa5b..2d66d4e3c 100644 --- a/src/main/scala/li/cil/oc/client/Sound.scala +++ b/src/main/scala/li/cil/oc/client/Sound.scala @@ -8,6 +8,7 @@ import java.util.Timer import java.util.TimerTask import java.util.UUID +import com.google.common.base.Charsets import li.cil.oc.OpenComputers import li.cil.oc.Settings import net.minecraft.client.Minecraft @@ -19,11 +20,13 @@ import net.minecraftforge.client.event.sound.SoundLoadEvent import net.minecraftforge.event.world.WorldEvent import net.minecraftforge.fml.client.FMLClientHandler import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import net.minecraftforge.fml.common.gameevent.TickEvent.ClientTickEvent import net.minecraftforge.fml.relauncher.ReflectionHelper import paulscode.sound.SoundSystem import paulscode.sound.SoundSystemConfig import scala.collection.mutable +import scala.io.Source object Sound { private val sources = mutable.Map.empty[TileEntity, PseudoLoopingStream] @@ -100,6 +103,33 @@ object Sound { manager = event.manager } + private var hasPreloaded = Settings.get.soundVolume <= 0 + + @SubscribeEvent + def onTick(e: ClientTickEvent) { + if (!hasPreloaded && soundSystem != null) { + hasPreloaded = true + new Thread(new Runnable() { + override def run(): Unit = { + val preloadConfigLocation = new ResourceLocation(Settings.resourceDomain, "sounds/preload.cfg") + val preloadConfigResource = Minecraft.getMinecraft.getResourceManager.getResource(preloadConfigLocation) + for (location <- Source.fromInputStream(preloadConfigResource.getInputStream)(Charsets.UTF_8).getLines()) { + val url = getClass.getClassLoader.getResource(location) + if (url != null) try { + val sourceName = "preload_" + location + soundSystem.newSource(false, sourceName, url, location, true, 0, 0, 0, SoundSystemConfig.ATTENUATION_NONE, 16) + soundSystem.activate(sourceName) + soundSystem.removeSource(sourceName) + } catch { + case _: Throwable => // Meh. + } + else OpenComputers.log.warn(s"Couldn't preload sound $location!") + } + } + }) + } + } + @SubscribeEvent def onWorldUnload(event: WorldEvent.Unload) { commandQueue.synchronized(commandQueue.clear()) diff --git a/src/main/scala/li/cil/oc/client/gui/Adapter.scala b/src/main/scala/li/cil/oc/client/gui/Adapter.scala index 5f46e94c3..88bf8d7b9 100644 --- a/src/main/scala/li/cil/oc/client/gui/Adapter.scala +++ b/src/main/scala/li/cil/oc/client/gui/Adapter.scala @@ -6,8 +6,8 @@ import li.cil.oc.common.tileentity import net.minecraft.entity.player.InventoryPlayer class Adapter(playerInventory: InventoryPlayer, val adapter: tileentity.Adapter) extends DynamicGuiContainer(new container.Adapter(playerInventory, adapter)) { - override def drawGuiContainerForegroundLayer(mouseX: Int, mouseY: Int) = { - super.drawGuiContainerForegroundLayer(mouseX, mouseY) + override def drawSecondaryForegroundLayer(mouseX: Int, mouseY: Int) = { + super.drawSecondaryForegroundLayer(mouseX, mouseY) fontRendererObj.drawString( Localization.localizeImmediately(adapter.getName), 8, 6, 0x404040) diff --git a/src/main/scala/li/cil/oc/client/gui/Assembler.scala b/src/main/scala/li/cil/oc/client/gui/Assembler.scala index c0d860536..fbe0fa444 100644 --- a/src/main/scala/li/cil/oc/client/gui/Assembler.scala +++ b/src/main/scala/li/cil/oc/client/gui/Assembler.scala @@ -60,7 +60,7 @@ class Assembler(playerInventory: InventoryPlayer, val assembler: tileentity.Asse add(buttonList, runButton) } - override def drawGuiContainerForegroundLayer(mouseX: Int, mouseY: Int) = { + override def drawSecondaryForegroundLayer(mouseX: Int, mouseY: Int) = { GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS) // Me lazy... prevents NEI render glitch. if (!assemblerContainer.isAssembling) { val message = diff --git a/src/main/scala/li/cil/oc/client/gui/Case.scala b/src/main/scala/li/cil/oc/client/gui/Case.scala index e8ae0b803..2c767b804 100644 --- a/src/main/scala/li/cil/oc/client/gui/Case.scala +++ b/src/main/scala/li/cil/oc/client/gui/Case.scala @@ -33,9 +33,8 @@ class Case(playerInventory: InventoryPlayer, val computer: tileentity.Case) exte add(buttonList, powerButton) } - override def drawGuiContainerForegroundLayer(mouseX: Int, mouseY: Int) = { - super.drawGuiContainerForegroundLayer(mouseX, mouseY) - GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS) // Me lazy... prevents NEI render glitch. + override protected def drawSecondaryForegroundLayer(mouseX: Int, mouseY: Int) = { + super.drawSecondaryForegroundLayer(mouseX, mouseY) fontRendererObj.drawString( Localization.localizeImmediately(computer.getName), 8, 6, 0x404040) @@ -44,7 +43,6 @@ class Case(playerInventory: InventoryPlayer, val computer: tileentity.Case) exte tooltip.add(if (computer.isRunning) Localization.Computer.TurnOff else Localization.Computer.TurnOn) copiedDrawHoveringText(tooltip, mouseX - guiLeft, mouseY - guiTop, fontRendererObj) } - GL11.glPopAttrib() } override def drawSecondaryBackgroundLayer() { diff --git a/src/main/scala/li/cil/oc/client/gui/Charger.scala b/src/main/scala/li/cil/oc/client/gui/Charger.scala index f27af6a15..12344a4c2 100644 --- a/src/main/scala/li/cil/oc/client/gui/Charger.scala +++ b/src/main/scala/li/cil/oc/client/gui/Charger.scala @@ -6,8 +6,8 @@ import li.cil.oc.common.tileentity import net.minecraft.entity.player.InventoryPlayer class Charger(playerInventory: InventoryPlayer, val charger: tileentity.Charger) extends DynamicGuiContainer(new container.Charger(playerInventory, charger)) { - override def drawGuiContainerForegroundLayer(mouseX: Int, mouseY: Int) = { - super.drawGuiContainerForegroundLayer(mouseX, mouseY) + override def drawSecondaryForegroundLayer(mouseX: Int, mouseY: Int) = { + super.drawSecondaryForegroundLayer(mouseX, mouseY) fontRendererObj.drawString( Localization.localizeImmediately(charger.getName), 8, 6, 0x404040) diff --git a/src/main/scala/li/cil/oc/client/gui/Database.scala b/src/main/scala/li/cil/oc/client/gui/Database.scala index 153e56437..31cf5bd1d 100644 --- a/src/main/scala/li/cil/oc/client/gui/Database.scala +++ b/src/main/scala/li/cil/oc/client/gui/Database.scala @@ -11,7 +11,7 @@ import org.lwjgl.opengl.GL11 class Database(playerInventory: InventoryPlayer, val databaseInventory: DatabaseInventory) extends DynamicGuiContainer(new container.Database(playerInventory, databaseInventory)) { ySize = 256 - override def drawGuiContainerForegroundLayer(mouseX: Int, mouseY: Int) {} + override def drawSecondaryForegroundLayer(mouseX: Int, mouseY: Int) {} override protected def drawGuiContainerBackgroundLayer(dt: Float, mouseX: Int, mouseY: Int) { GL11.glColor4f(1, 1, 1, 1) diff --git a/src/main/scala/li/cil/oc/client/gui/Disassembler.scala b/src/main/scala/li/cil/oc/client/gui/Disassembler.scala index 395642a40..041261a04 100644 --- a/src/main/scala/li/cil/oc/client/gui/Disassembler.scala +++ b/src/main/scala/li/cil/oc/client/gui/Disassembler.scala @@ -13,7 +13,7 @@ class Disassembler(playerInventory: InventoryPlayer, val disassembler: tileentit val progress = addWidget(new ProgressBar(18, 65)) - override def drawGuiContainerForegroundLayer(mouseX: Int, mouseY: Int) = { + override def drawSecondaryForegroundLayer(mouseX: Int, mouseY: Int) = { fontRendererObj.drawString( Localization.localizeImmediately(disassembler.getName), 8, 6, 0x404040) diff --git a/src/main/scala/li/cil/oc/client/gui/DiskDrive.scala b/src/main/scala/li/cil/oc/client/gui/DiskDrive.scala index 21e6c0e34..1fbe026c5 100644 --- a/src/main/scala/li/cil/oc/client/gui/DiskDrive.scala +++ b/src/main/scala/li/cil/oc/client/gui/DiskDrive.scala @@ -6,8 +6,8 @@ import li.cil.oc.common.tileentity import net.minecraft.entity.player.InventoryPlayer class DiskDrive(playerInventory: InventoryPlayer, val drive: tileentity.DiskDrive) extends DynamicGuiContainer(new container.DiskDrive(playerInventory, drive)) { - override def drawGuiContainerForegroundLayer(mouseX: Int, mouseY: Int) = { - super.drawGuiContainerForegroundLayer(mouseX, mouseY) + override def drawSecondaryForegroundLayer(mouseX: Int, mouseY: Int) = { + super.drawSecondaryForegroundLayer(mouseX, mouseY) fontRendererObj.drawString( Localization.localizeImmediately(drive.getName), 8, 6, 0x404040) diff --git a/src/main/scala/li/cil/oc/client/gui/Drone.scala b/src/main/scala/li/cil/oc/client/gui/Drone.scala index 202e3bfd5..7c6444c3c 100644 --- a/src/main/scala/li/cil/oc/client/gui/Drone.scala +++ b/src/main/scala/li/cil/oc/client/gui/Drone.scala @@ -87,7 +87,7 @@ class Drone(playerInventory: InventoryPlayer, val drone: entity.Drone) extends D override protected def changeSize(w: Double, h: Double, recompile: Boolean) = 2.0 - override protected def drawGuiContainerForegroundLayer(mouseX: Int, mouseY: Int) { + override protected def drawSecondaryForegroundLayer(mouseX: Int, mouseY: Int) { drawBufferLayer() GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS) // Me lazy... prevents NEI render glitch. if (isPointInRegion(power.x, power.y, power.width, power.height, mouseX, mouseY)) { diff --git a/src/main/scala/li/cil/oc/client/gui/DynamicGuiContainer.scala b/src/main/scala/li/cil/oc/client/gui/DynamicGuiContainer.scala index 318560a02..f011cfde3 100644 --- a/src/main/scala/li/cil/oc/client/gui/DynamicGuiContainer.scala +++ b/src/main/scala/li/cil/oc/client/gui/DynamicGuiContainer.scala @@ -1,18 +1,21 @@ package li.cil.oc.client.gui +import codechicken.nei.ItemPanel +import codechicken.nei.LayoutManager import li.cil.oc.Localization import li.cil.oc.client.Textures import li.cil.oc.common import li.cil.oc.common.container.ComponentSlot import li.cil.oc.common.container.Player +import li.cil.oc.integration.Mods import li.cil.oc.integration.util.NEI import li.cil.oc.util.RenderState import net.minecraft.client.gui.Gui -import net.minecraft.client.renderer.GlStateManager import net.minecraft.client.renderer.Tessellator import net.minecraft.inventory.Container import net.minecraft.inventory.Slot import net.minecraft.item.ItemStack +import net.minecraftforge.fml.common.Optional import org.lwjgl.opengl.GL11 import scala.collection.convert.WrapAsScala._ @@ -22,12 +25,24 @@ abstract class DynamicGuiContainer(container: Container) extends CustomGuiContai protected var hoveredStackNEI: Option[ItemStack] = None - override protected def drawGuiContainerForegroundLayer(mouseX: Int, mouseY: Int) { + protected def drawSecondaryForegroundLayer(mouseX: Int, mouseY: Int) { fontRendererObj.drawString( Localization.localizeImmediately("container.inventory"), 8, ySize - 96 + 2, 0x404040) } + override protected def drawGuiContainerForegroundLayer(mouseX: Int, mouseY: Int) { + GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS) + + drawSecondaryForegroundLayer(mouseX, mouseY) + + for (slot <- 0 until inventorySlots.inventorySlots.size()) { + drawSlotHighlight(inventorySlots.inventorySlots.get(slot).asInstanceOf[Slot]) + } + + GL11.glPopAttrib() + } + protected def drawSecondaryBackgroundLayer() {} override protected def drawGuiContainerBackgroundLayer(dt: Float, mouseX: Int, mouseY: Int) { @@ -41,9 +56,11 @@ abstract class DynamicGuiContainer(container: Container) extends CustomGuiContai GL11.glPushMatrix() GL11.glTranslatef(guiLeft, guiTop, 0) + GL11.glDisable(GL11.GL_DEPTH_TEST) for (slot <- 0 until inventorySlots.inventorySlots.size()) { drawSlotInventory(inventorySlots.inventorySlots.get(slot).asInstanceOf[Slot]) } + GL11.glEnable(GL11.GL_DEPTH_TEST) GL11.glPopMatrix() } @@ -55,12 +72,11 @@ abstract class DynamicGuiContainer(container: Container) extends CustomGuiContai super.drawScreen(mouseX, mouseY, dt) - GL11.glPushMatrix() - GL11.glTranslatef(guiLeft, guiTop, 0) - for (slot <- 0 until inventorySlots.inventorySlots.size()) { - drawSlotHighlight(inventorySlots.inventorySlots.get(slot).asInstanceOf[Slot]) + if (Mods.NotEnoughItems.isAvailable) { + GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS) + drawNEIHighlights() + GL11.glPopAttrib() } - GL11.glPopMatrix() } protected def drawSlotInventory(slot: Slot) { @@ -70,13 +86,13 @@ abstract class DynamicGuiContainer(container: Container) extends CustomGuiContai drawDisabledSlot(component) } case _ => + zLevel += 1 if (!isInPlayerInventory(slot)) { drawSlotBackground(slot.xDisplayPosition - 1, slot.yDisplayPosition - 1) } if (!slot.getHasStack) { slot match { case component: ComponentSlot => - GL11.glDisable(GL11.GL_DEPTH_TEST) if (component.tierIcon != null) { mc.getTextureManager.bindTexture(component.tierIcon) Gui.drawModalRectWithCustomSizedTexture(slot.xDisplayPosition, slot.yDisplayPosition, 0, 0, 16, 16, 16, 16) @@ -85,38 +101,37 @@ abstract class DynamicGuiContainer(container: Container) extends CustomGuiContai mc.getTextureManager.bindTexture(slot.getBackgroundLocation) Gui.drawModalRectWithCustomSizedTexture(slot.xDisplayPosition, slot.yDisplayPosition, 0, 0, 16, 16, 16, 16) } - GL11.glEnable(GL11.GL_DEPTH_TEST) case _ => } + zLevel -= 1 } } } protected def drawSlotHighlight(slot: Slot) { - slot match { + if (mc.thePlayer.inventory.getItemStack == null) slot match { case component: ComponentSlot if component.slot == common.Slot.None || component.tier == common.Tier.None => // Ignore. case _ => - if (mc.thePlayer.inventory.getItemStack == null) { - val currentIsInPlayerInventory = isInPlayerInventory(slot) - val drawHighlight = hoveredSlot match { - case Some(hovered) => - val hoveredIsInPlayerInventory = isInPlayerInventory(hovered) - (currentIsInPlayerInventory != hoveredIsInPlayerInventory) && - ((currentIsInPlayerInventory && slot.getHasStack && isSelectiveSlot(hovered) && hovered.isItemValid(slot.getStack)) || - (hoveredIsInPlayerInventory && hovered.getHasStack && isSelectiveSlot(slot) && slot.isItemValid(hovered.getStack))) - case _ => hoveredStackNEI match { - case Some(stack) => !currentIsInPlayerInventory && isSelectiveSlot(slot) && slot.isItemValid(stack) - case _ => false - } - } - if (drawHighlight) { - GlStateManager.disableLighting() - GL11.glDisable(GL11.GL_DEPTH_TEST) - drawGradientRect(slot.xDisplayPosition, slot.yDisplayPosition, slot.xDisplayPosition + 16, slot.yDisplayPosition + 16, 0x80FFFFFF, 0x80FFFFFF) - GlStateManager.enableLighting() - GL11.glEnable(GL11.GL_DEPTH_TEST) + val currentIsInPlayerInventory = isInPlayerInventory(slot) + val drawHighlight = hoveredSlot match { + case Some(hovered) => + val hoveredIsInPlayerInventory = isInPlayerInventory(hovered) + (currentIsInPlayerInventory != hoveredIsInPlayerInventory) && + ((currentIsInPlayerInventory && slot.getHasStack && isSelectiveSlot(hovered) && hovered.isItemValid(slot.getStack)) || + (hoveredIsInPlayerInventory && hovered.getHasStack && isSelectiveSlot(slot) && slot.isItemValid(hovered.getStack))) + case _ => hoveredStackNEI match { + case Some(stack) => !currentIsInPlayerInventory && isSelectiveSlot(slot) && slot.isItemValid(stack) + case _ => false } } + if (drawHighlight) { + zLevel += 100 + drawGradientRect( + slot.xDisplayPosition, slot.yDisplayPosition, + slot.xDisplayPosition + 16, slot.yDisplayPosition + 16, + 0x80FFFFFF, 0x80FFFFFF) + zLevel -= 100 + } } } @@ -128,11 +143,7 @@ abstract class DynamicGuiContainer(container: Container) extends CustomGuiContai protected def drawDisabledSlot(slot: ComponentSlot) { GL11.glColor4f(1, 1, 1, 1) mc.getTextureManager.bindTexture(slot.tierIcon) - GL11.glDisable(GL11.GL_DEPTH_TEST) - GL11.glDisable(GL11.GL_LIGHTING) Gui.drawModalRectWithCustomSizedTexture(slot.xDisplayPosition, slot.yDisplayPosition, 0, 0, 16, 16, 16, 16) - GL11.glEnable(GL11.GL_LIGHTING) - GL11.glEnable(GL11.GL_DEPTH_TEST) } protected def drawSlotBackground(x: Int, y: Int) { @@ -152,4 +163,26 @@ abstract class DynamicGuiContainer(container: Container) extends CustomGuiContai case player: Player => slot.inventory == player.playerInventory case _ => false } + + @Optional.Method(modid = Mods.IDs.NotEnoughItems) + private def drawNEIHighlights(): Unit = { + val panel = LayoutManager.itemPanel + if (panel == null) return + zLevel += 350 + for (index <- 0 until ItemPanel.items.size()) { + val rect = panel.getSlotRect(index) + val slot = panel.getSlotMouseOver(rect.x, rect.y) + if (slot != null) hoveredSlot match { + case Some(hovered) => + if (!isInPlayerInventory(hovered) && isSelectiveSlot(hovered) && hovered.isItemValid(slot.item)) { + drawGradientRect( + rect.x1 + 1, rect.y1 + 1, + rect.x2, rect.y2, + 0x40FFFFFF, 0x40FFFFFF) + } + case _ => + } + } + zLevel -= 350 + } } diff --git a/src/main/scala/li/cil/oc/client/gui/Raid.scala b/src/main/scala/li/cil/oc/client/gui/Raid.scala index 496bb6b0f..8ff5d4fbe 100644 --- a/src/main/scala/li/cil/oc/client/gui/Raid.scala +++ b/src/main/scala/li/cil/oc/client/gui/Raid.scala @@ -8,8 +8,8 @@ import net.minecraft.entity.player.InventoryPlayer import org.lwjgl.opengl.GL11 class Raid(playerInventory: InventoryPlayer, val raid: tileentity.Raid) extends DynamicGuiContainer(new container.Raid(playerInventory, raid)) { - override def drawGuiContainerForegroundLayer(mouseX: Int, mouseY: Int) = { - super.drawGuiContainerForegroundLayer(mouseX, mouseY) + override def drawSecondaryForegroundLayer(mouseX: Int, mouseY: Int) = { + super.drawSecondaryForegroundLayer(mouseX, mouseY) fontRendererObj.drawString( Localization.localizeImmediately(raid.getName), 8, 6, 0x404040) diff --git a/src/main/scala/li/cil/oc/client/gui/Robot.scala b/src/main/scala/li/cil/oc/client/gui/Robot.scala index 30e6b227e..9605b2fe8 100644 --- a/src/main/scala/li/cil/oc/client/gui/Robot.scala +++ b/src/main/scala/li/cil/oc/client/gui/Robot.scala @@ -128,7 +128,7 @@ class Robot(playerInventory: InventoryPlayer, val robot: tileentity.Robot) exten } } - override protected def drawGuiContainerForegroundLayer(mouseX: Int, mouseY: Int) { + override protected def drawSecondaryForegroundLayer(mouseX: Int, mouseY: Int) { drawBufferLayer() GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS) // Me lazy... prevents NEI render glitch. if (isPointInRegion(power.x, power.y, power.width, power.height, mouseX, mouseY)) { diff --git a/src/main/scala/li/cil/oc/client/gui/Screen.scala b/src/main/scala/li/cil/oc/client/gui/Screen.scala index 984e57fe5..2c6e80fe2 100644 --- a/src/main/scala/li/cil/oc/client/gui/Screen.scala +++ b/src/main/scala/li/cil/oc/client/gui/Screen.scala @@ -20,18 +20,18 @@ class Screen(val buffer: api.component.TextBuffer, val hasMouse: Boolean, val ha private var x, y = 0 - private var mx, my = 0 + private var mx, my = -1 override def handleMouseInput() { super.handleMouseInput() if (hasMouse && Mouse.hasWheel && Mouse.getEventDWheel != 0) { val mouseX = Mouse.getEventX * width / mc.displayWidth val mouseY = height - Mouse.getEventY * height / mc.displayHeight - 1 - val bx = (mouseX - x - bufferMargin) / TextBufferRenderCache.renderer.charRenderWidth + 1 - val by = (mouseY - y - bufferMargin) / TextBufferRenderCache.renderer.charRenderHeight + 1 + val bx = (mouseX - x - bufferMargin) / TextBufferRenderCache.renderer.charRenderWidth.toDouble + val by = (mouseY - y - bufferMargin) / TextBufferRenderCache.renderer.charRenderHeight.toDouble val bw = buffer.getWidth val bh = buffer.getHeight - if (bx > 0 && by > 0 && bx <= bw && by <= bh) { + if (bx >= 0 && by >= 0 && bx < bw && by < bh) { val scroll = math.signum(Mouse.getEventDWheel) buffer.mouseScroll(bx, by, scroll, null) } @@ -60,35 +60,35 @@ class Screen(val buffer: api.component.TextBuffer, val hasMouse: Boolean, val ha super.mouseReleased(mouseX, mouseY, button) if (hasMouse && button >= 0) { if (didDrag) { - val bx = ((mouseX - x - bufferMargin) / scale / TextBufferRenderCache.renderer.charRenderWidth).toInt + 1 - val by = ((mouseY - y - bufferMargin) / scale / TextBufferRenderCache.renderer.charRenderHeight).toInt + 1 + val bx = (mouseX - x - bufferMargin) / scale / TextBufferRenderCache.renderer.charRenderWidth + val by = (mouseY - y - bufferMargin) / scale / TextBufferRenderCache.renderer.charRenderHeight val bw = buffer.getWidth val bh = buffer.getHeight - if (bx > 0 && by > 0 && bx <= bw && by <= bh) { + if (bx >= 0 && by >= 0 && bx < bw && by < bh) { buffer.mouseUp(bx, by, button, null) } else { - buffer.mouseUp(-1, -1, button, null) + buffer.mouseUp(-1.0, -1.0, button, null) } } didDrag = false - mx = 0 - my = 0 + mx = -1 + my = -1 } } private def clickOrDrag(mouseX: Int, mouseY: Int, button: Int) { - val bx = ((mouseX - x - bufferMargin) / scale / TextBufferRenderCache.renderer.charRenderWidth).toInt + 1 - val by = ((mouseY - y - bufferMargin) / scale / TextBufferRenderCache.renderer.charRenderHeight).toInt + 1 + val bx = (mouseX - x - bufferMargin) / scale / TextBufferRenderCache.renderer.charRenderWidth + val by = (mouseY - y - bufferMargin) / scale / TextBufferRenderCache.renderer.charRenderHeight val bw = buffer.getWidth val bh = buffer.getHeight - if (bx > 0 && by > 0 && bx <= bw && by <= bh) { - if (bx != mx || by != my) { - if (mx > 0 && my > 0) buffer.mouseDrag(bx, by, button, null) + if (bx >= 0 && by >= 0 && bx < bw && by < bh) { + if (bx.toInt != mx || by.toInt != my) { + if (mx >= 0 && my >= 0) buffer.mouseDrag(bx, by, button, null) else buffer.mouseDown(bx, by, button, null) - didDrag = mx > 0 && my > 0 - mx = bx - my = by + didDrag = mx >= 0 && my >= 0 + mx = bx.toInt + my = by.toInt } } } diff --git a/src/main/scala/li/cil/oc/client/gui/Server.scala b/src/main/scala/li/cil/oc/client/gui/Server.scala index d455e2b2d..a8f4fee93 100644 --- a/src/main/scala/li/cil/oc/client/gui/Server.scala +++ b/src/main/scala/li/cil/oc/client/gui/Server.scala @@ -9,8 +9,8 @@ import net.minecraft.inventory.Slot import org.lwjgl.opengl.GL11 class Server(playerInventory: InventoryPlayer, serverInventory: ServerInventory) extends DynamicGuiContainer(new container.Server(playerInventory, serverInventory)) { - override def drawGuiContainerForegroundLayer(mouseX: Int, mouseY: Int) { - super.drawGuiContainerForegroundLayer(mouseX, mouseY) + override def drawSecondaryForegroundLayer(mouseX: Int, mouseY: Int) { + super.drawSecondaryForegroundLayer(mouseX, mouseY) fontRendererObj.drawString( Localization.localizeImmediately(serverInventory.getName), 8, 6, 0x404040) diff --git a/src/main/scala/li/cil/oc/client/gui/ServerRack.scala b/src/main/scala/li/cil/oc/client/gui/ServerRack.scala index 246b521e8..feff32150 100644 --- a/src/main/scala/li/cil/oc/client/gui/ServerRack.scala +++ b/src/main/scala/li/cil/oc/client/gui/ServerRack.scala @@ -73,8 +73,8 @@ class ServerRack(playerInventory: InventoryPlayer, val rack: tileentity.ServerRa add(buttonList, switchButton) } - override def drawGuiContainerForegroundLayer(mouseX: Int, mouseY: Int) = { - super.drawGuiContainerForegroundLayer(mouseX, mouseY) + override def drawSecondaryForegroundLayer(mouseX: Int, mouseY: Int) = { + super.drawSecondaryForegroundLayer(mouseX, mouseY) GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS) // Prevents NEI render glitch. fontRendererObj.drawString( diff --git a/src/main/scala/li/cil/oc/client/gui/Switch.scala b/src/main/scala/li/cil/oc/client/gui/Switch.scala index ac3938225..e56d20e63 100644 --- a/src/main/scala/li/cil/oc/client/gui/Switch.scala +++ b/src/main/scala/li/cil/oc/client/gui/Switch.scala @@ -11,8 +11,8 @@ class Switch(playerInventory: InventoryPlayer, val switch: tileentity.Switch) ex private val switchContainer = inventorySlots.asInstanceOf[container.Switch] private val format = new DecimalFormat("#.##hz") - override def drawGuiContainerForegroundLayer(mouseX: Int, mouseY: Int) = { - super.drawGuiContainerForegroundLayer(mouseX, mouseY) + override def drawSecondaryForegroundLayer(mouseX: Int, mouseY: Int) = { + super.drawSecondaryForegroundLayer(mouseX, mouseY) fontRendererObj.drawString( Localization.localizeImmediately(switch.getName), 8, 6, 0x404040) diff --git a/src/main/scala/li/cil/oc/common/EventHandler.scala b/src/main/scala/li/cil/oc/common/EventHandler.scala index 3fe8e2db5..e8a88959e 100644 --- a/src/main/scala/li/cil/oc/common/EventHandler.scala +++ b/src/main/scala/li/cil/oc/common/EventHandler.scala @@ -7,6 +7,7 @@ import li.cil.oc.api.Network import li.cil.oc.api.detail.ItemInfo import li.cil.oc.client.renderer.PetRenderer import li.cil.oc.client.{PacketSender => ClientPacketSender} +import li.cil.oc.common.item.data.MicrocontrollerData import li.cil.oc.integration.Mods import li.cil.oc.integration.util import li.cil.oc.server.{PacketSender => ServerPacketSender} @@ -45,7 +46,7 @@ object EventHandler { def scheduleWirelessRedstone(rs: server.component.RedstoneWireless) { if (SideTracker.isServer) pending.synchronized { - pending += (() => if (!rs.owner.isInvalid) { + pending += (() => if (rs.node.network != null) { util.WirelessRedstone.addReceiver(rs) util.WirelessRedstone.updateOutput(rs) }) @@ -90,11 +91,18 @@ object EventHandler { @SubscribeEvent def clientLoggedIn(e: ClientConnectedToServerEvent) { - PetRenderer.hidden.clear() - if (Settings.get.hideOwnPet) { - PetRenderer.hidden += Minecraft.getMinecraft.thePlayer.getName + try { + PetRenderer.hidden.clear() + if (Settings.get.hideOwnPet) { + PetRenderer.hidden += Minecraft.getMinecraft.thePlayer.getName + } + ClientPacketSender.sendPetVisibility() + } + catch { + case _: Throwable => + // Reportedly, things can derp if this is called at inopportune moments, + // such as the server shutting down. } - ClientPacketSender.sendPetVisibility() } lazy val drone = api.Items.get("drone") @@ -116,12 +124,12 @@ object EventHandler { didRecraft = recraft(e, mcu, stack => { // Restore EEPROM currently used in microcontroller. - new ItemUtils.MicrocontrollerData(stack).components.find(api.Items.get(_) == eeprom) + new MicrocontrollerData(stack).components.find(api.Items.get(_) == eeprom) }) || didRecraft didRecraft = recraft(e, drone, stack => { // Restore EEPROM currently used in drone. - new ItemUtils.MicrocontrollerData(stack).components.find(api.Items.get(_) == eeprom) + new MicrocontrollerData(stack).components.find(api.Items.get(_) == eeprom) }) || didRecraft // Presents? diff --git a/src/main/scala/li/cil/oc/common/PacketType.scala b/src/main/scala/li/cil/oc/common/PacketType.scala index cb9aa5d2d..6cd957cc2 100644 --- a/src/main/scala/li/cil/oc/common/PacketType.scala +++ b/src/main/scala/li/cil/oc/common/PacketType.scala @@ -40,6 +40,9 @@ object PacketType extends Enumeration { TextBufferMultiResolutionChange, TextBufferMultiMaxResolutionChange, TextBufferMultiSet, + TextBufferMultiRawSetText, + TextBufferMultiRawSetBackground, + TextBufferMultiRawSetForeground, TextBufferPowerChange, ScreenTouchMode, ServerPresence, diff --git a/src/main/scala/li/cil/oc/common/block/Adapter.scala b/src/main/scala/li/cil/oc/common/block/Adapter.scala index 8f0562934..8a0f2e905 100644 --- a/src/main/scala/li/cil/oc/common/block/Adapter.scala +++ b/src/main/scala/li/cil/oc/common/block/Adapter.scala @@ -1,33 +1,23 @@ package li.cil.oc.common.block -import li.cil.oc.OpenComputers import li.cil.oc.common.GuiType import li.cil.oc.common.tileentity import net.minecraft.block.Block import net.minecraft.block.state.IBlockState -import net.minecraft.entity.player.EntityPlayer import net.minecraft.util.BlockPos import net.minecraft.util.EnumFacing import net.minecraft.world.IBlockAccess import net.minecraft.world.World -class Adapter extends SimpleBlock { +class Adapter extends SimpleBlock with traits.GUI { + override def guiType = GuiType.Adapter + override def hasTileEntity(state: IBlockState) = true override def createNewTileEntity(world: World, metadata: Int) = new tileentity.Adapter() // ----------------------------------------------------------------------- // - override def localOnBlockActivated(world: World, pos: BlockPos, player: EntityPlayer, side: EnumFacing, hitX: Float, hitY: Float, hitZ: Float) = { - if (!player.isSneaking) { - if (!world.isRemote) { - player.openGui(OpenComputers, GuiType.Adapter.id, world, pos.getX, pos.getY, pos.getZ) - } - true - } - else false - } - override def onNeighborBlockChange(world: World, pos: BlockPos, state: IBlockState, neighborBlock: Block) = world.getTileEntity(pos) match { case adapter: tileentity.Adapter => adapter.neighborChanged() diff --git a/src/main/scala/li/cil/oc/common/block/Assembler.scala b/src/main/scala/li/cil/oc/common/block/Assembler.scala index 9ab930bc4..cbad9e7cb 100644 --- a/src/main/scala/li/cil/oc/common/block/Assembler.scala +++ b/src/main/scala/li/cil/oc/common/block/Assembler.scala @@ -1,17 +1,15 @@ package li.cil.oc.common.block -import li.cil.oc.OpenComputers import li.cil.oc.Settings import li.cil.oc.common.GuiType import li.cil.oc.common.tileentity import net.minecraft.block.state.IBlockState -import net.minecraft.entity.player.EntityPlayer import net.minecraft.util.BlockPos import net.minecraft.util.EnumFacing import net.minecraft.world.IBlockAccess import net.minecraft.world.World -class Assembler extends SimpleBlock with traits.PowerAcceptor with traits.StateAware { +class Assembler extends SimpleBlock with traits.PowerAcceptor with traits.StateAware with traits.GUI { setLightLevel(0.34f) override def isOpaqueCube = false @@ -20,23 +18,15 @@ class Assembler extends SimpleBlock with traits.PowerAcceptor with traits.StateA override def isBlockSolid(world: IBlockAccess, pos: BlockPos, side: EnumFacing) = side == EnumFacing.DOWN || side == EnumFacing.UP + override def isSideSolid(world: IBlockAccess, pos: BlockPos, side: EnumFacing) = side == EnumFacing.DOWN || side == EnumFacing.UP + // ----------------------------------------------------------------------- // override def energyThroughput = Settings.get.assemblerRate + override def guiType = GuiType.Assembler + override def hasTileEntity(state: IBlockState) = true override def createNewTileEntity(world: World, metadata: Int) = new tileentity.Assembler() - - // ----------------------------------------------------------------------- // - - override def localOnBlockActivated(world: World, pos: BlockPos, player: EntityPlayer, side: EnumFacing, hitX: Float, hitY: Float, hitZ: Float) = { - if (!player.isSneaking) { - if (!world.isRemote) { - player.openGui(OpenComputers, GuiType.Assembler.id, world, pos.getX, pos.getY, pos.getZ) - } - true - } - else false - } } diff --git a/src/main/scala/li/cil/oc/common/block/Cable.scala b/src/main/scala/li/cil/oc/common/block/Cable.scala index a63508731..bf67d839c 100644 --- a/src/main/scala/li/cil/oc/common/block/Cable.scala +++ b/src/main/scala/li/cil/oc/common/block/Cable.scala @@ -8,7 +8,9 @@ import codechicken.multipart.TFacePart import codechicken.multipart.TileMultipart import li.cil.oc.integration.fmp.CablePart */ + import li.cil.oc.api.network.Environment +import li.cil.oc.api.network.SidedComponent import li.cil.oc.api.network.SidedEnvironment import li.cil.oc.common.tileentity import li.cil.oc.integration.Mods @@ -65,6 +67,8 @@ class Cable extends SimpleBlock with traits.Extended { override def shouldSideBeRendered(world: IBlockAccess, pos: BlockPos, side: EnumFacing) = true + override def isSideSolid(world: IBlockAccess, pos: BlockPos, side: EnumFacing) = false + // ----------------------------------------------------------------------- // override def hasTileEntity(state: IBlockState) = true @@ -142,12 +146,15 @@ object Cable { case host: SidedEnvironment => if (host.getWorld.isRemote) host.canConnect(side) else host.sidedNode(side) != null + case host: Environment with SidedComponent => + host.canConnectNode(side) case host: Environment => true case host if Mods.ForgeMultipart.isAvailable => hasMultiPartNode(tileEntity) case _ => false } private def hasMultiPartNode(tileEntity: TileEntity) = false + /* TODO FMP tileEntity match { case host: TileMultipart => host.partList.exists(_.isInstanceOf[CablePart]) @@ -164,6 +171,7 @@ object Cable { } private def cableColorFMP(tileEntity: TileEntity) = EnumDyeColor.SILVER + /* TODO FMP tileEntity match { case host: TileMultipart => (host.partList collect { @@ -179,6 +187,7 @@ object Cable { } private def canConnectFromSideFMP(tileEntity: TileEntity, side: EnumFacing) = true + /* TODO FMP tileEntity match { case host: TileMultipart => diff --git a/src/main/scala/li/cil/oc/common/block/Capacitor.scala b/src/main/scala/li/cil/oc/common/block/Capacitor.scala index 6585e85c2..a2a701035 100644 --- a/src/main/scala/li/cil/oc/common/block/Capacitor.scala +++ b/src/main/scala/li/cil/oc/common/block/Capacitor.scala @@ -1,5 +1,7 @@ package li.cil.oc.common.block +import java.util.Random + import li.cil.oc.common.tileentity import net.minecraft.block.Block import net.minecraft.block.state.IBlockState @@ -8,6 +10,7 @@ import net.minecraft.world.World class Capacitor extends SimpleBlock { setLightLevel(0.34f) + setTickRandomly(true) // ----------------------------------------------------------------------- // @@ -17,6 +20,21 @@ class Capacitor extends SimpleBlock { // ----------------------------------------------------------------------- // + override def hasComparatorInputOverride = true + + override def getComparatorInputOverride(world: World, pos: BlockPos): Int = + world.getTileEntity(pos) match { + case capacitor: tileentity.Capacitor if !world.isRemote => + math.round(15 * capacitor.node.localBuffer / capacitor.node.localBufferSize).toInt + case _ => 0 + } + + override def updateTick(world: World, pos: BlockPos, state: IBlockState, rand: Random): Unit = { + world.notifyNeighborsOfStateChange(pos, this) + } + + override def tickRate(world: World) = 1 + override def onNeighborBlockChange(world: World, pos: BlockPos, state: IBlockState, neighborBlock: Block) = world.getTileEntity(pos) match { case capacitor: tileentity.Capacitor => capacitor.recomputeCapacity() diff --git a/src/main/scala/li/cil/oc/common/block/Case.scala b/src/main/scala/li/cil/oc/common/block/Case.scala index 63a18bdcb..4042703aa 100644 --- a/src/main/scala/li/cil/oc/common/block/Case.scala +++ b/src/main/scala/li/cil/oc/common/block/Case.scala @@ -2,11 +2,9 @@ package li.cil.oc.common.block import java.util -import li.cil.oc.OpenComputers import li.cil.oc.Settings import li.cil.oc.common.GuiType import li.cil.oc.common.tileentity -import li.cil.oc.integration.util.Wrench import li.cil.oc.util.Color import li.cil.oc.util.Rarity import li.cil.oc.util.Tooltip @@ -28,7 +26,7 @@ object Case { final val Running = PropertyBool.create("running") } -class Case(val tier: Int) extends RedstoneAware with traits.PowerAcceptor with traits.Rotatable with traits.StateAware { +class Case(val tier: Int) extends RedstoneAware with traits.PowerAcceptor with traits.Rotatable with traits.StateAware with traits.GUI { def getRunning(state: IBlockState) = if (state.getBlock == this) state.getValue(Case.Running).asInstanceOf[Boolean] @@ -69,27 +67,21 @@ class Case(val tier: Int) extends RedstoneAware with traits.PowerAcceptor with t override def energyThroughput = Settings.get.caseRate(tier) + override def guiType = GuiType.Case + override def createNewTileEntity(world: World, metadata: Int) = new tileentity.Case(tier) // ----------------------------------------------------------------------- // override def localOnBlockActivated(world: World, pos: BlockPos, player: EntityPlayer, side: EnumFacing, hitX: Float, hitY: Float, hitZ: Float) = { - if (!player.isSneaking && !Wrench.holdsApplicableWrench(player, pos)) { - if (!world.isRemote) { - player.openGui(OpenComputers, GuiType.Case.id, world, pos.getX, pos.getY, pos.getZ) + if (player.isSneaking) { + if (!world.isRemote) world.getTileEntity(pos) match { + case computer: tileentity.Case if !computer.machine.isRunning => computer.machine.start() + case _ => } true } - else if (player.getCurrentEquippedItem == null) { - if (!world.isRemote) { - world.getTileEntity(pos) match { - case computer: tileentity.Case if !computer.machine.isRunning => computer.machine.start() - case _ => - } - } - true - } - else false + else super.localOnBlockActivated(world, pos, player, side, hitX, hitY, hitZ) } override def removedByPlayer(world: World, pos: BlockPos, player: EntityPlayer, willHarvest: Boolean) = diff --git a/src/main/scala/li/cil/oc/common/block/Charger.scala b/src/main/scala/li/cil/oc/common/block/Charger.scala index 6789dfd1c..1b21d92f1 100644 --- a/src/main/scala/li/cil/oc/common/block/Charger.scala +++ b/src/main/scala/li/cil/oc/common/block/Charger.scala @@ -1,6 +1,5 @@ package li.cil.oc.common.block -import li.cil.oc.OpenComputers import li.cil.oc.Settings import li.cil.oc.common.GuiType import li.cil.oc.common.tileentity @@ -14,11 +13,13 @@ import net.minecraft.util.EnumFacing import net.minecraft.world.IBlockAccess import net.minecraft.world.World -class Charger extends RedstoneAware with traits.PowerAcceptor with traits.Rotatable with traits.StateAware { +class Charger extends RedstoneAware with traits.PowerAcceptor with traits.Rotatable with traits.StateAware with traits.GUI { override protected def setDefaultExtendedState(state: IBlockState) = setDefaultState(state) override def energyThroughput = Settings.get.chargerRate + override def guiType = GuiType.Charger + override def createNewTileEntity(world: World, metadata: Int) = new tileentity.Charger() // ----------------------------------------------------------------------- // @@ -28,26 +29,18 @@ class Charger extends RedstoneAware with traits.PowerAcceptor with traits.Rotata // ----------------------------------------------------------------------- // override def localOnBlockActivated(world: World, pos: BlockPos, player: EntityPlayer, side: EnumFacing, hitX: Float, hitY: Float, hitZ: Float) = - world.getTileEntity(pos) match { + if (Wrench.holdsApplicableWrench(player, pos)) world.getTileEntity(pos) match { case charger: tileentity.Charger => - if (Wrench.holdsApplicableWrench(player, pos)) { - if (!world.isRemote) { - charger.invertSignal = !charger.invertSignal - charger.chargeSpeed = 1.0 - charger.chargeSpeed - PacketSender.sendChargerState(charger) - Wrench.wrenchUsed(player, pos) - } - true + if (!world.isRemote) { + charger.invertSignal = !charger.invertSignal + charger.chargeSpeed = 1.0 - charger.chargeSpeed + PacketSender.sendChargerState(charger) + Wrench.wrenchUsed(player, pos) } - else if (!player.isSneaking) { - if (!world.isRemote) { - player.openGui(OpenComputers, GuiType.Charger.id, world, pos.getX, pos.getY, pos.getZ) - } - true - } - else false - case _ => super.localOnBlockActivated(world, pos, player, side, hitX, hitY, hitZ) + true + case _ => false } + else super.localOnBlockActivated(world, pos, player, side, hitX, hitY, hitZ) override def onNeighborBlockChange(world: World, pos: BlockPos, state: IBlockState, neighborBlock: Block) { world.getTileEntity(pos) match { diff --git a/src/main/scala/li/cil/oc/common/block/Disassembler.scala b/src/main/scala/li/cil/oc/common/block/Disassembler.scala index fd4b5ad4c..8e25576da 100644 --- a/src/main/scala/li/cil/oc/common/block/Disassembler.scala +++ b/src/main/scala/li/cil/oc/common/block/Disassembler.scala @@ -1,6 +1,5 @@ package li.cil.oc.common.block -import li.cil.oc.OpenComputers import li.cil.oc.Settings import li.cil.oc.common.GuiType import li.cil.oc.common.tileentity @@ -8,11 +7,9 @@ import li.cil.oc.util.Tooltip import net.minecraft.block.state.IBlockState import net.minecraft.entity.player.EntityPlayer import net.minecraft.item.ItemStack -import net.minecraft.util.BlockPos -import net.minecraft.util.EnumFacing import net.minecraft.world.World -class Disassembler extends SimpleBlock with traits.PowerAcceptor with traits.StateAware { +class Disassembler extends SimpleBlock with traits.PowerAcceptor with traits.StateAware with traits.GUI { override protected def tooltipBody(metadata: Int, stack: ItemStack, player: EntityPlayer, tooltip: java.util.List[String], advanced: Boolean) { tooltip.addAll(Tooltip.get(getClass.getSimpleName, (Settings.get.disassemblerBreakChance * 100).toInt.toString)) } @@ -21,19 +18,9 @@ class Disassembler extends SimpleBlock with traits.PowerAcceptor with traits.Sta override def energyThroughput = Settings.get.disassemblerRate + override def guiType = GuiType.Disassembler + override def hasTileEntity(state: IBlockState) = true override def createNewTileEntity(world: World, metadata: Int) = new tileentity.Disassembler() - - // ----------------------------------------------------------------------- // - - override def localOnBlockActivated(world: World, pos: BlockPos, player: EntityPlayer, side: EnumFacing, hitX: Float, hitY: Float, hitZ: Float) = { - if (!player.isSneaking) { - if (!world.isRemote) { - player.openGui(OpenComputers, GuiType.Disassembler.id, world, pos.getX, pos.getY, pos.getZ) - } - true - } - else false - } } diff --git a/src/main/scala/li/cil/oc/common/block/DiskDrive.scala b/src/main/scala/li/cil/oc/common/block/DiskDrive.scala index 8ddc4d0d9..ff950df55 100644 --- a/src/main/scala/li/cil/oc/common/block/DiskDrive.scala +++ b/src/main/scala/li/cil/oc/common/block/DiskDrive.scala @@ -1,6 +1,5 @@ package li.cil.oc.common.block -import li.cil.oc.OpenComputers import li.cil.oc.common.GuiType import li.cil.oc.common.tileentity import li.cil.oc.integration.Mods @@ -12,7 +11,7 @@ import net.minecraft.util.BlockPos import net.minecraft.util.EnumFacing import net.minecraft.world.World -class DiskDrive extends SimpleBlock with traits.Rotatable { +class DiskDrive extends SimpleBlock with traits.Rotatable with traits.GUI { override protected def setDefaultExtendedState(state: IBlockState) = setDefaultState(state) override protected def tooltipTail(metadata: Int, stack: ItemStack, player: EntityPlayer, tooltip: java.util.List[String], advanced: Boolean) { @@ -24,6 +23,8 @@ class DiskDrive extends SimpleBlock with traits.Rotatable { // ----------------------------------------------------------------------- // + override def guiType = GuiType.DiskDrive + override def hasTileEntity(state: IBlockState) = true override def createNewTileEntity(world: World, metadata: Int) = new tileentity.DiskDrive() @@ -41,30 +42,23 @@ class DiskDrive extends SimpleBlock with traits.Rotatable { // ----------------------------------------------------------------------- // override def localOnBlockActivated(world: World, pos: BlockPos, player: EntityPlayer, side: EnumFacing, hitX: Float, hitY: Float, hitZ: Float) = { - world.getTileEntity(pos) match { + // Behavior: sneaking -> Insert[+Eject], not sneaking -> GUI. + if (player.isSneaking) world.getTileEntity(pos) match { case drive: tileentity.DiskDrive => - // Behavior: sneaking -> Insert[+Eject], not sneaking -> GUI. - if (!player.isSneaking) { + val isDiskInDrive = drive.getStackInSlot(0) != null + val isHoldingDisk = drive.isItemValidForSlot(0, player.getCurrentEquippedItem) + if (isDiskInDrive) { if (!world.isRemote) { - player.openGui(OpenComputers, GuiType.DiskDrive.id, world, pos.getX, pos.getY, pos.getZ) + drive.dropSlot(0, 1, Option(drive.facing)) } - true } - else { - val isDiskInDrive = drive.getStackInSlot(0) != null - val isHoldingDisk = drive.isItemValidForSlot(0, player.getCurrentEquippedItem) - if (isDiskInDrive) { - if (!world.isRemote) { - drive.dropSlot(0, 1, Option(drive.facing)) - } - } - if (isHoldingDisk) { - // Insert the disk. - drive.setInventorySlotContents(0, player.inventory.decrStackSize(player.inventory.currentItem, 1)) - } - isDiskInDrive || isHoldingDisk + if (isHoldingDisk) { + // Insert the disk. + drive.setInventorySlotContents(0, player.inventory.decrStackSize(player.inventory.currentItem, 1)) } + isDiskInDrive || isHoldingDisk case _ => false } + else super.localOnBlockActivated(world, pos, player, side, hitX, hitY, hitZ) } } diff --git a/src/main/scala/li/cil/oc/common/block/Hologram.scala b/src/main/scala/li/cil/oc/common/block/Hologram.scala index d1e71d7f2..c7f10fa4b 100644 --- a/src/main/scala/li/cil/oc/common/block/Hologram.scala +++ b/src/main/scala/li/cil/oc/common/block/Hologram.scala @@ -32,6 +32,8 @@ class Hologram(val tier: Int) extends SimpleBlock { super.shouldSideBeRendered(world, pos, side) || side == EnumFacing.UP } + override def isSideSolid(world: IBlockAccess, pos: BlockPos, side: EnumFacing) = side == EnumFacing.DOWN + // ----------------------------------------------------------------------- // override def rarity(stack: ItemStack) = Rarity.byTier(tier) 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 081ef8183..b267bc333 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.RobotData import li.cil.oc.common.tileentity import li.cil.oc.util.Color import li.cil.oc.util.ItemCosts @@ -83,7 +84,7 @@ class Item(value: Block) extends ItemBlock(value) { // manually before it's placed to ensure different component addresses // in the different robots, to avoid interference of screens e.g. val needsCopying = player.capabilities.isCreativeMode && api.Items.get(stack) == api.Items.get("robot") - val stackToUse = if (needsCopying) new ItemUtils.RobotData(stack).copyItemStack() else stack + val stackToUse = if (needsCopying) new RobotData(stack).copyItemStack() else stack if (super.placeBlockAt(stackToUse, player, world, pos, side, hitX, hitY, hitZ, newState)) { // If it's a rotatable block try to make it face the player. world.getTileEntity(pos) match { 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 4e4c6a3e4..b53f3abe5 100644 --- a/src/main/scala/li/cil/oc/common/block/Microcontroller.scala +++ b/src/main/scala/li/cil/oc/common/block/Microcontroller.scala @@ -5,13 +5,12 @@ import java.util import li.cil.oc.Settings import li.cil.oc.client.KeyBindings import li.cil.oc.common.Tier +import li.cil.oc.common.item.data.MicrocontrollerData import li.cil.oc.common.tileentity import li.cil.oc.integration.util.NEI import li.cil.oc.integration.util.Wrench -import li.cil.oc.util.BlockPosition -import li.cil.oc.util.InventoryUtils -import li.cil.oc.util.ItemUtils import li.cil.oc.util.Rarity +import net.minecraft.block.Block import net.minecraft.block.state.IBlockState import net.minecraft.entity.EntityLivingBase import net.minecraft.entity.player.EntityPlayer @@ -19,10 +18,11 @@ import net.minecraft.item.ItemStack import net.minecraft.util.BlockPos import net.minecraft.util.EnumFacing import net.minecraft.util.MovingObjectPosition -import net.minecraft.world.IBlockAccess import net.minecraft.world.World -class Microcontroller extends RedstoneAware with traits.PowerAcceptor with traits.Rotatable with traits.StateAware { +import scala.reflect.ClassTag + +class Microcontroller(protected implicit val tileTag: ClassTag[tileentity.Microcontroller]) extends RedstoneAware with traits.PowerAcceptor with traits.Rotatable with traits.StateAware with traits.CustomDrops[tileentity.Microcontroller] { setCreativeTab(null) NEI.hide(this) @@ -36,17 +36,12 @@ class Microcontroller extends RedstoneAware with traits.PowerAcceptor with trait case _ => null } - // Custom drop logic for NBT tagged item stack. - override def getDrops(world: IBlockAccess, pos: BlockPos, state: IBlockState, fortune: Int) = new java.util.ArrayList[ItemStack]() - - override def onBlockHarvested(worldIn: World, pos: BlockPos, state: IBlockState, player: EntityPlayer) {} - // ----------------------------------------------------------------------- // override protected def tooltipTail(metadata: Int, stack: ItemStack, player: EntityPlayer, tooltip: util.List[String], advanced: Boolean) { super.tooltipTail(metadata, stack, player, tooltip, advanced) if (KeyBindings.showExtendedTooltips) { - val info = new ItemUtils.MicrocontrollerData(stack) + val info = new MicrocontrollerData(stack) for (component <- info.components) { tooltip.add("- " + component.getDisplayName) } @@ -54,7 +49,7 @@ class Microcontroller extends RedstoneAware with traits.PowerAcceptor with trait } override def rarity(stack: ItemStack) = { - val data = new ItemUtils.MicrocontrollerData(stack) + val data = new MicrocontrollerData(stack) Rarity.byTier(data.tier) } @@ -81,26 +76,16 @@ class Microcontroller extends RedstoneAware with traits.PowerAcceptor with trait else false } - override def onBlockPlacedBy(world: World, pos: BlockPos, state: IBlockState, placer: EntityLivingBase, stack: ItemStack) { - super.onBlockPlacedBy(world, pos, state, placer, stack) - if (!world.isRemote) world.getTileEntity(pos) match { - case mcu: tileentity.Microcontroller => - mcu.info.load(stack) - mcu.snooperNode.changeBuffer(mcu.info.storedEnergy - mcu.snooperNode.localBuffer) - case _ => - } + 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) } - override def removedByPlayer(world: World, pos: BlockPos, player: EntityPlayer, willHarvest: Boolean) = { - if (!world.isRemote) { - world.getTileEntity(pos) match { - case mcu: tileentity.Microcontroller => - mcu.saveComponents() - mcu.info.storedEnergy = mcu.snooperNode.localBuffer.toInt - InventoryUtils.spawnStackInWorld(BlockPosition(pos, world), mcu.info.createItemStack()) - case _ => - } - } - super.removedByPlayer(world, pos, player, willHarvest) + override protected def doCustomDrops(tileEntity: tileentity.Microcontroller, player: EntityPlayer, willHarvest: Boolean): Unit = { + super.doCustomDrops(tileEntity, player, willHarvest) + tileEntity.saveComponents() + tileEntity.info.storedEnergy = tileEntity.snooperNode.localBuffer.toInt + Block.spawnAsEntity(tileEntity.world, tileEntity.getPos, tileEntity.info.createItemStack()) } } 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 e9a199c8f..e243e30ff 100644 --- a/src/main/scala/li/cil/oc/common/block/Raid.scala +++ b/src/main/scala/li/cil/oc/common/block/Raid.scala @@ -1,19 +1,40 @@ package li.cil.oc.common.block -import li.cil.oc.OpenComputers +import java.util + +import li.cil.oc.client.KeyBindings import li.cil.oc.common.GuiType +import li.cil.oc.common.item.data.RaidData import li.cil.oc.common.tileentity +import net.minecraft.block.Block import net.minecraft.block.state.IBlockState +import net.minecraft.entity.EntityLivingBase import net.minecraft.entity.player.EntityPlayer +import net.minecraft.item.ItemStack import net.minecraft.util.BlockPos -import net.minecraft.util.EnumFacing import net.minecraft.world.World -class Raid extends SimpleBlock with traits.Rotatable { +import scala.reflect.ClassTag + +class Raid(protected implicit val tileTag: ClassTag[tileentity.Raid]) extends SimpleBlock with traits.Rotatable with traits.GUI with traits.CustomDrops[tileentity.Raid] { override protected def setDefaultExtendedState(state: IBlockState) = setDefaultState(state) override def hasTileEntity(state: IBlockState) = true + override protected def tooltipTail(metadata: Int, stack: ItemStack, player: EntityPlayer, tooltip: util.List[String], advanced: Boolean) { + super.tooltipTail(metadata, stack, player, tooltip, advanced) + if (KeyBindings.showExtendedTooltips) { + val data = new RaidData(stack) + for (disk <- data.disks) { + tooltip.add("- " + disk.getDisplayName) + } + } + } + + // ----------------------------------------------------------------------- // + + override def guiType = GuiType.Raid + override def createNewTileEntity(world: World, metadata: Int) = new tileentity.Raid() // ----------------------------------------------------------------------- // @@ -26,16 +47,27 @@ class Raid extends SimpleBlock with traits.Rotatable { case _ => 0 } - // ----------------------------------------------------------------------- // - - override def localOnBlockActivated(world: World, pos: BlockPos, player: EntityPlayer, side: EnumFacing, hitX: Float, hitY: Float, hitZ: Float) = { - world.getTileEntity(pos) match { - case drive: tileentity.Raid if !player.isSneaking => - if (!world.isRemote) { - player.openGui(OpenComputers, GuiType.Raid.id, world, pos.getX, pos.getY, pos.getZ) - } - true - case _ => false + 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)) } } + + override protected def doCustomDrops(tileEntity: tileentity.Raid, player: EntityPlayer, willHarvest: Boolean): Unit = { + super.doCustomDrops(tileEntity, player, willHarvest) + val stack = createItemStack() + val data = new RaidData() + data.disks = tileEntity.items.map(_.orNull) + tileEntity.filesystem.foreach(_.save(data.filesystem)) + data.label = Option(tileEntity.label.getLabel) + data.save(stack) + Block.spawnAsEntity(tileEntity.world, tileEntity.getPos, stack) + } } diff --git a/src/main/scala/li/cil/oc/common/block/RobotAfterimage.scala b/src/main/scala/li/cil/oc/common/block/RobotAfterimage.scala index 9055f23e0..2388ff489 100644 --- a/src/main/scala/li/cil/oc/common/block/RobotAfterimage.scala +++ b/src/main/scala/li/cil/oc/common/block/RobotAfterimage.scala @@ -4,9 +4,9 @@ import java.util.Random import li.cil.oc.Settings import li.cil.oc.api +import li.cil.oc.common.item.data.RobotData import li.cil.oc.common.tileentity import li.cil.oc.integration.util.NEI -import li.cil.oc.util.ItemUtils import li.cil.oc.util.Rarity import net.minecraft.block.state.IBlockState import net.minecraft.entity.player.EntityPlayer @@ -30,6 +30,8 @@ class RobotAfterimage extends SimpleBlock { override def isBlockSolid(world: IBlockAccess, pos: BlockPos, side: EnumFacing) = false + override def isSideSolid(world: IBlockAccess, pos: BlockPos, side: EnumFacing) = false + override def getPickBlock(target: MovingObjectPosition, world: World, pos: BlockPos) = findMovingRobot(world, pos) match { case Some(robot) => robot.info.createItemStack() @@ -43,7 +45,7 @@ class RobotAfterimage extends SimpleBlock { // ----------------------------------------------------------------------- // override def rarity(stack: ItemStack) = { - val data = new ItemUtils.RobotData(stack) + val data = new RobotData(stack) Rarity.byTier(data.tier) } diff --git a/src/main/scala/li/cil/oc/common/block/RobotProxy.scala b/src/main/scala/li/cil/oc/common/block/RobotProxy.scala index 936e27962..a15d4264d 100644 --- a/src/main/scala/li/cil/oc/common/block/RobotProxy.scala +++ b/src/main/scala/li/cil/oc/common/block/RobotProxy.scala @@ -7,13 +7,13 @@ import li.cil.oc.Settings import li.cil.oc.api import li.cil.oc.client.KeyBindings import li.cil.oc.common.GuiType +import li.cil.oc.common.item.data.RobotData import li.cil.oc.common.tileentity import li.cil.oc.integration.util.NEI import li.cil.oc.server.PacketSender import li.cil.oc.server.component.robot import li.cil.oc.util.BlockPosition import li.cil.oc.util.InventoryUtils -import li.cil.oc.util.ItemUtils import li.cil.oc.util.Rarity import li.cil.oc.util.Tooltip import net.minecraft.block.state.IBlockState @@ -46,6 +46,8 @@ class RobotProxy extends RedstoneAware with traits.StateAware { override def isBlockSolid(world: IBlockAccess, pos: BlockPos, side: EnumFacing) = false + override def isSideSolid(world: IBlockAccess, pos: BlockPos, side: EnumFacing) = false + override def getPickBlock(target: MovingObjectPosition, world: World, pos: BlockPos) = world.getTileEntity(pos) match { case proxy: tileentity.RobotProxy => proxy.robot.info.copyItemStack() @@ -55,7 +57,7 @@ class RobotProxy extends RedstoneAware with traits.StateAware { // ----------------------------------------------------------------------- // override def rarity(stack: ItemStack) = { - val data = new ItemUtils.RobotData(stack) + val data = new RobotData(stack) Rarity.byTier(data.tier) } @@ -71,7 +73,7 @@ class RobotProxy extends RedstoneAware with traits.StateAware { override protected def tooltipTail(metadata: Int, stack: ItemStack, player: EntityPlayer, tooltip: util.List[String], advanced: Boolean) { super.tooltipTail(metadata, stack, player, tooltip, advanced) if (KeyBindings.showExtendedTooltips) { - val info = new ItemUtils.RobotData(stack) + val info = new RobotData(stack) val components = info.containers ++ info.components if (components.length > 0) { tooltip.addAll(Tooltip.get("Server.Components")) diff --git a/src/main/scala/li/cil/oc/common/block/Screen.scala b/src/main/scala/li/cil/oc/common/block/Screen.scala index 6ce1cfb36..b2b3656d1 100644 --- a/src/main/scala/li/cil/oc/common/block/Screen.scala +++ b/src/main/scala/li/cil/oc/common/block/Screen.scala @@ -36,6 +36,8 @@ object Screen { class Screen(val tier: Int) extends RedstoneAware with traits.OmniRotatable { setLightLevel(0.34f) + override def isSideSolid(world: IBlockAccess, pos: BlockPos, side: EnumFacing) = toLocal(world, pos, side) != EnumFacing.SOUTH + override protected def setDefaultExtendedState(state: IBlockState) = setDefaultState(state) override protected def addExtendedState(state: IBlockState, world: IBlockAccess, pos: BlockPos) = @@ -83,8 +85,9 @@ class Screen(val tier: Int) extends RedstoneAware with traits.OmniRotatable { override def localOnBlockActivated(world: World, pos: BlockPos, player: EntityPlayer, side: EnumFacing, hitX: Float, hitY: Float, hitZ: Float) = rightClick(world, pos, player, side, hitX, hitY, hitZ, force = false) - def rightClick(world: World, pos: BlockPos, player: EntityPlayer, side: EnumFacing, hitX: Float, hitY: Float, hitZ: Float, force: Boolean) = - if (Wrench.holdsApplicableWrench(player, pos)) false + def rightClick(world: World, pos: BlockPos, player: EntityPlayer, + side: EnumFacing, hitX: Float, hitY: Float, hitZ: Float, force: Boolean) = { + if (Wrench.holdsApplicableWrench(player, pos) && getValidRotations(world, pos).contains(side) && !force) false else world.getTileEntity(pos) match { case screen: tileentity.Screen if screen.hasKeyboard && (force || player.isSneaking == screen.invertTouchMode) => // Yep, this GUI is actually purely client side. We could skip this @@ -98,6 +101,7 @@ class Screen(val tier: Int) extends RedstoneAware with traits.OmniRotatable { screen.click(player, hitX, hitY, hitZ) case _ => false } + } override def onEntityCollidedWithBlock(world: World, pos: BlockPos, entity: Entity) = if (world.isRemote) (entity, world.getTileEntity(pos)) match { diff --git a/src/main/scala/li/cil/oc/common/block/ServerRack.scala b/src/main/scala/li/cil/oc/common/block/ServerRack.scala index 92faf2fbf..82a78ccff 100644 --- a/src/main/scala/li/cil/oc/common/block/ServerRack.scala +++ b/src/main/scala/li/cil/oc/common/block/ServerRack.scala @@ -1,11 +1,9 @@ package li.cil.oc.common.block -import li.cil.oc.OpenComputers import li.cil.oc.Settings import li.cil.oc.common.GuiType import li.cil.oc.common.tileentity import net.minecraft.block.state.IBlockState -import net.minecraft.entity.player.EntityPlayer import net.minecraft.util.BlockPos import net.minecraft.util.EnumFacing import net.minecraft.world.IBlockAccess @@ -13,7 +11,7 @@ import net.minecraft.world.World import net.minecraftforge.fml.relauncher.Side import net.minecraftforge.fml.relauncher.SideOnly -class ServerRack extends RedstoneAware with traits.PowerAcceptor with traits.Rotatable with traits.StateAware { +class ServerRack extends RedstoneAware with traits.PowerAcceptor with traits.Rotatable with traits.StateAware with traits.GUI { override protected def setDefaultExtendedState(state: IBlockState) = setDefaultState(state) @SideOnly(Side.CLIENT) @@ -31,23 +29,15 @@ class ServerRack extends RedstoneAware with traits.PowerAcceptor with traits.Rot override def isBlockSolid(world: IBlockAccess, pos: BlockPos, side: EnumFacing) = side == EnumFacing.SOUTH + override def isSideSolid(world: IBlockAccess, pos: BlockPos, side: EnumFacing) = toLocal(world, pos, side) != EnumFacing.SOUTH + // ----------------------------------------------------------------------- // override def energyThroughput = Settings.get.serverRackRate + override def guiType = GuiType.Rack + override def hasTileEntity(state: IBlockState) = true override def createNewTileEntity(world: World, metadata: Int) = new tileentity.ServerRack() - - // ----------------------------------------------------------------------- // - - override def localOnBlockActivated(world: World, pos: BlockPos, player: EntityPlayer, side: EnumFacing, hitX: Float, hitY: Float, hitZ: Float) = { - if (!player.isSneaking) { - if (!world.isRemote) { - player.openGui(OpenComputers, GuiType.Rack.id, world, pos.getX, pos.getY, pos.getZ) - } - true - } - else false - } } 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 d67aa71a2..12f690080 100644 --- a/src/main/scala/li/cil/oc/common/block/SimpleBlock.scala +++ b/src/main/scala/li/cil/oc/common/block/SimpleBlock.scala @@ -156,7 +156,7 @@ abstract class SimpleBlock(material: Material = Material.iron) extends BlockCont // Block // ----------------------------------------------------------------------- // - override def isNormalCube(world: IBlockAccess, pos: BlockPos) = true + override def isSideSolid(world: IBlockAccess, pos: BlockPos, side: EnumFacing) = true override def canHarvestBlock(world: IBlockAccess, pos: BlockPos, player: EntityPlayer) = true diff --git a/src/main/scala/li/cil/oc/common/block/Switch.scala b/src/main/scala/li/cil/oc/common/block/Switch.scala index e5e66b83c..ab8c82e1d 100644 --- a/src/main/scala/li/cil/oc/common/block/Switch.scala +++ b/src/main/scala/li/cil/oc/common/block/Switch.scala @@ -1,31 +1,14 @@ package li.cil.oc.common.block -import li.cil.oc.OpenComputers import li.cil.oc.common.GuiType import li.cil.oc.common.tileentity import net.minecraft.block.state.IBlockState -import net.minecraft.entity.player.EntityPlayer -import net.minecraft.util.BlockPos -import net.minecraft.util.EnumFacing import net.minecraft.world.World -class Switch extends SimpleBlock { +class Switch extends SimpleBlock with traits.GUI { + override def guiType = GuiType.Switch + override def hasTileEntity(state: IBlockState) = true override def createNewTileEntity(world: World, metadata: Int) = new tileentity.Switch() - - // ----------------------------------------------------------------------- // - - override def localOnBlockActivated(world: World, pos: BlockPos, player: EntityPlayer, side: EnumFacing, hitX: Float, hitY: Float, hitZ: Float) = { - world.getTileEntity(pos) match { - case switch: tileentity.Switch => - if (!player.isSneaking) { - if (!world.isRemote) { - player.openGui(OpenComputers, GuiType.Switch.id, world, pos.getX, pos.getY, pos.getZ) - } - true - } - else false - } - } } 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 new file mode 100644 index 000000000..cc15911ff --- /dev/null +++ b/src/main/scala/li/cil/oc/common/block/traits/CustomDrops.scala @@ -0,0 +1,50 @@ +package li.cil.oc.common.block.traits + +import java.util + +import li.cil.oc.common.block.SimpleBlock +import net.minecraft.block.state.IBlockState +import net.minecraft.entity.EntityLivingBase +import net.minecraft.entity.player.EntityPlayer +import net.minecraft.item.ItemStack +import net.minecraft.tileentity.TileEntity +import net.minecraft.util.BlockPos +import net.minecraft.world.IBlockAccess +import net.minecraft.world.World + +import scala.reflect.ClassTag + +trait CustomDrops[Tile <: TileEntity] extends SimpleBlock { + protected def tileTag: ClassTag[Tile] + + override def getDrops(world: IBlockAccess, pos: BlockPos, state: IBlockState, fortune: Int): util.List[ItemStack] = new java.util.ArrayList[ItemStack]() + + override def breakBlock(world: World, pos: BlockPos, state: IBlockState): Unit = {} + + + override def removedByPlayer(world: World, pos: BlockPos, player: EntityPlayer, willHarvest: Boolean): Boolean = { + if (!world.isRemote) { + val matcher = tileTag + world.getTileEntity(pos) match { + case matcher(tileEntity) => doCustomDrops(tileEntity, player, willHarvest) + case _ => + } + } + super.removedByPlayer(world, pos, player, willHarvest) + } + + override def onBlockPlacedBy(world: World, pos: BlockPos, state: IBlockState, placer: EntityLivingBase, stack: ItemStack): Unit = { + super.onBlockPlacedBy(world, pos, state, placer, stack) + if (!world.isRemote) { + val matcher = tileTag + world.getTileEntity(pos) match { + case matcher(tileEntity) => doCustomInit(tileEntity, placer, stack) + case _ => + } + } + } + + protected def doCustomInit(tileEntity: Tile, player: EntityLivingBase, stack: ItemStack): Unit = {} + + protected def doCustomDrops(tileEntity: Tile, player: EntityPlayer, willHarvest: Boolean): Unit = {} +} diff --git a/src/main/scala/li/cil/oc/common/block/traits/GUI.scala b/src/main/scala/li/cil/oc/common/block/traits/GUI.scala new file mode 100644 index 000000000..3b224f612 --- /dev/null +++ b/src/main/scala/li/cil/oc/common/block/traits/GUI.scala @@ -0,0 +1,24 @@ +package li.cil.oc.common.block.traits + +import li.cil.oc.OpenComputers +import li.cil.oc.common.GuiType +import li.cil.oc.common.block.SimpleBlock +import net.minecraft.block.state.IBlockState +import net.minecraft.entity.player.EntityPlayer +import net.minecraft.util.BlockPos +import net.minecraft.util.EnumFacing +import net.minecraft.world.World + +trait GUI extends SimpleBlock { + def guiType: GuiType.EnumVal + + override def localOnBlockActivated(world: World, pos: BlockPos, player: EntityPlayer, side: EnumFacing, hitX: Float, hitY: Float, hitZ: Float): Boolean = { + if (!player.isSneaking) { + if (!world.isRemote) { + player.openGui(OpenComputers, guiType.id, world, pos.getX, pos.getY, pos.getZ) + } + true + } + else super.localOnBlockActivated(world, pos, player, side, hitX, hitY, hitZ) + } +} diff --git a/src/main/scala/li/cil/oc/common/component/TextBuffer.scala b/src/main/scala/li/cil/oc/common/component/TextBuffer.scala index d4a80c7a1..d7a660e98 100644 --- a/src/main/scala/li/cil/oc/common/component/TextBuffer.scala +++ b/src/main/scala/li/cil/oc/common/component/TextBuffer.scala @@ -41,14 +41,16 @@ class TextBuffer(val host: EnvironmentHost) extends prefab.ManagedEnvironment wi withConnector(). create() - private var maxResolution = Settings.screenResolutionsByTier(0) + private var maxResolution = Settings.screenResolutionsByTier(Tier.One) - private var maxDepth = Settings.screenDepthsByTier(0) + private var maxDepth = Settings.screenDepthsByTier(Tier.One) private var aspectRatio = (1.0, 1.0) private var powerConsumptionPerTick = Settings.get.screenCost + private var precisionMode = false + // For client side only. private var isRendering = true @@ -162,6 +164,21 @@ class TextBuffer(val host: EnvironmentHost) extends prefab.ManagedEnvironment wi } } + @Callback(direct = true, doc = """function():boolean -- Returns whether the screen is in high precision mode (sub-pixel mouse event positions).""") + def isPrecise(computer: Context, args: Arguments): Array[AnyRef] = result(precisionMode) + + @Callback(doc = """function(enabled:boolean):boolean -- Set whether to use high precision mode (sub-pixel mouse event positions).""") + def setPrecise(computer: Context, args: Arguments): Array[AnyRef] = { + // Available for T3 screens only... easiest way to check for us is to + // base it off of the maximum color depth. + if (maxDepth == Settings.screenDepthsByTier(Tier.Three)) { + val oldValue = precisionMode + precisionMode = args.checkBoolean(0) + result(oldValue) + } + else result(Unit, "unsupported operation") + } + // ----------------------------------------------------------------------- // override def setEnergyCostPerTick(value: Double) { @@ -189,7 +206,7 @@ class TextBuffer(val host: EnvironmentHost) extends prefab.ManagedEnvironment wi if (height < 1) throw new IllegalArgumentException("height must be larger or equal to one") maxResolution = (width, height) fullyLitCost = computeFullyLitCost() - proxy.onScreenMaxResolutionChange(width, width) + proxy.onBufferMaxResolutionChange(width, width) } override def getMaximumWidth = maxResolution._1 @@ -205,7 +222,7 @@ class TextBuffer(val host: EnvironmentHost) extends prefab.ManagedEnvironment wi if (w < 1 || h < 1 || w > mw || h > mw || h * w > mw * mh) throw new IllegalArgumentException("unsupported resolution") // Always send to clients, their state might be dirty. - proxy.onScreenResolutionChange(w, h) + proxy.onBufferResolutionChange(w, h) if (data.size = (w, h)) { if (node != null) { node.sendToReachable("computer.signal", "screen_resized", Int.box(w), Int.box(h)) @@ -227,7 +244,7 @@ class TextBuffer(val host: EnvironmentHost) extends prefab.ManagedEnvironment wi if (depth.ordinal > maxDepth.ordinal) throw new IllegalArgumentException("unsupported depth") // Always send to clients, their state might be dirty. - proxy.onScreenDepthChange(depth) + proxy.onBufferDepthChange(depth) data.format = PackedColor.Depth.format(depth) } @@ -236,7 +253,7 @@ class TextBuffer(val host: EnvironmentHost) extends prefab.ManagedEnvironment wi override def setPaletteColor(index: Int, color: Int) = data.format match { case palette: PackedColor.MutablePaletteFormat => palette(index) = color - proxy.onScreenPaletteChange(index) + proxy.onBufferPaletteChange(index) case _ => throw new Exception("palette not available") } @@ -251,7 +268,7 @@ class TextBuffer(val host: EnvironmentHost) extends prefab.ManagedEnvironment wi val value = PackedColor.Color(color, isFromPalette) if (data.foreground != value) { data.foreground = value - proxy.onScreenColorChange() + proxy.onBufferColorChange() } } @@ -265,7 +282,7 @@ class TextBuffer(val host: EnvironmentHost) extends prefab.ManagedEnvironment wi val value = PackedColor.Color(color, isFromPalette) if (data.background != value) { data.background = value - proxy.onScreenColorChange() + proxy.onBufferColorChange() } } @@ -275,27 +292,28 @@ class TextBuffer(val host: EnvironmentHost) extends prefab.ManagedEnvironment wi def copy(col: Int, row: Int, w: Int, h: Int, tx: Int, ty: Int) = if (data.copy(col, row, w, h, tx, ty)) - proxy.onScreenCopy(col, row, w, h, tx, ty) + proxy.onBufferCopy(col, row, w, h, tx, ty) def fill(col: Int, row: Int, w: Int, h: Int, c: Char) = if (data.fill(col, row, w, h, c)) - proxy.onScreenFill(col, row, w, h, c) + proxy.onBufferFill(col, row, w, h, c) - def set(col: Int, row: Int, s: String, vertical: Boolean) = if (col < data.width && (col >= 0 || -col < s.length)) { - // Make sure the string isn't longer than it needs to be, in particular to - // avoid sending too much data to our clients. - val (x, y, truncated) = - if (vertical) { - if (row < 0) (col, 0, s.substring(-row)) - else (col, row, s.substring(0, math.min(s.length, data.height - row))) - } - else { - if (col < 0) (0, row, s.substring(-col)) - else (col, row, s.substring(0, math.min(s.length, data.width - col))) - } - if (data.set(x, y, truncated, vertical)) - proxy.onScreenSet(x, row, truncated, vertical) - } + def set(col: Int, row: Int, s: String, vertical: Boolean): Unit = + if (col < data.width && (col >= 0 || -col < s.length)) { + // Make sure the string isn't longer than it needs to be, in particular to + // avoid sending too much data to our clients. + val (x, y, truncated) = + if (vertical) { + if (row < 0) (col, 0, s.substring(-row)) + else (col, row, s.substring(0, math.min(s.length, data.height - row))) + } + else { + if (col < 0) (0, row, s.substring(-col)) + else (col, row, s.substring(0, math.min(s.length, data.width - col))) + } + if (data.set(x, y, truncated, vertical)) + proxy.onBufferSet(x, row, truncated, vertical) + } def get(col: Int, row: Int) = data.get(col, row) @@ -321,6 +339,40 @@ class TextBuffer(val host: EnvironmentHost) extends prefab.ManagedEnvironment wi override def isBackgroundFromPalette(column: Int, row: Int) = data.format.isFromPalette(PackedColor.extractBackground(color(column, row))) + override def rawSetText(col: Int, row: Int, text: Array[Array[Char]]): Unit = { + for (y <- row until ((row + text.length) min data.height)) { + val line = text(y - row) + Array.copy(line, 0, data.buffer(y), col, line.length min data.width) + } + proxy.onBufferRawSetText(col, row, text) + } + + override def rawSetBackground(col: Int, row: Int, color: Array[Array[Int]]): Unit = { + for (y <- row until ((row + color.length) min data.height)) { + val line = color(y - row) + for (x <- col until ((col + line.length) min data.width)) { + val packedBackground = data.color(row)(col) & 0x00FF + val packedForeground = (data.format.deflate(PackedColor.Color(line(x - col))) << PackedColor.ForegroundShift) & 0xFF00 + data.color(row)(col) = (packedForeground | packedBackground).toShort + } + } + // TODO Better for bandwidth to send packed shorts here. Would need a special case for handling on client, though... + proxy.onBufferRawSetBackground(col, row, color) + } + + override def rawSetForeground(col: Int, row: Int, color: Array[Array[Int]]): Unit = { + for (y <- row until ((row + color.length) min data.height)) { + val line = color(y - row) + for (x <- col until ((col + line.length) min data.width)) { + val packedBackground = data.format.deflate(PackedColor.Color(line(x - col))) & 0x00FF + val packedForeground = data.color(row)(col) & 0xFF00 + data.color(row)(col) = (packedForeground | packedBackground).toShort + } + } + // TODO Better for bandwidth to send packed shorts here. Would need a special case for handling on client, though... + proxy.onBufferRawSetForeground(col, row, color) + } + private def color(column: Int, row: Int) = { if (column < 0 || column >= getWidth || row < 0 || row >= getHeight) throw new IndexOutOfBoundsException() @@ -351,16 +403,16 @@ class TextBuffer(val host: EnvironmentHost) extends prefab.ManagedEnvironment wi override def clipboard(value: String, player: EntityPlayer) = proxy.clipboard(value, player) - override def mouseDown(x: Int, y: Int, button: Int, player: EntityPlayer) = + override def mouseDown(x: Double, y: Double, button: Int, player: EntityPlayer) = proxy.mouseDown(x, y, button, player) - override def mouseDrag(x: Int, y: Int, button: Int, player: EntityPlayer) = + override def mouseDrag(x: Double, y: Double, button: Int, player: EntityPlayer) = proxy.mouseDrag(x, y, button, player) - override def mouseUp(x: Int, y: Int, button: Int, player: EntityPlayer) = + override def mouseUp(x: Double, y: Double, button: Int, player: EntityPlayer) = proxy.mouseUp(x, y, button, player) - override def mouseScroll(x: Int, y: Int, delta: Int, player: EntityPlayer) = + override def mouseScroll(x: Double, y: Double, delta: Int, player: EntityPlayer) = proxy.mouseScroll(x, y, delta, player) // ----------------------------------------------------------------------- // @@ -403,6 +455,12 @@ class TextBuffer(val host: EnvironmentHost) extends prefab.ManagedEnvironment wi if (nbt.hasKey(Settings.namespace + "hasPower")) { hasPower = nbt.getBoolean(Settings.namespace + "hasPower") } + if (nbt.hasKey(Settings.namespace + "maxWidth") && nbt.hasKey(Settings.namespace + "maxHeight")) { + val maxWidth = nbt.getInteger(Settings.namespace + "maxWidth") + val maxHeight = nbt.getInteger(Settings.namespace + "maxHeight") + maxResolution = (maxWidth, maxHeight) + } + precisionMode = nbt.getBoolean(Settings.namespace + "precise") } // Null check for Waila (and other mods that may call this client side). @@ -428,6 +486,9 @@ class TextBuffer(val host: EnvironmentHost) extends prefab.ManagedEnvironment wi SaveHandler.scheduleSave(host, nbt, node.address + "_buffer", data.save _) nbt.setBoolean(Settings.namespace + "isOn", isDisplaying) nbt.setBoolean(Settings.namespace + "hasPower", hasPower) + nbt.setInteger(Settings.namespace + "maxWidth", maxResolution._1) + nbt.setInteger(Settings.namespace + "maxHeight", maxResolution._2) + nbt.setBoolean(Settings.namespace + "precise", precisionMode) } } @@ -477,28 +538,40 @@ object TextBuffer { def render() = false - def onScreenColorChange(): Unit + def onBufferColorChange(): Unit - def onScreenCopy(col: Int, row: Int, w: Int, h: Int, tx: Int, ty: Int) { + def onBufferCopy(col: Int, row: Int, w: Int, h: Int, tx: Int, ty: Int) { owner.relativeLitArea = -1 } - def onScreenDepthChange(depth: ColorDepth): Unit + def onBufferDepthChange(depth: ColorDepth): Unit - def onScreenFill(col: Int, row: Int, w: Int, h: Int, c: Char) { + def onBufferFill(col: Int, row: Int, w: Int, h: Int, c: Char) { owner.relativeLitArea = -1 } - def onScreenPaletteChange(index: Int): Unit + def onBufferPaletteChange(index: Int): Unit - def onScreenResolutionChange(w: Int, h: Int) { + def onBufferResolutionChange(w: Int, h: Int) { owner.relativeLitArea = -1 } - def onScreenMaxResolutionChange(w: Int, h: Int) { + def onBufferMaxResolutionChange(w: Int, h: Int) { } - def onScreenSet(col: Int, row: Int, s: String, vertical: Boolean) { + def onBufferSet(col: Int, row: Int, s: String, vertical: Boolean) { + owner.relativeLitArea = -1 + } + + def onBufferRawSetText(col: Int, row: Int, text: Array[Array[Char]]) { + owner.relativeLitArea = -1 + } + + def onBufferRawSetBackground(col: Int, row: Int, color: Array[Array[Int]]) { + owner.relativeLitArea = -1 + } + + def onBufferRawSetForeground(col: Int, row: Int, color: Array[Array[Int]]) { owner.relativeLitArea = -1 } @@ -508,13 +581,13 @@ object TextBuffer { def clipboard(value: String, player: EntityPlayer): Unit - def mouseDown(x: Int, y: Int, button: Int, player: EntityPlayer): Unit + def mouseDown(x: Double, y: Double, button: Int, player: EntityPlayer): Unit - def mouseDrag(x: Int, y: Int, button: Int, player: EntityPlayer): Unit + def mouseDrag(x: Double, y: Double, button: Int, player: EntityPlayer): Unit - def mouseUp(x: Int, y: Int, button: Int, player: EntityPlayer): Unit + def mouseUp(x: Double, y: Double, button: Int, player: EntityPlayer): Unit - def mouseScroll(x: Int, y: Int, delta: Int, player: EntityPlayer): Unit + def mouseScroll(x: Double, y: Double, delta: Int, player: EntityPlayer): Unit } class ClientProxy(val owner: TextBuffer) extends Proxy { @@ -532,35 +605,35 @@ object TextBuffer { wasDirty } - override def onScreenColorChange() { + override def onBufferColorChange() { markDirty() } - override def onScreenCopy(col: Int, row: Int, w: Int, h: Int, tx: Int, ty: Int) { - super.onScreenCopy(col, row, w, h, tx, ty) + override def onBufferCopy(col: Int, row: Int, w: Int, h: Int, tx: Int, ty: Int) { + super.onBufferCopy(col, row, w, h, tx, ty) markDirty() } - override def onScreenDepthChange(depth: ColorDepth) { + override def onBufferDepthChange(depth: ColorDepth) { markDirty() } - override def onScreenFill(col: Int, row: Int, w: Int, h: Int, c: Char) { - super.onScreenFill(col, row, w, h, c) + override def onBufferFill(col: Int, row: Int, w: Int, h: Int, c: Char) { + super.onBufferFill(col, row, w, h, c) markDirty() } - override def onScreenPaletteChange(index: Int) { + override def onBufferPaletteChange(index: Int) { markDirty() } - override def onScreenResolutionChange(w: Int, h: Int) { - super.onScreenResolutionChange(w, h) + override def onBufferResolutionChange(w: Int, h: Int) { + super.onBufferResolutionChange(w, h) markDirty() } - override def onScreenSet(col: Int, row: Int, s: String, vertical: Boolean) { - super.onScreenSet(col, row, s, vertical) + override def onBufferSet(col: Int, row: Int, s: String, vertical: Boolean) { + super.onBufferSet(col, row, s, vertical) dirty = true } @@ -579,22 +652,22 @@ object TextBuffer { ClientPacketSender.sendClipboard(nodeAddress, value) } - override def mouseDown(x: Int, y: Int, button: Int, player: EntityPlayer) { + override def mouseDown(x: Double, y: Double, button: Int, player: EntityPlayer) { debug(s"{type = mouseDown, x = $x, y = $y, button = $button}") ClientPacketSender.sendMouseClick(nodeAddress, x, y, drag = false, button) } - override def mouseDrag(x: Int, y: Int, button: Int, player: EntityPlayer) { + override def mouseDrag(x: Double, y: Double, button: Int, player: EntityPlayer) { debug(s"{type = mouseDrag, x = $x, y = $y, button = $button}") ClientPacketSender.sendMouseClick(nodeAddress, x, y, drag = true, button) } - override def mouseUp(x: Int, y: Int, button: Int, player: EntityPlayer) { + override def mouseUp(x: Double, y: Double, button: Int, player: EntityPlayer) { debug(s"{type = mouseUp, x = $x, y = $y, button = $button}") ClientPacketSender.sendMouseUp(nodeAddress, x, y, button) } - override def mouseScroll(x: Int, y: Int, delta: Int, player: EntityPlayer) { + override def mouseScroll(x: Double, y: Double, delta: Int, player: EntityPlayer) { debug(s"{type = mouseScroll, x = $x, y = $y, delta = $delta}") ClientPacketSender.sendMouseScroll(nodeAddress, x, y, delta) } @@ -609,53 +682,71 @@ object TextBuffer { } class ServerProxy(val owner: TextBuffer) extends Proxy { - override def onScreenColorChange() { + override def onBufferColorChange() { owner.host.markChanged() owner.synchronized(ServerPacketSender.appendTextBufferColorChange(owner.pendingCommands, owner.data.foreground, owner.data.background)) } - override def onScreenCopy(col: Int, row: Int, w: Int, h: Int, tx: Int, ty: Int) { - super.onScreenCopy(col, row, w, h, tx, ty) + override def onBufferCopy(col: Int, row: Int, w: Int, h: Int, tx: Int, ty: Int) { + super.onBufferCopy(col, row, w, h, tx, ty) owner.host.markChanged() owner.synchronized(ServerPacketSender.appendTextBufferCopy(owner.pendingCommands, col, row, w, h, tx, ty)) } - override def onScreenDepthChange(depth: ColorDepth) { + override def onBufferDepthChange(depth: ColorDepth) { owner.host.markChanged() owner.synchronized(ServerPacketSender.appendTextBufferDepthChange(owner.pendingCommands, depth)) } - override def onScreenFill(col: Int, row: Int, w: Int, h: Int, c: Char) { - super.onScreenFill(col, row, w, h, c) + override def onBufferFill(col: Int, row: Int, w: Int, h: Int, c: Char) { + super.onBufferFill(col, row, w, h, c) owner.host.markChanged() owner.synchronized(ServerPacketSender.appendTextBufferFill(owner.pendingCommands, col, row, w, h, c)) } - override def onScreenPaletteChange(index: Int) { + override def onBufferPaletteChange(index: Int) { owner.host.markChanged() owner.synchronized(ServerPacketSender.appendTextBufferPaletteChange(owner.pendingCommands, index, owner.getPaletteColor(index))) } - override def onScreenResolutionChange(w: Int, h: Int) { - super.onScreenResolutionChange(w, h) + override def onBufferResolutionChange(w: Int, h: Int) { + super.onBufferResolutionChange(w, h) owner.host.markChanged() owner.synchronized(ServerPacketSender.appendTextBufferResolutionChange(owner.pendingCommands, w, h)) } - override def onScreenMaxResolutionChange(w: Int, h: Int) { + override def onBufferMaxResolutionChange(w: Int, h: Int) { if (owner.node.network != null) { - super.onScreenMaxResolutionChange(w, h) + super.onBufferMaxResolutionChange(w, h) owner.host.markChanged() owner.synchronized(ServerPacketSender.appendTextBufferMaxResolutionChange(owner.pendingCommands, w, h)) } } - override def onScreenSet(col: Int, row: Int, s: String, vertical: Boolean) { - super.onScreenSet(col, row, s, vertical) + override def onBufferSet(col: Int, row: Int, s: String, vertical: Boolean) { + super.onBufferSet(col, row, s, vertical) owner.host.markChanged() owner.synchronized(ServerPacketSender.appendTextBufferSet(owner.pendingCommands, col, row, s, vertical)) } + override def onBufferRawSetText(col: Int, row: Int, text: Array[Array[Char]]) { + super.onBufferRawSetText(col, row, text) + owner.host.markChanged() + owner.synchronized(ServerPacketSender.appendTextBufferRawSetText(owner.pendingCommands, col, row, text)) + } + + override def onBufferRawSetBackground(col: Int, row: Int, color: Array[Array[Int]]) { + super.onBufferRawSetBackground(col, row, color) + owner.host.markChanged() + owner.synchronized(ServerPacketSender.appendTextBufferRawSetBackground(owner.pendingCommands, col, row, color)) + } + + override def onBufferRawSetForeground(col: Int, row: Int, color: Array[Array[Int]]) { + super.onBufferRawSetForeground(col, row, color) + owner.host.markChanged() + owner.synchronized(ServerPacketSender.appendTextBufferRawSetForeground(owner.pendingCommands, col, row, color)) + } + override def keyDown(character: Char, code: Int, player: EntityPlayer) { sendToKeyboards("keyboard.keyDown", player, Char.box(character), Int.box(code)) } @@ -668,24 +759,41 @@ object TextBuffer { sendToKeyboards("keyboard.clipboard", player, value) } - override def mouseDown(x: Int, y: Int, button: Int, player: EntityPlayer) { - if (Settings.get.inputUsername) owner.node.sendToReachable("computer.checked_signal", player, "touch", Int.box(x), Int.box(y), Int.box(button), player.getName) - else owner.node.sendToReachable("computer.checked_signal", player, "touch", Int.box(x), Int.box(y), Int.box(button)) + override def mouseDown(x: Double, y: Double, button: Int, player: EntityPlayer) { + sendMouseEvent(player, "touch", x, y, button) } - override def mouseDrag(x: Int, y: Int, button: Int, player: EntityPlayer) { - if (Settings.get.inputUsername) owner.node.sendToReachable("computer.checked_signal", player, "drag", Int.box(x), Int.box(y), Int.box(button), player.getName) - else owner.node.sendToReachable("computer.checked_signal", player, "drag", Int.box(x), Int.box(y), Int.box(button)) + override def mouseDrag(x: Double, y: Double, button: Int, player: EntityPlayer) { + sendMouseEvent(player, "drag", x, y, button) } - override def mouseUp(x: Int, y: Int, button: Int, player: EntityPlayer) { - if (Settings.get.inputUsername) owner.node.sendToReachable("computer.checked_signal", player, "drop", Int.box(x), Int.box(y), Int.box(button), player.getName) - else owner.node.sendToReachable("computer.checked_signal", player, "drop", Int.box(x), Int.box(y), Int.box(button)) + override def mouseUp(x: Double, y: Double, button: Int, player: EntityPlayer) { + sendMouseEvent(player, "drop", x, y, button) } - override def mouseScroll(x: Int, y: Int, delta: Int, player: EntityPlayer) { - if (Settings.get.inputUsername) owner.node.sendToReachable("computer.checked_signal", player, "scroll", Int.box(x), Int.box(y), Int.box(delta), player.getName) - else owner.node.sendToReachable("computer.checked_signal", player, "scroll", Int.box(x), Int.box(y), Int.box(delta)) + override def mouseScroll(x: Double, y: Double, delta: Int, player: EntityPlayer) { + sendMouseEvent(player, "scroll", x, y, delta) + } + + private def sendMouseEvent(player: EntityPlayer, name: String, x: Double, y: Double, data: Int) = { + val args = mutable.ArrayBuffer.empty[AnyRef] + + args += player + args += name + if (owner.precisionMode) { + args += double2Double(x) + args += double2Double(y) + } + else { + args += int2Integer(x.toInt + 1) + args += int2Integer(y.toInt + 1) + } + args += int2Integer(data) + if (Settings.get.inputUsername) { + args += player.getName + } + + owner.node.sendToReachable("computer.checked_signal", args: _*) } private def sendToKeyboards(name: String, values: AnyRef*) { diff --git a/src/main/scala/li/cil/oc/common/container/ComponentSlot.scala b/src/main/scala/li/cil/oc/common/container/ComponentSlot.scala index f7847bf3f..6ddce3ba2 100644 --- a/src/main/scala/li/cil/oc/common/container/ComponentSlot.scala +++ b/src/main/scala/li/cil/oc/common/container/ComponentSlot.scala @@ -39,6 +39,15 @@ abstract class ComponentSlot(inventory: IInventory, index: Int, x: Int, y: Int) } } + override def putStack(stack: ItemStack): Unit = { + super.putStack(stack) + inventory match { + case playerAware: common.tileentity.traits.PlayerInputAware => + playerAware.onSetInventorySlotContents(container.playerInventory.player, getSlotIndex, stack) + case _ => + } + } + override def onSlotChanged() { super.onSlotChanged() for (slot <- container.inventorySlots) slot match { diff --git a/src/main/scala/li/cil/oc/common/entity/Drone.scala b/src/main/scala/li/cil/oc/common/entity/Drone.scala index 5b803db74..80ee3ca7b 100644 --- a/src/main/scala/li/cil/oc/common/entity/Drone.scala +++ b/src/main/scala/li/cil/oc/common/entity/Drone.scala @@ -18,12 +18,12 @@ import li.cil.oc.common.Slot import li.cil.oc.common.inventory.ComponentInventory import li.cil.oc.common.inventory.Inventory import li.cil.oc.common.inventory.MultiTank +import li.cil.oc.common.item.data.MicrocontrollerData import li.cil.oc.server.component import li.cil.oc.util.BlockPosition import li.cil.oc.util.ExtendedNBT._ import li.cil.oc.util.ExtendedWorld._ import li.cil.oc.util.InventoryUtils -import li.cil.oc.util.ItemUtils import net.minecraft.block.material.Material import net.minecraft.entity.Entity import net.minecraft.entity.item.EntityItem @@ -61,7 +61,7 @@ class Drone(val world: World) extends Entity(world) with MachineHost with intern var lastEnergyUpdate = 0 // Logic stuff, components, machine and such. - val info = new ItemUtils.MicrocontrollerData() + val info = new MicrocontrollerData() val machine = if (!world.isRemote) { val m = Machine.create(this) m.node.asInstanceOf[Connector].setLocalBufferSize(0) @@ -296,6 +296,7 @@ class Drone(val world: World) extends Entity(world) with MachineHost with intern def setRunning(value: Boolean) = dataWatcher.updateObject(5, byte2Byte(if (value) 1: Byte else 0: Byte)) + // Round target values to low accuracy to avoid floating point errors accumulating. def targetX_=(value: Float): Unit = dataWatcher.updateObject(6, float2Float(math.round(value * 4) / 4f)) 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 dd658b25d..285f101f9 100644 --- a/src/main/scala/li/cil/oc/common/init/Items.scala +++ b/src/main/scala/li/cil/oc/common/init/Items.scala @@ -13,10 +13,12 @@ import li.cil.oc.common.block.SimpleBlock import li.cil.oc.common.item import li.cil.oc.common.item.Delegator import li.cil.oc.common.item.SimpleItem +import li.cil.oc.common.item.data.MicrocontrollerData +import li.cil.oc.common.item.data.RobotData +import li.cil.oc.common.item.data.TabletData import li.cil.oc.common.recipe.Recipes import li.cil.oc.integration.Mods import li.cil.oc.util.Color -import li.cil.oc.util.ItemUtils import net.minecraft.block.Block import net.minecraft.creativetab.CreativeTabs import net.minecraft.item.Item @@ -149,7 +151,7 @@ object Items extends ItemAPI { } def createConfiguredDrone() = { - val data = new ItemUtils.MicrocontrollerData() + val data = new MicrocontrollerData() data.tier = Tier.Four data.storedEnergy = Settings.get.bufferDrone.toInt @@ -175,7 +177,7 @@ object Items extends ItemAPI { } def createConfiguredMicrocontroller() = { - val data = new ItemUtils.MicrocontrollerData() + val data = new MicrocontrollerData() data.tier = Tier.Four data.storedEnergy = Settings.get.bufferMicrocontroller.toInt @@ -198,7 +200,7 @@ object Items extends ItemAPI { } def createConfiguredRobot() = { - val data = new ItemUtils.RobotData() + val data = new RobotData() data.name = "Creatix" data.tier = Tier.Four @@ -241,6 +243,37 @@ object Items extends ItemAPI { stack } + def createConfiguredTablet() = { + val data = new TabletData() + + data.energy = Settings.get.bufferTablet + data.maxEnergy = data.energy + data.items = Array( + Option(get("screen1").createItemStack(1)), + Option(get("keyboard").createItemStack(1)), + + Option(get("signUpgrade").createItemStack(1)), + Option(get("pistonUpgrade").createItemStack(1)), + + Option(get("graphicsCard2").createItemStack(1)), + Option(get("redstoneCard2").createItemStack(1)), + Option(get("wlanCard").createItemStack(1)), + + Option(get("cpu3").createItemStack(1)), + Option(get("ram6").createItemStack(1)), + Option(get("ram6").createItemStack(1)), + + Option(createLuaBios()), + Option(createOpenOS()), + Option(get("hdd3").createItemStack(1)) + ) + + val stack = get("tablet").createItemStack(1) + data.save(stack) + + stack + } + // ----------------------------------------------------------------------- // def init() { @@ -397,7 +430,8 @@ object Items extends ItemAPI { Items.createLuaBios(), Items.createConfiguredDrone(), Items.createConfiguredMicrocontroller(), - Items.createConfiguredRobot() + Items.createConfiguredRobot(), + Items.createConfiguredTablet() ) override def getSubItems(item: Item, tab: CreativeTabs, list: util.List[_]): Unit = { diff --git a/src/main/scala/li/cil/oc/common/item/DebugCard.scala b/src/main/scala/li/cil/oc/common/item/DebugCard.scala index d52b29603..5791287bc 100644 --- a/src/main/scala/li/cil/oc/common/item/DebugCard.scala +++ b/src/main/scala/li/cil/oc/common/item/DebugCard.scala @@ -1,3 +1,27 @@ package li.cil.oc.common.item -class DebugCard(val parent: Delegator) extends Delegate +import java.util + +import li.cil.oc.common.item.data.DebugCardData +import net.minecraft.entity.player.EntityPlayer +import net.minecraft.item.ItemStack +import net.minecraft.world.World + +class DebugCard(val parent: Delegator) extends Delegate { + override protected def tooltipExtended(stack: ItemStack, tooltip: util.List[String]): Unit = { + super.tooltipExtended(stack, tooltip) + val data = new DebugCardData(stack) + data.player.foreach(name => tooltip.add(s"§8$name§r")) + } + + override def onItemRightClick(stack: ItemStack, world: World, player: EntityPlayer): ItemStack = { + if (player.isSneaking) { + val data = new DebugCardData(stack) + if (data.player.contains(player.getName)) data.player = None + else data.player = Option(player.getName) + data.save(stack) + player.swingItem() + } + stack + } +} diff --git a/src/main/scala/li/cil/oc/common/item/Delegator.scala b/src/main/scala/li/cil/oc/common/item/Delegator.scala index c5b6c83e2..99154391d 100644 --- a/src/main/scala/li/cil/oc/common/item/Delegator.scala +++ b/src/main/scala/li/cil/oc/common/item/Delegator.scala @@ -25,10 +25,11 @@ import scala.collection.mutable object Delegator { def subItem(stack: ItemStack) = - stack.getItem match { + if (stack != null) stack.getItem match { case delegator: Delegator => delegator.subItem(stack.getItemDamage) case _ => None } + else None } class Delegator extends Item { diff --git a/src/main/scala/li/cil/oc/common/item/Drone.scala b/src/main/scala/li/cil/oc/common/item/Drone.scala index 19be55688..94e8c3990 100644 --- a/src/main/scala/li/cil/oc/common/item/Drone.scala +++ b/src/main/scala/li/cil/oc/common/item/Drone.scala @@ -4,9 +4,9 @@ import java.util import li.cil.oc.client.KeyBindings import li.cil.oc.common.entity +import li.cil.oc.common.item.data.MicrocontrollerData import li.cil.oc.integration.util.NEI import li.cil.oc.util.BlockPosition -import li.cil.oc.util.ItemUtils import li.cil.oc.util.Rarity import net.minecraft.entity.player.EntityPlayer import net.minecraft.item.ItemStack @@ -19,15 +19,15 @@ class Drone(val parent: Delegator) extends Delegate { override protected def tooltipExtended(stack: ItemStack, tooltip: util.List[String]): Unit = { if (KeyBindings.showExtendedTooltips) { - val info = new ItemUtils.MicrocontrollerData(stack) - for (component <- info.components) { + val info = new MicrocontrollerData(stack) + for (component <- info.components if component != null) { tooltip.add("- " + component.getDisplayName) } } } override def rarity(stack: ItemStack) = { - val data = new ItemUtils.MicrocontrollerData(stack) + val data = new MicrocontrollerData(stack) Rarity.byTier(data.tier) } diff --git a/src/main/scala/li/cil/oc/common/item/Tablet.scala b/src/main/scala/li/cil/oc/common/item/Tablet.scala index d635508f7..66d808f38 100644 --- a/src/main/scala/li/cil/oc/common/item/Tablet.scala +++ b/src/main/scala/li/cil/oc/common/item/Tablet.scala @@ -24,10 +24,9 @@ import li.cil.oc.client.KeyBindings import li.cil.oc.common.GuiType import li.cil.oc.common.Slot import li.cil.oc.common.inventory.ComponentInventory +import li.cil.oc.common.item.data.TabletData import li.cil.oc.server.component import li.cil.oc.util.ExtendedNBT._ -import li.cil.oc.util.ItemUtils -import li.cil.oc.util.ItemUtils.TabletData import li.cil.oc.util.RotationHelper import li.cil.oc.util.Tooltip import net.minecraft.entity.Entity @@ -54,7 +53,7 @@ class Tablet(val parent: Delegator) extends Delegate { override protected def tooltipExtended(stack: ItemStack, tooltip: util.List[String]): Unit = { if (KeyBindings.showExtendedTooltips) { - val info = new ItemUtils.TabletData(stack) + val info = new TabletData(stack) // Ignore/hide the screen. val components = info.items.drop(1) if (components.length > 1) { @@ -70,7 +69,7 @@ class Tablet(val parent: Delegator) extends Delegate { override def durability(stack: ItemStack) = { if (stack.hasTagCompound) { - val data = new ItemUtils.TabletData() + val data = new TabletData() data.load(stack.getTagCompound) data.energy / data.maxEnergy } diff --git a/src/main/scala/li/cil/oc/common/item/data/DebugCardData.scala b/src/main/scala/li/cil/oc/common/item/data/DebugCardData.scala new file mode 100644 index 000000000..f22ac7a6b --- /dev/null +++ b/src/main/scala/li/cil/oc/common/item/data/DebugCardData.scala @@ -0,0 +1,34 @@ +package li.cil.oc.common.item.data + +import li.cil.oc.Settings +import net.minecraft.item.ItemStack +import net.minecraft.nbt.NBTTagCompound + +class DebugCardData extends ItemData { + def this(stack: ItemStack) { + this() + load(stack) + } + + var player: Option[String] = None + + override def load(nbt: NBTTagCompound) { + val tag = dataTag(nbt) + if (tag.hasKey(Settings.namespace + "player")) { + player = Option(tag.getString(Settings.namespace + "player")) + } + } + + override def save(nbt: NBTTagCompound) { + val tag = dataTag(nbt) + tag.removeTag(Settings.namespace + "player") + player.foreach(tag.setString(Settings.namespace + "player", _)) + } + + private def dataTag(nbt: NBTTagCompound) = { + if (!nbt.hasKey(Settings.namespace + "data")) { + nbt.setTag(Settings.namespace + "data", new NBTTagCompound()) + } + nbt.getCompoundTag(Settings.namespace + "data") + } +} 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 new file mode 100644 index 000000000..1df64ade7 --- /dev/null +++ b/src/main/scala/li/cil/oc/common/item/data/ItemData.scala @@ -0,0 +1,26 @@ +package li.cil.oc.common.item.data + +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) { + // Because ItemStack's load function doesn't copy the compound tag, + // but keeps it as is, leading to oh so fun bugs! + load(stack.getTagCompound.copy().asInstanceOf[NBTTagCompound]) + } + } + + def save(stack: ItemStack) { + if (!stack.hasTagCompound) { + stack.setTagCompound(new NBTTagCompound()) + } + save(stack.getTagCompound) + } +} diff --git a/src/main/scala/li/cil/oc/common/item/data/MicrocontrollerData.scala b/src/main/scala/li/cil/oc/common/item/data/MicrocontrollerData.scala new file mode 100644 index 000000000..a8657920f --- /dev/null +++ b/src/main/scala/li/cil/oc/common/item/data/MicrocontrollerData.scala @@ -0,0 +1,49 @@ +package li.cil.oc.common.item.data + +import li.cil.oc.Settings +import li.cil.oc.api +import li.cil.oc.common.Tier +import li.cil.oc.util.ExtendedNBT._ +import li.cil.oc.util.ItemUtils +import net.minecraft.item.ItemStack +import net.minecraft.nbt.NBTTagCompound +import net.minecraftforge.common.util.Constants.NBT + +class MicrocontrollerData extends ItemData { + def this(stack: ItemStack) { + this() + load(stack) + } + + var tier = Tier.One + + var components = Array.empty[ItemStack] + + var storedEnergy = 0 + + override def load(nbt: NBTTagCompound) { + tier = nbt.getByte(Settings.namespace + "tier") + components = nbt.getTagList(Settings.namespace + "components", NBT.TAG_COMPOUND). + toArray[NBTTagCompound].map(ItemUtils.loadStack) + storedEnergy = nbt.getInteger(Settings.namespace + "storedEnergy") + } + + override def save(nbt: NBTTagCompound) { + nbt.setByte(Settings.namespace + "tier", tier.toByte) + nbt.setNewTagList(Settings.namespace + "components", components.toIterable) + nbt.setInteger(Settings.namespace + "storedEnergy", storedEnergy) + } + + def createItemStack() = { + val stack = api.Items.get("microcontroller").createItemStack(1) + save(stack) + stack + } + + def copyItemStack() = { + val stack = createItemStack() + val newInfo = new MicrocontrollerData(stack) + newInfo.save(stack) + stack + } +} diff --git a/src/main/scala/li/cil/oc/common/item/data/NavigationUpgradeData.scala b/src/main/scala/li/cil/oc/common/item/data/NavigationUpgradeData.scala new file mode 100644 index 000000000..0173c41de --- /dev/null +++ b/src/main/scala/li/cil/oc/common/item/data/NavigationUpgradeData.scala @@ -0,0 +1,47 @@ +package li.cil.oc.common.item.data + +import li.cil.oc.Settings +import li.cil.oc.util.ExtendedNBT._ +import li.cil.oc.util.ItemUtils +import net.minecraft.item.ItemMap +import net.minecraft.item.ItemStack +import net.minecraft.nbt.NBTTagCompound +import net.minecraft.world.World + +class NavigationUpgradeData extends ItemData { + def this(stack: ItemStack) { + this() + load(stack) + } + + var map = new ItemStack(net.minecraft.init.Items.filled_map) + + def mapData(world: World) = try map.getItem.asInstanceOf[ItemMap].getMapData(map, world) catch { + case _: Throwable => throw new Exception("invalid map") + } + + override def load(stack: ItemStack) { + if (stack.hasTagCompound) { + load(stack.getTagCompound.getCompoundTag(Settings.namespace + "data")) + } + } + + override def save(stack: ItemStack) { + if (!stack.hasTagCompound) { + stack.setTagCompound(new NBTTagCompound()) + } + save(stack.getCompoundTag(Settings.namespace + "data")) + } + + override def load(nbt: NBTTagCompound) { + if (nbt.hasKey(Settings.namespace + "map")) { + map = ItemUtils.loadStack(nbt.getCompoundTag(Settings.namespace + "map")) + } + } + + override def save(nbt: NBTTagCompound) { + if (map != null) { + nbt.setNewCompoundTag(Settings.namespace + "map", map.writeToNBT) + } + } +} diff --git a/src/main/scala/li/cil/oc/common/item/data/RaidData.scala b/src/main/scala/li/cil/oc/common/item/data/RaidData.scala new file mode 100644 index 000000000..40e05bcec --- /dev/null +++ b/src/main/scala/li/cil/oc/common/item/data/RaidData.scala @@ -0,0 +1,43 @@ +package li.cil.oc.common.item.data + +import li.cil.oc.Settings +import li.cil.oc.api +import li.cil.oc.util.ExtendedNBT._ +import li.cil.oc.util.ItemUtils +import net.minecraft.item.ItemStack +import net.minecraft.nbt.NBTTagCompound +import net.minecraftforge.common.util.Constants.NBT + +class RaidData extends ItemData { + def this(stack: ItemStack) { + this() + load(stack) + } + + var disks = Array.empty[ItemStack] + + var filesystem = new NBTTagCompound() + + var label: Option[String] = None + + override def load(nbt: NBTTagCompound): Unit = { + disks = nbt.getTagList(Settings.namespace + "disks", NBT.TAG_COMPOUND). + toArray[NBTTagCompound].map(ItemUtils.loadStack) + filesystem = nbt.getCompoundTag(Settings.namespace + "filesystem") + if (nbt.hasKey(Settings.namespace + "label")) { + label = Option(nbt.getString(Settings.namespace + "label")) + } + } + + override def save(nbt: NBTTagCompound): Unit = { + nbt.setNewTagList(Settings.namespace + "disks", disks.toIterable) + nbt.setTag(Settings.namespace + "filesystem", filesystem) + label.foreach(nbt.setString(Settings.namespace + "label", _)) + } + + def createItemStack() = { + val stack = api.Items.get("raid").createItemStack(1) + save(stack) + stack + } +} diff --git a/src/main/scala/li/cil/oc/common/item/data/RobotData.scala b/src/main/scala/li/cil/oc/common/item/data/RobotData.scala new file mode 100644 index 000000000..c7c36a851 --- /dev/null +++ b/src/main/scala/li/cil/oc/common/item/data/RobotData.scala @@ -0,0 +1,114 @@ +package li.cil.oc.common.item.data + +import com.google.common.base.Charsets +import com.google.common.base.Strings +import li.cil.oc.OpenComputers +import li.cil.oc.Settings +import li.cil.oc.api +import li.cil.oc.integration.opencomputers.DriverScreen +import li.cil.oc.util.ExtendedNBT._ +import li.cil.oc.util.ItemUtils +import net.minecraft.item.ItemStack +import net.minecraft.nbt.NBTTagCompound +import net.minecraftforge.common.util.Constants.NBT + +import scala.io.Source + +object RobotData { + val names = try { + Source.fromInputStream(getClass.getResourceAsStream( + "/assets/" + Settings.resourceDomain + "/robot.names"))(Charsets.UTF_8). + getLines().map(_.takeWhile(_ != '#').trim()).filter(_ != "").toArray + } + catch { + case t: Throwable => + OpenComputers.log.warn("Failed loading robot name list.", t) + Array.empty[String] + } + + def randomName = if (names.length > 0) names((math.random * names.length).toInt) else "Robot" +} + +class RobotData extends ItemData { + def this(stack: ItemStack) { + this() + load(stack) + } + + var name = "" + + // Overall energy including components. + var totalEnergy = 0 + + // Energy purely stored in robot component - this is what we have to restore manually. + var robotEnergy = 0 + + var tier = 0 + + var components = Array.empty[ItemStack] + + var containers = Array.empty[ItemStack] + + var lightColor = 0xF23030 + + override def load(nbt: NBTTagCompound) { + if (nbt.hasKey("display") && nbt.getCompoundTag("display").hasKey("Name")) { + name = nbt.getCompoundTag("display").getString("Name") + } + if (Strings.isNullOrEmpty(name)) { + name = RobotData.randomName + } + totalEnergy = nbt.getInteger(Settings.namespace + "storedEnergy") + robotEnergy = nbt.getInteger(Settings.namespace + "robotEnergy") + tier = nbt.getInteger(Settings.namespace + "tier") + components = nbt.getTagList(Settings.namespace + "components", NBT.TAG_COMPOUND). + toArray[NBTTagCompound].map(ItemUtils.loadStack) + containers = nbt.getTagList(Settings.namespace + "containers", NBT.TAG_COMPOUND). + toArray[NBTTagCompound].map(ItemUtils.loadStack) + if (nbt.hasKey(Settings.namespace + "lightColor")) { + lightColor = nbt.getInteger(Settings.namespace + "lightColor") + } + } + + override def save(nbt: NBTTagCompound) { + if (!Strings.isNullOrEmpty(name)) { + if (!nbt.hasKey("display")) { + nbt.setTag("display", new NBTTagCompound()) + } + nbt.getCompoundTag("display").setString("Name", name) + } + nbt.setInteger(Settings.namespace + "storedEnergy", totalEnergy) + nbt.setInteger(Settings.namespace + "robotEnergy", robotEnergy) + nbt.setInteger(Settings.namespace + "tier", tier) + nbt.setNewTagList(Settings.namespace + "components", components.toIterable) + nbt.setNewTagList(Settings.namespace + "containers", containers.toIterable) + nbt.setInteger(Settings.namespace + "lightColor", lightColor) + } + + def createItemStack() = { + val stack = api.Items.get("robot").createItemStack(1) + save(stack) + stack + } + + def copyItemStack() = { + val stack = createItemStack() + // Forget all node addresses and so on. This is used when 'picking' a + // robot in creative mode. + val newInfo = new RobotData(stack) + newInfo.components.foreach(cs => Option(api.Driver.driverFor(cs)) match { + case Some(driver) if driver == DriverScreen => + val nbt = driver.dataTag(cs) + for (tagName <- nbt.getKeySet.toArray) { + nbt.removeTag(tagName.asInstanceOf[String]) + } + case _ => + }) + // Don't show energy info (because it's unreliable) but fill up the + // internal buffer. This is for creative use only, anyway. + newInfo.totalEnergy = 0 + newInfo.robotEnergy = 50000 + newInfo.save(stack) + stack + } +} diff --git a/src/main/scala/li/cil/oc/common/item/data/TabletData.scala b/src/main/scala/li/cil/oc/common/item/data/TabletData.scala new file mode 100644 index 000000000..635030cd1 --- /dev/null +++ b/src/main/scala/li/cil/oc/common/item/data/TabletData.scala @@ -0,0 +1,47 @@ +package li.cil.oc.common.item.data + +import li.cil.oc.Settings +import li.cil.oc.util.ExtendedNBT._ +import li.cil.oc.util.ItemUtils +import net.minecraft.item.ItemStack +import net.minecraft.nbt.NBTTagCompound +import net.minecraftforge.common.util.Constants.NBT + +class TabletData extends ItemData { + def this(stack: ItemStack) { + this() + load(stack) + } + + var items = Array.fill[Option[ItemStack]](32)(None) + var isRunning = false + var energy = 0.0 + var maxEnergy = 0.0 + + override def load(nbt: NBTTagCompound) { + nbt.getTagList(Settings.namespace + "items", NBT.TAG_COMPOUND).foreach((slotNbt: NBTTagCompound) => { + val slot = slotNbt.getByte("slot") + if (slot >= 0 && slot < items.length) { + items(slot) = Option(ItemUtils.loadStack(slotNbt.getCompoundTag("item"))) + } + }) + isRunning = nbt.getBoolean(Settings.namespace + "isRunning") + energy = nbt.getDouble(Settings.namespace + "energy") + maxEnergy = nbt.getDouble(Settings.namespace + "maxEnergy") + } + + override def save(nbt: NBTTagCompound) { + nbt.setNewTagList(Settings.namespace + "items", + items.zipWithIndex collect { + case (Some(stack), slot) => (stack, slot) + } map { + case (stack, slot) => + val slotNbt = new NBTTagCompound() + slotNbt.setByte("slot", slot.toByte) + slotNbt.setNewCompoundTag("item", stack.writeToNBT) + }) + nbt.setBoolean(Settings.namespace + "isRunning", isRunning) + nbt.setDouble(Settings.namespace + "energy", energy) + nbt.setDouble(Settings.namespace + "maxEnergy", maxEnergy) + } +} diff --git a/src/main/scala/li/cil/oc/common/recipe/ExtendedRecipe.scala b/src/main/scala/li/cil/oc/common/recipe/ExtendedRecipe.scala index ca718e78b..f10e5abeb 100644 --- a/src/main/scala/li/cil/oc/common/recipe/ExtendedRecipe.scala +++ b/src/main/scala/li/cil/oc/common/recipe/ExtendedRecipe.scala @@ -5,9 +5,10 @@ import java.util.UUID import li.cil.oc.Settings import li.cil.oc.api import li.cil.oc.api.detail.ItemInfo +import li.cil.oc.common.init.Items +import li.cil.oc.common.item.data.MicrocontrollerData import li.cil.oc.integration.Mods import li.cil.oc.util.ExtendedNBT._ -import li.cil.oc.util.ItemUtils import li.cil.oc.util.SideTracker import net.minecraft.init.Blocks import net.minecraft.inventory.InventoryCrafting @@ -20,6 +21,7 @@ import scala.util.control.Breaks._ object ExtendedRecipe { private lazy val drone = api.Items.get("drone") private lazy val eeprom = api.Items.get("eeprom") + private lazy val luaBios = Items.createLuaBios() private lazy val mcu = api.Items.get("microcontroller") private lazy val navigationUpgrade = api.Items.get("navigationUpgrade") private lazy val linkedCard = api.Items.get("linkedCard") @@ -71,7 +73,7 @@ object ExtendedRecipe { } } - if (api.Items.get(craftedStack) == eeprom) breakable { + if (api.Items.get(craftedStack) == eeprom && !ItemStack.areItemStackTagsEqual(craftedStack, luaBios)) breakable { for (slot <- 0 until inventory.getSizeInventory) { val stack = inventory.getStackInSlot(slot) if (stack != null && api.Items.get(stack) == eeprom && stack.hasTagCompound) { @@ -95,7 +97,7 @@ object ExtendedRecipe { // Find old Microcontroller. (0 until inventory.getSizeInventory).map(inventory.getStackInSlot).find(api.Items.get(_) == descriptor) match { case Some(oldMcu) => - val data = new ItemUtils.MicrocontrollerData(oldMcu) + val data = new MicrocontrollerData(oldMcu) // Remove old EEPROM. val oldRom = data.components.filter(api.Items.get(_) == eeprom) diff --git a/src/main/scala/li/cil/oc/common/template/DroneTemplate.scala b/src/main/scala/li/cil/oc/common/template/DroneTemplate.scala index 6adde8ba7..552fd0f26 100644 --- a/src/main/scala/li/cil/oc/common/template/DroneTemplate.scala +++ b/src/main/scala/li/cil/oc/common/template/DroneTemplate.scala @@ -5,6 +5,7 @@ import li.cil.oc.api import li.cil.oc.api.internal import li.cil.oc.common.Slot import li.cil.oc.common.Tier +import li.cil.oc.common.item.data.MicrocontrollerData import li.cil.oc.util.ExtendedNBT._ import li.cil.oc.util.ItemUtils import net.minecraft.inventory.IInventory @@ -29,7 +30,7 @@ object DroneTemplate extends Template { def assemble(inventory: IInventory) = { val items = (0 until inventory.getSizeInventory).map(inventory.getStackInSlot) - val data = new ItemUtils.MicrocontrollerData() + val data = new MicrocontrollerData() data.tier = caseTier(inventory) data.components = items.drop(1).filter(_ != null).toArray data.storedEnergy = Settings.get.bufferDrone.toInt @@ -43,7 +44,7 @@ object DroneTemplate extends Template { def selectDisassembler(stack: ItemStack) = api.Items.get(stack) == api.Items.get("drone") def disassemble(stack: ItemStack, ingredients: Array[ItemStack]) = { - val info = new ItemUtils.MicrocontrollerData(stack) + val info = new MicrocontrollerData(stack) val itemName = ItemUtils.caseNameWithTierSuffix("droneCase", info.tier) Array(api.Items.get(itemName).createItemStack(1)) ++ info.components diff --git a/src/main/scala/li/cil/oc/common/template/MicrocontrollerTemplate.scala b/src/main/scala/li/cil/oc/common/template/MicrocontrollerTemplate.scala index ed737a022..3cbd70a28 100644 --- a/src/main/scala/li/cil/oc/common/template/MicrocontrollerTemplate.scala +++ b/src/main/scala/li/cil/oc/common/template/MicrocontrollerTemplate.scala @@ -5,6 +5,7 @@ import li.cil.oc.api import li.cil.oc.api.internal import li.cil.oc.common.Slot import li.cil.oc.common.Tier +import li.cil.oc.common.item.data.MicrocontrollerData import li.cil.oc.util.ExtendedNBT._ import li.cil.oc.util.ItemUtils import net.minecraft.inventory.IInventory @@ -29,7 +30,7 @@ object MicrocontrollerTemplate extends Template { def assemble(inventory: IInventory) = { val items = (0 until inventory.getSizeInventory).map(inventory.getStackInSlot) - val data = new ItemUtils.MicrocontrollerData() + val data = new MicrocontrollerData() data.tier = caseTier(inventory) data.components = items.drop(1).filter(_ != null).toArray data.storedEnergy = Settings.get.bufferMicrocontroller.toInt @@ -43,7 +44,7 @@ object MicrocontrollerTemplate extends Template { def selectDisassembler(stack: ItemStack) = api.Items.get(stack) == api.Items.get("microcontroller") def disassemble(stack: ItemStack, ingredients: Array[ItemStack]) = { - val info = new ItemUtils.MicrocontrollerData(stack) + val info = new MicrocontrollerData(stack) val itemName = ItemUtils.caseNameWithTierSuffix("microcontrollerCase", info.tier) Array(api.Items.get(itemName).createItemStack(1)) ++ info.components diff --git a/src/main/scala/li/cil/oc/common/template/NavigationUpgradeTemplate.scala b/src/main/scala/li/cil/oc/common/template/NavigationUpgradeTemplate.scala index 0360b6140..abbf512de 100644 --- a/src/main/scala/li/cil/oc/common/template/NavigationUpgradeTemplate.scala +++ b/src/main/scala/li/cil/oc/common/template/NavigationUpgradeTemplate.scala @@ -1,7 +1,7 @@ package li.cil.oc.common.template import li.cil.oc.api -import li.cil.oc.util.ItemUtils +import li.cil.oc.common.item.data.NavigationUpgradeData import net.minecraft.item.ItemStack import net.minecraft.nbt.NBTTagCompound import net.minecraftforge.fml.common.event.FMLInterModComms @@ -12,7 +12,7 @@ object NavigationUpgradeTemplate { def selectDisassembler(stack: ItemStack) = api.Items.get(stack) == api.Items.get("navigationUpgrade") def disassemble(stack: ItemStack, ingredients: Array[ItemStack]) = { - val info = new ItemUtils.NavigationUpgradeData(stack) + val info = new NavigationUpgradeData(stack) ingredients.map { case part if part.getItem == net.minecraft.init.Items.filled_map => info.map case part => part diff --git a/src/main/scala/li/cil/oc/common/template/RobotTemplate.scala b/src/main/scala/li/cil/oc/common/template/RobotTemplate.scala index 973d91ebb..6c626a57e 100644 --- a/src/main/scala/li/cil/oc/common/template/RobotTemplate.scala +++ b/src/main/scala/li/cil/oc/common/template/RobotTemplate.scala @@ -5,6 +5,7 @@ import li.cil.oc.api import li.cil.oc.api.internal import li.cil.oc.common.Slot import li.cil.oc.common.Tier +import li.cil.oc.common.item.data.RobotData import li.cil.oc.util.ExtendedNBT._ import li.cil.oc.util.ItemUtils import net.minecraft.inventory.IInventory @@ -16,21 +17,21 @@ import net.minecraftforge.fml.common.event.FMLInterModComms object RobotTemplate extends Template { override protected def hostClass = classOf[internal.Robot] - def selectTier1(stack: ItemStack) = ItemUtils.caseTier(stack) == Tier.One + def selectTier1(stack: ItemStack) = api.Items.get(stack) == api.Items.get("case1") - def selectTier2(stack: ItemStack) = ItemUtils.caseTier(stack) == Tier.Two + def selectTier2(stack: ItemStack) = api.Items.get(stack) == api.Items.get("case2") - def selectTier3(stack: ItemStack) = ItemUtils.caseTier(stack) == Tier.Three + def selectTier3(stack: ItemStack) = api.Items.get(stack) == api.Items.get("case3") - def selectCreative(stack: ItemStack) = ItemUtils.caseTier(stack) == Tier.Four + def selectCreative(stack: ItemStack) = api.Items.get(stack) == api.Items.get("caseCreative") def validate(inventory: IInventory): Array[AnyRef] = validateComputer(inventory) def assemble(inventory: IInventory) = { val items = (0 until inventory.getSizeInventory).map(inventory.getStackInSlot) - val data = new ItemUtils.RobotData() + val data = new RobotData() data.tier = ItemUtils.caseTier(inventory.getStackInSlot(0)) - data.name = ItemUtils.RobotData.randomName + data.name = RobotData.randomName data.robotEnergy = Settings.get.bufferRobot.toInt data.totalEnergy = data.robotEnergy data.containers = items.slice(1, 4).filter(_ != null).toArray @@ -45,7 +46,7 @@ object RobotTemplate extends Template { def selectDisassembler(stack: ItemStack) = api.Items.get(stack) == api.Items.get("robot") def disassemble(stack: ItemStack, ingredients: Array[ItemStack]) = { - val info = new ItemUtils.RobotData(stack) + val info = new RobotData(stack) val itemName = ItemUtils.caseNameWithTierSuffix("case", info.tier) Array(api.Items.get(itemName).createItemStack(1)) ++ info.containers ++ info.components diff --git a/src/main/scala/li/cil/oc/common/template/TabletTemplate.scala b/src/main/scala/li/cil/oc/common/template/TabletTemplate.scala index 4c9eac43f..5973bf4e4 100644 --- a/src/main/scala/li/cil/oc/common/template/TabletTemplate.scala +++ b/src/main/scala/li/cil/oc/common/template/TabletTemplate.scala @@ -5,8 +5,8 @@ import li.cil.oc.api import li.cil.oc.api.internal import li.cil.oc.common.Slot import li.cil.oc.common.Tier +import li.cil.oc.common.item.data.TabletData import li.cil.oc.util.ExtendedNBT._ -import li.cil.oc.util.ItemUtils import net.minecraft.inventory.IInventory import net.minecraft.item.ItemStack import net.minecraft.nbt.NBTTagCompound @@ -32,7 +32,7 @@ object TabletTemplate extends Template { val items = mutable.ArrayBuffer( Option(api.Items.get("screen1").createItemStack(1)) ) ++ (1 until inventory.getSizeInventory).map(slot => Option(inventory.getStackInSlot(slot))) - val data = new ItemUtils.TabletData() + val data = new TabletData() data.items = items.filter(_.isDefined).toArray data.energy = Settings.get.bufferTablet data.maxEnergy = data.energy @@ -46,7 +46,7 @@ object TabletTemplate extends Template { def selectDisassembler(stack: ItemStack) = api.Items.get(stack) == api.Items.get("tablet") def disassemble(stack: ItemStack, ingredients: Array[ItemStack]) = { - val info = new ItemUtils.TabletData(stack) + val info = new TabletData(stack) Array(api.Items.get("tabletCase").createItemStack(1)) ++ info.items.collect { case Some(item) => item }.drop(1) // Screen. diff --git a/src/main/scala/li/cil/oc/common/tileentity/Charger.scala b/src/main/scala/li/cil/oc/common/tileentity/Charger.scala index 3ea9137de..9a53c0699 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Charger.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Charger.scala @@ -10,10 +10,10 @@ import li.cil.oc.api.network._ import li.cil.oc.common.Slot import li.cil.oc.common.entity.Drone import li.cil.oc.common.item.Tablet +import li.cil.oc.common.item.data.TabletData import li.cil.oc.server.{PacketSender => ServerPacketSender} import li.cil.oc.util.BlockPosition import li.cil.oc.util.ExtendedWorld._ -import li.cil.oc.util.ItemUtils import net.minecraft.entity.player.EntityPlayer import net.minecraft.item.ItemStack import net.minecraft.nbt.NBTTagCompound @@ -101,7 +101,7 @@ class Charger extends traits.Environment with traits.PowerAcceptor with traits.R handler(itemCharge) } } - val data = new ItemUtils.TabletData(stack) + val data = new TabletData(stack) tryCharge(data.energy, data.maxEnergy, (amount) => { data.energy = math.min(data.maxEnergy, data.energy + amount) data.save(stack) diff --git a/src/main/scala/li/cil/oc/common/tileentity/Disassembler.scala b/src/main/scala/li/cil/oc/common/tileentity/Disassembler.scala index a91daeaa6..1de6cc188 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Disassembler.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Disassembler.scala @@ -11,6 +11,7 @@ import li.cil.oc.util.BlockPosition import li.cil.oc.util.ExtendedNBT._ import li.cil.oc.util.InventoryUtils import li.cil.oc.util.ItemUtils +import net.minecraft.entity.player.EntityPlayer import net.minecraft.item.ItemStack import net.minecraft.nbt.NBTTagCompound import net.minecraft.util.EnumFacing @@ -20,7 +21,7 @@ import net.minecraftforge.fml.relauncher.SideOnly import scala.collection.mutable -class Disassembler extends traits.Environment with traits.PowerAcceptor with traits.Inventory with traits.StateAware { +class Disassembler extends traits.Environment with traits.PowerAcceptor with traits.Inventory with traits.StateAware with traits.PlayerInputAware { val node = api.Network.newNode(this, Visibility.None). withConnector(Settings.get.bufferConverter). create() @@ -33,6 +34,8 @@ class Disassembler extends traits.Environment with traits.PowerAcceptor with tra var buffer = 0.0 + var disassembleNextInstantly = false + def progress = if (queue.isEmpty) 0 else (1 - (queue.size * Settings.get.disassemblerItemCost - buffer) / totalRequiredEnergy) * 100 private def setActive(value: Boolean) = if (value != isActive) { @@ -64,7 +67,8 @@ class Disassembler extends traits.Environment with traits.PowerAcceptor with tra super.updateEntity() if (world.getTotalWorldTime % Settings.get.tickFrequency == 0) { if (queue.isEmpty) { - disassemble(decrStackSize(0, 1)) + val instant = disassembleNextInstantly // Is reset via decrStackSize + disassemble(decrStackSize(0, 1), instant) setActive(queue.nonEmpty) } else { @@ -76,10 +80,10 @@ class Disassembler extends traits.Environment with traits.PowerAcceptor with tra buffer += want } } - if (buffer >= Settings.get.disassemblerItemCost) { + while (buffer >= Settings.get.disassemblerItemCost && queue.nonEmpty) { buffer -= Settings.get.disassemblerItemCost val stack = queue.remove(0) - if (world.rand.nextDouble > Settings.get.disassemblerBreakChance) { + if (world.rand.nextDouble >= Settings.get.disassemblerBreakChance) { drop(stack) } } @@ -87,7 +91,7 @@ class Disassembler extends traits.Environment with traits.PowerAcceptor with tra } } - def disassemble(stack: ItemStack) { + def disassemble(stack: ItemStack, instant: Boolean = false) { // Validate the item, never trust Minecraft / other Mods on anything! if (stack != null && isItemValidForSlot(0, stack)) { val ingredients = ItemUtils.getIngredients(stack) @@ -99,6 +103,10 @@ class Disassembler extends traits.Environment with traits.PowerAcceptor with tra case _ => queue ++= ingredients } totalRequiredEnergy = queue.size * Settings.get.disassemblerItemCost + if (instant) { + buffer = totalRequiredEnergy + disassembleNextInstantly = false // Just to be sure... + } } } @@ -152,4 +160,17 @@ class Disassembler extends traits.Environment with traits.PowerAcceptor with tra override def isItemValidForSlot(i: Int, stack: ItemStack) = ((Settings.get.disassembleAllTheThings || api.Items.get(stack) != null) && ItemUtils.getIngredients(stack).nonEmpty) || DisassemblerTemplates.select(stack) != None + + override def setInventorySlotContents(slot: Int, stack: ItemStack): Unit = { + super.setInventorySlotContents(slot, stack) + if (!world.isRemote) { + disassembleNextInstantly = false + } + } + + override def onSetInventorySlotContents(player: EntityPlayer, slot: Int, stack: ItemStack): Unit = { + if (!world.isRemote) { + disassembleNextInstantly = stack != null && slot == 0 && player.capabilities.isCreativeMode + } + } } diff --git a/src/main/scala/li/cil/oc/common/tileentity/Microcontroller.scala b/src/main/scala/li/cil/oc/common/tileentity/Microcontroller.scala index 9ffdfe455..83fbd2c6f 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Microcontroller.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Microcontroller.scala @@ -12,8 +12,8 @@ import li.cil.oc.api.machine.Context import li.cil.oc.api.network._ import li.cil.oc.common.Slot import li.cil.oc.common.Tier +import li.cil.oc.common.item.data.MicrocontrollerData 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.EnumFacing @@ -21,7 +21,7 @@ import net.minecraftforge.fml.relauncher.Side import net.minecraftforge.fml.relauncher.SideOnly class Microcontroller extends traits.PowerAcceptor with traits.Computer with SidedEnvironment with internal.Microcontroller { - val info = new ItemUtils.MicrocontrollerData() + val info = new MicrocontrollerData() override val node = api.Network.newNode(this, Visibility.Network). withComponent("microcontroller"). diff --git a/src/main/scala/li/cil/oc/common/tileentity/Raid.scala b/src/main/scala/li/cil/oc/common/tileentity/Raid.scala index 1072e3e77..8908d87cd 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Raid.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Raid.scala @@ -77,7 +77,7 @@ class Raid extends traits.Environment with traits.Inventory with traits.Rotatabl } } - private def tryCreateRaid(id: String) { + def tryCreateRaid(id: String) { if (items.count(_.isDefined) == items.length) { val fs = api.FileSystem.asManagedEnvironment( api.FileSystem.fromSaveDirectory(id, wipeDisksAndComputeSpace, Settings.get.bufferChanges), diff --git a/src/main/scala/li/cil/oc/common/tileentity/Redstone.scala b/src/main/scala/li/cil/oc/common/tileentity/Redstone.scala index c1c9aa7e9..a94b06c37 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Redstone.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Redstone.scala @@ -1,6 +1,7 @@ package li.cil.oc.common.tileentity import li.cil.oc.Settings +import li.cil.oc.api import li.cil.oc.api.network.Visibility import li.cil.oc.common.tileentity.traits.BundledRedstoneAware import li.cil.oc.common.tileentity.traits.Environment @@ -11,12 +12,18 @@ import net.minecraft.nbt.NBTTagCompound import net.minecraft.util.EnumFacing class Redstone extends Environment with BundledRedstoneAware { - val instance = if (BundledRedstone.isAvailable) new component.Redstone[BundledRedstoneAware](this) with component.RedstoneBundled else new component.Redstone(this) + val instance = + if (BundledRedstone.isAvailable) + new component.Redstone.Bundled(this) + else + new component.Redstone.Vanilla(this) val node = instance.node - if (node != null) { + val dummyNode = if (node != null) { node.setVisibility(Visibility.Network) _isOutputEnabled = true + api.Network.newNode(this, Visibility.None).create() } + else null override def canUpdate = isServer @@ -34,8 +41,11 @@ class Redstone extends Environment with BundledRedstoneAware { // ----------------------------------------------------------------------- // - override protected def onRedstoneInputChanged(side: EnumFacing) { - super.onRedstoneInputChanged(side) - node.sendToReachable("computer.signal", "redstone_changed", Int.box(side.ordinal())) + override protected def onRedstoneInputChanged(side: EnumFacing, oldMaxValue: Int, newMaxValue: Int) { + super.onRedstoneInputChanged(side, oldMaxValue, newMaxValue) + if (node != null && node.network != null) { + node.connect(dummyNode) + dummyNode.sendToNeighbors("redstone.changed", side, int2Integer(oldMaxValue), int2Integer(newMaxValue)) + } } } diff --git a/src/main/scala/li/cil/oc/common/tileentity/Robot.scala b/src/main/scala/li/cil/oc/common/tileentity/Robot.scala index 37b06c629..ef5807c20 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Robot.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Robot.scala @@ -18,6 +18,7 @@ import li.cil.oc.common.Tier import li.cil.oc.common.inventory.InventorySelection import li.cil.oc.common.inventory.MultiTank import li.cil.oc.common.inventory.TankSelection +import li.cil.oc.common.item.data.RobotData import li.cil.oc.integration.opencomputers.DriverKeyboard import li.cil.oc.integration.opencomputers.DriverRedstoneCard import li.cil.oc.integration.opencomputers.DriverScreen @@ -28,7 +29,6 @@ import li.cil.oc.util.BlockPosition import li.cil.oc.util.ExtendedNBT._ import li.cil.oc.util.ExtendedWorld._ import li.cil.oc.util.InventoryUtils -import li.cil.oc.util.ItemUtils import net.minecraft.block.Block import net.minecraft.block.BlockLiquid import net.minecraft.client.Minecraft @@ -37,7 +37,6 @@ import net.minecraft.init.Blocks import net.minecraft.inventory.IInventory import net.minecraft.item.ItemStack import net.minecraft.nbt.NBTTagCompound -import net.minecraft.tileentity.TileEntity import net.minecraft.util.BlockPos import net.minecraft.util.EnumFacing import net.minecraftforge.common.MinecraftForge @@ -56,7 +55,7 @@ import scala.collection.mutable class Robot extends traits.Computer with traits.PowerInformation with IFluidHandler with internal.Robot with MultiTank with InventorySelection with TankSelection { var proxy: RobotProxy = _ - val info = new ItemUtils.RobotData() + val info = new RobotData() val bot = if (isServer) new robot.Robot(this) else null @@ -244,7 +243,7 @@ class Robot extends traits.Computer with traits.PowerInformation with IFluidHand // worked before the client is notified so that we can use the same trick on // the client by sending a corresponding packet. This also saves us from // having to send the complete state again (e.g. screen buffer) each move. -// world.setBlockToAir(newPosition) + // world.setBlockToAir(newPosition) // In some cases (though I couldn't quite figure out which one) setBlock // will return true, even though the block was not created / adjusted. val created = world.setBlockState(newPosition, blockRobotProxy.getDefaultState, 1) @@ -751,8 +750,13 @@ class Robot extends traits.Computer with traits.PowerInformation with IFluidHand override def dropSlot(slot: Int, count: Int, direction: Option[EnumFacing]) = InventoryUtils.dropSlot(BlockPosition(x, y, z, world), dynamicInventory, slot, count, direction) - override def dropAllSlots() = + override def dropAllSlots() = { + InventoryUtils.dropSlot(BlockPosition(x, y, z, world), this, 0, Int.MaxValue) + for (slot <- containerSlots) { + InventoryUtils.dropSlot(BlockPosition(x, y, z, world), this, slot, Int.MaxValue) + } InventoryUtils.dropAllSlots(BlockPosition(x, y, z, world), dynamicInventory) + } // ----------------------------------------------------------------------- // diff --git a/src/main/scala/li/cil/oc/common/tileentity/Screen.scala b/src/main/scala/li/cil/oc/common/tileentity/Screen.scala index 0d1c85b80..1db36b52f 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Screen.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Screen.scala @@ -139,7 +139,7 @@ class Screen(var tier: Int) extends traits.TextBuffer with SidedEnvironment with } // Convert to absolute coordinates and send the packet to the server. - origin.buffer.mouseDown((brx * bw).toInt + 1, (bry * bh).toInt + 1, 0, null) + origin.buffer.mouseDown(brx * bw, bry * bh, 0, null) true } @@ -319,8 +319,8 @@ class Screen(var tier: Int) extends traits.TextBuffer with SidedEnvironment with override def onAnalyze(player: EntityPlayer, side: EnumFacing, hitX: Float, hitY: Float, hitZ: Float) = Array(origin.node) - override protected def onRedstoneInputChanged(side: EnumFacing) { - super.onRedstoneInputChanged(side) + override protected def onRedstoneInputChanged(side: EnumFacing, oldMaxValue: Int, newMaxValue: Int) { + super.onRedstoneInputChanged(side, oldMaxValue, newMaxValue) val hasRedstoneInput = screens.map(_.maxInput).max > 0 if (hasRedstoneInput != hadRedstoneInput) { hadRedstoneInput = hasRedstoneInput diff --git a/src/main/scala/li/cil/oc/common/tileentity/ServerRack.scala b/src/main/scala/li/cil/oc/common/tileentity/ServerRack.scala index c03f97335..6ece15ecb 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/ServerRack.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/ServerRack.scala @@ -207,7 +207,7 @@ class ServerRack extends traits.PowerAcceptor with traits.Hub with traits.PowerB val l = 2 / 16.0 val h = 14 / 16.0 val slot = (((1 - hitY) - l) / (h - l) * 4).toInt - Some(math.max(0, math.min(servers.length, slot))) + Some(math.max(0, math.min(servers.length - 1, slot))) } else None } @@ -409,10 +409,10 @@ class ServerRack extends traits.PowerAcceptor with traits.Hub with traits.PowerB checkRedstoneInputChanged() } - override protected def onRedstoneInputChanged(side: EnumFacing) { - super.onRedstoneInputChanged(side) + override protected def onRedstoneInputChanged(side: EnumFacing, oldMaxValue: Int, newMaxValue: Int) { + super.onRedstoneInputChanged(side, oldMaxValue, newMaxValue) servers collect { - case Some(server) => server.machine.signal("redstone_changed", server.machine.node.address, Int.box(toLocal(side).ordinal())) + case Some(server) => server.machine.node.sendToNeighbors("redstone.changed", toLocal(side), int2Integer(oldMaxValue), int2Integer(newMaxValue)) } } diff --git a/src/main/scala/li/cil/oc/common/tileentity/traits/BundledRedstoneAware.scala b/src/main/scala/li/cil/oc/common/tileentity/traits/BundledRedstoneAware.scala index 994359314..572334707 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/traits/BundledRedstoneAware.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/traits/BundledRedstoneAware.scala @@ -55,13 +55,15 @@ trait BundledRedstoneAware extends RedstoneAware /* with IBundledEmitter with IB def bundledInput(side: EnumFacing, color: Int) = math.max(_bundledInput(side.ordinal())(color), _rednetInput(side.ordinal())(color)) - def rednetInput(side: EnumFacing, color: Int, value: Int) = - if (_rednetInput(side.ordinal())(color) != value) { - if (_rednetInput(side.ordinal())(color) != -1) { - onRedstoneInputChanged(side) + def rednetInput(side: EnumFacing, color: Int, value: Int): Unit = { + val oldValue = _rednetInput(side.ordinal())(color) + if (oldValue != value) { + if (oldValue != -1) { + onRedstoneInputChanged(side, oldValue, value) } _rednetInput(side.ordinal())(color) = value } + } def bundledOutput(side: EnumFacing) = _bundledOutput(toLocal(side).ordinal()) @@ -87,19 +89,20 @@ trait BundledRedstoneAware extends RedstoneAware /* with IBundledEmitter with IB override protected def updateRedstoneInput(side: EnumFacing) { super.updateRedstoneInput(side) - val oldBundledInput = _bundledInput(side.ordinal()) + val ownBundledInput = _bundledInput(side.ordinal()) val newBundledInput = computeBundledInput(side) + val oldMaxValue = ownBundledInput.max var changed = false if (newBundledInput != null) for (color <- 0 until 16) { - changed = changed || (oldBundledInput(color) >= 0 && oldBundledInput(color) != newBundledInput(color)) - oldBundledInput(color) = newBundledInput(color) + changed = changed || (ownBundledInput(color) >= 0 && ownBundledInput(color) != newBundledInput(color)) + ownBundledInput(color) = newBundledInput(color) } else for (color <- 0 until 16) { - changed = changed || oldBundledInput(color) > 0 - oldBundledInput(color) = 0 + changed = changed || ownBundledInput(color) > 0 + ownBundledInput(color) = 0 } if (changed) { - onRedstoneInputChanged(side) + onRedstoneInputChanged(side, oldMaxValue, ownBundledInput.max) } } diff --git a/src/main/scala/li/cil/oc/common/tileentity/traits/Computer.scala b/src/main/scala/li/cil/oc/common/tileentity/traits/Computer.scala index 841a18971..7fe0892de 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/traits/Computer.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/traits/Computer.scala @@ -180,9 +180,9 @@ trait Computer extends Environment with ComponentInventory with Rotatable with B checkRedstoneInputChanged() } - override protected def onRedstoneInputChanged(side: EnumFacing) { - super.onRedstoneInputChanged(side) - machine.signal("redstone_changed", machine.node.address, Int.box(toLocal(side).ordinal())) + override protected def onRedstoneInputChanged(side: EnumFacing, oldMaxValue: Int, newMaxValue: Int) { + super.onRedstoneInputChanged(side, oldMaxValue, newMaxValue) + machine.node.sendToNeighbors("redstone.changed", toLocal(side), int2Integer(oldMaxValue), int2Integer(newMaxValue)) } // ----------------------------------------------------------------------- // diff --git a/src/main/scala/li/cil/oc/common/tileentity/traits/PlayerInputAware.scala b/src/main/scala/li/cil/oc/common/tileentity/traits/PlayerInputAware.scala new file mode 100644 index 000000000..7701b48b0 --- /dev/null +++ b/src/main/scala/li/cil/oc/common/tileentity/traits/PlayerInputAware.scala @@ -0,0 +1,14 @@ +package li.cil.oc.common.tileentity.traits + +import net.minecraft.entity.player.EntityPlayer +import net.minecraft.inventory.IInventory +import net.minecraft.item.ItemStack + +// Used to get notifications from containers when a player changes a slot in +// this inventory. Normally the player causing a setInventorySlotContents is +// unavailable. Using this we gain access to the causing player, allowing for +// some player-specific logic, such as the disassembler working instantaneously +// when used by a player in creative mode. +trait PlayerInputAware extends IInventory { + def onSetInventorySlotContents(player: EntityPlayer, slot: Int, stack: ItemStack): Unit +} 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 7ea456c60..8872f2f23 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 @@ -84,8 +84,8 @@ trait RedstoneAware extends RotationAware /* with IConnectable with IRedstoneEmi val oldInput = _input(side.ordinal()) val newInput = computeInput(side) _input(side.ordinal()) = newInput - if (oldInput >= 0 && input(side) != oldInput) { - onRedstoneInputChanged(side) + if (oldInput >= 0 && newInput != oldInput) { + onRedstoneInputChanged(side, oldInput, newInput) } } @@ -148,7 +148,7 @@ trait RedstoneAware extends RotationAware /* with IConnectable with IRedstoneEmi } } - protected def onRedstoneInputChanged(side: EnumFacing) {} + protected def onRedstoneInputChanged(side: EnumFacing, oldMaxValue: Int, newMaxValue: Int) {} protected def onRedstoneOutputEnabledChanged() { world.notifyNeighborsOfStateChange(getPos, getBlockType) diff --git a/src/main/scala/li/cil/oc/common/tileentity/traits/power/Mekanism.scala b/src/main/scala/li/cil/oc/common/tileentity/traits/power/Mekanism.scala index 396e0db5d..cda7a8daf 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/traits/power/Mekanism.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/traits/power/Mekanism.scala @@ -14,7 +14,7 @@ // @Optional.Method(modid = Mods.IDs.Mekanism) // def transferEnergyToAcceptor(side: EnumFacing, amount: Double) = // if (!Mods.Mekanism.isAvailable) 0 -// else amount - tryChangeBuffer(side, amount * Settings.get.ratioMekanism) / Settings.get.ratioMekanism +// else tryChangeBuffer(side, amount * Settings.get.ratioMekanism) / Settings.get.ratioMekanism // // @Optional.Method(modid = Mods.IDs.Mekanism) // def getMaxEnergy = EnumFacing.values.map(globalBufferSize).max / Settings.get.ratioMekanism diff --git a/src/main/scala/li/cil/oc/integration/Mods.scala b/src/main/scala/li/cil/oc/integration/Mods.scala index ffd160b6c..851946fa3 100644 --- a/src/main/scala/li/cil/oc/integration/Mods.scala +++ b/src/main/scala/li/cil/oc/integration/Mods.scala @@ -21,6 +21,7 @@ object Mods { val AppliedEnergistics2 = new SimpleMod(IDs.AppliedEnergistics2, version = "@[rv1,)", providesPower = true) val BattleGear2 = new SimpleMod(IDs.BattleGear2) + val BloodMagic = new SimpleMod(IDs.BloodMagic) val BuildCraft = new SimpleMod(IDs.BuildCraft) val BuildCraftTiles = new SimpleMod(IDs.BuildCraftTiles) val BuildCraftTools = new SimpleMod(IDs.BuildCraftTools) @@ -71,37 +72,38 @@ object Mods { // ----------------------------------------------------------------------- // val Proxies = Array( -// integration.appeng.ModAppEng, -// integration.buildcraft.tools.ModBuildCraftAPITools, -// integration.buildcraft.tiles.ModBuildCraftAPITiles, -// integration.buildcraft.transport.ModBuildCraftAPITransport, -// integration.cofh.energy.ModCoFHEnergy, -// integration.cofh.item.ModCoFHItem, -// integration.cofh.tileentity.ModCoFHTileEntity, -// integration.cofh.transport.ModCoFHTransport, -// integration.enderstorage.ModEnderStorage, -// integration.forestry.ModForestry, -// integration.fmp.ModForgeMultipart, -// integration.gc.ModGalacticraft, -// integration.gregtech.ModGregtech, -// integration.ic2.ModIndustrialCraft2, -// integration.mfr.ModMineFactoryReloaded, -// integration.mystcraft.ModMystcraft, -// integration.railcraft.ModRailcraft, -// integration.stargatetech2.ModStargateTech2, -// integration.thaumcraft.ModThaumcraft, -// integration.thermalexpansion.ModThermalExpansion, -// integration.tcon.ModTinkersConstruct, -// integration.tmechworks.ModTMechworks, + // integration.appeng.ModAppEng, + // integration.bloodmagic.ModBloodMagic, + // integration.buildcraft.tools.ModBuildCraftAPITools, + // integration.buildcraft.tiles.ModBuildCraftAPITiles, + // integration.buildcraft.transport.ModBuildCraftAPITransport, + // integration.cofh.energy.ModCoFHEnergy, + // integration.cofh.item.ModCoFHItem, + // integration.cofh.tileentity.ModCoFHTileEntity, + // integration.cofh.transport.ModCoFHTransport, + // integration.enderstorage.ModEnderStorage, + // integration.forestry.ModForestry, + // integration.fmp.ModForgeMultipart, + // integration.gc.ModGalacticraft, + // integration.gregtech.ModGregtech, + // integration.ic2.ModIndustrialCraft2, + // integration.mfr.ModMineFactoryReloaded, + // integration.mystcraft.ModMystcraft, + // integration.railcraft.ModRailcraft, + // integration.stargatetech2.ModStargateTech2, + // integration.thaumcraft.ModThaumcraft, + // integration.thermalexpansion.ModThermalExpansion, + // integration.tcon.ModTinkersConstruct, + // integration.tmechworks.ModTMechworks, integration.vanilla.ModVanilla, -// integration.versionchecker.ModVersionChecker, -// integration.waila.ModWaila, -// integration.wrcbe.ModWRCBE, -// integration.wrsve.ModWRSVE, + // integration.versionchecker.ModVersionChecker, + // integration.waila.ModWaila, + // integration.wrcbe.ModWRCBE, + // integration.wrsve.ModWRSVE, -// // Register the general IPeripheral driver last, if at all, to avoid it -// // being used rather than other more concrete implementations. -// integration.computercraft.ModComputerCraft, + // // Register the general IPeripheral driver last, if at all, to avoid it + // // being used rather than other more concrete implementations. + // integration.computercraft.ModComputerCraft, // We go last to ensure all other mod integration is done, e.g. to // allow properly checking if wireless redstone is present. @@ -111,7 +113,7 @@ object Mods { def init(): Unit = { for (proxy <- Proxies) { tryInit(proxy) - } + } } private def tryInit(mod: ModProxy) { @@ -131,6 +133,7 @@ object Mods { object IDs { final val AppliedEnergistics2 = "appliedenergistics2" final val BattleGear2 = "battlegear2" + final val BloodMagic = "AWWayofTime" final val BuildCraft = "BuildCraft|Core" final val BuildCraftPower = "BuildCraftAPI|power" final val BuildCraftTiles = "BuildCraftAPI|tiles" diff --git a/src/main/scala/li/cil/oc/integration/appeng/DriverController.scala b/src/main/scala/li/cil/oc/integration/appeng/DriverController.scala index abe2a6180..c7698aec7 100644 --- a/src/main/scala/li/cil/oc/integration/appeng/DriverController.scala +++ b/src/main/scala/li/cil/oc/integration/appeng/DriverController.scala @@ -1,11 +1,15 @@ package li.cil.oc.integration.appeng +import appeng.api.AEApi import appeng.api.config.Actionable import appeng.api.networking.crafting.ICraftingLink import appeng.api.networking.crafting.ICraftingRequester +import appeng.api.networking.security.IActionHost import appeng.api.networking.security.MachineSource import appeng.api.storage.data.IAEItemStack import appeng.core.Api +import appeng.me.helpers.IGridProxyable +import appeng.tile.misc.TileInterface import appeng.tile.networking.TileController import appeng.util.item.AEItemStack import com.google.common.collect.ImmutableSet @@ -25,6 +29,7 @@ import li.cil.oc.util.ResultWrapper._ import net.minecraft.block.Block import net.minecraft.item.ItemStack import net.minecraft.nbt.NBTTagCompound +import net.minecraft.tileentity.TileEntity import net.minecraft.world.World import net.minecraftforge.common.DimensionManager import net.minecraftforge.common.util.Constants.NBT @@ -37,19 +42,30 @@ import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.Future object DriverController extends DriverTileEntity with EnvironmentAware { - def getTileEntityClass = classOf[TileController] + private type AETile = TileEntity with IGridProxyable with IActionHost + + def getTileEntityClass = { + if (AEApi.instance != null && AEApi.instance.blocks != null) { + if (AEApi.instance.blocks.blockController != null && AEApi.instance.blocks.blockController.item != null) + classOf[TileController] + else + classOf[TileInterface] + } + else null + } def createEnvironment(world: World, x: Int, y: Int, z: Int): ManagedEnvironment = - new Environment(world.getTileEntity(x, y, z).asInstanceOf[TileController]) + new Environment(world.getTileEntity(x, y, z).asInstanceOf[AETile]) override def providedEnvironment(stack: ItemStack) = if (stack != null && - Api.instance != null && - Api.instance.blocks != null && - Api.instance.blocks.blockController != null && - Block.getBlockFromItem(stack.getItem) == Api.instance.blocks.blockController.block) classOf[Environment] else null + AEApi.instance != null && + AEApi.instance.blocks != null && + AEApi.instance.blocks.blockController != null && + Block.getBlockFromItem(stack.getItem) == AEApi.instance.blocks.blockController.block) classOf[Environment] + else null - class Environment(tileEntity: TileController) extends ManagedTileEntityEnvironment[TileController](tileEntity, "me_controller") with NamedBlock { + class Environment(tileEntity: AETile) extends ManagedTileEntityEnvironment[AETile](tileEntity, "me_controller") with NamedBlock { override def preferredName = "me_controller" override def priority = 0 @@ -63,9 +79,17 @@ object DriverController extends DriverTileEntity with EnvironmentAware { "busy" -> cpu.isBusy))) @Callback(doc = "function():table -- Get a list of known item recipes. These can be used to issue crafting requests.") - def getCraftables(context: Context, args: Arguments): Array[AnyRef] = + def getCraftables(context: Context, args: Arguments): Array[AnyRef] = { result(tileEntity.getProxy.getStorage.getItemInventory.getStorageList. - filter(_.isCraftable).map(new Craftable(tileEntity, _)).toArray) + filter(_.isCraftable).map(stack => { + val patterns = tileEntity.getProxy.getCrafting.getCraftingFor(stack, null, 0, tileEntity.getWorldObj) + val result = patterns.find(pattern => pattern.getOutputs.exists(_.isSameType(stack))) match { + case Some(pattern) => pattern.getOutputs.find(_.isSameType(stack)).get + case _ => stack.copy.setStackSize(0) // Should not be possible, but hey... + } + new Craftable(tileEntity, result) + }).toArray) + } @Callback(doc = "function():table -- Get a list of the stored items in the network.") def getItemsInNetwork(context: Context, args: Arguments): Array[AnyRef] = @@ -96,7 +120,7 @@ object DriverController extends DriverTileEntity with EnvironmentAware { result(tileEntity.getProxy.getEnergy.getStoredPower) } - class Craftable(var controller: TileController, var stack: IAEItemStack) extends AbstractValue with ICraftingRequester { + class Craftable(var controller: AETile, var stack: IAEItemStack) extends AbstractValue with ICraftingRequester { def this() = this(null, null) private val links = mutable.Set.empty[ICraftingLink] @@ -180,8 +204,8 @@ object DriverController extends DriverTileEntity with EnvironmentAware { EventHandler.schedule(() => { val world = DimensionManager.getWorld(dimension) val tileEntity = world.getTileEntity(x, y, z) - if (tileEntity != null && tileEntity.isInstanceOf[TileController]) { - controller = tileEntity.asInstanceOf[TileController] + if (tileEntity != null && tileEntity.isInstanceOf[AETile]) { + controller = tileEntity.asInstanceOf[AETile] } }) } diff --git a/src/main/scala/li/cil/oc/integration/bloodmagic/ConverterBloodOrb.java b/src/main/scala/li/cil/oc/integration/bloodmagic/ConverterBloodOrb.java new file mode 100644 index 000000000..7d33b3dce --- /dev/null +++ b/src/main/scala/li/cil/oc/integration/bloodmagic/ConverterBloodOrb.java @@ -0,0 +1,30 @@ +package li.cil.oc.integration.bloodmagic; + +import WayofTime.alchemicalWizardry.api.items.interfaces.IBloodOrb; +import WayofTime.alchemicalWizardry.api.soulNetwork.SoulNetworkHandler; +import li.cil.oc.api.driver.Converter; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; + +import java.util.Map; + +public class ConverterBloodOrb implements Converter { + @Override + public void convert(Object value, Map output) { + if (value instanceof ItemStack) { + final ItemStack stack = (ItemStack) value; + final Item item = stack.getItem(); + if (item instanceof IBloodOrb) { + final IBloodOrb bloodOrb = (IBloodOrb) item; + final String ownerName = stack.stackTagCompound.getString("ownerName"); + final int maxOrbTier = SoulNetworkHandler.getCurrentMaxOrb(ownerName); + output.put("ownerName", ownerName); + output.put("networkOrbTier", maxOrbTier); + output.put("networkEssence", SoulNetworkHandler.getCurrentEssence(ownerName)); + output.put("maxNetworkEssence", SoulNetworkHandler.getMaximumForOrbTier(maxOrbTier)); + output.put("maxEssence", bloodOrb.getMaxEssence()); + output.put("orbTier", bloodOrb.getOrbLevel()); + } + } + } +} diff --git a/src/main/scala/li/cil/oc/integration/bloodmagic/DriverBloodAltar.java b/src/main/scala/li/cil/oc/integration/bloodmagic/DriverBloodAltar.java new file mode 100644 index 000000000..b1f72c588 --- /dev/null +++ b/src/main/scala/li/cil/oc/integration/bloodmagic/DriverBloodAltar.java @@ -0,0 +1,79 @@ +package li.cil.oc.integration.bloodmagic; + +import li.cil.oc.api.driver.NamedBlock; +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.ManagedEnvironment; +import li.cil.oc.api.prefab.DriverTileEntity; +import li.cil.oc.integration.ManagedTileEntityEnvironment; +import net.minecraft.world.World; +import WayofTime.alchemicalWizardry.api.tile.IBloodAltar; + +public class DriverBloodAltar extends DriverTileEntity { + @Override + public Class getTileEntityClass() { + return IBloodAltar.class; + } + + @Override + public ManagedEnvironment createEnvironment(World world, int x, int y, int z) { + return new Environment((IBloodAltar) world.getTileEntity(x, y, z)); + } + + public static final class Environment extends ManagedTileEntityEnvironment implements NamedBlock { + public Environment(final IBloodAltar tileEntity) { + super(tileEntity, "blood_altar"); + } + + @Override + public String preferredName() { + return "blood_altar"; + } + + @Override + public int priority() { + return 0; + } + + @Callback(doc = "function():number -- Get the capacity.") + public Object[] getCapacity(final Context context, final Arguments arguments) { + return new Object[]{tileEntity.getCapacity()}; + } + + @Callback(doc = "function():number -- Get the amount of blood currently contained by this altar.") + public Object[] getCurrentBlood(final Context context, final Arguments arguments) { + return new Object[]{tileEntity.getCurrentBlood()}; + } + + @Callback(doc = "function():number -- Get the current tier.") + public Object[] getTier(final Context context, final Arguments arguments) { + return new Object[]{tileEntity.getTier()}; + } + + @Callback(doc = "function():number -- Get the progress.") + public Object[] getProgress(final Context context, final Arguments arguments) { + return new Object[]{tileEntity.getProgress()}; + } + + @Callback(doc = "function():number -- Get the sacrifice multiplier.") + public Object[] getSacrificeMultiplier(final Context context, final Arguments arguments) { + return new Object[]{tileEntity.getCapacity()}; + } + + @Callback(doc = "function():number -- Get the self sacrifice multiplier.") + public Object[] getSelfSacrificeMultiplier(final Context context, final Arguments arguments) { + return new Object[]{tileEntity.getSelfSacrificeMultiplier()}; + } + + @Callback(doc = "function():number -- Get the orb multiplier.") + public Object[] getOrbMultiplier(final Context context, final Arguments arguments) { + return new Object[]{tileEntity.getOrbMultiplier()}; + } + + @Callback(doc = "function():number -- Get the dislocation multiplier.") + public Object[] getDislocationMultiplier(final Context context, final Arguments arguments) { + return new Object[]{tileEntity.getDislocationMultiplier()}; + } + } +} diff --git a/src/main/scala/li/cil/oc/integration/bloodmagic/DriverMasterRitualStone.java b/src/main/scala/li/cil/oc/integration/bloodmagic/DriverMasterRitualStone.java new file mode 100644 index 000000000..3168651dc --- /dev/null +++ b/src/main/scala/li/cil/oc/integration/bloodmagic/DriverMasterRitualStone.java @@ -0,0 +1,69 @@ +package li.cil.oc.integration.bloodmagic; + +import WayofTime.alchemicalWizardry.api.rituals.IMasterRitualStone; +import WayofTime.alchemicalWizardry.common.tileEntity.TEMasterStone; +import li.cil.oc.api.driver.NamedBlock; +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.ManagedEnvironment; +import li.cil.oc.api.prefab.DriverTileEntity; +import li.cil.oc.integration.ManagedTileEntityEnvironment; +import net.minecraft.world.World; + +public class DriverMasterRitualStone extends DriverTileEntity { + @Override + public Class getTileEntityClass() { + return IMasterRitualStone.class; + } + + @Override + public ManagedEnvironment createEnvironment(World world, int x, int y, int z) { + return new Environment((IMasterRitualStone) world.getTileEntity(x, y, z)); + } + + public static class Environment extends ManagedTileEntityEnvironment implements NamedBlock { + public Environment(IMasterRitualStone tileEntity) { + super(tileEntity, "master_ritual_stone"); + } + + @Override + public String preferredName() { + return "master_ritual_stone"; + } + + @Override + public int priority() { + return 0; + } + + @Callback(doc = "function():string -- Get the name of the player owning this master ritual stone.") + public Object[] getOwner(final Context context, final Arguments arguments) { + return new Object[]{tileEntity.getOwner()}; + } + + @Callback(doc = "function():string -- Get the current ritual.") + public Object[] getCurrentRitual(final Context context, final Arguments arguments) { + if (tileEntity instanceof TEMasterStone) { + TEMasterStone masterStone = (TEMasterStone) tileEntity; + return new Object[]{masterStone.getCurrentRitual()}; + } + return new Object[]{"internal error"}; + } + + @Callback(doc = "function():number -- Get the remaining cooldown.") + public Object[] getCooldown(final Context context, final Arguments arguments) { + return new Object[]{tileEntity.getCooldown()}; + } + + @Callback(doc = "function():number -- Get the running time.") + public Object[] getRunningTime(final Context context, final Arguments arguments) { + return new Object[]{tileEntity.getRunningTime()}; + } + + @Callback(doc = "function():boolean -- Get whether the tanks are empty.") + public Object[] areTanksEmpty(final Context context, final Arguments arguments) { + return new Object[]{tileEntity.areTanksEmpty()}; + } + } +} diff --git a/src/main/scala/li/cil/oc/integration/bloodmagic/ModBloodMagic.scala b/src/main/scala/li/cil/oc/integration/bloodmagic/ModBloodMagic.scala new file mode 100644 index 000000000..a7979a672 --- /dev/null +++ b/src/main/scala/li/cil/oc/integration/bloodmagic/ModBloodMagic.scala @@ -0,0 +1,15 @@ +package li.cil.oc.integration.bloodmagic + +import li.cil.oc.api.Driver +import li.cil.oc.integration.{Mods, ModProxy} + +object ModBloodMagic extends ModProxy { + override def getMod = Mods.BloodMagic + + override def initialize() { + Driver.add(new DriverBloodAltar) + Driver.add(new DriverMasterRitualStone) + + Driver.add(new ConverterBloodOrb) + } +} diff --git a/src/main/scala/li/cil/oc/integration/computercraft/SwitchPeripheral.scala b/src/main/scala/li/cil/oc/integration/computercraft/SwitchPeripheral.scala index a5ed79c98..c8d07ccd5 100644 --- a/src/main/scala/li/cil/oc/integration/computercraft/SwitchPeripheral.scala +++ b/src/main/scala/li/cil/oc/integration/computercraft/SwitchPeripheral.scala @@ -5,13 +5,90 @@ import dan200.computercraft.api.peripheral.IComputerAccess import dan200.computercraft.api.peripheral.IPeripheral import li.cil.oc.Settings import li.cil.oc.api +import li.cil.oc.api.machine.Context +import li.cil.oc.api.network.Component import li.cil.oc.common.tileentity.AccessPoint import li.cil.oc.common.tileentity.Switch import li.cil.oc.util.ResultWrapper._ +import net.minecraftforge.common.util.ForgeDirection +import scala.collection.convert.WrapAsJava._ +import scala.collection.convert.WrapAsScala._ import scala.collection.mutable class SwitchPeripheral(val switch: Switch) extends IPeripheral { + private val methods = Map[String, (IComputerAccess, ILuaContext, Array[AnyRef]) => Array[AnyRef]]( + // Generic modem methods. + "open" -> ((computer, context, arguments) => { + val port = checkPort(arguments, 0) + if (switch.openPorts(computer).size >= 128) + throw new IllegalArgumentException("too many open channels") + result(switch.openPorts(computer).add(port)) + }), + "isOpen" -> ((computer, context, arguments) => { + val port = checkPort(arguments, 0) + result(switch.openPorts(computer).contains(port)) + }), + "close" -> ((computer, context, arguments) => { + val port = checkPort(arguments, 0) + result(switch.openPorts(computer).remove(port)) + }), + "closeAll" -> ((computer, context, arguments) => { + switch.openPorts(computer).clear() + null + }), + "transmit" -> ((computer, context, arguments) => { + val sendPort = checkPort(arguments, 0) + val answerPort = checkPort(arguments, 1) + val data = Seq(Int.box(answerPort)) ++ arguments.drop(2) + val packet = api.Network.newPacket(s"cc${computer.getID}_${computer.getAttachmentName}", null, sendPort, data.toArray) + result(switch.tryEnqueuePacket(None, packet)) + }), + "isWireless" -> ((computer, context, arguments) => { + result(switch.isInstanceOf[AccessPoint]) + }), + + // Undocumented modem messages. + "callRemote" -> ((computer, context, arguments) => { + val address = checkString(arguments, 0) + visibleComponents.find(_.address == address) match { + case Some(component) => + val method = checkString(arguments, 1) + val fakeContext = new CCContext(computer, context) + component.invoke(method, fakeContext, arguments.drop(2): _*) + case _ => null + } + }), + "getMethodsRemote" -> ((computer, context, arguments) => { + val address = checkString(arguments, 0) + visibleComponents.find(_.address == address) match { + case Some(component) => result(mapAsJavaMap(component.methods.zipWithIndex.map(t => (t._2 + 1, t._1)).toMap)) + case _ => null + } + }), + "getNamesRemote" -> ((computer, context, arguments) => { + result(mapAsJavaMap(visibleComponents.map(_.address).zipWithIndex.map(t => (t._2 + 1, t._1)).toMap)) + }), + "getTypeRemote" -> ((computer, context, arguments) => { + val address = checkString(arguments, 0) + visibleComponents.find(_.address == address) match { + case Some(component) => result(component.name) + case _ => null + } + }), + "isPresentRemote" -> ((computer, context, arguments) => { + val address = checkString(arguments, 0) + result(visibleComponents.exists(_.address == address)) + }), + + // OC specific. + "maxPacketSize" -> ((computer, context, arguments) => { + result(Settings.get.maxNetworkPacketSize) + }) + ) + + private val methodNames = methods.keys.toArray.sorted + override def getType = "modem" override def attach(computer: IComputerAccess) { @@ -24,34 +101,9 @@ class SwitchPeripheral(val switch: Switch) extends IPeripheral { switch.openPorts -= computer } - override def getMethodNames = Array("open", "isOpen", "close", "closeAll", "maxPacketSize", "transmit", "isWireless") + override def getMethodNames = methodNames - override def callMethod(computer: IComputerAccess, context: ILuaContext, method: Int, arguments: Array[AnyRef]) = getMethodNames()(method) match { - case "open" => - val port = checkPort(arguments, 0) - if (switch.openPorts(computer).size >= 128) - throw new IllegalArgumentException("too many open channels") - result(switch.openPorts(computer).add(port)) - case "isOpen" => - val port = checkPort(arguments, 0) - result(switch.openPorts(computer).contains(port)) - case "close" => - val port = checkPort(arguments, 0) - result(switch.openPorts(computer).remove(port)) - case "closeAll" => - switch.openPorts(computer).clear() - null - case "maxPacketSize" => - result(Settings.get.maxNetworkPacketSize) - case "transmit" => - val sendPort = checkPort(arguments, 0) - val answerPort = checkPort(arguments, 1) - val data = Seq(Int.box(answerPort)) ++ arguments.drop(2) - val packet = api.Network.newPacket(s"cc${computer.getID}_${computer.getAttachmentName}", null, sendPort, data.toArray) - result(switch.tryEnqueuePacket(None, packet)) - case "isWireless" => result(switch.isInstanceOf[AccessPoint]) - case _ => null - } + override def callMethod(computer: IComputerAccess, context: ILuaContext, method: Int, arguments: Array[AnyRef]) = methods(methodNames(method))(computer, context, arguments) override def equals(other: IPeripheral) = other match { case peripheral: SwitchPeripheral => peripheral.switch == switch @@ -66,4 +118,41 @@ class SwitchPeripheral(val switch: Switch) extends IPeripheral { throw new IllegalArgumentException(s"bad argument #${index + 1} (number in [1, 65535] expected)") port } + + private def checkString(args: Array[AnyRef], index: Int) = { + if (args.length < index - 1 || !args(index).isInstanceOf[String]) + throw new IllegalArgumentException(s"bad argument #${index + 1} (string expected)") + args(index).asInstanceOf[String] + } + + private def visibleComponents = { + ForgeDirection.VALID_DIRECTIONS.flatMap(side => { + val node = switch.sidedNode(side) + node.reachableNodes.collect { + case component: Component if component.canBeSeenFrom(node) => component + } + }) + } + + class CCContext(val computer: IComputerAccess, val context: ILuaContext) extends Context { + override def node() = switch.node + + override def isPaused = false + + override def stop() = false + + override def canInteract(player: String) = true + + override def signal(name: String, args: AnyRef*) = { + computer.queueEvent(name, args.toArray) + true + } + + override def pause(seconds: Double) = false + + override def isRunning = true + + override def start() = false + } + } diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/DriverBlockEnvironments.scala b/src/main/scala/li/cil/oc/integration/opencomputers/DriverBlockEnvironments.scala index b23366b07..07b982113 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/DriverBlockEnvironments.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverBlockEnvironments.scala @@ -33,7 +33,7 @@ object DriverBlockEnvironments extends driver.Block with EnvironmentAware { else if (isOneOf(block.getBlock, "case1", "case2", "case3", "caseCreative", "microcontroller")) classOf[Machine] else if (isOneOf(block.getBlock, "hologram1", "hologram2")) classOf[tileentity.Hologram] else if (isOneOf(block.getBlock, "motionSensor")) classOf[tileentity.MotionSensor] - else if (isOneOf(block.getBlock, "redstone")) if (BundledRedstone.isAvailable) classOf[component.Redstone.Bundled] else classOf[component.Redstone.Simple] + else if (isOneOf(block.getBlock, "redstone")) if (BundledRedstone.isAvailable) classOf[component.Redstone.Bundled] else classOf[component.Redstone.Vanilla] else if (isOneOf(block.getBlock, "screen1")) classOf[common.component.TextBuffer].asInstanceOf[Class[_ <: Environment]] else if (isOneOf(block.getBlock, "screen2", "screen3")) classOf[common.component.Screen] else if (isOneOf(block.getBlock, "robot")) classOf[component.robot.Robot].asInstanceOf[Class[_ <: Environment]] diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/DriverRedstoneCard.scala b/src/main/scala/li/cil/oc/integration/opencomputers/DriverRedstoneCard.scala index a46833cdd..94728636b 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/DriverRedstoneCard.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverRedstoneCard.scala @@ -19,16 +19,22 @@ import net.minecraft.item.ItemStack object DriverRedstoneCard extends Item with HostAware with EnvironmentAware { override def worksWith(stack: ItemStack) = isOneOf(stack, api.Items.get("redstoneCard1"), api.Items.get("redstoneCard2")) - override def createEnvironment(stack: ItemStack, host: EnvironmentHost) = + override def createEnvironment(stack: ItemStack, host: EnvironmentHost) = { + val isAdvanced = tier(stack) == Tier.Two + val hasBundled = BundledRedstone.isAvailable && isAdvanced + val hasWireless = WirelessRedstone.isAvailable && isAdvanced host match { - case redstone: BundledRedstoneAware if BundledRedstone.isAvailable && tier(stack) == Tier.Two => - if (WirelessRedstone.isAvailable) new component.Redstone.BundledWireless(redstone) + case redstone: BundledRedstoneAware if hasBundled => + if (hasWireless) new component.Redstone.BundledWireless(redstone) else new component.Redstone.Bundled(redstone) case redstone: RedstoneAware => - if (tier(stack) == Tier.Two && WirelessRedstone.isAvailable) new component.Redstone.Wireless(redstone) - else new component.Redstone.Simple(redstone) - case _ => null + if (hasWireless) new component.Redstone.VanillaWireless(redstone) + else new component.Redstone.Vanilla(redstone) + case _ => + if (hasWireless) new component.Redstone.Wireless(host) + else null } + } override def slot(stack: ItemStack) = Slot.Card @@ -37,15 +43,16 @@ object DriverRedstoneCard extends Item with HostAware with EnvironmentAware { case _ => Tier.One } - override def providedEnvironment(stack: ItemStack): Class[_ <: Environment] = - if (stack.getItemDamage == api.Items.get("redstoneCard1").createItemStack(1).getItemDamage) - classOf[component.Redstone[RedstoneAware]] - else if (BundledRedstone.isAvailable) { - if (WirelessRedstone.isAvailable) classOf[component.Redstone.BundledWireless] + override def providedEnvironment(stack: ItemStack): Class[_ <: Environment] = { + val isAdvanced = tier(stack) == Tier.Two + val hasBundled = BundledRedstone.isAvailable && isAdvanced + val hasWireless = WirelessRedstone.isAvailable && isAdvanced + if (hasBundled) { + if (hasWireless) classOf[component.Redstone.BundledWireless] else classOf[component.Redstone.Bundled] } else { - if (WirelessRedstone.isAvailable) classOf[component.Redstone.Wireless] - else classOf[component.Redstone.Simple] + classOf[component.Redstone.Vanilla] } + } } diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/DriverTablet.scala b/src/main/scala/li/cil/oc/integration/opencomputers/DriverTablet.scala index 9c28a2e73..e451197e3 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/DriverTablet.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverTablet.scala @@ -4,7 +4,7 @@ import li.cil.oc.Settings import li.cil.oc.api import li.cil.oc.api.driver.EnvironmentHost import li.cil.oc.common.Slot -import li.cil.oc.util.ItemUtils +import li.cil.oc.common.item.data.TabletData import net.minecraft.item.ItemStack import net.minecraft.nbt.NBTTagCompound import net.minecraftforge.common.util.Constants.NBT @@ -14,7 +14,7 @@ object DriverTablet extends Item { isOneOf(stack, api.Items.get("tablet")) override def createEnvironment(stack: ItemStack, host: EnvironmentHost) = { - val data = new ItemUtils.TabletData(stack) + val data = new TabletData(stack) data.items.collect { case Some(fs) if DriverFileSystem.worksWith(fs) => fs }.headOption.map(DriverFileSystem.createEnvironment(_, host)).orNull @@ -23,7 +23,7 @@ object DriverTablet extends Item { override def slot(stack: ItemStack) = Slot.Tablet override def dataTag(stack: ItemStack) = { - val data = new ItemUtils.TabletData(stack) + val data = new TabletData(stack) val index = data.items.indexWhere { case Some(fs) => DriverFileSystem.worksWith(fs) case _ => false diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/ModOpenComputers.scala b/src/main/scala/li/cil/oc/integration/opencomputers/ModOpenComputers.scala index aeec689da..e151c8ce1 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/ModOpenComputers.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/ModOpenComputers.scala @@ -127,7 +127,6 @@ object ModOpenComputers extends ModProxy { "keyboard", "lanCard", "redstoneCard1", - "redstoneCard2", // TODO Move back down to wireless section once wireless redstone can work on its own. "screen1", "angelUpgrade", "craftingUpgrade", @@ -174,6 +173,7 @@ object ModOpenComputers extends ModProxy { "leashUpgrade") if (!WirelessRedstone.isAvailable) { + blacklistHost(classOf[internal.Drone], "redstoneCard2") blacklistHost(classOf[internal.Tablet], "redstoneCard2") } diff --git a/src/main/scala/li/cil/oc/integration/wrsve/WirelessRedstoneSVE.scala b/src/main/scala/li/cil/oc/integration/wrsve/WirelessRedstoneSVE.scala index bce44a695..c56db9c27 100644 --- a/src/main/scala/li/cil/oc/integration/wrsve/WirelessRedstoneSVE.scala +++ b/src/main/scala/li/cil/oc/integration/wrsve/WirelessRedstoneSVE.scala @@ -2,6 +2,7 @@ package li.cil.oc.integration.wrsve import li.cil.oc.integration.util.WirelessRedstone.WirelessRedstoneSystem import li.cil.oc.server.component.RedstoneWireless +import li.cil.oc.util.BlockPosition import net.minecraft.world.World import scala.language.reflectiveCalls @@ -9,15 +10,15 @@ import scala.language.reflectiveCalls object WirelessRedstoneSVE extends WirelessRedstoneSystem { private val ether = try { Option(Class.forName("net.slimevoid.wirelessredstone.ether.RedstoneEther").getMethod("getInstance").invoke(null).asInstanceOf[ { - def addTransmitter(world: World, x: Int, y: Int, z: Int, frequency: AnyRef) + def addTransmitter(world: World, x: Int, y: Int, z: Int, frequency: AnyRef): Unit - def remTransmitter(world: World, x: Int, y: Int, z: Int, frequency: AnyRef) + def remTransmitter(world: World, x: Int, y: Int, z: Int, frequency: AnyRef): Unit - def addReceiver(world: World, x: Int, y: Int, z: Int, frequency: AnyRef) + def addReceiver(world: World, x: Int, y: Int, z: Int, frequency: AnyRef): Unit - def remReceiver(world: World, x: Int, y: Int, z: Int, frequency: AnyRef) + def remReceiver(world: World, x: Int, y: Int, z: Int, frequency: AnyRef): Unit - def setTransmitterState(world: World, x: Int, y: Int, z: Int, frequency: AnyRef, state: Boolean) + def setTransmitterState(world: World, x: Int, y: Int, z: Int, frequency: AnyRef, state: Boolean): Unit def getFreqState(world: World, frequency: AnyRef): Boolean }]) @@ -27,25 +28,25 @@ object WirelessRedstoneSVE extends WirelessRedstoneSystem { } def removeTransmitter(rs: RedstoneWireless) { - val te = rs.owner - ether.foreach(_.remTransmitter(te.world, te.x, te.y, te.z, rs.wirelessFrequency.toString)) + val blockPos = BlockPosition(rs.redstone) + ether.foreach(_.remTransmitter(rs.redstone.world, blockPos.x, blockPos.y, blockPos.z, rs.wirelessFrequency.toString)) } def addReceiver(rs: RedstoneWireless) { - val te = rs.owner - ether.foreach(_.addReceiver(te.world, te.x, te.y, te.z, rs.wirelessFrequency.toString)) + val blockPos = BlockPosition(rs.redstone) + ether.foreach(_.addReceiver(rs.redstone.world, blockPos.x, blockPos.y, blockPos.z, rs.wirelessFrequency.toString)) } def removeReceiver(rs: RedstoneWireless) { - val te = rs.owner - ether.foreach(_.remReceiver(te.world, te.x, te.y, te.z, rs.wirelessFrequency.toString)) + val blockPos = BlockPosition(rs.redstone) + ether.foreach(_.remReceiver(rs.redstone.world, blockPos.x, blockPos.y, blockPos.z, rs.wirelessFrequency.toString)) } def updateOutput(rs: RedstoneWireless) { - val te = rs.owner - ether.foreach(_.addTransmitter(te.world, te.x, te.y, te.z, rs.wirelessFrequency.toString)) - ether.foreach(_.setTransmitterState(te.world, te.x, te.y, te.z, rs.wirelessFrequency.toString, rs.wirelessOutput)) + val blockPos = BlockPosition(rs.redstone) + ether.foreach(_.addTransmitter(rs.redstone.world, blockPos.x, blockPos.y, blockPos.z, rs.wirelessFrequency.toString)) + ether.foreach(_.setTransmitterState(rs.redstone.world, blockPos.x, blockPos.y, blockPos.z, rs.wirelessFrequency.toString, rs.wirelessOutput)) } - def getInput(rs: RedstoneWireless) = ether.fold(false)(_.getFreqState(rs.owner.world, rs.wirelessFrequency.toString)) + def getInput(rs: RedstoneWireless) = ether.fold(false)(_.getFreqState(rs.redstone.world, rs.wirelessFrequency.toString)) } diff --git a/src/main/scala/li/cil/oc/server/PacketHandler.scala b/src/main/scala/li/cil/oc/server/PacketHandler.scala index 370879ab4..df8db106a 100644 --- a/src/main/scala/li/cil/oc/server/PacketHandler.scala +++ b/src/main/scala/li/cil/oc/server/PacketHandler.scala @@ -124,26 +124,37 @@ object PacketHandler extends CommonPacketHandler { def onMouseClick(p: PacketParser) { ComponentTracker.get(p.player.worldObj, p.readUTF()) match { case Some(buffer: api.component.TextBuffer) => - val x = p.readShort() - val y = p.readShort() + val x = p.readFloat() + val y = p.readFloat() val dragging = p.readBoolean() val button = p.readByte() - if (dragging) buffer.mouseDrag(x, y, button, p.player.asInstanceOf[EntityPlayer]) - else buffer.mouseDown(x, y, button, p.player.asInstanceOf[EntityPlayer]) + val player = p.player.asInstanceOf[EntityPlayer] + if (dragging) buffer.mouseDrag(x, y, button, player) + else buffer.mouseDown(x, y, button, player) case _ => // Invalid Packet } } def onMouseUp(p: PacketParser) { ComponentTracker.get(p.player.worldObj, p.readUTF()) match { - case Some(buffer: api.component.TextBuffer) => buffer.mouseUp(p.readShort(), p.readShort(), p.readByte(), p.player.asInstanceOf[EntityPlayer]) + case Some(buffer: api.component.TextBuffer) => + val x = p.readFloat() + val y = p.readFloat() + val button = p.readByte() + val player = p.player.asInstanceOf[EntityPlayer] + buffer.mouseUp(x, y, button, player) case _ => // Invalid Packet } } def onMouseScroll(p: PacketParser) { ComponentTracker.get(p.player.worldObj, p.readUTF()) match { - case Some(buffer: api.component.TextBuffer) => buffer.mouseScroll(p.readShort(), p.readShort(), p.readByte(), p.player.asInstanceOf[EntityPlayer]) + case Some(buffer: api.component.TextBuffer) => + val x = p.readFloat() + val y = p.readFloat() + val button = p.readByte() + val player = p.player.asInstanceOf[EntityPlayer] + buffer.mouseScroll(x, y, button, player) case _ => // Invalid Packet } } diff --git a/src/main/scala/li/cil/oc/server/PacketSender.scala b/src/main/scala/li/cil/oc/server/PacketSender.scala index 68f019a71..7606d48c7 100644 --- a/src/main/scala/li/cil/oc/server/PacketSender.scala +++ b/src/main/scala/li/cil/oc/server/PacketSender.scala @@ -394,6 +394,51 @@ object PacketSender { pb.writeBoolean(vertical) } + def appendTextBufferRawSetText(pb: PacketBuilder, col: Int, row: Int, text: Array[Array[Char]]) { + pb.writePacketType(PacketType.TextBufferMultiRawSetText) + + pb.writeInt(col) + pb.writeInt(row) + pb.writeShort(text.length.toShort) + for (y <- 0 until text.length.toShort) { + val line = text(y) + pb.writeShort(line.length.toShort) + for (x <- 0 until line.length.toShort) { + pb.writeChar(line(x)) + } + } + } + + def appendTextBufferRawSetBackground(pb: PacketBuilder, col: Int, row: Int, color: Array[Array[Int]]) { + pb.writePacketType(PacketType.TextBufferMultiRawSetBackground) + + pb.writeInt(col) + pb.writeInt(row) + pb.writeShort(color.length.toShort) + for (y <- 0 until color.length.toShort) { + val line = color(y) + pb.writeShort(line.length.toShort) + for (x <- 0 until line.length.toShort) { + pb.writeInt(line(x)) + } + } + } + + def appendTextBufferRawSetForeground(pb: PacketBuilder, col: Int, row: Int, color: Array[Array[Int]]) { + pb.writePacketType(PacketType.TextBufferMultiRawSetForeground) + + pb.writeInt(col) + pb.writeInt(row) + pb.writeShort(color.length.toShort) + for (y <- 0 until color.length.toShort) { + val line = color(y) + pb.writeShort(line.length.toShort) + for (x <- 0 until line.length.toShort) { + pb.writeInt(line(x)) + } + } + } + def sendTextBufferInit(address: String, value: NBTTagCompound, player: EntityPlayerMP) { val pb = new CompressedPacketBuilder(PacketType.TextBufferInit) diff --git a/src/main/scala/li/cil/oc/server/component/DebugCard.scala b/src/main/scala/li/cil/oc/server/component/DebugCard.scala index a70874464..fdfc7e6be 100644 --- a/src/main/scala/li/cil/oc/server/component/DebugCard.scala +++ b/src/main/scala/li/cil/oc/server/component/DebugCard.scala @@ -48,6 +48,9 @@ class DebugCard(host: EnvironmentHost) extends prefab.ManagedEnvironment { // Used for delayed connecting to remote node again after loading. private var remoteNodePosition: Option[(Int, Int, Int)] = None + // Player this card is bound to (if any) to use for permissions. + var player: Option[String] = None + // ----------------------------------------------------------------------- // import li.cil.oc.server.component.DebugCard.checkEnabled @@ -92,7 +95,7 @@ class DebugCard(host: EnvironmentHost) extends prefab.ManagedEnvironment { def runCommand(context: Context, args: Arguments): Array[AnyRef] = { checkEnabled() val command = args.checkString(0) - val sender = new CommandSender(host) + val sender = new CommandSender(host, player) val value = MinecraftServer.getServer.getCommandManager.executeCommand(sender, command) result(value, sender.messages.orNull) } @@ -160,6 +163,9 @@ class DebugCard(host: EnvironmentHost) extends prefab.ManagedEnvironment { val z = nbt.getInteger(Settings.namespace + "remoteZ") remoteNodePosition = Some((x, y, z)) } + if (nbt.hasKey(Settings.namespace + "player")) { + player = Option(nbt.getString(Settings.namespace + "player")) + } } override def save(nbt: NBTTagCompound): Unit = { @@ -170,6 +176,7 @@ class DebugCard(host: EnvironmentHost) extends prefab.ManagedEnvironment { nbt.setInteger(Settings.namespace + "remoteY", y) nbt.setInteger(Settings.namespace + "remoteZ", z) } + player.foreach(nbt.setString(Settings.namespace + "player", _)) } } @@ -430,8 +437,17 @@ object DebugCard { } } - class CommandSender(val host: EnvironmentHost) extends ICommandSender { - val fakePlayer = FakePlayerFactory.get(host.world.asInstanceOf[WorldServer], Settings.get.fakePlayerProfile) + class CommandSender(val host: EnvironmentHost, val playerName: Option[String]) extends ICommandSender { + val fakePlayer = { + def defaultFakePlayer = FakePlayerFactory.get(host.world.asInstanceOf[WorldServer], Settings.get.fakePlayerProfile) + playerName match { + case Some(name) => Option(MinecraftServer.getServer.getConfigurationManager.getPlayerByUsername(name)) match { + case Some(player) => player + case _ => defaultFakePlayer + } + case _ => defaultFakePlayer + } + } var messages: Option[String] = None diff --git a/src/main/scala/li/cil/oc/server/component/NetworkCard.scala b/src/main/scala/li/cil/oc/server/component/NetworkCard.scala index 0992adc10..111e76ff2 100644 --- a/src/main/scala/li/cil/oc/server/component/NetworkCard.scala +++ b/src/main/scala/li/cil/oc/server/component/NetworkCard.scala @@ -1,5 +1,6 @@ package li.cil.oc.server.component +import com.google.common.base.Charsets import li.cil.oc.Settings import li.cil.oc.api import li.cil.oc.api.Network @@ -21,6 +22,8 @@ class NetworkCard(val host: EnvironmentHost) extends prefab.ManagedEnvironment { protected val openPorts = mutable.Set.empty[Int] + protected var wakeMessage: Option[String] = None + // ----------------------------------------------------------------------- // @Callback(doc = """function(port:number):boolean -- Opens the specified port. Returns true if the port was opened.""") @@ -75,6 +78,19 @@ class NetworkCard(val host: EnvironmentHost) extends prefab.ManagedEnvironment { @Callback(direct = true, doc = """function():number -- Gets the maximum packet size (config setting).""") def maxPacketSize(context: Context, args: Arguments): Array[AnyRef] = result(Settings.get.maxNetworkPacketSize) + @Callback(direct = true, doc = """function():string -- Get the current wake-up message.""") + def getWakeMessage(context: Context, args: Arguments): Array[AnyRef] = result(wakeMessage.orNull) + + @Callback(doc = """function(message:string):string -- Set the wake-up message.""") + def setWakeMessage(context: Context, args: Arguments): Array[AnyRef] = { + val oldMessage = wakeMessage + if (args.optAny(0, null) == null) + wakeMessage = None + else + wakeMessage = Option(args.checkString(0)) + result(oldMessage.orNull) + } + protected def doSend(packet: Packet) { node.sendToReachable("network.message", packet) } @@ -109,8 +125,19 @@ class NetworkCard(val host: EnvironmentHost) extends prefab.ManagedEnvironment { } protected def receivePacket(packet: Packet, distance: Double) { - if (packet.source != node.address && Option(packet.destination).forall(_ == node.address) && openPorts.contains(packet.port)) { - node.sendToReachable("computer.signal", Seq("modem_message", packet.source, Int.box(packet.port), Double.box(distance)) ++ packet.data: _*) + if (packet.source != node.address && Option(packet.destination).forall(_ == node.address)) { + if (openPorts.contains(packet.port)) { + node.sendToReachable("computer.signal", Seq("modem_message", packet.source, Int.box(packet.port), Double.box(distance)) ++ packet.data: _*) + } + // Accept wake-up messages regardless of port because we close all ports + // when our computer shuts down. + packet.data match { + case Array(message: Array[Byte]) if wakeMessage.contains(new String(message, Charsets.UTF_8)) => + node.sendToNeighbors("computer.start") + case Array(message: String) if wakeMessage.contains(message) => + node.sendToNeighbors("computer.start") + case _ => + } } } @@ -121,12 +148,16 @@ class NetworkCard(val host: EnvironmentHost) extends prefab.ManagedEnvironment { assert(openPorts.isEmpty) openPorts ++= nbt.getIntArray("openPorts") + if (nbt.hasKey("wakeMessage")) { + wakeMessage = Option(nbt.getString("wakeMessage")) + } } override def save(nbt: NBTTagCompound) { super.save(nbt) nbt.setIntArray("openPorts", openPorts.toArray) + wakeMessage.foreach(nbt.setString("wakeMessage", _)) } // ----------------------------------------------------------------------- // diff --git a/src/main/scala/li/cil/oc/server/component/Redstone.scala b/src/main/scala/li/cil/oc/server/component/Redstone.scala index 6d90fa9c0..4347c8d5b 100644 --- a/src/main/scala/li/cil/oc/server/component/Redstone.scala +++ b/src/main/scala/li/cil/oc/server/component/Redstone.scala @@ -1,62 +1,25 @@ package li.cil.oc.server.component -import li.cil.oc.api.Network -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.api.prefab +import li.cil.oc.api.driver.EnvironmentHost import li.cil.oc.common.tileentity.traits.BundledRedstoneAware import li.cil.oc.common.tileentity.traits.RedstoneAware import li.cil.oc.server.component -import net.minecraft.util.EnumFacing - -class Redstone[+Owner <: RedstoneAware](val owner: Owner) extends prefab.ManagedEnvironment { - override val node = Network.newNode(this, Visibility.Network). - withComponent("redstone", Visibility.Neighbors). - create() - - // ----------------------------------------------------------------------- // - - @Callback(direct = true, doc = """function(side:number):number -- Get the redstone input on the specified side.""") - def getInput(context: Context, args: Arguments): Array[AnyRef] = { - val side = checkSide(args, 0) - result(owner.input(side)) - } - - @Callback(direct = true, doc = """function(side:number):number -- Get the redstone output on the specified side.""") - def getOutput(context: Context, args: Arguments): Array[AnyRef] = { - val side = checkSide(args, 0) - result(owner.output(side)) - } - - @Callback(doc = """function(side:number, value:number):number -- Set the redstone output on the specified side.""") - def setOutput(context: Context, args: Arguments): Array[AnyRef] = { - val side = checkSide(args, 0) - val value = args.checkInteger(1) - owner.output(side, value) - context.pause(0.1) - result(owner.output(side)) - } - - // ----------------------------------------------------------------------- // - - protected def checkSide(args: Arguments, index: Int) = { - val side = args.checkInteger(index) - if (side < 0 || side > 5) - throw new IllegalArgumentException("invalid side") - owner.toGlobal(EnumFacing.getFront(side)) - } -} object Redstone { - class Simple(redstone: RedstoneAware) extends component.Redstone[RedstoneAware](redstone) + class Vanilla(val redstone: EnvironmentHost with RedstoneAware) + extends component.RedstoneVanilla - class Bundled(redstone: BundledRedstoneAware) extends component.Redstone[BundledRedstoneAware](redstone) with component.RedstoneBundled + class Bundled(val redstone: EnvironmentHost with BundledRedstoneAware) + extends component.RedstoneVanilla with component.RedstoneBundled - class Wireless(redstone: RedstoneAware) extends Simple(redstone) with component.RedstoneWireless + class Wireless(val redstone: EnvironmentHost) + extends component.RedstoneWireless - class BundledWireless(redstone: BundledRedstoneAware) extends Bundled(redstone) with component.RedstoneWireless + class VanillaWireless(val redstone: EnvironmentHost with RedstoneAware) + extends component.RedstoneVanilla with component.RedstoneWireless + + class BundledWireless(val redstone: EnvironmentHost with BundledRedstoneAware) + extends component.RedstoneVanilla with component.RedstoneBundled with component.RedstoneWireless } diff --git a/src/main/scala/li/cil/oc/server/component/RedstoneBundled.scala b/src/main/scala/li/cil/oc/server/component/RedstoneBundled.scala index b6b2b2ebc..02b3312eb 100644 --- a/src/main/scala/li/cil/oc/server/component/RedstoneBundled.scala +++ b/src/main/scala/li/cil/oc/server/component/RedstoneBundled.scala @@ -1,23 +1,26 @@ package li.cil.oc.server.component +import li.cil.oc.api.driver.EnvironmentHost 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.common.tileentity.traits.BundledRedstoneAware -trait RedstoneBundled extends Redstone[BundledRedstoneAware] { +trait RedstoneBundled extends RedstoneVanilla { + override def redstone: EnvironmentHost with BundledRedstoneAware + @Callback(direct = true, doc = """function(side:number, color:number):number -- Get the bundled redstone input on the specified side and with the specified color.""") def getBundledInput(context: Context, args: Arguments): Array[AnyRef] = { val side = checkSide(args, 0) val color = checkColor(args, 1) - result(owner.bundledInput(side, color)) + result(redstone.bundledInput(side, color)) } @Callback(direct = true, doc = """function(side:number, color:number):number -- Get the bundled redstone output on the specified side and with the specified color.""") def getBundledOutput(context: Context, args: Arguments): Array[AnyRef] = { val side = checkSide(args, 0) val color = checkColor(args, 1) - result(owner.bundledOutput(side, color)) + result(redstone.bundledOutput(side, color)) } @Callback(doc = """function(side:number, color:number, value:number):number -- Set the bundled redstone output on the specified side and with the specified color.""") @@ -26,7 +29,7 @@ trait RedstoneBundled extends Redstone[BundledRedstoneAware] { if (args.isTable(1)) { val table = args.checkTable(1) (0 to 15).map(color => (color, table.get(color))).foreach { - case (color, number: Number) => owner.bundledOutput(side, color, number.intValue()) + case (color, number: Number) => redstone.bundledOutput(side, color, number.intValue()) case _ => } context.pause(0.1) @@ -35,9 +38,9 @@ trait RedstoneBundled extends Redstone[BundledRedstoneAware] { else { val color = checkColor(args, 1) val value = args.checkInteger(2) - owner.bundledOutput(side, color, value) + redstone.bundledOutput(side, color, value) context.pause(0.1) - result(owner.bundledOutput(side, color)) + result(redstone.bundledOutput(side, color)) } } diff --git a/src/main/scala/li/cil/oc/server/component/RedstoneSignaller.scala b/src/main/scala/li/cil/oc/server/component/RedstoneSignaller.scala new file mode 100644 index 000000000..84b4d6016 --- /dev/null +++ b/src/main/scala/li/cil/oc/server/component/RedstoneSignaller.scala @@ -0,0 +1,50 @@ +package li.cil.oc.server.component + +import li.cil.oc.api.Network +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.Visibility +import li.cil.oc.api.prefab +import net.minecraft.nbt.NBTTagCompound + +trait RedstoneSignaller extends prefab.ManagedEnvironment { + override val node = Network.newNode(this, Visibility.Network). + withComponent("redstone", Visibility.Neighbors). + create() + + var wakeThreshold = 0 + + // ----------------------------------------------------------------------- // + + @Callback(direct = true, doc = """function():number -- Get the current wake-up threshold.""") + def getWakeThreshold(context: Context, args: Arguments): Array[AnyRef] = result(wakeThreshold) + + @Callback(doc = """function(threshold:number):number -- Set the wake-up threshold.""") + def setWakeThreshold(context: Context, args: Arguments): Array[AnyRef] = { + val oldThreshold = wakeThreshold + wakeThreshold = args.checkInteger(0) + result(oldThreshold) + } + + // ----------------------------------------------------------------------- // + + def onRedstoneChanged(side: AnyRef, oldMaxValue: Int, newMaxValue: Int): Unit = { + node.sendToReachable("computer.signal", "redstone_changed", side, int2Integer(oldMaxValue), int2Integer(newMaxValue)) + if (oldMaxValue < wakeThreshold && newMaxValue >= wakeThreshold) { + node.sendToNeighbors("computer.start") + } + } + + // ----------------------------------------------------------------------- // + + override def load(nbt: NBTTagCompound): Unit = { + super.load(nbt) + wakeThreshold = nbt.getInteger("wakeThreshold") + } + + override def save(nbt: NBTTagCompound): Unit = { + super.save(nbt) + nbt.setInteger("wakeThreshold", wakeThreshold) + } +} diff --git a/src/main/scala/li/cil/oc/server/component/RedstoneVanilla.scala b/src/main/scala/li/cil/oc/server/component/RedstoneVanilla.scala new file mode 100644 index 000000000..f9de50dca --- /dev/null +++ b/src/main/scala/li/cil/oc/server/component/RedstoneVanilla.scala @@ -0,0 +1,56 @@ +package li.cil.oc.server.component + +import li.cil.oc.api.driver.EnvironmentHost +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.tileentity.traits.RedstoneAware +import net.minecraft.util.EnumFacing + +trait RedstoneVanilla extends RedstoneSignaller { + def redstone: EnvironmentHost with RedstoneAware + + // ----------------------------------------------------------------------- // + + @Callback(direct = true, doc = """function(side:number):number -- Get the redstone input on the specified side.""") + def getInput(context: Context, args: Arguments): Array[AnyRef] = { + val side = checkSide(args, 0) + result(redstone.input(side)) + } + + @Callback(direct = true, doc = """function(side:number):number -- Get the redstone output on the specified side.""") + def getOutput(context: Context, args: Arguments): Array[AnyRef] = { + val side = checkSide(args, 0) + result(redstone.output(side)) + } + + @Callback(doc = """function(side:number, value:number):number -- Set the redstone output on the specified side.""") + def setOutput(context: Context, args: Arguments): Array[AnyRef] = { + val side = checkSide(args, 0) + val value = args.checkInteger(1) + redstone.output(side, value) + context.pause(0.1) + result(redstone.output(side)) + } + + // ----------------------------------------------------------------------- // + + override def onMessage(message: Message): Unit = { + super.onMessage(message) + if (message.name == "redstone.changed") message.data match { + case Array(side: EnumFacing, oldMaxValue: Number, newMaxValue: Number) => + onRedstoneChanged(int2Integer(side.ordinal()), oldMaxValue.intValue(), newMaxValue.intValue()) + case _ => + } + } + + // ----------------------------------------------------------------------- // + + protected def checkSide(args: Arguments, index: Int) = { + val side = args.checkInteger(index) + if (side < 0 || side > 5) + throw new IllegalArgumentException("invalid side") + redstone.toGlobal(EnumFacing.getFront(side)) + } +} diff --git a/src/main/scala/li/cil/oc/server/component/RedstoneWireless.scala b/src/main/scala/li/cil/oc/server/component/RedstoneWireless.scala index 877d38c95..04760308a 100644 --- a/src/main/scala/li/cil/oc/server/component/RedstoneWireless.scala +++ b/src/main/scala/li/cil/oc/server/component/RedstoneWireless.scala @@ -6,12 +6,12 @@ import codechicken.wirelessredstone.core.WirelessReceivingDevice import codechicken.wirelessredstone.core.WirelessTransmittingDevice */ +import li.cil.oc.api.driver.EnvironmentHost 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.EventHandler -import li.cil.oc.common.tileentity.traits.RedstoneAware import li.cil.oc.integration.Mods import li.cil.oc.integration.util import net.minecraft.nbt.NBTTagCompound @@ -21,7 +21,9 @@ import net.minecraftforge.fml.common.Optional new Optional.Interface(iface = "codechicken.wirelessredstone.core.WirelessReceivingDevice", modid = Mods.IDs.WirelessRedstoneCBE), new Optional.Interface(iface = "codechicken.wirelessredstone.core.WirelessTransmittingDevice", modid = Mods.IDs.WirelessRedstoneCBE) )) -trait RedstoneWireless extends Redstone[RedstoneAware] /* with WirelessReceivingDevice with WirelessTransmittingDevice TODO WRCBE */ { +trait RedstoneWireless extends RedstoneSignaller /* with WirelessReceivingDevice with WirelessTransmittingDevice TODO WRCBE */ { + def redstone: EnvironmentHost + var wirelessFrequency = 0 var wirelessInput = false @@ -77,15 +79,15 @@ trait RedstoneWireless extends Redstone[RedstoneAware] /* with WirelessReceiving override def updateDevice(frequency: Int, on: Boolean) { if (frequency == wirelessFrequency && on != wirelessInput) { wirelessInput = on - node.sendToReachable("computer.signal", "redstone_changed", "wireless") + onRedstoneChanged("wireless", if (on) 0 else 1, if (on) 1 else 0) } } @Optional.Method(modid = Mods.IDs.WirelessRedstoneCBE) - override def getPosition = Vector3.fromTileEntityCenter(owner) + override def getPosition = new Vector3(redstone.xPosition, redstone.yPosition, redstone.zPosition) @Optional.Method(modid = Mods.IDs.WirelessRedstoneCBE) - override def getDimension = owner.world.provider.getDimensionId + override def getDimension = redstone.world.provider.getDimensionId @Optional.Method(modid = Mods.IDs.WirelessRedstoneCBE) override def getFreq = wirelessFrequency diff --git a/src/main/scala/li/cil/oc/server/component/UpgradeNavigation.scala b/src/main/scala/li/cil/oc/server/component/UpgradeNavigation.scala index 22f639368..aefdd926a 100644 --- a/src/main/scala/li/cil/oc/server/component/UpgradeNavigation.scala +++ b/src/main/scala/li/cil/oc/server/component/UpgradeNavigation.scala @@ -8,7 +8,7 @@ import li.cil.oc.api.machine.Callback import li.cil.oc.api.machine.Context import li.cil.oc.api.network._ import li.cil.oc.api.prefab -import li.cil.oc.util.ItemUtils.NavigationUpgradeData +import li.cil.oc.common.item.data.NavigationUpgradeData import net.minecraft.nbt.NBTTagCompound class UpgradeNavigation(val host: EnvironmentHost with Rotatable) extends prefab.ManagedEnvironment { diff --git a/src/main/scala/li/cil/oc/server/component/robot/Player.scala b/src/main/scala/li/cil/oc/server/component/robot/Player.scala index d8e3deba6..3b845f557 100644 --- a/src/main/scala/li/cil/oc/server/component/robot/Player.scala +++ b/src/main/scala/li/cil/oc/server/component/robot/Player.scala @@ -309,6 +309,8 @@ class Player(val robot: tileentity.Robot) extends FakePlayer(robot.world.asInsta if (cobwebOverride) Settings.get.swingDelay else hardness * 1.5 / strength + if (breakTime.isInfinity) return 0 + val preEvent = new RobotBreakBlockEvent.Pre(robot, world, pos, breakTime * Settings.get.harvestRatio) MinecraftForge.EVENT_BUS.post(preEvent) if (preEvent.isCanceled) return 0 diff --git a/src/main/scala/li/cil/oc/server/component/traits/TankWorldControl.scala b/src/main/scala/li/cil/oc/server/component/traits/TankWorldControl.scala index 9c9b0825f..5f07e7892 100644 --- a/src/main/scala/li/cil/oc/server/component/traits/TankWorldControl.scala +++ b/src/main/scala/li/cil/oc/server/component/traits/TankWorldControl.scala @@ -7,8 +7,10 @@ import li.cil.oc.util.ExtendedArguments._ import li.cil.oc.util.ExtendedBlock._ import li.cil.oc.util.ExtendedWorld._ import li.cil.oc.util.ResultWrapper.result +import net.minecraft.block.BlockLiquid import net.minecraftforge.fluids.FluidRegistry import net.minecraftforge.fluids.FluidStack +import net.minecraftforge.fluids.IFluidBlock import net.minecraftforge.fluids.IFluidHandler trait TankWorldControl extends TankAware with WorldAware with SideRestricted { @@ -50,26 +52,41 @@ trait TankWorldControl extends TankAware with WorldAware with SideRestricted { case stack: FluidStack => val drained = handler.drain(facing.getOpposite, new FluidStack(stack, amount), true) if ((drained != null && drained.amount > 0) || amount == 0) { - tank.fill(drained, true) - result(true, drained.amount) + val filled = tank.fill(drained, true) + result(true, filled) } else result(Unit, "incompatible or no fluid") case _ => val transferred = tank.fill(handler.drain(facing.getOpposite, amount, true), true) result(transferred > 0, transferred) } - case _ => - val block = world.getBlock(blockPos) - val fluid = FluidRegistry.lookupFluidForBlock(block) - if (fluid == null) { + case _ => world.getBlock(blockPos) match { + case fluidBlock: IFluidBlock if fluidBlock.canDrain(world, blockPos.toBlockPos) => + val drained = fluidBlock.drain(world, blockPos.toBlockPos, false) + if ((drained != null && drained.amount > 0) && (drained.amount <= amount || amount == 0)) { + if (drained.amount <= amount) { + val filled = tank.fill(fluidBlock.drain(world, blockPos.toBlockPos, true), true) + result(true, filled) + } + else /* if (amount == 0) */ { + result(true, 0) + } + } + else result(Unit, "tank is full") + case liquidBlock: BlockLiquid if world.getBlockState(blockPos.toBlockPos) == liquidBlock.getDefaultState => + val fluid = FluidRegistry.lookupFluidForBlock(liquidBlock) + if (fluid == null) { + result(Unit, "incompatible or no fluid") + } + else if (tank.fill(new FluidStack(fluid, 1000), false) == 1000) { + tank.fill(new FluidStack(fluid, 1000), true) + world.setBlockToAir(blockPos) + result(true, 1000) + } + else result(Unit, "tank is full") + case _ => result(Unit, "incompatible or no fluid") - } - else if (tank.fill(new FluidStack(fluid, 1000), false) == 1000) { - tank.fill(new FluidStack(fluid, 1000), true) - world.setBlockToAir(blockPos) - result(true, 1000) - } - else result(Unit, "tank is full") + } } else result(Unit, "incompatible or no fluid") } diff --git a/src/main/scala/li/cil/oc/server/component/traits/WorldInventoryAnalytics.scala b/src/main/scala/li/cil/oc/server/component/traits/WorldInventoryAnalytics.scala index e5eaff024..edfc4e58e 100644 --- a/src/main/scala/li/cil/oc/server/component/traits/WorldInventoryAnalytics.scala +++ b/src/main/scala/li/cil/oc/server/component/traits/WorldInventoryAnalytics.scala @@ -13,25 +13,25 @@ import net.minecraft.item.ItemStack import net.minecraft.util.EnumFacing trait WorldInventoryAnalytics extends WorldAware with SideRestricted with NetworkAware { - @Callback(doc = """function(side:number):number -- Get the number of slots in the inventory on the specified side of the robot.""") + @Callback(doc = """function(side:number):number -- Get the number of slots in the inventory on the specified side of the device.""") def getInventorySize(context: Context, args: Arguments): Array[AnyRef] = { val facing = checkSideForAction(args, 0) withInventory(facing, inventory => result(inventory.getSizeInventory)) } - @Callback(doc = """function(side:number, slot:number):number -- Get number of items in the specified slot of the inventory on the specified side of the robot.""") + @Callback(doc = """function(side:number, slot:number):number -- Get number of items in the specified slot of the inventory on the specified side of the device.""") def getSlotStackSize(context: Context, args: Arguments): Array[AnyRef] = { val facing = checkSideForAction(args, 0) withInventory(facing, inventory => result(Option(inventory.getStackInSlot(args.checkSlot(inventory, 1))).fold(0)(_.stackSize))) } - @Callback(doc = """function(side:number, slot:number):number -- Get the maximum number of items in the specified slot of the inventory on the specified side of the robot.""") + @Callback(doc = """function(side:number, slot:number):number -- Get the maximum number of items in the specified slot of the inventory on the specified side of the device.""") def getSlotMaxStackSize(context: Context, args: Arguments): Array[AnyRef] = { val facing = checkSideForAction(args, 0) withInventory(facing, inventory => result(Option(inventory.getStackInSlot(args.checkSlot(inventory, 1))).fold(0)(_.getMaxStackSize))) } - @Callback(doc = """function(side:number, slotA:number, slotB:number):boolean -- Get whether the items in the two specified slots of the inventory on the specified side of the robot are of the same type.""") + @Callback(doc = """function(side:number, slotA:number, slotB:number):boolean -- Get whether the items in the two specified slots of the inventory on the specified side of the device are of the same type.""") def compareStacks(context: Context, args: Arguments): Array[AnyRef] = { val facing = checkSideForAction(args, 0) withInventory(facing, inventory => { @@ -44,12 +44,12 @@ trait WorldInventoryAnalytics extends WorldAware with SideRestricted with Networ }) } - @Callback(doc = """function(side:number, slot:number):table -- Get a description of the stack in the the inventory on the specified side of the robot.""") + @Callback(doc = """function(side:number, slot:number):table -- Get a description of the stack in the inventory on the specified side of the device.""") def getStackInSlot(context: Context, args: Arguments): Array[AnyRef] = if (Settings.get.allowItemStackInspection) { val facing = checkSideForAction(args, 0) withInventory(facing, inventory => result(inventory.getStackInSlot(args.checkSlot(inventory, 1)))) } - else result(Unit, "not enabled in config") + else result(null, "not enabled in config") @Callback(doc = """function(side:number, slot:number, dbAddress:string, dbSlot:number):boolean -- Store an item stack description in the specified slot of the database with the specified address.""") def store(context: Context, args: Arguments): Array[AnyRef] = { @@ -67,6 +67,6 @@ trait WorldInventoryAnalytics extends WorldAware with SideRestricted with Networ private def withInventory(side: EnumFacing, f: IInventory => Array[AnyRef]) = InventoryUtils.inventoryAt(position.offset(side)) match { case Some(inventory) if inventory.isUseableByPlayer(fakePlayer) => f(inventory) - case _ => result(Unit, "no inventory") + case _ => result(null, "no inventory") } } diff --git a/src/main/scala/li/cil/oc/server/machine/Machine.scala b/src/main/scala/li/cil/oc/server/machine/Machine.scala index a7fa810b6..5891e8e8e 100644 --- a/src/main/scala/li/cil/oc/server/machine/Machine.scala +++ b/src/main/scala/li/cil/oc/server/machine/Machine.scala @@ -90,7 +90,7 @@ class Machine(val host: MachineHost) extends prefab.ManagedEnvironment with mach override def getBootAddress = bootAddress - override def setBootAddress(value: String) = bootAddress = Option(value).map(_.take(36)).getOrElse("") + override def setBootAddress(value: String) = bootAddress = Option(value).fold("")(_.take(36)) override def components = scala.collection.convert.WrapAsJava.mapAsJavaMap(_components) @@ -255,7 +255,7 @@ class Machine(val host: MachineHost) extends prefab.ManagedEnvironment with mach }) override def invoke(address: String, method: String, args: Array[AnyRef]) = - Option(node.network.node(address)) match { + if (node != null && node.network != null) Option(node.network.node(address)) match { case Some(component: Component) if component.canBeSeenFrom(node) || component == node => val direct = component.annotation(method).direct if (direct && architecture.isInitialized) { @@ -264,6 +264,11 @@ class Machine(val host: MachineHost) extends prefab.ManagedEnvironment with mach component.invoke(method, this, args: _*) case _ => throw new IllegalArgumentException("no such component") } + else { + // Not really, but makes the VM stop, which is what we want in this case, + // because it means we've been disconnected / disposed already. + throw new LimitReachedException() + } override def invoke(value: Value, method: String, args: Array[AnyRef]): Array[AnyRef] = Callbacks(value).get(method) match { case Some(callback) => @@ -490,6 +495,8 @@ class Machine(val host: MachineHost) extends prefab.ManagedEnvironment with mach if (canInteract(player.getName)) signal(name, Seq(message.source.address) ++ args: _*) case _ => + if (message.name == "computer.start" && !isPaused) start() + else if (message.name == "computer.stop") stop() } } diff --git a/src/main/scala/li/cil/oc/server/network/Connector.scala b/src/main/scala/li/cil/oc/server/network/Connector.scala index 244a55968..977cdc35f 100644 --- a/src/main/scala/li/cil/oc/server/network/Connector.scala +++ b/src/main/scala/li/cil/oc/server/network/Connector.scala @@ -6,7 +6,7 @@ import li.cil.oc.api.network.{Node => ImmutableNode} import net.minecraft.nbt.NBTTagCompound trait Connector extends network.Connector with Node { - var localBufferSize: Double + var localBufferSize = 0.0 var localBuffer = 0.0 @@ -85,20 +85,24 @@ trait Connector extends network.Connector with Node { } def setLocalBufferSize(size: Double) { + val clampedSize = math.max(size, 0) this.synchronized(distributor match { case Some(d) => d.synchronized { + val oldSize = localBufferSize + // Must apply new size before trying to register with distributor, else + // we get ignored if our size is zero. + localBufferSize = clampedSize if (network != null) { - if (localBufferSize <= 0 && size > 0) d.addConnector(this) - else if (localBufferSize > 0 && size == 0) d.removeConnector(this) - else d.globalBufferSize = math.max(d.globalBufferSize - localBufferSize + size, 0) + if (localBufferSize <= 0 && clampedSize > 0) d.addConnector(this) + else if (oldSize > 0 && clampedSize == 0) d.removeConnector(this) + else d.globalBufferSize = math.max(d.globalBufferSize - oldSize + clampedSize, 0) } - localBufferSize = math.max(size, 0) - val surplus = math.max(localBuffer - localBufferSize, 0) + val surplus = math.max(localBuffer - clampedSize, 0) changeBuffer(-surplus) d.changeBuffer(surplus) } case _ => - localBufferSize = math.max(size, 0) + localBufferSize = clampedSize localBuffer = math.min(localBuffer, localBufferSize) }) } diff --git a/src/main/scala/li/cil/oc/server/network/Distributor.scala b/src/main/scala/li/cil/oc/server/network/Distributor.scala index 5d88f36b8..a1d2acc1b 100644 --- a/src/main/scala/li/cil/oc/server/network/Distributor.scala +++ b/src/main/scala/li/cil/oc/server/network/Distributor.scala @@ -3,15 +3,15 @@ package li.cil.oc.server.network trait Distributor { def globalBuffer: Double - def globalBuffer_=(value: Double) + def globalBuffer_=(value: Double): Unit def globalBufferSize: Double - def globalBufferSize_=(value: Double) + def globalBufferSize_=(value: Double): Unit - def addConnector(connector: Connector) + def addConnector(connector: Connector): Unit - def removeConnector(connector: Connector) + def removeConnector(connector: Connector): Unit def changeBuffer(delta: Double): Double } diff --git a/src/main/scala/li/cil/oc/server/network/Network.scala b/src/main/scala/li/cil/oc/server/network/Network.scala index 14eae3071..be8319782 100644 --- a/src/main/scala/li/cil/oc/server/network/Network.scala +++ b/src/main/scala/li/cil/oc/server/network/Network.scala @@ -14,11 +14,8 @@ import li.cil.oc.OpenComputers import li.cil.oc.Settings import li.cil.oc.api import li.cil.oc.api.network -import li.cil.oc.api.network.Environment -import li.cil.oc.api.network.SidedEnvironment -import li.cil.oc.api.network.Visibility -import li.cil.oc.api.network.WirelessEndpoint import li.cil.oc.api.network.{Node => ImmutableNode} +import li.cil.oc.api.network._ import li.cil.oc.common.tileentity import li.cil.oc.integration.Mods import li.cil.oc.server.network.{Node => MutableNode} @@ -255,11 +252,11 @@ private class Network private(private val data: mutable.Map[String, Network.Vert // never happen in normal operation anyway. It *can* happen when NBT // editing stuff or using mods to clone blocks (e.g. WorldEdit). otherNetwork.data.filter(entry => data.contains(entry._1)).toArray.foreach { - case (address, node: MutableNode) => - val neighbors = node.neighbors.toArray // Copy to be on the safe side. - node.remove() - node.address = java.util.UUID.randomUUID().toString - neighbors.foreach(_.connect(node)) + case (address, node: Network.Vertex) => + val neighbors = node.data.neighbors.toArray // Copy to be on the safe side. + node.data.remove() + node.data.address = java.util.UUID.randomUUID().toString + neighbors.foreach(_.connect(node.data)) } if (addedNode.reachability == Visibility.Neighbors) @@ -439,7 +436,10 @@ object Network extends api.detail.NetworkAPI { private def getNetworkNode(tileEntity: TileEntity, side: EnumFacing) = tileEntity match { case host: SidedEnvironment => Option(host.sidedNode(side)) - case host: Environment => Some(host.node) + case host: Environment with SidedComponent => + if (host.canConnectNode(side)) Option(host.node) + else None + case host: Environment => Option(host.node) case host if Mods.ForgeMultipart.isAvailable => getMultiPartNode(host) case _ => None } @@ -599,7 +599,7 @@ object Network extends api.detail.NetworkAPI { def create() = if (SideTracker.isServer) new Connector with NodeVarargPart { val host = _host val reachability = _reachability - var localBufferSize = _bufferSize + localBufferSize = _bufferSize } else null } @@ -609,7 +609,7 @@ object Network extends api.detail.NetworkAPI { val host = _host val reachability = _reachability val name = _name - var localBufferSize = _bufferSize + localBufferSize = _bufferSize setVisibility(_visibility) } else null diff --git a/src/main/scala/li/cil/oc/server/network/Node.scala b/src/main/scala/li/cil/oc/server/network/Node.scala index c18de90d0..69626642f 100644 --- a/src/main/scala/li/cil/oc/server/network/Node.scala +++ b/src/main/scala/li/cil/oc/server/network/Node.scala @@ -11,8 +11,9 @@ import scala.collection.convert.WrapAsJava._ import scala.collection.convert.WrapAsScala._ trait Node extends ImmutableNode { - val host: Environment - val reachability: Visibility + def host: Environment + + def reachability: Visibility final var address: String = null diff --git a/src/main/scala/li/cil/oc/util/Audio.scala b/src/main/scala/li/cil/oc/util/Audio.scala index 4495d0ae8..813186433 100644 --- a/src/main/scala/li/cil/oc/util/Audio.scala +++ b/src/main/scala/li/cil/oc/util/Audio.scala @@ -3,6 +3,7 @@ package li.cil.oc.util import java.nio.ByteBuffer import li.cil.oc.OpenComputers +import li.cil.oc.Settings import net.minecraft.client.Minecraft import net.minecraft.client.audio.SoundCategory import net.minecraftforge.fml.common.FMLCommonHandler @@ -23,7 +24,9 @@ import scala.collection.mutable * tick handler. */ object Audio { - private def sampleRate = 8000 + private def sampleRate = Settings.get.beepSampleRate + + private def amplitude = Settings.get.beepAmplitude private val sources = mutable.Set.empty[Source] @@ -39,7 +42,7 @@ object Audio { if (!disableAudio) { val distanceBasedGain = math.max(0, 1 - Minecraft.getMinecraft.thePlayer.getDistance(x, y, z) / 12).toFloat val gain = distanceBasedGain * volume - if (gain > 0 && AL.isCreated) { + if (gain > 0 && amplitude > 0 && AL.isCreated) { val sampleCounts = pattern.toCharArray. map(ch => if (ch == '.') durationInMilliseconds else 2 * durationInMilliseconds). map(_ * sampleRate / 1000) @@ -51,13 +54,7 @@ object Audio { for (sampleCount <- sampleCounts) { for (sample <- 0 until sampleCount) { val angle = 2 * math.Pi * offset - // We could sort of fake the square wave with a little less - // computational effort, but until somebody complains let's - // go with the fourier series! We leave out the 4 / Pi because - // it's just an approximation and we avoid clipping like this. - val value = (0 to 6).map(k => math.sin((1 + k * 2) * angle) / (1 + k * 2)).sum * Byte.MaxValue - // val tmp = math.sin(angle) * Byte.MaxValue - // val value = math.signum(tmp) * 0.99 + tmp * 0.01 + val value = (math.signum(math.sin(angle)) * amplitude).toByte ^ 0x80 offset += step if (offset > 1) offset -= 1 data.put(value.toByte) diff --git a/src/main/scala/li/cil/oc/util/ItemCosts.scala b/src/main/scala/li/cil/oc/util/ItemCosts.scala index cf3d9c3d8..37d75e494 100644 --- a/src/main/scala/li/cil/oc/util/ItemCosts.scala +++ b/src/main/scala/li/cil/oc/util/ItemCosts.scala @@ -19,13 +19,17 @@ import scala.collection.convert.WrapAsScala._ import scala.collection.mutable object ItemCosts { - protected val cache = mutable.Map.empty[ItemStack, Iterable[(ItemStack, Double)]] + private final val Timeout = 500 - cache += api.Items.get("ironNugget").createItemStack(1) -> Iterable((new ItemStack(Items.iron_ingot), 1.0 / 9.0)) + private val cache = mutable.Map.empty[ItemStackWrapper, Iterable[(ItemStack, Double)]] - def terminate(item: Item, meta: Int = 0) = cache += new ItemStack(item, 1, meta) -> mutable.Iterable((new ItemStack(item, 1, meta), 1)) + private var started = 0L - def terminate(block: Block) = cache += new ItemStack(block) -> mutable.Iterable((new ItemStack(block), 1)) + cache += new ItemStackWrapper(api.Items.get("ironNugget").createItemStack(1)) -> Iterable((new ItemStack(Items.iron_ingot), 1.0 / 9.0)) + + def terminate(item: Item, meta: Int = 0) = cache += new ItemStackWrapper(new ItemStack(item, 1, meta)) -> mutable.Iterable((new ItemStack(item, 1, meta), 1)) + + def terminate(block: Block) = cache += new ItemStackWrapper(new ItemStack(block)) -> mutable.Iterable((new ItemStack(block), 1)) terminate(Blocks.clay) terminate(Blocks.cobblestone) @@ -67,7 +71,8 @@ object ItemCosts { } } - protected def computeIngredients(what: ItemStack): Iterable[(ItemStack, Double)] = { + protected def computeIngredients(what: ItemStack): Iterable[(ItemStack, Double)] = cache.synchronized { + started = System.currentTimeMillis() def deflate(list: Iterable[(ItemStack, Double)]): Iterable[(ItemStack, Double)] = { val counts = mutable.Map.empty[ItemStack, Double] for ((stack, count) <- list) { @@ -80,53 +85,57 @@ object ItemCosts { } counts } - def accumulate(input: Any, path: Seq[ItemStack] = Seq.empty): Iterable[(ItemStack, Double)] = input match { - case stack: ItemStack => - cache.find { - case (key, value) => fuzzyEquals(key, stack) - } match { - case Some((_, value)) => value - case _ => - if (path.exists(value => fuzzyEquals(value, stack))) { - Iterable((stack, 1.0)) - } - else { - val recipes = CraftingManager.getInstance.getRecipeList.map(_.asInstanceOf[IRecipe]) - val recipe = recipes.find(recipe => recipe.getRecipeOutput != null && fuzzyEquals(stack, recipe.getRecipeOutput)) - val (ingredients, output) = recipe match { - case Some(recipe: ShapedRecipes) => (recipe.recipeItems.flatMap(accumulate(_, path :+ stack)).toIterable, recipe.getRecipeOutput.stackSize) - case Some(recipe: ShapelessRecipes) => (recipe.recipeItems.flatMap(accumulate(_, path :+ stack)).toIterable, recipe.getRecipeOutput.stackSize) - case Some(recipe: ShapedOreRecipe) => (recipe.getInput.flatMap(accumulate(_, path :+ stack)).toIterable, recipe.getRecipeOutput.stackSize) - case Some(recipe: ShapelessOreRecipe) => (recipe.getInput.flatMap(accumulate(_, path :+ stack)).toIterable, recipe.getRecipeOutput.stackSize) - case _ => FurnaceRecipes.instance.getSmeltingList.asInstanceOf[util.Map[ItemStack, ItemStack]].find { - case (_, value) => fuzzyEquals(stack, value) - } match { - case Some((rein, raus)) => (accumulate(rein, path :+ stack), raus.stackSize) - case _ => (Iterable((stack, 1.0)), 1) - } - } - val scaled = deflate(ingredients.map { - case (ingredient, count) => (ingredient.copy(), count / output) - }).toArray.sortBy(_._1.getUnlocalizedName) - cache += stack.copy() -> scaled - scaled - } - } - case list: util.ArrayList[ItemStack]@unchecked if !list.isEmpty => - var result = Iterable.empty[(ItemStack, Double)] - for (stack <- list if result.isEmpty) { + def accumulate(input: Any, path: Seq[ItemStack] = Seq.empty): Iterable[(ItemStack, Double)] = { + val passed = System.currentTimeMillis() - started + if (passed > Timeout) Iterable.empty + else input match { + case stack: ItemStack => cache.find { - case (key, value) => fuzzyEquals(key, stack) + case (key, value) => fuzzyEquals(key.inner, stack) } match { - case Some((_, value)) => result = value + case Some((_, value)) => value case _ => + if (path.exists(value => fuzzyEquals(value, stack))) { + Iterable((stack, 1.0)) + } + else { + val recipes = CraftingManager.getInstance.getRecipeList.map(_.asInstanceOf[IRecipe]) + val recipe = recipes.find(recipe => recipe.getRecipeOutput != null && fuzzyEquals(stack, recipe.getRecipeOutput)) + val (ingredients, output) = recipe match { + case Some(recipe: ShapedRecipes) => (recipe.recipeItems.flatMap(accumulate(_, path :+ stack)).toIterable, recipe.getRecipeOutput.stackSize) + case Some(recipe: ShapelessRecipes) => (recipe.recipeItems.flatMap(accumulate(_, path :+ stack)).toIterable, recipe.getRecipeOutput.stackSize) + case Some(recipe: ShapedOreRecipe) => (recipe.getInput.flatMap(accumulate(_, path :+ stack)).toIterable, recipe.getRecipeOutput.stackSize) + case Some(recipe: ShapelessOreRecipe) => (recipe.getInput.flatMap(accumulate(_, path :+ stack)).toIterable, recipe.getRecipeOutput.stackSize) + case _ => FurnaceRecipes.instance.getSmeltingList.asInstanceOf[util.Map[ItemStack, ItemStack]].find { + case (_, value) => fuzzyEquals(stack, value) + } match { + case Some((rein, raus)) => (accumulate(rein, path :+ stack), raus.stackSize) + case _ => (Iterable((stack, 1.0)), 1) + } + } + val scaled = deflate(ingredients.map { + case (ingredient, count) => (ingredient.copy(), count / output) + }).toArray.sortBy(_._1.getUnlocalizedName) + cache += new ItemStackWrapper(stack.copy()) -> scaled + scaled + } } - } - if (result.isEmpty) { - result = accumulate(list.get(0), path) - } - result - case _ => Iterable.empty + case list: util.ArrayList[ItemStack]@unchecked if !list.isEmpty => + var result = Iterable.empty[(ItemStack, Double)] + for (stack <- list if result.isEmpty) { + cache.find { + case (key, value) => fuzzyEquals(key.inner, stack) + } match { + case Some((_, value)) => result = value + case _ => + } + } + if (result.isEmpty) { + result = accumulate(list.get(0), path) + } + result + case _ => Iterable.empty + } } accumulate(what) } diff --git a/src/main/scala/li/cil/oc/util/ItemStackWrapper.scala b/src/main/scala/li/cil/oc/util/ItemStackWrapper.scala new file mode 100644 index 000000000..69168c00c --- /dev/null +++ b/src/main/scala/li/cil/oc/util/ItemStackWrapper.scala @@ -0,0 +1,30 @@ +package li.cil.oc.util + +import java.util.Objects + +import net.minecraft.item.Item +import net.minecraft.item.ItemStack + +import scala.language.implicitConversions + +class ItemStackWrapper(val inner: ItemStack) extends Ordered[ItemStackWrapper] { + def id = if (inner.getItem != null) Item.getIdFromItem(inner.getItem) else 0 + + def damage = if (inner.getItem != null) inner.getItemDamage else 0 + + override def compare(that: ItemStackWrapper) = { + if (this.id == that.id) this.damage - that.damage + else this.id - that.id + } + + override def hashCode() = Objects.hash(int2Integer(id), int2Integer(damage)) + + override def equals(obj: scala.Any) = obj match { + case that: ItemStackWrapper => compare(that) == 0 + case _ => false + } + + override def clone() = new ItemStackWrapper(inner) + + override def toString = inner.toString +} diff --git a/src/main/scala/li/cil/oc/util/ItemUtils.scala b/src/main/scala/li/cil/oc/util/ItemUtils.scala index 65e3b07f0..4781af358 100644 --- a/src/main/scala/li/cil/oc/util/ItemUtils.scala +++ b/src/main/scala/li/cil/oc/util/ItemUtils.scala @@ -4,17 +4,10 @@ import java.io.ByteArrayInputStream import java.io.ByteArrayOutputStream import java.util.Random -import com.google.common.base.Charsets -import com.google.common.base.Strings import li.cil.oc.OpenComputers -import li.cil.oc.Settings import li.cil.oc.api -import li.cil.oc.api.Persistable import li.cil.oc.common.Tier -import li.cil.oc.integration.opencomputers.DriverScreen -import li.cil.oc.util.ExtendedNBT._ import net.minecraft.item.ItemBucket -import net.minecraft.item.ItemMap import net.minecraft.item.ItemStack import net.minecraft.item.crafting.CraftingManager import net.minecraft.item.crafting.IRecipe @@ -22,14 +15,11 @@ import net.minecraft.item.crafting.ShapedRecipes import net.minecraft.item.crafting.ShapelessRecipes import net.minecraft.nbt.CompressedStreamTools import net.minecraft.nbt.NBTTagCompound -import net.minecraft.world.World -import net.minecraftforge.common.util.Constants.NBT import net.minecraftforge.oredict.ShapedOreRecipe import net.minecraftforge.oredict.ShapelessOreRecipe import scala.collection.convert.WrapAsScala._ import scala.collection.mutable -import scala.io.Source object ItemUtils { def caseTier(stack: ItemStack) = { @@ -128,246 +118,4 @@ object ItemUtils { case list: java.util.ArrayList[ItemStack]@unchecked if !list.isEmpty => list.get(rng.nextInt(list.size)) } - abstract class ItemData extends Persistable { - def load(stack: ItemStack) { - if (stack.hasTagCompound) { - // Because ItemStack's load function doesn't copy the compound tag, - // but keeps it as is, leading to oh so fun bugs! - load(stack.getTagCompound.copy().asInstanceOf[NBTTagCompound]) - } - } - - def save(stack: ItemStack) { - if (!stack.hasTagCompound) { - stack.setTagCompound(new NBTTagCompound()) - } - save(stack.getTagCompound) - } - } - - class MicrocontrollerData extends ItemData { - def this(stack: ItemStack) { - this() - load(stack) - } - - var tier = Tier.One - - var components = Array.empty[ItemStack] - - var storedEnergy = 0 - - override def load(nbt: NBTTagCompound) { - tier = nbt.getByte(Settings.namespace + "tier") - components = nbt.getTagList(Settings.namespace + "components", NBT.TAG_COMPOUND). - toArray[NBTTagCompound].map(loadStack) - storedEnergy = nbt.getInteger(Settings.namespace + "storedEnergy") - } - - override def save(nbt: NBTTagCompound) { - nbt.setByte(Settings.namespace + "tier", tier.toByte) - nbt.setNewTagList(Settings.namespace + "components", components.toIterable) - nbt.setInteger(Settings.namespace + "storedEnergy", storedEnergy) - } - - def createItemStack() = { - val stack = api.Items.get("microcontroller").createItemStack(1) - save(stack) - stack - } - - def copyItemStack() = { - val stack = createItemStack() - // Forget all node addresses and so on. This is used when 'picking' a - // microcontroller in creative mode. - val newInfo = new MicrocontrollerData(stack) - newInfo.components.foreach(cs => Option(api.Driver.driverFor(cs)) match { - case Some(driver) if driver == DriverScreen => - val nbt = driver.dataTag(cs) - for (tagName <- nbt.getKeySet.toArray) { - nbt.removeTag(tagName.asInstanceOf[String]) - } - case _ => - }) - newInfo.save(stack) - stack - } - } - - class NavigationUpgradeData extends ItemData { - def this(stack: ItemStack) { - this() - load(stack) - } - - var map = new ItemStack(net.minecraft.init.Items.filled_map) - - def mapData(world: World) = try map.getItem.asInstanceOf[ItemMap].getMapData(map, world) catch { - case _: Throwable => throw new Exception("invalid map") - } - - override def load(stack: ItemStack) { - if (stack.hasTagCompound) { - load(stack.getTagCompound.getCompoundTag(Settings.namespace + "data")) - } - } - - override def save(stack: ItemStack) { - if (!stack.hasTagCompound) { - stack.setTagCompound(new NBTTagCompound()) - } - save(stack.getCompoundTag(Settings.namespace + "data")) - } - - override def load(nbt: NBTTagCompound) { - if (nbt.hasKey(Settings.namespace + "map")) { - map = loadStack(nbt.getCompoundTag(Settings.namespace + "map")) - } - } - - override def save(nbt: NBTTagCompound) { - if (map != null) { - nbt.setNewCompoundTag(Settings.namespace + "map", map.writeToNBT) - } - } - } - - class RobotData extends ItemData { - def this(stack: ItemStack) { - this() - load(stack) - } - - var name = "" - - // Overall energy including components. - var totalEnergy = 0 - - // Energy purely stored in robot component - this is what we have to restore manually. - var robotEnergy = 0 - - var tier = 0 - - var components = Array.empty[ItemStack] - - var containers = Array.empty[ItemStack] - - var lightColor = 0xF23030 - - override def load(nbt: NBTTagCompound) { - if (nbt.hasKey("display") && nbt.getCompoundTag("display").hasKey("Name")) { - name = nbt.getCompoundTag("display").getString("Name") - } - if (Strings.isNullOrEmpty(name)) { - name = RobotData.randomName - } - totalEnergy = nbt.getInteger(Settings.namespace + "storedEnergy") - robotEnergy = nbt.getInteger(Settings.namespace + "robotEnergy") - tier = nbt.getInteger(Settings.namespace + "tier") - components = nbt.getTagList(Settings.namespace + "components", NBT.TAG_COMPOUND). - toArray[NBTTagCompound].map(loadStack) - containers = nbt.getTagList(Settings.namespace + "containers", NBT.TAG_COMPOUND). - toArray[NBTTagCompound].map(loadStack) - if (nbt.hasKey(Settings.namespace + "lightColor")) { - lightColor = nbt.getInteger(Settings.namespace + "lightColor") - } - } - - override def save(nbt: NBTTagCompound) { - if (!Strings.isNullOrEmpty(name)) { - if (!nbt.hasKey("display")) { - nbt.setTag("display", new NBTTagCompound()) - } - nbt.getCompoundTag("display").setString("Name", name) - } - nbt.setInteger(Settings.namespace + "storedEnergy", totalEnergy) - nbt.setInteger(Settings.namespace + "robotEnergy", robotEnergy) - nbt.setInteger(Settings.namespace + "tier", tier) - nbt.setNewTagList(Settings.namespace + "components", components.toIterable) - nbt.setNewTagList(Settings.namespace + "containers", containers.toIterable) - nbt.setInteger(Settings.namespace + "lightColor", lightColor) - } - - def createItemStack() = { - val stack = api.Items.get("robot").createItemStack(1) - save(stack) - stack - } - - def copyItemStack() = { - val stack = createItemStack() - // Forget all node addresses and so on. This is used when 'picking' a - // robot in creative mode. - val newInfo = new RobotData(stack) - newInfo.components.foreach(cs => Option(api.Driver.driverFor(cs)) match { - case Some(driver) if driver == DriverScreen => - val nbt = driver.dataTag(cs) - for (tagName <- nbt.getKeySet.toArray) { - nbt.removeTag(tagName.asInstanceOf[String]) - } - case _ => - }) - // Don't show energy info (because it's unreliable) but fill up the - // internal buffer. This is for creative use only, anyway. - newInfo.totalEnergy = 0 - newInfo.robotEnergy = 50000 - newInfo.save(stack) - stack - } - } - - object RobotData { - val names = try { - Source.fromInputStream(getClass.getResourceAsStream( - "/assets/" + Settings.resourceDomain + "/robot.names"))(Charsets.UTF_8). - getLines().map(_.takeWhile(_ != '#').trim()).filter(_ != "").toArray - } - catch { - case t: Throwable => - OpenComputers.log.warn("Failed loading robot name list.", t) - Array.empty[String] - } - - def randomName = if (names.length > 0) names((math.random * names.length).toInt) else "Robot" - } - - class TabletData extends ItemData { - def this(stack: ItemStack) { - this() - load(stack) - } - - var items = Array.fill[Option[ItemStack]](32)(None) - var isRunning = false - var energy = 0.0 - var maxEnergy = 0.0 - - override def load(nbt: NBTTagCompound) { - nbt.getTagList(Settings.namespace + "items", NBT.TAG_COMPOUND).foreach((slotNbt: NBTTagCompound) => { - val slot = slotNbt.getByte("slot") - if (slot >= 0 && slot < items.length) { - items(slot) = Option(loadStack(slotNbt.getCompoundTag("item"))) - } - }) - isRunning = nbt.getBoolean(Settings.namespace + "isRunning") - energy = nbt.getDouble(Settings.namespace + "energy") - maxEnergy = nbt.getDouble(Settings.namespace + "maxEnergy") - } - - override def save(nbt: NBTTagCompound) { - nbt.setNewTagList(Settings.namespace + "items", - items.zipWithIndex collect { - case (Some(stack), slot) => (stack, slot) - } map { - case (stack, slot) => - val slotNbt = new NBTTagCompound() - slotNbt.setByte("slot", slot.toByte) - slotNbt.setNewCompoundTag("item", stack.writeToNBT) - }) - nbt.setBoolean(Settings.namespace + "isRunning", isRunning) - nbt.setDouble(Settings.namespace + "energy", energy) - nbt.setDouble(Settings.namespace + "maxEnergy", maxEnergy) - } - } - } diff --git a/src/main/scala/li/cil/oc/util/PackedColor.scala b/src/main/scala/li/cil/oc/util/PackedColor.scala index 668a077ae..8496eb867 100644 --- a/src/main/scala/li/cil/oc/util/PackedColor.scala +++ b/src/main/scala/li/cil/oc/util/PackedColor.scala @@ -165,16 +165,16 @@ object PackedColor { case class Color(value: Int, isPalette: Boolean = false) // Colors are packed: 0xFFBB (F = foreground, B = background) - private val fgShift = 8 - private val bgMask = 0x000000FF + val ForegroundShift = 8 + val BackgroundMask = 0x000000FF def pack(foreground: Color, background: Color, format: ColorFormat) = { - (((format.deflate(foreground) & 0xFF) << fgShift) | (format.deflate(background) & 0xFF)).toShort + (((format.deflate(foreground) & 0xFF) << ForegroundShift) | (format.deflate(background) & 0xFF)).toShort } - def extractForeground(color: Short) = (color & 0xFFFF) >>> fgShift + def extractForeground(color: Short) = (color & 0xFFFF) >>> ForegroundShift - def extractBackground(color: Short) = color & bgMask + def extractBackground(color: Short) = color & BackgroundMask def unpackForeground(color: Short, format: ColorFormat) = format.inflate(extractForeground(color))