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.
3006 lines
97 KiB
3006 lines
97 KiB
local _, addon = ...
|
|
local Gemma = addon.Gemma;
|
|
local AtlasUtil = Gemma.AtlasUtil;
|
|
local ItemCache = Gemma.ItemCache;
|
|
local AcquireActionButton = Gemma.AcquireActionButton;
|
|
local L = Narci.L;
|
|
local GetItemIcon = C_Item.GetItemIconByID;
|
|
local FadeFrame = NarciFadeUI.Fade;
|
|
local GetItemBagPosition = NarciAPI.GetItemBagPosition;
|
|
local CopyTable = addon.CopyTable;
|
|
|
|
local GetItemGemID = C_Item.GetItemGemID;
|
|
local GetItemNumSockets = C_Item.GetItemNumSockets;
|
|
local GetInventoryItemLink = GetInventoryItemLink;
|
|
local GetItemCount = C_Item.GetItemCount;
|
|
|
|
local pairs = pairs;
|
|
local ipairs = ipairs;
|
|
local min = math.min;
|
|
local tsort = table.sort;
|
|
|
|
local CreateFrame = CreateFrame;
|
|
local Mixin = Mixin;
|
|
local strtrim = strtrim;
|
|
|
|
local MAX_SAVES = 10;
|
|
local LOADOUT_BUTTON_HEIGHT_COLLAPSED = 28;
|
|
local LOADOUT_BUTTON_HEIGHT_EXPANDED = 56;
|
|
local LOADOUT_BUTTON_WIDTH = 322;
|
|
|
|
|
|
local DataProvider;
|
|
local MainFrame, LoadoutFrame, MouseOverFrame, EquipButton, EditButton, ListRightIcon, SimpleTooltip;
|
|
local EditWindow, Planner;
|
|
|
|
|
|
local SOCKETABLE_SLOTS = {
|
|
1, 2, 3, 5, 9,
|
|
10, 6, 7, 8,
|
|
11, 12, 13, 14,
|
|
};
|
|
|
|
local STAT1_SLOTS = {
|
|
5, --Chest
|
|
7, --Legs
|
|
};
|
|
|
|
local STAT2_SLOTS = {
|
|
13, --Trinket1
|
|
14, --Trinket2
|
|
};
|
|
|
|
local STAT3_SLOTS = {
|
|
2, --Neck
|
|
11, --Ring1
|
|
12, --Ring2
|
|
};
|
|
|
|
local Automation = CreateFrame("Frame");
|
|
|
|
|
|
local function CreateReturnButton(parent)
|
|
local f = Gemma.CreateIconButton(parent);
|
|
AtlasUtil:SetAtlas(f.Icon, "gemlist-return");
|
|
f:SetSize(60, 32);
|
|
f:SetPoint("LEFT", parent, "TOPLEFT", 0, -22);
|
|
f.tooltipText = L["Return"];
|
|
return f
|
|
end
|
|
|
|
|
|
local LoadoutUtil = {};
|
|
do
|
|
--Save Format:
|
|
--[[
|
|
save = {
|
|
name = text,
|
|
timeCreated = time,
|
|
timeApplied = time,
|
|
gemInfo = table, --See DataProvider:GetEquippedLoadoutGemInfo() for gemInfo structure
|
|
},
|
|
--]]
|
|
|
|
local REF_TIME = 1717403000;
|
|
local NO_GEM_ID = 0;
|
|
local time = time;
|
|
|
|
local tinsert = table.insert;
|
|
local tremove = table.remove;
|
|
|
|
local function GetRelativeTime()
|
|
return time() - REF_TIME
|
|
end
|
|
|
|
function LoadoutUtil:LoadSaves()
|
|
if not NarcissusDB.PandariaRemixLoadout then
|
|
NarcissusDB.PandariaRemixLoadout = {};
|
|
end
|
|
|
|
local _, _, classID = UnitClass("player");
|
|
if not NarcissusDB.PandariaRemixLoadout[classID] then
|
|
NarcissusDB.PandariaRemixLoadout[classID] = {};
|
|
end
|
|
|
|
self.Saves = NarcissusDB.PandariaRemixLoadout[classID];
|
|
|
|
self:CreateOverviews();
|
|
end
|
|
|
|
function LoadoutUtil:GetCurrentGemInfo()
|
|
if not self.currentGemInfo then
|
|
self.currentGemInfo = DataProvider:GetEquippedLoadoutGemInfo();
|
|
end
|
|
return self.currentGemInfo
|
|
end
|
|
|
|
function LoadoutUtil:CreateNewLoadout(name, gemInfo)
|
|
local isDupe, dupeName = self:DoesGemLoadoutExist(gemInfo);
|
|
if isDupe then
|
|
return
|
|
end
|
|
|
|
tsort(gemInfo.tinker);
|
|
|
|
local data = {};
|
|
data.name = name;
|
|
data.gemInfo = gemInfo;
|
|
data.timeCreated = GetRelativeTime();
|
|
|
|
table.insert(self.Saves, data);
|
|
self:CreateOverviews();
|
|
|
|
LoadoutFrame.loadoutListChanged = true;
|
|
LoadoutFrame:RequestUpdate();
|
|
|
|
return true
|
|
end
|
|
|
|
function LoadoutUtil:OverwriteLoadout(loadoutIndex, name, gemInfo)
|
|
if loadoutIndex and self.Saves[loadoutIndex] then
|
|
local data = {};
|
|
data.name = name;
|
|
data.gemInfo = CopyTable(gemInfo);
|
|
data.timeCreated = GetRelativeTime();
|
|
self.Saves[loadoutIndex] = data;
|
|
|
|
self:CreateOverviews();
|
|
LoadoutFrame.loadoutListChanged = true;
|
|
LoadoutFrame.equipmentChanged = true;
|
|
LoadoutFrame:RequestUpdate();
|
|
|
|
return true
|
|
end
|
|
end
|
|
|
|
function LoadoutUtil:SaveCurrentItemsAsNewLoadout()
|
|
local name = "Loadout #"..(self:GetNumSaves() + 1);
|
|
local gemInfo = self:GetCurrentGemInfo();
|
|
self:CreateNewLoadout(name, gemInfo);
|
|
end
|
|
|
|
function LoadoutUtil:GetLoadoutName(loadoutIndex)
|
|
return self:GetLoadoutData(loadoutIndex).name
|
|
end
|
|
|
|
function LoadoutUtil:GetLoadoutGemInfo(loadoutIndex)
|
|
return self:GetLoadoutData(loadoutIndex).gemInfo
|
|
end
|
|
|
|
function LoadoutUtil:CopyLoadoutGemInfo(loadoutIndex)
|
|
return CopyTable(self:GetLoadoutGemInfo(loadoutIndex))
|
|
end
|
|
|
|
function LoadoutUtil:FormatStatText(stats)
|
|
--stats: { [statType] = amount }
|
|
|
|
local numStats = 7;
|
|
local valueText, statText;
|
|
local amount;
|
|
|
|
for i = 1, numStats do
|
|
amount = stats[i] or 0;
|
|
|
|
if amount == 0 then
|
|
valueText = amount;
|
|
else
|
|
valueText = "|cffffffff"..amount.."|r";
|
|
end
|
|
|
|
if statText then
|
|
statText = statText .. "-" .. valueText;
|
|
else
|
|
statText = valueText;
|
|
end
|
|
end
|
|
|
|
return statText
|
|
end
|
|
|
|
function LoadoutUtil:GetLoadoutStatText(gemInfo)
|
|
local numStats = 7;
|
|
local count = {};
|
|
local n;
|
|
|
|
for i = 1, numStats do
|
|
count[i] = 0;
|
|
end
|
|
|
|
for i = 1, 3 do
|
|
local stats = gemInfo["stats"..i];
|
|
if stats then
|
|
for j = 1, numStats do
|
|
n = stats[j];
|
|
if n then
|
|
count[j] = count[j] + n;
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
return self:FormatStatText(count)
|
|
end
|
|
|
|
function LoadoutUtil:CreateOverviews()
|
|
self.Overviews = {};
|
|
|
|
for i = 1, MAX_SAVES do
|
|
if self.Saves[i] then
|
|
local itemID;
|
|
local gemInfo = self:GetLoadoutGemInfo(i);
|
|
local n = 0;
|
|
local info = {};
|
|
info.icons = {};
|
|
|
|
for j = 1, 6 do
|
|
if j == 1 then
|
|
itemID = gemInfo.head;
|
|
elseif j == 2 then
|
|
itemID = gemInfo.feet;
|
|
else
|
|
itemID = gemInfo.tinker and gemInfo.tinker[j - 2];
|
|
end
|
|
|
|
if itemID then
|
|
n = n + 1;
|
|
info.icons[n] = GetItemIcon(itemID);
|
|
end
|
|
end
|
|
|
|
info.statText = self:GetLoadoutStatText(gemInfo);
|
|
|
|
self.Overviews[i] = info;
|
|
else
|
|
break
|
|
end
|
|
end
|
|
end
|
|
|
|
function LoadoutUtil:GetLoadoutData(loadoutIndex)
|
|
return self.Saves[loadoutIndex]
|
|
end
|
|
|
|
function LoadoutUtil:GetLoadoutOverview(loadoutIndex)
|
|
return self.Overviews[loadoutIndex]
|
|
end
|
|
|
|
function LoadoutUtil:GetNumSaves()
|
|
return min(#self.Saves, MAX_SAVES);
|
|
end
|
|
|
|
function LoadoutUtil:DeleteLoadout(loadoutIndex)
|
|
local data = table.remove(self.Saves, loadoutIndex);
|
|
table.remove(self.Overviews, loadoutIndex);
|
|
|
|
if LoadoutFrame:IsDataSelectedByIndex(loadoutIndex) then
|
|
LoadoutFrame:ClearSelection();
|
|
LoadoutFrame:SetExpandedButton(nil);
|
|
end
|
|
|
|
self.equippedLoadoutIndex = nil;
|
|
|
|
LoadoutFrame.loadoutListChanged = true;
|
|
LoadoutFrame:RequestUpdate();
|
|
end
|
|
|
|
function LoadoutUtil:GetGemsForLoadout(loadoutIndex)
|
|
local data = self:GetLoadoutData(loadoutIndex);
|
|
--debug
|
|
end
|
|
|
|
function LoadoutUtil:AreGemInfoSame(gemInfo1, gemInfo2)
|
|
if gemInfo1 and gemInfo2 then
|
|
if not (gemInfo1.head == gemInfo2.head) then
|
|
return false
|
|
end
|
|
|
|
if not (gemInfo1.feet == gemInfo2.feet) then
|
|
return false
|
|
end
|
|
|
|
local v1, v2;
|
|
|
|
for statType, amount in pairs(gemInfo2.stats1) do
|
|
v1 = amount or 0;
|
|
v2 = gemInfo1.stats1[statType] or 0;
|
|
if v1 ~= v2 then
|
|
return false
|
|
end
|
|
end
|
|
|
|
for statType, amount in pairs(gemInfo2.stats2) do
|
|
v1 = amount or 0;
|
|
v2 = gemInfo1.stats2[statType] or 0;
|
|
if v1 ~= v2 then
|
|
return false
|
|
end
|
|
end
|
|
|
|
for statType, amount in pairs(gemInfo2.stats3) do
|
|
v1 = amount or 0;
|
|
v2 = gemInfo1.stats3[statType] or 0;
|
|
if v1 ~= v2 then
|
|
return false
|
|
end
|
|
end
|
|
|
|
if #gemInfo1.tinker == #gemInfo2.tinker then
|
|
local tinker1 = {};
|
|
for _, itemID in ipairs(gemInfo1.tinker) do
|
|
tinker1[itemID] = true;
|
|
end
|
|
|
|
local allSame = true;
|
|
for _, itemID in ipairs(gemInfo2.tinker) do
|
|
allSame = allSame and tinker1[itemID];
|
|
end
|
|
|
|
if allSame then
|
|
return true
|
|
else
|
|
return false
|
|
end
|
|
else
|
|
return false
|
|
end
|
|
else
|
|
return false
|
|
end
|
|
end
|
|
|
|
function LoadoutUtil:IsLoadoutEquipped(loadoutIndex)
|
|
local gemInfo1 = self:GetCurrentGemInfo();
|
|
local gemInfo2 = self:GetLoadoutData(loadoutIndex).gemInfo;
|
|
|
|
return self:AreGemInfoSame(gemInfo1, gemInfo2)
|
|
end
|
|
|
|
function LoadoutUtil:IsLoadoutFullyEquipped(loadoutIndex)
|
|
--Include using best stats gems in bags
|
|
if self:IsLoadoutEquipped(loadoutIndex) then
|
|
local searchStatsOnly = true;
|
|
local slotActions, errors, requiredBagSpace = self:GetSlotActionsForLoadout(loadoutIndex, searchStatsOnly);
|
|
return (#slotActions.insert + #slotActions.remove) == 0
|
|
else
|
|
return false
|
|
end
|
|
end
|
|
|
|
function LoadoutUtil:GetEquippedLoadoutIndex()
|
|
--0: None
|
|
--nil: uncached
|
|
if not self.equippedLoadoutIndex then
|
|
for i = 1, self:GetNumSaves() do
|
|
if self:IsLoadoutFullyEquipped(i) then
|
|
self.equippedLoadoutIndex = i;
|
|
break
|
|
end
|
|
end
|
|
|
|
if self.equippedLoadoutIndex then
|
|
self:UpdateAppliedTime(self.equippedLoadoutIndex);
|
|
end
|
|
end
|
|
|
|
return self.equippedLoadoutIndex
|
|
end
|
|
|
|
function LoadoutUtil:DoesLoadoutNameExist(name, loadoutIndex)
|
|
for i = 1, self:GetNumSaves() do
|
|
if name == self:GetLoadoutName(i) and i ~= loadoutIndex then
|
|
return true
|
|
end
|
|
end
|
|
end
|
|
|
|
function LoadoutUtil:DoesGemLoadoutExist(gemInfo)
|
|
for i = 1, self:GetNumSaves() do
|
|
if self:AreGemInfoSame(gemInfo, self:GetLoadoutGemInfo(i)) then
|
|
return true, self:GetLoadoutName(i);
|
|
end
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
function LoadoutUtil:UpdateAppliedTime(loadoutIndex)
|
|
local data = self:GetLoadoutData(loadoutIndex);
|
|
data.timeApplied = GetRelativeTime();
|
|
end
|
|
|
|
function LoadoutUtil:GetLastAppliedLoadoutIndex()
|
|
local index;
|
|
local maxValue = 0;
|
|
local timeApplied;
|
|
|
|
for i = 1, self:GetNumSaves() do
|
|
timeApplied = self:GetLoadoutData(i).timeApplied;
|
|
if timeApplied then
|
|
if timeApplied > maxValue then
|
|
maxValue = timeApplied;
|
|
index = i;
|
|
end
|
|
end
|
|
end
|
|
|
|
return index
|
|
end
|
|
|
|
|
|
|
|
|
|
local TINKER_SLOT = {
|
|
3, 9, 10, 6
|
|
};
|
|
|
|
function LoadoutUtil:GetCurrentItems()
|
|
if self.currentSlotGems then
|
|
return self.currentSlotGems
|
|
end
|
|
|
|
local slotGems = {};
|
|
|
|
local itemLink, numSockets, gemItemID;
|
|
|
|
for _, slotID in ipairs(SOCKETABLE_SLOTS) do
|
|
itemLink = GetInventoryItemLink("player", slotID);
|
|
if itemLink then
|
|
numSockets = GetItemNumSockets(itemLink);
|
|
for socketIndex = 1, numSockets do
|
|
gemItemID = GetItemGemID(itemLink, socketIndex) or NO_GEM_ID;
|
|
if not slotGems[slotID] then
|
|
slotGems[slotID] = {};
|
|
end
|
|
slotGems[slotID][socketIndex] = gemItemID;
|
|
end
|
|
end
|
|
end
|
|
|
|
self.currentSlotGems = slotGems;
|
|
|
|
return slotGems
|
|
end
|
|
|
|
function LoadoutUtil:GetErrorMessage(errors)
|
|
local errorType, message;
|
|
local str;
|
|
|
|
for i, error in ipairs(errors) do
|
|
errorType = error[1];
|
|
if errorType == 1 then --Gem Uncollected
|
|
message = "Uncollected: "..error[2];
|
|
elseif errorType == 2 then --Not enough sockets
|
|
|
|
end
|
|
end
|
|
end
|
|
|
|
local function DoesPlayerHaveSparedItem(itemID)
|
|
local onPlayer = GetItemCount(itemID);
|
|
return onPlayer > 0
|
|
end
|
|
|
|
local function PopulateRequiredStatsTable(gemInfoStats, totalRequiredStats, slotRequiredStats)
|
|
for statType, amount in pairs(gemInfoStats) do
|
|
if not totalRequiredStats[statType] then
|
|
totalRequiredStats[statType] = 0;
|
|
end
|
|
totalRequiredStats[statType] = totalRequiredStats[statType] + amount;
|
|
slotRequiredStats[statType] = amount;
|
|
end
|
|
end
|
|
|
|
local function GetBestStatGemFromList(statGemCount, minusCount, bestIndex)
|
|
--bestIndex: best, 2nd best, 3rd best...
|
|
--statGemCount structure:
|
|
--{ {itemID = , inBagCount = , spareCount = } , {} } --items are from highest tier - lower
|
|
|
|
local n = 0;
|
|
|
|
for i, data in ipairs(statGemCount) do
|
|
n = n + data.inBagCount;
|
|
|
|
if n >= bestIndex then
|
|
if minusCount then
|
|
data.inBagCount = data.inBagCount - 1;
|
|
end
|
|
return data.itemID, true
|
|
end
|
|
|
|
n = n + data.spareCount;
|
|
|
|
if n >= bestIndex then
|
|
if minusCount then
|
|
data.spareCount = data.spareCount - 1;
|
|
end
|
|
return data.itemID, false
|
|
end
|
|
end
|
|
end
|
|
|
|
local function AddStatGemToList(statGemCount, gemItemID, isInBagItem)
|
|
for i, data in ipairs(statGemCount) do
|
|
if gemItemID == data.itemID then
|
|
if isInBagItem then
|
|
data.inBagCount = data.inBagCount + 1;
|
|
else
|
|
data.spareCount = data.spareCount + 1;
|
|
end
|
|
break
|
|
end
|
|
end
|
|
end
|
|
|
|
local function SortFunc_StatSlot(slotInfo1, slotInfo2)
|
|
--Sort by stat type and tier
|
|
if slotInfo1.isEmpty ~= slotInfo2.isEmpty then
|
|
return slotInfo1.isEmpty
|
|
elseif slotInfo1.isEmpty then --both empty
|
|
if slotInfo1[1] ~= slotInfo2[1] then --SlotID
|
|
return slotInfo1[1] < slotInfo2[1]
|
|
else --SocketIndex
|
|
return slotInfo1[2] < slotInfo2[2]
|
|
end
|
|
end
|
|
|
|
if slotInfo1.isRequired ~= slotInfo2.isRequired then
|
|
return not slotInfo1.isRequired;
|
|
end
|
|
|
|
if slotInfo1.statType ~= slotInfo2.statType then
|
|
return slotInfo1.statType < slotInfo2.statType
|
|
end
|
|
|
|
--Sort Low Tier to the top
|
|
return DataProvider:IsLeftStatGemBetter(slotInfo1[3], slotInfo2[3])
|
|
end
|
|
|
|
--[[
|
|
local function MergeStatSlot(currentSlotGems, slotID, statSlot, requiredStats, equippedStats)
|
|
if currentSlotGems[slotID] then
|
|
for socketIndex, gemItemID in ipairs(currentSlotGems[slotID]) do
|
|
local tbl = {slotID, socketIndex, gemItemID};
|
|
if gemItemID == NO_GEM_ID then
|
|
tbl.isEmpty = true;
|
|
tbl.isRequired = false;
|
|
else
|
|
local statType = DataProvider:GetStatType(gemItemID);
|
|
tbl.statType = statType;
|
|
|
|
if requiredStats[statType] then
|
|
tbl.isRequired = true;
|
|
if not equippedStats[statType] then
|
|
equippedStats[statType] = 0;
|
|
end
|
|
equippedStats[statType] = equippedStats[statType] + 1;
|
|
|
|
if equippedStats[statType] > requiredStats[statType] then
|
|
tbl.isRequired = false;
|
|
end
|
|
else
|
|
tbl.isRequired = false;
|
|
end
|
|
end
|
|
tinsert(statSlot, tbl);
|
|
end
|
|
|
|
tsort(statSlot, SortFunc_StatSlot);
|
|
end
|
|
end
|
|
--]]
|
|
|
|
local function MergeStatSlot(currentSlotGems, slotID, statSlot)
|
|
if currentSlotGems[slotID] then
|
|
for socketIndex, gemItemID in ipairs(currentSlotGems[slotID]) do
|
|
local tbl = {slotID, socketIndex, gemItemID};
|
|
tinsert(statSlot, tbl);
|
|
end
|
|
end
|
|
end
|
|
|
|
local function SortStatSlot(statSlot, requiredStats, equippedStats)
|
|
local gemItemID, statType;
|
|
|
|
for _, slotInfo in ipairs(statSlot) do
|
|
gemItemID = slotInfo[3];
|
|
if gemItemID == NO_GEM_ID then
|
|
slotInfo.isEmpty = true;
|
|
slotInfo.isRequired = false;
|
|
else
|
|
statType = DataProvider:GetStatType(gemItemID);
|
|
slotInfo.statType = statType;
|
|
|
|
if requiredStats[statType] then
|
|
slotInfo.isRequired = true;
|
|
else
|
|
slotInfo.isRequired = false;
|
|
end
|
|
end
|
|
end
|
|
tsort(statSlot, SortFunc_StatSlot);
|
|
|
|
for _, slotInfo in ipairs(statSlot) do
|
|
if slotInfo.isRequired then
|
|
statType = slotInfo.statType;
|
|
if not equippedStats[statType] then
|
|
equippedStats[statType] = 0;
|
|
end
|
|
equippedStats[statType] = equippedStats[statType] + 1;
|
|
|
|
if equippedStats[statType] > requiredStats[statType] then
|
|
slotInfo.isRequired = false;
|
|
end
|
|
end
|
|
end
|
|
tsort(statSlot, SortFunc_StatSlot);
|
|
end
|
|
|
|
local function ProcessStatSlots(requiredStats, statSlots, allStatGemCount, slotActions)
|
|
for statType, amount in pairs(requiredStats) do
|
|
local searchIndexOffset = 0;
|
|
|
|
for i = 1, amount do
|
|
local bestGemID, inBagItem = GetBestStatGemFromList(allStatGemCount[statType], false, i + searchIndexOffset);
|
|
local found;
|
|
|
|
if bestGemID then
|
|
local itemID;
|
|
for _, slotInfo in ipairs(statSlots) do
|
|
--slotInfo = {slotID, socketIndex, gemItemID}
|
|
itemID = slotInfo[3];
|
|
if (not slotInfo.used) and (not slotInfo.isEmpty) then
|
|
if not slotInfo.counted then
|
|
if itemID == bestGemID or DataProvider:IsLeftStatGemBetter(itemID, bestGemID) then
|
|
slotInfo.counted = true;
|
|
found = true;
|
|
searchIndexOffset = searchIndexOffset - 1;
|
|
break
|
|
end
|
|
end
|
|
end
|
|
end
|
|
--print("Search", bestGemID, ItemCache:GetItemName(bestGemID), "Found:", found)
|
|
else
|
|
--print(i + searchIndexOffset, "No Best Gem for Stat", statType, amount)
|
|
end
|
|
|
|
if bestGemID and (not found) then
|
|
bestGemID, inBagItem = GetBestStatGemFromList(allStatGemCount[statType], true, 1);
|
|
local bestSlotInfo;
|
|
|
|
if not bestSlotInfo then
|
|
for _, slotInfo in ipairs(statSlots) do
|
|
if (not slotInfo.used) and slotInfo.isEmpty then
|
|
slotInfo.used = true;
|
|
bestSlotInfo = slotInfo;
|
|
break
|
|
end
|
|
end
|
|
end
|
|
|
|
if not bestSlotInfo then
|
|
for _, slotInfo in ipairs(statSlots) do
|
|
if (not slotInfo.used) and not slotInfo.isRequired then
|
|
slotInfo.used = true;
|
|
bestSlotInfo = slotInfo;
|
|
local itemID = slotInfo[3];
|
|
local statType = slotInfo.statType;
|
|
if allStatGemCount[statType] then
|
|
AddStatGemToList(allStatGemCount[statType], itemID, false);
|
|
end
|
|
break
|
|
end
|
|
end
|
|
end
|
|
|
|
if not bestSlotInfo then
|
|
for _, slotInfo in ipairs(statSlots) do
|
|
if (not slotInfo.used) and slotInfo.isRequired then
|
|
local itemID = slotInfo[3];
|
|
if DataProvider:IsLeftStatGemBetter(bestGemID, itemID) then
|
|
slotInfo.used = true;
|
|
bestSlotInfo = slotInfo;
|
|
local statType = slotInfo.statType;
|
|
if allStatGemCount[statType] then
|
|
AddStatGemToList(allStatGemCount[statType], itemID, false);
|
|
end
|
|
break
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
if bestSlotInfo then
|
|
local slotID, socketIndex, gemItemID = bestSlotInfo[1], bestSlotInfo[2], bestSlotInfo[3];
|
|
if not bestSlotInfo.isEmpty then
|
|
tinsert(slotActions.remove, {slotID, socketIndex, gemItemID});
|
|
--print("Add", bestGemID, ItemCache:GetItemName(bestGemID), "to replace", slotID, socketIndex, gemItemID, ItemCache:GetItemName(gemItemID))
|
|
else
|
|
--print("Add", bestGemID, ItemCache:GetItemName(bestGemID), "to slot", slotID, socketIndex)
|
|
end
|
|
tinsert(slotActions.insert, {slotID, socketIndex, bestGemID});
|
|
if inBagItem then
|
|
tinsert(slotActions.remove, {bestGemID});
|
|
end
|
|
searchIndexOffset = searchIndexOffset - 1;
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function LoadoutUtil:GetSlotActionsForLoadout(loadoutIndex, searchStatsOnly)
|
|
--May ask the user to equip when switching a gem from low stat budget slot to a higher one
|
|
local gemInfo = self:GetLoadoutGemInfo(loadoutIndex);
|
|
local currentSlotGems = self:GetCurrentItems();
|
|
|
|
local slotActions = {
|
|
insert = {}, --{slotID, socketIndex, gemItemID}
|
|
remove = {}, --{slotID, socketIndex, (itemID)} or {itemID}
|
|
};
|
|
|
|
local errors = {};
|
|
local removeBagItem = {};
|
|
local equippedGemID, requiredGemID;
|
|
local slotID, socketIndex;
|
|
|
|
|
|
if not searchStatsOnly then
|
|
--Meta
|
|
slotID = 1;
|
|
socketIndex = 1;
|
|
if currentSlotGems[slotID] then
|
|
equippedGemID = currentSlotGems[slotID][socketIndex];
|
|
requiredGemID = gemInfo.head;
|
|
|
|
if requiredGemID and (equippedGemID and equippedGemID ~= requiredGemID) then
|
|
if DataProvider:IsGemCollected(requiredGemID) then
|
|
if equippedGemID ~= NO_GEM_ID then
|
|
tinsert(slotActions.remove,
|
|
{slotID, socketIndex, equippedGemID}
|
|
);
|
|
end
|
|
tinsert(slotActions.insert, {slotID, socketIndex, requiredGemID});
|
|
|
|
if not DoesPlayerHaveSparedItem(requiredGemID) then
|
|
tinsert(removeBagItem, requiredGemID);
|
|
end
|
|
else
|
|
tinsert(errors, {1, requiredGemID});
|
|
|
|
if equippedGemID == NO_GEM_ID then
|
|
local gemItemID = DataProvider:GetFallbackMeta();
|
|
tinsert(slotActions.insert, {slotID, socketIndex, gemItemID});
|
|
if not DoesPlayerHaveSparedItem(gemItemID) then
|
|
tinsert(removeBagItem, gemItemID);
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
--Cogwheel
|
|
slotID = 8;
|
|
socketIndex = 1;
|
|
if currentSlotGems[slotID] then
|
|
equippedGemID = currentSlotGems[slotID][socketIndex];
|
|
requiredGemID = gemInfo.feet;
|
|
if requiredGemID and (equippedGemID and equippedGemID ~= requiredGemID) then
|
|
if DataProvider:IsGemCollected(requiredGemID) then
|
|
if equippedGemID ~= NO_GEM_ID then
|
|
tinsert(slotActions.remove,
|
|
{slotID, socketIndex, equippedGemID}
|
|
);
|
|
end
|
|
tinsert(slotActions.insert, {slotID, socketIndex, requiredGemID});
|
|
|
|
if not DoesPlayerHaveSparedItem(requiredGemID) then
|
|
tinsert(removeBagItem, requiredGemID);
|
|
end
|
|
else
|
|
tinsert(errors, {1, requiredGemID});
|
|
|
|
if equippedGemID == NO_GEM_ID then
|
|
local gemItemID = DataProvider:GetFallbackCogwheel();
|
|
tinsert(slotActions.insert, {slotID, socketIndex, gemItemID});
|
|
if not DoesPlayerHaveSparedItem(gemItemID) then
|
|
tinsert(removeBagItem, gemItemID);
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
--Tinker
|
|
local requiredTinker = {};
|
|
local numEmptyTinkerSockets = 0;
|
|
|
|
for _, gemItemID in ipairs(gemInfo.tinker) do
|
|
if DataProvider:IsGemCollected(gemItemID) then
|
|
requiredTinker[gemItemID] = true;
|
|
else
|
|
tinsert(errors, {1, gemItemID});
|
|
end
|
|
end
|
|
|
|
local equippedTinker = {};
|
|
local removalCandidate = {}; --Empty this socket if it has a gem not included in the loadout
|
|
|
|
for _, slotID in ipairs(TINKER_SLOT) do
|
|
local equippedGems = currentSlotGems[slotID];
|
|
if equippedGems then
|
|
for socketIndex, gemItemID in ipairs(equippedGems) do
|
|
if not requiredTinker[gemItemID] then
|
|
local candidate = {slotID, socketIndex};
|
|
if gemItemID == NO_GEM_ID then
|
|
candidate.isEmpty = true;
|
|
numEmptyTinkerSockets = numEmptyTinkerSockets + 1;
|
|
else
|
|
candidate[3] = gemItemID;
|
|
end
|
|
tinsert(removalCandidate, candidate);
|
|
end
|
|
|
|
if gemItemID ~= NO_GEM_ID then
|
|
equippedTinker[gemItemID] = true;
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
for gemItemID in pairs(requiredTinker) do
|
|
if not equippedTinker[gemItemID] then
|
|
local candidate = tremove(removalCandidate);
|
|
if candidate then
|
|
if candidate.isEmpty then
|
|
numEmptyTinkerSockets = numEmptyTinkerSockets - 1;
|
|
else
|
|
tinsert(slotActions.remove, candidate);
|
|
end
|
|
slotID = candidate[1];
|
|
socketIndex = candidate[2];
|
|
tinsert(slotActions.insert, {slotID, socketIndex, gemItemID});
|
|
|
|
if not DoesPlayerHaveSparedItem(gemItemID) then
|
|
tinsert(removeBagItem, gemItemID);
|
|
end
|
|
else
|
|
--Not enough slot to equip
|
|
break
|
|
end
|
|
end
|
|
end
|
|
|
|
--Fill up empty sockets if players don't the items saved in their loadout
|
|
if numEmptyTinkerSockets > 0 then
|
|
local fallbackGems = DataProvider:GetFallbackTinkers(numEmptyTinkerSockets, requiredTinker);
|
|
local index = 0;
|
|
for _, candidate in ipairs(removalCandidate) do
|
|
if candidate.isEmpty then
|
|
index = index + 1;
|
|
local gemItemID = fallbackGems[index];
|
|
if gemItemID then
|
|
slotID = candidate[1];
|
|
socketIndex = candidate[2];
|
|
tinsert(slotActions.insert, {slotID, socketIndex, gemItemID});
|
|
|
|
if not DoesPlayerHaveSparedItem(gemItemID) then
|
|
tinsert(removeBagItem, gemItemID);
|
|
end
|
|
|
|
--print("Use Fallback Tinker", gemItemID, ItemCache:GetItemName(gemItemID))
|
|
else
|
|
break
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
--Stats
|
|
local requiredStats = {};
|
|
local requiredStats1 = {};
|
|
local requiredStats2 = {};
|
|
local requiredStats3 = {};
|
|
|
|
if gemInfo.stats1 then
|
|
PopulateRequiredStatsTable(gemInfo.stats1, requiredStats, requiredStats1);
|
|
end
|
|
|
|
if gemInfo.stats2 then
|
|
PopulateRequiredStatsTable(gemInfo.stats2, requiredStats, requiredStats2);
|
|
end
|
|
|
|
if gemInfo.stats3 then
|
|
PopulateRequiredStatsTable(gemInfo.stats3, requiredStats, requiredStats3);
|
|
end
|
|
|
|
|
|
local allStatGemCount = {};
|
|
for statType in pairs(requiredStats) do
|
|
allStatGemCount[statType] = DataProvider:GetAvailableGemListForStat(statType);
|
|
end
|
|
|
|
|
|
--Chest, Legs
|
|
local equippedStats1 = {};
|
|
local stat1Slots = {};
|
|
if gemInfo.stats1 then
|
|
for _, slotID in ipairs(STAT1_SLOTS) do
|
|
MergeStatSlot(currentSlotGems, slotID, stat1Slots);
|
|
end
|
|
SortStatSlot(stat1Slots, requiredStats1, equippedStats1);
|
|
ProcessStatSlots(requiredStats1, stat1Slots, allStatGemCount, slotActions);
|
|
end
|
|
|
|
--Trinkets
|
|
local equippedStats2 = {};
|
|
local stat2Slots = {};
|
|
if gemInfo.stats2 then
|
|
for _, slotID in ipairs(STAT2_SLOTS) do
|
|
MergeStatSlot(currentSlotGems, slotID, stat2Slots);
|
|
end
|
|
SortStatSlot(stat2Slots, requiredStats2, equippedStats2);
|
|
ProcessStatSlots(requiredStats2, stat2Slots, allStatGemCount, slotActions);
|
|
end
|
|
|
|
--Neck, Rings
|
|
local equippedStats3 = {};
|
|
local stat3Slots = {};
|
|
if gemInfo.stats3 then
|
|
for _, slotID in ipairs(STAT3_SLOTS) do
|
|
MergeStatSlot(currentSlotGems, slotID, stat3Slots);
|
|
end
|
|
SortStatSlot(stat3Slots, requiredStats3, equippedStats3);
|
|
ProcessStatSlots(requiredStats3, stat3Slots, allStatGemCount, slotActions);
|
|
end
|
|
|
|
|
|
for _, itemID in ipairs(removeBagItem) do
|
|
tinsert(slotActions.remove, {itemID});
|
|
end
|
|
|
|
local uniqueGems = {};
|
|
local itemID;
|
|
local requiredBagSpace = 1; --We required an extra bag slot in case something went wrong
|
|
|
|
for _, info in ipairs(slotActions.remove) do
|
|
itemID = info[3] or info[1];
|
|
if (itemID and not uniqueGems[itemID]) then
|
|
uniqueGems[itemID] = true;
|
|
if not DoesPlayerHaveSparedItem(itemID) then
|
|
requiredBagSpace = requiredBagSpace + 1;
|
|
end
|
|
end
|
|
end
|
|
|
|
return slotActions, errors, requiredBagSpace
|
|
end
|
|
end
|
|
|
|
|
|
|
|
|
|
local LoadoutButtonMixin = {};
|
|
local CreateLoadoutButton;
|
|
|
|
do
|
|
function LoadoutButtonMixin:OnEnter()
|
|
if not self.isEquipped then
|
|
self.Name:SetTextColor(1, 1, 1);
|
|
end
|
|
|
|
LoadoutFrame:SetExpandedButton(self);
|
|
|
|
if not LoadoutFrame:IsAnyDataSelected() then
|
|
MouseOverFrame:SetLoadout(self.index);
|
|
end
|
|
end
|
|
|
|
function LoadoutButtonMixin:OnLeave()
|
|
if self:IsVisible() and self:IsMouseOver() then
|
|
return
|
|
end
|
|
|
|
if self.isEquipped then
|
|
|
|
else
|
|
self.Name:SetTextColor(0.67, 0.67, 0.67);
|
|
end
|
|
|
|
LoadoutFrame:SetExpandedButton(nil);
|
|
end
|
|
|
|
function LoadoutButtonMixin:OnClick()
|
|
if LoadoutFrame:IsDataSelectedByIndex(self.index) then
|
|
LoadoutFrame:ClearSelection();
|
|
else
|
|
LoadoutFrame:SelectLoadoutByIndex(self.index);
|
|
end
|
|
end
|
|
|
|
function LoadoutButtonMixin:OnDoubleClick()
|
|
--Consume this action since we're using a gesture different from equipment set to equip gem loadout
|
|
end
|
|
|
|
function LoadoutButtonMixin:SetData(data, isEquipped)
|
|
self.Name:SetText(data.name);
|
|
self.isEquipped = isEquipped;
|
|
if isEquipped then
|
|
self.Name:SetTextColor(1, 0.82, 0);
|
|
else
|
|
self.Name:SetTextColor(0.67, 0.67, 0.67);
|
|
end
|
|
end
|
|
|
|
function LoadoutButtonMixin:SetExpanded(isExpanded)
|
|
if isExpanded and (not self.isExpanded) then
|
|
self.isExpanded = true;
|
|
self:SetHeight(LOADOUT_BUTTON_HEIGHT_EXPANDED);
|
|
self:SetAlpha(0);
|
|
elseif (not isExpanded) and self.isExpanded then
|
|
self.isExpanded = false;
|
|
self:SetHeight(LOADOUT_BUTTON_HEIGHT_COLLAPSED);
|
|
self:SetAlpha(1);
|
|
end
|
|
end
|
|
|
|
|
|
function CreateLoadoutButton(parent, index)
|
|
local f = CreateFrame("Button", nil, parent);
|
|
Mixin(f, LoadoutButtonMixin);
|
|
f:SetSize(LOADOUT_BUTTON_WIDTH, LOADOUT_BUTTON_HEIGHT_COLLAPSED);
|
|
f.index = index;
|
|
|
|
f:SetScript("OnEnter", f.OnEnter);
|
|
f:SetScript("OnLeave", f.OnLeave);
|
|
f:SetScript("OnClick", f.OnClick);
|
|
f:SetScript("OnDoubleClick", f.OnDoubleClick);
|
|
|
|
f.Name = f:CreateFontString(nil, "OVERLAY", "NarciGemmaFontLarge");
|
|
f.Name:SetJustifyH("LEFT");
|
|
f.Name:SetPoint("LEFT", f, "LEFT", 32, 0);
|
|
|
|
return f
|
|
end
|
|
end
|
|
|
|
|
|
|
|
|
|
local RightIconMixin = {};
|
|
do
|
|
function RightIconMixin:Init()
|
|
self:SetScript("OnEnter", self.OnEnter);
|
|
self:SetScript("OnLeave", self.OnLeave);
|
|
self:SetScript("OnHide", self.OnHide);
|
|
end
|
|
|
|
function RightIconMixin:SetEquipped()
|
|
AtlasUtil:SetAtlas(self.Icon, "remix-loadout-checkmark");
|
|
self.description = L["Loadout Equipped"];
|
|
end
|
|
|
|
function RightIconMixin:SetLastApplied()
|
|
AtlasUtil:SetAtlas(self.Icon, "remix-loadout-bluestar");
|
|
self.description = L["Last Used Loadout"];
|
|
end
|
|
|
|
function RightIconMixin:OnEnter()
|
|
SimpleTooltip:ShowTooltip(self, self.description);
|
|
end
|
|
|
|
function RightIconMixin:OnLeave()
|
|
SimpleTooltip:FadeOut();
|
|
|
|
if self.parentButton and self.parentButton:IsShown() then
|
|
self.parentButton:OnLeave();
|
|
end
|
|
end
|
|
|
|
function RightIconMixin:OnHide()
|
|
self:OnLeave();
|
|
end
|
|
|
|
function RightIconMixin:SetSmall(state)
|
|
if state then
|
|
self.Icon:SetSize(18, 18);
|
|
self:EnableMouse(false);
|
|
else
|
|
self.Icon:SetSize(24, 24);
|
|
self:EnableMouse(true);
|
|
end
|
|
end
|
|
|
|
function RightIconMixin:AnchorToLoadoutButton(loadoutButton)
|
|
self.parentButton = loadoutButton;
|
|
self:ClearAllPoints();
|
|
if loadoutButton.isExpanded then
|
|
ListRightIcon:SetPoint("RIGHT", loadoutButton, "RIGHT", -12, 0);
|
|
ListRightIcon:SetSmall(false);
|
|
else
|
|
ListRightIcon:SetPoint("RIGHT", loadoutButton, "RIGHT", -26, 0);
|
|
ListRightIcon:SetSmall(true);
|
|
end
|
|
self:SetFrameLevel(loadoutButton:GetFrameLevel() + 2);
|
|
self:Show();
|
|
end
|
|
end
|
|
|
|
|
|
|
|
|
|
local NewButtonMixin = {};
|
|
do
|
|
function NewButtonMixin:Init()
|
|
AtlasUtil:SetAtlas(self.Icon, "remix-loadout-plus");
|
|
self.Text:SetText(L["New Loadout"]);
|
|
self:SetHighlighed(false);
|
|
|
|
self:SetScript("OnEnter", self.OnEnter);
|
|
self:SetScript("OnLeave", self.OnLeave);
|
|
self:SetScript("OnClick", self.OnClick);
|
|
self:SetScript("OnMouseDown", self.OnMouseDown);
|
|
self:SetScript("OnMouseUp", self.OnMouseUp);
|
|
end
|
|
|
|
function NewButtonMixin:SetExpanded(isExpanded)
|
|
if isExpanded and (not self.isExpanded) then
|
|
self.isExpanded = true;
|
|
self:SetHeight(LOADOUT_BUTTON_HEIGHT_EXPANDED);
|
|
elseif (not isExpanded) and self.isExpanded then
|
|
self.isExpanded = false;
|
|
self:SetHeight(LOADOUT_BUTTON_HEIGHT_COLLAPSED);
|
|
end
|
|
end
|
|
|
|
function NewButtonMixin:SetHighlighed(state)
|
|
if state then
|
|
self.Text:SetTextColor(0.098, 1.000, 0.098); --0.098, 1.000, 0.098 1, 1, 1
|
|
self.Icon:SetVertexColor(0.098, 1.000, 0.098);
|
|
else
|
|
self.Text:SetTextColor(0.67, 0.67, 0.67);
|
|
self.Icon:SetVertexColor(0.67, 0.67, 0.67);
|
|
end
|
|
end
|
|
|
|
function NewButtonMixin:OnEnter()
|
|
self:SetHighlighed(true);
|
|
LoadoutFrame:SetExpandedButton(self);
|
|
if LoadoutFrame:IsAnyDataSelected() then
|
|
self:SetExpanded(false);
|
|
else
|
|
self:SetExpanded(true);
|
|
end
|
|
end
|
|
|
|
function NewButtonMixin:OnLeave()
|
|
self:SetHighlighed(false);
|
|
LoadoutFrame:SetExpandedButton(nil);
|
|
self:SetExpanded(false);
|
|
end
|
|
|
|
function NewButtonMixin:OnClick()
|
|
LoadoutFrame:OpenEditWindow();
|
|
end
|
|
|
|
function NewButtonMixin:OnMouseDown(button)
|
|
if button == "LeftButton" then
|
|
self.Icon:SetPoint("LEFT", self, "LEFT", 30, -1);
|
|
end
|
|
end
|
|
|
|
function NewButtonMixin:OnMouseUp()
|
|
self.Icon:SetPoint("LEFT", self, "LEFT", 30, 0);
|
|
end
|
|
end
|
|
|
|
|
|
|
|
|
|
local FOOTER_BUTTON_HEIGHT = 40;
|
|
local FOOTER_BUTTON_OFFSET = 16;
|
|
local FOOTER_BUTTON_WIDTH = 338 - 2*FOOTER_BUTTON_OFFSET - FOOTER_BUTTON_HEIGHT - 8;
|
|
|
|
local function SetupThreeSliceButton(self)
|
|
self:SetSize(FOOTER_BUTTON_WIDTH, FOOTER_BUTTON_HEIGHT);
|
|
|
|
if not self.ButtonText then
|
|
self.ButtonText = self:CreateFontString(nil, "OVERLAY", "NarciGemmaFontLarge");
|
|
end
|
|
self.ButtonText:SetJustifyH("CENTER");
|
|
self.ButtonText:SetPoint("CENTER", self, "CENTER", 0, 0);
|
|
self.ButtonText:SetWidth(FOOTER_BUTTON_WIDTH - 64);
|
|
|
|
self.Left = self:CreateTexture(nil, "BACKGROUND");
|
|
self.Center = self:CreateTexture(nil, "BACKGROUND");
|
|
self.Right = self:CreateTexture(nil, "BACKGROUND");
|
|
|
|
self.Left:SetPoint("BOTTOMLEFT", self, "BOTTOMLEFT", 0, 0);
|
|
self.Right:SetPoint("BOTTOMRIGHT", self, "BOTTOMRIGHT", 0, 0);
|
|
self.Center:SetPoint("TOPLEFT", self.Left, "TOPRIGHT", 0, 0);
|
|
self.Center:SetPoint("BOTTOMRIGHT", self.Right, "BOTTOMLEFT", 0, 0);
|
|
|
|
|
|
AtlasUtil:SetAtlas(self.Left, "remix-loadout-equip-left");
|
|
AtlasUtil:SetAtlas(self.Right, "remix-loadout-equip-right");
|
|
AtlasUtil:SetAtlas(self.Center, "remix-loadout-equip-center");
|
|
--self.Center:SetHorizTile(true); --Doesn't work?
|
|
end
|
|
|
|
|
|
|
|
|
|
local EquipButtonMixin = {};
|
|
do
|
|
local HIGHLIGHT_MIN_ALPHA = 0.2;
|
|
|
|
local function HighlightFrame_OnUpdate(self, elapsed)
|
|
if self.isFocused then
|
|
if self.targetAlpha then
|
|
self.breathAlpha = self.breathAlpha + 6*elapsed;
|
|
if self.breathAlpha >= 1 then
|
|
self.breathAlpha = 1;
|
|
self.targetAlpha = nil;
|
|
end
|
|
self.BreathLight:SetAlpha(self.breathAlpha);
|
|
end
|
|
else
|
|
self.breathAlpha = self.breathAlpha + self.breathDelta * elapsed;
|
|
if self.breathAlpha >= 1 then
|
|
self.breathDelta = -0.5;
|
|
self.breathAlpha = 1;
|
|
elseif self.breathAlpha <= HIGHLIGHT_MIN_ALPHA then
|
|
self.breathDelta = 1;
|
|
self.breathAlpha = HIGHLIGHT_MIN_ALPHA;
|
|
end
|
|
self.BreathLight:SetAlpha(self.breathAlpha);
|
|
end
|
|
end
|
|
|
|
function EquipButtonMixin:Init()
|
|
self.Init = nil;
|
|
|
|
SetupThreeSliceButton(self);
|
|
|
|
self:ClearAllPoints();
|
|
self:SetPoint("LEFT", LoadoutFrame, "BOTTOMLEFT", FOOTER_BUTTON_OFFSET, 34);
|
|
|
|
AtlasUtil:SetAtlas(self.HighlightFrame.BreathLight, "remix-loadout-equip-breathlight");
|
|
AtlasUtil:SetAtlas(self.HighlightFrame.LineLight, "remix-loadout-equip-linelight");
|
|
|
|
self.HighlightFrame:SetScript("OnUpdate", HighlightFrame_OnUpdate);
|
|
|
|
self:SetScript("OnEnter", self.OnEnter);
|
|
self:SetScript("OnLeave", self.OnLeave);
|
|
self:SetScript("OnEnable", self.OnEnable);
|
|
self:SetScript("OnDisable", self.OnDisable);
|
|
self:SetScript("OnMouseDown", self.OnMouseDown);
|
|
self:SetScript("OnMouseUp", self.OnMouseUp);
|
|
self:SetScript("OnClick", self.OnClick);
|
|
|
|
self:OnClearSelection();
|
|
end
|
|
|
|
function EquipButtonMixin:OnClearSelection()
|
|
self.action = nil;
|
|
self.actionType = nil;
|
|
self.ButtonText:SetText(L["Select A Loadout"]);
|
|
self.ButtonText:SetTextColor(0.5, 0.5, 0.5);
|
|
self:Disable();
|
|
end
|
|
|
|
function EquipButtonMixin:SetClickTimes(n, itemMissing)
|
|
if n == 0 then
|
|
self:Disable();
|
|
if itemMissing then
|
|
self.ButtonText:SetText(L["Loadout Equipped Partially"]);
|
|
else
|
|
self.ButtonText:SetText(L["Loadout Equipped"]);
|
|
end
|
|
self.ButtonText:SetTextColor(0.5, 0.5, 0.5);
|
|
elseif n == 1 then
|
|
self:Enable();
|
|
self.ButtonText:SetText(string.format(L["Format Click Times To Equip Singular"], n));
|
|
self.ButtonText:SetTextColor(1, 1, 1);
|
|
else
|
|
self:Enable();
|
|
self.ButtonText:SetText(string.format(L["Format Click Times To Equip Plural"], n));
|
|
self.ButtonText:SetTextColor(1, 1, 1);
|
|
end
|
|
end
|
|
|
|
function EquipButtonMixin:SetRequiredBagSpace(amount)
|
|
self:Disable();
|
|
self.ButtonText:SetText(L["Format Free Up Bag Slot"]:format(amount));
|
|
self.ButtonText:SetTextColor(1, 0.282, 0);
|
|
end
|
|
|
|
function EquipButtonMixin:OnLoadoutSelected()
|
|
self:Update();
|
|
self:PlaySelectionFeedback();
|
|
end
|
|
|
|
function EquipButtonMixin:Update()
|
|
if Automation:IsProcessing() then
|
|
self:Disable();
|
|
local text = Automation:GetProcessText();
|
|
if not text then
|
|
text = L["Equipping Gems"];
|
|
end
|
|
self.ButtonText:SetText(text);
|
|
self.ButtonText:SetTextColor(0.5, 0.5, 0.5);
|
|
return
|
|
end
|
|
|
|
local loadoutIndex = LoadoutFrame:GetSelectedLoadoutIndex();
|
|
Gemma.HideActionButton();
|
|
|
|
if loadoutIndex then
|
|
local numClicks;
|
|
local action;
|
|
|
|
local searchStatsOnly = LoadoutUtil:IsLoadoutEquipped(loadoutIndex);
|
|
|
|
local slotActions, errors, requiredBagSpace = LoadoutUtil:GetSlotActionsForLoadout(loadoutIndex, searchStatsOnly);
|
|
if not Gemma:DoesBagHaveEnoughSpace(requiredBagSpace) then
|
|
self:SetRequiredBagSpace(requiredBagSpace);
|
|
return
|
|
end
|
|
|
|
local itemMissing = #errors > 0
|
|
|
|
local numRemoval = #slotActions.remove;
|
|
local numInsert = #slotActions.insert;
|
|
numClicks = numRemoval;
|
|
|
|
if numRemoval == 0 and numInsert == 0 then
|
|
numClicks = 0;
|
|
else
|
|
if numInsert > 0 then
|
|
numClicks = numClicks + 1;
|
|
end
|
|
end
|
|
|
|
if numRemoval == 0 then
|
|
action = slotActions.insert;
|
|
self.actionType = 2;
|
|
else
|
|
action = slotActions.remove[1];
|
|
self.actionType = 1;
|
|
end
|
|
|
|
self:SetClickTimes(numClicks, itemMissing);
|
|
self.action = action;
|
|
|
|
if numClicks == 0 then
|
|
LoadoutUtil:UpdateAppliedTime(loadoutIndex);
|
|
end
|
|
|
|
if self:IsMouseOver() then
|
|
self:OnEnter();
|
|
end
|
|
else
|
|
self:OnClearSelection();
|
|
end
|
|
end
|
|
|
|
function EquipButtonMixin:PlaySelectionFeedback()
|
|
--self.AnimSelect:Stop();
|
|
--self.AnimSelect:Play();
|
|
self.HighlightFrame.breathDelta = 8;
|
|
end
|
|
|
|
local function RegisterBagEvent_OnUpdate(self, elapsed)
|
|
self.processDelay = self.processDelay + elapsed;
|
|
if self.processDelay >= 0 then
|
|
self.processDelay = -1;
|
|
if LoadoutFrame:IsVisible() then
|
|
if LoadoutFrame:IsMouseOver() then
|
|
|
|
else
|
|
self:SetScript("OnUpdate", nil);
|
|
LoadoutFrame:RegisterEvent("BAG_UPDATE");
|
|
end
|
|
else
|
|
self:SetScript("OnUpdate", nil);
|
|
end
|
|
end
|
|
end
|
|
|
|
function EquipButtonMixin:PauseBagEvent(pause)
|
|
--We update our frame x sec after "BAG_UPDATE" which affects EquipButton Action
|
|
|
|
self.processDelay = -1
|
|
|
|
if pause then
|
|
LoadoutFrame:UnregisterEvent("BAG_UPDATE");
|
|
self:SetScript("OnUpdate", RegisterBagEvent_OnUpdate);
|
|
else
|
|
LoadoutFrame:RegisterEvent("BAG_UPDATE");
|
|
self:SetScript("OnUpdate", nil);
|
|
end
|
|
end
|
|
|
|
function EquipButtonMixin:OnEnter(motion, fromActionButton)
|
|
if not self:IsEnabled() then return end;
|
|
|
|
FadeFrame(self.HighlightFrame.LineLight, 0.15, 1);
|
|
FadeFrame(self.HighlightFrame, 0.15, 1);
|
|
self.HighlightFrame.isFocused = true;
|
|
self.HighlightFrame.targetAlpha = true;
|
|
|
|
if fromActionButton then return end;
|
|
|
|
if self.action then
|
|
if self.actionType == 1 then --Remove
|
|
local ActionButton = AcquireActionButton(self);
|
|
if ActionButton then
|
|
local arg1, arg2 = self.action[1], self.action[2];
|
|
if arg2 then
|
|
self:PauseBagEvent(true);
|
|
ActionButton:SetAction_RemovePandariaGemOnPlayer(arg1, arg2);
|
|
else
|
|
self:PauseBagEvent(false);
|
|
ActionButton:SetAction_RemovePandariaGemInBag(arg1);
|
|
end
|
|
end
|
|
else --Insert
|
|
|
|
end
|
|
end
|
|
end
|
|
|
|
function EquipButtonMixin:OnLeave(motion, fromActionButton)
|
|
FadeFrame(self.HighlightFrame.LineLight, 0.15, 0);
|
|
self.HighlightFrame.isFocused = false;
|
|
self.HighlightFrame.targetAlpha = true;
|
|
end
|
|
|
|
function EquipButtonMixin:ShowHighlight()
|
|
self.HighlightFrame.breathAlpha = 0;
|
|
self.HighlightFrame.breathDelta = 1;
|
|
self.HighlightFrame:Show();
|
|
end
|
|
|
|
function EquipButtonMixin:SetBackgroundAlpha(alpha)
|
|
self.Left:SetAlpha(alpha);
|
|
self.Center:SetAlpha(alpha);
|
|
self.Right:SetAlpha(alpha);
|
|
end
|
|
|
|
function EquipButtonMixin:OnEnable()
|
|
self:SetBackgroundAlpha(1);
|
|
self:ShowHighlight();
|
|
end
|
|
|
|
function EquipButtonMixin:OnDisable()
|
|
self:SetBackgroundAlpha(0.5);
|
|
self:OnLeave(true);
|
|
self.HighlightFrame:Hide();
|
|
self:OnMouseUp();
|
|
end
|
|
|
|
function EquipButtonMixin:OnMouseDown(button)
|
|
if self:IsEnabled() and button == "LeftButton" then
|
|
--self.ButtonText:SetPoint("CENTER", self, "CENTER", 0, -2);
|
|
self.AnimPushed:Stop();
|
|
self.AnimPushed:Play();
|
|
end
|
|
|
|
LoadoutFrame:HideEditWindow();
|
|
end
|
|
|
|
function EquipButtonMixin:OnMouseUp()
|
|
--self.ButtonText:SetPoint("CENTER", self, "CENTER", 0, 0);
|
|
end
|
|
|
|
function EquipButtonMixin:OnClick(button)
|
|
if button ~= "LeftButton" then return end;
|
|
|
|
--MainFrame:ShowActionBlocker();
|
|
if self.action and self.actionType == 2 then
|
|
Automation:EquipGems(self.action);
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
|
|
|
|
local EditButtonMixin = {};
|
|
do
|
|
function EditButtonMixin:Init()
|
|
self:SetSize(FOOTER_BUTTON_HEIGHT, FOOTER_BUTTON_HEIGHT);
|
|
self:ClearAllPoints();
|
|
self:SetPoint("RIGHT", self:GetParent(), "BOTTOMRIGHT", -FOOTER_BUTTON_OFFSET, 34);
|
|
|
|
self.Background = self:CreateTexture(nil, "BACKGROUND");
|
|
self.Background:SetAllPoints(true);
|
|
AtlasUtil:SetAtlas(self.Background, "remix-loadout-edit-bg");
|
|
|
|
self.Icon = self:CreateTexture(nil, "OVERLAY");
|
|
self.Icon:SetPoint("CENTER", self, "CENTER", 0, 0);
|
|
AtlasUtil:SetAtlas(self.Icon, "remix-loadout-edit-setting");
|
|
|
|
self:SetScript("OnEnter", self.OnEnter);
|
|
self:SetScript("OnLeave", self.OnLeave);
|
|
self:SetScript("OnEnable", self.OnEnable);
|
|
self:SetScript("OnDisable", self.OnDisable);
|
|
self:SetScript("OnMouseDown", self.OnMouseDown);
|
|
self:SetScript("OnMouseUp", self.OnMouseUp);
|
|
self:SetScript("OnClick", self.OnClick);
|
|
|
|
self:Disable();
|
|
|
|
self:RegisterForClicks("LeftButtonUp");
|
|
self.tooltipText = L["Edit Loadout"];
|
|
end
|
|
|
|
function EditButtonMixin:OnLoadoutSelected()
|
|
self:Enable();
|
|
end
|
|
|
|
function EditButtonMixin:OnClearSelection()
|
|
self:Disable();
|
|
end
|
|
|
|
function EditButtonMixin:OnEnter()
|
|
self.Icon:SetVertexColor(1, 1, 1);
|
|
AtlasUtil:SetAtlas(self.Background, "remix-loadout-edit-highlight");
|
|
SimpleTooltip:ShowTooltip(self, self.tooltipText);
|
|
end
|
|
|
|
function EditButtonMixin:OnLeave()
|
|
self.Icon:SetVertexColor(0.67, 0.67, 0.67);
|
|
AtlasUtil:SetAtlas(self.Background, "remix-loadout-edit-bg");
|
|
SimpleTooltip:FadeOut();
|
|
end
|
|
|
|
function EditButtonMixin:OnEnable()
|
|
self:SetAlpha(1);
|
|
end
|
|
|
|
function EditButtonMixin:OnDisable()
|
|
self:SetAlpha(0.5);
|
|
self:OnLeave();
|
|
self:OnMouseUp();
|
|
end
|
|
|
|
function EditButtonMixin:OnMouseDown()
|
|
if not self:IsEnabled() then return end;
|
|
self.Icon:SetPoint("CENTER", self, "CENTER", 0, -1);
|
|
end
|
|
|
|
function EditButtonMixin:OnMouseUp()
|
|
self.Icon:SetPoint("CENTER", self, "CENTER", 0, 0);
|
|
end
|
|
|
|
function EditButtonMixin:OnClick(button)
|
|
if EditWindow and EditWindow:IsShown() then
|
|
LoadoutFrame:HideEditWindow();
|
|
return
|
|
end
|
|
|
|
local index = LoadoutFrame.selectedIndex;
|
|
if not index then return end;
|
|
|
|
LoadoutFrame:OpenEditWindow(index);
|
|
end
|
|
end
|
|
|
|
|
|
|
|
|
|
local MouseOverFrameMixin = {};
|
|
do
|
|
local PADDING_H = 20;
|
|
local PADDING_V = 8;
|
|
|
|
function MouseOverFrameMixin:Init()
|
|
self.Init = nil;
|
|
self.icons = {};
|
|
|
|
local iconSize = 16;
|
|
local iconGap = 2;
|
|
local numIcons = 6;
|
|
|
|
for i = 1, numIcons do
|
|
local icon = self:CreateTexture(nil, "OVERLAY");
|
|
self.icons[i] = icon;
|
|
icon:SetSize(iconSize, iconSize);
|
|
icon:SetTexCoord(0.1, 0.9, 0.1, 0.9);
|
|
icon:SetPoint("BOTTOMLEFT", self, "BOTTOMLEFT", PADDING_H + (i - 1) * (iconSize + iconGap), PADDING_V);
|
|
icon:SetAlpha(1 - 0.15*(i - 1));
|
|
end
|
|
|
|
local iconFrameWidth = numIcons * (iconSize + iconGap) - iconGap;
|
|
|
|
self.StatText:ClearAllPoints();
|
|
self.StatText:SetPoint("LEFT", self, "BOTTOMLEFT", PADDING_H + iconFrameWidth + PADDING_H, PADDING_V + 0.5*iconSize);
|
|
self.Name:SetPoint("TOPLEFT", self, "TOPLEFT", PADDING_H, -PADDING_V);
|
|
end
|
|
|
|
function MouseOverFrameMixin:SetLoadout(index)
|
|
if self.Init then
|
|
self:Init();
|
|
end
|
|
|
|
self.loadoutIndex = index;
|
|
|
|
local data = LoadoutUtil:GetLoadoutData(index);
|
|
if not data then return end;
|
|
|
|
local overview = LoadoutUtil:GetLoadoutOverview(index);
|
|
self.Name:SetText(data.name);
|
|
self.StatText:SetText(overview.statText);
|
|
|
|
for i = 1, 6 do
|
|
self.icons[i]:SetTexture(overview.icons[i]);
|
|
end
|
|
end
|
|
|
|
function MouseOverFrameMixin:SetSelected(isSelected)
|
|
self.isSelected = isSelected;
|
|
self:UpdateBackground();
|
|
end
|
|
|
|
function MouseOverFrameMixin:UpdateBackground()
|
|
if self.isSelected then
|
|
local isEquipped = self.loadoutIndex and self.loadoutIndex == LoadoutUtil:GetEquippedLoadoutIndex();
|
|
if isEquipped then
|
|
AtlasUtil:SetAtlas(MouseOverFrame.Background, "remix-loadout-detail-selection-equipped");
|
|
else
|
|
AtlasUtil:SetAtlas(MouseOverFrame.Background, "remix-loadout-detail-selection-regular");
|
|
end
|
|
else
|
|
AtlasUtil:SetAtlas(MouseOverFrame.Background, "remix-loadout-detail-bg");
|
|
end
|
|
end
|
|
|
|
function MouseOverFrameMixin:UpdateLoadout()
|
|
if self.loadoutIndex then
|
|
self:SetLoadout(self.loadoutIndex);
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
|
|
|
|
local LoadoutFrameMixin = {};
|
|
Gemma.LoadoutFrameMixin = LoadoutFrameMixin;
|
|
do
|
|
function LoadoutFrameMixin:OnLoad()
|
|
SimpleTooltip = Gemma.CreateSimpleTooltip(self);
|
|
|
|
MainFrame = Gemma.MainFrame;
|
|
LoadoutFrame = self;
|
|
|
|
EquipButton = self.EquipButton;
|
|
Mixin(EquipButton, EquipButtonMixin);
|
|
|
|
EditButton = self.EditButton;
|
|
Mixin(EditButton, EditButtonMixin);
|
|
|
|
MouseOverFrame = self.MouseOverFrame;
|
|
MouseOverFrame:SetSize(322, LOADOUT_BUTTON_HEIGHT_EXPANDED);
|
|
AtlasUtil:SetAtlas(MouseOverFrame.Background, "remix-loadout-detail-bg");
|
|
Mixin(MouseOverFrame, MouseOverFrameMixin);
|
|
|
|
ListRightIcon = self.LoadoutList.RightIcon;
|
|
|
|
|
|
self.loadoutButtons = {};
|
|
self.Title:SetTextColor(0.88, 0.88, 0.88);
|
|
self:SetTitle(L["Loadout"].." |cff80808010/10|r");
|
|
|
|
self:SetScript("OnShow", self.OnShow);
|
|
self:SetScript("OnHide", self.OnHide);
|
|
|
|
AtlasUtil:SetAtlas(self.Divider, "remix-ui-divider");
|
|
--AtlasUtil:SetAtlas(EquipButton.Background, "remix-loadout-detail-bg");
|
|
|
|
|
|
AtlasUtil:SetAtlas(self.ButtonHighlight.Texture, "remix-listbutton-highlight");
|
|
self.ButtonHighlight.Texture:SetBlendMode("ADD");
|
|
end
|
|
|
|
function LoadoutFrameMixin:InitFrame()
|
|
self.InitFrame = nil;
|
|
|
|
LoadoutUtil:LoadSaves();
|
|
DataProvider = Gemma:GetDataProviderByName("Pandaria");
|
|
EditButton:Init();
|
|
EquipButton:Init();
|
|
|
|
Mixin(self.LoadoutList.NewButton, NewButtonMixin);
|
|
self.LoadoutList.NewButton:Init();
|
|
|
|
Mixin(ListRightIcon, RightIconMixin);
|
|
ListRightIcon:Init();
|
|
|
|
self.equipmentChanged = true;
|
|
self.loadoutListChanged = true;
|
|
self:RequestUpdate();
|
|
|
|
self.defaultSelecetdLoadoutIndex = LoadoutUtil:GetLastAppliedLoadoutIndex();
|
|
end
|
|
|
|
function LoadoutFrameMixin:OnShow()
|
|
if self.InitFrame then
|
|
self:InitFrame();
|
|
end
|
|
|
|
self:SetScript("OnEvent", self.OnEvent);
|
|
self:RegisterEvent("PLAYER_EQUIPMENT_CHANGED");
|
|
self:RegisterEvent("BAG_UPDATE");
|
|
end
|
|
|
|
function LoadoutFrameMixin:OnHide()
|
|
--self:UnregisterEvent("BAG_UPDATE");
|
|
end
|
|
|
|
function LoadoutFrameMixin:OnUpdate(elapsed)
|
|
self.t = self.t + elapsed;
|
|
|
|
if self.t >= 0 then
|
|
self.t = 0;
|
|
else
|
|
return
|
|
end
|
|
|
|
local anyAction;
|
|
|
|
if self.equipmentChanged then
|
|
anyAction = true;
|
|
self:PostEquipmentChanged();
|
|
end
|
|
|
|
if self.loadoutListChanged then
|
|
anyAction = true;
|
|
self:PostLoadoutListChanged();
|
|
end
|
|
|
|
if not anyAction then
|
|
self:SetScript("OnUpdate", nil);
|
|
self.t = nil;
|
|
end
|
|
end
|
|
|
|
function LoadoutFrameMixin:RequestUpdate(delay)
|
|
delay = delay and -delay or 0;
|
|
self.t = delay;
|
|
self:SetScript("OnUpdate", self.OnUpdate);
|
|
end
|
|
|
|
function LoadoutFrameMixin:OnEvent(event, ...)
|
|
if event == "PLAYER_EQUIPMENT_CHANGED" or event == "BAG_UPDATE" then
|
|
self.equipmentChanged = true;
|
|
self.loadoutListChanged = true;
|
|
|
|
if event == "BAG_UPDATE" then
|
|
self:RequestUpdate(0.0); --Longer?
|
|
else
|
|
self:RequestUpdate();
|
|
end
|
|
|
|
if not self:IsVisible() then
|
|
self:UnregisterEvent(event);
|
|
return
|
|
end
|
|
end
|
|
|
|
--print(GetTime(), event);
|
|
end
|
|
|
|
function LoadoutFrameMixin:PostEquipmentChanged()
|
|
self.equipmentChanged = false;
|
|
LoadoutUtil.currentGemInfo = nil;
|
|
LoadoutUtil.currentSlotGems = nil;
|
|
LoadoutUtil.equippedLoadoutIndex = nil;
|
|
|
|
EquipButton:Update();
|
|
end
|
|
|
|
function LoadoutFrameMixin:PostLoadoutListChanged()
|
|
self.loadoutListChanged = nil;
|
|
self:UpdateLoadoutList();
|
|
end
|
|
|
|
function LoadoutFrameMixin:UpdateRightIcon()
|
|
for i, button in ipairs(self.loadoutButtons) do
|
|
if button:IsShown() then
|
|
if i == self.equippedIndex then
|
|
ListRightIcon:SetEquipped();
|
|
ListRightIcon:AnchorToLoadoutButton(button);
|
|
return
|
|
elseif i == self.lastAppliedIndex then
|
|
ListRightIcon:SetLastApplied();
|
|
ListRightIcon:AnchorToLoadoutButton(button);
|
|
return
|
|
end
|
|
else
|
|
break
|
|
end
|
|
end
|
|
|
|
ListRightIcon:Hide();
|
|
end
|
|
|
|
function LoadoutFrameMixin:UpdateLoadoutList()
|
|
local numSaves = LoadoutUtil:GetNumSaves();
|
|
local newButtonIndex = numSaves + 1; -- +New Loadout Button
|
|
local button;
|
|
local data;
|
|
|
|
local NewButton = self.LoadoutList.NewButton;
|
|
NewButton:Hide();
|
|
|
|
local equippedIndex = LoadoutUtil:GetEquippedLoadoutIndex();
|
|
local lastAppliedIndex;
|
|
if not equippedIndex then
|
|
lastAppliedIndex = LoadoutUtil:GetLastAppliedLoadoutIndex();
|
|
end
|
|
|
|
self.equippedIndex = equippedIndex;
|
|
self.lastAppliedIndex = lastAppliedIndex;
|
|
|
|
|
|
|
|
for i = 1, MAX_SAVES do
|
|
data = LoadoutUtil:GetLoadoutData(i);
|
|
button = self.loadoutButtons[i];
|
|
if data then
|
|
if not button then
|
|
button = CreateLoadoutButton(self.LoadoutList, i);
|
|
self.loadoutButtons[i] = button;
|
|
if i == 1 then
|
|
button:SetPoint("TOP", self.LoadoutList, "TOP", 0, 0);
|
|
else
|
|
button:SetPoint("TOP", self.loadoutButtons[i - 1], "BOTTOM", 0, 0);
|
|
end
|
|
end
|
|
button:SetData(data, i == equippedIndex);
|
|
button:Show();
|
|
else
|
|
if button then
|
|
button:Hide();
|
|
end
|
|
|
|
if i == newButtonIndex then
|
|
NewButton:ClearAllPoints();
|
|
NewButton:Show();
|
|
if i == 1 then
|
|
NewButton:SetPoint("TOP", self.LoadoutList, "TOP", 0, 0);
|
|
else
|
|
NewButton:SetPoint("TOP", self.loadoutButtons[i - 1], "BOTTOM", 0, 0);
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
self:SetTitle(L["Loadout"].." |cff808080"..numSaves.."/"..MAX_SAVES.."|r");
|
|
|
|
self:UpdateRightIcon();
|
|
|
|
|
|
MouseOverFrame:UpdateBackground();
|
|
MouseOverFrame:UpdateLoadout();
|
|
|
|
if self.defaultSelecetdLoadoutIndex then
|
|
local loadoutIndex = self.defaultSelecetdLoadoutIndex;
|
|
self.defaultSelecetdLoadoutIndex = nil;
|
|
C_Timer.After(0.0, function()
|
|
self:SelectLoadoutByIndex(loadoutIndex);
|
|
end);
|
|
end
|
|
end
|
|
|
|
function LoadoutFrameMixin:SetTitle(title)
|
|
self.Title:SetText(title);
|
|
end
|
|
|
|
function LoadoutFrameMixin:SetExpandedButton(loadoutButton)
|
|
local hl = self.ButtonHighlight;
|
|
hl:ClearAllPoints();
|
|
if loadoutButton then
|
|
hl:SetParent(loadoutButton);
|
|
hl:SetPoint("TOPLEFT", loadoutButton, "TOPLEFT", 0, 0);
|
|
hl:SetPoint("BOTTOMRIGHT", loadoutButton, "BOTTOMRIGHT", 0, 0);
|
|
hl:Show();
|
|
else
|
|
hl:Hide();
|
|
end
|
|
|
|
if self:IsAnyDataSelected() then
|
|
return
|
|
end
|
|
|
|
for i, button in ipairs(self.loadoutButtons) do
|
|
if button == loadoutButton then
|
|
button:SetExpanded(true);
|
|
else
|
|
button:SetExpanded(false);
|
|
end
|
|
end
|
|
|
|
MouseOverFrame:ClearAllPoints();
|
|
|
|
if loadoutButton and loadoutButton.index then
|
|
MouseOverFrame:SetPoint("CENTER", loadoutButton, "CENTER", 0, 0);
|
|
MouseOverFrame.Name:SetText(loadoutButton.Name:GetText());
|
|
MouseOverFrame:Show();
|
|
else
|
|
MouseOverFrame:Hide();
|
|
end
|
|
|
|
self:UpdateRightIcon();
|
|
end
|
|
|
|
function LoadoutFrameMixin:GetSelectedLoadoutIndex()
|
|
return self.selectedIndex
|
|
end
|
|
|
|
function LoadoutFrameMixin:IsAnyDataSelected()
|
|
return self.selectedIndex ~= nil
|
|
end
|
|
|
|
function LoadoutFrameMixin:IsDataSelectedByIndex(index)
|
|
return index and index == self.selectedIndex
|
|
end
|
|
|
|
function LoadoutFrameMixin:SelectLoadoutByIndex(index)
|
|
self.selectedIndex = nil;
|
|
local button = self.loadoutButtons[index];
|
|
self:SetExpandedButton(button);
|
|
|
|
local data = LoadoutUtil:GetLoadoutData(index);
|
|
self.selectedData = data;
|
|
self.selectedIndex = index;
|
|
|
|
if data then
|
|
MouseOverFrame:SetLoadout(index);
|
|
MouseOverFrame:SetSelected(true);
|
|
EquipButton:OnLoadoutSelected(index);
|
|
EditButton:OnLoadoutSelected(index);
|
|
|
|
if self:IsEditWindowShown() then
|
|
if index ~= EditWindow.loadoutIndex then
|
|
EditWindow:SetLoadout(index);
|
|
end
|
|
end
|
|
else
|
|
self:ClearSelection();
|
|
end
|
|
end
|
|
|
|
function LoadoutFrameMixin:ClearSelection()
|
|
self.selectedData = nil;
|
|
self.selectedIndex = nil;
|
|
MouseOverFrame:SetSelected(false);
|
|
EquipButton:OnClearSelection();
|
|
EditButton:OnClearSelection();
|
|
end
|
|
end
|
|
|
|
|
|
|
|
local EDIT_OPTIONS = {
|
|
"head",
|
|
"feet",
|
|
"tinker",
|
|
"stats1",
|
|
"stats2",
|
|
"stats3",
|
|
};
|
|
|
|
local OPTION_BUTTON_HEIGHT = 32;
|
|
local CreateEditOption;
|
|
local CreateNameEditButton;
|
|
|
|
do --Edit Options
|
|
local PADDING_H = 20;
|
|
local ARROW_SIZE = 16;
|
|
|
|
local OptionButtonMixin = {};
|
|
|
|
local function GetChoiceText_ItemID(self, itemID)
|
|
local anySelection = itemID ~= nil;
|
|
local fullySelected = anySelection;
|
|
local text;
|
|
|
|
if anySelection then
|
|
text = ItemCache:GetItemName(itemID, self);
|
|
end
|
|
|
|
return text, anySelection, fullySelected
|
|
end
|
|
|
|
local function GetChoiceText_ItemList(self, list)
|
|
local points = list and #list or 0;
|
|
local anySelection = points > 0;
|
|
local fullySelected = (not self.pointsRequired) or (points >= self.pointsRequired);
|
|
local text;
|
|
|
|
if anySelection then
|
|
text = L["Format Number Items Selected"]:format(points);
|
|
end
|
|
|
|
return text, anySelection, fullySelected
|
|
end
|
|
|
|
local function GetChoiceText_Stats(self, stats)
|
|
local points = 0;
|
|
|
|
if stats then
|
|
for _, amount in pairs(stats) do
|
|
points = points + amount;
|
|
end
|
|
end
|
|
|
|
local fullySelected = (not self.pointsRequired) or (points >= self.pointsRequired);
|
|
local anySelection = fullySelected;
|
|
local text;
|
|
|
|
if fullySelected then
|
|
text = LoadoutUtil:FormatStatText(stats);
|
|
end
|
|
|
|
return text, anySelection, fullySelected
|
|
end
|
|
|
|
function OptionButtonMixin:OnItemLoaded(itemID)
|
|
self:OnSelectionChanged();
|
|
end
|
|
|
|
function OptionButtonMixin:SetCategory_head()
|
|
self.Category:SetText(META_GEM);
|
|
self.pointsRequired = 1;
|
|
self.GetChoiceText = GetChoiceText_ItemID;
|
|
end
|
|
|
|
function OptionButtonMixin:SetCategory_feet()
|
|
self.Category:SetText(COGWHEEL_GEM);
|
|
self.pointsRequired = 1;
|
|
self.GetChoiceText = GetChoiceText_ItemID;
|
|
end
|
|
|
|
function OptionButtonMixin:SetCategory_tinker()
|
|
self.Category:SetText(L["Pandamonium Gem Category 2"]);
|
|
self.pointsRequired = 12;
|
|
self.GetChoiceText = GetChoiceText_ItemList;
|
|
end
|
|
|
|
function OptionButtonMixin:SetCategory_stats1()
|
|
self.Category:SetText(L["Pandamonium Slot Category 1"]);
|
|
self.pointsRequired = 6;
|
|
self.isStat = true;
|
|
self.GetChoiceText = GetChoiceText_Stats;
|
|
end
|
|
|
|
function OptionButtonMixin:SetCategory_stats2()
|
|
self.Category:SetText(L["Pandamonium Slot Category 2"]);
|
|
self.pointsRequired = 6;
|
|
self.isStat = true;
|
|
self.GetChoiceText = GetChoiceText_Stats;
|
|
end
|
|
|
|
function OptionButtonMixin:SetCategory_stats3()
|
|
self.Category:SetText(L["Pandamonium Slot Category 3"]);
|
|
self.pointsRequired = 9;
|
|
self.isStat = true;
|
|
self.GetChoiceText = GetChoiceText_Stats;
|
|
end
|
|
|
|
function OptionButtonMixin:UpdateChoiceText()
|
|
local text, anySelection, fullySelected = self:GetChoiceText(self.choice);
|
|
if fullySelected then
|
|
self.ChoiceText:SetText(text);
|
|
if self.isStat then
|
|
self.ChoiceText:SetTextColor(0.5, 0.5, 0.5);
|
|
else
|
|
self.ChoiceText:SetTextColor(0.88, 0.88, 0.88);
|
|
end
|
|
self.Category:SetTextColor(0.67, 0.67, 0.67);
|
|
self.Arrow:SetVertexColor(0.88, 0.88, 0.88);
|
|
else
|
|
if text then
|
|
self.ChoiceText:SetText(text);
|
|
else
|
|
self.ChoiceText:SetText(L["Select Gems"]);
|
|
end
|
|
self.ChoiceText:SetTextColor(1, 0.82, 0); --0.37, 0.74, 0.42
|
|
self.Category:SetTextColor(0.67, 0.67, 0.67);
|
|
self.Arrow:SetVertexColor(1, 0.82, 0);
|
|
end
|
|
self.fullySelected = fullySelected;
|
|
end
|
|
|
|
function OptionButtonMixin:SetChoice(choice)
|
|
self.choice = choice;
|
|
self:UpdateChoiceText();
|
|
return self.fullySelected
|
|
end
|
|
|
|
function OptionButtonMixin:SetCategory(key)
|
|
self.key = key;
|
|
self["SetCategory_"..key](self);
|
|
local textWidth = self.Category:GetWrappedWidth();
|
|
self.ChoiceText:SetWidth(math.floor(LOADOUT_BUTTON_WIDTH - (textWidth - ARROW_SIZE - OPTION_BUTTON_HEIGHT)));
|
|
end
|
|
|
|
function OptionButtonMixin:OnEnter()
|
|
self.Category:SetTextColor(1, 1, 1);
|
|
EditWindow:HighlightButton(self);
|
|
end
|
|
|
|
function OptionButtonMixin:OnLeave()
|
|
self.Category:SetTextColor(0.67, 0.67, 0.67);
|
|
EditWindow:HighlightButton(nil);
|
|
end
|
|
|
|
function OptionButtonMixin:OnClick()
|
|
EditWindow:ShowPlanner(self.key);
|
|
end
|
|
|
|
function CreateEditOption(parent)
|
|
local f = CreateFrame("Button", nil, parent);
|
|
f:SetSize(LOADOUT_BUTTON_WIDTH, OPTION_BUTTON_HEIGHT);
|
|
|
|
f.Category = f:CreateFontString(nil, "OVERLAY", "NarciGemmaFontLarge");
|
|
f.Category:SetJustifyH("LEFT");
|
|
f.Category:SetPoint("LEFT", f, "LEFT", PADDING_H, 0);
|
|
f.Category:SetTextColor(0.67, 0.67, 0.67);
|
|
|
|
f.ChoiceText = f:CreateFontString(nil, "OVERLAY", "NarciGemmaFontMedium");
|
|
f.ChoiceText:SetJustifyH("RIGHT");
|
|
f.ChoiceText:SetPoint("RIGHT", f, "RIGHT", -PADDING_H - ARROW_SIZE, 0);
|
|
f.ChoiceText:SetMaxLines(1);
|
|
|
|
f.Arrow = f:CreateTexture(nil, "OVERLAY");
|
|
f.Arrow:SetPoint("RIGHT", f, "RIGHT", -PADDING_H, 0);
|
|
AtlasUtil:SetAtlas(f.Arrow, "gemlist-next");
|
|
|
|
Mixin(f, OptionButtonMixin);
|
|
|
|
f:SetScript("OnEnter", f.OnEnter);
|
|
f:SetScript("OnLeave", f.OnLeave);
|
|
f:SetScript("OnClick", f.OnClick);
|
|
|
|
return f
|
|
end
|
|
|
|
|
|
local NameButtonMixin = {};
|
|
NameButtonMixin.OnEnter = OptionButtonMixin.OnEnter;
|
|
NameButtonMixin.OnLeave = OptionButtonMixin.OnLeave;
|
|
|
|
function NameButtonMixin:OnClick()
|
|
self.EditBox:SetFocus();
|
|
end
|
|
|
|
function NameButtonMixin:SetLoadoutName(name)
|
|
self.EditBox:SetText(name or "");
|
|
self.EditBox:SetHighlighed(false);
|
|
end
|
|
|
|
function NameButtonMixin:GetText()
|
|
local text = strtrim(self.EditBox:GetText());
|
|
if text ~= "" then
|
|
return text
|
|
end
|
|
end
|
|
|
|
local EditBoxMixin = {};
|
|
|
|
function EditBoxMixin:SetHighlighed(state)
|
|
if state then
|
|
self.Border:SetVertexColor(1, 1, 1);
|
|
else
|
|
if self:IsTextValid() then
|
|
self.Border:SetVertexColor(0.5, 0.5, 0.5);
|
|
else
|
|
self.Border:SetVertexColor(1, 0.82, 0.0);
|
|
end
|
|
end
|
|
end
|
|
|
|
function EditBoxMixin:OnEnter()
|
|
OptionButtonMixin.OnEnter(self:GetParent());
|
|
end
|
|
|
|
function EditBoxMixin:OnLeave()
|
|
OptionButtonMixin.OnLeave(self:GetParent());
|
|
end
|
|
|
|
function EditBoxMixin:OnEditFocusGained()
|
|
self:SetHighlighed(true);
|
|
end
|
|
|
|
function EditBoxMixin:OnEditFocusLost()
|
|
self:ClearHighlightText();
|
|
|
|
local text = strtrim(self:GetText() or "");
|
|
|
|
if text == "" and self.defaultText then
|
|
text = self.defaultText;
|
|
end
|
|
|
|
self:SetText(text);
|
|
self:SetHighlighed(false);
|
|
|
|
EditWindow:UpdateEditWindow();
|
|
end
|
|
|
|
function EditBoxMixin:OnEscapePressed()
|
|
self:ClearFocus();
|
|
end
|
|
|
|
function EditBoxMixin:OnEnterPressed()
|
|
self:ClearFocus();
|
|
end
|
|
|
|
function EditBoxMixin:IsTextValid()
|
|
return strtrim(self:GetText() or "") ~= ""
|
|
end
|
|
|
|
function CreateNameEditButton(parent)
|
|
local f = CreateFrame("Button", nil, parent);
|
|
f:SetSize(LOADOUT_BUTTON_WIDTH, OPTION_BUTTON_HEIGHT);
|
|
|
|
f.Category = f:CreateFontString(nil, "OVERLAY", "NarciGemmaFontLarge");
|
|
f.Category:SetJustifyH("LEFT");
|
|
f.Category:SetPoint("LEFT", f, "LEFT", PADDING_H, 0);
|
|
f.Category:SetTextColor(0.67, 0.67, 0.67);
|
|
f.Category:SetText(NAME);
|
|
|
|
local eb = CreateFrame("EditBox", nil, f);
|
|
f.EditBox = eb;
|
|
eb:SetFontObject("NarciGemmaFontMedium");
|
|
eb:SetTextColor(0.88, 0.88, 0.88);
|
|
eb:SetMaxLetters(16);
|
|
eb:SetTextInsets(8, 8, 0, 0);
|
|
eb:SetJustifyH("CENTER");
|
|
eb:SetAutoFocus(false);
|
|
eb:SetSize(24*7, 24);
|
|
eb:SetPoint("RIGHT", f, "RIGHT", -PADDING_H -2, 0);
|
|
eb.Border = eb:CreateTexture(nil, "BACKGROUND");
|
|
eb.Border:SetAllPoints(true);
|
|
AtlasUtil:SetAtlas(eb.Border, "remix-loadout-editbox-bg");
|
|
--eb.Background:GetTextureSliceMode(0);
|
|
--TextureSlice is kinda broken since 10.2.7
|
|
|
|
Mixin(f, NameButtonMixin);
|
|
f:SetScript("OnEnter", f.OnEnter);
|
|
f:SetScript("OnLeave", f.OnLeave);
|
|
f:SetScript("OnClick", f.OnClick);
|
|
|
|
Mixin(eb, EditBoxMixin);
|
|
eb:SetScript("OnEnter", eb.OnEnter);
|
|
eb:SetScript("OnLeave", eb.OnLeave);
|
|
eb:SetScript("OnEditFocusGained", eb.OnEditFocusGained);
|
|
eb:SetScript("OnEditFocusLost", eb.OnEditFocusLost);
|
|
eb:SetScript("OnEscapePressed", eb.OnEscapePressed);
|
|
eb:SetScript("OnEnterPressed", eb.OnEnterPressed);
|
|
|
|
return f
|
|
end
|
|
end
|
|
|
|
|
|
|
|
|
|
local SaveButtonMixin = {};
|
|
do --Save Button
|
|
function SaveButtonMixin:Init()
|
|
self.Init = nil;
|
|
|
|
SetupThreeSliceButton(self);
|
|
|
|
self.ButtonText:SetText(SAVE);
|
|
self.ButtonText:SetTextColor(1, 0.82, 0);
|
|
|
|
self:ClearAllPoints();
|
|
self:SetPoint("LEFT", self:GetParent(), "BOTTOMLEFT", FOOTER_BUTTON_OFFSET, 34);
|
|
self:SetHighlighed(false);
|
|
|
|
self:SetScript("OnEnter", self.OnEnter);
|
|
self:SetScript("OnLeave", self.OnLeave);
|
|
self:SetScript("OnMouseDown", self.OnMouseDown);
|
|
self:SetScript("OnMouseUp", self.OnMouseUp);
|
|
self:SetScript("OnEnable", self.OnEnable);
|
|
self:SetScript("OnDisable", self.OnDisable);
|
|
self:SetScript("OnClick", self.OnClick);
|
|
|
|
self:SetMotionScriptsWhileDisabled(true);
|
|
end
|
|
|
|
function SaveButtonMixin:OnEnable()
|
|
if self:IsMouseOver() then
|
|
self:SetHighlighed(true);
|
|
else
|
|
self:SetHighlighed(false);
|
|
end
|
|
end
|
|
|
|
function SaveButtonMixin:OnDisable()
|
|
self.ButtonText:SetTextColor(0.5, 0.5, 0.5);
|
|
end
|
|
|
|
function SaveButtonMixin:SetHighlighed(state)
|
|
if state then
|
|
AtlasUtil:SetAtlas(self.Left, "remix-loadout-equip-hl-left");
|
|
AtlasUtil:SetAtlas(self.Right, "remix-loadout-equip-hl-right");
|
|
AtlasUtil:SetAtlas(self.Center, "remix-loadout-equip-hl-center");
|
|
self.ButtonText:SetTextColor(1, 1, 1);
|
|
else
|
|
AtlasUtil:SetAtlas(self.Left, "remix-loadout-equip-left");
|
|
AtlasUtil:SetAtlas(self.Right, "remix-loadout-equip-right");
|
|
AtlasUtil:SetAtlas(self.Center, "remix-loadout-equip-center");
|
|
if self:IsEnabled() then
|
|
self.ButtonText:SetTextColor(1, 0.82, 0);
|
|
else
|
|
self.ButtonText:SetTextColor(0.5, 0.5, 0.5);
|
|
end
|
|
end
|
|
end
|
|
|
|
function SaveButtonMixin:OnEnter()
|
|
if self:IsEnabled() then
|
|
self:SetHighlighed(true);
|
|
end
|
|
|
|
SimpleTooltip:ShowTooltip(self, self.failureReason);
|
|
end
|
|
|
|
function SaveButtonMixin:OnLeave()
|
|
if self:IsEnabled() then
|
|
self:SetHighlighed(false);
|
|
end
|
|
|
|
SimpleTooltip:FadeOut();
|
|
end
|
|
|
|
function SaveButtonMixin:OnMouseDown(button)
|
|
if self:IsEnabled() and button == "LeftButton" then
|
|
self.ButtonText:SetPoint("CENTER", self, "CENTER", 0, -1);
|
|
end
|
|
end
|
|
|
|
function SaveButtonMixin:OnMouseUp()
|
|
self.ButtonText:SetPoint("CENTER", self, "CENTER", 0, 0);
|
|
end
|
|
|
|
function SaveButtonMixin:OnClick()
|
|
if self:IsEnabled() then
|
|
EditWindow:SaveChanges();
|
|
end
|
|
end
|
|
end
|
|
|
|
local DeleteButtonMixin = {};
|
|
do
|
|
function DeleteButtonMixin:Init()
|
|
self.Init = nil;
|
|
|
|
EditButtonMixin.Init(self);
|
|
self.deleteDuration = 1.0;
|
|
AtlasUtil:SetAtlas(self.Icon, "remix-loadout-edit-delete");
|
|
self:Enable();
|
|
|
|
local bar = self:CreateTexture(nil, "OVERLAY");
|
|
self.CounterBar = bar;
|
|
self.barWidth = FOOTER_BUTTON_HEIGHT;
|
|
bar:Hide();
|
|
bar:SetColorTexture(1.000, 0.125, 0.125);
|
|
bar:SetWidth(1);
|
|
bar:SetHeight(4);
|
|
bar:SetPoint("BOTTOMLEFT", self, "TOPLEFT", 0, 4);
|
|
|
|
self:OnLeave();
|
|
end
|
|
|
|
function DeleteButtonMixin:OnEnter()
|
|
EditButtonMixin.OnEnter(self);
|
|
end
|
|
|
|
function DeleteButtonMixin:OnLeave()
|
|
EditButtonMixin.OnLeave(self);
|
|
end
|
|
|
|
function DeleteButtonMixin:OnMouseDown(button)
|
|
EditButtonMixin.OnMouseDown(self);
|
|
if self:IsEnabled() and button == "LeftButton" then
|
|
if self.useLongClick then
|
|
self:ShowCounter();
|
|
end
|
|
end
|
|
end
|
|
|
|
function DeleteButtonMixin:OnMouseUp()
|
|
EditButtonMixin.OnMouseUp(self);
|
|
self:HideCounter();
|
|
end
|
|
|
|
function DeleteButtonMixin:OnClick()
|
|
if self.useLongClick then
|
|
|
|
else
|
|
self:DeleteSelectedLoadout();
|
|
end
|
|
end
|
|
|
|
function DeleteButtonMixin:SetLongClickMode(useLongClick)
|
|
self.useLongClick = useLongClick;
|
|
if useLongClick then
|
|
self.tooltipText = L["Delete Loadout Long Click"];
|
|
else
|
|
self.tooltipText = L["Delete Loadout One Click"];
|
|
end
|
|
end
|
|
|
|
function DeleteButtonMixin:OnUpdate(elapsed)
|
|
self.t = self.t + elapsed;
|
|
local a;
|
|
if self.t >= self.deleteDuration then
|
|
a = 1;
|
|
self:HideCounter();
|
|
self:DeleteSelectedLoadout();
|
|
else
|
|
a = self.t / self.deleteDuration;
|
|
end
|
|
self.CounterBar:SetWidth(a * self.barWidth);
|
|
end
|
|
|
|
function DeleteButtonMixin:DeleteSelectedLoadout()
|
|
local loadoutIndex = EditWindow and EditWindow.loadoutIndex;
|
|
if loadoutIndex then
|
|
LoadoutUtil:DeleteLoadout(loadoutIndex);
|
|
elseif EditWindow and EditWindow:IsCreatingNewLoadout() then
|
|
EditWindow:DeleteDraft();
|
|
end
|
|
LoadoutFrame:HideEditWindow();
|
|
end
|
|
|
|
function DeleteButtonMixin:ShowCounter()
|
|
self.CounterBar:Show();
|
|
self.CounterBar:SetWidth(0.1);
|
|
self.t = 0;
|
|
self:SetScript("OnUpdate", self.OnUpdate);
|
|
end
|
|
|
|
function DeleteButtonMixin:HideCounter()
|
|
self.CounterBar:Hide();
|
|
self.t = nil;
|
|
self:SetScript("OnUpdate", nil);
|
|
EditButtonMixin.OnMouseUp(self);
|
|
end
|
|
|
|
function DeleteButtonMixin:OnHide()
|
|
self:HideCounter();
|
|
end
|
|
end
|
|
|
|
|
|
|
|
|
|
local WINDOW_GAP = 8;
|
|
|
|
do --Edit Loadout
|
|
local EditWindowMixin = {};
|
|
|
|
function EditWindowMixin:SetLoadout(loadoutIndex)
|
|
self.newLoadout = false;
|
|
self.loadoutIndex = loadoutIndex;
|
|
self.loadoutName = LoadoutUtil:GetLoadoutName(loadoutIndex);
|
|
self.NameButton.EditBox.defaultText = self.loadoutName;
|
|
self.gemInfo = LoadoutUtil:CopyLoadoutGemInfo(loadoutIndex);
|
|
self.DeleteButton:SetLongClickMode(true);
|
|
self.NameButton:SetLoadoutName(self.loadoutName);
|
|
self:UpdateEditWindow();
|
|
self:SetTitle(self.loadoutName or L["Edit Loadout"]);
|
|
self:ShowNewLoadoutPrompt(false);
|
|
|
|
if Planner then
|
|
Planner:Hide();
|
|
end
|
|
end
|
|
|
|
function EditWindowMixin:SetNewLoadout()
|
|
self.newLoadout = true;
|
|
self.loadoutIndex = nil;
|
|
self.NameButton.EditBox.defaultText = nil;
|
|
self.loadoutName = nil;
|
|
|
|
if self.unsavedGemInfo then
|
|
--Previously unsave loadout
|
|
self.gemInfo = self.unsavedGemInfo;
|
|
self:ShowNewLoadoutPrompt(false);
|
|
else
|
|
--self.gemInfo = {};
|
|
--self.unsavedGemInfo = self.gemInfo;
|
|
self:ShowNewLoadoutPrompt(true);
|
|
return
|
|
end
|
|
|
|
self.DeleteButton:SetLongClickMode(false);
|
|
self.NameButton:SetLoadoutName(self.loadoutName);
|
|
self:UpdateEditWindow();
|
|
|
|
self:SetTitle(L["New Loadout"]);
|
|
end
|
|
|
|
function EditWindowMixin:SetBlankLoadout()
|
|
self.unsavedGemInfo = {};
|
|
self:SetNewLoadout();
|
|
end
|
|
|
|
function EditWindowMixin:SetNewLoadoutFromEquipped()
|
|
local gemInfo = LoadoutUtil:GetCurrentGemInfo();
|
|
if gemInfo then
|
|
self.unsavedGemInfo = CopyTable(gemInfo);
|
|
self:SetNewLoadout();
|
|
else
|
|
self:SetBlankLoadout();
|
|
end
|
|
end
|
|
|
|
function EditWindowMixin:IsCreatingNewLoadout()
|
|
return self:IsShown() and self.newLoadout
|
|
end
|
|
|
|
function EditWindowMixin:DeleteDraft()
|
|
self.unsavedGemInfo = nil;
|
|
end
|
|
|
|
function EditWindowMixin:UpdateEditWindow()
|
|
local selected = true;
|
|
local allSelected = true;
|
|
|
|
for i, button in ipairs(self.optionButtons) do
|
|
selected = button:SetChoice(self.gemInfo[EDIT_OPTIONS[i]]);
|
|
allSelected = allSelected and selected;
|
|
end
|
|
|
|
|
|
local NameButton = self.NameButton;
|
|
local SaveButton = self.SaveButton;
|
|
|
|
local name = NameButton:GetText();
|
|
self.loadoutName = name;
|
|
|
|
local canSave;
|
|
local showError;
|
|
|
|
if allSelected then
|
|
if name then
|
|
if self:IsCreatingNewLoadout() then
|
|
if LoadoutUtil:DoesLoadoutNameExist(name) then
|
|
canSave = false;
|
|
SaveButton.failureReason = L["Loadout Save Failure Dupe Name Format"];
|
|
showError = true;
|
|
else
|
|
local isDupe, dupeName = LoadoutUtil:DoesGemLoadoutExist(self.gemInfo);
|
|
if isDupe then
|
|
canSave = false;
|
|
SaveButton.failureReason = L["Loadout Save Failure Dupe Loadout Format"]:format(dupeName);
|
|
showError = true;
|
|
else
|
|
canSave = true;
|
|
end
|
|
end
|
|
else
|
|
if LoadoutUtil:DoesLoadoutNameExist(name, self.loadoutIndex) then
|
|
canSave = false;
|
|
SaveButton.failureReason = L["Loadout Save Failure Dupe Name Format"];
|
|
showError = true;
|
|
else
|
|
canSave = true;
|
|
end
|
|
end
|
|
else
|
|
canSave = false;
|
|
SaveButton.failureReason = L["Loadout Save Failure No Name"];
|
|
end
|
|
else
|
|
canSave = false;
|
|
SaveButton.failureReason = L["Loadout Save Failure Incomplete Choices"];
|
|
end
|
|
|
|
if canSave then
|
|
SaveButton.failureReason = nil;
|
|
SaveButton:Enable();
|
|
else
|
|
SaveButton:Disable();
|
|
end
|
|
|
|
if showError then
|
|
SaveButton:OnEnter();
|
|
else
|
|
SimpleTooltip:FadeOut();
|
|
end
|
|
end
|
|
|
|
function EditWindowMixin:ShowNewLoadoutPrompt(state)
|
|
if state then
|
|
self:SetTitle(L["New Loadout"]);
|
|
if not self.PromptFrame then
|
|
local f = CreateFrame("Frame", nil, self);
|
|
self.PromptFrame = f;
|
|
f:SetPoint("TOPLEFT", self, "TOPLEFT", 0, -40);
|
|
f:SetPoint("BOTTOMRIGHT", self, "BOTTOMRIGHT", 0, 0);
|
|
|
|
local function PromptButton_OnEnter(s)
|
|
s.Name:SetTextColor(1, 1, 1);
|
|
self:HighlightButton(s);
|
|
end
|
|
|
|
local function PromptButton_OnLeave(s)
|
|
s.Name:SetTextColor(0.67, 0.67, 0.67);
|
|
self:HighlightButton(nil);
|
|
end
|
|
|
|
local promptButtonHeight = 44;
|
|
|
|
local function CreatePromptButton()
|
|
local bt = CreateFrame("Button", nil, f);
|
|
bt:SetSize(LOADOUT_BUTTON_WIDTH, promptButtonHeight);
|
|
|
|
bt:SetScript("OnEnter", f.OnEnter);
|
|
bt:SetScript("OnLeave", f.OnLeave);
|
|
bt:SetScript("OnClick", f.OnClick);
|
|
|
|
bt.Name = f:CreateFontString(nil, "OVERLAY", "NarciGemmaFontLarge");
|
|
bt.Name:SetJustifyH("LEFT");
|
|
bt.Name:SetPoint("LEFT", bt, "LEFT", 32, 0);
|
|
bt.Name:SetTextColor(0.67, 0.67, 0.67);
|
|
|
|
bt:SetScript("OnEnter", PromptButton_OnEnter);
|
|
bt:SetScript("OnLeave", PromptButton_OnLeave);
|
|
|
|
return bt
|
|
end
|
|
|
|
local numButtons = 2;
|
|
--local fromY = -0.5*(self.PromptFrame:GetHeight() - numButtons * promptButtonHeight);
|
|
local fromY = -0.5*promptButtonHeight;
|
|
local bt1 = CreatePromptButton();
|
|
bt1:SetPoint("TOP", f, "TOP", 0, fromY - 0*promptButtonHeight);
|
|
bt1.Name:SetText(L["New Loadout Blank"]);
|
|
bt1:SetScript("OnClick", function()
|
|
self:SetBlankLoadout();
|
|
end);
|
|
|
|
local bt2 = CreatePromptButton();
|
|
bt2:SetPoint("TOP", f, "TOP", 0, fromY - 1*promptButtonHeight);
|
|
bt2.Name:SetText(L["New Loadout From Equipped"]);
|
|
bt2:SetScript("OnClick", function()
|
|
self:SetNewLoadoutFromEquipped();
|
|
end);
|
|
end
|
|
self.PromptFrame:Show();
|
|
else
|
|
if self.PromptFrame then
|
|
self.PromptFrame:Hide();
|
|
end
|
|
end
|
|
|
|
state = not state;
|
|
self.SaveButton:SetShown(state);
|
|
self.DeleteButton:SetShown(state);
|
|
self.OptionFrame:SetShown(state);
|
|
self:ShowFooterDivider(state);
|
|
end
|
|
|
|
function EditWindowMixin:InitEditWindow()
|
|
self.optionButtons = {};
|
|
|
|
local button;
|
|
local fromY = -46;
|
|
|
|
self.OptionFrame = CreateFrame("Frame", nil, self);
|
|
self.OptionFrame:SetAllPoints(true);
|
|
|
|
for i, categoryKey in ipairs(EDIT_OPTIONS) do
|
|
button = CreateEditOption(self.OptionFrame);
|
|
self.optionButtons[i] = button;
|
|
button:SetCategory(categoryKey);
|
|
button:SetPoint("TOP", self, "TOP", 0, fromY + (1 - i) * OPTION_BUTTON_HEIGHT);
|
|
button:UpdateChoiceText();
|
|
end
|
|
|
|
|
|
local DeleteButton = CreateFrame("Button", nil, self);
|
|
self.DeleteButton = DeleteButton;
|
|
Mixin(DeleteButton, DeleteButtonMixin);
|
|
DeleteButton:Init();
|
|
|
|
local SaveButton = CreateFrame("Button", nil, self);
|
|
self.SaveButton = SaveButton;
|
|
Mixin(SaveButton, SaveButtonMixin);
|
|
SaveButton:Init();
|
|
|
|
AtlasUtil:SetAtlas(self.ButtonHighlight.Texture, "remix-listbutton-highlight");
|
|
self.ButtonHighlight.Texture:SetBlendMode("ADD");
|
|
|
|
local CancelButton = CreateReturnButton(self);
|
|
self.CancelButton = CancelButton;
|
|
CancelButton:SetScript("OnClick", function()
|
|
self:Hide();
|
|
end);
|
|
|
|
|
|
local NameButton = CreateNameEditButton(self.OptionFrame);
|
|
self.NameButton = NameButton;
|
|
NameButton:SetPoint("TOP", self, "TOP", 0, fromY + (1 - #EDIT_OPTIONS - 2) * OPTION_BUTTON_HEIGHT);
|
|
end
|
|
|
|
function EditWindowMixin:HighlightButton(optionButton)
|
|
self.ButtonHighlight:Hide();
|
|
self.ButtonHighlight:ClearAllPoints();
|
|
if optionButton then
|
|
self.ButtonHighlight:SetPoint("TOPLEFT", optionButton, "TOPLEFT", 0, 0);
|
|
self.ButtonHighlight:SetPoint("BOTTOMRIGHT", optionButton, "BOTTOMRIGHT", 0, 0);
|
|
self.ButtonHighlight:Show();
|
|
end
|
|
end
|
|
|
|
function EditWindowMixin:ShowPlanner(categoryKey)
|
|
if not Planner then
|
|
Planner = Gemma.CreateLoadoutPlanner(self);
|
|
Planner:SetFrameStrata("HIGH");
|
|
Planner:SetClampedToScreen(true);
|
|
Planner:ClearAllPoints();
|
|
Planner:SetPoint("TOPLEFT", self, "TOPRIGHT", WINDOW_GAP, 0);
|
|
|
|
local CancelButton = CreateReturnButton(Planner);
|
|
Planner.CancelButton = CancelButton;
|
|
CancelButton:SetScript("OnClick", function()
|
|
Planner:Hide();
|
|
end);
|
|
|
|
|
|
local bt = Planner.AcceptButton;
|
|
SetupThreeSliceButton(bt);
|
|
bt:ClearAllPoints();
|
|
bt:SetPoint("CENTER", Planner, "BOTTOM", 0, 34);
|
|
bt.ButtonText:SetText(ACCEPT);
|
|
bt.ButtonText:SetTextColor(1, 0.82, 0);
|
|
|
|
bt.SetHighlighed = SaveButtonMixin.SetHighlighed;
|
|
bt.OnEnter = SaveButtonMixin.OnEnter;
|
|
bt.OnLeave = SaveButtonMixin.OnLeave;
|
|
bt.OnMouseDown = SaveButtonMixin.OnMouseDown;
|
|
bt.OnMouseUp = SaveButtonMixin.OnMouseUp;
|
|
|
|
bt:SetScript("OnEnter", bt.OnEnter);
|
|
bt:SetScript("OnLeave", bt.OnLeave);
|
|
bt:SetScript("OnMouseDown", bt.OnMouseDown);
|
|
bt:SetScript("OnMouseUp", bt.OnMouseUp);
|
|
end
|
|
|
|
Planner:ShowTab(categoryKey, self.gemInfo[categoryKey]);
|
|
Planner:Show();
|
|
end
|
|
|
|
function EditWindowMixin:SetPendingChoice(categoryKey, newChoice)
|
|
self.gemInfo[categoryKey] = newChoice;
|
|
self:UpdateEditWindow();
|
|
end
|
|
|
|
function EditWindowMixin:SaveChanges()
|
|
if self:IsCreatingNewLoadout() then
|
|
LoadoutUtil:CreateNewLoadout(self.loadoutName, self.gemInfo);
|
|
self:DeleteDraft();
|
|
else
|
|
LoadoutUtil:OverwriteLoadout(self.loadoutIndex, self.loadoutName, self.gemInfo);
|
|
end
|
|
self:Hide();
|
|
end
|
|
|
|
|
|
function LoadoutFrameMixin:OpenEditWindow(loadoutIndex)
|
|
if not EditWindow then
|
|
EditWindow = Gemma.CreateWindow(self);
|
|
EditWindow:SetPoint("TOPLEFT", self, "TOPRIGHT", WINDOW_GAP, 0);
|
|
Mixin(EditWindow, EditWindowMixin);
|
|
--EditWindow:SetTitle(L["Edit Loadout"]);
|
|
EditWindow:InitEditWindow();
|
|
end
|
|
|
|
if loadoutIndex then
|
|
--Edit Existing Loadout
|
|
EditWindow:SetLoadout(loadoutIndex);
|
|
else
|
|
--Create New Loadout
|
|
if not EditWindow:IsCreatingNewLoadout() then
|
|
EditWindow:SetNewLoadout();
|
|
end
|
|
end
|
|
|
|
EditWindow:Show();
|
|
end
|
|
|
|
function LoadoutFrameMixin:HideEditWindow()
|
|
if EditWindow then
|
|
EditWindow:Hide();
|
|
end
|
|
end
|
|
|
|
function LoadoutFrameMixin:IsEditWindowShown()
|
|
return EditWindow and EditWindow:IsShown()
|
|
end
|
|
end
|
|
|
|
|
|
|
|
|
|
do --Auto Equip Gems
|
|
local UIParent = UIParent;
|
|
|
|
local IsSocketOccupied = Gemma.IsSocketOccupied;
|
|
local ClearCursor = ClearCursor;
|
|
local ClickSocketButton = ClickSocketButton;
|
|
local AcceptSockets = AcceptSockets;
|
|
local CloseSocketInfo = CloseSocketInfo;
|
|
local PickupContainerItem = C_Container.PickupContainerItem;
|
|
local SocketInventoryItem = SocketInventoryItem;
|
|
|
|
|
|
function Automation:SuppressGameEvent(state)
|
|
if state then
|
|
UIParent:UnregisterEvent("SOCKET_INFO_UPDATE");
|
|
if ItemSocketingFrame then
|
|
ItemSocketingFrame:UnregisterEvent("SOCKET_INFO_UPDATE");
|
|
ItemSocketingFrame:UnregisterEvent("SOCKET_INFO_ACCEPT");
|
|
end
|
|
else
|
|
UIParent:RegisterEvent("SOCKET_INFO_UPDATE");
|
|
if ItemSocketingFrame then
|
|
ItemSocketingFrame:RegisterEvent("SOCKET_INFO_UPDATE");
|
|
ItemSocketingFrame:RegisterEvent("SOCKET_INFO_ACCEPT");
|
|
end
|
|
end
|
|
end
|
|
|
|
function Automation:PlaceGemInSlot(gemItemID, slotID, socketIndex)
|
|
ClearCursor();
|
|
if not (gemItemID and slotID) then return; end
|
|
|
|
local bagID, slotIndex = GetItemBagPosition(gemItemID);
|
|
if not(bagID and slotIndex) then return; end
|
|
|
|
PickupContainerItem(bagID, slotIndex);
|
|
SocketInventoryItem(slotID);
|
|
|
|
if IsSocketOccupied(socketIndex) then
|
|
--Something went wrong. Socket isn't empty
|
|
return
|
|
else
|
|
ClickSocketButton(socketIndex);
|
|
ClearCursor();
|
|
AcceptSockets();
|
|
end
|
|
|
|
return true
|
|
end
|
|
|
|
function Automation:EquipNext()
|
|
MainFrame:ShowActionBlocker();
|
|
self.isProcessing = true;
|
|
self.actionIndex = self.actionIndex + 1;
|
|
local action = self.actionList[self.actionIndex];
|
|
local success;
|
|
|
|
if action then
|
|
local slotID = action[1];
|
|
local socketIndex = action[2];
|
|
local gemItemID = action[3];
|
|
|
|
local itemLink = GetInventoryItemLink("player", slotID);
|
|
|
|
if itemLink then
|
|
local numSockets = GetItemNumSockets(itemLink);
|
|
if numSockets > 0 and numSockets >= socketIndex then
|
|
CloseSocketInfo();
|
|
if self:PlaceGemInSlot(gemItemID, slotID, socketIndex) then
|
|
success = true;
|
|
end
|
|
end
|
|
end
|
|
|
|
self:SetScript("OnUpdate", self.OnUpdate);
|
|
|
|
EquipButton:Update();
|
|
else
|
|
self:OnFinishsed();
|
|
end
|
|
|
|
if not self.actionList[self.actionIndex + 1] then
|
|
--So the EquipButton can update on the next PLAYER_EQUIPMENT_CHANGED
|
|
self.isProcessing = false;
|
|
end
|
|
|
|
return success
|
|
end
|
|
|
|
function Automation:OnUpdate(elapsed)
|
|
self.t = self.t + elapsed;
|
|
if self.t > 0 then
|
|
self.socketInfoSuccess = false;
|
|
self:SetScript("OnUpdate", nil);
|
|
self:EquipNext();
|
|
end
|
|
end
|
|
|
|
function Automation:StartEquipping()
|
|
self.t = 0;
|
|
self.actionIndex = 0;
|
|
self.socketInfoSuccess = false;
|
|
self.isProcessing = true;
|
|
self:RegisterEvent("SOCKET_INFO_SUCCESS");
|
|
self:RegisterEvent("SOCKET_INFO_ACCEPT");
|
|
self:RegisterEvent("SOCKET_INFO_UPDATE");
|
|
self:SuppressGameEvent(true);
|
|
self:SetScript("OnEvent", self.OnEvent);
|
|
self:SetScript("OnUpdate", self.OnUpdate);
|
|
|
|
MainFrame:ShowActionBlocker();
|
|
end
|
|
|
|
function Automation:Stop()
|
|
self.t = 0;
|
|
self.isProcessing = false;
|
|
self:SetScript("OnUpdate", nil);
|
|
self:UnregisterEvent("SOCKET_INFO_SUCCESS");
|
|
self:UnregisterEvent("SOCKET_INFO_ACCEPT");
|
|
self:UnregisterEvent("SOCKET_INFO_UPDATE");
|
|
self:SuppressGameEvent(false);
|
|
CloseSocketInfo();
|
|
end
|
|
|
|
function Automation:OnFinishsed()
|
|
self:Stop();
|
|
end
|
|
|
|
function Automation:OnEvent(event, ...)
|
|
--Event Sequence:
|
|
---- ACCEPT - SUCCESS - 3 UPDATE at the same time
|
|
--print(GetTime(), event)
|
|
if event == "SOCKET_INFO_SUCCESS" or event == "SOCKET_INFO_ACCEPT" then
|
|
self.socketInfoSuccess = true;
|
|
self.t = -1;
|
|
elseif event == "SOCKET_INFO_UPDATE" then
|
|
if self.socketInfoSuccess then
|
|
self.t = -0.0;
|
|
end
|
|
end
|
|
end
|
|
|
|
function Automation:EquipGems(actionList)
|
|
--actionList: actions.insert
|
|
--{ {slotID, socketIndex, gemItemID}, ... }
|
|
self.actionList = actionList;
|
|
self.numActions = #actionList;
|
|
self:StartEquipping();
|
|
end
|
|
|
|
function Automation:IsProcessing()
|
|
return self.isProcessing == true
|
|
end
|
|
|
|
function Automation:GetProcessText()
|
|
return L["Format Equipping Progress"]:format(self.actionIndex, self.numActions)
|
|
end
|
|
|
|
function Automation:DebugMode()
|
|
self:RegisterEvent("SOCKET_INFO_SUCCESS");
|
|
self:RegisterEvent("SOCKET_INFO_ACCEPT");
|
|
self:RegisterEvent("SOCKET_INFO_UPDATE");
|
|
self:RegisterEvent("SOCKET_INFO_CLOSE");
|
|
self:SetScript("OnEvent", Automation.OnEvent);
|
|
end
|
|
end
|