Merge branch 'master-MC1.7.10' of github.com:MightyPirates/OpenComputers into master-MC1.8

Conflicts:
	src/main/scala/li/cil/oc/client/PacketHandler.scala
	src/main/scala/li/cil/oc/client/Textures.scala
	src/main/scala/li/cil/oc/client/renderer/block/BlockRenderer.scala
	src/main/scala/li/cil/oc/common/block/Geolyzer.scala
	src/main/scala/li/cil/oc/common/block/NetSplitter.scala
	src/main/scala/li/cil/oc/common/init/Blocks.scala
	src/main/scala/li/cil/oc/server/PacketSender.scala
	src/main/scala/li/cil/oc/server/component/traits/TankWorldControl.scala
	src/main/scala/li/cil/oc/util/ExtendedArguments.scala
This commit is contained in:
Florian Nücke 2015-08-29 02:42:54 +02:00
commit 0a9e950869
25 changed files with 505 additions and 123 deletions

View File

@ -767,6 +767,10 @@ opencomputers {
# Per-byte cost for ECDSA operation is controlled by `complex` value, # Per-byte cost for ECDSA operation is controlled by `complex` value,
# because data is hashed with SHA256 before signing/verifying # because data is hashed with SHA256 before signing/verifying
dataCardAsymmetric: 10.0 dataCardAsymmetric: 10.0
# Energy required for one transposer operation (regardless of the number
# of items / fluid volume moved).
transposer: 1
} }
# The rate at which different blocks accept external power. All of these # The rate at which different blocks accept external power. All of these

View File

@ -0,0 +1,9 @@
# Transposer
![Such a poser.](oredict:oc:transposer)
The transposer bridges the gap between redstone controlled hoppers and [robots](robot.md), allowing [computer](../general/computer.md)-controlled transferral of items and fluids between adjacent blocks.
*Note that this block has no internal inventory.*
Besides moving things around, it can also be used to inspect the contents of the adjacent inventories, like an [adapter](adapter.md) with an [inventory controller upgrade](../item/inventoryControllerUpgrade.md) could, and the contents of adjacent tanks, like and adapter with a [tank controller upgrade](../item/tankControllerUpgrade.md) could.

View File

@ -23,6 +23,7 @@ tile.oc.hologram2.name=Hologram Projector (Tier 2)
tile.oc.keyboard.name=Keyboard tile.oc.keyboard.name=Keyboard
tile.oc.microcontroller.name=Microcontroller tile.oc.microcontroller.name=Microcontroller
tile.oc.motionSensor.name=Motion Sensor tile.oc.motionSensor.name=Motion Sensor
tile.oc.netSplitter.name=Net Splitter
tile.oc.powerConverter.name=Power Converter tile.oc.powerConverter.name=Power Converter
tile.oc.powerDistributor.name=Power Distributor tile.oc.powerDistributor.name=Power Distributor
tile.oc.print.name=3D Print tile.oc.print.name=3D Print
@ -37,7 +38,7 @@ tile.oc.screen2.name=Screen (Tier 2)
tile.oc.screen3.name=Screen (Tier 3) tile.oc.screen3.name=Screen (Tier 3)
tile.oc.serverRack.name=Server Rack tile.oc.serverRack.name=Server Rack
tile.oc.switch.name=§cSwitch§7 tile.oc.switch.name=§cSwitch§7
tile.oc.netSplitter.name=Net Splitter tile.oc.transposer.name=Transposer
tile.oc.waypoint.name=Waypoint tile.oc.waypoint.name=Waypoint
# Items # Items
@ -348,6 +349,7 @@ oc:tooltip.Tier=§8Tier %s
oc:tooltip.NetSplitter=Acts as a dynamic connector. Connectivity of each side can be toggled by hitting it with a wrench. Connectivity of all sides can be inverted by applying a redstone signal. oc:tooltip.NetSplitter=Acts as a dynamic connector. Connectivity of each side can be toggled by hitting it with a wrench. Connectivity of all sides can be inverted by applying a redstone signal.
oc:tooltip.TooLong=Hold [§f%s§7] for a detailed tooltip. oc:tooltip.TooLong=Hold [§f%s§7] for a detailed tooltip.
oc:tooltip.Transistor=A basic element in most other computer parts. It's a bit twisted, but it does the job. oc:tooltip.Transistor=A basic element in most other computer parts. It's a bit twisted, but it does the job.
oc:tooltip.Transposer=Allows automated transferral of items and fluids between adjacent inventories and fluid containers.
oc:tooltip.UpgradeAngel=Allows robots to place blocks in thin air, even if there is no point of reference. oc:tooltip.UpgradeAngel=Allows robots to place blocks in thin air, even if there is no point of reference.
oc:tooltip.UpgradeBattery=Increase the amount of energy a device can store, allowing it work longer without having to be recharged. [nl] Capacity: §f%s§7 oc:tooltip.UpgradeBattery=Increase the amount of energy a device can store, allowing it work longer without having to be recharged. [nl] Capacity: §f%s§7
oc:tooltip.UpgradeChunkloader=If a robot moves in a forest and no one is around to see it, does it really move? This upgrades makes sure it does. It keeps the chunk a device is in loaded, but continually consumes energy while active. oc:tooltip.UpgradeChunkloader=If a robot moves in a forest and no one is around to see it, does it really move? This upgrades makes sure it does. It keeps the chunk a device is in loaded, but continually consumes energy while active.

View File

