682 lines
23 KiB
Java
682 lines
23 KiB
Java
package de.jeff_media.ChestSort;
|
|
|
|
import java.io.File;
|
|
import java.io.FileNotFoundException;
|
|
import java.io.FilenameFilter;
|
|
import java.util.ArrayList;
|
|
import java.util.Arrays;
|
|
import java.util.Comparator;
|
|
import java.util.HashMap;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Scanner;
|
|
|
|
import org.bukkit.Bukkit;
|
|
import org.bukkit.Material;
|
|
import org.bukkit.enchantments.Enchantment;
|
|
import org.bukkit.entity.HumanEntity;
|
|
import org.bukkit.entity.Player;
|
|
import org.bukkit.event.inventory.InventoryClickEvent;
|
|
import org.bukkit.event.inventory.InventoryType;
|
|
import org.bukkit.inventory.Inventory;
|
|
import org.bukkit.inventory.ItemStack;
|
|
import org.bukkit.inventory.PlayerInventory;
|
|
import org.bukkit.inventory.meta.EnchantmentStorageMeta;
|
|
import org.bukkit.inventory.meta.ItemMeta;
|
|
import org.bukkit.inventory.meta.PotionMeta;
|
|
import org.bukkit.potion.PotionData;
|
|
|
|
import de.jeff_media.ChestSort.hooks.CrackShotHook;
|
|
import de.jeff_media.ChestSort.hooks.InventoryPagesHook;
|
|
import de.jeff_media.ChestSort.utils.CategoryLinePair;
|
|
import de.jeff_media.ChestSort.utils.TypeMatchPositionPair;
|
|
|
|
public class ChestSortOrganizer {
|
|
|
|
// This is the heart of ChestSort!
|
|
// All of the sorting stuff happens here.
|
|
|
|
/*
|
|
* Thoughts before implementing: We create a string from each item that can be
|
|
* sorted. We will omit certain parts of the name and put them behind the main
|
|
* name for sorting reasons. E.g. ACACIA_LOG -> LOG_ACACIA (so all LOGs are
|
|
* grouped) Diamond, Gold, Iron, Stone, Wood does NOT have to be sorted, because
|
|
* they are already alphabetically in the right order
|
|
*/
|
|
|
|
ChestSortPlugin plugin;
|
|
CrackShotHook crackShotHook;
|
|
InventoryPagesHook inventoryPagesHook;
|
|
|
|
private static final int maxInventorySize=54;
|
|
private static final int playerInvStartSlot=9; // Inclusive
|
|
private static final int playerInvEndSlot=35; // Inclusive
|
|
|
|
// All available colors in the game. We will strip this from the item names and
|
|
// keep the color in a separate variable
|
|
static final String[] colors = { "white", "orange", "magenta", "light_blue", "light_gray", "yellow", "lime", "pink",
|
|
"gray", "cyan", "purple", "blue", "brown", "green", "red", "black" };
|
|
|
|
// The same applies for wood. We strip the wood name from the item name and keep
|
|
// it in the above mentioned color variable
|
|
static final String[] woodNames = { "acacia", "birch", "jungle", "oak", "spruce", "dark_oak" };
|
|
|
|
private static final String emptyPlaceholderString = "~";
|
|
|
|
// We store a list of all Category objects
|
|
ArrayList<ChestSortCategory> categories = new ArrayList<ChestSortCategory>();
|
|
ArrayList<String> stickyCategoryNames = new ArrayList<String>();
|
|
|
|
ChestSortOrganizer(ChestSortPlugin plugin) {
|
|
this.plugin = plugin;
|
|
|
|
// Load Categories
|
|
File categoriesFolder = new File(
|
|
plugin.getDataFolder().getAbsolutePath() + File.separator + "categories" + File.separator);
|
|
File[] listOfCategoryFiles = categoriesFolder.listFiles(new FilenameFilter() {
|
|
public boolean accept(File directory, String fileName) {
|
|
if (!fileName.endsWith(".txt")) {
|
|
return false;
|
|
}
|
|
if (fileName.matches("(?i)^\\d\\d\\d.*\\.txt$")) // Category between 900 and 999-... are default
|
|
// categories
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
});
|
|
for (File file : listOfCategoryFiles) {
|
|
if (file.isFile()) {
|
|
// Category name is the filename without .txt
|
|
String categoryName = file.getName().replaceFirst(".txt", "");
|
|
|
|
if (plugin.debug) {
|
|
plugin.getLogger().info("Loading category file " + file.getName());
|
|
}
|
|
try {
|
|
ChestSortCategory category = new ChestSortCategory(categoryName, loadCategoryFile(file));
|
|
categories.add(category);
|
|
if (plugin.debug) {
|
|
plugin.getLogger().info("Loaded category file " + file.getName() + " ("
|
|
+ category.typeMatches.length + " items)");
|
|
}
|
|
} catch (FileNotFoundException e) {
|
|
plugin.getLogger().warning("Could not load category file: " + file.getName());
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Make categories sticky
|
|
for(String catName : stickyCategoryNames) {
|
|
for(ChestSortCategory cat : categories) {
|
|
if(catName.equalsIgnoreCase(cat.name)) {
|
|
cat.setSticky();
|
|
}
|
|
}
|
|
}
|
|
|
|
crackShotHook = new CrackShotHook(plugin);
|
|
inventoryPagesHook = new InventoryPagesHook(plugin);
|
|
|
|
}
|
|
|
|
// Returns an array with all typematches listed in the category file
|
|
TypeMatchPositionPair[] loadCategoryFile(File file) throws FileNotFoundException {
|
|
// This is called "sticky" in the category files. When it is enabled, it has the
|
|
// same effect as when you set {keepCategoryOrder} in your sorting-method, but
|
|
// you can set it per category
|
|
boolean appendLineNumber = false;
|
|
Scanner sc = new Scanner(file);
|
|
List<TypeMatchPositionPair> lines = new ArrayList<TypeMatchPositionPair>();
|
|
short currentLineNumber = 1;
|
|
while (sc.hasNextLine()) {
|
|
String currentLine = sc.nextLine();
|
|
currentLine = currentLine.trim().replaceAll(" ", "");
|
|
|
|
if (currentLine.contains("#")) {
|
|
// the first #
|
|
String[] split = currentLine.split("#");
|
|
if (split.length > 0) {
|
|
currentLine = split[0];
|
|
} else {
|
|
currentLine = null;
|
|
}
|
|
}
|
|
if ("".equals(currentLine)) {
|
|
currentLine = null;
|
|
}
|
|
if (currentLine != null) {
|
|
if (currentLine.toLowerCase().startsWith("sticky=")) {
|
|
|
|
if (currentLine.toLowerCase().endsWith("=true")) {
|
|
appendLineNumber = true;
|
|
makeCategoryStickyByFileName(file.getName());
|
|
if (plugin.debug)
|
|
plugin.getLogger().info("Sticky set to true in " + file.getName());
|
|
}
|
|
} else {
|
|
if (currentLine != null) {
|
|
lines.add(new TypeMatchPositionPair(currentLine, currentLineNumber, appendLineNumber));
|
|
if (plugin.debug)
|
|
plugin.getLogger().info("Added typeMatch to category file: " + currentLine);
|
|
}
|
|
}
|
|
}
|
|
currentLineNumber++;
|
|
}
|
|
TypeMatchPositionPair[] result = lines.toArray(new TypeMatchPositionPair[0]);
|
|
sc.close();
|
|
return result;
|
|
}
|
|
|
|
private void makeCategoryStickyByFileName(String name) {
|
|
String catName = name.replaceAll("\\.txt$", "");
|
|
|
|
stickyCategoryNames.add(catName);
|
|
|
|
|
|
}
|
|
|
|
// Convert the item name to what I call a "sortable item name".
|
|
// Sorry, the method name is a bit misleading.
|
|
// The array's [0] value contains the item name with a few fixes, see below
|
|
// The array's [1] value contains the color or wood name of the item, or
|
|
// "<none>"
|
|
String[] getTypeAndColor(String typeName) {
|
|
|
|
// [0] = Sortable Item name
|
|
// [1] = Color/Wood
|
|
|
|
String myColor = (plugin.debug) ? "~color~" : emptyPlaceholderString;
|
|
|
|
// Only work with lowercase
|
|
typeName = typeName.toLowerCase();
|
|
|
|
// When a color occurs at the beginning (e.g. "white_wool"), we omit the color
|
|
// so that the color will not
|
|
// determine the beginning letters of the sortable item name
|
|
for (String color : colors) {
|
|
if (typeName.startsWith(color)) {
|
|
typeName = typeName.replaceFirst(color + "_", "");
|
|
myColor = color;
|
|
}
|
|
}
|
|
// Same for wood, but the wood name can also be in the middle of the item name,
|
|
// e.g. "stripped_oak_log"
|
|
|
|
for (String woodName : woodNames) {
|
|
if (typeName.equals(woodName + "_wood")) {
|
|
typeName = "log_wood";
|
|
myColor = woodName;
|
|
} else if (typeName.startsWith(woodName)) {
|
|
typeName = typeName.replaceFirst(woodName + "_", "");
|
|
myColor = woodName;
|
|
} else if (typeName.equals("stripped_" + woodName + "_log")) {
|
|
// typeName = typeName.replaceFirst("stripped_"+woodName+"_", "stripped_");
|
|
typeName = "log_stripped";
|
|
myColor = woodName;
|
|
} else if (typeName.equals("stripped_" + woodName + "_wood")) {
|
|
typeName = "log_wood_stripped";
|
|
myColor = woodName;
|
|
}
|
|
}
|
|
// when typeName is exactly "log", change to "log_a" so it gets sorted before the stripped variants
|
|
if(typeName.equals("log")) {
|
|
typeName = "log_a";
|
|
}
|
|
|
|
// "egg" has to be put in front to group spawn eggs
|
|
// e.g. cow_spawn_egg -> egg_cow_spawn
|
|
if (typeName.endsWith("_egg")) {
|
|
typeName = typeName.replaceFirst("_egg", "");
|
|
typeName = "egg_" + typeName;
|
|
}
|
|
|
|
// polished_andesite -> andesite_polished
|
|
if (typeName.startsWith("polished_")) {
|
|
typeName = typeName.replaceFirst("polished_", "");
|
|
typeName = typeName + "_polished";
|
|
}
|
|
|
|
// Group wet and dry sponges
|
|
if (typeName.equalsIgnoreCase("wet_sponge")) {
|
|
typeName = "sponge_wet";
|
|
}
|
|
|
|
// Group pumpkins and jack-o-lanterns / carved pumpkins
|
|
if (typeName.equalsIgnoreCase("carved_pumpkin")) {
|
|
typeName = "pumpkin_carved";
|
|
}
|
|
|
|
// Sort armor: helmet, chestplate, leggings, boots
|
|
// We add a number to keep the armor in the "right" order
|
|
if (typeName.endsWith("helmet")) {
|
|
typeName = typeName.replaceFirst("helmet", "1_helmet");
|
|
} else if (typeName.endsWith("chestplate")) {
|
|
typeName = typeName.replaceFirst("chestplate", "2_chestplate");
|
|
} else if (typeName.endsWith("leggings")) {
|
|
typeName = typeName.replaceFirst("leggings", "3_leggings");
|
|
} else if (typeName.endsWith("boots")) {
|
|
typeName = typeName.replaceFirst("boots", "4_boots");
|
|
}
|
|
|
|
// Group horse armor
|
|
if (typeName.endsWith("horse_armor")) {
|
|
typeName = typeName.replaceFirst("_horse_armor", "");
|
|
typeName = "horse_armor_" + typeName;
|
|
}
|
|
|
|
String[] typeAndColor = new String[2];
|
|
typeAndColor[0] = typeName;
|
|
typeAndColor[1] = myColor;
|
|
|
|
return typeAndColor;
|
|
}
|
|
|
|
// This method takes a sortable item name and checks all categories for a match
|
|
// If none, matches, return "<none>" (it will be put behind all categorized
|
|
// items when sorting by category)
|
|
CategoryLinePair getCategoryLinePair(String typeName) {
|
|
typeName = typeName.toLowerCase();
|
|
for (ChestSortCategory cat : categories) {
|
|
short matchingLineNumber = cat.matches(typeName);
|
|
if (matchingLineNumber != 0) {
|
|
return new CategoryLinePair(cat.name, matchingLineNumber);
|
|
}
|
|
}
|
|
return new CategoryLinePair((plugin.debug) ? "~category~" : emptyPlaceholderString, (short) 0);
|
|
}
|
|
|
|
// This puts together the sortable item name, the category, the color, and
|
|
// whether the item is a block or a "regular item"
|
|
String getSortableString(ItemStack item) {
|
|
char blocksFirst;
|
|
char itemsFirst;
|
|
if (item.getType().isBlock()) {
|
|
blocksFirst = '!'; // ! is before # in ASCII
|
|
itemsFirst = '#';
|
|
} else {
|
|
blocksFirst = '#';
|
|
itemsFirst = '!';
|
|
}
|
|
|
|
String[] typeAndColor = getTypeAndColor(item.getType().name());
|
|
String typeName = typeAndColor[0];
|
|
String color = typeAndColor[1];
|
|
String potionEffect = ",";
|
|
|
|
// Potions
|
|
if (item.getItemMeta() != null) {
|
|
ItemMeta meta = item.getItemMeta();
|
|
if (meta instanceof PotionMeta) {
|
|
PotionMeta potionMeta = (PotionMeta) meta;
|
|
// Only continue if Method "getBasePotionData" exists
|
|
Class<? extends PotionMeta> potionMetaClass = potionMeta.getClass();
|
|
try {
|
|
if (potionMetaClass.getDeclaredMethod("getBasePotionData", null) != null) {
|
|
if (potionMeta.getBasePotionData() != null) {
|
|
PotionData pdata = potionMeta.getBasePotionData();
|
|
if (pdata != null && pdata.getType() != null && pdata.getType().getEffectType() != null) {
|
|
potionEffect = "|" + pdata.getType().getEffectType().getName();
|
|
}
|
|
}
|
|
}
|
|
} catch (NoSuchMethodException | SecurityException e) { }
|
|
|
|
// potionEffects = potionEffects.substring(0, potionEffects.length()-1);
|
|
}
|
|
}
|
|
|
|
String hookChangedName = item.getType().name();
|
|
|
|
// CrackShot Support Start
|
|
if(plugin.hookCrackShot) {
|
|
if(crackShotHook.getCrackShotWeaponName(item)!=null) {
|
|
typeName = plugin.getConfig().getString("hook-crackshot-prefix") + "_" + crackShotHook.getCrackShotWeaponName(item);
|
|
color="";
|
|
hookChangedName = typeName;
|
|
}
|
|
}
|
|
// CrackShot Support End
|
|
|
|
CategoryLinePair categoryLinePair = getCategoryLinePair(hookChangedName);
|
|
String categoryName = categoryLinePair.getCategoryName();
|
|
String categorySticky = categoryName;
|
|
String lineNumber = getCategoryLinePair(hookChangedName).getFormattedPosition();
|
|
if(stickyCategoryNames.contains(categoryName)) {
|
|
categorySticky = categoryName+"~"+lineNumber;
|
|
}
|
|
|
|
String customName = (plugin.debug) ? "~customName~" : emptyPlaceholderString;
|
|
if (item.getItemMeta().hasDisplayName() && item.getItemMeta().getDisplayName() != null) {
|
|
customName = item.getItemMeta().getDisplayName();
|
|
}
|
|
String lore = (plugin.debug) ? "~lore~" : emptyPlaceholderString;
|
|
if (item.getItemMeta().hasLore() && item.getItemMeta().getLore() != null
|
|
&& item.getItemMeta().getLore().size() != 0) {
|
|
String[] loreArray = item.getItemMeta().getLore().toArray(new String[0]);
|
|
lore = String.join(",", loreArray);
|
|
}
|
|
|
|
// Put enchanted items before unenchanted ones
|
|
typeName = typeName + String.format("%05d", 10000-getNumberOfEnchantments(item));
|
|
|
|
// Generate the strings that finally are used for sorting.
|
|
// They are generated according to the config.yml's sorting-method option
|
|
String sortableString = plugin.sortingMethod.replaceAll(",", "|");
|
|
sortableString = sortableString.replace("{itemsFirst}", String.valueOf(itemsFirst));
|
|
sortableString = sortableString.replace("{blocksFirst}", String.valueOf(blocksFirst));
|
|
sortableString = sortableString.replace("{name}", typeName+potionEffect);
|
|
sortableString = sortableString.replace("{color}", color);
|
|
sortableString = sortableString.replace("{category}", categorySticky);
|
|
sortableString = sortableString.replace("{keepCategoryOrder}", lineNumber);
|
|
sortableString = sortableString.replace("{customName}", customName);
|
|
sortableString = sortableString.replace("{lore}", lore);
|
|
|
|
return sortableString;
|
|
|
|
}
|
|
|
|
// Sort a complete inventory
|
|
void sortInventory(Inventory inv) {
|
|
sortInventory(inv, 0, inv.getSize() - 1);
|
|
}
|
|
|
|
// Sort an inventory only between startSlot and endSlot
|
|
void sortInventory(Inventory inv, int startSlot, int endSlot) {
|
|
Class<? extends Inventory> invClass = inv.getClass();
|
|
try {
|
|
if(invClass.getMethod("getLocation", null) != null) {
|
|
// This whole try/catch fixes MethodNotFoundException when using inv.getLocation in Spigot 1.8.
|
|
}
|
|
if(inv.getLocation() != null) {
|
|
ChestSortEvent chestSortEvent = new ChestSortEvent(inv);
|
|
chestSortEvent.loc = inv.getLocation();
|
|
Bukkit.getPluginManager().callEvent(chestSortEvent);
|
|
if (chestSortEvent.isCancelled()) {
|
|
return;
|
|
}
|
|
}
|
|
} catch (NoSuchMethodException | SecurityException | IllegalStateException e) {
|
|
// TODO Auto-generated catch block
|
|
|
|
}
|
|
|
|
|
|
if (plugin.debug) {
|
|
System.out.println(" ");
|
|
System.out.println(" ");
|
|
}
|
|
|
|
ArrayList<Integer> unsortableSlots = new ArrayList<Integer>();
|
|
|
|
// We copy the complete inventory into an array
|
|
ItemStack[] items = inv.getContents();
|
|
|
|
// Get rid of all stuff before startSlot...
|
|
for (int i = 0; i < startSlot; i++) {
|
|
items[i] = null;
|
|
}
|
|
// ... and after endSlot
|
|
for (int i = endSlot + 1; i < inv.getSize(); i++) {
|
|
items[i] = null;
|
|
}
|
|
// Get rid of all stuff that contains more than maxStackSize
|
|
// We do not need this as ChestSort will keep the "overstacked" stacks intact
|
|
/*for(int i = 0; i<endSlot; i++) {
|
|
if(inv.getItem(i) != null && inv.getItem(i).getAmount() > inv.getItem(i).getMaxStackSize()) {
|
|
//System.out.println("Debug: "+inv.getItem(i).getMaxStackSize());
|
|
//items[i] = null;
|
|
//unsortableSlots.add(i);
|
|
}
|
|
}*/
|
|
// Do not move Minepacks' backpacks
|
|
if(plugin.hookMinepacks) {
|
|
for(int i = startSlot; i<= endSlot; i++) {
|
|
if(plugin.listener.minepacksHook.isMinepacksBackpack(items[i])) {
|
|
items[i] = null;
|
|
unsortableSlots.add(i);
|
|
}
|
|
}
|
|
}
|
|
// If InventoryPages is installed: get rid of the buttons
|
|
if(plugin.hookInventoryPages) {
|
|
for(int i = startSlot; i<= endSlot; i++) {
|
|
if(inventoryPagesHook.isButton(items[i], i,inv)) {
|
|
//System.out.println("Inventory Pages Button found at slot " + i);
|
|
items[i] = null;
|
|
unsortableSlots.add(i);
|
|
}
|
|
}
|
|
}
|
|
// Do not move ItemStacks with more than 64 items
|
|
for(int i = startSlot; i<= endSlot; i++) {
|
|
if(isOversizedStack(items[i])) {
|
|
items[i] = null;
|
|
unsortableSlots.add(i);
|
|
}
|
|
}
|
|
|
|
// Remove the stuff from the original inventory
|
|
for (int i = startSlot; i <= endSlot; i++) {
|
|
if(!unsortableSlots.contains(i))
|
|
{
|
|
inv.clear(i);
|
|
}
|
|
}
|
|
|
|
// We don't want to have stacks of null, so we create a new ArrayList and put in
|
|
// everything != null
|
|
ArrayList<ItemStack> nonNullItemsList = new ArrayList<ItemStack>();
|
|
for (ItemStack item : items) {
|
|
if (item != null) {
|
|
nonNullItemsList.add(item);
|
|
}
|
|
}
|
|
|
|
// We no longer need the original array that includes all the null-stacks
|
|
items = null;
|
|
|
|
// We need the new list as array. So why did'nt we take an array from the
|
|
// beginning?
|
|
// Because I did not bother to count the number of non-null items beforehand.
|
|
// TODO: Feel free to make a Pull request if you want to save your server a few
|
|
// nanoseconds :)
|
|
ItemStack[] nonNullItems = nonNullItemsList.toArray(new ItemStack[nonNullItemsList.size()]);
|
|
|
|
// Sort the array with ItemStacks according to each ItemStacks' sortable String
|
|
Arrays.sort(nonNullItems, new Comparator<ItemStack>() {
|
|
@Override
|
|
public int compare(ItemStack s1, ItemStack s2) {
|
|
return (getSortableString(s1).compareTo(getSortableString(s2)));
|
|
}
|
|
});
|
|
|
|
// Now, we put everything back in a temporary inventory to combine ItemStacks
|
|
// even when using strict slot sorting
|
|
// Thanks to SnackMix for this idea!
|
|
// Without doing this, it would not be possible to sort an inventory with a
|
|
// startSlot other than 0,
|
|
// because Spigot's add(ItemStack...) method will always to store the ItemStack
|
|
// in the first possible slot
|
|
|
|
// Create the temporary inventory with a null holder. 54 slots is enough for
|
|
// every inventory
|
|
Inventory tempInventory = Bukkit.createInventory(null, maxInventorySize); // cannot be bigger than 54 as of 1.14
|
|
|
|
for (ItemStack item : nonNullItems) {
|
|
if (plugin.debug)
|
|
System.out.println(getSortableString(item));
|
|
// Add the item to the temporary inventory
|
|
tempInventory.addItem(item);
|
|
}
|
|
|
|
// Now, we iterate through all slots between startSlot and endSlot in the
|
|
// original inventory
|
|
// and set those to whatever the temporary inventory contains
|
|
// Since we already deleted all those slots, there is no chance for item
|
|
// duplication
|
|
int currentSlot = startSlot;
|
|
for (ItemStack item : tempInventory.getContents()) {
|
|
// Ignore null ItemStacks. TODO: Actually, we could skip the for-loop here
|
|
// because
|
|
// our temporary inventory was already sorted. Feel free to make a pull request
|
|
// to
|
|
// save your server half a nanosecond :)
|
|
if (item != null)
|
|
{
|
|
|
|
while(unsortableSlots.contains(currentSlot) && currentSlot < endSlot) {
|
|
currentSlot++;
|
|
}
|
|
inv.setItem(currentSlot, item);
|
|
}
|
|
currentSlot++;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
static int getNumberOfEnchantments(ItemStack is) {
|
|
|
|
int totalEnchants = 0;
|
|
|
|
//if(!is.getItemMeta().hasEnchants()) return 0;
|
|
if(is.getItemMeta() instanceof EnchantmentStorageMeta) {
|
|
EnchantmentStorageMeta storageMeta = (EnchantmentStorageMeta) is.getItemMeta();
|
|
Map<Enchantment,Integer> storedEnchants = storageMeta.getStoredEnchants();
|
|
for(int level : storedEnchants.values()) {
|
|
totalEnchants += level;
|
|
}
|
|
}
|
|
|
|
Map<Enchantment,Integer> enchants= is.getItemMeta().getEnchants();
|
|
|
|
for(int level : enchants.values()) {
|
|
totalEnchants += level;
|
|
}
|
|
return totalEnchants;
|
|
}
|
|
|
|
public void updateInventoryView(InventoryClickEvent event) {
|
|
for(HumanEntity viewer : event.getViewers()) {
|
|
if(viewer instanceof Player) {
|
|
Player playerViewer = (Player) viewer;
|
|
playerViewer.updateInventory();
|
|
}
|
|
}
|
|
}
|
|
|
|
public void updateInventoryView(Inventory inventory) {
|
|
for(HumanEntity viewer : inventory.getViewers()) {
|
|
if(viewer instanceof Player) {
|
|
Player playerViewer = (Player) viewer;
|
|
playerViewer.updateInventory();
|
|
}
|
|
}
|
|
}
|
|
|
|
public boolean isOversizedStack(ItemStack item) {
|
|
if(item!=null && item.getAmount()>64) return true;
|
|
return false;
|
|
}
|
|
|
|
public void stuffInventoryIntoAnother(Inventory source, Inventory destination,Inventory origSource, boolean onlyMatchingStuff) {
|
|
|
|
Material placeholderMaterial = Material.DIRT;
|
|
ItemStack[] hotbarStuff = new ItemStack[9];
|
|
boolean destinationIsPlayerInventory = true;
|
|
if(destination.getHolder()==null || !(destination.getHolder() instanceof Player) || destination.getType() != InventoryType.PLAYER) {
|
|
destinationIsPlayerInventory = false;
|
|
}
|
|
|
|
// Dont fill hotbar
|
|
if(destinationIsPlayerInventory) {
|
|
for(int i = 0; i<9;i++) {
|
|
hotbarStuff[i] = destination.getItem(i);
|
|
destination.setItem(i, new ItemStack(placeholderMaterial,64));
|
|
}
|
|
}
|
|
|
|
|
|
ArrayList<ItemStack> leftovers = new ArrayList<ItemStack>();
|
|
|
|
for(int i = 0;i<source.getSize();i++) {
|
|
|
|
|
|
|
|
ItemStack current = source.getItem(i);
|
|
if(current == null) continue;
|
|
if(onlyMatchingStuff && !doesInventoryContain(destination,current.getType())) continue;
|
|
if(isOversizedStack(current)) continue;
|
|
source.clear(i);
|
|
HashMap<Integer,ItemStack> currentLeftovers = destination.addItem(current);
|
|
|
|
|
|
for(ItemStack currentLeftover : currentLeftovers.values()) {
|
|
leftovers.add(currentLeftover);
|
|
}
|
|
}
|
|
|
|
origSource.addItem(leftovers.toArray(new ItemStack[leftovers.size()]));
|
|
|
|
// Restore hotbar
|
|
if(destinationIsPlayerInventory) {
|
|
for(int i=0;i<9;i++) {
|
|
destination.setItem(i, hotbarStuff[i]);
|
|
}
|
|
}
|
|
|
|
updateInventoryView(destination);
|
|
updateInventoryView(source);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*public void stuffPlayerInventoryIntoAnother(PlayerInventory source, Inventory destination) {
|
|
stuffPlayerInventoryIntoAnother(source,destination,false);
|
|
}*/
|
|
|
|
public void stuffPlayerInventoryIntoAnother(PlayerInventory source,
|
|
Inventory destination, boolean onlyMatchingStuff) {
|
|
boolean destinationIsShulkerBox = destination.getType().name().equalsIgnoreCase("SHULKER_BOX");
|
|
Inventory temp = Bukkit.createInventory(null, maxInventorySize);
|
|
for(int i = playerInvStartSlot;i<=playerInvEndSlot;i++) {
|
|
if(source.getItem(i)==null) continue;
|
|
|
|
// This prevents Minepacks from being put into Minepacks
|
|
/*if(plugin.hookMinepacks && plugin.listener.minepacksHook.isMinepacksBackpack(destination)
|
|
&& plugin.listener.minepacksHook.isMinepacksBackpack(source.getItem(i))) continue;*/
|
|
// This prevents Minepacks from being moved at all
|
|
if(plugin.hookMinepacks && plugin.listener.minepacksHook.isMinepacksBackpack(source.getItem(i))) continue;
|
|
|
|
if(plugin.hookInventoryPages
|
|
&& plugin.organizer.inventoryPagesHook.isButton(source.getItem(i), i, source)) continue;
|
|
|
|
if(destinationIsShulkerBox && source.getItem(i).getType().name().endsWith("SHULKER_BOX")) continue;
|
|
|
|
if(isOversizedStack(source.getItem(i))) continue;
|
|
|
|
if(onlyMatchingStuff && !doesInventoryContain(destination,source.getItem(i).getType())) continue;
|
|
|
|
temp.addItem(source.getItem(i));
|
|
source.clear(i);
|
|
}
|
|
stuffInventoryIntoAnother(temp,destination,source,false);
|
|
}
|
|
|
|
static boolean doesInventoryContain(Inventory inv, Material mat) {
|
|
for(ItemStack item : inv.getContents()) {
|
|
if(item==null) continue;
|
|
if(item.getType() == mat) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
}
|