Make spell books return to the slots they came from (provided the container isn't closed)
This commit is contained in:
@@ -11,6 +11,7 @@ import electroblob.wizardry.spell.Spell;
|
||||
import electroblob.wizardry.tileentity.TileEntityArcaneWorkbench;
|
||||
import electroblob.wizardry.util.ISpellSortable;
|
||||
import electroblob.wizardry.util.WandHelper;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.inventory.ClickType;
|
||||
@@ -230,8 +231,7 @@ public class ContainerArcaneWorkbench extends Container implements ISpellSortabl
|
||||
// Workbench -> inventory/bookshelves
|
||||
if(clickedSlotId <= UPGRADE_SLOT){
|
||||
// Try to move the stack into the bookshelves. If this fails...
|
||||
if(getBookshelfSlots().isEmpty() || !this.mergeItemStack(stack, getBookshelfSlots().get(0).slotNumber,
|
||||
getBookshelfSlots().get(getBookshelfSlots().size()-1).slotNumber + 1, false)){
|
||||
if(!mergeStackIntoBookshelves(stack)){
|
||||
// ...try to move the stack into the player's inventory. If this fails...
|
||||
if(!this.mergeItemStack(stack, UPGRADE_SLOT + 1, UPGRADE_SLOT + 1 + PLAYER_INVENTORY_SIZE, true)){
|
||||
return ItemStack.EMPTY; // ...nothing else happens.
|
||||
@@ -259,8 +259,7 @@ public class ContainerArcaneWorkbench extends Container implements ISpellSortabl
|
||||
// Try to move the stack into the workbench. If this fails...
|
||||
if(slotRange == null || !this.mergeItemStack(stack, slotRange[0], slotRange[1] + 1, false)){
|
||||
// ...try to move the stack into the bookshelves. If this fails...
|
||||
if(getBookshelfSlots().isEmpty() || !this.mergeItemStack(stack, getBookshelfSlots().get(0).slotNumber,
|
||||
getBookshelfSlots().get(getBookshelfSlots().size()-1).slotNumber + 1, false)){
|
||||
if(!mergeStackIntoBookshelves(stack)){
|
||||
return ItemStack.EMPTY; // ...nothing else happens.
|
||||
}
|
||||
}
|
||||
@@ -328,7 +327,7 @@ public class ContainerArcaneWorkbench extends Container implements ISpellSortabl
|
||||
ItemStack stack = player.inventory.getItemStack();
|
||||
|
||||
if(!stack.isEmpty() && !getBookshelfSlots().isEmpty()){
|
||||
mergeItemStack(stack, getBookshelfSlots().get(0).slotNumber, getBookshelfSlots().get(getBookshelfSlots().size() - 1).slotNumber + 1, false);
|
||||
mergeStackIntoBookshelves(stack);
|
||||
return stack;
|
||||
}
|
||||
}
|
||||
@@ -336,6 +335,78 @@ public class ContainerArcaneWorkbench extends Container implements ISpellSortabl
|
||||
return super.slotClick(slotId, dragType, clickTypeIn, player);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to merge the given stack into the bookshelf slots, accounting for their previous contents. The stack will
|
||||
* be merged into slots in the following order of priority:
|
||||
* <p></p>
|
||||
* 1. Slots that currently contain a matching item<br>
|
||||
* 2. Empty slots that previously contained a matching item<br>
|
||||
* 3. Empty slots that did not previously contain any items<br>
|
||||
* 4. Empty slots that previously contained a non-matching item
|
||||
* @param stack The {@link ItemStack} to be merged; will be reduced by the number of items that fitted into the
|
||||
* bookshelf slots
|
||||
* @return True if the entire stack was merged into the bookshelf slots, false if not (equivalent to calling
|
||||
* {@link ItemStack#isEmpty()} on the input stack after this method returns)
|
||||
*/
|
||||
private boolean mergeStackIntoBookshelves(ItemStack stack){
|
||||
|
||||
// LinkedHashSet preserves iteration order whilst ignoring duplicates - neat!
|
||||
Set<VirtualSlot> slots = new LinkedHashSet<>(bookshelfSlots.size());
|
||||
|
||||
// Add all slots that currently contain a matching stack
|
||||
slots.addAll(bookshelfSlots.stream().filter(s -> WizardryUtilities.canMerge(stack, s.getStack())).collect(Collectors.toSet()));
|
||||
// Then add all empty slots that previously contained a matching stack, if they weren't already added
|
||||
// No need to actually check if they're empty since inserting a new stack overwrites prevStack anyway
|
||||
slots.addAll(bookshelfSlots.stream().filter(s -> WizardryUtilities.canMerge(stack, s.getPrevStack())).collect(Collectors.toSet()));
|
||||
// Then add all slots that did not previously contain anything
|
||||
slots.addAll(bookshelfSlots.stream().filter(s -> s.getPrevStack().isEmpty()).collect(Collectors.toSet()));
|
||||
// Finally add all other empty slots (these will be the ones that used to contain something else)
|
||||
slots.addAll(bookshelfSlots.stream().filter(s -> !s.getHasStack()).collect(Collectors.toSet()));
|
||||
|
||||
slots.removeIf(s -> !s.isItemValid(stack)); // Should never be true, but just in case...
|
||||
|
||||
// Now we have a set of slots in order of priority to try merging into
|
||||
// We already know the stack will fit, so it's simply a question of distributing it
|
||||
// This is waaay neater and more flexible than vanilla's (disgusting) mergeItemStack implementation - and that
|
||||
// only has two priorities of slot to deal with!
|
||||
|
||||
for(Slot slot : slots){
|
||||
|
||||
ItemStack contents = slot.getStack();
|
||||
|
||||
if(contents.isEmpty()){
|
||||
|
||||
// Not sure why mergeItemStack differentiates between full/partial merging, as far as I can tell the
|
||||
// following line will work for both cases
|
||||
slot.putStack(stack.splitStack(contents.getMaxStackSize()));
|
||||
slot.onSlotChanged();
|
||||
|
||||
if(stack.isEmpty()) return true; // The whole stack has been merged, so we're done!
|
||||
|
||||
}else{
|
||||
|
||||
int totalItemCount = contents.getCount() + stack.getCount();
|
||||
int maxSize = Math.min(slot.getSlotStackLimit(), stack.getMaxStackSize());
|
||||
|
||||
if(totalItemCount <= maxSize){
|
||||
|
||||
stack.setCount(0);
|
||||
contents.setCount(totalItemCount);
|
||||
slot.onSlotChanged();
|
||||
return true; // The whole stack has been merged, so we're done!
|
||||
|
||||
}else if(contents.getCount() < maxSize){
|
||||
|
||||
stack.shrink(maxSize - contents.getCount());
|
||||
contents.setCount(maxSize);
|
||||
slot.onSlotChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false; // If we get this far, it didn't all fit so return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Called (via {@link electroblob.wizardry.packet.PacketControlInput PacketControlInput}) when the apply button in
|
||||
* the arcane workbench GUI is pressed.
|
||||
|
||||
@@ -20,11 +20,14 @@ import net.minecraft.tileentity.TileEntity;
|
||||
public class VirtualSlot extends Slot {
|
||||
|
||||
private final TileEntity tileEntity;
|
||||
/** Allows the virtual slot to remember what was last stored in it, so items can be put back in the same place. */
|
||||
private ItemStack prevStack; // For now this doesn't persist over GUI close
|
||||
|
||||
public VirtualSlot(IInventory inventory, int index){
|
||||
super(inventory, index, -999, -999);
|
||||
if(!(inventory instanceof TileEntity)) throw new IllegalArgumentException("Inventory must be a tile entity!");
|
||||
this.tileEntity = (TileEntity)inventory;
|
||||
this.prevStack = getStack().copy(); // We MUST copy the stack or it will get changed from elsewhere later!
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -59,15 +62,28 @@ public class VirtualSlot extends Slot {
|
||||
return isValid() ? super.onTake(player, stack) : ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSlotChanged(){
|
||||
super.onSlotChanged();
|
||||
if(this.getHasStack()) this.prevStack = this.getStack().copy(); // Ignore stack removal (insertion of empty stacks)
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getStack(){
|
||||
return isValid() ? super.getStack() : ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
/** Returns the stack that was last in this slot. */
|
||||
public ItemStack getPrevStack(){
|
||||
return prevStack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putStack(ItemStack stack){
|
||||
if(isValid() && inventory instanceof TileEntityBookshelf) ((TileEntityBookshelf)inventory).sync();
|
||||
if(isValid()) super.putStack(stack);
|
||||
if(isValid()){
|
||||
if(inventory instanceof TileEntityBookshelf) ((TileEntityBookshelf)inventory).sync();
|
||||
super.putStack(stack);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -39,6 +39,7 @@ import javax.annotation.Nullable;
|
||||
import java.util.*;
|
||||
import java.util.function.BiPredicate;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* <i>"Where do you put random but useful bits and pieces? {@code WizardryUtilities} of course - the 'stuff that doesn't
|
||||
@@ -810,6 +811,22 @@ public final class WizardryUtilities {
|
||||
return copy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the two given item stacks can be merged, i.e. if they both contain the same (stackable) item,
|
||||
* metadata and NBT. Importantly, the number of items in each stack need not be the same. No actual merging is
|
||||
* performed by this method; the input stacks will not be modified.
|
||||
* @param stack1 The first stack to be tested for mergeability
|
||||
* @param stack2 The second stack to be tested for mergeability (order does not matter)
|
||||
* @return True if the two stacks can be merged, false if not
|
||||
*/
|
||||
public static boolean canMerge(ItemStack stack1, ItemStack stack2){
|
||||
return !stack1.isEmpty() && !stack2.isEmpty()
|
||||
&& stack1.isStackable() && stack2.isStackable()
|
||||
&& stack1.getItem() == stack2.getItem()
|
||||
&& (!stack1.getHasSubtypes() || stack1.getMetadata() == stack2.getMetadata())
|
||||
&& ItemStack.areItemStackTagsEqual(stack1, stack2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given player is opped on the given server. If the server is a singleplayer or LAN server, this
|
||||
* means they have cheats enabled.
|
||||
|
||||
Reference in New Issue
Block a user