@ -634,6 +634,12 @@ screen3 {
[yellowDust, "oc:circuitChip3", glass] [yellowDust, "oc:circuitChip3", glass]
[obsidian, yellowDust, obsidian]] [obsidian, yellowDust, obsidian]]
} }
transposer {
input: [[ingotIron, "oc:inventoryControllerUpgrade", ingotIron]
[hopper, bucket, hopper]
[ingotIron, "oc:tankControllerUpgrade", ingotIron]]
output: 4
}
waypoint { waypoint {
input: [[ingotIron, "oc:circuitChip1", ingotIron] input: [[ingotIron, "oc:circuitChip1", ingotIron]
["oc:materialTransistor", "oc:materialInterweb", "oc:materialTransistor"], ["oc:materialTransistor", "oc:materialInterweb", "oc:materialTransistor"],

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 523 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 425 B

View File

@ -25,6 +25,7 @@ object Constants {
final val Keyboard = "keyboard" final val Keyboard = "keyboard"
final val Microcontroller = "microcontroller" final val Microcontroller = "microcontroller"
final val MotionSensor = "motionSensor" final val MotionSensor = "motionSensor"
final val NetSplitter = "netSplitter"
final val PowerConverter = "powerConverter" final val PowerConverter = "powerConverter"
final val PowerDistributor = "powerDistributor" final val PowerDistributor = "powerDistributor"
final val Print = "print" final val Print = "print"
@ -39,7 +40,7 @@ object Constants {
final val ScreenTier3 = "screen3" final val ScreenTier3 = "screen3"
final val ServerRack = "serverRack" final val ServerRack = "serverRack"
final val Switch = "switch" final val Switch = "switch"
final val NetSplitter = "netSplitter" final val Transposer = "transposer"
final val Waypoint = "waypoint" final val Waypoint = "waypoint"
def Case(tier: Int) = ItemUtils.caseNameWithTierSuffix("case", tier) def Case(tier: Int) = ItemUtils.caseNameWithTierSuffix("case", tier)

View File

@ -201,6 +201,7 @@ class Settings(val config: Config) {
val dataCardComplex = config.getDouble("power.cost.dataCardComplex") max 0 val dataCardComplex = config.getDouble("power.cost.dataCardComplex") max 0
val dataCardComplexByte = config.getDouble("power.cost.dataCardComplexByte") max 0 val dataCardComplexByte = config.getDouble("power.cost.dataCardComplexByte") max 0
val dataCardAsymmetric = config.getDouble("power.cost.dataCardAsymmetric") max 0 val dataCardAsymmetric = config.getDouble("power.cost.dataCardAsymmetric") max 0
val transposerCost = config.getDouble("power.cost.transposer") max 0
// power.rate // power.rate
val accessPointRate = config.getDouble("power.rate.accessPoint") max 0 val accessPointRate = config.getDouble("power.rate.accessPoint") max 0

View File

@ -81,6 +81,7 @@ object PacketHandler extends CommonPacketHandler {
case PacketType.ServerPresence => onServerPresence(p) case PacketType.ServerPresence => onServerPresence(p)
case PacketType.Sound => onSound(p) case PacketType.Sound => onSound(p)
case PacketType.SoundPattern => onSoundPattern(p) case PacketType.SoundPattern => onSoundPattern(p)
case PacketType.TransposerActivity => onTransposerActivity(p)
case PacketType.WaypointLabel => onWaypointLabel(p) case PacketType.WaypointLabel => onWaypointLabel(p)
case _ => // Invalid packet. case _ => // Invalid packet.
} }
@ -260,6 +261,15 @@ object PacketHandler extends CommonPacketHandler {
} }
} }
def onNetSplitterState(p: PacketParser) =
p.readTileEntity[NetSplitter]() match {
case Some(t) =>
t.isInverted = p.readBoolean()
t.openSides = t.uncompressSides(p.readByte())
t.world.markBlockForUpdate(t.getPos)
case _ => // Invalid packet.
}
def onParticleEffect(p: PacketParser) = { def onParticleEffect(p: PacketParser) = {
val dimension = p.readInt() val dimension = p.readInt()
world(p.player, dimension) match { world(p.player, dimension) match {
@ -589,15 +599,6 @@ object PacketHandler extends CommonPacketHandler {
buffer.rawSetForeground(col, row, color) buffer.rawSetForeground(col, row, color)
} }
def onNetSplitterState(p: PacketParser) =
p.readTileEntity[NetSplitter]() match {
case Some(t) =>
t.isInverted = p.readBoolean()
t.openSides = t.uncompressSides(p.readByte())
t.world.markBlockForUpdate(t.getPos)
case _ => // Invalid packet.
}
def onScreenTouchMode(p: PacketParser) = def onScreenTouchMode(p: PacketParser) =
p.readTileEntity[Screen]() match { p.readTileEntity[Screen]() match {
case Some(t) => t.invertTouchMode = p.readBoolean() case Some(t) => t.invertTouchMode = p.readBoolean()
@ -638,6 +639,12 @@ object PacketHandler extends CommonPacketHandler {
} }
} }
def onTransposerActivity(p: PacketParser) =
p.readTileEntity[Transposer]() match {
case Some(transposer) => transposer.lastOperation = System.currentTimeMillis()
case _ => // Invalid packet.
}
def onWaypointLabel(p: PacketParser) = def onWaypointLabel(p: PacketParser) =
p.readTileEntity[Waypoint]() match { p.readTileEntity[Waypoint]() match {
case Some(waypoint) => waypoint.label = p.readUTF() case Some(waypoint) => waypoint.label = p.readUTF()

View File

@ -63,6 +63,7 @@ private[oc] class Proxy extends CommonProxy {
else else
ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.Hologram], HologramRendererFallback) ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.Hologram], HologramRendererFallback)
ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.Microcontroller], MicrocontrollerRenderer) ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.Microcontroller], MicrocontrollerRenderer)
ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.NetSplitter], NetSplitterRenderer)
ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.PowerDistributor], PowerDistributorRenderer) ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.PowerDistributor], PowerDistributorRenderer)
ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.Printer], PrinterRenderer) ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.Printer], PrinterRenderer)
ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.Raid], RaidRenderer) ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.Raid], RaidRenderer)
@ -72,7 +73,7 @@ private[oc] class Proxy extends CommonProxy {
ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.Relay], SwitchRenderer) ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.Relay], SwitchRenderer)
ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.RobotProxy], RobotRenderer) ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.RobotProxy], RobotRenderer)
ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.Screen], ScreenRenderer) ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.Screen], ScreenRenderer)
ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.NetSplitter], NetSplitterRenderer) ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.Transposer], TransposerRenderer)
ClientRegistry.registerKeyBinding(KeyBindings.materialCosts) ClientRegistry.registerKeyBinding(KeyBindings.materialCosts)
ClientRegistry.registerKeyBinding(KeyBindings.clipboardPaste) ClientRegistry.registerKeyBinding(KeyBindings.clipboardPaste)

View File

