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.

912 lines
32 KiB

--lua
local unpack, select, pairs, strjoin, type, wipe, tostring, tonumber, error, setmetatable, tinsert, tsort, tremove, max, min
= unpack, select, pairs, strjoin, type, wipe, tostring, tonumber, error, setmetatable, table.insert, table.sort, table.remove, math.max, math.min
-- WoW API
local GetSpecializationInfoByID, GetItemStatDelta, IsControlKeyDown, IsShiftKeyDown, SetCursor, ResetCursor, DressUpItemLink, GetItemIcon, GetAddOnMetadata, GetSpecializationInfo, PlaySound, GetNumSpecializations, UnitAffectingCombat
= GetSpecializationInfoByID, GetItemStatDelta, IsControlKeyDown, IsShiftKeyDown, SetCursor, ResetCursor, DressUpItemLink, GetItemIcon, GetAddOnMetadata, GetSpecializationInfo, PlaySound, GetNumSpecializations, UnitAffectingCombat
local BestInSlot, L, AceGUI = unpack(select(2, ...))
local frameName = "BestInSlotFrame"
local frame
local menuItems = {}
local menuLabels
local container, menuContainer
local selectedMenuId = 1
local MINIMUMWIDTH, MINIMUMHEIGHT = 250, 300
local animating = false
local biggestWidth, biggestHeight
local function getWidthForModule(module)
local type = type(module.Width)
if type == "number" then return module.Width
elseif type == "function" then return module.Width() end
end
local function getHeightForModule(module)
local type = type(module.Height)
if type == "number" then return module.Height
elseif type == "function" then return module.Height() end
end
local function getBiggestWidth()
if not biggestWidth then
biggestWidth = getWidthForModule(menuItems[1])
for i=2,#menuItems do
local modWidth = getWidthForModule(menuItems[i])
if modWidth > biggestWidth then
biggestWidth = modWidth
end
end
end
return biggestWidth
end
local function getBiggestHeight()
if not biggestHeight then
biggestHeight = getHeightForModule(menuItems[1])
for i=2,#menuItems do
local height = getHeightForModule(menuItems[i])
if height > biggestHeight then
biggestHeight = height
end
end
end
return biggestHeight
end
local function addGuildInfoToTooltip(tooltip, itemId, difficultyId)
if BestInSlot.db.char.options.guildtooltip then
local players = BestInSlot:GetGuildMembersByItemID(itemId, difficultyId)
local first = true
local obtainedPlayers = {}
for player, playerSpecs in pairs(players) do
local guildRank = BestInSlot:GetGuildRank(player)
if guildRank and BestInSlot.db.char.options.showGuildRankInTooltip[guildRank] then
local specNames = {}
local specNamesObtained = {}
for specId, obtained in pairs(playerSpecs) do
local tbl = specNames
if obtained then tbl = specNamesObtained end
local specName = select(2, GetSpecializationInfoByID(specId))
tinsert(tbl, specName)
end
if #specNames > 0 then
if first then
tooltip:AddLine(L["The following people in your guild also need this item:"], nil, nil, nil, true)
first = false
end
tooltip:AddLine(("- %s (%s)"):format(BestInSlot:GetPlayerString(player), strjoin("/", unpack(specNames))))
end
if #specNamesObtained > 0 then
obtainedPlayers[player] = specNamesObtained
end
end
end
first = true
for player, specNames in pairs(obtainedPlayers) do
if first then
tooltip:AddLine("")
tooltip:AddLine(L["The following people in your guild already obtained this item"], nil, nil, nil, true)
first = false
end
tooltip:AddLine(("- %s (%s)"):format(BestInSlot:GetPlayerString(player), strjoin("/", unpack(specNames))))
end
end
end
--- Helper for starting an animation
local function startAnimating()
animating = true
end
--- Helper for stopping an animation
local function stopAnimating()
animating = false
end
--- Set the size of the frame
-- @param #number targetWidth The new width
-- @param #number targetHeight the new height
-- @param #function func The function to run when the frame is done resizing
function BestInSlot:SetFrameSize(targetWidth, targetHeight, func, ...)
if not frame then return end
if self.options.windowFixed then
frame:SetWidth(getBiggestWidth())
frame:SetHeight(getBiggestHeight())
if func and type(func) == "function" then
func(...)
end
return
end
local currentWidth, currentHeight = frame.frame.width, frame.frame.height
if not targetWidth then targetWidth = currentWidth end
if not targetHeight then targetHeight = currentHeight end
if targetWidth == currentWidth and targetHeight == currentHeight then
if type(func) == "function" then
func(...)
end
return
end
if self.options.instantAnimation then
frame:SetHeight(targetHeight)
frame:SetWidth(targetWidth)
if type(func) == "function" then
func(...)
end
else
local ANIMATIONTIME = 500 --milliseconds
local timer = 0
local incWidth = (targetWidth - currentWidth) / (ANIMATIONTIME / 80)
local incHeight = (targetHeight - currentHeight) / (ANIMATIONTIME / 80)
startAnimating()
local resizeTimerId
local funcArgs = {...}
resizeTimerId = self:ScheduleRepeatingTimer(function()
if not frame then stopAnimating() BestInSlot:CancelTimer(resizeTimerId) end
if incHeight < 0 then
frame:SetHeight(max(frame.frame.height + incHeight, targetHeight))
else
frame:SetHeight(min(frame.frame.height + incHeight, targetHeight))
end
if incWidth < 0 then
frame:SetWidth(max(frame.frame.width + incWidth, targetWidth))
else
frame:SetWidth(min(frame.frame.width + incWidth, targetWidth))
end
if frame.frame.width == targetWidth and frame.frame.height == targetHeight then
BestInSlot:CancelTimer(resizeTimerId)
if func and type(func) == "function" then
stopAnimating()
func(unpack(funcArgs))
end
end
end,
0.025)
end
end
--- Sets the content to the supplied menuWidget
-- @param #table menuWidget An AceGUI Widget with the UserData "id" set to the id of the menu to set the content to
local function SetContent(menuWidget)
local id = menuWidget:GetUserData("id")
if not id then error("The menu widget that has been made didn't provide an id") end
if animating then return false end
return BestInSlot:SetMenu(id)
end
function BestInSlot:SetMenu(id, ...)
local args = {...}
local item = menuItems[id]
if selectedMenuId then
local selectedItem = menuItems[selectedMenuId]
selectedItem:Close()
BestInSlot:HideItemTooltip()
wipe(container:GetUserDataTable())
local menuLabel = menuLabels[selectedMenuId]
if menuLabel then
menuLabel:SetColor(0.8941, 0.73725, 0.01961)
menuLabel:SetHighlight("Interface\\QuestFrame\\UI-QuestTitleHighlight")
end
end
local menuLabel = menuLabels[id]
if menuLabel then
menuLabel:SetColor(1,1,1)
menuLabel:SetHighlight("Interface\\QuestFrame\\UI-QuestLogTitleHighlight")
end
container:ReleaseChildren()
container:SetLayout("Flow")
self:SetFrameSize(getWidthForModule(item), getHeightForModule(item), function() container:ResumeLayout() item:Draw(container, unpack(args)) BestInSlot:CheckTutorials(item.Description) end)
selectedMenuId = id
return true
end
function BestInSlot:HideItemTooltip()
if GameTooltip:IsShown() then
GameTooltip.itemInfo = nil
GameTooltip:Hide()
end
if BISComparisonTooltip then
BISComparisonTooltip:Hide()
end
end
local playerClass
function BestInSlot:GetItemTooltip(itemid, difficulty, frame, itemlink)
if frame then
GameTooltip:SetOwner(frame.frame)
GameTooltip:SetAnchorType("ANCHOR_NONE")
GameTooltip:SetPoint("TOPLEFT", frame.frame, "TOPRIGHT", 10, 0)
else
GameTooltip:SetOwner(UIParent)
end
if not self:ItemExists(itemid) and itemlink then
GameTooltip:SetHyperlink(itemlink)
end
local item = self:GetItem(itemid, difficulty)
if not item then return end
playerClass = playerClass or select(3, UnitClass("player"))
GameTooltip:SetHyperlink(item.link, playerClass, select(2, self:GetSelected(self.SPECIALIZATION)))
GameTooltip.itemInfo = item
if self.options.DEBUG then
GameTooltip:AddLine("itemid: "..tostring(itemid))
GameTooltip:AddLine("dungeon: "..tostring(item.dungeon))
GameTooltip:AddLine("bossid: "..tostring(item.bossid))
GameTooltip:AddLine("link: "..tostring(item.link))
GameTooltip:AddLine("difficulty: "..tostring(item.difficulty))
GameTooltip:AddLine("equipslot: "..tostring(item.equipSlot))
if item.misc then
GameTooltip:AddLine("Item is misc source: "..item.misc)
end
if item.tieritem then
GameTooltip:AddLine("Item is tier item")
end
if item.tiertoken then
GameTooltip:AddLine("Item is tier token")
end
end
GameTooltip:Show()
return GameTooltip
end
function BestInSlot:GetItemComparisonTooltip(tooltip, selectedItem, difficulty)
local compareItem = tooltip.itemInfo
local item = self:GetItem(selectedItem, difficulty)
if not compareItem then return end
if compareItem.itemid == item.itemid and compareItem.difficulty == item.difficulty then return end --don't show then it's comparing itself!
if not BISComparisonTooltip then
CreateFrame("GameTooltip", "BISComparisonTooltip", tooltip, "GameTooltipTemplate")
end
if not BISComparisonTooltip:IsShown() then
BISComparisonTooltip:SetOwner(BestInSlot.frame.frame)
BISComparisonTooltip:SetAnchorType("ANCHOR_NONE")
BISComparisonTooltip:SetPoint("TOPLEFT", GameTooltip, "TOPRIGHT")
end
BISComparisonTooltip:SetHyperlink(item.link, playerClass, select(2, self:GetSelected(self.SPECIALIZATION)))
local statDelta = GetItemStatDelta(item.link, compareItem.link)
for stat,value in pairs(statDelta) do
BISComparisonTooltip:AddLine(("%s%d %s"..FONT_COLOR_CODE_CLOSE):format((value > 0 and GREEN_FONT_COLOR_CODE or RED_FONT_COLOR_CODE), value, tostring(_G[stat])))
end
BISComparisonTooltip:Show()
end
function BestInSlot:GameTooltip_OnTooltipSetItem(tooltip, ...)
if self.db.char.options.showBiSTooltip and (self.db.char.options.tooltipCombat or not UnitAffectingCombat("player")) then
local _, link = tooltip:GetItem()
if link then
local itemId, instanceID = self:GetItemInfoFromLink(link)
local exists, existType = self:ItemExists(itemId)
if not exists then return end
if itemId then
instanceID = tonumber(instanceID)
instanceID = self:GetDifficultyIdForDungeon(instanceID, nil, true) or 1
local bisInfo
if existType == "item" then
bisInfo = self:IsItemBestInSlot(itemId, instanceID)
else
bisInfo = self:IsTokenBestInSlot(itemId, instanceID)
end
local isBiS = false --catch empty tables
if bisInfo and type(bisInfo) == "table" then
local specsText = ""
local first = true
for specId in pairs(bisInfo) do
isBiS = true
if not first then
specsText = specsText.."/"
end
specsText = specsText..(type(specId) == "string" and specId or select(2,GetSpecializationInfoByID(specId))) --custom lists can just show as custom list, otherwise lookup spec name
first = false
end
if isBiS then
local color = self.colorHighlight
if self:HasItem(itemId, instanceID, true) then
color = GREEN_FONT_COLOR_CODE
end
tooltip:AddLine(("%1$s (%2$s)"):format(color.."BestInSlot"..self.colorNormal, color..specsText.."|r"))
end
end
local item = self:GetItem(itemId)
if item and (self.db.char.options.tooltipSource or self.options.DEBUG) then
if item.multiplesources then
tooltip:AddLine(SOURCES..":")
for dungeonname,v in pairs(item.multiplesources) do
for bossid in pairs(v) do
tooltip:AddLine(("%s%s %s(%s)|r"):format(self.colorHighlight, self:GetDescription(self.BOSS, dungeonname, bossid), self.colorNormal, dungeonname))
end
end
elseif item.dungeon and item.bossid then
tooltip:AddLine(("%s %s%s"):format(SOURCE, self.colorHighlight, self:GetDescription(self.BOSS, item.dungeon, item.bossid)))
elseif item.misc then
tooltip:AddLine(("%s %s%s"):format(SOURCE, self.colorHighlight, item.misc))
end
end
if self:ItemExists(itemId) then
addGuildInfoToTooltip(tooltip, itemId, instanceID)
end
end
end
end
end
local function helperOnEnter(widget, ...)
local itemid = widget:GetUserData("itemid")
local itemlink = widget:GetUserData("itemlink")
local callbacks = widget:GetUserData("callbacks")
local difficulty = widget:GetUserData("difficulty")
if not itemid then return end
BestInSlot:GetItemTooltip(itemid,difficulty, BestInSlot.frame, itemlink)
if IsControlKeyDown() then
SetCursor("INSPECT_CURSOR")
else
ResetCursor()
end
if callbacks.OnEnter then
for i=1,#callbacks.OnEnter do
callbacks.OnEnter[i](widget, ...)
end
end
end
local function helperOnLeave(widget, ...)
ResetCursor()
local callbacks = widget:GetUserData("callbacks")
BestInSlot:HideItemTooltip()
if callbacks.OnLeave then
for i=1,#callbacks.OnLeave do
callbacks.OnLeave[i](widget, ...)
end
end
end
local function helperOnClick(widget, ...)
local itemlink = widget:GetUserData("itemlink")
local callbacks = widget:GetUserData("callbacks")
if not itemlink then return end
if IsShiftKeyDown() then
if not ChatEdit_InsertLink(itemlink) then
ChatFrame_OpenChat(itemlink)
end
elseif IsControlKeyDown() then
DressUpItemLink(itemlink)
end
if callbacks.OnClick then
for i=1,#callbacks.OnClick do
callbacks.OnClick[i](widget, ...)
end
end
end
local function itemLinkCallback(self, name, func)
if type(func) ~= "function" then error("2nd parameter should be function") end
local callbacks = self:GetUserData("callbacks")
if name == "OnEnter" or name == "OnLeave" or name == "OnClick" then
if not callbacks[name] then
callbacks[name] = {}
end
tinsert(callbacks[name], func)
else
error("Invalid callback method")
end
end
function BestInSlot:GetItemLinkLabel(itemid, difficulty, withIcon)
if not withIcon and withIcon ~= false then
withIcon = true
end
if not itemid then
error("GetItemLinkLabel cannot be called without itemid argument")
return
end
local label = AceGUI:Create("InteractiveLabel")
label:SetUserData("itemid", itemid)
label:SetUserData("difficulty", difficulty)
label:SetCallback("OnEnter", helperOnEnter)
label:SetCallback("OnLeave", helperOnLeave)
label:SetCallback("OnClick", helperOnClick)
if itemid ~= "PLACEHOLDER" then
local item = self:GetItem(itemid, difficulty)
if not item then
label:SetText("Undefined itemid")
else
label:SetUserData("itemlink", item.link)
label:SetText(item.link)
end
if withIcon then
label:SetImage(GetItemIcon(itemid))
end
else
label:SetText("")
end
label:SetUserData("callbacks", {})
return label
end
local UISpecialFramesId
local function frameClosed(widget)
if UISpecialFrames[UISpecialFramesId] == frameName then
tremove(UISpecialFrames, UISpecialFramesId)
end
BestInSlot:HideFrame()
end
local function GetFrame()
if frame then return end
frame = AceGUI:Create("Frame")
frame.frame:SetFrameStrata("HIGH")
BestInSlot.frame = frame
--make closable with escape
_G[frameName] = frame.frame
tinsert(UISpecialFrames, frameName)
UISpecialFramesId = #UISpecialFrames
frame:SetTitle("Best In Slot Redux "..GetAddOnMetadata("BestInSlotRedux", "Version"))
frame:SetStatusText(("By: %s, %s, %s, %s, %s."):format(BestInSlot.Author0, BestInSlot.Author4, BestInSlot.Author1, BestInSlot.Author2, BestInSlot.Author3))
local FrameBackdrop = {
bgFile = "Interface\\DialogFrame\\UI-DialogBox-Background-Dark",
edgeFile = "Interface\\DialogFrame\\UI-DialogBox-Border",
tile = true, tileSize = 32, edgeSize = 32,
insets = { left = 8, right = 8, top = 8, bottom = 8 }
}
frame.frame:SetBackdrop(FrameBackdrop)
frame:SetCallback("OnClose", frameClosed)
frame:EnableResize(nil)
local dimensionsSet = false
local initItem
if selectedMenuId then --GUI has been opened before
local item = menuItems[selectedMenuId]
initItem = item
if item.Height then
frame:SetHeight(getHeightForModule(item))
frame:SetWidth(getWidthForModule(item))
dimensionsSet = true
end
end
if not dimensionsSet or BestInSlot.db.char.options.windowFixed then
if BestInSlot.db.char.options.windowFixed then
frame:SetHeight(getBiggestHeight())
frame:SetWidth(getBiggestWidth())
else
frame:SetHeight(MINIMUMHEIGHT)
frame:SetWidth(MINIMUMWIDTH)
end
end
frame:PauseLayout()
frame:SetPoint("TOPLEFT", UIParent, "TOPLEFT", 200, -250)
if not BestInSlot.hasModules then
local label = AceGUI:Create("Label")
label:SetText(L["This addon requires atleast 1 expansion module! Please enable one!"])
label:SetFullWidth(true)
label:SetPoint("TOPLEFT", frame.frame, "TOPLEFT")
frame:AddChild(label)
return
end
menuContainer = AceGUI:Create("SimpleGroup")
menuContainer:SetPoint("TOPLEFT", frame.frame, "TOPLEFT", 10, -20)
menuContainer:SetPoint("BOTTOMRIGHT", frame.frame, "BOTTOMLEFT", 160, 50)
menuContainer:PauseLayout()
local previousLabel
menuLabels = {}
for i=1,#menuItems do
local item = menuItems[i]
if item.IsShown() then
local interactiveLabel = AceGUI:Create("InteractiveLabel")
interactiveLabel:SetText(item.Description)
interactiveLabel:SetWidth(140)
interactiveLabel:SetFont(GameFontNormalSmall:GetFont(), 14, nil)
interactiveLabel:SetUserData("name", item.Description)
interactiveLabel:SetUserData("id", i)
if selectedMenuId and selectedMenuId == i then
interactiveLabel:SetHighlight("Interface\\QuestFrame\\UI-QuestLogTitleHighlight")
interactiveLabel:SetColor(1,1,1)
else
interactiveLabel:SetColor(0.8941, 0.73725, 0.01961)
interactiveLabel:SetHighlight("Interface\\QuestFrame\\UI-QuestTitleHighlight")
end
interactiveLabel:SetCallback("OnClick", SetContent)
if not previousLabel then
interactiveLabel:SetPoint("TOPLEFT", menuContainer.frame, "TOPLEFT", 10, -10)
interactiveLabel:SetPoint("TOPRIGHT", menuContainer.frame, "TOPRIGHT", -10, -10)
else
interactiveLabel:SetPoint("TOPLEFT", previousLabel.frame, "BOTTOMLEFT", 0, -10)
interactiveLabel:SetPoint("TOPRIGHT", previousLabel.frame, "BOTTOMRIGHT", 0, -10)
end
previousLabel = interactiveLabel
menuLabels[i] = interactiveLabel
menuContainer:AddChild(interactiveLabel)
end
end
frame:AddChild(menuContainer)
frame:SetUserData("menu", menuContainer)
container = AceGUI:Create("SimpleGroup")
container:SetPoint("TOPRIGHT", frame.frame, "TOPRIGHT", -20, -20)
container:SetPoint("BOTTOMLEFT", menuContainer.frame, "BOTTOMRIGHT", 5, 0)
container:SetLayout("Flow")
frame:AddChild(container)
frame:SetUserData("content", container)
if initItem then
initItem:Draw(container)
BestInSlot:CheckTutorials()
end
local windowpos = BestInSlot.db.char.windowpos
if windowpos.point then
frame:ClearAllPoints()
frame:SetPoint(windowpos.point, windowpos.relativeTo, windowpos.relativePoint, windowpos.xOffset, windowpos.yOffset)
end
end
function BestInSlot:HideFrame()
if frame then
local point, relativeTo, relativePoint, xOffset, yOffset = frame.frame:GetPoint(1)
self.db.char.windowpos = {
point = point,
relativeTo = type(relativeTo) == "table" and relativeTo:GetName() or relativeTo,
relativePoint = relativePoint,
xOffset = xOffset,
yOffset = yOffset
}
if selectedMenuId then
local item = menuItems[selectedMenuId]
if item then
item:Close()
end
end
frame.frame:SetBackdrop({ --set backdrop back to the original backdrop for other users
bgFile = "Interface\\DialogFrame\\UI-DialogBox-Background",
edgeFile = "Interface\\DialogFrame\\UI-DialogBox-Border",
tile = true, tileSize = 32, edgeSize = 32,
insets = { left = 8, right = 8, top = 8, bottom = 8 }
})
frame.frame:SetFrameStrata("FULLSCREEN_DIALOG")
frame:Release()
frame = nil
container = nil
menuContainer = nil
PlaySound(SOUNDKIT.IG_CHARACTER_INFO_CLOSE)
if BestInSlotTutorialFrame:IsVisible() then
BestInSlotTutorialFrame:Hide()
end
BestInSlot.frame = nil
menuLabels = nil
end
end
function BestInSlot:GetSelectedMenuItem()
return menuItems[selectedMenuId].Description
end
local function defaultDraw(menu, container)
local label = AceGUI:Create("Label")
label:SetText("This menu has not been implemented yet")
container:AddChild(label)
end
local function defaultClose() end
local function defaultIsShown() return true end
-- {
-- descr = "Description",
-- draw = drawFunction(),
-- [width = 100,]
-- [height = 100,]
-- [close = closeFunction(),]
-- [isShown = isShownFunction(),]
-- }
local menuPrototype = setmetatable({
Draw = defaultDraw,
Width = 500,
Height = 600,
Close = defaultClose,
IsShown = defaultIsShown,
},
{__index = BestInSlot})
function BestInSlot:GetMenuPrototype(menuName)
if not menuName or type(menuName) ~= "string" then error("GetMenuPrototype must always have a name in the form of a string") end
local menuIndex = #menuItems + 1
local prototype = setmetatable({Description = menuName, menuindex = menuIndex}, {__index = menuPrototype})
menuItems[menuIndex] = prototype
return prototype
end
-------------------------------------------------
-- Functions for add-on wide selection values
--
local selected
function BestInSlot:InitSelectedSettings()
if selected == nil then
selected = self.db.char.selected
local verify = {self.EXPANSION, self.RAIDTIER, self.INSTANCE, self.DIFFICULTY, self.SPECIALIZATION}
local verified = true
for _, type in pairs(verify) do
if not selected[type] then
verified = false
break
end
end
if not verified or not tContains(self:GetInstances(), selected[self.INSTANCE]) then
selected = {} --when not verified, reset selected
self:SetSelected(self.EXPANSION, self:GetLatest(self.EXPANSION))
self:SetSelected(self.SPECIALIZATION)
else
self:ValidateSelected(self.INSTANCE)
end
end
end
function BestInSlot:GetSelected(bisType)
if not selected then
self:InitSelectedSettings()
end
if not selected[self.SPECIALIZATION] or selected[self.SPECIALIZATION] == -1 then
self:SetSelected(self.SPECIALIZATION)
end
if bisType then
if selected[bisType] then
if bisType == self.SPECIALIZATION then
return selected[bisType], type(selected[bisType]) == "string" and self:GetSpecForCustomList(selected[bisType]) or selected[bisType]
end
return selected[bisType]
else
error("You supplied an invalid type!")
end
else
return {expansion = selected[self.EXPANSION], raidtier = selected[self.RAIDTIER], instance = selected[self.INSTANCE], difficulty = selected[self.DIFFICULTY], specialization = selected[self.SPECIALIZATION]}
end
end
function BestInSlot:ValidateSelected(type)
if type == self.RAIDTIER then
local expansion = self:GetExpansions(type, selected[type])
if selected[self.EXPANSION] ~= expansion then
self:SetSelected(self.EXPANSION, expansion)
end
local difficulties = self:GetDifficulties(type, selected[type])
if not difficulties[selected[self.DIFFICULTY]] then
self:SetSelected(self.DIFFICULTY, difficulties[#difficulties])
end
elseif type == self.INSTANCE then
local raidTier = self:GetRaidTiers(type, selected[type])
if selected[self.RAIDTIER] ~= raidTier then
self:SetSelected(self.RAIDTIER, raidTier)
end
elseif type == self.DIFFICULTY then
local difficulties = self:GetDifficulties(self.RAIDTIER, selected[self.RAIDTIER])
if selected[type] > #difficulties then
self:SetSelected(type, #difficulties)
end
end
self.db.char.selected = selected
end
local eventBuffer = {}
local eventTimer
local function sendEvents()
for k,v in pairs(eventBuffer) do
BestInSlot:SendEvent("SelectedChange", k, v)
end
wipe(eventBuffer)
eventTimer = nil
end
function BestInSlot:SetSelected(type, value)
if not selected then
self:InitSelectedSettings()
end
if value and selected[type] == value then return end
if value then
selected[type] = value
end
if type == self.EXPANSION then
self:SetSelected(self.RAIDTIER, self:GetLatest(self.RAIDTIER, self.EXPANSION, value))
elseif type == self.RAIDTIER then
self:SetSelected(self.INSTANCE, self:GetLatest(self.INSTANCE, self.RAIDTIER, value))
elseif type == self.INSTANCE then
if (not selected[self.DIFFICULTY]) or #(self:GetDifficulties(type, selected[type])) < selected[self.DIFFICULTY] then
self:SetSelected(self.DIFFICULTY, self:GetLatest(self.DIFFICULTY, self.INSTANCE, value))
end
elseif type == self.SPECIALIZATION then
if not value then
selected[self.SPECIALIZATION] = GetSpecializationInfo(self:GetSpecialization())
end
end
eventBuffer[type] = value
if not eventTimer then
eventTimer = C_Timer.After(0.001, sendEvents)
end
self:ValidateSelected(type)
self.db.char.selected = selected
end
local function getRaidTierDropdown(dropdown)
local order = BestInSlot:GetRaidTiers()
local list = {}
for i=1,#order do
list[order[i]] = BestInSlot:GetDescription(BestInSlot.RAIDTIER, order[i])
end
dropdown:SetList(list)
local value = BestInSlot:GetSelected(BestInSlot.RAIDTIER)
dropdown:SetValue(value)
dropdown:SetLabel(L["Raid Tier"])
dropdown:SetUserData(BestInSlot.RAIDTIER, value)
return dropdown
end
local function getDifficultyDropdown(dropdown)
local raidTierValue = BestInSlot:GetSelected(BestInSlot.RAIDTIER)
dropdown:SetList(BestInSlot:GetDifficulties(BestInSlot.RAIDTIER, raidTierValue))
local value = BestInSlot:GetSelected(BestInSlot.DIFFICULTY)
dropdown:SetValue(value)
dropdown:SetLabel(L["Difficulty"])
dropdown:SetUserData(BestInSlot.DIFFICULTY, value)
dropdown:SetUserData(BestInSlot.RAIDTIER, raidTierValue)
return dropdown
end
local function getSpecializationDropdown(dropdown)
local customLists = BestInSlot.db.char.customlists
local hasCustomLists = #customLists > 0
dropdown:SetList({})
if hasCustomLists then
dropdown:AddItem("_header1", L["Specialization"])
dropdown:SetItemDisabled("_header1", true)
end
for i=1,GetNumSpecializations() do
local id, name = GetSpecializationInfo(i)
dropdown:AddItem(id, name)
end
dropdown:SetLabel(L["Specialization"])
if #customLists > 0 then
dropdown:AddItem("_spacer", "")
dropdown:SetItemDisabled("_spacer", true)
dropdown:AddItem("_header", L["Custom Lists"])
dropdown:SetItemDisabled("_header", true)
for i=1,#customLists do
dropdown:AddItem(customLists[i].name, customLists[i].name)
end
end
local value = BestInSlot:GetSelected(BestInSlot.SPECIALIZATION)
dropdown:SetValue(value)
dropdown:SetUserData(BestInSlot.SPECIALIZATION, value)
end
local function getDungeonDropdown(dropdown)
dropdown:SetList({})
local options = {}
local instances = BestInSlot:GetInstances()
tsort(instances, function(a,b) return BestInSlot:GetRaidTiers(BestInSlot.INSTANCE, a) < BestInSlot:GetRaidTiers(BestInSlot.INSTANCE, b) end)
local selectedDungeon = BestInSlot:GetSelected(BestInSlot.INSTANCE)
local lastExpansion
for i=1,#instances do
local expansion = BestInSlot:GetExpansions(BestInSlot.INSTANCE, instances[i])
if expansion ~= lastExpansion then
local name = BestInSlot:GetDescription(BestInSlot.EXPANSION, expansion)
dropdown:AddItem(name, " "..name)
dropdown:SetItemDisabled(name, true)
lastExpansion = expansion
end
dropdown:AddItem(instances[i], BestInSlot:GetDescription(BestInSlot.INSTANCE, instances[i]))
end
dropdown:SetLabel(L["Raid Instance"])
dropdown:SetValue(selectedDungeon)
dropdown:SetUserData(BestInSlot.INSTANCE, selectedDungeon)
end
local function dropDownCallback(dropdown, event, value, ...)
local udt = dropdown:GetUserDataTable()
BestInSlot:SetSelected(udt.type, value)
if udt.customcallback then udt.customcallback(dropdown, event, value, ...) end
end
local function dropdownReleased(dropdown)
BestInSlot:UnregisterEvent("SelectedChange", dropdown:GetUserData("eventid"))
end
local dropdownConstructors = {
[BestInSlot.RAIDTIER] = getRaidTierDropdown,
[BestInSlot.INSTANCE] = getDungeonDropdown,
[BestInSlot.DIFFICULTY] = getDifficultyDropdown,
[BestInSlot.SPECIALIZATION] = getSpecializationDropdown
}
local function verifyRequireChange(widget, typeChanged, value)
local udt = widget:GetUserDataTable()
return udt[typeChanged] and udt[typeChanged] ~= value or false
end
local function defaultOnChange(event, typeChanged, valueChanged, dropdown, ...)
if verifyRequireChange(dropdown, typeChanged, valueChanged) then
dropdown:SetValue(valueChanged)
dropdown:SetUserData(typeChanged, valueChanged)
end
end
local function difficultyOnChange(event, typeChanged, valueChanged, dropdown, ...)
if typeChanged == BestInSlot.RAIDTIER or typeChanged == BestInSlot.INSTANCE then
dropdownConstructors[BestInSlot.DIFFICULTY](dropdown)
elseif typeChanged == BestInSlot.DIFFICULTY then
dropdown:SetValue(valueChanged)
dropdown:SetUserData(typeChanged, valueChanged)
end
end
local dropdownOnChange = {
[BestInSlot.RAIDTIER] = defaultOnChange,
[BestInSlot.INSTANCE] = defaultOnChange,
[BestInSlot.DIFFICULTY] = difficultyOnChange,
[BestInSlot.SPECIALIZATION] = defaultOnChange
}
function BestInSlot:GetDropdown(type, dropdown, callback)
local dropdown = dropdown or AceGUI:Create("Dropdown")
if not dropdownConstructors[type] then error("Can't make a dropdown of that type!") end
dropdownConstructors[type](dropdown)
dropdown:SetUserData("type", type)
dropdown:SetUserData("customcallback", callback)
dropdown:SetUserData("eventid",self:RegisterEvent("SelectedChange", dropdownOnChange[type], dropdown))
dropdown:SetCallback("OnValueChanged", dropDownCallback)
dropdown:SetCallback("OnRelease", dropdownReleased)
return dropdown
end
local function uneditableOnTextChanged(widget)
widget:SetText(widget:GetUserData("contents"))
end
function BestInSlot:CreateUneditableTextbox(contents, description, type)
local editBox = AceGUI:Create(type or "EditBox")
if description then
editBox:SetLabel(description)
end
editBox:SetText(contents)
editBox:SetUserData("contents", contents)
editBox:SetFullWidth(true)
editBox:SetCallback("OnTextChanged", uneditableOnTextChanged)
return editBox
end
function BestInSlot:QuickCreate(widgtype, options, addTo, point, ...)
local result = AceGUI:Create(widgtype)
if options then
for k,v in pairs(options) do
if type(v) == "table" then
result[k](result, unpack(v))
else
result[k](result, v)
end
end
end
if addTo then
if point then
result:SetPoint(point, addTo.frame, ...)
end
addTo:AddChild(result)
end
return result
end
function BestInSlot:ShowFrame()
if not frame then
GetFrame()
PlaySound(SOUNDKIT.IG_CHARACTER_INFO_OPEN)
end
end
function BestInSlot:ToggleFrame()
if not frame then
self:ShowFrame()
else
self:HideFrame()
end
end
BestInSlot:RegisterSlashCmd("show", (L["%s or %s"]):format("/bis", "/bestinslot"), function() BestInSlot:ShowFrame() end, 1)
BestInSlot:RegisterSlashCmd("reset", ("/bis %s - %s"):format("reset", L["Resets the window to it's original position"]), function() if frame then BestInSlot:HideFrame() end wipe(BestInSlot.db.char.windowpos) end)