--[=[
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 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 )
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 ( 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 ( 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
if ( spellData.class == playerClass or spellData.raceid == playerRaceId ) then --need to implement here to get the racial as racial cooldowns does not carry a class
if ( spellBookSpellList [ spellID ] ) 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
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 timeLeft, charges, startTimeOffset, duration, buffDuration
function openRaidLib . CooldownManager . GetPlayerCooldownStatus ( spellId )
--check if is a charge spell
local cooldownInfo = LIB_OPEN_RAID_COOLDOWNS_INFO [ spellId ]
if ( cooldownInfo ) 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 --time left, charges, startTime, 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
--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
} ,
}