/**
* Copyright (c) 2011-2015, SpaceToad and the BuildCraft Team
* http://www.mod-buildcraft.com
* <p/>
* BuildCraft is distributed under the terms of the Minecraft Mod Public
* License 1.0, or MMPL. Please check the contents of the license located in
* http://www.mod-buildcraft.com/MMPL-1.0.txt
*/
package buildcraft.silicon;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import io.netty.buffer.ByteBuf;
import net.minecraft.inventory.IInventory;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.nbt.NBTTagString;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.relauncher.Side;
import net.minecraftforge.common.util.Constants;
import net.minecraftforge.fluids.FluidStack;
import buildcraft.BuildCraftCore;
import buildcraft.api.recipes.CraftingResult;
import buildcraft.api.recipes.IFlexibleCrafter;
import buildcraft.api.recipes.IFlexibleRecipe;
import buildcraft.api.recipes.IFlexibleRecipeViewable;
import buildcraft.core.lib.network.command.CommandWriter;
import buildcraft.core.lib.network.command.ICommandReceiver;
import buildcraft.core.lib.network.command.PacketCommand;
import buildcraft.core.lib.utils.NetworkUtils;
import buildcraft.core.lib.utils.StringUtils;
import buildcraft.core.recipes.AssemblyRecipeManager;
public class TileAssemblyTable extends TileLaserTableBase implements IInventory, IFlexibleCrafter, ICommandReceiver {
public String currentRecipeId = "";
public IFlexibleRecipe<ItemStack> currentRecipe;
public HashMap<String, CraftingResult<ItemStack>> plannedOutputIcons = new HashMap<String, CraftingResult<ItemStack>>();
private HashSet<String> plannedOutput = new HashSet<String>();
private boolean queuedNetworkUpdate = false;
public List<CraftingResult<ItemStack>> getPotentialOutputs() {
List<CraftingResult<ItemStack>> result = new LinkedList<CraftingResult<ItemStack>>();
for (IFlexibleRecipe<ItemStack> recipe : AssemblyRecipeManager.INSTANCE.getRecipes()) {
CraftingResult<ItemStack> r = recipe.craft(this, true);
if (r != null) {
result.add(r);
}
}
return result;
}
private void queueNetworkUpdate() {
queuedNetworkUpdate = true;
}
@Override
public boolean canUpdate() {
return !FMLCommonHandler.instance().getEffectiveSide().isClient();
}
@Override
public void updateEntity() { // WARNING: run only server-side, see canUpdate()
super.updateEntity();
if (queuedNetworkUpdate) {
sendNetworkUpdate();
queuedNetworkUpdate = false;
}
if (currentRecipe == null) {
return;
}
if (!currentRecipe.canBeCrafted(this)) {
setNextCurrentRecipe();
if (currentRecipe == null) {
return;
}
}
if (getEnergy() >= currentRecipe.craft(this, true).energyCost) {
if (currentRecipe.canBeCrafted(this)) {
CraftingResult<ItemStack> result = currentRecipe.craft(this, false);
setEnergy(Math.max(0, getEnergy() - result.energyCost));
outputStack(result.crafted.copy(), true);
setNextCurrentRecipe();
}
}
}
/* IINVENTORY */
@Override
public int getSizeInventory() {
return 12;
}
@Override
public void setInventorySlotContents(int slot, ItemStack stack) {
super.setInventorySlotContents(slot, stack);
if (currentRecipe == null) {
setNextCurrentRecipe();
}
}
@Override
public String getInventoryName() {
return StringUtils.localize("tile.assemblyTableBlock.name");
}
@Override
public void readData(ByteBuf stream) {
super.readData(stream);
currentRecipeId = NetworkUtils.readUTF(stream);
plannedOutput.clear();
int size = stream.readUnsignedByte();
for (int i = 0; i < size; i++) {
plannedOutput.add(NetworkUtils.readUTF(stream));
}
// Update plannedOutputIcons
generatePlannedOutputIcons();
currentRecipe = AssemblyRecipeManager.INSTANCE.getRecipe(currentRecipeId);
}
private void generatePlannedOutputIcons() {
for (String s : plannedOutput) {
IFlexibleRecipe<ItemStack> recipe = AssemblyRecipeManager.INSTANCE.getRecipe(s);
if (recipe != null) {
CraftingResult<ItemStack> result = recipe.craft(this, true);
if (result != null && result.usedItems != null && result.usedItems.size() > 0) {
plannedOutputIcons.put(s, result);
} else if (recipe instanceof IFlexibleRecipeViewable) {
// !! HACK !! TODO !! HACK !!
Object out = ((IFlexibleRecipeViewable) recipe).getOutput();
if (out instanceof ItemStack) {
result = new CraftingResult<ItemStack>();
result.crafted = (ItemStack) out;
result.recipe = recipe;
plannedOutputIcons.put(s, result);
}
}
} else {
plannedOutput.remove(s);
}
}
for (String s : plannedOutputIcons.keySet().toArray(new String[plannedOutputIcons.size()])) {
if (!(plannedOutput.contains(s))) {
plannedOutputIcons.remove(s);
}
}
}
@Override
public void writeData(ByteBuf stream) {
super.writeData(stream);
NetworkUtils.writeUTF(stream, currentRecipeId);
stream.writeByte(plannedOutput.size());
for (String s : plannedOutput) {
NetworkUtils.writeUTF(stream, s);
}
}
@Override
public void readFromNBT(NBTTagCompound nbt) {
super.readFromNBT(nbt);
NBTTagList list = nbt.getTagList("plannedIds", Constants.NBT.TAG_STRING);
for (int i = 0; i < list.tagCount(); ++i) {
IFlexibleRecipe<ItemStack> recipe = AssemblyRecipeManager.INSTANCE.getRecipe(list.getStringTagAt(i));
if (recipe != null) {
plannedOutput.add(recipe.getId());
}
}
if (nbt.hasKey("recipeId")) {
IFlexibleRecipe<ItemStack> recipe = AssemblyRecipeManager.INSTANCE.getRecipe(nbt.getString("recipeId"));
if (recipe != null) {
setCurrentRecipe(recipe);
}
}
}
@Override
public void writeToNBT(NBTTagCompound nbt) {
super.writeToNBT(nbt);
NBTTagList list = new NBTTagList();
for (String recipe : plannedOutput) {
list.appendTag(new NBTTagString(recipe));
}
nbt.setTag("plannedIds", list);
if (currentRecipe != null) {
nbt.setString("recipeId", currentRecipe.getId());
}
}
public boolean isPlanned(IFlexibleRecipe<ItemStack> recipe) {
if (recipe == null) {
return false;
}
return plannedOutput.contains(recipe.getId());
}
public boolean isAssembling(IFlexibleRecipe<ItemStack> recipe) {
return recipe != null && recipe == currentRecipe;
}
private void setCurrentRecipe(IFlexibleRecipe<ItemStack> recipe) {
currentRecipe = recipe;
if (recipe != null) {
currentRecipeId = recipe.getId();
} else {
currentRecipeId = "";
}
// Update plannedOutputIcons
generatePlannedOutputIcons();
if (worldObj != null && !worldObj.isRemote) {
queueNetworkUpdate();
}
}
@Override
public int getRequiredEnergy() {
if (currentRecipe != null) {
CraftingResult<ItemStack> result = currentRecipe.craft(this, true);
if (result != null) {
return result.energyCost;
} else {
return 0;
}
} else {
return 0;
}
}
public void planOutput(IFlexibleRecipe<ItemStack> recipe) {
if (recipe != null && !isPlanned(recipe)) {
plannedOutput.add(recipe.getId());
if (!isAssembling(currentRecipe) || !isPlanned(currentRecipe)) {
setCurrentRecipe(recipe);
}
queueNetworkUpdate();
}
}
public void cancelPlanOutput(IFlexibleRecipe<ItemStack> recipe) {
if (isAssembling(recipe)) {
setCurrentRecipe(null);
}
plannedOutput.remove(recipe.getId());
if (!plannedOutput.isEmpty()) {
setCurrentRecipe(AssemblyRecipeManager.INSTANCE.getRecipe(plannedOutput.iterator().next()));
}
queueNetworkUpdate();
}
public void setNextCurrentRecipe() {
boolean takeNext = false;
for (String recipeId : plannedOutput) {
IFlexibleRecipe<ItemStack> recipe = AssemblyRecipeManager.INSTANCE.getRecipe(recipeId);
if (recipe == null) {
continue;
}
if (recipe == currentRecipe) {
takeNext = true;
} else if (takeNext && recipe.canBeCrafted(this)) {
setCurrentRecipe(recipe);
return;
}
}
for (String recipeId : plannedOutput) {
IFlexibleRecipe<ItemStack> recipe = AssemblyRecipeManager.INSTANCE.getRecipe(recipeId);
if (recipe == null) {
continue;
}
if (recipe.canBeCrafted(this)) {
setCurrentRecipe(recipe);
return;
}
}
setCurrentRecipe(null);
}
public void rpcSelectRecipe(final String id, final boolean select) {
BuildCraftCore.instance.sendToServer(new PacketCommand(this, "select", new CommandWriter() {
public void write(ByteBuf data) {
NetworkUtils.writeUTF(data, id);
data.writeBoolean(select);
}
}));
}
@Override
public void receiveCommand(String command, Side side, Object sender, ByteBuf stream) {
if (side.isServer() && "select".equals(command)) {
String id = NetworkUtils.readUTF(stream);
boolean select = stream.readBoolean();
IFlexibleRecipe<ItemStack> recipe = AssemblyRecipeManager.INSTANCE.getRecipe(id);
if (recipe != null) {
if (select) {
planOutput(recipe);
} else {
cancelPlanOutput(recipe);
}
}
}
}
@Override
public boolean hasWork() {
return currentRecipe != null;
}
@Override
public boolean canCraft() {
return hasWork();
}
@Override
public boolean isItemValidForSlot(int slot, ItemStack stack) {
return true;
}
@Override
public boolean hasCustomInventoryName() {
return false;
}
@Override
public int getCraftingItemStackSize() {
return getSizeInventory();
}
@Override
public ItemStack getCraftingItemStack(int slotid) {
return getStackInSlot(slotid);
}
@Override
public ItemStack decrCraftingItemStack(int slotid, int val) {
return decrStackSize(slotid, val);
}
@Override
public FluidStack getCraftingFluidStack(int tankid) {
return null;
}
@Override
public FluidStack decrCraftingFluidStack(int tankid, int val) {
return null;
}
@Override
public int getCraftingFluidStackSize() {
return 0;
}
}