local ADDON_NAME , Internal = ...
local L = Internal.L
local UnitClass = UnitClass ;
local GetClassColor = C_ClassColor.GetClassColor ;
local LOCALIZED_CLASS_NAMES_MALE = LOCALIZED_CLASS_NAMES_MALE ;
local LearnPvpTalent = LearnPvpTalent ;
local GetPvpTalentSlotInfo = C_SpecializationInfo.GetPvpTalentSlotInfo ;
local GetPvpTalentUnlockLevel = C_SpecializationInfo.GetPvpTalentUnlockLevel ;
local GetAllSelectedPvpTalentIDs = C_SpecializationInfo.GetAllSelectedPvpTalentIDs ;
local GetPvpTalentSlotInfoForSpecID = Internal.GetPvpTalentSlotInfoForSpecID ;
local GetSpecialization = GetSpecialization ;
local GetSpecializationInfo = GetSpecializationInfo ;
local GetSpecializationInfoByID = GetSpecializationInfoByID ;
local UIDropDownMenu_SetText = UIDropDownMenu_SetText ;
local UIDropDownMenu_EnableDropDown = UIDropDownMenu_EnableDropDown ;
local UIDropDownMenu_DisableDropDown = UIDropDownMenu_DisableDropDown ;
local UIDropDownMenu_SetSelectedValue = UIDropDownMenu_SetSelectedValue ;
local AddSet = Internal.AddSet ;
local format = string.format ;
local HelpTipBox_Anchor = Internal.HelpTipBox_Anchor ;
local HelpTipBox_SetText = Internal.HelpTipBox_SetText ;
local function CompareSets ( a , b )
if not tCompare ( a.talents , b.talents , 10 ) then
return false
end
if type ( a.restrictions ) ~= type ( b.restrictions ) and not tCompare ( a.restrictions , b.restrictions , 10 ) then
return false
end
return true
end
-- Make sure talent sets dont have incorrect id, call from GetTalentSet and the UI?
local function FixPvPTalentSet ( set )
local changed = false
for talentID in pairs ( set.talents ) do
local available = Internal.VerifyPvPTalentForSpec ( set.specID , talentID )
if not available then
set.talents [ talentID ] = nil
changed = true
end
end
return changed
end
local function UpdateSetFilters ( set )
local specID = set.specID ;
local filters = set.filters or { }
Internal.UpdateRestrictionFilters ( set )
filters.spec = specID
if specID then
filters.role , filters.class = select ( 5 , GetSpecializationInfoByID ( specID ) )
else
filters.role , filters.class = nil , nil
end
-- Rebuild character list
filters.character = filters.character or { }
local characters = filters.character
table.wipe ( characters )
local class = filters.class
for _ , character in Internal.CharacterIterator ( ) do
if class == Internal.GetCharacterInfo ( character ) . class then
characters [ # characters + 1 ] = character
end
end
set.filters = filters
return set
end
local function GetPvPTalentSet ( id )
if type ( id ) == " table " then
return id ;
else
return BtWLoadoutsSets.pvptalents [ id ] ;
end
end
local function PvPTalentSetIsValid ( set )
local set = GetPvPTalentSet ( set ) ;
local playerSpecID = GetSpecializationInfo ( GetSpecialization ( ) ) ;
local playerClass = select ( 2 , UnitClass ( " player " ) ) ;
local specClass = select ( 6 , GetSpecializationInfoByID ( set.specID ) ) ;
return true , ( playerClass == specClass ) , ( playerSpecID == set.specID )
end
local function IsPvPTalentSetActive ( set )
local playerLevel = UnitLevel ( " player " )
local talents = { } ;
local slots = { }
-- Clone the talents list so we can remove things as needed
for talentID in pairs ( set.talents ) do
if GetPvpTalentUnlockLevel ( talentID ) <= playerLevel then
talents [ talentID ] = true ;
end
end
-- All the talents arent available yet so we are as active as we can get
if next ( talents ) == nil then
return true
end
local index = 1
local slotInfo = GetPvpTalentSlotInfo ( index )
while slotInfo do
if slotInfo.enabled then
if slotInfo.selectedTalentID and talents [ slotInfo.selectedTalentID ] then
talents [ slotInfo.selectedTalentID ] = nil
else
slots [ slotInfo ] = true
end
end
index = index + 1
slotInfo = GetPvpTalentSlotInfo ( index )
end
-- All the talents that are available are currenctly active
if next ( talents ) == nil then
return true
end
for slotInfo in pairs ( slots ) do
for _ , talentID in ipairs ( slotInfo.availableTalentIDs ) do
-- One of the talents that is available can go in a free slot so we arent active yet
if talents [ talentID ] then
return false
end
end
end
return true ;
end
local function ActivatePvPTalentSet ( set , state )
local success , complete = true , true ;
local talents = { } ;
local usedSlots = { } ;
for talentID in pairs ( set.talents ) do
talents [ talentID ] = true ;
end
local index = 1
local slotInfo = GetPvpTalentSlotInfo ( index )
while slotInfo do
local talentID = slotInfo.selectedTalentID ;
if talentID and talents [ talentID ] then
usedSlots [ index ] = true ;
talents [ talentID ] = nil ;
end
index = index + 1
slotInfo = GetPvpTalentSlotInfo ( index )
end
if state.conflictAndStrife then
local talentIDs = GetAllSelectedPvpTalentIDs ( )
for _ , talentID in pairs ( talentIDs ) do
if talents [ talentID ] then
talents [ talentID ] = nil ;
end
end
end
local index = 1
local slotInfo = GetPvpTalentSlotInfo ( index )
while slotInfo do
if not usedSlots [ index ] and slotInfo.enabled then
for _ , talentID in ipairs ( slotInfo.availableTalentIDs ) do
if talents [ talentID ] then
local slotSuccess = LearnPvpTalent ( talentID , index )
success = slotSuccess and success ;
complete = false
usedSlots [ index ] = true ;
talents [ talentID ] = nil ;
Internal.LogMessage ( " Switching pvp talent %d to %s (%s) " , index , GetPvpTalentLink ( talentID ) , slotSuccess and " true " or " false " )
break ;
end
end
end
index = index + 1
slotInfo = GetPvpTalentSlotInfo ( index )
end
return complete , false
end
local function RefreshPvPTalentSet ( set )
local talents = set.talents or { }
wipe ( talents )
local talentIDs = GetAllSelectedPvpTalentIDs ( ) ;
for _ , talentID in ipairs ( talentIDs ) do
talents [ talentID ] = true ;
end
set.talents = talents
return UpdateSetFilters ( set )
end
local function AddPvPTalentSet ( )
local specIndex = GetSpecialization ( )
if specIndex == 5 then
specIndex = 1
end
local specID , specName = GetSpecializationInfo ( specIndex ) ;
return AddSet ( " pvptalents " , RefreshPvPTalentSet ( {
specID = specID ,
name = format ( L [ " New %s Set " ] , specName ) ,
useCount = 0 ,
talents = { } ,
} ) )
end
local function GetPvPTalentSetsByName ( name )
return Internal.GetSetsByName ( " pvptalents " , name )
end
local function GetPvPTalentSetByName ( name )
return Internal.GetSetByName ( " pvptalents " , name , PvPTalentSetIsValid )
end
local function GetPvPTalentSets ( id , ... )
if id ~= nil then
return BtWLoadoutsSets.pvptalents [ id ] , Internal.GetPvPTalentSets ( ... ) ;
end
end
function Internal . GetPvPTalentSetIfNeeded ( id )
if id == nil then
return ;
end
local set = Internal.GetPvPTalentSet ( id ) ;
if IsPvPTalentSetActive ( set ) then
return ;
end
return set ;
end
local function CombinePvPTalentSets ( result , state , ... )
result = result or { } ;
result.talents = { } ;
for i = 1 , select ( ' # ' , ... ) do
local set = select ( i , ... ) ;
if Internal.AreRestrictionsValidForPlayer ( set.restrictions ) then
for talentID in pairs ( set.talents ) do
if result.talents [ talentID ] == nil then
result.talents [ talentID ] = true ;
end
end
end
end
if state and state.blockers and not IsPvPTalentSetActive ( result ) then
state.blockers [ Internal.GetRestedTomeBlocker ( ) ] = true
state.blockers [ Internal.GetCombatBlocker ( ) ] = true
end
return result ;
end
local function DeletePvPTalentSet ( id )
Internal.DeleteSet ( BtWLoadoutsSets.pvptalents , id ) ;
if type ( id ) == " table " then
id = id.setID ;
end
for _ , set in pairs ( BtWLoadoutsSets.profiles ) do
if type ( set ) == " table " then
for index , setID in ipairs ( set.pvptalents ) do
if setID == id then
table.remove ( set.pvptalents , index )
end
end
end
end
local frame = BtWLoadoutsFrame.PvPTalents ;
local set = frame.set ;
if set.setID == id then
frame.set = nil ; -- = select(2,next(BtWLoadoutsSets.pvptalents)) or {};
BtWLoadoutsFrame : Update ( ) ;
end
end
local function CheckErrors ( errorState , set )
set = GetPvPTalentSet ( set )
errorState.specID = errorState.specID or set.specID
if errorState.specID ~= set.specID then
return L [ " Incompatible Specialization " ]
end
if not Internal.AreRestrictionsValidFor ( set.restrictions , errorState.specID ) then
return L [ " Incompatible Restrictions " ]
end
end
Internal.FixPvPTalentSet = FixPvPTalentSet
Internal.GetPvPTalentSet = GetPvPTalentSet
Internal.GetPvPTalentSetsByName = GetPvPTalentSetsByName
Internal.GetPvPTalentSetByName = GetPvPTalentSetByName
Internal.AddPvPTalentSet = AddPvPTalentSet
Internal.RefreshPvPTalentSet = RefreshPvPTalentSet
Internal.DeletePvPTalentSet = DeletePvPTalentSet
Internal.ActivatePvPTalentSet = ActivatePvPTalentSet
Internal.IsPvPTalentSetActive = IsPvPTalentSetActive
Internal.CombinePvPTalentSets = CombinePvPTalentSets
Internal.GetPvPTalentSets = GetPvPTalentSets
-- Initializes the set dropdown menu for the Loadouts page
local function SetDropDownInit ( self , set , index )
Internal.SetDropDownInit ( self , set , index , " pvptalents " , BtWLoadoutsFrame.PvPTalents )
end
Internal.AddLoadoutSegment ( {
id = " pvptalents " ,
name = L [ " PvP Talents " ] ,
after = " essences " , -- Essences can give pvp talents
events = " PLAYER_PVP_TALENT_UPDATE " ,
add = AddPvPTalentSet ,
get = GetPvPTalentSets ,
getByName = GetPvPTalentSetByName ,
combine = CombinePvPTalentSets ,
isActive = IsPvPTalentSetActive ,
activate = ActivatePvPTalentSet ,
dropdowninit = SetDropDownInit ,
checkerrors = CheckErrors ,
export = function ( set )
return {
version = 1 ,
name = set.name ,
specID = set.specID ,
talents = set.talents ,
restrictions = set.restrictions ,
}
end ,
import = function ( source , version , name , ... )
assert ( version == 1 )
local specID = source.specID or ...
return AddSet ( " pvptalents " , UpdateSetFilters ( {
specID = specID ,
name = name or source.name ,
useCount = 0 ,
talents = source.talents ,
restrictions = source.restrictions ,
} ) )
end ,
getByValue = function ( set )
return Internal.GetSetByValue ( BtWLoadoutsSets.pvptalents , set , CompareSets )
end ,
verify = function ( source , ... )
local specID = source.specID or ...
if not specID or not GetSpecializationInfoByID ( specID ) then
return false , L [ " Invalid specialization " ]
end
if type ( source.talents ) ~= " table " then
return false , L [ " Missing talents " ]
end
if source.restrictions ~= nil and type ( source.restrictions ) ~= " table " then
return false , L [ " Missing restrictions " ]
end
-- @TODO verify talent ids?
return true
end ,
} )
local function CompareTalentList ( a , b )
if # a ~= # b then
return false
end
for i = 1 , # a do
if a [ i ] ~= b [ i ] then
return false
end
end
return true
end
BtWLoadoutsPvPTalentsMixin = { }
function BtWLoadoutsPvPTalentsMixin : OnLoad ( )
self.RestrictionsDropDown : SetSupportedTypes ( " covenant " , " race " )
self.RestrictionsDropDown : SetScript ( " OnChange " , function ( )
self : Update ( )
end )
self.temp = { } ; -- Stores talents for currently unselected specs incase the user switches to them
self.GridPool = CreateFramePool ( " FRAME " , self , " BtWLoadoutsTalentSelectionTemplate " )
end
function BtWLoadoutsPvPTalentsMixin : OnShow ( )
if not self.initialized then
self.SpecDropDown . includeNone = false ;
self.SpecDropDown . includeClass = false ;
UIDropDownMenu_SetWidth ( self.SpecDropDown , 170 ) ;
UIDropDownMenu_JustifyText ( self.SpecDropDown , " LEFT " ) ;
self.SpecDropDown . GetValue = function ( )
if self.set then
return self.set . specID
end
end
self.SpecDropDown . SetValue = function ( _ , _ , arg1 )
CloseDropDownMenus ( ) ;
local set = self.set ;
if set then
local temp = self.temp ;
-- @TODO: If we always access talents by set.talents then we can just swap tables in and out of
-- the temp table instead of copying the talentIDs around
-- We are going to copy the currently selected talents for the currently selected spec into
-- a temporary table incase the user switches specs back
local specID = set.specID ;
if temp [ specID ] then
wipe ( temp [ specID ] ) ;
else
temp [ specID ] = { } ;
end
for talentID in pairs ( set.talents ) do
temp [ specID ] [ talentID ] = true ;
end
-- Clear the current talents and copy back the previously selected talents if they exist
specID = arg1 ;
set.specID = specID ;
wipe ( set.talents ) ;
if temp [ specID ] then
for talentID in pairs ( temp [ specID ] ) do
set.talents [ talentID ] = true ;
end
end
self : Update ( )
end
end
self.initialized = true ;
end
end
function BtWLoadoutsPvPTalentsMixin : ChangeSet ( set )
self.set = set
wipe ( self.temp ) ;
self : Update ( )
end
function BtWLoadoutsPvPTalentsMixin : UpdateSetName ( value )
if self.set and self.set . name ~= not value then
self.set . name = value ;
self : Update ( ) ;
end
end
function BtWLoadoutsPvPTalentsMixin : OnButtonClick ( button )
CloseDropDownMenus ( )
if button.isAdd then
self.Name : ClearFocus ( ) ;
self : ChangeSet ( AddPvPTalentSet ( ) )
C_Timer.After ( 0 , function ( )
self.Name : HighlightText ( ) ;
self.Name : SetFocus ( ) ;
end )
elseif button.isDelete then
local set = self.set ;
if set.useCount > 0 then
StaticPopup_Show ( " BTWLOADOUTS_DELETEINUSESET " , set.name , nil , {
set = set ,
func = DeletePvPTalentSet ,
} ) ;
else
StaticPopup_Show ( " BTWLOADOUTS_DELETESET " , set.name , nil , {
set = set ,
func = DeletePvPTalentSet ,
} ) ;
end
elseif button.isRefresh then
local set = self.set ;
RefreshPvPTalentSet ( set )
self : Update ( )
elseif button.isExport then
local set = self.set ;
self : GetParent ( ) : SetExport ( Internal.Export ( " pvptalents " , set.setID ) )
elseif button.isActivate then
local set = self.set ;
if select ( 6 , GetSpecializationInfoByID ( set.specID ) ) == select ( 2 , UnitClass ( " player " ) ) then
Internal.ActivateProfile ( {
pvptalents = { set.setID }
} ) ;
end
end
end
function BtWLoadoutsPvPTalentsMixin : OnSidebarItemClick ( button )
CloseDropDownMenus ( )
if button.isHeader then
button.collapsed [ button.id ] = not button.collapsed [ button.id ]
self : Update ( )
else
if IsModifiedClick ( " SHIFT " ) then
local set = GetPvPTalentSet ( button.id ) ;
if select ( 6 , GetSpecializationInfoByID ( set.specID ) ) == select ( 2 , UnitClass ( " player " ) ) then
Internal.ActivateProfile ( {
pvptalents = { button.id }
} ) ;
end
else
self.Name : ClearFocus ( ) ;
self : ChangeSet ( GetPvPTalentSet ( button.id ) )
end
end
end
function BtWLoadoutsPvPTalentsMixin : OnSidebarItemDoubleClick ( button )
CloseDropDownMenus ( )
if button.isHeader then
return
end
local set = GetPvPTalentSet ( button.id ) ;
if select ( 6 , GetSpecializationInfoByID ( set.specID ) ) == select ( 2 , UnitClass ( " player " ) ) then
Internal.ActivateProfile ( {
pvptalents = { button.id }
} ) ;
end
end
function BtWLoadoutsPvPTalentsMixin : OnSidebarItemDragStart ( button )
CloseDropDownMenus ( )
if button.isHeader then
return
end
local icon = " INV_Misc_QuestionMark " ;
local set = GetPvPTalentSet ( button.id ) ;
local command = format ( " /btwloadouts activate pvptalents %d " , button.id ) ;
if set.specID then
icon = select ( 4 , GetSpecializationInfoByID ( set.specID ) ) ;
end
if command then
local macroId ;
local numMacros = GetNumMacros ( ) ;
for i = 1 , numMacros do
if GetMacroBody ( i ) : trim ( ) == command then
macroId = i ;
break ;
end
end
if not macroId then
if numMacros == MAX_ACCOUNT_MACROS then
print ( L [ " Cannot create any more macros " ] ) ;
return ;
end
if InCombatLockdown ( ) then
print ( L [ " Cannot create macros while in combat " ] ) ;
return ;
end
macroId = CreateMacro ( set.name , icon , command , false ) ;
else
-- Rename the macro while not in combat
if not InCombatLockdown ( ) then
icon = select ( 2 , GetMacroInfo ( macroId ) )
EditMacro ( macroId , set.name , icon , command )
end
end
if macroId then
PickupMacro ( macroId ) ;
end
end
end
function BtWLoadoutsPvPTalentsMixin : Update ( )
self : GetParent ( ) : SetTitle ( L [ " PvP Talents " ] ) ;
local sidebar = BtWLoadoutsFrame.Sidebar
sidebar : SetSupportedFilters ( " spec " , " class " , " role " , " character " , " covenant " , " race " )
sidebar : SetSets ( BtWLoadoutsSets.pvptalents )
sidebar : SetCollapsed ( BtWLoadoutsCollapsed.pvptalents )
sidebar : SetCategories ( BtWLoadoutsCategories.pvptalents )
sidebar : SetFilters ( BtWLoadoutsFilters.pvptalents )
sidebar : SetSelected ( self.set )
sidebar : Update ( )
self.set = sidebar : GetSelected ( )
local set = self.set
local showingNPE = BtWLoadoutsFrame : SetNPEShown ( set == nil , L [ " PvP Talents " ] , L [ " Create different pvp talent layouts for the type of content you wish to do. " ] )
self : GetParent ( ) . ExportButton : SetEnabled ( true )
self : GetParent ( ) . DeleteButton : SetEnabled ( true ) ;
if not showingNPE then
local specID = set.specID
UpdateSetFilters ( set )
sidebar : Update ( )
set.restrictions = set.restrictions or { }
self.RestrictionsDropDown : SetSelections ( set.restrictions )
self.RestrictionsDropDown : SetLimitations ( )
self.RestrictionsButton : SetEnabled ( true ) ;
if not self.Name : HasFocus ( ) then
self.Name : SetText ( self.set . name or " " ) ;
end
local _ , specName , _ , icon , _ , classID = GetSpecializationInfoByID ( specID ) ;
local className = LOCALIZED_CLASS_NAMES_MALE [ classID ] ;
local classColor = GetClassColor ( classID ) ;
UIDropDownMenu_SetText ( self.SpecDropDown , format ( " %s: %s " , classColor : WrapTextInColorCode ( className ) , specName ) ) ;
do
self.GridPool : ReleaseAll ( )
local previous
local index = 1
local slotInfo = GetPvpTalentSlotInfoForSpecID ( specID , index )
while slotInfo do
local slotGrid
for grid in self.GridPool : EnumerateActive ( ) do
if CompareTalentList ( grid.talents , slotInfo.availableTalentIDs ) then
grid : SetMaxSelections ( grid : GetMaxSelections ( ) + 1 )
slotGrid = grid
end
end
if not slotGrid then
slotGrid = self.GridPool : Acquire ( )
slotGrid : SetTalents ( slotInfo.availableTalentIDs , true )
slotGrid : SetMaxSelections ( 1 )
if previous then
slotGrid : SetPoint ( " TOP " , previous , " BOTTOM " )
else
slotGrid : SetPoint ( " TOPLEFT " , 0 , - 38 )
end
slotGrid : Show ( )
previous = slotGrid
end
index = index + 1
slotInfo = GetPvpTalentSlotInfoForSpecID ( specID , index )
end
end
local playerSpecIndex = GetSpecialization ( )
self : GetParent ( ) . RefreshButton : SetEnabled ( playerSpecIndex and specID == GetSpecializationInfo ( playerSpecIndex ) )
self : GetParent ( ) . ActivateButton : SetEnabled ( classID == select ( 2 , UnitClass ( " player " ) ) ) ;
local helpTipBox = self : GetParent ( ) . HelpTipBox ;
helpTipBox : Hide ( ) ;
else
local specIndex = GetSpecialization ( )
if not specIndex or specIndex == 5 then
specIndex = 1
end
local specID , specName = GetSpecializationInfo ( specIndex ) ;
self.Name : SetText ( format ( L [ " New %s Set " ] , specName ) ) ;
local _ , specName , _ , icon , _ , classID = GetSpecializationInfoByID ( specID ) ;
local className = LOCALIZED_CLASS_NAMES_MALE [ classID ] ;
local classColor = GetClassColor ( classID ) ;
UIDropDownMenu_SetText ( self.SpecDropDown , format ( " %s: %s " , classColor : WrapTextInColorCode ( className ) , specName ) ) ;
do
self.GridPool : ReleaseAll ( )
local previous
local index = 1
local slotInfo = GetPvpTalentSlotInfoForSpecID ( specID , index )
while slotInfo do
local slotGrid
for grid in self.GridPool : EnumerateActive ( ) do
if CompareTalentList ( grid.talents , slotInfo.availableTalentIDs ) then
grid : SetMaxSelections ( grid : GetMaxSelections ( ) + 1 )
slotGrid = grid
end
end
if not slotGrid then
slotGrid = self.GridPool : Acquire ( )
slotGrid : SetTalents ( slotInfo.availableTalentIDs , true )
slotGrid : SetMaxSelections ( 1 )
if previous then
slotGrid : SetPoint ( " TOP " , previous , " BOTTOM " )
else
slotGrid : SetPoint ( " TOPLEFT " , 0 , - 38 )
end
slotGrid : Show ( )
previous = slotGrid
end
index = index + 1
slotInfo = GetPvpTalentSlotInfoForSpecID ( specID , index )
end
end
local helpTipBox = self : GetParent ( ) . HelpTipBox ;
helpTipBox : Hide ( ) ;
end
end
function BtWLoadoutsPvPTalentsMixin : SetSetByID ( setID )
self.set = GetPvPTalentSet ( setID )
end