Package forestry.factory.gadgets

Source Code of forestry.factory.gadgets.TileWorktable$RecipeMemory

/*******************************************************************************
* Copyright (c) 2011-2014 SirSengir.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Lesser Public License v3
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/lgpl-3.0.txt
*
* Various Contributors including, but not limited to:
* SirSengir (original work), CovertJaguar, Player, Binnie, MysteriousAges
******************************************************************************/
package forestry.factory.gadgets;

import java.util.LinkedList;

import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.inventory.Container;
import net.minecraft.inventory.IInventory;
import net.minecraft.inventory.InventoryCrafting;
import net.minecraft.item.ItemStack;
import net.minecraft.item.crafting.CraftingManager;
import net.minecraft.item.crafting.IRecipe;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.world.World;

import forestry.api.core.ForestryAPI;
import forestry.api.core.INBTTagable;
import forestry.core.gadgets.TileBase;
import forestry.core.gui.ContainerDummy;
import forestry.core.interfaces.ICrafter;
import forestry.core.network.ForestryPacket;
import forestry.core.network.GuiId;
import forestry.core.network.PacketIds;
import forestry.core.network.PacketTileNBT;
import forestry.core.network.PacketTileUpdate;
import forestry.core.proxy.Proxies;
import forestry.core.utils.InventoryAdapter;
import forestry.core.utils.PlainInventory;
import forestry.core.utils.StackUtils;
import forestry.core.utils.TileInventoryAdapter;

public class TileWorktable extends TileBase implements ICrafter {

  /* CONSTANTS */
  public final static int SLOT_CRAFTING_1 = 0;
  public final static int SLOT_CRAFTING_RESULT = 9;
  public final static short SLOT_INVENTORY_1 = 0;
  public final static short SLOT_INVENTORY_COUNT = 18;
  private final static Container DUMMY_CONTAINER = new ContainerDummy();
  private final static RecipeBridge RECIPE_BRIDGE = new RecipeBridge();

  private static class RecipeBridge {
    public ItemStack findMatchingRecipe(InventoryCrafting crafting, World world) {
      return CraftingManager.getInstance().findMatchingRecipe(crafting, world);
    }
  }

  /* RECIPE MEMORY */
  public static final class Recipe implements INBTTagable {

    private InventoryAdapter matrix;
    private long lastUsed;
    private boolean locked;
    private World cachedWorld;
    private ItemStack cachedRecipeOutput;

    public Recipe(InventoryCrafting crafting) {
      this.matrix = new InventoryAdapter(new PlainInventory(crafting));
    }

    public Recipe(NBTTagCompound nbttagcompound) {
      readFromNBT(nbttagcompound);
    }

    public void setCacheWorld(World world) {
      this.cachedWorld = world;
    }

    private boolean updateCachedRecipeOutput() {

      if (cachedRecipeOutput != null)
        return true;

      InventoryCrafting crafting = new InventoryCrafting(DUMMY_CONTAINER, 3, 3);
      for (int i = 0; i < crafting.getSizeInventory(); i++) {
        crafting.setInventorySlotContents(i, matrix.getStackInSlot(i));
      }

      cachedRecipeOutput = RECIPE_BRIDGE.findMatchingRecipe(crafting, cachedWorld);
      return cachedRecipeOutput != null;
    }

    public void updateLastUse(World world) {
      lastUsed = world.getTotalWorldTime();
      setCacheWorld(world);
    }

    public void toogleLock() {
      locked = !locked;
    }

    public InventoryAdapter getMatrix() {
      return matrix;
    }

    public ItemStack getCraftingResult(InventoryCrafting inventorycrafting) {
      if (updateCachedRecipeOutput())
        return cachedRecipeOutput;

      return null;
    }

    public ItemStack getRecipeOutput() {
      if (updateCachedRecipeOutput())
        return cachedRecipeOutput;

      return null;
    }

    public long getLastUsed() {
      return this.lastUsed;
    }

    public boolean isLocked() {
      return this.locked;
    }

    public int getRecipeSize() {
      return matrix.getSizeInventory();
    }

    public boolean hasSameOutput(InventoryCrafting crafting, World world) {

      if (!updateCachedRecipeOutput())
        return false;

      ItemStack recipeOutput = RECIPE_BRIDGE.findMatchingRecipe(crafting, world);
      return recipeOutput != null && cachedRecipeOutput.isItemEqual(recipeOutput);
    }

