package fi.dy.masa.enderutilities.item; import java.util.Iterator; import java.util.List; import net.minecraft.client.renderer.block.model.ModelResourceLocation; import net.minecraft.client.resources.I18n; import net.minecraft.creativetab.CreativeTabs; import net.minecraft.entity.Entity; import net.minecraft.entity.item.EntityItem; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.init.SoundEvents; import net.minecraft.item.ItemStack; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.ActionResult; import net.minecraft.util.EnumActionResult; import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumHand; import net.minecraft.util.NonNullList; import net.minecraft.util.ResourceLocation; import net.minecraft.util.SoundCategory; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.MathHelper; import net.minecraft.util.text.TextFormatting; import net.minecraft.world.World; import net.minecraftforge.event.entity.player.EntityItemPickupEvent; import net.minecraftforge.fml.common.FMLCommonHandler; import net.minecraftforge.fml.common.Loader; import net.minecraftforge.fml.common.ModContainer; import net.minecraftforge.items.CapabilityItemHandler; import net.minecraftforge.items.IItemHandler; import net.minecraftforge.items.IItemHandlerModifiable; import net.minecraftforge.items.wrapper.CombinedInvWrapper; import net.minecraftforge.items.wrapper.PlayerOffhandInvWrapper; import net.minecraftforge.items.wrapper.RangedWrapper; import fi.dy.masa.enderutilities.EnderUtilities; import fi.dy.masa.enderutilities.config.Configs; import fi.dy.masa.enderutilities.event.PlayerItemPickupEvent; import fi.dy.masa.enderutilities.inventory.container.ContainerHandyBag; import fi.dy.masa.enderutilities.inventory.container.base.SlotRange; import fi.dy.masa.enderutilities.inventory.item.InventoryItemModular; import fi.dy.masa.enderutilities.inventory.wrapper.PlayerMainInvWrapperNoSync; import fi.dy.masa.enderutilities.item.base.IModule; import fi.dy.masa.enderutilities.item.base.ItemInventoryModular; import fi.dy.masa.enderutilities.item.base.ItemModule.ModuleType; import fi.dy.masa.enderutilities.item.part.ItemEnderPart; import fi.dy.masa.enderutilities.reference.HotKeys; import fi.dy.masa.enderutilities.reference.HotKeys.EnumKey; import fi.dy.masa.enderutilities.reference.Reference; import fi.dy.masa.enderutilities.reference.ReferenceGuiIds; import fi.dy.masa.enderutilities.registry.EnderUtilitiesItems; import fi.dy.masa.enderutilities.registry.ModRegistry; import fi.dy.masa.enderutilities.util.EUStringUtils; import fi.dy.masa.enderutilities.util.InventoryUtils; import fi.dy.masa.enderutilities.util.nbt.NBTUtils; import fi.dy.masa.enderutilities.util.nbt.UtilItemModular; public class ItemHandyBag extends ItemInventoryModular { public static final int META_TIER_1 = 0; public static final int META_TIER_2 = 1; public static final int INV_SIZE_TIER_1 = 27; public static final int INV_SIZE_TIER_2 = 55; public static final int GUI_ACTION_SELECT_MODULE = 0; public static final int GUI_ACTION_MOVE_ITEMS = 1; public static final int GUI_ACTION_SORT_ITEMS = 2; public static final int GUI_ACTION_TOGGLE_BLOCK = 3; public static final int GUI_ACTION_TOGGLE_UPDATE = 4; public static final int GUI_ACTION_TOGGLE_MODES = 5; public static final int GUI_ACTION_TOGGLE_SHIFTCLICK = 6; public static final int GUI_ACTION_TOGGLE_SHIFTCLICK_DOUBLETAP = 7; public static final int GUI_ACTION_OPEN_BAUBLES = 100; public ItemHandyBag(String name) { super(name); this.setMaxStackSize(1); this.setMaxDamage(0); this.setHasSubtypes(true); this.commonTooltip = "item.enderutilities.handybag.tooltips"; } @Override public void onUpdate(ItemStack stack, World world, Entity entity, int slot, boolean isCurrent) { super.onUpdate(stack, world, entity, slot, isCurrent); if (world.isRemote == false) { if (entity instanceof EntityPlayer) { this.restockPlayerInventory(stack, world, (EntityPlayer) entity); } if (Configs.handyBagEnableItemUpdate) { this.updateItems(stack, world, entity, slot); } } } @Override public EnumActionResult onItemUse(EntityPlayer player, World world, BlockPos pos, EnumHand hand, EnumFacing side, float hitX, float hitY, float hitZ) { // If the bag is sneak + right clicked on an inventory, then we try to dump all the contents to that inventory if (player.isSneaking()) { this.tryMoveItems(world, pos, side, player.getHeldItem(hand), player); return EnumActionResult.SUCCESS; } return super.onItemUse(player,world, pos, hand, side, hitX, hitY, hitZ); } @Override public ActionResult<ItemStack> onItemRightClick(World world, EntityPlayer player, EnumHand hand) { ItemStack stack = player.getHeldItem(hand); if (world.isRemote == false) { // These two lines are to fix the UUID being missing the first time the GUI opens, // if the item is grabbed from the creative inventory or from JEI or from /give NBTUtils.getUUIDFromItemStack(stack, "UUID", true); player.openContainer.detectAndSendChanges(); player.openGui(EnderUtilities.instance, ReferenceGuiIds.GUI_ID_HANDY_BAG_RIGHT_CLICK, world, (int)player.posX, (int)player.posY, (int)player.posZ); } return new ActionResult<ItemStack>(EnumActionResult.SUCCESS, stack); } @Override public void onCreated(ItemStack stack, World world, EntityPlayer player) { super.onCreated(stack, world, player); // Create the UUID when the item is crafted NBTUtils.getUUIDFromItemStack(stack, "UUID", true); } @Override public String getTranslationKey(ItemStack stack) { return super.getTranslationKey() + "_" + stack.getMetadata(); } @Override public String getItemStackDisplayName(ItemStack stack) { ItemStack moduleStack = this.getSelectedModuleStack(stack, ModuleType.TYPE_MEMORY_CARD_ITEMS); if (moduleStack.isEmpty() == false && moduleStack.getTagCompound() != null) { String itemName = super.getItemStackDisplayName(stack); //I18n.format(this.getUnlocalizedName(stack) + ".name").trim(); String rst = TextFormatting.RESET.toString() + TextFormatting.WHITE.toString(); // If the currently selected module has been renamed, show that name if (moduleStack.hasDisplayName()) { String pre = TextFormatting.GREEN.toString() + TextFormatting.ITALIC.toString(); if (itemName.length() >= 14) { return EUStringUtils.getInitialsWithDots(itemName) + " " + pre + moduleStack.getDisplayName() + rst; } return itemName + " " + pre + moduleStack.getDisplayName() + rst; } return itemName; } return super.getItemStackDisplayName(stack); } @Override public void addTooltipLines(ItemStack containerStack, EntityPlayer player, List<String> list, boolean verbose) { if (containerStack.getTagCompound() == null) { return; } String preGreen = TextFormatting.GREEN.toString(); String preRed = TextFormatting.RED.toString(); String preWhite = TextFormatting.WHITE.toString(); String rst = TextFormatting.RESET.toString() + TextFormatting.GRAY.toString(); String strPickupMode = I18n.format("enderutilities.tooltip.item.pickupmode" + (verbose ? "" : ".short")) + ": "; String strRestockMode = I18n.format("enderutilities.tooltip.item.restockmode" + (verbose ? "" : ".short")) + ": "; PickupMode pickupMode = PickupMode.fromStack(containerStack); if (pickupMode == PickupMode.NONE) strPickupMode += preRed; else if (pickupMode == PickupMode.MATCHING) strPickupMode += TextFormatting.YELLOW.toString(); else if (pickupMode == PickupMode.ALL) strPickupMode += preGreen; strPickupMode += pickupMode.getDisplayName() + rst; RestockMode restockMode = RestockMode.fromStack(containerStack); if (restockMode == RestockMode.DISABLED) strRestockMode += preRed; else if (restockMode == RestockMode.ALL) strRestockMode += preGreen; else strRestockMode += TextFormatting.YELLOW.toString(); strRestockMode += restockMode.getDisplayName() + rst; if (verbose) { list.add(strPickupMode); list.add(strRestockMode); } else { list.add(strPickupMode + " / " + strRestockMode); } String str; if (bagIsOpenable(containerStack)) { str = I18n.format("enderutilities.tooltip.item.enabled") + ": " + preGreen + I18n.format("enderutilities.tooltip.item.yes"); } else { str = I18n.format("enderutilities.tooltip.item.enabled") + ": " + preRed + I18n.format("enderutilities.tooltip.item.no"); } list.add(str); int installed = this.getInstalledModuleCount(containerStack, ModuleType.TYPE_MEMORY_CARD_ITEMS); if (installed > 0) { int slotNum = UtilItemModular.getStoredModuleSelection(containerStack, ModuleType.TYPE_MEMORY_CARD_ITEMS); String preBlue = TextFormatting.BLUE.toString(); String preWhiteIta = preWhite + TextFormatting.ITALIC.toString(); String strShort = I18n.format("enderutilities.tooltip.item.selectedmemorycard.short"); ItemStack moduleStack = this.getSelectedModuleStack(containerStack, ModuleType.TYPE_MEMORY_CARD_ITEMS); int max = this.getMaxModules(containerStack, ModuleType.TYPE_MEMORY_CARD_ITEMS); if (moduleStack.isEmpty() == false && moduleStack.getItem() == EnderUtilitiesItems.ENDER_PART) { String dName = (moduleStack.hasDisplayName() ? preWhiteIta + moduleStack.getDisplayName() + rst + " " : ""); list.add(String.format("%s %s(%s%d%s / %s%d%s)", strShort, dName, preBlue, slotNum + 1, rst, preBlue, max, rst)); ((ItemEnderPart) moduleStack.getItem()).addTooltipLines(moduleStack, player, list, false); return; } else { String strNo = I18n.format("enderutilities.tooltip.item.selectedmemorycard.notinstalled"); list.add(String.format("%s %s (%s%d%s / %s%d%s)", strShort, strNo, preBlue, slotNum + 1, rst, preBlue, max, rst)); } } else { list.add(I18n.format("enderutilities.tooltip.item.nomemorycards")); } } private static InventoryItemModular getInventoryForBag(ItemStack bagStack, EntityPlayer player) { InventoryItemModular bagInv = null; // If this bag is currently open, then use that inventory instead of creating a new one, // otherwise the open GUI/inventory will overwrite the changes from the picked up items. if (player.openContainer instanceof ContainerHandyBag && ((ContainerHandyBag) player.openContainer).inventoryItemModular.getModularItemStack() == bagStack) { bagInv = ((ContainerHandyBag) player.openContainer).inventoryItemModular; } else { bagInv = new InventoryItemModular(bagStack, player, true, ModuleType.TYPE_MEMORY_CARD_ITEMS); } if (bagInv.isAccessibleBy(player) == false) { return null; } return bagInv; } private void updateItems(ItemStack bagStack, World world, Entity entity, int bagSlot) { if (entity instanceof EntityPlayer) { EntityPlayer player = (EntityPlayer) entity; InventoryItemModular bagInv = getInventoryForBag(bagStack, player); if (bagInv != null && player.inventory.getStackInSlot(bagSlot) == bagStack) { int moduleSlot = bagInv.getSelectedModuleIndex(); if (moduleSlot >= 0) { ItemStack cardStack = bagInv.getModuleInventory().getStackInSlot(moduleSlot); if (cardStack.isEmpty() == false) { // Try to find an empty slot in the player's inventory, for temporarily moving the updated item to int tmpSlot = InventoryUtils.getFirstEmptySlot(new PlayerMainInvWrapperNoSync(player.inventory)); long[] masks = new long[] { 0x1FFFFFFL, 0x1FFF8000000L, 0x7FFE0000000000L }; long mask = NBTUtils.getLong(cardStack, "HandyBag", "UpdateMask"); int numSections = bagStack.getMetadata() == 1 ? 3 : 1; int invSize = bagInv.getSlots(); // If there were no empty slots, then we use the bag's slot instead... risky! if (tmpSlot == -1) { tmpSlot = bagSlot; } ItemStack stackInTmpSlot = player.inventory.getStackInSlot(tmpSlot); boolean isCurrentItem = tmpSlot == player.inventory.currentItem; for (int section = 0; section < numSections; section++) { if ((mask & masks[section]) != 0) { SlotRange range = getSlotRangeForSection(section); for (int slot = range.first; slot < range.lastExc && slot < invSize; slot++) { if ((mask & (1L << slot)) != 0) { ItemStack stackTmp = bagInv.getStackInSlot(slot); if (stackTmp.isEmpty() == false) { ItemStack stackOrig = stackTmp.copy(); // Temporarily move the item-being-updated into the temporary slot in the player's inventory player.inventory.setInventorySlotContents(tmpSlot, stackTmp); try { stackTmp.updateAnimation(world, entity, tmpSlot, isCurrentItem); } catch (Throwable t) { EnderUtilities.logger.warn("Exception while updating items inside a Handy Bag!", t); } // The stack changed while being updated, write it back to the bag's inventory if (ItemStack.areItemStacksEqual(stackTmp, stackOrig) == false) { bagInv.setStackInSlot(slot, stackTmp.isEmpty() ? ItemStack.EMPTY : stackTmp); } } } } } } // Restore the Handy Bag into the original slot it was in player.inventory.setInventorySlotContents(tmpSlot, stackInTmpSlot); } } } } } private void restockPlayerInventory(ItemStack stack, World world, EntityPlayer player) { RestockMode mode = RestockMode.fromStack(stack); // If Restock mode is enabled, then we will fill the stacks in the player's inventory from the bag if (world.isRemote == false && mode != RestockMode.DISABLED) { InventoryItemModular bagInv = getInventoryForBag(stack, player); if (bagInv != null) { IItemHandler wrappedBagInv = getWrappedEnabledInv(stack, bagInv); IItemHandlerModifiable playerInv = (IItemHandlerModifiable) player.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, null); // Only re-stock the hotbar and the offhand slot if (mode == RestockMode.HOTBAR) { playerInv = new CombinedInvWrapper(new RangedWrapper(playerInv, 0, 9), new PlayerOffhandInvWrapper(player.inventory)); } InventoryUtils.fillStacksOfMatchingItems(wrappedBagInv, playerInv); player.openContainer.detectAndSendChanges(); } } } private EnumActionResult tryMoveItems(World world, BlockPos pos, EnumFacing side, ItemStack stack, EntityPlayer player) { TileEntity te = world.getTileEntity(pos); if (te == null || te.hasCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, side) == false) { return EnumActionResult.PASS; } IItemHandler inv = te.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, side); InventoryItemModular bagInv = getInventoryForBag(stack, player); if (inv == null || bagInv == null) { return EnumActionResult.PASS; } IItemHandler wrappedBagInv = getWrappedEnabledInv(stack, bagInv); RestockMode restockMode = RestockMode.fromStack(stack); if (restockMode == RestockMode.HOTBAR || restockMode == RestockMode.ALL) { if (world.isRemote == false) { if (restockMode == RestockMode.HOTBAR) { InventoryUtils.tryMoveMatchingItems(wrappedBagInv, inv); } else { InventoryUtils.tryMoveAllItems(wrappedBagInv, inv); } player.getEntityWorld().playSound(null, player.getPosition(), SoundEvents.ENTITY_ENDERMEN_TELEPORT, SoundCategory.MASTER, 0.2f, 1.8f); } return EnumActionResult.SUCCESS; } PickupMode pickupMode = PickupMode.fromStack(stack); if (pickupMode == PickupMode.MATCHING || pickupMode == PickupMode.ALL) { if (world.isRemote == false) { if (pickupMode == PickupMode.MATCHING) { InventoryUtils.tryMoveMatchingItems(inv, wrappedBagInv); } else { InventoryUtils.tryMoveAllItems(inv, wrappedBagInv); } player.getEntityWorld().playSound(null, player.getPosition(), SoundEvents.ENTITY_ENDERMEN_TELEPORT, SoundCategory.MASTER, 0.2f, 1.8f); } return EnumActionResult.SUCCESS; } return EnumActionResult.PASS; } private static ItemStack handleItems(ItemStack itemsIn, ItemStack bagStack, EntityPlayer player) { PickupMode pickupMode = PickupMode.fromStack(bagStack); IItemHandler playerInv = player.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, null); // First try to fill all existing stacks in the player's inventory if (pickupMode != PickupMode.NONE) { itemsIn = InventoryUtils.tryInsertItemStackToExistingStacksInInventory(playerInv, itemsIn); } if (itemsIn.isEmpty()) { return ItemStack.EMPTY; } InventoryItemModular bagInv = getInventoryForBag(bagStack, player); if (bagInv != null) { IItemHandler wrappedBagInv = getWrappedEnabledInv(bagStack, bagInv); // If there is no space left in existing stacks in the player's inventory // then add the items to the bag, if one of the pickup modes is enabled. if (pickupMode == PickupMode.ALL || (pickupMode == PickupMode.MATCHING && InventoryUtils.getSlotOfFirstMatchingItemStack(wrappedBagInv, itemsIn) != -1)) { itemsIn = InventoryUtils.tryInsertItemStackToInventory(wrappedBagInv, itemsIn); } } return itemsIn; } /** * Tries to first fill the matching stacks in the player's inventory, * and then depending on the bag's mode, tries to add the remaining items * to the bag's inventory. * @param event * @return true if all items were handled and further processing of the event should not occur */ public static boolean onItemPickupEvent(PlayerItemPickupEvent event) { if (event.getEntityPlayer().getEntityWorld().isRemote) { return false; } boolean pickedUp = false; EntityPlayer player = event.getEntityPlayer(); IItemHandler playerInv = player.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, null); List<Integer> bagSlots = InventoryUtils.getSlotNumbersOfMatchingItems(playerInv, EnderUtilitiesItems.HANDY_BAG); Iterator<ItemStack> iter = event.drops.iterator(); while (iter.hasNext()) { ItemStack stack = iter.next(); if (stack.isEmpty()) { iter.remove(); continue; } // Not all the items could fit into existing stacks in the player's inventory, move them directly to the bag for (int slot : bagSlots) { ItemStack bagStack = playerInv.getStackInSlot(slot); // Bag is not locked if (bagStack.isEmpty() == false && bagStack.getItem() == EnderUtilitiesItems.HANDY_BAG && ItemHandyBag.bagIsOpenable(bagStack)) { ItemStack stackOrig = stack; stack = handleItems(stack, bagStack, player); if (stack.isEmpty()) { iter.remove(); pickedUp = true; } else if (stackOrig.getCount() != stack.getCount()) { stackOrig.setCount(stack.getCount()); pickedUp = true; } } } } // At least some items were picked up if (pickedUp) { player.getEntityWorld().playSound(null, player.getPosition(), SoundEvents.ENTITY_ITEM_PICKUP, SoundCategory.MASTER, 0.2F, ((itemRand.nextFloat() - itemRand.nextFloat()) * 0.7F + 1.0F) * 2.0F); } if (event.drops.isEmpty()) { event.setCanceled(true); return true; } return false; } /** * Tries to first fill the matching stacks in the player's inventory, * and then depending on the bag's mode, tries to add the remaining items * to the bag's inventory. * @param event * @return true if all items were handled and further processing of the event should not occur */ public static boolean onEntityItemPickupEvent(EntityItemPickupEvent event) { EntityItem entityItem = event.getItem(); ItemStack stack = entityItem.getItem(); EntityPlayer player = event.getEntityPlayer(); if (player.getEntityWorld().isRemote || entityItem.isDead || stack.isEmpty()) { return true; } ItemStack origStack = ItemStack.EMPTY; final int origStackSize = stack.getCount(); int stackSizeLast = origStackSize; boolean ret = false; IItemHandler playerInv = player.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, null); // Not all the items could fit into existing stacks in the player's inventory, move them directly to the bag List<Integer> slots = InventoryUtils.getSlotNumbersOfMatchingItems(playerInv, EnderUtilitiesItems.HANDY_BAG); for (int slot : slots) { ItemStack bagStack = playerInv.getStackInSlot(slot); // Bag is not locked if (bagStack.isEmpty() == false && bagStack.getItem() == EnderUtilitiesItems.HANDY_BAG && ItemHandyBag.bagIsOpenable(bagStack)) { // Delayed the stack copying until we know if there is a valid bag, // so check if the stack was copied already or not. if (origStack == ItemStack.EMPTY) { origStack = stack.copy(); } stack = handleItems(stack, bagStack, player); if (stack.isEmpty() || stack.getCount() != stackSizeLast) { if (stack.isEmpty()) { entityItem.setDead(); event.setCanceled(true); ret = true; break; } ItemStack pickedUpStack = origStack.copy(); pickedUpStack.setCount(stackSizeLast - stack.getCount()); FMLCommonHandler.instance().firePlayerItemPickupEvent(player, entityItem, pickedUpStack); player.onItemPickup(entityItem, origStackSize); } stackSizeLast = stack.getCount(); } } // Not everything was handled, update the stack if (entityItem.isDead == false && stack.getCount() != origStackSize) { entityItem.setItem(stack); } // At least some items were picked up if (entityItem.isSilent() == false && (entityItem.isDead || stack.getCount() != origStackSize)) { player.getEntityWorld().playSound(null, player.getPosition(), SoundEvents.ENTITY_ITEM_PICKUP, SoundCategory.MASTER, 0.2F, ((itemRand.nextFloat() - itemRand.nextFloat()) * 0.7F + 1.0F) * 2.0F); } return ret; } private static boolean bagIsOpenable(ItemStack stack) { // Can open a fresh bag with no data if (stack.getTagCompound() == null) { return true; } // If the bag is locked from opening if (stack.getTagCompound().getCompoundTag("HandyBag").getBoolean("DisableOpen")) { return false; } return true; } /** * Returns an ItemStack containing an enabled Handy Bag in the player's inventory, or null if none is found. */ public static ItemStack getOpenableBag(EntityPlayer player) { IItemHandler playerInv = player.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, null); List<Integer> slots = InventoryUtils.getSlotNumbersOfMatchingItems(playerInv, EnderUtilitiesItems.HANDY_BAG); for (int slot : slots) { ItemStack stack = playerInv.getStackInSlot(slot); if (bagIsOpenable(stack)) { return stack; } } return ItemStack.EMPTY; } @Override public int getSizeInventory(ItemStack containerStack) { return containerStack.getMetadata() == META_TIER_2 ? INV_SIZE_TIER_2 : INV_SIZE_TIER_1; } public static void performGuiAction(EntityPlayer player, int action, int element) { if (player.openContainer instanceof ContainerHandyBag) { ContainerHandyBag container = (ContainerHandyBag)player.openContainer; InventoryItemModular inv = container.inventoryItemModular; ItemStack stack = inv.getModularItemStack(); if (stack.isEmpty() == false && stack.getItem() == EnderUtilitiesItems.HANDY_BAG) { int max = ((ItemHandyBag)stack.getItem()).getMaxModules(stack, ModuleType.TYPE_MEMORY_CARD_ITEMS); // Changing the selected module via the GUI buttons if (action == GUI_ACTION_SELECT_MODULE && element >= 0 && element < max) { UtilItemModular.setModuleSelection(stack, ModuleType.TYPE_MEMORY_CARD_ITEMS, element); inv.readFromContainerItemStack(); } else if (action == GUI_ACTION_MOVE_ITEMS) { IItemHandlerModifiable playerMainInv = (IItemHandlerModifiable) player.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, EnumFacing.UP); IItemHandlerModifiable offhandInv = new PlayerOffhandInvWrapper(player.inventory); IItemHandler playerInv = new CombinedInvWrapper(playerMainInv, offhandInv); IItemHandler wrappedBagInv = getWrappedEnabledInv(stack, inv); switch (element & 0x7FFF) { case 0: // Move all items to Bag // Holding shift, move all items, even from hotbar if ((element & 0x8000) != 0) { InventoryUtils.tryMoveAllItems(playerInv, wrappedBagInv); } else { InventoryUtils.tryMoveAllItemsWithinSlotRange(playerInv, wrappedBagInv, new SlotRange(9, 27), new SlotRange(wrappedBagInv)); } break; case 1: // Move matching items to Bag // Holding shift, move all items, even from hotbar if ((element & 0x8000) != 0) { InventoryUtils.tryMoveMatchingItems(playerInv, wrappedBagInv); } else { InventoryUtils.tryMoveMatchingItemsWithinSlotRange(playerInv, wrappedBagInv, new SlotRange(9, 27), new SlotRange(wrappedBagInv)); } break; case 2: // Leave one stack of each item type and fill that stack InventoryUtils.leaveOneFullStackOfEveryItem(playerInv, wrappedBagInv, true); break; case 3: // Fill stacks in player inventory from bag InventoryUtils.fillStacksOfMatchingItems(wrappedBagInv, playerInv); break; case 4: // Move matching items to player inventory InventoryUtils.tryMoveMatchingItems(wrappedBagInv, playerInv); break; case 5: // Move all items to player inventory InventoryUtils.tryMoveAllItems(wrappedBagInv, playerInv); break; } } else if (action == GUI_ACTION_SORT_ITEMS && element >= 0 && element <= 3) { // Player inventory if (element == 3) { IItemHandlerModifiable playerMainInv = (IItemHandlerModifiable) player.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, EnumFacing.UP); InventoryUtils.sortInventoryWithinRange(playerMainInv, new SlotRange(9, 27)); return; } // The basic tier bag only has one sort button/inventory section if (element > 0 && stack.getMetadata() == 0) { return; } InventoryUtils.sortInventoryWithinRange(inv, getSlotRangeForSection(element)); } else if (action == GUI_ACTION_TOGGLE_BLOCK && element >= 0 && element <= 2) { setSlotMask(inv, stack, element, "LockMask"); } else if (action == GUI_ACTION_TOGGLE_UPDATE && element >= 0 && element <= 2) { setSlotMask(inv, stack, element, "UpdateMask"); } else if (action == GUI_ACTION_TOGGLE_MODES && (element & 0x03) >= 0 && (element & 0x03) <= 2) { switch (element & 0x03) { case 0: NBTUtils.toggleBoolean(stack, "HandyBag", "DisableOpen"); break; case 1: PickupMode.cycleMode(stack, player, (element & 0x8000) != 0); break; case 2: RestockMode.cycleMode(stack, player, (element & 0x8000) != 0); break; default: } } else if (action == GUI_ACTION_TOGGLE_SHIFTCLICK) { ShiftMode.cycleMode(stack, element != 0); } else if (action == GUI_ACTION_TOGGLE_SHIFTCLICK_DOUBLETAP) { if (ShiftMode.fromStack(stack) == ShiftMode.DOUBLE_TAP) { ShiftMode.toggleDoubleTapEffectiveMode(stack); } } else if (action == GUI_ACTION_OPEN_BAUBLES && ModRegistry.isModLoadedBaubles()) { try { ModContainer baublesContainer = Loader.instance().getIndexedModList().get(ModRegistry.MODID_BAUBLES); if (baublesContainer != null) { Object baubles = baublesContainer.getMod(); BlockPos pos = player.getPosition(); player.openGui(baubles, 0, player.getEntityWorld(), pos.getX(), pos.getY(), pos.getZ()); } } catch (Exception e) { EnderUtilities.logger.warn("Failed to open the Baubles GUI from Handy Bag", e); } } } } } private static void setSlotMask(InventoryItemModular inv, ItemStack bagStack, int bagSection, String tagName) { int slot = inv.getSelectedModuleIndex(); if (slot >= 0) { ItemStack cardStack = inv.getModuleInventory().getStackInSlot(slot); if (cardStack.isEmpty() == false) { long[] masks = new long[] { 0x1FFFFFFL, 0x1FFF8000000L, 0x7FFE0000000000L }; long mask = NBTUtils.getLong(cardStack, "HandyBag", tagName); mask ^= masks[bagSection]; NBTUtils.setLong(cardStack, "HandyBag", tagName, mask); UtilItemModular.setSelectedModuleStackAbs(bagStack, ModuleType.TYPE_MEMORY_CARD_ITEMS, cardStack); } } } private static SlotRange getSlotRangeForSection(int section) { if (section == 0) { return new SlotRange(0, 27); } else if (section == 1) { return new SlotRange(27, 14); } return new SlotRange(41, 14); } private static IItemHandler getWrappedEnabledInv(ItemStack stack, IItemHandlerModifiable baseInv) { // For the basic version of the bag, there is no locking/sections, so just return the base inventory if (stack.getMetadata() != 1) { return baseInv; } long[] masks = new long[] { 0x1FFFFFFL, 0x1FFF8000000L, 0x7FFE0000000000L }; ItemStack cardStack = UtilItemModular.getSelectedModuleStackAbs(stack, ModuleType.TYPE_MEMORY_CARD_ITEMS); if (cardStack.isEmpty()) { return InventoryUtils.NULL_INV; } long lockMask = NBTUtils.getLong(cardStack, "HandyBag", "LockMask"); IItemHandlerModifiable inv = null; for (int i = 0; i < 3; i++) { if ((lockMask & masks[i]) == 0) { SlotRange range = getSlotRangeForSection(i); if (inv == null) { inv = new RangedWrapper(baseInv, range.first, range.lastExc); } else { inv = new CombinedInvWrapper(inv, new RangedWrapper(baseInv, range.first, range.lastExc)); } } } return inv != null ? inv : InventoryUtils.NULL_INV; } @Override public boolean doKeyBindingAction(EntityPlayer player, ItemStack stack, int key) { // Alt + Toggle mode: Toggle the private/public mode if (EnumKey.TOGGLE.matches(key, HotKeys.MOD_ALT)) { UtilItemModular.changePrivacyModeOnSelectedModuleAbs(stack, player, ModuleType.TYPE_MEMORY_CARD_ITEMS); return true; } // Shift + Toggle mode: Cycle Pickup Mode else if (EnumKey.TOGGLE.matches(key, HotKeys.MOD_SHIFT)) { PickupMode.cycleMode(stack, player, EnumKey.keypressActionIsReversed(key)); return true; } // Just Toggle mode: Toggle Restock mode else if (EnumKey.TOGGLE.matches(key, HotKeys.MOD_NONE)) { RestockMode.cycleMode(stack, player, EnumKey.keypressActionIsReversed(key)); return true; } // Alt + Shift + Toggle mode: Toggle Locked Mode else if (EnumKey.TOGGLE.matches(key, HotKeys.MOD_SHIFT_ALT)) { NBTUtils.toggleBoolean(stack, "HandyBag", "DisableOpen"); return true; } // Ctrl (+ Shift) + Toggle mode: Change the selected Memory Card else if (EnumKey.TOGGLE.matches(key, HotKeys.MOD_CTRL, HotKeys.MOD_SHIFT) || EnumKey.SCROLL.matches(key, HotKeys.MOD_CTRL)) { this.changeSelectedModule(stack, ModuleType.TYPE_MEMORY_CARD_ITEMS, EnumKey.keypressActionIsReversed(key) || EnumKey.keypressContainsShift(key)); return true; } return false; } @Override public boolean useAbsoluteModuleIndexing(ItemStack stack) { return true; } @Override public int getMaxModules(ItemStack containerStack) { return 4; } @Override public int getMaxModules(ItemStack containerStack, ModuleType moduleType) { return moduleType.equals(ModuleType.TYPE_MEMORY_CARD_ITEMS) ? this.getMaxModules(containerStack) : 0; } @Override public int getMaxModules(ItemStack containerStack, ItemStack moduleStack) { if (moduleStack.getItem() instanceof IModule) { IModule imodule = (IModule)moduleStack.getItem(); if (imodule.getModuleType(moduleStack).equals(ModuleType.TYPE_MEMORY_CARD_ITEMS)) { int tier = imodule.getModuleTier(moduleStack); if (tier >= ItemEnderPart.MEMORY_CARD_TYPE_ITEMS_6B && tier <= ItemEnderPart.MEMORY_CARD_TYPE_ITEMS_12B) { return this.getMaxModules(containerStack); } } } return 0; } @Override public void getSubItemsCustom(CreativeTabs creativeTab, NonNullList<ItemStack> list) { list.add(new ItemStack(this, 1, 0)); // Tier 1 list.add(new ItemStack(this, 1, 1)); // Tier 2 } @Override public ResourceLocation[] getItemVariants() { String rl = Reference.MOD_ID + ":" + "item_" + this.name; ResourceLocation[] variants = new ResourceLocation[36]; int i = 0; for (String strL : new String[] { "false", "true" }) { for (String strP : new String[] { "none", "matching", "all" }) { for (String strR : new String[] { "disabled", "all", "hotbar" }) { for (String strT : new String[] { "0", "1" }) { String variant = String.format("locked=%s,pickupmode=%s,restockmode=%s,tier=%s", strL, strP, strR, strT); variants[i++] = new ModelResourceLocation(rl, variant); } } } } return variants; } @Override public ModelResourceLocation getModelLocation(ItemStack stack) { String variant = "locked=" + (bagIsOpenable(stack) ? "false" : "true") + ",pickupmode=" + PickupMode.fromStack(stack).getVariantName() + ",restockmode=" + RestockMode.fromStack(stack).getName() + ",tier=" + MathHelper.clamp(stack.getMetadata(), 0, 1); return new ModelResourceLocation(Reference.MOD_ID + ":" + "item_" + this.name, variant); } private static void setModeOnCard(ItemStack bagStack, EntityPlayer player, String modeName, int value) { InventoryItemModular bagInv = null; // If this bag is currently open, then use that inventory instead of creating a new one, // otherwise the open GUI/inventory will overwrite the changes from the picked up items. if (player.openContainer instanceof ContainerHandyBag && ((ContainerHandyBag) player.openContainer).inventoryItemModular.getModularItemStack() == bagStack) { bagInv = ((ContainerHandyBag) player.openContainer).inventoryItemModular; } if (bagInv != null) { if (bagInv.isAccessibleBy(player) == false) { return; } int moduleSlot = bagInv.getSelectedModuleIndex(); if (moduleSlot >= 0) { ItemStack cardStack = bagInv.getModuleInventory().getStackInSlot(moduleSlot); if (cardStack.isEmpty() == false) { NBTUtils.setByte(cardStack, "HandyBag", modeName, (byte) value); bagInv.getModuleInventory().onContentsChanged(moduleSlot); // Write to NBT } } } else { ItemStack cardStack = UtilItemModular.getSelectedModuleStackAbs(bagStack, ModuleType.TYPE_MEMORY_CARD_ITEMS); if (cardStack.isEmpty() == false) { NBTUtils.setByte(cardStack, "HandyBag", modeName, (byte) value); UtilItemModular.setSelectedModuleStackAbs(bagStack, ModuleType.TYPE_MEMORY_CARD_ITEMS, cardStack); } } } public enum PickupMode { NONE (0, "enderutilities.tooltip.item.disabled", "none"), MATCHING (1, "enderutilities.tooltip.item.matching", "matching"), ALL (2, "enderutilities.tooltip.item.all", "all"); private final String displayName; private final String variantName; private PickupMode (int id, String displayName, String variantName) { this.displayName = displayName; this.variantName = variantName; } public String getDisplayName() { return I18n.format(this.displayName); } public String getVariantName() { return this.variantName; } public static PickupMode fromStack(ItemStack bagStack) { int id = getModeId(bagStack); return (id >= 0 && id < values().length) ? values()[id] : NONE; } public static void cycleMode(ItemStack bagStack, EntityPlayer player, boolean reverse) { int id = getModeId(bagStack) + (reverse ? -1 : 1); if (id < 0) { id = values().length - 1; } else if (id >= values().length) { id = 0; } setModeId(bagStack, player, id); } private static int getModeId(ItemStack bagStack) { ItemStack cardStack = UtilItemModular.getSelectedModuleStackAbs(bagStack, ModuleType.TYPE_MEMORY_CARD_ITEMS); if (cardStack.isEmpty() == false) { return NBTUtils.getByte(cardStack, "HandyBag", "PickupMode"); } return PickupMode.NONE.ordinal(); } private static void setModeId(ItemStack bagStack, EntityPlayer player, int id) { setModeOnCard(bagStack, player, "PickupMode", (byte) id); } } public enum RestockMode { DISABLED ("disabled"), ALL ("all"), HOTBAR ("hotbar"); private final String name; private RestockMode (String name) { this.name = name; } public String getName() { return this.name; } public String getDisplayName() { return I18n.format("enderutilities.tooltip.item." + this.getName()); } public static RestockMode fromStack(ItemStack bagStack) { int id = getModeId(bagStack); return (id >= 0 && id < values().length) ? values()[id] : DISABLED; } public static void cycleMode(ItemStack bagStack, EntityPlayer player, boolean reverse) { int id = getModeId(bagStack) + (reverse ? -1 : 1); if (id < 0) { id = values().length - 1; } else if (id >= values().length) { id = 0; } setModeId(bagStack, player, id); } private static int getModeId(ItemStack bagStack) { ItemStack cardStack = UtilItemModular.getSelectedModuleStackAbs(bagStack, ModuleType.TYPE_MEMORY_CARD_ITEMS); if (cardStack.isEmpty() == false) { return NBTUtils.getByte(cardStack, "HandyBag", "RestockMode"); } return RestockMode.DISABLED.ordinal(); } private static void setModeId(ItemStack bagStack, EntityPlayer player, int id) { setModeOnCard(bagStack, player, "RestockMode", (byte) id); } } public enum ShiftMode { TO_BAG ("enderutilities.gui.label.handybag.shiftclick.tobag"), INV_HOTBAR ("enderutilities.gui.label.handybag.shiftclick.invhotbar"), DOUBLE_TAP ("enderutilities.gui.label.handybag.shiftclick.doubletapshift"); private final String unlocName; private ShiftMode (String unlocName) { this.unlocName = unlocName; } public String getUnlocName() { return this.unlocName; } public String getDisplayName() { return I18n.format(this.getUnlocName()); } public static ShiftMode fromId(int id) { return (id >= 0 && id < values().length) ? values()[id] : TO_BAG; } public static ShiftMode fromStack(ItemStack bagStack) { return fromId(getModeId(bagStack) & 0x03); } public static void cycleMode(ItemStack bagStack, boolean reverse) { // The topmost bit indicates the current "double-tapped-mode" // So when the main mode is "double-tap-to-toggle", then the topmost bit indicates // whether the currently active mode is to-bag or between-inventory-and-hotbar int rawMode = getModeId(bagStack); int id = (rawMode & 0x03) + (reverse ? -1 : 1); if (id < 0) { id = values().length - 1; } else if (id >= values().length) { id = 0; } rawMode = (rawMode & 0x80) | id; setModeId(bagStack, rawMode); } public static void toggleDoubleTapEffectiveMode(ItemStack bagStack) { // The topmost bit indicates the current "double-tapped-mode" // So when the main mode is "double-tap-to-toggle", then the topmost bit indicates // whether the currently active mode is to-bag or between-inventory-and-hotbar byte rawMode = (byte) (getModeId(bagStack) ^ 0x80); setModeId(bagStack, rawMode); } /** * Returns either TO_BAG or INV_HOTBAR, taking into account * a possible active DOUBLE_TAP mode's current "double-tap-status". */ public static ShiftMode getEffectiveMode(ItemStack bagStack) { int rawMode = getModeId(bagStack); ShiftMode mode = fromId(rawMode & 0x03); if (mode == ShiftMode.DOUBLE_TAP) { return (rawMode & 0x80) != 0 ? ShiftMode.INV_HOTBAR : ShiftMode.TO_BAG; } else { return mode; } } private static int getModeId(ItemStack bagStack) { return NBTUtils.getByte(bagStack, "HandyBag", "ShiftMode"); } private static void setModeId(ItemStack bagStack, int id) { NBTUtils.setByte(bagStack, "HandyBag", "ShiftMode", (byte) id); } } }