You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1876 lines
74 KiB
1876 lines
74 KiB
|
4 years ago
|
-- lua functions
|
||
|
|
local select, setmetatable, error, type, rawget, rawset, pairs, tonumber, strsplit, tContains, unpack, tostring, wipe, tinsert, tsort, tconcat, tremove
|
||
|
|
= select, setmetatable, error, type, rawget, rawset, pairs, tonumber, strsplit, tContains, unpack, tostring, wipe, table.insert, table.sort, table.concat, table.remove
|
||
|
|
-- WoW API
|
||
|
|
local GetNumSpecializations, GetSpecializationInfo, GetItemInfo, IsEquippedItem, GetContainerNumSlots, GetContainerItemID, UnitClass, GetInventorySlotInfo, GetNumGuildMembers, ConvertRGBtoColorString, GetInventoryItemLink, GetContainerItemInfo, GetAddOnMetadata, GetItemSpecInfo, GetItemUniqueness, IsInGuild, GetGuildInfo, GetSpecialization, GetSpecializationInfoByID, IsInRaid, UnitName, IsInGroup, GetGuildRosterInfo, UnitFullName, UnitRace, UnitSex
|
||
|
|
= GetNumSpecializations, GetSpecializationInfo, GetItemInfo, IsEquippedItem, GetContainerNumSlots, GetContainerItemID, UnitClass, GetInventorySlotInfo, GetNumGuildMembers, ConvertRGBtoColorString, GetInventoryItemLink, GetContainerItemInfo, GetAddOnMetadata, GetItemSpecInfo, GetItemUniqueness, IsInGuild, GetGuildInfo, GetSpecialization, GetSpecializationInfoByID, IsInRaid, UnitName, IsInGroup, GetGuildRosterInfo, UnitFullName, UnitRace, UnitSex
|
||
|
|
|
||
|
|
local E = select(2, ...)
|
||
|
|
local BestInSlot = LibStub("AceAddon-3.0"):NewAddon("BestInSlotRedux", "AceComm-3.0", "AceHook-3.0", "AceSerializer-3.0", "AceTimer-3.0")
|
||
|
|
local AceGUI = LibStub("AceGUI-3.0")
|
||
|
|
local L = LibStub("AceLocale-3.0"):GetLocale("BestInSlotRedux")
|
||
|
|
local AceEvent = LibStub("AceEvent-3.0")
|
||
|
|
E[1] = BestInSlot
|
||
|
|
E[2] = L
|
||
|
|
E[3] = AceGUI
|
||
|
|
BestInSlot.unsafeIDs = {}
|
||
|
|
BestInSlot.options = {}
|
||
|
|
BestInSlot.defaultModuleState = false
|
||
|
|
BestInSlot.options.DEBUG = false
|
||
|
|
-- Authors
|
||
|
|
BestInSlot.Author1 = ("%s%s @ %s"):format("|c"..RAID_CLASS_COLORS.DEMONHUNTER.colorStr, "Beleria".."|r",ConvertRGBtoColorString(PLAYER_FACTION_COLORS[1]).."Argent Dawn-EU|r")
|
||
|
|
BestInSlot.Author2 = ("%s%s @ %s"):format("|c"..RAID_CLASS_COLORS.PALADIN.colorStr, "Anhility".."|r",ConvertRGBtoColorString(PLAYER_FACTION_COLORS[1]).."Ravencrest-EU|r")
|
||
|
|
BestInSlot.Author3 = ("%s%s @ %s"):format("|c"..RAID_CLASS_COLORS.ROGUE.colorStr, "Sar\195\173th".."|r",ConvertRGBtoColorString(PLAYER_FACTION_COLORS[1]).."Tarren Mill-EU|r")
|
||
|
|
BestInSlot.Author0 = ("%s%s @ %s"):format("|c"..RAID_CLASS_COLORS.ROGUE.colorStr, "Swarley".."|r",ConvertRGBtoColorString(PLAYER_FACTION_COLORS[1]).."Burning Legion-NA|r")
|
||
|
|
BestInSlot.Author4 = ("%s%s @ %s"):format("|c"..RAID_CLASS_COLORS.DRUID.colorStr, "Dioxina".."|r",ConvertRGBtoColorString(PLAYER_FACTION_COLORS[1]).."Antonidas-EU|r")
|
||
|
|
--@non-debug@
|
||
|
|
BestInSlot.version = 20200604165054
|
||
|
|
--@end-non-debug@
|
||
|
|
BestInSlot.AlphaVersion = not (GetAddOnMetadata("BestInSlotRedux", "Version"):find("Release") and true or false)
|
||
|
|
local slashCommands = {}
|
||
|
|
local defaults = {
|
||
|
|
char = {
|
||
|
|
['*'] = { --raidTier
|
||
|
|
['*'] = { --raidDifficulty
|
||
|
|
['*'] = { --listType (spec as number, customList as string)
|
||
|
|
['*'] = nil
|
||
|
|
}
|
||
|
|
},
|
||
|
|
},
|
||
|
|
latestVersion = 1,
|
||
|
|
selected = {},
|
||
|
|
windowpos = {},
|
||
|
|
customlists = {},
|
||
|
|
tutorials = {},
|
||
|
|
options = {
|
||
|
|
windowFixed = false,
|
||
|
|
showBiSTooltip = true,
|
||
|
|
sendAutomaticUpdates = true,
|
||
|
|
receiveAutomaticUpdates = true,
|
||
|
|
minimapButton = true,
|
||
|
|
guildtooltip = true,
|
||
|
|
showBossTooltip = true,
|
||
|
|
keepHistory = false,
|
||
|
|
tooltipCombat = false,
|
||
|
|
historyLength = "30d",
|
||
|
|
historyAutoDelete = true,
|
||
|
|
tooltipSource = true,
|
||
|
|
statsInManager = true,
|
||
|
|
showGuildRankInTooltip = {
|
||
|
|
['*'] = true
|
||
|
|
},
|
||
|
|
overviewfilter = {},
|
||
|
|
},
|
||
|
|
},
|
||
|
|
global = {
|
||
|
|
options = {
|
||
|
|
instantAnimation = false,
|
||
|
|
},
|
||
|
|
customitems = {
|
||
|
|
|
||
|
|
},
|
||
|
|
tutorials = true,
|
||
|
|
},
|
||
|
|
factionrealm = {
|
||
|
|
_history = {
|
||
|
|
['*'] = {}, --players database
|
||
|
|
},
|
||
|
|
['*'] = { --guildname
|
||
|
|
['*'] = { -- charactername
|
||
|
|
['*'] = { -- raidTier
|
||
|
|
['*'] = { -- difficulty
|
||
|
|
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
profile = {
|
||
|
|
minimap = {
|
||
|
|
hide = false,
|
||
|
|
}
|
||
|
|
},
|
||
|
|
}
|
||
|
|
---
|
||
|
|
--Datatypes to be used with some of BestInSlots functions
|
||
|
|
---
|
||
|
|
BestInSlot.EXPANSION = 1
|
||
|
|
BestInSlot.RAIDTIER = 2
|
||
|
|
BestInSlot.INSTANCE = 3
|
||
|
|
BestInSlot.BOSS = 4
|
||
|
|
BestInSlot.DIFFICULTY = 5
|
||
|
|
BestInSlot.SPECIALIZATION = 6
|
||
|
|
|
||
|
|
BestInSlot.MSGPREFIX = "BiS"
|
||
|
|
---
|
||
|
|
--Color codes used by the add-on
|
||
|
|
---
|
||
|
|
BestInSlot.colorHighlight = RED_FONT_COLOR_CODE
|
||
|
|
BestInSlot.colorNormal = NORMAL_FONT_COLOR_CODE
|
||
|
|
---
|
||
|
|
--data = {
|
||
|
|
-- raidTiers = {
|
||
|
|
-- [raidTierId] = {
|
||
|
|
-- description = "Raid Tier Description",
|
||
|
|
-- difficulties = {"difficultyName1", "difficultyName2"},
|
||
|
|
-- expansion = expansionId,
|
||
|
|
-- instances = {},
|
||
|
|
-- tierTokens = {},
|
||
|
|
-- tierItems = {
|
||
|
|
-- [Class1] = {
|
||
|
|
-- [difficultyName1] = {
|
||
|
|
-- tierItemId1,
|
||
|
|
-- tierItemId2,
|
||
|
|
-- tierItemId3,
|
||
|
|
-- },
|
||
|
|
-- [difficultyName2] = {
|
||
|
|
-- tierItemId1,
|
||
|
|
-- tierItemId2,
|
||
|
|
-- tierItemId3,
|
||
|
|
-- },
|
||
|
|
-- }
|
||
|
|
-- }
|
||
|
|
-- }
|
||
|
|
-- },
|
||
|
|
-- instances = {
|
||
|
|
-- [instanceId] = {
|
||
|
|
-- raidTier = raidTierID,
|
||
|
|
-- expansion = expansionId,
|
||
|
|
-- description = "Description
|
||
|
|
-- }
|
||
|
|
-- },
|
||
|
|
-- expansions = {
|
||
|
|
-- [expansionId] = {
|
||
|
|
-- raidTiers = {},
|
||
|
|
-- instances = {},
|
||
|
|
-- description = "Description",
|
||
|
|
-- }
|
||
|
|
-- }
|
||
|
|
--}
|
||
|
|
---
|
||
|
|
local data = {
|
||
|
|
raidTiers = {},
|
||
|
|
instances = {__default={
|
||
|
|
difficultyconversion = {
|
||
|
|
[1] = 1, --Dungeon Normal
|
||
|
|
[2] = 2, --Dungeon Heroic
|
||
|
|
[3] = 23, --Dungeon Mythic
|
||
|
|
},
|
||
|
|
bonusids = {
|
||
|
|
[1] = {3524},
|
||
|
|
[2] = {3524},
|
||
|
|
[3] = {3524}
|
||
|
|
},
|
||
|
|
}},
|
||
|
|
expansions = {},
|
||
|
|
bosses = {},
|
||
|
|
tiertokens = {},
|
||
|
|
}
|
||
|
|
---
|
||
|
|
--itemData = {
|
||
|
|
-- [instanceName]={
|
||
|
|
-- [bossId] = {
|
||
|
|
-- [itemid] = {
|
||
|
|
-- dungeon = "dungeon",
|
||
|
|
-- link = "link",
|
||
|
|
-- isBiS = {
|
||
|
|
-- [difficulty] = {
|
||
|
|
-- [specId] = true
|
||
|
|
-- }
|
||
|
|
-- }
|
||
|
|
-- [difficulty] = ..
|
||
|
|
-- equipSlot = "INVTYPE_[SLOT]"
|
||
|
|
-- }
|
||
|
|
-- }
|
||
|
|
-- }
|
||
|
|
--}
|
||
|
|
--
|
||
|
|
--itemData's metatable can accept itemids. When it is requested an itemid it'll look in nested tables to find the item in question
|
||
|
|
---
|
||
|
|
local itemDataCache = {}
|
||
|
|
local itemData = setmetatable({},{
|
||
|
|
__index = function(tbl, key)
|
||
|
|
local value = itemDataCache[key]
|
||
|
|
if value then return value end
|
||
|
|
for dungeon,dungeonData in pairs(tbl) do --we can do this without error checking because the __newindex metamethod will check if it's a table
|
||
|
|
for bossId, bossData in pairs(dungeonData) do
|
||
|
|
if bossData[key] then itemDataCache[key] = bossData[key] return itemDataCache[key] end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end,
|
||
|
|
__newindex = function(table, key, value)
|
||
|
|
if type(value) ~= "table" then error("Can only add tables to the itemData table") end
|
||
|
|
rawset(table, key, value)
|
||
|
|
end
|
||
|
|
})
|
||
|
|
local tierTokenData = {}
|
||
|
|
local customItems = {}
|
||
|
|
BestInSlot.slots = {"HeadSlot", "NeckSlot","ShoulderSlot","BackSlot","ChestSlot","WristSlot","HandsSlot","WaistSlot","LegsSlot","FeetSlot", "Finger0Slot","Finger1Slot","Trinket0Slot","Trinket1Slot", "MainHandSlot","SecondaryHandSlot"}
|
||
|
|
BestInSlot.invSlots = {
|
||
|
|
[1] = "INVTYPE_HEAD",
|
||
|
|
[2] = "INVTYPE_NECK",
|
||
|
|
[3] = "INVTYPE_SHOULDER",
|
||
|
|
[4] = "INVTYPE_BODY",
|
||
|
|
[5] = {"INVTYPE_CHEST", "INVTYPE_ROBE"},
|
||
|
|
[6] = "INVTYPE_WAIST",
|
||
|
|
[7] = "INVTYPE_LEGS",
|
||
|
|
[8] = "INVTYPE_FEET",
|
||
|
|
[9] = "INVTYPE_WRIST",
|
||
|
|
[10] = "INVTYPE_HAND",
|
||
|
|
[11] = "INVTYPE_FINGER",
|
||
|
|
[12] = "INVTYPE_FINGER",
|
||
|
|
[13] = "INVTYPE_TRINKET",
|
||
|
|
[14] = "INVTYPE_TRINKET",
|
||
|
|
[15] = "INVTYPE_CLOAK",
|
||
|
|
[16] = {"INVTYPE_WEAPON", "INVTYPE_2HWEAPON", "INVTYPE_WEAPONMAINHAND", "INVTYPE_RANGED", "INVTYPE_RANGEDRIGHT", "INVTYPE_NON_EQUIP"},
|
||
|
|
[17] = {"INVTYPE_WEAPONOFFHAND", "INVTYPE_SHIELD", "INVTYPE_WEAPON", "INVTYPE_HOLDABLE", "INVTYPE_NON_EQUIP"},
|
||
|
|
--[18] = {"INVTYPE_RANGED", "INVTYPE_THROWN", "INVTYPE_RANGEDRIGHT", "INVTYPE_RELIC"}
|
||
|
|
}
|
||
|
|
BestInSlot.dualWield = {250, 251, 252, 268, 269, 259, 260, 261, 263, 71, 72}
|
||
|
|
|
||
|
|
------------------------------------------------------------------------------------------------------------------------------------------------
|
||
|
|
-- MODULE REGISTRATION
|
||
|
|
------------------------------------------------------------------------------------------------------------------------------------------------
|
||
|
|
|
||
|
|
--- This function can be used by modules to add their data to the add-on. It checks if the proper values are set
|
||
|
|
--@param #string unlocalizedName the Localized Name of the expansion to register
|
||
|
|
--@param #string localizedDescription The localized description of the expansion to add
|
||
|
|
function BestInSlot:RegisterExpansion(unlocalizedName, localizedDescription)
|
||
|
|
if data.expansions[unlocalizedName] then
|
||
|
|
return
|
||
|
|
end
|
||
|
|
data.expansions[unlocalizedName] = {description = localizedDescription, raidTiers = {}, instances = {}}
|
||
|
|
end
|
||
|
|
--- Registers a raid tier to BestInSlot
|
||
|
|
-- @param #string expansion The expansion ID, must have been registered before by using BestInSlot:RegisterExpansion
|
||
|
|
-- @param #number raidTier The number corresponding with the Raid Tier, must be unique. The standard is to use the patch version the raid tier belongs to (e.g. 50400 for patch 5.4)
|
||
|
|
-- @param #string description The description of the raid tier. By default 'Patch 5.4'
|
||
|
|
-- @param #... The next parameters are considered the difficulties you would like to add, can be "Normal", "Heroic", and "Mythic".
|
||
|
|
function BestInSlot:RegisterRaidTier(expansion, raidTier, description, ...)
|
||
|
|
local difficulties = {...}
|
||
|
|
if data.raidTiers[raidTier] then error("This raid tier is already registered!") end
|
||
|
|
if not data.expansions[expansion] then error("The expansion has not been registered yet!") end
|
||
|
|
if not description or type(description) ~= "string" then error("The raid tier needs to provide a description!") end
|
||
|
|
if not difficulties or #difficulties == 0 then error("The raid tier "..description.." needs to provide difficulties!") end
|
||
|
|
for i = 1,#difficulties do
|
||
|
|
if type(difficulties[i]) ~= "string" then error("Difficulty parameter not set correctly") end
|
||
|
|
end
|
||
|
|
data.raidTiers[raidTier] = {description = description, difficulties = difficulties}
|
||
|
|
data.raidTiers[raidTier].expansion = expansion
|
||
|
|
data.raidTiers[raidTier].instances = {}
|
||
|
|
data.raidTiers[raidTier].module = (raidTier >= 69000) and "PvP" or (raidTier < 60000) and "WoDDungeon" or "WoD"
|
||
|
|
tinsert(data.expansions[expansion].raidTiers, raidTier)
|
||
|
|
end
|
||
|
|
|
||
|
|
local newInstanceMetatable = {
|
||
|
|
--[[__index = function(tbl, key)
|
||
|
|
local value = rawget(tbl, key)
|
||
|
|
if value then return value end
|
||
|
|
for k,v in pairs(tbl) do
|
||
|
|
if v[key] then return v[key] end
|
||
|
|
end
|
||
|
|
end,]]
|
||
|
|
__newindex = function(tbl, key, value)
|
||
|
|
if type(value) ~= "table" then error("Can only add tables with item info inside instance tables") end
|
||
|
|
if key == "tieritems" or key == "misc" or key == "customitems" then
|
||
|
|
rawset(tbl, key, value)
|
||
|
|
return
|
||
|
|
end
|
||
|
|
key = tonumber(key)
|
||
|
|
if not key then error("Key must be a number!") end
|
||
|
|
rawset(tbl, key, value)
|
||
|
|
end
|
||
|
|
}
|
||
|
|
|
||
|
|
local instanceDefaultIndexMetatable = {
|
||
|
|
__index = function(tbl, key)
|
||
|
|
return data.instances.__default[key]
|
||
|
|
end
|
||
|
|
}
|
||
|
|
|
||
|
|
---Register a raid instance to BestInSlot
|
||
|
|
--@param #number raidTier The raidTier ID as used at BestInSlot:RegisterRaidTier
|
||
|
|
--@param #string unlocalizedName An unlocalized name of the raid to add, to identify the instance, must be unique!
|
||
|
|
--@param #string description A localized description of the raid instance to add
|
||
|
|
--@param #table args Optional arguments to override default values. See data.instances.__default
|
||
|
|
function BestInSlot:RegisterRaidInstance(raidTier, unlocalizedName, description, args)
|
||
|
|
if not data.raidTiers[raidTier] then error("The raid tier "..raidTier.." has not been registered yet!") end
|
||
|
|
local localizedExpansion = data.raidTiers[raidTier].expansion
|
||
|
|
if not data.expansions[localizedExpansion] then error("The expansion "..localizedExpansion.." has not been registered yet!") end
|
||
|
|
if not data.raidTiers[raidTier] then error("The raid tier "..raidTier.." has not been registered yet") end
|
||
|
|
if data.instances[unlocalizedName] then error("This raid instance has already been registered!") end
|
||
|
|
data.instances[unlocalizedName] = setmetatable({expansion = localizedExpansion, raidTier = raidTier, description = description}, instanceDefaultIndexMetatable)
|
||
|
|
if args then
|
||
|
|
for k,v in pairs(args) do
|
||
|
|
data.instances[unlocalizedName][k] = v
|
||
|
|
end
|
||
|
|
end
|
||
|
|
tinsert(data.expansions[localizedExpansion].instances, unlocalizedName)
|
||
|
|
tinsert(data.raidTiers[raidTier].instances, unlocalizedName)
|
||
|
|
itemData[unlocalizedName] = setmetatable({}, newInstanceMetatable)
|
||
|
|
if self.db.global.customitems[unlocalizedName] then
|
||
|
|
for itemlink in pairs(self.db.global.customitems[unlocalizedName]) do
|
||
|
|
self:RegisterCustomItem(unlocalizedName, nil, itemlink) --The second parameter will be extracted out of the itemlink if not supplied
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
---Register tier items that drop in the supplied raidTier
|
||
|
|
--@param #number raidTier The raid tier that drops the tier items
|
||
|
|
--@param #table tierItems A table in the following format: {DEATHKNIGHT = { NORMAL = { normalItem1, normalItem2, ...}, HEROIC = { heroicItem1, heroicItem2},} SHAMAN = {....}} Where the difficulties must correspond with the earlier registered difficulties
|
||
|
|
function BestInSlot:RegisterTierItems(dungeon, tierItems)
|
||
|
|
if not data.instances[dungeon] then error("This dungeon is not registered yet") end
|
||
|
|
local tieritems = {}
|
||
|
|
for class in pairs(RAID_CLASS_COLORS) do
|
||
|
|
if not tierItems[class] then error("You are missing class "..class.." in the tier item list.") end
|
||
|
|
for i=1,#tierItems[class] do
|
||
|
|
local itemid = tierItems[class][i]
|
||
|
|
local _, link, _, _, _, _, _, _, equipSlot = GetItemInfo(i)
|
||
|
|
if not link then self.unsafeIDs[itemid] = true end
|
||
|
|
tieritems[itemid] = {
|
||
|
|
bossid = equipSlot and data.tiertokens[dungeon][self:GetItemSlotID(equipSlot)].bossid,
|
||
|
|
dungeon = dungeon,
|
||
|
|
difficulty = -1,
|
||
|
|
link = link,
|
||
|
|
equipSlot = equipSlot,
|
||
|
|
isBiS = {},
|
||
|
|
tieritem = class,
|
||
|
|
}
|
||
|
|
end
|
||
|
|
end
|
||
|
|
itemData[dungeon].tieritems = tieritems
|
||
|
|
end
|
||
|
|
---Register tier tokens, not supported for MoP or lower
|
||
|
|
function BestInSlot:RegisterTierTokens(raidTier, tierTokens)
|
||
|
|
if not data.raidTiers[raidTier] then error("This raid tier is not registered yet") end
|
||
|
|
local raidTierData = data.raidTiers[raidTier]
|
||
|
|
local difficulties = raidTierData.difficulties
|
||
|
|
for slotId, tierSlots in pairs(tierTokens) do
|
||
|
|
for tokenId, tokenClasses in pairs(tierSlots) do
|
||
|
|
tierTokenData[tokenId] = {classes = tokenClasses, raidtier = raidTier, slotid = slotId}
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
---Adds the named difficulty to the available difficulty
|
||
|
|
--@param #number raidtier The Raidtier to append the difficulty to
|
||
|
|
--@param #string difficulty The name of the difficulty
|
||
|
|
function BestInSlot:AddDifficultyToRaidTier(raidtier, difficulty)
|
||
|
|
if not data.raidTiers[raidtier] then error("Raidtier '"..tostring(raidtier).."' does not exist!") end
|
||
|
|
tinsert(data.raidTiers[raidtier].difficulties, difficulty)
|
||
|
|
end
|
||
|
|
|
||
|
|
|
||
|
|
--- Register Miscelaneous items
|
||
|
|
-- @param #number raidTier The Raid tier to add the misc items to
|
||
|
|
-- @param #table miscItems A table containing the miscelaneous items, should be formatted in the following format: {["Legendary Cloak Quest"] = {idCloak1, idCloak2, ...}, ["Ordos"] = {idOrdos1, idOrdos2, ...}}
|
||
|
|
-- @param #bool legionLegendary
|
||
|
|
function BestInSlot:RegisterMiscItems(instance, miscItems, legionLegendary)
|
||
|
|
if not data.instances[instance] then error("This instance is not registered yet") end
|
||
|
|
local misc = {}
|
||
|
|
for miscName,miscLootTable in pairs(miscItems) do
|
||
|
|
if type(miscName) ~= "string" or type(miscLootTable) ~= "table" then error("Misc table is not formatted properly, should be {key = {itemId1, itemId2}} Where key is a description of the source}") end
|
||
|
|
for i=1,#miscLootTable do
|
||
|
|
local itemid = miscLootTable[i]
|
||
|
|
local itemtable
|
||
|
|
if type(itemid) == "table" then
|
||
|
|
itemtable = itemid
|
||
|
|
itemid = itemtable.id
|
||
|
|
if not itemid then self.console:AddError("ItemTable didn't provide id", itemid) end
|
||
|
|
end
|
||
|
|
local link, equipSlot
|
||
|
|
if legionLegendary == true then --fix for Legion Legendaries itemlevel
|
||
|
|
_, link, _, _, _, _, _, _, equipSlot = GetItemInfo(("item:%d::::::::::::1:3630"):format(itemid))
|
||
|
|
else
|
||
|
|
_, link, _, _, _, _, _, _, equipSlot = GetItemInfo(("item:%d::::::::::::1:3524"):format(itemid))
|
||
|
|
end
|
||
|
|
if not link then self.unsafeIDs[itemid] = true end
|
||
|
|
misc[itemid] = {
|
||
|
|
dungeon = instance,
|
||
|
|
difficulty = miscLootTable.difficulty,
|
||
|
|
link = link,
|
||
|
|
equipSlot = equipSlot,
|
||
|
|
isBiS = {},
|
||
|
|
misc = miscName,
|
||
|
|
}
|
||
|
|
end
|
||
|
|
end
|
||
|
|
itemData[instance].misc = misc
|
||
|
|
end
|
||
|
|
|
||
|
|
local bossNewIndexMetatable = {
|
||
|
|
__newindex = function(tbl, key, value)
|
||
|
|
if type(value) ~= "table" then BestInSlot.console:AddError("Item info must be a table!", key, value) end
|
||
|
|
key = tonumber(key)
|
||
|
|
if not key then BestInSlot.console:AddError("Item info must be a table!", key, value) end
|
||
|
|
rawset(tbl, key, value)
|
||
|
|
end
|
||
|
|
}
|
||
|
|
|
||
|
|
--- Register boss loot of an instance. Must call this function in the order you want to put the bosses in
|
||
|
|
-- @param #string unlocalizedInstanceName The unlocalized name of the instance to add the loot to.
|
||
|
|
-- @param #table lootTable The table containing the loot for the boss, must be formatted as follows: {["Normal"] = {itemId1, itemId2}, ["Heroic"] = {itemId1, itemId2}}
|
||
|
|
-- @param #string bossName Localized name of the boss, you can use LibBabbleBoss-3.0 for this.
|
||
|
|
-- @param #number tierToken If supplied, registers this item as a boss that drops the supplied tiertoken. 1 = HeadSlot, 3 = ShoulderSlot, 5 = ChestSlot, 7 = LegsSlot, 10 = Handslot, 15 = BackSlot.
|
||
|
|
function BestInSlot:RegisterBossLoot(unlocalizedInstanceName, lootTable, bossName, tierToken, bossId)
|
||
|
|
local instance = data.instances[unlocalizedInstanceName]
|
||
|
|
if not instance then error("The instance \""..unlocalizedInstanceName.."\" has not yet been registered!") end
|
||
|
|
lootTable.info = {name = bossName}
|
||
|
|
local bossLootTable = bossId and itemData[unlocalizedInstanceName][bossId] or setmetatable({}, bossNewIndexMetatable)
|
||
|
|
local addToBoss = bossId ~= nil
|
||
|
|
local bossId = bossId or #itemData[unlocalizedInstanceName] + 1
|
||
|
|
for i=1,#lootTable do
|
||
|
|
local itemid = lootTable[i]
|
||
|
|
local itemtable
|
||
|
|
if type(itemid) == "table" then
|
||
|
|
itemtable = itemid
|
||
|
|
itemid = itemtable.id
|
||
|
|
if not itemid then self.console:AddError("ItemTable didn't provide id", itemid) end
|
||
|
|
end
|
||
|
|
local item = itemData[itemid]
|
||
|
|
if item and not item.customitem then --The item already existed
|
||
|
|
if not item.multiplesources then
|
||
|
|
item.multiplesources = {}
|
||
|
|
if item.bossid and item.dungeon then
|
||
|
|
item.multiplesources[item.dungeon] = {}
|
||
|
|
item.multiplesources[item.dungeon][item.bossid] = true
|
||
|
|
else
|
||
|
|
self:Print(item)
|
||
|
|
self:Print(self.unsafeIDs)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
item.multiplesources[unlocalizedInstanceName] = item.multiplesources[unlocalizedInstanceName] or {}
|
||
|
|
item.multiplesources[unlocalizedInstanceName][bossId] = true
|
||
|
|
bossLootTable[itemid] = item
|
||
|
|
else
|
||
|
|
if item and item.customitem then
|
||
|
|
self:Print("You have added a custom item that is being registered as a module. This is being removed from your custom items.", true)
|
||
|
|
self:Print("Removing: "..item.link, true)
|
||
|
|
self:UnregisterCustomItem(itemid)
|
||
|
|
end
|
||
|
|
local _, link, _, _, _, _, _, _, equipSlot = GetItemInfo(itemid)
|
||
|
|
if not link then self.unsafeIDs[itemid] = true end
|
||
|
|
bossLootTable[itemid] = {
|
||
|
|
dungeon = unlocalizedInstanceName,
|
||
|
|
bossid = bossId,
|
||
|
|
difficulty = itemtable and itemtable.difficulty or -1,
|
||
|
|
link = link,
|
||
|
|
equipSlot = equipSlot,
|
||
|
|
exceptions = itemtable and itemtable.exceptions,
|
||
|
|
}
|
||
|
|
end
|
||
|
|
end
|
||
|
|
data.bosses[unlocalizedInstanceName] = data.bosses[unlocalizedInstanceName] or {}
|
||
|
|
if not addToBoss then
|
||
|
|
tinsert(itemData[unlocalizedInstanceName], bossLootTable) --add loot to itemData
|
||
|
|
tinsert(data.bosses[unlocalizedInstanceName], bossName) --add Boss info to data
|
||
|
|
end
|
||
|
|
if tierToken then
|
||
|
|
data.tiertokens[unlocalizedInstanceName] = data.tiertokens[unlocalizedInstanceName] or {}
|
||
|
|
data.tiertokens[unlocalizedInstanceName][tierToken] = {dungeon = unlocalizedInstanceName, bossid = #data.bosses[unlocalizedInstanceName]}
|
||
|
|
end
|
||
|
|
BestInSlot.hasModules = true
|
||
|
|
end
|
||
|
|
|
||
|
|
--Simple helper to check if an array has any key
|
||
|
|
local function hasItems(array)
|
||
|
|
for _ in pairs(array) do
|
||
|
|
return true
|
||
|
|
end
|
||
|
|
return false
|
||
|
|
end
|
||
|
|
|
||
|
|
--- Called on initializing the add-on
|
||
|
|
function BestInSlot:OnInitialize()
|
||
|
|
self.db = LibStub("AceDB-3.0"):New("BestInSlotDB", defaults)
|
||
|
|
SLASH_BESTINSLOT1, SLASH_BESTINSLOT2 = '/bestinslot', '/bis'
|
||
|
|
self:RegisterComm(self.MSGPREFIX)
|
||
|
|
self.options.instantAnimation = self.db.global.options.instantAnimation
|
||
|
|
self.options.showBiSTooltip = self.db.char.options.showBiSTooltip
|
||
|
|
self.options.windowFixed = self.db.char.options.windowFixed
|
||
|
|
self.options.sendAutomaticUpdates = self.db.char.options.sendAutomaticUpdates
|
||
|
|
self.options.receiveAutomaticUpdates = self.db.char.options.receiveAutomaticUpdates
|
||
|
|
|
||
|
|
AceEvent:RegisterEvent("GET_ITEM_INFO_RECEIVED", function(event, itemid) BestInSlot:SendEvent("GET_ITEM_INFO_RECEIVED", itemid) end)
|
||
|
|
self:RegisterEvent("GET_ITEM_INFO_RECEIVED", "OnItemInfoGenerated")
|
||
|
|
|
||
|
|
self:Print((L["has been initialized, use %s to show the GUI"]):format((L["%s or %s"]):format(self.colorHighlight.."/bis"..self.colorNormal, self.colorHighlight.."/bestinslot"..self.colorNormal)))
|
||
|
|
end
|
||
|
|
|
||
|
|
--- Called on enabling the add-on
|
||
|
|
function BestInSlot:OnEnable()
|
||
|
|
self:HookScript(GameTooltip, "OnTooltipSetItem", "GameTooltip_OnTooltipSetItem")
|
||
|
|
if AtlasLootTooltip then
|
||
|
|
self:HookScript(AtlasLootTooltip, "OnTooltipSetItem", "GameTooltip_OnTooltipSetItem")
|
||
|
|
end
|
||
|
|
self:HookScript(ItemRefTooltip, "OnTooltipSetItem", "GameTooltip_OnTooltipSetItem")
|
||
|
|
AceEvent:RegisterEvent("PLAYER_GUILD_UPDATE", function()
|
||
|
|
BestInSlot:SendEvent("PLAYER_GUILD_UPDATE")
|
||
|
|
end)
|
||
|
|
self:MiniMapButtonVisible(self.db.char.options.minimapButton)
|
||
|
|
self:SetBestInSlotInfo()
|
||
|
|
local coreModules = {
|
||
|
|
"ZoneDetect",
|
||
|
|
"History",
|
||
|
|
"History",
|
||
|
|
"Timer"
|
||
|
|
}
|
||
|
|
for i=1, #coreModules do
|
||
|
|
self:EnableModule(coreModules[i])
|
||
|
|
end
|
||
|
|
|
||
|
|
--Enable BiS Core modules
|
||
|
|
local loadOrder = {}
|
||
|
|
local waitList = {}
|
||
|
|
local ZoneDetect = self:GetModule("ZoneDetect")
|
||
|
|
for k,v in pairs(BestInSlot.modules) do
|
||
|
|
if not v.enabledState then
|
||
|
|
if v.dependancy then
|
||
|
|
waitList[v.dependancy] = waitList[v.dependancy] or {}
|
||
|
|
tinsert(waitList[v.dependancy], v)
|
||
|
|
else
|
||
|
|
tinsert(loadOrder, v)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
while #loadOrder ~= 0 do
|
||
|
|
local module = tremove(loadOrder)
|
||
|
|
local moduleName = module.moduleName
|
||
|
|
module:Enable()
|
||
|
|
module:InitializeZoneDetect(ZoneDetect)
|
||
|
|
if waitList[moduleName] then
|
||
|
|
local addToList = waitList[moduleName]
|
||
|
|
for i=1,#addToList do
|
||
|
|
tinsert(loadOrder, addToList[i])
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function BestInSlot:OnDisable()
|
||
|
|
self:Unhook(GameTooltip, "OnTooltipSetItem")
|
||
|
|
self:Unhook(ItemRefTooltip, "OnTooltipSetItem")
|
||
|
|
end
|
||
|
|
|
||
|
|
function BestInSlot:GetDifficultyIdForDungeon(bisId, dungeon, toBiS)
|
||
|
|
local returnId, bonusIds = nil
|
||
|
|
if not toBiS then
|
||
|
|
if dungeon and data.instances[dungeon] then
|
||
|
|
returnId, bonusIds = data.instances[dungeon].difficultyconversion[bisId], data.instances[dungeon].bonusids[bisId]
|
||
|
|
else
|
||
|
|
returnId, bonusIds = data.instances.__default.difficultyconversion[bisId], data.instances.__default.bonusids[bisId]
|
||
|
|
end
|
||
|
|
else
|
||
|
|
local tbl = data.instances[dungeon] or data.instances.__default
|
||
|
|
for BiSId, WoWId in pairs(tbl.difficultyconversion) do
|
||
|
|
if bisId == WoWId then
|
||
|
|
returnId, bonusIds = BiSId, tbl.bonusids[BiSId]
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
if not returnId then return end
|
||
|
|
if type(bonusIds) == "table" then
|
||
|
|
return returnId, unpack(bonusIds)
|
||
|
|
else
|
||
|
|
return returnId, bonusIds
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
--- Checks wether the player has the supplied item equipped, it should consider normal and warforged items the same
|
||
|
|
-- @param #number itemid The item ID to check if it's equipped
|
||
|
|
-- @return #boolean True if item equipped, otherwise false
|
||
|
|
function BestInSlot:HasItemEquipped(itemid, difficulty)
|
||
|
|
local item = self:GetItem(itemid, difficulty)
|
||
|
|
if not item then return end
|
||
|
|
for i=1,#self.slots do
|
||
|
|
local slotID = GetInventorySlotInfo(self.slots[i])
|
||
|
|
local link = GetInventoryItemLink("player", slotID)
|
||
|
|
if link then
|
||
|
|
local id, instanceDifficulty = self:GetItemInfoFromLink(link)
|
||
|
|
instanceDifficulty = tonumber(instanceDifficulty)
|
||
|
|
if id == itemid then
|
||
|
|
if difficulty == nil then
|
||
|
|
local bisId = self:GetDifficultyIdForDungeon(instanceDifficulty, item.dungeon, true)
|
||
|
|
if bisId then
|
||
|
|
return {bisId}
|
||
|
|
end
|
||
|
|
elseif self:GetDifficultyIdForDungeon(difficulty, item.dungeon) == instanceDifficulty then
|
||
|
|
return true
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
if difficulty then
|
||
|
|
return {}
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function BestInSlot:GetItemInfoFromLink(itemlink)
|
||
|
|
local _,itemid, enchantId, gemId1, gemId2, gemId3, gemId4, suffixId, uniqueId, linkLevel, specId, upgradeId, instanceDifficultyID, numBonusId, bonusId1, bonusId2, bonusId3, bonusId4, bonusId5, upgradeVal = (":"):split(itemlink)
|
||
|
|
return tonumber(itemid), tonumber(instanceDifficultyID), bonusId1, bonusId2
|
||
|
|
end
|
||
|
|
|
||
|
|
--- Checks if the player has an item in their bags, or an item similar to it (warforged version for example)
|
||
|
|
-- @param #number itemid The itemID to check if the player has it in their bags
|
||
|
|
-- @return #boolean true if the player has the item in their bag, otherwise false
|
||
|
|
function BestInSlot:HasItemInBag(itemid, difficulty)
|
||
|
|
local item = self:GetItem(itemid)
|
||
|
|
if not item then return false end
|
||
|
|
local bags = NUM_BAG_SLOTS
|
||
|
|
local difficulties
|
||
|
|
local name = GetItemInfo(itemid)
|
||
|
|
for i=0,bags do
|
||
|
|
local bagSize = GetContainerNumSlots(i)
|
||
|
|
for j=1,bagSize do
|
||
|
|
local texture, count, locked, quality, readable, lootable, link, isFiltered = GetContainerItemInfo(i, j)
|
||
|
|
if link then
|
||
|
|
local id, instanceDifficulty = self:GetItemInfoFromLink(link)
|
||
|
|
instanceDifficulty = tonumber(instanceDifficulty)
|
||
|
|
if id == itemid then
|
||
|
|
if difficulty == nil then
|
||
|
|
difficulties = difficulties or {}
|
||
|
|
local bisId = self:GetDifficultyIdForDungeon(instanceDifficulty, item.dungeon, true)
|
||
|
|
if bisId then
|
||
|
|
tinsert(difficulties, bisId)
|
||
|
|
end
|
||
|
|
elseif self:GetDifficultyIdForDungeon(difficulty, item.dungeon) == instanceDifficulty then
|
||
|
|
return true
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
return difficulties
|
||
|
|
end
|
||
|
|
|
||
|
|
function BestInSlot:OnItemInfoGenerated(event, itemid)
|
||
|
|
local item = itemData[itemid]
|
||
|
|
if item then
|
||
|
|
local _, link, _, _, _, _, _, _, equipSlot = GetItemInfo(item.customitem or itemid)
|
||
|
|
if not link then return end
|
||
|
|
item.link = link
|
||
|
|
item.equipSlot = equipSlot
|
||
|
|
if item.tieritem then
|
||
|
|
item.bossid = equipSlot and data.tiertokens[item.dungeon][self:GetItemSlotID(equipSlot)].bossid
|
||
|
|
end
|
||
|
|
if self.unsafeIDs[itemid] then self.unsafeIDs[itemid] = nil end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
--- Checks if the player has an item either in their bags, or equipped
|
||
|
|
-- @param #number itemid The itemID to check if the player has
|
||
|
|
-- @param #number [difficulty] The DifficultyId that the item must have.
|
||
|
|
-- @param #bool [checkHigherDifficulties] When true will check higher difficulties and return the difficulty number when found, or nil when not found.
|
||
|
|
-- @return #boolean true if the player has it, otherwise false
|
||
|
|
function BestInSlot:HasItem(itemid, difficulty, checkHigherDifficulties)
|
||
|
|
if not difficulty or not checkHigherDifficulties then
|
||
|
|
return self:HasItemEquipped(itemid, difficulty) or self:HasItemInBag(itemid, difficulty)
|
||
|
|
else
|
||
|
|
local equippedResult = self:HasItemEquipped(itemid)
|
||
|
|
local result = self:HasItemInBag(itemid) or {}
|
||
|
|
if equippedResult then
|
||
|
|
if not tContains(result, equippedResult[1]) then
|
||
|
|
tinsert(result, equippedResult[1])
|
||
|
|
end
|
||
|
|
end
|
||
|
|
if #result > 0 then
|
||
|
|
tsort(result)
|
||
|
|
if result[#result] >= difficulty then
|
||
|
|
return result[#result]
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
------------------------------------------------------------------------------------------------------------------------------------------------
|
||
|
|
-- Slash Commands
|
||
|
|
------------------------------------------------------------------------------------------------------------------------------------------------
|
||
|
|
|
||
|
|
--- A BestInSlot function to register a custom slash command. This will automatically be displayed at /help and should be able to be called through /bis [cmd]
|
||
|
|
-- @param #string cmd The command to register
|
||
|
|
-- @param #string descr The description to be displayed when '/bis help' is being typed
|
||
|
|
-- @param #function func The function to be called when this slash command is invoked
|
||
|
|
-- @param #number prefOrder The preferred location of this message in the '/bis help' dialog. Can be nil
|
||
|
|
function BestInSlot:RegisterSlashCmd(cmd, descr, func, prefOrder)
|
||
|
|
if type(cmd) ~= "string" then error("Command should be a string") end
|
||
|
|
if type(func) ~= "function" then error("Second argument of RegisterSlashCmd should be the function that should be called when the command is given") end
|
||
|
|
if type(descr) ~= "string" then error("Slashcommand should provide a description as third parameter") end
|
||
|
|
if prefOrder and type(prefOrder) ~= "number" then error("If provided, prefOrder should be a number") end
|
||
|
|
cmd = (cmd):lower()
|
||
|
|
if slashCommands[cmd] then error("Slash command "..cmd.." is already registered!") end
|
||
|
|
slashCommands[cmd] = {func = func, descr = descr, prefOrder = prefOrder}
|
||
|
|
end
|
||
|
|
|
||
|
|
function SlashCmdList.BESTINSLOT(msg, editbox)
|
||
|
|
local args = {}
|
||
|
|
local first = true
|
||
|
|
local command
|
||
|
|
for w in (msg):gmatch("%w+") do
|
||
|
|
if first then
|
||
|
|
command = w
|
||
|
|
first = false
|
||
|
|
else
|
||
|
|
tinsert(args, w)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
if not command then
|
||
|
|
slashCommands.show.func()
|
||
|
|
else
|
||
|
|
command = (command):lower()
|
||
|
|
if not slashCommands[command] then
|
||
|
|
BestInSlot:Print((L["Command not recognized, try '%s' for help"]):format("/bis help"), true)
|
||
|
|
else
|
||
|
|
slashCommands[command].func(unpack(args))
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
BestInSlot:RegisterSlashCmd("help", (L["%s - this dialog"]):format("/bis help"), function()
|
||
|
|
local orderedList = {}
|
||
|
|
for k in pairs(slashCommands) do
|
||
|
|
tinsert(orderedList, k)
|
||
|
|
end
|
||
|
|
tsort(orderedList)
|
||
|
|
for i=1,#orderedList do
|
||
|
|
if slashCommands[orderedList[i]].prefOrder then
|
||
|
|
tinsert(orderedList, slashCommands[orderedList[i]].prefOrder, orderedList[i])
|
||
|
|
tremove(orderedList, i + 1)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
DEFAULT_CHAT_FRAME:AddMessage(BestInSlot.colorHighlight..("-"):rep(5)..BestInSlot.colorNormal.."BestInSlotRedux "..L["commands"]..BestInSlot.colorHighlight..("-"):rep(5).."|r")
|
||
|
|
BestInSlot:Print(("%s: %s (%s)"):format(GAME_VERSION_LABEL, GetAddOnMetadata("BestInSlotRedux", "Version"), BestInSlot.version))
|
||
|
|
for i=1,#orderedList do
|
||
|
|
BestInSlot:Print(slashCommands[orderedList[i]].descr, true)
|
||
|
|
end
|
||
|
|
DEFAULT_CHAT_FRAME:AddMessage(BestInSlot.colorHighlight..("-"):rep(36).."|r")
|
||
|
|
end)
|
||
|
|
|
||
|
|
BestInSlot:RegisterSlashCmd("debug", (L["%s - enable/disable debug messages"]):format("/bis debug"), function()
|
||
|
|
if BestInSlot.options.DEBUG then
|
||
|
|
BestInSlot:Print(L["Disabling debug messages"])
|
||
|
|
BestInSlot.options.DEBUG = false
|
||
|
|
else
|
||
|
|
BestInSlot.options.DEBUG = true
|
||
|
|
BestInSlot:Print(L["Enabling debug messages"])
|
||
|
|
end
|
||
|
|
BestInSlot:SendEvent("DebugOptionsChanged", BestInSlot.options.DEBUG)
|
||
|
|
end)
|
||
|
|
|
||
|
|
local itemslotidCache = {}
|
||
|
|
|
||
|
|
function BestInSlot:GetItemSlotID(equipSlot, spec)
|
||
|
|
if not equipSlot then return end
|
||
|
|
if spec == 72 and equipSlot == "INVTYPE_2HWEAPON" then return 16,17 end --fury warrior 2-handers
|
||
|
|
if itemslotidCache[equipSlot] then
|
||
|
|
if #itemslotidCache[equipSlot] == 1 then return itemslotidCache[equipSlot][1] else return itemslotidCache[equipSlot][1], itemslotidCache[equipSlot][2] end
|
||
|
|
end
|
||
|
|
local result = {}
|
||
|
|
for i=1,#self.invSlots do
|
||
|
|
if type(self.invSlots[i]) == "string" then
|
||
|
|
if self.invSlots[i] == equipSlot then
|
||
|
|
tinsert(result, i)
|
||
|
|
end
|
||
|
|
elseif type(self.invSlots[i]) == "table" then
|
||
|
|
for j=1,#self.invSlots[i] do
|
||
|
|
if self.invSlots[i][j] == equipSlot then
|
||
|
|
tinsert(result, i)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
itemslotidCache[equipSlot] = result
|
||
|
|
return unpack(result)
|
||
|
|
end
|
||
|
|
|
||
|
|
------------------------------------------------------------------------------------------------------------------------------------------------
|
||
|
|
-- Getters for data
|
||
|
|
------------------------------------------------------------------------------------------------------------------------------------------------
|
||
|
|
|
||
|
|
---
|
||
|
|
-- This function returns an array of items by id to define in what order it should show the items provided in the itemArray
|
||
|
|
-- @param #array itemArray The array of items to sort
|
||
|
|
-- @param #string mode The mode to sort at, currently only supports "SORT_MODE_ILVL" and defaults to that
|
||
|
|
---
|
||
|
|
function BestInSlot:GetLootOrder(itemArray, mode)
|
||
|
|
local sortArray = {}
|
||
|
|
local mode = mode or "SORT_MODE_ILVL"
|
||
|
|
if mode == "SORT_MODE_ILVL" then
|
||
|
|
for k,v in pairs(itemArray) do
|
||
|
|
if #sortArray == 0 then
|
||
|
|
tinsert(sortArray, k)
|
||
|
|
else
|
||
|
|
local position
|
||
|
|
for i=1,#sortArray do
|
||
|
|
if sortArray[i] > k then
|
||
|
|
position = i
|
||
|
|
break
|
||
|
|
end
|
||
|
|
end
|
||
|
|
position = position or #sortArray + 1
|
||
|
|
tinsert(sortArray, position, k)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
return sortArray
|
||
|
|
end
|
||
|
|
|
||
|
|
--- Retrieve the loot table that's personalized for the player.
|
||
|
|
-- @param #number raidTier The raidtier to retrieve the loot table for
|
||
|
|
-- @param #number slotID The slotID to retrieve the loot table for
|
||
|
|
-- @param #number difficulty The difficulty ID to retrieve the loot table for
|
||
|
|
-- @param #number specializationId The specializationID to retrieve the loot for
|
||
|
|
-- @param #boolean lowerRaidTiers Show loot for lower raid tiers as well
|
||
|
|
-- @param #number The specialization to use to compare uniquness for
|
||
|
|
-- @return #table The loot table for the player
|
||
|
|
function BestInSlot:GetPersonalizedLootTableBySlot(raidTier, slotId, difficulty, specializationId, lowerRaidTiers, uniquenessSpec)
|
||
|
|
local specRole, class = select(6, GetSpecializationInfoByID(specializationId))
|
||
|
|
uniquenessSpec = uniquenessSpec or specializationId
|
||
|
|
if specializationId == 72 and slotId == 17 then --Fury warriors can wield everything in their offhand
|
||
|
|
return self:GetPersonalizedLootTableBySlot(raidTier, 16, difficulty, specializationId, lowerRaidTiers) --return main hand loot list instead
|
||
|
|
end
|
||
|
|
|
||
|
|
local items = self:GetLootTableBySlot(raidTier, slotId, difficulty, lowerRaidTiers)
|
||
|
|
if not items then
|
||
|
|
return
|
||
|
|
end
|
||
|
|
for id, item in pairs(items) do
|
||
|
|
local canUse
|
||
|
|
local statFilter = GetItemSpecInfo(item.itemid)
|
||
|
|
if item.exceptions then
|
||
|
|
local checks = {specRole, class}
|
||
|
|
for i, check in pairs({"role", "class"}) do
|
||
|
|
local checkitem = item.exceptions[check]
|
||
|
|
if checkitem and type(checkitem) == "table" and tContains(checkitem, checks[i]) or checkitem == checks[i] then
|
||
|
|
exceptions[item.itemid] = true
|
||
|
|
break
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
if statFilter then
|
||
|
|
if #statFilter == 0 then --There is no itemspecinfo available for this item, normally the table should be nil
|
||
|
|
if raidTier > 70000 and (slotId == 2 or slotId == 11 or slotId == 12) and item.misc ~= LOOT_JOURNAL_LEGENDARIES then
|
||
|
|
canUse = true
|
||
|
|
else
|
||
|
|
canUse = item.customitem ~= nil
|
||
|
|
end
|
||
|
|
else
|
||
|
|
canUse = tContains(statFilter, specializationId)
|
||
|
|
end
|
||
|
|
else
|
||
|
|
canUse = false
|
||
|
|
end
|
||
|
|
if canUse and tContains(data.raidTiers[raidTier].instances, item.dungeon) then --check item uniqueness
|
||
|
|
local family, count = GetItemUniqueness(item.itemid)
|
||
|
|
if count == 1 and self:IsItemBestInSlot(item.itemid, difficulty, uniquenessSpec) then
|
||
|
|
canUse = false
|
||
|
|
end
|
||
|
|
end
|
||
|
|
if canUse and slotId == 17 and item.equipSlot == "INVTYPE_WEAPON" then
|
||
|
|
canUse = false
|
||
|
|
for i=1,#self.dualWield do
|
||
|
|
if self.dualWield[i] == specializationId then
|
||
|
|
canUse = true
|
||
|
|
break
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
if not canUse then
|
||
|
|
items[id] = nil
|
||
|
|
end
|
||
|
|
end
|
||
|
|
local addSpec
|
||
|
|
if specializationId == 73 then --Prot warriors
|
||
|
|
addSpec = 71 --Arms
|
||
|
|
elseif specializationId == 104 then --Guardian Druid
|
||
|
|
addSpec = 103 --Feral Druid
|
||
|
|
elseif specializationId == 66 then --Prot Pally
|
||
|
|
addSpec = 70 --Ret Pally
|
||
|
|
elseif specializationId == 250 then --Blood DK
|
||
|
|
addSpec = 252 --Unholy DK
|
||
|
|
elseif specializationId == 268 then --Brewmaster Monk
|
||
|
|
addSpec = 269 --Windwalker Monk
|
||
|
|
elseif specializationId == 105 then --Resto Druid
|
||
|
|
addSpec = 102 --Balance Druid
|
||
|
|
elseif specializationId == 264 then --Resto Shaman
|
||
|
|
addSpec = 262 --Elemental Shaman
|
||
|
|
elseif specializationId == 257 or specializationId == 256 then --Both Healing Priests
|
||
|
|
addSpec = 258 --Shadow Priest
|
||
|
|
end
|
||
|
|
--ToDo Implement fix for paladin
|
||
|
|
if addSpec then
|
||
|
|
local dpsItems = self:GetPersonalizedLootTableBySlot(raidTier, slotId, difficulty, addSpec, lowerRaidTiers, specializationId)
|
||
|
|
for itemid, item in pairs(dpsItems) do
|
||
|
|
if not items[itemid] then
|
||
|
|
items[itemid] = item
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
return items
|
||
|
|
end
|
||
|
|
|
||
|
|
local function addLootToTableByFilter(tbl, itemlist, slotId, difficulty)
|
||
|
|
for id in pairs(itemlist) do
|
||
|
|
local item = BestInSlot:GetItem(id, difficulty)
|
||
|
|
if (not slotId) or (type(BestInSlot.invSlots[slotId]) == "string" and BestInSlot.invSlots[slotId] == item.equipSlot) or (type(BestInSlot.invSlots[slotId]) == "table" and tContains(BestInSlot.invSlots[slotId],item.equipSlot)) then
|
||
|
|
if (not difficulty) or (not item.difficulty or (item.difficulty == -1 or item.difficulty == difficulty or (type(item.difficulty) == "table") and tContains(item.difficulty, difficulty)) ) then
|
||
|
|
tbl[id] = item
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
--- Gets the loottable for the supplied dungeon
|
||
|
|
-- @param #string dungeon Unlocalized name of the dungeon
|
||
|
|
-- @param #number slotId Optional slotId to add
|
||
|
|
function BestInSlot:GetLootTableByDungeon(dungeon, slotId, difficulty)
|
||
|
|
local items = {}
|
||
|
|
local dungeonData = itemData[dungeon]
|
||
|
|
for bossId=1,#dungeonData do
|
||
|
|
addLootToTableByFilter(items, dungeonData[bossId], slotId, difficulty)
|
||
|
|
end
|
||
|
|
if dungeonData.tieritems then
|
||
|
|
addLootToTableByFilter(items, dungeonData.tieritems, slotId, difficulty)
|
||
|
|
end
|
||
|
|
if dungeonData.misc then
|
||
|
|
addLootToTableByFilter(items, dungeonData.misc, slotId, difficulty)
|
||
|
|
end
|
||
|
|
if dungeonData.customitems then
|
||
|
|
addLootToTableByFilter(items, dungeonData.customitems, slotId, difficulty)
|
||
|
|
end
|
||
|
|
return items
|
||
|
|
end
|
||
|
|
|
||
|
|
local function helperFullLootTable(tbl, itemlist, difficulty)
|
||
|
|
for id in pairs(itemlist) do
|
||
|
|
local item = BestInSlot:GetItem(id, difficulty)
|
||
|
|
if (not difficulty) or (not item.difficulty or (item.difficulty == -1 or item.difficulty == difficulty or (type(item.difficulty) == "table") and tContains(item.difficulty, difficulty)) ) then
|
||
|
|
tbl[id] = item
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function BestInSlot:GetFullLootTableForRaidTier(raidTier, difficulty)
|
||
|
|
local items = {}
|
||
|
|
for _, dungeon in pairs(self:GetInstances(self.RAIDTIER, raidTier)) do
|
||
|
|
local dungeonData = itemData[dungeon]
|
||
|
|
for bossId=1,#dungeonData do
|
||
|
|
helperFullLootTable(items, dungeonData[bossId], difficulty)
|
||
|
|
end
|
||
|
|
if dungeonData.tieritems then
|
||
|
|
helperFullLootTable(items, dungeonData.tieritems, difficulty)
|
||
|
|
end
|
||
|
|
if dungeonData.misc then
|
||
|
|
helperFullLootTable(items, dungeonData.misc, difficulty)
|
||
|
|
end
|
||
|
|
if dungeonData.customitems then
|
||
|
|
helperFullLootTable(items, dungeonData.customitems, difficulty)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
return items
|
||
|
|
end
|
||
|
|
|
||
|
|
--- Get the loottable for the supplied raidTier, slot, and difficulty
|
||
|
|
-- @param #number raidTier The raidtier to request the loot table off
|
||
|
|
-- @param #number slotId The Slot ID to request
|
||
|
|
-- @param #number difficulty The difficulty ID of the raidTier to request the data off
|
||
|
|
-- @param #boolean lowerRaidTiers Get data for lower raid tiers as well
|
||
|
|
-- @return #table The loot table
|
||
|
|
function BestInSlot:GetLootTableBySlot(raidTier, slotId, difficulty, lowerRaidTiers)
|
||
|
|
local items = {}
|
||
|
|
local dungeons = data.raidTiers[raidTier].instances
|
||
|
|
for i=1,#dungeons do
|
||
|
|
for id, item in pairs(self:GetLootTableByDungeon(dungeons[i], slotId, difficulty)) do
|
||
|
|
items[id] = item
|
||
|
|
end
|
||
|
|
end
|
||
|
|
if lowerRaidTiers then
|
||
|
|
local module = data.raidTiers[raidTier].module
|
||
|
|
local raidTiers = self:GetRaidTiers()
|
||
|
|
for i=1,#raidTiers do
|
||
|
|
if raidTiers[i] == raidTier then break end --stop the loop at the raidTier that we already have data from
|
||
|
|
if module == data.raidTiers[raidTiers[i]].module then
|
||
|
|
for id, item in pairs(self:GetLootTableBySlot(raidTiers[i], slotId, difficulty)) do
|
||
|
|
items[id] = item
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
return items
|
||
|
|
end
|
||
|
|
|
||
|
|
function BestInSlot:ItemExists(itemid)
|
||
|
|
if itemData[itemid] ~= nil then
|
||
|
|
return true, "item"
|
||
|
|
elseif tierTokenData[itemid] ~= nil then
|
||
|
|
return true, "tiertoken"
|
||
|
|
else
|
||
|
|
return false
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
|
||
|
|
--- Gets an item string for use in the WoWAPI
|
||
|
|
--@param #number itemid The itemid of the itemstring
|
||
|
|
function BestInSlot:GetItemString(itemid, difficulty)
|
||
|
|
if not itemid then error("You should provide an itemid!") end
|
||
|
|
difficulty = difficulty or 1
|
||
|
|
local instanceDifficulty, bonusID1, bonusID2, bonusID3, bonusID4, bonusID5 = self:GetDifficultyIdForDungeon(difficulty, itemData[itemid] and itemData[itemid].dungeon)
|
||
|
|
numBonusIDs = (bonusID5 ~= nil and 5) or (bonusID4 ~= nil and 4) or (bonusID3 ~= nil and 3) or (bonusID2 ~= nil and 2) or (bonusID1 ~= nil and 1) or 0
|
||
|
|
--item:itemId:enchantId:gemId1:gemId2:gemId3:gemId4:suffixId:uniqueId:linkLevel:specializationID:upgradeId:instanceDifficultyId:numBonusIds:bonusId1:bonusId2:upgradeValue
|
||
|
|
return ("item:%d:::::::::::%d:%d:%d:%d:%d:%d:%d:"):format(itemid, instanceDifficulty, numBonusIDs, bonusID1, bonusID2, bonusID3, bonusID4, bonusID5)
|
||
|
|
end
|
||
|
|
|
||
|
|
--- Gets the internal item table for the specified itemid
|
||
|
|
--@param #number itemid The ID of the item
|
||
|
|
--@param #string difficulty The difficulty of the item
|
||
|
|
--@return #table The internal item table
|
||
|
|
function BestInSlot:GetItem(itemid, difficulty)
|
||
|
|
if type(itemid) == "string" then
|
||
|
|
itemid = tonumber(itemid)
|
||
|
|
end
|
||
|
|
if itemid then
|
||
|
|
if self.unsafeIDs[itemid] then
|
||
|
|
self:OnItemInfoGenerated(nil, itemid)
|
||
|
|
end
|
||
|
|
if self.unsafeIDs[itemid] then self.console:AddError("Couldn't fetch data for itemid: "..itemid) end
|
||
|
|
if itemData[itemid] then
|
||
|
|
local newItemTable = setmetatable({}, {__index=itemData[itemid]})
|
||
|
|
if difficulty and newItemTable.difficulty == -1 then --This is an item that has multiple states, therefore we need to set it's state
|
||
|
|
newItemTable.itemstr = self:GetItemString(itemid, difficulty)
|
||
|
|
local link = select(2,GetItemInfo(newItemTable.itemstr))
|
||
|
|
newItemTable.link = link
|
||
|
|
newItemTable.difficulty = difficulty
|
||
|
|
else
|
||
|
|
newItemTable.itemstr = "item:"..itemid
|
||
|
|
end
|
||
|
|
newItemTable.itemid = itemid
|
||
|
|
return newItemTable
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function BestInSlot:GetItemSources(itemid)
|
||
|
|
if self:ItemExists(itemid) then
|
||
|
|
local item = itemData[itemid]
|
||
|
|
local sources = {}
|
||
|
|
sources[item.dungeon] = {}
|
||
|
|
sources[item.dungeon][item.bossid] = true
|
||
|
|
if item.multiplesources then
|
||
|
|
for k,v in pairs(item.multiplesources) do
|
||
|
|
sources[k] = sources[k] or {}
|
||
|
|
for bossId in pairs(v) do
|
||
|
|
sources[k][bossId] = true
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
return sources
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function BestInSlot:AddCustomItem(itemid, itemstr, dungeon, updatePrevious)--, warlordsCrafted, stage, suffix)
|
||
|
|
if updatePrevious then
|
||
|
|
local item = itemData[itemid]
|
||
|
|
if item then
|
||
|
|
itemData[item.dungeon].customitems[itemid] = nil
|
||
|
|
rawset(itemData, itemid, nil)
|
||
|
|
self.db.global.customitems[item.dungeon][item.customitem] = nil
|
||
|
|
end
|
||
|
|
end
|
||
|
|
self.db.global.customitems[dungeon] = self.db.global.customitems[dungeon] or {}
|
||
|
|
self.db.global.customitems[dungeon][itemstr] = true
|
||
|
|
self:RegisterCustomItem(dungeon, itemid, itemstr)
|
||
|
|
end
|
||
|
|
|
||
|
|
local function helperGetCustomItems(dungeon)
|
||
|
|
local result = {}
|
||
|
|
local count = 0
|
||
|
|
if itemData[dungeon] and itemData[dungeon].customitems then
|
||
|
|
for itemid, item in pairs(itemData[dungeon].customitems) do
|
||
|
|
tinsert(result, itemid)
|
||
|
|
count = count + 1
|
||
|
|
end
|
||
|
|
end
|
||
|
|
return result, count
|
||
|
|
end
|
||
|
|
|
||
|
|
function BestInSlot:GetCustomItems(dungeon)
|
||
|
|
if not dungeon then
|
||
|
|
local result = {}
|
||
|
|
local totalcount = 0
|
||
|
|
for _, instance in pairs(self:GetInstances()) do
|
||
|
|
local count
|
||
|
|
result[instance], count = helperGetCustomItems(instance)
|
||
|
|
totalcount = totalcount + count
|
||
|
|
end
|
||
|
|
return result, totalcount
|
||
|
|
end
|
||
|
|
return helperGetCustomItems(dungeon)
|
||
|
|
end
|
||
|
|
|
||
|
|
function BestInSlot:RegisterCustomItem(dungeon, itemid, itemlink)
|
||
|
|
if not itemData[dungeon] then error("Invalid dungeon given to RegisterCustomItem!") end
|
||
|
|
if not itemid then
|
||
|
|
itemid = self:GetItemInfoFromLink(itemlink)
|
||
|
|
itemid = tonumber(itemid)
|
||
|
|
if not itemid then error("Couldn't convert itemlink to itemid!") end
|
||
|
|
end
|
||
|
|
itemData[dungeon].customitems = itemData[dungeon].customitems or {}
|
||
|
|
local _, link, _, _, _, _, _, _, equipSlot = GetItemInfo(itemlink)
|
||
|
|
if not link then self.unsafeIDs[itemid] = true end
|
||
|
|
itemData[dungeon].customitems[itemid] = {
|
||
|
|
dungeon = dungeon,
|
||
|
|
isBiS = {
|
||
|
|
|
||
|
|
},
|
||
|
|
link = link,
|
||
|
|
equipSlot = equipSlot,
|
||
|
|
customitem = itemlink,
|
||
|
|
}
|
||
|
|
end
|
||
|
|
|
||
|
|
function BestInSlot:UnregisterCustomItem(itemid)
|
||
|
|
local item = itemData[itemid]
|
||
|
|
self:Print(item)
|
||
|
|
if not item or not item.customitem then return end
|
||
|
|
local dungeon = item.dungeon
|
||
|
|
local raidtier = self:GetRaidTiers(self.INSTANCE, dungeon)
|
||
|
|
for difficulty, difficTable in pairs(item.isBiS) do
|
||
|
|
for specId, bis in pairs(difficTable) do
|
||
|
|
if bis then
|
||
|
|
self:SetItemBestInSlot(raidtier, difficulty, specId, self:GetItemSlotID(item.equipSlot), nil)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
self.db.global.customitems[dungeon][item.customitem] = nil
|
||
|
|
itemData[dungeon].customitems[itemid] = nil
|
||
|
|
rawset(itemData, itemid, nil) --rawSet overrides default prevention of removing item info.
|
||
|
|
end
|
||
|
|
--- Gets a localized description for the supplied unlocalized identifier
|
||
|
|
-- @param #number datatype Optional datatype to query can be BestInSlot.EXPANSION, BestInSlot_TYPE_RAIDTIER, BestInSlot.INSTANCE, BestInSlot.BOSS or BestInSlot.DIFFICULTY
|
||
|
|
-- @param #multiple arg1 The filter for the supplied datatype
|
||
|
|
-- @param #multiple arg2 The second filter for the supplied datatype, needed for TYPE_BOSS and TYPE_DIFFUCLTY
|
||
|
|
function BestInSlot:GetDescription(datatype, arg1, arg2)
|
||
|
|
if not arg1 or not datatype then error("This function requires atleast 2 arguments, a datatype and an argument for that datatype") end
|
||
|
|
if datatype == self.RAIDTIER then
|
||
|
|
arg1 = tonumber(arg1)
|
||
|
|
if not arg1 or not data.raidTiers[arg1] then return "" end
|
||
|
|
return data.raidTiers[arg1].description
|
||
|
|
elseif datatype == self.EXPANSION then
|
||
|
|
if not data.expansions[arg1] then return "" end
|
||
|
|
return data.expansions[arg1].description
|
||
|
|
elseif datatype == self.INSTANCE then
|
||
|
|
if not data.instances[arg1] then return "" end
|
||
|
|
return data.instances[arg1].description
|
||
|
|
elseif datatype == self.BOSS then
|
||
|
|
arg2 = tonumber(arg2)
|
||
|
|
if not arg2 then error("The GetDescription function for datatype TYPE_BOSS requires 2 arguments") end
|
||
|
|
if not data.bosses[arg1] then return "" end
|
||
|
|
return data.bosses[arg1][arg2]
|
||
|
|
elseif datatype == self.DIFFICULTY then
|
||
|
|
arg2 = tonumber(arg2)
|
||
|
|
if not arg2 then error("The GetDescription function for datatype TYPE_DIFFICULTY requires 2 argumets") end
|
||
|
|
if type(arg1) == "number" then --assume it's a raid tier
|
||
|
|
return data.raidTiers[arg1].difficulties[arg2]
|
||
|
|
elseif type(arg1) == "string" then --assume it's a dungeon
|
||
|
|
return data.raidTiers[data.instances[arg1].raidTier].difficulties[arg2]
|
||
|
|
else
|
||
|
|
error(tostring(arg1).." is not a string or number.")
|
||
|
|
end
|
||
|
|
end
|
||
|
|
error(tostring(datatype).." is an invalid datatype!")
|
||
|
|
end
|
||
|
|
--- Gets the expansions
|
||
|
|
-- @param #number datatype Optional datatype to query, if nil it will give all expansions as result can be BestInSlot.EXPANSION, BestInSlot_TYPE_RAIDTIER, BestInSlot.INSTANCE, BestInSlot.BOSS or BestInSlot.DIFFICULTY
|
||
|
|
-- @param #multiple arg The filter for the supplied datatype
|
||
|
|
function BestInSlot:GetExpansions(datatype, arg)
|
||
|
|
if not datatype then
|
||
|
|
local expansions = {}
|
||
|
|
for k,v in pairs(data.expansions) do
|
||
|
|
tinsert(expansions, k)
|
||
|
|
end
|
||
|
|
return expansions
|
||
|
|
elseif datatype == self.RAIDTIER then
|
||
|
|
if data.raidTiers[arg] then
|
||
|
|
return data.raidTiers[arg].expansion
|
||
|
|
end
|
||
|
|
return ""
|
||
|
|
elseif datatype == self.INSTANCE then
|
||
|
|
if data.instances[arg] then
|
||
|
|
return data.instances[arg].expansion
|
||
|
|
end
|
||
|
|
return ""
|
||
|
|
else
|
||
|
|
error("Invalid type given!")
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
--- Gets the raidtiers
|
||
|
|
-- @param #number datatype Optional datatype to query, if nil it will give everything as result can be BestInSlot.EXPANSION, BestInSlot_TYPE_RAIDTIER, BestInSlot.INSTANCE
|
||
|
|
-- @param #multiple arg The filter for the supplied datatype
|
||
|
|
function BestInSlot:GetRaidTiers(datatype, arg)
|
||
|
|
local raidTiers = {}
|
||
|
|
-- No Filter
|
||
|
|
if not datatype or datatype == self.RAIDTIER then
|
||
|
|
for k,v in pairs(data.raidTiers) do
|
||
|
|
if not datatype or (v.module == data.raidTiers[arg].module and k < arg) then
|
||
|
|
tinsert(raidTiers, k)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
tsort(raidTiers)
|
||
|
|
return raidTiers
|
||
|
|
elseif not arg then return
|
||
|
|
-- Expansion filter
|
||
|
|
elseif datatype == self.EXPANSION then
|
||
|
|
if not data.expansions[arg] then return raidTiers end
|
||
|
|
for i=1,#data.expansions[arg].raidTiers do
|
||
|
|
tinsert(raidTiers, data.expansions[arg].raidTiers[i])
|
||
|
|
end
|
||
|
|
tsort(raidTiers)
|
||
|
|
return raidTiers
|
||
|
|
-- Instance filter, will return a single raidTier, not as table!
|
||
|
|
elseif datatype == self.INSTANCE then
|
||
|
|
if data.instances[arg] then
|
||
|
|
return data.instances[arg].raidTier
|
||
|
|
else return end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
--- Gets the difficulties
|
||
|
|
-- @param #number datatype Optional datatype to query, if nil it will give everything as result can be BestInSlot.EXPANSION, BestInSlot_TYPE_RAIDTIER, BestInSlot.INSTANCE
|
||
|
|
-- @param #multiple The filter for the supplied datatype
|
||
|
|
function BestInSlot:GetDifficulties(datatype, arg)
|
||
|
|
local difficulties = {}
|
||
|
|
if datatype == self.RAIDTIER then
|
||
|
|
if not data.raidTiers[arg] then return difficulties end
|
||
|
|
for i=1,#data.raidTiers[arg].difficulties do
|
||
|
|
tinsert(difficulties, data.raidTiers[arg].difficulties[i])
|
||
|
|
end
|
||
|
|
return difficulties
|
||
|
|
elseif datatype == self.INSTANCE then
|
||
|
|
if not arg or not data.instances[arg] then return difficulties end
|
||
|
|
local raidTier = data.instances[arg].raidTier
|
||
|
|
for i=1,#data.raidTiers[raidTier].difficulties do
|
||
|
|
tinsert(difficulties, data.raidTiers[raidTier].difficulties[i])
|
||
|
|
end
|
||
|
|
return difficulties
|
||
|
|
end
|
||
|
|
error("Invalid datatype given!")
|
||
|
|
end
|
||
|
|
|
||
|
|
--- Gets the instances
|
||
|
|
-- @param #number datatype Optional datatype to query, if nil it will give everything as result can be BestInSlot.EXPANSION, BestInSlot_TYPE_RAIDTIER, BestInSlot.INSTANCE
|
||
|
|
-- @param #multiple The filter for the supplied datatype
|
||
|
|
function BestInSlot:GetInstances(datatype, arg)
|
||
|
|
local instances = {}
|
||
|
|
if not datatype then
|
||
|
|
for k,v in pairs(data.instances) do
|
||
|
|
if k ~= "__default" then
|
||
|
|
tinsert(instances, k)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
return instances
|
||
|
|
elseif datatype == BestInSlot.RAIDTIER then
|
||
|
|
if not data.raidTiers[arg] then return instances end
|
||
|
|
for i=1, #data.raidTiers[arg].instances do
|
||
|
|
tinsert(instances, data.raidTiers[arg].instances[i])
|
||
|
|
end
|
||
|
|
return instances
|
||
|
|
end
|
||
|
|
error("Invalid datatype given!")
|
||
|
|
end
|
||
|
|
|
||
|
|
function BestInSlot:GetLatest(datatype, filterdatatype, arg)
|
||
|
|
-- Get Latest Expansion
|
||
|
|
if datatype == self.EXPANSION then
|
||
|
|
local selectedExpansion
|
||
|
|
local selectedRaidTier = 0
|
||
|
|
for k,v in pairs(data.expansions) do
|
||
|
|
for i=1,#v.raidTiers do
|
||
|
|
if v.raidTiers[i] > selectedRaidTier then
|
||
|
|
selectedRaidTier = v.raidTiers[i]
|
||
|
|
selectedExpansion = k
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
return selectedExpansion
|
||
|
|
|
||
|
|
-- Get Latest Raid Tier
|
||
|
|
elseif datatype == self.RAIDTIER then
|
||
|
|
local selected = 0
|
||
|
|
for k,v in pairs(data.raidTiers) do
|
||
|
|
local isFiltered = false
|
||
|
|
if filterdatatype then
|
||
|
|
if filterdatatype == self.EXPANSION and v.expansion ~= arg then
|
||
|
|
isFiltered = true
|
||
|
|
end
|
||
|
|
end
|
||
|
|
if not isFiltered then
|
||
|
|
selected = math.max(selected, k)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
return selected
|
||
|
|
-- Get Latest Instance (based on latest raid tier, if no args given)
|
||
|
|
elseif datatype == self.INSTANCE then
|
||
|
|
local raidTier
|
||
|
|
if arg then
|
||
|
|
if filterdatatype == self.RAIDTIER then
|
||
|
|
raidTier = arg
|
||
|
|
else
|
||
|
|
error("This has not been implemented yet!")
|
||
|
|
end
|
||
|
|
else
|
||
|
|
raidTier = self:GetLatest(self.RAIDTIER)
|
||
|
|
end
|
||
|
|
return data.raidTiers[raidTier].instances[#data.raidTiers[raidTier].instances]
|
||
|
|
elseif datatype == self.DIFFICULTY then
|
||
|
|
if arg then
|
||
|
|
if filterdatatype == self.INSTANCE then
|
||
|
|
local raidTier = self:GetRaidTiers(self.INSTANCE, arg)
|
||
|
|
return self:GetLatest(self.DIFFICULTY, self.RAIDTIER, raidTier)
|
||
|
|
elseif filterdatatype == self.RAIDTIER then
|
||
|
|
local raidTierData = data.raidTiers[arg].difficulties
|
||
|
|
return #raidTierData
|
||
|
|
end
|
||
|
|
end
|
||
|
|
error("This has not been implemented yet!")
|
||
|
|
end
|
||
|
|
error(tostring(datatype).." is an invalid datatype")
|
||
|
|
end
|
||
|
|
|
||
|
|
---Gets the bosses for the instance
|
||
|
|
--@param #string instance The instance to get the bosses for
|
||
|
|
--@return #table Table with boss names, the id of the boss is the same as the table index
|
||
|
|
function BestInSlot:GetInstanceBosses(instance)
|
||
|
|
local bosses = {}
|
||
|
|
local instanceData = data.bosses[instance]
|
||
|
|
if not instanceData then return bosses end
|
||
|
|
for i=1,#instanceData do
|
||
|
|
bosses[i] = instanceData[i]
|
||
|
|
end
|
||
|
|
return bosses
|
||
|
|
end
|
||
|
|
|
||
|
|
local function helperGetBestInSlotItems(raidTier, difficulty, specialization, slotId)
|
||
|
|
if not raidTier or not difficulty or not specialization then
|
||
|
|
BestInSlot.console:AddError("Not enough parameters given for function 'helperGetBestInSlotItems'", raidTier, difficulty, specialization, slotId)
|
||
|
|
if slotId then return nil else return {} end
|
||
|
|
end
|
||
|
|
local slots = BestInSlot.slots
|
||
|
|
local requiredItems = {}
|
||
|
|
local slotInfo = {}
|
||
|
|
if not data.raidTiers[raidTier] then return requiredItems end
|
||
|
|
for i=1,#slots do
|
||
|
|
local slotid = GetInventorySlotInfo(slots[i])
|
||
|
|
local itemid = BestInSlot.db.char[raidTier][difficulty][specialization][slotid]
|
||
|
|
if type(itemid) == "number" then
|
||
|
|
local item = BestInSlot:GetItem(itemid)
|
||
|
|
local itemRecord = {item = itemid, obtained = BestInSlot:HasItem(itemid, difficulty, true) or false, customitem = item and item.customitem}
|
||
|
|
requiredItems[i] = itemRecord
|
||
|
|
slotInfo[i] = slotid
|
||
|
|
end
|
||
|
|
end
|
||
|
|
if raidTier >= 70000 and raidTier < 80000 and BestInSlot.Artifacts then
|
||
|
|
local relics = { BestInSlot.Artifacts:GetBestInSlotRelics(raidTier, difficulty, specialization) }
|
||
|
|
for i=1,3 do
|
||
|
|
if relics[i] then
|
||
|
|
requiredItems[29 + i] = {item = relics[i], obtained = BestInSlot:HasItem(relics[i], difficulty, true) or false}
|
||
|
|
slotInfo[29 + i] = 29 + i
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
return requiredItems, slotInfo
|
||
|
|
end
|
||
|
|
|
||
|
|
--- Get the current BestInSlot items for the raidTier and difficulty
|
||
|
|
-- @param #number raidTier The raidTier to get the BestInSlot from
|
||
|
|
-- @param #number difficulty The Difficulty to request
|
||
|
|
function BestInSlot:GetBestInSlotItems(raidTier, difficulty, specialization, slotId)
|
||
|
|
if not raidTier or not difficulty then
|
||
|
|
BestInSlot.console:AddError("BestInSlot:GetBestInSlot() missed parameter RaidTier or difficulty", raidTier, difficulty, specialization)
|
||
|
|
return {}
|
||
|
|
end
|
||
|
|
if not specialization then
|
||
|
|
local bisItems = {}
|
||
|
|
local slotInfo = {}
|
||
|
|
for i=1,GetNumSpecializations() do
|
||
|
|
local specId = GetSpecializationInfo(i)
|
||
|
|
bisItems[specId], slotInfo[specId] = helperGetBestInSlotItems(raidTier,difficulty,specId, slotId)
|
||
|
|
end
|
||
|
|
bisItems.spec = GetSpecializationInfo(self:GetSpecialization())
|
||
|
|
return bisItems, slotInfo
|
||
|
|
else
|
||
|
|
return helperGetBestInSlotItems(raidTier,difficulty,specialization, slotId)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
local function helperOrderBiSItems(table, orderTable)
|
||
|
|
local newTable = {}
|
||
|
|
for i in pairs(orderTable) do
|
||
|
|
newTable[orderTable[i]] = table[i]
|
||
|
|
end
|
||
|
|
return newTable
|
||
|
|
end
|
||
|
|
--- Get the current BestInSlot items for the raidTier and difficulty ordered by SlotId instead of a plain list
|
||
|
|
-- @param #number raidTier The raidTier to get the BestInSlot from
|
||
|
|
-- @param #number difficulty The Difficulty to request
|
||
|
|
-- @param #number specialization The specialization ID to request
|
||
|
|
function BestInSlot:GetOrderedBestInSlotItems(raidTier, difficulty, specialization)
|
||
|
|
local BiSList, OrderedList = self:GetBestInSlotItems(raidTier, difficulty, specialization)
|
||
|
|
if specialization then
|
||
|
|
return helperOrderBiSItems(BiSList, OrderedList)
|
||
|
|
else
|
||
|
|
local newTable = {}
|
||
|
|
for k,v in pairs(BiSList) do
|
||
|
|
if v and OrderedList[k] then
|
||
|
|
newTable[k] = helperOrderBiSItems(v, OrderedList[k])
|
||
|
|
end
|
||
|
|
end
|
||
|
|
return newTable
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
---
|
||
|
|
function BestInSlot:SetBestInSlotInfo()
|
||
|
|
local raidTiers = self:GetRaidTiers()
|
||
|
|
local specs = self:GetCustomLists(self:GetAllSpecializations())
|
||
|
|
local result
|
||
|
|
for i=1,#raidTiers do
|
||
|
|
local raidTier = raidTiers[i]
|
||
|
|
local bisList = self.db.char[raidTier]
|
||
|
|
local difficulties = self:GetDifficulties(self.RAIDTIER, raidTier)
|
||
|
|
for difficId in pairs(bisList) do
|
||
|
|
for specId in pairs(specs) do
|
||
|
|
local specBiS = bisList[difficId][specId]
|
||
|
|
for j in pairs(specBiS) do
|
||
|
|
local item = itemData[specBiS[j]]
|
||
|
|
if item then
|
||
|
|
item.isBiS = item.isBiS or {}
|
||
|
|
item.isBiS[difficId] = item.isBiS[difficId] or {}
|
||
|
|
item.isBiS[difficId][specId] = true
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
|
||
|
|
function BestInSlot:IsItemTierToken(itemId)
|
||
|
|
return (tierTokenData[itemId] ~= nil)
|
||
|
|
end
|
||
|
|
|
||
|
|
local function helperIsTokenBestInSlot(BiSList, difficulty, specId, slotid)
|
||
|
|
for i, iteminfo in pairs(BiSList) do
|
||
|
|
local item = BestInSlot:GetItem(iteminfo.item, difficulty)
|
||
|
|
if item.tieritem and BestInSlot:GetItemSlotID(item.equipSlot) == slotid then
|
||
|
|
return BestInSlot:IsItemBestInSlot(item.itemid, difficulty, specId)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function BestInSlot:IsTokenBestInSlot(tokenItemId, difficulty, specId)
|
||
|
|
if not self:IsItemTierToken(tokenItemId) then return false end
|
||
|
|
local iteminfo = tierTokenData[tokenItemId]
|
||
|
|
local _, class = UnitClass("player")
|
||
|
|
if not tContains(iteminfo.classes, class) then return false end
|
||
|
|
if not specId then
|
||
|
|
local array = {}
|
||
|
|
for specId, BiSList in pairs(self:GetBestInSlotItems(iteminfo.raidtier, difficulty)) do
|
||
|
|
if specId ~= "spec" then
|
||
|
|
array[specId] = helperIsTokenBestInSlot(BiSList, difficulty, specId, iteminfo.slotid)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
return array;
|
||
|
|
else
|
||
|
|
return helperIsTokenBestInSlot(self:GetBestInSlotItems(iteminfo.raidtier, difficulty, specId), difficulty, specId, iteminfo.slotid)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
--- Checks wether the supplied itemid is BestInSlot
|
||
|
|
-- @param number itemdId The itemid to check
|
||
|
|
-- @param number difficulty The difficulty for which to query the list
|
||
|
|
-- @return table Table with the specs for which this item is BiS.
|
||
|
|
-- @return boolean false if not best in slot
|
||
|
|
function BestInSlot:IsItemBestInSlot(itemId, difficulty, specId)
|
||
|
|
if self:IsItemTierToken(itemId) then
|
||
|
|
return self:IsTokenBestInSlot(itemId, difficulty, specId)
|
||
|
|
end
|
||
|
|
|
||
|
|
local item = BestInSlot:GetItem(itemId)
|
||
|
|
if item then
|
||
|
|
if not item.isBiS then
|
||
|
|
self:SetBestInSlotInfo()
|
||
|
|
if not item.isBiS then --assume theere are no BiS items
|
||
|
|
itemData[itemId].isBiS = {}
|
||
|
|
end
|
||
|
|
end
|
||
|
|
if difficulty == nil then
|
||
|
|
return item.isBiS
|
||
|
|
elseif specId == nil then
|
||
|
|
return item.isBiS[difficulty]
|
||
|
|
else
|
||
|
|
if item.isBiS[difficulty] then
|
||
|
|
return item.isBiS[difficulty][specId]
|
||
|
|
else
|
||
|
|
return item.isBiS[difficulty]
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
local automaticUpdateQueue = {}
|
||
|
|
local automaticUpdateTimers
|
||
|
|
|
||
|
|
local function doAutomaticUpdate()
|
||
|
|
for i=1,#automaticUpdateQueue do
|
||
|
|
BestInSlot:SendAddonMessage("automaticUpdate",automaticUpdateQueue[i], "GUILD")
|
||
|
|
end
|
||
|
|
wipe(automaticUpdateQueue)
|
||
|
|
end
|
||
|
|
|
||
|
|
local function queueAutomaticUpdate(data)
|
||
|
|
local wasPresent = false
|
||
|
|
for i=1,#automaticUpdateQueue do
|
||
|
|
local item = automaticUpdateQueue[i]
|
||
|
|
if item.raidTier == data.raidTier and item.difficulty == data.difficulty and item.spec == data.spec and item.spec == data.spec then
|
||
|
|
item.bis = data.bis
|
||
|
|
if automaticUpdateTimers ~= nil then
|
||
|
|
automaticUpdateTimers:Cancel()
|
||
|
|
end
|
||
|
|
automaticUpdateTimers = C_Timer.NewTimer(10, doAutomaticUpdate)
|
||
|
|
wasPresent = true
|
||
|
|
break
|
||
|
|
end
|
||
|
|
end
|
||
|
|
if not wasPresent then
|
||
|
|
tinsert(automaticUpdateQueue, data)
|
||
|
|
if automaticUpdateTimers == nil or automaticUpdateTimers._remainingIterations == 0 then
|
||
|
|
automaticUpdateTimers = C_Timer.NewTimer(10, doAutomaticUpdate)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
--- Set the item as BestInSlot item
|
||
|
|
-- @param #number raidTier The raid tier to add the item to
|
||
|
|
-- @param #number difficultyId The difficulty to add the item to
|
||
|
|
-- @param #number specialization The specialization ID
|
||
|
|
-- @param #number slotId The slot to add the item to
|
||
|
|
-- @param #number itemId The item ID to add
|
||
|
|
function BestInSlot:SetItemBestInSlot(raidTier, difficultyId, specialization, slotId, itemId)
|
||
|
|
local currentId = self.db.char[raidTier][difficultyId][specialization][slotId]
|
||
|
|
if type(currentId) == "number" then --Checks if the number was fetched, default return from DB is an empty array
|
||
|
|
if itemData[currentId] then --checks if the item is present in the cahce, if so we need to set it's BiS to false
|
||
|
|
if itemData[currentId].isBiS and itemData[currentId].isBiS[difficultyId] then
|
||
|
|
local lowerRaidTiers = self:GetRaidTiers(self.RAIDTIER, raidTier)
|
||
|
|
local isStillBiS = false
|
||
|
|
local compareSlots
|
||
|
|
if slotId == 11 or slotId == 12 then compareSlots = {11,12}
|
||
|
|
elseif slotId == 13 or slotId == 14 then compareSlots = {13,14}
|
||
|
|
elseif slotId == 16 or slotId == 17 then compareSlots = {16,17}
|
||
|
|
else compareSlots = {slotId} end
|
||
|
|
local i = 1
|
||
|
|
while i <= #lowerRaidTiers and not isStillBiS do
|
||
|
|
for j=1,#compareSlots do
|
||
|
|
local id = self.db.char[lowerRaidTiers[i]][difficultyId][specialization][compareSlots[j]]
|
||
|
|
if id == currentId then
|
||
|
|
isStillBiS = true
|
||
|
|
break
|
||
|
|
end
|
||
|
|
end
|
||
|
|
i = i + 1
|
||
|
|
end
|
||
|
|
if not isStillBiS then
|
||
|
|
itemData[currentId].isBiS[difficultyId][specialization] = nil
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
self.db.char[raidTier][difficultyId][specialization][slotId] = itemId
|
||
|
|
if IsInGuild() and self.options.sendAutomaticUpdates and type(specialization) ~= "string" then
|
||
|
|
queueAutomaticUpdate({raidTier = raidTier, difficulty = difficultyId, bis = self:GetBestInSlotItems(raidTier, difficultyId, specialization), spec = specialization})
|
||
|
|
end
|
||
|
|
if itemId and itemData[itemId] then
|
||
|
|
itemData[itemId].isBiS = itemData[itemId].isBiS or {}
|
||
|
|
itemData[itemId].isBiS[difficultyId] = itemData[itemId].isBiS[difficultyId] or {}
|
||
|
|
itemData[itemId].isBiS[difficultyId][specialization] = true
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
local function helperSaveBestInSlotList(tierdb, guildname, guildplayer, bislist, raidtier, difficulty, spec, registerHistory)
|
||
|
|
if not tierdb[spec] then
|
||
|
|
if registerHistory and not BestInSlot.History:HasHistory(guildplayer, raidtier, difficulty, spec) then
|
||
|
|
BestInSlot.History:Add(guildplayer, {raidtier = raidtier, difficulty = difficulty, spec = spec} , BestInSlot.History.NEWLIST)
|
||
|
|
end
|
||
|
|
tierdb[spec] = bislist
|
||
|
|
else
|
||
|
|
for slot, slotdata in pairs(bislist) do
|
||
|
|
local previtem = tierdb[spec][slot]
|
||
|
|
if not previtem or previtem.item ~= slotdata.item then
|
||
|
|
tierdb[spec][slot] = slotdata
|
||
|
|
if registerHistory then
|
||
|
|
BestInSlot.History:Add(guildplayer, {raidtier = raidtier, difficulty = difficulty, slot = slot, previtem = previtem and previtem.item, newitem = slotdata.item})
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function BestInSlot:SaveGuildBestInSlotList(guildname, guildplayer, bislist, raidtier, difficulty, spec, version)
|
||
|
|
local chardb = self.db.factionrealm[guildname][guildplayer]
|
||
|
|
chardb.activeSpec = bislist.spec
|
||
|
|
local tierdb = chardb[raidtier][difficulty]
|
||
|
|
local registerHistory = version > 275
|
||
|
|
self.console:Add("Save guild bis list", guildname, guildplayer, raidtier, difficulty, spec, version, registerHistory, bislist)
|
||
|
|
if not spec then
|
||
|
|
for specid, specdata in pairs(bislist) do
|
||
|
|
if specid ~= "spec" then
|
||
|
|
helperSaveBestInSlotList(tierdb, guildname, guildplayer, specdata, raidtier, difficulty, specid, registerHistory)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
else
|
||
|
|
helperSaveBestInSlotList(tierdb, guildname, guildplayer, bislist, raidtier, difficulty, spec, registerHistory)
|
||
|
|
end
|
||
|
|
self:SendEvent("GuildCacheUpdated")
|
||
|
|
end
|
||
|
|
|
||
|
|
local function tableHasItems(table)
|
||
|
|
if type(table) == "table" then
|
||
|
|
for _ in pairs(table) do
|
||
|
|
return true
|
||
|
|
end
|
||
|
|
end
|
||
|
|
return false
|
||
|
|
end
|
||
|
|
|
||
|
|
local GetCacheDataCache = {}
|
||
|
|
local GuildBiSCache = {}
|
||
|
|
|
||
|
|
local function refreshCache()
|
||
|
|
BestInSlot:UnregisterEvent("GuildCacheUpdated", GetCacheDataCache.eventId)
|
||
|
|
wipe(GetCacheDataCache)
|
||
|
|
wipe(GuildBiSCache)
|
||
|
|
end
|
||
|
|
--- Gets the cached BiS lists
|
||
|
|
-- @return The Bis lists
|
||
|
|
function BestInSlot:GetCacheData()
|
||
|
|
if #GetCacheDataCache > 0 then
|
||
|
|
return unpack(GetCacheDataCache)
|
||
|
|
end
|
||
|
|
local db = self.db.factionrealm
|
||
|
|
local result = {}
|
||
|
|
local raidTiers = {}
|
||
|
|
local guilds = {}
|
||
|
|
local difficulties = {}
|
||
|
|
for guildName, guildData in pairs(db) do
|
||
|
|
if guildName ~= "_history" then
|
||
|
|
if tableHasItems(guildData) then
|
||
|
|
tinsert(guilds, guildName)
|
||
|
|
for playerName, playerData in pairs(guildData) do
|
||
|
|
if tableHasItems(playerData) then
|
||
|
|
--checks if the player allready exists in the table
|
||
|
|
result[playerName] = {guild = guildName}
|
||
|
|
|
||
|
|
for raidTier, raidTierData in pairs(playerData) do
|
||
|
|
if tableHasItems(raidTierData) then
|
||
|
|
--checks if the raidTier allready exists in the table
|
||
|
|
local addRaidTier = true
|
||
|
|
for i=1,#raidTiers do
|
||
|
|
if raidTiers[i] == raidTier then
|
||
|
|
addRaidTier = false
|
||
|
|
break
|
||
|
|
end
|
||
|
|
end
|
||
|
|
if addRaidTier then
|
||
|
|
tinsert(raidTiers, raidTier)
|
||
|
|
end
|
||
|
|
|
||
|
|
for difficulty, bisList in pairs(raidTierData) do
|
||
|
|
if tableHasItems(bisList) then
|
||
|
|
difficulties[raidTier] = difficulties[raidTier] or {}
|
||
|
|
local addDifficulty = true
|
||
|
|
for i=1,#difficulties[raidTier] do
|
||
|
|
if difficulties[raidTier][i] == difficulty then
|
||
|
|
addDifficulty = false
|
||
|
|
break
|
||
|
|
end
|
||
|
|
end
|
||
|
|
if addDifficulty then
|
||
|
|
tinsert(difficulties[raidTier], difficulty)
|
||
|
|
tsort(difficulties[raidTier])
|
||
|
|
end
|
||
|
|
tinsert(result[playerName], {raidTier, difficulty})
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
GetCacheDataCache = {
|
||
|
|
result, raidTiers, guilds, difficulties
|
||
|
|
}
|
||
|
|
GetCacheDataCache.eventId = self:RegisterEvent("GuildCacheUpdated", refreshCache)
|
||
|
|
return unpack(GetCacheDataCache)
|
||
|
|
end
|
||
|
|
|
||
|
|
---Gets a list with players who need the specified itemid
|
||
|
|
-- @return table Table with player as key, and their specs as table as value
|
||
|
|
function BestInSlot:GetGuildMembersByItemID(itemid, difficulty)
|
||
|
|
if GuildBiSCache[itemid] and GuildBiSCache[itemid][difficulty] then
|
||
|
|
return GuildBiSCache[itemid][difficulty]
|
||
|
|
end
|
||
|
|
if not IsInGuild() then return {} end
|
||
|
|
local guildName = GetGuildInfo("player")
|
||
|
|
local playerData, raidData, guildData, difficultyData = self:GetCacheData()
|
||
|
|
if not tContains(guildData, guildName) then return {} end
|
||
|
|
local result = {}
|
||
|
|
local tokenData = tierTokenData[itemid]
|
||
|
|
for player, playerInfo in pairs(playerData) do
|
||
|
|
local class = self:GetPlayerClass(player)
|
||
|
|
for i=1, #playerInfo do
|
||
|
|
local raidInfo = playerInfo[i]
|
||
|
|
if raidInfo[2] == difficulty then --Check if the player has set the difficulty that we ask
|
||
|
|
local raidBiSList = self.db.factionrealm[guildName][player][raidInfo[1]][raidInfo[2]]
|
||
|
|
for specId, specData in pairs(raidBiSList) do
|
||
|
|
if type(specData) == "table" then
|
||
|
|
for j in pairs(specData) do
|
||
|
|
if not tokenData then
|
||
|
|
if specData[j].item == itemid then
|
||
|
|
result[player] = result[player] or {}
|
||
|
|
result[player][specId] = specData[j].obtained
|
||
|
|
break
|
||
|
|
end
|
||
|
|
else
|
||
|
|
local itemid2 = specData[j].item
|
||
|
|
local item2 = itemData[itemid2]
|
||
|
|
if item2 then
|
||
|
|
local raidtier = self:GetRaidTiers(self.INSTANCE, item2.dungeon)
|
||
|
|
local equipslot = BestInSlot:GetItemSlotID(item2.equipSlot)
|
||
|
|
if tokenData.raidtier == raidtier and tContains(tokenData.classes, class) and equipslot == tokenData.slotid then
|
||
|
|
result[player] = result[player] or {}
|
||
|
|
result[player][specId] = specData[j].obtained
|
||
|
|
break
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
if not GuildBiSCache[itemid] then GuildBiSCache[itemid] = {} end
|
||
|
|
GuildBiSCache[itemid][difficulty] = result
|
||
|
|
return result
|
||
|
|
end
|
||
|
|
--- Function to catch the nil that GetSpecialization() returns
|
||
|
|
-- @return 1,2,3,4 depending on the character's specialization, defaults to 1 if none selected
|
||
|
|
function BestInSlot:GetSpecialization()
|
||
|
|
return GetSpecialization() or 1
|
||
|
|
end
|
||
|
|
|
||
|
|
--- Function to get all the specializations of the player
|
||
|
|
-- @return table Table with as index the global ID and value the localized description
|
||
|
|
function BestInSlot:GetAllSpecializations()
|
||
|
|
local result = {}
|
||
|
|
for i=1,GetNumSpecializations() do
|
||
|
|
local id, name = GetSpecializationInfo(i)
|
||
|
|
result[id] = name
|
||
|
|
end
|
||
|
|
return result
|
||
|
|
end
|
||
|
|
|
||
|
|
local playerClassCache = setmetatable({DEFAULT = {}}, {
|
||
|
|
__index = function(table, key)
|
||
|
|
if rawget(table, key) then return rawget(table, key) end
|
||
|
|
local searchRealm
|
||
|
|
local searchName = key
|
||
|
|
if (key):find("-") then
|
||
|
|
searchName, searchRealm = (key):match("(%D+)-(%D+)")
|
||
|
|
end
|
||
|
|
if IsInRaid() then
|
||
|
|
for i=1,40 do
|
||
|
|
local name, realm = UnitName("raid"..i)
|
||
|
|
if name and (not realm or realm == searchRealm) and name == searchName then
|
||
|
|
local class, localized = UnitClass("raid"..i) --1 = Hunter, 2==HUNTER
|
||
|
|
table[key] = {localized, class}
|
||
|
|
return {class, localized}
|
||
|
|
end
|
||
|
|
end
|
||
|
|
elseif IsInGroup() then
|
||
|
|
for i=1,5 do
|
||
|
|
local name, realm = UnitName("party"..i)
|
||
|
|
if name and (not realm or realm == searchRealm) and name == searchName then
|
||
|
|
local localized, class = UnitClass("party"..i) --1 = Hunter, 2==HUNTER
|
||
|
|
table[key] = {class, localized}
|
||
|
|
return {class, localized}
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
if IsInGuild() then
|
||
|
|
local playerRealm = select(2, UnitFullName("player"))
|
||
|
|
for i=1,GetNumGuildMembers() do
|
||
|
|
local nameWithRealm, _, _, _, localizedClass, _, _, _, _, _, class = GetGuildRosterInfo(i) --Warrior, WARRIOR
|
||
|
|
local playerName, realm = (nameWithRealm):match("(%D+)-(%D+)")
|
||
|
|
if playerName == searchName and ((not searchRealm and realm == playerRealm) or realm == searchRealm) then
|
||
|
|
table[key] = {class, localizedClass}
|
||
|
|
return {class, localizedClass}
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
return rawget(table, "DEFAULT")
|
||
|
|
end
|
||
|
|
})
|
||
|
|
|
||
|
|
--- Lookup what class a player in the guild is
|
||
|
|
-- @param #string name The name of the player to lookup
|
||
|
|
-- @return #string The class of the player if found
|
||
|
|
-- @return #nil Nil if the player was not found
|
||
|
|
function BestInSlot:GetPlayerClass(name)
|
||
|
|
if not name then return end
|
||
|
|
return playerClassCache[name][1], playerClassCache[name][2]
|
||
|
|
end
|
||
|
|
|
||
|
|
function BestInSlot:GetPlayerString(name)
|
||
|
|
local playerClass = self:GetPlayerClass(name)
|
||
|
|
if not playerClass then
|
||
|
|
return ("|cffb5b4ff%s|r"):format(name or "Unknown name")
|
||
|
|
else
|
||
|
|
return ("%s%s|r"):format(self:GetClassColor(playerClass) or "|cffb5b4ff", name or "Unknown name")
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function BestInSlot:GetClassColor(class)
|
||
|
|
if RAID_CLASS_COLORS[class] then
|
||
|
|
return "|c"..RAID_CLASS_COLORS[class].colorStr
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function BestInSlot:GetClassString(class)
|
||
|
|
return (self:GetClassColor(class) or "|cffb5ffb4")..(LOCALIZED_CLASS_NAMES_MALE[class] or "Unknown class").."|r"
|
||
|
|
end
|
||
|
|
|
||
|
|
local guildRankCache = {}
|
||
|
|
function BestInSlot:GetGuildRank(player)
|
||
|
|
if not guildRankCache[player] then
|
||
|
|
local searchName, searchRealm = player, nil
|
||
|
|
local playerRealm = select(2, UnitFullName("player"))
|
||
|
|
if((player):find("-")) then
|
||
|
|
searchName, searchRealm = (player):match("(%D+)-(%D+)")
|
||
|
|
end
|
||
|
|
for i=1,GetNumGuildMembers() do
|
||
|
|
local nameWithRealm, rankDescr, guildRank = GetGuildRosterInfo(i)
|
||
|
|
local playerName, realmName = (nameWithRealm):match("(%D+)-(%D+)")
|
||
|
|
if playerName == searchName and ((not searchRealm and realmName == playerRealm) or (searchRealm and realmName == searchRealm) )then
|
||
|
|
guildRankCache[player] = {guildRank + 1, rankDescr}
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
if guildRankCache[player] then return unpack(guildRankCache[player]) end
|
||
|
|
end
|
||
|
|
|
||
|
|
function BestInSlot:GetPlayerInfo()
|
||
|
|
return {race = select(2, UnitRace("player")), sex = UnitSex("player") - 2, name = UnitName("player"), class = select(2, UnitClass("player"))}
|
||
|
|
end
|