From a796c3204cb613fc7caecb799b55d879685ebebc Mon Sep 17 00:00:00 2001 From: Aleksi Blinnikka Date: Sun, 18 Mar 2018 16:16:20 +0200 Subject: [PATCH 1/1] Add CD tracker Some problems with inspecting talents etc. --- OmaCD/Bres.lua | 49 +++++ OmaCD/Cooldowns.lua | 495 ++++++++++++++++++++++++++++++++++++++++++++ OmaCD/OmaCD.toc | 8 + 3 files changed, 552 insertions(+) create mode 100644 OmaCD/Bres.lua create mode 100644 OmaCD/Cooldowns.lua create mode 100644 OmaCD/OmaCD.toc diff --git a/OmaCD/Bres.lua b/OmaCD/Bres.lua new file mode 100644 index 0000000..0bdde11 --- /dev/null +++ b/OmaCD/Bres.lua @@ -0,0 +1,49 @@ +-- Bres.lua +local _; +local min, ceil = math.min, math.ceil; +local GetSpellCharges = GetSpellCharges; +local GetTime = GetTime; +local CTimerAfter = C_Timer.After; + +local frame = CreateFrame("Frame", "OmaBres", UIParent); + +local function tick() + local charges, maxCharges, start, duration = GetSpellCharges(20484); -- Rebirth + if charges then + frame:Show(); + local remain = duration - (GetTime() - start); + frame.charge:SetText(charges); + frame.cd:SetCooldown(start, duration); + CTimerAfter(min(ceil(remain), 20), tick); + else + frame:Hide(); + CTimerAfter(5, tick); + end +end + +local function bres() + frame:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", 580, 0); + frame:SetWidth(48); + frame:SetHeight(48); + frame.base = frame:CreateTexture(nil, "BACKGROUND"); + frame.base:SetAllPoints(); + frame.base:SetColorTexture(0, 0, 0, 0.5); + frame.icon = frame:CreateTexture(nil, "BORDER"); + frame.icon:SetPoint("TOPLEFT", frame.base, "TOPLEFT", 1, -1); + frame.icon:SetPoint("BOTTOMRIGHT", frame.base, "BOTTOMRIGHT", -1, 1); + frame.icon:SetTexCoord(0.07, 0.93, 0.07, 0.93); + frame.icon:SetTexture(136080); -- Rebirth + frame.charge = frame:CreateFontString(nil, "OVERLAY", "NumberFontNormalLarge"); + frame.charge:SetPoint("BOTTOMRIGHT"); + frame.cd = CreateFrame("Cooldown", "OmaBresCD", frame, "CooldownFrameTemplate"); + frame.cd:SetAllPoints(); + frame:UnregisterAllEvents(); + tick(); +end + +frame:RegisterEvent("PLAYER_LOGIN"); +frame:SetScript("OnEvent", function(self, event) + if event == "PLAYER_LOGIN" then + return bres(); + end +end); diff --git a/OmaCD/Cooldowns.lua b/OmaCD/Cooldowns.lua new file mode 100644 index 0000000..916a5a0 --- /dev/null +++ b/OmaCD/Cooldowns.lua @@ -0,0 +1,495 @@ +-- Cooldowns.lua +local _; +local pairs, ipairs, unpack = pairs, ipairs, unpack; +local tremove, format, ssub = table.remove, string.format, string.sub; +local floor, strsplit = math.floor, strsplit; +local GetTime, UnitGUID, UnitClass = GetTime, UnitGUID, UnitClass; +local UnitName, GetSpellTexture = UnitName, GetSpellTexture; +local IsInGroup, IsInRaid = IsInGroup, IsInRaid; +local CreateFrame, CTimerAfter = CreateFrame, C_Timer.After; +local UnitIsUnit, IsEncounterInProgress = UnitIsUnit, IsEncounterInProgress; +local UnitExists, UnitGroupRolesAssigned = UnitExists, UnitGroupRolesAssigned; +local UnitIsDeadOrGhost, UnitIsConnected = UnitIsDeadOrGhost, UnitIsConnected; +local GetTalentInfo, GetInspectSpecialization = GetTalentInfo, GetInspectSpecialization; +local GetSpecializationInfo, GetSpecialization = GetSpecializationInfo, GetSpecialization; +local GetInventoryItemLink, GetInventoryItemID = GetInventoryItemLink, GetInventoryItemID; +local CanInspect, NotifyInspect, ClearInspectPlayer = CanInspect, NotifyInspect, ClearInspectPlayer; +local INVSLOT_MAINHAND, INVSLOT_WRIST = INVSLOT_MAINHAND, INVSLOT_WRIST; + +local upColor = {0, 0.7, 0.2, 0.7}; +local downColor = {1, 0, 0, 0.5}; + +local cdframe = CreateFrame("Frame", "OmaCD", UIParent); +local frames = {}; +local unused = {}; +local shown = {}; -- simple array of frames in shown order + +local specs = { + ["PALADIN"] = 65, + ["PRIEST"] = false, -- priest has 2 healing specs + ["SHAMAN"] = 264, + ["MONK"] = 270, + ["DRUID"] = 105, +}; +local trackedcds = { + [65] = { -- Holy Paladin + [31821] = 180, -- Aura Mastery + }, + [105] = { -- Resto Druid + [740] = 180, -- Tranquility + }, + [256] = { -- Discipline Priest + [62618] = 180, -- Power Word: Barrier + }, + [257] = { -- Holy Priest + [64843] = 180, -- Divine Hymn + }, + [264] = { -- Resto Shaman + [108280] = 180, -- Healing Tide Totem + [98008] = 180, -- Spirit Link Totem + }, + [270] = { -- Mistweaver Monk + [115310] = (180-40), -- Revival, already lowered by Artifact when lvl 75 + }, +}; +local monkRelics = { -- Tendrils of Revival + ["151012"] = -10, + ["151010"] = -10, + ["147112"] = -10, + ["152291"] = -10, +}; + +local cds = {}; -- CD durations currently available modified by passive cdfixes +-- CD fixes active e.g. cdfixes[guid] = -60, no need for spellid as +-- only classes with one CD have fixes +local cdfixes = {}; +local runningcds = {}; -- CDs currently active + +local idToGuid = {}; +local guidToId = {}; +local guidToSpecid = {}; +local monksWithLegendary = {}; +local inspectSent = {}; +local dead = {}; +local disconnected = {}; + +local function CdUp(guid, spellid) + local frame = frames[guid][spellid]; + if runningcds[guid] then runningcds[guid][spellid] = nil end + frame.base:SetVertexColor(unpack(upColor)); + frame.text:SetText(frame.name); +end + +local function CdDown(guid, spellid, start) + local frame = frames[guid][spellid]; + if not runningcds[guid] then runningcds[guid] = {} end + runningcds[guid][spellid] = start; + frame.base:SetVertexColor(unpack(downColor)); + if start then frame.text:SetText(cds[guid][spellid]) end +end + +local function sendInspect(guid, id, func) + if not inspectSent[guid] and CanInspect(id) then + inspectSent[guid] = true; + local _, _, _, _, _, name = GetPlayerInfoByGUID(guid); + print(guid, name, "send inspect"); + NotifyInspect(id); + CTimerAfter(20, func); -- precaution for never receiving INSPECT_READY + end +end + +local encounter = nil; +local function tick() + if not encounter and IsEncounterInProgress() then + encounter = true; + elseif encounter and not IsEncounterInProgress() then + encounter = nil; + -- for some reason can't hook to ENCOUNTER_END event so have to track with timer + -- TODO support Argus, encounterID 1866 + for guid, t in pairs(runningcds) do + for spellid, _ in pairs(t) do + CdUp(guid, spellid); + end + end + end + local current = GetTime(); + for guid, t in pairs(runningcds) do + for spellid, start in pairs(t) do + local remain = cds[guid][spellid] - (current - start); + if remain <= 0 then -- CD back up + CdUp(guid, spellid); + else -- still on CD + if remain >= 60 then + frames[guid][spellid].text:SetFormattedText("%d:%02d", floor(remain/60), remain%60); + else + frames[guid][spellid].text:SetText(floor(remain)); + end + end + end + end + CTimerAfter(0.5, tick); +end + +local function refreshPositions() + for i, frame in ipairs(shown) do + local prev = shown[i-1] or cdframe; + frame:ClearAllPoints(); + frame:SetPoint("TOPLEFT", prev, "BOTTOMLEFT", 0, -1); + end +end + +local function removeFrame(frame) + unused[#unused+1] = frame; + frames[frame.guid][frame.spellid] = nil; + cds[frame.guid][frame.spellid] = nil; + if runningcds[frame.guid] then runningcds[frame.guid][frame.spellid] = nil end + tremove(shown, frame.pos); + for i, frame in ipairs(shown) do + frame.pos = i; + end + frame:ClearAllPoints(); + frame:Hide(); + refreshPositions(); +end + +local unique = 1; +local function getFrame() + local frame = tremove(unused); + if not frame then + frame = CreateFrame("Frame", format("OmaCD%i", unique), cdframe); + unique = unique + 1; + frame:SetWidth(75); + frame:SetHeight(16); + frame.base = frame:CreateTexture(nil, "BACKGROUND"); + frame.base:SetAllPoints(); + frame.base:SetColorTexture(1, 1, 1); + frame.icon = frame:CreateTexture(nil, "ARTWORK"); + frame.icon:SetPoint("TOPLEFT"); + frame.icon:SetPoint("BOTTOMLEFT"); + frame.icon:SetWidth(16); + frame.icon:SetTexCoord(0.07, 0.93, 0.07, 0.93); + frame.text = frame:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall"); + frame.text:SetPoint("LEFT", frame, "LEFT", 20, 0); + frame.text:SetJustifyH("LEFT"); + end + return frame; +end +local function updateCD(guid, specid, spellid) + -- update cd value + local cd = trackedcds[specid][spellid]; + if cdfixes[guid] then cd = cd + cdfixes[guid] end + if not cds[guid] then cds[guid] = {} end + cds[guid][spellid] = cd; + if frames[guid] and frames[guid][spellid] then return end + + -- no cd frame yet, create one + local frame = getFrame(); + frame.name = ssub(UnitName(guidToId[guid]), 1, 8); + frame.guid = guid; + frame.spellid = spellid; + frame.base:SetVertexColor(unpack(upColor)); + frame.icon:SetTexture(GetSpellTexture(spellid)); + frame.text:SetText(frame.name); + frame:Show(); + if not frames[guid] then frames[guid] = {} end + frames[guid][spellid] = frame; + frame.pos = #shown+1; + local prev = shown[#shown] or cdframe; + shown[#shown+1] = frame; + frame:SetPoint("TOPLEFT", prev, "BOTTOMLEFT", 0, -1); + return frame; +end + +local function updateUnitCD(guid) + local specid = guidToSpecid[guid]; + if specid then + for spellid, _ in pairs(trackedcds[specid]) do + updateCD(guid, specid, spellid); + end + end +end + +local function updateMonk(guid) + local id = guidToId[guid]; + if not id then return end -- called because another addon inspected + local weapon = GetInventoryItemLink(id, INVSLOT_MAINHAND); + if not weapon then + -- try again directly + if CanInspect(id) then + print(guid, "send inspect");NotifyInspect(id); + CTimerAfter(20, updateMonk); + end + return; + end + if inspectSent[guid] then inspectSent[guid] = nil end + print("Got monk", guid); + local _, _, _, relic1, relic2, relic3 = strsplit(":", weapon); + local cdfix = 0; + if monkRelics[relic1] then cdfix = cdfix + monkRelics[relic1] end + if monkRelics[relic2] then cdfix = cdfix + monkRelics[relic2] end + if monkRelics[relic3] then cdfix = cdfix + monkRelics[relic3] end + if cdfix ~= 0 then cdfixes[guid] = cdfix end + + local wrist = GetInventoryItemID(guidToId[guid], INVSLOT_WRIST); + if wrist == 137096 then -- Petrichor Lagniappe + monksWithLegendary[guid] = true; + else + monksWithLegendary[guid] = nil; + end + updateUnitCD(guid); +end + +local function updateDruid(guid, player) + local id = guidToId[guid]; + if id then + -- Inner Peace talent + local selected; + if player then + _, _, _, selected = GetTalentInfo(6, 2, 1); + else + _, _, _, selected = GetTalentInfo(6, 2, 1, true, id); + end + if selected then + cdfixes[guid] = -60; + else + cdfixes[guid] = nil; + end + updateUnitCD(guid); + end +end + +local function updatePriest(guid) + local id = guidToId[guid]; + if id then + local specid = GetInspectSpecialization(id); + if specid == 0 then + -- try again directly + if CanInspect(id) then + print(guid, "send inspect");NotifyInspect(id); + CTimerAfter(20, updatePriest); + end + end + if inspectSent[guid] then inspectSent[guid] = nil end + print("Got priest", guid); + guidToSpecid[guid] = specid; + updateUnitCD(guid); + end +end + +local function updatePlayer() + local specid = GetSpecializationInfo(GetSpecialization() or 1); + local guid = UnitGUID("player"); + if trackedcds[specid] then + guidToId[guid] = "player"; + guidToSpecid[guid] = specid; + if specid == 270 then -- Monk + updateMonk(guid); + elseif specid == 105 then -- Druid + updateDruid(guid, true); + else + updateUnitCD(guid); + end + idToGuid["player"] = guid; + else + idToGuid["player"] = nil; + for _, frame in pairs(frames[guid]) do + removeFrame(frame); + end + end +end + +local function updateUnitid(id) + local guid = UnitGUID(id); + if UnitIsUnit(id, "player") then -- player is special case + return updatePlayer(); + elseif UnitExists(id) and UnitGroupRolesAssigned(id) == "HEALER" then + local _, class = UnitClass(id); + if specs[class] ~= nil then + guidToId[guid] = id; + if specs[class] == false then + -- Priest, have to inspect to get spec, try immediately, if not available, inspect + local specid = GetInspectSpecialization(id); + if specid == 0 then + sendInspect(guid, id, updatePriest); + elseif specid == 256 or specid == 257 then + guidToSpecid[guid] = specid; -- only Holy and Discipline + updateUnitCD(guid); + end + elseif specs[class] == 270 then + -- Monk, have to check inventory for CD modifications + guidToSpecid[guid] = specs[class]; + -- updateUnitCD (without having fixes yet) + updateUnitCD(guid); + sendInspect(guid, id, updateMonk); + elseif specs[class] == 105 then + -- Druid, have to inspect to get talents + guidToSpecid[guid] = specs[class]; + -- updateUnitCD (without having fixes yet) + updateUnitCD(guid); + sendInspect(guid, id, updateDruid); + else + guidToSpecid[guid] = specs[class]; + updateUnitCD(guid); + end + idToGuid[id] = guid; + end + else + idToGuid[id] = nil; + if frames[guid] then + for _, frame in pairs(frames[guid]) do + removeFrame(frame); + end + end + end +end + +local updateQueued = nil; +local function updateUnitids() + local size = 0; + local prefix; + local newIdToGuid = {}; + local prevIdToGuid = {}; + guidToId = {}; + updateQueued = nil; + + for id, guid in pairs(idToGuid) do + prevIdToGuid[id] = guid; + end + + if IsInGroup() then + if IsInRaid() then + size = 40; + prefix = "raid"; + else + size = 4; + prefix = "party"; + end + end + + updatePlayer(); + if prevIdToGuid["player"] then prevIdToGuid["player"] = nil end + for i = 1,size do + local id = format("%s%i", prefix, i); + if not UnitIsUnit(id, "player") then + updateUnitid(id); + end + if prevIdToGuid[id] then prevIdToGuid[id] = nil end + end + + -- clean up players leaving + for id, guid in pairs(prevIdToGuid) do + if frames[guid] then + for _, frame in pairs(frames[guid]) do + removeFrame(frame); + end + end + end +end + +local events = { + ["UNIT_HEALTH"] = function(id) + local guid = idToGuid[id]; + if guid and cds[guid] and UnitIsDeadOrGhost(id) then + dead[guid] = true; + for spellid, _ in pairs(cds[guid]) do + CdDown(guid, spellid, nil); + end + elseif guid and dead[guid] and cds[guid] then + dead[guid] = nil; + for spellid, _ in pairs(cds[guid]) do + CdUp(guid, spellid); + end + end + end, + ["UNIT_CONNECTION"] = function(id) + local guid = idToGuid[id]; + if guid and cds[guid] and not UnitIsConnected(id) then + disconnected[guid] = true; + for spellid, _ in pairs(cds[guid]) do + CdDown(guid, spellid, nil); + end + elseif guid and disconnected[guid] and cds[guid] then + disconnected[guid] = nil; + for spellid, _ in pairs(cds[guid]) do + CdUp(guid, spellid); + end + end + end, + ["UNIT_SPELLCAST_SUCCEEDED"] = function(id, _, _, _, spellid) + local guid = idToGuid[id]; + if guid and frames[guid] then + local frame = frames[guid][spellid]; + if frame then + CdDown(guid, spellid, GetTime()); + elseif monksWithLegendary[guid] and spellid == 115151 then + -- Renewing Mist with legendary affects Revival + if runningcds[guid] and runningcds[guid][115310] then + runningcds[guid][115310] = runningcds[guid][115310] - 2; + end + end + end + end, + ["PLAYER_SPECIALIZATION_CHANGED"] = function(id) + updateUnitid(id); + end, + ["UNIT_INVENTORY_CHANGED"] = function(id) + local guid = idToGuid[id]; + if guid and guidToSpecid[guid] == 270 then + print("OmaCD Inventory: Monk inventory update"); + updateMonk(guid); + end + end, + ["GROUP_ROSTER_UPDATE"] = function() + if not updateQueued then + updateQueued = true + CTimerAfter(2, updateUnitids); + end + end, + ["INSPECT_READY"] = function(guid) + local specid = guidToSpecid[guid]; + if inspectSent[guid] then inspectSent[guid] = nil end + if specid == 105 then -- Druid + print("OmaCD Inspect: druid,", guid); + updateDruid(guid); + ClearInspectPlayer(); + elseif specid == 270 then -- Monk + print("OmaCD Inspect: monk,", guid); + -- don't clear inspect player, have to track inventory changes + elseif specid == false or specid == 256 or specid == 257 then -- Priest + print("OmaCD Inspect: priest,", guid); + updatePriest(guid); + ClearInspectPlayer(); + end + end, +}; +events["PLAYER_ROLES_ASSIGNED"] = events["GROUP_ROSTER_UPDATE"]; + +local function cdtracker() + cdframe:SetFrameStrata("LOW"); + cdframe:SetPoint("TOPLEFT", UIParent, "TOPLEFT", 200, -200); + cdframe:SetWidth(1); + cdframe:SetHeight(1); + + cdframe:UnregisterAllEvents(); + cdframe:SetScript("OnEvent", function(self, event, ...) + events[event](...); + end); + cdframe:RegisterEvent("UNIT_HEALTH"); + cdframe:RegisterEvent("UNIT_CONNECTION"); + cdframe:RegisterEvent("UNIT_SPELLCAST_SUCCEEDED"); + cdframe:RegisterEvent("PLAYER_SPECIALIZATION_CHANGED"); + cdframe:RegisterEvent("UNIT_INVENTORY_CHANGED"); + cdframe:RegisterEvent("GROUP_ROSTER_UPDATE"); + cdframe:RegisterEvent("INSPECT_READY"); + cdframe:RegisterEvent("PLAYER_ROLES_ASSIGNED"); + -- initial update + events["GROUP_ROSTER_UPDATE"](); + CTimerAfter(0.5, tick); +end + +cdframe:RegisterEvent("PLAYER_LOGIN"); +cdframe:SetScript("OnEvent", function(self, event) + if event == "PLAYER_LOGIN" then + return cdtracker(); + end +end); diff --git a/OmaCD/OmaCD.toc b/OmaCD/OmaCD.toc new file mode 100644 index 0000000..6e1a15e --- /dev/null +++ b/OmaCD/OmaCD.toc @@ -0,0 +1,8 @@ +## Interface: 70300 +## Title: Oma Cooldown Tracker +## Version: 1.0 +## Author: schyrio +## Notes: My CD tracker + +Bres.lua +Cooldowns.lua -- 2.39.5