package erogenousbeef.bigreactors.common.tileentity.base;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraftforge.common.util.ForgeDirection;
import net.minecraftforge.fluids.Fluid;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.FluidTank;
import net.minecraftforge.fluids.FluidTankInfo;
import net.minecraftforge.fluids.IFluidHandler;
import net.minecraftforge.fluids.IFluidTank;
import erogenousbeef.bigreactors.common.interfaces.IMultipleFluidHandler;
public abstract class TileEntityPoweredInventoryFluid extends
TileEntityPoweredInventory implements IFluidHandler, IMultipleFluidHandler {
private FluidTank[] tanks;
private FluidTank[][] tankExposureCache;
protected static final FluidTank[] kEmptyFluidTankList = new FluidTank[0];
protected static final int FLUIDTANK_NONE = -1;
public TileEntityPoweredInventoryFluid() {
super();
tanks = new FluidTank[getNumTanks()];
tankExposureCache = new FluidTank[getNumTanks()][1];
for(int i = 0; i < getNumTanks(); i++) {
tanks[i] = new FluidTank(getTankSize(i));
tankExposureCache[i][0] = tanks[i];
}
}
// Internal Helpers
private void readFluidsFromNBT(NBTTagCompound tag) {
// Initialize tanks to empty, as we send sparse updates.
for(int i = 0; i < tanks.length; i++) {
tanks[i].setFluid(null);
}
if(tag.hasKey("fluids")) {
NBTTagList tagList = tag.getTagList("fluids", 10);
for(int i = 0; i < tagList.tagCount(); i++) {
NBTTagCompound fluidTag = tagList.getCompoundTagAt(i);
int fluidIdx = fluidTag.getInteger("tagIdx");
FluidStack newFluid = FluidStack.loadFluidStackFromNBT(fluidTag);
tanks[fluidIdx].setFluid(newFluid);
}
}
}
private void writeFluidsToNBT(NBTTagCompound tag) {
NBTTagList fluidTagList = new NBTTagList();
for(int i = 0; i < getNumTanks(); i++) {
if(tanks[i] != null && tanks[i].getFluid() != null) {
NBTTagCompound fluidTag = new NBTTagCompound();
fluidTag.setInteger("tagIdx", i);
tanks[i].getFluid().writeToNBT(fluidTag);
fluidTagList.appendTag(fluidTag);
}
}
if(fluidTagList.tagCount() > 0) {
tag.setTag("fluids", fluidTagList);
}
}
// TileEntity overrides
@Override
public void readFromNBT(NBTTagCompound tag) {
super.readFromNBT(tag);
readFluidsFromNBT(tag);
}
@Override
public void writeToNBT(NBTTagCompound tag) {
super.writeToNBT(tag);
writeFluidsToNBT(tag);
}
// TileEntityBeefBase
@Override
protected void onSendUpdate(NBTTagCompound updateTag) {
super.onSendUpdate(updateTag);
writeFluidsToNBT(updateTag);
}
@Override
public void onReceiveUpdate(NBTTagCompound updateTag) {
super.onReceiveUpdate(updateTag);
readFluidsFromNBT(updateTag);
}
// IFluidHandler
/**
* The number of fluid tanks in this machine.
* @return The number of fluid tanks in this machine.
*/
public abstract int getNumTanks();
/**
* Returns the size of the tank at a given position
* @param tankIndex The index of the given tank in the tanks array.
* @return The volume of the tank, in fluid units. 1000 = 1 Bucket.
*/
public abstract int getTankSize(int tankIndex);
/**
* Returns the index of the tank which is exposed on a given world side.
* Remember to translate this into a reference side!
* @param side The world side on which the device is being queried for fluid tank exposures.
* @return The index of the exposed tank, or -1 for none.
*/
public abstract int getExposedTankFromSide(int side);
/**
* Fills fluid into internal tanks, distribution is left to the ITankContainer.
* @param from Orientation the fluid is pumped in from.
* @param resource FluidStack representing the maximum amount of fluid filled into the ITankContainer
* @param doFill If false filling will only be simulated.
* @return Amount of resource that was filled into internal tanks.
*/
public int fill(ForgeDirection from, FluidStack resource, boolean doFill) {
int tankToFill = FLUIDTANK_NONE;
if(from != ForgeDirection.UNKNOWN) {
tankToFill = getExposedTankFromSide(from.ordinal());
}
else {
tankToFill = getDefaultTankForFluid(resource.getFluid());
}
if(tankToFill <= FLUIDTANK_NONE) {
return 0;
} else {
return fill(tankToFill, resource, doFill);
}
}
/**
* Fills fluid into the specified internal tank.
* @param tankIndex the index of the tank to fill
* @param resource FluidStack representing the maximum amount of fluid filled into the ITankContainer
* @param doFill If false filling will only be simulated.
* @return Amount of resource that was filled into internal tanks.
*/
public int fill(int tankIndex, FluidStack resource, boolean doFill) {
if(!isFluidValidForTank(tankIndex, resource)) {
return 0;
}
int res = tanks[tankIndex].fill(resource, doFill);
return res;
}
/** Drains fluid out of internal tanks, distribution is left entirely to the IFluidHandler.
*
* This method is not Fluid-sensitive.
* @param from Orientation the fluid is drained to.
* @param maxDrain Maximum amount of fluid to drain.
* @param doDrain If false draining will only be simulated.
* @return FluidStack representing the fluid and amount actually drained from the ITankContainer
*/
public FluidStack drain(ForgeDirection from, int maxDrain, boolean doDrain) {
int tankToDrain = 0;
if(from != ForgeDirection.UNKNOWN) {
tankToDrain = getExposedTankFromSide(from.ordinal());
}
if(tankToDrain <= FLUIDTANK_NONE) {
return null;
} else {
return drain(tankToDrain, maxDrain, doDrain);
}
}
/**
* Drains fluid out of the specified internal tank.
* @param tankIndex the index of the tank to drain
* @param maxDrain Maximum amount of fluid to drain.
* @param doDrain If false draining will only be simulated.
* @return FluidStack representing the fluid and amount actually drained from the ITankContainer
*/
public FluidStack drain(int tankIndex, int maxDrain, boolean doDrain) {
return tanks[tankIndex].drain(maxDrain, doDrain);
}
/**
* Drains fluid out of internal tanks, distribution is left entirely to the IFluidHandler.
*
* @param from
* Orientation the Fluid is drained to.
* @param resource
* FluidStack representing the Fluid and maximum amount of fluid to be drained.
* @param doDrain
* If false, drain will only be simulated.
* @return FluidStack representing the Fluid and amount that was (or would have been, if
* simulated) drained.
*/
public FluidStack drain(ForgeDirection from, FluidStack resource, boolean doDrain) {
int tankToDrain = 0;
if(from != ForgeDirection.UNKNOWN) {
tankToDrain = getExposedTankFromSide(from.ordinal());
}
if(tankToDrain == FLUIDTANK_NONE) {
return null;
}
else {
// Can't drain that fluid from that side.
if(!resource.isFluidEqual( tanks[tankToDrain].getFluid() )) {
return null;
}
return drain(tankToDrain, resource.amount, doDrain);
}
}
/**
* @param direction tank side: UNKNOWN for default tank set
* @return Array of {@link FluidTank}s contained in this ITankContainer for this direction
*/
public IFluidTank[] getTanks(ForgeDirection direction) {
if(direction == ForgeDirection.UNKNOWN) {
return tanks;
}
else {
int exposure = getExposedTankFromSide(direction.ordinal());
if(exposure == FLUIDTANK_NONE) {
return kEmptyFluidTankList;
}
return tankExposureCache[exposure];
}
}
/**
* Return the tank that this tank container desired to be used for the specified fluid type from the specified direction
*
* @param direction the direction
* @param type the fluid type, null is always an acceptable value
* @return a tank or null for no such tank
*/
public IFluidTank getTank(ForgeDirection direction, FluidStack type) {
if(direction == ForgeDirection.UNKNOWN) {
return null;
}
else {
int tankIdx = getExposedTankFromSide(direction.ordinal());
if(tankIdx == FLUIDTANK_NONE) {
return null;
}
IFluidTank t = tanks[tankIdx];
if(type == null || isFluidValidForTank(tankIdx, type)) {
return t;
}
return null;
}
}
/**
* Returns true if the given fluid can be inserted into the given direction.
*
* More formally, this should return true if fluid is able to enter from the given direction.
*/
public boolean canFill(ForgeDirection from, Fluid fluid) {
int tankIdx = 0;
if(from != ForgeDirection.UNKNOWN) {
tankIdx = getExposedTankFromSide(from.ordinal());
}
if(tankIdx == FLUIDTANK_NONE) { return false; }
IFluidTank tank = tanks[tankIdx];
if(tank.getFluidAmount() <= 0) {
return true;
}
else {
return tank.getFluid().fluidID == fluid.getID();
}
}
/**
* Returns true if the given fluid can be extracted from the given direction.
*
* More formally, this should return true if fluid is able to leave from the given direction.
*/
public boolean canDrain(ForgeDirection from, Fluid fluid) {
int tankIdx = 0;
if(from != ForgeDirection.UNKNOWN) {
tankIdx = getExposedTankFromSide(from.ordinal());
}
if(tankIdx == FLUIDTANK_NONE) { return false; }
IFluidTank tank = tanks[tankIdx];
if(tank.getFluidAmount() <= 0) {
return false;
}
else {
return tank.getFluid().fluidID == fluid.getID();
}
}
/**
* Returns an array of objects which represent the internal tanks. These objects cannot be used
* to manipulate the internal tanks. See {@link FluidTankInfo}.
*
* @param from
* Orientation determining which tanks should be queried.
* @return Info for the relevant internal tanks.
*/
public FluidTankInfo[] getTankInfo(ForgeDirection from) {
return getTankInfo();
}
/**
* Returns an array of objects which represent the internal tanks. These objects cannot be used
* to manipulate the internal tanks. See {@link FluidTankInfo}.
*
* @return Info for the relevant internal tanks.
*/
public FluidTankInfo[] getTankInfo() {
FluidTankInfo[] infos = new FluidTankInfo[tanks.length];
for(int i = 0; i < tanks.length; i++) {
infos[i] = tanks[i].getInfo();
}
return infos;
}
/**
* Check if the given fluid is valid for the given tank.
* Note that the fluid may be null.
*
* @param tankIdx The index of the tank to check validity for.
* @param type The fluid to check validity for, or null.
* @return True if the fluid can go in the identified tank, false otherwise.
*/
protected abstract boolean isFluidValidForTank(int tankIdx, FluidStack type);
// Helpers
/**
* @param fluid The fluid whose default tank is being queried.
* @return The index of the tank into which a given fluid should be deposited by default.
*/
protected abstract int getDefaultTankForFluid(Fluid fluid);
}