/**
* Copyright (C) 2011 Jacob Scott <jascottytechie@gmail.com>
* Description: ( static methods for manipulating itemstacks )
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package me.jascotty2.libv01.bukkit.inventory;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import me.jascotty2.libv01.bukkit.item.JItem;
import me.jascotty2.libv01.bukkit.item.JItems;
import org.bukkit.Material;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.inventory.ItemStack;
public class ItemStackManip {
/**
* items that should not stack
*/
// mushroom soup, buckets
public final static List<Integer> noStack = Arrays.asList(282, 325, 326, 327, 335);
/**
* check if this stack cannot hold one more of this item
* @param items
* @param check
* @return
*/
public static boolean is_full(ItemStack[] items, ItemStack check) {
return is_full(items, check, false);
}
public static boolean is_full(ItemStack[] items, Material check) {
return check != null ? is_full(items, check.getId(), false) : false;
}
public static boolean is_full(ItemStack[] items, Material check, boolean extraStack) {
return check != null ? is_full(items, check.getId(), extraStack) : false;
}
/**
* check if this stack cannot hold one more of this item
* @param items
* @param check
* @param extraStack if to assume is allowed to stack some unstackables
* @return
*/
public static boolean is_full(ItemStack[] items, ItemStack check, boolean extraStack) {
int amt = check.getAmount();
for (ItemStack item : items) {
int mx = !extraStack || noStack.contains(item == null ? 0 : item.getTypeId())
? JItems.getMaxStack(item) : 64;
if (item == null || item.getAmount() == 0
|| (item.getTypeId() == check.getTypeId()
&& (item.getAmount() + amt <= mx))) {
return false;
} else if (item.getTypeId() == check.getTypeId()) {
amt -= mx - item.getAmount();
}
}
return true;
}
public static boolean is_full(ItemStack[] items, int check, boolean extraStack) {
int amt = 1;
for (ItemStack item : items) {
int mx = !extraStack || noStack.contains(item == null ? 0 : item.getTypeId())
? JItems.getMaxStack(item) : 64;
if (item == null || item.getAmount() == 0
|| (item.getTypeId() == check
&& (item.getAmount() + amt <= mx))) {
return false;
} else if (item.getTypeId() == check) {
amt -= mx - item.getAmount();
}
}
return true;
}
/**
* count how many of this item is in the ItemStack
* @param items
* @param check
* @return
*/
public static int count(ItemStack[] items, JItem check) {
if (check == null) {
return emptySlots(items);
}
int amt = 0;
for (ItemStack item : items) {
if (item != null && check.equals(item)) {
amt += item.getAmount();
}
}
return amt;
}
/**
* count how many times this material occurs in the ItemStack
* @param items
* @param check
* @return
*/
public static int count(ItemStack[] items, Material check) {
if (check == null) {
return emptySlots(items);
}
int amt = 0;
for (ItemStack item : items) {
if (item != null && item.getType() == check) {
amt += item.getAmount();
}
}
return amt;
}
/**
* count how many slots in this ItemStack array are empty
* @param items
* @return
*/
public static int emptySlots(ItemStack[] items) {
int amt = 0;
if (items != null) {
for (ItemStack item : items) {
if (item == null || item.getAmount() <= 0) {
++amt;
}
}
}
return amt;
}
public static boolean contains(ItemStack[] items, Material check) {
if (items != null) {
if (check == null) {
for (ItemStack item : items) {
if (item == null) {
return true;
}
}
} else {
for (ItemStack item : items) {
if (item != null && item.getType() == check) {
return true;
}
}
}
}
return false;
}
public static boolean contains(ItemStack[] items, Material check, short damage) {
if (items != null) {
if (check == null) {
for (ItemStack item : items) {
if (item == null) {
return true;
}
}
} else {
for (ItemStack item : items) {
if (item != null && item.getType() == check
&& item.getDurability() == damage) {
return true;
}
}
}
}
return false;
}
public static boolean canHold(ItemStack[] items, JItem check, int amt, boolean extraStack){
return amountCanHold(items, check, extraStack) >= amt;
}
public static int amountCanHold(ItemStack[] items, JItem check, boolean extraStack) {
int amt = 0;
if (items == null) {
return 0;
} else if (check == null) {
return emptySlots(items);
} else if (check.isEntity()) {
return -1;
}
// else if (check.isKit()) {
// Kit kit = check instanceof Kit ? (Kit) check : JItemDB.getKit(check);
// Kit.KitItem kititems[] = kit.getKitItems();
// ItemStack invCopy[] = copy(items);
// // loop through & "add" one at a time
// while (true) {
// int numtoadd = 0;
// for (int itn = 0; itn < kititems.length; ++itn) {
// numtoadd = kititems[itn].itemAmount;
// int maxStack = !extraStack || noStack.contains(kititems[itn].ID()) ? kititems[itn].MaxStackSize() : 64;
// for (int i = 0; i < invCopy.length && numtoadd > 0; ++i) {
// if (invCopy[i] == null || invCopy[i].getAmount() == 0) {
// invCopy[i] = kititems[itn].toItemStack();
// invCopy[i].setAmount(numtoadd);
// numtoadd -= numtoadd;
// } else if (invCopy[i].getAmount() < maxStack && kititems[itn].iequals(invCopy[i])) {
// int d = maxStack < numtoadd ? maxStack : numtoadd;
// invCopy[i].setAmount(invCopy[i].getAmount() + d);
// numtoadd -= d;
// }
// }
// if (numtoadd > 0) {
// // has scanned through full stack & cannot add more
// return amt;
// }
// }
// // 1 was added to the copy
// ++amt;
// }
// }
else {
int max = !extraStack || noStack.contains(check.ID()) ? check.MaxStackSize() : 64;
for (ItemStack item : items) {
if (item == null || item.getAmount() == 0
|| (check.equals(item) && item.getAmount() <= max)) {
amt += max - (item == null ? 0 : item.getAmount());
}
}
}
return amt;
}
public static int amountCanHold(ItemStack[] items, ItemStack check, boolean extraStack) {
int amt = 0;
if (items == null) {
return 0;
} else if (check == null) {
return emptySlots(items);
} else {
int max = !extraStack || noStack.contains(check.getTypeId()) ? JItems.getMaxStack(check) : 64;
for (ItemStack item : items) {
if (item == null || item.getAmount() == 0
|| (check.equals(item) && item.getAmount() <= max)) {
amt += max - (item == null ? 0 : item.getAmount());
}
}
}
return amt;
}
public static ItemStack[] remove(ItemStack[] items, ItemStack check) {
return remove(items, check, 0);
}
public static ItemStack[] remove(ItemStack[] items, ItemStack check, int start) {
if (items != null) {
int total = check.getAmount();
for (int i = start; i < items.length && total > 0; ++i) {
if (items[i] != null) {
if (items[i].getType() == check.getType()
&& (items[i].getData() == null
|| items[i].getData().getData() == check.getData().getData())) {
int a = items[i].getAmount();
if (total < a) {
items[i].setAmount(a - total);
total = 0;
} else {
items[i] = null;
total -= a;
}
return items;
}
}
}
}
return items;
}
public static ItemStack[] remove(ItemStack[] items, Material check) {
for (int i = 0; i < items.length; ++i) {
if (items[i] != null) {
if (items[i].getType() == check) {
if (items[i].getAmount() > 1) {
items[i].setAmount(items[i].getAmount() - 1);
} else {
items[i] = null;
}
return items;
}
}
}
return items;
}
public static ItemStack[] remove(ItemStack[] items, Material check, short damage) {
for (int i = 0; i < items.length; ++i) {
if (items[i] != null) {
if (items[i].getType() == check && items[i].getDurability() == damage) {
if (items[i].getAmount() > 1) {
items[i].setAmount(items[i].getAmount() - 1);
} else {
items[i] = null;
}
return items;
}
}
}
return items;
}
public static ItemStack[] remove(ItemStack[] items, JItem search, int amt) {
return remove(items, search, amt, 0);
}
public static ItemStack[] remove(ItemStack[] items, JItem search, int total, int start) {
if (items == null) {
return null;
} else if (search == null) {
return items;
} else if (!search.isKit() && !search.isEntity()) {
for (int i = start; i < items.length && total > 0; ++i) {
if (items[i] != null && search.equals(items[i])) {
int a = items[i].getAmount();
if (total < a) {
items[i].setAmount(a - total);
total = 0;
} else {
items[i] = null;
total -= a;
}
}
}
}
return items;
}
public static ItemStack[] remove(ItemStack[] items, ItemStack[] search) {
return remove(items, search, 0);
}
public static ItemStack[] remove(ItemStack[] items, ItemStack[] search, int start) {
for (ItemStack i : search) {
remove(items, i, start);
}
return items;
}
public static ItemStack[] remove(ItemStack[] items, List<ItemStack> search) {
return remove(items, search, 0);
}
public static ItemStack[] remove(ItemStack[] items, List<ItemStack> search, int start) {
for (ItemStack i : search) {
remove(items, i, start);
}
return items;
}
// public static ItemStack[] add(ItemStack[] items, JItem toAdd, int amt) {
// return add(items, toAdd, amt, false);
// }
// public static ItemStack[] add(ItemStack[] items, JItem toAdd, int amt, boolean extraStack) {
// if (items == null) {
// return null;
// } else if (toAdd == null) {
// return items;
// } else if (toAdd.IsValidItem()) {
// int mx = !extraStack || noStack.contains(toAdd.ID()) ? toAdd.MaxStackSize() : 64;
// while(amt > 0){
// add(items, toAdd.toItemStack(amt > mx ? mx : amt), extraStack);
// amt -= mx;
// }
// return items;
// } else if (toAdd.isKit()) {
// Kit kit = toAdd instanceof Kit ? (Kit) toAdd : JItemDB.getKit(toAdd);
// Kit.KitItem kititems[] = kit.getKitItems();
//
// // add one of each until fails
// for (int num = 0; num < amt; ++num) {
// int numtoadd = 0;
// for (int itn = 0; itn < kit.numItems(); ++itn) {
// numtoadd = kititems[itn].itemAmount;
// int maxStack = !extraStack || noStack.contains(kititems[itn].ID()) ? kititems[itn].MaxStackSize() : 64;
// for (int i = 0; i < items.length && numtoadd > 0; ++i) {
// if (items[i] == null || items[i].getAmount() == 0) {
// items[i] = kititems[itn].toItemStack();
// items[i].setAmount(numtoadd);
// numtoadd -= numtoadd;
// } else if (items[i].getAmount() < maxStack && kititems[itn].iequals(items[i])) {
// int d = maxStack < numtoadd ? maxStack : numtoadd;
// items[i].setAmount(items[i].getAmount() + d);
// numtoadd -= d;
// }
// }
// if (numtoadd > 0) {
// break;
// }
// }
// if (numtoadd > 0) {
// //early exit while adding
// break;
// }
// }
// }
// return items;
// }
/**
* add an ItemStack to another
* @param items
* @param toAdd
* @return
*/
public static ItemStack add(ItemStack[] items, ItemStack toAdd) {
return add(items, toAdd, false);
}
public static List<ItemStack> add(ItemStack[] items, List<ItemStack> toAdd, boolean extraStack) {
for(ItemStack i : toAdd) {
ItemStack i2 = add(items, i, extraStack);
if(i2.getAmount() > 0) {
i.setAmount(i2.getAmount());
} else {
toAdd.remove(i);
}
}
return toAdd;
}
/**
* add an ItemStack to an array
* @param items
* @param toAdd
* @param extraStack whether to allow some nonstackable items to stack
* @return
*/
public static ItemStack add(ItemStack[] items, ItemStack toAdd, boolean extraStack) {
ItemStack ret = toAdd.clone();
int mx = !extraStack || noStack.contains(toAdd.getTypeId())
? JItems.getMaxStack(toAdd) : 64;
boolean firstRun = true;
for (int i = 0; i < items.length; ++i) {
if (!firstRun && (items[i] == null || items[i].getAmount() == 0)) {
if(items[i] == null) {
items[i] = toAdd;
} else {
items[i].setTypeId(toAdd.getTypeId());
items[i].setDurability(toAdd.getDurability());
items[i].addEnchantments(toAdd.getEnchantments());
}
items[i].setAmount(ret.getAmount());
ret.setAmount(0);
return ret;
} else if (items[i] != null
&& items[i].getTypeId() == toAdd.getTypeId()
&& items[i].getDurability() == toAdd.getDurability() //(!JItems.hasData(toAdd.getTypeId()) || items[i].getData().getData() == toAdd.getData().getData())
&& sameEnchants(items[i], toAdd)
&& items[i].getAmount() < mx) {
// on first run, look for other stacks in array that could be incremented instead
if (items[i].getAmount() + ret.getAmount() <= mx) {
items[i].setAmount(items[i].getAmount() + ret.getAmount());
ret.setAmount(0);
return ret;
} else {
ret.setAmount(ret.getAmount() - (mx - items[i].getAmount()));
items[i].setAmount(mx);
}
} else if (firstRun && i + 1 >= items.length) {
firstRun = false;
i = -1; // negative, because gets incremented again
}
}
return ret;
}
private static boolean sameEnchants(ItemStack a, ItemStack b) {
if(a.getEnchantments().size() == b.getEnchantments().size()) {
for(Enchantment e : a.getEnchantments().keySet()) {
if(!b.containsEnchantment(e) || b.getEnchantmentLevel(e) != a.getEnchantmentLevel(e)) {
return false;
}
}
return true;
}
return false;
}
/**
* creates a summary of the itemstack array, <br/>
* so that each item is only listed once
* @param items
* @return
*/
public static List<ItemStack> itemStackSummary(ItemStack[] items) {
return itemStackSummary(items, 0);
}
public static List<ItemStack> itemStackSummary(ItemStack[] items, int start) {
ArrayList<ItemStack> summ = new ArrayList<ItemStack>();
if (items != null) {
for (int i = start; i < items.length; ++i) {
if (items[i] != null) {
int iti = indexOf(summ, items[i]);
if (iti < 0) {
summ.add(items[i].clone());
} else {
summ.get(iti).setAmount(summ.get(iti).getAmount() + items[i].getAmount());
}
}
}
}
return summ;
}
public static List<ItemStack> itemStackSummary(ItemStack[] items, JItem[] search, int start) {
ArrayList<ItemStack> summ = new ArrayList<ItemStack>();
if (items != null) {
for (int i = start; i < items.length; ++i) {
if (items[i] != null && (search == null || JItem.contains(search, items[i]))) {
int iti = indexOf(summ, items[i]);
if (iti < 0) {
summ.add(items[i].clone());
} else {
summ.get(iti).setAmount(summ.get(iti).getAmount() + items[i].getAmount());
}
}
}
}
return summ;
}
/**
* checks for differences between two itemstack arrays <br/>
* result amounts are second - first <br/>
* if there are less of an item in the second, the result will have a negative amount
* @param stack1 first to compare against
* @param stack2 second to check for differences
* @return list of results
*/
public static List<ItemStack> itemStackDifferences(ItemStack[] stack1, ItemStack[] stack2) {
ArrayList<ItemStack> changedItems = new ArrayList<ItemStack>();
if (stack1 == null) {
changedItems.addAll(Arrays.asList(copy(stack2)));
return changedItems;
} else if (stack2 == null) {
changedItems.addAll(Arrays.asList(copy(stack1)));
for(ItemStack i : changedItems) {
i.setAmount(-i.getAmount());
}
return changedItems;
}
// first, compile list of items before shopping
List<ItemStack> oldInventory = itemStackSummary(stack1);
// and after
List<ItemStack> newInventory = itemStackSummary(stack2);
// list of differences
// find those items that have changed / removed to the second
for (ItemStack i : oldInventory) {
int iti = indexOf(newInventory, i);
if (iti >= 0) {
// in second: check for changes
if (i.getAmount() != newInventory.get(iti).getAmount()) {
i.setAmount(newInventory.get(iti).getAmount() - i.getAmount());
changedItems.add(i);
}
} else {
// not in the second
i.setAmount(-i.getAmount());
changedItems.add(i);
}
}
// check for items added to the second
for (ItemStack i : newInventory) {
if (indexOf(oldInventory, i) == -1) {
// not in the first
changedItems.add(i);
}
}
return changedItems;
}
/**
* ignoring amount, find the index of an itemstack in a list
* @param source list to search
* @param search search criteria
* @return index, or -1 if not found
*/
public static int indexOf(List<ItemStack> source, ItemStack search) {
int ind = 0;
if (search == null) {
for (ItemStack i : source) {
if (i == null) {
return ind;
}
++ind;
}
} else {
for (ItemStack i : source) {
if (i != null && i.getType() == search.getType()
&& i.getDurability() == search.getDurability()){//&& (i.getData() == null || (i.getData().getData() == search.getData().getData()))) {
return ind;
}
++ind;
}
}
return -1;
}
/**
* ignoring amount, find the index of an itemstack in a list
* @param source list to search
* @param search search criteria
* @return index, or -1 if not found
*/
public static int indexOf(List<ItemStack> source, JItem search) {
int ind = 0;
if (search == null) {
for (ItemStack i : source) {
if (i == null) {
return ind;
}
++ind;
}
} else {
for (ItemStack i : source) {
if (search.equals(i)) {
return ind;
}
++ind;
}
}
return -1;
}
/**
* makes a copy of a minecraft ItemStack as a bukkit ItemStack
* @param minecraftItemStack
* @return
*/
// public static ItemStack[] copy(net.minecraft.server.ItemStack[] minecraftItemStack) {
// ItemStack[] invCpy = new ItemStack[minecraftItemStack.length];
// for (int i = 0; i < minecraftItemStack.length; ++i) {
// invCpy[i] = minecraftItemStack[i] == null ? null
// : new ItemStack(minecraftItemStack[i].getItem().id,
// minecraftItemStack[i].count,
// (short) minecraftItemStack[i].getData(),
// (byte) minecraftItemStack[i].getData());
// }
// return invCpy;
// }
/**
* makes a copy of a bukkit ItemStack
* @param items
* @return
*/
public static ItemStack[] copy(ItemStack[] items) {
ItemStack[] invCpy = new ItemStack[items.length];
for (int i = 0; i < items.length; ++i) {
invCpy[i] = items[i] == null ? null : items[i].clone();
}
return invCpy;
}
} // end class ItemStackManip