Package com.bergerkiller.bukkit.tc.utils

Source Code of com.bergerkiller.bukkit.tc.utils.TransferSignUtil

package com.bergerkiller.bukkit.tc.utils;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;

import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.block.BlockState;
import org.bukkit.block.Chest;
import org.bukkit.block.Dispenser;
import org.bukkit.block.Dropper;
import org.bukkit.block.Furnace;
import org.bukkit.inventory.DoubleChestInventory;
import org.bukkit.inventory.FurnaceInventory;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.ItemStack;

import com.bergerkiller.bukkit.common.bases.IntVector2;
import com.bergerkiller.bukkit.common.inventory.InventoryBase;
import com.bergerkiller.bukkit.common.inventory.ItemParser;
import com.bergerkiller.bukkit.common.utils.BlockUtil;
import com.bergerkiller.bukkit.common.utils.CommonUtil;
import com.bergerkiller.bukkit.common.utils.FaceUtil;
import com.bergerkiller.bukkit.common.utils.ItemUtil;
import com.bergerkiller.bukkit.common.utils.MathUtil;
import com.bergerkiller.bukkit.common.utils.ParseUtil;
import com.bergerkiller.bukkit.common.utils.RecipeUtil;
import com.bergerkiller.bukkit.tc.InteractType;
import com.bergerkiller.bukkit.tc.TrainCarts;
import com.bergerkiller.bukkit.tc.controller.MinecartMember;
import com.bergerkiller.bukkit.tc.controller.type.MinecartMemberChest;
import com.bergerkiller.bukkit.tc.events.SignActionEvent;
import com.bergerkiller.bukkit.tc.itemanimation.ItemAnimatedInventory;

/**
* Utilities for dealing with item transfers between different containers
*/
public class TransferSignUtil {
  private static final HashSet<InventoryHolder> chestsBuffer = new HashSet<InventoryHolder>();

  public static Inventory getInventory(SignActionEvent info) {
    if (info.isCartSign()) {
      if (info.getMember() instanceof MinecartMemberChest) {
        return ((MinecartMemberChest) info.getMember()).getEntity().getInventory();
      } else {
        return null;
      }
    } else {
      return info.getGroup().getInventory();
    }
  }

  public static Collection<InventoryHolder> getInventories(SignActionEvent info) {
    if (info.isCartSign()) {
      if (info.getMember() instanceof MinecartMemberChest) {
        return Arrays.asList((InventoryHolder) info.getMember().getEntity().getEntity());
      } else {
        return Collections.emptyList();
      }
    } else {
      Collection<InventoryHolder> trainInvs = new ArrayList<InventoryHolder>(info.getGroup().size());
      for (MinecartMember<?> member : info.getGroup()) {
        if (member instanceof MinecartMemberChest) {
          trainInvs.add((InventoryHolder) member.getEntity().getEntity());
        }
      }
      return trainInvs;
    }
  }

  public static int depositInFurnace(Inventory from, Furnace toFurnace, ItemParser parser, boolean isFuelPreferred) {
    final Inventory to = toFurnace.getInventory();
    List<ItemParser> heatables = new ArrayList<ItemParser>();
    List<ItemParser> fuels = new ArrayList<ItemParser>();
    if (!parser.hasType()) {
      // Add all heatables and fuels
      for (ItemParser p : TrainCarts.plugin.getParsers("heatable", 1)) {
        if (p == null || !p.hasType()) {
          heatables.clear();
          break;
        } else {
          heatables.add(p);
        }
      }
      for (ItemParser p : TrainCarts.plugin.getParsers("fuel", 1)) {
        if (p == null || !p.hasType()) {
          fuels.clear();
          break;
        } else {
          fuels.add(p);
        }
      }
      if (heatables.isEmpty() && fuels.isEmpty()) {
        return 0;
      }
    } else {
      // Is the parser fuel or heatable?
      boolean heatable = RecipeUtil.isHeatableItem(parser.getItemStack(1));
      boolean fuel = RecipeUtil.isFuelItem(parser.getTypeId());
      if (heatable && fuel) {
        if (isFuelPreferred) {
          fuels.add(parser);
        } else {
          heatables.add(parser);
        }
      } else if (heatable) {
        heatables.add(parser);
      } else if (fuel) {
        fuels.add(parser);
      } else {
        return 0;
      }
    }
    final int startAmount = parser.hasAmount() ? parser.getAmount() : Integer.MAX_VALUE;
    int amountToTransfer = startAmount;

    // Transfer heatable items
    for (ItemParser p : heatables) {
      ItemStack item = to.getItem(0);
      if (item == null) {
        item = ItemUtil.emptyItem();
      }
      amountToTransfer -= ItemUtil.transfer(from, item, p, amountToTransfer);
      to.setItem(0, item);
    }

    // Transfer fuel (requires manual limiting if no amount is set)
    for (ItemParser p : fuels) {
      if (p == null) {
        continue;
      }
      if (amountToTransfer == 0) {
        break;
      }

      int transferCount = amountToTransfer;
      ItemStack fuel = to.getItem(1);
      if (fuel == null) {
        fuel = ItemUtil.emptyItem();
      }
      if (!p.hasAmount()) {
        // Fill the minimal amount needed to burn all the heatables in the furnace
        ItemStack cookeditem = to.getItem(0);
        if (cookeditem == null || cookeditem.getType() == Material.AIR) continue;
        int fuelNeeded = cookeditem.getAmount() * 200;
        if (fuelNeeded == 0) continue; //nothing to cook
        //===================================================
        fuelNeeded -= toFurnace.getCookTime();
        if (fuelNeeded <= 0) continue; //we got enough
        //===================================================
        int fuelPerItem = 0;
        if (fuel.getType() == Material.AIR) {
          fuelPerItem = RecipeUtil.getFuelTime(p.getItemStack(1));
        } else {
          fuelPerItem = RecipeUtil.getFuelTime(fuel);
        }
        //====================================================
        if (fuelPerItem == 0) continue;
        fuelNeeded -= fuelPerItem * fuel.getAmount();
        if (fuelNeeded <= 0) continue;
        //====================================================
        transferCount = Math.min(amountToTransfer, (int) Math.ceil((double) fuelNeeded / (double) fuelPerItem));
      }
      amountToTransfer -= ItemUtil.transfer(from, fuel, p, transferCount);
      to.setItem(1, fuel);
    }
    return startAmount - amountToTransfer;
  }

