local ADDON_NAME , Internal = ...
local L = Internal.L
local External = { }
_G [ ADDON_NAME ] = External
local HelpTipBox_Anchor = Internal.HelpTipBox_Anchor ;
local HelpTipBox_SetText = Internal.HelpTipBox_SetText ;
local IsResting = IsResting ;
local UnitAura = UnitAura ;
local UnitClass = UnitClass ;
local UnitLevel = UnitLevel ;
local UnitCastingInfo = UnitCastingInfo ;
local GetClassColor = C_ClassColor.GetClassColor ;
local LOCALIZED_CLASS_NAMES_MALE = LOCALIZED_CLASS_NAMES_MALE ;
local GetItemCount = GetItemCount ;
local GetItemInfo = GetItemInfo ;
local SetSpecialization = SetSpecialization ;
local GetSpecialization = GetSpecialization ;
local GetNumSpecializations = GetNumSpecializations ;
local GetSpecializationInfo = GetSpecializationInfo ;
local GetSpecializationInfoByID = GetSpecializationInfoByID ;
local StaticPopup_Show = StaticPopup_Show ;
local StaticPopup_Hide = StaticPopup_Hide ;
local StaticPopup_Visible = StaticPopup_Visible ;
local UIDropDownMenu_SetText = UIDropDownMenu_SetText ;
local UIDropDownMenu_EnableDropDown = UIDropDownMenu_EnableDropDown ;
local UIDropDownMenu_DisableDropDown = UIDropDownMenu_DisableDropDown ;
local UIDropDownMenu_SetSelectedValue = UIDropDownMenu_SetSelectedValue ;
local format = string.format ;
local AddSet = Internal.AddSet ;
local DeleteSet = Internal.DeleteSet ;
local GetCharacterInfo = Internal.GetCharacterInfo ;
local GetCharacterSlug = Internal.GetCharacterSlug
local loadoutSegments = { }
local loadoutSegmentsByID = { }
local loadoutSegmentsUIOrder = { }
-- We need to add a small delay after switching specs before changing other things because Blizzard is
-- still changing things after the cast is finished
local specChangeInfo = {
spellId = 200749 , -- 200749 is the changing spec spell id
endTime = nil ,
castGUID = nil ,
}
local function IsChangingSpec ( verifyCastGUID )
if not specChangeInfo.endTime then
return false
end
if specChangeInfo.endTime + .5 < GetTime ( ) then
specChangeInfo.endTime = nil
specChangeInfo.castGUID = nil
return false
end
if verifyCastGUID ~= nil and specChangeInfo.castGUID ~= verifyCastGUID then
return false
end
return true
end
-- Activating a set can take multiple passes, things maybe delayed
-- by switching spec or waiting for the player to use a tome
local target = { } ;
local targetstate = { } ;
-- Handles events during loadout changing
local eventHandler = CreateFrame ( " Frame " ) ;
eventHandler : Hide ( ) ;
local function HideLoadoutPopups ( )
StaticPopup_Hide ( " BTWLOADOUTS_PARTIAL " )
StaticPopup_Hide ( " BTWLOADOUTS_NEEDITEM " )
StaticPopup_Hide ( " BTWLOADOUTS_NEEDITEMPARTIAL " )
end
local uiErrorTracking
local function CancelActivateProfile ( )
C_Timer.After ( 1 , function ( )
UIErrorsFrame : Clear ( )
if uiErrorTracking then
UIErrorsFrame : RegisterEvent ( " UI_ERROR_MESSAGE " )
end
uiErrorTracking = nil
end )
wipe ( target ) ;
wipe ( targetstate ) ;
HideLoadoutPopups ( )
eventHandler : UnregisterAllEvents ( ) ;
eventHandler : Hide ( ) ;
Internal.Call ( " LOADOUT_CHANGE_END " )
Internal.LogMessage ( " --- END --- " )
end
Internal.CancelActivateProfile = CancelActivateProfile ;
local function ContinuePartialActivateProfile ( )
target.dirty = true
target.allowPartial = true
Internal.LogMessage ( " Allow partial activation " )
end
Internal.ContinuePartialActivateProfile = ContinuePartialActivateProfile ;
local function ContinueIgnoreItemActivateProfile ( )
target.dirty = true
target.ignoreItem = true
Internal.LogMessage ( " Ignore using items for activation " )
end
Internal.ContinueIgnoreItemActivateProfile = ContinueIgnoreItemActivateProfile ;
local function ContinueIgnoreChainsActivateProfile ( )
target.dirty = true
target.ignoreJailersChains = true
end
Internal.ContinueIgnoreChainsActivateProfile = ContinueIgnoreChainsActivateProfile ;
local errorState = { } -- Reusable state for checking loadouts for errors
local function LoadoutHasErrors ( set )
wipe ( errorState )
errorState.specID = set.specID
for _ , segment in ipairs ( loadoutSegments ) do
if segment.enabled and set [ segment.id ] then
for index , subsetID in ipairs ( set [ segment.id ] ) do
if segment.checkerrors then
local error = segment.checkerrors ( errorState , subsetID )
if error then
return true , errorState.specID
end
end
end
end
end
return false , errorState.specID
end
local function GetLoadoutErrors ( errors , set )
wipe ( errorState )
errorState.specID = set.specID
local hasError = false
for _ , segment in ipairs ( loadoutSegments ) do
if segment.enabled and set [ segment.id ] then
local segmenterrors = errors [ segment.id ]
if not segmenterrors then
segmenterrors = { }
errors [ segment.id ] = segmenterrors
else
wipe ( errors [ segment.id ] )
end
for index , subsetID in ipairs ( set [ segment.id ] ) do
if segment.checkerrors then
local error = segment.checkerrors ( errorState , subsetID )
if error then
hasError = true
errors [ segment.id ] [ index ] = error
end
end
end
end
end
return hasError , errors , errorState.specID
end
-- Checks of a loadout is activatable
local function IsLoadoutActivatable ( set )
local specID = set.specID
if specID and not Internal.CanSwitchToSpecialization ( specID ) then
return false
end
return true
end
local function UpdateSetFilters ( set )
local specID = set.specID
local filters = set.filters or { }
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
if type ( filters.character ) ~= " table " then
filters.character = { }
end
local characters = filters.character
wipe ( characters )
if type ( set.character ) == " table " and next ( set.character ) ~= nil then
for character in pairs ( set.character ) do
characters [ # characters + 1 ] = character
end
else
local class = filters.class
for _ , character in Internal.CharacterIterator ( ) do
if class == Internal.GetCharacterInfo ( character ) . class or specID == nil then
characters [ # characters + 1 ] = character
end
end
end
filters.disabled = set.disabled ~= true and 0 or 1
set.filters = filters
return set
end
local function AddProfile ( )
local set = {
name = L [ " New Loadout " ] ,
version = 2 ,
useCount = 0 ,
}
for _ , segment in ipairs ( loadoutSegments ) do
set [ segment.id ] = { }
end
return AddSet ( " profiles " , UpdateSetFilters ( set ) )
end
local function GetProfile ( id )
return BtWLoadoutsSets.profiles [ id ] ;
end
local function GetProfileByName ( name )
for _ , set in pairs ( BtWLoadoutsSets.profiles ) do
if type ( set ) == " table " and set.name : lower ( ) : trim ( ) == name : lower ( ) : trim ( ) then
return set ;
end
end
end
Internal.GetProfileByName = GetProfileByName
local function DeleteProfile ( id )
do
local set = type ( id ) == " table " and id or GetProfile ( id ) ;
for _ , segment in ipairs ( loadoutSegments ) do
local ids = set [ segment.id ]
if ids then
for _ , id in ipairs ( ids ) do
local subSet = segment.get ( id )
subSet.useCount = ( subSet.useCount or 1 ) - 1 ;
end
end
end
-- Disconnect conditions for the deleted loadout
for _ , superset in pairs ( BtWLoadoutsSets.conditions ) do
if type ( superset ) == " table " and superset.profileSet == set.setID then
Internal.RemoveConditionFromMap ( superset ) ;
superset.profileSet = nil ;
end
end
end
DeleteSet ( BtWLoadoutsSets.profiles , id ) ;
local frame = BtWLoadoutsFrame.Loadouts ;
local set = frame.set ;
if set == id or set.setID == id then
frame.set = nil ; --select(2,next(BtWLoadoutsSets.profiles)) or {};
BtWLoadoutsFrame : Update ( ) ;
end
end
local function ActivateProfile ( profile )
local hasErrors , specID = LoadoutHasErrors ( profile )
if not hasErrors and not IsLoadoutActivatable ( profile ) then
--@TODO display an error
return ;
end
target.name = profile.name
target.state = targetstate
if specID then
target.specID = specID or profile.specID ;
end
for _ , segment in ipairs ( loadoutSegments ) do
local id = segment.id
if segment.enabled and profile [ id ] and # profile [ id ] > 0 then
target [ id ] = target [ id ] or { } ;
for _ , setID in ipairs ( profile [ id ] ) do
target [ id ] [ # target [ id ] + 1 ] = setID ;
end
end
end
if not target.active then
Internal.Call ( " LOADOUT_CHANGE_START " )
Internal.ClearLog ( )
Internal.LogMessage ( " --- START --- " )
Internal.LogMessage ( format ( " %s: %s " , ( select ( 2 , GetAddOnInfo ( ADDON_NAME ) ) ) , ( GetAddOnMetadata ( ADDON_NAME , " Version " ) ) ) )
else
Internal.Call ( " LOADOUT_CHANGE_APPEND " )
Internal.LogMessage ( " --- APPEND --- " )
end
target.active = true
target.dirty = true ;
eventHandler : RegisterEvent ( " GET_ITEM_INFO_RECEIVED " ) ;
eventHandler : RegisterEvent ( " PLAYER_REGEN_DISABLED " ) ;
eventHandler : RegisterEvent ( " PLAYER_REGEN_ENABLED " ) ;
eventHandler : RegisterEvent ( " PLAYER_UPDATE_RESTING " ) ;
eventHandler : RegisterUnitEvent ( " UNIT_AURA " , " player " ) ;
eventHandler : RegisterEvent ( " PLAYER_SPECIALIZATION_CHANGED " ) ;
eventHandler : RegisterEvent ( " ACTIVE_TALENT_GROUP_CHANGED " ) ;
eventHandler : RegisterEvent ( " ZONE_CHANGED " ) ;
eventHandler : RegisterEvent ( " ZONE_CHANGED_INDOORS " ) ;
eventHandler : RegisterEvent ( " ITEM_UNLOCKED " ) ;
eventHandler : RegisterEvent ( " SPELL_UPDATE_COOLDOWN " ) ;
eventHandler : RegisterEvent ( " PLAYER_STOPPED_MOVING " ) ;
eventHandler : RegisterEvent ( " PLAYER_TALENT_UPDATE " ) ;
eventHandler : RegisterEvent ( " PLAYER_LEARN_TALENT_FAILED " ) ;
eventHandler : RegisterEvent ( " PLAYER_PVP_TALENT_UPDATE " ) ;
eventHandler : RegisterEvent ( " PLAYER_LEARN_PVP_TALENT_FAILED " ) ;
eventHandler : RegisterEvent ( " BAG_UPDATE_DELAYED " ) ;
if C_Covenants then -- Skip for pre-Shadowlands
eventHandler : RegisterEvent ( " SOULBIND_ACTIVATED " ) ;
end
eventHandler : RegisterUnitEvent ( " UNIT_SPELLCAST_START " , " player " ) ;
eventHandler : RegisterUnitEvent ( " UNIT_SPELLCAST_INTERRUPTED " , " player " ) ;
eventHandler : Show ( ) ;
end
local IsProfileActive , AddWipeCacheEvents
do
local state = { }
local temp = { }
local function IsActive ( set )
if set.specID and UnitLevel ( " player " ) >= 10 then
local playerSpecID = GetSpecializationInfo ( GetSpecialization ( ) ) ;
if set.specID ~= playerSpecID then
return false ;
end
end
wipe ( state )
for _ , segment in ipairs ( loadoutSegments ) do
local ids = set [ segment.id ]
if segment.enabled and ids and # ids > 0 then
wipe ( temp ) ;
segment.combine ( temp , state , segment.get ( unpack ( ids ) ) ) ;
if not segment.isActive ( temp ) then
return false ;
end
end
end
return true ;
end
local activeLoadoutCache = setmetatable ( { } , {
__index = function ( self , key )
if type ( key ) == " number " then
local result = self [ GetProfile ( key ) ]
self [ key ] = result
return result
elseif type ( key ) == " table " then
local result = IsActive ( key )
self [ key ] = result
return result
end
end ,
} ) ;
function IsProfileActive ( set )
return activeLoadoutCache [ set ]
end
local wipeEventHandler = CreateFrame ( " Frame " ) ;
wipeEventHandler : Hide ( ) ;
wipeEventHandler : SetScript ( " OnEvent " , function ( )
wipe ( activeLoadoutCache ) ;
end ) ;
function AddWipeCacheEvents ( ... )
for i = 1 , select ( ' # ' , ... ) do
local event = select ( i , ... )
wipeEventHandler : RegisterEvent ( event )
end
end
end
local function IsLoadoutEnabled ( set )
if set.disabled then
return false
end
if set.character ~= nil and next ( set.character ) ~= nil then
local character = Internal.GetCharacterSlug ( )
return set.character [ character ] ~= nil
end
return true
end
function Internal . ImportLoadout ( source , version , name )
assert ( version == 2 )
local set = {
name = name ,
version = 2 ,
useCount = 0 ,
specID = source.specID
}
for _ , segment in Internal.EnumerateLoadoutSegments ( ) do
if source [ segment.id ] then
set [ segment.id ] = { unpack ( source [ segment.id ] ) }
else
set [ segment.id ] = { }
end
end
return AddSet ( " profiles " , UpdateSetFilters ( set ) )
end
local function GetActiveProfiles ( )
if target.active then
if target.name then
return format ( L [ " Changing to %s... " ] , target.name )
else
return L [ " Changing... " ]
end
end
local activeProfiles = { }
for _ , profile in pairs ( BtWLoadoutsSets.profiles ) do
if type ( profile ) == " table " and IsLoadoutEnabled ( profile ) and IsProfileActive ( profile ) then
activeProfiles [ # activeProfiles + 1 ] = profile.name
end
end
if # activeProfiles == 0 then
return nil
end
table.sort ( activeProfiles )
return table.concat ( activeProfiles , " / " ) ;
end
local combinedSets = { }
local blockers = { }
local blockerTemp = { }
local function ContinueActivateProfile ( )
local set = target
local state = target.state
set.dirty = false
if Internal.CheckTimeout ( ) then
Internal.LogMessage ( " --- TIMEOUT --- " )
CancelActivateProfile ( )
return
end
Internal.LogNewPass ( )
Internal.SetWaitReason ( ) -- Clear wait reason
Internal.UpdateLauncher ( GetActiveProfiles ( ) ) ;
if IsChangingSpec ( ) then
Internal.SetWaitReason ( L [ " Waiting for specialization change " ] )
HideLoadoutPopups ( )
return ;
end
wipe ( state )
wipe ( blockers )
local segments = 0
local specID = set.specID ;
local playerSpecID = GetSpecializationInfo ( GetSpecialization ( ) )
if specID ~= nil and specID ~= playerSpecID then
-- Need to change spec
segments = segments + 1
blockers [ Internal.GetTaxiBlocker ( ) ] = 1
blockers [ Internal.GetFlyingBlocker ( ) ] = 1
blockers [ Internal.GetMovingBlocker ( ) ] = 1
blockers [ Internal.GetCombatBlocker ( ) ] = 1
blockers [ Internal.GetMythicPlusBlocker ( ) ] = 1
blockers [ Internal.GetJailersChainBlocker ( ) ] = 1
end
state.ignoreItem = target.ignoreItem
state.allowPartial = target.allowPartial
wipe ( combinedSets )
for _ , segment in ipairs ( loadoutSegments ) do
if segment.enabled and target [ segment.id ] then
wipe ( blockerTemp )
state.blockers = blockerTemp
combinedSets [ segment.id ] = segment.combine ( combinedSets [ segment.id ] , state , segment.get ( unpack ( target [ segment.id ] ) ) )
segments = segments + 1
for blocker , complete in pairs ( blockerTemp ) do
blockers [ blocker ] = ( blockers [ blocker ] or 0 ) + ( complete and 1 or 0 )
end
end
end
for blocker , count in pairs ( blockers ) do
if count == segments and blocker : IsActive ( state ) and not blocker : UsableItem ( ) then
if blocker : ShouldWait ( state ) then
Internal.SetWaitReason ( blocker : GetWaitReasonMessage ( ) )
else
CancelActivateProfile ( )
end
return
end
end
if not target.allowPartial then
local supportPartialMessages = { }
local supportPartial = false
for blocker , count in pairs ( blockers ) do
if count ~= segments and blocker : IsActive ( state ) and not blocker : UsableItem ( ) then
if blocker : ShouldWait ( state ) then
Internal.SetWaitReason ( blocker : GetWaitReasonMessage ( ) )
end
local message = blocker : PopupMessagePartial ( )
supportPartialMessages [ # supportPartialMessages + 1 ] = message
supportPartial = true
end
end
if supportPartial then
if # supportPartialMessages > 0 then
StaticPopup_Hide ( " BTWLOADOUTS_NEEDITEMPARTIAL " )
StaticPopup_Hide ( " BTWLOADOUTS_NEEDITEM " )
if not StaticPopup_Visible ( " BTWLOADOUTS_PARTIAL " ) then
if # supportPartialMessages > 1 then
StaticPopup_Show ( " BTWLOADOUTS_PARTIAL " , L [ " Your loadout cannot be completely activated " ] ) ;
else
StaticPopup_Show ( " BTWLOADOUTS_PARTIAL " , supportPartialMessages [ 1 ] ) ;
end
end
end
return
end
end
if specID ~= nil and specID ~= playerSpecID and UnitLevel ( " player " ) >= 10 then
for specIndex = 1 , GetNumSpecializations ( ) do
if GetSpecializationInfo ( specIndex ) == specID then
Internal.LogMessage ( " Switching specialization to %s " , ( select ( 2 , GetSpecializationInfo ( specIndex ) ) ) )
Internal.SetWaitReason ( L [ " Waiting for specialization change " ] )
SetSpecialization ( specIndex ) ;
target.dirty = false ;
return ;
end
end
end
if state.customWait then
Internal.SetWaitReason ( state.customWait )
HideLoadoutPopups ( )
return
end
if not target.ignoreItem then
for blocker , count in pairs ( blockers ) do
if blocker : IsActive ( state ) and blocker : UsableItem ( ) and blocker : ShouldWait ( state ) then
Internal.SetWaitReason ( blocker : GetWaitReasonMessage ( ) )
local itemID , name , link , quality , icon = blocker : UsableItem ( ) ;
StaticPopup_Hide ( " BTWLOADOUTS_PARTIAL " )
if count == segments then
StaticPopup_Hide ( " BTWLOADOUTS_NEEDITEMPARTIAL " )
local message , item = blocker : PopupMessage ( )
if name ~= nil and not StaticPopup_Visible ( " BTWLOADOUTS_NEEDITEM " ) then
local r , g , b = GetItemQualityColor ( quality or 2 ) ;
StaticPopup_Show ( " BTWLOADOUTS_NEEDITEM " , message , item , { [ " texture " ] = icon , [ " name " ] = name , [ " color " ] = { r , g , b , 1 } , [ " link " ] = link , [ " count " ] = 1 } ) ;
elseif itemID == nil then
StaticPopup_Hide ( " BTWLOADOUTS_NEEDITEM " )
end
else
StaticPopup_Hide ( " BTWLOADOUTS_NEEDITEM " )
local message , item = blocker : PopupMessagePartial ( )
if name ~= nil and not StaticPopup_Visible ( " BTWLOADOUTS_NEEDITEMPARTIAL " ) then
local r , g , b = GetItemQualityColor ( quality or 2 ) ;
StaticPopup_Show ( " BTWLOADOUTS_NEEDITEMPARTIAL " , message , item , { [ " texture " ] = icon , [ " name " ] = name , [ " color " ] = { r , g , b , 1 } , [ " link " ] = link , [ " count " ] = 1 } ) ;
elseif itemID == nil then
StaticPopup_Hide ( " BTWLOADOUTS_NEEDITEMPARTIAL " )
end
end
return
end
end
end
HideLoadoutPopups ( )
if uiErrorTracking == nil then
uiErrorTracking = UIErrorsFrame : IsEventRegistered ( " UI_ERROR_MESSAGE " )
UIErrorsFrame : UnregisterEvent ( " UI_ERROR_MESSAGE " )
end
local complete = true ;
for _ , segment in ipairs ( loadoutSegments ) do
if segment.enabled and combinedSets [ segment.id ] then
local segmentComplete , segmentDirty = segment.activate ( combinedSets [ segment.id ] , state )
if not segmentComplete then
complete = false
end
if segmentDirty then
set.dirty = true
end
end
end
-- Done
if complete then
CancelActivateProfile ( ) ;
end
Internal.UpdateLauncher ( GetActiveProfiles ( ) ) ;
end
function Internal . DirtyAfter ( timer )
C_Timer.After ( timer , function ( )
target.dirty = true ;
end ) ;
end
eventHandler : SetScript ( " OnEvent " , function ( self , event , ... )
self [ event ] ( self , ... ) ;
end ) ;
function eventHandler : GET_ITEM_INFO_RECEIVED ( )
target.dirty = true ;
end
function eventHandler : PLAYER_REGEN_DISABLED ( )
HideLoadoutPopups ( )
end
function eventHandler : PLAYER_REGEN_ENABLED ( )
target.dirty = true ;
end
function eventHandler : PLAYER_UPDATE_RESTING ( )
target.dirty = true ;
end
function eventHandler : PLAYER_STOPPED_MOVING ( )
target.dirty = true ;
end
function eventHandler : UNIT_AURA ( )
C_Timer.After ( 1 , function ( )
target.dirty = true ;
end ) ;
end
function eventHandler : PLAYER_SPECIALIZATION_CHANGED ( ... )
-- Added delay just to be safe
C_Timer.After ( 1 , function ( )
target.dirty = true ;
end ) ;
end
function eventHandler : ACTIVE_TALENT_GROUP_CHANGED ( ... )
end
function eventHandler : ZONE_CHANGED ( ... )
target.dirty = true ;
end
eventHandler.ZONE_CHANGED_INDOORS = eventHandler.ZONE_CHANGED ;
function eventHandler : ITEM_UNLOCKED ( ... )
target.dirty = true ;
end
function eventHandler : PLAYER_TALENT_UPDATE ( ... )
target.dirty = true ;
end
function eventHandler : PLAYER_LEARN_TALENT_FAILED ( ... )
target.dirty = true ;
end
function eventHandler : PLAYER_PVP_TALENT_UPDATE ( ... )
target.dirty = true ;
end
function eventHandler : PLAYER_LEARN_PVP_TALENT_FAILED ( ... )
target.dirty = true ;
end
function eventHandler : BAG_UPDATE_DELAYED ( ... )
target.dirty = true ;
end
function eventHandler : SOULBIND_ACTIVATED ( ... )
target.dirty = true ;
end
function eventHandler : SPELL_UPDATE_COOLDOWN ( )
-- Added delay because it didnt seem to always trigger correctly
C_Timer.After ( 1 , function ( )
target.dirty = true ;
end ) ;
end
function eventHandler : UNIT_SPELLCAST_START ( )
local endTime , _ , castGUID , _ , spellId = select ( 5 , UnitCastingInfo ( " player " ) )
if spellId == specChangeInfo.spellId then
specChangeInfo.endTime = endTime / 1000
specChangeInfo.castGUID = castGUID
end
end
function eventHandler : UNIT_SPELLCAST_INTERRUPTED ( _ , castGUID , spellId , ... )
if spellId == specChangeInfo.spellId and IsChangingSpec ( castGUID ) then
CancelActivateProfile ( ) ;
Internal.UpdateLauncher ( GetActiveProfiles ( ) ) ;
end
end
eventHandler : SetScript ( " OnUpdate " , function ( self )
if target.dirty then
ContinueActivateProfile ( ) ;
end
end )
-- [[ Internal API ]]
-- Loadouts are split into segments, ... @TODO
do
local function MustBeBefore ( a , b )
if b.after then
for after in string.gmatch ( b.after , " [^,]+ " ) do
if after == a.id then
return true
end
end
end
return false
end
function Internal . AddLoadoutSegment ( details )
details.enabled = details.enabled == nil and true or details.enabled
if ( details.import or details.export or details.verify or details.getByValue ) and not ( details.import and details.export and details.verify and details.getByValue ) then
error ( L [ " Segments that support import/export must define import, export, verify, and getByValue functions " ] )
end
loadoutSegmentsUIOrder [ # loadoutSegmentsUIOrder + 1 ] = details
loadoutSegmentsByID [ details.id ] = details
if details.events then
AddWipeCacheEvents ( strsplit ( " , " , details.events ) )
end
for index , segment in ipairs ( loadoutSegments ) do
if MustBeBefore ( details , segment ) then
table.insert ( loadoutSegments , index , details )
return
end
end
loadoutSegments [ # loadoutSegments + 1 ] = details
end
function Internal . EnumerateLoadoutSegments ( )
return function ( tbl , index )
repeat
index = index + 1
until not tbl [ index ] or tbl [ index ] . enabled
if tbl [ index ] then
return index , tbl [ index ]
end
end , loadoutSegmentsUIOrder , 0
end
function Internal . GetLoadoutSegment ( id )
return loadoutSegmentsByID [ id ]
end
function Internal . SetLoadoutSegmentEnabled ( id , value )
loadoutSegmentsByID [ id ] . enabled = value and true or false
end
function Internal . GetLoadoutSegmentEnabled ( id )
return loadoutSegmentsByID [ id ] . enabled and true or false
end
end
function Internal . IsActivatingLoadout ( )
return target.active
end
function Internal . SetWaitReason ( reason )
if reason == nil then
target.timeout = target.timeout or ( GetTime ( ) + 10 ) -- Set a timeout
else
target.timeout = nil
end
target.currentWaitReason = reason
end
function Internal . CheckTimeout ( )
if not target.timeout then
return false
end
return target.timeout < GetTime ( )
end
function Internal . GetWaitReason ( )
return target.currentWaitReason
end
Internal.GetProfile = GetProfile
Internal.GetActiveProfiles = GetActiveProfiles
Internal.ActivateProfile = ActivateProfile
Internal.LoadoutHasErrors = LoadoutHasErrors
Internal.GetLoadoutErrors = GetLoadoutErrors
Internal.IsLoadoutActivatable = IsLoadoutActivatable
Internal.IsProfileActive = IsProfileActive
Internal.AddProfile = AddProfile
Internal.DeleteProfile = DeleteProfile
Internal.IsLoadoutEnabled = IsLoadoutEnabled
-- [[ External API ]]
-- Return: id, name, specID, character
function External . GetLoadoutInfo ( id )
local set = GetProfile ( id )
if not set then
return
end
return set.setID , set.name , set.specID , set.character
end
function External . IsLoadoutActive ( id )
local set = GetProfile ( id )
if not set then
return
end
return IsProfileActive ( set )
end
do
local loadouts = { }
-- Get a list of all loadouts
-- Return: id, ...
function External . GetLoadouts ( )
wipe ( loadouts ) ;
for id , set in pairs ( BtWLoadoutsSets.profiles ) do
if type ( set ) == " table " then
loadouts [ # loadouts + 1 ] = id
end
end
return unpack ( loadouts ) ;
end
-- Get a list of all currently active loadouts
-- Return: id, ...
function External . GetActiveLoadouts ( )
wipe ( loadouts ) ;
for id , set in pairs ( BtWLoadoutsSets.profiles ) do
if type ( set ) == " table " and IsProfileActive ( set ) then
loadouts [ # loadouts + 1 ] = id
end
end
return unpack ( loadouts ) ;
end
-- Get a list of all loadouts valid for the current character
-- Return: id, ...
function External . GetCharacterLoadouts ( )
wipe ( loadouts ) ;
for id , set in pairs ( BtWLoadoutsSets.profiles ) do
if type ( set ) == " table " and IsLoadoutActivatable ( set ) then
loadouts [ # loadouts + 1 ] = id
end
end
return unpack ( loadouts ) ;
end
end
function External . ActivateLoadout ( id )
local loadout
if type ( id ) == " number " then
loadout = GetProfile ( id )
else
loadout = GetProfileByName ( id )
end
if loadout == nil then
error ( L [ " Unknown loadout " .. tostring ( id ) ] )
end
ActivateProfile ( loadout )
end
BtWLoadoutsSetsScrollListItemMixin = { }
function BtWLoadoutsSetsScrollListItemMixin : OnLoad ( )
self : RegisterForDrag ( " LeftButton " ) ;
end
function BtWLoadoutsSetsScrollListItemMixin : Set ( item )
self.item = item
local button = self
if item and not item.ignore then
button.type = item.type
button.isAdd = item.isAdd
button.isHeader = item.isHeader
button.name = item.name
button.error = item.error
button.ErrorBorder : SetShown ( item.error ~= nil )
button.ErrorOverlay : SetShown ( item.error ~= nil )
if item.isSeparator then
button : Hide ( )
else
button.index = item.index
button.id = item.id
button : SetEnabled ( not item.isHeader )
if item.isHeader then
button.Name : SetPoint ( " LEFT " , 0 , 0 )
button.Name : SetTextColor ( 0.75 , 0.61 , 0 )
-- if item.isEmpty then
button.ExpandedIcon : Hide ( )
button.CollapsedIcon : Hide ( )
-- elseif item.isCollapsed then
-- button.ExpandedIcon:Hide()
-- button.CollapsedIcon:Show()
-- else
-- button.ExpandedIcon:Show()
-- button.CollapsedIcon:Hide()
-- end
button.AddButton : Show ( )
button.MoveButton : SetEnabled ( false )
button.MoveButton : Hide ( )
button.RemoveButton : SetEnabled ( false )
button.RemoveButton : Hide ( )
elseif item.isAdd then
button.Name : SetPoint ( " LEFT " , 15 , 0 )
button.Name : SetTextColor ( 0.973 , 0.937 , 0.580 )
button.AddButton : Hide ( )
button.MoveButton : SetEnabled ( false )
button.MoveButton : Hide ( )
button.RemoveButton : SetEnabled ( false )
button.RemoveButton : Hide ( )
button.ExpandedIcon : Hide ( )
button.CollapsedIcon : Hide ( )
else
button.Name : SetPoint ( " LEFT " , 15 , 0 )
button.Name : SetTextColor ( 1 , 1 , 1 )
button.AddButton : Hide ( )
button.MoveButton : SetEnabled ( true )
button.MoveButton : Hide ( )
button.RemoveButton : SetEnabled ( true )
button.RemoveButton : Hide ( )
-- button.AddButton:Hide()
-- button.RemoveButton:Show()
-- button.MoveDownButton:Show()
-- button.MoveUpButton:Show()
-- button.MoveUpButton:SetEnabled(not item.first)
-- button.MoveDownButton:SetEnabled(not item.last)
button.ExpandedIcon : Hide ( )
button.CollapsedIcon : Hide ( )
end
button.Name : SetText ( item.name )
button : Show ( ) ;
end
else
button : Hide ( ) ;
end
end
function BtWLoadoutsSetsScrollListItemMixin : OnClick ( )
if self.isHeader then
local frame = self : GetParent ( ) : GetParent ( ) : GetParent ( )
frame.Collapsed [ self.type ] = not frame.Collapsed [ self.type ]
frame : Update ( )
elseif self.isAdd then
self : Add ( self )
else
local DropDown = self : GetParent ( ) : GetParent ( ) . DropDown
local index = self.index
local segment = self.type
local tab = BtWLoadoutsFrame.Loadouts
Internal.GetLoadoutSegment ( segment ) . dropdowninit ( DropDown , tab.set , index )
-- DropDown:SetSelected(tab.set[segment][index])
-- DropDown:SetSets(BtWLoadoutsSets[segment])
-- DropDown:SetCategories(BtWLoadoutsCategories[segment])
-- DropDown:OnChange(function (newSetID)
-- local previousSetID = tab.set[segment][index]
-- print(previousSetID, newSetID)
-- if set.talents[index] then
-- local subset = Internal.GetTalentSet(set.talents[index]);
-- subset.useCount = (subset.useCount or 1) - 1;
-- end
-- end)
-- UIDropDownMenu_SetInitializeFunction(DropDown, function (self, level, menuList)
-- return Internal.GetLoadoutSegment(segment).dropdowninit(self, level, menuList, index)
-- end)
ToggleDropDownMenu ( nil , nil , DropDown , self , 0 , 0 )
PlaySound ( SOUNDKIT.IG_MAINMENU_OPTION_CHECKBOX_ON )
end
end
function BtWLoadoutsSetsScrollListItemMixin : OnEnter ( )
local scrollChild = self : GetParent ( )
local currentDrag = scrollChild.currentDrag
if currentDrag and currentDrag ~= self then
if self.isHeader or self.isAdd or self.type ~= currentDrag.type then
return
end
local frame = scrollChild : GetParent ( ) : GetParent ( )
local a , b = self.item , currentDrag.item
-- Flip the set ids within the loadout and flip their indexes too
frame.set [ a.type ] [ a.index ] , frame.set [ a.type ] [ b.index ] = frame.set [ a.type ] [ b.index ] , frame.set [ a.type ] [ a.index ]
a.index , b.index = b.index , a.index
-- Update the buttons with the flipped sets
self : Set ( b )
currentDrag : Set ( a )
self : GetParent ( ) . currentDrag = self
return
end
self.MoveButton : SetShown ( self.MoveButton : IsEnabled ( ) )
self.RemoveButton : SetShown ( self.RemoveButton : IsEnabled ( ) )
if self.error then
GameTooltip : SetOwner ( self , " ANCHOR_RIGHT " )
GameTooltip : SetText ( self.name , 1 , 1 , 1 )
GameTooltip : AddLine ( format ( " \n |cffff0000%s|r " , self.error ) )
GameTooltip : Show ( )
end
end
function BtWLoadoutsSetsScrollListItemMixin : OnLeave ( )
if not MouseIsOver ( self ) then
self.MoveButton : Hide ( )
self.RemoveButton : Hide ( )
end
GameTooltip : Hide ( )
end
function BtWLoadoutsSetsScrollListItemMixin : StartDrag ( )
if self.isHeader or self.isAdd then
return
end
self : GetParent ( ) . currentDrag = self
end
function BtWLoadoutsSetsScrollListItemMixin : Add ( button )
local DropDown = self : GetParent ( ) : GetParent ( ) . DropDown
local segment = self.type
local tab = BtWLoadoutsFrame.Loadouts
Internal.GetLoadoutSegment ( segment ) . dropdowninit ( DropDown , tab.set )
ToggleDropDownMenu ( nil , nil , DropDown , button , 0 , 0 )
PlaySound ( SOUNDKIT.IG_MAINMENU_OPTION_CHECKBOX_ON )
end
function BtWLoadoutsSetsScrollListItemMixin : Remove ( )
local tab = self : GetParent ( ) : GetParent ( ) : GetParent ( )
local set = tab.set
local index = self.index
assert ( type ( set [ self.type ] ) == " table " and index ~= nil and index >= 1 and index <= # set [ self.type ] )
table.remove ( set [ self.type ] , index ) ;
tab : Update ( )
end
function BtWLoadoutsSetsScrollListItemMixin : MoveUp ( )
local tab = self : GetParent ( ) : GetParent ( ) : GetParent ( )
local set = tab.set
local index = self.index
assert ( type ( set [ self.type ] ) == " table " and index > 1 and index <= # set [ self.type ] )
set [ self.type ] [ index - 1 ] , set [ self.type ] [ index ] = set [ self.type ] [ index ] , set [ self.type ] [ index - 1 ]
tab : Update ( )
end
function BtWLoadoutsSetsScrollListItemMixin : MoveDown ( )
local tab = self : GetParent ( ) : GetParent ( ) : GetParent ( )
local set = tab.set
local index = self.index
assert ( type ( set [ self.type ] ) == " table " and index >= 1 and index < # set [ self.type ] )
set [ self.type ] [ index + 1 ] , set [ self.type ] [ index ] = set [ self.type ] [ index ] , set [ self.type ] [ index + 1 ]
tab : Update ( )
end
local function SetsScrollFrameUpdate ( self )
self : GetScrollChild ( ) . currentDrag = nil -- Clear current drag
local buttons = self.buttons
local items = self.items
local offset = HybridScrollFrame_GetOffset ( self )
if not buttons then
return
end
local totalHeight , displayedHeight = # items * ( buttons [ 1 ] : GetHeight ( ) + 1 ) , self : GetHeight ( )
local hasScrollBar = totalHeight > displayedHeight
for i , button in ipairs ( buttons ) do
button : SetWidth ( hasScrollBar and 530 or 540 )
local item = items [ i + offset ]
button : Set ( item )
end
HybridScrollFrame_Update ( self , totalHeight , displayedHeight )
end
local function AddItem ( items , index )
local item = items [ index ] or { }
items [ index ] = item
wipe ( item )
return item , index + 1
end
local function BuildSubSetItems ( type , header , getcallback , sets , items , index , isCollapsed , errors )
local item
do
item , index = AddItem ( items , index )
item.name = header
item.type = type
item.isCollapsed = isCollapsed
item.isHeader = true
-- item.isEmpty = subset == nil
end
if not isCollapsed then
if sets and # sets > 0 then
for i , setID in ipairs ( sets ) do
local subset = getcallback ( setID )
item , index = AddItem ( items , index )
if subset.character then
local characterInfo = Internal.GetCharacterInfo ( subset.character ) ;
if characterInfo then
item.name = format ( " %s |cFFD5D5D5(%s - %s)|r " , subset.name , characterInfo.name , characterInfo.realm ) ;
else
item.name = format ( " %s |cFFD5D5D5(%s)|r " , subset.name , subset.character ) ;
end
else
item.name = subset.name ~= " " and subset.name or L [ " Unnamed " ] ;
end
item.error = errors [ i ]
item.type = type
item.index = i
item.id = subset.setID
item.first = i == 1
item.last = i == # sets
end
else
item , index = AddItem ( items , index )
item.type = type
item.name = L [ " Add " ]
item.isAdd = true
end
end
return index
end
local function AddSeparator ( items , index )
-- item, index = AddItem(items, index)
-- item.isSeparator = true
return index
end
local function BuildSetItems ( set , items , collapsed , errors )
local index = 1
for i , segment in Internal.EnumerateLoadoutSegments ( ) do
if i ~= 1 then
index = AddSeparator ( items , index )
end
index = BuildSubSetItems ( segment.id , segment.name , segment.get , set [ segment.id ] , items , index , collapsed [ segment.id ] , errors [ segment.id ] )
end
while items [ index ] do
table.remove ( items , index )
end
return items
end
local function shallowcopy ( tbl )
local result = { }
for k , v in pairs ( tbl ) do
result [ k ] = v
end
return result
end
-- Stores errors for currently viewed set
local errors = { }
BtWLoadoutsLoadoutsMixin = { }
function BtWLoadoutsLoadoutsMixin : OnLoad ( )
self : RegisterEvent ( " GLOBAL_MOUSE_UP " )
HybridScrollFrame_CreateButtons ( self.SetsScroll , " BtWLoadoutsSetsScrollListItemTemplate " , 4 , - 3 , " TOPLEFT " , " TOPLEFT " , 0 , - 1 , " TOP " , " BOTTOM " ) ;
self.SetsScroll . update = SetsScrollFrameUpdate ;
self.temp = { } -- Stores character restrictions for unselected specs
end
function BtWLoadoutsLoadoutsMixin : OnEvent ( )
if self.SetsScroll : GetScrollChild ( ) . currentDrag ~= nil then
self : GetParent ( ) : Update ( )
end
end
function BtWLoadoutsLoadoutsMixin : OnShow ( )
if not self.initialized then
self.SpecDropDown . includeNone = true ;
UIDropDownMenu_SetWidth ( self.SpecDropDown , 175 ) ;
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 classFile = set.specID and select ( 6 , GetSpecializationInfoByID ( set.specID ) )
self.temp [ classFile or " NONE " ] = set.character
set.specID = arg1 ;
classFile = set.specID and select ( 6 , GetSpecializationInfoByID ( set.specID ) )
set.character = self.temp [ classFile or " NONE " ] or shallowcopy ( set.character )
self : Update ( )
end
end
self.CharacterDropDown . GetValue = function ( self )
local frame = self : GetParent ( )
if frame.set == nil then
return { } -- Needs a table since its a multi select
end
if type ( frame.set . character ) ~= " table " then
frame.set . character = { }
end
return frame.set . character
end
self.CharacterDropDown . SetValue = function ( self , button , arg1 , arg2 , checked )
local frame = self : GetParent ( )
if frame.set then
if arg1 == nil then
wipe ( frame.set . character )
elseif frame.set . character [ arg1 ] then
frame.set . character [ arg1 ] = nil
else
frame.set . character [ arg1 ] = true
end
BtWLoadoutsFrame : Update ( )
end
end
UIDropDownMenu_SetWidth ( self.CharacterDropDown , 175 ) ;
UIDropDownMenu_JustifyText ( self.CharacterDropDown , " LEFT " ) ;
self.initialized = true ;
end
end
function BtWLoadoutsLoadoutsMixin : ChangeSet ( set )
self.set = set
wipe ( self.temp ) ;
self : Update ( )
end
function BtWLoadoutsLoadoutsMixin : UpdateSetEnabled ( value )
if self.set and self.set . disabled ~= not value then
self.set . disabled = not value ;
self : Update ( ) ;
end
end
function BtWLoadoutsLoadoutsMixin : UpdateSetName ( value )
if self.set and self.set . name ~= not value then
self.set . name = value ;
BtWLoadoutsHelpTipFlags [ " TUTORIAL_RENAME_SET " ] = true
self : Update ( ) ;
end
end
function BtWLoadoutsLoadoutsMixin : OnButtonClick ( button )
CloseDropDownMenus ( )
if button.isAdd then
BtWLoadoutsHelpTipFlags [ " TUTORIAL_NEW_SET " ] = true ;
self.Name : ClearFocus ( )
self : ChangeSet ( AddProfile ( ) )
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 = DeleteProfile ,
} )
else
StaticPopup_Show ( " BTWLOADOUTS_DELETESET " , set.name , nil , {
set = set ,
func = DeleteProfile ,
} )
end
elseif button.isRefresh then
-- Do nothing
elseif button.isExport then
local set = self.set ;
self : GetParent ( ) : SetExport ( External.Export ( set.setID ) )
elseif button.isActivate then
BtWLoadoutsHelpTipFlags [ " TUTORIAL_ACTIVATE_SET " ] = true
ActivateProfile ( self.set )
self : Update ( )
end
end
function BtWLoadoutsLoadoutsMixin : OnSidebarItemClick ( button )
CloseDropDownMenus ( )
if button.isHeader then
button.collapsed [ button.id ] = not button.collapsed [ button.id ]
self : Update ( )
else
if IsModifiedClick ( " SHIFT " ) then
ActivateProfile ( GetProfile ( button.id ) ) ;
else
self.Name : ClearFocus ( ) ;
self : ChangeSet ( GetProfile ( button.id ) ) ;
end
end
end
function BtWLoadoutsLoadoutsMixin : OnSidebarItemDoubleClick ( button )
CloseDropDownMenus ( )
if button.isHeader then
return
end
ActivateProfile ( GetProfile ( button.id ) ) ;
end
function BtWLoadoutsLoadoutsMixin : OnSidebarItemDragStart ( button )
CloseDropDownMenus ( )
if button.isHeader then
return
end
local icon = " INV_Misc_QuestionMark " ;
local set = GetProfile ( button.id ) ;
local command = format ( " /btwloadouts activate loadout %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 BtWLoadoutsLoadoutsMixin : Update ( )
self : GetParent ( ) : SetTitle ( L [ " Loadouts " ] ) ;
local sidebar = BtWLoadoutsFrame.Sidebar
sidebar : SetSupportedFilters ( " spec " , " class " , " role " , " character " , " disabled " )
sidebar : SetSets ( BtWLoadoutsSets.profiles )
sidebar : SetCollapsed ( BtWLoadoutsCollapsed.profiles )
sidebar : SetCategories ( BtWLoadoutsCategories.profiles )
sidebar : SetFilters ( BtWLoadoutsFilters.profiles )
sidebar : SetSelected ( self.set )
sidebar : Update ( )
self.set = sidebar : GetSelected ( )
local set = self.set
local showingNPE = BtWLoadoutsFrame : SetNPEShown ( set == nil , L [ " Loadouts " ] , L [ " Add sets of one or more types (Talents, Soulbinds, etc.) to your loadouts to swap them together. " ] )
self : GetParent ( ) . ExportButton : SetEnabled ( true )
self : GetParent ( ) . RefreshButton : SetEnabled ( false )
self : GetParent ( ) . DeleteButton : SetEnabled ( true )
self.Collapsed = self.Collapsed or { }
if not showingNPE then
local hasErrors , errors , specID = GetLoadoutErrors ( errors , set )
if type ( specID ) == " number " and set.specID == nil then
set.specID = specID
end
specID = set.specID ;
UpdateSetFilters ( set )
sidebar : Update ( )
local classFile = specID and select ( 6 , GetSpecializationInfoByID ( specID ) )
if specID == nil or specID == 0 then
UIDropDownMenu_SetText ( self.SpecDropDown , L [ " None " ] ) ;
else
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 ) ) ;
end
if classFile and type ( set.character ) == " table " and not set.character [ " inherit " ] then
-- Filter out any characters that are not valid for the selected spec
for character in pairs ( set.character ) do
local characterData = Internal.GetCharacterInfo ( character )
if not characterData or characterData.class ~= classFile then
set.character [ character ] = nil
end
end
end
self.CharacterDropDown : SetClass ( classFile )
self.CharacterDropDown : UpdateName ( )
self.Enabled : SetChecked ( not set.disabled ) ;
self.SetsScroll . items = BuildSetItems ( set , self.SetsScroll . items or { } , self.Collapsed , errors )
SetsScrollFrameUpdate ( self.SetsScroll )
if not self.Name : HasFocus ( ) then
self.Name : SetText ( set.name or " " ) ;
end
self : GetParent ( ) . ActivateButton : SetEnabled ( classFile == select ( 2 , UnitClass ( " player " ) ) ) ;
-- Tutorial stuff
local helpTipBox = self : GetParent ( ) . HelpTipBox ;
if not BtWLoadoutsHelpTipFlags [ " TUTORIAL_RENAME_SET " ] then
helpTipBox.closeFlag = " TUTORIAL_RENAME_SET " ;
HelpTipBox_Anchor ( helpTipBox , " TOP " , self.Name ) ;
helpTipBox : Show ( ) ;
HelpTipBox_SetText ( helpTipBox , L [ " Change the name of your new loadout. " ] ) ;
elseif not BtWLoadoutsHelpTipFlags [ " TUTORIAL_CREATE_TALENT_SET " ] then
helpTipBox.closeFlag = " TUTORIAL_CREATE_TALENT_SET " ;
HelpTipBox_Anchor ( helpTipBox , " TOP " , self.SetsScroll ) ;
helpTipBox : Show ( ) ;
HelpTipBox_SetText ( helpTipBox , L [ " Create a talent set for your new loadout. " ] ) ;
elseif not BtWLoadoutsHelpTipFlags [ " TUTORIAL_ACTIVATE_SET " ] then
helpTipBox.closeFlag = " TUTORIAL_ACTIVATE_SET " ;
HelpTipBox_Anchor ( helpTipBox , " TOP " , self : GetParent ( ) . ActivateButton ) ;
helpTipBox : Show ( ) ;
HelpTipBox_SetText ( helpTipBox , L [ " Activate your loadout. " ] ) ;
else
helpTipBox.closeFlag = nil ;
helpTipBox : Hide ( ) ;
end
else
self.Name : SetText ( L [ " New Loadout " ] ) ;
self.SetsScroll . items = BuildSetItems ( { } , self.SetsScroll . items or { } , self.Collapsed , { } )
SetsScrollFrameUpdate ( self.SetsScroll )
self.Enabled : SetChecked ( true )
UIDropDownMenu_SetText ( self.SpecDropDown , L [ " None " ] )
-- Tutorial stuff
local helpTipBox = self : GetParent ( ) . HelpTipBox ;
if not BtWLoadoutsHelpTipFlags [ " TUTORIAL_NEW_SET " ] then
helpTipBox.closeFlag = " TUTORIAL_NEW_SET " ;
HelpTipBox_Anchor ( helpTipBox , " BOTTOM " , self : GetParent ( ) . NPE.AddButton ) ;
helpTipBox : Show ( ) ;
HelpTipBox_SetText ( helpTipBox , L [ " To begin, create a new set. " ] ) ;
else
helpTipBox.closeFlag = nil ;
helpTipBox : Hide ( ) ;
end
end
end
function BtWLoadoutsLoadoutsMixin : SetSetByID ( setID )
self.set = GetProfile ( setID )
end