    @Override
    public final void readFromNBT(NBTTagCompound nbttagcompound) {
      matrix = new InventoryAdapter(new InventoryCrafting(DUMMY_CONTAINER, 3, 3));
      matrix.readFromNBT(nbttagcompound);
      sanitizeMatrix();
      lastUsed = nbttagcompound.getLong("LastUsed");
      locked = nbttagcompound.getBoolean("Locked");
    }

    @Override
    public void writeToNBT(NBTTagCompound nbttagcompound) {
      sanitizeMatrix();
      matrix.writeToNBT(nbttagcompound);
      nbttagcompound.setLong("LastUsed", lastUsed);
      nbttagcompound.setBoolean("Locked", locked);
    }

    /**
     * This is a nasty hack to hide a bug where the recipe stacks somehow
     * get the stackSize set to -1. Which makes the recipe no longer work,
     * ever. I've seen the bug, even inspected the NBT data of an afflicted
     * worktable, but can't duplicate it, and can't trace it. -CovertJaguar
     */
    private void sanitizeMatrix() {
      for (int slot = 0; slot < matrix.getSizeInventory(); slot++) {
        ItemStack stack = matrix.getStackInSlot(slot);
        if (stack != null)
          stack.stackSize = 1;
      }
    }
  }

  public static class RecipeMemory implements INBTTagable, IInventory {

    private LinkedList<Recipe> recipes = new LinkedList<Recipe>();
    private long lastUpdate;
    public final int capacity = 9;

    public long getLastUpdate() {
      return lastUpdate;
    }

    public void memorizeRecipe(World world, Recipe recipe, InventoryCrafting crafting) {

      lastUpdate = world.getTotalWorldTime();
      recipe.updateLastUse(world);

      Recipe memory = getMemorized(crafting, world);
      if (memory != null) {
        int index = recipes.indexOf(memory);
        recipes.set(index, recipe);
        return;
      }

      if (recipes.size() < capacity) {
        recipes.add(recipe);
        return;
      }

      Recipe oldest = null;
      for (Recipe existing : recipes) {
        if (oldest != null && oldest.getLastUsed() < existing.getLastUsed())
          continue;
        if (existing.isLocked())
          continue;

        oldest = existing;
      }
      if (oldest != null) {
        recipes.remove(oldest);
        recipes.add(recipe);
      }

    }

    public Recipe getRecipe(int recipeIndex) {
      if (recipes.size() > recipeIndex)
        return recipes.get(recipeIndex);

      return null;
    }

    public ItemStack getRecipeOutput(int recipeIndex) {
      if (recipes.size() > recipeIndex)
        return recipes.get(recipeIndex).getRecipeOutput();
      else
        return null;
    }

    public boolean isLocked(int recipeIndex) {
      if (recipes.size() > recipeIndex)
        return recipes.get(recipeIndex).isLocked();

      return false;
    }

    public void toggleLock(World world, int recipeIndex) {
      lastUpdate = world.getTotalWorldTime();
      if (recipes.size() > recipeIndex)
        recipes.get(recipeIndex).toogleLock();
    }

    private Recipe getMemorized(InventoryCrafting crafting, World world) {
      for (Recipe recipe : recipes) {
        if (recipe.hasSameOutput(crafting, world))
          return recipe;
      }

      return null;
    }

    @Override
    public void readFromNBT(NBTTagCompound nbttagcompound) {
      recipes = new LinkedList<Recipe>();
      if (!nbttagcompound.hasKey("RecipeMemory"))
        return;

      NBTTagList nbttaglist = nbttagcompound.getTagList("RecipeMemory", 10);
      for (int j = 0; j < nbttaglist.tagCount(); ++j) {
        NBTTagCompound nbttagcompound2 = nbttaglist.getCompoundTagAt(j);
        recipes.add(new Recipe(nbttagcompound2));
      }
    }

    @Override
    public void writeToNBT(NBTTagCompound nbttagcompound) {
      NBTTagList nbttaglist = new NBTTagList();
      for (int i = 0; i < recipes.size(); i++) {
        if (recipes.get(i) != null) {
          NBTTagCompound nbttagcompound2 = new NBTTagCompound();
          recipes.get(i).writeToNBT(nbttagcompound2);
          nbttaglist.appendTag(nbttagcompound2);
        }
      }
      nbttagcompound.setTag("RecipeMemory", nbttaglist);
    }

