blob: b2535a43c5456c464a5d35f21d5ac63a2e137f77 [file] [log] [blame] [raw]
package mekanism.common.tile;
import io.netty.buffer.ByteBuf;
import java.util.ArrayList;
import java.util.List;
import mekanism.api.EnumColor;
import mekanism.api.IConfigCardAccess;
import mekanism.api.transmitters.TransmissionType;
import mekanism.common.PacketHandler;
import mekanism.common.SideData;
import mekanism.common.Upgrade;
import mekanism.common.base.IRedstoneControl;
import mekanism.common.base.ISideConfiguration;
import mekanism.common.base.IUpgradeTile;
import mekanism.common.block.states.BlockStateMachine.MachineType;
import mekanism.common.capabilities.Capabilities;
import mekanism.common.config.MekanismConfig.usage;
import mekanism.common.content.assemblicator.RecipeFormula;
import mekanism.common.item.ItemCraftingFormula;
import mekanism.common.security.ISecurityTile;
import mekanism.common.tile.component.TileComponentConfig;
import mekanism.common.tile.component.TileComponentEjector;
import mekanism.common.tile.component.TileComponentSecurity;
import mekanism.common.tile.component.TileComponentUpgrade;
import mekanism.common.tile.prefab.TileEntityElectricBlock;
import mekanism.common.util.ChargeUtils;
import mekanism.common.util.InventoryUtils;
import mekanism.common.util.MekanismUtils;
import mekanism.common.util.StackUtils;
import net.minecraft.inventory.InventoryCrafting;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.NonNullList;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.fml.common.FMLCommonHandler;
public class TileEntityFormulaicAssemblicator extends TileEntityElectricBlock implements ISideConfiguration, IUpgradeTile, IRedstoneControl, IConfigCardAccess, ISecurityTile
{
public InventoryCrafting dummyInv = MekanismUtils.getDummyCraftingInv();
public double BASE_ENERGY_PER_TICK = usage.metallurgicInfuserUsage;
public double energyPerTick = BASE_ENERGY_PER_TICK;
public int BASE_TICKS_REQUIRED = 40;
public int ticksRequired = BASE_TICKS_REQUIRED;
public int operatingTicks;
public boolean autoMode = false;
public boolean isRecipe = false;
public boolean stockControl = false;
public boolean needsOrganize = true; //organize on load
public int pulseOperations;
public RecipeFormula formula;
public RedstoneControl controlType = RedstoneControl.DISABLED;
public TileComponentUpgrade upgradeComponent;
public TileComponentEjector ejectorComponent;
public TileComponentConfig configComponent;
public TileComponentSecurity securityComponent;
public ItemStack lastFormulaStack = ItemStack.EMPTY;
public boolean needsFormulaUpdate = false;
public ItemStack lastOutputStack = ItemStack.EMPTY;
public TileEntityFormulaicAssemblicator()
{
super("FormulaicAssemblicator", MachineType.FORMULAIC_ASSEMBLICATOR.baseEnergy);
configComponent = new TileComponentConfig(this, TransmissionType.ITEM, TransmissionType.ENERGY);
configComponent.addOutput(TransmissionType.ITEM, new SideData("None", EnumColor.GREY, InventoryUtils.EMPTY));
configComponent.addOutput(TransmissionType.ITEM, new SideData("Input", EnumColor.DARK_RED,
new int[] {3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20}));
configComponent.addOutput(TransmissionType.ITEM, new SideData("Output", EnumColor.DARK_BLUE, new int[] {21, 22, 23, 24, 25, 26}));
configComponent.addOutput(TransmissionType.ITEM, new SideData("Energy", EnumColor.DARK_GREEN, new int[] {1}));
configComponent.setConfig(TransmissionType.ITEM, new byte[] {0, 0, 0, 3, 1, 2});
configComponent.setInputConfig(TransmissionType.ENERGY);
inventory = NonNullList.withSize(36, ItemStack.EMPTY);
upgradeComponent = new TileComponentUpgrade(this, 0);
ejectorComponent = new TileComponentEjector(this);
ejectorComponent.setOutputData(TransmissionType.ITEM, configComponent.getOutputs(TransmissionType.ITEM).get(2));
securityComponent = new TileComponentSecurity(this);
}
@Override
public void onUpdate()
{
super.onUpdate();
if(!world.isRemote)
{
if(formula != null && stockControl && needsOrganize)
{
needsOrganize = false;
organizeStock();
}
ChargeUtils.discharge(1, this);
if(controlType != RedstoneControl.PULSE)
{
pulseOperations = 0;
}
else if(MekanismUtils.canFunction(this))
{
pulseOperations++;
}
RecipeFormula prev = formula;
if(!inventory.get(2).isEmpty() && inventory.get(2).getItem() instanceof ItemCraftingFormula)
{
ItemCraftingFormula item = (ItemCraftingFormula)inventory.get(2).getItem();
if(formula == null || lastFormulaStack != inventory.get(2))
{
loadFormula();
}
}
else {
formula = null;
}
if(prev != formula)
{
needsFormulaUpdate = true;
}
lastFormulaStack = inventory.get(2);
if(autoMode && formula == null)
{
toggleAutoMode();
}
if(autoMode && formula != null && ((controlType == RedstoneControl.PULSE && pulseOperations > 0) || MekanismUtils.canFunction(this)))
{
boolean canOperate = true;
if(!isRecipe)
{
canOperate = moveItemsToGrid();
}
if(canOperate)
{
isRecipe = true;
if(operatingTicks >= ticksRequired)
{
if(doSingleCraft())
{
operatingTicks = 0;
if(pulseOperations > 0)
{
pulseOperations--;
}
ejectorComponent.outputItems();
}
}
else {
if(getEnergy() >= energyPerTick)
{
operatingTicks++;
setEnergy(getEnergy() - energyPerTick);
}
}
}
else {
operatingTicks = 0;
}
}
else {
operatingTicks = 0;
}
}
}
public void loadFormula()
{
ItemCraftingFormula item = (ItemCraftingFormula)inventory.get(2).getItem();
if(item.getInventory(inventory.get(2)) != null && !item.isInvalid(inventory.get(2)))
{
RecipeFormula itemFormula = new RecipeFormula(world, item.getInventory(inventory.get(2)));
if(itemFormula.isValidFormula(world))
{
if(formula != null && !formula.isFormulaEqual(world, itemFormula))
{
formula = itemFormula;
operatingTicks = 0;
}
else if(formula == null)
{
formula = itemFormula;
}
}
else {
formula = null;
item.setInvalid(inventory.get(2), true);
}
}
else {
formula = null;
}
}
@Override
public void markDirty()
{
super.markDirty();
if(world != null && !world.isRemote)
{
if(formula == null)
{
for(int i = 0; i < 9; i++)
{
dummyInv.setInventorySlotContents(i, inventory.get(27+i));
}
lastOutputStack = MekanismUtils.findMatchingRecipe(dummyInv, world);
isRecipe = !lastOutputStack.isEmpty();
}
else {
isRecipe = formula.matches(world, inventory, 27);
lastOutputStack = isRecipe ? formula.recipe.getRecipeOutput() : ItemStack.EMPTY;
}
needsOrganize = true;
}
}
private boolean doSingleCraft()
{
for(int i = 0; i < 9; i++)
{
dummyInv.setInventorySlotContents(i, inventory.get(27+i));
}
ItemStack output = lastOutputStack;
if(!output.isEmpty() && tryMoveToOutput(output, false))
{
tryMoveToOutput(output, true);
for(int i = 27; i <= 35; i++)
{
if(!inventory.get(i).isEmpty())
{
ItemStack stack = inventory.get(i).copy();
inventory.get(i).shrink(1);
if(inventory.get(i).getCount() == 0 && stack.getItem().hasContainerItem(stack))
{
ItemStack container = stack.getItem().getContainerItem(stack);
if(!container.isEmpty() && container.isItemStackDamageable() && container.getItemDamage() > container.getMaxDamage())
{
container = ItemStack.EMPTY;
}
if(!container.isEmpty())
{
boolean move = tryMoveToOutput(container.copy(), false);
if(move)
{
tryMoveToOutput(container.copy(), true);
}
inventory.set(i, move ? ItemStack.EMPTY : container.copy());
}
}
}
}
if(formula != null)
{
moveItemsToGrid();
}
markDirty();
return true;
}
return false;
}
private boolean craftSingle()
{
if(formula != null)
{
boolean canOperate = true;
if(!formula.matches(world, inventory, 27))
{
canOperate = moveItemsToGrid();
}
if(canOperate)
{
return doSingleCraft();
}
}
else {
return doSingleCraft();
}
return false;
}
private boolean moveItemsToGrid()
{
boolean ret = true;
for(int i = 27; i <= 35; i++)
{
if(formula.isIngredientInPos(world, inventory.get(i), i-27))
{
continue;
}
if(!inventory.get(i).isEmpty())
{
inventory.set(i, tryMoveToInput(inventory.get(i)));
markDirty();
if(!inventory.get(i).isEmpty())
{
ret = false;
}
}
else {
boolean found = false;
for(int j = 20; j >= 3; j--)
{
if(!inventory.get(j).isEmpty() && formula.isIngredientInPos(world, inventory.get(j), i-27))
{
inventory.set(i, StackUtils.size(inventory.get(j), 1));
inventory.get(j).shrink(1);
markDirty();
found = true;
break;
}
}
if(!found)
{
ret = false;
}
}
}
return ret;
}
private void craftAll()
{
while(craftSingle());
}
private void moveItemsToInput(boolean forcePush)
{
for(int i = 27; i <= 35; i++)
{
if(!inventory.get(i).isEmpty() && (forcePush || (formula != null && !formula.isIngredientInPos(world, inventory.get(i), i-27))))
{
inventory.set(i, tryMoveToInput(inventory.get(i)));
}
}
markDirty();
}
private void toggleAutoMode()
{
if(autoMode)
{
operatingTicks = 0;
autoMode = false;
}
else if(formula != null)
{
moveItemsToInput(false);
autoMode = true;
}
markDirty();
}
private void toggleStockControl()
{
if(!world.isRemote && formula != null)
{
stockControl = !stockControl;
if(stockControl)
{
organizeStock();
}
}
}
private void organizeStock()
{
for(int j = 3; j <= 20; j++)
{
for(int i = 20; i > j; i--)
{
if(!inventory.get(i).isEmpty())
{
if(inventory.get(j).isEmpty())
{
inventory.set(j, inventory.get(i));
inventory.set(i, ItemStack.EMPTY);
markDirty();
return;
}
else if(inventory.get(j).getCount() < inventory.get(j).getMaxStackSize())
{
if(InventoryUtils.areItemsStackable(inventory.get(i), inventory.get(j)))
{
int newCount = inventory.get(j).getCount() + inventory.get(i).getCount();
inventory.get(j).setCount(Math.min(inventory.get(j).getMaxStackSize(), newCount));
inventory.get(i).setCount(Math.max(0, newCount - inventory.get(j).getMaxStackSize()));
markDirty();
return;
}
}
}
}
}
}
private ItemStack tryMoveToInput(ItemStack stack)
{
stack = stack.copy();
for(int i = 3; i <= 20; i++)
{
if(inventory.get(i).isEmpty())
{
inventory.set(i, stack);
return ItemStack.EMPTY;
}
else if(InventoryUtils.areItemsStackable(stack, inventory.get(i)) && inventory.get(i).getCount() < inventory.get(i).getMaxStackSize())
{
int toUse = Math.min(stack.getCount(), inventory.get(i).getMaxStackSize()-inventory.get(i).getCount());
inventory.get(i).grow(toUse);
stack.shrink(toUse);
if(stack.getCount() == 0)
{
return ItemStack.EMPTY;
}
}
}
return stack;
}
private boolean tryMoveToOutput(ItemStack stack, boolean doMove)
{
stack = stack.copy();
for(int i = 21; i <= 26; i++)
{
if(inventory.get(i).isEmpty())
{
if(doMove)
{
inventory.set(i, stack);
}
return true;
}
else if(InventoryUtils.areItemsStackable(stack, inventory.get(i)) && inventory.get(i).getCount() < inventory.get(i).getMaxStackSize())
{
int toUse = Math.min(stack.getCount(), inventory.get(i).getMaxStackSize()-inventory.get(i).getCount());
if(doMove)
{
inventory.get(i).grow(toUse);
}
stack.shrink(toUse);
if(stack.getCount() == 0)
{
return true;
}
}
}
return false;
}
private void encodeFormula()
{
if(!inventory.get(2).isEmpty() && inventory.get(2).getItem() instanceof ItemCraftingFormula)
{
ItemCraftingFormula item = (ItemCraftingFormula)inventory.get(2).getItem();
if(item.getInventory(inventory.get(2)) == null)
{
RecipeFormula formula = new RecipeFormula(world, inventory, 27);
if(formula.isValidFormula(world))
{
item.setInventory(inventory.get(2), formula.input);
markDirty();
}
}
}
}
@Override
public boolean canSetFacing(int side)
{
return side != 0 && side != 1;
}
@Override
public int[] getSlotsForFace(EnumFacing side)
{
return configComponent.getOutput(TransmissionType.ITEM, side, facing).availableSlots;
}
@Override
public boolean canExtractItem(int slotID, ItemStack itemstack, EnumFacing side)
{
if(slotID == 1)
{
return ChargeUtils.canBeOutputted(itemstack, false);
}
else if(slotID >= 21 && slotID <= 26)
{
return true;
}
return false;
}
@Override
public boolean isItemValidForSlot(int slotID, ItemStack itemstack)
{
if(slotID >= 3 && slotID <= 20)
{
if(formula == null)
{
return true;
}
else {
List<Integer> indices = formula.getIngredientIndices(world, itemstack);
if(indices.size() > 0)
{
if(stockControl)
{
int filled = 0;
for(int i = 3; i < 20; i++)
{
if(!inventory.get(i).isEmpty())
{
if(formula.isIngredientInPos(world, inventory.get(i), indices.get(0)))
{
filled++;
}
}
}
return filled < indices.size()*2;
}
else {
return true;
}
}
}
}
else if(slotID == 1)
{
return ChargeUtils.canBeDischarged(itemstack);
}
return false;
}
@Override
public void readFromNBT(NBTTagCompound nbtTags)
{
super.readFromNBT(nbtTags);
autoMode = nbtTags.getBoolean("autoMode");
operatingTicks = nbtTags.getInteger("operatingTicks");
controlType = RedstoneControl.values()[nbtTags.getInteger("controlType")];
pulseOperations = nbtTags.getInteger("pulseOperations");
stockControl = nbtTags.getBoolean("stockControl");
}
@Override
public NBTTagCompound writeToNBT(NBTTagCompound nbtTags)
{
super.writeToNBT(nbtTags);
nbtTags.setBoolean("autoMode", autoMode);
nbtTags.setInteger("operatingTicks", operatingTicks);
nbtTags.setInteger("controlType", controlType.ordinal());
nbtTags.setInteger("pulseOperations", pulseOperations);
nbtTags.setBoolean("stockControl", stockControl);
return nbtTags;
}
@Override
public void handlePacketData(ByteBuf dataStream)
{
if(FMLCommonHandler.instance().getEffectiveSide().isServer())
{
int type = dataStream.readInt();
if(type == 0)
{
toggleAutoMode();
}
else if(type == 1)
{
encodeFormula();
}
else if(type == 2)
{
craftSingle();
}
else if(type == 3)
{
craftAll();
}
else if(type == 4)
{
if(formula != null)
{
moveItemsToGrid();
}
else {
moveItemsToInput(true);
}
}
else if(type == 5)
{
toggleStockControl();
}
return;
}
super.handlePacketData(dataStream);
if(FMLCommonHandler.instance().getEffectiveSide().isClient())
{
autoMode = dataStream.readBoolean();
operatingTicks = dataStream.readInt();
controlType = RedstoneControl.values()[dataStream.readInt()];
isRecipe = dataStream.readBoolean();
stockControl = dataStream.readBoolean();
if(dataStream.readBoolean())
{
if(dataStream.readBoolean())
{
NonNullList<ItemStack> inv = NonNullList.withSize(9, ItemStack.EMPTY);
for(int i = 0; i < 9; i++)
{
if(dataStream.readBoolean())
{
inv.set(i, PacketHandler.readStack(dataStream));
}
}
formula = new RecipeFormula(world, inv);
}
else {
formula = null;
}
}
}
}
@Override
public ArrayList getNetworkedData(ArrayList data)
{
super.getNetworkedData(data);
data.add(autoMode);
data.add(operatingTicks);
data.add(controlType.ordinal());
data.add(isRecipe);
data.add(stockControl);
if(needsFormulaUpdate)
{
data.add(true);
if(formula != null)
{
data.add(true);
for(int i = 0; i < 9; i++)
{
if(!formula.input.get(i).isEmpty())
{
data.add(true);
data.add(formula.input.get(i));
}
else {
data.add(false);
}
}
}
else {
data.add(false);
}
}
else {
data.add(false);
}
needsFormulaUpdate = false;
return data;
}
@Override
public RedstoneControl getControlType()
{
return controlType;
}
@Override
public void setControlType(RedstoneControl type)
{
controlType = type;
MekanismUtils.saveChunk(this);
}
@Override
public boolean canPulse()
{
return true;
}
@Override
public TileComponentConfig getConfig()
{
return configComponent;
}
@Override
public EnumFacing getOrientation()
{
return facing;
}
@Override
public TileComponentUpgrade getComponent()
{
return upgradeComponent;
}
@Override
public TileComponentEjector getEjector()
{
return ejectorComponent;
}
@Override
public TileComponentSecurity getSecurity()
{
return securityComponent;
}
@Override
public void recalculateUpgradables(Upgrade upgrade)
{
super.recalculateUpgradables(upgrade);
switch(upgrade)
{
case SPEED:
ticksRequired = MekanismUtils.getTicks(this, BASE_TICKS_REQUIRED);
energyPerTick = MekanismUtils.getEnergyPerTick(this, BASE_ENERGY_PER_TICK);
break;
case ENERGY:
energyPerTick = MekanismUtils.getEnergyPerTick(this, BASE_ENERGY_PER_TICK);
maxEnergy = MekanismUtils.getMaxEnergy(this, BASE_MAX_ENERGY);
setEnergy(Math.min(getMaxEnergy(), getEnergy()));
break;
default:
break;
}
}
@Override
public boolean hasCapability(Capability<?> capability, EnumFacing side)
{
return capability == Capabilities.CONFIG_CARD_CAPABILITY || super.hasCapability(capability, side);
}
@Override
public <T> T getCapability(Capability<T> capability, EnumFacing side)
{
if(capability == Capabilities.CONFIG_CARD_CAPABILITY)
{
return (T)this;
}
return super.getCapability(capability, side);
}
}