@ -143,6 +143,7 @@ object Textures {
val RaidFrontError = L("overlay/raid_front_error") val RaidFrontError = L("overlay/raid_front_error")
val ScreenUpIndicator = L("overlay/screen_up_indicator") val ScreenUpIndicator = L("overlay/screen_up_indicator")
val SwitchSideOn = L("overlay/switch_side_on") val SwitchSideOn = L("overlay/switch_side_on")
val TransposerOn = L("overlay/transposer_on")
val Cable = L("cable") val Cable = L("cable")
val CableCap = L("cableCap") val CableCap = L("cableCap")

View File

@ -0,0 +1,78 @@
package li.cil.oc.client.renderer.tileentity
import li.cil.oc.client.Textures
import li.cil.oc.common.tileentity
import li.cil.oc.util.RenderState
import net.minecraft.client.renderer.Tessellator
import net.minecraft.client.renderer.texture.TextureMap
import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer
import net.minecraft.tileentity.TileEntity
import org.lwjgl.opengl.GL11
object TransposerRenderer extends TileEntitySpecialRenderer {
override def renderTileEntityAt(tileEntity: TileEntity, x: Double, y: Double, z: Double, f: Float, damage: Int) {
RenderState.checkError(getClass.getName + ".renderTileEntityAt: entering (aka: wasntme)")
val transposer = tileEntity.asInstanceOf[tileentity.Transposer]
val activity = math.max(0, 1 - (System.currentTimeMillis() - transposer.lastOperation) / 1000.0)
if (activity > 0) {
GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS)
RenderState.disableLighting()
RenderState.makeItBlend()
RenderState.setBlendAlpha(activity.toFloat)
GL11.glPushMatrix()
GL11.glTranslated(x + 0.5, y + 0.5, z + 0.5)
GL11.glScaled(1.0025, -1.0025, 1.0025)
GL11.glTranslatef(-0.5f, -0.5f, -0.5f)
bindTexture(TextureMap.locationBlocksTexture)
val t = Tessellator.getInstance
val r = t.getWorldRenderer
r.startDrawingQuads()
val icon = Textures.getSprite(Textures.Block.TransposerOn)
r.addVertexWithUV(0, 1, 0, icon.getMaxU, icon.getMinV)
r.addVertexWithUV(1, 1, 0, icon.getMinU, icon.getMinV)
r.addVertexWithUV(1, 1, 1, icon.getMinU, icon.getMaxV)
r.addVertexWithUV(0, 1, 1, icon.getMaxU, icon.getMaxV)
r.addVertexWithUV(0, 0, 0, icon.getMaxU, icon.getMaxV)
r.addVertexWithUV(0, 0, 1, icon.getMaxU, icon.getMinV)
r.addVertexWithUV(1, 0, 1, icon.getMinU, icon.getMinV)
r.addVertexWithUV(1, 0, 0, icon.getMinU, icon.getMaxV)
r.addVertexWithUV(1, 1, 0, icon.getMinU, icon.getMaxV)
r.addVertexWithUV(0, 1, 0, icon.getMaxU, icon.getMaxV)
r.addVertexWithUV(0, 0, 0, icon.getMaxU, icon.getMinV)
r.addVertexWithUV(1, 0, 0, icon.getMinU, icon.getMinV)
r.addVertexWithUV(0, 1, 1, icon.getMinU, icon.getMaxV)
r.addVertexWithUV(1, 1, 1, icon.getMaxU, icon.getMaxV)
r.addVertexWithUV(1, 0, 1, icon.getMaxU, icon.getMinV)
r.addVertexWithUV(0, 0, 1, icon.getMinU, icon.getMinV)
r.addVertexWithUV(0, 1, 0, icon.getMinU, icon.getMaxV)
r.addVertexWithUV(0, 1, 1, icon.getMaxU, icon.getMaxV)
r.addVertexWithUV(0, 0, 1, icon.getMaxU, icon.getMinV)
r.addVertexWithUV(0, 0, 0, icon.getMinU, icon.getMinV)
r.addVertexWithUV(1, 1, 1, icon.getMinU, icon.getMaxV)
r.addVertexWithUV(1, 1, 0, icon.getMaxU, icon.getMaxV)
r.addVertexWithUV(1, 0, 0, icon.getMaxU, icon.getMinV)
r.addVertexWithUV(1, 0, 1, icon.getMinU, icon.getMinV)
t.draw()
RenderState.enableLighting()
GL11.glPopMatrix()
GL11.glPopAttrib()
}
RenderState.checkError(getClass.getName + ".renderTileEntityAt: leaving")
}
}

View File

