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.

1400 lines
46 KiB

local _, addon = ...
Narci_EquipmentSetManager = {};
local ESM = Narci_EquipmentSetManager;
local L = Narci.L;
local EquipmentSetDB; --NarcissusDB_PC.EquipmentSetDB
local EquipmentSetManagerFrame;
local MAX_TALENT_TIERS = 7;
local MAX_SETS = MAX_EQUIPMENT_SETS_PER_PLAYER or 10;
local Format_Digit = "%.2f";
local PREFIX_RED = "|cffff5050";
local PREFIX_YELLOW = "|cFFFFD100";
local PREFIX_GREY = "|cffa6a6a6"; --65% White
local ICON_NEW_SET = "Interface\\AddOns\\Narcissus\\Art\\Widgets\\EquipmentSetManager\\NewSet";
local ICON_SELECTED = " |TInterface\\AddOns\\Narcissus\\Art\\Widgets\\Arrows\\Tick:12:12:0:0:64:64:0:64:0:64|t";
local SETBUTTON_HEIGHT = 48;
local FadeFrame = NarciFadeUI.Fade;
local GetItemStats = NarciAPI_GetItemStats;
local SmartFontType = NarciAPI.SmartFontType;
local GetAllSelectedTalentIDsAndIcons = NarciAPI.GetAllSelectedTalentIDsAndIcons;
local DoesItemExist = C_Item.DoesItemExist;
local C_EquipmentSet = C_EquipmentSet;
local GetNumEquipmentSets = C_EquipmentSet.GetNumEquipmentSets; --Returns the number of saved equipment sets.
local GetItemLocations = C_EquipmentSet.GetItemLocations;
local GetEquipmentSetInfo = C_EquipmentSet.GetEquipmentSetInfo;
local UnpackLocation = addon.TransitionAPI.EquipmentManager_UnpackLocation;
local GetItemInventoryType = C_Item.GetItemInventoryType;
local After = C_Timer.After;
local sin = math.sin;
local cos = math.cos;
local max = math.max;
local pi = math.pi;
local NarciThemeUtil = NarciThemeUtil;
-----------------------------------------------------------------------------------
--[[ LibEasing
--
-- Original Lua implementations
-- from 'EmmanuelOga'
-- https://github.com/EmmanuelOga/easing/
--
-- Adapted from
-- Tweener's easing functions (Penner's Easing Equations)
-- and http://code.google.com/p/tweener/ (jstweener javascript version)
--]]
local function outSine(t, b, c, d)
return c * sin(t / d * (pi / 2)) + b
end
local function inOutSine(t, b, c, d)
return -c / 2 * (cos(pi * t / d) - 1) + b
end
-----------------------------------------------------------------------------------
local DataProvider = {};
function DataProvider:SetNumMissingSets(numMissing)
if numMissing < 0 then
if not self.numMissing then
self.numMissing = 0;
else
self.numMissing = max(0, self.numMissing - 1);
end
else
self.numMissing = numMissing;
end
return self.numMissing;
end
function DataProvider:GetNumMissingSets()
if not self.numMissing then
self.numMissing = 0;
return 0
else
return self.numMissing;
end
end
local function GetMaxAndKey(table)
--Get the biggest and 2nd biggest number in a table and their keys --/dump GetMaxAndKey({1,128,129,126})
if not table and type(table) ~="table" and #table < 2 then return; end;
local max1,key1, max2, key2;
for k, v in pairs(table) do
if not max1 then
max1 = v;
key1 = k;
elseif not max2 then
max2 = v;
key2 = k;
end
if table[k] > max1 then
max2 = max1;
key2 = key1;
max1 = v;
key1 = k;
elseif max2 and table[k] > max2 then
max2 = v;
key2 = k
end
end
return max1,key1, max2, key2;
end
local RepositionFrame = CreateFrame("Frame", nil, nil, "NarciUpdateFrameTemplate");
RepositionFrame.isOpen = false;
RepositionFrame:Hide();
RepositionFrame.duration = 0.35;
local function RepositionFrame_OnShow(self)
if not self.anchorTo then
self.anchorTo = Narci_RadarChartFrame;
end
self.point, self.relativeTo, self.relativePoint, _, self.StartPoint = self.anchorTo:GetPoint();
end
local function UpdateEquipmentSetButtonPosition(parentOffset)
--offset: -24 ~ -96 → 48 ~ 0 y = 2/3 * x + 64
local buttons = ESM.buttons;
local button = buttons[1];
local offset = parentOffset * 2 / 3 + 64;
button:SetPoint("TOP", button:GetParent(), "TOP", 0, offset);
for i = 2, #buttons do
buttons[i]:SetPoint("TOP", buttons[i -1], "BOTTOM", 0, offset);
end
end
local function RepositionFrame_OnUpdate(self, elapsed)
self.t = self.t + elapsed
local EndPoint = self.EndPoint;
local StartPoint = self.StartPoint;
local offset = outSine(self.t, StartPoint, EndPoint - StartPoint, self.duration);
if self.t >= self.duration then
offset = EndPoint;
self:Hide();
end
self.anchorTo:SetPoint(self.point, self.relativeTo, self.relativePoint, 0, offset);
UpdateEquipmentSetButtonPosition(offset);
end
RepositionFrame:SetScript("OnShow", RepositionFrame_OnShow);
RepositionFrame:SetScript("OnUpdate", RepositionFrame_OnUpdate);
----------------------------------------------------------------------------------
---- For GamePad ----
function ESM:GetSetButtons()
return self.buttons
end
function ESM:GetNumSetButtons()
return self.numSetButton or 1
end
function ESM:OnNumSetsChanged(numSets)
end
----------------------------------------------------------------------------------
local function LoadSetInfo(SetButton, SetName)
local equipmentSetID = SetButton.setID;
if not EquipmentSetDB or not equipmentSetID then return end
local texs = SetButton.texs;
local icon;
local SetInfo = EquipmentSetDB[equipmentSetID];
local tier = 1;
if SetInfo and SetName == SetInfo[1] then
local SavedTalents = SetInfo[2];
for i = #SavedTalents, 1, -1 do
icon = SavedTalents[tier][2];
texs[i]:SetTexture(icon);
texs[i]:Show();
tier = tier + 1;
end
end
end
local function SaveSetInfo(setID)
--Save current set name and talents in the database
if not EquipmentSetDB then return; end;
local talentInfo = GetAllSelectedTalentIDsAndIcons();
local name = GetEquipmentSetInfo(setID);
if name then
EquipmentSetDB[setID] = {name, talentInfo};
end
end
local function WipeSetInfo(setID)
if not EquipmentSetDB then return; end;
EquipmentSetDB[setID] = nil;
end
local function AsignTalentIcons(button) --/run Narci_EquipmentSetManager:AsignTalentIcons(TV)
local texs = button.texs;
local talentInfo = GetAllSelectedTalentIDsAndIcons();
local tiers = #talentInfo;
if not tiers or tiers < 1 then return; end;
local index = 1;
for i = tiers, 1, -1 do
texs[index]:SetTexture(talentInfo[i].texture);
texs[index]:Show();
index = index + 1;
end
end
local function FadeInTalentIcons(button, action)
local texFrame = button.texFrame;
if action then
FadeFrame(texFrame, 0.15, 1);
else
FadeFrame(texFrame, 0.2, 0.4);
end
end
local function SetBackgroundColor(self)
local r, g, b = NarciThemeUtil:GetColor();
self.Bar2:SetColorTexture(r, g, b, 0.75);
self.Color:SetColorTexture(r, g, b, 0.75);
end
local function AnimateBackgroundColor(self)
local r, g, b = NarciThemeUtil:GetColor();
self.Color:SetColorTexture(r, g, b, 0.75);
self.BarColors = {r, g, b};
self.Bar2.animBling:Play();
end
local function ResetBackgroundColor(button)
button.Bar1:SetColorTexture(0.1, 0.1, 0.1, 0.75);
button.Bar2:SetColorTexture(0.25, 0.25, 0.25, 0.75);
button.Color:SetColorTexture(0.2, 0.2, 0.2, 0.75);
end
local function ResetAllBackgroundColor()
local buttons = ESM.buttons;
for i = 1, #buttons do
ResetBackgroundColor(buttons[i]);
end
end
local KeyStats = {"prim", "stamina", "crit", "haste", "mastery", "versatility", "ilvl", "gems"};
local StatsName = {["crit"] = NARCI_CRITICAL_STRIKE, ["haste"] = STAT_HASTE, ["mastery"] = STAT_MASTERY, ["versatility"] = STAT_VERSATILITY,};
local function InventoryTypeToInventorySlotID(Types)
if not Types then return {}; end;
local SlotIDs = {};
local slotID;
for _, v in pairs(Types) do
slotID = nil;
if v > 0 and v < 11 then --this is the range where SlotID matches the TypeID
slotID = v;
elseif v == 11 then --finger
if not SlotIDs[11] then
slotID = 11;
else
slotID = 12;
end
elseif v == 12 then --trinket
if not SlotIDs[13] then
slotID = 13;
else
slotID = 14;
end
elseif v == 13 or v == 21 then --One-Hand/Main hand
slotID = 16;
elseif v == 14 or v == 22 then --Off-hand
slotID = 17;
elseif v == 15 or v == 17 then --Ranged/Two-Hand
slotID = 16;
elseif v == 16 then --Back
slotID = 15;
elseif v == 19 then --Tabard
slotID = 19;
elseif v == 20 then --Shirt
slotID = 5;
end
if slotID then
SlotIDs[slotID] = true;
end
end
return SlotIDs;
end
local function GetTotalStats(equipmentSetID)
--print("|cFFFFD100"..equipmentSetID.."|r");
local locations = GetItemLocations(equipmentSetID)
if not locations then return; end;
local player, bank, bags, voidStorage, slot, bag;
local itemlocation;
local TotalStats, Stats = {}, {};
local toltalItems = 0; --Ignore 4/19 Tabard & shirt
local inventoryType;
local IncludedTypes = {};
local KeyStats = KeyStats;
local function add(key)
TotalStats[key] = TotalStats[key] + Stats[key];
end
for i = 1, #KeyStats do
TotalStats[KeyStats[i]] = 0;
end
for k, v in pairs(locations) do
itemlocation = nil;
if k ~= 4 and k ~= 19 and v then --ignore shirt and tabard during calculation
toltalItems = toltalItems + 1;
player, bank, bags, voidStorage, slot, bag = UnpackLocation(v);
if bag and slot then
--print("bag: "..bag.." slot: "..slot);
itemlocation = ItemLocation:CreateFromBagAndSlot(bag, slot);
if itemlocation then
inventoryType = GetItemInventoryType(itemlocation);
tinsert(IncludedTypes, inventoryType);
end
elseif slot then
--print("slot: "..slot);
itemlocation = ItemLocation:CreateFromEquipmentSlot(slot);
end
if itemlocation then
Stats = GetItemStats(itemlocation);
local itemName = C_Item.GetItemName(itemlocation);
--print(string.format("|cff959595%s:|r %s, %s, %s, %s, %s", itemName, Stats.crit, Stats.haste, Stats.mastery, Stats.versatility, Stats.stamina));
for j = 1, #KeyStats do
add(KeyStats[j])
end
end
end
end
TotalStats.AverageIlvl = math.floor(100*TotalStats.ilvl/toltalItems + 0.5)/100;
TotalStats.IncludedSlots = InventoryTypeToInventorySlotID(IncludedTypes);
return TotalStats;
end
local RelevantSlots = {1, 2, 3, 5, 6, 7, 8, 9 ,10 ,11, 12, 13, 14 ,15, 16, 17}; --Irrelevant: 4 Shirt, 19 Tabard
local HealthFactor = 1;
local function GetHPFactor()
--Calculate stamina → Health conversion rate
local ConversionRate;
local stamina, _, posBuff, negBuff = UnitStat("player", LE_UNIT_STAT_STAMINA);
local BasicStamima = stamina - posBuff - negBuff;
local Health = UnitHealth("player");
if stamina == 0 then
ConversionRate = 20;
else
ConversionRate = Health / stamina;
end
--Calculate stamina gain from current equipment
local Stats;
local StaminaFromItems = 0;
local itemlocation;
for i = 1, #RelevantSlots do
itemlocation = ItemLocation:CreateFromEquipmentSlot(RelevantSlots[i]);
if DoesItemExist(itemlocation) then
Stats = GetItemStats(itemlocation);
StaminaFromItems = StaminaFromItems + Stats.stamina;
end
end
HealthFactor = ConversionRate * stamina / (StaminaFromItems + BasicStamima) ;
end
--[[ --Deprecated Method
local function GetHealthFactor()
--Calculate stamina → Health conversion rate
local ConversionRate;
local stamina = UnitStat("player", LE_UNIT_STAT_STAMINA);
local Health = UnitHealth("player");
if stamina == 0 then
ConversionRate = 20;
else
ConversionRate = Health / stamina;
end
--Calculate Stamina bonus from spec (tank) and Heart
--Gained from Azerite Heart Essence
local EXP = 0; --exponential
local slotId = 2; --Neck
local itemLocation = ItemLocation:CreateFromEquipmentSlot(slotId);
if DoesItemExist(itemLocation) and C_AzeriteItem.IsAzeriteItem(itemLocation) then
local GetMilestoneInfo = C_AzeriteEssence.GetMilestoneInfo;
local info;
for i = 110, 113 do
info = GetMilestoneInfo(i);
if info and info.unlocked then
EXP = EXP + 1;
else
break;
end
end
end
local HeartBonus = 1.03^EXP; --3%
--Gained from Armor Skill
local spec = GetSpecialization() or 1;
local role = GetSpecializationRole(spec);
local ArmorBonus = 1;
if role == "TANK" and UnitLevel("player") >= 50 then --Prim stat bonus starts at level 50 **9.0 Level Squash
ArmorBonus = 1.05;
end
--Gained from other spell
local SpellBonus = 1;
if IsSpellKnown(203513) then --Demonic Wards DH
SpellBonus = 1.65;
elseif IsSpellKnown(48263) then --Veteran of the Third War DK
if role == "TANK" then
SpellBonus = 1.65;
else
SpellBonus = 1.10;
end
end
print(string.format("%s | %s, %s, %s", ConversionRate, HeartBonus, ArmorBonus, SpellBonus));
HealthFactor = ConversionRate * HeartBonus * ArmorBonus * SpellBonus;
end
--]]
local function GetEquipmentSetStatsAndHealth(equipmentSetID)
local Stats, tempTable = {}, {};
tempTable = GetTotalStats(equipmentSetID);
if not tempTable then return; end;
Stats.crit, Stats.haste, Stats.mastery, Stats.versatility = tempTable.crit, tempTable.haste, tempTable.mastery, tempTable.versatility;
local max1, key1, max2, key2 = GetMaxAndKey(Stats)
local stat, effectiveStat, posBuff, negBuff = UnitStat("player", LE_UNIT_STAT_STAMINA);
local base = stat - posBuff - negBuff;
local health = (base + tempTable.stamina) * HealthFactor; --HP20/Stamina
--print("HP: "..health)
health = math.floor(health / 1000);
return max1,key1, max2, key2, health, tempTable;
end
local isDuplicatedSet = false;
local function InitializeOptionalSetButton(frame, equipmentSetID, avgItemLevel) --/run Narci_EquipmentSetManager:InitializeOptionalSetButton(TV)
equipmentSetID = equipmentSetID or 1;
--AsignTalentIcons(frame);
local name, iconFileID, setID, isEquipped, numItems, _, _, numLost, _ = GetEquipmentSetInfo(equipmentSetID);
frame.numLost = numLost;
local max1,key1, max2, key2, health, statsTable = GetEquipmentSetStatsAndHealth(equipmentSetID);
local statString = StatsName[key1];
if key2 then
--statString = statString.." |cff959595".."||".."|r "..StatsName[key2];
statString = statString.." || "..StatsName[key2];
end
frame.SetIcon.Icon:SetTexture(iconFileID);
frame.iconID = iconFileID;
frame.SetName:SetText(name);
frame.SetName:Disable();
After(0, function()
SmartFontType(frame.SetName);
end)
frame.Health:SetText(health.." K")
frame.statsTable = statsTable;
frame.setID = equipmentSetID;
local ilvl = math.floor(statsTable.AverageIlvl);
if avgItemLevel and ilvl < avgItemLevel - 15 then
frame.IlvlBackground:SetColorTexture(1, 0.3137, 0.3137, 1); --Mark it red
statString = PREFIX_RED .. L["Low Item Level"];
else
frame.IlvlBackground:SetColorTexture(1, 1, 1, 0.6);
statString = PREFIX_GREY .. statString;
end
if numItems < 11 then--Naked set
ilvl = "N/A";
frame.IlvlBackground:SetColorTexture(1, 1, 1, 0.25);
end
frame.Ilvl:SetText(ilvl);
local isItemMissing = numLost > 0;
frame.hasMissingItem = isItemMissing;
if isItemMissing then
if numLost == 1 then
statString = L["1 Missing Item"];
else
statString = string.format(L["n Missing Items"], numLost);
end
if numLost < 3 then
statString = PREFIX_YELLOW .. statString;
else
statString = PREFIX_RED .. statString;
end
end
if isDuplicatedSet and isEquipped then
statString = PREFIX_YELLOW .. L["Duplicated Set"];
end
isDuplicatedSet = (isDuplicatedSet or isEquipped);
frame.Enhancement:SetText(statString);
if isEquipped then
SetBackgroundColor(frame);
end
LoadSetInfo(frame, name);
frame.Bar1:SetHeight(SETBUTTON_HEIGHT / 2);
frame.TalentAnchor:SetPoint("RIGHT", frame.Bar1, "TOPRIGHT", 0, -12);
frame:Show();
--print("setID: "..setID)
return isItemMissing
end
local UpdateEquipmentNum;
local ShouldUpdateEquipment = false;
local ShouldUpdateTalent = false;
local function ShowOrHideSettings(self, visibility, useAnimation)
if useAnimation then
if visibility then
FadeFrame(self.ConfirmButton, 0.15, 1);
FadeFrame(self.CancelButton, 0.15, 1);
FadeFrame(self.DeleteButton, 0.15, 1);
else
FadeFrame(self.DeleteButton, 0.15, 0);
FadeFrame(self.ConfirmButton, 0.15, 0);
FadeFrame(self.CancelButton, 0.15, 0);
end
else
self.DeleteButton:SetShown(visibility);
self.ConfirmButton:SetShown(visibility);
self.CancelButton:SetShown(visibility);
end
end
local function UpdateScrollRange()
local frame = EquipmentSetManagerFrame;
local savedSets = GetNumEquipmentSets() or 0;
local TotalButton = math.min(savedSets + 1, MAX_SETS);
local buttonHeight = SETBUTTON_HEIGHT;
local TotalTab = max(TotalButton - 4, 1);
local MaxScroll = math.floor((TotalTab - 0.5) * buttonHeight + 0.5);
frame.ListScrollFrame.range = MaxScroll;
frame.ListScrollFrame.scrollBar:SetMinMaxValues(0, MaxScroll)
--[[
Switch.Level:SetText(savedSets.."/"..MAX_SETS);
Switch.Header:SetText("SETS");
--]]
end
local function InitializeEquipmentSetManager()
--Refresh stamina factor
GetHPFactor();
isDuplicatedSet = false;
------------------------
local button;
local buttons = ESM.buttons;
local SetIDs = C_EquipmentSet.GetEquipmentSetIDs() or {};
local setID;
local totalSets = GetNumEquipmentSets();
local avgItemLevel = GetAverageItemLevel();
local isItemMissing;
local numMissing = 0;
local numVisible = 0;
for i = 1, MAX_SETS do
setID = SetIDs[i];
button = buttons[i];
if setID then
isItemMissing = InitializeOptionalSetButton(button, setID, avgItemLevel);
ShowOrHideSettings(button, false, true);
if isItemMissing then
if numMissing then
numMissing = numMissing + 1;
else
numMissing = 1;
end
end
numVisible = numVisible + 1;
else
button.setID = nil;
button.SetIcon.Icon:SetTexture(ICON_NEW_SET);
button.SetName:SetText(PAPERDOLL_NEWEQUIPMENTSET);
button.SetName:Enable();
button.SetName:ClearFocus();
if i == totalSets + 1 then
button:Show(); --to create new set: if there are 3 saved sets, this button will be #4
numVisible = numVisible + 1;
else
button:Hide();
end
button:Reset();
end
button:SetID(i);
button:SetHeight(SETBUTTON_HEIGHT);
end
DataProvider:SetNumMissingSets(numMissing);
Narci_NavBar:UpdateSets(totalSets, numMissing);
ESM.numSetButton = numVisible;
ESM:OnNumSetsChanged(numVisible);
end
local function SetEnhancements(c, h, m, v)
--crit, haste, mastery, versatility
--In order to preview the stats on each set, the radar chart and four enchancement stats stop auto updating once equipment set manager is activated
--So enchancement stats need to be set manually
local Radar = Narci_RadarChartFrame;
local ConvertRatio = Narci.ConvertRatio;
local table = {{c, "crit"}, {h, "haste"}, {m, "mastery"}, {v, "versatility"}};
local stat = 0;
local percent = 0;
local statBase = 0;
local ratio = 0;
local ratingText;
local percentageText;
local key;
for i = 1, #table do
key = table[i][2];
stat = table[i][1]; --rating
statBase = ConvertRatio[key.."Base"];
ratio = ConvertRatio[key] or ratio;
percent = max(0, stat * ratio + statBase); --Convert a rating to percentage / + basic percent (example: 10% basic critical chance)
percentageText = string.format(Format_Digit, percent).."%";
key = string.gsub(key, "^%l", string.upper)
Radar[key].ValueRating:SetText(stat);
Radar[key].Value:SetText(percentageText);
end
end
local function HighlightRelevantSlots(table)
local slotTable = Narci.slotTable;
for i = 1, #slotTable do
slotTable[i].Highlight:Hide();
slotTable[i].Highlight:SetAlpha(0);
end
if not table then return; end;
for k, v in pairs(table) do
if v then
slotTable[k].Highlight:Show();
slotTable[k].Highlight:SetAlpha(1);
end
end
end
----------------------------------------------------------
--Event listener
local function InitializeScripts()
local Overlay1 = EquipmentSetManagerFrame.ListScrollFrame.OverlayFrame1;
local SaveItemButton = Overlay1.SaveItem;
local SaveTalentButton = Overlay1.SaveTalent;
local function ChangeColorAndText(self)
if self.IsOn then
self.Background:SetColorTexture(self.r, self.g, self.b);
self:SetText(self.EnabledText.. ICON_SELECTED);
else
self.Background:SetColorTexture(0.2, 0.2, 0.2);
self:SetText(PREFIX_GREY .. self.EnabledText);
end
end
local function SaveButton_OnShow(self)
ChangeColorAndText(self);
end
local function SaveItemButton_OnClick(self)
self.IsOn = not self.IsOn;
ShouldUpdateEquipment = self.IsOn;
ChangeColorAndText(self);
end
local function SaveTalentButton_OnClick(self)
self.IsOn = not self.IsOn;
ShouldUpdateTalent = self.IsOn;
ChangeColorAndText(self);
end
SaveItemButton:SetScript("OnShow", SaveButton_OnShow);
SaveTalentButton:SetScript("OnShow", SaveButton_OnShow);
SaveItemButton:SetScript("OnClick", SaveItemButton_OnClick);
SaveTalentButton:SetScript("OnClick", SaveTalentButton_OnClick);
SaveItemButton.EnabledText = L["Update Items"];
SaveItemButton.DisabledText = L["Don't Update Items"];
SaveTalentButton.EnabledText = L["Update Talents"];
SaveTalentButton.DisabledText = L["Don't Update Talents"];
end
----------------------------------------------------------
NarciEquipmentSetButtonMixin = {};
local function NarciEquipmentSetButton_AnimFrame_OnShow(self)
local _;
local SetButton = self:GetParent();
self.StartHeight = SetButton.Bar1:GetHeight();
_, _ , _, self.StartX = SetButton.TalentAnchor:GetPoint();
--self.fontName = SetButton.SetName:GetFont() --fontName, fontHeight, fontFlags
end
local function NarciEquipmentSetButton_AnimFrame_OnUpdate(self, elapsed)
local t = self.t;
local delay = self.Delay;
local height = inOutSine(t, self.StartHeight, self.EndHeight - self.StartHeight , 0.25);
local offset;
if t >= 0.25 then
height = self.EndHeight;
else
offset = self.StartX;
end
if t <= 0.25 + delay and t >= delay then
offset = outSine(t - delay, self.StartX, self.EndX - self.StartX , 0.25);
elseif t > 0.25 + delay then
offset = self.EndX;
self:Hide();
end
local parent = self:GetParent();
parent.Bar1:SetHeight(height);
parent.TalentAnchor:SetPoint("RIGHT", parent.Bar1, "TOPRIGHT", offset, -12);
--parent.SetName:SetHeight(10 + height/12)
--parent.SetName:SetFont(self.fontName, 10 + height/12);
self.t = self.t + elapsed;
end
local function ShowFlyoutBlack(state)
if state then
Narci_FlyoutBlack:In();
else
Narci_FlyoutBlack:Out();
end
end
local function HideIconSelector()
local ListScrollFrame = Narci_EquipmentSetManagerFrame.ListScrollFrame;
local Overlay1 = ListScrollFrame.OverlayFrame1;
local Overlay2 = ListScrollFrame.OverlayFrame2;
ESM.IconSelector:Hide();
Overlay1:Hide();
Overlay2:Hide();
ShowFlyoutBlack(false);
end
local function ShowIconSelector(SetButton)
local Selector = ESM.IconSelector;
Selector:Load();
local specName, specIcon, roleIcon, dungeonIcon, pvpIcon, spellIcons, roleName, spellNames = ESM:GetCurrentSpecializationNameAndIcons();
--Icon Selector
Selector:ClearAllPoints();
Selector:SetFrameLevel(40);
Selector.SetButton = SetButton;
local icons = Selector.icons;
local buttons = Selector.buttons;
icons[1]:SetTexture(roleIcon);
buttons[1].iconID = roleIcon;
buttons[1].name = roleName;
icons[2]:SetTexture(dungeonIcon);
buttons[2].iconID = dungeonIcon;
buttons[2].name = CHALLENGES;
icons[3]:SetTexture(pvpIcon);
buttons[3].iconID = pvpIcon;
buttons[3].name = PVP;
local icon, button;
--print(#spellIcons)
for i = 4, #icons do
icon = spellIcons[i - 3];
button = buttons[i];
icons[i]:SetTexture(icon);
if icon then
button.iconID = icon;
button.name = spellNames[i - 3];
button:Enable();
else
button.iconID = nil;
button.name = "";
button:Disable();
end
end
Selector:SetPoint("BOTTOMRIGHT", Narci_EquipmentSetManagerFrame, "BOTTOMLEFT", -4, 0);
FadeFrame(Selector, 0.15, 1);
ShowFlyoutBlack(true);
return specName, specIcon;
end
local function CollapseButton(self)
local AnimFrame = self.AnimFrame;
AnimFrame:Hide();
AnimFrame.EndHeight = SETBUTTON_HEIGHT / 2;
AnimFrame.EndX = 0;
AnimFrame.Delay = 0.225;
self.SetName:Disable();
ShowOrHideSettings(self, false, true);
AnimFrame:Show();
end
local function ExpandButton(self)
local AnimFrame = self.AnimFrame;
AnimFrame:Hide();
FadeFrame(self.Highlight, 0.15, 0);
AnimFrame.EndHeight = SETBUTTON_HEIGHT;
AnimFrame.EndX = 140;
AnimFrame.Delay = 0;
self.SetName:Enable();
self.SetName:SetFocus();
ShowOrHideSettings(self, true, true);
local ListScrollFrame = Narci_EquipmentSetManagerFrame.ListScrollFrame;
local Overlay1 = ListScrollFrame.OverlayFrame1;
local Overlay2 = ListScrollFrame.OverlayFrame2;
FadeFrame(ESM.IconSelector, 0.15, 1);
FadeFrame(Overlay1, 0.15, 1);
FadeFrame(Overlay2, 0.15, 1);
AnimFrame:Show();
end
local function SetToEditMode(self, bool, showExtraButton)
--self = SetButton
--Collapsing animation
if bool then
ExpandButton(self);
else
CollapseButton(self);
return;
end
local ListScrollFrame = Narci_EquipmentSetManagerFrame.ListScrollFrame;
local Overlay1 = ListScrollFrame.OverlayFrame1;
local Overlay2 = ListScrollFrame.OverlayFrame2;
Overlay1:ClearAllPoints();
Overlay2:ClearAllPoints();
Overlay1:SetPoint("TOPLEFT", ListScrollFrame, "TOPLEFT", 4, 0);
Overlay1:SetPoint("BOTTOMRIGHT", self, "TOPRIGHT", 0, 0);
Overlay2:SetPoint("BOTTOMRIGHT", ListScrollFrame, "BOTTOMRIGHT", -4, 0);
Overlay2:SetPoint("TOPLEFT", self, "BOTTOMLEFT", 0, 0);
local SaveItem = Overlay1.SaveItem;
local SaveTalent = Overlay1.SaveTalent;
Overlay1.TargetButton = self;
if showExtraButton then
SaveItem:ClearAllPoints();
SaveTalent:ClearAllPoints();
if self:GetID() == 1 then
SaveItem:SetPoint("TOPLEFT", Overlay2, "TOPLEFT", 0, 0);
SaveItem:SetPoint("TOPRIGHT", Overlay2, "TOP", 0, 0);
else
SaveItem:SetPoint("BOTTOMLEFT", Overlay1, "BOTTOMLEFT", 0, 0);
SaveItem:SetPoint("BOTTOMRIGHT", Overlay1, "BOTTOM", 0, 0);
end
SaveTalent:SetPoint("LEFT", SaveItem, "RIGHT", 0, 0);
SaveTalent:SetPoint("RIGHT", Overlay1, "RIGHT", 0, 0);
if self.numLost ~= 0 and ShouldUpdateEquipment and UpdateEquipmentNum == self:GetID() then
SaveItem.IsOn = true;
end
SaveItem:Show();
SaveTalent:Show();
else
SaveItem:Hide();
SaveTalent:Hide();
end
Narci_EquipmentSetManagerFrame.ArtFrame.Shadow:SetAlpha(0);
end
local function PlayBling(self)
local Bling = EquipmentSetManagerFrame.BlingFrame;
Bling:StopAnimating();
Bling:ClearAllPoints();
Bling:SetPoint("TOPLEFT", self, "TOPLEFT", 0, 0);
Bling:SetPoint("BOTTOMRIGHT", self, "BOTTOMRIGHT", 0, 0);
Bling.Bling.animIn:Play();
PlaySound(117310);
end
local function PlayHighlight(self)
local Highlight = EquipmentSetManagerFrame.HighlightFrame;
Highlight:StopAnimating();
Highlight:ClearAllPoints();
Highlight:SetPoint("TOPLEFT", self, "TOPLEFT", 0, 0);
Highlight:SetPoint("BOTTOMRIGHT", self, "BOTTOMRIGHT", 0, 0);
local r, g, b = NarciThemeUtil:GetColor();
local w = 0.4;
r, g, b = r + w, g + w, b + w;
Highlight.Color:SetColorTexture(r, g, b);
Highlight.Color.animIn:Play();
end
local function ShowNewSetButton()
local savedSets = GetNumEquipmentSets();
local buttons = ESM.buttons;
if savedSets < MAX_SETS then
FadeFrame(buttons[savedSets + 1], 0.35, 1, 0);
end
UpdateScrollRange();
end
local IgnoreSlotsForSave = C_EquipmentSet.IgnoreSlotForSave;
local GetEquipmentSetIDByName = C_EquipmentSet.GetEquipmentSetID;
local function IsNameDuplicated(name, setID)
--Check if the new name has been taken
local oldID = GetEquipmentSetIDByName(name);
if oldID and oldID ~= setID then
return true;
else
return false;
end
end
local function CreateNewSet(self)
--self = SetButton
local specName, specIcon = ShowIconSelector(self);
self.SetName:Enable();
self.SetName:SetFocus();
self.SetName:SetText(specName);
self.SetName:HighlightText();
FadeFrame(self.Highlight, 0.15, 0);
SetToEditMode(self, true, false);
self:SetIconWithTransition(specIcon, specName);
end
local function ConfirmButton_OnClick(self)
local SetButton = self:GetParent();
local setID = SetButton.setID;
local name = SetButton.SetName:GetText() or SetButton.TextBackup or "";
local icon = SetButton.iconID;
Narci_AlertFrame_Autohide:SetAnchor(SetButton);
if IsNameDuplicated(name, setID) then
--A set with that name already exists
Narci_AlertFrame_Autohide:AddMessage(EQUIPMENT_SETS_CANT_RENAME, true);
return;
end
SetToEditMode(SetButton, false);
HideIconSelector();
if setID then
if type(icon) == "number" then
C_EquipmentSet.ModifyEquipmentSet(setID, name, icon);
else
C_EquipmentSet.ModifyEquipmentSet(setID, name);
end
local name, _, _, isEquipped = GetEquipmentSetInfo(setID);
if ShouldUpdateTalent then
SaveSetInfo(setID);
--print("Talent Saved")
end
if ShouldUpdateEquipment then
C_EquipmentSet.SaveEquipmentSet(setID);
end
LoadSetInfo(SetButton, name);
After(0.25, function()
isDuplicatedSet = false;
InitializeOptionalSetButton(SetButton, setID);
if not SetButton.hasMissingItem then
DataProvider:SetNumMissingSets(-1);
end
local numMissing = DataProvider:GetNumMissingSets();
local savedSets = GetNumEquipmentSets();
Narci_NavBar:UpdateSets(savedSets, numMissing);
end)
else
if type(icon) == "number" then
C_EquipmentSet.CreateEquipmentSet(name, icon);
else
C_EquipmentSet.CreateEquipmentSet(name);
end
After(0.25, function()
IgnoreSlotsForSave(4);
IgnoreSlotsForSave(19);
local NewSetID = GetEquipmentSetIDByName(name);
if NewSetID then
C_EquipmentSet.SaveEquipmentSet(NewSetID);
SaveSetInfo(NewSetID);
InitializeOptionalSetButton(SetButton, NewSetID);
ShowNewSetButton();
end
end)
end
SetButton.SetName:ClearFocus();
PlayBling(SetButton);
DataProvider:SetNumMissingSets(-1)
if SetButton.hasMissingItem then
DataProvider.numMissing = DataProvider.numMissing + 1;
end
local savedSets = GetNumEquipmentSets();
local numMissing = DataProvider:GetNumMissingSets();
Narci_NavBar:UpdateSets(savedSets, numMissing);
local numVisible = savedSets + 1;
if numVisible > MAX_SETS then
numVisible = MAX_SETS;
end
ESM.numSetButton = numVisible;
ESM:OnNumSetsChanged(numVisible);
end
local function CancelButton_OnClick(self)
local SetButton = self:GetParent();
local setID = SetButton.setID;
if setID and type(setID) == "number" then
local name, iconFileID = GetEquipmentSetInfo(setID);
SetButton.SetName:SetText(name);
SetButton:SetIconWithTransition(iconFileID);
SetToEditMode(SetButton, false);
else
SetButton.SetName:Disable();
SetButton.SetName:SetText(PAPERDOLL_NEWEQUIPMENTSET);
SetButton:SetIconWithTransition(ICON_NEW_SET);
ShowOrHideSettings(SetButton, false, true);
end
HideIconSelector();
end
local function DeleteTimer_OnPlay(self)
local SetButton = self:GetParent():GetParent():GetParent();
local setID = SetButton.setID;
if not (setID and type(setID) == "number") then
SetButton.CancelButton:Click();
end
ShouldUpdateEquipment = false;
end
local function DeleteTimer_OnFinished(self)
local SetButton = self:GetParent():GetParent():GetParent();
Narci_EquipmentSetManagerFrame.ArtFrame.Shadow:SetAlpha(0);
local setID = SetButton.setID;
if setID and type(setID) == "number" then
self:GetParent().FadeOut:Play();
SetButton.EraseFrame:Show();
C_EquipmentSet.DeleteEquipmentSet(setID);
WipeSetInfo(setID);
local savedSets = GetNumEquipmentSets() or 0;
HideIconSelector();
if not SetButton.hasMissingItem then
DataProvider:SetNumMissingSets(-1);
end
local numMissing = DataProvider:GetNumMissingSets();
Narci_NavBar:UpdateSets(savedSets, numMissing);
end
end
local function SetIcon_OnPostClick(self)
self:GetParent():Click("RightButton");
end
local function RepositionButton(deletedButton)
--After deleting a set, replace it with the button beneath it then move it to the bottom of the list
local buttons = ESM.buttons;
local ID = deletedButton:GetID();
--Re-index
for i = ID, (MAX_SETS - 1) do
buttons[i] = buttons[i + 1];
end
buttons[MAX_SETS] = deletedButton;
--Reposition
local button = buttons[ID];
deletedButton:ClearAllPoints();
button:ClearAllPoints();
deletedButton:SetPoint("TOP", buttons[MAX_SETS - 1], "BOTTOM", 0, 0);
if ID == 1 then
button:SetPoint("TOP", deletedButton:GetParent(), "TOP", 0, 0);
else
button:SetPoint("TOP", buttons[ID - 1], "BOTTOM", 0, 0);
end
end
local function NarciEquipmentSetButton_EraseFrame_OnUpdate(self, elapsed)
local t = self.t;
local height = inOutSine(t, SETBUTTON_HEIGHT, 0.001 - SETBUTTON_HEIGHT , 0.4);
if t >= 0.4 then
height = 0.001;
self:Hide();
local SetButton = self:GetParent();
ShowOrHideSettings(SetButton, false, true);
RepositionButton(SetButton);
After(0.2, InitializeEquipmentSetManager);
end
self:GetParent():SetHeight(height);
self.t = self.t + elapsed;
end
function NarciEquipmentSetButtonMixin:OnLoad()
self.texs = {};
self.statsTable = {};
self.statsTable.crit = 0;
self.statsTable.haste = 0;
self.statsTable.mastery = 0;
self.statsTable.versatility = 0;
self.setID = nil;
--Create Talent Icon
local texFrame = CreateFrame("Frame", nil, self);
self.SetName:SetFrameLevel(texFrame:GetFrameLevel() + 1)
texFrame:SetAlpha(0.4);
local offset = 4;
local tex;
for i = 1, MAX_TALENT_TIERS do
tex = texFrame:CreateTexture(nil,"OVERLAY", "NarciEquipmentSetTalentTexture", 1);
if i == 1 then
tex:SetPoint("RIGHT", self.TalentAnchor, "RIGHT", -6 - 8, 0);
else
tex:SetPoint("RIGHT", self.texs[i-1], "LEFT", -offset, 0);
end
tinsert(self.texs, tex);
end
self.texFrame = texFrame;
self.BarColors = {0.25, 0.25, 0.25};
self:RegisterForClicks("LeftButtonUp", "RightButtonUp");
self:RegisterForDrag("LeftButton");
self.AnimFrame:SetScript("OnShow", NarciEquipmentSetButton_AnimFrame_OnShow);
self.AnimFrame:SetScript("OnUpdate", NarciEquipmentSetButton_AnimFrame_OnUpdate);
self.EraseFrame:SetScript("OnUpdate", NarciEquipmentSetButton_EraseFrame_OnUpdate);
self.Color = self.SetName.RightEndColor; --Lays above talent icons
self.ConfirmButton:SetScript("OnClick", ConfirmButton_OnClick);
self.CancelButton:SetScript("OnClick", CancelButton_OnClick);
self.DeleteButton.Fill.Timer:SetScript("OnPlay", DeleteTimer_OnPlay);
self.DeleteButton.Fill.Timer:SetScript("OnFinished", DeleteTimer_OnFinished);
self.SetIcon:SetScript("PostClick", SetIcon_OnPostClick);
self.Highlight:SetAlpha(0);
self:SetScript("OnLoad", nil);
self.OnLoad = nil;
end
function NarciEquipmentSetButtonMixin:SetIconWithTransition(iconFileID, iconName)
self.iconID = iconFileID;
self.iconName = iconName;
local SetIcon = self.SetIcon;
SetIcon:StopAnimating();
SetIcon.IconTemp:SetTexture(iconFileID);
SetIcon.IconTemp:SetAlpha(1);
SetIcon.Icon.Transition:Play();
end
function NarciEquipmentSetButtonMixin:Reset()
local height, offset;
if self.setID and type(self.setID) == "number" then
height = SETBUTTON_HEIGHT / 2;
offset = 0;
else
height = SETBUTTON_HEIGHT;
offset = 140;
ResetBackgroundColor(self);
--Remove talent icons
local texs = self.texs;
for i = 1, MAX_TALENT_TIERS do
texs[i]:SetTexture(nil);
texs[i]:Hide();
end
end
self.numLost = 0;
self.SetName:Disable();
self.Bar1:SetHeight(height);
self.TalentAnchor:SetPoint("RIGHT", self.Bar1, "TOPRIGHT", offset, -12);
ShowOrHideSettings(self, false, false);
end
function NarciEquipmentSetButtonMixin:OnEnter()
if self.SetName:HasFocus() then return; end;
FadeFrame(self.Highlight, 0.5, 0.15);
FadeInTalentIcons(self, true);
--Stats Preview
if self.setID then
local c, h, m, v = self.statsTable.crit, self.statsTable.haste, self.statsTable.mastery, self.statsTable.versatility;
Narci_RadarChartFrame:AnimateValue(c, h, m, v);
SetEnhancements(c, h, m, v);
end
HighlightRelevantSlots(self.statsTable.IncludedSlots);
--All set buttons share the same shadow
local shadow = EquipmentSetManagerFrame.ArtFrame.Shadow;
local _, anchorTo = shadow:GetPoint();
if not anchorTo or anchorTo ~= self then
shadow:ClearAllPoints();
shadow:SetPoint("CENTER", self, "CENTER", 0 , 0);
FadeFrame(shadow, 0.25, 0.6, 0);
end
end
function NarciEquipmentSetButtonMixin:OnLeave()
if self:IsMouseOver() and EquipmentSetManagerFrame:IsMouseOver() then
return
end
FadeFrame(self.Highlight, 1, 0);
FadeInTalentIcons(self, false);
HighlightRelevantSlots();
EquipmentSetManagerFrame.ArtFrame.Shadow:SetAlpha(0);
end
function NarciEquipmentSetButtonMixin:OnClick(button)
if not self.setID or self.setID == nil then
CreateNewSet(self);
return
end
if button == "RightButton" then
SetToEditMode(self, true, true);
self.iconName = L["Old Icon"];
ShowIconSelector(self);
end
end
function NarciEquipmentSetButtonMixin:OnDoubleClick(button)
if not self.setID or InCombatLockdown() or type(self.setID) ~= "number" or button ~="LeftButton" then return; end;
self:RegisterEvent("EQUIPMENT_SWAP_FINISHED");
Narci_AlertFrame_Autohide:SetAnchor(self);
C_EquipmentSet.UseEquipmentSet(self.setID);
ShouldUpdateEquipment = true;
UpdateEquipmentNum = self:GetID();
end
function NarciEquipmentSetButtonMixin:OnDragStart()
if not self.setID or type(self.setID) ~= "number" then return; end;
C_EquipmentSet.PickupEquipmentSet(self.setID);
end
function NarciEquipmentSetButtonMixin:OnEvent(event,...)
if event == "UI_ERROR_MESSAGE" then
local _, msg = ...
Narci_AlertFrame_Autohide:AddMessage(msg);
elseif event == "EQUIPMENT_SWAP_FINISHED" then
local result, setID = ...;
self:UnregisterEvent("EQUIPMENT_SWAP_FINISHED");
if result then
HighlightRelevantSlots();
After(0.2, InitializeEquipmentSetManager);
ResetAllBackgroundColor();
PlayHighlight(self);
AnimateBackgroundColor(self); --Succeed
else
FadeFrame(self.RedOverlay, 0.1, 1);
After(0.5, function()
FadeFrame(self.RedOverlay, 0.45, 0);
end)
self.animError:Play(); --Failed to change set
end
end
end
local function CreateEquipmentSetButtons(self)
local ScrollChild = self.ScrollChild;
local button;
local buttons = {};
for i = 1 , MAX_SETS do
button = CreateFrame("Button", nil, ScrollChild, "NarciEquipmentSetButtonTemplate"); --"NarciEquipmentSetButton"..i
if i == 1 then
button:SetPoint("TOP", ScrollChild, "TOP", 0, 0);
else
button:SetPoint("TOP", buttons[i - 1], "BOTTOM", 0, 0);
end
tinsert(buttons, button);
end
ESM.buttons = buttons;
end
local function UpdateInnerShadowStates(self)
local currValue = self:GetValue();
local minVal, maxVal = self:GetMinMaxValues();
if ( currValue >= maxVal -20) then
self.BottomShadow:Hide();
else
self.BottomShadow:Show();
end
if ( currValue <= minVal +20) then
self.TopShadow:Hide();
else
self.TopShadow:Show();
end
end
function Narci_EquipmentSetManager_ScrollFrame_OnLoad(self)
local buttonHeight = SETBUTTON_HEIGHT;
local TotalTab = MAX_SETS - 4;
local TotalHeight = math.floor(TotalTab * buttonHeight + 0.5);
local MaxScroll = math.floor((TotalTab - 0.5) * buttonHeight + 0.5);
self.scrollBar:SetMinMaxValues(0, MaxScroll)
self.scrollBar:SetValueStep(0.001);
self.buttonHeight = TotalHeight;
self.range = MaxScroll;
self.scrollBar:SetScript("OnValueChanged", function(bar, value)
self:SetVerticalScroll(value);
UpdateInnerShadowStates(bar);
end)
CreateEquipmentSetButtons(self);
NarciAPI_SmoothScroll_Initialization(self, nil, nil, 2/(TotalTab), 0.14, 0.4);
end
NarciViewUtil.showSetsCallBack = function() --NarciViewUtil is declared in NarciAPI.lua
InitializeEquipmentSetManager();
ShouldUpdateEquipment = false;
ESM.isOpen = true;
UpdateScrollRange();
local Overlay1 = EquipmentSetManagerFrame.ListScrollFrame.OverlayFrame1;
local SaveItemButton = Overlay1.SaveItem;
local SaveTalentButton = Overlay1.SaveTalent;
local r, g, b = NarciThemeUtil:GetColor();
SaveItemButton.r, SaveItemButton.g, SaveItemButton.b = r, g, b;
SaveTalentButton.r, SaveTalentButton.g, SaveTalentButton.b = r, g, b;
end
NarciViewUtil.hideSetsCallBack = function()
ESM.isOpen = false;
HideIconSelector();
end
-------------------------------------------------------------------
local EL = CreateFrame("Frame");
EL:RegisterEvent("PLAYER_ENTERING_WORLD");
--[[
EL:RegisterEvent("EQUIPMENT_SETS_CHANGED");
EL:RegisterEvent("EQUIPMENT_SWAP_FINISHED");
EL:RegisterEvent("EQUIPMENT_SWAP_PENDING");
EL:RegisterEvent("TRANSMOG_OUTFITS_CHANGED");
EL:RegisterEvent("WEAR_EQUIPMENT_SET");
--]]
EL:SetScript("OnEvent", function(self, event, ...)
if event == "PLAYER_ENTERING_WORLD" then
self:UnregisterEvent("PLAYER_ENTERING_WORLD");
After(1, function()
NarcissusDB_PC = NarcissusDB_PC or {};
EquipmentSetDB = NarcissusDB_PC.EquipmentSetDB;
EquipmentSetManagerFrame = Narci_EquipmentSetManagerFrame;
InitializeScripts();
end);
end
--[[
print(PREFIX_YELLOW .. event .. "|r");
if event == "EQUIPMENT_SWAP_FINISHED" then
local result, setID = ...;
if not setID then return; end;
print("Set #"..setID.." "..tostring(result));
elseif event == "WEAR_EQUIPMENT_SET" then
local setID = ...;
print("Wearing Set #"..setID);
elseif event == "EQUIPMENT_SETS_CHANGED" then
local a = ...;
print(a);
end
--]]
end)