  public static IntVector2 readRadius(String text) {
    // Parse radius width and height (negative allowed for reversed sorting)
    int radWidth = TrainCarts.defaultTransferRadius;
    int radHeight = TrainCarts.defaultTransferRadius;
    int radStartIndex = text.lastIndexOf(' ');
    if (radStartIndex != -1) {
      String radText = text.substring(radStartIndex + 1);
      String[] parts = radText.split(":");
      if (parts.length == 1) {
        radWidth = radHeight = ParseUtil.parseInt(radText, TrainCarts.defaultTransferRadius);
      } else if (parts.length == 2) {
        radWidth = ParseUtil.parseInt(parts[0], TrainCarts.defaultTransferRadius);
        radHeight = ParseUtil.parseInt(parts[1], TrainCarts.defaultTransferRadius);
      }
    }
    // Limit radius
    radWidth = MathUtil.clamp(radWidth, TrainCarts.maxTransferRadius);
    radHeight = MathUtil.clamp(radHeight, TrainCarts.maxTransferRadius);
    // Done
    return new IntVector2(radWidth, radHeight);
  }

  public static Collection<BlockState> getBlockStates(SignActionEvent info, IntVector2 radius) {
    return getBlockStates(info, radius.x, radius.z);
  }

  public static Collection<BlockState> getBlockStates(SignActionEvent info, int radWidth, int radHeight) {
    // Obtain the BlockFaces using absolute width and height
    final Block centerBlock = info.getRails();
    int radX = Math.abs(radWidth);
    int radY = Math.abs(radHeight);
    int radZ = Math.abs(radWidth);
    BlockFace dir = info.getRailDirection();
    if (FaceUtil.isVertical(dir)) {
      radY = 0;
    } else if (FaceUtil.isAlongX(dir)) {
      radX = 0;
    } else if (FaceUtil.isAlongZ(dir)) {
      radZ = 0;
    }
    List<BlockState> states = new ArrayList<BlockState>(BlockUtil.getBlockStates(centerBlock, radX, radY, radZ));

    // Get rid of twice-stored double chests
    try {
      Iterator<BlockState> iter = states.iterator();
      while (iter.hasNext()) {
        BlockState next = iter.next();
        if (!(next instanceof Chest)) {
          continue;
        }
        DoubleChestInventory inventory = CommonUtil.tryCast(((Chest) next).getInventory(), DoubleChestInventory.class);
        if (inventory == null) {
          continue;
        }
        if (chestsBuffer.add(inventory.getLeftSide().getHolder()) &&
            chestsBuffer.add(inventory.getRightSide().getHolder())) {
          continue;
        }
        // Already added chest(s), disregard
        iter.remove();
      }
    } finally {
      chestsBuffer.clear();
    }

    // Sort the resulting states based on distance from the center
    final boolean widthInv = radWidth < 0;
    final boolean heightInv = radHeight < 0;
    Collections.sort(states, new Comparator<BlockState>() {

      public int getIndex(BlockState state) {
        int dx = MathUtil.invert(Math.abs(centerBlock.getX() - state.getX()), widthInv);
        int dy = MathUtil.invert(Math.abs(centerBlock.getY() - state.getY()), heightInv);
        int dz = MathUtil.invert(Math.abs(centerBlock.getZ() - state.getZ()), widthInv);
        // Magical formula timez!
        return dx + 16 * dz + 256 * dy;
      }

      @Override
      public int compare(BlockState o1, BlockState o2) {
        return getIndex(o1) - getIndex(o2);
      }
    });
   
    return states;
  }

