blob: 804be4a6ebf2ed56609ff9b00c35db7c093e7669 [file] [log] [blame] [raw]
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.BlockDynamicLiquid
import net.minecraft.block.BlockLiquid
import net.minecraft.block.BlockStaticLiquid
import net.minecraft.init.Blocks
import net.minecraftforge.common.util.ForgeDirection
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: ForgeDirection, sink: IFluidHandler, sinkSide: ForgeDirection, 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: ForgeDirection, sinkPos: BlockPosition, sinkSide: ForgeDirection, limit: Int = FluidContainerRegistry.BUCKET_VOLUME) =
fluidHandlerAt(sourcePos).fold(0)(source =>
fluidHandlerAt(sinkPos).fold(0)(sink =>
transferBetweenFluidHandlers(source, sourceSide, sink, sinkSide, limit)))
/**
* Lookup fluid taking into account flowing liquid blocks...
*/
def lookupFluidForBlock(block: Block): Fluid = {
if (block == Blocks.flowing_lava) FluidRegistry.LAVA
else if (block == Blocks.flowing_water) FluidRegistry.WATER
else FluidRegistry.lookupFluidForBlock(block)
}
// ----------------------------------------------------------------------- //
private class GenericBlockWrapper(position: BlockPosition) extends IFluidHandler {
override def canDrain(from: ForgeDirection, fluid: Fluid): Boolean = currentWrapper.fold(false)(_.canDrain(from, fluid))
override def drain(from: ForgeDirection, resource: FluidStack, doDrain: Boolean): FluidStack = currentWrapper.fold(null: FluidStack)(_.drain(from, resource, doDrain))
override def drain(from: ForgeDirection, maxDrain: Int, doDrain: Boolean): FluidStack = currentWrapper.fold(null: FluidStack)(_.drain(from, maxDrain, doDrain))
override def canFill(from: ForgeDirection, fluid: Fluid): Boolean = currentWrapper.fold(false)(_.canFill(from, fluid))
override def fill(from: ForgeDirection, resource: FluidStack, doFill: Boolean): Int = currentWrapper.fold(0)(_.fill(from, resource, doFill))
override def getTankInfo(from: ForgeDirection): 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: BlockStaticLiquid if lookupFluidForBlock(block) != null && isFullLiquidBlock => Option(new LiquidBlockWrapper(position, block))
case block: BlockDynamicLiquid if lookupFluidForBlock(block) != null && isFullLiquidBlock => Option(new LiquidBlockWrapper(position, block))
case block: Block if block.isAir(position) || block.isReplaceable(position) => Option(new AirBlockWrapper(position, block))
case _ => None
}
else None
def isFullLiquidBlock = position.world.get.getBlockMetadata(position) == 0
}
private trait BlockWrapperBase extends IFluidHandler {
protected def uncheckedDrain(doDrain: Boolean): FluidStack
override def drain(from: ForgeDirection, 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: ForgeDirection, maxDrain: Int, doDrain: Boolean): FluidStack = {
val drained = uncheckedDrain(false)
if (drained != null && drained.amount <= maxDrain) {
uncheckedDrain(doDrain)
}
else null
}
override def canFill(from: ForgeDirection, fluid: Fluid): Boolean = false
override def fill(from: ForgeDirection, 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: ForgeDirection, fluid: Fluid): Boolean = block.canDrain(position)
override def getTankInfo(from: ForgeDirection): 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 = lookupFluidForBlock(block)
override def canDrain(from: ForgeDirection, fluid: Fluid): Boolean = true
override def getTankInfo(from: ForgeDirection): 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: ForgeDirection, fluid: Fluid): Boolean = false
override def drain(from: ForgeDirection, resource: FluidStack, doDrain: Boolean): FluidStack = null
override def drain(from: ForgeDirection, maxDrain: Int, doDrain: Boolean): FluidStack = null
override def canFill(from: ForgeDirection, fluid: Fluid): Boolean = fluid.canBePlacedInWorld
override def fill(from: ForgeDirection, resource: FluidStack, doFill: Boolean): Int = {
if (resource != null && resource.getFluid.canBePlacedInWorld && resource.getFluid.getBlock != null) {
if (doFill) {
val world = position.world.get
if (!world.isAirBlock(position) && !world.isAnyLiquid(position.bounds))
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: ForgeDirection): Array[FluidTankInfo] = Array.empty
}
}