Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- local addonName, vars = ...
- SavedInstances = vars
- local addon = vars
- local addonAbbrev = "SI"
- vars.core = LibStub("AceAddon-3.0"):NewAddon(addonName, "AceEvent-3.0", "AceTimer-3.0", "AceBucket-3.0")
- local core = vars.core
- local L = vars.L
- vars.LDB = LibStub("LibDataBroker-1.1", true)
- vars.icon = vars.LDB and LibStub("LibDBIcon-1.0", true)
- local QTip = LibStub("LibQTip-1.0")
- local dataobject, db, config
- local maxdiff = 23 -- max number of instance difficulties
- local maxcol = 4 -- max columns per player+instance
- addon.svnrev = {}
- addon.svnrev["SavedInstances.lua"] = tonumber(("$Revision: 518 $"):match("%d+"))
- -- local (optimal) references to provided functions
- local table, math, bit, string, pairs, ipairs, unpack, strsplit, time, type, wipe, tonumber, select, strsub =
- table, math, bit, string, pairs, ipairs, unpack, strsplit, time, type, wipe, tonumber, select, strsub
- local GetSavedInstanceInfo, GetNumSavedInstances, GetSavedInstanceChatLink, GetLFGDungeonNumEncounters, GetLFGDungeonEncounterInfo, GetNumRandomDungeons, GetLFGRandomDungeonInfo, GetLFGDungeonInfo, LFGGetDungeonInfoByID, GetLFGDungeonRewards, GetTime, UnitIsUnit, GetInstanceInfo, IsInInstance, SecondsToTime, GetQuestResetTime, GetGameTime, GetCurrencyInfo, GetNumGroupMembers =
- GetSavedInstanceInfo, GetNumSavedInstances, GetSavedInstanceChatLink, GetLFGDungeonNumEncounters, GetLFGDungeonEncounterInfo, GetNumRandomDungeons, GetLFGRandomDungeonInfo, GetLFGDungeonInfo, LFGGetDungeonInfoByID, GetLFGDungeonRewards, GetTime, UnitIsUnit, GetInstanceInfo, IsInInstance, SecondsToTime, GetQuestResetTime, GetGameTime, GetCurrencyInfo, GetNumGroupMembers
- -- local (optimal) references to Blizzard's strings
- local RAID_CLASS_COLORS = RAID_CLASS_COLORS
- local RAID_FINDER = PLAYER_DIFFICULTY3
- local FONTEND = FONT_COLOR_CODE_CLOSE
- local GOLDFONT = NORMAL_FONT_COLOR_CODE
- local YELLOWFONT = LIGHTYELLOW_FONT_COLOR_CODE
- local REDFONT = RED_FONT_COLOR_CODE
- local GREENFONT = GREEN_FONT_COLOR_CODE
- local WHITEFONT = HIGHLIGHT_FONT_COLOR_CODE
- local GRAYFONT = GRAY_FONT_COLOR_CODE
- local GRAY_COLOR = { 0.5, 0.5, 0.5, 1 }
- local LFD_RANDOM_REWARD_EXPLANATION2 = LFD_RANDOM_REWARD_EXPLANATION2
- local INSTANCE_SAVED, TRANSFER_ABORT_TOO_MANY_INSTANCES, NO_RAID_INSTANCES_SAVED =
- INSTANCE_SAVED, TRANSFER_ABORT_TOO_MANY_INSTANCES, NO_RAID_INSTANCES_SAVED
- vars.Indicators = {
- ICON_STAR = ICON_LIST[1] .. "16:16:0:0|t",
- ICON_CIRCLE = ICON_LIST[2] .. "16:16:0:0|t",
- ICON_DIAMOND = ICON_LIST[3] .. "16:16:0:0|t",
- ICON_TRIANGLE = ICON_LIST[4] .. "16:16:0:0|t",
- ICON_MOON = ICON_LIST[5] .. "16:16:0:0|t",
- ICON_SQUARE = ICON_LIST[6] .. "16:16:0:0|t",
- ICON_CROSS = ICON_LIST[7] .. "16:16:0:0|t",
- ICON_SKULL = ICON_LIST[8] .. "16:16:0:0|t",
- BLANK = "None",
- }
- vars.Categories = { }
- local maxExpansion
- for i = 0,10 do
- local ename = _G["EXPANSION_NAME"..i]
- if ename then
- maxExpansion = i
- vars.Categories["D"..i] = ename .. ": " .. LFG_TYPE_DUNGEON
- vars.Categories["R"..i] = ename .. ": " .. LFG_TYPE_RAID
- else
- break
- end
- end
- local tooltip, indicatortip
- local thisToon = UnitName("player") .. " - " .. GetRealmName()
- local maxlvl = MAX_PLAYER_LEVEL_TABLE[#MAX_PLAYER_LEVEL_TABLE]
- local scantt = CreateFrame("GameTooltip", "SavedInstancesScanTooltip", UIParent, "GameTooltipTemplate")
- local currency = {
- --395, -- Justice Points
- 1191, -- Valor Points
- --392, -- Honor Points
- --390, -- Conquest Points
- 738, -- Lesser Charm of Good Fortune
- 697, -- Elder Charm of Good Fortune
- 752, -- Mogu Rune of Fate
- 776, -- Warforged Seal
- 777, -- Timeless Coin
- 402, -- Ironpaw Token
- 81, -- Epicurean Award
- 515, -- Darkmoon Prize Ticket
- 241, -- Champion's Seal
- 391, -- Tol Barad Commendation
- 416, -- Mark of the World Tree
- 789, -- Bloody Coin
- 823, -- Apexis Crystal
- 824, -- Garrison Resources
- 1101,-- Oil
- 1155,-- Ancient Mana
- 1220,-- Order Resources
- 994, -- Seal of Tempered Fate
- 1129,-- Seal of Inevitable Fate
- 1166,-- Timewarped Badge
- 1226,-- Nethershards
- }
- addon.currency = currency
- addon.LFRInstances = {
- -- total is boss count, base is boss offset,
- -- parent is instance name to use
- -- altid is for alternate LFRID for higher level toons
- [416] = { total=4, base=1, parent=448, altid=843 }, -- DS1: The Siege of Wyrmrest Temple
- [417] = { total=4, base=5, parent=448, altid=844 }, -- DS2: Fall of Deathwing
- [527] = { total=3, base=1, parent=532, altid=830 }, -- MSV1: Guardians of Mogu'shan
- [528] = { total=3, base=4, parent=532, altid=831 }, -- MSV2: The Vault of Mysteries
- [529] = { total=3, base=1, parent=534, altid=832 }, -- HoF1: The Dread Approach
- [530] = { total=3, base=4, parent=534, altid=833 }, -- HoF2: Nightmare of Shek'zeer
- [526] = { total=4, base=1, parent=536, altid=834 }, -- TeS1: Terrace of Endless Spring
- [610] = { total=3, base=1, parent=634, altid=835 }, -- ToT1: Last Stand of the Zandalari
- [611] = { total=3, base=4, parent=634, altid=836 }, -- ToT2: Forgotten Depths
- [612] = { total=3, base=7, parent=634, altid=837 }, -- ToT3: Halls of Flesh-Shaping
- [613] = { total=3, base=10, parent=634, altid=838 }, -- ToT4: Pinnacle of Storms
- [716] = { total=4, base=1, parent=766, altid=839 }, -- SoO1: Vale of Eternal Sorrows
- [717] = { total=4, base=5, parent=766, altid=840 }, -- SoO2: Gates of Retribution
- [724] = { total=3, base=9, parent=766, altid=841 }, -- SoO3: The Underhold
- [725] = { total=3, base=12, parent=766, altid=842 }, -- SoO4: Downfall
- [849] = { total=3, base=1, parent=897, altid=nil }, -- Highmaul1: Walled City
- [850] = { total=3, base=4, parent=897, altid=nil }, -- Highmaul2: Arcane Sanctum
- [851] = { total=1, base=7, parent=897, altid=nil }, -- Highmaul3: Imperator's Rise
- [847] = { total=3, base=1, parent=900, altid=nil, remap={ 1, 2, 7 } }, -- BRF1: Slagworks
- [846] = { total=3, base=4, parent=900, altid=nil, remap={ 3, 5, 8 } }, -- BRF2: The Black Forge
- [848] = { total=3, base=7, parent=900, altid=nil, remap={ 4, 6, 9 } }, -- BRF3: Iron Assembly
- [823] = { total=1, base=10, parent=900, altid=nil }, -- BRF4: Blackhand's Crucible
- [982] = { total=3, base=1, parent=989, altid=nil }, -- Hellfire1: Hellbreach
- [983] = { total=3, base=4, parent=989, altid=nil }, -- Hellfire2: Halls of Blood
- [984] = { total=3, base=7, parent=989, altid=nil, remap={ 7, 8, 11 } }, -- Hellfire3: Bastion of Shadows
- [985] = { total=3, base=10, parent=989, altid=nil, remap={ 9, 10, 12 } }, -- Hellfire4: Destructor's Rise
- [986] = { total=1, base=13, parent=989, altid=nil }, -- Hellfire5: Black Gate
- [1287] ={ total=3, base=1, parent=1350,altid=nil }, -- EN1: Darkbough
- [1288] ={ total=3, base=4, parent=1350,altid=nil }, -- EN2: Tormented Guardians
- [1289] ={ total=1, base=7, parent=1350,altid=nil }, -- EN3: Rift of Aln
- [1290] ={ total=3, base=1, parent=1353,altid=nil }, -- NH1: Arcing Aqueducts
- [1291] ={ total=3, base=4, parent=1353,altid=nil }, -- NH2: Royal Athenaeum
- [1292] ={ total=3, base=7, parent=1353,altid=nil }, -- NH3: Nightspire
- [1293] ={ total=1, base=10, parent=1353,altid=nil }, -- NH4: Betrayer's Rise
- }
- local tmp = {}
- for id, info in pairs(addon.LFRInstances) do
- tmp[id] = info
- if info.altid then
- tmp[info.altid] = info
- end
- end
- addon.LFRInstances = tmp
- addon.WorldBosses = {
- -- encounter index is embedded in the Hjournal hyperlink
- [691] = { quest=32099, expansion=4, level=90 }, -- Sha of Anger
- [725] = { quest=32098, expansion=4, level=90 }, -- Galleon
- [814] = { quest=32518, expansion=4, level=90 }, -- Nalak
- [826] = { quest=32519, expansion=4, level=90 }, -- Oondasta
- [857] = { quest=33117, expansion=4, level=90, name=L["The Four Celestials"] }, -- Chi-Ji
- --[858] = { quest=nil, expansion=4, level=90 }, -- Yu'lon
- --[859] = { quest=nil, expansion=4, level=90 }, -- Niuzao
- --[860] = { quest=nil, expansion=4, level=90 }, -- Xuen
- [861] = { quest=nil, expansion=4, level=90 }, -- Ordos
- --[[
- [1291] = { quest=37460, expansion=5, level=100 }, -- Drov the Ruiner
- [1211] = { quest=37462, expansion=5, level=100 }, -- Tarlna the Ageless
- --]]
- [1211] = { quest=37462, expansion=5, level=100, -- Drov/Tarlna share a loot and quest atm
- name=select(2,EJ_GetCreatureInfo(1,1291)):match("^[^ ]+").." / "..
- select(2,EJ_GetCreatureInfo(1,1211)):match("^[^ ]+")},
- [1291] = { remove=true }, -- Drov cleanup
- [1262] = { quest=37464, expansion=5, level=100 }, -- Rukhmar
- [1452] = { quest=94015, expansion=5, level=100 }, -- Kazzak
- -- bosses with no EJ entry (eid is a placeholder)
- [9001] = { quest=38276, name=GARRISON_LOCATION_TOOLTIP.." "..BOSS, expansion=5, level=100 },
- }
- local _specialQuests = {
- -- Isle of Thunder
- [32610] = { zid=928, lid=94221 }, -- Shan'ze Ritual Stone looted
- [32611] = { zid=928, lid1=95350 },-- Incantation of X looted
- [32626] = { zid=928, lid=94222 }, -- Key to the Palace of Lei Shen looted
- [32609] = { zid=928, aid=8104, aline="Left5" }, -- Trove of the Thunder King (outdoor chest)
- -- Timeless Isle
- [32962] = { zid=951, aid=8743, daily=true }, -- Zarhym
- [32961] = { zid=951, daily=true }, -- Scary Ghosts and Nice Sprites
- [32956] = { zid=951, aid=8727, acid=2, aline="Right7" }, -- Blackguard's Jetsam
- [32957] = { zid=951, aid=8727, acid=1, aline="Left7" }, -- Sunken Treasure
- [32970] = { zid=951, aid=8727, acid=3, aline="Left8" }, -- Gleaming Treasure Satchel
- [32968] = { zid=951, aid=8726, acid=2, aline="Right7" }, -- Rope-Bound Treasure Chest
- [32969] = { zid=951, aid=8726, acid=1, aline="Left7" }, -- Gleaming Treasure Chest
- [32971] = { zid=951, aid=8726, acid=3, aline="Left8" }, -- Mist-Covered Treasure Chest
- -- Garrison
- [37638] = { zone=GARRISON_LOCATION_TOOLTIP, aid=9162 }, -- Bronze Defender
- [37639] = { zone=GARRISON_LOCATION_TOOLTIP, aid=9164 }, -- Silver Defender
- [37640] = { zone=GARRISON_LOCATION_TOOLTIP, aid=9165 }, -- Golden Defender
- [38482] = { zone=GARRISON_LOCATION_TOOLTIP, aid=9826 }, -- Platinum Defender
- -- Tanaan Jungle
- [39287] = { zid=945, daily=true }, -- Deathtalon
- [39288] = { zid=945, daily=true }, -- Terrorfist
- [39289] = { zid=945, daily=true }, -- Doomroller
- [39290] = { zid=945, daily=true }, -- Vengeance
- }
- function addon:specialQuests()
- for qid, qinfo in pairs(_specialQuests) do
- qinfo.quest = qid
- if not qinfo.name and (qinfo.lid or qinfo.lid1) then
- local itemname, itemlink = GetItemInfo(qinfo.lid or qinfo.lid1)
- if itemlink and qinfo.lid then
- qinfo.name = itemlink.." ("..LOOT..")"
- elseif itemname and qinfo.lid1 then
- local name = itemname:match("^[^%s]+")
- if name and #name > 0 then
- qinfo.name = name.." ("..LOOT..")"
- end
- end
- elseif not qinfo.name and qinfo.aid and qinfo.acid then
- local l = GetAchievementCriteriaInfo(qinfo.aid, qinfo.acid)
- if l then
- qinfo.name = l:gsub("%p$","")
- end
- elseif not qinfo.name and qinfo.aid then
- scantt:SetOwner(UIParent,"ANCHOR_NONE")
- scantt:SetAchievementByID(qinfo.aid)
- local l = _G[scantt:GetName().."Text"..(qinfo.aline or "Left1")]
- l = l and l:GetText()
- if l then
- qinfo.name = l:gsub("%p$","")
- end
- elseif not qinfo.name then
- local title, link = addon:QuestInfo(qid)
- if title then
- title = title:gsub("%p?%s*[Tt]racking%s*[Qq]uest","")
- title = strtrim(title)
- qinfo.name = title
- end
- end
- if not qinfo.zone and qinfo.zid then
- qinfo.zone = GetMapNameByID(qinfo.zid)
- end
- end
- return _specialQuests
- end
- local QuestExceptions = {
- -- some quests are misidentified in scope
- [7905] = "Regular", -- Darkmoon Faire referral -- old addon versions misidentified this as monthly
- [7926] = "Regular", -- Darkmoon Faire referral
- [37819] = "Regular", -- Darkmoon Faire races referral
- [31752] = "AccountDaily", -- Blingtron
- [34774] = "AccountDaily", -- Blingtron 5000
- -- also pre-populate a few important quests
- [32640] = "Weekly", -- Champions of the Thunder King
- [32641] = "Weekly", -- Champions of the Thunder King
- [32718] = "Regular", -- Mogu Runes of Fate -- ticket 142: outdated quest flag still shows up
- [32719] = "Regular", -- Mogu Runes of Fate
- [33133] = "Regular", -- Warforged Seals outdated quests, no longer weekly
- [33134] = "Regular", -- Warforged Seals
- [33338] = "Weekly", -- Empowering the Hourglass
- [33334] = "Weekly", -- Strong Enough to Survive
- }
- local WoDSealQuests = {
- [36058] = "Weekly", -- Seal of Dwarven Bunker
- -- Seal of Ashran quests
- [36054] = "Weekly",
- [37454] = "Weekly",
- [37455] = "Weekly",
- [36056] = "Weekly",
- [37456] = "Weekly",
- [37457] = "Weekly",
- [36057] = "Weekly",
- [37458] = "Weekly",
- [37459] = "Weekly",
- [36055] = "Weekly",
- [37452] = "Weekly",
- [37453] = "Weekly",
- }
- for k,v in pairs(WoDSealQuests) do
- QuestExceptions[k] = v
- end
- function addon:QuestInfo(questid)
- if not questid or questid == 0 then return nil end
- scantt:SetOwner(UIParent,"ANCHOR_NONE")
- scantt:SetHyperlink("\124cffffff00\124Hquest:"..questid..":90\124h[]\124h\124r")
- local l = _G[scantt:GetName().."TextLeft1"]
- l = l and l:GetText()
- if not l or #l == 0 then return nil end -- cache miss
- return l, "\124cffffff00\124Hquest:"..questid..":90\124h["..l.."]\124h\124r"
- end
- local function chatMsg(...)
- DEFAULT_CHAT_FRAME:AddMessage("\124cFFFF0000"..addonName.."\124r: "..string.format(...))
- end
- addon.chatMsg = chatMsg
- local function debug(...)
- --addon.db.dbg = true
- if addon.db.dbg then
- chatMsg(...)
- end
- end
- addon.debug = debug
- local function bugReport(msg)
- addon.bugreport = addon.bugreport or {}
- local now = GetTime()
- if now < (addon.bugreport[msg] or 0)+60 then return end
- addon.bugreport[msg] = now
- chatMsg(msg)
- if now < (addon.bugreport["url"] or 0)+5 then return end
- chatMsg("Please report this bug at: http://www.wowace.com/addons/saved_instances/tickets/")
- addon.bugreport["url"] = now
- end
- local GTToffset = time() - GetTime()
- local function GetTimeToTime(val)
- if not val then return nil end
- return val + GTToffset
- end
- function addon:timedebug()
- chatMsg("Version: %s (%s)", addon.version, addon.revision)
- chatMsg("Realm: %s (%s)", GetRealmName(), addon:GetRegion())
- chatMsg("Zone: %s (%s)", GetRealZoneText(), addon:GetCurrentMapAreaID())
- chatMsg("time()=%s GetTime()=%s", time(), GetTime())
- chatMsg("Local time: %s local", date("%A %c"))
- chatMsg("GetGameTime: %s:%s server",GetGameTime())
- chatMsg("CalendarGetDate: %s %s/%s/%s server",CalendarGetDate())
- chatMsg("GetQuestResetTime: %s",SecondsToTime(GetQuestResetTime()))
- chatMsg(date("Daily reset: %a %c local (based on GetQuestResetTime)",time()+GetQuestResetTime()))
- chatMsg("Local to Server offset: %d hours",SavedInstances:GetServerOffset())
- local t = SavedInstances:GetNextDailyResetTime()
- chatMsg("Next daily reset: %s local, %s server",date("%a %c",t), date("%a %c",t+3600*SavedInstances:GetServerOffset()))
- local t = SavedInstances:GetNextWeeklyResetTime()
- chatMsg("Next weekly reset: %s local, %s server",date("%a %c",t), date("%a %c",t+3600*SavedInstances:GetServerOffset()))
- local t = SavedInstances:GetNextDailySkillResetTime()
- chatMsg("Next skill reset: %s local, %s server",date("%a %c",t), date("%a %c",t+3600*SavedInstances:GetServerOffset()))
- local t = SavedInstances:GetNextDarkmoonResetTime()
- chatMsg("Next darkmoon reset: %s local, %s server",date("%a %c",t), date("%a %c",t+3600*SavedInstances:GetServerOffset()))
- end
- -- abbreviate expansion names (which apparently are not localized in any western character set)
- local function abbreviate(iname)
- iname = iname:gsub("Burning Crusade", "BC")
- iname = iname:gsub("Wrath of the Lich King", "WotLK")
- iname = iname:gsub("Cataclysm", "Cata")
- iname = iname:gsub("Mists of Pandaria", "MoP")
- iname = iname:gsub("Warlords of Draenor", "WoD")
- return iname
- end
- function addon:formatNumber(num,ismoney)
- num = tonumber(num)
- if not num then return "" end
- local post = ""
- if ismoney then
- if num < 1000*10000 then -- less than 1k, show it all
- return GetMoneyString(num)
- end
- num = math.floor(num / 10000)
- post = " \124TInterface\\MoneyFrame\\UI-GoldIcon:0:0:2:0\124t"
- end
- if vars.db.Tooltip.NumberFormat then
- local str = ""
- local neg = num < 0
- num = math.abs(num)
- local int = math.floor(num)
- local dec = num - int
- local t = tostring(int)
- if #t > 4 then -- leave 4 digit numbers
- while #t > 3 do
- str = LARGE_NUMBER_SEPERATOR .. t:sub(-3) .. str
- t = t:sub(1,-4)
- end
- end
- str = t..str
- if dec > 0 then
- str = str..string.format("%15g",dec):match("(%..*)$")
- end
- if neg then
- str = "-"..str
- end
- return str..post
- else
- return num..post
- end
- end
- vars.defaultDB = {
- DBVersion = 12,
- History = { }, -- for tracking 5 instance per hour limit
- -- key: instance string; value: time first entered
- Toons = { }, -- table key: "Toon - Realm"; value:
- -- Class: string
- -- Level: integer
- -- AlwaysShow: boolean REMOVED
- -- Show: string "always", "never", "saved"
- -- Daily1: expiry (normal) REMOVED
- -- Daily2: expiry (heroic) REMOVED
- -- LFG1: expiry (random dungeon)
- -- LFG2: expiry (deserter)
- -- WeeklyResetTime: expiry
- -- DailyResetTime: expiry
- -- DailyCount: integer REMOVED
- -- PlayedLevel: integer
- -- PlayedTotal: integer
- -- Money: integer
- -- Zone: string
- -- currency: key: currencyID value:
- -- amount: integer
- -- earnedThisWeek: integer
- -- weeklyMax: integer
- -- totalMax: integer
- -- season: integer
- -- Quests: key: QuestID value:
- -- Title: string
- -- Link: hyperlink
- -- Zone: string
- -- isDaily: boolean
- -- Expires: expiration (non-daily)
- -- Skills: key: SpellID or CDID value:
- -- Title: string
- -- Link: hyperlink
- -- Expires: expiration
- -- FarmPlanted: integer
- -- FarmHarvested: integer
- -- FarmCropPlanted: key: spellID value: count
- -- FarmCropReady: key: spellID value: count
- -- FarmExpires: expiration
- -- BonusRoll: key: int value:
- -- name: string
- -- time: int
- -- currencyID: int
- -- money: integer or nil
- -- item: linkstring or nil
- Indicators = {
- D1Indicator = "BLANK", -- indicator: ICON_*, BLANK
- D1Text = "KILLED/TOTAL",
- D1Color = { 0, 0.6, 0 }, -- dark green
- D1ClassColor = true,
- D2Indicator = "BLANK",
- D2Text = "KILLED/TOTALH",
- D2Color = { 0, 1, 0 }, -- green
- D2ClassColor = true,
- D3Indicator = "BLANK",
- D3Text = "KILLED/TOTALM",
- D3Color = { 1, 0, 0 }, -- red
- D3ClassColor = true,
- R0Indicator = "BLANK",
- R0Text = "KILLED/TOTAL",
- R0Color = { 0.6, 0.6, 0 }, -- dark yellow
- R0ClassColor = true,
- R1Indicator = "BLANK",
- R1Text = "KILLED/TOTAL",
- R1Color = { 0.6, 0.6, 0 }, -- dark yellow
- R1ClassColor = true,
- R2Indicator = "BLANK",
- R2Text = "KILLED/TOTAL",
- R2Color = { 0.6, 0, 0 }, -- dark red
- R2ClassColor = true,
- R3Indicator = "BLANK",
- R3Text = "KILLED/TOTALH",
- R3Color = { 1, 1, 0 }, -- yellow
- R3ClassColor = true,
- R4Indicator = "BLANK",
- R4Text = "KILLED/TOTALH",
- R4Color = { 1, 0, 0 }, -- red
- R4ClassColor = true,
- R5Indicator = "BLANK",
- R5Text = "KILLED/TOTAL",
- R5Color = { 0, 0, 1 }, -- blue
- R5ClassColor = true,
- R6Indicator = "BLANK",
- R6Text = "KILLED/TOTAL",
- R6Color = { 0, 1, 0 }, -- green
- R6ClassColor = true,
- R7Indicator = "BLANK",
- R7Text = "KILLED/TOTALH",
- R7Color = { 1, 1, 0 }, -- yellow
- R7ClassColor = true,
- R8Indicator = "BLANK",
- R8Text = "KILLED/TOTALM",
- R8Color = { 1, 0, 0 }, -- red
- R8ClassColor = true,
- },
- Tooltip = {
- ReverseInstances = false,
- ShowExpired = false,
- ShowHoliday = true,
- ShowRandom = true,
- CombineWorldBosses = false,
- CombineLFR = true,
- TrackDailyQuests = true,
- TrackWeeklyQuests = true,
- ShowCategories = false,
- CategorySpaces = false,
- RowHighlight = 0.1,
- Scale = 1,
- FitToScreen = true,
- NewFirst = true,
- RaidsFirst = true,
- NumberFormat = true,
- CategorySort = "EXPANSION", -- "EXPANSION", "TYPE"
- ShowSoloCategory = false,
- ShowHints = true,
- ReportResets = true,
- LimitWarn = true,
- HistoryText = false,
- ShowServer = false,
- ServerSort = true,
- ServerOnly = false,
- ConnectedRealms = "group",
- SelfFirst = true,
- SelfAlways = false,
- TrackLFG = true,
- TrackDeserter = true,
- TrackSkills = true,
- TrackFarm = true,
- TrackBonus = false,
- TrackPlayed = true,
- AugmentBonus = true,
- CurrencyValueColor = true,
- Currency776 = false, -- Warforged Seals
- Currency738 = false, -- Lesser Charm of Good Fortune
- Currency823 = true, -- Apexis Crystal
- Currency824 = true, -- Garrison Resources
- Currency1101= true, -- Oil
- Currency994 = false, -- Seal of Tempered Fate
- Currency1129= false, -- Seal of Inevitable Fate
- Currency1155= true, -- Ancient Mana
- Currency1166= true, -- Timewarped Badge
- Currency1191= true, -- Valor Points
- Currency1220= true, -- Order Resources
- Currency1226= true, -- Nethershards
- CurrencyMax = false,
- CurrencyEarned = true,
- },
- Instances = { }, -- table key: "Instance name"; value:
- -- Show: boolean
- -- Raid: boolean
- -- Holiday: boolean
- -- Random: boolean
- -- Expansion: integer
- -- RecLevel: integer
- -- LFDID: integer
- -- LFDupdated: integer REMOVED
- -- REMOVED Encounters[integer] = { GUID : integer, Name : string }
- -- table key: "Toon - Realm"; value:
- -- table key: "Difficulty"; value:
- -- ID: integer, positive for a Blizzard Raid ID,
- -- negative value for an LFR encounter count
- -- Expires: integer
- -- Locked: boolean, whether toon is locked to the save
- -- Extended: boolean, whether this is an extended raid lockout
- -- Link: string hyperlink to the save
- -- 1..numEncounters: boolean LFR isLooted
- MinimapIcon = { hide = false },
- Quests = {}, -- Account-wide Quests: key: QuestID value: same as toon Quest database
- QuestDB = { -- permanent repeatable quest DB: key: questid value: mapid
- Daily = {},
- Weekly = {},
- Darkmoon = {},
- AccountDaily = {},
- AccountWeekly = {},
- },
- RealmMap = {},
- }
- -- skinning support
- -- skinning addons should hook this function, eg:
- -- hooksecurefunc(SavedInstances,"SkinFrame",function(self,frame,name) frame:SetWhatever() end)
- function addon:SkinFrame(frame,name)
- -- default behavior (ticket 81)
- if IsAddOnLoaded("ElvUI") or IsAddOnLoaded("Tukui") then
- if frame.StripTextures then
- frame:StripTextures()
- end
- if frame.SetTemplate then
- frame:SetTemplate("Transparent")
- end
- --local close = _G[name.."CloseButton"]
- -- easy BugFix by q3fuba
- local close = frame.CloseButton
- if close and close.SetAlpha then
- if ElvUI then
- ElvUI[1]:GetModule('Skins'):HandleCloseButton(close)
- end
- if Tukui and Tukui[1] and Tukui[1].SkinCloseButton then
- Tukui[1].SkinCloseButton(close)
- end
- close:SetAlpha(1)
- end
- end
- end
- -- general helper functions below
- local function ColorCodeOpenRGB(r,g,b,a)
- return format("|c%02x%02x%02x%02x", math.floor(a * 255), math.floor(r * 255), math.floor(g * 255), math.floor(b * 255))
- end
- local function ColorCodeOpen(color)
- return ColorCodeOpenRGB(color[1] or color.r,
- color[2] or color.g,
- color[3] or color.b,
- color[4] or color.a or 1)
- end
- local function ClassColorise(class, targetstring)
- local c = (CUSTOM_CLASS_COLORS and CUSTOM_CLASS_COLORS[class]) or RAID_CLASS_COLORS[class]
- if c.colorStr then
- c = "|c"..c.colorStr
- else
- c = ColorCodeOpen( c )
- end
- return c .. targetstring .. FONTEND
- end
- function addon.ColoredToon(toon, fullname)
- local str = (fullname and toon) or strsplit(' ',toon)
- local t = vars.db.Toons[toon]
- local class = t and t.Class
- if class then
- return ClassColorise(class, str)
- else
- return str
- end
- end
- local function CurrencyColor(amt, max)
- amt = amt or 0
- local samt = addon:formatNumber(amt)
- if max == nil or max == 0 then
- return samt
- end
- if vars.db.Tooltip.CurrencyValueColor then
- local pct = amt / max
- local color = GREENFONT
- if pct == 1 then
- color = REDFONT
- elseif pct > 0.75 then
- color = GOLDFONT
- end
- samt = color .. samt .. FONTEND
- end
- return samt
- end
- local function TableLen(table)
- local i = 0
- for _, _ in pairs(table) do
- i = i + 1
- end
- return i
- end
- -- returns how many hours the server time is ahead of local time
- -- convert local time -> server time: add this value
- -- convert server time -> local time: subtract this value
- function addon:GetServerOffset()
- local serverDay = CalendarGetDate() - 1 -- 1-based starts on Sun
- local localDay = tonumber(date("%w")) -- 0-based starts on Sun
- local serverHour, serverMinute = GetGameTime()
- local localHour, localMinute = tonumber(date("%H")), tonumber(date("%M"))
- if serverDay == (localDay + 1)%7 then -- server is a day ahead
- serverHour = serverHour + 24
- elseif localDay == (serverDay + 1)%7 then -- local is a day ahead
- localHour = localHour + 24
- end
- local server = serverHour + serverMinute / 60
- local localT = localHour + localMinute / 60
- local offset = floor((server - localT) * 2 + 0.5) / 2
- return offset
- end
- function addon:GetRegion()
- if not addon.region then
- local reg
- reg = GetCVar("portal")
- if reg == "public-test" then -- PTR uses US region resets, despite the misleading realm name suffix
- reg = "US"
- end
- if not reg or #reg ~= 2 then
- local gcr = GetCurrentRegion()
- reg = gcr and ({ "US", "KR", "EU", "TW", "CN" })[gcr]
- end
- if not reg or #reg ~= 2 then
- reg = (GetCVar("realmList") or ""):match("^(%a+)%.")
- end
- if not reg or #reg ~= 2 then -- other test realms?
- reg = (GetRealmName() or ""):match("%((%a%a)%)")
- end
- reg = reg and reg:upper()
- if reg and #reg == 2 then
- addon.region = reg
- end
- end
- return addon.region
- end
- function addon:GetNextDailyResetTime()
- local resettime = GetQuestResetTime()
- if not resettime or resettime <= 0 or -- ticket 43: can fail during startup
- -- also right after a daylight savings rollover, when it returns negative values >.<
- resettime > 24*3600+30 then -- can also be wrong near reset in an instance
- return nil
- end
- if false then -- this should no longer be a problem after the 7.0 reset time changes
- -- ticket 177/191: GetQuestResetTime() is wrong for Oceanic+Brazilian characters in PST instances
- local serverHour, serverMinute = GetGameTime()
- local serverResetTime = (serverHour*3600 + serverMinute*60 + resettime) % 86400 -- GetGameTime of the reported reset
- local diff = serverResetTime - 10800 -- how far from 3AM server
- if math.abs(diff) > 3.5*3600 -- more than 3.5 hours - ignore TZ differences of US continental servers
- and addon:GetRegion() == "US" then
- local diffhours = math.floor((diff + 1800)/3600)
- resettime = resettime - diffhours*3600
- if resettime < -900 then -- reset already passed, next reset
- resettime = resettime + 86400
- elseif resettime > 86400+900 then
- resettime = resettime - 86400
- end
- debug("Adjusting GetQuestResetTime() discrepancy of %d seconds (%d hours). Reset in %d seconds", diff, diffhours, resettime)
- end
- end
- return time() + resettime
- end
- do
- local midnight = {hour=23, min=59, sec=59}
- function addon:GetNextDailySkillResetTime() -- trade skill reset time
- -- this is just a "best guess" because in reality,
- -- different trade skills reset at up to 3 different times
- if false then -- at next server midnight
- midnight.month, midnight.day, midnight.year = select(2,CalendarGetDate()) -- date in server timezone
- local ret = time(midnight)
- local offset = addon:GetServerOffset() * 3600
- ret = ret - offset
- return ret
- else -- at next daily quest reset time
- local rt = addon:GetNextDailyResetTime()
- if not rt then return nil end
- --local info = date("*t"); print(info.isdst)
- -- Blizzard's ridiculous reset crap:
- -- trade skills ignore daylight savings after the date it changes UNTIL the next restart, then go back to observing it
- if false then
- rt = rt + 3600
- end
- if false then
- rt = rt - 3600
- if time() > rt then -- past trade reset but before daily reset, next day
- rt = rt + 24*3600
- end
- end
- return rt
- end
- end
- end
- function addon:GetNextWeeklyResetTime()
- if not addon.resetDays then
- local region = addon:GetRegion()
- if not region then return nil end
- addon.resetDays = {}
- addon.resetDays.DLHoffset = 0
- if region == "US" then
- addon.resetDays["2"] = true -- tuesday
- -- ensure oceanic servers over the dateline still reset on tues UTC (wed 1/2 AM server)
- addon.resetDays.DLHoffset = -3
- elseif region == "EU" then
- addon.resetDays["3"] = true -- wednesday
- elseif region == "CN" or region == "KR" or region == "TW" then -- XXX: codes unconfirmed
- addon.resetDays["4"] = true -- thursday
- else
- addon.resetDays["2"] = true -- tuesday?
- end
- end
- local offset = (addon:GetServerOffset() + addon.resetDays.DLHoffset) * 3600
- local nightlyReset = addon:GetNextDailyResetTime()
- if not nightlyReset then return nil end
- --while date("%A",nightlyReset+offset) ~= WEEKDAY_TUESDAY do
- while not addon.resetDays[date("%w",nightlyReset+offset)] do
- nightlyReset = nightlyReset + 24 * 3600
- end
- return nightlyReset
- end
- do
- local dmf_end = {hour=23, min=59}
- function addon:GetNextDarkmoonResetTime()
- -- Darkmoon faire runs from first Sunday of each month to following Saturday
- -- this function returns an approximate time after the end of the current month's faire
- local weekday, month, day, year = CalendarGetDate() -- date in server timezone (Sun==1)
- local firstweekday = select(4,CalendarGetAbsMonth(month, year)) -- (Sun == 1)
- local firstsunday = ((firstweekday == 1) and 1) or (9 - firstweekday)
- dmf_end.year = year
- dmf_end.month = month
- dmf_end.day = firstsunday + 7 -- 1 days of "slop"
- -- Unfortunately, DMF boundary ignores daylight savings, and the time of day varies across regions
- -- Report a reset well past end to make sure we don't drop quests early
- local ret = time(dmf_end)
- local offset = addon:GetServerOffset() * 3600
- ret = ret - offset
- return ret
- end
- end
- function addon:QuestCount(toonname)
- local t
- if toonname then
- t = vars and vars.db.Toons and vars.db.Toons[toonname]
- else -- account-wide quests
- t = db
- end
- if not t then return 0,0 end
- local dailycount, weeklycount = 0,0
- -- ticket 96: GetDailyQuestsCompleted() is unreliable, the response is laggy and it fails to count some quests
- for _,info in pairs(t.Quests) do
- if info.isDaily then
- dailycount = dailycount + 1
- else
- weeklycount = weeklycount + 1
- end
- end
- return dailycount, weeklycount
- end
- -- local addon functions below
- local function GetLastLockedInstance()
- local numsaved = GetNumSavedInstances()
- if numsaved > 0 then
- for i = 1, numsaved do
- local name, id, expires, diff, locked, extended, mostsig, raid, players, diffname = GetSavedInstanceInfo(i)
- if locked then
- return name, id, expires, diff, locked, extended, mostsig, raid, players, diffname
- end
- end
- end
- end
- function addon:normalizeName(str)
- return str:gsub("%p",""):gsub("%s"," "):gsub("%s%s"," "):gsub("^%s+",""):gsub("%s+$",""):upper()
- end
- addon.transInstance = {
- -- lockout hyperlink id = LFDID
- [543] = 188, -- Hellfire Citadel: Ramparts
- [540] = 189, -- Hellfire Citadel: Shattered Halls : deDE
- [542] = 187, -- Hellfire Citadel: Blood Furnace esES
- [534] = 195, -- The Battle for Mount Hyjal
- [509] = 160, -- Ruins of Ahn'Qiraj
- [557] = 179, -- Auchindoun: Mana-Tombs : ticket 72 zhTW
- [556] = 180, -- Auchindoun: Sethekk Halls : ticket 151 frFR
- [568] = 340, -- Zul'Aman: frFR
- [1004] = 474, -- Scarlet Monastary: deDE
- [600] = 215, -- Drak'Tharon: ticket 105 deDE
- [560] = 183, -- Escape from Durnholde Keep: ticket 124 deDE
- [531] = 161, -- AQ temple: ticket 137 frFR
- [1228] = 897, -- Highmaul: ticket 175 ruRU
- [552] = 1011, -- Arcatraz: ticket 216 frFR
- }
- -- some instances (like sethekk halls) are named differently by GetSavedInstanceInfo() and LFGGetDungeonInfoByID()
- -- we use the latter name to key our database, and this function to convert as needed
- function addon:FindInstance(name, raid)
- if not name or #name == 0 then return nil end
- local nname = addon:normalizeName(name)
- -- first pass, direct match
- local info = vars.db.Instances[name]
- if info then
- return name, info.LFDID
- end
- -- hyperlink id lookup: must precede substring match for ticket 99
- -- (so transInstance can override incorrect substring matches)
- for i = 1, GetNumSavedInstances() do
- local link = GetSavedInstanceChatLink(i) or ""
- local lid,lname = link:match(":(%d+):%d+:%d+\124h%[(.+)%]\124h")
- lname = lname and addon:normalizeName(lname)
- lid = lid and tonumber(lid)
- local lfdid = lid and addon.transInstance[lid]
- if lname == nname and lfdid then
- local truename = addon:UpdateInstance(lfdid)
- if truename then
- return truename, lfdid
- end
- end
- end
- -- normalized substring match
- for truename, info in pairs(vars.db.Instances) do
- local tname = addon:normalizeName(truename)
- if (tname:find(nname, 1, true) or nname:find(tname, 1, true)) and
- info.Raid == raid then -- Tempest Keep: The Botanica
- --debug("FindInstance("..name..") => "..truename)
- return truename, info.LFDID
- end
- end
- return nil
- end
- -- provide either id or name/raid to get the instance truename and db entry
- function addon:LookupInstance(id, name, raid)
- --debug("LookupInstance("..(id or "nil")..","..(name or "nil")..","..(raid and "true" or "false")..")")
- local truename, instance
- if name then
- truename, id = addon:FindInstance(name, raid)
- end
- if id then
- truename = addon:UpdateInstance(id)
- end
- if truename then
- instance = vars.db.Instances[truename]
- end
- if not instance then
- debug("LookupInstance() failed to find instance: "..(name or "")..":"..(id or 0).." : "..GetLocale())
- addon.warned = addon.warned or {}
- if not addon.warned[name] then
- addon.warned[name] = true
- local lid
- for i = 1, GetNumSavedInstances() do
- local link = GetSavedInstanceChatLink(i) or ""
- local tlid,tlname = link:match(":(%d+):%d+:%d+\124h%[(.+)%]\124h")
- if tlname == name then lid = tlid end
- end
- bugReport("SavedInstances: ERROR: Refresh() failed to find instance: "..name.." : "..GetLocale().." : "..(lid or "x"))
- end
- instance = {}
- --vars.db.Instances[name] = instance
- end
- return truename, instance
- end
- function addon:InstanceCategory(instance)
- if not instance then return nil end
- local instance = vars.db.Instances[instance]
- if instance.Holiday then return "H" end
- if instance.Random then return "N" end
- return ((instance.Raid and "R") or ((not instance.Raid) and "D")) .. instance.Expansion
- end
- function addon:InstancesInCategory(targetcategory)
- -- returns a table of the form { "instance1", "instance2", ... }
- if (not targetcategory) then return { } end
- local list = { }
- for instance, _ in pairs(vars.db.Instances) do
- if addon:InstanceCategory(instance) == targetcategory then
- table.insert(list, instance)
- end
- end
- return list
- end
- function addon:CategorySize(category)
- if not category then return nil end
- local i = 0
- for instance, _ in pairs(vars.db.Instances) do
- if category == addon:InstanceCategory(instance) then
- i = i + 1
- end
- end
- return i
- end
- local _instance_exceptions = {
- -- workaround a Blizzard bug:
- -- since 5.0, some old raid lockout tooltips are missing boss kill info
- -- currently affects 25+ man BC/Vanilla raids (but not Kara or AQ Ruins, go figure)
- -- starting in 6.1 we have the kill bitmap but no boss names
- [48] = { -- Molten Core
- 12118, -- Lucifron
- 11982, -- Magmadar
- 12259, -- Gehennas
- 12057, -- Garr
- 12264, -- Shazzrah
- 12056, -- Baron Geddon
- 12098, -- Sulfuron Harbinger
- 11988, -- Golemagg the Incinerator
- 12018, -- Majordomo Executus
- 11502, -- Ragnaros
- },
- [50] = { -- Blackwing Lair
- 12435, -- Razorgore the Untamed
- 13020, -- Vaelastrasz the Corrupt
- 12017, -- Boodlord Lashlayer
- 11983, -- Firemaw
- 14601, -- Ebonroc
- 11981, -- Flamegor
- 14020, -- Chromaggus
- 11583, -- Nefarian
- },
- [161] = { -- Ahn'Qiraj Temple
- 15263, -- Prophet Skeram
- 15543, -- Princess Yauj (also Vem and Lord Kri)
- 15516, -- Bodyguard Sartura
- 15510, -- Fankriss the Unyielding
- 15299, -- Viscidus
- 15509, -- Princess Huhuran
- 15276, -- Emperor Vek'lor
- 15517, -- Ouro
- 15727, -- C'Thun
- },
- [176] = { -- Magtheridon's Lair
- 17257, -- Magtheridon
- },
- [177] = { -- Gruul's Lair
- 18831, -- High King Maulgar
- 19044, -- Gruul
- },
- [193] = { -- Tempest Keep
- 19514, -- A'lar
- 19516, -- Void Reaver
- 18805, -- High Astromancer Solarian
- 19622, -- Kael'thas Sunstrider
- },
- [194] = { -- Serpentshrine Cavern
- 21216, -- Hydross the Unstable
- 21217, -- The Lurker Below
- 21215, -- Leotheras the Blind
- 21214, -- Fathom-Lord Karathress
- 21213, -- Morogrim Tidewalker
- 21212, -- Lady Vashj
- },
- [195] = { -- Hyjal Past
- 17767, -- Rage Winterchill
- 17808, -- Anetheron
- 17888, -- Kaz'rogal
- 17842, -- Azgalor
- 17968, -- Archimonde
- },
- [196] = { -- Black Temple
- 22887, -- High Warlord Naj'entus
- 22898, -- Supremus
- 22841, -- Shade of Akama
- 22871, -- Teron Gorefiend
- 22948, -- Gurtogg Bloodboil
- 22856, -- Reliquary of Souls
- 22947, -- Mother Shahraz
- 23426, -- Illidari Council
- 22917, -- Illidan Stormrage
- },
- [199] = { -- Sunwell
- 24850, -- Kalecgos
- 24882, -- Brutallus
- 25038, -- Felmyst
- 25166, -- Grand Warlock Alythess
- 25741, -- M'uru
- 25315, -- Kil'jaeden
- },
- }
- function addon:instanceException(LFDID)
- if not LFDID then return nil end
- local exc = _instance_exceptions[LFDID]
- if exc then -- localize boss names
- local total = 0
- for idx, id in ipairs(exc) do
- if type(id) == "number" then
- scantt:SetOwner(UIParent,"ANCHOR_NONE")
- scantt:SetHyperlink(("unit:Creature-0-0-0-0-%d:0000000000"):format(id))
- local line = scantt:IsShown() and _G[scantt:GetName().."TextLeft1"]
- line = line and line:GetText()
- if line and #line > 0 then
- exc[idx] = line
- end
- end
- total = total + 1
- end
- exc.total = total
- end
- return exc
- end
- function addon:instanceBosses(instance,toon,diff)
- local killed,total,base = 0,0,1
- local remap = nil
- local inst = vars.db.Instances[instance]
- local save = inst and inst[toon] and inst[toon][diff]
- if inst.WorldBoss then
- return (save[1] and 1 or 0), 1, 1
- end
- if not inst or not inst.LFDID then return 0,0,1 end
- local exc = addon:instanceException(inst.LFDID)
- total = (exc and exc.total) or GetLFGDungeonNumEncounters(inst.LFDID)
- local LFR = addon.LFRInstances[inst.LFDID]
- if LFR then
- total = LFR.total or total
- base = LFR.base or base
- remap = LFR.remap
- end
- if not save then
- return killed, total, base, remap
- elseif save.Link then
- local bits = save.Link:match(":(%d+)\124h")
- bits = bits and tonumber(bits)
- if bits then
- while bits > 0 do
- if bit.band(bits,1) > 0 then
- killed = killed + 1
- end
- bits = bit.rshift(bits,1)
- end
- end
- elseif save.ID < 0 then
- for i=1,-1*save.ID do
- killed = killed + (save[i] and 1 or 0)
- end
- end
- return killed, total, base, remap
- end
- local lfrkey = "^"..L["LFR"]..": "
- local function instanceSort(i1, i2)
- local instance1 = vars.db.Instances[i1]
- local instance2 = vars.db.Instances[i2]
- local level1 = instance1.RecLevel or 0
- local level2 = instance2.RecLevel or 0
- local id1 = instance1.LFDID or instance1.WorldBoss or 0
- local id2 = instance2.LFDID or instance2.WorldBoss or 0
- local key1 = level1*1000000+id1
- local key2 = level2*1000000+id2
- if i1:match(lfrkey) then key1 = key1 - 20000 end
- if i2:match(lfrkey) then key2 = key2 - 20000 end
- if instance1.WorldBoss then key1 = key1 - 30000 end
- if instance2.WorldBoss then key2 = key2 - 30000 end
- if vars.db.Tooltip.ReverseInstances then
- return key1 < key2
- else
- return key2 < key1
- end
- end
- addon.oi_cache = {}
- function addon:OrderedInstances(category)
- -- returns a table of the form { "instance1", "instance2", ... }
- local instances = addon.oi_cache[category]
- if not instances then
- instances = addon:InstancesInCategory(category)
- table.sort(instances, instanceSort)
- if addon.instancesUpdated then
- addon.oi_cache[category] = instances
- end
- end
- return instances
- end
- function addon:OrderedCategories()
- -- returns a table of the form { "category1", "category2", ... }
- if addon.oc_cache then return addon.oc_cache end
- local orderedlist = { }
- local firstexpansion, lastexpansion, expansionstep, firsttype, lasttype
- if vars.db.Tooltip.NewFirst then
- firstexpansion = maxExpansion
- lastexpansion = 0
- expansionstep = -1
- else
- firstexpansion = 0
- lastexpansion = maxExpansion
- expansionstep = 1
- end
- if vars.db.Tooltip.RaidsFirst then
- firsttype = "R"
- lasttype = "D"
- else
- firsttype = "D"
- lasttype = "R"
- end
- for i = firstexpansion, lastexpansion, expansionstep do
- table.insert(orderedlist, firsttype .. i)
- if vars.db.Tooltip.CategorySort == "EXPANSION" then
- table.insert(orderedlist, lasttype .. i)
- end
- end
- if vars.db.Tooltip.CategorySort == "TYPE" then
- for i = firstexpansion, lastexpansion, expansionstep do
- table.insert(orderedlist, lasttype .. i)
- end
- end
- addon.oc_cache = orderedlist
- return orderedlist
- end
- local function DifficultyString(instance, diff, toon, expired, killoverride, totoverride)
- local setting,color
- if not instance then
- setting = "D1"
- else
- local inst = vars.db.Instances[instance]
- if not inst or not inst.Raid then -- 5-man
- if diff == 2 then -- heroic
- setting = "D2"
- elseif diff == 23 then -- mythic
- setting = "D3"
- else -- normal?
- setting = "D1"
- end
- elseif inst.Expansion == 0 then -- classic raid
- setting = "R0"
- elseif diff >= 3 and diff <= 7 then -- pre-WoD raids
- setting = "R"..(diff-2)
- elseif diff >= 14 and diff <= 16 then -- WoD raids
- setting = "R"..(diff-8)
- else -- don't know
- setting = "D1"
- end
- end
- local prefs = vars.db.Indicators
- local classcolor = prefs[setting .. "ClassColor"]
- if classcolor == nil then
- classcolor = vars.defaultDB.Indicators[setting .. "ClassColor"]
- end
- if expired then
- color = GRAY_COLOR
- elseif classcolor then
- color = (CUSTOM_CLASS_COLORS or RAID_CLASS_COLORS)[vars.db.Toons[toon].Class]
- else
- prefs[setting.."Color"] = prefs[setting.."Color"] or vars.defaultDB.Indicators[setting.."Color"]
- color = prefs[setting.."Color"]
- end
- local text = prefs[setting.."Text"] or vars.defaultDB.Indicators[setting.."Text"]
- local indicator = prefs[setting.."Indicator"] or vars.defaultDB.Indicators[setting.."Indicator"]
- text = ColorCodeOpen(color) .. text .. FONTEND
- if text:find("ICON", 1, true) and indicator ~= "BLANK" then
- text = text:gsub("ICON", FONTEND .. vars.Indicators[indicator] .. ColorCodeOpen(color))
- end
- if text:find("KILLED", 1, true) or text:find("TOTAL", 1, true) then
- local killed, total
- if killoverride then
- killed, total = killoverride, totoverride
- else
- killed, total = addon:instanceBosses(instance,toon,diff)
- end
- if killed == 0 and total == 0 then -- boss kill info missing
- killed = "*"
- total = "*"
- elseif killed == 1 and total == 1 and not expired then
- text = "\124T"..READY_CHECK_READY_TEXTURE..":0|t" -- checkmark
- end
- text = text:gsub("KILLED",killed)
- text = text:gsub("TOTAL",total)
- end
- return text
- end
- -- run about once per session to update our database of instance info
- function addon:UpdateInstanceData()
- --debug("UpdateInstanceData()")
- if addon.instancesUpdated then return end -- nil before first use in UI
- addon.instancesUpdated = true
- local added = 0
- local lfdid_to_name = {}
- local wbid_to_name = {}
- local id_blacklist = {}
- local starttime = debugprofilestop()
- local maxid = 1500
- -- previously we used GetFullRaidList() and LFDDungeonList to help populate the instance list
- -- Unfortunately those are loaded lazily, and forcing them to load from here can lead to taint.
- -- They are also somewhat incomplete, so instead we just brute force it, which is reasonably fast anyhow
- for id=1,maxid do
- local instname, newentry, blacklist = addon:UpdateInstance(id)
- if newentry then
- added = added + 1
- end
- if blacklist then
- id_blacklist[id] = true
- end
- if instname then
- if lfdid_to_name[id] then
- debug("Duplicate entry in lfdid_to_name: "..id..":"..lfdid_to_name[id]..":"..instname)
- end
- lfdid_to_name[id] = instname
- end
- end
- for eid,info in pairs(addon.WorldBosses) do
- info.eid = eid
- if not info.name then
- info.name = select(2,EJ_GetCreatureInfo(1,eid))
- end
- info.name = info.name or "UNKNOWN"..eid
- local instance = vars.db.Instances[info.name]
- if info.remove then -- cleanup hook
- vars.db.Instances[info.name] = nil
- addon.WorldBosses[eid] = nil
- else
- if not instance then
- added = added + 1
- instance = {}
- vars.db.Instances[info.name] = instance
- end
- instance.Show = instance.Show or "saved"
- instance.WorldBoss = eid
- instance.Expansion = info.expansion
- instance.RecLevel = info.level
- instance.Raid = true
- wbid_to_name[eid] = info.name
- end
- end
- -- instance merging: this algorithm removes duplicate entries created by client locale changes using the same database
- -- we really should re-key the database by ID, but this is sufficient for now
- local renames = 0
- local merges = 0
- local conflicts = 0
- for instname, inst in pairs(vars.db.Instances) do
- local truename
- if inst.WorldBoss then
- truename = wbid_to_name[inst.WorldBoss]
- elseif inst.LFDID then
- truename = lfdid_to_name[inst.LFDID]
- else
- debug("Ignoring bogus entry in instance database: "..instname)
- end
- if not truename then
- if inst.LFDID and id_blacklist[inst.LFDID] then
- debug("Removing blacklisted entry in instance database: "..instname)
- vars.db.Instances[instname] = nil
- else
- debug("Ignoring unmatched entry in instance database: "..instname)
- end
- elseif instname == truename then
- -- this is the canonical entry, nothing to do
- else -- this is a stale entry, merge data and remove it
- local trueinst = vars.db.Instances[truename]
- if not trueinst or trueinst == inst then
- debug("Merge error in UpdateInstanceData: "..truename)
- else
- for key, info in pairs(inst) do
- if key:find(" - ") then -- is a character key
- if trueinst[key] then
- -- merge conflict: keep the trueinst data
- debug("Merge conflict on "..truename..":"..instname..":"..key)
- conflicts = conflicts + 1
- else
- trueinst[key] = info
- merges = merges + 1
- end
- end
- end
- -- copy config settings, favoring old entry
- trueinst.Show = inst.Show or trueinst.Show
- -- clear stale entry
- vars.db.Instances[instname] = nil
- renames = renames + 1
- end
- end
- end
- -- addon.lfdid_to_name = lfdid_to_name
- -- addon.wbid_to_name = wbid_to_name
- vars.config:BuildOptions() -- refresh config table
- starttime = debugprofilestop()-starttime
- debug("UpdateInstanceData(): completed in %.3f ms : %d added, %d renames, %d merges, %d conflicts.",
- starttime, added, renames, merges, conflicts)
- if addon.RefreshPending then
- addon.RefreshPending = nil
- core:Refresh()
- end
- end
- --if LFDParentFrame then hooksecurefunc(LFDParentFrame,"Show",function() addon:UpdateInstanceData() end) end
- function addon:UpdateInstance(id)
- -- returns: <instance_name>, <is_new_instance>, <blacklisted_id>
- --debug("UpdateInstance: "..id)
- if not id or id <= 0 then return end
- local name, typeID, subtypeID,
- minLevel, maxLevel, recLevel, minRecLevel, maxRecLevel,
- expansionLevel, groupID, textureFilename,
- difficulty, maxPlayers, description, isHoliday = GetLFGDungeonInfo(id)
- -- name is nil for non-existent ids
- -- isHoliday is for single-boss holiday instances that don't generate raid saves
- -- typeID 4 = outdoor area, typeID 6 = random
- maxPlayers = tonumber(maxPlayers)
- if not name or not expansionLevel or not recLevel or (typeID > 2 and typeID ~= TYPEID_RANDOM_DUNGEON) then return end
- if name:find(PVP_RATED_BATTLEGROUND) then return nil, nil, true end -- ignore 10v10 rated bg
- if subtypeID == LFG_SUBTYPEID_SCENARIO and typeID ~= TYPEID_RANDOM_DUNGEON then -- ignore non-random scenarios
- return nil, nil, true
- end
- if typeID == 2 and subtypeID == 0 and difficulty == 14 and maxPlayers == 0 then
- --print("ignoring "..id, GetLFGDungeonInfo(id))
- return nil, nil, true -- ignore bogus LFR entries
- end
- if typeID == 1 and subtypeID == 5 and difficulty == 14 and maxPlayers == 25 then
- --print("ignoring "..id, GetLFGDungeonInfo(id))
- return nil, nil, true -- ignore old Flex entries
- end
- if addon.LFRInstances[id] then -- ensure uniqueness (eg TeS LFR)
- local lfrid = vars.db.Instances[name] and vars.db.Instances[name].LFDID
- if lfrid and addon.LFRInstances[lfrid] then
- vars.db.Instances[name] = nil -- clean old LFR entries
- end
- vars.db.Instances[L["Flex"]..": "..name] = nil -- clean old flex entries
- name = L["LFR"]..": "..name
- end
- if id == 852 and expansionLevel == 5 then -- XXX: Molten Core hack
- return nil, nil, true -- ignore Molten Core holiday version, which has no save
- end
- if id == 767 then -- ignore bogus Ordos entry
- return nil, nil, true
- end
- if id == 768 then -- ignore bogus Celestials entry
- return nil, nil, true
- end
- local instance = vars.db.Instances[name]
- local newinst = false
- if not instance then
- debug("UpdateInstance: "..id.." "..(name or "nil").." "..(expansionLevel or "nil").." "..(recLevel or "nil").." "..(maxPlayers or "nil"))
- instance = {}
- newinst = true
- end
- vars.db.Instances[name] = instance
- instance.Show = instance.Show or "saved"
- instance.Encounters = nil -- deprecated
- instance.LFDupdated = nil
- instance.LFDID = id
- instance.Holiday = isHoliday or nil
- instance.Expansion = expansionLevel
- if not instance.RecLevel or instance.RecLevel < 1 then instance.RecLevel = recLevel end
- if recLevel > 0 and recLevel < instance.RecLevel then instance.RecLevel = recLevel end -- favor non-heroic RecLevel
- instance.Raid = (maxPlayers > 5 or (maxPlayers == 0 and typeID == 2))
- if typeID == TYPEID_RANDOM_DUNGEON then
- instance.Random = true
- end
- if subtypeID == LFG_SUBTYPEID_SCENARIO then
- instance.Scenario = true
- end
- return name, newinst
- end
- function addon:updateSpellTip(spellid)
- local slot
- vars.db.spelltip = vars.db.spelltip or {}
- vars.db.spelltip[spellid] = vars.db.spelltip[spellid] or {}
- for i=1,20 do
- local id = select(11,UnitDebuff("player",i))
- if id == spellid then slot = i end
- end
- if slot then
- scantt:SetOwner(UIParent,"ANCHOR_NONE")
- scantt:SetUnitDebuff("player",slot)
- for i=1,scantt:NumLines()-1 do
- local left = _G[scantt:GetName().."TextLeft"..i]
- vars.db.spelltip[spellid][i] = left:GetText()
- end
- end
- end
- -- run regularly to update lockouts and cached data for this toon
- function addon:UpdateToonData()
- addon.activeHolidays = addon.activeHolidays or {}
- wipe(addon.activeHolidays)
- -- blizz internally conflates all the holiday flags, so we have to detect which is really active
- for i=1, GetNumRandomDungeons() do
- local id, name = GetLFGRandomDungeonInfo(i);
- local d = vars.db.Instances[name]
- if d and d.Holiday then
- addon.activeHolidays[name] = true
- end
- end
- local nextreset = addon:GetNextDailyResetTime()
- for instance, i in pairs(vars.db.Instances) do
- for toon, t in pairs(vars.db.Toons) do
- if i[toon] then
- for difficulty, d in pairs(i[toon]) do
- if d.Expires and d.Expires < time() then
- d.Locked = false
- d.Expires = 0
- if d.ID < 0 then
- i[toon][difficulty] = nil
- end
- end
- end
- end
- end
- if (i.Holiday and addon.activeHolidays[instance]) or
- (i.Random and not i.Holiday) then
- local id = i.LFDID
- GetLFGDungeonInfo(id) -- forces update
- local donetoday, money = GetLFGDungeonRewards(id)
- if donetoday and i.Random and (
- (i.LFDID == 258) or -- random classic dungeon
- (i.LFDID == 995 or i.LFDID == 744) or -- timewalking dungeons
- (UnitLevel("player") == 85 and
- (i.LFDID == 300 or i.LFDID == 301 or i.LFDID == 434)) -- reg/her cata and HoT at 85
- ) then -- donetoday flag is falsely set for some level/dungeon combos where no daily incentive is available
- donetoday = false
- end
- if nextreset and donetoday and (i.Holiday or (money and money > 0)) then
- i[thisToon] = i[thisToon] or {}
- i[thisToon][1] = i[thisToon][1] or {}
- local d = i[thisToon][1]
- d.ID = -1
- d.Locked = false
- d.Expires = nextreset
- end
- end
- end
- -- update random toon info
- local t = vars.db.Toons[thisToon]
- local now = time()
- if addon.logout or addon.PlayedTime or addon.playedpending then
- if addon.PlayedTime then
- local more = now - addon.PlayedTime
- t.PlayedTotal = t.PlayedTotal + more
- t.PlayedLevel = t.PlayedLevel + more
- addon.PlayedTime = now
- end
- else
- addon.playedpending = true
- addon.playedreg = addon.playedreg or {}
- wipe(addon.playedreg)
- for i=1,10 do
- local c = _G["ChatFrame"..i]
- if c and c:IsEventRegistered("TIME_PLAYED_MSG") then
- c:UnregisterEvent("TIME_PLAYED_MSG") -- prevent spam
- addon.playedreg[c] = true
- end
- end
- RequestTimePlayed()
- end
- t.LFG1 = GetTimeToTime(GetLFGRandomCooldownExpiration()) or t.LFG1
- t.LFG2 = GetTimeToTime(select(7,UnitDebuff("player",GetSpellInfo(71041)))) or t.LFG2 -- GetLFGDeserterExpiration()
- if t.LFG2 then addon:updateSpellTip(71041) end
- addon.pvpdesertids = addon.pvpdesertids or { 26013, -- BG queue
- 194958 } -- Ashran
- for _,id in ipairs(addon.pvpdesertids) do
- t.pvpdesert = GetTimeToTime(select(7,UnitDebuff("player",GetSpellInfo(id)))) or t.pvpdesert
- if t.pvpdesert then addon:updateSpellTip(id) end
- end
- for toon, ti in pairs(vars.db.Toons) do
- if ti.LFG1 and (ti.LFG1 < now) then ti.LFG1 = nil end
- if ti.LFG2 and (ti.LFG2 < now) then ti.LFG2 = nil end
- if ti.pvpdesert and (ti.pvpdesert < now) then ti.pvpdesert = nil end
- ti.Quests = ti.Quests or {}
- end
- local IL,ILe = GetAverageItemLevel()
- if IL and tonumber(IL) and tonumber(IL) > 0 then -- can fail during logout
- t.IL, t.ILe = tonumber(IL), tonumber(ILe)
- end
- local rating = (GetPersonalRatedBGInfo and GetPersonalRatedBGInfo()) -- 5.3
- or (GetPersonalRatedInfo and GetPersonalRatedInfo(4))
- t.RBGrating = tonumber(rating) or t.RBGrating
- core:scan_item_cds()
- -- Daily Reset
- if nextreset and nextreset > time() then
- for toon, ti in pairs(vars.db.Toons) do
- if not ti.DailyResetTime or (ti.DailyResetTime < time()) then
- for id,qi in pairs(ti.Quests) do
- if qi.isDaily then
- ti.Quests[id] = nil
- end
- end
- ti.DailyResetTime = (ti.DailyResetTime and ti.DailyResetTime + 24*3600) or nextreset
- end
- end
- t.DailyResetTime = nextreset
- if not db.DailyResetTime or (db.DailyResetTime < time()) then -- AccountDaily reset
- for id,qi in pairs(db.Quests) do
- if qi.isDaily then
- db.Quests[id] = nil
- end
- end
- db.DailyResetTime = nextreset
- end
- end
- -- Skill Reset
- for toon, ti in pairs(vars.db.Toons) do
- if ti.Skills then
- for spellid, sinfo in pairs(ti.Skills) do
- if sinfo.Expires and sinfo.Expires < time() then
- ti.Skills[spellid] = nil
- end
- end
- end
- if ti.FarmExpires and ti.FarmExpires < time() then
- ti.FarmPlanted = 0
- ti.FarmHarvested = 0
- if ti.FarmCropPlanted and next(ti.FarmCropPlanted) then
- ti.FarmCropReady = ti.FarmCropPlanted
- ti.FarmCropPlanted = nil
- end
- ti.FarmExpires = nil
- end
- end
- -- Weekly Reset
- local nextreset = addon:GetNextWeeklyResetTime()
- if nextreset and nextreset > time() then
- for toon, ti in pairs(vars.db.Toons) do
- if not ti.WeeklyResetTime or (ti.WeeklyResetTime < time()) then
- ti.currency = ti.currency or {}
- for _,idx in ipairs(currency) do
- local ci = ti.currency[idx]
- if ci and ci.earnedThisWeek then
- ci.earnedThisWeek = 0
- end
- end
- ti.WeeklyResetTime = (ti.WeeklyResetTime and ti.WeeklyResetTime + 7*24*3600) or nextreset
- end
- end
- t.WeeklyResetTime = nextreset
- end
- for toon, ti in pairs(vars.db.Toons) do
- for id,qi in pairs(ti.Quests) do
- if not qi.isDaily and (qi.Expires or 0) < time() then
- ti.Quests[id] = nil
- end
- if QuestExceptions[id] == "Regular" then -- adjust exceptions
- ti.Quests[id] = nil
- end
- end
- end
- for id,qi in pairs(db.Quests) do -- AccountWeekly reset
- if not qi.isDaily and (qi.Expires or 0) < time() then
- db.Quests[id] = nil
- end
- end
- addon:UpdateCurrency()
- local zone = GetRealZoneText()
- if zone and #zone > 0 then
- t.Zone = zone
- end
- local lrace, race = UnitRace("player")
- local faction, lfaction = UnitFactionGroup("player")
- t.Faction = faction
- if race == "Pandaren" then
- t.Race = lrace.." ("..lfaction..")"
- else
- t.Race = lrace
- end
- t.LastSeen = time()
- end
- function addon:UpdateCurrency()
- if addon.logout then return end -- currency is unreliable during logout
- local t = vars.db.Toons[thisToon]
- t.Money = GetMoney()
- t.currency = wipe(t.currency or {})
- for _,idx in ipairs(currency) do
- local _, amount, _, earnedThisWeek, weeklyMax, totalMax, discovered = GetCurrencyInfo(idx)
- if idx == 390 and amount == 0 then
- discovered = false -- discovery flag broken for conquest points
- end
- if not discovered then
- t.currency[idx] = nil
- else
- local ci = t.currency[idx] or {}
- ci.amount, ci.earnedThisWeek, ci.weeklyMax, ci.totalMax = amount, earnedThisWeek, weeklyMax, totalMax
- if idx == 396 then -- VP has a weekly max scaled by 100
- ci.weeklyMax = ci.weeklyMax and math.floor(ci.weeklyMax/100)
- end
- if idx == 390 or idx == 395 or idx == 396 then -- these have a total max scaled by 100
- ci.totalMax = ci.totalMax and math.floor(ci.totalMax/100)
- end
- if idx == 390 then -- these have a weekly earned scaled by 100
- ci.earnedThisWeek = ci.earnedThisWeek and math.floor(ci.earnedThisWeek/100)
- end
- if idx == 1129 then -- Seal of Tempered Fate returns zero for weekly quantities
- ci.weeklyMax = 3 -- the max via quests
- ci.earnedThisWeek = 0
- for id in pairs(WoDSealQuests) do
- if IsQuestFlaggedCompleted(id) then
- ci.earnedThisWeek = ci.earnedThisWeek + 1
- end
- end
- end
- ci.season = addon:GetSeasonCurrency(idx)
- if ci.weeklyMax == 0 then ci.weeklyMax = nil end -- don't store useless info
- if ci.totalMax == 0 then ci.totalMax = nil end -- don't store useless info
- if ci.earnedThisWeek == 0 then ci.earnedThisWeek = nil end -- don't store useless info
- t.currency[idx] = ci
- end
- end
- end
- function addon:QuestIsDarkmoonMonthly()
- if QuestIsDaily() then return false end
- local id = GetQuestID()
- local scope = id and QuestExceptions[id]
- if scope and scope ~= "Darkmoon" then return false end -- one-time referral quests
- for i=1,GetNumRewardCurrencies() do
- local name,texture,amount = GetQuestCurrencyInfo("reward",i)
- if texture:find("_ticket_darkmoon_") then
- return true
- end
- end
- return false
- end
- function addon:GetCurrentMapAreaID()
- local oldmap = GetCurrentMapAreaID()
- local oldlvl = GetCurrentMapDungeonLevel()
- SetMapToCurrentZone()
- local map = GetCurrentMapAreaID()
- SetMapByID(oldmap)
- if oldlvl and oldlvl > 0 then
- SetDungeonMapLevel(oldlvl)
- end
- return map
- end
- local function SI_GetQuestReward()
- local t = vars and vars.db.Toons[thisToon]
- if not t then return end
- local id = GetQuestID() or -1
- local title = GetTitleText() or ""
- local link = nil
- local isMonthly = addon:QuestIsDarkmoonMonthly()
- local isWeekly = QuestIsWeekly()
- local isDaily = QuestIsDaily()
- local isAccount
- local index = GetQuestLogIndexByID(id)
- if index and index > 0 then
- link = GetQuestLink(index)
- end
- if id > 1 then -- try harder to fetch names
- local t,l = addon:QuestInfo(id)
- if not (link and #link > 0) then
- link = l
- end
- if not (title and #title > 0) then
- title = t or "<unknown>"
- end
- end
- local questTagID, tagName = GetQuestTagInfo(id)
- if questTagID and tagName then
- isAccount = (questTagID == QUEST_TAG_ACCOUNT)
- else
- isAccount = db.QuestDB.AccountDaily[id] or db.QuestDB.AccountWeekly[id]
- debug("Fetched isAccount")
- end
- if QuestExceptions[id] then
- local qe = QuestExceptions[id]
- isAccount = qe:find("Account") and true
- isDaily = qe:find("Daily") and true
- isWeekly = qe:find("Weekly") and true
- isMonthly = qe:find("Darkmoon") and true
- end
- local expires
- local questDB
- if isWeekly then
- expires = addon:GetNextWeeklyResetTime()
- questDB = (isAccount and db.QuestDB.AccountWeekly) or db.QuestDB.Weekly
- elseif isMonthly then
- expires = addon:GetNextDarkmoonResetTime()
- questDB = db.QuestDB.Darkmoon
- elseif isDaily then
- questDB = (isAccount and db.QuestDB.AccountDaily) or db.QuestDB.Daily
- end
- debug("Quest Complete: "..(link or title).." "..id.." : "..title.." "..
- (isAccount and "(Account) " or "")..
- (isMonthly and "(Monthly)" or isWeekly and "(Weekly)" or isDaily and "(Daily)" or "(Regular)").." "..
- (expires and date("%c",expires) or ""))
- if not isMonthly and not isWeekly and not isDaily then return end
- local mapid = addon:GetCurrentMapAreaID()
- questDB[id] = mapid
- local qinfo = { ["Title"] = title, ["Link"] = link,
- ["isDaily"] = isDaily,
- ["Expires"] = expires,
- ["Zone"] = GetMapNameByID(mapid) }
- local scope = t
- if isAccount then
- scope = db
- if t.Quests then t.Quests[id] = nil end -- make sure we promote account quests
- end
- scope.Quests = scope.Quests or {}
- scope.Quests[id] = qinfo
- local dc, wc = addon:QuestCount(thisToon)
- local adc, awc = addon:QuestCount(nil)
- debug("DailyCount: "..dc.." WeeklyCount: "..wc.." AccountDailyCount: "..adc.." AccountWeeklyCount: "..awc)
- end
- hooksecurefunc("GetQuestReward", SI_GetQuestReward)
- local function coloredText(fontstring)
- if not fontstring then return nil end
- local text = fontstring:GetText()
- if not text then return nil end
- local textR, textG, textB, textAlpha = fontstring:GetTextColor()
- return string.format("|c%02x%02x%02x%02x"..text.."|r",
- textAlpha*255, textR*255, textG*255, textB*255)
- end
- local function openIndicator(...)
- indicatortip = QTip:Acquire("SavedInstancesIndicatorTooltip", ...)
- indicatortip:Clear()
- indicatortip:SetHeaderFont(core:HeaderFont())
- indicatortip:SetScale(vars.db.Tooltip.Scale)
- end
- local function finishIndicator(parent)
- parent = parent or tooltip
- indicatortip:SetAutoHideDelay(0.1, parent)
- indicatortip.OnRelease = function() indicatortip = nil end -- extra-safety: update our variable on auto-release
- indicatortip:SmartAnchorTo(parent)
- indicatortip:SetFrameLevel(150) -- ensure visibility when forced to overlap main tooltip
- addon:SkinFrame(indicatortip,"SavedInstancesIndicatorTooltip")
- indicatortip:Show()
- end
- local function ShowToonTooltip(cell, arg, ...)
- local toon = arg
- if not toon then return end
- local t = vars.db.Toons[toon]
- if not t then return end
- openIndicator(2, "LEFT","RIGHT")
- local ftex = ""
- if t.Faction == "Alliance" then
- ftex = "\124TInterface\\TargetingFrame\\UI-PVP-Alliance:0:0:0:0:100:100:0:50:0:55\124t "
- elseif t.Faction == "Horde" then
- ftex = "\124TInterface\\TargetingFrame\\UI-PVP-Horde:0:0:0:0:100:100:10:70:0:55\124t"
- end
- indicatortip:SetCell(indicatortip:AddHeader(),1,ftex..ClassColorise(t.Class, toon))
- indicatortip:SetCell(1,2,ClassColorise(t.Class, LEVEL.." "..t.Level.." "..(t.LClass or "")))
- indicatortip:AddLine(STAT_AVERAGE_ITEM_LEVEL,("%d "):format(t.IL or 0)..STAT_AVERAGE_ITEM_LEVEL_EQUIPPED:format(t.ILe or 0))
- if t.RBGrating and t.RBGrating > 0 then
- indicatortip:AddLine(BATTLEGROUND_RATING, t.RBGrating)
- end
- if t.Money then
- indicatortip:AddLine(MONEY,addon:formatNumber(t.Money,true))
- end
- if t.Zone then
- indicatortip:AddLine(ZONE,t.Zone)
- end
- --[[
- if t.Race then
- indicatortip:AddLine(RACE,t.Race)
- end
- ]]
- if t.LastSeen then
- local when = date("%c",t.LastSeen)
- indicatortip:AddLine(L["Last updated"],when)
- end
- if vars.db.Tooltip.TrackPlayed and t.PlayedTotal and t.PlayedLevel and ChatFrame_TimeBreakDown then
- --indicatortip:AddLine((TIME_PLAYED_TOTAL):format((TIME_DAYHOURMINUTESECOND):format(ChatFrame_TimeBreakDown(t.PlayedTotal))))
- --indicatortip:AddLine((TIME_PLAYED_LEVEL):format((TIME_DAYHOURMINUTESECOND):format(ChatFrame_TimeBreakDown(t.PlayedLevel))))
- indicatortip:AddLine((TIME_PLAYED_TOTAL):format(""),SecondsToTime(t.PlayedTotal))
- indicatortip:AddLine((TIME_PLAYED_LEVEL):format(""),SecondsToTime(t.PlayedLevel))
- end
- finishIndicator()
- end
- local function ShowQuestTooltip(cell, arg, ...)
- local toon,cnt,isDaily = unpack(arg)
- local qstr = cnt.." "..(isDaily and L["Daily Quests"] or L["Weekly Quests"])
- local t = db
- local scopestr = L["Account"]
- local reset
- if toon then
- t = vars.db.Toons[toon]
- if not t then return end
- scopestr = ClassColorise(t.Class, toon)
- reset = (isDaily and t.DailyResetTime) or (not isDaily and t.WeeklyResetTime)
- end
- openIndicator(2, "LEFT","RIGHT")
- indicatortip:AddHeader(scopestr, qstr)
- if not reset then
- reset = (isDaily and addon:GetNextDailyResetTime()) or (not isDaily and addon:GetNextWeeklyResetTime())
- end
- if reset then
- indicatortip:AddLine(YELLOWFONT .. L["Time Left"] .. ":" .. FONTEND,
- SecondsToTime(reset - time()))
- end
- local ql = {}
- for id,qi in pairs(t.Quests) do
- if (not isDaily) == (not qi.isDaily) then
- table.insert(ql,(qi.Zone or "").." # "..id)
- end
- end
- table.sort(ql)
- for _,e in ipairs(ql) do
- local id = tonumber(e:match("# (%d+)"))
- local qi = id and t.Quests[id]
- local line = indicatortip:AddLine()
- local link = qi.Link
- if not link then -- sometimes missing the actual link due to races, fake it for display to prevent confusion
- if qi.Title:find("("..LOOT..")") then
- link = qi.Title
- else
- link = "\124cffffff00["..(qi.Title or "???").."]\124r"
- end
- end
- indicatortip:SetCell(line,1,(qi.Zone or ""),"LEFT")
- indicatortip:SetCell(line,2,link,"RIGHT")
- end
- finishIndicator()
- end
- local function skillsort(s1, s2)
- if s1.Expires ~= s2.Expires then
- return (s1.Expires or 0) < (s2.Expires or 0)
- else
- return (s1.Title or "") < (s2.Title or "")
- end
- end
- local function ShowSkillTooltip(cell, arg, ...)
- local toon, cnt = unpack(arg)
- local cstr = cnt.." "..L["Trade Skill Cooldowns"]
- local t = vars.db.Toons[toon]
- if not t then return end
- openIndicator(3, "LEFT","RIGHT","RIGHT")
- local tname = ClassColorise(t.Class, toon)
- indicatortip:AddHeader()
- indicatortip:SetCell(1,1,tname,"LEFT")
- indicatortip:SetCell(1,2,cstr,"RIGHT",2)
- local tmp = {}
- for _,sinfo in pairs(t.Skills) do
- table.insert(tmp,sinfo)
- end
- table.sort(tmp, skillsort)
- for _,sinfo in ipairs(tmp) do
- local line = indicatortip:AddLine()
- local title = sinfo.Link or sinfo.Title or "???"
- local tstr = SecondsToTime((sinfo.Expires or 0) - time())
- indicatortip:SetCell(line,1,title,"LEFT",2)
- indicatortip:SetCell(line,3,tstr,"RIGHT")
- end
- finishIndicator()
- end
- function addon:plantName(spellid)
- local name = GetSpellInfo(spellid)
- if not name then return "unknown" end
- name = name:gsub(L["Plant"],"")
- name = name:gsub(L["Throw"],"")
- name = name:gsub(L["Seeds"],"")
- name = name:gsub(L["Seed"],"")
- name = strtrim(name)
- return name
- end
- local function ShowFarmTooltip(cell, arg, ...)
- local toon = arg
- local t = vars.db.Toons[toon]
- if not t then return end
- openIndicator(2, "LEFT","RIGHT")
- local tname = ClassColorise(t.Class, toon)
- indicatortip:AddHeader()
- indicatortip:SetCell(1,1,tname,"LEFT")
- indicatortip:SetCell(1,2,L["Farm Crops"],"RIGHT")
- local exp = t.FarmExpires
- if exp and exp > time() then
- indicatortip:AddLine(YELLOWFONT .. L["Time Left"] .. ":" .. FONTEND, SecondsToTime(exp - time()))
- end
- indicatortip:AddLine(YELLOWFONT .. L["Crops harvested today"] .. ":" .. FONTEND,(t.FarmHarvested or 0))
- indicatortip:AddLine(YELLOWFONT .. L["Crops planted today"] .. ":" .. FONTEND, (t.FarmPlanted or 0))
- local crops
- if t.FarmCropPlanted and next(t.FarmCropPlanted) then
- crops = t.FarmCropPlanted
- indicatortip:AddLine(YELLOWFONT .. L["Crops growing"] .. ":" .. FONTEND)
- elseif t.FarmCropReady and next(t.FarmCropReady) then
- crops = t.FarmCropReady
- indicatortip:AddLine(YELLOWFONT .. L["Crops ready"] .. ":" .. FONTEND)
- end
- if crops then
- for spellid,cnt in pairs(crops) do
- local line = indicatortip:AddLine()
- indicatortip:SetCell(line,1, addon:plantName(spellid),"LEFT")
- indicatortip:SetCell(line,2,"x"..cnt,"RIGHT")
- end
- end
- finishIndicator()
- end
- local function ShowBonusTooltip(cell, arg, ...)
- local toon = arg
- local parent
- if type(toon) == "table" then
- toon, parent = unpack(toon)
- end
- local t = vars.db.Toons[toon]
- if not t or not t.BonusRoll then return end
- openIndicator(4, "LEFT","LEFT","LEFT","LEFT")
- local tname = ClassColorise(t.Class, toon)
- indicatortip:AddHeader()
- indicatortip:SetCell(1,1,tname,"LEFT",2)
- indicatortip:SetCell(1,3,L["Recent Bonus Rolls"],"RIGHT",2)
- local line = indicatortip:AddLine()
- for i,roll in ipairs(t.BonusRoll) do
- if i > 10 then break end
- local line = indicatortip:AddLine()
- local icon = roll.currencyID and select(3,GetCurrencyInfo(roll.currencyID))
- if icon then
- indicatortip:SetCell(line,1, " \124T"..icon..":0\124t ")
- end
- if roll.name then
- indicatortip:SetCell(line,2,roll.name)
- end
- if roll.item then
- indicatortip:SetCell(line,3,roll.item)
- elseif roll.money then
- indicatortip:SetCell(line,3,GetMoneyString(roll.money))
- end
- if roll.time then
- indicatortip:SetCell(line,4,date("%b %d %H:%M",roll.time))
- end
- end
- finishIndicator(parent)
- end
- local function ShowAccountSummary(cell, arg, ...)
- openIndicator(2, "LEFT","RIGHT")
- indicatortip:SetCell(indicatortip:AddHeader(),1,GOLDFONT..L["Account Summary"]..FONTEND,"LEFT",2)
- local tmoney = 0
- local ttime = 0
- local ttoons = 0
- local tmaxtoons = 0
- local r = {}
- for toon, t in pairs(vars.db.Toons) do -- deliberately include ALL toons
- local realm = toon:match(" %- (.+)$")
- local money = t.Money or 0
- tmoney = tmoney + money
- local ri = r[realm] or { ["realm"] = realm, ["money"] = 0, ["cnt"] = 0 }
- ri.money = ri.money + money
- ri.cnt = ri.cnt + 1
- r[realm] = ri
- ttime = ttime + (t.PlayedTotal or 0)
- ttoons = ttoons + 1
- if t.Level == maxlvl then
- tmaxtoons = tmaxtoons + 1
- end
- end
- indicatortip:AddLine(L["Characters"], ttoons)
- indicatortip:AddLine(string.format(L["Level %d Characters"],maxlvl), tmaxtoons)
- if vars.db.Tooltip.TrackPlayed then
- indicatortip:AddLine((TIME_PLAYED_TOTAL):format(""),SecondsToTime(ttime))
- end
- indicatortip:AddLine(TOTAL.." "..MONEY,addon:formatNumber(tmoney,true))
- local rmoney = {}
- for _,ri in pairs(r) do table.insert(rmoney,ri) end
- table.sort(rmoney,function(a,b) return a.money > b.money end)
- for _,ri in ipairs(rmoney) do
- if ri.money > 10000*10000 and ri.cnt > 1 then -- show multi-toon servers with over 10k wealth
- indicatortip:AddLine(ri.realm.." "..MONEY,addon:formatNumber(ri.money,true))
- end
- end
- -- history information
- indicatortip:AddLine("")
- addon:HistoryUpdate()
- local tmp = {}
- local cnt = 0
- for _,ii in pairs(db.History) do
- table.insert(tmp,ii)
- end
- local cnt = #tmp
- table.sort(tmp, function(i1,i2) return i1.last < i2.last end)
- indicatortip:SetHeaderFont(tooltip:GetHeaderFont())
- indicatortip:SetCell(indicatortip:AddHeader(),1,GOLDFONT..cnt.." "..L["Recent Instances"]..": "..FONTEND,"LEFT",2)
- for _,ii in ipairs(tmp) do
- local tstr = REDFONT..SecondsToTime(ii.last+addon.histReapTime - time(),false,false,1)..FONTEND
- indicatortip:AddLine(tstr, ii.desc)
- end
- indicatortip:AddLine("")
- indicatortip:SetCell(indicatortip:AddLine(),1,
- string.format(L["These are the instances that count towards the %i instances per hour account limit, and the time until they expire."],
- addon.histLimit),"LEFT",2,nil,nil,nil,250)
- finishIndicator()
- end
- local function ShowWorldBossTooltip(cell, arg, ...)
- local worldbosses = arg[1]
- local toon = arg[2]
- local saved = arg[3]
- if not worldbosses or not toon then return end
- openIndicator(2, "LEFT","RIGHT")
- local line = indicatortip:AddHeader()
- local toonstr = (db.Tooltip.ShowServer and toon) or strsplit(' ', toon)
- local t = vars.db.Toons[toon]
- local reset = t.WeeklyResetTime or addon:GetNextWeeklyResetTime()
- indicatortip:SetCell(line, 1, ClassColorise(vars.db.Toons[toon].Class, toonstr), indicatortip:GetHeaderFont(), "LEFT")
- indicatortip:SetCell(line, 2, GOLDFONT .. L["World Bosses"] .. FONTEND, indicatortip:GetHeaderFont(), "RIGHT")
- indicatortip:AddLine(YELLOWFONT .. L["Time Left"] .. ":" .. FONTEND, SecondsToTime(reset - time()))
- for _, instance in ipairs(worldbosses) do
- local thisinstance = vars.db.Instances[instance]
- if thisinstance then
- local info = thisinstance[toon] and thisinstance[toon][2]
- local n = indicatortip:AddLine()
- indicatortip:SetCell(n, 1, instance, "LEFT")
- if info and info[1] then
- indicatortip:SetCell(n, 2, REDFONT..ERR_LOOT_GONE..FONTEND, "RIGHT")
- else
- indicatortip:SetCell(n, 2, GREENFONT..AVAILABLE..FONTEND, "RIGHT")
- end
- end
- end
- finishIndicator()
- end
- local function ShowLFRTooltip(cell, arg, ...)
- local boxname = arg[1]
- local toon = arg[2]
- local lfrmap = arg[3]
- local t = vars.db.Toons[toon]
- if not boxname or not t or not lfrmap then return end
- openIndicator(3, "LEFT", "LEFT","RIGHT")
- local line = indicatortip:AddHeader()
- local toonstr = (db.Tooltip.ShowServer and toon) or strsplit(' ', toon)
- local reset = t.WeeklyResetTime or addon:GetNextWeeklyResetTime()
- indicatortip:SetCell(line, 1, ClassColorise(vars.db.Toons[toon].Class, toonstr), indicatortip:GetHeaderFont(), "LEFT", 1)
- indicatortip:SetCell(line, 2, GOLDFONT .. boxname .. FONTEND, indicatortip:GetHeaderFont(), "RIGHT", 2)
- indicatortip:AddLine(YELLOWFONT .. L["Time Left"] .. ":" .. FONTEND, nil, SecondsToTime(reset - time()))
- for i=1,20 do
- local instance = lfrmap[boxname..":"..i]
- local diff = 2
- if instance then
- indicatortip:SetCell(indicatortip:AddLine(), 1, YELLOWFONT .. instance .. FONTEND, "CENTER",3)
- local thisinstance = vars.db.Instances[instance]
- local info = thisinstance[toon] and thisinstance[toon][diff]
- local killed, total, base, remap = addon:instanceBosses(instance,toon,diff)
- for i=base,base+total-1 do
- local bossid = i
- if remap then
- bossid = remap[i-base+1]
- end
- local bossname = GetLFGDungeonEncounterInfo(thisinstance.LFDID, bossid);
- local n = indicatortip:AddLine()
- indicatortip:SetCell(n, 1, bossname, "LEFT", 2)
- if info and info[bossid] then
- indicatortip:SetCell(n, 3, REDFONT..ERR_LOOT_GONE..FONTEND, "RIGHT", 1)
- else
- indicatortip:SetCell(n, 3, GREENFONT..AVAILABLE..FONTEND, "RIGHT", 1)
- end
- end
- end
- end
- finishIndicator()
- end
- local function ShowIndicatorTooltip(cell, arg, ...)
- local instance = arg[1]
- local toon = arg[2]
- local diff = arg[3]
- if not instance or not toon or not diff then return end
- openIndicator(3, "LEFT", "LEFT","RIGHT")
- local thisinstance = vars.db.Instances[instance]
- local worldboss = thisinstance and thisinstance.WorldBoss
- local info = thisinstance[toon][diff]
- local id = info.ID
- local nameline = indicatortip:AddHeader()
- indicatortip:SetCell(nameline, 1, DifficultyString(instance, diff, toon), indicatortip:GetHeaderFont(), "LEFT", 1)
- indicatortip:SetCell(nameline, 2, GOLDFONT .. instance .. FONTEND, indicatortip:GetHeaderFont(), "RIGHT", 2)
- local toonline = indicatortip:AddHeader()
- local toonstr = (db.Tooltip.ShowServer and toon) or strsplit(' ', toon)
- indicatortip:SetCell(toonline, 1, ClassColorise(vars.db.Toons[toon].Class, toonstr), indicatortip:GetHeaderFont(), "LEFT", 1)
- indicatortip:SetCell(toonline, 2, addon:idtext(thisinstance,diff,info), "RIGHT", 2)
- local EMPH = " !!! "
- if info.Extended then
- indicatortip:SetCell(indicatortip:AddLine(),1,WHITEFONT .. EMPH .. L["Extended Lockout - Not yet saved"] .. EMPH .. FONTEND,"CENTER",3)
- elseif info.Locked == false and info.ID > 0 then
- indicatortip:SetCell(indicatortip:AddLine(),1,WHITEFONT .. EMPH .. L["Expired Lockout - Can be extended"] .. EMPH .. FONTEND,"CENTER",3)
- end
- if info.Expires > 0 then
- indicatortip:AddLine(YELLOWFONT .. L["Time Left"] .. ":" .. FONTEND, nil, SecondsToTime(thisinstance[toon][diff].Expires - time()))
- end
- if (info.ID or 0) > 0 and (
- (thisinstance.Raid and (diff == 5 or diff == 6 or diff == 16)) -- raid: 10 heroic, 25 heroic or mythic
- or
- (diff == 23) -- mythic 5-man
- ) then
- local n = indicatortip:AddLine()
- indicatortip:SetCell(n, 1, YELLOWFONT .. ID .. ":" .. FONTEND, "LEFT", 1)
- indicatortip:SetCell(n, 2, info.ID, "RIGHT", 2)
- end
- if info.Link then
- scantt:SetOwner(UIParent,"ANCHOR_NONE")
- scantt:SetHyperlink(info.Link)
- local name = scantt:GetName()
- local gotbossinfo
- for i=2,scantt:NumLines() do
- local left,right = _G[name.."TextLeft"..i], _G[name.."TextRight"..i]
- if right and right:GetText() then
- local n = indicatortip:AddLine()
- indicatortip:SetCell(n, 1, coloredText(left), "LEFT", 2)
- indicatortip:SetCell(n, 3, coloredText(right), "RIGHT", 1)
- gotbossinfo = true
- else
- indicatortip:SetCell(indicatortip:AddLine(),1,coloredText(left),"CENTER",3)
- end
- end
- if not gotbossinfo then
- local exc = addon:instanceException(thisinstance.LFDID)
- local bits = tonumber(info.Link:match(":(%d+)\124h"))
- if exc and bits then
- for i=1,exc.total do
- local n = indicatortip:AddLine()
- indicatortip:SetCell(n, 1, exc[i], "LEFT", 2)
- local text = "\124cff00ff00"..BOSS_ALIVE.."\124r"
- if bit.band(bits,1) > 0 then
- text = "\124cffff1f1f"..BOSS_DEAD.."\124r"
- end
- indicatortip:SetCell(n, 3, text, "RIGHT", 1)
- bits = bit.rshift(bits,1)
- end
- else
- indicatortip:SetCell(indicatortip:AddLine(),1,WHITEFONT ..
- L["Boss kill information is missing for this lockout.\nThis is a Blizzard bug affecting certain old raids."] ..
- FONTEND,"CENTER",3)
- end
- end
- end
- if info.ID < 0 then
- local killed, total, base, remap = addon:instanceBosses(instance,toon,diff)
- for i=base,base+total-1 do
- local bossid = i
- if remap then
- bossid = remap[i-base+1]
- end
- local bossname
- if worldboss then
- bossname = addon.WorldBosses[worldboss].name or "UNKNOWN"
- else
- bossname = GetLFGDungeonEncounterInfo(thisinstance.LFDID, bossid);
- end
- local n = indicatortip:AddLine()
- indicatortip:SetCell(n, 1, bossname, "LEFT", 2)
- if info[bossid] then
- indicatortip:SetCell(n, 3, REDFONT..ERR_LOOT_GONE..FONTEND, "RIGHT", 1)
- else
- indicatortip:SetCell(n, 3, GREENFONT..AVAILABLE..FONTEND, "RIGHT", 1)
- end
- end
- end
- finishIndicator()
- end
- local colorpat = "\124c%c%c%c%c%c%c%c%c"
- local weeklycap = CURRENCY_WEEKLY_CAP:gsub("%%%d*\$?([ds])","%%%1")
- local weeklycap_scan = weeklycap:gsub("%%d","(%%d+)"):gsub("%%s","(\124c%%x%%x%%x%%x%%x%%x%%x%%x)")
- weeklycap = weeklycap:gsub("%%d","%%s")
- local totalcap = CURRENCY_TOTAL_CAP:gsub("%%%d*\$?([ds])","%%%1")
- local totalcap_scan = totalcap:gsub("%%d","(%%d+)"):gsub("%%s","(\124c%%x%%x%%x%%x%%x%%x%%x%%x)")
- totalcap = totalcap:gsub("%%d","%%s")
- local season_scan = CURRENCY_SEASON_TOTAL:gsub("%%%d*\$?([ds])","(%%%1*)")
- function addon:GetSeasonCurrency(idx)
- scantt:SetOwner(UIParent,"ANCHOR_NONE")
- scantt:SetCurrencyByID(idx)
- local name = scantt:GetName()
- for i=1,scantt:NumLines() do
- local left = _G[name.."TextLeft"..i]
- if left:GetText():find(season_scan) then
- return left:GetText()
- end
- end
- return nil
- end
- local function ShowSpellIDTooltip(cell, arg, ...)
- local toon, spellid, timestr = unpack(arg)
- if not toon or not spellid or not timestr then return end
- openIndicator(2, "LEFT","RIGHT")
- indicatortip:AddHeader(ClassColorise(vars.db.Toons[toon].Class, strsplit(' ', toon)), timestr)
- if spellid > 0 then
- local tip = vars.db.spelltip and vars.db.spelltip[spellid]
- for i=1,#tip do
- indicatortip:AddLine("")
- indicatortip:SetCell(indicatortip:GetLineCount(),1,tip[i], nil, "LEFT",2, nil, nil, nil, 250)
- end
- else
- local queuestr = LFG_RANDOM_COOLDOWN_YOU:match("^(.+)\n")
- indicatortip:AddLine(LFG_TYPE_RANDOM_DUNGEON)
- indicatortip:AddLine("")
- indicatortip:SetCell(indicatortip:GetLineCount(),1,queuestr, nil, "LEFT",2, nil, nil, nil, 250)
- end
- finishIndicator()
- end
- local function ShowCurrencyTooltip(cell, arg, ...)
- local toon, idx, ci = unpack(arg)
- if not toon or not idx or not ci then return end
- local name,_,tex = GetCurrencyInfo(idx)
- tex = " \124T"..tex..":0\124t"
- openIndicator(2, "LEFT","RIGHT")
- indicatortip:AddHeader(ClassColorise(vars.db.Toons[toon].Class, strsplit(' ', toon)), CurrencyColor(ci.amount or 0,ci.totalMax)..tex)
- scantt:SetOwner(UIParent,"ANCHOR_NONE")
- scantt:SetCurrencyByID(idx)
- local name = scantt:GetName()
- local spacer
- for i=1,scantt:NumLines() do
- local left = _G[name.."TextLeft"..i]
- local text = left:GetText()
- if text:find(weeklycap_scan) or
- text:find(totalcap_scan) or
- text:find(season_scan) then
- -- omit player's values
- else
- indicatortip:AddLine("")
- indicatortip:SetCell(indicatortip:GetLineCount(),1,coloredText(left), nil, "LEFT",2, nil, nil, nil, 250)
- spacer = #strtrim(text) == 0
- end
- end
- if ci.weeklyMax and ci.weeklyMax > 0 then
- if not spacer then indicatortip:AddLine(" "); spacer = true end
- indicatortip:AddLine(weeklycap:format("", CurrencyColor(ci.earnedThisWeek or 0,ci.weeklyMax), addon:formatNumber(ci.weeklyMax)))
- end
- if ci.totalMax and ci.totalMax > 0 then
- if not spacer then indicatortip:AddLine(" "); spacer = true end
- indicatortip:AddLine(totalcap:format("", CurrencyColor(ci.amount or 0,ci.totalMax), addon:formatNumber(ci.totalMax)))
- end
- if ci.season and #ci.season > 0 then
- if not spacer then indicatortip:AddLine(" "); spacer = true end
- local str = ci.season
- local num = str:match("(%d+)")
- if num then
- str = str:gsub(num,addon:formatNumber(num))
- end
- indicatortip:AddLine(str)
- end
- finishIndicator()
- end
- local function ShowCurrencySummary(cell, arg, ...)
- local idx = arg
- if not idx then return end
- local name,_,tex = GetCurrencyInfo(idx)
- tex = " \124T"..tex..":0\124t"
- openIndicator(2, "LEFT","RIGHT")
- indicatortip:AddHeader(name, "")
- local total = 0
- local tmax
- local temp = {}
- for toon, t in pairs(vars.db.Toons) do -- deliberately include ALL toons
- local ci = t.currency and t.currency[idx]
- if ci and ci.amount then
- tmax = tmax or ci.totalMax
- table.insert(temp, { ["toon"] = toon, ["amount"] = ci.amount,
- ["str1"] = ClassColorise(t.Class, toon),
- ["str2"] = CurrencyColor(ci.amount or 0,tmax)..tex,
- })
- total = total + ci.amount
- end
- end
- indicatortip:SetCell(1,2,CurrencyColor(total,0)..tex)
- --indicatortip:AddLine(TOTAL, CurrencyColor(total,tmax)..tex)
- --indicatortip:AddLine(" ")
- addon.currency_sort = addon.currency_sort or function(a,b)
- if a.amount > b.amount then
- return true
- elseif a.amount < b.amount then
- return false
- end
- local an, as = a.toon:match('^(.*) [-] (.*)$')
- local bn, bs = b.toon:match('^(.*) [-] (.*)$')
- if db.Tooltip.ServerSort and as ~= bs then
- return as < bs
- else
- return a.toon < b.toon
- end
- end
- table.sort(temp, addon.currency_sort)
- for _,t in ipairs(temp) do
- indicatortip:AddLine(t.str1, t.str2)
- end
- finishIndicator()
- end
- -- global addon code below
- function core:toonInit()
- local ti = db.Toons[thisToon] or { }
- db.Toons[thisToon] = ti
- ti.LClass, ti.Class = UnitClass("player")
- ti.Level = UnitLevel("player")
- ti.Show = ti.Show or "saved"
- ti.Quests = ti.Quests or {}
- ti.Skills = ti.Skills or {}
- -- try to get a reset time, but don't overwrite existing, which could break quest list
- -- real update comes later in UpdateToonData
- ti.DailyResetTime = ti.DailyResetTime or addon:GetNextDailyResetTime()
- ti.WeeklyResetTime = ti.WeeklyResetTime or addon:GetNextWeeklyResetTime()
- end
- function core:OnInitialize()
- SavedInstancesDB = SavedInstancesDB or vars.defaultDB
- -- begin backwards compatibility
- if not SavedInstancesDB.DBVersion or SavedInstancesDB.DBVersion < 10 then
- SavedInstancesDB = vars.defaultDB
- elseif SavedInstancesDB.DBVersion < 12 then
- SavedInstancesDB.Indicators = vars.defaultDB.Indicators
- SavedInstancesDB.DBVersion = 12
- end
- -- end backwards compatibilty
- db = db or SavedInstancesDB
- vars.db = db
- config = vars.config
- core:toonInit()
- db.Lockouts = nil -- deprecated
- db.History = db.History or {}
- db.Quests = db.Quests or vars.defaultDB.Quests
- db.QuestDB = db.QuestDB or vars.defaultDB.QuestDB
- for name,default in pairs(vars.defaultDB.Tooltip) do
- db.Tooltip[name] = (db.Tooltip[name]==nil and default) or db.Tooltip[name]
- end
- for _, id in ipairs(addon.currency) do
- local name = "Currency"..id
- db.Tooltip[name] = (db.Tooltip[name]==nil and vars.defaultDB.Tooltip[name]) or db.Tooltip[name]
- end
- local currtmp = {}
- for _,idx in ipairs(currency) do currtmp[idx] = true end
- for toon, t in pairs(vars.db.Toons) do
- t.Order = t.Order or 50
- if t.currency then -- clean old undiscovered currency entries
- for idx, ci in pairs(t.currency) do
- -- detect outdated entries because new version doesn't explicitly store max zeros
- if (ci.amount == 0 and (ci.weeklyMax == 0 or ci.totalMax == 0))
- or ci.amount == nil -- another outdated entry type created by old weekly reset logic
- or not currtmp[idx] -- removed currency
- then
- t.currency[idx] = nil
- end
- end
- end
- end
- for qid, _ in pairs(db.QuestDB.Daily) do
- if db.QuestDB.AccountDaily[qid] then
- debug("Removing duplicate questDB entry: "..qid)
- db.QuestDB.Daily[qid] = nil
- end
- end
- for qid, escope in pairs(QuestExceptions) do -- upgrade QuestDB with new exceptions
- local val = -1 -- default to a blank zone
- for scope, qdb in pairs(db.QuestDB) do
- val = qdb[qid] or val
- qdb[qid] = nil
- end
- if db.QuestDB[escope] then
- db.QuestDB[escope][qid] = val
- end
- end
- addon:SetupVersion()
- RequestRaidInfo() -- get lockout data
- RequestLFDPlayerLockInfo()
- vars.dataobject = vars.LDB and vars.LDB:NewDataObject("SavedInstances", {
- text = addonAbbrev,
- type = "launcher",
- icon = "Interface\\Addons\\SavedInstances\\icon.tga",
- OnEnter = function(frame)
- if not addon:IsDetached() and not db.Tooltip.DisableMouseover then
- core:ShowTooltip(frame)
- end
- end,
- OnLeave = function(frame) end,
- OnClick = function(frame, button)
- if button == "MiddleButton" then
- if InCombatLockdown() then return end
- ToggleFriendsFrame(4) -- open Blizzard Raid window
- RaidInfoFrame:Show()
- elseif button == "LeftButton" then
- addon:ToggleDetached()
- else
- config:ShowConfig()
- end
- end
- })
- if vars.icon then
- vars.icon:Register(addonName, vars.dataobject, db.MinimapIcon)
- vars.icon:Refresh(addonName)
- end
- addon.BonusRollShow() -- catch roll-on-load
- end
- function addon:SetupVersion()
- if addon.version then return end
- local svnrev = 0
- local T_svnrev = addon.svnrev
- T_svnrev["X-Build"] = tonumber((GetAddOnMetadata(addonName, "X-Build") or ""):match("%d+"))
- T_svnrev["X-Revision"] = tonumber((GetAddOnMetadata(addonName, "X-Revision") or ""):match("%d+"))
- for _,v in pairs(T_svnrev) do -- determine highest file revision
- if v and v > svnrev then
- svnrev = v
- end
- end
- addon.revision = svnrev
- T_svnrev["X-Curse-Packaged-Version"] = GetAddOnMetadata(addonName, "X-Curse-Packaged-Version")
- T_svnrev["Version"] = GetAddOnMetadata(addonName, "Version")
- addon.version = T_svnrev["X-Curse-Packaged-Version"] or T_svnrev["Version"] or "@"
- if string.find(addon.version, "@") then -- dev copy uses "@.project-version.@"
- addon.version = "r"..svnrev
- end
- end
- function core:OnEnable()
- self:RegisterBucketEvent("UPDATE_INSTANCE_INFO", 2, function() core:Refresh(nil) end)
- self:RegisterBucketEvent("LOOT_CLOSED", 1, function() core:QuestRefresh(nil) end)
- self:RegisterBucketEvent("LFG_UPDATE_RANDOM_INFO", 1, function() addon:UpdateInstanceData(); addon:UpdateToonData() end)
- self:RegisterBucketEvent("RAID_INSTANCE_WELCOME", 1, RequestRaidInfo)
- self:RegisterEvent("CHAT_MSG_SYSTEM", "CheckSystemMessage")
- self:RegisterEvent("CHAT_MSG_CURRENCY", "CheckSystemMessage")
- self:RegisterEvent("CHAT_MSG_LOOT", "CheckSystemMessage")
- self:RegisterBucketEvent("CURRENCY_DISPLAY_UPDATE", 0.25, function() addon:UpdateCurrency() end)
- self:RegisterEvent("UNIT_SPELLCAST_SUCCEEDED")
- self:RegisterBucketEvent("TRADE_SKILL_LIST_UPDATE", 1)
- self:RegisterBucketEvent("PLAYER_ENTERING_WORLD", 1, RequestRaidInfo)
- self:RegisterBucketEvent("LFG_LOCK_INFO_RECEIVED", 1, RequestRaidInfo)
- self:RegisterEvent("BONUS_ROLL_RESULT", "BonusRollResult")
- self:RegisterEvent("PLAYER_LOGOUT", function() addon.logout = true ; addon:UpdateToonData() end) -- update currency spent
- self:RegisterEvent("LFG_COMPLETION_REWARD", "RefreshLockInfo") -- for random daily dungeon tracking
- self:RegisterEvent("BOSS_KILL")
- self:RegisterEvent("ENCOUNTER_END", "EncounterEnd")
- self:RegisterEvent("CHAT_MSG_MONSTER_YELL")
- self:RegisterEvent("TIME_PLAYED_MSG", function(_,total,level)
- local t = thisToon and vars and vars.db and vars.db.Toons[thisToon]
- if total > 0 and t then
- t.PlayedTotal = total
- t.PlayedLevel = level
- end
- addon.PlayedTime = time()
- if addon.playedpending then
- for c,_ in pairs(addon.playedreg) do
- c:RegisterEvent("TIME_PLAYED_MSG") -- Restore default
- end
- addon.playedpending = false
- end
- end)
- self:RegisterEvent("ADDON_LOADED")
- core:ADDON_LOADED()
- if not addon.resetDetect then
- addon.resetDetect = CreateFrame("Button", "SavedInstancesResetDetectHiddenFrame", UIParent)
- for _,e in pairs({
- "RAID_INSTANCE_WELCOME",
- "PLAYER_ENTERING_WORLD", "CHAT_MSG_SYSTEM", "CHAT_MSG_ADDON",
- "ZONE_CHANGED_NEW_AREA",
- "INSTANCE_BOOT_START", "INSTANCE_BOOT_STOP", "GROUP_ROSTER_UPDATE",
- }) do
- addon.resetDetect:RegisterEvent(e)
- end
- end
- addon.resetDetect:SetScript("OnEvent", addon.HistoryEvent)
- RegisterAddonMessagePrefix(addonName)
- addon:HistoryEvent("PLAYER_ENTERING_WORLD") -- update after initial load
- addon:specialQuests()
- core:updateRealmMap()
- end
- function core:ADDON_LOADED()
- if DBM and DBM.EndCombat and not addon.dbmhook then
- addon.dbmhook = true
- hooksecurefunc(DBM, "EndCombat", function(self, mod, wipe)
- core:BossModEncounterEnd("DBM:EndCombat", mod and mod.combatInfo and mod.combatInfo.name)
- end)
- end
- if BigWigsLoader and not addon.bigwigshook then
- addon.bigwigshook = true
- BigWigsLoader.RegisterMessage(self, "BigWigs_OnBossWin", function(self, event, mod)
- core:BossModEncounterEnd("BigWigs_OnBossWin", mod and mod.displayName)
- end)
- end
- end
- function core:OnDisable()
- self:UnregisterAllEvents()
- addon.resetDetect:SetScript("OnEvent", nil)
- end
- function core:RequestLockInfo() -- request lock info from the server immediately
- RequestRaidInfo()
- RequestLFDPlayerLockInfo()
- end
- function core:RefreshLockInfo() -- throttled lock update with retry
- local now = GetTime()
- if now > (core.lastrefreshlock or 0) + 1 then
- core.lastrefreshlock = now
- core:RequestLockInfo()
- end
- if now > (core.lastrefreshlocksched or 0) + 120 then
- -- make sure we update any lockout info (sometimes there's server-side delay)
- core.lastrefreshlockshed = now
- core:ScheduleTimer("RequestLockInfo",5)
- core:ScheduleTimer("RequestLockInfo",30)
- core:ScheduleTimer("RequestLockInfo",60)
- core:ScheduleTimer("RequestLockInfo",90)
- core:ScheduleTimer("RequestLockInfo",120)
- end
- end
- local currency_msg = CURRENCY_GAINED:gsub(":.*$","")
- function core:CheckSystemMessage(event, msg)
- local inst, t = IsInInstance()
- -- note: currency is already updated in TooltipShow,
- -- here we just hook JP/VP currency messages to capture lockout changes
- if inst and (t == "party" or t == "raid") and -- dont update on bg honor
- (msg:find(INSTANCE_SAVED) or -- first boss kill
- msg:find(currency_msg)) -- subsequent boss kills (unless capped or over level)
- then
- core:RefreshLockInfo()
- end
- end
- function core:updateRealmMap()
- local realm = GetRealmName():gsub("%s+","")
- local lmap = GetAutoCompleteRealms()
- local rmap = vars.db.RealmMap or {}
- vars.db.RealmMap = rmap
- if lmap and next(lmap) then -- connected realms detected
- table.sort(lmap)
- local mapid = rmap[realm] -- find existing map
- if not mapid then
- for _,r in ipairs(lmap) do
- mapid = mapid or rmap[r]
- end
- end
- if mapid then -- check for possible expansion
- local oldmap = rmap[mapid]
- if oldmap and #lmap > #oldmap then
- rmap[mapid] = lmap
- end
- else -- new map
- mapid = #rmap + 1
- rmap[mapid] = lmap
- end
- for _,r in ipairs(rmap[mapid]) do -- maintain inverse mapping
- rmap[r] = mapid
- end
- end
- end
- function core:getRealmGroup(realm)
- -- returns realm-group-id, { realm1, realm2, ...} for connected realm, or nil,nil for unconnected
- realm = realm:gsub("%s+","")
- local rmap = vars.db.RealmMap
- local gid = rmap and rmap[realm]
- return gid, gid and rmap[gid]
- end
- function core:CHAT_MSG_MONSTER_YELL(event, msg, bossname)
- -- cheapest possible outdoor boss detection for players lacking a proper boss mod
- -- should work for sha and nalak, oon and gal report a related mob
- local t = vars.db.Toons[thisToon]
- local now = time()
- if bossname and t then
- bossname = tostring(bossname) -- for safety
- local diff = select(4,GetInstanceInfo())
- if diff and #diff > 0 then bossname = bossname .. ": ".. diff end
- t.lastbossyell = bossname
- t.lastbossyelltime = now
- --debug("CHAT_MSG_MONSTER_YELL: "..tostring(bossname));
- end
- end
- function core:BossModEncounterEnd(modname, bossname)
- local t = vars.db.Toons[thisToon]
- local now = time()
- if bossname and t and now > (t.lastbosstime or 0) + 2*60 then
- -- boss mods can often detect completion before ENCOUNTER_END
- -- also some world bosses never send ENCOUNTER_END
- -- enough timeout to prevent overwriting, but short enough to prevent cross-boss contamination
- bossname = tostring(bossname) -- for safety
- local diff = select(4,GetInstanceInfo())
- if diff and #diff > 0 then bossname = bossname .. ": ".. diff end
- t.lastboss = bossname
- t.lastbosstime = now
- end
- debug("%s refresh: %s",(modname or "BossMod"),tostring(bossname));
- core:RefreshLockInfo()
- end
- function core:EncounterEnd(event, encounterID, encounterName, difficultyID, raidSize, endStatus)
- debug("EncounterEnd:%s:%s:%s:%s:%s",tostring(encounterID),tostring(encounterName),tostring(difficultyID),tostring(raidSize),tostring(endStatus))
- if endStatus ~= 1 then return end -- wipe
- core:RefreshLockInfo()
- local t = vars.db.Toons[thisToon]
- if not t then return end
- local name = encounterName
- if difficultyID and difficultyID > 0 then
- local diff = GetDifficultyInfo(difficultyID)
- if diff and #diff > 0 then
- name = name ..": "..diff
- end
- end
- t.lastboss = name
- t.lastbosstime = time()
- end
- function core:BOSS_KILL(event, encounterID, encounterName, ...)
- debug("BOSS_KILL:%s:%s",tostring(encounterID),tostring(encounterName)) -- ..":"..strjoin(":",...))
- local name = encounterName
- if name and type(name) == "string" then
- name = name:gsub(",.*$","") -- remove extraneous trailing boss titles
- name = strtrim(name)
- core:BossModEncounterEnd("BOSS_KILL", name)
- end
- end
- function addon:InGroup()
- if IsInRaid() then return "RAID"
- elseif GetNumGroupMembers() > 0 then return "PARTY"
- else return nil end
- end
- local function doExplicitReset(instancemsg, failed)
- if HasLFGRestrictions() or IsInInstance() or
- (addon:InGroup() and not UnitIsGroupLeader("player")) then return end
- if not failed then
- addon:HistoryUpdate(true)
- end
- local reportchan = addon:InGroup()
- if reportchan then
- if not failed then
- SendAddonMessage(addonName, "GENERATION_ADVANCE", reportchan)
- end
- if vars.db.Tooltip.ReportResets then
- local msg = instancemsg or RESET_INSTANCES
- msg = msg:gsub("\1241.+;.+;","") -- ticket 76, remove |1;; escapes on koKR
- SendChatMessage("<"..addonName.."> "..msg, reportchan)
- end
- end
- end
- hooksecurefunc("ResetInstances", doExplicitReset)
- local resetmsg = INSTANCE_RESET_SUCCESS:gsub("%%s",".+")
- local resetfails = { INSTANCE_RESET_FAILED, INSTANCE_RESET_FAILED_OFFLINE, INSTANCE_RESET_FAILED_ZONING }
- for k,v in pairs(resetfails) do
- resetfails[k] = v:gsub("%%s",".+")
- end
- local raiddiffmsg = ERR_RAID_DIFFICULTY_CHANGED_S:gsub("%%s",".+")
- local dungdiffmsg = ERR_DUNGEON_DIFFICULTY_CHANGED_S:gsub("%%s",".+")
- local delaytime = 3 -- seconds to wait on zone change for settings to stabilize
- function addon.HistoryEvent(f, evt, ...)
- --myprint("HistoryEvent: "..evt, ...)
- if evt == "CHAT_MSG_ADDON" then
- local prefix, message, channel, sender = ...
- if prefix ~= addonName then return end
- if message:match("^GENERATION_ADVANCE$") and not UnitIsUnit(sender,"player") then
- addon:HistoryUpdate(true)
- end
- elseif evt == "CHAT_MSG_SYSTEM" then
- local msg = ...
- if msg:match("^"..resetmsg.."$") then -- I performed expicit reset
- doExplicitReset(msg)
- elseif msg:match("^"..INSTANCE_SAVED.."$") then -- just got saved
- core:ScheduleTimer("HistoryUpdate", delaytime+1)
- elseif (msg:match("^"..raiddiffmsg.."$") or msg:match("^"..dungdiffmsg.."$")) and
- not addon:histZoneKey() then -- ignore difficulty messages when creating a party while inside an instance
- addon:HistoryUpdate(true)
- elseif msg:match(TRANSFER_ABORT_TOO_MANY_INSTANCES) then
- addon:HistoryUpdate(false,true)
- else
- for _,m in pairs(resetfails) do
- if msg:match("^"..m.."$") then
- doExplicitReset(msg, true) -- send failure chat message
- end
- end
- end
- elseif evt == "INSTANCE_BOOT_START" then -- left group inside instance, resets on boot
- addon:HistoryUpdate(true)
- elseif evt == "INSTANCE_BOOT_STOP" and addon:InGroup() then -- invited back
- addon.delayedReset = false
- elseif evt == "GROUP_ROSTER_UPDATE" and
- addon.histInGroup and not addon:InGroup() and -- ignore failed invites when solo
- not addon:histZoneKey() then -- left group outside instance, resets now
- addon:HistoryUpdate(true)
- elseif evt == "PLAYER_ENTERING_WORLD" or evt == "ZONE_CHANGED_NEW_AREA" or evt == "RAID_INSTANCE_WELCOME" then
- -- delay updates while settings stabilize
- local waittime = delaytime + math.max(0,10 - GetFramerate())
- addon.delayUpdate = time() + waittime
- core:ScheduleTimer("HistoryUpdate", waittime+1)
- end
- end
- addon.histReapTime = 60*60 -- 1 hour
- addon.histLimit = 10 -- instances per hour
- function addon:histZoneKey()
- local instname, insttype, diff, diffname, maxPlayers, playerDifficulty, isDynamicInstance = GetInstanceInfo()
- if insttype == nil or insttype == "none" or insttype == "arena" or insttype == "pvp" then -- pvp doesnt count
- return nil
- end
- if IsInLFGDungeon() or IsInScenarioGroup() then -- LFG instances don't count
- return nil
- end
- if C_Garrison.IsOnGarrisonMap() then -- Garrisons don't count
- return nil
- end
- -- check if we're locked (using FindInstance so we don't complain about unsaved unknown instances)
- local truename = addon:FindInstance(instname, insttype == "raid")
- local locked = false
- local inst = truename and vars.db.Instances[truename]
- inst = inst and inst[thisToon]
- for d=1,maxdiff do
- if inst and inst[d] and inst[d].Locked then
- locked = true
- end
- end
- if diff == 1 and maxPlayers == 5 then -- never locked to 5-man regs
- locked = false
- end
- local toonstr = thisToon
- if not db.Tooltip.ShowServer then
- toonstr = strsplit(" - ", toonstr)
- end
- local desc = toonstr .. ": " .. instname
- if diffname and #diffname > 0 then
- desc = desc .. " - " .. diffname
- end
- local key = thisToon..":"..instname..":"..insttype..":"..diff
- if not locked then
- key = key..":"..vars.db.histGeneration
- end
- return key, desc, locked
- end
- function addon:HistoryUpdate(forcereset, forcemesg)
- vars.db.histGeneration = vars.db.histGeneration or 1
- if forcereset and addon:histZoneKey() then -- delay reset until we zone out
- debug("HistoryUpdate reset delayed")
- addon.delayedReset = true
- end
- if (forcereset or addon.delayedReset) and not addon:histZoneKey() then
- debug("HistoryUpdate generation advance")
- vars.db.histGeneration = (vars.db.histGeneration + 1) % 100000
- addon.delayedReset = false
- end
- local now = time()
- if addon.delayUpdate and now < addon.delayUpdate then
- debug("HistoryUpdate delayed")
- return
- end
- local zoningin = false
- local newzone, newdesc, locked = addon:histZoneKey()
- -- touch zone we left
- if addon.histLastZone then
- local lz = vars.db.History[addon.histLastZone]
- if lz then
- lz.last = now
- end
- elseif newzone then
- zoningin = true
- end
- addon.histLastZone = newzone
- addon.histInGroup = addon:InGroup()
- -- touch/create new zone
- if newzone then
- local nz = vars.db.History[newzone]
- if not nz then
- nz = { create = now, desc = newdesc }
- vars.db.History[newzone] = nz
- if locked then -- creating a locked instance, delete unlocked version
- vars.db.History[newzone..":"..vars.db.histGeneration] = nil
- end
- end
- nz.last = now
- end
- -- reap old zones
- local livecnt = 0
- local oldestkey, oldesttime
- for zk, zi in pairs(vars.db.History) do
- if now > zi.last + addon.histReapTime or
- zi.last > (now + 3600) then -- temporary bug fix
- debug("Reaping %s",zi.desc)
- vars.db.History[zk] = nil
- else
- livecnt = livecnt + 1
- if not oldesttime or zi.last < oldesttime then
- oldestkey = zk
- oldesttime = zi.last
- end
- end
- end
- local oldestrem = oldesttime and (oldesttime+addon.histReapTime-now)
- local oldestremt = (oldestrem and SecondsToTime(oldestrem,false,false,1)) or "n/a"
- local oldestremtm = (oldestrem and SecondsToTime(math.floor((oldestrem+59)/60)*60,false,false,1)) or "n/a"
- if addon.db.dbg then
- local msg = livecnt.." live instances, oldest ("..(oldestkey or "none")..") expires in "..oldestremt..". Current Zone="..(newzone or "nil")
- if msg ~= addon.lasthistdbg then
- addon.lasthistdbg = msg
- debug(msg)
- end
- --myprint(vars.db.History)
- end
- -- display update
- if forcemesg or (vars.db.Tooltip.LimitWarn and zoningin and livecnt >= addon.histLimit-1) then
- chatMsg(L["Warning: You've entered about %i instances recently and are approaching the %i instance per hour limit for your account. More instances should be available in %s."],livecnt, addon.histLimit, oldestremt)
- end
- addon.histLiveCount = livecnt
- addon.histOldest = oldestremt
- if db.Tooltip.HistoryText and livecnt > 0 then
- vars.dataobject.text = "("..livecnt.."/"..(oldestremt or "?")..")"
- addon.histTextthrottle = math.min(oldestrem+1, addon.histTextthrottle or 15)
- addon.resetDetect:SetScript("OnUpdate", addon.histTextUpdate)
- else
- vars.dataobject.text = addonAbbrev
- addon.resetDetect:SetScript("OnUpdate", nil)
- end
- end
- function core:HistoryUpdate(...) return addon:HistoryUpdate(...) end
- function addon.histTextUpdate(self, elap)
- addon.histTextthrottle = addon.histTextthrottle - elap
- if addon.histTextthrottle > 0 then return end
- addon.histTextthrottle = 15
- addon:HistoryUpdate()
- end
- local function localarr(name) -- save on memory churn by reusing arrays in updates
- name = "localarr#"..name
- core[name] = core[name] or {}
- return wipe(core[name])
- end
- function core:memcheck(context)
- UpdateAddOnMemoryUsage()
- local newval = GetAddOnMemoryUsage("SavedInstances")
- core.memusage = core.memusage or 0
- if newval ~= core.memusage then
- debug("%.3f KB in %s",(newval - core.memusage),context)
- core.memusage = newval
- end
- end
- -- Lightweight refresh of just quest flag information
- -- all may be nil if not instantiataed
- function core:QuestRefresh(recoverdaily, questcomplete, nextreset, weeklyreset)
- local tiq = vars.db.Toons[thisToon]
- tiq = tiq and tiq.Quests
- if not tiq then return end
- nextreset = nextreset or addon:GetNextDailyResetTime()
- weeklyreset = weeklyreset or addon:GetNextWeeklyResetTime()
- if not nextreset or not weeklyreset then return end
- for _, qinfo in pairs(addon:specialQuests()) do
- local qid = qinfo.quest
- if IsQuestFlaggedCompleted(qid) or (questcomplete and questcomplete[qid]) then
- local q = tiq[qid] or {}
- tiq[qid] = q
- q.Title = qinfo.name
- q.Zone = qinfo.zone
- if qinfo.daily then
- q.Expires = nextreset
- q.isDaily = true
- else
- q.Expires = weeklyreset
- q.isDaily = nil
- end
- end
- end
- local now = time()
- db.QuestDB.Weekly.expires = weeklyreset
- db.QuestDB.AccountWeekly.expires = weeklyreset
- db.QuestDB.Darkmoon.expires = addon:GetNextDarkmoonResetTime()
- for scope, list in pairs(db.QuestDB) do
- local questlist = tiq
- if scope:find("Account") then
- questlist = db.Quests
- end
- if recoverdaily or (scope ~= "Daily") then
- for qid, mapid in pairs(list) do
- if tonumber(qid) and (IsQuestFlaggedCompleted(qid) or
- (questcomplete and questcomplete[qid])) and not questlist[qid] and -- recovering a lost quest
- (list.expires == nil or list.expires > now) then -- don't repop darkmoon quests from last faire
- local title, link = addon:QuestInfo(qid)
- if title then
- local found
- for _,info in pairs(questlist) do
- if title == info.Title then -- avoid faction duplicates, since both flags are set
- found = true
- break
- end
- end
- if not found then
- debug("Recovering lost quest: "..title.." ("..scope..")")
- questlist[qid] = { ["Title"] = title, ["Link"] = link,
- ["isDaily"] = (scope:find("Daily") and true) or nil,
- ["Expires"] = list.expires,
- ["Zone"] = GetMapNameByID(mapid) }
- end
- end
- end
- end
- end
- end
- addon:QuestCount(thisToon)
- end
- function core:Refresh(recoverdaily)
- -- update entire database from the current character's perspective
- addon:UpdateInstanceData()
- if not addon.instancesUpdated then addon.RefreshPending = true; return end -- wait for UpdateInstanceData to succeed
- local nextreset = addon:GetNextDailyResetTime()
- if not nextreset or ((nextreset - time()) > (24*3600 - 5*60)) then -- allow 5 minutes for quest DB to update after daily rollover
- debug("Skipping core:Refresh() near daily reset")
- addon:UpdateToonData()
- return
- end
- local temp = localarr("RefreshTemp")
- for name, instance in pairs(vars.db.Instances) do -- clear current toons lockouts before refresh
- local id = instance.LFDID
- if instance[thisToon]
- -- disabled for ticket 178/195:
- --and not (id and addon.LFRInstances[id] and select(2,GetLFGDungeonNumEncounters(id)) == 0) -- ticket 103
- then
- temp[name] = instance[thisToon] -- use a temp to reduce memory churn
- for diff,info in pairs(temp[name]) do
- wipe(info)
- end
- instance[thisToon] = nil
- end
- end
- local numsaved = GetNumSavedInstances()
- if numsaved > 0 then
- for i = 1, numsaved do
- local name, id, expires, diff, locked, extended, mostsig, raid, players, diffname = GetSavedInstanceInfo(i)
- local truename, instance = addon:LookupInstance(nil, name, raid)
- if expires and expires > 0 then
- expires = expires + time()
- else
- expires = 0
- end
- instance.Raid = instance.Raid or raid
- instance[thisToon] = instance[thisToon] or temp[truename] or { }
- local info = instance[thisToon][diff] or {}
- wipe(info)
- info.ID = id
- info.Expires = expires
- info.Link = GetSavedInstanceChatLink(i)
- info.Locked = locked
- info.Extended = extended
- instance[thisToon][diff] = info
- end
- end
- local weeklyreset = addon:GetNextWeeklyResetTime()
- for id,_ in pairs(addon.LFRInstances) do
- local numEncounters, numCompleted = GetLFGDungeonNumEncounters(id);
- if ( numCompleted and numCompleted > 0 and weeklyreset ) then
- local truename, instance = addon:LookupInstance(id, nil, true)
- instance[thisToon] = instance[thisToon] or temp[truename] or { }
- local info = instance[thisToon][2] or {}
- instance[thisToon][2] = info
- if not (info.Expires and info.Expires < (time() + 300)) then -- ticket 109: don't refresh expiration close to reset
- wipe(info)
- info.Expires = weeklyreset
- end
- info.ID = -1*numEncounters
- for i=1, numEncounters do
- local bossName, texture, isKilled = GetLFGDungeonEncounterInfo(id, i);
- info[i] = isKilled
- end
- end
- end
- local questcomplete = GetQuestsCompleted(localarr("QuestCompleteTemp"))
- local wbsave = localarr("wbsave")
- if GetNumSavedWorldBosses and GetSavedWorldBossInfo then -- 5.4
- for i=1,GetNumSavedWorldBosses() do
- local name, id, reset = GetSavedWorldBossInfo(i)
- wbsave[name] = true
- end
- end
- for _,einfo in pairs(addon.WorldBosses) do
- if weeklyreset and (
- (einfo.quest and IsQuestFlaggedCompleted(einfo.quest)) or
- (questcomplete and einfo.quest and questcomplete[einfo.quest]) or
- wbsave[einfo.savename or einfo.name]
- ) then
- local truename = einfo.name
- local instance = vars.db.Instances[truename]
- instance[thisToon] = instance[thisToon] or temp[truename] or { }
- local info = instance[thisToon][2] or {}
- wipe(info)
- instance[thisToon][2] = info
- info.Expires = weeklyreset
- info.ID = -1
- info[1] = true
- end
- end
- core:QuestRefresh(recoverdaily, questcomplete, nextreset, weeklyreset)
- local icnt, dcnt = 0,0
- for name, _ in pairs(temp) do
- if vars.db.Instances[name][thisToon] then
- for diff,info in pairs(vars.db.Instances[name][thisToon]) do
- if not info.ID then
- vars.db.Instances[name][thisToon][diff] = nil
- dcnt = dcnt + 1
- end
- end
- else
- icnt = icnt + 1
- end
- end
- --debug("Refresh temp reaped "..icnt.." instances and "..dcnt.." diffs")
- wipe(temp)
- addon:UpdateToonData()
- end
- local function UpdateTooltip(self,elap)
- if not tooltip or not tooltip:IsShown() then return end
- if addon.firstupdate then
- -- ticket 155: fix QTip backdrop which somehow gets corrupted sometimes, no idea why
- tooltip:SetBackdrop(GameTooltip:GetBackdrop())
- tooltip:SetBackdropColor(GameTooltip:GetBackdropColor());
- tooltip:SetBackdropBorderColor(GameTooltip:GetBackdropBorderColor())
- addon.firstupdate = false
- -- easy BugFix by q3fuba
- addon:SkinFrame(tooltip, tooltip:GetName())
- end
- addon.updatetooltip_throttle = (addon.updatetooltip_throttle or 10) + elap
- if addon.updatetooltip_throttle < 0.5 then return end
- addon.updatetooltip_throttle = 0
- if tooltip.anchorframe then
- core:ShowTooltip(tooltip.anchorframe)
- end
- end
- -- sorted traversal function for character table
- local cpairs
- do
- local cnext_list = {}
- local cnext_pos
- local cnext_ekey
- local function cnext(t,i)
- local e = cnext_list[cnext_pos]
- if not e then
- return nil
- else
- cnext_pos = cnext_pos + 1
- local n = e[cnext_ekey]
- return n, t[n]
- end
- end
- local function cpairs_sort(a,b)
- -- generic multi-key sort
- for k,av in ipairs(a) do
- local bv = b[k]
- if av ~= bv then
- return av < bv
- end
- end
- return false -- required for sort stability when a==a
- end
- cpairs = function(t, usecache)
- local settings = db.Tooltip
- local realmgroup_key
- local realmgroup_min
- if not usecache then
- local thisrealm = GetRealmName()
- if settings.ConnectedRealms ~= "ignore" then
- local group = core:getRealmGroup(thisrealm)
- thisrealm = group or thisrealm
- end
- wipe(cnext_list)
- cnext_pos = 1
- for n,_ in pairs(t) do
- local t = vars.db.Toons[n]
- local tn, tr = n:match('^(.*) [-] (.*)$')
- if t and
- (t.Show ~= "never" or (n == thisToon and settings.SelfAlways)) and
- (not settings.ServerOnly
- or thisrealm == tr
- or thisrealm == core:getRealmGroup(tr))
- then
- local e = {}
- cnext_ekey = 1
- if settings.SelfFirst then
- if n == thisToon then
- e[cnext_ekey] = 1
- else
- e[cnext_ekey] = 2
- end
- cnext_ekey = cnext_ekey + 1
- end
- if settings.ServerSort then
- if settings.ConnectedRealms == "ignore" then
- e[cnext_ekey] = tr
- cnext_ekey = cnext_ekey + 1
- else
- local rgroup = core:getRealmGroup(tr)
- if rgroup then -- connected realm
- realmgroup_min = realmgroup_min or {}
- if not realmgroup_min[rgroup] or tr < realmgroup_min[rgroup] then
- realmgroup_min[rgroup] = tr -- lowest active realm in group
- end
- else
- rgroup = tr
- end
- realmgroup_key = cnext_ekey
- e[cnext_ekey] = rgroup
- cnext_ekey = cnext_ekey + 1
- if settings.ConnectedRealms == "group" then
- e[cnext_ekey] = tr
- cnext_ekey = cnext_ekey + 1
- end
- end
- end
- e[cnext_ekey] = t.Order
- cnext_ekey = cnext_ekey + 1
- e[cnext_ekey] = n
- cnext_list[cnext_pos] = e
- cnext_pos = cnext_pos + 1
- end
- end
- if realmgroup_key then -- second pass, convert group id to min name
- for _,e in ipairs(cnext_list) do
- local id = e[realmgroup_key]
- if type(id) == "number" then
- e[realmgroup_key] = realmgroup_min[id]
- end
- end
- end
- table.sort(cnext_list, cpairs_sort)
- --myprint(cnext_list)
- end
- cnext_pos = 1
- return cnext, t, nil
- end
- end
- function addon:IsDetached()
- return addon.detachframe and addon.detachframe:IsShown()
- end
- function addon:HideDetached()
- addon.detachframe:Hide()
- end
- function addon:ToggleDetached()
- if addon:IsDetached() then
- addon:HideDetached()
- else
- addon:ShowDetached()
- end
- end
- function addon:ShowDetached()
- if not addon.detachframe then
- local f = CreateFrame("Frame","SavedInstancesDetachHeader",UIParent,"BasicFrameTemplate")
- f:SetMovable(true)
- f:SetFrameStrata("TOOLTIP")
- f:SetFrameLevel(100) -- prevent weird interlacings with other tooltips
- f:SetClampedToScreen(true)
- f:EnableMouse(true)
- f:SetUserPlaced(true)
- f:SetAlpha(0.5)
- if vars.db.Tooltip.posx and vars.db.Tooltip.posy then
- f:SetPoint("TOPLEFT",vars.db.Tooltip.posx,-vars.db.Tooltip.posy)
- else
- f:SetPoint("CENTER")
- end
- f:SetScript("OnMouseDown", function() f:StartMoving() end)
- f:SetScript("OnMouseUp", function() f:StopMovingOrSizing()
- vars.db.Tooltip.posx = f:GetLeft()
- vars.db.Tooltip.posy = UIParent:GetTop() - (f:GetTop()*f:GetScale())
- end)
- f:SetScript("OnHide", function() if tooltip then QTip:Release(tooltip); tooltip = nil end end )
- f:SetScript("OnUpdate", function(self)
- if not tooltip then f:Hide(); return end
- local w,h = tooltip:GetSize()
- self:SetSize(w*tooltip:GetScale(),(h+20)*tooltip:GetScale())
- --[[
- tooltip:ClearAllPoints()
- tooltip:SetPoint("BOTTOMLEFT",addon.detachframe)
- tooltip:SetFrameLevel(addon.detachframe:GetFrameLevel()+1)
- tooltip:Show()
- --]]
- end)
- f:SetScript("OnKeyDown", function(self,key)
- if key == "ESCAPE" then
- f:SetPropagateKeyboardInput(false)
- f:Hide();
- end
- end)
- f:EnableKeyboard(true)
- addon.detachframe = f
- end
- local f = addon.detachframe
- f:Show()
- addon:SkinFrame(f,f:GetName())
- f:SetPropagateKeyboardInput(true)
- if tooltip then tooltip:Hide() end
- core:ShowTooltip(f)
- end
- -----------------------------------------------------------------------------------------------
- -- tooltip event handlers
- local function OpenLFD(self, instanceid, button)
- if LFDParentFrame and LFDParentFrame:IsVisible() and LFDQueueFrame.type ~= instanceid then
- -- changing entries
- else
- ToggleLFDParentFrame()
- end
- if LFDParentFrame and LFDParentFrame:IsVisible() and LFDQueueFrame_SetType then
- LFDQueueFrame_SetType(instanceid)
- end
- end
- local function OpenLFR(self, instanceid, button)
- if RaidFinderFrame and RaidFinderFrame:IsVisible() and RaidFinderQueueFrame.raid ~= instanceid then
- -- changing entries
- else
- PVEFrame_ToggleFrame("GroupFinderFrame", RaidFinderFrame)
- end
- if RaidFinderFrame and RaidFinderFrame:IsVisible() and RaidFinderQueueFrame_SetRaid then
- RaidFinderQueueFrame_SetRaid(instanceid)
- end
- end
- local function OpenLFS(self, instanceid, button)
- if ScenarioFinderFrame and ScenarioFinderFrame:IsVisible() and ScenarioQueueFrame.type ~= instanceid then
- -- changing entries
- else
- PVEFrame_ToggleFrame("GroupFinderFrame", ScenarioFinderFrame)
- end
- if ScenarioFinderFrame and ScenarioFinderFrame:IsVisible() and ScenarioQueueFrame_SetType then
- ScenarioQueueFrame_SetType(instanceid)
- end
- end
- local function OpenCurrency(self, _, button)
- ToggleCharacter("TokenFrame")
- end
- local function ChatLink(self, link, button)
- if not link then return end
- if ChatEdit_GetActiveWindow() then
- ChatEdit_InsertLink(link)
- else
- ChatFrame_OpenChat(link, DEFAULT_CHAT_FRAME)
- end
- end
- local function CloseTooltips()
- GameTooltip:Hide()
- if indicatortip then
- indicatortip:Hide()
- end
- end
- local function DoNothing() end
- -----------------------------------------------------------------------------------------------
- local function ShowAll()
- return (IsAltKeyDown() and true) or false
- end
- local columnCache = { [true] = {}, [false] = {} }
- local function addColumns(columns, toon, tooltip)
- for c = 1, maxcol do
- columns[toon..c] = columns[toon..c] or tooltip:AddColumn("CENTER")
- end
- columnCache[ShowAll()][toon] = true
- end
- addon.scaleCache = {}
- function core:HeaderFont()
- if not addon.headerfont then
- local temp = QTip:Acquire("SavedInstancesHeaderTooltip", 1, "LEFT")
- addon.headerfont = CreateFont("SavedInstancedTooltipHeaderFont")
- local hFont = temp:GetHeaderFont()
- local hFontPath, hFontSize,_ hFontPath, hFontSize, _ = hFont:GetFont()
- addon.headerfont:SetFont(hFontPath, hFontSize, "OUTLINE")
- QTip:Release(temp)
- end
- return addon.headerfont
- end
- function core:ShowTooltip(anchorframe)
- local showall = ShowAll()
- if tooltip and tooltip:IsShown() and
- core.showall == showall and
- core.scale == (addon.scaleCache[showall] or vars.db.Tooltip.Scale)
- then
- return -- skip update
- end
- local starttime = debugprofilestop()
- core.showall = showall
- local showexpired = showall or vars.db.Tooltip.ShowExpired
- if tooltip then QTip:Release(tooltip) end
- tooltip = QTip:Acquire("SavedInstancesTooltip", 1, "LEFT")
- tooltip:SetCellMarginH(0)
- tooltip.anchorframe = anchorframe
- tooltip:SetScript("OnUpdate", UpdateTooltip)
- addon.firstupdate = true
- tooltip:Clear()
- core.scale = addon.scaleCache[showall] or vars.db.Tooltip.Scale
- tooltip:SetScale(core.scale)
- tooltip:SetHeaderFont(core:HeaderFont())
- addon:HistoryUpdate()
- local headText
- if addon.histLiveCount and addon.histLiveCount > 0 then
- headText = string.format("%s%s (%d/%s)%s",GOLDFONT,addonName,addon.histLiveCount,(addon.histOldest or "?"),FONTEND)
- else
- headText = string.format("%s%s%s",GOLDFONT,addonName,FONTEND)
- end
- local headLine = tooltip:AddHeader(headText)
- tooltip:SetCellScript(headLine, 1, "OnEnter", ShowAccountSummary )
- tooltip:SetCellScript(headLine, 1, "OnLeave", CloseTooltips)
- addon:UpdateToonData()
- local columns = localarr("columns")
- for toon,_ in cpairs(columnCache[showall]) do
- addColumns(columns, toon, tooltip)
- columnCache[showall][toon] = false
- end
- -- allocating columns for characters
- for toon, t in cpairs(vars.db.Toons) do
- if vars.db.Toons[toon].Show == "always" or
- (toon == thisToon and vars.db.Tooltip.SelfAlways) then
- addColumns(columns, toon, tooltip)
- end
- end
- -- determining how many instances will be displayed per category
- local categoryshown = localarr("categoryshown") -- remember if each category will be shown
- local instancesaved = localarr("instancesaved") -- remember if each instance has been saved or not (boolean)
- local wbcons = vars.db.Tooltip.CombineWorldBosses
- local worldbosses = wbcons and localarr("worldbosses")
- local wbalways = false
- local lfrcons = vars.db.Tooltip.CombineLFR
- local lfrbox = lfrcons and localarr("lfrbox")
- local lfrmap = lfrcons and localarr("lfrmap")
- for _, category in ipairs(addon:OrderedCategories()) do
- for _, instance in ipairs(addon:OrderedInstances(category)) do
- local inst = vars.db.Instances[instance]
- if inst.Show == "always" then
- categoryshown[category] = true
- end
- if inst.Show ~= "never" then
- if wbcons and inst.WorldBoss and inst.Expansion <= GetExpansionLevel() then
- if vars.db.Tooltip.ReverseInstances then
- table.insert(worldbosses, instance)
- else
- table.insert(worldbosses, 1, instance)
- end
- wbalways = wbalways or (inst.Show == "always")
- end
- local lfrinfo = lfrcons and inst.LFDID and addon.LFRInstances[inst.LFDID]
- local lfrboxid
- if lfrinfo then
- lfrboxid = lfrinfo.parent
- lfrmap[inst.LFDID] = instance
- if inst.Show == "always" then
- lfrbox[lfrboxid] = true
- end
- end
- for toon, t in cpairs(vars.db.Toons, true) do
- for diff = 1, maxdiff do
- if inst[toon] and inst[toon][diff] then
- if (inst[toon][diff].Expires > 0) then
- if lfrinfo then
- lfrbox[lfrboxid] = true
- instancesaved[lfrboxid] = true
- elseif wbcons and inst.WorldBoss then
- instancesaved[L["World Bosses"]] = true
- else
- instancesaved[instance] = true
- end
- categoryshown[category] = true
- elseif showall then
- categoryshown[category] = true
- end
- end
- end
- end
- end
- end
- end
- local categories = 0
- -- determining how many categories have instances that will be shown
- if vars.db.Tooltip.ShowCategories then
- for category, _ in pairs(categoryshown) do
- categories = categories + 1
- end
- end
- -- allocating tooltip space for instances, categories, and space between categories
- local categoryrow = localarr("categoryrow") -- remember where each category heading goes
- local instancerow = localarr("instancerow") -- remember where each instance goes
- local blankrow = localarr("blankrow") -- track blank lines
- local firstcategory = true -- use this to skip spacing before the first category
- local function addsep()
- local line = tooltip:AddSeparator(6,0,0,0,0)
- blankrow[line] = true
- return line
- end
- for _, category in ipairs(addon:OrderedCategories()) do
- if categoryshown[category] then
- if not firstcategory and vars.db.Tooltip.CategorySpaces then
- addsep()
- end
- if (categories > 1 or vars.db.Tooltip.ShowSoloCategory) and categoryshown[category] then
- local line = tooltip:AddLine()
- categoryrow[category] = line
- blankrow[line] = true
- end
- for _, instance in ipairs(addon:OrderedInstances(category)) do
- local inst = vars.db.Instances[instance]
- if not (wbcons and inst.WorldBoss) and
- not (lfrcons and addon.LFRInstances[inst.LFDID]) then
- if inst.Show == "always" then
- instancerow[instance] = instancerow[instance] or tooltip:AddLine()
- end
- if inst.Show ~= "never" then
- for toon, t in cpairs(vars.db.Toons, true) do
- for diff = 1, maxdiff do
- if inst[toon] and inst[toon][diff] and (inst[toon][diff].Expires > 0 or showexpired) then
- instancerow[instance] = instancerow[instance] or tooltip:AddLine()
- addColumns(columns, toon, tooltip)
- end
- end
- end
- end
- end
- if lfrcons and inst.LFDID then
- -- check if this parent instance has corresponding lfrboxes, and create them
- if lfrbox[inst.LFDID] then
- lfrbox[L["LFR"]..": "..instance] = tooltip:AddLine()
- end
- lfrbox[inst.LFDID] = nil
- end
- end
- firstcategory = false
- end
- end
- -- now printing instance data
- for instance, row in pairs(instancerow) do
- local inst = vars.db.Instances[instance]
- tooltip:SetCell(row, 1, (instancesaved[instance] and GOLDFONT or GRAYFONT) .. instance .. FONTEND)
- if addon.LFRInstances[inst.LFDID] then
- tooltip:SetLineScript(row, "OnMouseDown", OpenLFR, inst.LFDID)
- end
- for toon, t in cpairs(vars.db.Toons, true) do
- if inst[toon] then
- local showcol = localarr("showcol")
- local showcnt = 0
- for diff = 1, maxdiff do
- if inst[toon][diff] and (inst[toon][diff].Expires > 0 or showexpired) then
- showcnt = showcnt + 1
- showcol[diff] = true
- end
- end
- local base = 1
- local span = maxcol
- if showcnt > 1 then
- span = 1
- end
- if showcnt > maxcol then
- bugReport("Column overflow! showcnt="..showcnt)
- end
- for diff = 1, maxdiff do
- if showcol[diff] then
- local col = columns[toon..base]
- tooltip:SetCell(row, col,
- DifficultyString(instance, diff, toon, inst[toon][diff].Expires == 0), span)
- tooltip:SetCellScript(row, col, "OnEnter", ShowIndicatorTooltip, {instance, toon, diff})
- tooltip:SetCellScript(row, col, "OnLeave", CloseTooltips)
- if addon.LFRInstances[inst.LFDID] then
- tooltip:SetCellScript(row, col, "OnMouseDown", OpenLFR, inst.LFDID)
- else
- local link = inst[toon][diff].Link
- if link then
- tooltip:SetCellScript(row, col, "OnMouseDown", ChatLink, link)
- end
- end
- base = base + 1
- elseif columns[toon..diff] and showcnt > 1 then
- tooltip:SetCell(row, columns[toon..diff], "")
- end
- end
- end
- end
- end
- -- combined LFRs
- if lfrcons then
- for boxname, line in pairs(lfrbox) do
- if type(boxname) == "number" then
- bugReport("Unrecognized LFR instance parent id= "..boxname)
- lfrbox[boxname] = nil
- end
- end
- for boxname, line in pairs(lfrbox) do
- local boxtype, pinstance = boxname:match("^([^:]+): (.+)$")
- local pinst = vars.db.Instances[pinstance]
- local boxid = pinst.LFDID
- local firstid
- local total = 0
- for lfdid, lfrinfo in pairs(addon.LFRInstances) do
- if lfrinfo.parent == pinst.LFDID and lfrmap[lfdid] then
- firstid = math.min(lfdid, firstid or lfdid)
- total = total + lfrinfo.total
- lfrmap[boxname..":"..lfrinfo.base] = lfrmap[lfdid]
- end
- end
- tooltip:SetCell(line, 1, (instancesaved[boxid] and GOLDFONT or GRAYFONT) .. boxname .. FONTEND)
- tooltip:SetLineScript(line, "OnMouseDown", OpenLFR, firstid)
- for toon, t in cpairs(vars.db.Toons, true) do
- local saved = 0
- local diff = 2
- for key, instance in pairs(lfrmap) do
- if string.match(key,boxname..":%d+$") then
- saved = saved + addon:instanceBosses(instance, toon, diff)
- end
- end
- if saved > 0 then
- addColumns(columns, toon, tooltip)
- local col = columns[toon..1]
- tooltip:SetCell(line, col, DifficultyString(pinstance, diff, toon, false, saved, total),4)
- tooltip:SetCellScript(line, col, "OnEnter", ShowLFRTooltip, {boxname, toon, lfrmap})
- tooltip:SetCellScript(line, col, "OnLeave", CloseTooltips)
- end
- end
- end
- end
- -- combined world bosses
- if wbcons and next(worldbosses) and (wbalways or instancesaved[L["World Bosses"]]) then
- if not firstcategory and vars.db.Tooltip.CategorySpaces then
- addsep()
- end
- local line = tooltip:AddLine((instancesaved[L["World Bosses"]] and YELLOWFONT or GRAYFONT) .. L["World Bosses"] .. FONTEND)
- for toon, t in cpairs(vars.db.Toons, true) do
- local saved = 0
- local diff = 2
- for _, instance in ipairs(worldbosses) do
- local inst = vars.db.Instances[instance]
- if inst[toon] and inst[toon][diff] and inst[toon][diff].Expires > 0 then
- saved = saved + 1
- end
- end
- if saved > 0 then
- addColumns(columns, toon, tooltip)
- local col = columns[toon..1]
- tooltip:SetCell(line, col, DifficultyString(worldbosses[1], diff, toon, false, saved, #worldbosses),4)
- tooltip:SetCellScript(line, col, "OnEnter", ShowWorldBossTooltip, {worldbosses, toon, saved})
- tooltip:SetCellScript(line, col, "OnLeave", CloseTooltips)
- end
- end
- end
- local holidayinst = localarr("holidayinst")
- local firstlfd = true
- for instance, info in pairs(vars.db.Instances) do
- if showall or
- (info.Holiday and vars.db.Tooltip.ShowHoliday) or
- (info.Random and vars.db.Tooltip.ShowRandom) then
- for toon, t in cpairs(vars.db.Toons, true) do
- local d = info[toon] and info[toon][1]
- if d then
- addColumns(columns, toon, tooltip)
- local row = holidayinst[instance]
- if not row then
- if not firstcategory and vars.db.Tooltip.CategorySpaces and firstlfd then
- addsep()
- firstlfd = false
- end
- row = tooltip:AddLine(YELLOWFONT .. abbreviate(instance) .. FONTEND)
- holidayinst[instance] = row
- end
- local tstr = SecondsToTime(d.Expires - time(), false, false, 1)
- tooltip:SetCell(row, columns[toon..1], ClassColorise(t.Class,tstr), "CENTER",maxcol)
- if info.Scenario then
- tooltip:SetLineScript(row, "OnMouseDown", OpenLFS, info.LFDID)
- else
- tooltip:SetLineScript(row, "OnMouseDown", OpenLFD, info.LFDID)
- end
- end
- end
- end
- end
- -- random dungeon
- if vars.db.Tooltip.TrackLFG or showall then
- local cd1,cd2 = false,false
- for toon, t in cpairs(vars.db.Toons, true) do
- cd2 = cd2 or t.LFG2
- cd1 = cd1 or (t.LFG1 and (not t.LFG2 or showall))
- if t.LFG1 or t.LFG2 then
- addColumns(columns, toon, tooltip)
- end
- end
- local randomLine
- if cd1 or cd2 then
- if not firstcategory and vars.db.Tooltip.CategorySpaces and firstlfd then
- addsep()
- firstlfd = false
- end
- local cooldown = ITEM_COOLDOWN_TOTAL:gsub("%%s",""):gsub("%p","")
- cd1 = cd1 and tooltip:AddLine(YELLOWFONT .. LFG_TYPE_RANDOM_DUNGEON..cooldown .. FONTEND)
- cd2 = cd2 and tooltip:AddLine(YELLOWFONT .. GetSpellInfo(71041) .. FONTEND)
- end
- for toon, t in cpairs(vars.db.Toons, true) do
- local d1 = (t.LFG1 and t.LFG1 - time()) or -1
- local d2 = (t.LFG2 and t.LFG2 - time()) or -1
- if d1 > 0 and (d2 < 0 or showall) then
- local col = columns[toon..1]
- local tstr = SecondsToTime(d1, false, false, 1)
- tooltip:SetCell(cd1, col, ClassColorise(t.Class,tstr), "CENTER",maxcol)
- tooltip:SetCellScript(cd1, col, "OnEnter", ShowSpellIDTooltip, {toon,-1,tstr})
- tooltip:SetCellScript(cd1, col, "OnLeave", CloseTooltips)
- end
- if d2 > 0 then
- local col = columns[toon..1]
- local tstr = SecondsToTime(d2, false, false, 1)
- tooltip:SetCell(cd2, col, ClassColorise(t.Class,tstr), "CENTER",maxcol)
- tooltip:SetCellScript(cd2, col, "OnEnter", ShowSpellIDTooltip, {toon,71041,tstr})
- tooltip:SetCellScript(cd2, col, "OnLeave", CloseTooltips)
- end
- end
- end
- if vars.db.Tooltip.TrackDeserter or showall then
- local show = false
- for toon, t in cpairs(vars.db.Toons, true) do
- if t.pvpdesert then
- show = true
- addColumns(columns, toon, tooltip)
- end
- end
- if show then
- if not firstcategory and vars.db.Tooltip.CategorySpaces and firstlfd then
- addsep()
- firstlfd = false
- end
- show = tooltip:AddLine(YELLOWFONT .. DESERTER .. FONTEND)
- end
- for toon, t in cpairs(vars.db.Toons, true) do
- if t.pvpdesert and time() < t.pvpdesert then
- local col = columns[toon..1]
- local tstr = SecondsToTime(t.pvpdesert - time(), false, false, 1)
- tooltip:SetCell(show, col, ClassColorise(t.Class,tstr), "CENTER",maxcol)
- tooltip:SetCellScript(show, col, "OnEnter", ShowSpellIDTooltip, {toon,26013,tstr})
- tooltip:SetCellScript(show, col, "OnLeave", CloseTooltips)
- end
- end
- end
- do
- local showd, showw
- for toon, t in cpairs(vars.db.Toons, true) do
- local dc, wc = addon:QuestCount(toon)
- if dc > 0 and (vars.db.Tooltip.TrackDailyQuests or showall) then
- showd = true
- addColumns(columns, toon, tooltip)
- end
- if wc > 0 and (vars.db.Tooltip.TrackWeeklyQuests or showall) then
- showw = true
- addColumns(columns, toon, tooltip)
- end
- end
- local adc, awc = addon:QuestCount(nil)
- if adc > 0 and (vars.db.Tooltip.TrackDailyQuests or showall) then showd = true end
- if awc > 0 and (vars.db.Tooltip.TrackWeeklyQuests or showall) then showw = true end
- if not firstcategory and vars.db.Tooltip.CategorySpaces and (showd or showw) then
- addsep()
- end
- if showd then
- showd = tooltip:AddLine(YELLOWFONT .. L["Daily Quests"] .. (adc > 0 and " ("..adc..")" or "") .. FONTEND)
- if adc > 0 then
- tooltip:SetCellScript(showd, 1, "OnEnter", ShowQuestTooltip, {nil,adc,true})
- tooltip:SetCellScript(showd, 1, "OnLeave", CloseTooltips)
- end
- end
- if showw then
- showw = tooltip:AddLine(YELLOWFONT .. L["Weekly Quests"] .. (awc > 0 and " ("..awc..")" or "") .. FONTEND)
- if awc > 0 then
- tooltip:SetCellScript(showw, 1, "OnEnter", ShowQuestTooltip, {nil,awc,false})
- tooltip:SetCellScript(showw, 1, "OnLeave", CloseTooltips)
- end
- end
- for toon, t in cpairs(vars.db.Toons, true) do
- local dc, wc = addon:QuestCount(toon)
- local col = columns[toon..1]
- if showd and col and dc > 0 then
- tooltip:SetCell(showd, col, ClassColorise(t.Class,dc), "CENTER",maxcol)
- tooltip:SetCellScript(showd, col, "OnEnter", ShowQuestTooltip, {toon,dc,true})
- tooltip:SetCellScript(showd, col, "OnLeave", CloseTooltips)
- end
- if showw and col and wc > 0 then
- tooltip:SetCell(showw, col, ClassColorise(t.Class,wc), "CENTER",maxcol)
- tooltip:SetCellScript(showw, col, "OnEnter", ShowQuestTooltip, {toon,wc,false})
- tooltip:SetCellScript(showw, col, "OnLeave", CloseTooltips)
- end
- end
- end
- if vars.db.Tooltip.TrackSkills or showall then
- local show = false
- for toon, t in cpairs(vars.db.Toons, true) do
- if t.Skills and next(t.Skills) then
- show = true
- addColumns(columns, toon, tooltip)
- end
- end
- if show then
- if not firstcategory and vars.db.Tooltip.CategorySpaces then
- addsep()
- end
- show = tooltip:AddLine(YELLOWFONT .. L["Trade Skill Cooldowns"] .. FONTEND)
- end
- for toon, t in cpairs(vars.db.Toons, true) do
- local cnt = 0
- if t.Skills then
- for _ in pairs(t.Skills) do cnt = cnt + 1 end
- end
- if cnt > 0 then
- local col = columns[toon..1]
- tooltip:SetCell(show, col, ClassColorise(t.Class,cnt), "CENTER",maxcol)
- tooltip:SetCellScript(show, col, "OnEnter", ShowSkillTooltip, {toon, cnt})
- tooltip:SetCellScript(show, col, "OnLeave", CloseTooltips)
- end
- end
- end
- if vars.db.Tooltip.TrackFarm or showall then
- local toonfarm = localarr("toonfarm")
- local show
- for toon, t in cpairs(vars.db.Toons, true) do
- if (t.FarmPlanted or 0) > 0 or (t.FarmHarvested or 0) > 0 or
- (t.FarmCropReady and next(t.FarmCropReady)) then
- toonfarm[toon] = (t.FarmHarvested or 0).."/"..(t.FarmPlanted or 0)
- show = true
- addColumns(columns, toon, tooltip)
- end
- end
- if show then
- if not firstcategory and vars.db.Tooltip.CategorySpaces then
- addsep()
- end
- show = tooltip:AddLine(YELLOWFONT .. L["Farm Crops"] .. FONTEND)
- end
- for toon, t in cpairs(vars.db.Toons, true) do
- if toonfarm[toon] then
- local col = columns[toon..1]
- tooltip:SetCell(show, col, ClassColorise(t.Class,toonfarm[toon]), "CENTER",maxcol)
- tooltip:SetCellScript(show, col, "OnEnter", ShowFarmTooltip, toon)
- tooltip:SetCellScript(show, col, "OnLeave", CloseTooltips)
- end
- end
- end
- if vars.db.Tooltip.TrackBonus or showall then
- local show
- local toonbonus = localarr("toonbonus")
- for toon, t in cpairs(vars.db.Toons, true) do
- if t.BonusRoll and t.BonusRoll[1] then
- local gold = 0
- for _,roll in ipairs(t.BonusRoll) do
- if roll.money then
- gold = gold + 1
- else
- break
- end
- end
- toonbonus[toon] = gold
- show = true
- addColumns(columns, toon, tooltip)
- end
- end
- if show then
- if not firstcategory and vars.db.Tooltip.CategorySpaces then
- addsep()
- end
- show = tooltip:AddLine(YELLOWFONT .. L["Roll Bonus"] .. FONTEND)
- end
- for toon, t in cpairs(vars.db.Toons, true) do
- if toonbonus[toon] then
- local col = columns[toon..1]
- local str = toonbonus[toon]
- if str > 0 then str = "+"..str end
- tooltip:SetCell(show, col, ClassColorise(t.Class,str), "CENTER",maxcol)
- tooltip:SetCellScript(show, col, "OnEnter", ShowBonusTooltip, toon)
- tooltip:SetCellScript(show, col, "OnLeave", CloseTooltips)
- end
- end
- end
- local firstcurrency = true
- for _,idx in ipairs(currency) do
- local setting = vars.db.Tooltip["Currency"..idx]
- if setting or showall then
- local show
- for toon, t in cpairs(vars.db.Toons, true) do
- -- ci.name, ci.amount, ci.earnedThisWeek, ci.weeklyMax, ci.totalMax
- local ci = t.currency and t.currency[idx]
- local gotsome
- if ci then
- gotsome = ((ci.earnedThisWeek or 0) > 0 and (ci.weeklyMax or 0) > 0) or
- ((ci.amount or 0) > 0 and showall)
- -- or ((ci.amount or 0) > 0 and ci.weeklyMax == 0 and t.Level == maxlvl)
- end
- if ci and gotsome then
- addColumns(columns, toon, tooltip)
- end
- if ci and (gotsome or (ci.amount or 0) > 0) and columns[toon..1] then
- local name,_,tex = GetCurrencyInfo(idx)
- show = string.format(" \124T%s:0\124t%s",tex,name)
- end
- end
- local currLine
- if show then
- if not firstcategory and vars.db.Tooltip.CategorySpaces and firstcurrency then
- addsep()
- firstcurrency = false
- end
- currLine = tooltip:AddLine(YELLOWFONT .. show .. FONTEND)
- tooltip:SetLineScript(currLine, "OnMouseDown", OpenCurrency)
- tooltip:SetCellScript(currLine, 1, "OnEnter", ShowCurrencySummary, idx)
- tooltip:SetCellScript(currLine, 1, "OnLeave", CloseTooltips)
- tooltip:SetCellScript(currLine, 1, "OnMouseDown", OpenCurrency)
- for toon, t in cpairs(vars.db.Toons, true) do
- local ci = t.currency and t.currency[idx]
- local col = columns[toon..1]
- if ci and col then
- local earned, weeklymax, totalmax = "","",""
- if vars.db.Tooltip.CurrencyMax then
- if (ci.weeklyMax or 0) > 0 then
- weeklymax = "/"..addon:formatNumber(ci.weeklyMax)
- end
- if (ci.totalMax or 0) > 0 then
- totalmax = "/"..addon:formatNumber(ci.totalMax)
- end
- end
- if vars.db.Tooltip.CurrencyEarned or showall then
- earned = CurrencyColor(ci.amount,ci.totalMax)..totalmax
- end
- local str
- if (ci.amount or 0) > 0 or (ci.earnedThisWeek or 0) > 0 then
- if (ci.weeklyMax or 0) > 0 then
- str = earned.." ("..CurrencyColor(ci.earnedThisWeek,ci.weeklyMax)..weeklymax..")"
- elseif (ci.amount or 0) > 0 then
- str = CurrencyColor(ci.amount,ci.totalMax)..totalmax
- end
- end
- if str then
- if not vars.db.Tooltip.CurrencyValueColor then
- str = ClassColorise(t.Class,str)
- end
- tooltip:SetCell(currLine, col, str, "CENTER",maxcol)
- tooltip:SetCellScript(currLine, col, "OnEnter", ShowCurrencyTooltip, {toon, idx, ci})
- tooltip:SetCellScript(currLine, col, "OnLeave", CloseTooltips)
- tooltip:SetCellScript(currLine, col, "OnMouseDown", OpenCurrency)
- end
- end
- end
- end
- end
- end
- -- toon names
- for toondiff, col in pairs(columns) do
- local toon = strsub(toondiff, 1, #toondiff-1)
- local diff = strsub(toondiff, #toondiff, #toondiff)
- if diff == "1" then
- local toonname, toonserver = toon:match('^(.*) [-] (.*)$')
- local toonstr = toonname
- if db.Tooltip.ShowServer then
- toonstr = toonstr .. "\n" .. toonserver
- end
- tooltip:SetCell(headLine, col, ClassColorise(vars.db.Toons[toon].Class, toonstr),
- tooltip:GetHeaderFont(), "CENTER", maxcol)
- tooltip:SetCellScript(headLine, col, "OnEnter", ShowToonTooltip, toon)
- tooltip:SetCellScript(headLine, col, "OnLeave", CloseTooltips)
- --[[
- tooltip:SetCellScript(headLine, col, "OnEnter", function()
- for i=0,3 do
- tooltip:SetColumnColor(col+i,0.5,0.5,0.5)
- end
- end)
- tooltip:SetCellScript(headLine, col, "OnLeave", function()
- for i=0,3 do
- tooltip:SetColumnColor(col,0,0,0)
- end
- end)
- --]]
- end
- end
- -- we now know enough to put in the category names where necessary
- if vars.db.Tooltip.ShowCategories then
- for category, row in pairs(categoryrow) do
- if (categories > 1 or vars.db.Tooltip.ShowSoloCategory) and categoryshown[category] then
- tooltip:SetCell(row, 1, YELLOWFONT .. vars.Categories[category] .. FONTEND, "LEFT", tooltip:GetColumnCount())
- end
- end
- end
- local hi = true
- for i=2,tooltip:GetLineCount() do -- row highlighting
- tooltip:SetLineScript(i, "OnEnter", DoNothing)
- tooltip:SetLineScript(i, "OnLeave", DoNothing)
- if hi and not blankrow[i] then
- tooltip:SetLineColor(i, 1,1,1, db.Tooltip.RowHighlight)
- hi = false
- else
- tooltip:SetLineColor(i, 0,0,0, 0)
- hi = true
- end
- end
- -- finishing up, with hints
- if TableLen(instancerow) == 0 then
- local noneLine = tooltip:AddLine()
- tooltip:SetCell(noneLine, 1, GRAYFONT .. NO_RAID_INSTANCES_SAVED .. FONTEND, "LEFT", tooltip:GetColumnCount())
- end
- if vars.db.Tooltip.ShowHints then
- tooltip:AddSeparator(8,0,0,0,0)
- local hintLine, hintCol
- if not addon:IsDetached() then
- hintLine, hintCol = tooltip:AddLine()
- tooltip:SetCell(hintLine, hintCol, L["|cffffff00Left-click|r to detach tooltip"], "LEFT", tooltip:GetColumnCount())
- hintLine, hintCol = tooltip:AddLine()
- tooltip:SetCell(hintLine, hintCol, L["|cffffff00Middle-click|r to show Blizzard's Raid Information"], "LEFT", tooltip:GetColumnCount())
- hintLine, hintCol = tooltip:AddLine()
- tooltip:SetCell(hintLine, hintCol, L["|cffffff00Right-click|r to configure SavedInstances"], "LEFT", tooltip:GetColumnCount())
- end
- hintLine, hintCol = tooltip:AddLine()
- tooltip:SetCell(hintLine, hintCol, L["Hover mouse on indicator for details"], "LEFT", tooltip:GetColumnCount())
- if not showall then
- hintLine, hintCol = tooltip:AddLine()
- tooltip:SetCell(hintLine, hintCol, L["Hold Alt to show all data"], "LEFT", math.max(1,tooltip:GetColumnCount()-maxcol))
- if tooltip:GetColumnCount() < maxcol+1 then
- tooltip:AddLine(addonName.." version "..addon.version)
- else
- tooltip:SetCell(hintLine, tooltip:GetColumnCount()-maxcol+1, addon.version, "RIGHT", maxcol)
- end
- end
- end
- -- cache check
- local fail = false
- local maxidx = 0
- for toon,val in cpairs(columnCache[showall]) do
- if not val then -- remove stale column
- columnCache[showall][toon] = nil
- fail = true
- else
- local thisidx = columns[toon..1]
- if thisidx < maxidx then -- sort failure caused by new middle-insertion
- fail = true
- end
- maxidx = thisidx
- end
- end
- if fail then -- retry with corrected cache
- debug("Tooltip cache miss")
- addon.scaleCache[showall] = nil
- --core:ShowTooltip(anchorframe)
- -- reschedule continuation to reduce time-slice exceeded errors in combat
- core:ScheduleTimer("ShowTooltip", 0, anchorframe)
- else -- render it
- addon:SkinFrame(tooltip,"SavedInstancesTooltip")
- if addon:IsDetached() then
- --[[
- tooltip.anchorframe = UIParent
- tooltip:SmartAnchorTo(UIParent)
- tooltip:SetAutoHideDelay(nil, UIParent)
- --]]
- --tooltip:UpdateScrolling(100000)
- tooltip:Show()
- QTip.layoutCleaner:CleanupLayouts()
- tooltip:ClearAllPoints()
- tooltip:SetPoint("BOTTOMLEFT",addon.detachframe)
- tooltip:SetFrameLevel(addon.detachframe:GetFrameLevel()+1)
- else
- tooltip:SmartAnchorTo(anchorframe)
- tooltip:SetAutoHideDelay(0.1, anchorframe)
- tooltip:Show()
- end
- tooltip.OnRelease = function() -- extra-safety: update our variable on auto-release
- tooltip:ClearAllPoints()
- tooltip = nil
- end
- if db.Tooltip.FitToScreen then
- -- scale check
- QTip.layoutCleaner:CleanupLayouts()
- local scale = tooltip:GetScale()
- local w,h = tooltip:GetSize()
- local sw,sh = UIParent:GetSize()
- w = w*scale;
- h = h*scale;
- if w > sw or h > sh then
- scale = scale / math.max(w/sw, h/sh)
- scale = scale*0.95 -- 5% slop to speed convergeance
- debug("Downscaling to %.4f",scale)
- tooltip:SetScale(scale)
- tooltip:Hide()
- addon.scaleCache[showall] = scale
- core:ScheduleTimer("ShowTooltip", 0, anchorframe) -- re-render fonts
- end
- end
- end
- starttime = debugprofilestop()-starttime
- debug("ShowTooltip(): completed in %.3fms", starttime)
- end
- local function ResetConfirmed()
- debug("Resetting characters")
- if addon:IsDetached() then
- addon:HideDetached()
- end
- -- clear saves
- for instance, i in pairs(vars.db.Instances) do
- for toon, t in pairs(vars.db.Toons) do
- i[toon] = nil
- end
- end
- wipe(vars.db.Toons) -- clear toon db
- addon.PlayedTime = nil -- reset played cache
- core:toonInit() -- rebuild thisToon
- core:Refresh()
- vars.config:BuildOptions() -- refresh config table
- vars.config:ReopenConfigDisplay(vars.config.ftoon)
- end
- StaticPopupDialogs["SAVEDINSTANCES_RESET"] = {
- preferredIndex = 3, -- reduce the chance of UI taint
- text = L["Are you sure you want to reset the SavedInstances character database? Characters will be re-populated as you log into them."],
- button1 = OKAY,
- button2 = CANCEL,
- OnAccept = ResetConfirmed,
- timeout = 0,
- whileDead = true,
- hideOnEscape = true,
- enterClicksFirstButton = false,
- showAlert = true,
- }
- local function DeleteCharacter(toon)
- if toon == thisToon or not vars.db.Toons[toon] then
- chatMsg("ERROR: Failed to delete "..toon..". Character is active or does not exist.")
- return
- end
- debug("Deleting character: "..toon)
- if addon:IsDetached() then
- addon:HideDetached()
- end
- -- clear saves
- for instance, i in pairs(vars.db.Instances) do
- i[toon] = nil
- end
- vars.db.Toons[toon] = nil
- vars.config:BuildOptions() -- refresh config table
- vars.config:ReopenConfigDisplay(vars.config.ftoon)
- end
- StaticPopupDialogs["SAVEDINSTANCES_DELETE_CHARACTER"] = {
- preferredIndex = 3, -- reduce the chance of UI taint
- text = string.format(L["Are you sure you want to remove %s from the SavedInstances character database?"],"\n\n%s%s\n\n").."\n\n"..
- L["This should only be used for characters who have been renamed or deleted, as characters will be re-populated when you log into them."],
- button1 = OKAY,
- button2 = CANCEL,
- OnAccept = function(self,data) DeleteCharacter(data) end,
- timeout = 0,
- whileDead = true,
- hideOnEscape = true,
- enterClicksFirstButton = false,
- showAlert = true,
- }
- local trade_spells = {
- -- Alchemy
- -- Vanilla
- [11479] = "xmute", -- Transmute: Iron to Gold
- [11480] = "xmute", -- Transmute: Mithril to Truesilver
- [17559] = "xmute", -- Transmute: Air to Fire
- [17566] = "xmute", -- Transmute: Earth to Life
- [17561] = "xmute", -- Transmute: Earth to Water
- [17560] = "xmute", -- Transmute: Fire to Earth
- [17565] = "xmute", -- Transmute: Life to Earth
- [17563] = "xmute", -- Transmute: Undeath to Water
- [17562] = "xmute", -- Transmute: Water to Air
- [17564] = "xmute", -- Transmute: Water to Undeath
- -- BC
- [28566] = "xmute", -- Transmute: Primal Air to Fire
- [28585] = "xmute", -- Transmute: Primal Earth to Life
- [28567] = "xmute", -- Transmute: Primal Earth to Water
- [28568] = "xmute", -- Transmute: Primal Fire to Earth
- [28583] = "xmute", -- Transmute: Primal Fire to Mana
- [28584] = "xmute", -- Transmute: Primal Life to Earth
- [28582] = "xmute", -- Transmute: Primal Mana to Fire
- [28580] = "xmute", -- Transmute: Primal Shadow to Water
- [28569] = "xmute", -- Transmute: Primal Water to Air
- [28581] = "xmute", -- Transmute: Primal Water to Shadow
- -- WotLK
- [60893] = 3, -- Northrend Alchemy Research: 3 days
- [53777] = "xmute", -- Transmute: Eternal Air to Earth
- [53776] = "xmute", -- Transmute: Eternal Air to Water
- [53781] = "xmute", -- Transmute: Eternal Earth to Air
- [53782] = "xmute", -- Transmute: Eternal Earth to Shadow
- [53775] = "xmute", -- Transmute: Eternal Fire to Life
- [53774] = "xmute", -- Transmute: Eternal Fire to Water
- [53773] = "xmute", -- Transmute: Eternal Life to Fire
- [53771] = "xmute", -- Transmute: Eternal Life to Shadow
- [54020] = "xmute", -- Transmute: Eternal Might
- [53779] = "xmute", -- Transmute: Eternal Shadow to Earth
- [53780] = "xmute", -- Transmute: Eternal Shadow to Life
- [53783] = "xmute", -- Transmute: Eternal Water to Air
- [53784] = "xmute", -- Transmute: Eternal Water to Fire
- [66658] = "xmute", -- Transmute: Ametrine
- [66659] = "xmute", -- Transmute: Cardinal Ruby
- [66660] = "xmute", -- Transmute: King's Amber
- [66662] = "xmute", -- Transmute: Dreadstone
- [66663] = "xmute", -- Transmute: Majestic Zircon
- [66664] = "xmute", -- Transmute: Eye of Zul
- -- Cata
- [78866] = "xmute", -- Transmute: Living Elements
- --[80243] = "xmute", -- Transmute: Truegold, cd removed (5.2.0 verified)
- [80244] = "xmute", -- Transmute: Pyrium Bar
- -- MoP
- [114780] = "xmute", -- Transmute: Living Steel
- -- WoD
- [175880] = true, -- Secrets of Draenor
- [156587] = true, -- Alchemical Catalyst (4)
- [168042] = true, -- Alchemical Catalyst (10), 3 charges w/ 24hr recharge
- [181643] = "xmute", -- Transmute: Savage Blood
- -- Enchanting
- [28027] = "sphere", -- Prismatic Sphere (2-day shared, 5.2.0 verified)
- [28028] = "sphere", -- Void Sphere (2-day shared, 5.2.0 verified)
- [116499] = true, -- Sha Crystal
- [177043] = true, -- Secrets of Draenor
- [169092] = true, -- Temporal Crystal
- -- Jewelcrafting
- [47280] = true, -- Brilliant Glass, still has a cd (5.2.0 verified)
- --[62242] = true, -- Icy Prism, cd removed (5.2.0 verified)
- [73478] = true, -- Fire Prism, still has a cd (5.2.0 verified)
- [131691] = "facet", -- Imperial Amethyst/Facets of Research
- [131686] = "facet", -- Primordial Ruby/Facets of Research
- [131593] = "facet", -- River's Heart/Facets of Research
- [131695] = "facet", -- Sun's Radiance/Facets of Research
- [131690] = "facet", -- Vermilion Onyx/Facets of Research
- [131688] = "facet", -- Wild Jade/Facets of Research
- [140050] = true, -- Serpent's Heart
- [176087] = true, -- Secrets of Draenor
- [170700] = true, -- Taladite Crystal
- -- Tailoring
- [143011] = true, -- Celestial Cloth
- [125557] = true, -- Imperial Silk
- [56005] = 7, -- Glacial Bag (5.2.0 verified)
- [176058] = true, -- Secrets of Draenor
- [168835] = true, -- Hexweave Cloth
- -- Dreamcloth
- [75141] = 7, -- Dream of Skywall
- [75145] = 7, -- Dream of Ragnaros
- [75144] = 7, -- Dream of Hyjal
- [75142] = 7, -- Dream of Deepholm
- [75146] = 7, -- Dream of Azshara
- --[18560] = true, -- Mooncloth, cd removed (5.2.0 verified, tooltip is wrong)
- -- Inscription
- [61288] = true, -- Minor Inscription Research
- [61177] = true, -- Northrend Inscription Research
- [86654] = true, -- Horde Forged Documents
- [89244] = true, -- Alliance Forged Documents
- [112996] = true, -- Scroll of Wisdom
- [169081] = true, -- War Paints
- [177045] = true, -- Secrets of Draenor
- [176513] = true, -- Draenor Merchant Order
- -- Blacksmithing
- [138646] = true, -- Lightning Steel Ingot
- [143255] = true, -- Balanced Trillium Ingot
- [171690] = true, -- Truesteel Ingot
- [176090] = true, -- Secrets of Draenor
- -- Leatherworking
- [140040] = "magni", -- Magnificence of Leather
- [140041] = "magni", -- Magnificence of Scales
- [142976] = true, -- Hardened Magnificent Hide
- [171391] = true, -- Burnished Leather
- [176089] = true, -- Secrets of Draenor
- -- Engineering
- [139176] = true, -- Stabilized Lightning Source
- [169080] = true, -- Gearspring Parts
- [177054] = true, -- Secrets of Draenor
- [126459] = "item", -- Blingtron 4000
- [161414] = "item", -- Blingtron 5000
- [54710] = "item", -- MOLL-E
- [67826] = "item", -- Jeeves
- [67833] = "item", -- Wormhole Generator: Northrend
- [126755] = "item", -- Wormhole Generator: Pandaria
- [163830] = "item", -- Wormhole Centrifuge
- [23453] = "item", -- Ultrasafe Transporter: Gadgetzhan
- [36941] = "item", -- Ultrasafe Transporter: Toshley's Station
- }
- local cdname = {
- ["xmute"] = GetSpellInfo(2259).. ": "..L["Transmute"],
- ["facet"] = GetSpellInfo(25229)..": "..L["Facets of Research"],
- ["sphere"] = GetSpellInfo(7411).. ": "..GetSpellInfo(28027),
- ["magni"] = GetSpellInfo(2108).. ": "..GetSpellInfo(140040)
- }
- local itemcds = { -- [itemid] = spellid
- [87214] = 126459, -- Blingtron 4000
- [111821] = 161414, -- Blingtron 5000
- [40768] = 54710, -- MOLL-E
- [49040] = 67826, -- Jeeves
- [112059] = 163830, -- Wormhole Centrifuge
- [48933] = 67833, -- Wormhole Generator: Northrend
- [87215] = 126755, -- Wormhole Generator: Pandaria
- [18986] = 23453, -- Ultrasafe Transporter: Gadgetzhan
- [30544] = 36941, -- Ultrasafe Transporter: Toshley's Station
- }
- function core:scan_item_cds()
- for itemid, spellid in pairs(itemcds) do
- local start, duration = GetItemCooldown(itemid)
- if start and duration and start > 0 then
- core:record_skill(spellid, GetTimeToTime(start+duration))
- end
- end
- end
- function core:record_skill(spellID, expires)
- if not spellID then return end
- local cdinfo = trade_spells[spellID]
- if not cdinfo then
- addon.skillwarned = addon.skillwarned or {}
- if expires and expires > 0 and not addon.skillwarned[spellID] then
- addon.skillwarned[spellID] = true
- bugReport("Unrecognized trade skill cd "..(GetSpellInfo(spellID) or "??").." ("..spellID..")")
- end
- return
- end
- local t = vars and vars.db.Toons[thisToon]
- if not t then return end
- local spellName = GetSpellInfo(spellID)
- t.Skills = t.Skills or {}
- local idx = spellID
- local title = spellName
- local link = nil
- if cdinfo == "item" then
- if not expires then
- core:ScheduleTimer("scan_item_cds", 2) -- theres a delay for the item to go on cd
- return
- end
- for itemid, spellid in pairs(itemcds) do
- if spellid == spellID then
- title,link = GetItemInfo(itemid) -- use item name as some item spellnames are ambiguous or wrong
- title = title or spellName
- end
- end
- elseif type(cdinfo) == "string" then
- idx = cdinfo
- title = cdname[cdinfo] or title
- elseif expires ~= 0 then
- local slink = GetSpellLink(spellID)
- if slink and #slink > 0 then -- tt scan for the full name with profession
- link = "\124cffffd000\124Henchant:"..spellID.."\124h[X]\124h\124r"
- scantt:SetOwner(UIParent,"ANCHOR_NONE")
- scantt:SetHyperlink(link)
- local l = _G[scantt:GetName().."TextLeft1"]
- l = l and l:GetText()
- if l and #l > 0 then
- title = l
- link = link:gsub("X",l)
- else
- link = nil
- end
- end
- end
- if expires == 0 then
- if t.Skills[idx] then -- a cd ended early
- debug("Clearing Trade skill cd: %s (%s)",spellName,spellID)
- end
- t.Skills[idx] = nil
- return
- elseif not expires then
- expires = addon:GetNextDailySkillResetTime()
- if not expires then return end -- ticket 127
- if type(cdinfo) == "number" then -- over a day, make a rough guess
- expires = expires + (cdinfo-1)*24*60*60
- end
- end
- expires = math.floor(expires)
- local sinfo = t.Skills[idx] or {}
- t.Skills[idx] = sinfo
- local change = expires - (sinfo.Expires or 0)
- if math.abs(change) > 180 and addon.db.dbg then -- updating expiration guess (more than 3 min update lag)
- debug("Trade skill cd: "..(link or title).." ("..spellID..") "..
- (sinfo.Expires and string.format("%d",change).." sec" or "(new)")..
- " Local time: "..date("%c",expires))
- end
- sinfo.Title = title
- sinfo.Link = link
- sinfo.Expires = expires
- return true
- end
- function core:TradeSkillRescan(spellid)
- local scan = core:TRADE_SKILL_LIST_UPDATE()
- if TradeSkillFrame and TradeSkillFrame.filterTbl and
- (scan == 0 or not addon.seencds or not addon.seencds[spellid]) then
- -- scan failed, probably because the skill is hidden - try again
- addon.filtertmp = wipe(addon.filtertmp or {})
- for k,v in pairs(TradeSkillFrame.filterTbl) do addon.filtertmp[k] = v end
- TradeSkillOnlyShowMakeable(false)
- TradeSkillOnlyShowSkillUps(false)
- SetTradeSkillCategoryFilter(-1)
- SetTradeSkillInvSlotFilter(-1, 1, 1)
- ExpandTradeSkillSubClass(0)
- local rescan = core:TRADE_SKILL_LIST_UPDATE()
- debug("Rescan: "..(rescan==scan and "Failed" or "Success"))
- TradeSkillOnlyShowMakeable(addon.filtertmp.hasMaterials);
- TradeSkillOnlyShowSkillUps(addon.filtertmp.hasSkillUp);
- SetTradeSkillCategoryFilter(addon.filtertmp.subClassValue or -1)
- SetTradeSkillInvSlotFilter(addon.filtertmp.slotValue or -1, 1, 1)
- end
- end
- function core:TRADE_SKILL_LIST_UPDATE()
- local cnt = 0
- if C_TradeSkillUI.IsTradeSkillLinked() or C_TradeSkillUI.IsTradeSkillGuild() then return end
- local recipeids = C_TradeSkillUI.GetFilteredRecipeIDs()
- for _, spellid in ipairs(recipeids) do
- local cd, daily = C_TradeSkillUI.GetRecipeCooldown(spellid)
- if cd and daily -- GetTradeSkillCooldown often returns WRONG answers for daily cds
- and not tonumber(trade_spells[spellid]) then -- daily flag incorrectly set for some multi-day cds (Northrend Alchemy Research)
- cd = addon:GetNextDailySkillResetTime()
- elseif cd then
- cd = time() + cd -- on cd
- else
- cd = 0 -- off cd or no cd
- end
- core:record_skill(spellid, cd)
- if cd then
- addon.seencds = addon.seencds or {}
- addon.seencds[spellid] = true
- cnt = cnt + 1
- end
- end
- return cnt
- end
- local farm_spells = {
- [111102]="plant", -- Plant Green Cabbage
- [123361]="plant", -- Plant Juicycrunch Carrot
- [123388]="plant", -- Plant Scallions
- [123485]="plant", -- Plant Mogu Pumpkin
- [123535]="plant", -- Plant Red Blossom Leek
- [123565]="plant", -- Plant Pink Turnip
- [123568]="plant", -- Plant White Turnip
- [123771]="plant", -- Plant Golden Seed
- [123772]="plant", -- Plant Seed of Harmony
- [123773]="plant", -- Plant Snakeroot Seed
- [123774]="plant", -- Plant Enigma Seed
- [123775]="plant", -- Plant Magebulb Seed
- [123776]="plant", -- Plant Soybean Seed
- [123777]="plant", -- Plant Ominous Seed
- [123892]="plant", -- Plant Autumn Blossom Sapling
- [123893]="plant", -- Plant Spring Blossom Seed
- [123894]="plant", -- Plant Winter Blossom Sapling
- [123895]="plant", -- Plant Kyparite Seed
- [129623]="plant", -- Plant Windshear Cactus Seed
- [129628]="plant", -- Plant Raptorleaf Seed
- [129863]="plant", -- Plant Songbell Seed
- [129974]="plant", -- Plant Witchberries
- [129976]="plant", -- Plant Jade Squash
- [129978]="plant", -- Plant Striped Melon
- [130170]="plant", -- Plant Spring Blossom Sapling
- [133036]="plant", -- Plant Unstable Portal Shard
- [116356]="throw", -- Throw Green Cabbage Seeds
- [123362]="throw", -- Throw Juicycrunch Carrot Seeds
- [123389]="throw", -- Throw Scallion Seeds
- [123486]="throw", -- Throw Mogu Pumpkin Seeds
- [123537]="throw", -- Throw Red Blossom Leek Seeds
- [123566]="throw", -- Throw Pink Turnip Seeds
- [123567]="throw", -- Throw White Turnip Seeds
- [131093]="throw", -- Throw Witchberry Seeds
- [131094]="throw", -- Throw Jade Squash Seeds
- [131095]="throw", -- Throw Striped Melon Seeds
- [139975]="throw", -- Throw Songbell Seeds
- [139977]="throw", -- Throw Snakeroot Seeds
- [139978]="throw", -- Throw Enigma Seeds
- [139981]="throw", -- Throw Magebulb Seeds
- [139983]="throw", -- Throw Windshear Cactus Seeds
- [139986]="throw", -- Throw Raptorleaf Seeds
- [111123]="harvest", -- Harvest Green Cabbage
- [115063]="harvest", -- Harvest EZ-Gro Green Cabbage
- [123353]="harvest", -- Harvest Juicycrunch Carrot
- [123355]="harvest", -- Harvest Plump Green Cabbage
- [123356]="harvest", -- Harvest Plump Juicycrunch Carrot
- [123375]="harvest", -- Harvest Scallions
- [123380]="harvest", -- Harvest Plump Scallions
- [123445]="harvest", -- Harvest Mogu Pumpkin
- [123451]="harvest", -- Harvest Plump Mogu Pumpkin
- [123516]="harvest", -- Harvest Winter Blossom Tree
- [123522]="harvest", -- Harvest Plump Red Blossom Leek
- [123524]="harvest", -- Harvest Red Blossom Leek
- [123548]="harvest", -- Harvest Pink Turnip
- [123549]="harvest", -- Harvest Plump Pink Turnip
- [123570]="harvest", -- Harvest White Turnip
- [123571]="harvest", -- Harvest Plump White Turnip
- [129673]="harvest", -- Harvest Golden Lotus
- [129674]="harvest", -- Harvest Fool\'s Cap
- [129675]="harvest", -- Harvest Snow Lily
- [129676]="harvest", -- Harvest Silkweed
- [129687]="harvest", -- Harvest Green Tea Leaf
- [129705]="harvest", -- Harvest Rain Poppy
- [129757]="harvest", -- Harvest Snakeroot
- [129796]="harvest", -- Harvest Magebulb
- [129814]="harvest", -- Harvest Windshear Cactus
- [129843]="harvest", -- Harvest Raptorleaf
- [129887]="harvest", -- Harvest Songbell
- [129983]="harvest", -- Harvest Witchberries
- [129984]="harvest", -- Harvest Plump Witchberries
- [130025]="harvest", -- Harvest Jade Squash
- [130026]="harvest", -- Harvest Plump Jade Squash
- [130042]="harvest", -- Harvest Striped Melon
- [130043]="harvest", -- Harvest Plump Striped Melon
- [130109]="harvest", -- Harvest Terrible Turnip
- [130140]="harvest", -- Harvest Autumn Blossom Tree
- [130168]="harvest", -- Harvest Spring Blossom Tree
- [133106]="harvest", -- Harvest Portal Shard
- }
- function core:record_farm(spellID)
- local ft = farm_spells[spellID]
- if not ft then return end
- local t = vars and vars.db.Toons[thisToon]
- if not t then return end
- if ft == "plant" or ft == "throw" then
- local amt = (ft == "plant" and 1 or 4)
- t.FarmPlanted = (t.FarmPlanted or 0) + amt
- t.FarmCropPlanted = t.FarmCropPlanted or {}
- t.FarmCropPlanted[spellID] = (t.FarmCropPlanted[spellID] or 0) + amt
- elseif ft == "harvest" then
- if t.FarmExpires and time() + 60 > t.FarmExpires then -- assume this is a fresh day
- t.FarmExpires = t.FarmExpires - 60
- addon:UpdateToonData() -- ticket 132: ensure refresh if we're harvesting right after reset
- end
- t.FarmHarvested = (t.FarmHarvested or 0) + 1
- t.FarmCropReady = nil
- end
- t.FarmExpires = addon:GetNextDailySkillResetTime()
- debug("Farm "..ft..": planted="..(t.FarmPlanted or 0)..
- " harvested="..(t.FarmHarvested or 0).." expires="..date("%c",t.FarmExpires or 0))
- end
- function core:UNIT_SPELLCAST_SUCCEEDED(evt, unit, spellName, rank, lineID, spellID)
- if unit ~= "player" then return end
- if trade_spells[spellID] then
- debug("UNIT_SPELLCAST_SUCCEEDED: %s (%s)",GetSpellLink(spellID),spellID)
- if not core:record_skill(spellID) then return end
- core:ScheduleTimer("TradeSkillRescan", 0.5, spellID)
- elseif farm_spells[spellID] then
- debug("UNIT_SPELLCAST_SUCCEEDED: %s (%s)",GetSpellLink(spellID),spellID)
- core:record_farm(spellID)
- end
- end
- function core:BonusRollResult(event, rewardType, rewardLink, rewardQuantity, rewardSpecID)
- local t = vars.db.Toons[thisToon]
- debug("BonusRollResult:%s:%s:%s:%s (boss=%s|%s)",
- tostring(rewardType), tostring(rewardLink), tostring(rewardQuantity), tostring(rewardSpecID),
- tostring(t and t.lastboss), tostring(t and t.lastbossyell))
- if not t then return end
- if not rewardType then return end -- sometimes get a bogus message, ignore it
- t.BonusRoll = t.BonusRoll or {}
- --local rewardstr = _G["BONUS_ROLL_REWARD_"..string.upper(rewardType)]
- local now = time()
- local bossname = t.lastboss
- if now > (t.lastbosstime or 0) + 3*60 then -- user rolled before lastboss was updated, ignore the stale one. Roll timeout is 3 min.
- bossname = nil
- end
- if not bossname and t.lastbossyell and now < (t.lastbossyelltime or 0) + 10*60 then
- bossname = t.lastbossyell -- yell fallback
- end
- if not bossname then
- bossname = GetSubZoneText() or GetRealZoneText() -- zone fallback
- end
- local roll = { name = bossname, time = now, currencyID = BonusRollFrame.currencyID }
- if rewardType == "money" then
- roll.money = rewardQuantity
- elseif rewardType == "item" then
- roll.item = rewardLink
- end
- table.insert(t.BonusRoll, 1, roll)
- local limit = 25
- for i=limit+1, table.maxn(t.BonusRoll) do
- t.BonusRoll[i] = nil
- end
- end
- function addon.BonusRollShow()
- local t = vars.db.Toons[thisToon]
- if not t or not BonusRollFrame then return end
- local binfo = t.BonusRoll
- local frame = addon.BonusFrame
- if not binfo or #binfo == 0 or not vars.db.Tooltip.AugmentBonus then
- if frame then frame:Hide() end
- return
- end
- if not frame then
- frame = CreateFrame("Button", "SavedInstancesBonusRollFrame", BonusRollFrame, "SpellBookSkillLineTabTemplate")
- addon.BonusFrame = frame
- --frame:SetSize(BonusRollFrame:GetHeight(), BonusRollFrame:GetHeight())
- frame:SetPoint("LEFT", BonusRollFrame, "RIGHT",0,8)
- frame.text = addon.BonusFrame:CreateFontString(nil, "OVERLAY","GameFontNormal")
- frame.text:SetPoint("CENTER")
- frame:SetScript("OnEnter", function() ShowBonusTooltip(nil, { thisToon, frame }) end )
- frame:SetScript("OnLeave", CloseTooltips)
- frame:SetScript("OnClick", nil)
- frame.text:Show()
- end
- local bonus = 0
- for _,rinfo in ipairs(binfo) do
- if rinfo.money then
- bonus = bonus + 1
- else
- break
- end
- end
- frame.text:SetText((bonus > 0 and "+" or "")..bonus)
- frame:Show()
- end
- hooksecurefunc("BonusRollFrame_StartBonusRoll", addon.BonusRollShow)
Advertisement
Add Comment
Please, Sign In to add comment