    @Override
    public int getSizeInventory() {
      return recipes.size();
    }

    @Override
    public ItemStack getStackInSlot(int i) {
      return getRecipeOutput(i);
    }

    @Override
    public ItemStack decrStackSize(int i, int j) {
      return null;
    }

    @Override
    public ItemStack getStackInSlotOnClosing(int i) {
      return null;
    }

    @Override
    public void setInventorySlotContents(int i, ItemStack itemstack) {
    }

    @Override
    public String getInventoryName() {
      return "";
    }

    @Override
    public boolean hasCustomInventoryName() {
      return false;
    }

    @Override
    public int getInventoryStackLimit() {
      return 64;
    }

    @Override
    public void markDirty() {
    }

    @Override
    public boolean isUseableByPlayer(EntityPlayer entityplayer) {
      return true;
    }

    @Override
    public void openInventory() {
    }

    @Override
    public void closeInventory() {
    }

    @Override
    public boolean isItemValidForSlot(int i, ItemStack itemstack) {
      return false;
    }
  }
  /* MEMBERS */
  private Recipe currentRecipe;
  private InventoryCrafting currentCrafting;
  private final RecipeMemory memorized;
  private final TileInventoryAdapter craftingInventory;
  private final TileInventoryAdapter accessibleInventory;

  public TileWorktable() {
    craftingInventory = new TileInventoryAdapter(this, 10, "CraftItems");
    accessibleInventory = new TileInventoryAdapter(this, 18, "Items");

    memorized = new RecipeMemory();
  }

  @Override
  public void openGui(EntityPlayer player, TileBase tile) {
    player.openGui(ForestryAPI.instance, GuiId.WorktableGUI.ordinal(), player.worldObj, xCoord, yCoord, zCoord);
  }

  /* LOADING & SAVING */
  @Override
  public void writeToNBT(NBTTagCompound nbttagcompound) {
    super.writeToNBT(nbttagcompound);

    craftingInventory.writeToNBT(nbttagcompound);
    accessibleInventory.writeToNBT(nbttagcompound);

    memorized.writeToNBT(nbttagcompound);
  }

  @Override
  public void readFromNBT(NBTTagCompound nbttagcompound) {
    super.readFromNBT(nbttagcompound);

    craftingInventory.readFromNBT(nbttagcompound);
    accessibleInventory.readFromNBT(nbttagcompound);

    memorized.readFromNBT(nbttagcompound);
  }

  /* NETWORK */
  @Override
  public void fromPacket(ForestryPacket packetRaw) {
    if (packetRaw instanceof PacketTileUpdate) {
      super.fromPacket(packetRaw);
      return;
    }

    PacketTileNBT packet = (PacketTileNBT) packetRaw;
    readFromNBT(packet.getTagCompound());
  }

  public void sendAll(EntityPlayer player) {
    Proxies.net.sendToPlayer(new PacketTileNBT(PacketIds.TILE_NBT, this), player);
  }

  /* RECIPE SELECTION */
  public RecipeMemory getMemory() {
    return memorized;
  }

  public void chooseRecipe(int recipeIndex) {
    if (recipeIndex >= memorized.capacity) {
      for (int slot = 0; slot < craftingInventory.getSizeInventory(); slot++) {
        craftingInventory.setInventorySlotContents(slot, null);
      }
      return;
    }

    Recipe recipe = memorized.getRecipe(recipeIndex);
    recipe.sanitizeMatrix();
    IInventory matrix = recipe.getMatrix();
    if (matrix == null)
      return;

    for (int slot = 0; slot < matrix.getSizeInventory(); slot++) {
      craftingInventory.setInventorySlotContents(slot, matrix.getStackInSlot(slot));
    }
  }

  /* CRAFTING */
  public void setRecipe(InventoryCrafting crafting) {

    ItemStack recipeOutput = RECIPE_BRIDGE.findMatchingRecipe(crafting, worldObj);
    if (recipeOutput == null) {
      currentRecipe = null;
      currentCrafting = null;
    } else {
      currentRecipe = new Recipe(crafting);
      currentCrafting = crafting;
    }
    updateCraftResult(currentCrafting);
  }

