diff --git a/build.gradle b/build.gradle index 9ab34a6ce..042f39c1d 100644 --- a/build.gradle +++ b/build.gradle @@ -164,6 +164,10 @@ repositories { name 'ExtraCells' artifactPattern "http://addons-origin.cursecdn.com/files/${config.ec.cf}/[module]-[revision].[ext]" } + ivy { + name 'ThaumicEnergistics' + artifactPattern "http://addons-origin.cursecdn.com/files/${config.thaumicenergistics.cf}/[module]-[revision].[ext]" + } */ } @@ -214,6 +218,7 @@ dependencies { provided name: 'Railcraft', version: config.rc.version, ext: 'jar' provided name: 'BloodMagic', version: config.bloodmagic.version, ext: 'jar' provided name: 'ExtraCells', version: config.ec.version, ext: 'jar' + provided name: 'ThaumicEnergistics', version: config.thaumicenergistics.version, ext: 'jar' provided "cyano.poweradvantage:PowerAdvantage-API:${config.poweradvantage.version}" */ diff --git a/build.properties b/build.properties index e3fd34946..dd7f62bfd 100644 --- a/build.properties +++ b/build.properties @@ -43,6 +43,8 @@ rc.cf=2219/321 rc.version=1.7.10-9.4.0.0 redlogic.version=59.0.3 rotc.version=V5c +thaumicenergistics.cf=2277/520 +thaumicenergistics.version=1.0.0.1-RV2 tis3d.version=MC1.8.9-0.8.0.2 tmech.version=75.0afb56c re.version=3.0.0.342 diff --git a/src/main/java/li/cil/oc/api/Driver.java b/src/main/java/li/cil/oc/api/Driver.java index ba795fea7..4d220b1c5 100644 --- a/src/main/java/li/cil/oc/api/Driver.java +++ b/src/main/java/li/cil/oc/api/Driver.java @@ -5,11 +5,13 @@ import li.cil.oc.api.driver.Converter; import li.cil.oc.api.driver.EnvironmentProvider; import li.cil.oc.api.driver.InventoryProvider; import li.cil.oc.api.driver.Item; +import li.cil.oc.api.driver.SidedBlock; import li.cil.oc.api.network.EnvironmentHost; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.inventory.IInventory; import net.minecraft.item.ItemStack; import net.minecraft.util.BlockPos; +import net.minecraft.util.EnumFacing; import net.minecraft.world.World; import java.util.Collection; @@ -28,7 +30,7 @@ import java.util.Collection; * at that time. Only start calling these methods in the init phase or later. * * @see Network - * @see Block + * @see SidedBlock * @see Item */ public final class Driver { @@ -43,12 +45,31 @@ public final class Driver { * phases. * * @param driver the driver to register. + * @deprecated Use {@link SidedBlock} instead. */ + @Deprecated // TODO Remove in OC 1.7 public static void add(final Block driver) { if (API.driver != null) API.driver.add(driver); } + /** + * Registers a new side-aware block driver. + *

+ * Whenever the neighboring blocks of an Adapter block change, it checks if + * there exists a driver for the changed block, and if it is configured to + * interface that block type connects it to the component network. + *

+ * This must be called in the init phase, not the pre- or post-init + * phases. + * + * @param driver the driver to register. + */ + public static void add(final SidedBlock driver) { + if (API.driver != null) + API.driver.add(driver); + } + /** * Registers a new item driver. *

@@ -121,13 +142,35 @@ public final class Driver { * @param world the world containing the block. * @param pos the position of the block. * @return a driver for the block, or null if there is none. + * @deprecated Use {@link #driverFor(World, BlockPos, EnumFacing)}, + * passing null if the side is to be ignored. */ + @Deprecated // TODO Remove in OC 1.7 public static Block driverFor(World world, BlockPos pos) { if (API.driver != null) return API.driver.driverFor(world, pos); return null; } + /** + * Looks up a driver for the block at the specified position in the + * specified world. + *

+ * Note that several drivers for a single block can exist. Because of this + * block drivers are always encapsulated in a 'compound' driver, which is + * what will be returned here. In other words, you should will not + * get actual instances of drivers registered via {@link #add(li.cil.oc.api.driver.Block)}. + * + * @param world the world containing the block. + * @param pos the position of the block. + * @return a driver for the block, or null if there is none. + */ + public static SidedBlock driverFor(World world, BlockPos pos, EnumFacing side) { + if (API.driver != null) + return API.driver.driverFor(world, pos, side); + return null; + } + /** * Looks up a driver for the specified item stack. *

diff --git a/src/main/java/li/cil/oc/api/detail/DriverAPI.java b/src/main/java/li/cil/oc/api/detail/DriverAPI.java index 89c7a8c46..5bd02475a 100644 --- a/src/main/java/li/cil/oc/api/detail/DriverAPI.java +++ b/src/main/java/li/cil/oc/api/detail/DriverAPI.java @@ -5,11 +5,13 @@ import li.cil.oc.api.driver.Converter; import li.cil.oc.api.driver.EnvironmentProvider; import li.cil.oc.api.driver.InventoryProvider; import li.cil.oc.api.driver.Item; +import li.cil.oc.api.driver.SidedBlock; import li.cil.oc.api.network.EnvironmentHost; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.inventory.IInventory; import net.minecraft.item.ItemStack; import net.minecraft.util.BlockPos; +import net.minecraft.util.EnumFacing; import net.minecraft.world.World; import java.util.Collection; @@ -26,9 +28,25 @@ public interface DriverAPI { * phases. * * @param driver the driver for a block component. + * @deprecated Use {@link SidedBlock} instead. */ + @Deprecated // TODO Remove in OC 1.7 void add(Block driver); + /** + * Registers a new side-aware block driver. + *

+ * Whenever the neighboring blocks of an Adapter block change, it checks if + * there exists a driver for the changed block, and if it is configured to + * interface that block type connects it to the component network. + *

+ * This must be called in the init phase, not the pre- or post-init + * phases. + * + * @param driver the driver to register. + */ + void add(SidedBlock driver); + /** * Registers a new driver for an item component. *

@@ -89,9 +107,28 @@ public interface DriverAPI { * @param world the world containing the block. * @param pos the position of the block. * @return a driver for the block, or null if there is none. + * @deprecated Use {@link #driverFor(World, BlockPos, EnumFacing)}, + * passing null if the side is to be ignored. */ + @Deprecated // TODO Remove in OC 1.7 Block driverFor(World world, BlockPos pos); + /** + * Looks up a driver for the block at the specified position in the + * specified world. + *

+ * Note that several drivers for a single block can exist. Because of this + * block drivers are always encapsulated in a 'compound' driver, which is + * what will be returned here. In other words, you should will not + * get actual instances of drivers registered via {@link #add(li.cil.oc.api.driver.Block)}. + * + * @param world the world containing the block. + * @param pos the position of the block. + * @param side the side of the block. + * @return a driver for the block, or null if there is none. + */ + SidedBlock driverFor(World world, BlockPos pos, EnumFacing side); + /** * Looks up a driver for the specified item stack. *

diff --git a/src/main/java/li/cil/oc/api/driver/Block.java b/src/main/java/li/cil/oc/api/driver/Block.java index f9ed7c21f..d58c2aa45 100644 --- a/src/main/java/li/cil/oc/api/driver/Block.java +++ b/src/main/java/li/cil/oc/api/driver/Block.java @@ -21,7 +21,11 @@ import net.minecraft.world.World; * Note that it is possible to write one driver that supports as many different * blocks as you wish. I'd recommend writing one per device (type), though, to * keep things modular. + * + * @deprecated Use {@link SidedBlock} instead, ignoring the side argument if + * the side doesn't matter. */ +@Deprecated // TODO Remove in OC 1.7 public interface Block { /** * Used to determine the block types this driver handles. diff --git a/src/main/java/li/cil/oc/api/driver/SidedBlock.java b/src/main/java/li/cil/oc/api/driver/SidedBlock.java new file mode 100644 index 000000000..1dec06d5d --- /dev/null +++ b/src/main/java/li/cil/oc/api/driver/SidedBlock.java @@ -0,0 +1,71 @@ +package li.cil.oc.api.driver; + +import li.cil.oc.api.network.ManagedEnvironment; +import net.minecraft.util.BlockPos; +import net.minecraft.util.EnumFacing; +import net.minecraft.world.World; + +/** + * Interface for side-aware block component drivers. + *

+ * This driver type is used for components that are blocks, i.e. that can be + * placed in the world, but cannot be modified to or don't want to have their + * {@link net.minecraft.tileentity.TileEntity} implement one of the interfaces + * for environments ({@link li.cil.oc.api.network.Environment} or + * {@link li.cil.oc.api.network.SidedEnvironment}). + *

+ * A block driver is used by Adapter blocks to check its neighbors and + * whether those neighbors should be treated as components or not. If a driver + * is present, it will be used to create a {@link ManagedEnvironment} that is + * managed by the adapter. + *

+ * Note that it is possible to write one driver that supports as many different + * blocks as you wish. I'd recommend writing one per device (type), though, to + * keep things modular. + *

+ * Note that side-aware block drivers are queried before regular block drivers, + * because they are more specific. + */ +public interface SidedBlock { + /** + * Used to determine the block types this driver handles. + *

+ * This is used to determine which driver to use for a block placed next to + * an Adapter block. Note that the return value should not change + * over time; if it does, though, an already installed component will not + * be removed, since this value is only checked when scanning blocks. You + * can force this by sending a neighbor block change notification. + *

+ * The side is relative to the block, i.e. "south" is the side of the block + * facing south. + * + * @param world the world in which the block to check lives. + * @param pos the position coordinate of the block to check. + * @param side the side of the block to check. + * @return true if the block is supported; false otherwise. + */ + boolean worksWith(World world, BlockPos pos, EnumFacing side); + + /** + * Create a new managed environment interfacing the specified block. + *

+ * This is used to connect the component to the component network when it + * is detected next to an Adapter. Components that are not part of + * the component network probably don't make much sense (can't think of any + * uses at this time), but you may still opt to not implement this - i.e. + * it is safe to return null here. + *

+ * This is expected to return a new instance each time it is + * called. The created instance's life cycle is managed by the + * Adapter block that caused its creation. + *

+ * The side is relative to the block, i.e. "south" is the side of the block + * facing south. + * + * @param world the world containing the block to get the environment for. + * @param pos the position coordinate of the block to check. + * @param side the side of the block to check. + * @return the environment for the block at that location. + */ + ManagedEnvironment createEnvironment(World world, BlockPos pos, EnumFacing side); +} \ No newline at end of file diff --git a/src/main/resources/application.conf b/src/main/resources/application.conf index a30cfcc45..3dff4bf38 100644 --- a/src/main/resources/application.conf +++ b/src/main/resources/application.conf @@ -1337,7 +1337,8 @@ opencomputers { # computers (such as magic related mods, which is why Thaumcraft is on this # list by default.) modBlacklist: [ - "Thaumcraft" + "Thaumcraft", + "thaumicenergistics" ] # A list of tile entities by class name that should NOT be accessible via diff --git a/src/main/resources/assets/opencomputers/doc/en_US/item/hoverBoots.md b/src/main/resources/assets/opencomputers/doc/en_US/item/hoverBoots.md index 444dca718..8ff8a375d 100644 --- a/src/main/resources/assets/opencomputers/doc/en_US/item/hoverBoots.md +++ b/src/main/resources/assets/opencomputers/doc/en_US/item/hoverBoots.md @@ -9,3 +9,5 @@ Either way, these boots have a few useful properties: as long as they have power Additionally, due to always being in kind of a hovering state anyway, they allow you to seamlessly walk up steps of up to one block height. This is particularly handy when sprinting up a mountain, for example, a very commonplace fitness routine amongst Minecrafters. Or so I hear. The boots can be recharged in an OpenComputers [charger](../block/charger.md) or any other such device, like the Applied Energistics 2 charger, an IndustrialCraft 2 battery box, or the Energetic Infuser from Thermal Expansion. + +You can change the boots' color by crafting them with any type of dye. Akin to leather armor, it is possible to apply multiple dyes at once or in succession to create a large variety of colors. The dye can be removed again either by crafting the boots with a bucket of water or by tossing them into a cauldron. diff --git a/src/main/resources/assets/opencomputers/loot/OpenOS/bin/cp.lua b/src/main/resources/assets/opencomputers/loot/OpenOS/bin/cp.lua index ebca97702..bbeed1f81 100644 --- a/src/main/resources/assets/opencomputers/loot/OpenOS/bin/cp.lua +++ b/src/main/resources/assets/opencomputers/loot/OpenOS/bin/cp.lua @@ -59,6 +59,11 @@ local function areEqual(path1, path2) return result end +local mounts = {} +for dev,path in fs.mounts() do + mounts[fs.canonical(path)] = dev +end + local function recurse(fromPath, toPath, origin) status(fromPath, toPath) if fs.isDirectory(fromPath) then @@ -66,17 +71,17 @@ local function recurse(fromPath, toPath, origin) io.write("omitting directory `" .. fromPath .. "'\n") return true end - if fs.canonical(fs.path(toPath)):find(fs.canonical(fromPath),1,true) then - return nil, "cannot copy a directory, `" .. fromPath .. "', into itself, `" .. toPath .. "'" - end if fs.exists(toPath) and not fs.isDirectory(toPath) then -- my real cp always does this, even with -f, -n or -i. return nil, "cannot overwrite non-directory `" .. toPath .. "' with directory `" .. fromPath .. "'" end - fs.makeDirectory(toPath) - if options.x and origin and fs.get(fromPath) ~= origin then + if options.x and origin and mounts[fs.canonical(fromPath)] then return true end + if fs.get(fromPath) == fs.get(toPath) and fs.canonical(fs.path(toPath)):find(fs.canonical(fromPath),1,true) then + return nil, "cannot copy a directory, `" .. fromPath .. "', into itself, `" .. toPath .. "'" + end + fs.makeDirectory(toPath) for file in fs.list(fromPath) do local result, reason = recurse(fs.concat(fromPath, file), fs.concat(toPath, file), origin or fs.get(fromPath)) if not result then diff --git a/src/main/resources/assets/opencomputers/loot/OpenOS/bin/install.lua b/src/main/resources/assets/opencomputers/loot/OpenOS/bin/install.lua index 95ae88c66..940eddbd3 100644 --- a/src/main/resources/assets/opencomputers/loot/OpenOS/bin/install.lua +++ b/src/main/resources/assets/opencomputers/loot/OpenOS/bin/install.lua @@ -60,7 +60,7 @@ io.write("Installing " .. name .." to device " .. (choice.getLabel() or choice.a os.sleep(0.25) local cpPath = filesystem.concat(findMount(filesystem.get(os.getenv("_")).address), "bin/cp") local cpOptions = "-vrx" .. (options.u and "ui " or "") -local cpSource = filesystem.concat(findMount(fromAddress), options.fromDir or "/", "*") +local cpSource = filesystem.concat(findMount(fromAddress), options.fromDir or "/") local cpDest = findMount(choice.address) .. "/" local result, reason = os.execute(cpPath .. " " .. cpOptions .. " " .. cpSource .. " " .. cpDest) if not result then diff --git a/src/main/resources/assets/opencomputers/loot/OpenOS/lib/text.lua b/src/main/resources/assets/opencomputers/loot/OpenOS/lib/text.lua index 163afe255..3e5839bcf 100644 --- a/src/main/resources/assets/opencomputers/loot/OpenOS/lib/text.lua +++ b/src/main/resources/assets/opencomputers/loot/OpenOS/lib/text.lua @@ -118,31 +118,42 @@ function text.removeEscapes(txt) end ------------------------------------------------------------------------------- - -function --[[@delayloaded-start@]] text.split(input, delimiters, dropDelims) +function --[[@delayloaded-start@]] text.split(input, delimiters, dropDelims, di) checkArg(1, input, "string") checkArg(2, delimiters, "table") checkArg(3, dropDelims, "boolean", "nil") + checkArg(4, di, "number", "nil") - local input_table = text.internal.table_view(input) - local delim_table = tx.select(delimiters, function(e,i,t) - return text.internal.table_view(e) - end) - local parts = tx.partition(input_table, function(e,i,t) - local ns, ne = tx.first(t,delim_table,i) - if not ns or dropDelims or ns == i then - return ns, ne - else -- not droping delims and ns>i - return i, ns-1 + if #input == 0 then return {} end + di = di or 1 + local result = {input} + if di > #delimiters then return result end + + local function add(part, index, r, s, e) + local sub = part:sub(s,e) + if #sub == 0 then return index end + local subs = r and text.split(sub,delimiters,dropDelims,r) or {sub} + for i=1,#subs do + table.insert(result, index+i-1, subs[i]) end - end, dropDelims) - return tx.select(parts, function(e,i,t) - local inner = '' - tx.foreach(e, function(ee,ii,tt) - inner = inner..ee - end) - return inner - end) + return index+#subs + end + + local i,d=1,delimiters[di] + while true do + local next = table.remove(result,i) + if not next then break end + local si,ei = next:find(d) + if si and ei and ei~=0 then -- delim found + i=add(next, i, di+1, 1, si-1) + i=dropDelims and i or add(next, i, false, si, ei) + i=add(next, i, di, ei+1) + else + i=add(next, i, di+1, 1, #next) + end + end + + return result end --[[@delayloaded-end@]] ----------------------------------------------------------------------------- diff --git a/src/main/resources/assets/opencomputers/textures/items/HoverBootsLight.png b/src/main/resources/assets/opencomputers/textures/items/HoverBootsLight.png new file mode 100644 index 000000000..f23122510 Binary files /dev/null and b/src/main/resources/assets/opencomputers/textures/items/HoverBootsLight.png differ diff --git a/src/main/resources/assets/opencomputers/textures/items/hoverBoots.png b/src/main/resources/assets/opencomputers/textures/items/hoverBoots.png index 0096a1ccd..1a7f2d070 100644 Binary files a/src/main/resources/assets/opencomputers/textures/items/hoverBoots.png and b/src/main/resources/assets/opencomputers/textures/items/hoverBoots.png differ diff --git a/src/main/scala/li/cil/oc/client/renderer/PetRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/PetRenderer.scala index 22cff77ac..7b456bc01 100644 --- a/src/main/scala/li/cil/oc/client/renderer/PetRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/PetRenderer.scala @@ -8,7 +8,6 @@ import li.cil.oc.api.event.RobotRenderEvent import li.cil.oc.client.renderer.tileentity.RobotRenderer import li.cil.oc.util.RenderState import net.minecraft.client.Minecraft -import net.minecraft.client.gui.inventory.GuiContainer import net.minecraft.client.renderer.GlStateManager import net.minecraft.entity.Entity import net.minecraftforge.client.event.RenderPlayerEvent @@ -26,16 +25,16 @@ object PetRenderer { // http://goo.gl/frLWYR private val entitledPlayers = Map( - "Sangar" ->(0.3, 0.9, 0.6), - "Jodarion" ->(1.0, 0.0, 0.0), - "DaKaTotal" ->(0.5, 0.7, 1.0), - "MichiRavencroft" ->(1.0, 0.0, 0.0), - "Vexatos" ->(0.18, 0.95, 0.922), - "StoneNomad" ->(0.8, 0.77, 0.75), - "LizzyTheSiren" ->(0.3, 0.3, 1.0), - "vifino" ->(0.2, 1.0, 0.1), - "Izaya" ->(0.0, 0.2, 0.6), - "Wobbo" ->(0.098, 0.471, 0.784) + "9f1f262f-0d68-4e13-9161-9eeaf4a0a1a8" ->(0.3, 0.9, 0.6), // Sangar + "18f8bed4-f027-44af-8947-6a3a2317645a" ->(1.0, 0.0, 0.0), // Jodarion + "36123742-2cf6-4cfc-8b65-278581b3caeb" ->(0.5, 0.7, 1.0), // DaKaTotal + "2c0c214b-96f4-4565-b513-de90d5fbc977" ->(1.0, 0.0, 0.0), // MichiRavencroft + "f3ba6ec8-c280-4950-bb08-1fcb2eab3a9c" ->(0.18, 0.95, 0.922), // Vexatos + "9d636bdd-b9f4-4b80-b9ce-586ca04bd4f3" ->(0.8, 0.77, 0.75), // StoneNomad + "23c7ed71-fb13-4abe-abe7-f355e1de6e62" ->(0.3, 0.3, 1.0), // LizzyTheSiren + "076541f1-f10a-46de-a127-dfab8adfbb75" ->(0.2, 1.0, 0.1), // vifino + "e7e90198-0ccf-4662-a827-192ec8f4419d" ->(0.0, 0.2, 0.6), // Izaya + "f514ee69-7bbb-4e46-9e94-d8176324cec2" ->(0.098, 0.471, 0.784) // Wobbo ) private val petLocations = com.google.common.cache.CacheBuilder.newBuilder(). @@ -47,9 +46,9 @@ object PetRenderer { @SubscribeEvent def onPlayerRender(e: RenderPlayerEvent.Pre) { - val name = e.entityPlayer.getName - if (hidden.contains(name) || !entitledPlayers.contains(name)) return - rendering = Some(entitledPlayers(name)) + val uuid = e.entityPlayer.getUniqueID.toString + if (hidden.contains(uuid) || !entitledPlayers.contains(uuid)) return + rendering = Some(entitledPlayers(uuid)) val worldTime = e.entityPlayer.getEntityWorld.getTotalWorldTime val timeJitter = e.entityPlayer.hashCode ^ 0xFF @@ -144,8 +143,7 @@ object PetRenderer { GlStateManager.translate(0.3, -0.1, -0.2) } - // Someone please tell me a cleaner solution than this... - private def isForInventory = new Exception().getStackTrace.exists(_.getClassName == classOf[GuiContainer].getName) + private def isForInventory = Minecraft.getMinecraft.currentScreen != null && owner == Minecraft.getMinecraft.thePlayer } @SubscribeEvent diff --git a/src/main/scala/li/cil/oc/client/renderer/item/HoverBootRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/item/HoverBootRenderer.scala index f05e4c90e..c5d67782e 100644 --- a/src/main/scala/li/cil/oc/client/renderer/item/HoverBootRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/item/HoverBootRenderer.scala @@ -91,6 +91,8 @@ object HoverBootRenderer extends ModelBiped { bipedRightArm.isHidden = true bipedLeftArm.isHidden = true + var lightColor = 0x66DD55 + override def render(entity: Entity, f0: Float, f1: Float, f2: Float, f3: Float, f4: Float, f5: Float): Unit = { // Because Forge is being a dummy... isSneak = entity.isSneaking @@ -103,10 +105,12 @@ object HoverBootRenderer extends ModelBiped { override def render(dt: Float): Unit = { GlStateManager.pushAttrib() GlStateManager.disableLighting() - RenderHelper.disableStandardItemLighting() GlStateManager.depthFunc(GL11.GL_LEQUAL) GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE) - GlStateManager.color(0x66 / 255f, 0xDD / 255f, 0x55 / 255f) + val r = ((lightColor >>> 16) & 0xFF) / 255f + val g = ((lightColor >>> 8) & 0xFF) / 255f + val b = ((lightColor >>> 0) & 0xFF) / 255f + GlStateManager.color(r, g, b) super.render(dt) diff --git a/src/main/scala/li/cil/oc/common/item/HoverBoots.scala b/src/main/scala/li/cil/oc/common/item/HoverBoots.scala index babaf66da..48ad8b351 100644 --- a/src/main/scala/li/cil/oc/common/item/HoverBoots.scala +++ b/src/main/scala/li/cil/oc/common/item/HoverBoots.scala @@ -3,10 +3,14 @@ package li.cil.oc.common.item import li.cil.oc.Settings import li.cil.oc.client.renderer.item.HoverBootRenderer import li.cil.oc.common.item.data.HoverBootsData +import li.cil.oc.util.ItemColorizer +import net.minecraft.block.BlockCauldron import net.minecraft.client.model.ModelBiped import net.minecraft.entity.Entity import net.minecraft.entity.EntityLivingBase +import net.minecraft.entity.item.EntityItem import net.minecraft.entity.player.EntityPlayer +import net.minecraft.init.Blocks import net.minecraft.item.EnumRarity import net.minecraft.item.ItemArmor import net.minecraft.item.ItemStack @@ -56,7 +60,10 @@ class HoverBoots extends ItemArmor(ItemArmor.ArmorMaterial.DIAMOND, 0, 3) with t @SideOnly(Side.CLIENT) override def getArmorModel(entityLiving: EntityLivingBase, itemStack: ItemStack, armorSlot: Int): ModelBiped = { - if (armorSlot == 4 - armorType) HoverBootRenderer + if (armorSlot == 4 - armorType) { + HoverBootRenderer.lightColor = if (ItemColorizer.hasColor(itemStack)) ItemColorizer.getColor(itemStack) else 0x66DD55 + HoverBootRenderer + } else super.getArmorModel(entityLiving, itemStack, armorSlot) } @@ -77,6 +84,29 @@ class HoverBoots extends ItemArmor(ItemArmor.ArmorMaterial.DIAMOND, 0, 3) with t } } + override def onEntityItemUpdate(entity: EntityItem): Boolean = { + if (entity != null && entity.worldObj != null && !entity.worldObj.isRemote && ItemColorizer.hasColor(entity.getEntityItem)) { + val pos = entity.getPosition + val state = entity.worldObj.getBlockState(pos) + if (state.getBlock == Blocks.cauldron) { + val level = state.getValue(BlockCauldron.LEVEL).toInt + if (level > 0) { + ItemColorizer.removeColor(entity.getEntityItem) + entity.worldObj.setBlockState(pos, state.withProperty(BlockCauldron.LEVEL, Int.box(level - 1)), 3) + return true + } + } + } + super.onEntityItemUpdate(entity) + } + + override def getColorFromItemStack(itemStack: ItemStack, pass: Int): Int = { + if (pass == 1) { + return if (ItemColorizer.hasColor(itemStack)) ItemColorizer.getColor(itemStack) else 0x66DD55 + } + super.getColorFromItemStack(itemStack, pass) + } + override def showDurabilityBar(stack: ItemStack): Boolean = true override def getDurabilityForDisplay(stack: ItemStack): Double = { diff --git a/src/main/scala/li/cil/oc/common/recipe/ColorizeRecipe.scala b/src/main/scala/li/cil/oc/common/recipe/ColorizeRecipe.scala new file mode 100644 index 000000000..ead6dc432 --- /dev/null +++ b/src/main/scala/li/cil/oc/common/recipe/ColorizeRecipe.scala @@ -0,0 +1,86 @@ +package li.cil.oc.common.recipe + +import li.cil.oc.util.Color +import li.cil.oc.util.ItemColorizer +import net.minecraft.entity.passive.EntitySheep +import net.minecraft.inventory.InventoryCrafting +import net.minecraft.item.Item +import net.minecraft.item.ItemStack +import net.minecraft.world.World + +/** + * @author asie, Vexatos + */ +class ColorizeRecipe(target: Item, source: Array[Item] = null) extends ContainerItemAwareRecipe { + val targetItem = target + val sourceItems = if (source != null) source else Array(targetItem) + + override def matches(crafting: InventoryCrafting, world: World): Boolean = { + val stacks = (0 until crafting.getSizeInventory).flatMap(i => Option(crafting.getStackInSlot(i))) + val targets = stacks.filter(stack => sourceItems.contains(stack.getItem) || stack.getItem == targetItem) + val other = stacks.filterNot(targets.contains) + targets.size == 1 && other.nonEmpty && other.forall(Color.isDye) + } + + override def getCraftingResult(crafting: InventoryCrafting): ItemStack = { + var targetStack: ItemStack = null + val color = Array[Int](0, 0, 0) + var colorCount = 0 + var maximum = 0 + + (0 until crafting.getSizeInventory).flatMap(i => Option(crafting.getStackInSlot(i))).foreach { stack => + if (sourceItems.contains(stack.getItem) + || stack.getItem == targetItem) { + targetStack = stack.copy() + targetStack.stackSize = 1 + } else { + val dye = Color.findDye(stack) + if (dye.isEmpty) + return null + + val itemColor = EntitySheep.func_175513_a(Color.byOreName(dye.get)) + val red = (itemColor(0) * 255.0F).toInt + val green = (itemColor(1) * 255.0F).toInt + val blue = (itemColor(2) * 255.0F).toInt + maximum += Math.max(red, Math.max(green, blue)) + color(0) += red + color(1) += green + color(2) += blue + colorCount = colorCount + 1 + } + } + + if (targetStack == null) return null + + if (targetItem == targetStack.getItem) { + if (ItemColorizer.hasColor(targetStack)) { + val itemColor = ItemColorizer.getColor(targetStack) + val red = (itemColor >> 16 & 255).toFloat / 255.0F + val green = (itemColor >> 8 & 255).toFloat / 255.0F + val blue = (itemColor & 255).toFloat / 255.0F + maximum = (maximum.toFloat + Math.max(red, Math.max(green, blue)) * 255.0F).toInt + color(0) = (color(0).toFloat + red * 255.0F).toInt + color(1) = (color(1).toFloat + green * 255.0F).toInt + color(2) = (color(2).toFloat + blue * 255.0F).toInt + colorCount = colorCount + 1 + } + } else if (sourceItems.contains(targetStack.getItem)) { + targetStack = new ItemStack(targetItem, targetStack.stackSize, targetStack.getItemDamage) + } + + var red = color(0) / colorCount + var green = color(1) / colorCount + var blue = color(2) / colorCount + val max = maximum.toFloat / colorCount.toFloat + val div = Math.max(red, Math.max(green, blue)).toFloat + red = (red.toFloat * max / div).toInt + green = (green.toFloat * max / div).toInt + blue = (blue.toFloat * max / div).toInt + ItemColorizer.setColor(targetStack, (red << 16) | (green << 8) | blue) + targetStack + } + + override def getRecipeSize = 10 + + override def getRecipeOutput = null +} diff --git a/src/main/scala/li/cil/oc/common/recipe/ContainerItemAwareRecipe.scala b/src/main/scala/li/cil/oc/common/recipe/ContainerItemAwareRecipe.scala new file mode 100644 index 000000000..f065e11b3 --- /dev/null +++ b/src/main/scala/li/cil/oc/common/recipe/ContainerItemAwareRecipe.scala @@ -0,0 +1,13 @@ +package li.cil.oc.common.recipe + +import net.minecraft.inventory.InventoryCrafting +import net.minecraft.item.crafting.IRecipe + +trait ContainerItemAwareRecipe extends IRecipe { + override def getRemainingItems(inv: InventoryCrafting) = + (0 until inv.getSizeInventory). + map(inv.getStackInSlot). + map(net.minecraftforge.common.ForgeHooks.getContainerItem). + filter(_ != null). + toArray +} diff --git a/src/main/scala/li/cil/oc/common/recipe/DecolorizeRecipe.scala b/src/main/scala/li/cil/oc/common/recipe/DecolorizeRecipe.scala new file mode 100644 index 000000000..871118da9 --- /dev/null +++ b/src/main/scala/li/cil/oc/common/recipe/DecolorizeRecipe.scala @@ -0,0 +1,44 @@ +package li.cil.oc.common.recipe + +import li.cil.oc.util.ItemColorizer +import net.minecraft.init.Items +import net.minecraft.inventory.InventoryCrafting +import net.minecraft.item.Item +import net.minecraft.item.ItemStack +import net.minecraft.world.World + +/** + * @author Vexatos + */ +class DecolorizeRecipe(target: Item) extends ContainerItemAwareRecipe { + val targetItem = target + + override def matches(crafting: InventoryCrafting, world: World): Boolean = { + val stacks = (0 until crafting.getSizeInventory).flatMap(i => Option(crafting.getStackInSlot(i))) + val targets = stacks.filter(stack => stack.getItem == targetItem) + val other = stacks.filterNot(targets.contains) + targets.size == 1 && other.size == 1 && other.forall(_.getItem == Items.water_bucket) + } + + override def getCraftingResult(crafting: InventoryCrafting): ItemStack = { + var targetStack: ItemStack = null + + (0 until crafting.getSizeInventory).flatMap(i => Option(crafting.getStackInSlot(i))).foreach { stack => + if (stack.getItem == targetItem) { + targetStack = stack.copy() + targetStack.stackSize = 1 + } else if (stack.getItem != Items.water_bucket) { + return null + } + } + + if (targetStack == null) return null + + ItemColorizer.removeColor(targetStack) + targetStack + } + + override def getRecipeSize = 10 + + override def getRecipeOutput = null +} diff --git a/src/main/scala/li/cil/oc/common/recipe/Recipes.scala b/src/main/scala/li/cil/oc/common/recipe/Recipes.scala index 3f56c067c..573d4fb3d 100644 --- a/src/main/scala/li/cil/oc/common/recipe/Recipes.scala +++ b/src/main/scala/li/cil/oc/common/recipe/Recipes.scala @@ -102,6 +102,8 @@ object Recipes { def init() { RecipeSorter.register(Settings.namespace + "extshaped", classOf[ExtendedShapedOreRecipe], Category.SHAPED, "after:forge:shapedore") RecipeSorter.register(Settings.namespace + "extshapeless", classOf[ExtendedShapelessOreRecipe], Category.SHAPELESS, "after:forge:shapelessore") + RecipeSorter.register(Settings.namespace + "colorizer", classOf[ColorizeRecipe], Category.SHAPELESS, "after:forge:shapelessore") + RecipeSorter.register(Settings.namespace + "decolorizer", classOf[DecolorizeRecipe], Category.SHAPELESS, "after:oc:colorizer") for ((name, stack) <- oreDictEntries) { if (!OreDictionary.getOres(name).contains(stack)) { @@ -333,6 +335,10 @@ object Recipes { api.Items.get(Constants.BlockName.AccessPoint).createItemStack(1)) GameRegistry.addShapelessRecipe(api.Items.get(Constants.BlockName.Relay).createItemStack(1), api.Items.get(Constants.BlockName.Switch).createItemStack(1)) + + // Hover Boot dyeing + GameRegistry.addRecipe(new ColorizeRecipe(api.Items.get(Constants.ItemName.HoverBoots).item())) + GameRegistry.addRecipe(new DecolorizeRecipe(api.Items.get(Constants.ItemName.HoverBoots).item())) } catch { case e: Throwable => OpenComputers.log.error("Error parsing recipes, you may not be able to craft any items from this mod!", e) diff --git a/src/main/scala/li/cil/oc/integration/Mods.scala b/src/main/scala/li/cil/oc/integration/Mods.scala index 7742ba1bb..cc73c6619 100644 --- a/src/main/scala/li/cil/oc/integration/Mods.scala +++ b/src/main/scala/li/cil/oc/integration/Mods.scala @@ -78,6 +78,7 @@ object Mods { override def isModAvailable: Boolean = isModAvailable_ } val Thaumcraft = new SimpleMod(IDs.Thaumcraft) + val ThaumicEnergistics = new SimpleMod(IDs.ThaumicEnergistics) val ThermalExpansion = new SimpleMod(IDs.ThermalExpansion, providesPower = true) val TinkersConstruct = new SimpleMod(IDs.TinkersConstruct) val TIS3D = new SimpleMod(IDs.TIS3D, version = "@[0.7,)") @@ -125,6 +126,7 @@ object Mods { // integration.rotarycraft.ModRotaryCraft, // integration.stargatetech2.ModStargateTech2, // integration.thaumcraft.ModThaumcraft, + // integration.thaumicenergistics.ModThaumicEnergistics, // integration.thermalexpansion.ModThermalExpansion, integration.tis3d.ModTIS3D, // integration.tcon.ModTinkersConstruct, @@ -219,6 +221,7 @@ object Mods { final val RotaryCraft = "RotaryCraft" final val StargateTech2 = "StargateTech2" final val Thaumcraft = "Thaumcraft" + final val ThaumicEnergistics = "thaumicenergistics" final val ThermalExpansion = "ThermalExpansion" final val TinkersConstruct = "TConstruct" final val TIS3D = "tis3d" diff --git a/src/main/scala/li/cil/oc/integration/thaumicenergistics/DriverBlockInterface.scala b/src/main/scala/li/cil/oc/integration/thaumicenergistics/DriverBlockInterface.scala new file mode 100644 index 000000000..3ee7429c3 --- /dev/null +++ b/src/main/scala/li/cil/oc/integration/thaumicenergistics/DriverBlockInterface.scala @@ -0,0 +1,27 @@ +package li.cil.oc.integration.thaumicenergistics + +import appeng.tile.misc.TileInterface +import li.cil.oc.api.driver.EnvironmentProvider +import li.cil.oc.api.network.ManagedEnvironment +import li.cil.oc.api.prefab.DriverTileEntity +import li.cil.oc.integration.ManagedTileEntityEnvironment +import li.cil.oc.integration.appeng.AEUtil +import net.minecraft.item.ItemStack +import net.minecraft.world.World + +object DriverBlockInterface extends DriverTileEntity { + def getTileEntityClass: Class[_] = classOf[TileInterface] + + def createEnvironment(world: World, x: Int, y: Int, z: Int): ManagedEnvironment = + new Environment(world.getTileEntity(x, y, z).asInstanceOf[TileInterface]) + + final class Environment(val tile: TileInterface) extends ManagedTileEntityEnvironment[TileInterface](tile, "me_interface") with NetworkControl[TileInterface] + + object Provider extends EnvironmentProvider { + override def getEnvironment(stack: ItemStack): Class[_] = + if (AEUtil.isBlockInterface(stack)) + classOf[Environment] + else null + } + +} diff --git a/src/main/scala/li/cil/oc/integration/thaumicenergistics/DriverController.scala b/src/main/scala/li/cil/oc/integration/thaumicenergistics/DriverController.scala new file mode 100644 index 000000000..8a0e90053 --- /dev/null +++ b/src/main/scala/li/cil/oc/integration/thaumicenergistics/DriverController.scala @@ -0,0 +1,33 @@ +package li.cil.oc.integration.thaumicenergistics + +import appeng.api.networking.security.IActionHost +import appeng.me.helpers.IGridProxyable +import li.cil.oc.api.driver.EnvironmentProvider +import li.cil.oc.api.network.ManagedEnvironment +import li.cil.oc.api.prefab.DriverTileEntity +import li.cil.oc.integration.ManagedTileEntityEnvironment +import li.cil.oc.integration.appeng.AEUtil +import net.minecraft.item.ItemStack +import net.minecraft.tileentity.TileEntity +import net.minecraft.world.World + +import scala.language.existentials + +object DriverController extends DriverTileEntity { + private type TileController = TileEntity with IGridProxyable with IActionHost + + def getTileEntityClass = AEUtil.controllerClass + + def createEnvironment(world: World, x: Int, y: Int, z: Int): ManagedEnvironment = + new Environment(world.getTileEntity(x, y, z).asInstanceOf[TileController]) + + final class Environment(val tile: TileController) extends ManagedTileEntityEnvironment[TileController](tile, "me_controller") with NetworkControl[TileController] + + object Provider extends EnvironmentProvider { + override def getEnvironment(stack: ItemStack): Class[_] = + if (AEUtil.isController(stack)) + classOf[Environment] + else null + } + +} diff --git a/src/main/scala/li/cil/oc/integration/thaumicenergistics/ModThaumicEnergistics.scala b/src/main/scala/li/cil/oc/integration/thaumicenergistics/ModThaumicEnergistics.scala new file mode 100644 index 000000000..7eb2cf180 --- /dev/null +++ b/src/main/scala/li/cil/oc/integration/thaumicenergistics/ModThaumicEnergistics.scala @@ -0,0 +1,18 @@ +package li.cil.oc.integration.thaumicenergistics + +import li.cil.oc.api.Driver +import li.cil.oc.integration.Mod +import li.cil.oc.integration.ModProxy +import li.cil.oc.integration.Mods + +object ModThaumicEnergistics extends ModProxy { + override def getMod: Mod = Mods.ThaumicEnergistics + + override def initialize(): Unit = { + Driver.add(DriverController) + Driver.add(DriverBlockInterface) + + Driver.add(DriverController.Provider) + Driver.add(DriverBlockInterface.Provider) + } +} \ No newline at end of file diff --git a/src/main/scala/li/cil/oc/integration/thaumicenergistics/NetworkControl.scala b/src/main/scala/li/cil/oc/integration/thaumicenergistics/NetworkControl.scala new file mode 100644 index 000000000..61a18b252 --- /dev/null +++ b/src/main/scala/li/cil/oc/integration/thaumicenergistics/NetworkControl.scala @@ -0,0 +1,23 @@ +package li.cil.oc.integration.thaumicenergistics + +import appeng.api.networking.security.IActionHost +import appeng.me.helpers.IGridProxyable +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.util.ResultWrapper._ +import net.minecraft.tileentity.TileEntity +import thaumicenergistics.api.IThEEssentiaGas + +import scala.collection.convert.WrapAsScala._ + +// Note to self: this class is used by ExtraCells (and potentially others), do not rename / drastically change it. +trait NetworkControl[AETile >: Null <: TileEntity with IGridProxyable with IActionHost] { + def tile: AETile + + @Callback(doc = "function():table -- Get a list of the stored essentia in the network.") + def getEssentiaInNetwork(context: Context, args: Arguments): Array[AnyRef] = + result(tile.getProxy.getStorage.getFluidInventory.getStorageList.filter(stack => + stack.getFluid != null && stack.getFluid.isInstanceOf[IThEEssentiaGas]). + map(ThaumicEnergisticsUtils.getAspect).toArray) +} diff --git a/src/main/scala/li/cil/oc/integration/thaumicenergistics/ThaumicEnergisticsUtils.scala b/src/main/scala/li/cil/oc/integration/thaumicenergistics/ThaumicEnergisticsUtils.scala new file mode 100644 index 000000000..0f1079c8f --- /dev/null +++ b/src/main/scala/li/cil/oc/integration/thaumicenergistics/ThaumicEnergisticsUtils.scala @@ -0,0 +1,11 @@ +package li.cil.oc.integration.thaumicenergistics + +import appeng.api.storage.data.IAEFluidStack + +object ThaumicEnergisticsUtils { + def getAspect(fluid: IAEFluidStack) = { + val aspect = fluid.getFluidStack.copy() + aspect.amount = (fluid.getStackSize / 128).toInt + aspect + } +} diff --git a/src/main/scala/li/cil/oc/server/component/UpgradeCrafting.scala b/src/main/scala/li/cil/oc/server/component/UpgradeCrafting.scala index 54a890997..33ff61d88 100644 --- a/src/main/scala/li/cil/oc/server/component/UpgradeCrafting.scala +++ b/src/main/scala/li/cil/oc/server/component/UpgradeCrafting.scala @@ -27,7 +27,7 @@ class UpgradeCrafting(val host: EnvironmentHost with internal.Robot) extends pre @Callback(doc = """function([count:number]):number -- Tries to craft the specified number of items in the top left area of the inventory.""") def craft(context: Context, args: Arguments): Array[AnyRef] = { - val count = args.optInteger(0, Int.MaxValue) + val count = args.optInteger(0, 64) max 0 min 64 result(CraftingInventory.craft(count): _*) } diff --git a/src/main/scala/li/cil/oc/server/driver/CompoundBlockDriver.scala b/src/main/scala/li/cil/oc/server/driver/CompoundBlockDriver.scala index 30f9fb3cc..6c8eab122 100644 --- a/src/main/scala/li/cil/oc/server/driver/CompoundBlockDriver.scala +++ b/src/main/scala/li/cil/oc/server/driver/CompoundBlockDriver.scala @@ -9,13 +9,20 @@ import net.minecraft.item.Item import net.minecraft.item.ItemStack import net.minecraft.tileentity.TileEntity import net.minecraft.util.BlockPos +import net.minecraft.util.EnumFacing import net.minecraft.world.World -class CompoundBlockDriver(val blocks: driver.Block*) extends driver.Block { - override def createEnvironment(world: World, pos: BlockPos) = { - val list = blocks.map { +// TODO Remove blocks in OC 1.7. +class CompoundBlockDriver(val sidedBlocks: Array[driver.SidedBlock], val blocks: Array[driver.Block]) extends driver.SidedBlock { + override def createEnvironment(world: World, pos: BlockPos, side: EnumFacing) = { + val list = sidedBlocks.map { + driver => Option(driver.createEnvironment(world, pos, side)) match { + case Some(environment) => (driver.getClass.getName, environment) + case _ => null + } + } ++ blocks.map { driver => Option(driver.createEnvironment(world, pos)) match { - case Some(environment) => (driver, environment) + case Some(environment) => (driver.getClass.getName, environment) case _ => null } } filter (_ != null) @@ -23,10 +30,10 @@ class CompoundBlockDriver(val blocks: driver.Block*) extends driver.Block { else new CompoundBlockEnvironment(cleanName(tryGetName(world, pos, list.map(_._2))), list: _*) } - override def worksWith(world: World, pos: BlockPos) = blocks.forall(_.worksWith(world, pos)) + override def worksWith(world: World, pos: BlockPos, side: EnumFacing) = sidedBlocks.forall(_.worksWith(world, pos, side)) && blocks.forall(_.worksWith(world, pos)) override def equals(obj: Any) = obj match { - case multi: CompoundBlockDriver if multi.blocks.length == blocks.length => blocks.intersect(multi.blocks).length == blocks.length + case multi: CompoundBlockDriver if multi.sidedBlocks.length == sidedBlocks.length && multi.blocks.length == blocks.length => sidedBlocks.intersect(multi.sidedBlocks).length == sidedBlocks.length && blocks.intersect(multi.blocks).length == blocks.length case _ => false } diff --git a/src/main/scala/li/cil/oc/server/driver/CompoundBlockEnvironment.scala b/src/main/scala/li/cil/oc/server/driver/CompoundBlockEnvironment.scala index c45a5846f..09bad3df2 100644 --- a/src/main/scala/li/cil/oc/server/driver/CompoundBlockEnvironment.scala +++ b/src/main/scala/li/cil/oc/server/driver/CompoundBlockEnvironment.scala @@ -5,12 +5,12 @@ import java.nio.charset.Charset import com.google.common.hash.Hashing import li.cil.oc.OpenComputers import li.cil.oc.api -import li.cil.oc.api.driver import li.cil.oc.api.network._ import li.cil.oc.util.ExtendedNBT._ import net.minecraft.nbt.NBTTagCompound -class CompoundBlockEnvironment(val name: String, val environments: (driver.Block, ManagedEnvironment)*) extends ManagedEnvironment { +// TODO Remove block in OC 1.7. +class CompoundBlockEnvironment(val name: String, val environments: (String, ManagedEnvironment)*) extends ManagedEnvironment { // Block drivers with visibility < network usually won't make much sense, // but let's play it safe and use the least possible visibility based on // the drivers we encapsulate. @@ -58,12 +58,11 @@ class CompoundBlockEnvironment(val name: String, val environments: (driver.Block if (nbt.hasKey("typeHash") && nbt.getLong("typeHash") != typeHash) return node.load(nbt) for ((driver, environment) <- environments) { - val name = driver.getClass.getName - if (nbt.hasKey(name)) { + if (nbt.hasKey(driver)) { try { - environment.load(nbt.getCompoundTag(name)) + environment.load(nbt.getCompoundTag(driver)) } catch { - case e: Throwable => OpenComputers.log.warn(s"A block component of type '${environment.getClass.getName}' (provided by driver '$name') threw an error while loading.", e) + case e: Throwable => OpenComputers.log.warn(s"A block component of type '${environment.getClass.getName}' (provided by driver '$driver') threw an error while loading.", e) } } } @@ -73,11 +72,10 @@ class CompoundBlockEnvironment(val name: String, val environments: (driver.Block nbt.setLong("typeHash", typeHash) node.save(nbt) for ((driver, environment) <- environments) { - val name = driver.getClass.getName try { - nbt.setNewCompoundTag(name, environment.save) + nbt.setNewCompoundTag(driver, environment.save) } catch { - case e: Throwable => OpenComputers.log.warn(s"A block component of type '${environment.getClass.getName}' (provided by driver '$name') threw an error while saving.", e) + case e: Throwable => OpenComputers.log.warn(s"A block component of type '${environment.getClass.getName}' (provided by driver '$driver') threw an error while saving.", e) } } } diff --git a/src/main/scala/li/cil/oc/server/driver/Registry.scala b/src/main/scala/li/cil/oc/server/driver/Registry.scala index b55580653..0a30f2383 100644 --- a/src/main/scala/li/cil/oc/server/driver/Registry.scala +++ b/src/main/scala/li/cil/oc/server/driver/Registry.scala @@ -10,10 +10,12 @@ import li.cil.oc.api.driver.InventoryProvider import li.cil.oc.api.driver.item.HostAware import li.cil.oc.api.machine.Value import li.cil.oc.api.network.EnvironmentHost +import li.cil.oc.api.network.ManagedEnvironment import net.minecraft.entity.player.EntityPlayer import net.minecraft.inventory.IInventory import net.minecraft.item.ItemStack import net.minecraft.util.BlockPos +import net.minecraft.util.EnumFacing import net.minecraft.world.World import scala.collection.convert.WrapAsJava._ @@ -38,6 +40,8 @@ import scala.math.ScalaNumber private[oc] object Registry extends api.detail.DriverAPI { val blocks = mutable.ArrayBuffer.empty[api.driver.Block] + val sidedBlocks = mutable.ArrayBuffer.empty[api.driver.SidedBlock] + val items = mutable.ArrayBuffer.empty[api.driver.Item] val converters = mutable.ArrayBuffer.empty[api.driver.Converter] @@ -59,9 +63,17 @@ private[oc] object Registry extends api.detail.DriverAPI { } } + override def add(driver: api.driver.SidedBlock) { + if (locked) throw new IllegalStateException("Please register all drivers in the init phase.") + if (!sidedBlocks.contains(driver)) { + OpenComputers.log.debug(s"Registering block driver ${driver.getClass.getName}.") + sidedBlocks += driver + } + } + override def add(driver: api.driver.Item) { if (locked) throw new IllegalStateException("Please register all drivers in the init phase.") - if (!blocks.contains(driver)) { + if (!items.contains(driver)) { OpenComputers.log.debug(s"Registering item driver ${driver.getClass.getName}.") items += driver } @@ -91,9 +103,21 @@ private[oc] object Registry extends api.detail.DriverAPI { } } - override def driverFor(world: World, pos: BlockPos) = - blocks.filter(_.worksWith(world, pos)) match { - case drivers if drivers.nonEmpty => new CompoundBlockDriver(drivers: _*) + // TODO Remove in OC 1.7 + override def driverFor(world: World, pos: BlockPos) = { + driverFor(world, pos, null) match { + case driver: api.driver.SidedBlock => new api.driver.Block { + override def worksWith(world: World, pos: BlockPos): Boolean = driver.worksWith(world, pos, null) + + override def createEnvironment(world: World, pos: BlockPos): ManagedEnvironment = driver.createEnvironment(world, pos, null) + } + case _ => null + } + } + + override def driverFor(world: World, pos: BlockPos, side: EnumFacing): api.driver.SidedBlock = + (sidedBlocks.filter(_.worksWith(world, pos, side)), blocks.filter(_.worksWith(world, pos))) match { + case (sidedDrivers, drivers) if sidedDrivers.nonEmpty || drivers.nonEmpty => new CompoundBlockDriver(sidedDrivers.toArray, drivers.toArray) case _ => null } diff --git a/src/main/scala/li/cil/oc/util/ItemColorizer.scala b/src/main/scala/li/cil/oc/util/ItemColorizer.scala new file mode 100644 index 000000000..7043d29ce --- /dev/null +++ b/src/main/scala/li/cil/oc/util/ItemColorizer.scala @@ -0,0 +1,47 @@ +package li.cil.oc.util + +import net.minecraft.item.ItemStack +import net.minecraft.nbt.NBTTagCompound + +/** + * @author asie, Vexatos + */ +object ItemColorizer { + /** + * Return whether the specified armor ItemStack has a color. + */ + def hasColor(stack: ItemStack): Boolean = stack.hasTagCompound && stack.getTagCompound.hasKey("display") && stack.getTagCompound.getCompoundTag("display").hasKey("color") + + /** + * Return the color for the specified armor ItemStack. + */ + def getColor(stack: ItemStack): Int = { + val tag = stack.getTagCompound + if (tag != null) { + val displayTag = tag.getCompoundTag("display") + if (displayTag == null) -1 else if (displayTag.hasKey("color")) displayTag.getInteger("color") else -1 + } + else -1 + } + + def removeColor(stack: ItemStack) { + val tag = stack.getTagCompound + if (tag != null) { + val displayTag = tag.getCompoundTag("display") + if (displayTag.hasKey("color")) displayTag.removeTag("color") + } + } + + def setColor(stack: ItemStack, color: Int) { + var tag = stack.getTagCompound + if (tag == null) { + tag = new NBTTagCompound + stack.setTagCompound(tag) + } + val displayTag = tag.getCompoundTag("display") + if (!tag.hasKey("display")) { + tag.setTag("display", displayTag) + } + displayTag.setInteger("color", color) + } +}