package fi.dy.masa.enderutilities.item; import java.util.ArrayList; 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.entity.item.EntityItem; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.init.SoundEvents; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.tileentity.TileEntity; import net.minecraft.tileentity.TileEntityEnderChest; import net.minecraft.util.ActionResult; import net.minecraft.util.EnumActionResult; import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumHand; import net.minecraft.util.ResourceLocation; import net.minecraft.util.SoundCategory; import net.minecraft.util.math.BlockPos; import net.minecraft.util.text.TextFormatting; import net.minecraft.world.World; import net.minecraftforge.common.util.Constants; import net.minecraftforge.event.entity.player.EntityItemPickupEvent; import net.minecraftforge.fml.common.FMLCommonHandler; import net.minecraftforge.items.CapabilityItemHandler; import net.minecraftforge.items.IItemHandler; import fi.dy.masa.enderutilities.EnderUtilities; import fi.dy.masa.enderutilities.event.PlayerItemPickupEvent; import fi.dy.masa.enderutilities.inventory.container.ContainerPickupManager; import fi.dy.masa.enderutilities.inventory.container.base.SlotRange; import fi.dy.masa.enderutilities.inventory.item.InventoryItem; import fi.dy.masa.enderutilities.item.base.IKeyBound; import fi.dy.masa.enderutilities.item.base.IModule; import fi.dy.masa.enderutilities.item.base.ItemLocationBoundModular; import fi.dy.masa.enderutilities.item.base.ItemModule.ModuleType; import fi.dy.masa.enderutilities.item.part.ItemLinkCrystal; 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.util.InventoryUtils; import fi.dy.masa.enderutilities.util.nbt.NBTUtils; import fi.dy.masa.enderutilities.util.nbt.UtilItemModular; public class ItemPickupManager extends ItemLocationBoundModular implements IKeyBound { public static final String TAG_NAME_CONTAINER = "PickupManager"; public static final String TAG_NAME_TX_INVENTORY = "TransportItems"; public static final String TAG_NAME_FILTER_INVENTORY_PRE = "FilterItems_"; public static final String TAG_NAME_PRESET_SELECTION = "SelPreset"; public static final String TAG_NAME_PRESET = "Preset_"; public static final String TAG_NAME_LOCKED = "Locked"; public static final String TAG_NAME_INVFILTER_ENABLED = "InvFiltEnabled"; public static final String TAG_NAME_INVFILTER_MODE = "InvFiltMode"; public static final String TAG_NAME_INVFILTER_META = "InvFiltMeta"; public static final String TAG_NAME_INVFILTER_NBT = "InvFiltNBT"; public static final String TAG_NAME_TXFILTER_ENABLED = "TxFiltEnabled"; public static final String TAG_NAME_TXFILTER_MODE = "TxFiltMode"; public static final String TAG_NAME_TXFILTER_META = "TxFiltMeta"; public static final String TAG_NAME_TXFILTER_NBT = "TxFiltNBT"; public static final int ENDER_CHARGE_COST_PER_SENT_ITEM = 2; public static final int NUM_PRESETS = 4; public static final int GUI_ACTION_SELECT_MODULE = 0; public static final int GUI_ACTION_CHANGE_PRESET = 1; public static final int GUI_ACTION_TOGGLE_INVENTORY_SETTINGS = 2; public static final int GUI_ACTION_TOGGLE_TRANSPORT_SETTINGS = 3; public static final SlotRange TRANSPORT_FILTER_SLOTS = new SlotRange(0, 18); public static final SlotRange INVENTORY_FILTER_SLOTS = new SlotRange(18, 18); public ItemPickupManager(String name) { super(name); this.setMaxStackSize(1); this.setMaxDamage(0); } @Override public EnumActionResult onItemUse(EntityPlayer player, World world, BlockPos pos, EnumHand hand, EnumFacing side, float hitX, float hitY, float hitZ) { if (world.isRemote == false) { TileEntity te = world.getTileEntity(pos); // When sneak-right-clicking on an inventory or an Ender Chest, and the installed Link Crystal is a block type crystal, // then bind the crystal to the block clicked on. if (player.isSneaking() && te != null && (te.getClass() == TileEntityEnderChest.class || te.hasCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, side))) { ItemStack stack = player.getHeldItem(hand); UtilItemModular.setTarget(stack, player,pos, side, hitX, hitY, hitZ, false, false); } } return EnumActionResult.SUCCESS; } @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_PICKUP_MANAGER, 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 getItemStackDisplayName(ItemStack stack) { int preset = NBTUtils.getByte(stack, TAG_NAME_CONTAINER, TAG_NAME_PRESET_SELECTION); int lc = UtilItemModular.getStoredModuleSelection(stack, ModuleType.TYPE_LINKCRYSTAL); String pre = TextFormatting.GREEN.toString(); String rst = TextFormatting.RESET.toString() + TextFormatting.WHITE.toString(); String str = " - pre: " + pre + (preset + 1) + rst + " LC: " + pre + lc + rst; String target = this.getTargetDisplayName(stack); if (target != null) { str = str + " - " + pre + target + rst; } return this.getBaseItemDisplayName(stack) + str; } @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 rst = TextFormatting.RESET.toString() + TextFormatting.GRAY.toString(); String str; if (isEnabled(containerStack)) { str = I18n.format("enderutilities.tooltip.item.enabled") + ": " + preGreen + I18n.format("enderutilities.tooltip.item.yes") + rst; } else { str = I18n.format("enderutilities.tooltip.item.enabled") + ": " + preRed + I18n.format("enderutilities.tooltip.item.no") + rst; } list.add(str); int preset = NBTUtils.getByte(containerStack, TAG_NAME_CONTAINER, TAG_NAME_PRESET_SELECTION) + 1; list.add(I18n.format("enderutilities.tooltip.item.preset") + ": " + TextFormatting.BLUE.toString() + preset + rst); super.addTooltipLines(containerStack, player, list, verbose); } public static NBTTagCompound getSelectedPresetTag(ItemStack stack, boolean create) { if (stack.isEmpty()) { return null; } NBTTagCompound containerTag = NBTUtils.getCompoundTag(stack, TAG_NAME_CONTAINER, create); if (containerTag != null) { int selection = containerTag.getByte(TAG_NAME_PRESET_SELECTION); if (containerTag.hasKey(TAG_NAME_PRESET + selection, Constants.NBT.TAG_COMPOUND)) { return containerTag.getCompoundTag(TAG_NAME_PRESET + selection); } else if (create) { NBTTagCompound tag = new NBTTagCompound(); containerTag.setTag(TAG_NAME_PRESET + selection, tag); return tag; } } return null; } public static byte getSettingValue(ItemStack stack, String tagName) { NBTTagCompound tag = getSelectedPresetTag(stack, false); return tag != null ? tag.getByte(tagName) : 0; } public static void setSettingValue(ItemStack stack, String tagName, byte value) { NBTTagCompound tag = getSelectedPresetTag(stack, true); tag.setByte(tagName, value); } public static boolean isEnabled(ItemStack stack) { return NBTUtils.getBoolean(stack, TAG_NAME_CONTAINER, TAG_NAME_LOCKED) == false; } /** * Returns the ItemStacks of enabled Pickup Managers in the player's inventory */ public static List<ItemStack> getEnabledItems(EntityPlayer player) { IItemHandler playerInv = player.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, null); List<Integer> slots = InventoryUtils.getSlotNumbersOfMatchingItems(playerInv, EnderUtilitiesItems.PICKUP_MANAGER); List<ItemStack> enabledItems = new ArrayList<ItemStack>(); for (int slot : slots) { ItemStack stack = playerInv.getStackInSlot(slot); if (isEnabled(stack)) { enabledItems.add(stack); } } return enabledItems; } /** * Returns the first found ItemStack containing an enabled Pickup Manager in the player's inventory, or null if none are found. */ public static ItemStack getFirstEnabledItem(EntityPlayer player) { List<ItemStack> items = getEnabledItems(player); return items.size() > 0 ? items.get(0) : ItemStack.EMPTY; } public boolean tryTransportItemsFromTransportSlot(InventoryItem inv, EntityPlayer player, ItemStack manager) { ItemStack stack = inv.getStackInSlot(0); if (stack.isEmpty()) { return false; } int sizeOrig = stack.getCount(); int max = stack.getMaxStackSize(); stack = inv.extractItem(0, max, false); while (stack.isEmpty() == false) { stack = this.tryTransportItems(player, manager, stack); // Could not transport the whole stack (anymore) if (stack.isEmpty() == false) { inv.insertItem(0, stack, false); break; } stack = inv.extractItem(0, max, false); } stack = inv.getStackInSlot(0); return stack.isEmpty() || stack.getCount() != sizeOrig; } public ItemStack tryTransportItems(EntityPlayer player, ItemStack manager, ItemStack itemsIn) { if (itemsIn.isEmpty()) { return itemsIn; } IItemHandler inv = UtilItemModular.getBoundInventory(manager, player, 30); if (inv == null) { return itemsIn; } //return InventoryUtils.tryInsertItemStackToInventory(inv, itemsIn); ItemStack stackToSend = itemsIn.copy(); int cost = ENDER_CHARGE_COST_PER_SENT_ITEM; // Not enough Ender Charge to send all the items if (UtilItemModular.useEnderCharge(manager, cost * itemsIn.getCount(), true) == false) { int available = UtilItemModular.getAvailableEnderCharge(manager); if (available < cost) { return itemsIn; } stackToSend.setCount(Math.min(itemsIn.getCount(), available / cost)); } int numTransported = stackToSend.getCount(); ItemStack itemsRemaining = InventoryUtils.tryInsertItemStackToInventory(inv, stackToSend); if (itemsRemaining.isEmpty() == false) { numTransported -= itemsRemaining.getCount(); } itemsIn.shrink(numTransported); // Get the final charge amount UtilItemModular.useEnderCharge(manager, numTransported * cost, false); if (itemsIn.isEmpty()) { return ItemStack.EMPTY; } return itemsIn; } /** * Tries to handle the given ItemStack. Returns true if all items were handled and further processing should be canceled. */ public Result handleItems(EntityPlayer player, ItemStack manager, ItemStack itemsIn) { byte preset = NBTUtils.getByte(manager, TAG_NAME_CONTAINER, TAG_NAME_PRESET_SELECTION); InventoryItem inv = new InventoryItem(manager, 36, 1, false, player.getEntityWorld().isRemote, TAG_NAME_FILTER_INVENTORY_PRE + preset); inv.readFromContainerItemStack(); // Transport filters/functionality enabled if (getSettingValue(manager, TAG_NAME_TXFILTER_ENABLED) != 0) { boolean ignoreMeta = getSettingValue(manager, TAG_NAME_TXFILTER_META) != 0; boolean ignoreNbt = getSettingValue(manager, TAG_NAME_TXFILTER_NBT) == 0; boolean match = InventoryUtils.matchingStackFoundInSlotRange(inv, TRANSPORT_FILTER_SLOTS, itemsIn, ignoreMeta, ignoreNbt); // 0 = Black list byte mode = getSettingValue(manager, TAG_NAME_TXFILTER_MODE); // White list and match found, or black list and no match found if ((mode != 0 && match) || (mode == 0 && match == false)) { ItemStack stackTmp = this.tryTransportItems(player, manager, itemsIn); // All items successfully transported if (stackTmp.isEmpty()) { return Result.TRANSPORTED; } else { itemsIn.setCount(stackTmp.getCount()); } } } // Pickup-to-inventory filtering enabled if (getSettingValue(manager, TAG_NAME_INVFILTER_ENABLED) != 0) { boolean ignoreMeta = getSettingValue(manager, TAG_NAME_INVFILTER_META) != 0; boolean ignoreNbt = getSettingValue(manager, TAG_NAME_INVFILTER_NBT) == 0; boolean match = InventoryUtils.matchingStackFoundInSlotRange(inv, INVENTORY_FILTER_SLOTS, itemsIn, ignoreMeta, ignoreNbt); // 0 = Black list byte mode = getSettingValue(manager, TAG_NAME_INVFILTER_MODE); // White list if (mode != 0) { return match ? Result.WHITELISTED : Result.NOT_WHITELISTED; } // Black list else if (mode == 0) { return match ? Result.BLACKLISTED : Result.NOT_BLACKLISTED; } } return Result.NOT_HANDLED; } /** * Try to handle the items being picked up. * @param event * @return true to prevent further processing of the event */ public static boolean onItemPickupEvent(PlayerItemPickupEvent event) { if (event.getEntityPlayer().getEntityWorld().isRemote) { return false; } EntityPlayer player = event.getEntityPlayer(); List<ItemStack> managers = getEnabledItems(player); boolean deny = managers.size() > 0; boolean blackListed = false; boolean transported = false; Iterator<ItemStack> iter = event.drops.iterator(); while (iter.hasNext()) { ItemStack stackIn = iter.next(); if (stackIn.isEmpty()) { continue; } //int i = 0; for (ItemStack manager : managers) { Result result = ((ItemPickupManager) manager.getItem()).handleItems(player, manager, stackIn); //System.out.println("i: " + i++ + " result: " + result); // Blacklisted or successfully transported, cancel further processing if (result == Result.BLACKLISTED) { blackListed = true; deny = true; break; } else if (result == Result.TRANSPORTED) { iter.remove(); transported = true; break; } // Whitelisted, no need to check any further managers, just allow picking it up else if (result == Result.WHITELISTED && blackListed == false) { deny = false; break; } // Filters disabled or filtering mode was black list, and the item was not on the black list => allow through if (blackListed == false && (result == Result.NOT_HANDLED || result == Result.NOT_BLACKLISTED)) { deny = false; } } } // At least some items were picked up if (transported) { 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 (deny) { event.setCanceled(true); } return deny; } /** * Try to handle the items being picked up. * @param event * @return true to prevent further processing of the event */ 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(); List<ItemStack> managers = getEnabledItems(player); // If there are enabled managers in the player's inventory, then initialize to "deny" boolean deny = managers.size() > 0; //int i = 0; for (ItemStack manager : managers) { // 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(); } Result result = ((ItemPickupManager) manager.getItem()).handleItems(player, manager, stack); //System.out.println("i: " + i++ + " result: " + result); // Blacklisted or successfully transported, cancel further processing if (result == Result.BLACKLISTED || result == Result.TRANSPORTED) { if (result == Result.TRANSPORTED) { entityItem.setDead(); } deny = true; break; } // Whitelisted, no need to check any further managers, just allow picking it up if (result == Result.WHITELISTED) { deny = false; break; } // Filters disabled or filtering mode was black list, and the item was not on the black list => allow through if (result == Result.NOT_HANDLED || result == Result.NOT_BLACKLISTED) { deny = false; } } // At least some items were picked up if (stack.getCount() != origStackSize || entityItem.isDead) { if (entityItem.isSilent() == false) { player.getEntityWorld().playSound(null, player.getPosition(), SoundEvents.ENTITY_ITEM_PICKUP, SoundCategory.MASTER, 0.2F, ((player.getEntityWorld().rand.nextFloat() - player.getEntityWorld().rand.nextFloat()) * 0.7F + 1.0F) * 2.0F); } if (stack.isEmpty() || entityItem.isDead) { FMLCommonHandler.instance().firePlayerItemPickupEvent(player, entityItem, origStack); player.onItemPickup(entityItem, origStackSize); } } if (deny) { event.setCanceled(true); } return deny; } @Override public boolean doKeyBindingAction(EntityPlayer player, ItemStack stack, int key) { // Just Toggle mode: Toggle locked state if (EnumKey.TOGGLE.matches(key, HotKeys.MOD_NONE)) { NBTUtils.toggleBoolean(stack, TAG_NAME_CONTAINER, TAG_NAME_LOCKED); return true; } // Shift + Toggle mode: Cycle the slot mask preset else if (EnumKey.TOGGLE.matches(key, HotKeys.MOD_SHIFT) || EnumKey.SCROLL.matches(key, HotKeys.MOD_SHIFT)) { NBTUtils.cycleByteValue(stack, TAG_NAME_CONTAINER, TAG_NAME_PRESET_SELECTION, NUM_PRESETS - 1, EnumKey.keypressActionIsReversed(key)); return true; } else { return super.doKeyBindingAction(player, stack, key); } } public static void performGuiAction(EntityPlayer player, int action, int element) { if ((player.openContainer instanceof ContainerPickupManager) == false) { return; } ContainerPickupManager container = (ContainerPickupManager)player.openContainer; ItemStack stack = container.getContainerItem(); if (stack.isEmpty() || (stack.getItem() instanceof ItemPickupManager) == false) { return; } if (action == GUI_ACTION_SELECT_MODULE && element >= 0 && element < 3) { UtilItemModular.setModuleSelection(stack, ModuleType.TYPE_LINKCRYSTAL, element); ((ItemPickupManager) stack.getItem()).tryTransportItemsFromTransportSlot(container.inventoryItemTransmit, player, stack); } else if (action == GUI_ACTION_CHANGE_PRESET && element >= 0 && element < NUM_PRESETS) { NBTUtils.setByte(stack, TAG_NAME_CONTAINER, TAG_NAME_PRESET_SELECTION, (byte)element); container.inventoryItemFilters.setItemStorageTagName("FilterItems_" + element); container.inventoryItemFilters.readFromContainerItemStack(); } else if (action == GUI_ACTION_TOGGLE_INVENTORY_SETTINGS && element >= 0 && element < 4) { String tagName; if (element == 0) tagName = TAG_NAME_INVFILTER_ENABLED; else if (element == 1) tagName = TAG_NAME_INVFILTER_MODE; else if (element == 2) tagName = TAG_NAME_INVFILTER_META; else tagName = TAG_NAME_INVFILTER_NBT; setSettingValue(stack, tagName, (byte)(getSettingValue(stack, tagName) ^ 0x1)); } else if (action == GUI_ACTION_TOGGLE_TRANSPORT_SETTINGS && element >= 0 && element < 4) { String tagName; if (element == 0) tagName = TAG_NAME_TXFILTER_ENABLED; else if (element == 1) tagName = TAG_NAME_TXFILTER_MODE; else if (element == 2) tagName = TAG_NAME_TXFILTER_META; else tagName = TAG_NAME_TXFILTER_NBT; setSettingValue(stack, tagName, (byte)(getSettingValue(stack, tagName) ^ 0x1)); } } @Override public boolean useAbsoluteModuleIndexing(ItemStack stack) { return true; } @Override public int getMaxModules(ItemStack containerStack) { return 4; } @Override public int getMaxModules(ItemStack containerStack, ModuleType moduleType) { if (ModuleType.TYPE_LINKCRYSTAL.equals(moduleType)) { return 3; } if (ModuleType.TYPE_ENDERCAPACITOR.equals(moduleType)) { return 1; } return 0; } @Override public int getMaxModules(ItemStack containerStack, ItemStack moduleStack) { if (moduleStack.getItem() instanceof IModule) { IModule imodule = (IModule)moduleStack.getItem(); ModuleType moduleType = imodule.getModuleType(moduleStack); if (ModuleType.TYPE_LINKCRYSTAL.equals(moduleType)) { if (imodule.getModuleTier(moduleStack) == ItemLinkCrystal.TYPE_BLOCK) { return this.getMaxModules(containerStack, ModuleType.TYPE_LINKCRYSTAL); } } else { return this.getMaxModules(containerStack, moduleType); } } return 0; } public enum Result { TRANSPORTED, WHITELISTED, NOT_WHITELISTED, BLACKLISTED, NOT_BLACKLISTED, NOT_HANDLED; } @Override public ResourceLocation[] getItemVariants() { String rl = Reference.MOD_ID + ":" + "item_" + this.name; return new ResourceLocation[] { new ModelResourceLocation(rl, "locked=false"), new ModelResourceLocation(rl, "locked=true") }; } @Override public ModelResourceLocation getModelLocation(ItemStack stack) { String rl = Reference.MOD_ID + ":" + "item_" + this.name; return new ModelResourceLocation(rl, "locked=" + NBTUtils.getBoolean(stack, TAG_NAME_CONTAINER, TAG_NAME_LOCKED)); } }