You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
571 lines
20 KiB
571 lines
20 KiB
--[=[
|
|
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 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 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
|
|
for i = INVSLOT_FIRST_EQUIPPED, INVSLOT_LAST_EQUIPPED do
|
|
local durability, maxDurability = GetInventoryItemDurability(i)
|
|
if (durability and maxDurability) then
|
|
local itemDurability = durability / maxDurability * 100
|
|
durabilityTotalPercent = durabilityTotalPercent + itemDurability
|
|
totalItems = totalItems + 1
|
|
end
|
|
end
|
|
|
|
if (totalItems == 0) then
|
|
return 100
|
|
end
|
|
|
|
return floor(durabilityTotalPercent / totalItems)
|
|
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
|
|
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 = {}
|
|
|
|
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]
|
|
if (enchantAttribute) then --this slot can receive an enchat
|
|
|
|
--check if this slot is relevant for the class, some slots can have enchants only for Agility which won't matter for Priests as an example
|
|
--if the value is an integer it points to an attribute (int, dex, str), otherwise it's true (boolean)
|
|
local slotIsRelevant = true
|
|
if (type(enchantAttribute) == "number") then
|
|
if (specMainAttribute ~= enchantAttribute) then
|
|
slotIsRelevant = false
|
|
end
|
|
end
|
|
|
|
if (slotIsRelevant) then
|
|
--does the slot has any enchant?
|
|
if (not enchantId or enchantId == "0" or enchantId == "") then
|
|
slotsWithoutEnchant[#slotsWithoutEnchant+1] = equipmentSlotId
|
|
else
|
|
--convert to integer
|
|
local enchantIdInt = tonumber(enchantId)
|
|
if (enchantIdInt) then
|
|
--does the enchant is relevent for the character?
|
|
if (not LIB_OPEN_RAID_ENCHANT_IDS[enchantIdInt]) then
|
|
slotsWithoutEnchant[#slotsWithoutEnchant+1] = equipmentSlotId
|
|
end
|
|
else
|
|
--the enchat has an invalid id
|
|
slotsWithoutEnchant[#slotsWithoutEnchant+1] = equipmentSlotId
|
|
end
|
|
end
|
|
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 (not LIB_OPEN_RAID_GEM_IDS[gemId]) then
|
|
slotsWithoutGems[#slotsWithoutGems+1] = equipmentSlotId
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
return slotsWithoutGems, slotsWithoutEnchant
|
|
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)
|
|
cooldowns[#cooldowns+1] = cooldownSpellId
|
|
local timeLeft, charges, startTimeOffset, duration = openRaidLib.CooldownManager.GetPlayerCooldownStatus(cooldownSpellId)
|
|
cooldowns[#cooldowns+1] = timeLeft
|
|
cooldowns[#cooldowns+1] = charges
|
|
cooldowns[#cooldowns+1] = startTimeOffset
|
|
cooldowns[#cooldowns+1] = duration
|
|
cooldownsHash[cooldownSpellId] = {timeLeft, charges, startTimeOffset, duration, timeNow}
|
|
end
|
|
|
|
local canAddCooldown = function(cooldownInfo)
|
|
local needPetNpcId = cooldownInfo.pet
|
|
if (needPetNpcId) then
|
|
if (not playerHasPetOfNpcId(needPetNpcId)) then
|
|
return false
|
|
end
|
|
end
|
|
return true
|
|
end
|
|
|
|
--build a list with the local player cooldowns
|
|
--called only from SendAllPlayerCooldowns()
|
|
function openRaidLib.CooldownManager.GetPlayerCooldownList()
|
|
--get the player specId
|
|
local specId = openRaidLib.GetPlayerSpecId()
|
|
if (specId) then
|
|
--get the cooldowns for the specialization
|
|
local playerCooldowns = LIB_OPEN_RAID_COOLDOWNS_BY_SPEC[specId]
|
|
if (not playerCooldowns) then
|
|
openRaidLib.DiagnosticError("CooldownManager|GetPlayerCooldownList|can't find player cooldowns for specId:", specId)
|
|
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, cooldownType in pairs(playerCooldowns) do
|
|
--get all the information about this cooldow
|
|
local cooldownInfo = LIB_OPEN_RAID_COOLDOWNS_INFO[cooldownSpellId]
|
|
if (cooldownInfo) then
|
|
--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 isIgnoredByTalentId = false
|
|
if (ignoredByTalentId) then
|
|
if (talentsHash[ignoredByTalentId]) then
|
|
isIgnoredByTalentId = true
|
|
end
|
|
end
|
|
|
|
if (not isIgnoredByTalentId) 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
|
|
end
|
|
return cooldowns, cooldownsHash
|
|
else
|
|
return {}, {}
|
|
end
|
|
end
|
|
|
|
--check if a player cooldown is ready or if is in cooldown
|
|
--@spellId: the spellId to check for cooldown
|
|
function openRaidLib.CooldownManager.GetPlayerCooldownStatus(spellId)
|
|
--check if is a charge spell
|
|
local cooldownInfo = LIB_OPEN_RAID_COOLDOWNS_INFO[spellId]
|
|
if (cooldownInfo) then
|
|
if (cooldownInfo.charges and cooldownInfo.charges > 1) then
|
|
local chargesAvailable, chargesTotal, start, duration = GetSpellCharges(spellId)
|
|
|
|
if (chargesAvailable == chargesTotal) then
|
|
return 0, chargesTotal, 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 --time left, charges, startTime
|
|
end
|
|
|
|
else
|
|
local start, duration = GetSpellCooldown(spellId)
|
|
if (start == 0) then --cooldown is ready
|
|
return 0, 1, 0, 0 --time left, charges, startTime
|
|
else
|
|
local timeLeft = start + duration - GetTime()
|
|
local startTimeOffset = start - GetTime()
|
|
return ceil(timeLeft), 0, ceil(startTimeOffset), duration --time left, charges, startTime, duration
|
|
end
|
|
end
|
|
else
|
|
return openRaidLib.DiagnosticError("CooldownManager|GetPlayerCooldownStatus()|cooldownInfo not found|", 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
|
|
},
|
|
}
|
|
|