Package xk.xact.api

Source Code of xk.xact.api.CraftingHandler

package xk.xact.api;

import cpw.mods.fml.common.registry.GameRegistry;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.inventory.InventoryCrafting;
import net.minecraft.item.ItemStack;
import net.minecraft.item.crafting.CraftingManager;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.entity.player.PlayerDestroyItemEvent;
import net.minecraftforge.oredict.OreDictionary;
import xk.xact.inventory.InventoryUtils;
import xk.xact.network.CommonProxy;
import xk.xact.recipes.CraftRecipe;
import xk.xact.recipes.RecipeUtils;
import xk.xact.util.Utils;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import static xk.xact.util.Utils.copyArray;

// Used to craft, check if can craft, etc.

/**
* This handles the crafting of the recipes for the Crafter machine and for the Chips.
*/
public abstract class CraftingHandler {


  protected final ICraftingDevice device;

  protected CraftingHandler(ICraftingDevice device) {
    this.device = device;
  }

  /**
   * Easy way to create a CraftingHandler.
   *
   * @param device the ICraftingDevice from which to reference the AvailableInventories
   */
  public static CraftingHandler createCraftingHandler(final ICraftingDevice device) {
    return new CraftingHandler( device ) {
      @Override
      public List getAvailableInventories() {
        return device.getAvailableInventories();
      }
    };
  }


  /**
   * Whether if the specified recipe can be crafted.
   * In other words, if there are enough items to craft it.
   *
   * @param recipe the recipe to check.
   * @return true if contains all the required ingredients.
   */
  public boolean canCraft(CraftRecipe recipe, EntityPlayer player) {
    if( recipe == null )
      return false;

    if( player != null && player.capabilities.isCreativeMode )
      return true;

    ItemStack[] ingredients = recipe.getSimplifiedIngredients();
    for( ItemStack cur : ingredients ) {
      if( cur == null ) continue;

      int found = getCountFor( recipe, cur, false );
      if( found < cur.stackSize )
        return false;
    }
    return true;
  }

  public void doCraft(CraftRecipe recipe, EntityPlayer player, ItemStack craftedItem) {
    InventoryCrafting craftMatrix = generateTemporaryCraftingGridFor( recipe, player, true );
    if( craftMatrix == null )
      return;

    craftedItem.onCrafting( device.getWorld(), player, craftedItem.stackSize );
    GameRegistry.onItemCrafted( player, craftedItem, craftMatrix );

    consumeIngredients( craftMatrix, player );

    // Update the device's state.
    device.updateState();
  }

  /**
   * Manages the crafting of a recipe.
   * Fires the crafting event with the ingredients provided.
   *
   * @param recipe      the CraftRecipe from which to take ingredients of the recipe.
   * @param craftMatrix the crafting grid with the ingredients for this recipe.
   */
  public ItemStack getRecipeResult(CraftRecipe recipe, InventoryCrafting craftMatrix) {
    if( recipe == null )
      return null;

    ItemStack craftedItem = recipe.getResult();

    if( craftMatrix != null ) {
      ItemStack stack = recipe.getRecipePointer().getOutputFrom( craftMatrix );
      if( stack == null )
        stack = CraftingManager.getInstance().findMatchingRecipe( craftMatrix, device.getWorld() );
      if( stack != null )
        craftedItem = stack;
    }

    return craftedItem;
  }

  public void consumeIngredients(InventoryCrafting craftMatrix, EntityPlayer player) {
    // consume the items

    ArrayList<ItemStack> remainingList = new ArrayList<ItemStack>();

    mainLoop:
    for( int i = 0; i < craftMatrix.getSizeInventory(); i++ ) {
      ItemStack stackInSlot = craftMatrix.getStackInSlot( i );
      if( stackInSlot == null )
        continue;

      craftMatrix.decrStackSize( i, 1 );

      if( stackInSlot.getItem().hasContainerItem() ) {
        ItemStack containerStack = stackInSlot.getItem().getContainerItemStack( stackInSlot );

        if( containerStack.isItemStackDamageable() && containerStack.getItemDamage() > containerStack.getMaxDamage() ) {
          MinecraftForge.EVENT_BUS.post( new PlayerDestroyItemEvent( player, containerStack ) );
          device.getWorld().playSoundAtEntity( player, "random.break", 0.8F, 0.8F + player.worldObj.rand.nextFloat() * 0.4F );
          containerStack = null;
        }

        if( containerStack != null ) {
          if( stackInSlot.getItem().doesContainerItemLeaveCraftingGrid( stackInSlot ) ) {
            for( Object inventory : getAvailableInventories() ) {
              IInventoryAdapter adapter = InventoryUtils.getInventoryAdapter( inventory );
              if( adapter != null ) {
                containerStack = adapter.placeItem( containerStack );
                if( containerStack == null )
                  continue mainLoop;
              }
            }
          }

          remainingList.add( containerStack );
        }
      }
    }
    craftMatrix.onInventoryChanged();

    // give back the remaining items
    for( ItemStack stack : remainingList ) {
      if( !addToInventories( stack ) )
        player.dropPlayerItem( stack );
    }
    ItemStack[] remainingItems = InventoryUtils.getContents( craftMatrix );
    for( ItemStack stack : remainingItems ) {
      if( !addToInventories( stack ) )
        player.dropPlayerItem( stack );
    }
  }

