--[[ LootLink 3.5: An in-game item database copyright 2004 by Telo - Watches all chat links you see to cache link color and link data - Automatically extracts data from items already in or added to your inventory - Automatically caches link data from items already in or added to your bank - Automatically inspects your target and extracts data for each of their equipped items - Automatically gets link data from your auction queries - Can perform a fully automatic scan of the entire auction house inventory - Stores sell prices for items that you've had in inventory when you've talked to a merchant and displays them in the tooltips for stacks of items that you are looting, stacks of items in your inventory and entries in the LootLink browse window - Converts green loot messages into correctly colored item messages if the item is cached - Provides a browsable, searchable window that allows you to find any item in the cache - Allows you to shift-click items in the browse window to insert links in the chat edit box ]] -------------------------------------------------------------------------------------------------- -- Local LootLink variables -------------------------------------------------------------------------------------------------- -- Function hooks local lOriginal_CanSendAuctionQuery; local lOriginal_AuctionFrameBrowse_OnEvent; local lOriginal_ContainerFrameItemButton_OnEnter; local lOriginal_ContainerFrame_Update; local lOriginal_GameTooltip_SetLootItem; local lOriginal_GameTooltip_SetOwner; local lOriginal_GameTooltip_OnHide; local lOriginal_GameTooltip_ClearMoney; local lOriginal_GameTooltip_ClearMoney_Temp; local lOriginal_ShoppingTooltip1_SetMerchantCompareItem; local lOriginal_ShoppingTooltip2_SetMerchantCompareItem; local lOriginal_ShoppingTooltip1_SetAuctionCompareItem; local lOriginal_ShoppingTooltip2_SetAuctionCompareItem; local lOriginal_SetItemRef; local lOriginal_OnTooltipAddMoney; --local ll = {}; -- If non-nil, kick off a full auction scan next time auctioneer is used local lScanAuction; -- Used for scanning inventory items for their sell prices at a merchant local lBagID; local lSlotID; -- If non-nil, don't add extra information to the tooltip on GameTooltip_ClearMoney local lSuppressInfoAdd; -- If non-nil, check for appearance of GameTooltip for adding information local lCheckTooltip; -- Timer for frequency of tooltip checks local lCheckTimer = 0; -- The current owner of the GameTooltip local lGameToolTipOwner; -- Cache of auction item information local lAuctionItemInfo; -- Used to remember that confirmation is needed of irreversible commands local lResetNeedsConfirm; local lMakeHomeNeedsConfirm; local lLightModeNeedsConfirm; -- If non-nil, the data version upgrade reminder is not displayed on every /lootlink or /ll command local lDisableVersionReminder; -- The current server name and index local lServer; local lServerIndex; -- The number of items in the database, total and for this server local lItemLinksSizeTotal; local lItemLinksSizeServer; local STATE_NAME = 0; local STATE_BOUND = 1; local STATE_UNIQUE = 2; local STATE_LOCATION = 3; local STATE_TYPE = 4; local STATE_DAMAGE = 5; local STATE_DPS = 6; local STATE_ARMOR = 7; local STATE_BLOCK = 8; local STATE_CONTAINER = 9; local STATE_REQUIRES = 10; local STATE_FINISH = 11; local STATE_SPACE = 12 local STATE_SET = 13; local tooltipSpace = " "..string.char(10); local BINDS_DOES_NOT_BIND = 0; local BINDS_EQUIP = 1; local BINDS_PICKUP = 2; local BINDS_USED = 3; local TYPE_ARMOR = 0; local TYPE_WEAPON = 1; local TYPE_SHIELD = 2; local TYPE_RECIPE = 3; local TYPE_CONTAINER = 4; local TYPE_MISC = 5; local SUBTYPE_ARMOR_CLOTH = 0; local SUBTYPE_ARMOR_LEATHER = 1; local SUBTYPE_ARMOR_MAIL = 2; local SUBTYPE_ARMOR_PLATE = 3; local SUBTYPE_ARMOR_RELIC = 4; local lColorSortTable = { }; lColorSortTable["ffff8000"] = 1; -- legendary, orange lColorSortTable["ffa335ee"] = 2; -- epic, purple lColorSortTable["ff0070dd"] = 3; -- rare, blue lColorSortTable["ff1eff00"] = 4; -- uncommon, green lColorSortTable["ffffffff"] = 5; -- common, white lColorSortTable["ff9d9d9d"] = 6; -- poor, gray lColorSortTable["ff40ffc0"] = 100; -- unknown, teal local ArmorSubtypes = { }; ArmorSubtypes[CLOTH] = SUBTYPE_ARMOR_CLOTH; ArmorSubtypes[LEATHER] = SUBTYPE_ARMOR_LEATHER; ArmorSubtypes[MAIL] = SUBTYPE_ARMOR_MAIL; ArmorSubtypes[PLATE] = SUBTYPE_ARMOR_PLATE; ArmorSubtypes[INVTYPE_RELIC] = SUBTYPE_ARMOR_RELIC; local SUBTYPE_WEAPON_AXE = 0; local SUBTYPE_WEAPON_BOW = 1; local SUBTYPE_WEAPON_DAGGER = 2; local SUBTYPE_WEAPON_MACE = 3; local SUBTYPE_WEAPON_FISHING_POLE = 4; local SUBTYPE_WEAPON_STAFF = 5; local SUBTYPE_WEAPON_SWORD = 6; local SUBTYPE_WEAPON_GUN = 7; local SUBTYPE_WEAPON_WAND = 8; local SUBTYPE_WEAPON_THROWN = 9; local SUBTYPE_WEAPON_POLEARM = 10; local SUBTYPE_WEAPON_FIST_WEAPON = 11; local SUBTYPE_WEAPON_CROSSBOW = 12; local WeaponSubtypes = { }; WeaponSubtypes[AXE] = SUBTYPE_WEAPON_AXE; WeaponSubtypes[BOW] = SUBTYPE_WEAPON_BOW; WeaponSubtypes[DAGGER] = SUBTYPE_WEAPON_DAGGER; WeaponSubtypes[MACE] = SUBTYPE_WEAPON_MACE; WeaponSubtypes[FISHING_POLE] = SUBTYPE_WEAPON_FISHING_POLE; WeaponSubtypes[STAFF] = SUBTYPE_WEAPON_STAFF; WeaponSubtypes[SWORD] = SUBTYPE_WEAPON_SWORD; WeaponSubtypes[GUN] = SUBTYPE_WEAPON_GUN; WeaponSubtypes[WAND] = SUBTYPE_WEAPON_WAND; WeaponSubtypes[THROWN] = SUBTYPE_WEAPON_THROWN; WeaponSubtypes[POLEARM] = SUBTYPE_WEAPON_POLEARM; WeaponSubtypes[FIST_WEAPON] = SUBTYPE_WEAPON_FIST_WEAPON; WeaponSubtypes[CROSSBOW] = SUBTYPE_WEAPON_CROSSBOW; local SUBTYPE_SHIELD_BUCKLER = 0; local SUBTYPE_SHIELD_SHIELD = 1; local ShieldSubtypes = { }; ShieldSubtypes["Buckler"] = SUBTYPE_SHIELD_BUCKLER; ShieldSubtypes[SHIELD] = SUBTYPE_SHIELD_SHIELD; local SUBTYPE_RECIPE_ALCHEMY = 0; local SUBTYPE_RECIPE_BLACKSMITHING = 1; local SUBTYPE_RECIPE_COOKING = 2; local SUBTYPE_RECIPE_ENCHANTING = 3; local SUBTYPE_RECIPE_ENGINEERING = 4; local SUBTYPE_RECIPE_LEATHERWORKING = 5; local SUBTYPE_RECIPE_TAILORING = 6; local SUBTYPE_RECIPE_FIRST_AID = 7; local SUBTYPE_RECIPE_FISHING = 8; local RecipeSubtypes = { }; RecipeSubtypes[ALCHEMY] = SUBTYPE_RECIPE_ALCHEMY; RecipeSubtypes[BLACKSMITHING] = SUBTYPE_RECIPE_BLACKSMITHING; RecipeSubtypes[COOKING] = SUBTYPE_RECIPE_COOKING; RecipeSubtypes[ENCHANTING] = SUBTYPE_RECIPE_ENCHANTING; RecipeSubtypes[ENGINEERING] = SUBTYPE_RECIPE_ENGINEERING; RecipeSubtypes[LEATHERWORKING] = SUBTYPE_RECIPE_LEATHERWORKING; RecipeSubtypes[TAILORING] = SUBTYPE_RECIPE_TAILORING; RecipeSubtypes[FIRST_AID] = SUBTYPE_RECIPE_FIRST_AID; RecipeSubtypes[FISHING] = SUBTYPE_RECIPE_FISHING; local SUBTYPE_CONTAINER_BAG = 0; local SUBTYPE_CONTAINER_AMMO_POUCH = 1; local SUBTYPE_CONTAINER_QUIVER = 2; local SUBTYPE_CONTAINER_SOUL_BAG = 3; local SUBTYPE_CONTAINER_HERB_BAG = 4; local SUBTYPE_CONTAINER_ENCHANTING_BAG = 5; local SUBTYPE_CONTAINER_ENGINEERING_BAG = 6; local ContainerSubtypes = { }; ContainerSubtypes[BAG] = SUBTYPE_CONTAINER_BAG; ContainerSubtypes[AMMO_POUCH] = SUBTYPE_CONTAINER_AMMO_POUCH; ContainerSubtypes[QUIVER] = SUBTYPE_CONTAINER_QUIVER; ContainerSubtypes[SOUL_BAG] = SUBTYPE_CONTAINER_SOUL_BAG; ContainerSubtypes[HERB_BAG] = SUBTYPE_CONTAINER_HERB_BAG; ContainerSubtypes[ENCHANTING_BAG] = SUBTYPE_CONTAINER_ENCHANTING_BAG; ContainerSubtypes[ENGINEERING_BAG] = SUBTYPE_CONTAINER_ENGINEERING_BAG; local lTypeAndSubtypeToSkill = { }; lTypeAndSubtypeToSkill[TYPE_ARMOR] = { }; lTypeAndSubtypeToSkill[TYPE_ARMOR][SUBTYPE_ARMOR_CLOTH] = CLOTH; lTypeAndSubtypeToSkill[TYPE_ARMOR][SUBTYPE_ARMOR_LEATHER] = LEATHER; lTypeAndSubtypeToSkill[TYPE_ARMOR][SUBTYPE_ARMOR_MAIL] = MAIL; lTypeAndSubtypeToSkill[TYPE_ARMOR][SUBTYPE_ARMOR_PLATE] = PLATE_MAIL; lTypeAndSubtypeToSkill[TYPE_ARMOR][SUBTYPE_ARMOR_RELIC] = INVTYPE_RELIC; lTypeAndSubtypeToSkill[TYPE_WEAPON] = { }; lTypeAndSubtypeToSkill[TYPE_WEAPON][SUBTYPE_WEAPON_AXE] = { } lTypeAndSubtypeToSkill[TYPE_WEAPON][SUBTYPE_WEAPON_AXE][0] = AXES; lTypeAndSubtypeToSkill[TYPE_WEAPON][SUBTYPE_WEAPON_AXE][1] = TWO_HANDED_AXES; lTypeAndSubtypeToSkill[TYPE_WEAPON][SUBTYPE_WEAPON_BOW] = BOWS; lTypeAndSubtypeToSkill[TYPE_WEAPON][SUBTYPE_WEAPON_DAGGER] = DAGGERS; lTypeAndSubtypeToSkill[TYPE_WEAPON][SUBTYPE_WEAPON_MACE] = { } lTypeAndSubtypeToSkill[TYPE_WEAPON][SUBTYPE_WEAPON_MACE][0] = MACES; lTypeAndSubtypeToSkill[TYPE_WEAPON][SUBTYPE_WEAPON_MACE][1] = TWO_HANDED_MACES; lTypeAndSubtypeToSkill[TYPE_WEAPON][SUBTYPE_WEAPON_FISHING_POLE] = FISHING_POLE; lTypeAndSubtypeToSkill[TYPE_WEAPON][SUBTYPE_WEAPON_STAFF] = STAVES; lTypeAndSubtypeToSkill[TYPE_WEAPON][SUBTYPE_WEAPON_SWORD] = { }; lTypeAndSubtypeToSkill[TYPE_WEAPON][SUBTYPE_WEAPON_SWORD][0] = SWORDS; lTypeAndSubtypeToSkill[TYPE_WEAPON][SUBTYPE_WEAPON_SWORD][1] = TWO_HANDED_SWORDS; lTypeAndSubtypeToSkill[TYPE_WEAPON][SUBTYPE_WEAPON_GUN] = GUNS; lTypeAndSubtypeToSkill[TYPE_WEAPON][SUBTYPE_WEAPON_WAND] = WANDS; lTypeAndSubtypeToSkill[TYPE_WEAPON][SUBTYPE_WEAPON_THROWN] = THROWN; lTypeAndSubtypeToSkill[TYPE_WEAPON][SUBTYPE_WEAPON_POLEARM] = POLEARMS; --@todo Telo: unconfirmed lTypeAndSubtypeToSkill[TYPE_WEAPON][SUBTYPE_WEAPON_FIST_WEAPON] = UNARMED; lTypeAndSubtypeToSkill[TYPE_WEAPON][SUBTYPE_WEAPON_CROSSBOW] = CROSSBOWS; lTypeAndSubtypeToSkill[TYPE_SHIELD] = { }; lTypeAndSubtypeToSkill[TYPE_SHIELD][SUBTYPE_SHIELD_BUCKLER] = SHIELD; --@todo Telo: deprecated subtype, should remove lTypeAndSubtypeToSkill[TYPE_SHIELD][SUBTYPE_SHIELD_SHIELD] = SHIELD; lTypeAndSubtypeToSkill[TYPE_RECIPE] = { }; lTypeAndSubtypeToSkill[TYPE_RECIPE][SUBTYPE_RECIPE_ALCHEMY] = ALCHEMY; lTypeAndSubtypeToSkill[TYPE_RECIPE][SUBTYPE_RECIPE_BLACKSMITHING] = BLACKSMITHING; lTypeAndSubtypeToSkill[TYPE_RECIPE][SUBTYPE_RECIPE_COOKING] = COOKING; lTypeAndSubtypeToSkill[TYPE_RECIPE][SUBTYPE_RECIPE_ENCHANTING] = ENCHANTING; lTypeAndSubtypeToSkill[TYPE_RECIPE][SUBTYPE_RECIPE_ENGINEERING] = ENGINEERING; lTypeAndSubtypeToSkill[TYPE_RECIPE][SUBTYPE_RECIPE_LEATHERWORKING] = LEATHERWORKING; lTypeAndSubtypeToSkill[TYPE_RECIPE][SUBTYPE_RECIPE_TAILORING] = TAILORING; lTypeAndSubtypeToSkill[TYPE_RECIPE][SUBTYPE_RECIPE_FIRST_AID] = FIRST_AID; lTypeAndSubtypeToSkill[TYPE_RECIPE][SUBTYPE_RECIPE_FISHING] = FISHING; lTypeAndSubtypeToSkill[TYPE_CONTAINER] = { }; lTypeAndSubtypeToSkill[TYPE_CONTAINER][SUBTYPE_CONTAINER_BAG] = BAG; lTypeAndSubtypeToSkill[TYPE_CONTAINER][SUBTYPE_CONTAINER_AMMO_POUCH] = AMMO_POUCH; lTypeAndSubtypeToSkill[TYPE_CONTAINER][SUBTYPE_CONTAINER_QUIVER] = QUIVER; lTypeAndSubtypeToSkill[TYPE_CONTAINER][SUBTYPE_CONTAINER_SOUL_BAG] = SOUL_BAG; lTypeAndSubtypeToSkill[TYPE_CONTAINER][SUBTYPE_CONTAINER_ENCHANTING_BAG] = ENCHANTING_BAG; local LocationTypes = { }; LocationTypes[INVTYPE_HOLDABLE] = { i = 0, type = TYPE_MISC }; LocationTypes[INVTYPE_CLOAK] = { i = 1, type = TYPE_ARMOR, subtypes = ArmorSubtypes }; LocationTypes[INVTYPE_WEAPON] = { i = 2, type = TYPE_WEAPON, subtypes = WeaponSubtypes }; LocationTypes[INVTYPE_2HWEAPON] = { i = 3, type = TYPE_WEAPON, subtypes = WeaponSubtypes }; LocationTypes[INVTYPE_WEAPONOFFHAND] = { i = 4, type = TYPE_SHIELD, subtypes = ShieldSubtypes }; LocationTypes[INVTYPE_WRIST] = { i = 5, type = TYPE_ARMOR, subtypes = ArmorSubtypes }; LocationTypes[INVTYPE_CHEST] = { i = 6, type = TYPE_ARMOR, subtypes = ArmorSubtypes }; LocationTypes[INVTYPE_LEGS] = { i = 7, type = TYPE_ARMOR, subtypes = ArmorSubtypes }; LocationTypes[INVTYPE_FEET] = { i = 8, type = TYPE_ARMOR, subtypes = ArmorSubtypes }; LocationTypes[INVTYPE_BODY] = { i = 9, type = TYPE_MISC }; LocationTypes[INVTYPE_RANGED] = { i = 10, type = TYPE_WEAPON, subtypes = WeaponSubtypes }; LocationTypes[INVTYPE_WEAPONMAINHAND] = { i = 11, type = TYPE_WEAPON, subtypes = WeaponSubtypes }; LocationTypes[INVTYPE_WAIST] = { i = 12, type = TYPE_ARMOR, subtypes = ArmorSubtypes }; LocationTypes[INVTYPE_HEAD] = { i = 13, type = TYPE_ARMOR, subtypes = ArmorSubtypes }; LocationTypes[GUN] = { i = 14, type = TYPE_WEAPON, subtype = SUBTYPE_WEAPON_GUN }; LocationTypes[INVTYPE_FINGER] = { i = 15, type = TYPE_MISC }; LocationTypes[INVTYPE_HAND] = { i = 16, type = TYPE_ARMOR, subtypes = ArmorSubtypes }; LocationTypes[INVTYPE_SHOULDER] = { i = 17, type = TYPE_ARMOR, subtypes = ArmorSubtypes }; LocationTypes[WAND] = { i = 18, type = TYPE_WEAPON, subtype = SUBTYPE_WEAPON_WAND }; LocationTypes[INVTYPE_TRINKET] = { i = 19, type = TYPE_MISC }; LocationTypes[INVTYPE_TABARD] = { i = 20, type = TYPE_MISC }; LocationTypes[INVTYPE_NECK] = { i = 21, type = TYPE_MISC }; LocationTypes[THROWN] = { i = 22, type = TYPE_WEAPON, subtype = SUBTYPE_WEAPON_THROWN }; LocationTypes[CROSSBOW] = { i = 23, type = TYPE_WEAPON, subtype = SUBTYPE_WEAPON_CROSSBOW }; LocationTypes[INVTYPE_RELIC] = { i = 24, type = TYPE_ARMOR, subtype = SUBTYPE_ARMOR_RELIC }; local INVENTORY_SLOT_LIST = { { name = "HeadSlot" }, { name = "NeckSlot" }, { name = "ShoulderSlot" }, { name = "BackSlot" }, { name = "ChestSlot" }, { name = "ShirtSlot" }, { name = "TabardSlot" }, { name = "WristSlot" }, { name = "HandsSlot" }, { name = "WaistSlot" }, { name = "LegsSlot" }, { name = "FeetSlot" }, { name = "Finger0Slot" }, { name = "Finger1Slot" }, { name = "Trinket0Slot" }, { name = "Trinket1Slot" }, { name = "MainHandSlot" }, { name = "SecondaryHandSlot" }, { name = "RangedSlot" }, }; local ChatMessageTypes = { "CHAT_MSG_SYSTEM", "CHAT_MSG_SAY", "CHAT_MSG_TEXT_EMOTE", "CHAT_MSG_YELL", "CHAT_MSG_WHISPER", "CHAT_MSG_PARTY", "CHAT_MSG_GUILD", "CHAT_MSG_OFFICER", "CHAT_MSG_CHANNEL", "CHAT_MSG_RAID", "CHAT_MSG_LOOT", }; local LOOTLINK_DROPDOWN_LIST = { { name = SORT_NAME, sortType = "name" }, { name = SORT_RARITY, sortType = "rarity" }, { name = SORT_LOCATION, sortType = "location" }, { name = SORT_TYPE, sortType = "type" }, { name = SORT_SUBTYPE, sortType = "subtype" }, { name = SORT_LEVEL, sortType = "level" }, { name = SORT_BINDS, sortType = "binds" }, { name = SORT_UNIQUE, sortType = "unique" }, { name = SORT_ARMOR, sortType = "armor" }, { name = SORT_BLOCK, sortType = "block" }, { name = SORT_MINDAMAGE, sortType = "minDamage" }, { name = SORT_MAXDAMAGE, sortType = "maxDamage" }, { name = SORT_DPS, sortType = "DPS" }, { name = SORT_SPEED, sortType = "speed" }, { name = SORT_SLOTS, sortType = "slots" }, { name = SORT_SKILL, sortType = "skill" }, { name = SORT_SET, sortType = "set" }, }; local LLS_RARITY_LIST = { { name = ANY, value = nil }, { name = UNKNOWN, value = "ff40ffc0", r = 64 / 255, g = 255 / 255, b = 192 / 255 }, { name = POOR, value = "ff9d9d9d", r = 157 / 255, g = 157 / 255, b = 157 / 255 }, { name = COMMON, value = "ffffffff", r = 1, g = 1, b = 1 }, { name = UNCOMMON, value = "ff1eff00", r = 30 / 255, g = 1, b = 0 }, { name = RARE, value = "ff0070dd", r = 0, g = 70 / 255, b = 221 / 255 }, { name = EPIC, value = "ffa335ee", r = 163 / 255, g = 53 / 255, b = 238 / 255 }, { name = LEGENDARY, value = "ffff8000", r = 1, g = 128 / 255, b = 0 }, }; local LLS_BINDS_LIST = { { name = ANY, value = nil }, { name = DOES_NOT, value = BINDS_DOES_NOT_BIND }, { name = ON_EQUIP, value = BINDS_EQUIP }, { name = ON_PICKUP, value = BINDS_PICKUP }, { name = ON_USE, value = BINDS_USE }, }; local LLS_TYPE_LIST = { { name = ANY, value = nil }, { name = ARMOR, value = TYPE_ARMOR }, { name = CONTAINER, value = TYPE_CONTAINER }, { name = OTHER, value = TYPE_MISC }, { name = RECIPE, value = TYPE_RECIPE }, { name = SHIELD, value = TYPE_SHIELD }, { name = WEAPON, value = TYPE_WEAPON }, }; local LLS_SUBTYPE_ARMOR_LIST = { { name = ANY, value = nil }, { name = CLOTH, value = SUBTYPE_ARMOR_CLOTH }, { name = LEATHER, value = SUBTYPE_ARMOR_LEATHER }, { name = MAIL, value = SUBTYPE_ARMOR_MAIL }, { name = PLATE, value = SUBTYPE_ARMOR_PLATE }, { name = INVTYPE_RELIC, value = SUBTYPE_ARMOR_RELIC }, }; local LLS_SUBTYPE_WEAPON_LIST = { { name = ANY, value = nil }, { name = AXE, value = SUBTYPE_WEAPON_AXE }, { name = BOW, value = SUBTYPE_WEAPON_BOW }, { name = CROSSBOW, value = SUBTYPE_WEAPON_CROSSBOW }, { name = DAGGER, value = SUBTYPE_WEAPON_DAGGER }, { name = FISHING_POLE, value = SUBTYPE_WEAPON_FISHING_POLE }, { name = FIST_WEAPON, value = SUBTYPE_WEAPON_FIST_WEAPON }, { name = GUN, value = SUBTYPE_WEAPON_GUN }, { name = MACE, value = SUBTYPE_WEAPON_MACE }, { name = POLEARM, value = SUBTYPE_WEAPON_POLEARM }, { name = STAFF, value = SUBTYPE_WEAPON_STAFF }, { name = SWORD, value = SUBTYPE_WEAPON_SWORD }, { name = THROWN, value = SUBTYPE_WEAPON_THROWN }, { name = WAND, value = SUBTYPE_WEAPON_WAND }, }; local LLS_SUBTYPE_SHIELD_LIST = { { name = ANY, value = nil }, { name = BUCKLER, value = SUBTYPE_SHIELD_BUCKLER }, { name = SHIELD, value = SUBTYPE_SHIELD_SHIELD }, }; local LLS_SUBTYPE_RECIPE_LIST = { { name = ANY, value = nil }, { name = ALCHEMY, value = SUBTYPE_RECIPE_ALCHEMY }, { name = BLACKSMITHING, value = SUBTYPE_RECIPE_BLACKSMITHING }, { name = COOKING, value = SUBTYPE_RECIPE_COOKING }, { name = ENCHANTING, value = SUBTYPE_RECIPE_ENCHANTING }, { name = ENGINEERING, value = SUBTYPE_RECIPE_ENGINEERING }, { name = LEATHERWORKING, value = SUBTYPE_RECIPE_LEATHERWORKING }, { name = TAILORING, value = SUBTYPE_RECIPE_TAILORING }, }; local LLS_SUBTYPE_CONTAINER_LIST = { { name = ANY, value = nil }, { name = BAG, value = SUBTYPE_CONTAINER_BAG }, { name = AMMO_POUCH, value = SUBTYPE_CONTAINER_AMMO_POUCH }, { name = QUIVER, value = SUBTYPE_CONTAINER_QUIVER }, { name = ENCHANTING_BAG, value = SUBTYPE_CONTAINER_ENCHANTING_BAG }, { name = ENGINEERING_BAG, value = SUBTYPE_CONTAINER_ENGINEERING_BAG }, { name = HERB_BAG, value = SUBTYPE_CONTAINER_HERB_BAG }, { name = SOUL_BAG, value = SUBTYPE_CONTAINER_SOUL_BAG }, }; local LLS_LOCATION_LIST = { { name = ANY, }, { name = NONE, }, { name = INVTYPE_CLOAK, }, { name = INVTYPE_CHEST, }, { name = CROSSBOW, }, { name = INVTYPE_FEET, }, { name = INVTYPE_FINGER, }, { name = INVTYPE_HAND, }, { name = INVTYPE_HEAD, }, { name = INVTYPE_HOLDABLE, }, { name = GUN, }, { name = INVTYPE_LEGS, }, { name = INVTYPE_WEAPONMAINHAND, }, { name = INVTYPE_NECK, }, { name = INVTYPE_WEAPONOFFHAND, }, { name = INVTYPE_WEAPON, }, { name = INVTYPE_RANGED, }, { name = INVTYPE_BODY, }, { name = INVTYPE_SHOULDER, }, { name = INVTYPE_TABARD, }, { name = THROWN, }, { name = INVTYPE_TRINKET, }, { name = INVTYPE_2HWEAPON, }, { name = INVTYPE_WAIST, }, { name = WAND, }, { name = INVTYPE_WRIST, }, }; local LLS_EXTRA_LIST = { { name = ALL, }, { name = ONLY, }, { name = NONE, }, }; local LLO_RARITY_LIST = { { name = NONE }, { name = "|cff9d9d9d"..POOR }, { name = "|cffffffff"..COMMON }, { name = "|cff1eff00"..UNCOMMON }, { name = "|cff0070dd"..RARE }, { name = "|cffa335ee"..EPIC }, { name = "|cffff8000"..LEGENDARY }, }; local lMagicCharacters = { }; lMagicCharacters["^"] = 1; lMagicCharacters["$"] = 1; lMagicCharacters["("] = 1; lMagicCharacters[")"] = 1; lMagicCharacters["%"] = 1; lMagicCharacters["."] = 1; lMagicCharacters["["] = 1; lMagicCharacters["]"] = 1; lMagicCharacters["*"] = 1; lMagicCharacters["+"] = 1; lMagicCharacters["-"] = 1; lMagicCharacters["?"] = 1; local lBankBagIDs = { BANK_CONTAINER, 5, 6, 7, 8, 9, 10, }; -- Item types that can't have enchants or "of" stat bonuses local lNoBonuses = { [""] = 2, ["INVTYPE_TRINKET"] = 2, ["INVTYPE_AMMO"] = 2, ["INVTYPE_THROWN"] = 2, ["INVTYPE_BAG"] = 2, ["INVTYPE_TABARD"] = 2, ["INVTYPE_BODY"] = 2, ["INVTYPE_HOLDABLE"] = 1, ["INVTYPE_FINGER"] = 1, ["INVTYPE_WAIST"] = 1, } local lRarityFilter = { ff40ffc0 = -1, ff9d9d9d = 0, ffffffff = 1, ff1eff00 = 2, ff0070dd = 3, ffa335ee = 4, ffff8000 = 5, }; -------------------------------------------------------------------------------------------------- -- Global LootLink variables -------------------------------------------------------------------------------------------------- LOOTLINK_VERSION = 350; -- version 3.50 LOOTLINK_ITEM_HEIGHT = 16; LOOTLINK_ITEMS_SHOWN = 22; UIPanelWindows["LootLinkFrame"] = { area = "left", pushable = 11, whileDead = 1 }; UIPanelWindows["LootLinkSearchFrame"] = { area = "center", pushable = 0, whileDead = 1 }; UIPanelWindows["LootLinkOptionsFrame"] = { area = "center", pushable = 0, whileDead = 1 }; -------------------------------------------------------------------------------------------------- -- Internal functions -------------------------------------------------------------------------------------------------- -- -- Functions with no other functional dependencies -- local function LootLink_MakeIntFromHexString(str) local remain = str; local amount = 0; while( remain ~= "" ) do amount = amount * 16; local byteVal = string.byte(strupper(strsub(remain, 1, 1))); if( byteVal >= string.byte("0") and byteVal <= string.byte("9") ) then amount = amount + (byteVal - string.byte("0")); elseif( byteVal >= string.byte("A") and byteVal <= string.byte("F") ) then amount = amount + 10 + (byteVal - string.byte("A")); end remain = strsub(remain, 2); end return amount; end local function LootLink_GetRGBFromHexColor(hexColor) local red = LootLink_MakeIntFromHexString(strsub(hexColor, 3, 4)) / 255; local green = LootLink_MakeIntFromHexString(strsub(hexColor, 5, 6)) / 255; local blue = LootLink_MakeIntFromHexString(strsub(hexColor, 7, 8)) / 255; return red, green, blue; end local function LootLink_MouseIsOver(frame) local x, y = GetCursorPosition(); if( not frame ) then return nil; end x = x / frame:GetEffectiveScale(); y = y / frame:GetEffectiveScale(); local left = frame:GetLeft(); local right = frame:GetRight(); local top = frame:GetTop(); local bottom = frame:GetBottom(); if( not left or not right or not top or not bottom ) then return nil; end if( (x > left and x < right) and (y > bottom and y < top) ) then return 1; else return nil; end end local function LootLink_GetDataVersion() if( not LootLinkState or not LootLinkState.DataVersion ) then return 0; end return LootLinkState.DataVersion; end local function LootLink_SetDataVersion(version) if( not LootLinkState ) then LootLinkState = { }; end if( not LootLinkState.DataVersion or LootLinkState.DataVersion < version ) then LootLinkState.DataVersion = version; end end local function LootLink_AddServer(name) if( not LootLinkState ) then LootLinkState = { }; end if( not LootLinkState.Servers ) then LootLinkState.Servers = 0; LootLinkState.ServerNamesToIndices = { }; end if( not LootLinkState.ServerNamesToIndices[name] ) then LootLinkState.ServerNamesToIndices[name] = LootLinkState.Servers; LootLinkState.Servers = LootLinkState.Servers + 1; end return LootLinkState.ServerNamesToIndices[name]; end local function LootLink_ConvertServerFormat(item) if( item.servers ) then local i, v; local list; for i, v in item.servers do if( not list ) then list = TEXT(i); else list = list.." "..i; end end item.s = list; item.servers = nil; end end local function LootLink_CheckItemServerRaw(item, serverIndex) if( not item.s ) then return nil; end local server; for server in string.gfind(item.s, "(%d+)") do if( tonumber(server) == serverIndex ) then return 1; end end return nil; end local function LootLink_AddItemServer(item, serverIndex) if( not item.s ) then item.s = TEXT(serverIndex); elseif( not LootLink_CheckItemServerRaw(item, serverIndex) ) then item.s = item.s.." "..serverIndex; end end local function LootLink_EscapeString(string) return string.gsub(string, "\"", "\\\""); end local function LootLink_UnescapeString(string) return string.gsub(string, "\\\"", "\""); end local function LootLink_EscapePattern(string) local result = ""; local remain = string; local char; while( remain ~= "" ) do char = strsub(remain, 1, 1); if( lMagicCharacters[char] ) then result = result.."%"..char; else result = result..char; end remain = strsub(remain, 2); end return result; end local function LootLink_CheckNumeric(string) local remain = string; local hasNumber; local hasPeriod; local char; while( remain ~= "" ) do char = strsub(remain, 1, 1); if( char >= "0" and char <= "9" ) then hasNumber = 1; elseif( char == "." and not hasPeriod ) then hasPeriod = 1; else return nil; end remain = strsub(remain, 2); end return hasNumber; end local function LootLink_NameFromLink(link) local name; if( not link ) then return nil; end for name in string.gfind(link, "|c%x+|Hitem:%d+:%d+:%d+:%d+|h%[(.-)%]|h|r") do return name; end return nil; end function LootLink_MoneyToggle(force) if( lOriginal_GameTooltip_ClearMoney_Temp ) then GameTooltip_ClearMoney = lOriginal_GameTooltip_ClearMoney_Temp; lOriginal_GameTooltip_ClearMoney_Temp = nil; elseif ( not force ) then lOriginal_GameTooltip_ClearMoney_Temp = GameTooltip_ClearMoney; GameTooltip_ClearMoney = LootLink_GameTooltip_ClearMoney_Temp; else DEFAULT_CHAT_FRAME:AddMessage( "LootLink_MoneyToggle just tried to break?" ); end end local function LootLink_MatchType(left, right) local lt = LocationTypes[left]; local _type; local subtype; if( lt ) then local subtypes; -- Check for weapon override of base type if( WeaponSubtypes[right] ) then _type = TYPE_WEAPON; subtypes = WeaponSubtypes; else _type = lt.type; subtypes = lt.subtypes; end if( subtypes ) then subtype = subtypes[right]; else subtype = lt.subtype; end return nil, lt.i, _type, subtype; end return 1, nil, TYPE_MISC, nil; end local function LootLink_HideTooltipMoney() LootLinkTooltipMoneyFrame:SetPoint("LEFT", "LootLinkTooltipTextLeft1", "LEFT", 0, 0); LootLinkTooltipMoneyFrame:Hide(); end local function LootLink_SetTooltipMoney(frame, count, money, stack) if( count and count > 1 ) then money = money * count; frame:AddLine(format(LOOTLINK_SELL_PRICE_N, count), 1.0, 1.0, 1.0); elseif( stack ) then frame:AddLine(LOOTLINK_SELL_PRICE_EACH, 1.0, 1.0, 1.0); else frame:AddLine(LOOTLINK_SELL_PRICE, 1.0, 1.0, 1.0); end local numLines = frame:NumLines(); local moneyFrame = getglobal(frame:GetName().."MoneyFrame"); local newLine = frame:GetName().."TextLeft"..numLines; moneyFrame:SetPoint("LEFT", newLine, "RIGHT", 4, 0); moneyFrame:Show(); MoneyFrame_Update(moneyFrame:GetName(), money); frame:SetMinimumWidth(moneyFrame:GetWidth() + getglobal(newLine):GetWidth() - 10); end local function LL_SearchData(item, tag) if( item.d ) then local s, e; local value; s, e, value = string.find(item.d, tag.."(.-)·") if( value ) then return tonumber(value); end end return nil; end -- -- Functions that are dependent on preceding local functions, ordered appropriately -- local function LootLink_GetName( elem ) if ( type(elem) == "table" ) then return elem[1], elem[2]; else return elem; end end local function LootLink_GetValue( elem, index, link ) local name; if ( type(elem) == "table" ) then index = elem[2]; elem = elem[1]; end if ( link and ItemLinks[elem] and ItemLinks[elem].i ~= link ) then if ( ItemLinks[elem].m ) then for k, v in ItemLinks[elem].m do if v.i == link then index = k; break; end end if ( not index ) then --DEFAULT_CHAT_FRAME:AddMessage( elem.." "..link.." doesn't appear to exist yet?" ); return nil; end end end if ( index and ItemLinks[elem] and ItemLinks[elem].m ) then return ItemLinks[elem].m[index], index; elseif ( ItemLinks[elem] ) then return ItemLinks[elem]; else return nil; end end local function LootLink_InitSizes(serverIndex) local index; local value; lItemLinksSizeTotal = 0; lItemLinksSizeServer = 0; for index, value in ItemLinks do lItemLinksSizeTotal = lItemLinksSizeTotal + 1; if( LootLink_CheckItemServer(value, serverIndex) ) then lItemLinksSizeServer = lItemLinksSizeServer + 1; end end end local function LootLink_GetSize(serverIndex) if( not serverIndex ) then return lItemLinksSizeTotal; end return lItemLinksSizeServer; end local function LootLink_Status() DEFAULT_CHAT_FRAME:AddMessage(format(LOOTLINK_STATUS_HEADER, LOOTLINK_VERSION / 100)); if( LootLinkState ) then DEFAULT_CHAT_FRAME:AddMessage(format(LOOTLINK_DATA_VERSION, LootLink_GetSize(), LootLink_GetSize(lServerIndex), lServer, LootLink_GetDataVersion() / 100)); if( LootLinkState.HideInfo ) then DEFAULT_CHAT_FRAME:AddMessage(LOOTLINK_INFO_HIDDEN); else DEFAULT_CHAT_FRAME:AddMessage(LOOTLINK_INFO_SHOWN); end if( LootLinkState.LightMode ) then DEFAULT_CHAT_FRAME:AddMessage(LOOTLINK_LIGHT_MODE); else DEFAULT_CHAT_FRAME:AddMessage(LOOTLINK_FULL_MODE); end else DEFAULT_CHAT_FRAME:AddMessage(LOOTLINK_INFO_SHOWN); end if( lScanAuction ) then DEFAULT_CHAT_FRAME:AddMessage(LOOTLINK_SCHEDULED_AUCTION_SCAN); end end local function LootLink_GetHyperlink(name, index) local itemLink = LootLink_GetValue( name, index ); if( itemLink and itemLink.i --[[and LootLink_CheckItemServer(itemLink, lServerIndex)]] ) then -- Remove instance-specific data that we captured from the link we return local item = string.gsub(itemLink.i, "(%d+):(%d+):(%d+):(%d+)", "%1:%2:%3:0"); return "item:"..item; end return nil; end local function LootLink_GetLink(name, index) local itemLink = LootLink_GetValue(name, index); if( itemLink and itemLink.c and itemLink.i --[[and (IsAltKeyDown() or LootLink_CheckItemServer(itemLink, lServerIndex))]] ) then local link = "|c"..itemLink.c.."|H"..LootLink_GetHyperlink(name, index).."|h["..name.."]|h|r"; return link; end return nil; end local function LootLink_GetSet(set) if ( not LootLinkState.sets ) then LootLinkState.sets = {} end local sets = LootLinkState.sets; for k, v in sets do if ( v == set ) then return k; end end tinsert(sets, set); return getn(sets); end function LootLink_BuildSearchData(name, value, alternate) local state, itemLink, loop, index, field, left, right, iStart, iEnd, val1, val2, val3, foundRequires, _type, subtype = STATE_NAME, LootLink_GetHyperlink(name, alternate); if( not itemLink ) then return nil; end -- This is the only place the tooltip hyperlink is set directly, therefore -- this should only be called when we know that the tooltip will be valid -- Protect money frame while we set hidden tooltip LootLink_MoneyToggle(); LLHiddenTooltip:SetOwner(WorldFrame,"ANCHOR_NONE"); LLHiddenTooltip:SetHyperlink(itemLink); LootLink_MoneyToggle(1); -- Double check that the data is good local realname, link, quality, level, _, _, _, count = GetItemInfo(itemLink); if ( not realname ) then LootLink_ScheduleItem(name,LootLink_GetValue(name,alternate).i); return nil; else local tooltipname = LLHiddenTooltipTextLeft1:GetText(); if ( realname ~= tooltipname ) then -- if this returns, we're in trouble. return nil; end end local savedD = value.d; local savedT = value.t; value.d = ""; if( not LootLinkState.LightMode ) then value.t = ""; end value.c = string.gsub( ITEM_QUALITY_COLORS[quality].hex, "|c(.+)", "%1" ); for index = 1, 30, 1 do field = getglobal("LLHiddenTooltipTextLeft"..index); if( field and field:IsShown() ) then left = field:GetText(); else left = nil; end field = getglobal("LLHiddenTooltipTextRight"..index); if( field and field:IsShown() ) then right = field:GetText(); else right = nil; end if( left ) then if( not LootLinkState.LightMode ) then -- cull set data to show 0/# and don't save durability left = string.gsub( left, "(.+ %()%d+(%/%d%))", "%10%2" ); if ( string.sub( left, 1, 10 ) ~= "Durability" ) then value.t = value.t..left.."·"; end end else left = ""; end if( right ) then if( not LootLinkState.LightMode ) then value.t = value.t..right.."·"; end else right = ""; end loop = 1; while( loop ) do if( state == STATE_NAME ) then state = STATE_BOUND; loop = nil; elseif( state == STATE_BOUND ) then iStart, iEnd, val1 = string.find(left, "Binds when (.+)"); if( val1 ) then if( val1 == "equipped" ) then value.d = value.d.."bi"..BINDS_EQUIP.."·"; elseif( val1 == "picked up" ) then value.d = value.d.."bi"..BINDS_PICKUP.."·"; elseif( val1 == "used" ) then value.d = value.d.."bi"..BINDS_USED.."·"; end loop = nil; else value.d = value.d.."bi"..BINDS_DOES_NOT_BIND.."·"; end state = STATE_UNIQUE; elseif( state == STATE_UNIQUE ) then if( string.find(left, "Unique") ) then value.d = value.d.."un1·"; loop = nil; end state = STATE_LOCATION; elseif( state == STATE_LOCATION ) then local location; loop, location, _type, subtype = LootLink_MatchType(left, right); if( location ) then value.d = value.d.."lo"..location.."·"; end if( _type == TYPE_WEAPON ) then state = STATE_DAMAGE; elseif( _type == TYPE_ARMOR or _type == TYPE_SHIELD ) then state = STATE_ARMOR; else state = STATE_CONTAINER; end elseif( state == STATE_DAMAGE ) then iStart, iEnd, val1, val2 = string.find(left, "(%d+) %- (%d+) Damage"); if( val1 and val2 ) then value.d = value.d.."mi"..val1.."·ma"..val2.."·"; end iStart, iEnd, val1 = string.find(right, "Speed (.+)"); if( val1 ) then value.d = value.d.."sp"..val1.."·"; end state = STATE_DPS; loop = nil; elseif( state == STATE_DPS ) then iStart, iEnd, val1 = string.find(left, "%((.+) damage per second%)"); if( val1 ) then value.d = value.d.."dp"..val1.."·"; end state = STATE_REQUIRES; loop = nil; elseif( state == STATE_ARMOR ) then iStart, iEnd, val1 = string.find(left, "(%d+) Armor"); if( val1 ) then value.d = value.d.."ar"..val1.."·"; end if( _type == TYPE_SHIELD ) then state = STATE_BLOCK; else state = STATE_REQUIRES; end loop = nil; elseif( state == STATE_BLOCK ) then iStart, iEnd, val1 = string.find(left, "(%d+) Block"); if( val1 ) then value.d = value.d.."bl"..val1.."·"; end state = STATE_REQUIRES; loop = nil; elseif( state == STATE_CONTAINER ) then iStart, iEnd, val1, val2 = string.find(left, "(%d+) Slot (.+)"); if( val1 ) then _type = TYPE_CONTAINER; subtype = ContainerSubtypes[val2]; value.d = value.d.."sl"..val1.."·"; loop = nil; end state = STATE_REQUIRES; elseif( state == STATE_REQUIRES ) then iStart, iEnd, val1 = string.find(left, "Requires (.+)"); if( val1 ) then iStart, iEnd, val2 = string.find(val1, "Level (%d+)"); if( val2 ) then value.d = value.d.."le"..val2.."·"; else iStart, iEnd, val2, val3 = string.find(val1, "(.+) %((%d+)%)"); if( val2 and val3 ) then val1 = RecipeSubtypes[val2]; if( val1 and _type == TYPE_MISC ) then _type = TYPE_RECIPE; subtype = val1; value.d = value.d.."sk"..val3.."·"; end end end else state = STATE_SPACE; end loop = nil; elseif( state == STATE_SPACE ) then if ( left == tooltipSpace ) then state = STATE_SET; end loop = nil; elseif( state == STATE_SET ) then if ( left ~= "" ) then iStart, iEnd, val1, val2 = string.find(left, "(.+) %(%d+%/(%d+)%)"); if( val2 ) then local set = LootLink_GetSet(val1); value.d = value.d.."se"..set.."·"; state = STATE_FINISH; end end loop = nil; elseif( state == STATE_FINISH ) then loop = nil; end end end if( _type ) then value.d = value.d.."ty".._type.."·"; end if( subtype ) then value.d = value.d.."su"..subtype.."·"; end if ( realname and name ~= realname ) then if ( not ItemLinks[realname] ) then ItemLinks[realname] = {}; ItemLinks[realname] = value; else if ( ItemLinks[realname].i ~= value.i and ItemLinks[realname].m ) then local found; for k, v in ItemLinks[realname].m do if v.i == value.i then found = 1; break; end end if ( not found ) then tinsert( ItemLinks[realname].m, value ); end end end end if ( savedD ~= value.d or savedT ~= value.t ) then return 2; end return 1; end local function BuildUsabilityData(data) local nSkills; local iSkill; local HeaderData = { }; local name, header, isExpanded, rank; local Collapse = { }; local nToCollapse = 0; local iCollapse; data.class = UnitClass("player"); data.race = UnitRace("player"); data.level = UnitLevel("player"); data.skills = { }; -- We need to expand all of the skills, but first want to save off their state nSkills = GetNumSkillLines(); for iSkill = 1, nSkills do local name, header, isExpanded, rank = GetSkillLineInfo(iSkill); if( header and not isExpanded ) then -- Since we don't know the final index for this item yet, we'll store it by name HeaderData[name] = 0; end end -- Now expand everything and save off our known skills ExpandSkillHeader(0); nSkills = GetNumSkillLines() for iSkill = 1, nSkills do local name, header, isExpanded, rank = GetSkillLineInfo(iSkill); if( not header ) then data.skills[name] = rank; elseif( HeaderData[name] ) then -- We now know the final index for this header item HeaderData[name] = iSkill; end end -- Finally, return the skills page to its original state for name, iSkill in HeaderData do Collapse[nToCollapse + 1] = iSkill; nToCollapse = nToCollapse + 1; end if( nToCollapse > 0 ) then table.sort(Collapse); for iCollapse = nToCollapse, 1, -1 do CollapseSkillHeader(Collapse[iCollapse]); end end end local function LootLink_GetSkillRank(ud, _type, subtype, location) local entry = lTypeAndSubtypeToSkill[_type][subtype]; if( type(entry) == "table" ) then -- Two-handed vs. One-handed weapon if( location ) then if( location == 3 ) then return ud.skills[entry[1]]; else return ud.skills[entry[0]]; end else return nil; end else -- Everything else return ud.skills[entry]; end end local function LootLink_SetHyperlink(tooltip, name, link, index) -- If the link isn't in the local cache, it may not be valid if ( not link ) then return nil; end local value = ItemLinks[name]; if ( index and value.m ) then value = value.m[index]; end if( not GetItemInfo(link) ) then -- To avoid disconnects, we'll create our own lame tooltip for these if( value ) then local extraSkip = 0; local lines = 1; local usabilityData; -- Name, in rarity color tooltip:SetText("|c"..value.c..name.."|r"); -- Binds on equip, binds on pickup local binds = LL_SearchData(value, "bi"); if( binds == BINDS_EQUIP ) then tooltip:AddLine(ITEM_BIND_ON_EQUIP, 1, 1, 1); lines = lines + 1; elseif( binds == BINDS_PICKUP ) then tooltip:AddLine(ITEM_BIND_ON_PICKUP, 1, 1, 1); lines = lines + 1; elseif( binds == BINDS_USED ) then tooltip:AddLine(ITEM_BIND_ON_USE, 1, 1, 1); lines = lines + 1; end -- Unique? local unique = LL_SearchData(value, "un"); if( unique ) then tooltip:AddLine("Unique", 1, 1, 1); lines = lines + 1; end local _type = LL_SearchData(value, "ty"); local subtype = LL_SearchData(value, "su"); -- Equip location and type/subtype local location = LL_SearchData(value, "lo"); if( location ) then local subtypes; local name; for i, v in LocationTypes do if( v.i == location ) then name = i; subtypes = v.subtypes; break; end end if( name ) then tooltip:AddLine(name, 1, 1, 1); lines = lines + 1; if( subtype ) then if( _type == TYPE_WEAPON ) then subtypes = WeaponSubtypes; end if( subtypes ) then for i, v in subtypes do if( v == subtype ) then if( i == name ) then local line = getglobal(tooltip:GetName().."TextLeft"..lines); if( not usabilityData ) then usabilityData = { }; BuildUsabilityData(usabilityData); end if( not LootLink_GetSkillRank(usabilityData, _type, subtype, location) ) then line:SetTextColor(1, 0, 0); end else local line = getglobal(tooltip:GetName().."TextRight"..lines); line:SetText(i); if( not usabilityData ) then usabilityData = { }; BuildUsabilityData(usabilityData); end if( LootLink_GetSkillRank(usabilityData, _type, subtype, location) ) then line:SetTextColor(1, 1, 1); else line:SetTextColor(1, 0, 0); end line:Show(); extraSkip = extraSkip + 1; break; end end end end end end end -- Now do type specific data if( _type == TYPE_ARMOR ) then local armor = LL_SearchData(value, "ar"); if( armor ) then tooltip:AddLine(armor.." Armor", 1, 1, 1); lines = lines + 1; end elseif( _type == TYPE_WEAPON ) then local min = LL_SearchData(value, "mi"); local max = LL_SearchData(value, "ma"); local speed = LL_SearchData(value, "sp"); local dps = LL_SearchData(value, "dp"); if( min and max ) then tooltip:AddLine(min.." - "..max.." Damage", 1, 1, 1); lines = lines + 1; if( speed ) then local line = getglobal(tooltip:GetName().."TextRight"..lines); line:SetText(format("Speed %.2f", tonumber(speed))); line:SetTextColor(1, 1, 1); line:Show(); extraSkip = extraSkip + 1; end end if( dps ) then tooltip:AddLine("("..dps.." damage per second)", 1, 1, 1); lines = lines + 1; end elseif( _type == TYPE_SHIELD ) then local armor = LL_SearchData(value, "ar"); local block = LL_SearchData(value, "bl"); if( armor ) then tooltip:AddLine(armor.." Armor", 1, 1, 1); lines = lines + 1; end if( block ) then tooltip:AddLine(block.." Block", 1, 1, 1); lines = lines + 1; end elseif( _type == TYPE_RECIPE ) then local skill = LL_SearchData(value, "sk"); if( skill and subtype ) then for i, v in RecipeSubtypes do if( v == subtype ) then if( not usabilityData ) then usabilityData = { }; BuildUsabilityData(usabilityData); end local rank = LootLink_GetSkillRank(usabilityData, _type, subtype, location); if( not rank or rank < skill ) then tooltip:AddLine("Requires "..i.." ("..skill..")", 1, 0, 0); else tooltip:AddLine("Requires "..i.." ("..skill..")", 1, 1, 1); end lines = lines + 1; break; end end end elseif( _type == TYPE_CONTAINER ) then local slots = LL_SearchData(value, "sl"); if( slots ) then local subtype = LL_SearchData(value, "su") or 0; local bag = lTypeAndSubtypeToSkill[_type][subtype]; -- repurposed skill table, fix later? tooltip:AddLine(slots.." Slot "..bag, 1, 1, 1); lines = lines + 1; end end local level = LL_SearchData(value, "le"); -- Now add any extra text data that we have if( value.t ) then local skip = lines + extraSkip; local piece; for piece in string.gfind(value.t, "(.-)·") do if( lines < 29 ) then if( skip == 0 ) then if( string.find(piece, "Requires Level .*") ) then if( level and tonumber(level) > UnitLevel("player") ) then tooltip:AddLine(piece, 1, 0, 0, 1, 1); else tooltip:AddLine(piece, 1, 1, 1, 1, 1); end elseif( string.find(piece, ":") ) then if( string.find(piece, "^Class") ) then if( string.find(piece, "^Class.*"..UnitClass("player")) ) then tooltip:AddLine(piece, 1, 1, 1, 1, 1); else tooltip:AddLine(piece, 1, 0, 0, 1, 1); end else tooltip:AddLine(piece, 0, 1, 0, 1, 1); end elseif( string.find(piece, "\"") or string.find(piece, "%(%d+/%d+%)") ) then tooltip:AddLine(piece, 1, 0.8235, 0, 1, 1); elseif( string.find(piece, "^ ") ) then tooltip:AddLine(piece, 0.5, 0.5, 0.5, 1, 1); else tooltip:AddLine(piece, 1, 1, 1, 1, 1); end lines = lines + 1; else skip = skip - 1; end end end end -- And, after all that, let the user know that we faked this tooltip if( lines < 30 ) then tooltip:AddLine("|cff40ffc0<"..LOOTLINK_TOOLTIP_GENERATED..">|r"); lines = lines + 1; end if( lines < 30 and not LootLink_CheckItemServer(value, lServerIndex) ) then tooltip:AddLine("|cff40ffc0<"..LOOTLINK_TOOLTIP_SERVER..">|r"); lines = lines + 1; end -- Finally, show the tooltip, which adjusts its size tooltip:Show(); end else -- Get the actual tooltip from the cache tooltip:SetHyperlink(link); -- After setting the tooltip, parse its data LootLink_ScheduleItem(name, value.i); --LootLink_BuildSearchData(name, value, index); if( not LootLink_ValidateItem(name, link) ) then tooltip:AddLine("|cff40ffc0<"..LOOTLINK_TOOLTIP_BAD_ITEM..">|r"); elseif( not LootLink_CheckItemServerRaw(value, lServerIndex) ) then tooltip:AddLine("|cff40ffc0<"..LOOTLINK_TOOLTIP_SERVER..">|r"); --LootLink_AddItemServer(value, lServerIndex); --lItemLinksSizeServer = lItemLinksSizeServer + 1; --return 1; end end end local function LootLink_SetTitle() local lootLinkTitle = getglobal("LootLinkTitleText"); local total = LootLink_GetSize(lServerIndex); local size; if( not DisplayIndices ) then size = total; else size = DisplayIndices.onePastEnd - 1; end if( size < total ) then if( size == 1 ) then lootLinkTitle:SetText(TEXT(LOOTLINK_TITLE_FORMAT_PARTIAL_SINGULAR)); else lootLinkTitle:SetText(format(TEXT(LOOTLINK_TITLE_FORMAT_PARTIAL_PLURAL), size)); end else if( size == 1 ) then lootLinkTitle:SetText(TEXT(LOOTLINK_TITLE_FORMAT_SINGULAR)); else lootLinkTitle:SetText(format(TEXT(LOOTLINK_TITLE_FORMAT_PLURAL), size)); end end end local function LootLink_MatchesSearch(name, value, ud) if( not value ) then return nil; end if( LootLinkState ) then if ( LootLinkState.spoof and name ) then if ( not LootLink_ValidateItem( name, value.i ) ) then return nil; end end if ( LootLinkState.server ) then if ( not LootLink_CheckItemServer(value, lServerIndex) ) then return nil; end end if ( LootLinkState.rarityfilter ) then if ( value.c and lRarityFilter[value.c] < LootLinkState.rarityfilter ) then return nil; end end if ( LootLinkState.noname ) then if ( ItemLinks[name] and ItemLinks[name].i and ItemLinks[name].i ~= value.i ) then return nil; end else if ( LootLinkState.novariants ) then if ( string.gsub(value.i, "%d+:%d+:(%d+):%d+", "%1") ~="0" and ItemLinks[name] and ItemLinks[name].i and ItemLinks[name].i ~= value.i ) then return nil; end end if ( LootLinkState.enchants ) then if ( string.gsub(value.i, "%d+:(%d+):%d+:%d+", "%1") ~= "0" ) then return nil; end end end end if( not LootLinkFrame.SearchParams or not name ) then return 1; end if( value.d ) then local sp = LootLinkFrame.SearchParams; if( sp.server ) then if ( not LootLink_CheckItemServer(value, lServerIndex) ) then return nil; end end if( sp.spoof ) then if ( LootLink_ValidateItem( name, value.i ) ) then return nil; end end if( sp.text ) then if( not value.t ) then return nil; end if( not string.find(string.lower(value.t), string.lower(sp.text), 1, sp.plain) ) then return nil; end end if( sp.name ) then if( not string.find(string.lower(name), string.lower(sp.name), 1, sp.plain) ) then return nil; end end if( sp.rarity ) then if( LLS_RARITY_LIST[sp.rarity].value ~= value.c ) then return nil; end end if( sp.binds ) then if( LLS_BINDS_LIST[sp.binds].value ~= LL_SearchData(value, "bi") ) then return nil; end end if( sp.unique ) then if( sp.unique ~= LL_SearchData(value, "un") ) then return nil; end end if( sp.usable ) then local _type = LL_SearchData(value, "ty"); local subtype = LL_SearchData(value, "su"); local level = LL_SearchData(value, "le"); local skill = LL_SearchData(value, "sk"); -- Check for the required skill if( _type ) then if( subtype ) then local rank = LootLink_GetSkillRank(ud, _type, subtype, LL_SearchData(value, "lo")); if( not rank or (skill and skill > rank) ) then return nil; end end end -- Check for the required class if( value.t and string.find(value.t, "·Class") and not string.find(value.t, "·Class.*"..UnitClass("player")) ) then return nil; end -- Check level requirement if( level and level > ud.level ) then return nil; end end if( sp.location ) then local location = LL_SearchData(value, "lo"); if( sp.location == 2 ) then if( location ) then return nil; end elseif( sp.location ~= 1 ) then if( LocationTypes[LLS_LOCATION_LIST[sp.location].name].i ~= location ) then return nil; end end end if( sp.minLevel ) then local level = LL_SearchData(value, "le"); if( not level or level < sp.minLevel ) then return nil; end end if( sp.maxLevel ) then local level = LL_SearchData(value, "le"); if( level and level > sp.maxLevel ) then return nil; end end if( sp.set ) then local set = LL_SearchData(value, "se"); if( not set ) then return nil; end end if( sp.type ) then local _type = LLS_TYPE_LIST[sp.type].value; if( _type ~= LL_SearchData(value, "ty") ) then return nil; end if( sp.subtype ) then local subtype; if( _type == TYPE_ARMOR ) then subtype = LLS_SUBTYPE_ARMOR_LIST[sp.subtype].value; elseif( _type == TYPE_SHIELD ) then subtype = LLS_SUBTYPE_SHIELD_LIST[sp.subtype].value; elseif( _type == TYPE_WEAPON ) then subtype = LLS_SUBTYPE_WEAPON_LIST[sp.subtype].value; elseif( _type == TYPE_RECIPE ) then subtype = LLS_SUBTYPE_RECIPE_LIST[sp.subtype].value; elseif( _type == TYPE_CONTAINER ) then subtype = LLS_SUBTYPE_CONTAINER_LIST[sp.subtype].value; end if( subtype and subtype ~= LL_SearchData(value, "su") ) then return nil; end end if( _type == TYPE_WEAPON ) then if( sp.minMinDamage ) then local damage = LL_SearchData(value, "mi"); if( not damage or damage < sp.minMinDamage ) then return nil; end end if( sp.minMaxDamage ) then local damage = LL_SearchData(value, "ma"); if( not damage or damage < sp.minMaxDamage ) then return nil; end end if( sp.maxSpeed ) then local speed = LL_SearchData(value, "sp"); if( not speed or speed > sp.maxSpeed ) then return nil; end end if( sp.minDPS ) then local DPS = LL_SearchData(value, "dp"); if( not DPS or DPS < sp.minDPS ) then return nil; end end elseif( _type == TYPE_ARMOR or _type == TYPE_SHIELD ) then if( sp.minArmor ) then local armor = LL_SearchData(value, "ar"); if( not armor or armor < sp.minArmor ) then return nil; end end if( _type == TYPE_SHIELD ) then if( sp.minBlock ) then local block = LL_SearchData(value, "bl"); if( not block or block < sp.minBlock ) then return nil; end end end elseif( _type == TYPE_CONTAINER ) then if( sp.minSlots ) then local slots = LL_SearchData(value, "sl"); if( not slots or slots < sp.minSlots ) then return nil; end end elseif( _type == TYPE_RECIPE ) then if( sp.minSkill ) then local skill = LL_SearchData(value, "sk"); if( not skill or skill < sp.minSkill ) then return nil; end end if( sp.maxSkill ) then local skill = LL_SearchData(value, "sk"); if( not skill or skill > sp.maxSkill ) then return nil; end end end end else return nil; end return 1; end local function LootLink_NameComparison(elem1, elem2) local v1, i1 = elem1[1], elem1[3]; local v2, i2 = elem2[1], elem2[3]; if ( v1 == v2 ) then if ( i1 and i2 ) then return i1 < i2; elseif ( i1 ) then return false; elseif ( i2 ) then return true; end end return v1 < v2; end local function LootLink_ColorComparison(elem1, elem2) local color1; local color2; local v1 = elem1[2]; local v2 = elem2[2]; if( v1 and v1.c ) then color1 = v1.c; else color1 = "ffffffff"; end if( v2 and v2.c ) then color2 = v2.c; else color2 = "ffffffff"; end if( color1 == color2 ) then return LootLink_NameComparison( elem1, elem2 ); end if( not lColorSortTable[color1] and not lColorSortTable[color2] ) then return color1 < color2; end if( lColorSortTable[color1] ) then if( lColorSortTable[color2] ) then return lColorSortTable[color1] < lColorSortTable[color2]; end return nil; end return 1; end local function LootLink_GenericComparison(elem1, elem2, v1, v2) if( v1 == v2 ) then return LootLink_NameComparison( elem1, elem2 ); end if( not v1 ) then return 1; end if( not v2 ) then return nil; end if( tonumber(v1) and tonumber(v2) ) then return tonumber(v1) < tonumber(v2); end return v1 < v2; end local function LootLink_BindsComparison(elem1, elem2) local v1, v2; local value1 = elem1[2]; local value2 = elem2[2]; if( value1 and value1.d ) then v1 = LL_SearchData(ItemLinks[elem1], "bi"); end if( value2 and value2.d ) then v2 = LL_SearchData(ItemLinks[elem2], "bi"); end return LootLink_GenericComparison(elem1, elem2, v1, v2); end local function LootLink_UniqueComparison(elem1, elem2) local v1, v2; local value1 = elem1[2]; local value2 = elem2[2]; if( value1 and value1.d ) then v1 = LL_SearchData(value1, "un"); end if( value2 and value2.d ) then v2 = LL_SearchData(value2, "un"); end return LootLink_GenericComparison(elem1, elem2, v1, v2); end local function LootLink_LocationComparison(elem1, elem2) local v1, v2; local value1 = elem1[2]; local value2 = elem2[2]; if( value1 and value1.d ) then v1 = LL_SearchData(value1, "lo"); end if( value2 and value2.d ) then v2 = LL_SearchData(value2, "lo"); end return LootLink_GenericComparison(elem1, elem2, v1, v2); end local function LootLink_TypeComparison(elem1, elem2) local v1, v2; local value1 = elem1[2]; local value2 = elem2[2]; if( value1 and value1.d ) then v1 = LL_SearchData(value1, "ty"); end if( value2 and value2.d ) then v2 = LL_SearchData(value2, "ty"); end return LootLink_GenericComparison(elem1, elem2, v1, v2); end local function LootLink_SubtypeComparison(elem1, elem2) local v1, v2; local value1 = elem1[2]; local value2 = elem2[2]; if( value1 and value1.d ) then v1 = LL_SearchData(value1, "su"); end if( value2 and value2.d ) then v2 = LL_SearchData(value2, "su"); end return LootLink_GenericComparison(elem1, elem2, v1, v2); end local function LootLink_MinDamageComparison(elem1, elem2) local v1, v2; local value1 = elem1[2]; local value2 = elem2[2]; if( value1 and value1.d ) then v1 = LL_SearchData(value1, "mi"); end if( value2 and value2.d ) then v2 = LL_SearchData(value2, "mi"); end return LootLink_GenericComparison(elem1, elem2, v1, v2); end local function LootLink_MaxDamageComparison(elem1, elem2) local v1, v2; local value1 = elem1[2]; local value2 = elem2[2]; if( value1 and value1.d ) then v1 = LL_SearchData(value1, "ma"); end if( value2 and value2.d ) then v2 = LL_SearchData(value2, "ma"); end return LootLink_GenericComparison(elem1, elem2, v1, v2); end local function LootLink_SpeedComparison(elem1, elem2) local v1, v2; local value1 = elem1[2]; local value2 = elem2[2]; if( value1 and value1.d ) then v1 = LL_SearchData(value1, "sp"); end if( value2 and value2.d ) then v2 = LL_SearchData(value2, "sp"); end return LootLink_GenericComparison(elem1, elem2, v1, v2); end local function LootLink_DPSComparison(elem1, elem2) local v1, v2; local value1 = elem1[2]; local value2 = elem2[2]; if( value1 and value1.d ) then v1 = LL_SearchData(value1, "dp"); end if( value2 and value2.d ) then v2 = LL_SearchData(value2, "dp"); end return LootLink_GenericComparison(elem1, elem2, v1, v2); end local function LootLink_ArmorComparison(elem1, elem2) local v1, v2; local value1 = elem1[2]; local value2 = elem2[2]; if( value1 and value1.d ) then v1 = LL_SearchData(value1, "ar"); end if( value2 and value2.d ) then v2 = LL_SearchData(value2, "ar"); end return LootLink_GenericComparison(elem1, elem2, v1, v2); end local function LootLink_BlockComparison(elem1, elem2) local v1, v2; local value1 = elem1[2]; local value2 = elem2[2]; if( value1 and value1.d ) then v1 = LL_SearchData(value1, "bl"); end if( value2 and value2.d ) then v2 = LL_SearchData(value2, "bl"); end return LootLink_GenericComparison(elem1, elem2, v1, v2); end local function LootLink_SlotsComparison(elem1, elem2) local v1, v2; local value1 = elem1[2]; local value2 = elem2[2]; if( value1 and value1.d ) then v1 = LL_SearchData(value1, "sl"); end if( value2 and value2.d ) then v2 = LL_SearchData(value2, "sl"); end return LootLink_GenericComparison(elem1, elem2, v1, v2); end local function LootLink_SkillComparison(elem1, elem2) local v1, v2; local value1 = elem1[2]; local value2 = elem2[2]; if( value1 and value1.d ) then v1 = LL_SearchData(value1, "sk"); end if( value2 and value2.d ) then v2 = LL_SearchData(value2, "sk"); end return LootLink_GenericComparison(elem1, elem2, v1, v2); end local function LootLink_LevelComparison(elem1, elem2) local v1, v2; local value1 = elem1[2]; local value2 = elem2[2]; if( value1 and value1.d ) then v1 = LL_SearchData(value1, "le"); end if( value2 and value2.d ) then v2 = LL_SearchData(value2, "le"); end return LootLink_GenericComparison(elem1, elem2, v1, v2); end -- Ugly, but functional local function LootLink_SetComparison(elem1, elem2) local v1, v2; local value1 = elem1[2]; local value2 = elem2[2]; local sets = LootLinkState.sets; if ( not sets ) then return LootLink_NameComparison(elem1, elem2); end if( value1 and value1.d ) then v1 = LL_SearchData(value1, "se"); v1 = sets[v1]; end if( value2 and value2.d ) then v2 = LL_SearchData(value2, "se"); v2 = sets[v2]; end if ( not v1 and not v2 ) then return LootLink_NameComparison(elem1, elem2); elseif ( not v1 ) then return nil; elseif ( not v2 ) then return 1; else if ( v1 == v2 ) then return LootLink_NameComparison(elem1, elem2); else return v1 < v2; end end end local function LootLink_Sort() if( LOOTLINK_DROPDOWN_LIST[UIDropDownMenu_GetSelectedID(LootLinkFrameDropDown)].sortType ) then local sortType = LOOTLINK_DROPDOWN_LIST[UIDropDownMenu_GetSelectedID(LootLinkFrameDropDown)].sortType; if( sortType == "name" ) then table.sort(DisplayIndices, LootLink_NameComparison); elseif( sortType == "rarity" ) then table.sort(DisplayIndices, LootLink_ColorComparison); elseif( sortType == "binds" ) then table.sort(DisplayIndices, LootLink_BindsComparison); elseif( sortType == "unique" ) then table.sort(DisplayIndices, LootLink_UniqueComparison); elseif( sortType == "location" ) then table.sort(DisplayIndices, LootLink_LocationComparison); elseif( sortType == "type" ) then table.sort(DisplayIndices, LootLink_TypeComparison); elseif( sortType == "subtype" ) then table.sort(DisplayIndices, LootLink_SubtypeComparison); elseif( sortType == "minDamage" ) then table.sort(DisplayIndices, LootLink_MinDamageComparison); elseif( sortType == "maxDamage" ) then table.sort(DisplayIndices, LootLink_MaxDamageComparison); elseif( sortType == "speed" ) then table.sort(DisplayIndices, LootLink_SpeedComparison); elseif( sortType == "DPS" ) then table.sort(DisplayIndices, LootLink_DPSComparison); elseif( sortType == "armor" ) then table.sort(DisplayIndices, LootLink_ArmorComparison); elseif( sortType == "block" ) then table.sort(DisplayIndices, LootLink_BlockComparison); elseif( sortType == "slots" ) then table.sort(DisplayIndices, LootLink_SlotsComparison); elseif( sortType == "skill" ) then table.sort(DisplayIndices, LootLink_SkillComparison); elseif( sortType == "level" ) then table.sort(DisplayIndices, LootLink_LevelComparison); elseif( sortType == "set" ) then table.sort(DisplayIndices, LootLink_SetComparison); end end end local function LootLink_BuildDisplayIndices() local iNew = 1; local index; local value; local UsabilityData; if( LootLinkFrame.SearchParams and LootLinkFrame.SearchParams.usable ) then UsabilityData = { }; BuildUsabilityData(UsabilityData); end DisplayIndices = { }; for index, value in ItemLinks do if( LootLink_MatchesSearch(index, value, UsabilityData) ) then DisplayIndices[iNew] = { index, value, nil }; iNew = iNew + 1; end if( value.m ) then for i, altvalue in value.m do if( LootLink_MatchesSearch(index, altvalue, UsabilityData) ) then DisplayIndices[iNew] = { index, altvalue, i }; iNew = iNew + 1; end end end end DisplayIndices.onePastEnd = iNew; LootLink_SetDataVersion(100); -- version 1.00 LootLink_Sort(); LootLink_SetTitle(); LootLinkQuickSearch_Clear(); end local function LootLink_Reset() ItemLinks = { }; LootLink_SetDataVersion(110); -- version 1.10 LootLink_InitSizes(lServerIndex); if( DisplayIndices ) then LootLink_BuildDisplayIndices(); LootLink_Update(); end end local function LootLink_MakeHome() local index; local value; LootLink_SetDataVersion(110); -- version 1.10 for index, value in ItemLinks do if( not value._ ) then -- If this item predates multiple server support, mark is as seen on this server LootLink_AddItemServer(value, lServerIndex); else -- Otherwise just wipe the flag since it only applies to pre-1.10 data value._ = nil; end end LootLink_InitSizes(lServerIndex); end local function LootLink_LightMode() local index; local value; for index, value in ItemLinks do value.t = nil; end LootLinkState.LightMode = 1; end -- -- Uncategorized functions; will sort later -- local function LootLinkFrameDropDown_Initialize() local info; for i = 1, getn(LOOTLINK_DROPDOWN_LIST), 1 do info = { }; info.text = LOOTLINK_DROPDOWN_LIST[i].name; info.func = LootLinkFrameDropDownButton_OnClick; UIDropDownMenu_AddButton(info); end end local function LLS_RarityDropDown_Initialize() local info; for i = 1, getn(LLS_RARITY_LIST), 1 do info = { }; info.text = LLS_RARITY_LIST[i].name; info.func = LLS_RarityDropDown_OnClick; if( LLS_RARITY_LIST[i].value ) then info.textR = LLS_RARITY_LIST[i].r; info.textG = LLS_RARITY_LIST[i].g; info.textB = LLS_RARITY_LIST[i].b; end UIDropDownMenu_AddButton(info); end end local function LLO_RarityDropDown_Initialize() local info; for i = 1, getn(LLO_RARITY_LIST), 1 do info = { }; info.text = LLO_RARITY_LIST[i].name; info.func = LLO_RarityDropDown_OnClick; UIDropDownMenu_AddButton(info); end end local function LLS_BindsDropDown_Initialize() local info; for i = 1, getn(LLS_BINDS_LIST), 1 do info = { }; info.text = LLS_BINDS_LIST[i].name; info.func = LLS_BindsDropDown_OnClick; UIDropDownMenu_AddButton(info); end end local function LLS_LocationDropDown_Initialize() local info; for i = 1, getn(LLS_LOCATION_LIST), 1 do info = { }; info.text = LLS_LOCATION_LIST[i].name; info.func = LLS_LocationDropDown_OnClick; UIDropDownMenu_AddButton(info); end end local function LLS_TypeDropDown_Initialize() local info; for i = 1, getn(LLS_TYPE_LIST), 1 do info = { }; info.text = LLS_TYPE_LIST[i].name; info.func = LLS_TypeDropDown_OnClick; UIDropDownMenu_AddButton(info); end end local function LLS_SubtypeDropDownArmor_Initialize() local info; for i = 1, getn(LLS_SUBTYPE_ARMOR_LIST), 1 do info = { }; info.text = LLS_SUBTYPE_ARMOR_LIST[i].name; info.func = LLS_SubtypeDropDown_OnClick; UIDropDownMenu_AddButton(info); end end local function LLS_SubtypeDropDownShield_Initialize() local info; for i = 1, getn(LLS_SUBTYPE_SHIELD_LIST), 1 do info = { }; info.text = LLS_SUBTYPE_SHIELD_LIST[i].name; info.func = LLS_SubtypeDropDown_OnClick; UIDropDownMenu_AddButton(info); end end local function LLS_SubtypeDropDownWeapon_Initialize() local info; for i = 1, getn(LLS_SUBTYPE_WEAPON_LIST), 1 do info = { }; info.text = LLS_SUBTYPE_WEAPON_LIST[i].name; info.func = LLS_SubtypeDropDown_OnClick; UIDropDownMenu_AddButton(info); end end local function LLS_SubtypeDropDownRecipe_Initialize() local info; for i = 1, getn(LLS_SUBTYPE_RECIPE_LIST), 1 do info = { }; info.text = LLS_SUBTYPE_RECIPE_LIST[i].name; info.func = LLS_SubtypeDropDown_OnClick; UIDropDownMenu_AddButton(info); end end local function LLS_SubtypeDropDownContainer_Initialize() local info; for i = 1, getn(LLS_SUBTYPE_CONTAINER_LIST), 1 do info = { }; info.text = LLS_SUBTYPE_CONTAINER_LIST[i].name; info.func = LLS_SubtypeDropDown_OnClick; UIDropDownMenu_AddButton(info); end end local function LootLink_UIDropDownMenu_SetSelectedID(frame, id, names) UIDropDownMenu_SetSelectedID(frame, id); if( not frame ) then frame = this; end UIDropDownMenu_SetText(names[id].name, frame); end local function LootLink_SetupTypeUI(iType, iSubtype) local _type = LLS_TYPE_LIST[iType].value; if( not iSubtype ) then iSubtype = 1; end -- Hide all of the variable labels and fields to start getglobal("LLS_SubtypeLabel"):Hide(); getglobal("LLS_SubtypeDropDown"):Hide(); getglobal("LLS_MinimumArmorLabel"):Hide(); getglobal("LLS_MinimumBlockLabel"):Hide(); getglobal("LLS_MinimumDamageLabel"):Hide(); getglobal("LLS_MaximumDamageLabel"):Hide(); getglobal("LLS_MaximumSpeedLabel"):Hide(); getglobal("LLS_MinimumDPSLabel"):Hide(); getglobal("LLS_MinimumSlotsLabel"):Hide(); getglobal("LLS_MinimumSkillLabel"):Hide(); getglobal("LLS_MaximumSkillLabel"):Hide(); getglobal("LLS_MinimumArmorEditBox"):Hide(); getglobal("LLS_MinimumBlockEditBox"):Hide(); getglobal("LLS_MinimumDamageEditBox"):Hide(); getglobal("LLS_MaximumDamageEditBox"):Hide(); getglobal("LLS_MaximumSpeedEditBox"):Hide(); getglobal("LLS_MinimumDPSEditBox"):Hide(); getglobal("LLS_MinimumSlotsEditBox"):Hide(); getglobal("LLS_MinimumSkillEditBox"):Hide(); getglobal("LLS_MaximumSkillEditBox"):Hide(); if( _type ~= TYPE_OTHER ) then local label = getglobal("LLS_SubtypeLabel"); local dropdown = getglobal("LLS_SubtypeDropDown"); local initfunc; -- Show the dropdown and its label label:Show(); dropdown:Show(); if( _type == TYPE_ARMOR ) then label:SetText(LLS_SUBTYPE_ARMOR); initfunc = LLS_SubtypeDropDownArmor_Initialize; getglobal("LLS_MinimumArmorLabel"):Show(); getglobal("LLS_MinimumArmorEditBox"):Show(); elseif( _type == TYPE_SHIELD ) then label:SetText(LLS_SUBTYPE_SHIELD); initfunc = LLS_SubtypeDropDownShield_Initialize; getglobal("LLS_MinimumArmorLabel"):Show(); getglobal("LLS_MinimumBlockLabel"):Show(); getglobal("LLS_MinimumArmorEditBox"):Show(); getglobal("LLS_MinimumBlockEditBox"):Show(); elseif( _type == TYPE_WEAPON ) then label:SetText(LLS_SUBTYPE_WEAPON); initfunc = LLS_SubtypeDropDownWeapon_Initialize; getglobal("LLS_MinimumDamageLabel"):Show(); getglobal("LLS_MaximumDamageLabel"):Show(); getglobal("LLS_MaximumSpeedLabel"):Show(); getglobal("LLS_MinimumDPSLabel"):Show(); getglobal("LLS_MinimumDamageEditBox"):Show(); getglobal("LLS_MaximumDamageEditBox"):Show(); getglobal("LLS_MaximumSpeedEditBox"):Show(); getglobal("LLS_MinimumDPSEditBox"):Show(); elseif( _type == TYPE_CONTAINER ) then label:SetText(LLS_SUBTYPE_CONTAINER); initfunc = LLS_SubtypeDropDownContainer_Initialize; getglobal("LLS_MinimumSlotsLabel"):Show(); getglobal("LLS_MinimumSlotsEditBox"):Show(); else label:SetText(LLS_SUBTYPE_RECIPE); initfunc = LLS_SubtypeDropDownRecipe_Initialize; getglobal("LLS_MinimumSkillLabel"):Show(); getglobal("LLS_MaximumSkillLabel"):Show(); getglobal("LLS_MinimumSkillEditBox"):Show(); getglobal("LLS_MaximumSkillEditBox"):Show(); end UIDropDownMenu_Initialize(dropdown, initfunc); UIDropDownMenu_SetSelectedID(LLS_SubtypeDropDown, iSubtype); end LLS_TextEditBox:SetFocus(); end local function LootLink_InspectSlot(unit, id) local link = GetInventoryItemLink(unit, id); if( link ) then local name = LootLink_ProcessLinks(link, 1); if( name and ItemLinks[name] ) then local count = GetInventoryItemCount(unit, id); if( count > 1 ) then ItemLinks[name].x = 1; end LootLink_Event_InspectSlot(name, count, ItemLinks[name], unit, id); end end end function LootLink_Inspect(who) if ( not UnitIsPlayer(who) ) then return; end if ( who == "mouseover" ) then if ( not LLInspect_List ) then LLInspect_List = {}; end local name = UnitName(who); local time = GetTime(); local lasttime = LLInspect_List[name]; if ( lasttime and time - lasttime < 120 ) then return nil; end if ( not lasttime ) then LLInspect_List.total = (LLInspect_List.total or 0) + 1; end if ( LLInspect_List.total and LLInspect_List.total > 250 ) then local list = 0; for k, v in LLInspect_List do list = list + 1; v = nil; if ( list == 100 ) then break; end end end LLInspect_List[name] = time; end local index; for index = 1, getn(INVENTORY_SLOT_LIST), 1 do LootLink_InspectSlot(who, INVENTORY_SLOT_LIST[index].id) end end local function LootLink_ScanInventory() local bagid; local size; local slotid; local link; for bagid = 0, 4, 1 do size = GetContainerNumSlots(bagid); if( size ) then for slotid = size, 1, -1 do link = GetContainerItemLink(bagid, slotid); if( link ) then local name = LootLink_ProcessLinks(link, 1); if( name and ItemLinks[name] ) then local texture, count, locked, quality, readable = GetContainerItemInfo(bagid, slotid); if( count > 1 ) then ItemLinks[name].x = 1; end LootLink_Event_ScanInventory(name, count, ItemLinks[name], bagid, slotid); end end end end end end local function LootLink_ScanSellPrices() local bagid; local size; local slotid; local link; LootLink_MoneyToggle(); for bagid = 0, 4, 1 do lBagID = bagid; size = GetContainerNumSlots(bagid); if( size ) then for slotid = size, 1, -1 do lSlotID = slotid; LLHiddenTooltip:SetBagItem(bagid, slotid); end end end lBagID = nil; lSlotID = nil; LootLink_MoneyToggle(1); end local function LootLink_StartAuctionScan() -- Start with the first page lCurrentAuctionPage = nil; -- Hook the functions that we need for the scan if( not lOriginal_CanSendAuctionQuery ) then lOriginal_CanSendAuctionQuery = CanSendAuctionQuery; CanSendAuctionQuery = LootLink_CanSendAuctionQuery; end if( not lOriginal_AuctionFrameBrowse_OnEvent ) then lOriginal_AuctionFrameBrowse_OnEvent = AuctionFrameBrowse_OnEvent; AuctionFrameBrowse_OnEvent = LootLink_AuctionFrameBrowse_OnEvent; end LootLink_Event_StartAuctionScan(); end local function LootLink_StopAuctionScan() LootLink_Event_StopAuctionScan(); -- Unhook the scanning functions if( lOriginal_CanSendAuctionQuery ) then CanSendAuctionQuery = lOriginal_CanSendAuctionQuery; lOriginal_CanSendAuctionQuery = nil; end if( lOriginal_AuctionFrameBrowse_OnEvent ) then AuctionFrameBrowse_OnEvent = lOriginal_AuctionFrameBrowse_OnEvent; lOriginal_AuctionFrameBrowse_OnEvent = nil; end end local function LootLink_AuctionNextQuery() if( lCurrentAuctionPage ) then local numBatchAuctions, totalAuctions = GetNumAuctionItems("list"); local maxPages = floor(totalAuctions / NUM_AUCTION_ITEMS_PER_PAGE); if( lCurrentAuctionPage < maxPages ) then lCurrentAuctionPage = lCurrentAuctionPage + 1; BrowseNoResultsText:SetText(format(LOOTLINK_AUCTION_PAGE_N, lCurrentAuctionPage + 1, maxPages + 1)); else lScanAuction = nil; LootLink_StopAuctionScan(); if( totalAuctions > 0 ) then BrowseNoResultsText:SetText(LOOTLINK_AUCTION_SCAN_DONE); LootLink_Event_FinishedAuctionScan(); end return; end else lCurrentAuctionPage = 0; BrowseNoResultsText:SetText(LOOTLINK_AUCTION_SCAN_START); end QueryAuctionItems("", "", "", nil, nil, nil, lCurrentAuctionPage, nil, nil); LootLink_Event_AuctionQuery(lCurrentAuctionPage); end local function LootLink_ScanAuction() local numBatchAuctions, totalAuctions = GetNumAuctionItems("list"); local auctionid; local link; if( numBatchAuctions > 0 ) then for auctionid = 1, numBatchAuctions do link = GetAuctionItemLink("list", auctionid); if( link ) then local name = LootLink_ProcessLinks(link, 1); if( name and ItemLinks[name] ) then local name_, texture, count, quality, canUse, level, minBid, minIncrement, buyoutPrice, bidAmount, highBidder, owner = GetAuctionItemInfo("list", auctionid); if( count > 1 ) then ItemLinks[name].x = 1; end LootLink_Event_ScanAuction(name, count, ItemLinks[name], lCurrentAuctionPage, auctionid); end end end end end local function LootLink_ScanBank() local index; local bagid; local size; local slotid; local link; for index, bagid in lBankBagIDs do size = GetContainerNumSlots(bagid); if( size ) then for slotid = size, 1, -1 do link = GetContainerItemLink(bagid, slotid); if( link ) then local name = LootLink_ProcessLinks(link, 1); if( name and ItemLinks[name] ) then local texture, count, locked, quality, readable = GetContainerItemInfo(bagid, slotid); if( count > 1 ) then ItemLinks[name].x = 1; end LootLink_Event_ScanBank(name, count, ItemLinks[name], bagid, slotid); end end end end end end local function LootLink_ScanSellPrice(price) local link = GetContainerItemLink(lBagID, lSlotID); local texture, itemCount, locked, quality, readable = GetContainerItemInfo(lBagID, lSlotID); local name; if( itemCount and itemCount > 1 ) then price = price / itemCount; end name = LootLink_NameFromLink(link); if( name and ItemLinks[name] ) then ItemLinks[name].p = price; if( itemCount and itemCount > 1 ) then ItemLinks[name].x = 1; end end end local function LootLink_CheckTooltipInfo() if( GameTooltip:IsVisible() ) then -- If we've already added our information or money is already showing, no need to do it again if( GameTooltip.llDone or GameTooltipMoneyFrame:IsVisible() ) then return nil; end -- Don't add information to tooltips generated while the mouse is over the minimap if( LootLink_MouseIsOver(MinimapCluster) ) then return nil; end local field = getglobal("GameTooltipTextLeft1"); if( field and field:IsShown() ) then local name = field:GetText(); if( name ) then if( ItemLinks[name] ) then LootLink_AddTooltipInfo(name, GameTooltip); end -- Whether or not we had information for this item, there's no need to check it again return nil; end end end return 1; end local function LootLink_CheckVersionReminder() local index; local value; local version; version = LootLink_GetDataVersion(); for index, value in LOOTLINK_DATA_UPGRADE_HELP do if( version < value.version ) then DEFAULT_CHAT_FRAME:AddMessage(value.text); end end end local function LootLink_UpgradeData() local index; local item; for index, item in ItemLinks do LootLink_ConvertServerFormat(item); if( item.item ) then item.i = item.item; item.item = nil; end if( item.color ) then item.c = item.color; item.color = nil; end if( item.price ) then item.p = item.price; item.price = nil; end if( item.stack ) then item.x = item.stack; item.stack = nil; end if( item.SearchData ) then local data = ""; if( item.SearchData.type ) then data = data.."ty"..item.SearchData.type.."·"; end if( item.SearchData.location ) then data = data.."lo"..LocationTypes[item.SearchData.location].i.."·"; end if( item.SearchData.subtype ) then data = data.."su"..item.SearchData.subtype.."·"; end if( item.SearchData.binds ) then data = data.."bi"..item.SearchData.binds.."·"; end if( item.SearchData.level ) then data = data.."le"..item.SearchData.level.."·"; end if( item.SearchData.armor ) then data = data.."ar"..item.SearchData.armor.."·"; end if( item.SearchData.minDamage ) then data = data.."mi"..item.SearchData.minDamage.."·"; end if( item.SearchData.maxDamage ) then data = data.."ma"..item.SearchData.maxDamage.."·"; end if( item.SearchData.speed ) then data = data.."sp"..item.SearchData.speed.."·"; end if( item.SearchData.DPS ) then data = data.."dp"..item.SearchData.DPS.."·"; end if( item.SearchData.unique ) then data = data.."un"..item.SearchData.unique.."·"; end if( item.SearchData.block ) then data = data.."bl"..item.SearchData.block.."·"; end if( item.SearchData.slots ) then data = data.."sl"..item.SearchData.slots.."·"; end if( item.SearchData.skill ) then data = data.."sk"..item.SearchData.skill.."·"; end if( item.SearchData.text ) then item.t = item.SearchData.text; end item.d = data; item.SearchData = nil; end end end local function LootLink_VariablesLoaded() local index; local value; if( not LootLinkState ) then LootLinkState = { }; elseif ( LootLinkState.options ) then for k, v in LootLinkState.options do LootLinkState[k] = v; end LootLinkState.options = nil; end if( not ItemLinks ) then LootLink_Reset(); end lServer = GetCVar("realmName"); lServerIndex = LootLink_AddServer(lServer); LootLink_UpgradeData(); LootLink_InitSizes(lServerIndex); -- Display and move minimap button -- if ( not LootLinkState.hideminimap ) then LootLinkMinimapButton:Show(); LootLink_Minimap_Position(); end if ( LootLinkState.mouseover ) then this:RegisterEvent("UPDATE_MOUSEOVER_UNIT"); end if ( LootLinkState.sortType ) then LootLink_UIDropDownMenu_SetSelectedID(LootLinkFrameDropDown, LootLinkState.sortType, LOOTLINK_DROPDOWN_LIST); end end -------------------------------------------------------------------------------------------------- -- OnFoo functions -------------------------------------------------------------------------------------------------- function LootLink_OnLoad() local index; local value; for index, value in ChatMessageTypes do this:RegisterEvent(value); end for index = 1, getn(INVENTORY_SLOT_LIST), 1 do INVENTORY_SLOT_LIST[index].id = GetInventorySlotInfo(INVENTORY_SLOT_LIST[index].name); end this:RegisterEvent("PLAYER_TARGET_CHANGED"); this:RegisterEvent("PLAYER_ENTERING_WORLD"); this:RegisterEvent("PLAYER_LEAVING_WORLD"); this:RegisterEvent("BANKFRAME_OPENED"); this:RegisterEvent("VARIABLES_LOADED"); this:RegisterEvent("AUCTION_HOUSE_SHOW"); this:RegisterEvent("AUCTION_HOUSE_CLOSE"); this:RegisterEvent("AUCTION_ITEM_LIST_UPDATE"); this:RegisterEvent("MERCHANT_SHOW"); --this:RegisterEvent("PLAYER_LOGOUT"); this:RegisterEvent("MEMORY_EXHAUSTED"); this:RegisterEvent("MEMORY_RECOVERED"); -- Register our slash command SLASH_LOOTLINK1 = "/lootlink"; SLASH_LOOTLINK2 = "/ll"; SlashCmdList["LOOTLINK"] = function(msg) LootLink_SlashCommandHandler(msg); end -- Hook the GameTooltip:SetOwner function lOriginal_GameTooltip_SetOwner = GameTooltip.SetOwner; GameTooltip.SetOwner = LootLink_GameTooltip_SetOwner; -- Hook the ContainerFrameItemButton_OnEnter, ContainerFrame_Update and GameTooltip:SetLootItem functions lOriginal_ContainerFrameItemButton_OnEnter = ContainerFrameItemButton_OnEnter; ContainerFrameItemButton_OnEnter = LootLink_ContainerFrameItemButton_OnEnter; lOriginal_ContainerFrame_Update = ContainerFrame_Update; ContainerFrame_Update = LootLink_ContainerFrame_Update; lOriginal_GameTooltip_SetLootItem = GameTooltip.SetLootItem; GameTooltip.SetLootItem = LootLink_GameTooltip_SetLootItem; -- Hook GameTooltip_OnHide and GameTooltip_ClearMoney lOriginal_GameTooltip_OnHide = GameTooltip_OnHide; GameTooltip_OnHide = LootLink_GameTooltip_OnHide; lOriginal_GameTooltip_ClearMoney = GameTooltip_ClearMoney; GameTooltip_ClearMoney = LootLink_GameTooltip_ClearMoney; -- Hook the shopping tooltip functions lOriginal_ShoppingTooltip1_SetMerchantCompareItem = ShoppingTooltip1.SetMerchantCompareItem; ShoppingTooltip1.SetMerchantCompareItem = LootLink_ShoppingTooltip1_SetMerchantCompareItem; lOriginal_ShoppingTooltip2_SetMerchantCompareItem = ShoppingTooltip2.SetMerchantCompareItem; ShoppingTooltip2.SetMerchantCompareItem = LootLink_ShoppingTooltip2_SetMerchantCompareItem; lOriginal_ShoppingTooltip1_SetAuctionCompareItem = ShoppingTooltip1.SetAuctionCompareItem; ShoppingTooltip1.SetAuctionCompareItem = LootLink_ShoppingTooltip1_SetAuctionCompareItem; lOriginal_ShoppingTooltip2_SetAuctionCompareItem = ShoppingTooltip2.SetAuctionCompareItem; ShoppingTooltip2.SetAuctionCompareItem = LootLink_ShoppingTooltip2_SetAuctionCompareItem; -- Hook SetItemRef lOriginal_SetItemRef = SetItemRef; SetItemRef = LootLink_SetItemRef; -- Hook our hidden tooltip's OnTooltipAddMoney lOriginal_OnTooltipAddMoney = LLHiddenTooltip:GetScript("OnTooltipAddMoney"); LLHiddenTooltip:SetScript("OnTooltipAddMoney", LootLink_OnTooltipAddMoney); -- Hook chat edit box lOriginal_ChatEdit_OnTabPressed = ChatEdit_OnTabPressed; ChatEdit_OnTabPressed = LootLink_ChatEdit_OnTabPressed; lOriginal_ChatEdit_OnTextChanged = ChatEdit_OnTextChanged; ChatEdit_OnTextChanged = LootLink_ChatEdit_OnTextChanged; -- Hook CloseWindows lOriginal_CloseWindows = CloseWindows; CloseWindows = LootLink_CloseWindows; if( DEFAULT_CHAT_FRAME ) then DEFAULT_CHAT_FRAME:AddMessage(LOOTLINK_LOADED); end UIErrorsFrame:AddMessage(LOOTLINK_LOADED, 1.0, 1.0, 1.0, 1.0, UIERRORS_HOLD_TIME); end function LootLink_Minimap_OnEnter() GameTooltip:SetOwner(this, "ANCHOR_BOTTOMLEFT"); GameTooltip:AddLine(LOOTLINK_TITLE); if ( not LootLinkState.lockminimap ) then GameTooltip:AddLine("|cffaaaaaa "..LOOTLINK_MINIMAP_DRAG); end if ( LootLinkFrame.process ) then GameTooltip:AddLine("|cffaaaaaa "..getglobal("LLP_"..LootLinkFrame.process.."_LABEL")); if ( LootLink_Process:IsVisible() ) then GameTooltip:AddLine("|cffaaaaaa "..LOOTLINK_MINIMAP_PROCESS_SHOW); else GameTooltip:AddLine("|cffaaaaaa "..LOOTLINK_MINIMAP_PROCESS_HIDE); end end GameTooltip:Show(); end function LootLink_Minimap_Position(x,y) if ( x or y ) then if ( x ) then if ( x < 0 ) then x = x + 360; end LootLinkState.x = x; end if ( y ) then LootLinkState.y = y; end end x, y = LootLinkState.x, LootLinkState.y if ( LootLinkOptionsFrame:IsVisible() ) then LLO_MinimapPos:SetValue( x or LLO_MINIMAP_X ); LLO_MinimapOffset:SetValue( y or LLO_MINIMAP_Y ); end LootLinkMinimapButton:SetPoint("TOPLEFT","Minimap","TOPLEFT",53-((80+(y or LLO_MINIMAP_Y))*cos(x or LLO_MINIMAP_X)),((80+(y or LLO_MINIMAP_Y))*sin(x or LLO_MINIMAP_X))-55); end function LootLink_Minimap_DragStart() if ( LootLinkState.lockminimap ) then return; end this:SetScript("OnUpdate", LootLink_Minimap_DragUpdate); end function LootLink_Minimap_DragStop() LootLinkMinimapButton:UnlockHighlight() this:SetScript("OnUpdate", nil); end function LootLink_Minimap_DragUpdate() -- Thanks to Gello for making this a ton shorter LootLinkMinimapButton:LockHighlight(); local curX, curY = GetCursorPosition(); local mapX, mapY = Minimap:GetCenter(); local x, y; if ( IsShiftKeyDown() ) then y = math.pow( math.pow(curY - mapY * Minimap:GetEffectiveScale(), 2) + math.pow(mapX * Minimap:GetEffectiveScale() - curX, 2), 0.5) - 70; y = min( max( y, -30 ), 30 ); end x = math.deg(math.atan2( curY - mapY * Minimap:GetEffectiveScale(), mapX * Minimap:GetEffectiveScale() - curX )); LootLink_Minimap_Position(x,y); end function LootLink_CanSendAuctionQuery() local value = lOriginal_CanSendAuctionQuery(); if( value ) then LootLink_AuctionNextQuery(); return nil; end return value; end function LootLink_AuctionFrameBrowse_OnEvent() -- Intentionally empty; don't allow the auction UI to update while we're scanning end function LootLink_GameTooltip_SetOwner(this, owner, anchor) lOriginal_GameTooltip_SetOwner(this, owner, anchor); lGameToolTipOwner = owner; end function LootLink_GameTooltip_ClearMoney() lOriginal_GameTooltip_ClearMoney(); if( not lSuppressInfoAdd ) then lCheckTooltip = LootLink_CheckTooltipInfo(); end end function LootLink_GameTooltip_ClearMoney_Temp() -- Intentionally empty; don't clear money while we use our hidden tooltip end function LootLink_ContainerFrameItemButton_OnEnter() LootLink_AutoInfoOff(); lOriginal_ContainerFrameItemButton_OnEnter(); if( not InRepairMode() and not MerchantFrame:IsVisible() ) then local frameID = this:GetParent():GetID(); local buttonID = this:GetID(); local link = GetContainerItemLink(frameID, buttonID); local name = LootLink_NameFromLink(link); if( name and ItemLinks[name] ) then local texture, itemCount, locked, quality, readable = GetContainerItemInfo(frameID, buttonID); LootLink_AddTooltipInfo(name, GameTooltip, itemCount); GameTooltip:Show(); end end LootLink_AutoInfoOn(); end function LootLink_ContainerFrame_Update(frame) LootLink_AutoInfoOff(); lOriginal_ContainerFrame_Update(frame); if( not InRepairMode() and not MerchantFrame:IsVisible() and GameTooltip:IsVisible() ) then local frameID = frame:GetID(); local frameName = frame:GetName(); local iButton; for iButton = 1, frame.size do local button = getglobal(frameName.."Item"..iButton); if( GameTooltip:IsOwned(button) ) then local buttonID = button:GetID(); local link = GetContainerItemLink(frameID, buttonID); local name = LootLink_NameFromLink(link); if( name and ItemLinks[name] ) then local texture, itemCount, locked, quality, readable = GetContainerItemInfo(frameID, buttonID); LootLink_AddTooltipInfo(name, GameTooltip, itemCount); GameTooltip:Show(); end end end end LootLink_AutoInfoOn(); end function LootLink_GameTooltip_OnHide() lOriginal_GameTooltip_OnHide(); GameTooltip.llDone = nil; lCheckTooltip = nil; end function LootLink_GameTooltip_SetLootItem(this, slot) LootLink_AutoInfoOff(); lOriginal_GameTooltip_SetLootItem(this, slot); local link = GetLootSlotLink(slot); local name = LootLink_NameFromLink(link); if( name and ItemLinks[name] and ItemLinks[name].p ) then local texture, item, quantity, quality = GetLootSlotInfo(slot); LootLink_AddTooltipInfo(name, GameTooltip, quantity); end LootLink_AutoInfoOn(); end function LootLink_ShoppingTooltip1_SetMerchantCompareItem(this, id, num) LootLink_MoneyToggle(); local retval = lOriginal_ShoppingTooltip1_SetMerchantCompareItem(this, id, num); LootLink_MoneyToggle(1); return retval; end function LootLink_ShoppingTooltip2_SetMerchantCompareItem(this, id, num) LootLink_MoneyToggle(); local retval = lOriginal_ShoppingTooltip2_SetMerchantCompareItem(this, id, num); LootLink_MoneyToggle(1); return retval; end function LootLink_ShoppingTooltip1_SetAuctionCompareItem(this, _type, index, num) LootLink_MoneyToggle(); local retval = lOriginal_ShoppingTooltip1_SetAuctionCompareItem(this, _type, index, num); LootLink_MoneyToggle(1); return retval; end function LootLink_ShoppingTooltip2_SetAuctionCompareItem(this, _type, index, num) LootLink_MoneyToggle(); local retval = lOriginal_ShoppingTooltip2_SetAuctionCompareItem(this, _type, index, num); LootLink_MoneyToggle(1); return retval; end function LootLink_SetItemRef(link, text, button) lOriginal_SetItemRef(link, text, button); -- If this is a tooltip-creating link, add our information to it if( strsub(link, 1, 6) ~= "Player" ) then ItemRefTooltipMoneyFrame:SetPoint("LEFT", "ItemRefTooltipTextLeft1", "LEFT", 0, 0); ItemRefTooltipMoneyFrame:Hide(); if( ItemRefTooltip:IsVisible() ) then local field = getglobal("ItemRefTooltipTextLeft1"); if( field and field:IsShown() ) then local name = field:GetText(); if( name and ItemLinks[name] ) then LootLink_AddTooltipInfo(name, ItemRefTooltip); end end end end end function LootLink_OnTooltipAddMoney() lOriginal_OnTooltipAddMoney(); if( lBagID and lSlotID ) then LootLink_ScanSellPrice(arg1); end end function LootLink_OnEvent() if( event == "PLAYER_TARGET_CHANGED" ) then if( UnitIsUnit("target", "player") ) then return; elseif( UnitIsPlayer("target") ) then LootLink_Inspect("target"); end elseif( event == "UPDATE_MOUSEOVER_UNIT" ) then LootLink_Inspect("mouseover"); elseif( event == "PLAYER_ENTERING_WORLD" ) then this:RegisterEvent("UNIT_INVENTORY_CHANGED"); -- Now that we're in the world, we want to watch for inventory changes this:RegisterEvent("UNIT_INVENTORY_CHANGED"); -- Check to see if the user needs to upgrade their database LootLink_CheckVersionReminder(); -- Do initial scan of inventory and equipped items LootLink_ScanInventory(); LootLink_Inspect("player"); elseif( event == "PLAYER_LEAVING_WORLD" ) then -- When we leave the world, we don't need to watch for inventory changes, -- especially since zoning will cause one UNIT_INVENTORY_CHANGED event for -- each item in the player's inventory and that they have equipped this:UnregisterEvent("UNIT_INVENTORY_CHANGED"); elseif( event == "UNIT_INVENTORY_CHANGED" ) then if( arg1 == "player" ) then LootLink_ScanInventory(); LootLink_Inspect("player"); end elseif( event == "BANKFRAME_OPENED" ) then LootLink_ScanBank(); elseif( event == "VARIABLES_LOADED" ) then LootLink_VariablesLoaded(); elseif( event == "AUCTION_HOUSE_SHOW" ) then if( lScanAuction ) then LootLink_StartAuctionScan(); end elseif( event == "AUCTION_HOUSE_CLOSED" ) then LootLink_StopAuctionScan(); elseif( event == "AUCTION_ITEM_LIST_UPDATE" ) then LootLink_ScanAuction(); elseif( event == "MERCHANT_SHOW" ) then LootLink_ScanSellPrices(); --elseif( event == "PLAYER_LOGOUT" ) then --LootLink_ClearDelayed(); elseif( event == "MEMORY_EXHAUSTED" ) then LLP_OUTOFMEM = 1; elseif( event == "MEMORY_RECOVERED" ) then LLP_OUTOFMEM = nil; else local name = LootLink_ProcessLinks(arg1, event == "CHAT_MSG_LOOT"); if( name and ItemLinks[name] ) then LootLink_Event_ScanChat(name, ItemLinks[name], arg1); end end end function LootLink_RemoveItem(name, link, displayindex, index) local value = ItemLinks[name]; if ( not value ) then return; end local found; if ( link and string.sub(link, 1, 5) == "item:" ) then link = string.sub( link, 6 ); end if ( displayindex and not link and not index ) then link = DisplayIndices[displayindex][2].i; end if ( not link and not index ) then index = 0; end if ( link or index ) then if ( (index and index == 0) or (value.i and value.i == link) ) then found = 1; if ( value.m and value.m[1] ) then for k, v in value.m[1] do value[k] = v; end tremove( value.m, 1 ); if ( getn(value.m) == 0 ) then value.m = nil; end else ItemLinks[name] = nil; di = 0; end else if ( value.m ) then for i=1, getn(value.m) do if ( (index and index == i) or (value.m[i].i == link) ) then found = 1; tremove( value.m, i ); break; end end end end if ( found ) then LootLink_SortAlternates( name ); if ( DisplayIndices and LootLinkFrame:IsVisible() ) then local dName, start; local i = 1; if ( DisplayIndices[i] ) then dName = DisplayIndices[i][1]; end while dName do if dName == name then if not start then start = i; end elseif start then break; end i = i+1; if ( DisplayIndices[i] ) then dName = DisplayIndices[i][1]; else dName = nil; end end if ( start and i ) then tremove(DisplayIndices, i-1); DisplayIndices.onePastEnd = DisplayIndices.onePastEnd - 1; if ( displayindex ) then LootLink_Update(); LootLink_SetTitle(); end end else DisplayIndices = nil; end return 1; end end end function LootLinkItemButton_OnClick(button) local name = this:GetText(); local index = this.i; if( button == "LeftButton" ) then local data = DisplayIndices[this:GetID()+FauxScrollFrame_GetOffset(LootLinkListScrollFrame)]; if( IsShiftKeyDown() and ChatFrameEditBox:IsVisible() ) then if ( not LootLink_CheckItemServer(data[2], lServerIndex) ) then local dialog = StaticPopup_Show("LOOTLINK_LINK_CONFIRM", LOOTLINK_STATICPOPUP_SERVER, LOOTLINK_STATICPOPUP_LINKITEM); if ( dialog ) then dialog.data = data; end elseif ( not GetItemInfo("item:"..data[2].i) ) then local dialog = StaticPopup_Show("LOOTLINK_LINK_CONFIRM", LOOTLINK_STATICPOPUP_INVALID, LOOTLINK_STATICPOPUP_LINKITEM); if ( dialog ) then dialog.data = data; end else ChatFrameEditBox:Insert(LootLink_GetLink(name, data[3])); end end if( IsControlKeyDown() ) then if ( not LootLink_CheckItemServer(data[2], lServerIndex) ) then local dialog = StaticPopup_Show("LOOTLINK_DRESSUP_CONFIRM", LOOTLINK_STATICPOPUP_SERVER, LOOTLINK_STATICPOPUP_DRESSUP); if ( dialog ) then dialog.data = data; end elseif ( not GetItemInfo("item:"..data[2].i) ) then local dialog = StaticPopup_Show("LOOTLINK_DRESSUP_CONFIRM", LOOTLINK_STATICPOPUP_INVALID, LOOTLINK_STATICPOPUP_DRESSUP); if ( dialog ) then dialog.data = data; end else DressUpItemLink(LootLink_GetLink(name, data[3])); end end elseif( button == "RightButton" ) then if( IsShiftKeyDown() and IsControlKeyDown() ) then local data = DisplayIndices[this:GetID()+FauxScrollFrame_GetOffset(LootLinkListScrollFrame)]; local dialog = StaticPopup_Show("LOOTLINK_DELETEITEM_CONFIRM", data[2].c, name); if ( dialog ) then dialog.data = data; end --if ( LootLink_RemoveItem(name, nil, this:GetID()+FauxScrollFrame_GetOffset(LootLinkListScrollFrame)) ) then -- LootLink_SortAlternates(name); --end end end --@todo Telo: add a method to correct the color of items end function LootLink_OnShow() PlaySound("igMainMenuOpen"); LootLink_Update(); end function LootLink_OnHide() PlaySound("igMainMenuClose"); end function LootLink_ClearDelayed() if ( LootLinkFrame.schedule ) then for k, v in LootLinkFrame.schedule do local value, index = LootLink_GetValue( v[1], nil, v[2] ); if ( v and not v.t ) then if ( LootLink_RemoveItem(v[1], v[2]) ) then LootLink_SortAlternates(v[1]); end end end end LootLinkFrame.schedule = nil; end function LootLink_ScheduleItem(name,link,time) if ( not LootLinkFrame.schedule ) then LootLinkFrame.schedule = {}; end for k, v in LootLinkFrame.schedule do if ( v[1] == name and v[2] == link ) then return; end end LLHiddenTooltip:SetHyperlink("item:"..link); tinsert( LootLinkFrame.schedule, {name,link,(time or LOOTLINK_SCHEDULE_DELAY)} ); end function LootLink_OnUpdate(elapsed) if( lCheckTooltip ) then lCheckTooltip = LootLink_CheckTooltipInfo(); end if ( LootLinkFrame.process ) then LootLinkProcess_Run(); end local list = LootLinkFrame.schedule; if ( list ) then for k, v in list do v[3] = v[3] - elapsed; if ( v[3] < 0 ) then local value, index = LootLink_GetValue( v[1], nil, v[2] ); if ( not value ) then -- Item was remove from ItemLinks table, stop trying to check for it. tremove( list, k ); return; elseif ( LootLink_BuildSearchData( v[1], value, index ) ) then tremove( list, k ); return; else -- Still don't have information from server. if ( v[4] and v[4] == 60 ) then -- we've tried long enough, fail this item; if ( LootLink_RemoveItem(v[1], v[2]) ) then LootLink_SortAlternates(name); end tremove( list, k ); return; end v[4] = (v[4] or 0) + 1; v[3] = LOOTLINK_SCHEDULE_DELAY; end end end if ( getn(list) == 0 ) then list = nil; end end end function LootLinkItemButton_OnEnter() local link = LootLink_GetHyperlink(this:GetText(), this.i); if( link ) then LootLinkFrame.TooltipButton = this:GetID(); LootLinkTooltip:SetOwner(this, "ANCHOR_BOTTOMRIGHT"); if ( LootLink_SetHyperlink(LootLinkTooltip, this:GetText(), link, this.i) ) then LootLink_Update(); end LootLink_AddTooltipTexture(link); LootLink_AddTooltipInfo(this:GetText(), nil, nil, this.i); end end function LootLinkItemButton_OnLeave() if( LootLinkFrame.TooltipButton ) then LootLinkFrame.TooltipButton = nil; HideUIPanel(LootLinkTooltip); end LootLink_AddTooltipTexture(); end function LootLinkFrameDropDown_OnLoad() UIDropDownMenu_Initialize(LootLinkFrameDropDown, LootLinkFrameDropDown_Initialize); LootLink_UIDropDownMenu_SetSelectedID(LootLinkFrameDropDown, 1, LOOTLINK_DROPDOWN_LIST); UIDropDownMenu_SetWidth(80); UIDropDownMenu_SetButtonWidth(24); UIDropDownMenu_JustifyText("LEFT", LootLinkFrameDropDown) end function LootLinkFrameDropDownButton_OnClick() local oldID = UIDropDownMenu_GetSelectedID(LootLinkFrameDropDown); UIDropDownMenu_SetSelectedID(LootLinkFrameDropDown, this:GetID()); if( oldID ~= this:GetID() ) then LootLink_Refresh(); end LootLinkState.sortType = this:GetID(); end function LLS_RarityDropDown_OnLoad() UIDropDownMenu_SetWidth(90); UIDropDownMenu_SetButtonWidth(24); UIDropDownMenu_JustifyText("LEFT", LLS_RarityDropDown); end function LLS_RarityDropDown_OnClick() UIDropDownMenu_SetSelectedID(LLS_RarityDropDown, this:GetID()); end function LLS_BindsDropDown_OnLoad() UIDropDownMenu_SetWidth(90); UIDropDownMenu_SetButtonWidth(24); UIDropDownMenu_JustifyText("LEFT", LLS_BindsDropDown); end function LLS_BindsDropDown_OnClick() UIDropDownMenu_SetSelectedID(LLS_BindsDropDown, this:GetID()); end function LLS_LocationDropDown_OnLoad() UIDropDownMenu_SetWidth(90); UIDropDownMenu_SetButtonWidth(24); UIDropDownMenu_JustifyText("LEFT", LLS_LocationDropDown); end function LLS_LocationDropDown_OnClick() UIDropDownMenu_SetSelectedID(LLS_LocationDropDown, this:GetID()); end function LLS_TypeDropDown_OnLoad() UIDropDownMenu_SetWidth(90); UIDropDownMenu_SetButtonWidth(24); UIDropDownMenu_JustifyText("LEFT", LLS_TypeDropDown); end function LLS_TypeDropDown_OnClick() local oldID = UIDropDownMenu_GetSelectedID(LLS_TypeDropDown); UIDropDownMenu_SetSelectedID(LLS_TypeDropDown, this:GetID()); if( oldID ~= this:GetID() ) then LootLink_SetupTypeUI(this:GetID(), 1); end end function LLS_SubtypeDropDown_OnLoad() UIDropDownMenu_SetWidth(90); UIDropDownMenu_SetButtonWidth(24); UIDropDownMenu_JustifyText("LEFT", LLS_SubtypeDropDown); end function LLS_SubtypeDropDown_OnClick() UIDropDownMenu_SetSelectedID(LLS_SubtypeDropDown, this:GetID()); end function LLO_RarityDropDown_OnLoad() UIDropDownMenu_SetWidth(90); UIDropDownMenu_SetButtonWidth(24); UIDropDownMenu_JustifyText("LEFT", LLO_RarityDropDown); end function LLO_RarityDropDown_OnClick() UIDropDownMenu_SetSelectedID(LLO_RarityDropDown, this:GetID()); end -------------------------------------------------------------------------------------------------- -- Callback functions -------------------------------------------------------------------------------------------------- function ToggleLootLink() if( LootLinkFrame:IsVisible() ) then HideUIPanel(LootLinkFrame); else ShowUIPanel(LootLinkFrame); end end function LootLink_SlashCommandHandler(msg) local reset; local makehome; local light; local aborted; if( not lDisableVersionReminder ) then LootLink_CheckVersionReminder(); end if( not msg or msg == "" ) then ToggleLootLink(); else local command = string.lower(msg); if( command == LOOTLINK_HELP ) then local index = 0; local value = getglobal("LOOTLINK_HELP_TEXT"..index); while( value ) do DEFAULT_CHAT_FRAME:AddMessage(value); index = index + 1; value = getglobal("LOOTLINK_HELP_TEXT"..index); end elseif( command == LOOTLINK_STATUS ) then LootLink_Status(); elseif( command == LOOTLINK_AUCTION or command == LOOTLINK_SCAN ) then if( AuctionFrame and AuctionFrame:IsVisible() ) then local iButton; local button; -- Hide the UI from any current results, show the no results text so we can use it BrowseNoResultsText:Show(); for iButton = 1, NUM_BROWSE_TO_DISPLAY do button = getglobal("BrowseButton"..iButton); button:Hide(); end BrowsePrevPageButton:Hide(); BrowseNextPageButton:Hide(); BrowseSearchCountText:Hide(); LootLink_StartAuctionScan(); else lScanAuction = 1; LootLink_Status(); end elseif( command == LOOTLINK_SHOWINFO ) then LootLinkState.HideInfo = nil; LootLink_Status(); elseif( command == LOOTLINK_HIDEINFO ) then LootLinkState.HideInfo = 1; LootLink_Status(); elseif( command == LOOTLINK_FULLMODE ) then LootLinkState.LightMode = nil; LootLink_Status(); else local iStart; local iEnd; local args; iStart, iEnd, command, args = string.find(command, "^(%w+)%s*(.*)$"); if( command == LOOTLINK_RESET ) then if( lResetNeedsConfirm ) then if( args == LOOTLINK_CONFIRM ) then LootLink_Reset(); lResetNeedsConfirm = nil; lDisableVersionReminder = nil DEFAULT_CHAT_FRAME:AddMessage(LOOTLINK_RESET_DONE); end else reset = 1; lResetNeedsConfirm = 1; lDisableVersionReminder = 1; end elseif( LootLink_GetDataVersion() < 110 and command == LOOTLINK_MAKEHOME ) then if( lMakeHomeNeedsConfirm ) then if( args == LOOTLINK_CONFIRM ) then LootLink_MakeHome(); lMakeHomeNeedsConfirm = nil; lDisableVersionReminder = nil; DEFAULT_CHAT_FRAME:AddMessage(LOOTLINK_MAKEHOME_DONE); end else makehome = 1; lMakeHomeNeedsConfirm = 1; lDisableVersionReminder = 1; end elseif( command == LOOTLINK_LIGHTMODE ) then if( lLightModeNeedsConfirm ) then if( args == LOOTLINK_CONFIRM ) then LootLink_LightMode(); lLightModeNeedsConfirm = nil; DEFAULT_CHAT_FRAME:AddMessage(LOOTLINK_LIGHTMODE_DONE); end else light = 1; lLightModeNeedsConfirm = 1; end end end end if( not reset ) then if( lResetNeedsConfirm ) then aborted = 1; lResetNeedsConfirm = nil; DEFAULT_CHAT_FRAME:AddMessage(LOOTLINK_RESET_ABORTED); end end if( not makehome ) then if( lMakeHomeNeedsConfirm ) then aborted = 1; lMakeHomeNeedsConfirm = nil; DEFAULT_CHAT_FRAME:AddMessage(LOOTLINK_MAKEHOME_ABORTED); end end if( not light ) then if( lLightModeNeedsConfirm ) then lLightModeNeedsConfirm = nil; DEFAULT_CHAT_FRAME:AddMessage(LOOTLINK_LIGHTMODE_ABORTED); end end if( reset ) then DEFAULT_CHAT_FRAME:AddMessage(LOOTLINK_RESET_NEEDS_CONFIRM); end if( makehome ) then DEFAULT_CHAT_FRAME:AddMessage(LOOTLINK_MAKEHOME_NEEDS_CONFIRM); end if( light ) then DEFAULT_CHAT_FRAME:AddMessage(LOOTLINK_LIGHTMODE_NEEDS_CONFIRM); end if( aborted and not reset and not makehome ) then lDisableVersionReminder = nil end end function LootLink_Update() local iItem; if( not DisplayIndices ) then LootLink_BuildDisplayIndices(); end FauxScrollFrame_Update(LootLinkListScrollFrame, DisplayIndices.onePastEnd - 1, LOOTLINK_ITEMS_SHOWN, LOOTLINK_ITEM_HEIGHT, nil, nil, nil, LootLinkHighlightFrame, 293, 316); LootLinkHighlightFrame:Hide(); for iItem = 1, LOOTLINK_ITEMS_SHOWN, 1 do local itemIndex = iItem + FauxScrollFrame_GetOffset(LootLinkListScrollFrame); local lootLinkItem = getglobal("LootLinkItem"..iItem); if( itemIndex < DisplayIndices.onePastEnd ) then local value = DisplayIndices[itemIndex][2]; --LootLink_GetValue(DisplayIndices[itemIndex]); if ( not value ) then LootLink_Refresh(); return; end if ( value ) then local color = { }; local name = DisplayIndices[itemIndex][1]; --LootLink_GetName(DisplayIndices[itemIndex]); local index = DisplayIndices[itemIndex][3]; local extra = getglobal(lootLinkItem:GetName().."Type"); lootLinkItem.i = nil; local text = getglobal(lootLinkItem:GetName().."Text"); local indexed = getglobal(lootLinkItem:GetName().."Indexed"); text:SetPoint( "LEFT", 20, 0 ); text:SetWidth(275); getglobal(lootLinkItem:GetName().."Indexed"):Hide(); if ( index and index > 0 ) then lootLinkItem.i = index; if ( DisplayIndices[itemIndex-1] and DisplayIndices[itemIndex-1][1] == name ) then local _,_, enchant, bonus = string.find(value.i, "%d+:(%d+):(%d+):%d+"); bonus = tonumber(bonus); enchant = tonumber(enchant); if ( bonus > 0 or enchant > 0 ) then text:SetPoint( "LEFT", 30, 0 ); text:SetWidth(245); indexed:Show(); end if ( enchant > 0 ) then indexed:SetVertexColor(0,1,0); else indexed:SetVertexColor(1,1,1); end end end local link = "item:"..value.i; local realname if ( link ) then realname = GetItemInfo( link ); end local onserver = LootLink_CheckItemServer(value, lServerIndex); extra:SetVertexColor(1.0,1.0,1.0) SetDesaturation(extra,0); extra:SetAlpha(1.0); if ( not onserver ) then extra:SetTexture("Interface\\GossipFrame\\TaxiGossipIcon"); extra:Show(); elseif ( not realname ) then extra:SetTexture("Interface\\GossipFrame\\ActiveQuestIcon"); extra:SetAlpha(0.5); SetDesaturation(extra,1); extra:Show(); elseif ( not LootLink_ValidateItem(name, link) ) then extra:SetTexture("Interface\\GossipFrame\\AvailableQuestIcon"); extra:SetVertexColor(1.0,0,0) extra:Show(); else extra:Hide(); end lootLinkItem:SetText(name); if( value.c ) then color.r, color.g, color.b = LootLink_GetRGBFromHexColor(value.c); lootLinkItem:SetTextColor(color.r, color.g, color.b); lootLinkItem.r = color.r; lootLinkItem.g = color.g; lootLinkItem.b = color.b; else lootLinkItem.r = 0; lootLinkItem.g = 0; lootLinkItem.b = 0; end if ( LootLinkFrame.qs and LootLinkFrame.qs == itemIndex ) then if ( color ) then LootLinkHighlight:SetVertexColor(color.r, color.g, color.b); else LootLinkHighlight:SetVertexColor(0.5, 0.5, 0.5); end lootLinkItem:SetTextColor(HIGHLIGHT_FONT_COLOR.r, HIGHLIGHT_FONT_COLOR.g, HIGHLIGHT_FONT_COLOR.b); LootLinkHighlightFrame:SetPoint("TOPLEFT", "LootLinkItem"..iItem, "TOPLEFT", 0, 0); LootLinkHighlightFrame:Show(); end lootLinkItem:Show(); if( LootLinkFrame.TooltipButton and LootLinkFrame.TooltipButton == iItem ) then if( value.i ) then local link = LootLink_GetHyperlink(name, index); LootLinkTooltip:SetOwner(lootLinkItem, "ANCHOR_BOTTOMRIGHT"); LootLink_SetHyperlink(LootLinkTooltip, name, link, index); LootLink_AddTooltipInfo(name); LootLink_AddTooltipTexture(link); LootLinkTooltip:Show(); else LootLinkItemButton_OnLeave(); end end end else lootLinkItem:Hide(); end end end function LootLink_Search() LootLinkSearchFrame:Show(); end function LootLink_Refresh() FauxScrollFrame_SetOffset(LootLinkListScrollFrame, 0); getglobal("LootLinkListScrollFrameScrollBar"):SetValue(0); LootLink_BuildDisplayIndices(); LootLink_Update(); end function LootLink_DoSearch() LootLink_Refresh(); end function LootLinkSearch_LoadValues() local sp = LootLinkFrame.SearchParams; local field; if( LootLinkState.LightMode ) then getglobal("LLS_TextDisabled"):Show(); getglobal("LLS_TextEditBox"):Hide(); getglobal("LLS_NameEditBox"):SetFocus(); else getglobal("LLS_TextDisabled"):Hide(); field = getglobal("LLS_TextEditBox"); field:Show(); field:SetFocus(); if( sp and sp.text ) then field:SetText(sp.text); else field:SetText(""); end end field = getglobal("LLS_NameEditBox"); if( sp and sp.name ) then field:SetText(sp.name); else field:SetText(""); end UIDropDownMenu_Initialize(LLS_RarityDropDown, LLS_RarityDropDown_Initialize); if( sp and sp.rarity ) then LootLink_UIDropDownMenu_SetSelectedID(LLS_RarityDropDown, sp.rarity, LLS_RARITY_LIST); else LootLink_UIDropDownMenu_SetSelectedID(LLS_RarityDropDown, 1, LLS_RARITY_LIST); end if( sp and sp.server ) then getglobal("LLS_ServerCheckButton"):SetChecked(1); else getglobal("LLS_ServerCheckButton"):SetChecked(0); end UIDropDownMenu_Initialize(LLS_BindsDropDown, LLS_BindsDropDown_Initialize); if( sp and sp.binds ) then LootLink_UIDropDownMenu_SetSelectedID(LLS_BindsDropDown, sp.binds, LLS_BINDS_LIST); else LootLink_UIDropDownMenu_SetSelectedID(LLS_BindsDropDown, 1, LLS_BINDS_LIST); end if( sp and sp.unique ) then getglobal("LLS_UniqueCheckButton"):SetChecked(1); else getglobal("LLS_UniqueCheckButton"):SetChecked(0); end UIDropDownMenu_Initialize(LLS_LocationDropDown, LLS_LocationDropDown_Initialize); if( sp and sp.location ) then LootLink_UIDropDownMenu_SetSelectedID(LLS_LocationDropDown, sp.location, LLS_LOCATION_LIST); else LootLink_UIDropDownMenu_SetSelectedID(LLS_LocationDropDown, 1, LLS_LOCATION_LIST); end if( LootLinkState and LootLinkState.spoof ) then LLS_SpoofCheckButton:Hide(); else LLS_SpoofCheckButton:Show(); if( sp and sp.spoof ) then getglobal("LLS_SpoofCheckButton"):SetChecked(1); else getglobal("LLS_SpoofCheckButton"):SetChecked(0); end end if( sp and sp.usable ) then getglobal("LLS_UsableCheckButton"):SetChecked(1); else getglobal("LLS_UsableCheckButton"):SetChecked(0); end field = getglobal("LLS_MinimumLevelEditBox"); if( sp and sp.minLevel ) then field:SetText(sp.minLevel); else field:SetText(""); end field = getglobal("LLS_MaximumLevelEditBox"); if( sp and sp.maxLevel ) then field:SetText(sp.maxLevel); else field:SetText(""); end UIDropDownMenu_Initialize(LLS_TypeDropDown, LLS_TypeDropDown_Initialize); if( sp and sp.type ) then LootLink_UIDropDownMenu_SetSelectedID(LLS_TypeDropDown, sp.type, LLS_TYPE_LIST); else LootLink_UIDropDownMenu_SetSelectedID(LLS_TypeDropDown, 1, LLS_TYPE_LIST); end if( sp and sp.type ) then LootLink_SetupTypeUI(sp.type, sp.subtype); else LootLink_SetupTypeUI(1, 1); end field = getglobal("LLS_MinimumDamageEditBox"); if( sp and sp.minMinDamage ) then field:SetText(sp.minMinDamage); else field:SetText(""); end field = getglobal("LLS_MaximumDamageEditBox"); if( sp and sp.minMaxDamage ) then field:SetText(sp.minMaxDamage); else field:SetText(""); end field = getglobal("LLS_MaximumSpeedEditBox"); if( sp and sp.maxSpeed ) then field:SetText(sp.maxSpeed); else field:SetText(""); end field = getglobal("LLS_MinimumDPSEditBox"); if( sp and sp.minDPS ) then field:SetText(sp.minDPS); else field:SetText(""); end field = getglobal("LLS_MinimumArmorEditBox"); if( sp and sp.minArmor ) then field:SetText(sp.minArmor); else field:SetText(""); end field = getglobal("LLS_MinimumBlockEditBox"); if( sp and sp.minBlock ) then field:SetText(sp.minBlock); else field:SetText(""); end field = getglobal("LLS_MinimumSlotsEditBox"); if( sp and sp.minSlots ) then field:SetText(sp.minSlots); else field:SetText(""); end field = getglobal("LLS_MinimumSkillEditBox"); if( sp and sp.minSkill ) then field:SetText(sp.minSkill); else field:SetText(""); end field = getglobal("LLS_MaximumSkillEditBox"); if( sp and sp.maxSkill ) then field:SetText(sp.maxSkill); else field:SetText(""); end --[[if( sp and sp.set ) then getglobal("LLS_SetCheckButton"):SetChecked(1); else getglobal("LLS_SetCheckButton"):SetChecked(0); end]] end function LootLinkSearch_SaveValues() local sp; local interesting; local field; local value; LootLinkFrame.SearchParams = { }; sp = LootLinkFrame.SearchParams; field = getglobal("LLS_TextEditBox"); value = field:GetText(); if( value and value ~= "" ) then sp.text = value; interesting = 1; end field = getglobal("LLS_NameEditBox"); value = field:GetText(); if( value and value ~= "" ) then sp.name = value; interesting = 1; end value = UIDropDownMenu_GetSelectedID(LLS_RarityDropDown); if( value and value ~= 1 ) then sp.rarity = value; interesting = 1; end field = getglobal("LLS_ServerCheckButton"); value = field:GetChecked(); if( value ) then sp.server = value; interesting = 1; end value = UIDropDownMenu_GetSelectedID(LLS_BindsDropDown); if( value and value ~= 1 ) then sp.binds = value; interesting = 1; end field = getglobal("LLS_UniqueCheckButton"); value = field:GetChecked(); if( value ) then sp.unique = value; interesting = 1; end value = UIDropDownMenu_GetSelectedID(LLS_LocationDropDown); if( value and value ~= 1 ) then sp.location = value; interesting = 1; end field = getglobal("LLS_UsableCheckButton"); value = field:GetChecked(); if( value ) then sp.usable = value; interesting = 1; end field = getglobal("LLS_MinimumLevelEditBox"); value = field:GetText(); if( value and LootLink_CheckNumeric(value) ) then sp.minLevel = tonumber(value); interesting = 1; end field = getglobal("LLS_MaximumLevelEditBox"); value = field:GetText(); if( value and LootLink_CheckNumeric(value) ) then sp.maxLevel = tonumber(value); interesting = 1; end value = UIDropDownMenu_GetSelectedID(LLS_TypeDropDown); if( value and value ~= 1 ) then sp.type = value; interesting = 1; end field = getglobal("LLS_SpoofCheckButton"); value = field:GetChecked(); if( value ) then sp.spoof = value; interesting = 1; end value = UIDropDownMenu_GetSelectedID(LLS_SubtypeDropDown); if( value and value ~= 1 ) then sp.subtype = value; if( sp.type and sp.type ~= 1 ) then interesting = 1; end end field = getglobal("LLS_MinimumDamageEditBox"); value = field:GetText(); if( value and LootLink_CheckNumeric(value) ) then sp.minMinDamage = tonumber(value); interesting = 1; end field = getglobal("LLS_MaximumDamageEditBox"); value = field:GetText(); if( value and LootLink_CheckNumeric(value) ) then sp.minMaxDamage = tonumber(value); interesting = 1; end field = getglobal("LLS_MaximumSpeedEditBox"); value = field:GetText(); if( value and LootLink_CheckNumeric(value) ) then sp.maxSpeed = tonumber(value); interesting = 1; end field = getglobal("LLS_MinimumDPSEditBox"); value = field:GetText(); if( value and LootLink_CheckNumeric(value) ) then sp.minDPS = tonumber(value); interesting = 1; end field = getglobal("LLS_MinimumArmorEditBox"); value = field:GetText(); if( value and LootLink_CheckNumeric(value) ) then sp.minArmor = tonumber(value); interesting = 1; end field = getglobal("LLS_MinimumBlockEditBox"); value = field:GetText(); if( value and LootLink_CheckNumeric(value) ) then sp.minBlock = tonumber(value); interesting = 1; end field = getglobal("LLS_MinimumSlotsEditBox"); value = field:GetText(); if( value and LootLink_CheckNumeric(value) ) then sp.minSlots = tonumber(value); interesting = 1; end field = getglobal("LLS_MinimumSkillEditBox"); value = field:GetText(); if( value and LootLink_CheckNumeric(value) ) then sp.minSkill = tonumber(value); interesting = 1; end field = getglobal("LLS_MaximumSkillEditBox"); value = field:GetText(); if( value and LootLink_CheckNumeric(value) ) then sp.maxSkill = tonumber(value); interesting = 1; end --[[value = getglobal("LLS_SetCheckButton"):GetChecked(); if ( value ) then sp.set = value; interesting = 1; end]] -- Only save search params if we had interesting data on the page if( not interesting ) then LootLinkFrame.SearchParams = nil; else if( IsControlKeyDown() ) then sp.plain = nil; else sp.plain = 1; end end end function LootLinkSearchFrame_SaveSearchParams() LootLinkSearchFrame.OldSearchParams = LootLinkFrame.SearchParams; end function LootLinkSearchFrame_RestoreSearchParams() LootLinkFrame.SearchParams = LootLinkSearchFrame.OldSearchParams; end function LootLinkSearchFrame_ChangeFocus() local frames = {"LLS_TextEditBox", "LLS_NameEditBox", "LLS_MinimumLevelEditBox", "LLS_MaximumLevelEditBox", "LLS_MinimumSlotsEditBox", "LLS_MinimumDamageEditBox", "LLS_MaximumDamageEditBox", "LLS_MaximumSpeedEditBox", "LLS_MinimumDPSEditBox", "LLS_MinimumArmorEditBox", "LLS_MinimumBlockEditBox", "LLS_MinimumSkillEditBox", "LLS_MaximumSkillEditBox", } local name = this:GetName(); for i=1, 13 do if ( frames[i] == name ) then if ( IsShiftKeyDown() ) then i = i-1; else i = i+1; end while frames[i] do local nextframe = getglobal(frames[i]); if ( nextframe:IsVisible() ) then nextframe:SetFocus(); return; end if ( IsShiftKeyDown() ) then i = i-1; else i = i+1; end end return; end end end function LootLinkSearchFrame_Cancel() PlaySound("gsTitleOptionExit"); LootLinkSearchFrame:Hide(); LootLinkSearchFrame_RestoreSearchParams(); end function LootLinkSearchFrame_Okay() PlaySound("gsTitleOptionOK"); LootLinkSearchFrame:Hide(); LootLinkSearch_SaveValues(); LootLink_DoSearch(); end function LootLinkQuickSearch_Search(Next) if ( not Next ) then Next = 0; end local text = LootLinkFrameQuickSearch:GetText(); local length = string.len( text ); local last = LootLinkFrameQuickSearch.last; local function DoScroll(i) local offset = FauxScrollFrame_GetOffset(LootLinkListScrollFrame); if ( i < offset or i > offset + LOOTLINK_ITEMS_SHOWN ) then if ( i > DisplayIndices.onePastEnd - LOOTLINK_ITEMS_SHOWN ) then i = DisplayIndices.onePastEnd - LOOTLINK_ITEMS_SHOWN; end i = i-1; local val = (i * 16) - 0.5 LootLinkListScrollFrameScrollBar:SetValue(val); LootLinkListScrollFrame.offset = i; end end local function DoSearch(index, offset) if ( not offset ) then offset = getn(DisplayIndices); end for i=index+Next, offset do local name = LootLink_GetName(DisplayIndices[i]); if ( name and string.find( string.lower(name), string.lower(text), 1, 1 ) ) then LootLinkFrame.qs = i; DoScroll(i); return 1; end end end if ( text and text ~= "" ) then if ( not last or length < last ) then if ( not DoSearch(LootLinkFrame.qs) and not DoSearch(1, LootLinkFrame.qs+1) ) then LootLinkFrameQuickSearch.last = length; LootLinkFrameQuickSearch:SetTextColor(1,0,0); LootLinkFrame.qs = 0; else LootLinkFrameQuickSearch:SetTextColor(1,1,1); LootLinkFrameQuickSearch.last = nil; end end else LootLinkFrame.qs = 0; LootLinkFrameQuickSearch:SetTextColor(1,1,1); end LootLink_Update(); end function LootLinkQuickSearch_FullSearch() local name = this:GetText(); if ( name ) then if ( name == "" ) then name = nil; end if ( name and not LootLinkFrame.SearchParams ) then LootLinkFrame.SearchParams = { }; end if ( LootLinkFrame.SearchParams ) then LootLinkFrame.SearchParams.name = name; end end LootLink_DoSearch(); LootLinkFrameQuickSearch:SetText(name or ""); end function LootLinkQuickSearch_Clear() LootLinkFrameQuickSearch:SetText(""); LootLinkFrameQuickSearch.last = nil; end LootLinkOptionsList = { hideminimap = { frame = "LLO_HideMinimap", flip = 1 }, lockminimap = { frame = "LLO_LockMinimap" }, hideicon = { frame = "LLO_IconCheckButton", flip = 1 }, HideInfo = { frame = "LLO_ExtraInfoCheckButton", flip = 1 }, mouseover = { frame = "LLO_MouseoverCheckButton" }, typelinks = { frame = "LLO_TypeLinksCheckButton" }, autotypelinks = { frame = "LLO_AutoTypeLinksCheckButton", dependancy = "typelinks" }, server = { frame = "LLO_ServerCheckButton", refresh = 1 }, spoof = { frame = "LLO_SpoofCheckButton", refresh = 1 }, nosame = { frame = "LLO_SameNameCheckButton", flip = 1, refresh = 1 }, novariants = { frame = "LLO_VariantCheckButton", flip = 1, dependancy = "nosame", refresh = 1 }, enchants = { frame = "LLO_EnchantsCheckButton", flip = 1, dependancy = "nosame", refresh = 1 }, x = { frame = "LLO_MinimapPos", slider = LLO_MINIMAP_X}, y = { frame = "LLO_MinimapOffset", slider = LLO_MINIMAP_Y }, rarityfilter = { frame = "LLO_RarityDropDown", dropdown = {LLO_RarityDropDown_Initialize, 2, LLO_RARITY_LIST}, refresh = 1 }, }; function LootLinkOptions_LoadValues() local op = LootLinkState; for k, v in LootLinkOptionsList do local frame = getglobal( v.frame ); if ( frame ) then if ( v.dropdown ) then UIDropDownMenu_Initialize(frame, v.dropdown[1]); LootLink_UIDropDownMenu_SetSelectedID(frame, 1, v.dropdown[3]); end local value = op[k]; if value then if ( v.slider ) then frame:SetValue( value ); LootLinkOptionsFrame[k] = value; elseif ( v.dropdown ) then LootLink_UIDropDownMenu_SetSelectedID(frame, value+v.dropdown[2], v.dropdown[3]); end elseif ( v.slider ) then frame:SetValue( v.slider ); LootLinkOptionsFrame[k] = nil; end if ( not v.slider and not v.dropdown ) then if ( v.flip ) then frame:SetChecked(1-(value or 0)) else frame:SetChecked(value) end end if ( v.dependancy ) then if ( getglobal(LootLinkOptionsList[v.dependancy].frame):GetChecked() ) then frame:Enable(); getglobal(frame:GetName().."Text"):SetTextColor(1, 0.82, 0); else frame:Disable(); getglobal(frame:GetName().."Text"):SetTextColor(0.3, 0.3, 0.3); end end else ChatFrame1:AddMessage(k.." no frame?"); end end LootLinkOptionsFrame.needsave = 1; end function LootLinkOptions_SaveValues() local op = LootLinkState; local refresh; for k, v in LootLinkOptionsList do local frame = getglobal( v.frame ); if ( frame ) then local value = op[k]; if ( v.dropdown ) then value = UIDropDownMenu_GetSelectedID(frame); if ( value == 1 ) then value = nil; else value = value - (v.dropdown[2] or 0); end elseif ( not v.slider ) then local checked = frame:GetChecked(); if ( v.flip ) then if ( checked ) then checked = nil else checked = 1; end end value = checked; end if ( v.refresh and op[k] ~= value ) then refresh = 1; end op[k] = value; else ChatFrame1:AddMessage(k.." no frame?"); end end if ( refresh ) then LootLink_Refresh(); end LootLinkOptionsFrame.needsave = nil; end function LootLinkOptionsFrame_Defaults() local op = LootLinkState; for k, v in LootLinkOptionsList do op[k] = nil; end LootLinkOptions_LoadValues(); end function LootLinkOptionsFrame_Cancel() if ( LootLinkOptionsFrame.needsave ) then LootLinkOptionsFrame.needsave = nil; if ( LootLinkOptionsFrame.x or LootLinkOptionsFrame.y ) then LootLink_Minimap_Position( LootLinkOptionsFrame.x, LootLinkOptionsFrame.y ); end end PlaySound("gsTitleOptionExit"); LootLinkOptionsFrame:Hide(); end function LootLinkOptionsFrame_Okay() PlaySound("gsTitleOptionOK"); LootLinkOptions_SaveValues(); LootLinkOptionsFrame:Hide(); end function LootLink_CloseWindows(ignoreCenter) if ( LootLinkOptionsFrame:IsVisible() ) then LootLinkOptionsFrame_Cancel(); return 1; end if ( LootLinkSearchFrame:IsVisible() ) then LootLinkSearchFrame_Cancel(); return 1; end return lOriginal_CloseWindows(ignoreCenter); end function LootLink_ValidateItem(name, link, fixlink) -- Basic validation of enchants and stat bonuses -- todo : more complex evaluation of the stat bonuses taking in to account item level and base stats if ( not link ) then return nil; elseif ( string.sub(link, 1, 5) ~= "item:" ) then link = "item:"..link; end local realname, ilink, quality, level, sType, sSubType, count, equip = GetItemInfo(link); if ( realname ) then if ( realname ~= name ) then -- quickly kill anything that's really wrong. if ( not fixlink ) then return nil; end name = realname; end local _,_, itemid, enchant, stat = string.find( link, "(%d+):(%d+):(%d+):%d+" ); if ( tonumber(stat) > 0 ) then if ( quality ~= 2 and quality ~= 3 and itemid ~= "20039" ) then -- special case for Dark Iron Boots, grr. -- Nothing but Uncommon and Rare items have stat bonuses if ( not fixlink ) then return nil; end stat = 0; elseif( lNoBonuses[equip] == 2 ) then -- Nothing that can't be equiped can't have stat bonuses if ( not fixlink ) then return nil; end stat = 0; end end if ( tonumber(enchant) > 0 ) then if ( lNoBonuses[equip] ) then -- Nothing that isn't armor or weapons can have enchants if ( not fixlink ) then return nil; end enchant = 0; end end return name, itemid..":"..enchant..":"..stat..":0"; else if ( not fixlink ) then return 1; end return name, string.sub(link,6); end end -------------------------------------------------------------------------------------------------- -- Clean and Purge functions -------------------------------------------------------------------------------------------------- local LLPtodo; local LLPvars; function LootLink_CheckDuplicate(name) local links = {}; local value = ItemLinks[name]; local dups = 0; if ( value and value.m ) then links[value.i] = 1; local i = 1; --while value and value.m and value.m[i] do for i=getn(value.m), 1, -1 do if ( links[value.m[i].i] ) then dups = dups + 1; LootLink_RemoveItem(name, nil, nil, i); else links[value.m[i].i] = 1; end end end return dups; end function LootLink_Process_Cancel() LootLink_Process:Hide(); LootLink_Process:SetScript("OnUpdate", nil); LootLink_Process.fade = nil; LootLinkFrame.process = nil; LootLink_Process_ToggleButtons(1); if ( LLPtodo ) then local text = getglobal("LLP_"..LLPvars.functype.."_CANCEL"); text = string.gsub( text, "%%r", LLPvars.total-LLPvars.done ); text = string.gsub( text, "%%u", LLPvars.updated ); text = string.gsub( text, "%%t", LLPvars.total ); text = string.gsub( text, "%%f", LLPvars.failed ); DEFAULT_CHAT_FRAME:AddMessage(text); LootLink_Process_Time:SetText(""); LLPvars = nil; LLPtodo = nil; end end function LootLink_Process_Memory() LootLink_Process_Cancel(); DEFAULT_CHAT_FRAME:AddMessage(LLP_ABORT1); DEFAULT_CHAT_FRAME:AddMessage(LLP_ABORT2); end function LootLink_Process_ToggleButtons(on) if ( on ) then LLO_Purge:Enable(); LLO_Fix:Enable(); else LLO_Purge:Disable(); LLO_Fix:Disable(); end end function LootLinkProcess_PURGE() local todo = LLPtodo[LLPvars.done+1]; local name, link = todo[1], todo[2]; local _,_,itemid,enchantid,suffixid=string.find(link, "(%d+):(%d+):(%d+):%d+"); LootLink_SortAlternates(name); local op = LootLinkState; local value = ItemLinks[name]; if ( value and value.m ) then if ( op.nosame or (op.novariants and tonumber(suffixid) > 0) ) then LLPvars.updated = LLPvars.updated + getn(value.m); value.m = nil; end if ( op.enchants ) then if ( tonumber(enchantid) > 0 ) then if ( LootLink_RemoveItem(name, link) ) then return 1; end end end end value = LootLink_GetValue(name, link); if ( not value ) then return 2; end if ( value.c and op.rarityfilter ) then local rarity = lRarityFilter[value.c]; if ( rarity and rarity < op.rarityfilter ) then if ( LootLink_RemoveItem(name, link) ) then return 1; end end end if ( LootLinkState.spoof ) then local valid = LootLink_ValidateItem(name, link) if ( not valid ) then if ( LootLink_RemoveItem(name, link) ) then return 1; end elseif ( valid == 1 ) then return 2; end end end function LootLinkProcess_FIXCACHE() local todo = LLPtodo[LLPvars.done+1]; local name, link, index = todo[1], todo[2], todo[3]; local realname = GetItemInfo("item:"..link); local value = LootLink_GetValue( name, nil, link ); if ( not value ) then return; end value.i = string.gsub(link,"(%d+:%d+:%d+:)%d+","%10"); if ( realname ) then if ( LootLink_BuildSearchData( name, value, index ) == 2 ) then return 1; end elseif ( not value.t or value.t == "" ) then value.t = name.."·Bad Search Data·"; return 1; else local changes; if ( value.t ) then local t = value.t; local d = value.d; local s, e, val = string.find(t, "^(.-)·"); if ( not val or not string.find(name, val) ) then return 2; end if ( string.find(t, "%/") ) then -- cull out durability data. t = string.gsub(t, "Durability %d+ %/ %d+·", ""); -- check set data. s,e,val = string.find(t, ".+·(.-) %(%d%/%d+%)·.+"); if ( val ) then t = string.gsub(t, "(.+ %()%d+(%/%d+%)·.+)", "%10%2"); local newset = LootLink_GetSet(val); local setnum = LL_SearchData(value, "se"); if ( d ) then if ( not setnum ) then d = d.."se"..newset.."·"; changes = 1; elseif( newset ~= setnum ) then d = string.gsub(d, "(.+·se)%d+(·.+)", "%1"..newset.."%2"); changes = 1; end end end end if ( value.t ~= t ) then changes = 1; end value.t = t; value.d = d; end if ( changes ) then return 1; end return 2; end end function LootLinkProcess_Process(func) if ( not LLPvars ) then LootLink_Process_ToggleButtons(); LLPvars = {}; LLPvars.func = getglobal( "LootLinkProcess_"..func ); if ( not LLPvars.func ) then LootLink_Process_ToggleButtons(1); preprocess = nil; return; end HideUIPanel(LootLinkFrame); LootLink_Process_Header:SetText(getglobal("LLP_"..func.."_LABEL") ); LootLink_Process_Num:SetText("Preprocessing"); LootLink_Process_Bar:SetMinMaxValues(0,1); LootLink_Process_Bar:SetValue(0); LLPvars.functype = func; LLPvars.elapsed = 0; LLPvars.done = 0; LLPvars.updated = 0; LLPvars.failed = 0; LLPvars.wait = 0; LLPvars.total = 0; LootLink_Process:SetAlpha( 1 ); LootLink_Process:Show(); LootLinkFrame.process = func; end end function LootLinkProcess_Run() local thisprocess = 0; local func = LLPvars.func; if ( LLP_OUTOFMEM ) then LootLink_Process_Memory(); end if ( LLPvars.wait ) then LLPvars.wait = LLPvars.wait - arg1; if ( LLPvars.wait <= 0 ) then LLPvars.wait = nil; end return; end if ( not LLPtodo ) then local normal, mult = 0,0; LLPtodo = {}; for k, v in ItemLinks do if ( strlen(k) == 0 ) then ItemLinks[k] = nil; LLPvars.updated = LLPvars.updated + 1; elseif ( v.i ) then normal = normal + 1; tinsert( LLPtodo, {k, v.i} ); if ( GetItemInfo("item:"..v.i) ) then LLHiddenTooltip:SetHyperlink("item:"..v.i); end if ( v.m ) then for i=1, getn(v.m) do if v.m[i].i then mult = mult + 1; tinsert( LLPtodo, {k, v.m[i].i, i} ); if ( GetItemInfo("item:"..v.m[i].i) ) then LLHiddenTooltip:SetHyperlink("item:"..v.m[i].i); end else LootLink_RemoveItem(k, i); LLPvars.updated = LLPvars.updated + 1; end end end else LootLink_RemoveItem(k); LLPvars.updated = LLPvars.updated + 1; end end LLPvars.total = normal + mult; LootLink_Process_Bar:SetValue(0) LootLink_Process_Bar:SetMinMaxValues(0, LLPvars.total) LLPvars.wait = 2; return; end local todo = LLPtodo; LLPvars.elapsed = LLPvars.elapsed + arg1; while todo and todo[LLPvars.done+1] do local ret = func(); if ( ret ) then if ( ret == 1 ) then LLPvars.updated = LLPvars.updated + 1; else LLPvars.failed = LLPvars.failed + 1; end end LLPvars.updated = LLPvars.updated + LootLink_CheckDuplicate(todo[LLPvars.done+1][1]); LootLink_SortAlternates(todo[LLPvars.done+1][1]); thisprocess = thisprocess + 1; LLPvars.done = LLPvars.done + 1; if ( thisprocess == 50 ) then thisprocess = 0; LootLink_Process_Bar:SetValue( LLPvars.done ); LootLink_Process_Num:SetText( LLPvars.done .. "/".. LLPvars.total ); local time = (LLPvars.elapsed / LLPvars.done ) * (LLPvars.total - LLPvars.done); local s = mod( time, 60 ); local m = (time-s) / 60; s = floor(s) if ( strlen(s) == 1 ) then s = "0"..s; end LootLink_Process_Time:SetText( "Estimated "..m..":"..s.." remaining" ); if ( DisplayIndices and LootLinkFrame:IsVisible() ) then LootLink_Update(); LootLink_SetTitle(); end return; end end if ( todo and not todo[LLPvars.done+1] ) then local text = getglobal("LLP_"..LLPvars.functype.."_END"); text = string.gsub( text, "%%u", LLPvars.updated ); text = string.gsub( text, "%%t", LLPvars.total ); text = string.gsub( text, "%%f", LLPvars.failed ); DEFAULT_CHAT_FRAME:AddMessage(text); LootLink_Process_Time:SetText(""); LootLink_Process_Num:SetText("Finished"); LootLink_Process_Bar:SetValue(LLPvars.total); LootLink_Process_ToggleButtons(1); LLPvars = nil; LLPtodo = nil; LootLinkFrame.process = nil; LootLink_Process.fade = 2; LootLink_Process:SetScript("OnUpdate", LootLink_Process_Fade); end end function LootLink_Process_Fade() if ( LootLink_Process.fade ) then LootLink_Process.fade = LootLink_Process.fade - (arg1 / 2); if ( LootLink_Process.fade < 1 ) then this:SetAlpha( LootLink_Process.fade ); if ( LootLink_Process.fade <= 0 ) then this:Hide(); LootLink_Process.fade = nil; LootLink_Process:SetScript("OnUpdate", nil); end end end end -------------------------------------------------------------------------------------------------- -- External functions -------------------------------------------------------------------------------------------------- -- Looks for and deconstructs links contained in a text string; if trusted, existing information will be verified -- Trusted sources are everything except chat links; since these can be spoofed, we want to allow them to be replaced function LootLink_ProcessLinks(text, trusted) local enchants, spoof, nosame, rarityfilter, lastName = LootLinkState.enchants, LootLinkState.spoof, LootLinkState.nosame, LootLinkState.rarityfilter; if( text ) then for color, item, name in string.gfind(text, "|c(%x+)|Hitem:(%d+:%d+:%d+:%d+)|h%[(.-)%]|h|r") do if ( item ~= "0:0:0:0" and ( not rarityfilter or lRarityFilter[color] > rarityfilter ) and LootLink_CheckSame(name, item) ) then local fixedname, fixeditem; if ( not trusted ) then if ( spoof ) then name, item = LootLink_ValidateItem(name, item, 1); else fixedname, fixeditem = LootLink_ValidateItem(name, item, 1); if ( fixedname and fixedname ~= name ) then LootLink_AddItem(fixedname, fixeditem, color, trusted); end end end local enchant; if ( not enchants and not nosame ) then enchant = string.gsub(item, "^(%d+):(%d+):(%d+):%d+$", "%1:%2:%3:0"); end item = string.gsub(item, "^(%d+):%d+:(%d+):%d+$", "%1:0:%2:0"); LootLink_AddItem(name, item, color, trusted); if ( enchant and enchant ~= item ) then LootLink_AddItem(name, enchant, color, trusted); end LootLink_SortAlternates(name); lastName = name; end end end return lastName; end function LootLink_CheckSame(name, item) local _,_,itemid,suffixid=string.find(item, "(%d+):%d+:(%d+):%d+"); local value = ItemLinks[name]; if ( value and (LootLinkState.nosame or LootLinkState.novariants) ) then -- Assumes no same name items are both different itemid and have suffixids if ( value.i and value.i ~= item ) then for id, suf in string.gfind( value.i, "(%d+):%d+:(%d+):") do if ( nosame and tonumber(id) > tonumber(itemid) ) then return; elseif ( tonumber(suf) > tonumber(suffixid) ) then return; end end end end return 1; end -- old filter function, keeping for posterity --[[function LootLink_FilterCheck(name, item, color) if ( item == "0:0:0:0" ) then return; end local op = LootLinkState; if ( op.rarityfilter ) then local rarity = lRarityFilter[color]; if ( rarity and rarity < op.rarityfilter ) then return; end end local _,_,itemid,enchantid,suffixid=string.find(item, "(%d+):(%d+):(%d+):%d+"); if ( not tonumber(enchantid) ) then --ChatFrame1:AddMessage((name or "??").." no enchant id "..(item or "???")); return; end local value = ItemLinks[name]; if ( value and (op.nosame or op.novariants) ) then -- Assumes no same name items are both different itemid and have suffixids if ( value.i and value.i ~= item ) then for id, suf in string.gfind( value.i, "(%d+):%d+:(%d+):") do if ( op.nosame and tonumber(id) > tonumber(itemid) ) then return; elseif ( tonumber(suf) > tonumber(suffixid) ) then return; end end end end return 1; end]] -- Add items to the database function LootLink_AddItem(name, link, color, trusted) local nosame, value, index = LootLinkState.nosame; if ( name and link ) then if ( not ItemLinks[name] ) then ItemLinks[name] = { }; lItemLinksSizeTotal = lItemLinksSizeTotal + 1; if( LootLink_GetDataVersion() < 110 ) then -- Set a flag to indicate that this item is new and should be skipped on a makehome ItemLinks[name]._ = 1; end end value = ItemLinks[name]; if ( not nosame and LootLinkState.novariants ) then for suffixid in string.gfind(link, "%d+:%d+:(%d+):%d+") do if ( tonumber(suffixid) > 0 ) then nosame = 1; end end end if ( not nosame and value.i and value.i ~= link ) then if ( value.m and getn(value.m) > 0 ) then for k, v in value.m do if ( v.i == link ) then if ( not trusted ) then return nil; end index = k; break; end end end if ( index ) then value = value.m[index]; else if ( not value.m ) then value.m = {}; end tinsert( value.m, {} ); index = getn(value.m); value = value.m[index]; lItemLinksSizeTotal = lItemLinksSizeTotal + 1; end end value.i = link; value.c = color; LootLink_BuildSearchData(name, value, index); if( not LootLink_CheckItemServerRaw(value, lServerIndex) ) then LootLink_AddItemServer(value, lServerIndex); end return value; end end -- Sort same name items by ids function LootLink_SortAlternates(name) if ( not name ) then return; end local value = ItemLinks[name]; local newval; local values = {}; local itemval = {}; -- cleaned up by Adrine function sortByLink(e1, e2) if ( not e1 ) then return true; elseif ( not e2 ) then return false; end local _,_,a,b,c = string.find(e1.i, "(%d+):(%d+):(%d+)"); local _,_,d,e,f = string.find(e2.i, "(%d+):(%d+):(%d+)"); if a ~= d then return tonumber(a) > tonumber(d); end if c ~= f then return tonumber(c) > tonumber(f); end if b ~= e then return tonumber(b) < tonumber(e); end return false; end if value and value.m then if ( getn(value.m) == 0 ) then value.m = nil; return nil; end for k, v in value do if ( k ~= "m" ) then itemval[k] = v; end end tinsert( values, itemval ); for k, v in value.m do tinsert( values, v ); end table.sort(values, sortByLink); newval = values[1]; newval.m = {}; for i=2, getn(values) do tinsert( newval.m, values[i] ); end else return nil; end ItemLinks[name] = {}; ItemLinks[name] = newval; end -- Adds icon of the item if it's valid function LootLink_AddTooltipTexture(link) if ( link and not LootLinkState.hideicon) then local _, _, _, _, _, _, _, _, itemTexture = GetItemInfo(link); if ( itemTexture ) then local iItem = LootLinkFrame.TooltipButton or 0; LootLinkTooltipIconTexture:SetTexture( itemTexture ); LootLinkTooltipIcon:ClearAllPoints(); LootLinkTooltipIcon:SetPoint("BOTTOMLEFT", getglobal("LootLinkItem"..iItem), "BOTTOMRIGHT"); LootLinkTooltipIcon:Show(); return; end end LootLinkTooltipIcon:Hide(); end -- Adds extra tooltip information for the item with the given name function LootLink_AddTooltipInfo(name, tooltip, quantity, index) if( not tooltip ) then tooltip = LootLinkTooltip; end if( not quantity ) then quantity = 1; end if( tooltip == LootLinkTooltip ) then LootLink_HideTooltipMoney(); end if( not LootLinkState.HideInfo and ItemLinks[name] ) then if( tooltip == GameTooltip ) then GameTooltip.llDone = 1; end if( ItemLinks[name].p ) then LootLink_SetTooltipMoney(tooltip, quantity, ItemLinks[name].p, ItemLinks[name].x); end LootLink_AddExtraTooltipInfo(tooltip, name, quantity, ItemLinks[name]); tooltip:Show(); end end -- This will set up a tooltip with item information for the given name if it's known function LootLink_SetTooltip(tooltip, name, quantity) local link; if( tooltip and name ) then link = LootLink_GetHyperlink(name); if( link ) then LootLink_SetHyperlink(tooltip, name, link); if( quantity ) then quantity = tonumber(quantity); else quantity = 1; end if( quantity > 0 ) then LootLink_AddTooltipInfo(name, tooltip, quantity); end end end end -- function LootLink_ChatEdit_OnTabPressed() lOriginal_ChatEdit_OnTabPressed(); end function LootLink_ChatEdit_OnTextChanged() local text = this:GetText(); if (string.sub(text, 1, 7) ~= "/script" or string.sub(text, 1, 5) ~= "/dump") then --text = string.gsub(text, "([|]?[h]?)%[(.-)%]([|]?[h]?)", FLT_LinkifyName); --end end lOriginal_ChatEdit_OnTextChanged(); end -- Calling this will allow LootLink to automatically add information to tooltips when needed function LootLink_AutoInfoOn() lSuppressInfoAdd = nil; end -- Calling this will prevent LootLink from automatically adding information to tooltips function LootLink_AutoInfoOff() lSuppressInfoAdd = 1; end -- Use this function to get the current server name from LootLink's perspective function LootLink_GetCurrentServerName() return lServer; end -- Use this function to get the current server index function LootLink_GetCurrentServerIndex() return lServerIndex; end -- Use this function to map a server name to the server index for the ItemLinks[name].servers array function LootLink_GetServerIndex(name) if( not LootLinkState or not LootLinkState.ServerNamesToIndices ) then return nil; end return LootLinkState.ServerNamesToIndices[name]; end -- Use this function to check whether an ItemLinks[name] entry is valid for a given server index function LootLink_CheckItemServer(item, serverIndex) -- If we haven't converted and this item predates multiple server support, count it as valid if( LootLink_GetDataVersion() < 110 and not item._ ) then return 1; end return LootLink_CheckItemServerRaw(item, serverIndex); end -- Used for debugging changes in item data and tooltip format function LootLink_Validate() for index, value in ItemLinks do if( not value.d ) then DEFAULT_CHAT_FRAME:AddMessage(index.." has no search data"); -- else -- if( not LL_SearchData(value, "su") ) then -- DEFAULT_CHAT_FRAME:AddMessage(index.." has no subtype"); -- end end end end -------------------------------------------------------------------------------------------------- -- Hookable callback functions -------------------------------------------------------------------------------------------------- -- Hook this function to add any extra information you like to the tooltip function LootLink_AddExtraTooltipInfo(tooltip, name, quantity, item) -- tooltip: the current tooltip frame -- name: the name of the item -- quantity: the number of items, if known, else 1 -- item: ItemLinks[name]; LootLink's data for this item end -- Hook this function to be called whenever an equipment slot is successfully inspected function LootLink_Event_InspectSlot(name, count, item, unit, slotid) -- name: the name of the item -- count: the number of items, if known, else 1 -- item: ItemLinks[name]; LootLink's data for this item -- unit: "target", "player", etc. -- slotid: the id of the slot inspected end -- Hook this function to be called whenever an inventory slot is successfully inspected function LootLink_Event_ScanInventory(name, count, item, bagid, slotid) -- name: the name of the item -- count: the number of items, if known, else 1 -- item: ItemLinks[name]; LootLink's data for this item -- bagid: the id of the bag containing the item -- slotid: the id of the slot inspected end -- Hook this function to be called whenever a bank slot is successfully inspected function LootLink_Event_ScanBank(name, count, item, bagid, slotid) -- name: the name of the item -- count: the number of items, if known, else 1 -- item: ItemLinks[name]; LootLink's data for this item -- bagid: the id of the bag containing the item -- slotid: the id of the slot inspected end -- Hook this function to be called whenever an auction entry is successfully inspected function LootLink_Event_ScanAuction(name, count, item, auctionpage, auctionid) -- name: the name of the item -- count: the number of items, if known, else 1 -- item: ItemLinks[name]; LootLink's data for this item -- auctionpage: the page number this item was found on -- auctionid: the id of the inspected item end -- Hook this function to be called whenever a chat message is successfully inspected function LootLink_Event_ScanChat(name, item, text) -- name: the name of the last item in the chat message -- item: ItemLinks[name]; LootLink's data for this item -- text: the inspected chat message end -- Hook this function to be called whenever LootLink starts an auction scan function LootLink_Event_StartAuctionScan() end -- Hook this function to be called whenever LootLink stops an auction scan function LootLink_Event_StopAuctionScan() end -- Hook this function to be called whenever LootLink completes a full auction scan function LootLink_Event_FinishedAuctionScan() end -- Hook this function to be called whenever LootLink sends a new auction query function LootLink_Event_AuctionQuery(auctionpage) -- auctionpage: the page number for the query that was just sent end -------------------------------------------------------------------------------------------------- -- Static Popup Dialogs -------------------------------------------------------------------------------------------------- StaticPopupDialogs["LOOTLINK_DELETEITEM_CONFIRM"] = { text = TEXT(LOOTLINK_STATICPOPUP_DELETE_ITEM_CONFIRM), button1 = TEXT(YES), button2 = TEXT(NO), OnAccept = function(data) if ( not data ) then return; end if ( LootLink_RemoveItem(data[1], data[2].i, data[3] ) ) then LootLink_Update(); LootLink_SetTitle(); end end, showAlert = 1, timeout = 0, exclusive = 1, whileDead = 1, hideOnEscape = 1, }; StaticPopupDialogs["LOOTLINK_LITEMODE_CONFIRM"] = { text = TEXT(LOOTLINK_STATICPOPUP_LITE_MODE_CONFIRM), button1 = TEXT(OKAY), button2 = TEXT(CANCEL), OnCancel = function() LLO_LiteModeCheckButton:SetChecked(); end, showAlert = 1, timeout = 0, exclusive = 1, whileDead = 1, hideOnEscape = 1, }; StaticPopupDialogs["LOOTLINK_LINK_CONFIRM"] = { text = TEXT(LOOTLINK_STATICPOPUP_ITEM_CONFIRM), button1 = TEXT(OKAY), button2 = TEXT(CANCEL), OnAccept = function(data) ChatFrameEditBox:Insert(LootLink_GetLink(data[1], data[3])); end, showAlert = 1, timeout = 0, exclusive = 1, whileDead = 1, hideOnEscape = 1, }; StaticPopupDialogs["LOOTLINK_DRESSUP_CONFIRM"] = { text = TEXT(LOOTLINK_STATICPOPUP_ITEM_CONFIRM), button1 = TEXT(OKAY), button2 = TEXT(CANCEL), OnAccept = function(data) DressUpItemLink(LootLink_GetLink(data[1], data[3])); end, showAlert = 1, timeout = 0, exclusive = 1, whileDead = 1, hideOnEscape = 1, }; -------------------------------------------------------------------------------------------------- -- Type links - borrowed from Gazmik Fizzwidget's Linkerator -------------------------------------------------------------------------------------------------- function LootLink_TypeLinks_LinkifyName(head, text, tail) if (head ~= "|h" and tail ~= "|h") then -- only linkify things text that isn't linked already local link = LootLink_GetLink(text); if (link) then return link; end end return head.."["..text.."]"..tail; end function LootLink_TypeLinks_ParseChatMessage(text) return string.gsub(text, "([|]?[h]?)%[(.-)%]([|]?[h]?)", LootLink_TypeLinks_LinkifyName); end -- thank you ckknight function LootLink_TypeLinks_AutoComplete(text) local _,_,body,name = string.find( text, "(.*%[)(%S[^%[^%]]+)$" ); if ( name ) then local textlen, namelen, found = strlen(text), strlen(name); if ( this.lastLength and this.lastItem and textlen > this.lastLength ) then if string.find(strlower(this.lastItem), strlower(name)) then this.lastLength = textlen; this:SetText(body..this.lastItem); this:HighlightText(textlen); return 1; end end this.lastLength = textlen; for k, v in ItemLinks do if ( strlen(k) >= namelen and strlower(strsub(k,1,namelen)) == strlower(name) ) then found = 1; if ( (not this.itemComplete or not this.itemComplete[k]) ) then if ( not this.itemComplete ) then this.itemComplete = {}; end this.ignoreTextChange = 1; this:SetText(body..k); this:HighlightText(textlen); this.lastItem = k; this.itemComplete[k] = 1; return 1; end end end if ( found ) then this.lastLength = nil; this.lastItem = nil; this.itemComplete = nil; return LootLink_TypeLinks_AutoComplete(text); end end this.lastItem = nil; this.itemComplete = nil; end -- Hooks lOrig_ChatEdit_OnTextChanged = ChatEdit_OnTextChanged; function ChatEdit_OnTextChanged() if ( LootLinkState.typelinks ) then local text = this:GetText(); if (string.find(text, "^/script") or string.find(text, "^/dump")) then -- don't parse else local newtext = LootLink_TypeLinks_ParseChatMessage(text); if ( text ~= newtext ) then this:SetText(newtext); return; end end end lOrig_ChatEdit_OnTextChanged(this); end lOrig_ChatEdit_OnChar = ChatFrameEditBox:GetScript("OnChar"); function ChatEdit_OnChar() if ( LootLinkState.typelinks and LootLinkState.autotypelinks ) then local text = this:GetText(); if ( string.find(text, "^/script") or string.find(text, "^/dump")) then else LootLink_TypeLinks_AutoComplete(text); end elseif ( lOrig_ChatEdit_OnChar ) then lOrig_ChatEdit_OnChar(this); end end ChatFrameEditBox:SetScript("OnChar", ChatEdit_OnChar); lOrig_ChatEdit_OnTabPressed = ChatEdit_OnTabPressed; function ChatEdit_OnTabPressed() this.lastLength = nil; if ( LootLinkState.typelinks and LootLinkState.autotypelinks and this.lastItem ) then this:Insert(""); else lOrig_ChatEdit_OnTabPressed(); end end lOrig_ChatEdit_OnEnterPressed = ChatEdit_OnEnterPressed; function ChatEdit_OnEnterPressed() if ( LootLinkState.typelinks and LootLinkState.autotypelinks and this.itemComplete ) then this:SetText(this:GetText().."]"); else lOrig_ChatEdit_OnEnterPressed(); end this.itemComplete = nil; end