--[=[ This file has the functions to get player information Dumping them here, make the code of the main file smaller --]=] if (not LIB_OPEN_RAID_CAN_LOAD) then return end local openRaidLib = LibStub:GetLibrary("LibOpenRaid-1.0") local CONST_TALENT_VERSION_CLASSIC = 1 local CONST_TALENT_VERSION_LEGION = 4 local CONST_TALENT_VERSION_DRAGONFLIGHT = 5 local CONST_BTALENT_VERSION_COVENANTS = 9 local CONST_SPELLBOOK_CLASSSPELLS_TABID = 2 local CONST_SPELLBOOK_GENERAL_TABID = 1 local CONST_ISITEM_BY_TYPEID = { [10] = true, --healing items [11] = true, --attack items [12] = true, --utility items } local GetItemInfo = GetItemInfo local GetItemStats = GetItemStats local GetInventoryItemLink = GetInventoryItemLink local isTimewalkWoW = function() local _, _, _, buildInfo = GetBuildInfo() if (buildInfo < 40000) then return true end end local IsDragonflight = function() return select(4, GetBuildInfo()) >= 100000 end local IsShadowlands = function() local versionString, revision, launchDate, gameVersion = GetBuildInfo() if (gameVersion >= 90000 and gameVersion < 100000) then return true end end --information about the player character to send, each expansion has its own system and data can be different --it's always a number function openRaidLib.UnitInfoManager.GetPlayerInfo1() if (IsShadowlands()) then --return the renown level within the player covenant local renown = C_CovenantSanctumUI.GetRenownLevel() or 1 return renown end return 0 end --information about the player character to send, each expansion has its own system and data can be different --it's always a number function openRaidLib.UnitInfoManager.GetPlayerInfo2() if (IsShadowlands()) then --return which covenant the player picked local covenant = C_Covenants.GetActiveCovenantID() or 0 return covenant end return 0 end --default player class-spec talent system function openRaidLib.GetTalentVersion() local _, _, _, buildInfo = GetBuildInfo() if (buildInfo >= 1 and buildInfo <= 40000) then --vanilla tbc wotlk cataclysm return CONST_TALENT_VERSION_CLASSIC end if (buildInfo >= 70000 and buildInfo <= 100000) then --legion bfa shadowlands return CONST_TALENT_VERSION_LEGION end if (buildInfo >= 100000 and buildInfo <= 200000) then --dragonflight return CONST_TALENT_VERSION_DRAGONFLIGHT end end --secondary talent tree, can be a legendary weapon talent tree, covenant talent tree, etc... function openRaidLib.GetBorrowedTalentVersion() if (IsShadowlands()) then return CONST_BTALENT_VERSION_COVENANTS end end local getDragonflightTalentsExportedString = function() local exportStream = ExportUtil.MakeExportDataStream() local configId = C_ClassTalents.GetActiveConfigID() if (configId) then local configInfo = C_Traits.GetConfigInfo(configId) local currentSpecID = PlayerUtil.GetCurrentSpecID() local treeInfo = C_Traits.GetTreeInfo(configId, configInfo.treeIDs[1]) local treeHash = C_Traits.GetTreeHash(treeInfo.ID) local serializationVersion = C_Traits.GetLoadoutSerializationVersion() end end local getDragonflightTalentsAsIndexTable = function() local allTalents = {} local configId = C_ClassTalents.GetActiveConfigID() if (not configId) then return allTalents end local configInfo = C_Traits.GetConfigInfo(configId) for treeIndex, treeId in ipairs(configInfo.treeIDs) do local treeNodes = C_Traits.GetTreeNodes(treeId) for nodeIdIndex, treeNodeID in ipairs(treeNodes) do local traitNodeInfo = C_Traits.GetNodeInfo(configId, treeNodeID) if (traitNodeInfo) then local activeEntry = traitNodeInfo.activeEntry if (activeEntry) then local entryId = activeEntry.entryID local rank = activeEntry.rank if (rank > 0) then --get the entry info local traitEntryInfo = C_Traits.GetEntryInfo(configId, entryId) local definitionId = traitEntryInfo.definitionID --definition info local traitDefinitionInfo = C_Traits.GetDefinitionInfo(definitionId) local spellId = traitDefinitionInfo.overriddenSpellID or traitDefinitionInfo.spellID local spellName, _, spellTexture = GetSpellInfo(spellId) if (spellName) then allTalents[#allTalents+1] = spellId end end end end end end return allTalents end --creates two tables, one with indexed talents and another with pairs values ([talentId] = true) function openRaidLib.UnitInfoManager.GetPlayerTalentsAsPairsTable() local talentsPairs = {} local talentVersion = openRaidLib.GetTalentVersion() if (talentVersion == CONST_TALENT_VERSION_DRAGONFLIGHT) then local allTalents = getDragonflightTalentsAsIndexTable() for i = 1, #allTalents do local spellId = allTalents[i] talentsPairs[spellId] = true end elseif (talentVersion == CONST_TALENT_VERSION_LEGION) then for i = 1, 7 do for o = 1, 3 do local talentId, _, _, selected = GetTalentInfo(i, o, 1) if (selected) then talentsPairs[talentId] = true break end end end end return talentsPairs end function openRaidLib.UnitInfoManager.GetPlayerTalents() local talents = {} local talentVersion = openRaidLib.GetTalentVersion() if (talentVersion == CONST_TALENT_VERSION_DRAGONFLIGHT) then talents = getDragonflightTalentsAsIndexTable() elseif (talentVersion == CONST_TALENT_VERSION_LEGION) then talents = {0, 0, 0, 0, 0, 0, 0} for talentTier = 1, 7 do for talentColumn = 1, 3 do local talentId, name, texture, selected, available = GetTalentInfo(talentTier, talentColumn, 1) if (selected) then talents[talentTier] = talentId break end end end end return talents end function openRaidLib.UnitInfoManager.GetPlayerPvPTalents() if (IsDragonflight()) then return {} end local talentsPvP = {0, 0, 0} local talentList = C_SpecializationInfo.GetAllSelectedPvpTalentIDs() for talentIndex, talentId in ipairs(talentList) do local doesExists = GetPvpTalentInfoByID(talentId) if (doesExists) then talentsPvP[talentIndex] = talentId end end return talentsPvP end --return the current specId of the player function openRaidLib.GetPlayerSpecId() if (isTimewalkWoW()) then return 0 end local spec = GetSpecialization() if (spec) then local specId = GetSpecializationInfo(spec) if (specId and specId > 0) then return specId end end end --borrowed talent tree from shadowlands function openRaidLib.UnitInfoManager.GetPlayerConduits() local conduits = {} local soulbindID = C_Soulbinds.GetActiveSoulbindID() if (soulbindID ~= 0) then local soulbindData = C_Soulbinds.GetSoulbindData(soulbindID) if (soulbindData ~= 0) then local tree = soulbindData.tree local nodes = tree.nodes table.sort(nodes, function(t1, t2) return t1.row < t2.row end) local C_Soulbinds_GetConduitCollectionData = C_Soulbinds.GetConduitCollectionData for nodeId, nodeInfo in ipairs(nodes) do --check if the node is a conduit placed by the player if (nodeInfo.state == Enum.SoulbindNodeState.Selected) then local conduitId = nodeInfo.conduitID local conduitRank = nodeInfo.conduitRank if (conduitId and conduitRank) then --have spell id when it's a default conduit from the game local spellId = nodeInfo.spellID --have conduit id when is a conduid placed by the player local conduitId = nodeInfo.conduitID if (spellId == 0) then --is player conduit spellId = C_Soulbinds.GetConduitSpellID(nodeInfo.conduitID, nodeInfo.conduitRank) conduits[#conduits+1] = spellId local collectionData = C_Soulbinds_GetConduitCollectionData(conduitId) conduits[#conduits+1] = collectionData and collectionData.conduitItemLevel or 0 else --is default conduit conduits[#conduits+1] = spellId conduits[#conduits+1] = 0 end end end end end end return conduits end function openRaidLib.UnitInfoManager.GetPlayerBorrowedTalents() local borrowedTalentVersion = openRaidLib.GetBorrowedTalentVersion() if (borrowedTalentVersion == CONST_BTALENT_VERSION_COVENANTS) then return openRaidLib.UnitInfoManager.GetPlayerConduits() end return {} end function openRaidLib.GearManager.GetPlayerItemLevel() if (_G.GetAverageItemLevel) then local _, itemLevel = GetAverageItemLevel() itemLevel = floor(itemLevel) return itemLevel else return 0 end end --return an integer between zero and one hundret indicating the player gear durability function openRaidLib.GearManager.GetPlayerGearDurability() local durabilityTotalPercent, totalItems = 0, 0 --hold the lowest item durability of all the player gear --this prevent the case where the player has an average of 80% durability but an item with 15% durability local lowestGearDurability = 100 for i = INVSLOT_FIRST_EQUIPPED, INVSLOT_LAST_EQUIPPED do local durability, maxDurability = GetInventoryItemDurability(i) if (durability and maxDurability) then local itemDurability = durability / maxDurability * 100 if (itemDurability < lowestGearDurability) then lowestGearDurability = itemDurability end durabilityTotalPercent = durabilityTotalPercent + itemDurability totalItems = totalItems + 1 end end if (totalItems == 0) then return 100, lowestGearDurability end return floor(durabilityTotalPercent / totalItems), lowestGearDurability end function openRaidLib.GearManager.GetPlayerWeaponEnchant() local weaponEnchant = 0 local _, _, _, mainHandEnchantId, _, _, _, offHandEnchantId = GetWeaponEnchantInfo() if (LIB_OPEN_RAID_WEAPON_ENCHANT_IDS[mainHandEnchantId]) then weaponEnchant = 1 elseif(LIB_OPEN_RAID_WEAPON_ENCHANT_IDS[offHandEnchantId]) then weaponEnchant = 1 end return weaponEnchant, mainHandEnchantId or 0, offHandEnchantId or 0 end function openRaidLib.GearManager.GetPlayerGemsAndEnchantInfo() --hold equipmentSlotId of equipment with a gem socket but it's empty local slotsWithoutGems = {} --hold equipmentSlotId of equipments without an enchant local slotsWithoutEnchant = {} local gearWithEnchantIds = {} for equipmentSlotId = 1, 17 do local itemLink = GetInventoryItemLink("player", equipmentSlotId) if (itemLink) then --get the information from the item local _, itemId, enchantId, gemId1, gemId2, gemId3, gemId4, suffixId, uniqueId, levelOfTheItem, specId, upgradeInfo, instanceDifficultyId, numBonusIds, restLink = strsplit(":", itemLink) local gemsIds = {gemId1, gemId2, gemId3, gemId4} --enchant --check if the slot can receive enchat and if the equipment has an enchant local enchantAttribute = LIB_OPEN_RAID_ENCHANT_SLOTS[equipmentSlotId] local nEnchantId = 0 if (enchantAttribute) then --this slot can receive an enchant if (enchantId and enchantId ~= "") then local number = tonumber(enchantId) nEnchantId = number gearWithEnchantIds[#gearWithEnchantIds+1] = nEnchantId else gearWithEnchantIds[#gearWithEnchantIds+1] = 0 end --6400 and above is dragonflight enchantId number space if (nEnchantId < 6300 and not LIB_OPEN_RAID_DEATHKNIGHT_RUNEFORGING_ENCHANT_IDS[nEnchantId]) then slotsWithoutEnchant[#slotsWithoutEnchant+1] = equipmentSlotId end end --gems local itemStatsTable = {} --fill the table above with information about the item GetItemStats(itemLink, itemStatsTable) --check if the item has a socket if (itemStatsTable.EMPTY_SOCKET_PRISMATIC) then --check if the socket is empty for i = 1, itemStatsTable.EMPTY_SOCKET_PRISMATIC do local gemId = tonumber(gemsIds[i]) if (not gemId or gemId == 0) then slotsWithoutGems[#slotsWithoutGems+1] = equipmentSlotId --check if the gem is not a valid gem (deprecated gem) elseif (gemId < 180000) then slotsWithoutGems[#slotsWithoutGems+1] = equipmentSlotId end end end end end return slotsWithoutGems, slotsWithoutEnchant end function openRaidLib.GearManager.BuildPlayerEquipmentList() local equipmentList = {} local debug for equipmentSlotId = 1, 17 do local itemLink = GetInventoryItemLink("player", equipmentSlotId) if (itemLink) then local itemStatsTable = {} local itemID, enchantID, gemID1, gemID2, gemID3, gemID4, suffixID, uniqueID, linkLevel, specializationID, modifiersMask, itemContext = select(2, strsplit(":", itemLink)) itemID = tonumber(itemID) local effectiveILvl, isPreview, baseILvl = GetDetailedItemLevelInfo(itemLink) if (not effectiveILvl) then openRaidLib.mainControl.scheduleUpdatePlayerData() effectiveILvl = 0 openRaidLib.__errors[#openRaidLib.__errors+1] = "Fail to get Item Level: " .. (itemID or "invalid itemID") .. " " .. (itemLink and itemLink:gsub("|H", "") or "invalid itemLink") end GetItemStats(itemLink, itemStatsTable) local gemSlotsAvailable = itemStatsTable and itemStatsTable.EMPTY_SOCKET_PRISMATIC or 0 local noPrefixItemLink = itemLink : gsub("^|c%x%x%x%x%x%x%x%x|Hitem", "") local linkTable = {strsplit(":", noPrefixItemLink)} local numModifiers = linkTable[14] numModifiers = numModifiers and tonumber(numModifiers) or 0 for i = #linkTable, 14 + numModifiers + 1, -1 do tremove(linkTable, i) end local newItemLink = table.concat(linkTable, ":") newItemLink = newItemLink equipmentList[#equipmentList+1] = {equipmentSlotId, gemSlotsAvailable, effectiveILvl, newItemLink} if (equipmentSlotId == 2) then debug = {itemLink:gsub("|H", ""), newItemLink} end end end --[[ debug local str = "" for i = 1, #equipmentList do local t = equipmentList[i] local s = t[1] .. "," .. t[2] .. "," .. t[3] .. "," .. t[4] str = str .. s end table.insert(debug, str) dumpt(debug) --]] return equipmentList end local playerHasPetOfNpcId = function(npcId) if (UnitExists("pet") and UnitHealth("pet") >= 1) then local guid = UnitGUID("pet") if (guid) then local split = {strsplit("-", guid)} local playerPetNpcId = tonumber(split[6]) if (playerPetNpcId) then if (npcId == playerPetNpcId) then return true end end end end end local addCooldownToTable = function(cooldowns, cooldownsHash, cooldownSpellId, timeNow) local timeLeft, charges, startTimeOffset, duration, auraDuration = openRaidLib.CooldownManager.GetPlayerCooldownStatus(cooldownSpellId) cooldowns[#cooldowns+1] = cooldownSpellId cooldowns[#cooldowns+1] = timeLeft cooldowns[#cooldowns+1] = charges cooldowns[#cooldowns+1] = startTimeOffset cooldowns[#cooldowns+1] = duration cooldowns[#cooldowns+1] = auraDuration cooldownsHash[cooldownSpellId] = {timeLeft, charges, startTimeOffset, duration, timeNow, auraDuration} end local canAddCooldown = function(cooldownInfo) local petNpcIdNeeded = cooldownInfo.pet if (petNpcIdNeeded) then if (not playerHasPetOfNpcId(petNpcIdNeeded)) then return false end end return true end local getSpellListAsHashTableFromSpellBook = function() local completeListOfSpells = {} --this line might not be compatible with classic local specId, specName, _, specIconTexture = GetSpecializationInfo(GetSpecialization()) --local classNameLoc, className, classId = UnitClass("player") --not in use local locPlayerRace, playerRace, playerRaceId = UnitRace("player") --get racials from the general tab local tabName, tabTexture, offset, numSpells, isGuild, offspecId = GetSpellTabInfo(CONST_SPELLBOOK_GENERAL_TABID) offset = offset + 1 local tabEnd = offset + numSpells for entryOffset = offset, tabEnd - 1 do local spellType, spellId = GetSpellBookItemInfo(entryOffset, "player") if (spellId and LIB_OPEN_RAID_COOLDOWNS_INFO[spellId] and LIB_OPEN_RAID_COOLDOWNS_INFO[spellId].raceid == playerRaceId) then spellId = C_SpellBook.GetOverrideSpell(spellId) local spellName = GetSpellInfo(spellId) local bIsPassive = IsPassiveSpell(spellId, "player") if (spellName and not bIsPassive) then completeListOfSpells[spellId] = true end end end --get spells from the Spec spellbook for i = 1, GetNumSpellTabs() do local tabName, tabTexture, offset, numSpells, isGuild, offspecId = GetSpellTabInfo(i) if (tabTexture == specIconTexture) then offset = offset + 1 local tabEnd = offset + numSpells for entryOffset = offset, tabEnd - 1 do local spellType, spellId = GetSpellBookItemInfo(entryOffset, "player") if (spellId) then if (spellType == "SPELL") then spellId = C_SpellBook.GetOverrideSpell(spellId) local spellName = GetSpellInfo(spellId) local bIsPassive = IsPassiveSpell(spellId, "player") if LIB_OPEN_RAID_MULTI_OVERRIDE_SPELLS[spellId] then for _, overrideSpellId in pairs(LIB_OPEN_RAID_MULTI_OVERRIDE_SPELLS[spellId]) do completeListOfSpells[overrideSpellId] = true end elseif (spellName and not bIsPassive) then completeListOfSpells[spellId] = true end end end end end end --get class shared spells from the spell book local tabName, tabTexture, offset, numSpells, isGuild, offspecId = GetSpellTabInfo(CONST_SPELLBOOK_CLASSSPELLS_TABID) offset = offset + 1 local tabEnd = offset + numSpells for entryOffset = offset, tabEnd - 1 do local spellType, spellId = GetSpellBookItemInfo(entryOffset, "player") if (spellId) then if (spellType == "SPELL") then spellId = C_SpellBook.GetOverrideSpell(spellId) local spellName = GetSpellInfo(spellId) local bIsPassive = IsPassiveSpell(spellId, "player") if LIB_OPEN_RAID_MULTI_OVERRIDE_SPELLS[spellId] then for _, overrideSpellId in pairs(LIB_OPEN_RAID_MULTI_OVERRIDE_SPELLS[spellId]) do completeListOfSpells[overrideSpellId] = true end elseif (spellName and not bIsPassive) then completeListOfSpells[spellId] = true end end end end local getNumPetSpells = function() --'HasPetSpells' contradicts the name and return the amount of pet spells available instead of a boolean return HasPetSpells() end --get pet spells from the pet spellbook local numPetSpells = getNumPetSpells() if (numPetSpells) then for i = 1, numPetSpells do local spellName, _, unmaskedSpellId = GetSpellBookItemName(i, "pet") if (unmaskedSpellId) then unmaskedSpellId = C_SpellBook.GetOverrideSpell(unmaskedSpellId) local bIsPassive = IsPassiveSpell(unmaskedSpellId, "pet") if (spellName and not bIsPassive) then completeListOfSpells[unmaskedSpellId] = true end end end end return completeListOfSpells end local updateCooldownAvailableList = function() table.wipe(LIB_OPEN_RAID_PLAYERCOOLDOWNS) local _, playerClass = UnitClass("player") local locPlayerRace, playerRace, playerRaceId = UnitRace("player") local spellBookSpellList = getSpellListAsHashTableFromSpellBook() --build a list of all spells assigned as cooldowns for the player class for spellID, spellData in pairs(LIB_OPEN_RAID_COOLDOWNS_INFO) do --type 10 is an item cooldown and does not have a class or raceid if (spellData.class == playerClass or spellData.raceid == playerRaceId or CONST_ISITEM_BY_TYPEID[spellData.type]) then --need to implement here to get the racial as racial cooldowns does not carry a class --type 10 is an item cooldown and does not have a spellbook entry if (spellBookSpellList[spellID] or CONST_ISITEM_BY_TYPEID[spellData.type]) then LIB_OPEN_RAID_PLAYERCOOLDOWNS[spellID] = spellData end end end end --build a list with the local player cooldowns --called only from SendAllPlayerCooldowns() function openRaidLib.CooldownManager.GetPlayerCooldownList() --update the list of cooldowns the player has available if (IsDragonflight()) then --this fill the global LIB_OPEN_RAID_PLAYERCOOLDOWNS updateCooldownAvailableList() --get the player specId local specId = openRaidLib.GetPlayerSpecId() if (specId) then --get the cooldowns for the specialization local playerCooldowns = LIB_OPEN_RAID_PLAYERCOOLDOWNS if (not playerCooldowns) then openRaidLib.DiagnosticError("CooldownManager|GetPlayerCooldownList|LIB_OPEN_RAID_PLAYERCOOLDOWNS is nil") return {}, {} end local cooldowns = {} --table to ship on comm local cooldownsHash = {} --table with [spellId] = cooldownInfo local talentsHash = openRaidLib.UnitInfoManager.GetPlayerTalentsAsPairsTable() local timeNow = GetTime() for cooldownSpellId, cooldownInfo in pairs(playerCooldowns) do --does this cooldown is based on a talent? local talentId = cooldownInfo.talent --check if the player has a talent which makes this cooldown unavailable local ignoredByTalentId = cooldownInfo.ignoredIfTalent local bIsIgnoredByTalentId = false if (ignoredByTalentId) then if (talentsHash[ignoredByTalentId]) then bIsIgnoredByTalentId = true end end if (not bIsIgnoredByTalentId) then if (talentId) then --check if the player has the talent selected if (talentsHash[talentId]) then if (canAddCooldown(cooldownInfo)) then addCooldownToTable(cooldowns, cooldownsHash, cooldownSpellId, timeNow) end end else if (canAddCooldown(cooldownInfo)) then addCooldownToTable(cooldowns, cooldownsHash, cooldownSpellId, timeNow) end end end end return cooldowns, cooldownsHash else return {}, {} end end return {}, {} end --aura frame handles only UNIT_AURA events to grab the duration of the buff placed by the aura local bIsNewUnitAuraAvailable = C_UnitAuras and C_UnitAuras.GetAuraDataBySlot and true local auraSpellID local auraDurationTime local handleBuffAura = function(aura) local auraInfo = C_UnitAuras.GetAuraDataByAuraInstanceID("player", aura.auraInstanceID) if (auraInfo) then local spellId = auraInfo.spellId if (auraSpellID == spellId) then auraSpellID = nil auraDurationTime = auraInfo.duration return true end end end local getAuraDuration = function(spellId) --some auras does not have the same spellId of the cast as the spell for its aura duration --in these cases, it's necessary to declare the buff spellId which tells the duration of the effect by adding 'durationSpellId = spellId' within the cooldown data if (not LIB_OPEN_RAID_PLAYERCOOLDOWNS[spellId]) then --local spellname = GetSpellInfo(spellId) --print("spell not found:", spellname) return 0 end local customBuffDuration = LIB_OPEN_RAID_PLAYERCOOLDOWNS[spellId].durationSpellId --spellId = customBuffDuration or spellId --can't replace the spellId by customBuffDurationSpellId has it wount be found in LIB_OPEN_RAID_PLAYERCOOLDOWNS if (bIsNewUnitAuraAvailable) then local bBatchCount = false local bUsePackedAura = true auraSpellID = customBuffDuration or spellId auraDurationTime = 0 --reset duration AuraUtil.ForEachAura("player", "HELPFUL", bBatchCount, handleBuffAura, bUsePackedAura) --check auras to find a buff for the spellId if (auraDurationTime == 0) then --if the buff wasn't found, attempt to get the duration from the file return LIB_OPEN_RAID_PLAYERCOOLDOWNS[spellId].duration or 0 end return auraDurationTime else --this is classic end end ---get the duration of a buff placed by a spell ---@param spellId number ---@return number duration function openRaidLib.CooldownManager.GetSpellBuffDuration(spellId) return getAuraDuration(spellId) end ---check if a player cooldown is ready or if is in cooldown ---@spellId: the spellId to check for cooldown ---@return number timeLeft ---@return number charges ---@return number startTimeOffset ---@return number duration ---@return number buffDuration function openRaidLib.CooldownManager.GetPlayerCooldownStatus(spellId) --check if is a charge spell local spellData = LIB_OPEN_RAID_COOLDOWNS_INFO[spellId] if (spellData) then local buffDuration = getAuraDuration(spellId) local chargesAvailable, chargesTotal, start, duration = GetSpellCharges(spellId) if chargesAvailable then if (chargesAvailable == chargesTotal) then return 0, chargesTotal, 0, 0, 0 --all charges are ready to use else --return the time to the next charge local timeLeft = start + duration - GetTime() local startTimeOffset = start - GetTime() return ceil(timeLeft), chargesAvailable, startTimeOffset, duration, buffDuration end else local start, duration = GetSpellCooldown(spellId) if (start == 0) then --cooldown is ready return 0, 1, 0, 0, 0 --time left, charges, startTime else local timeLeft = start + duration - GetTime() local startTimeOffset = start - GetTime() return ceil(timeLeft), 0, ceil(startTimeOffset), duration, buffDuration --time left, charges, startTime, duration, buffDuration end end else return openRaidLib.DiagnosticError("CooldownManager|GetPlayerCooldownStatus()|cooldownInfo not found|", spellId) end end do --make new namespace openRaidLib.AuraTracker = {} function openRaidLib.AuraTracker.ScanCallback(aura) local unitId = openRaidLib.AuraTracker.CurrentUnitId local thisUnitAuras = openRaidLib.AuraTracker.CurrentAuras[unitId] local auraInfo = C_UnitAuras.GetAuraDataByAuraInstanceID(unitId, aura.auraInstanceID) if (auraInfo) then local spellId = auraInfo.spellId if (spellId) then thisUnitAuras[spellId] = true openRaidLib.AuraTracker.AurasFoundOnScan[spellId] = true end end end function openRaidLib.AuraTracker.ScanUnitAuras(unitId) local batchCount = nil local usePackedAura = true openRaidLib.AuraTracker.CurrentUnitId = unitId openRaidLib.AuraTracker.AurasFoundOnScan = {} AuraUtil.ForEachAura(unitId, "HELPFUL", batchCount, openRaidLib.AuraTracker.ScanCallback, usePackedAura) local thisUnitAuras = openRaidLib.AuraTracker.CurrentAuras[unitId] for spellId in pairs(thisUnitAuras) do if (not openRaidLib.AuraTracker.AurasFoundOnScan[spellId]) then --aura removed openRaidLib.internalCallback.TriggerEvent("unitAuraRemoved", unitId, spellId) end end end --run when the open raid lib loads function openRaidLib.AuraTracker.StartScanUnitAuras(unitId) openRaidLib.AuraTracker.CurrentAuras = { [unitId] = {} } local auraFrameEvent = CreateFrame("frame") auraFrameEvent:RegisterUnitEvent("UNIT_AURA", unitId) auraFrameEvent:SetScript("OnEvent", function() openRaidLib.AuraTracker.ScanUnitAuras(unitId) end) end --test case: local debugModule = {} function debugModule.AuraRemoved(event, unitId, spellId) local spellName = GetSpellInfo(spellId) --print("aura removed:", unitId, spellId, spellName) end openRaidLib.internalCallback.RegisterCallback("unitAuraRemoved", debugModule.AuraRemoved) end --which is the main attribute of each spec --1 Intellect --2 Agility --3 Strenth openRaidLib.specAttribute = { ["DEMONHUNTER"] = { [577] = 2, [581] = 2, }, ["DEATHKNIGHT"] = { [250] = 3, [251] = 3, [252] = 3, }, ["WARRIOR"] = { [71] = 3, [72] = 3, [73] = 3, }, ["MAGE"] = { [62] = 1, [63] = 1, [64] = 1, }, ["ROGUE"] = { [259] = 2, [260] = 2, [261] = 2, }, ["DRUID"] = { [102] = 1, [103] = 2, [104] = 2, [105] = 1, }, ["HUNTER"] = { [253] = 2, [254] = 2, [255] = 2, }, ["SHAMAN"] = { [262] = 1, [263] = 2, [264] = 1, }, ["PRIEST"] = { [256] = 1, [257] = 1, [258] = 1, }, ["WARLOCK"] = { [265] = 1, [266] = 1, [267] =1 , }, ["PALADIN"] = { [65] = 1, [66] = 3, [70] = 3, }, ["MONK"] = { [268] = 2, [269] = 2, [270] = 1, }, ["EVOKER"] = { [1467] = 1, --Devastation [1468] = 1, --Preservation }, }