package edx.basic.fluid.grate import java.util.{Collections, Comparator, HashMap, PriorityQueue} import cpw.mods.fml.relauncher.{Side, SideOnly} import edx.basic.fluid.grate.TileGrate._ import edx.core.Reference import edx.core.prefab.node.{NodeFluidPressure, TileFluidProvider} import net.minecraft.block.material.Material import net.minecraft.client.renderer.texture.IIconRegister import net.minecraft.entity.player.EntityPlayer import net.minecraft.nbt.NBTTagCompound import net.minecraft.util.{ChatComponentText, IIcon} import net.minecraft.world.IBlockAccess import net.minecraftforge.common.util.ForgeDirection import net.minecraftforge.fluids.{Fluid, FluidContainerRegistry, FluidRegistry, FluidStack} import resonantengine.lib.mod.config.Config import resonantengine.lib.transform.vector.Vector3 import resonantengine.lib.utility.FluidUtility import resonantengine.prefab.block.impl.TRotatable object TileGrate { @Config(comment = "The multiplier for the influence of the grate. Dependent on pressure.") private var grateEffectMultiplier: Double = 5 @Config(comment = "The speed in which the grate drains blocks. Dependent on grate block influence.") private var grateDrainSpeedMultiplier: Double = 0.01 @SideOnly(Side.CLIENT) private var iconFront: IIcon = _ @SideOnly(Side.CLIENT) private var iconSide: IIcon = _ } class TileGrate extends TileFluidProvider(Material.rock) with TRotatable { fluidNode = new NodeFluidPressure(this) private var gratePath: GratePathfinder = _ private var fillOver: Boolean = true isOpaqueCube = false normalRender = true override def getIcon(world: IBlockAccess, side: Int): IIcon = { if (side == getDirection.ordinal()) iconFront else iconSide } override def getIcon(side: Int, metadata: Int): IIcon = if (side == 1) iconFront else iconSide @SideOnly(Side.CLIENT) override def registerIcons(iconRegister: IIconRegister) { iconFront = iconRegister.registerIcon(Reference.prefix + "grate_front") iconSide = iconRegister.registerIcon(Reference.prefix + "grate") } override def readFromNBT(nbt: NBTTagCompound) { super.readFromNBT(nbt) fillOver = nbt.getBoolean("fillOver") } override def writeToNBT(nbt: NBTTagCompound) { super.writeToNBT(nbt) nbt.setBoolean("fillOver", fillOver) } override def canFill(from: ForgeDirection, fluid: Fluid): Boolean = getDirection != from override def canDrain(from: ForgeDirection, fluid: Fluid): Boolean = getDirection != from override def update() { super.update() if (!world.isRemote) { if (ticks % 10 == 0) { val pressure = fluidNode.asInstanceOf[NodeFluidPressure].pressure(getDirection) val blockEffect = Math.abs(pressure * grateEffectMultiplier).toInt fluidNode.getTank.setCapacity(Math.max(blockEffect * FluidContainerRegistry.BUCKET_VOLUME * grateDrainSpeedMultiplier, FluidContainerRegistry.BUCKET_VOLUME).toInt) if (pressure > 0) { //Output fluid if (fluidNode.getFluidAmount >= FluidContainerRegistry.BUCKET_VOLUME) { if (gratePath == null) { gratePath = new GratePathfinder(true) gratePath.startFill(toVectorWorld, fluidNode.getFluid.getFluid.getID) } val filledInWorld = gratePath.tryFill(fluidNode.getFluidAmount, blockEffect) fluidNode.drain(filledInWorld, true) } } else if (pressure < 0) { //Input fluid val maxDrain = fluidNode.getCapacity - fluidNode.getFluidAmount if (maxDrain > 0) { if (gratePath == null) { gratePath = new GratePathfinder(false) if (!gratePath.startDrain(toVector3)) { resetPath() } } if (gratePath != null && gratePath.tryPopulateDrainMap(blockEffect)) { fluidNode.fill(gratePath.tryDrain(maxDrain, true), true) } } } } } } def resetPath() { this.gratePath = null } override def fill(from: ForgeDirection, resource: FluidStack, doFill: Boolean): Int = fluidNode.fill(resource, doFill) override def drain(from: ForgeDirection, resource: FluidStack, doDrain: Boolean): FluidStack = { if (resource != null) { return drain(from, resource.amount, doDrain) } null } override def drain(from: ForgeDirection, maxDrain: Int, doDrain: Boolean): FluidStack = { fluidNode.drain(maxDrain, doDrain) } protected override def configure(player: EntityPlayer, side: Int, hit: Vector3): Boolean = { if (!player.isSneaking) { if (!world.isRemote) { fillOver = !fillOver player.addChatMessage(new ChatComponentText("Grate now set to " + (if (fillOver) "" else "not ") + "fill higher than itself.")) gratePath = null } return true } super.configure(player, side, hit) } class GratePathfinder(checkVertical: Boolean) { var fluidType: Fluid = _ var start: Vector3 = _ var navigationMap: HashMap[Vector3, Vector3] = new HashMap[Vector3, Vector3]() var workingNodes: PriorityQueue[ComparableVector] = if (checkVertical) new PriorityQueue[ComparableVector]() else new PriorityQueue[ComparableVector](1024, new Comparator[ComparableVector]() { override def compare(a: ComparableVector, b: ComparableVector): Int = { val wa = a.asInstanceOf[ComparableVector] val wb = b.asInstanceOf[ComparableVector] wa.iterations - wb.iterations } }) var drainNodes = new PriorityQueue[ComparableVector](1024, Collections.reverseOrder()) def startFill(start: Vector3, fluidID: Int) { this.fluidType = FluidRegistry.getFluid(fluidID) this.start = start val check = start + TileGrate.this.getDirection navigationMap.put(check, start) workingNodes.add(new ComparableVector(check, 0)) } def tryFill(amount: Int, tries: Int): Int = { var filled = 0 if (amount >= FluidContainerRegistry.BUCKET_VOLUME) { for (i <- 0 until tries) { val next = workingNodes.poll() if (next == null) { TileGrate.this.resetPath() return 0 } if (!isConnected(next.position)) { TileGrate.this.resetPath() return 0 } val didFill = FluidUtility.fillBlock(TileGrate.this.worldObj, next.position, new FluidStack(fluidType, amount), true) filled += didFill if (FluidUtility.getFluidAmountFromBlock(TileGrate.this.worldObj, next.position) > 0 || didFill > 0) { addNextFill(next) } if (filled >= amount) { return filled } } } filled } def addNextFill(next: ComparableVector) { for (i <- 0 until 6) { val newPosition = next.position.clone().add(ForgeDirection.getOrientation(i)) if (!this.navigationMap.containsKey(newPosition) && !(!fillOver && newPosition.yi > y)) { this.navigationMap.put(newPosition, next.position) this.workingNodes.add(new ComparableVector(newPosition, next.iterations + 1)) } } } def isConnected(nCheck: Vector3): Boolean = { var check = nCheck if (check.equals(start)) return true //Trace back in the navigation map to see if it reaches the starting point do { check = navigationMap.get(check) if (check.equals(null)) return false if (check.equals(start)) return true } while (FluidUtility.getFluidFromBlock(TileGrate.this.worldObj, check) != null && fluidType.getID == FluidUtility.getFluidFromBlock(TileGrate.this.worldObj, check).getID) return false } def startDrain(start: Vector3): Boolean = { fluidType = null this.start = start val check = start + TileGrate.this.getDirection navigationMap.put(check, start) workingNodes.add(new ComparableVector(check, 0)) fluidType = FluidUtility.getFluidFromBlock(TileGrate.this.worldObj, check) fluidType != null } def tryPopulateDrainMap(tries: Int): Boolean = { for (i <- 0 until tries) { val check = workingNodes.poll() if (check == null) { return true } val checkFluid = FluidUtility.getFluidFromBlock(TileGrate.this.worldObj, check.position) if (checkFluid != null && fluidType.getID == checkFluid.getID) { addNextDrain(check) val checkAmount = FluidUtility.getFluidAmountFromBlock(TileGrate.this.worldObj, check.position) if (checkAmount > 0) { drainNodes.add(check) } } } return drainNodes.size > 0 } def addNextDrain(next: ComparableVector) { for (i <- 0 until 6) { val check = next.position.clone().add(ForgeDirection.getOrientation(i)) val checkFluid = FluidUtility.getFluidFromBlock(TileGrate.this.worldObj, check) if (checkFluid != null && fluidType.getID == checkFluid.getID) { if (!navigationMap.containsKey(check)) { navigationMap.put(check, next.position) workingNodes.add(new ComparableVector(check, next.iterations + 1)) } } } } def tryDrain(targetAmount: Int, doDrain: Boolean): FluidStack = { var drainedAmount = 0 var break = false //Check all the potential drain coordinates while (!drainNodes.isEmpty && !break) { val fluidCoord = drainNodes.peek() if (!isConnected(fluidCoord.position)) { //The coordinate is not connected with the starting coordinate, therefore, we will not drain this. TileGrate.this.resetPath() return new FluidStack(fluidType, drainedAmount) } if (FluidUtility.getFluidFromBlock(TileGrate.this.worldObj, fluidCoord.position) == null || this.fluidType.getID != FluidUtility.getFluidFromBlock(TileGrate.this.worldObj, fluidCoord.position).getID) { drainNodes.poll() } else { val checkAmount = FluidUtility.getFluidAmountFromBlock(TileGrate.this.worldObj, fluidCoord.position) if (drainedAmount + checkAmount <= targetAmount) { if (checkAmount == 0) { drainNodes.poll() } else { val fluidStack = FluidUtility.drainBlock(TileGrate.this.worldObj, fluidCoord.position, doDrain, 2) //TODO: Do a delayed block update! drainNodes.poll() if (fluidStack != null) { drainedAmount += fluidStack.amount if (drainedAmount >= targetAmount) { break = true } } } } else { break = true } } } TileGrate.this.resetPath() if (drainedAmount > 0) { return new FluidStack(fluidType, drainedAmount) } null } } class ComparableVector(var position: Vector3, var iterations: Int) extends Comparable[ComparableVector] { override def compareTo(obj: ComparableVector): Int = { val wr = obj.asInstanceOf[ComparableVector] if (this.position.y == wr.position.y) { return this.iterations - wr.iterations } this.position.yi - wr.position.yi } } }