package com.austinv11.peripheralsplusplus.turtles.peripherals;
import com.austinv11.peripheralsplusplus.reference.Config;
import dan200.computercraft.api.lua.ILuaContext;
import dan200.computercraft.api.lua.LuaException;
import dan200.computercraft.api.peripheral.IComputerAccess;
import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.api.turtle.ITurtleAccess;
import dan200.computercraft.api.turtle.TurtleSide;
import net.minecraft.block.Block;
import net.minecraft.init.Blocks;
import net.minecraft.init.Items;
import net.minecraft.inventory.IInventory;
import net.minecraft.item.ItemBucket;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.AxisAlignedBB;
import net.minecraftforge.common.util.ForgeDirection;
import net.minecraftforge.fluids.*;
import java.util.HashMap;
public class PeripheralTank extends MountedPeripheral {
private ITurtleAccess turtle;
private TurtleSide side;
private FluidTank fluidTank = new FluidTank(Config.maxNumberOfMillibuckets);
private boolean doUpdate = false;
public PeripheralTank(ITurtleAccess turtle, TurtleSide side) {
this.turtle = turtle;
this.side = side;
if (!turtle.getWorld().isRemote) {
NBTTagCompound turtleTag = turtle.getUpgradeNBTData(side);
NBTTagCompound tankData = turtleTag.getCompoundTag("TankData");
if (tankData != null) {
fluidTank.readFromNBT(tankData);
}
}
}
@Override
public String getType() {
return "tank";
}
@Override
public String[] getMethodNames() {
return new String[] {"getFluid", "fill", "drain", "empty", "emptyUp", "emptyDown", "suck", "suckUp", "suckDown"};
}
@Override
public Object[] callMethod(IComputerAccess computer, ILuaContext context, int method, Object[] arguments) throws LuaException, InterruptedException {
ForgeDirection dir;
int posX, posY, posZ;
try {
switch (method) {
// "getFluid": Return information about the fluid within the tank
case 0:
if (fluidTank.getFluid() != null) {
HashMap<String, Object> map = new HashMap<String, Object>();
map.put("amount", fluidTank.getFluidAmount()); //
map.put("name", fluidTank.getFluid().getLocalizedName()); // Add appropriate information to return map
map.put("id", fluidTank.getFluid().getFluidID()); //
return new Object[]{map};
}
break;
// "fill": Move fluid from internal tank to container in specified slot / currently selected slot
// Returns amount of fluid moved
case 1:
int fillSlot; // The slot that contains the container
// If a slot is specified, check that it is valid and set it. If none is specified, use the currently selected slot
if (arguments.length > 1) {
if (arguments[0] instanceof Double) {
fillSlot = (int) (double) (Double) arguments[0];
} else {
throw new LuaException("Bad argument #1 (expected number)");
}
} else {
fillSlot = turtle.getSelectedSlot();
}
// Get the item in that slot and check that it is a valid container
ItemStack fillStack = turtle.getInventory().getStackInSlot(fillSlot);
if (fillStack.getItem() instanceof ItemBucket) {
// Check that the container is empty
if (((ItemBucket) fillStack.getItem()).isFull != Blocks.air) {
return new Object[]{0};
}
// Check that there is fluid to fill with
if (fluidTank.getFluid() == null) {
return new Object[]{0};
}
// Find a slot to put the full bucket in.
if (fillStack.stackSize > 1) {
int emptySlot = getFirstEmptySlotIndex(turtle.getInventory());
if (emptySlot == -1) {
return new Object[]{0};
}
turtle.getInventory().decrStackSize(fillSlot, 1);
fillSlot = emptySlot;
}
// Move the fluid
if (fluidTank.getFluid().getFluid().getBlock() == Blocks.water) {
turtle.getInventory().setInventorySlotContents(fillSlot, new ItemStack(Items.water_bucket));
} else if (fluidTank.getFluid().getFluid().getBlock() == Blocks.lava) {
turtle.getInventory().setInventorySlotContents(fillSlot, new ItemStack(Items.lava_bucket));
}
fluidTank.drain(1000, true);
doUpdate = true;
return new Object[]{1000};
} else if (fillStack.getItem() instanceof IFluidContainerItem) {
try {
IFluidContainerItem containerItem = (IFluidContainerItem) fillStack.getItem();
// Check that there is fluid to fill with
if (fluidTank.getFluid() == null) {
return new Object[]{0};
}
// Check that the fluids are equal
if (!fluidTank.getFluid().isFluidEqual(containerItem.getFluid(fillStack))) {
// Make sure that the container is not empty.
if (containerItem.getFluid(fillStack) != null) {
return new Object[]{0};
}
}
// Check that there is room in the container
if (containerItem.getFluid(fillStack) != null && containerItem.getFluid(fillStack).amount + 1000 > containerItem.getCapacity(fillStack)) {
return new Object[]{0};
}
containerItem.fill(fillStack, new FluidStack(fluidTank.getFluid(), 1000), true);
fluidTank.drain(1000, true);
doUpdate = true;
return new Object[]{1000};
} catch (Exception e) {
e.printStackTrace();
}
}
return new Object[]{0};
// "drain": Moves fluid from container in specified slot to internal tank
// Returns amount of fluid moved
case 2:
try {
int drainSlot; // The slot that contains the container
// If a slot is specified, check that it is valid and set it. If none is specified, use the currently selected slot
if (arguments.length > 1) {
if (arguments[0] instanceof Double) {
drainSlot = (int) (double) (Double) arguments[0];
} else {
throw new LuaException("Bad argument #1 (expected number)");
}
} else {
drainSlot = turtle.getSelectedSlot();
}
// Get the item in that slot and check that it is a valid container
ItemStack drainStack = turtle.getInventory().getStackInSlot(drainSlot);
if (!(drainStack.getItem() instanceof ItemBucket) && !(drainStack.getItem() instanceof IFluidContainerItem)) {
return new Object[]{0};
}
// Check that the fluid in the container matches the fluid in the internal tank
if (fluidTank.getFluid() != null) {
// Determine if the fluids match. Retrieve the container fluid based on type because people dumb and don't register their containers
boolean matches = fluidTank.getFluid().isFluidEqual(drainStack.getItem() instanceof ItemBucket ? FluidContainerRegistry.getFluidForFilledItem(drainStack) : ((IFluidContainerItem) drainStack.getItem()).getFluid(drainStack));
if (!matches) {
return new Object[]{0};
}
}
// Check that there is room for the fluid
if (fluidTank.getFluidAmount() + 1000 > fluidTank.getCapacity()) {
return new Object[]{0};
}
// Move the fluid
// Retrieve the fluid based on type because people dumb and don't register their containers
FluidStack fluidStack;
if (drainStack.getItem() instanceof ItemBucket) {
if (FluidContainerRegistry.getFluidForFilledItem(drainStack) == null) {
return new Object[] {0};
}
fluidStack = new FluidStack(FluidContainerRegistry.getFluidForFilledItem(drainStack), 1000);
} else {
fluidStack = new FluidStack(((IFluidContainerItem) drainStack.getItem()).getFluid(drainStack), 1000);
}
fluidTank.fill(fluidStack, true);
if (drainStack.getItem() instanceof ItemBucket) {
turtle.getInventory().setInventorySlotContents(drainSlot, new ItemStack(Items.bucket));
} else {
IFluidContainerItem containerItem = (IFluidContainerItem) drainStack.getItem();
containerItem.drain(drainStack, 1000, true);
}
doUpdate = true;
return new Object[]{1000};
} catch (Exception e) {
e.printStackTrace();
}
case 3:
case 4:
case 5:
// "empty[dir]" Places a block of the fluid in the internal tank in the world.
// Returns the amount of fluid emptied (always either 0 or 1000 for now)
if (arguments.length > 0 && !(arguments[0] instanceof Double))
throw new LuaException("Bad argument #1 (expected number)");
// Get the correct direction relative to the turtle depending on the method executed
dir = method == 3 ? ForgeDirection.getOrientation(turtle.getDirection()) : method == 4 ? ForgeDirection.UP : ForgeDirection.DOWN;
posX = turtle.getPosition().posX + dir.offsetX; //
posY = turtle.getPosition().posY + dir.offsetY; // Get the position of the block from the direction
posZ = turtle.getPosition().posZ + dir.offsetZ; //
// Check that the block where the turtle is trying to place the fluid is air
if (turtle.getWorld().isAirBlock(posX, posY, posZ)) {
if (fluidTank.getFluid() != null) {
if (fluidTank.getFluid().getFluid().canBePlacedInWorld()) {
Block fluidBlock = fluidTank.getFluid().getFluid().getBlock();
turtle.getWorld().setBlock(posX, posY, posZ, getFlowingBlock(fluidBlock)); // Place the flowing version of the fluid
fluidTank.drain(1000, true);
doUpdate = true;
return new Object[]{1000};
}
}
} else if (turtle.getWorld().getTileEntity(posX, posY, posZ) instanceof IFluidHandler) {
IFluidHandler fluidHandler = (IFluidHandler) turtle.getWorld().getTileEntity(posX, posY, posZ);
// Check that the fluids are the same
if (fluidHandler.getTankInfo(dir.getOpposite())[0].fluid != null) {
if (!fluidTank.getFluid().isFluidEqual(fluidHandler.getTankInfo(dir.getOpposite())[0].fluid)) {
return new Object[]{0};
}
if (fluidHandler.getTankInfo(dir.getOpposite())[0].fluid.amount + 1000 > fluidHandler.getTankInfo(dir.getOpposite())[0].capacity) {
return new Object[] {0};
}
}
if (fluidTank.getFluidAmount() - 1000 < 0) {
return new Object[]{0};
}
fluidHandler.fill(dir.getOpposite(), new FluidStack(fluidTank.getFluid().getFluid(), 1000), true);
fluidTank.drain(1000, true);
doUpdate = true;
return new Object[]{1000};
}
return new Object[] {0};
case 6:
case 7:
case 8:
// "suck[dir]" Takes a block of fluid from the world into the internal tank
// Returns the amount of fluid moved (always either 0 or 1000 for now)
if (arguments.length > 0 && !(arguments[0] instanceof Double))
throw new LuaException("Bad argument #1 (expected number)");
// Get the correct direction relative to the turtle depending on the method executed
dir = method == 6 ? ForgeDirection.getOrientation(turtle.getDirection()) : method == 7 ? ForgeDirection.UP : ForgeDirection.DOWN;
posX = turtle.getPosition().posX + dir.offsetX; //
posY = turtle.getPosition().posY + dir.offsetY; // Get the position of the block from the direction
posZ = turtle.getPosition().posZ + dir.offsetZ; //
Block block = turtle.getWorld().getBlock(posX, posY, posZ);
// Check that there is fluid in the specified space
if (turtle.getWorld().isAnyLiquid(AxisAlignedBB.getBoundingBox(posX, posY, posZ, posX, posY, posZ))) {
// We can only pick up the source versions of the block.
if (turtle.getWorld().getBlockMetadata(posX, posY, posZ) != 0) {
return new Object[]{0};
}
// Check that the fluid being taken is equal to the fluid in the internal tank (if there is any)
if (fluidTank.getFluid() != null) {
if (!fluidTank.getFluid().isFluidEqual(new FluidStack(FluidRegistry.lookupFluidForBlock(block), 0))) {
return new Object[]{0};
}
}
// Check that there is room for the fluid
if (fluidTank.getFluidAmount() + 1000 <= fluidTank.getCapacity()) {
// Move the fluid
fluidTank.fill(new FluidStack(FluidRegistry.lookupFluidForBlock(block), 1000), true);
turtle.getWorld().setBlock(posX, posY, posZ, Blocks.air);
doUpdate = true;
return new Object[]{1000};
}
} else if (turtle.getWorld().getTileEntity(posX, posY, posZ) instanceof IFluidHandler) {
IFluidHandler fluidHandler = (IFluidHandler) turtle.getWorld().getTileEntity(posX, posY, posZ);
if (fluidHandler.getTankInfo(dir.getOpposite())[0].fluid == null) {
return new Object[] {0};
}
// Check that the fluids are the same
if (fluidTank.getFluid() != null) {
if (!fluidHandler.getTankInfo(dir.getOpposite())[0].fluid.isFluidEqual(fluidTank.getFluid())) {
return new Object[]{0};
}
}
if (fluidHandler.getTankInfo(dir.getOpposite())[0].fluid.amount - 1000 < 0 && fluidTank.getFluidAmount() + 1000 <= fluidTank.getCapacity()) {
return new Object[]{0};
}
fluidTank.fill(new FluidStack(fluidHandler.getTankInfo(dir.getOpposite())[0].fluid.getFluid(), 1000), true);
fluidHandler.drain(dir.getOpposite(), 1000, true);
doUpdate = true;
return new Object[]{1000};
}
return new Object[]{0};
}
} catch (Exception e) {
e.printStackTrace();
}
return new Object[0];
}
public void update() {
if (doUpdate) {
NBTTagCompound newTag = new NBTTagCompound();
NBTTagCompound turtleTag = turtle.getUpgradeNBTData(side);
fluidTank.writeToNBT(newTag);
turtleTag.setTag("TankData", newTag);
turtle.updateUpgradeNBTData(side);
doUpdate = false;
}
}
/**
* Gets the flowing version of a fluid block. Only vanilla liquids are two separate blocks.
* @param staticBlock The static version of the fluid (water or lava)
* @return The flowing version (flowing_water or flowing_lava). If the staticBlock is neither, then it is a forge fluid which is already the flowing version.
*/
private Block getFlowingBlock(Block staticBlock) {
if (staticBlock == Blocks.water) {
return Blocks.flowing_water;
} else if (staticBlock == Blocks.lava) {
return Blocks.flowing_lava;
}
return staticBlock;
}
// Finds the first empty slot in the given inventory.
private int getFirstEmptySlotIndex(IInventory inv) {
for (int slot = 0; slot < inv.getSizeInventory(); slot++) {
if (inv.getStackInSlot(slot) == null) {
return slot;
}
}
return -1;
}
@Override
public boolean equals(IPeripheral other) {
return (this == other);
}
}