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.

726 lines
21 KiB

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 playerLevel = UnitLevel("player")
local talents = {};
local usedSlots = {};
for talentID in pairs(set.talents) do
if GetPvpTalentUnlockLevel(talentID) <= playerLevel then
talents[talentID] = true;
end
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