  /**
   * Tries to add the ItemStack on to the first available inventory,
   * and will iterate through them until it's correctly placed.
   * <p/>
   * Will return true if the stack was completely merged on the inventories.
   * <p/>
   * Note: the stack will be manipulated.
   *
   * @param stack the stack to be added to the inventories.
   * @return true on success. False if the stack couldn't be completely merged to the inventories.
   * @see CraftingHandler#getAvailableInventories()
   */
  protected boolean addToInventories(ItemStack stack) {
    if( stack == null )
      return true; // technically, counts as a success.

    for( Object inventory : getAvailableInventories() ) {
      IInventoryAdapter adapter = InventoryUtils.getInventoryAdapter( inventory );
      ItemStack result = adapter.placeItem( stack );
      if( result == null ) {
        stack.stackSize = 0;
        return true;
      } else {
        stack.stackSize = result.stackSize;
      }
    }
    return false;
  }


  /**
   * Provide all the inventories to be included for taking resources from, and for placing remaining items once crafted.
   * <p/>
   * You might want (or not) to include the player's inventory at the end of this list.
   *
   * @return a list of inventories.
   */
  public abstract List getAvailableInventories();


  /**
   * Gets the amount of items of the same kind of the passed stack on the available inventories.
   *
   * @param recipe   the CraftRecipe to check
   * @param stack    the stack to compare with.
   * @param countAll whether if this should keep counting after finding enough.
   *                 Enough implies that the amount found is equal or higher than the amount required.
   * @return the count of the items found.
   */
  public int getCountFor(CraftRecipe recipe, ItemStack stack, boolean countAll) {
    int found = 0;
    int ingredientIndex = RecipeUtils.getIngredientIndex( recipe, stack );
    if( ingredientIndex == -1 )
      return 0; // not an ingredient! do something!
    for( Object inventory : getAvailableInventories() ) {
      IInventoryAdapter adapter = InventoryUtils.getInventoryAdapter( inventory );
      for( ItemStack item : adapter ) {
        if( !countAll && found >= stack.stackSize ) {
          return found;
        }
        if( item == null )
          continue;
        if( isItemMatchingIngredient( item, recipe, ingredientIndex ) ) {
          found += item.stackSize;
        }
      }
    }
    return found;
  }

  /**
   * Will generate a temporary crafting grid based on the recipe's ingredients.
   * The items that populate the grid will be taken from the available inventories.
   *
   * @param recipe the recipe from which to take the ingredients.
   */
  public InventoryCrafting generateTemporaryCraftingGridFor(CraftRecipe recipe, EntityPlayer player, boolean takeItems) {
    if( !canCraft( recipe, player ) ) {
      Utils.debug( "XACT: generateTemporaryCraftingGridFor: !canCraft" );
      return null;
    }
    boolean creativeMod = player != null && !CommonProxy.isFakePlayer( player ) && player.capabilities.isCreativeMode;
    if( creativeMod )
      return InventoryUtils.simulateCraftingInventory( recipe.getIngredients() );

    ItemStack[] contents = findAndGetRecipeIngredients( recipe, takeItems );
    return InventoryUtils.simulateCraftingInventory( contents );
  }

  /**
   * Gets the amount of missing ingredients.
   * The array contains the amount (associated by index with the ingredient it represents) missing of that item.
   *
   * @param recipe the CraftRecipe representation of the recipe.
   * @return an array of int. any value of 0 represents there's no items left.
   * @see xk.xact.recipes.CraftRecipe#getSimplifiedIngredients()
   */
  public int[] getMissingIngredientsCount(CraftRecipe recipe) { // Example: Missing 3 redstone, 2 cobblestone.
    ItemStack[] ingredients = recipe.getSimplifiedIngredients();
    int[] retValue = new int[ingredients.length];
    for( int i = 0; i < ingredients.length; i++ ) {
      ItemStack stack = ingredients[i];
      if( stack == null ) {
        retValue[i] = 0;
        continue;
      }
      int found = getCountFor( recipe, stack, false );
      retValue[i] = (found >= stack.stackSize) ? 0 : stack.stackSize - found;
    }
    return retValue;
  }

