1830359943
Release Documentation: - Classic now uses the same combat log reader as retail (Flamanis). - Merged Rage of Fyr'alath spells (equara). - Added Rogue Ambushes to merged spells (WillowGryph). - The Remove Common Segments option now also removes segments trash between raid bosses. - Fixed an issue where auras applied before combat start, such as Power Infusion and Prescience, which are counted towards the target, were not being accounted for. - Added to Combat Class: classCombat:GetRunTimeNoDefault(). This returns the run time of the Mythic+ if available, nil otherwise. Technical Notes: - Classic now uses retail parser. - Combat class now have the member: classCombat:GetRunTimeNoDefault(); Returns the run time of a M+ (after completed). - The Utility class's buff scan at the start of combat has been improved, and the code has been cleaned. Also, the scan runs now on the next frame after combat start. - Augmentation Evoker won't track auras from the combat start aura scan, if the player isn't in combat (example: a player in the group enters in combat). - Remove tier bonus for Augmentation Evoker Ebon Might damage prediction and nerfed Close as Cluthmates to 10%. - Segments Container's ResetDataByCombatType() now supports multiple combat types per classification. - Code cleanup on Segments menu code to use the new Mythic+ functions added to Combat class. - Mythic+ start detection produced errors if a WORLD_STATE_TIMER_START event triggered before the CHALLENGE_MODE_START event. - Mythic+ finish code was bugging when 'time' returned by C_ChallengeMode.GetCompletionInfo() wasn't being checked again nil value. - Rogue's Ambush ability and Rage of Fyr'alath spellIds added to override_spellId within the parser. - Details! Framework updated. - Open Raid Library updated.
979 lines
35 KiB
Lua
979 lines
35 KiB
Lua
--[=[
|
|
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")
|
|
local spellData = LIB_OPEN_RAID_COOLDOWNS_INFO[spellId]
|
|
if (spellData) then
|
|
local raceId = spellData.raceid
|
|
if (raceId) then
|
|
if (type(raceId) == "table") then
|
|
if (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
|
|
|
|
elseif (type(raceId) == "number") then
|
|
if (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
|
|
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
|
|
|
|
else
|
|
if (not spellName) then
|
|
--print("no spellname")
|
|
--print(GetSpellInfo(spellId))
|
|
elseif (bIsPassive) then
|
|
--print("is passive")
|
|
--print(GetSpellInfo(spellId))
|
|
end
|
|
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 race id
|
|
|
|
local passRaceId = false
|
|
local raceId = spellData.raceid
|
|
if (raceId) then
|
|
if (type(raceId) == "table") then
|
|
if (raceId[playerRaceId]) then
|
|
passRaceId = true
|
|
end
|
|
elseif (type(raceId) == "number") then
|
|
if (raceId == playerRaceId) then
|
|
passRaceId = true
|
|
end
|
|
end
|
|
end
|
|
|
|
if (spellData.class == playerClass or passRaceId 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 specializationid
|
|
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 auraUnitId
|
|
|
|
local handleBuffAura = function(aura)
|
|
local auraInfo = C_UnitAuras.GetAuraDataByAuraInstanceID(auraUnitId, 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, unitId)
|
|
--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
|
|
auraUnitId = unitId or "player"
|
|
|
|
AuraUtil.ForEachAura(auraUnitId, "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
|
|
---@param unitId string?
|
|
---@return number duration
|
|
function openRaidLib.CooldownManager.GetSpellBuffDuration(spellId, unitId)
|
|
return getAuraDuration(spellId, unitId)
|
|
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
|
|
|
|
|
|
do
|
|
local getUnitName = function(unitId)
|
|
local unitName, realmName = UnitName(unitId)
|
|
if (unitName) then
|
|
if (realmName and realmName ~= "") then
|
|
unitName = unitName .. "-" .. realmName
|
|
end
|
|
return unitName
|
|
end
|
|
end
|
|
|
|
local predicateFunc = function(spellIdToFind, casterName, _, name, icon, applications, dispelName, duration, expirationTime, sourceUnitId, isStealable, nameplateShowPersonal, spellId, canApplyAura, isBossAura, isFromPlayerOrPlayerPet, nameplateShowAll, timeMod, applications)
|
|
if (spellIdToFind == spellId and UnitExists(sourceUnitId)) then
|
|
if (casterName == getUnitName(sourceUnitId)) then
|
|
return true
|
|
end
|
|
end
|
|
end
|
|
|
|
---find the duration of a debuff by passing the spellId and the caster name
|
|
---@param unitId unit
|
|
---@param spellId spellid
|
|
---@param casterName actorname
|
|
---@return auraduration|nil auraDuration
|
|
---@return number|nil expirationTime
|
|
function openRaidLib.AuraTracker.FindBuffDuration(unitId, casterName, spellId)
|
|
local name, texture, count, buffType, duration, expirationTime = AuraUtil.FindAura(predicateFunc, unitId, "HELPFUL", spellId, casterName)
|
|
if (name) then
|
|
return duration, expirationTime
|
|
end
|
|
end
|
|
|
|
---find the duration of a buff placed by a unit
|
|
---@param targetString string
|
|
---@param casterString string
|
|
---@param spellId number
|
|
function openRaidLib.AuraTracker.FindBuffDurationByUnitName(targetString, casterString, spellId)
|
|
local targetName = Ambiguate(targetString, "none")
|
|
local casterName = Ambiguate(casterString, "none")
|
|
return openRaidLib.AuraTracker.FindBuffDuration(targetName, casterName, spellId)
|
|
end
|
|
|
|
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
|
|
[1473] = 1, --Augmentation
|
|
},
|
|
}
|