  /**
   * Finds all nearby block states with which can be interacted according to the sign
   *
   * @param info to use
   * @param mode to use, collect or dispense
   * @return A collection of BlockStates to operate on
   */
  public static Collection<InventoryHolder> findBlocks(SignActionEvent info, String mode) {
    Collection<InteractType> typesToCheck = InteractType.parse(mode, info.getLine(1));
    if (typesToCheck.isEmpty()) {
      return Collections.emptyList();
    }

    // Parse radius width and height (negative allowed for reversed sorting)
    IntVector2 radius = readRadius(info.getLine(1));

    // Get the blocks to collect/deposit using radiuses previously parsed
    Collection<BlockState> found = TransferSignUtil.getBlockStates(info, radius);
    if (found.isEmpty()) {
      return Collections.emptyList();
    }

    List<InventoryHolder> rval = new ArrayList<InventoryHolder>(found.size());
    // This weird for loop is needed because typesToCheck is not a set!
    // The order in which inventories are added is of importance
    for (InteractType type : typesToCheck) {
      switch (type) {
        case CHEST : {
          for (BlockState state : found) {
            if (state instanceof Chest) {
              rval.add((Chest) state);
            }
          }
          break;
        }
        case FURNACE : {
          for (BlockState state : found) {
            if (state instanceof Furnace) {
              rval.add((Furnace) state);
            }
          }
          break;
        }
        case DISPENSER : {
          for (BlockState state : found) {
            if (state instanceof Dispenser) {
              rval.add((Dispenser) state);
            }
          }
          break;
        }
        case DROPPER : {
            for(BlockState state : found) {
                if (state instanceof Dropper) {
                    rval.add((Dropper) state);
                }
            }
        }
        case GROUNDITEM : {
          rval.add(new GroundItemsState(info.getRails(), Math.abs(radius.x)));
          break;
        }
      }
    }
    return rval;
  }

  public static int transferAllItems(Collection<InventoryHolder> fromHolders, Collection<InventoryHolder> toHolders, ItemParser itemParser, boolean isFuelPreferred) {
    int amount, transferred = 0;
    for (InventoryHolder fromHolder : fromHolders) {
      Inventory from = fromHolder.getInventory();

      // Averaged?
      if (itemParser instanceof AveragedItemParser) {
        // Perform 'one by one' logic - which is a lot slower
        final int totalAmount = itemParser.hasAmount() ? itemParser.getAmount() : Integer.MAX_VALUE;
        final ItemParser single = itemParser.setAmount(1);
        boolean continueTransferring;
        int transferredAmount = 0;
        do {
          // Start of the loop: If nothing is transferred, break it.
          continueTransferring = false;
          // Go by all inventories
          for (InventoryHolder toHolder : toHolders) {
            Inventory to = toHolder.getInventory();
            amount = transferItems(from, to, single, isFuelPreferred);
            if (amount > 0) {
              transferred += amount;
              transferredAmount += amount;
              // Continue transferring? Evaluate the current transfer amount
              if (!(continueTransferring = (transferredAmount < totalAmount))) {
                break;
              }
            }
          }
        } while (continueTransferring);
      } else {
        // Perform regular item transfer: fill one by one
        for (InventoryHolder toHolder : toHolders) {
          Inventory to = toHolder.getInventory();
          amount = transferItems(from, to, itemParser, isFuelPreferred);
          transferred += amount;
          // Update item parser amount
          if (amount > 0 && itemParser.hasAmount()) {
            itemParser = itemParser.setAmount(itemParser.getAmount() - amount);
          }
        }
      }
    }
    return transferred;
  }
 
  /**
   * Performs item transfer from one inventory to the other, utilizing the item parser specified
   *
   * @param from inventory
   * @param to inventory
   * @param itemParser to use
   * @param isFuelPreferred - whether (when depositing into furnaces) fuel is preferred over heatable
   * @return the amount of items transferred
   */
  public static int transferItems(Inventory from, Inventory to, ItemParser itemParser, boolean isFuelPreferred) {
    final InventoryHolder toHolder = to.getHolder();
    final InventoryHolder fromHolder = from.getHolder();

    // Obtaining items from a furnace? Only use output slot!
    if (from instanceof FurnaceInventory) {
      final FurnaceInventory finv = (FurnaceInventory) from;
      from = new InventoryBase() {
        @Override
        public int getSize() {
          return 1;
        }

        @Override
        public ItemStack getItem(int index) {
          return finv.getResult();
        }

        @Override
        public void setItem(int index, ItemStack item) {
          finv.setResult(item);
        }
      };
    }

    // Do not deposit using animations for ground items, it shows duplicates which looks bad
    if (TrainCarts.showTransferAnimations && !(from instanceof GroundItemsInventory)) {
      from = ItemAnimatedInventory.convert(from, fromHolder, toHolder);
    }

    // Depositing into a furnace or other type of inventory?
    if (toHolder instanceof Furnace) {
      return depositInFurnace(from, (Furnace) toHolder, itemParser, isFuelPreferred);
    } else {
      return ItemUtil.transfer(from, to, itemParser, itemParser.getAmount());
    }
  }
}
TOP

Related Classes of com.bergerkiller.bukkit.tc.utils.TransferSignUtil

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.