Skip to content

Commit 99a1f35

Browse files
committed
Custom loadouts can now be locked to prevent editing (closes #5)
1 parent 1b829f6 commit 99a1f35

File tree

6 files changed

+97
-49
lines changed

6 files changed

+97
-49
lines changed

core/ManagerApi.lua

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ API.Event = {
3939
--- @param displayInfo TLM_LoadoutDisplayInfo
4040
--- @return TalentLoadoutManagerAPI_LoadoutInfo
4141
local function CreateLoadoutInfoFromDisplayInfo(displayInfo)
42+
--- @type TalentLoadoutManagerAPI_LoadoutInfo
4243
return {
4344
id = displayInfo.id,
4445
displayName = displayInfo.displayName,
@@ -50,6 +51,7 @@ local function CreateLoadoutInfoFromDisplayInfo(displayInfo)
5051
parentMapping = displayInfo.parentMapping,
5152
classID = displayInfo.classID,
5253
specID = displayInfo.specID,
54+
isLocked = displayInfo.isLocked,
5355
};
5456
end
5557

@@ -160,6 +162,15 @@ function GlobalAPI:RenameLoadout(loadoutID, newName)
160162
end
161163
end
162164

165+
function GlobalAPI:SetLoadoutLocked(loadoutID, isLocked)
166+
local displayInfo = TLM:GetLoadoutByID(loadoutID);
167+
if not displayInfo then
168+
return false;
169+
end
170+
171+
return TLM:SetLoadoutLocked(displayInfo.classID, displayInfo.specID, loadoutID, isLocked);
172+
end
173+
163174
--- you cannot delete a Blizzard loadout if you are not the owner
164175
--- @param loadoutID number|string - the loadout ID, this can be a blizzard ConfigID, or a custom TLM loadout ID
165176
--- @return boolean - true if the delete was successful

core/TalentLoadoutManager.lua

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ function TLM:OnInitialize()
6666
self.db = TalentLoadoutManagerDB;
6767
self.charDb = TalentLoadoutManagerCharDB;
6868
self.cache = {
69+
--- @type table<string|number, TLM_LoadoutDisplayInfo>
6970
loadoutByID = {},
7071
exportStrings = {},
7172
deserializationCache = {},
@@ -234,6 +235,7 @@ function TLM:RebuildLoadoutByIDCache()
234235
if playerName ~= self.playerName then
235236
displayName = displayName .. " (" .. playerName .. ")";
236237
end
238+
--- @type TLM_LoadoutDisplayInfo
237239
local displayInfo = {
238240
id = configID,
239241
displayName = displayName,
@@ -244,6 +246,7 @@ function TLM:RebuildLoadoutByIDCache()
244246
parentMapping = nil,
245247
classID = classID,
246248
specID = specID,
249+
isLocked = loadoutInfo.isLocked or false,
247250
};
248251
self.cache.loadoutByID[configID] = displayInfo;
249252
end
@@ -255,6 +258,7 @@ function TLM:RebuildLoadoutByIDCache()
255258
for specID, loadoutList in pairs(specList) do
256259
for loadoutID, loadoutInfo in pairs(loadoutList) do
257260
local namePrefix = loadoutInfo.levelingOrder and XP_ATLAS or "";
261+
--- @type TLM_LoadoutDisplayInfo
258262
local displayInfo = {
259263
id = loadoutID,
260264
displayName = namePrefix .. (loadoutInfo.name):gsub('.-||', '', 1),
@@ -265,6 +269,7 @@ function TLM:RebuildLoadoutByIDCache()
265269
parentMapping = self:GetParentMappingForLoadout(loadoutInfo, specID),
266270
classID = classID,
267271
specID = specID,
272+
isLocked = loadoutInfo.isLocked or false,
268273
};
269274
self.cache.loadoutByID[loadoutID] = displayInfo;
270275
end
@@ -445,9 +450,11 @@ function TLM:UpdateBlizzardLoadout(configID, specID)
445450
selectedNodes = serialized,
446451
name = configInfo.name,
447452
id = configID,
453+
isLocked = false,
448454
};
449455
self.db.blizzardLoadouts[classID][specID][self.playerName][configID] = loadoutInfo;
450456
local displayName = BLIZZ_ATLAS .. (loadoutInfo.name):gsub('.-||', '', 1);
457+
--- @type TLM_LoadoutDisplayInfo
451458
local displayInfo = {
452459
id = configID,
453460
displayName = displayName,
@@ -457,6 +464,7 @@ function TLM:UpdateBlizzardLoadout(configID, specID)
457464
isBlizzardLoadout = true,
458465
classID = classID,
459466
specID = specID,
467+
isLocked = loadoutInfo.isLocked or false,
460468
};
461469
self.cache.loadoutByID[configID] = displayInfo;
462470
self:TriggerEvent(self.Event.LoadoutUpdated, classID, specID, configID, displayInfo);
@@ -482,6 +490,7 @@ function TLM:UpdateCustomLoadout(customLoadoutID, selectedNodes, levelingOrder,
482490
loadoutInfo.levelingOrder = levelingOrder;
483491

484492
local namePrefix = loadoutInfo.levelingOrder and XP_ATLAS or "";
493+
--- @type TLM_LoadoutDisplayInfo
485494
local displayInfo = {
486495
id = customLoadoutID,
487496
displayName = namePrefix .. (loadoutInfo.name):gsub('.-||', '', 1),
@@ -492,6 +501,7 @@ function TLM:UpdateCustomLoadout(customLoadoutID, selectedNodes, levelingOrder,
492501
parentMapping = self:GetParentMappingForLoadout(loadoutInfo, specID),
493502
classID = classID,
494503
specID = specID,
504+
isLocked = loadoutInfo.isLocked or false,
495505
}
496506
self.cache.loadoutByID[customLoadoutID] = displayInfo;
497507
self:TriggerEvent(self.Event.LoadoutUpdated, classID, specID, customLoadoutID, displayInfo);
@@ -852,6 +862,7 @@ function TLM:ApplyCustomLoadout(loadoutInfo, autoApply)
852862

853863
self.charDb.selectedCustomLoadoutID[self.playerSpecID] = loadoutInfo.id;
854864
local namePrefix = loadoutInfo.levelingOrder and XP_ATLAS or "";
865+
--- @type TLM_LoadoutDisplayInfo
855866
local displayInfo = {
856867
id = loadoutInfo.id,
857868
displayName = namePrefix .. (loadoutInfo.name):gsub('.-||', '', 1),
@@ -862,6 +873,7 @@ function TLM:ApplyCustomLoadout(loadoutInfo, autoApply)
862873
parentMapping = self:GetParentMappingForLoadout(loadoutInfo, self.playerSpecID),
863874
classID = self.playerClassID,
864875
specID = self.playerSpecID,
876+
isLocked = loadoutInfo.isLocked or false,
865877
}
866878
self:TriggerEvent(self.Event.CustomLoadoutApplied, self.playerClassID, specID, loadoutInfo.id, displayInfo);
867879

@@ -1016,6 +1028,7 @@ function TLM:CreateCustomLoadoutFromLoadoutData(loadoutInfo, classIDOrNil, specI
10161028
selectedNodes = loadoutInfo.selectedNodes,
10171029
levelingOrder = loadoutInfo.levelingOrder,
10181030
parentMapping = {},
1031+
isLocked = false,
10191032
}
10201033
if classID == self.playerClassID and specID == self.playerSpecID then
10211034
newLoadoutInfo.parentMapping[self.playerName] = self:GetActiveBlizzardLoadoutConfigID();
@@ -1033,6 +1046,7 @@ function TLM:CreateCustomLoadoutFromLoadoutData(loadoutInfo, classIDOrNil, specI
10331046
parentMapping = self:GetParentMappingForLoadout(newLoadoutInfo, specID),
10341047
classID = classID,
10351048
specID = specID,
1049+
isLocked = newLoadoutInfo.isLocked or false,
10361050
}
10371051
self.cache.loadoutByID[id] = displayInfo;
10381052
self:TriggerEvent(self.Event.LoadoutUpdated, classID, specID, id, displayInfo);
@@ -1055,6 +1069,7 @@ function TLM:CreateCustomLoadoutFromImportString(importString, autoApply, name,
10551069
name = name,
10561070
selectedNodes = selectedNodes,
10571071
levelingOrder = errorOrLevelingOrder,
1072+
isLocked = false,
10581073
}
10591074
loadoutInfo = self:CreateCustomLoadoutFromLoadoutData(loadoutInfo, classID, specID);
10601075
if load and classID == self.playerClassID and specID == self.playerSpecID then
@@ -1075,13 +1090,36 @@ function TLM:CreateCustomLoadoutFromActiveTalents(name, classIDOrNil, specIDOrNi
10751090
name = name,
10761091
selectedNodes = selectedNodes,
10771092
levelingOrder = nil,
1093+
isLocked = false,
10781094
}
10791095
loadoutInfo = self:CreateCustomLoadoutFromLoadoutData(loadoutInfo, classID, specID);
10801096
self:ApplyCustomLoadout(loadoutInfo);
10811097

10821098
return loadoutInfo;
10831099
end
10841100

1101+
function TLM:SetLoadoutLocked(classIDOrNil, specIDOrNil, loadoutID, isLocked)
1102+
local classID = tonumber(classIDOrNil) or self.playerClassID;
1103+
local specID = tonumber(specIDOrNil) or self.playerSpecID;
1104+
assert(type(loadoutID) == "string" or type(loadoutID) == "number", "loadoutID must be a string or number");
1105+
1106+
if self.db.customLoadouts[classID] and self.db.customLoadouts[classID][specID] and self.db.customLoadouts[classID][specID][loadoutID] then
1107+
local loadoutInfo = self.db.customLoadouts[classID][specID][loadoutID];
1108+
loadoutInfo.isLocked = isLocked;
1109+
1110+
local displayInfo = self.cache.loadoutByID[loadoutID]
1111+
displayInfo.isLocked = isLocked;
1112+
1113+
self.cache.loadoutByID[loadoutID] = displayInfo;
1114+
self:TriggerEvent(self.Event.LoadoutUpdated, classID, specID, loadoutID, displayInfo);
1115+
self:TriggerEvent(self.Event.LoadoutListUpdated);
1116+
1117+
return true;
1118+
end
1119+
1120+
return false;
1121+
end
1122+
10851123
function TLM:RenameCustomLoadout(classIDOrNil, specIDOrNil, loadoutID, newName)
10861124
local classID = tonumber(classIDOrNil) or self.playerClassID;
10871125
local specID = tonumber(specIDOrNil) or self.playerSpecID;
@@ -1092,17 +1130,9 @@ function TLM:RenameCustomLoadout(classIDOrNil, specIDOrNil, loadoutID, newName)
10921130
loadoutInfo.name = newName;
10931131

10941132
local namePrefix = loadoutInfo.levelingOrder and XP_ATLAS or "";
1095-
local displayInfo = {
1096-
id = loadoutID,
1097-
displayName = namePrefix .. (loadoutInfo.name):gsub('.-||', '', 1),
1098-
loadoutInfo = loadoutInfo,
1099-
owner = nil,
1100-
playerIsOwner = true,
1101-
isBlizzardLoadout = false,
1102-
parentMapping = self:GetParentMappingForLoadout(loadoutInfo, specID),
1103-
classID = classID,
1104-
specID = specID,
1105-
}
1133+
local displayInfo = self.cache.loadoutByID[loadoutID]
1134+
displayInfo.displayName = namePrefix .. (loadoutInfo.name):gsub('.-||', '', 1);
1135+
11061136
self.cache.loadoutByID[loadoutID] = displayInfo;
11071137
self:TriggerEvent(self.Event.LoadoutUpdated, classID, specID, loadoutID, displayInfo);
11081138
self:TriggerEvent(self.Event.LoadoutListUpdated);

modules/DefaultUISidebar.lua

Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -96,30 +96,10 @@ function Module:DoImportIntoCurrent(importText, autoApply)
9696
return result, errorOrNil;
9797
end
9898

99+
--- @return BlizzMoveAPI_AddonFrameTable
99100
function Module:GetBlizzMoveFrameTable()
101+
--- @type BlizzMoveAPI_AddonFrameTable
100102
return {
101-
["Blizzard_ClassTalentUI"] = { --- @todo: delete in TWW
102-
["ClassTalentFrame"] = {
103-
MinVersion = 100000,
104-
MaxVersion = 110000,
105-
SubFrames = {
106-
["ClassTalentFrame.TalentsTab.ButtonsParent"] = {
107-
MinVersion = 100000,
108-
MaxVersion = 110000,
109-
},
110-
["ClassTalentFrame.TalentsTab"] = {
111-
MinVersion = 100000,
112-
MaxVersion = 110000,
113-
SubFrames = {
114-
["TLM-SideBar"] = {
115-
FrameReference = self.SideBar,
116-
Detachable = true,
117-
},
118-
},
119-
},
120-
},
121-
},
122-
},
123103
["Blizzard_PlayerSpells"] = {
124104
["PlayerSpellsFrame"] = {
125105
MinVersion = 110000,

modules/SideBarMixin.lua

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ local SETTING_SUFFIX_COLLAPSED = "_Collapsed";
2424
local SETTING_SUFFIX_ANCHOR_LOCATION = "_AnchorLocation";
2525
local ANCHOR_LEFT = 0;
2626
local ANCHOR_RIGHT = 1;
27+
local LOCK_MARKUP = CreateAtlasMarkup("AdventureMapIcon-Lock", 16, 16);
2728

2829
function SideBarMixin:OnInitialize()
2930
local loadoutNameSubText = "Anything before the first '||' character will not display. This allows you to sort loadouts by adding a prefix.";
@@ -256,6 +257,7 @@ do
256257
error('override in implementation');
257258
end
258259

260+
--- @return BlizzMoveAPI_AddonFrameTable
259261
function SideBarMixin:GetBlizzMoveFrameTable()
260262
error('override in implementation');
261263
end
@@ -633,12 +635,15 @@ function SideBarMixin:CreateScrollBox(parentContainer)
633635
--- @param frame TLM_ElementFrame
634636
local function elementFrameApplyColors(frame)
635637
local isSelected = (frame == self.activeLoadoutFrame);
638+
--- @type ColorType
636639
local textColor = isSelected
637640
and Config:GetConfig('sideBarActiveElementTextColor')
638641
or Config:GetConfig('sideBarInactiveElementTextColor');
642+
--- @type ColorType
639643
local backgroundColor = isSelected
640644
and Config:GetConfig('sideBarActiveElementBackgroundColor')
641645
or Config:GetConfig('sideBarInactiveElementBackgroundColor');
646+
--- @type ColorType
642647
local highlightBackgroundColor = isSelected
643648
and Config:GetConfig('sideBarActiveElementHighlightBackgroundColor')
644649
or Config:GetConfig('sideBarInactiveElementHighlightBackgroundColor');
@@ -674,6 +679,9 @@ function SideBarMixin:CreateScrollBox(parentContainer)
674679
self.activeLoadoutFrame = frame;
675680
end
676681
local text = entry.text;
682+
if entry.data.isLocked then
683+
text = LOCK_MARKUP .. " " .. text;
684+
end
677685
if entry.parentID then
678686
text = " || " .. text;
679687
end
@@ -751,7 +759,16 @@ function SideBarMixin:GenerateMenu(rootDescription, frame, loadoutInfo)
751759
end
752760
rootDescription:CreateButton("Save current talents into loadout", function()
753761
self:UpdateCustomLoadoutWithCurrentTalents(loadoutInfo.id);
754-
end):SetEnabled(not loadoutInfo.isBlizzardLoadout);
762+
end):SetEnabled(not loadoutInfo.isBlizzardLoadout and not loadoutInfo.isLocked);
763+
764+
local lock = rootDescription:CreateCheckbox(
765+
"Locked",
766+
function() return loadoutInfo.isLocked; end,
767+
function() self:ToggleLock(loadoutInfo.id); return MenuResponse.CloseAll; end
768+
);
769+
lock:SetEnabled(not loadoutInfo.isBlizzardLoadout);
770+
lock:SetTitleAndTextTooltip("Lock loadout", "Locking a loadout blocks you from saving changes to it.");
771+
755772
rootDescription:CreateButton("Rename", function()
756773
StaticPopup_Show(self.renameDialogName, loadoutInfo.name, nil, loadoutInfo);
757774
end):SetEnabled(loadoutInfo.playerIsOwner);
@@ -805,6 +822,13 @@ function SideBarMixin:RemoveAllLoadoutsByOwner(owner)
805822
end
806823
end
807824

825+
function SideBarMixin:ToggleLock(loadoutID)
826+
local loadoutInfo = GlobalAPI:GetLoadoutInfoByID(loadoutID);
827+
if not loadoutInfo then return; end
828+
829+
GlobalAPI:SetLoadoutLocked(loadoutID, not loadoutInfo.isLocked);
830+
end
831+
808832
--- @param frame TLM_ElementFrame
809833
--- @param loadoutInfo TLM_SideBarLoadoutInfo
810834
function SideBarMixin:SetElementAsActive(frame, loadoutInfo)
@@ -815,7 +839,7 @@ function SideBarMixin:SetElementAsActive(frame, loadoutInfo)
815839
previouslyActiveLoadoutFrame:ApplyColors();
816840
end
817841
frame:ApplyColors();
818-
self.SideBar.SaveButton:SetEnabled(self.activeLoadout and not self.activeLoadout.isBlizzardLoadout);
842+
self.SideBar.SaveButton:SetEnabled(self.activeLoadout and not self.activeLoadout.isBlizzardLoadout and not self.activeLoadout.isLocked);
819843
end
820844

821845
--- @param frame TLM_ElementFrame
@@ -947,16 +971,16 @@ function SideBarMixin:SortElements(dataProviderEntries)
947971
table.sort(elements, compare);
948972
local lookup = {};
949973
for index, element in ipairs(elements) do
950-
element.order = index;
951-
element.subOrder = 0;
974+
element.order = index; ---@diagnostic disable-line: inject-field
975+
element.subOrder = 0; ---@diagnostic disable-line: inject-field
952976
lookup[element.data.id] = element;
953977
end
954978

955979
for index, element in ipairs(elements) do
956980
local parentIndex = element.parentID and lookup[element.parentID] and lookup[element.parentID].order;
957981
if parentIndex then
958-
element.order = parentIndex;
959-
element.subOrder = index;
982+
element.order = parentIndex; ---@diagnostic disable-line: inject-field
983+
element.subOrder = index; ---@diagnostic disable-line: inject-field
960984
end
961985
end
962986

@@ -1012,7 +1036,7 @@ function SideBarMixin:RefreshSideBarData()
10121036
self.DataProvider = CreateDataProvider(dataProviderEntries);
10131037
self.SideBar.ScrollBoxContainer.ScrollBox:SetDataProvider(self.DataProvider);
10141038

1015-
self.SideBar.SaveButton:SetEnabled(self.activeLoadout and not self.activeLoadout.isBlizzardLoadout);
1039+
self.SideBar.SaveButton:SetEnabled(self.activeLoadout and not self.activeLoadout.isBlizzardLoadout and not self.activeLoadout.isLocked);
10161040
end
10171041

10181042
function SideBarMixin:ShowConfigDialog()
@@ -1023,6 +1047,8 @@ function SideBarMixin:TryIntegrateWithBlizzMove()
10231047
if not self.IntegrateWithBlizzMove or not C_AddOns.IsAddOnLoaded('BlizzMove') then return; end
10241048

10251049
local compatible = false;
1050+
--- @type BlizzMoveAPI|nil
1051+
local BlizzMoveAPI = _G.BlizzMoveAPI; ---@diagnostic disable-line: undefined-field
10261052
if(BlizzMoveAPI and BlizzMoveAPI.GetVersion and BlizzMoveAPI.RegisterAddOnFrames) then
10271053
local _, _, _, _, versionInt = BlizzMoveAPI:GetVersion()
10281054
if (versionInt == nil or versionInt >= 30200) then

modules/TalentTreeViewerSidebar.lua

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,16 +26,9 @@ function Module:OnEnable()
2626
end);
2727
end
2828

29-
--- @return TalentViewer|TalentViewerTWW
29+
--- @return TalentViewerTWW
3030
function Module:GetTalentTreeViewer()
31-
if TalentViewerLoader then return TalentViewerLoader:GetTalentViewer(); end
32-
if not TalentViewer then --- @todo delete in TWW
33-
C_AddOns.LoadAddOn(TalentViewerLoader and TalentViewerLoader:GetLodAddonName() or 'TalentTreeViewer')
34-
if not TalentViewer then
35-
error("TalentTreeViewer failed to load")
36-
end
37-
end
38-
return TalentViewer;
31+
return TalentViewerLoader:GetTalentViewer();
3932
end
4033

4134
---@return TalentViewerUIMixinTWW
@@ -72,6 +65,9 @@ function Module:GetLoadouts()
7265
end
7366

7467
function Module:GetActiveLoadout(forceRefresh)
68+
if forceRefresh and self.activeLoadout and self.activeLoadout.id then
69+
return GlobalAPI:GetLoadoutInfoByID(self.activeLoadout.id);
70+
end
7571
return self.activeLoadout;
7672
end
7773

@@ -103,7 +99,9 @@ function Module:DoImportIntoCurrent(importText, autoApply)
10399
return loadoutInfo, errorOrNil;
104100
end
105101

102+
--- @return BlizzMoveAPI_AddonFrameTable
106103
function Module:GetBlizzMoveFrameTable()
104+
--- @type BlizzMoveAPI_AddonFrameTable
107105
return {
108106
[TalentViewerLoader and TalentViewerLoader:GetLodAddonName() or 'TalentTreeViewer'] = {
109107
['TalentViewer_DF'] = {

0 commit comments

Comments
 (0)