package tc.oc.pgm.kits; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.PlayerInventory; import tc.oc.commons.bukkit.inventory.InventorySlot; import tc.oc.commons.bukkit.inventory.InventoryUtils; import tc.oc.commons.bukkit.inventory.Slot; import tc.oc.commons.bukkit.item.ItemUtils; import tc.oc.pgm.events.ItemTransferEvent; import tc.oc.pgm.events.PlayerItemTransferEvent; import tc.oc.pgm.match.MatchPlayer; public class ItemKitApplicator { private final Map<Slot, ItemStack> hardItems = new HashMap<>(); private final Map<Slot, ItemStack> softItems = new HashMap<>(); private final List<ItemStack> freeItems = new ArrayList<>(); public void add(ItemStack item) { freeItems.add(item); } public void put(Slot slot, ItemStack item, boolean force) { (force ? hardItems : softItems).put(slot, item); } public boolean isEmpty(Slot slot) { return ItemUtils.isNothing(softItems.get(slot)); } public void apply(MatchPlayer player) { final PlayerInventory inv = player.getInventory(); // Place forced items first hardItems.forEach((slot, stack) -> fireEventAndTransfer(player, slot, stack, false)); final Map<Slot, ItemStack> softItems = new HashMap<>(Maps.transformValues(this.softItems, ItemStack::clone)); final List<ItemStack> freeItems = new ArrayList<>(Lists.transform(this.freeItems, ItemStack::clone)); final Iterable<ItemStack> kitItems = Iterables.concat(softItems.values(), freeItems); // Tools in the player's inv are repaired using matching tools in the kit with less damage for(ItemStack kitStack : kitItems) { for(ItemStack invStack : inv.contents()) { if(invStack != null) { if(kitStack.getAmount() > 0 && kitStack.getType().getMaxDurability() > 0 && kitStack.getType().equals(invStack.getType()) && kitStack.getEnchantments().equals(invStack.getEnchantments()) && kitStack.getDurability() < invStack.getDurability()) { invStack.setDurability(kitStack.getDurability()); kitStack.setAmount(0); break; } } } } // Items in the player's inv that stack with kit items are deducted from the kit for(ItemStack invStack : inv.contents()) { if(invStack != null) { int amount = invStack.getAmount(); for(ItemStack kitStack : kitItems) { if(amount <= 0) break; if(kitStack.isSimilar(invStack)) { int reduce = Math.min(amount, kitStack.getAmount()); if(reduce > 0) { amount -= reduce; ItemUtils.addAmount(kitStack, -reduce); } } } } } // Fill partial stacks of kit items that are already in the player's inv. // We must do this in a seperate pass so that kit stacks don't combine with // other kit stacks. for(ItemStack kitStack : kitItems) { InventoryUtils.similar(inv, kitStack).forEach(slot -> { final int quantity = slot.maxTransferrableIn(kitStack, inv); if(quantity > 0) { fireEventAndTransfer(player, slot, kitStack, true); ItemUtils.addAmount(kitStack, -quantity); } }); } // Put the remaining kit slotted items into their designated inv slots. // If a slot is occupied, add the stack to freeItems. softItems.forEach((kitSlot, kitStack) -> { if(kitStack.getAmount() > 0) { if(kitSlot.isEmpty(inv)) { fireEventAndTransfer(player, kitSlot, kitStack, false); } else { freeItems.add(kitStack); } } }); // Add free items to the inventory one at a time, firing an event // for each partial stack transferred. freeItems.forEach(stack -> fireEventAndTransfer(player, stack)); } public static void fireEventAndTransfer(MatchPlayer player, ItemStack stack) { InventoryUtils.chooseStorageSlots(player.getInventory(), stack) .forEach((slot, partial) -> fireEventAndTransfer(player, slot, partial, true)); } public static boolean fireEventAndTransfer(MatchPlayer player, Slot slot, ItemStack stack, boolean combine) { final PlayerItemTransferEvent event = new PlayerItemTransferEvent(null, ItemTransferEvent.Type.PLUGIN, player.getBukkit(), Optional.empty(), Optional.of(new InventorySlot<>(player.getInventory(), slot)), stack, null, stack.getAmount(), null); player.getMatch().callEvent(event); if(event.isCancelled()) return false; stack = event.getItemStack().clone(); stack.setAmount(event.getQuantity() + (combine ? ItemUtils.amount(slot.getItem(player)) : 0)); slot.putItem(player, stack); return true; } }