package blusunrize.immersiveengineering.common.blocks.metal;
import blusunrize.immersiveengineering.api.ApiUtils;
import blusunrize.immersiveengineering.api.IEEnums.SideConfig;
import blusunrize.immersiveengineering.api.IEProperties;
import blusunrize.immersiveengineering.api.IEProperties.PropertyBoolInverted;
import blusunrize.immersiveengineering.api.Lib;
import blusunrize.immersiveengineering.api.MultiblockHandler.IMultiblock;
import blusunrize.immersiveengineering.api.crafting.IMultiblockRecipe;
import blusunrize.immersiveengineering.api.crafting.IngredientStack;
import blusunrize.immersiveengineering.api.energy.immersiveflux.FluxStorage;
import blusunrize.immersiveengineering.api.energy.immersiveflux.FluxStorageAdvanced;
import blusunrize.immersiveengineering.common.blocks.IEBlockInterfaces.*;
import blusunrize.immersiveengineering.common.blocks.TileEntityMultiblockPart;
import blusunrize.immersiveengineering.common.util.ChatUtils;
import blusunrize.immersiveengineering.common.util.EnergyHelper.IEForgeEnergyWrapper;
import blusunrize.immersiveengineering.common.util.EnergyHelper.IIEInternalFluxHandler;
import blusunrize.immersiveengineering.common.util.Utils;
import blusunrize.immersiveengineering.common.util.inventory.IIEInventory;
import net.minecraft.block.state.IBlockState;
import net.minecraft.entity.item.EntityItem;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.text.TextComponentTranslation;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.IFluidTank;
import net.minecraftforge.fluids.capability.IFluidHandler;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
import net.minecraftforge.items.IItemHandlerModifiable;
import net.minecraftforge.items.ItemHandlerHelper;
import net.minecraftforge.oredict.OreDictionary;
import javax.annotation.Nonnull;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public abstract class TileEntityMultiblockMetal<T extends TileEntityMultiblockMetal<T, R>, R extends IMultiblockRecipe> extends TileEntityMultiblockPart<T> implements IIEInventory, IIEInternalFluxHandler, IHammerInteraction, IMirrorAble, IProcessTile
{
/**H L W*/
protected final int[] structureDimensions;
public final FluxStorageAdvanced energyStorage;
protected final boolean hasRedstoneControl;
protected final IMultiblock mutliblockInstance;
protected boolean redstoneControlInverted = false;
public int controllingComputers = 0;
public boolean computerOn = true;
public TileEntityMultiblockMetal(IMultiblock mutliblockInstance, int[] structureDimensions, int energyCapacity, boolean redstoneControl)
{
this.structureDimensions = structureDimensions;
this.energyStorage = new FluxStorageAdvanced(energyCapacity);
this.hasRedstoneControl = redstoneControl;
this.mutliblockInstance = mutliblockInstance;
}
// =================================
// DATA MANAGEMENT
// =================================
@Override
public void readCustomNBT(NBTTagCompound nbt, boolean descPacket)
{
super.readCustomNBT(nbt, descPacket);
energyStorage.readFromNBT(nbt);
redstoneControlInverted = nbt.getBoolean("redstoneControlInverted");
NBTTagList processNBT = nbt.getTagList("processQueue", 10);
processQueue.clear();
for(int i=0; i<processNBT.tagCount(); i++)
{
NBTTagCompound tag = processNBT.getCompoundTagAt(i);
IMultiblockRecipe recipe = readRecipeFromNBT(tag);
if(recipe!=null)
{
int processTick = tag.getInteger("process_processTick");
MultiblockProcess process = loadProcessFromNBT(tag);
if(process!=null)
{
process.processTick = processTick;
processQueue.add(process);
}
}
}
if(descPacket)
{
controllingComputers = nbt.getBoolean("computerControlled") ? 1 : 0;
computerOn = nbt.getBoolean("computerOn");
}
}
@Override
public void writeCustomNBT(NBTTagCompound nbt, boolean descPacket)
{
super.writeCustomNBT(nbt, descPacket);
energyStorage.writeToNBT(nbt);
nbt.setBoolean("redstoneControlInverted", redstoneControlInverted);
NBTTagList processNBT = new NBTTagList();
for(MultiblockProcess process : this.processQueue)
processNBT.appendTag(writeProcessToNBT(process));
nbt.setTag("processQueue", processNBT);
if(descPacket)
{
nbt.setBoolean("computerControlled", controllingComputers > 0);
nbt.setBoolean("computerOn", computerOn);
}
}
protected abstract R readRecipeFromNBT(NBTTagCompound tag);
protected MultiblockProcess loadProcessFromNBT(NBTTagCompound tag)
{
IMultiblockRecipe recipe = readRecipeFromNBT(tag);
if(recipe!=null)
if(isInWorldProcessingMachine())
return new MultiblockProcessInWorld(recipe, tag.getFloat("process_transformationPoint"), Utils.loadItemStacksFromNBT(tag.getTag("process_inputItem")));
else
return new MultiblockProcessInMachine(recipe, tag.getIntArray("process_inputSlots")).setInputTanks(tag.getIntArray("process_inputTanks"));
return null;
}
protected NBTTagCompound writeProcessToNBT(MultiblockProcess process)
{
NBTTagCompound tag = process.recipe.writeToNBT(new NBTTagCompound());
tag.setInteger("process_processTick", process.processTick);
process.writeExtraDataToNBT(tag);
return tag;
}
// =================================
// ENERGY MANAGEMENT
// =================================
public abstract int[] getEnergyPos();
public boolean isEnergyPos()
{
for(int i : getEnergyPos())
if(pos==i)
return true;
return false;
}
@Nonnull
@Override
public FluxStorage getFluxStorage()
{
T master = this.master();
if(master!=null)
return master.energyStorage;
return energyStorage;
}
@Nonnull
@Override
public SideConfig getEnergySideConfig(EnumFacing facing)
{
return this.formed&&this.isEnergyPos()?SideConfig.INPUT:SideConfig.NONE;
}
IEForgeEnergyWrapper wrapper = new IEForgeEnergyWrapper(this, null);
@Override
public IEForgeEnergyWrapper getCapabilityWrapper(EnumFacing facing)
{
if(this.formed&&this.isEnergyPos())
return wrapper;
return null;
}
@Override
public void postEnergyTransferUpdate(int energy, boolean simulate)
{
if(!simulate)
this.updateMasterBlock(null, energy!=0);
}
@SideOnly(Side.CLIENT)
@Override
public AxisAlignedBB getRenderBoundingBox()
{
if(!isDummy())
{
BlockPos nullPos = this.getBlockPosForPos(0);
return new AxisAlignedBB(nullPos, nullPos.offset(facing,structureDimensions[1]).offset(mirrored?facing.rotateYCCW():facing.rotateY(),structureDimensions[2]).up(structureDimensions[0]));
}
return super.getRenderBoundingBox();
}
// =================================
// REDSTONE CONTROL
// =================================
public abstract int[] getRedstonePos();
public boolean isRedstonePos()
{
if(!hasRedstoneControl || getRedstonePos()==null)
return false;
for(int i : getRedstonePos())
if(pos==i)
return true;
return false;
}
@Override
public boolean hammerUseSide(EnumFacing side, EntityPlayer player, float hitX, float hitY, float hitZ)
{
if(this.isRedstonePos())
{
TileEntityMultiblockMetal<T, R> master = master();
master.redstoneControlInverted = !master.redstoneControlInverted;
ChatUtils.sendServerNoSpamMessages(player, new TextComponentTranslation(Lib.CHAT_INFO+"rsControl."+(master.redstoneControlInverted?"invertedOn":"invertedOff")));
this.updateMasterBlock(null, true);
return true;
}
return false;
}
public boolean isRSDisabled()
{
if(controllingComputers > 0 && !computerOn)
return true;
int[] rsPositions = getRedstonePos();
if(rsPositions==null || rsPositions.length<1)
return false;
for(int rsPos : rsPositions)
{
T tile = this.getTileForPos(rsPos);
if(tile!=null)
{
boolean b = worldObj.isBlockIndirectlyGettingPowered(tile.getPos())>0;
return redstoneControlInverted != b;
}
}
return false;
}
// =================================
// POSITION MANAGEMENT
// =================================
public BlockPos getBlockPosForPos(int targetPos)
{
int blocksPerLevel = structureDimensions[1]*structureDimensions[2];
// dist = target position - current position
int distH = (targetPos/blocksPerLevel)-(pos/blocksPerLevel);
int distL = (targetPos%blocksPerLevel / structureDimensions[2])-(pos%blocksPerLevel / structureDimensions[2]);
int distW = (targetPos%structureDimensions[2])-(pos%structureDimensions[2]);
int w = mirrored?-distW:distW;
return getPos().offset(facing, distL).offset(facing.rotateY(), w).add(0, distH, 0);
}
public T getTileForPos(int targetPos)
{
BlockPos target = getBlockPosForPos(targetPos);
TileEntity tile = worldObj.getTileEntity(target);
if(this.getClass().isInstance(tile))
return (T)tile;
return null;
}
@Override
public ItemStack getOriginalBlock()
{
if(pos<0)
return null;
ItemStack s = null;
try{
int blocksPerLevel = structureDimensions[1]*structureDimensions[2];
int h = (pos/blocksPerLevel);
int l = (pos%blocksPerLevel / structureDimensions[2]);
int w = (pos%structureDimensions[2]);
s = this.mutliblockInstance.getStructureManual()[h][l][w];
}catch(Exception e){e.printStackTrace();}
return s!=null?s.copy():null;
}
@Override
public void disassemble()
{
if(formed && !worldObj.isRemote)
{
BlockPos startPos = this.getBlockPosForPos(0);
for(int yy=0;yy<structureDimensions[0];yy++)
for(int ll=0;ll<structureDimensions[1];ll++)
for(int ww=0;ww<structureDimensions[2];ww++)
{
int w = mirrored?-ww:ww;
BlockPos pos = startPos.offset(facing, ll).offset(facing.rotateY(), w).add(0, yy, 0);
ItemStack s = null;
TileEntity te = worldObj.getTileEntity(pos);
if(te instanceof TileEntityMultiblockMetal)
{
s = ((TileEntityMultiblockMetal)te).getOriginalBlock();
((TileEntityMultiblockMetal)te).formed=false;
}
if(pos.equals(getPos()))
s = this.getOriginalBlock();
IBlockState state = Utils.getStateFromItemStack(s);
if(state!=null)
{
if(pos.equals(getPos()))
worldObj.spawnEntityInWorld(new EntityItem(worldObj, pos.getX()+.5,pos.getY()+.5,pos.getZ()+.5, s));
else
replaceStructureBlock(pos, state, s, yy,ll,ww);
}
}
}
}
public void replaceStructureBlock(BlockPos pos, IBlockState state, ItemStack stack, int h, int l, int w)
{
if(state.getBlock()==this.getBlockType())
worldObj.setBlockToAir(pos);
worldObj.setBlockState(pos, state);
TileEntity tile = worldObj.getTileEntity(pos);
if(tile instanceof ITileDrop)
((ITileDrop)tile).readOnPlacement(null, stack);
}
@Override
public boolean getIsMirrored()
{
return this.mirrored;
}
@Override
public PropertyBoolInverted getBoolProperty(Class<? extends IUsesBooleanProperty> inf)
{
return IEProperties.BOOLEANS[0];
}
// =================================
// PROCESS MANAGEMENT
// =================================
public List<MultiblockProcess<R>> processQueue = new ArrayList<MultiblockProcess<R>>();
public int tickedProcesses = 0;
@Override
public void update()
{
tickedProcesses = 0;
if(worldObj.isRemote || isDummy() || isRSDisabled())
return;
int max = getMaxProcessPerTick();
int i = 0;
Iterator<MultiblockProcess<R>> processIterator = processQueue.iterator();
tickedProcesses = 0;
while(processIterator.hasNext() && i++<max)
{
MultiblockProcess<R> process = processIterator.next();
if(process.canProcess(this))
{
process.doProcessTick(this);
tickedProcesses++;
}
if(process.clearProcess)
processIterator.remove();
}
}
public abstract IFluidTank[] getInternalTanks();
public abstract R findRecipeForInsertion(ItemStack inserting);
public abstract int[] getOutputSlots();
public abstract int[] getOutputTanks();
public abstract boolean additionalCanProcessCheck(MultiblockProcess<R> process);
public abstract void doProcessOutput(ItemStack output);
public abstract void doProcessFluidOutput(FluidStack output);
public abstract void onProcessFinish(MultiblockProcess<R> process);
public abstract int getMaxProcessPerTick();
public abstract int getProcessQueueMaxLength();
public abstract float getMinProcessDistance(MultiblockProcess<R> process);
public abstract boolean isInWorldProcessingMachine();
public boolean addProcessToQueue(MultiblockProcess<R> process, boolean simulate)
{
return addProcessToQueue(process, simulate, false);
}
public boolean addProcessToQueue(MultiblockProcess<R> process, boolean simulate, boolean addToPrevious)
{
if(addToPrevious&&process instanceof MultiblockProcessInWorld)
{
for(MultiblockProcess<R> curr:processQueue)
if(curr instanceof MultiblockProcessInWorld && process.recipe.equals(curr.recipe))
{
MultiblockProcessInWorld p = (MultiblockProcessInWorld)curr;
boolean canStack = true;
for(ItemStack old : (List<ItemStack>)p.inputItems)
{
for(ItemStack in : (List<ItemStack>)((MultiblockProcessInWorld)process).inputItems)
if(OreDictionary.itemMatches(old, in, true) && Utils.compareItemNBT(old, in))
if(old.stackSize+in.stackSize>old.getMaxStackSize())
{
canStack = false;
break;
}
if(!canStack)
break;
}
if(canStack)
{
if(!simulate)
for(ItemStack old : (List<ItemStack>)p.inputItems)
{
for(ItemStack in : (List<ItemStack>)((MultiblockProcessInWorld)process).inputItems)
if(OreDictionary.itemMatches(old, in, true) && Utils.compareItemNBT(old, in))
{
old.stackSize+=in.stackSize;
break;
}
}
return true;
}
}
}
if(getProcessQueueMaxLength()<0 || processQueue.size() < getProcessQueueMaxLength())
{
float dist = 1;
MultiblockProcess<R> p = null;
if(processQueue.size()>0)
{
p = processQueue.get(processQueue.size() - 1);
if(p != null)
dist = p.processTick / (float) p.maxTicks;
}
if(p != null && dist < getMinProcessDistance(p))
return false;
if(!simulate)
processQueue.add(process);
return true;
}
return false;
}
@Override
public int[] getCurrentProcessesStep()
{
T master = master();
if(master!=this && master!=null)
return master.getCurrentProcessesStep();
int[] ia = new int[processQueue.size()];
for(int i=0; i<ia.length; i++)
ia[i] = processQueue.get(i).processTick;
return ia;
}
@Override
public int[] getCurrentProcessesMax()
{
T master = master();
if(master!=this && master!=null)
return master.getCurrentProcessesMax();
int[] ia = new int[processQueue.size()];
for(int i=0; i<ia.length; i++)
ia[i] = processQueue.get(i).maxTicks;
return ia;
}
public boolean shouldRenderAsActive()
{
return (controllingComputers <= 0 || computerOn) && getEnergyStored(null) > 0 && !isRSDisabled() && !processQueue.isEmpty();
}
public abstract static class MultiblockProcess<R extends IMultiblockRecipe>
{
public R recipe;
public int processTick;
public int maxTicks;
public int energyPerTick;
public boolean clearProcess = false;
public MultiblockProcess(R recipe)
{
this.recipe = recipe;
this.processTick = 0;
this.maxTicks = this.recipe.getTotalProcessTime();
this.energyPerTick = this.recipe.getTotalProcessEnergy()/this.maxTicks;
}
protected List<ItemStack> getRecipeItemOutputs(TileEntityMultiblockMetal multiblock)
{
return recipe.getActualItemOutputs(multiblock);
}
protected List<FluidStack> getRecipeFluidOutputs(TileEntityMultiblockMetal multiblock)
{
return recipe.getActualFluidOutputs(multiblock);
}
public boolean canProcess(TileEntityMultiblockMetal multiblock)
{
if(multiblock.energyStorage.extractEnergy(energyPerTick, true)==energyPerTick)
{
List<ItemStack> outputs = recipe.getItemOutputs();
if(outputs!=null && !outputs.isEmpty())
{
int[] outputSlots = multiblock.getOutputSlots();
for(ItemStack output : outputs)
if(output!=null)
{
boolean canOutput = false;
if(outputSlots==null)
canOutput = true;
else
{
for(int iOutputSlot : outputSlots)
{
ItemStack s = multiblock.getInventory()[iOutputSlot];
if(s==null || (ItemHandlerHelper.canItemStacksStack(s, output) && s.stackSize+output.stackSize<= multiblock.getSlotLimit(iOutputSlot)))
{
canOutput = true;
break;
}
}
}
if(!canOutput)
return false;
}
}
List<FluidStack> fluidOutputs = recipe.getFluidOutputs();
if(fluidOutputs!=null && !fluidOutputs.isEmpty())
{
IFluidTank[] tanks = multiblock.getInternalTanks();
int[] outputTanks = multiblock.getOutputTanks();
for(FluidStack output : fluidOutputs)
if(output!=null && output.amount>0)
{
boolean canOutput = false;
if(tanks==null || outputTanks==null)
canOutput = true;
else
{
for(int iOutputTank : outputTanks)
if(iOutputTank>=0&&iOutputTank<tanks.length && tanks[iOutputTank]!=null && tanks[iOutputTank].fill(output,false)==output.amount)
{
canOutput = true;
break;
}
}
if(!canOutput)
return false;
}
}
return multiblock.additionalCanProcessCheck(this);
}
return false;
}
public void doProcessTick(TileEntityMultiblockMetal multiblock)
{
int energyExtracted = energyPerTick;
int ticksAdded = 1;
if(this.recipe.getMultipleProcessTicks()>1)
{
//Average Insertion, tracked by the advanced flux storage
int averageInsertion = multiblock.energyStorage.getAverageInsertion();
//Average Insertion musn'T be greater than possible extraction
averageInsertion = multiblock.energyStorage.extractEnergy(averageInsertion, true);
if(averageInsertion>energyExtracted)
{
int possibleTicks = Math.min(averageInsertion/energyPerTick, Math.min(this.recipe.getMultipleProcessTicks(), this.maxTicks-this.processTick));
if(possibleTicks>1)
{
ticksAdded = possibleTicks;
energyExtracted *= ticksAdded;
}
}
}
multiblock.energyStorage.extractEnergy(energyExtracted, false);
this.processTick+=ticksAdded;
if(this.processTick>=this.maxTicks)
{
this.processFinish(multiblock);
}
}
protected void processFinish(TileEntityMultiblockMetal multiblock)
{
List<ItemStack> outputs = getRecipeItemOutputs(multiblock);
if(outputs!=null && !outputs.isEmpty())
{
int[] outputSlots = multiblock.getOutputSlots();
for(ItemStack output : outputs)
if(output!=null)
if(outputSlots==null || multiblock.getInventory()==null)
multiblock.doProcessOutput(output.copy());
else
{
for(int iOutputSlot:outputSlots)
{
ItemStack s = multiblock.getInventory()[iOutputSlot];
if(s==null)
{
multiblock.getInventory()[iOutputSlot] = output.copy();
break;
}
else if(ItemHandlerHelper.canItemStacksStack(s, output) && s.stackSize+output.stackSize<= multiblock.getSlotLimit(iOutputSlot))
{
multiblock.getInventory()[iOutputSlot].stackSize += output.stackSize;
break;
}
}
}
}
List<FluidStack> fluidOutputs = getRecipeFluidOutputs(multiblock);
if(fluidOutputs!=null && !fluidOutputs.isEmpty())
{
IFluidTank[] tanks = multiblock.getInternalTanks();
int[] outputTanks = multiblock.getOutputTanks();
for(FluidStack output : fluidOutputs)
if(output!=null && output.amount>0)
{
if(tanks==null || outputTanks==null)
multiblock.doProcessFluidOutput(output);
else
{
for(int iOutputTank : outputTanks)
if(iOutputTank >= 0&&iOutputTank < tanks.length&&tanks[iOutputTank]!=null&&tanks[iOutputTank].fill(output, false)==output.amount)
{
tanks[iOutputTank].fill(output, true);
break;
}
}
}
}
multiblock.onProcessFinish(this);
this.clearProcess = true;
}
protected abstract void writeExtraDataToNBT(NBTTagCompound nbt);
}
public static class MultiblockProcessInMachine<R extends IMultiblockRecipe> extends MultiblockProcess<R>
{
protected int[] inputSlots = new int[0];
protected int[] inputTanks = new int[0];
public MultiblockProcessInMachine(R recipe, int... inputSlots)
{
super(recipe);
this.inputSlots = inputSlots;
}
public MultiblockProcessInMachine setInputTanks(int... inputTanks)
{
this.inputTanks = inputTanks;
return this;
}
public int[] getInputSlots()
{
return this.inputSlots;
}
public int[] getInputTanks()
{
return this.inputTanks;
}
protected List<IngredientStack> getRecipeItemInputs(TileEntityMultiblockMetal multiblock)
{
return recipe.getItemInputs();
}
protected List<FluidStack> getRecipeFluidInputs(TileEntityMultiblockMetal multiblock)
{
return recipe.getFluidInputs();
}
@Override
public void doProcessTick(TileEntityMultiblockMetal multiblock)
{
ItemStack[] inv = multiblock.getInventory();
if(recipe.getItemInputs()!=null && inv!=null)
{
ItemStack[] query = new ItemStack[inputSlots.length];
for(int i=0; i<inputSlots.length; i++)
if(inputSlots[i]>=0&&inputSlots[i]<inv.length)
query[i] = multiblock.getInventory()[inputSlots[i]];
if(!ApiUtils.stacksMatchIngredientList(recipe.getItemInputs(), query))
{
this.clearProcess = true;
return;
}
}
// FluidTank[] tanks = multiblock.getInternalTanks();
// if(tanks!=null)
// {
// ItemStack[] query = new ItemStack[inputSlots.length];
// for(int i=0; i inputSlots.length; i++)
// if(inputSlots[i]>=0&&inputSlots[i]<inv.length)
// query[i] = multiblock.getInventory()[inputSlots[i]];
// if(!ApiUtils.stacksMatchIngredientList(recipe.getItemInputs(), query))
// {
// this.clearProcess = true;
// return;
// }
// }
super.doProcessTick(multiblock);
}
@Override
protected void processFinish(TileEntityMultiblockMetal multiblock)
{
super.processFinish(multiblock);
ItemStack[] inv = multiblock.getInventory();
List<IngredientStack> itemInputList = this.getRecipeItemInputs(multiblock);
if(inv != null && this.inputSlots != null && itemInputList != null)
{
Iterator<IngredientStack> iterator = new ArrayList(itemInputList).iterator();
while(iterator.hasNext())
{
IngredientStack ingr = iterator.next();
int ingrSize = ingr.inputSize;
for(int slot : this.inputSlots)
if(inv[slot] != null && ingr.matchesItemStackIgnoringSize(inv[slot]))
{
int taken = Math.min(inv[slot].stackSize, ingrSize);
if((inv[slot].stackSize -= taken) <= 0)
inv[slot] = null;
if((ingrSize -= taken) <= 0)
break;
}
}
}
IFluidTank[] tanks = multiblock.getInternalTanks();
List<FluidStack> fluidInputList = this.getRecipeFluidInputs(multiblock);
if(tanks != null && this.inputTanks != null && fluidInputList != null)
{
Iterator<FluidStack> iterator = new ArrayList(fluidInputList).iterator();
while(iterator.hasNext())
{
FluidStack ingr = iterator.next();
int ingrSize = ingr.amount;
for(int tank : this.inputTanks)
if(tanks[tank]!=null)
{
if(tanks[tank] instanceof IFluidHandler && ((IFluidHandler)tanks[tank]).drain(ingr, false)!=null)
{
FluidStack taken = ((IFluidHandler)tanks[tank]).drain(ingr, true);
if((ingrSize -= taken.amount) <= 0)
break;
}
else if(tanks[tank].getFluid()!=null&&tanks[tank].getFluid().isFluidEqual(ingr))
{
int taken = Math.min(tanks[tank].getFluidAmount(), ingrSize);
tanks[tank].drain(taken, true);
if((ingrSize -= taken) <= 0)
break;
}
}
}
}
}
@Override
protected void writeExtraDataToNBT(NBTTagCompound nbt)
{
if(inputSlots!=null)
nbt.setIntArray("process_inputSlots", inputSlots);
if(inputTanks!=null)
nbt.setIntArray("process_inputTanks", inputTanks);
}
}
public static class MultiblockProcessInWorld<R extends IMultiblockRecipe> extends MultiblockProcess<R>
{
public List<ItemStack> inputItems;
protected float transformationPoint;
public MultiblockProcessInWorld(R recipe, float transformationPoint, ItemStack... inputItem)
{
super(recipe);
this.inputItems = new ArrayList<>(inputItem.length);
for(ItemStack s : inputItem)
this.inputItems.add(s);
this.transformationPoint = transformationPoint;
}
public List<ItemStack> getDisplayItem()
{
if(processTick / (float)maxTicks > transformationPoint)
{
List<ItemStack> list = this.recipe.getItemOutputs();
if(!list.isEmpty())
return list;
}
return inputItems;
}
@Override
protected void writeExtraDataToNBT(NBTTagCompound nbt)
{
nbt.setTag("process_inputItem", Utils.writeInventory(inputItems));
nbt.setFloat("process_transformationPoint", transformationPoint);
}
@Override
protected void processFinish(TileEntityMultiblockMetal multiblock)
{
super.processFinish(multiblock);
int size = -1;
for(ItemStack inputItem : this.inputItems)
{
for(IngredientStack s : recipe.getItemInputs())
if(s.matchesItemStackIgnoringSize(inputItem))
{
size = s.inputSize;
break;
}
if(size>0 && inputItem.stackSize>size)
{
inputItem.splitStack(size);
processTick = 0;
clearProcess = false;
}
}
}
}
public static class MultiblockInventoryHandler_DirectProcessing implements IItemHandlerModifiable
{
TileEntityMultiblockMetal multiblock;
float transformationPoint = .5f;
boolean doProcessStacking = false;
public MultiblockInventoryHandler_DirectProcessing(TileEntityMultiblockMetal multiblock)
{
this.multiblock = multiblock;
}
public MultiblockInventoryHandler_DirectProcessing setTransformationPoint(float point)
{
this.transformationPoint = point;
return this;
}
public MultiblockInventoryHandler_DirectProcessing setProcessStacking(boolean stacking)
{
this.doProcessStacking = stacking;
return this;
}
@Override
public int getSlots()
{
return 1;
}
@Override
public ItemStack getStackInSlot(int slot)
{
return null;
}
@Override
public ItemStack insertItem(int slot, ItemStack stack, boolean simulate)
{
stack = stack.copy();
IMultiblockRecipe recipe = this.multiblock.findRecipeForInsertion(stack);
if(recipe==null)
return stack;
ItemStack displayStack = null;
for(IngredientStack ingr : recipe.getItemInputs())
if(ingr.matchesItemStack(stack))
{
displayStack = Utils.copyStackWithAmount(stack, ingr.inputSize);
break;
}
if(multiblock.addProcessToQueue(new MultiblockProcessInWorld(recipe, transformationPoint, displayStack), simulate, doProcessStacking))
{
multiblock.markDirty();
multiblock.markContainingBlockForUpdate(null);
stack.stackSize -= displayStack.stackSize;
if(stack.stackSize<=0)
stack = null;
}
return stack;
}
@Override
public ItemStack extractItem(int slot, int amount, boolean simulate)
{
return null;
}
@Override
public void setStackInSlot(int slot, ItemStack stack)
{
}
}
}