  /**
   * Provides a list of the missing ingredients.
   *
   * @param recipe the CraftRecipe representation of the recipe.
   * @return an array of ItemStack, each of which has as stackSize the amount missing.
   * @see CraftingHandler#getMissingIngredientsCount(xk.xact.recipes.CraftRecipe)
   */
  public ItemStack[] getMissingIngredients(CraftRecipe recipe) {
    if( recipe == null )
      return new ItemStack[0];

    ArrayList<ItemStack> list = new ArrayList<ItemStack>();
    int[] missingCount = getMissingIngredientsCount( recipe );
    ItemStack[] ingredients = copyArray( recipe.getSimplifiedIngredients() );

    for( int i = 0; i < missingCount.length; i++ ) {
      int missing = missingCount[i];
      if( missing > 0 ) {
        ingredients[i].stackSize = missing;
        list.add( ingredients[i] );
      }
    }

    return list.toArray( new ItemStack[list.size()] );
  }

  /**
   * A string listing the missing ingredients for the specified recipe.
   * Useful to tell the user what's missing.
   */
  public String getMissingIngredientsString(CraftRecipe recipe) {
    if( recipe == null )
      return "<invalid recipe>";

    String retValue = "";
    ItemStack[] ingredients = getMissingIngredients( recipe );

    for( int i = 0; i < ingredients.length; i++ ) {
      if( i > 0 )
        retValue += ", ";
      retValue += Utils.stackDescription( ingredients[i] );
    }
    return retValue.equals( "" ) ? "none." : retValue;
  }

  public boolean[] getMissingIngredientsArray(CraftRecipe recipe) {
    boolean[] missingArray = new boolean[9];
    if( recipe == null ) {
      return missingArray;
    }

    ItemStack[] ingredients = recipe.getIngredients();
    ItemStack[] missingIngredients = getMissingIngredients( recipe );

    for( ItemStack currentMissed : missingIngredients ) {
      if( currentMissed == null )
        continue;

      int remaining = currentMissed.stackSize;
      for( int i = 0; remaining > 0 && i < ingredients.length; i++ ) {
        if( ingredients[i] != null && ingredients[i].isItemEqual( currentMissed )
            && ItemStack.areItemStackTagsEqual( ingredients[i], currentMissed ) ) {
          remaining--;
          missingArray[i] = true;
        }

      }
    }

    return missingArray;
  }

  protected ItemStack[] findAndGetRecipeIngredients(CraftRecipe recipe, boolean doRemove) {
    Map<Integer, int[]> gridIndexes = recipe.getGridIndexes();
    ItemStack[] simplifiedIngredients = recipe.getSimplifiedIngredients();
    ItemStack[] contents = new ItemStack[9];

    ingredient:
    for( int i = 0; i < simplifiedIngredients.length; i++ ) {
      int[] indexes = gridIndexes.get( i );
      int required = simplifiedIngredients[i].stackSize;

      for( Object inventory : getAvailableInventories() ) {
        IInventoryAdapter adapter = InventoryUtils.getInventoryAdapter( inventory );
        for( ItemStack item : adapter ) {
          if( required <= 0 ) {
            continue ingredient; // next ingredient
          }
          if( item == null || item.stackSize <= 0 ) {
            continue; // next inventory item
          }

          int available = item.stackSize;
          for( int index : indexes ) {
            if( required <= 0 || available <= 0 )
              break;

            if( contents[index] != null || !isItemMatchingIngredient( item, recipe, index )) {
              continue; // try on next grid slot.
            }

            if( doRemove ) {
              contents[index] = adapter.takeItem( item, 1 );
            } else {
              contents[index] = item.copy();
              contents[index].stackSize = 1;
            }
            required--;
            available -= contents[index] == null ? 0 : contents[index].stackSize;
          }
        }
      }
    }
    return contents;
  }

  protected boolean isItemMatchingIngredient(ItemStack item, CraftRecipe recipe, int ingredientIndex) {
    if( item == null )
      return false;
    ItemStack ingredient = recipe.getIngredients()[ingredientIndex];

    if( InventoryUtils.similarStacks( item, ingredient, true ) )
      return true;

    // Ore dictionary?
    if( recipe.isOreRecipe() ) {
      int oreID = OreDictionary.getOreID( ingredient );

      if( oreID != -1 ) {
        ArrayList<ItemStack> equivalencies = OreDictionary.getOres( oreID );

        for( ItemStack current : equivalencies ) {
          if( InventoryUtils.similarStacks( item, current, true ) ) // do I need this initial check?
            return true;
          if( item.itemID == current.itemID ) {
            if( current.getItemDamage() == -1 || item.getItemDamage() == current.getItemDamage() )
              return true;
          }
        }
      }
    }

    // regular test: if replacing the item on that spot still matches with the recipe.
    return recipe.matchesIngredient( ingredientIndex, item, device.getWorld() );
  }

}
TOP

Related Classes of xk.xact.api.CraftingHandler

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.