@ -20,6 +20,7 @@ object PacketType extends Enumeration {
HologramTranslation, HologramTranslation,
HologramValues, HologramValues,
LootDisk, LootDisk,
NetSplitterState,
ParticleEffect, ParticleEffect,
PetVisibility, // Goes both ways. PetVisibility, // Goes both ways.
PowerState, PowerState,
@ -49,11 +50,11 @@ object PacketType extends Enumeration {
TextBufferMultiRawSetBackground, TextBufferMultiRawSetBackground,
TextBufferMultiRawSetForeground, TextBufferMultiRawSetForeground,
TextBufferPowerChange, TextBufferPowerChange,
NetSplitterState,
ScreenTouchMode, ScreenTouchMode,
ServerPresence, ServerPresence,
Sound, Sound,
SoundPattern, SoundPattern,
TransposerActivity,
WaypointLabel, // Goes both ways. WaypointLabel, // Goes both ways.
// Client -> Server // Client -> Server

View File

@ -0,0 +1,18 @@
package li.cil.oc.common.block
import li.cil.oc.common.tileentity
import net.minecraft.block.state.IBlockState
import net.minecraft.util.BlockPos
import net.minecraft.util.EnumFacing
import net.minecraft.world.IBlockAccess
import net.minecraft.world.World
class Transposer extends SimpleBlock {
override def isSideSolid(world: IBlockAccess, pos: BlockPos, side: EnumFacing): Boolean = false
// ----------------------------------------------------------------------- //
override def hasTileEntity(state: IBlockState): Boolean = true
override def createNewTileEntity(world: World, meta: Int) = new tileentity.Transposer()
}

View File

@ -24,6 +24,7 @@ object Blocks {
GameRegistry.registerTileEntity(classOf[tileentity.Geolyzer], Settings.namespace + "geolyzer") GameRegistry.registerTileEntity(classOf[tileentity.Geolyzer], Settings.namespace + "geolyzer")
GameRegistry.registerTileEntity(classOf[tileentity.Microcontroller], Settings.namespace + "microcontroller") GameRegistry.registerTileEntity(classOf[tileentity.Microcontroller], Settings.namespace + "microcontroller")
GameRegistry.registerTileEntity(classOf[tileentity.MotionSensor], Settings.namespace + "motionSensor") GameRegistry.registerTileEntity(classOf[tileentity.MotionSensor], Settings.namespace + "motionSensor")
GameRegistry.registerTileEntity(classOf[tileentity.NetSplitter], Settings.namespace + "netSplitter")
GameRegistry.registerTileEntity(classOf[tileentity.PowerConverter], Settings.namespace + "powerConverter") GameRegistry.registerTileEntity(classOf[tileentity.PowerConverter], Settings.namespace + "powerConverter")
GameRegistry.registerTileEntity(classOf[tileentity.PowerDistributor], Settings.namespace + "powerDistributor") GameRegistry.registerTileEntity(classOf[tileentity.PowerDistributor], Settings.namespace + "powerDistributor")
GameRegistry.registerTileEntity(classOf[tileentity.Print], Settings.namespace + "print") GameRegistry.registerTileEntity(classOf[tileentity.Print], Settings.namespace + "print")
@ -35,7 +36,7 @@ object Blocks {
GameRegistry.registerTileEntity(classOf[tileentity.Switch], Settings.namespace + "switch") GameRegistry.registerTileEntity(classOf[tileentity.Switch], Settings.namespace + "switch")
GameRegistry.registerTileEntity(classOf[tileentity.Screen], Settings.namespace + "screen") GameRegistry.registerTileEntity(classOf[tileentity.Screen], Settings.namespace + "screen")
GameRegistry.registerTileEntity(classOf[tileentity.ServerRack], Settings.namespace + "serverRack") GameRegistry.registerTileEntity(classOf[tileentity.ServerRack], Settings.namespace + "serverRack")
GameRegistry.registerTileEntity(classOf[tileentity.NetSplitter], Settings.namespace + "netSplitter") GameRegistry.registerTileEntity(classOf[tileentity.Transposer], Settings.namespace + "transposer")
GameRegistry.registerTileEntity(classOf[tileentity.Waypoint], Settings.namespace + "waypoint") GameRegistry.registerTileEntity(classOf[tileentity.Waypoint], Settings.namespace + "waypoint")
Items.registerBlock(new AccessPoint(), Constants.BlockName.AccessPoint) Items.registerBlock(new AccessPoint(), Constants.BlockName.AccessPoint)
@ -79,5 +80,8 @@ object Blocks {
// v1.5.14 // v1.5.14
Recipes.addBlock(new NetSplitter(), Constants.BlockName.NetSplitter, "oc:netSplitter") Recipes.addBlock(new NetSplitter(), Constants.BlockName.NetSplitter, "oc:netSplitter")
// v1.5.16
Recipes.addBlock(new Transposer(), Constants.BlockName.Transposer, "oc:transposer")
} }
} }

View File

@ -0,0 +1,25 @@
package li.cil.oc.common.tileentity
import li.cil.oc.server.component
import net.minecraft.nbt.NBTTagCompound
class Transposer extends traits.Environment {
val transposer = new component.Transposer(this)
def node = transposer.node
// Used on client side to check whether to render activity indicators.
var lastOperation = 0L
override def canUpdate = false
override def readFromNBTForServer(nbt: NBTTagCompound) {
super.readFromNBTForServer(nbt)
transposer.load(nbt)
}
override def writeToNBTForServer(nbt: NBTTagCompound) {
super.writeToNBTForServer(nbt)
transposer.save(nbt)
}
}

View File

@ -227,6 +227,16 @@ object PacketSender {
} }
} }
def sendNetSplitterState(t: tileentity.NetSplitter): Unit = {
val pb = new SimplePacketBuilder(PacketType.NetSplitterState)
pb.writeTileEntity(t)
pb.writeBoolean(t.isInverted)
pb.writeByte(t.compressSides)
pb.sendToPlayersNearTileEntity(t)
}
def sendParticleEffect(position: BlockPosition, particleType: EnumParticleTypes, count: Int, velocity: Double, direction: Option[EnumFacing] = None): Unit = if (count > 0) { def sendParticleEffect(position: BlockPosition, particleType: EnumParticleTypes, count: Int, velocity: Double, direction: Option[EnumFacing] = None): Unit = if (count > 0) {
val pb = new SimplePacketBuilder(PacketType.ParticleEffect) val pb = new SimplePacketBuilder(PacketType.ParticleEffect)
@ -522,16 +532,6 @@ object PacketSender {
pb.sendToPlayersNearHost(host) pb.sendToPlayersNearHost(host)
} }
def sendNetSplitterState(t: tileentity.NetSplitter): Unit = {
val pb = new SimplePacketBuilder(PacketType.NetSplitterState)
pb.writeTileEntity(t)
pb.writeBoolean(t.isInverted)
pb.writeByte(t.compressSides)
pb.sendToPlayersNearTileEntity(t)
}
def sendScreenTouchMode(t: tileentity.Screen, value: Boolean) { def sendScreenTouchMode(t: tileentity.Screen, value: Boolean) {
val pb = new SimplePacketBuilder(PacketType.ScreenTouchMode) val pb = new SimplePacketBuilder(PacketType.ScreenTouchMode)
@ -612,6 +612,14 @@ object PacketSender {
pb.sendToNearbyPlayers(world, x, y, z, Option(32)) pb.sendToNearbyPlayers(world, x, y, z, Option(32))
} }
def sendTransposerActivity(t: tileentity.Transposer) {
val pb = new SimplePacketBuilder(PacketType.TransposerActivity)
pb.writeTileEntity(t)
pb.sendToPlayersNearTileEntity(t, Option(32))
}
def sendWaypointLabel(t: Waypoint): Unit = { def sendWaypointLabel(t: Waypoint): Unit = {
val pb = new SimplePacketBuilder(PacketType.WaypointLabel) val pb = new SimplePacketBuilder(PacketType.WaypointLabel)

View File

@ -0,0 +1,67 @@
package li.cil.oc.server.component
import li.cil.oc.Settings
import li.cil.oc.api
import li.cil.oc.api.machine.Arguments
import li.cil.oc.api.machine.Callback
import li.cil.oc.api.machine.Context
import li.cil.oc.api.network.Visibility
import li.cil.oc.api.prefab
import li.cil.oc.common.tileentity
import li.cil.oc.server.{PacketSender => ServerPacketSender}
import li.cil.oc.util.BlockPosition
import li.cil.oc.util.ExtendedArguments._
import li.cil.oc.util.FluidUtils
import li.cil.oc.util.InventoryUtils
import net.minecraft.util.EnumFacing
import scala.language.existentials
class Transposer(val host: tileentity.Transposer) extends prefab.ManagedEnvironment with traits.WorldInventoryAnalytics with traits.WorldTankAnalytics {
override val node = api.Network.newNode(this, Visibility.Network).
withComponent("transposer").
withConnector().
create()
override def position = BlockPosition(host)
override protected def checkSideForAction(args: Arguments, n: Int) = args.checkSide(n, EnumFacing.values: _*)
@Callback(doc = """function(sourceSide:number, sinkSide:number[, count:number[, sourceSlot:number, sinkSlot:number]]):number -- Transfer some items between two inventories.""")
def transferItem(context: Context, args: Arguments): Array[AnyRef] = {
val sourceSide = checkSideForAction(args, 0)
val sourcePos = position.offset(sourceSide)
val sinkSide = checkSideForAction(args, 1)
val sinkPos = position.offset(sinkSide)
val count = args.optItemCount(2)
if (node.tryChangeBuffer(-Settings.get.transposerCost)) {
ServerPacketSender.sendTransposerActivity(host)
if (args.count > 3) {
val sourceSlot = args.checkSlot(InventoryUtils.inventoryAt(sourcePos).getOrElse(throw new IllegalArgumentException("no inventory")), 3)
val sinkSlot = args.checkSlot(InventoryUtils.inventoryAt(sinkPos).getOrElse(throw new IllegalArgumentException("no inventory")), 4)
result(InventoryUtils.transferBetweenInventoriesSlotsAt(sourcePos, sourceSide.getOpposite, sourceSlot, sinkPos, Option(sinkSide.getOpposite), sinkSlot, count))
}
else result(InventoryUtils.transferBetweenInventoriesAt(sourcePos, sourceSide.getOpposite, sinkPos, Option(sinkSide.getOpposite), count))
}
else result(Unit, "not enough energy")
}
@Callback(doc = """function(sourceSide:number, sinkSide:number[, count:number]):number -- Transfer some items between two inventories.""")
def transferFluid(context: Context, args: Arguments): Array[AnyRef] = {
val sourceSide = checkSideForAction(args, 0)
val sourcePos = position.offset(sourceSide)
val sinkSide = checkSideForAction(args, 1)
val sinkPos = position.offset(sinkSide)
val count = args.optFluidCount(2)
if (node.tryChangeBuffer(-Settings.get.transposerCost)) {
ServerPacketSender.sendTransposerActivity(host)
result(FluidUtils.transferBetweenFluidHandlersAt(sourcePos, sourceSide.getOpposite, sinkPos, sinkSide.getOpposite, count))
}
else result(Unit, "not enough energy")
}
}

View File

@ -4,14 +4,9 @@ import li.cil.oc.api.machine.Arguments
import li.cil.oc.api.machine.Callback import li.cil.oc.api.machine.Callback
import li.cil.oc.api.machine.Context import li.cil.oc.api.machine.Context
import li.cil.oc.util.ExtendedArguments._ import li.cil.oc.util.ExtendedArguments._
import li.cil.oc.util.ExtendedBlock._ import li.cil.oc.util.FluidUtils
import li.cil.oc.util.ExtendedWorld._
import li.cil.oc.util.ResultWrapper.result 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.FluidStack
import net.minecraftforge.fluids.IFluidBlock
import net.minecraftforge.fluids.IFluidHandler
trait TankWorldControl extends TankAware with WorldAware with SideRestricted { trait TankWorldControl extends TankAware with WorldAware with SideRestricted {
@Callback(doc = "function(side:number):boolean -- Compare the fluid in the selected tank with the fluid on the specified side. Returns true if equal.") @Callback(doc = "function(side:number):boolean -- Compare the fluid in the selected tank with the fluid on the specified side. Returns true if equal.")
@ -19,16 +14,10 @@ trait TankWorldControl extends TankAware with WorldAware with SideRestricted {
val side = checkSideForAction(args, 0) val side = checkSideForAction(args, 0)
fluidInTank(selectedTank) match { fluidInTank(selectedTank) match {
case Some(stack) => case Some(stack) =>
val blockPos = position.offset(side) FluidUtils.fluidHandlerAt(position.offset(side)) match {
if (world.blockExists(blockPos)) world.getTileEntity(blockPos) match { case Some(handler) => result(Option(handler.getTankInfo(side.getOpposite)).exists(_.exists(other => stack.isFluidEqual(other.fluid))))
case handler: IFluidHandler => case _ => result(false)
result(Option(handler.getTankInfo(side.getOpposite)).exists(_.exists(other => stack.isFluidEqual(other.fluid))))
case _ =>
val block = world.getBlock(blockPos)
val fluid = FluidRegistry.lookupFluidForBlock(block)
result(stack.getFluid == fluid)
} }
else result(false)
case _ => result(false) case _ => result(false)
} }
} }
@ -36,18 +25,14 @@ trait TankWorldControl extends TankAware with WorldAware with SideRestricted {
@Callback(doc = "function(side:boolean[, amount:number=1000]):boolean, number or string -- Drains the specified amount of fluid from the specified side. Returns the amount drained, or an error message.") @Callback(doc = "function(side:boolean[, amount:number=1000]):boolean, number or string -- Drains the specified amount of fluid from the specified side. Returns the amount drained, or an error message.")
def drain(context: Context, args: Arguments): Array[AnyRef] = { def drain(context: Context, args: Arguments): Array[AnyRef] = {
val facing = checkSideForAction(args, 0) val facing = checkSideForAction(args, 0)
val count = args.optFluidCount(1) val count = args.optFluidCount(1) max 0
getTank(selectedTank) match { getTank(selectedTank) match {
case Some(tank) => case Some(tank) =>
val space = tank.getCapacity - tank.getFluidAmount val space = tank.getCapacity - tank.getFluidAmount
val amount = math.min(count, space) val amount = math.min(count, space)
if (count > 0 && amount == 0) { if (count < 1 || amount > 0) {
result(Unit, "tank is full") FluidUtils.fluidHandlerAt(position.offset(facing)) match {
} case Some(handler) =>
else {
val blockPos = position.offset(facing)
if (world.blockExists(blockPos)) world.getTileEntity(blockPos) match {
case handler: IFluidHandler =>
tank.getFluid match { tank.getFluid match {
case stack: FluidStack => case stack: FluidStack =>
val drained = handler.drain(facing.getOpposite, new FluidStack(stack, amount), true) val drained = handler.drain(facing.getOpposite, new FluidStack(stack, amount), true)
@ -60,36 +45,10 @@ trait TankWorldControl extends TankAware with WorldAware with SideRestricted {
val transferred = tank.fill(handler.drain(facing.getOpposite, amount, true), true) val transferred = tank.fill(handler.drain(facing.getOpposite, amount, true), true)
result(transferred > 0, transferred) result(transferred > 0, transferred)
} }
case _ => world.getBlock(blockPos) match { case _ => result(Unit, "incompatible or no fluid")
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") 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 result(Unit, "incompatible or no fluid")
}
case _ => result(Unit, "no tank selected") case _ => result(Unit, "no tank selected")
} }
} }
@ -97,16 +56,13 @@ trait TankWorldControl extends TankAware with WorldAware with SideRestricted {
@Callback(doc = "function(side:number[, amount:number=1000]):boolean, number of string -- Eject the specified amount of fluid to the specified side. Returns the amount ejected or an error message.") @Callback(doc = "function(side:number[, amount:number=1000]):boolean, number of string -- Eject the specified amount of fluid to the specified side. Returns the amount ejected or an error message.")
def fill(context: Context, args: Arguments): Array[AnyRef] = { def fill(context: Context, args: Arguments): Array[AnyRef] = {
val facing = checkSideForAction(args, 0) val facing = checkSideForAction(args, 0)
val count = args.optFluidCount(1) val count = args.optFluidCount(1) max 0
getTank(selectedTank) match { getTank(selectedTank) match {
case Some(tank) => case Some(tank) =>
val amount = math.min(count, tank.getFluidAmount) val amount = math.min(count, tank.getFluidAmount)
if (count > 0 && amount == 0) { if (count < 1 || amount > 0) {
result(Unit, "tank is empty") FluidUtils.fluidHandlerAt(position.offset(facing)) match {
} case Some(handler) =>
val blockPos = position.offset(facing)
if (world.blockExists(blockPos)) world.getTileEntity(blockPos) match {
case handler: IFluidHandler =>
tank.getFluid match { tank.getFluid match {
case stack: FluidStack => case stack: FluidStack =>
val filled = handler.fill(facing.getOpposite, new FluidStack(stack, amount), true) val filled = handler.fill(facing.getOpposite, new FluidStack(stack, amount), true)
@ -118,28 +74,10 @@ trait TankWorldControl extends TankAware with WorldAware with SideRestricted {
case _ => case _ =>
result(Unit, "tank is empty") result(Unit, "tank is empty")
} }
case _ => case _ => result(Unit, "no space")
val block = world.getBlock(blockPos)
if (!block.isAir(blockPos) && !block.isReplaceable(blockPos)) {
result(Unit, "no space")
}
else if (tank.getFluidAmount < 1000) {
result(Unit, "tank is empty")
}
else if (!tank.getFluid.getFluid.canBePlacedInWorld) {
result(Unit, "incompatible fluid")
}
else {
val fluidBlock = tank.getFluid.getFluid.getBlock
tank.drain(1000, true)
world.breakBlock(blockPos)
world.setBlock(blockPos, fluidBlock)
// This fake neighbor update is required to get stills to start flowing.
world.notifyBlockOfNeighborChange(blockPos, world.getBlock(position))
result(true, 1000)
} }
} }
else result(Unit, "no space") else result(Unit, "tank is empty")
case _ => result(Unit, "no tank selected") case _ => result(Unit, "no tank selected")
} }
} }

View File

@ -5,15 +5,15 @@ import li.cil.oc.api.machine.Arguments
import li.cil.oc.api.machine.Callback import li.cil.oc.api.machine.Callback
import li.cil.oc.api.machine.Context import li.cil.oc.api.machine.Context
import li.cil.oc.server.component.result import li.cil.oc.server.component.result
import li.cil.oc.util.ExtendedWorld._ import li.cil.oc.util.FluidUtils
import net.minecraftforge.fluids.IFluidHandler
trait WorldTankAnalytics extends WorldAware with SideRestricted { trait WorldTankAnalytics extends WorldAware with SideRestricted {
@Callback(doc = """function(side:number):number -- Get the amount of fluid in the tank on the specified side.""") @Callback(doc = """function(side:number):number -- Get the amount of fluid in the tank on the specified side.""")
def getTankLevel(context: Context, args: Arguments): Array[AnyRef] = { def getTankLevel(context: Context, args: Arguments): Array[AnyRef] = {
val facing = checkSideForAction(args, 0) val facing = checkSideForAction(args, 0)
world.getTileEntity(position.offset(facing)) match {
case handler: IFluidHandler => FluidUtils.fluidHandlerAt(position.offset(facing)) match {
case Some(handler) =>
result(handler.getTankInfo(facing.getOpposite).map(info => Option(info.fluid).fold(0)(_.amount)).sum) result(handler.getTankInfo(facing.getOpposite).map(info => Option(info.fluid).fold(0)(_.amount)).sum)
case _ => result(Unit, "no tank") case _ => result(Unit, "no tank")
} }
@ -22,8 +22,8 @@ trait WorldTankAnalytics extends WorldAware with SideRestricted {
@Callback(doc = """function(side:number):number -- Get the capacity of the tank on the specified side.""") @Callback(doc = """function(side:number):number -- Get the capacity of the tank on the specified side.""")
def getTankCapacity(context: Context, args: Arguments): Array[AnyRef] = { def getTankCapacity(context: Context, args: Arguments): Array[AnyRef] = {
val facing = checkSideForAction(args, 0) val facing = checkSideForAction(args, 0)
world.getTileEntity(position.offset(facing)) match { FluidUtils.fluidHandlerAt(position.offset(facing)) match {
case handler: IFluidHandler => case Some(handler) =>
result(handler.getTankInfo(facing.getOpposite).map(_.capacity).foldLeft(0)((max, capacity) => math.max(max, capacity))) result(handler.getTankInfo(facing.getOpposite).map(_.capacity).foldLeft(0)((max, capacity) => math.max(max, capacity)))
case _ => result(Unit, "no tank") case _ => result(Unit, "no tank")
} }
@ -32,8 +32,8 @@ trait WorldTankAnalytics extends WorldAware with SideRestricted {
@Callback(doc = """function(side:number):table -- Get a description of the fluid in the the tank on the specified side.""") @Callback(doc = """function(side:number):table -- Get a description of the fluid in the the tank on the specified side.""")
def getFluidInTank(context: Context, args: Arguments): Array[AnyRef] = if (Settings.get.allowItemStackInspection) { def getFluidInTank(context: Context, args: Arguments): Array[AnyRef] = if (Settings.get.allowItemStackInspection) {
val facing = checkSideForAction(args, 0) val facing = checkSideForAction(args, 0)
world.getTileEntity(position.offset(facing)) match { FluidUtils.fluidHandlerAt(position.offset(facing)) match {
case handler: IFluidHandler => case Some(handler) =>
result(handler.getTankInfo(facing.getOpposite)) result(handler.getTankInfo(facing.getOpposite))
case _ => result(Unit, "no tank") case _ => result(Unit, "no tank")
} }

View File

@ -4,6 +4,7 @@ import li.cil.oc.api.internal.MultiTank
import li.cil.oc.api.machine.Arguments import li.cil.oc.api.machine.Arguments
import net.minecraft.inventory.IInventory import net.minecraft.inventory.IInventory
import net.minecraft.util.EnumFacing import net.minecraft.util.EnumFacing
import net.minecraftforge.fluids.FluidContainerRegistry
import scala.language.implicitConversions import scala.language.implicitConversions
@ -16,7 +17,7 @@ object ExtendedArguments {
if (!isDefined(index) || !hasValue(index)) default if (!isDefined(index) || !hasValue(index)) default
else math.max(0, math.min(64, args.checkInteger(index))) else math.max(0, math.min(64, args.checkInteger(index)))
def optFluidCount(index: Int, default: Int = 1000) = def optFluidCount(index: Int, default: Int = FluidContainerRegistry.BUCKET_VOLUME) =
if (!isDefined(index) || !hasValue(index)) default if (!isDefined(index) || !hasValue(index)) default
else math.max(0, args.checkInteger(index)) else math.max(0, args.checkInteger(index))

View File

@ -1,6 +1,7 @@
package li.cil.oc.util package li.cil.oc.util
import net.minecraft.block.Block import net.minecraft.block.Block
import net.minecraftforge.fluids.IFluidBlock
import scala.language.implicitConversions import scala.language.implicitConversions
@ -22,4 +23,14 @@ object ExtendedBlock {
def getCollisionBoundingBoxFromPool(position: BlockPosition) = block.getCollisionBoundingBox(position.world.get, position.toBlockPos, position.world.get.getBlockState(position.toBlockPos)) def getCollisionBoundingBoxFromPool(position: BlockPosition) = block.getCollisionBoundingBox(position.world.get, position.toBlockPos, position.world.get.getBlockState(position.toBlockPos))
} }
implicit def extendedFluidBlock(block: IFluidBlock): ExtendedFluidBlock = new ExtendedFluidBlock(block)
class ExtendedFluidBlock(val block: IFluidBlock) {
def drain(position: BlockPosition, doDrain: Boolean) = block.drain(position.world.get, position.toBlockPos, doDrain)
def canDrain(position: BlockPosition) = block.canDrain(position.world.get, position.toBlockPos)
def getFilledPercentage(position: BlockPosition) = block.getFilledPercentage(position.world.get, position.toBlockPos)
}
} }

View File

@ -0,0 +1,157 @@
package li.cil.oc.util
import li.cil.oc.util.ExtendedBlock._
import li.cil.oc.util.ExtendedWorld._
import net.minecraft.block.Block
import net.minecraft.block.BlockLiquid
import net.minecraft.util.EnumFacing
import net.minecraftforge.fluids.Fluid
import net.minecraftforge.fluids.FluidContainerRegistry
import net.minecraftforge.fluids.FluidRegistry
import net.minecraftforge.fluids.FluidStack
import net.minecraftforge.fluids.FluidTank
import net.minecraftforge.fluids.FluidTankInfo
import net.minecraftforge.fluids.IFluidBlock
import net.minecraftforge.fluids.IFluidHandler
object FluidUtils {
/**
* Retrieves an actual fluid handler implementation for a specified world coordinate.
* <p/>
* This performs special handling for in-world liquids.
*/
def fluidHandlerAt(position: BlockPosition): Option[IFluidHandler] = position.world match {
case Some(world) if world.blockExists(position) => world.getTileEntity(position) match {
case handler: IFluidHandler => Option(handler)
case _ => Option(new GenericBlockWrapper(position))
}
case _ => None
}
/**
* Transfers some fluid between two fluid handlers.
* <p/>
* This will try to extract up the specified amount of fluid from any handler,
* then insert it into the specified sink handler. If the insertion fails, the
* fluid will remain in the source handler.
* <p/>
* This returns <tt>true</tt> if some fluid was transferred.
*/
def transferBetweenFluidHandlers(source: IFluidHandler, sourceSide: EnumFacing, sink: IFluidHandler, sinkSide: EnumFacing, limit: Int = FluidContainerRegistry.BUCKET_VOLUME) = {
val drained = source.drain(sourceSide, limit, false)
val filled = sink.fill(sinkSide, drained, false)
sink.fill(sinkSide, source.drain(sourceSide, filled, true), true)
}
/**
* Utility method for calling <tt>transferBetweenFluidHandlers</tt> on handlers
* in the world.
* <p/>
* This uses the <tt>fluidHandlerAt</tt> method, and therefore handles special
* cases such as fluid blocks.
*/
def transferBetweenFluidHandlersAt(sourcePos: BlockPosition, sourceSide: EnumFacing, sinkPos: BlockPosition, sinkSide: EnumFacing, limit: Int = FluidContainerRegistry.BUCKET_VOLUME) =
fluidHandlerAt(sourcePos).fold(0)(source =>
fluidHandlerAt(sinkPos).fold(0)(sink =>
transferBetweenFluidHandlers(source, sourceSide, sink, sinkSide, limit)))
// ----------------------------------------------------------------------- //
private class GenericBlockWrapper(position: BlockPosition) extends IFluidHandler {
override def canDrain(from: EnumFacing, fluid: Fluid): Boolean = currentWrapper.fold(false)(_.canDrain(from, fluid))
override def drain(from: EnumFacing, resource: FluidStack, doDrain: Boolean): FluidStack = currentWrapper.fold(null: FluidStack)(_.drain(from, resource, doDrain))
override def drain(from: EnumFacing, maxDrain: Int, doDrain: Boolean): FluidStack = currentWrapper.fold(null: FluidStack)(_.drain(from, maxDrain, doDrain))
override def canFill(from: EnumFacing, fluid: Fluid): Boolean = currentWrapper.fold(false)(_.canFill(from, fluid))
override def fill(from: EnumFacing, resource: FluidStack, doFill: Boolean): Int = currentWrapper.fold(0)(_.fill(from, resource, doFill))
override def getTankInfo(from: EnumFacing): Array[FluidTankInfo] = currentWrapper.fold(Array.empty[FluidTankInfo])(_.getTankInfo(from))
def currentWrapper = if (position.world.get.blockExists(position)) position.world.get.getBlock(position) match {
case block: IFluidBlock => Option(new FluidBlockWrapper(position, block))
case block: BlockLiquid if FluidRegistry.lookupFluidForBlock(block) != null => Option(new LiquidBlockWrapper(position, block))
case block: Block if block.isAir(position) || block.isReplaceable(position) => Option(new AirBlockWrapper(position, block))
case _ => None
}
else None
}
private trait BlockWrapperBase extends IFluidHandler {
protected def uncheckedDrain(doDrain: Boolean): FluidStack
override def drain(from: EnumFacing, resource: FluidStack, doDrain: Boolean): FluidStack = {
val drained = uncheckedDrain(false)
if (drained != null && (resource == null || (drained.getFluid == resource.getFluid && drained.amount <= resource.amount))) {
uncheckedDrain(doDrain)
}
else null
}
override def drain(from: EnumFacing, maxDrain: Int, doDrain: Boolean): FluidStack = {
val drained = uncheckedDrain(false)
if (drained != null && drained.amount <= maxDrain) {
uncheckedDrain(doDrain)
}
else null
}
override def canFill(from: EnumFacing, fluid: Fluid): Boolean = false
override def fill(from: EnumFacing, resource: FluidStack, doFill: Boolean): Int = 0
}
private class FluidBlockWrapper(val position: BlockPosition, val block: IFluidBlock) extends BlockWrapperBase {
final val AssumedCapacity = FluidContainerRegistry.BUCKET_VOLUME
override def canDrain(from: EnumFacing, fluid: Fluid): Boolean = block.canDrain(position)
override def getTankInfo(from: EnumFacing): Array[FluidTankInfo] = Array(new FluidTankInfo(new FluidTank(block.getFluid, (block.getFilledPercentage(position) * AssumedCapacity).toInt, AssumedCapacity)))
override protected def uncheckedDrain(doDrain: Boolean): FluidStack = block.drain(position, doDrain)
}
private class LiquidBlockWrapper(val position: BlockPosition, val block: BlockLiquid) extends BlockWrapperBase {
val fluid = FluidRegistry.lookupFluidForBlock(block)
override def canDrain(from: EnumFacing, fluid: Fluid): Boolean = true
override def getTankInfo(from: EnumFacing): Array[FluidTankInfo] = Array(new FluidTankInfo(new FluidTank(fluid, FluidContainerRegistry.BUCKET_VOLUME, FluidContainerRegistry.BUCKET_VOLUME)))
override protected def uncheckedDrain(doDrain: Boolean): FluidStack = {
if (doDrain) {
position.world.get.setBlockToAir(position)
}
new FluidStack(fluid, FluidContainerRegistry.BUCKET_VOLUME)
}
}
private class AirBlockWrapper(val position: BlockPosition, val block: Block) extends IFluidHandler {
override def canDrain(from: EnumFacing, fluid: Fluid): Boolean = false
override def drain(from: EnumFacing, resource: FluidStack, doDrain: Boolean): FluidStack = null
override def drain(from: EnumFacing, maxDrain: Int, doDrain: Boolean): FluidStack = null
override def canFill(from: EnumFacing, fluid: Fluid): Boolean = fluid.canBePlacedInWorld
override def fill(from: EnumFacing, resource: FluidStack, doFill: Boolean): Int = {
if (resource != null && resource.getFluid.canBePlacedInWorld && resource.getFluid.getBlock != null) {
if (doFill) {
val world = position.world.get
world.breakBlock(position)
world.setBlock(position, resource.getFluid.getBlock)
// This fake neighbor update is required to get stills to start flowing.
world.notifyBlockOfNeighborChange(position, world.getBlock(position))
}
FluidContainerRegistry.BUCKET_VOLUME
}
else 0
}
override def getTankInfo(from: EnumFacing): Array[FluidTankInfo] = Array.empty
}
}

View File

@ -221,6 +221,48 @@ object InventoryUtils {
def extractFromInventoryAt(consumer: (ItemStack) => Unit, position: BlockPosition, side: EnumFacing, limit: Int = 64) = def extractFromInventoryAt(consumer: (ItemStack) => Unit, position: BlockPosition, side: EnumFacing, limit: Int = 64) =
inventoryAt(position).exists(extractFromInventory(consumer, _, side, limit)) inventoryAt(position).exists(extractFromInventory(consumer, _, side, limit))
/**
* Transfers some items between two inventories.
* <p/>
* This will try to extract up the specified number of items from any inventory,
* then insert it into the specified sink inventory. If the insertion fails, the
* items will remain in the source inventory.
* <p/>
* This uses the <tt>extractFromInventory</tt> and <tt>insertIntoInventory</tt>
* methods, and therefore handles special cases such as sided inventories and
* stack size limits.
* <p/>
* This returns <tt>true</tt> if at least one item was transferred.
*/
def transferBetweenInventories(source: IInventory, sourceSide: EnumFacing, sink: IInventory, sinkSide: Option[EnumFacing], limit: Int = 64) =
extractFromInventory(
insertIntoInventory(_, sink, sinkSide, limit), source, sourceSide, limit)
/**
* Like <tt>transferBetweenInventories</tt> but moving between specific slots.
*/
def transferBetweenInventoriesSlots(source: IInventory, sourceSide: EnumFacing, sourceSlot: Int, sink: IInventory, sinkSide: Option[EnumFacing], sinkSlot: Int, limit: Int = 64) =
extractFromInventorySlot(
insertIntoInventorySlot(_, sink, sinkSide, sinkSlot, limit), source, sourceSide, sourceSlot, limit)
/**
* Utility method for calling <tt>transferBetweenInventories</tt> on inventories
* in the world.
*/
def transferBetweenInventoriesAt(source: BlockPosition, sourceSide: EnumFacing, sink: BlockPosition, sinkSide: Option[EnumFacing], limit: Int = 64) =
inventoryAt(source).exists(sourceInventory =>
inventoryAt(sink).exists(sinkInventory =>
transferBetweenInventories(sourceInventory, sourceSide, sinkInventory, sinkSide, limit)))
/**
* Utility method for calling <tt>transferBetweenInventoriesSlots</tt> on inventories
* in the world.
*/
def transferBetweenInventoriesSlotsAt(sourcePos: BlockPosition, sourceSide: EnumFacing, sourceSlot: Int, sinkPos: BlockPosition, sinkSide: Option[EnumFacing], sinkSlot: Int, limit: Int = 64) =
inventoryAt(sourcePos).exists(sourceInventory =>
inventoryAt(sinkPos).exists(sinkInventory =>
transferBetweenInventoriesSlots(sourceInventory, sourceSide, sourceSlot, sinkInventory, sinkSide, sinkSlot, limit)))
/** /**
* Utility method for dropping contents from a single inventory slot into * Utility method for dropping contents from a single inventory slot into
* the world. * the world.