  private void updateCraftResult(InventoryCrafting inventorycrafting) {
    if (currentRecipe != null) {
      ItemStack result = currentRecipe.getCraftingResult(inventorycrafting);
      if (result != null) {
        craftingInventory.setInventorySlotContents(SLOT_CRAFTING_RESULT, result.copy());
        return;
      }
    }

    craftingInventory.setInventorySlotContents(SLOT_CRAFTING_RESULT, null);
  }

  private boolean canCraftCurrentRecipe() {
    if (currentRecipe == null)
      return false;

    // Need at least one matched set
    ItemStack[] set = craftingInventory.getStacks(SLOT_CRAFTING_1, 9);
    ItemStack[] stock = accessibleInventory.getStacks(SLOT_INVENTORY_1, SLOT_INVENTORY_COUNT);
    if (StackUtils.containsSets(set, stock, true, true) == 0)
      return false;

    // Check that it doesn't make a different recipe.
    // For example:
    // Wood Logs are all ore dictionary equivalent with each other,
    // but an Oak Log shouldn't be able to make Ebony Wood Planks
    // because it makes Oak Wood Planks using the same recipe.
    // Strategy:
    // Create a fake crafting inventory using items we have in stock
    // in place of the ones in the saved crafting inventory.
    // Check that the recipe it makes is the same as the currentRecipe.
    InventoryCrafting crafting = new InventoryCrafting(DUMMY_CONTAINER, 3, 3);
    ItemStack[] stockCopy = StackUtils.condenseStacks(stock);

    for (int slot = 0; slot < currentCrafting.getSizeInventory(); slot++) {
      ItemStack recipeStack = currentCrafting.getStackInSlot(slot);
      if (recipeStack == null)
        continue;

      // Use crafting equivalent (not oredict) items first
      for (ItemStack stockStack : stockCopy) {
        if (stockStack.stackSize > 0 && StackUtils.isCraftingEquivalent(recipeStack, stockStack, false, false)) {
          ItemStack stack = new ItemStack(stockStack.getItem(), 1, stockStack.getItemDamage());
          stockStack.stackSize--;
          crafting.setInventorySlotContents(slot, stack);
          break;
        }
      }

      // Use oredict items if crafting equivalent items aren't available
      if (crafting.getStackInSlot(slot) == null) {
        for (ItemStack stockStack : stockCopy) {
          if (stockStack.stackSize > 0 && StackUtils.isCraftingEquivalent(recipeStack, stockStack, true, true)) {
            ItemStack stack = new ItemStack(stockStack.getItem(), 1, stockStack.getItemDamage());
            stockStack.stackSize--;
            crafting.setInventorySlotContents(slot, stack);
            break;
          }
        }
      }
    }
    ItemStack recipeOutput = RECIPE_BRIDGE.findMatchingRecipe(crafting, worldObj);
    if (recipeOutput == null)
      return false;

    return recipeOutput.isItemEqual(currentRecipe.getRecipeOutput());
  }

  private boolean removeResources(EntityPlayer player) {
    ItemStack[] set = craftingInventory.getStacks(SLOT_CRAFTING_1, 9);
    return accessibleInventory.removeSets(1, set, SLOT_INVENTORY_1, SLOT_INVENTORY_COUNT, player, true, true, true);
  }

  @Override
  public boolean canTakeStack(int slotIndex) {
    if (slotIndex == SLOT_CRAFTING_RESULT)
      return canCraftCurrentRecipe();
    return true;
  }

  @Override
  public ItemStack takenFromSlot(int slotIndex, boolean consumeRecipe, EntityPlayer player) {
    if (!removeResources(player))
      return null;

    if (Proxies.common.isSimulating(worldObj))
      memorized.memorizeRecipe(worldObj, currentRecipe, currentCrafting);

    updateCraftResult(currentCrafting);
    return currentRecipe.getRecipeOutput().copy();
  }

  @Override
  public ItemStack getResult() {
    if (currentRecipe == null)
      return null;

    if (currentRecipe.getRecipeOutput() != null)
      return currentRecipe.getRecipeOutput().copy();
    return null;
  }

  /* INVENTORY */
  @Override
  public InventoryAdapter getInternalInventory() {
    return accessibleInventory;
  }

  /**
   * @return Inaccessible crafting inventory for the craft grid.
   */
  public InventoryAdapter getCraftingInventory() {
    return craftingInventory;
  }
}
TOP

Related Classes of forestry.factory.gadgets.TileWorktable$RecipeMemory

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.