Initial Forever beta addons
This commit is contained in:
commit
3329ac83ec
|
|
@ -0,0 +1,265 @@
|
|||
local addonName = ...
|
||||
|
||||
local EquippedItemLevel = CreateFrame("Frame")
|
||||
_G[addonName] = EquippedItemLevel
|
||||
|
||||
local slots = {
|
||||
{ id = 1, frame = "CharacterHeadSlot" },
|
||||
{ id = 2, frame = "CharacterNeckSlot" },
|
||||
{ id = 3, frame = "CharacterShoulderSlot" },
|
||||
{ id = 5, frame = "CharacterChestSlot" },
|
||||
{ id = 6, frame = "CharacterWaistSlot" },
|
||||
{ id = 7, frame = "CharacterLegsSlot" },
|
||||
{ id = 8, frame = "CharacterFeetSlot" },
|
||||
{ id = 9, frame = "CharacterWristSlot" },
|
||||
{ id = 10, frame = "CharacterHandsSlot" },
|
||||
{ id = 11, frame = "CharacterFinger0Slot" },
|
||||
{ id = 12, frame = "CharacterFinger1Slot" },
|
||||
{ id = 13, frame = "CharacterTrinket0Slot" },
|
||||
{ id = 14, frame = "CharacterTrinket1Slot" },
|
||||
{ id = 15, frame = "CharacterBackSlot" },
|
||||
{ id = 16, frame = "CharacterMainHandSlot" },
|
||||
{ id = 17, frame = "CharacterSecondaryHandSlot" },
|
||||
}
|
||||
|
||||
local itemLevels = {}
|
||||
local updateQueued = false
|
||||
local characterFrameHooked = false
|
||||
local bagFrameHooks = {}
|
||||
local equippedLowest, equippedHighest
|
||||
|
||||
local function GetCachedItemInfo(itemLink)
|
||||
if C_Item and C_Item.GetItemInfo then
|
||||
return C_Item.GetItemInfo(itemLink)
|
||||
end
|
||||
|
||||
if GetItemInfo then
|
||||
return GetItemInfo(itemLink)
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
local function GetItemLevel(itemLink)
|
||||
if not itemLink then
|
||||
return nil
|
||||
end
|
||||
|
||||
if C_Item and C_Item.GetDetailedItemLevelInfo then
|
||||
local level = C_Item.GetDetailedItemLevelInfo(itemLink)
|
||||
if level and level > 0 then
|
||||
return level
|
||||
end
|
||||
end
|
||||
|
||||
local _, _, _, level = GetCachedItemInfo(itemLink)
|
||||
return level
|
||||
end
|
||||
|
||||
local function GetGradientColor(level, lowest, highest)
|
||||
if lowest == highest then
|
||||
return 0, 1, 0
|
||||
end
|
||||
|
||||
local progress = (level - lowest) / (highest - lowest)
|
||||
progress = math.max(0, math.min(1, progress))
|
||||
if progress <= 0.5 then
|
||||
return 1, progress * 2, 0
|
||||
end
|
||||
|
||||
return 2 - progress * 2, 1, 0
|
||||
end
|
||||
|
||||
local function IsGearItem(itemLink)
|
||||
if not itemLink then
|
||||
return false
|
||||
end
|
||||
|
||||
local _, _, _, _, _, _, _, _, equipLocation = GetCachedItemInfo(itemLink)
|
||||
return equipLocation
|
||||
and equipLocation ~= ""
|
||||
and equipLocation ~= "INVTYPE_NON_EQUIP"
|
||||
and equipLocation ~= "INVTYPE_NON_EQUIP_IGNORE"
|
||||
and equipLocation ~= "INVTYPE_BAG"
|
||||
and equipLocation ~= "INVTYPE_TABARD"
|
||||
end
|
||||
|
||||
local function CreateLabel(slot)
|
||||
local button = _G[slot.frame]
|
||||
if not button or button.EquippedItemLevelLabel then
|
||||
return
|
||||
end
|
||||
|
||||
local label = button:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
|
||||
label:SetPoint("BOTTOMRIGHT", button, "BOTTOMRIGHT", -2, 2)
|
||||
label:SetFont("Fonts\\FRIZQT__.TTF", 12, "OUTLINE")
|
||||
label:SetJustifyH("RIGHT")
|
||||
label:Hide()
|
||||
button.EquippedItemLevelLabel = label
|
||||
end
|
||||
|
||||
function EquippedItemLevel:Update()
|
||||
updateQueued = false
|
||||
|
||||
local lowest, highest
|
||||
for _, slot in ipairs(slots) do
|
||||
CreateLabel(slot)
|
||||
|
||||
local itemLink = GetInventoryItemLink("player", slot.id)
|
||||
local level = GetItemLevel(itemLink)
|
||||
itemLevels[slot.id] = level
|
||||
|
||||
if level then
|
||||
lowest = not lowest and level or math.min(lowest, level)
|
||||
highest = not highest and level or math.max(highest, level)
|
||||
end
|
||||
end
|
||||
|
||||
equippedLowest, equippedHighest = lowest, highest
|
||||
|
||||
for _, slot in ipairs(slots) do
|
||||
local button = _G[slot.frame]
|
||||
local label = button and button.EquippedItemLevelLabel
|
||||
local level = itemLevels[slot.id]
|
||||
|
||||
if label and level and lowest and highest then
|
||||
local red, green, blue = GetGradientColor(level, lowest, highest)
|
||||
label:SetText(string.format("%.0f", level))
|
||||
label:SetTextColor(red, green, blue)
|
||||
label:Show()
|
||||
elseif label then
|
||||
label:Hide()
|
||||
end
|
||||
end
|
||||
|
||||
self:UpdateBagLabels()
|
||||
end
|
||||
|
||||
function EquippedItemLevel:QueueUpdate()
|
||||
if updateQueued then
|
||||
return
|
||||
end
|
||||
|
||||
updateQueued = true
|
||||
C_Timer.After(0.1, function()
|
||||
EquippedItemLevel:Update()
|
||||
end)
|
||||
end
|
||||
|
||||
function EquippedItemLevel:HookCharacterFrame()
|
||||
if characterFrameHooked or not CharacterFrame then
|
||||
return
|
||||
end
|
||||
|
||||
CharacterFrame:HookScript("OnShow", function()
|
||||
EquippedItemLevel:QueueUpdate()
|
||||
end)
|
||||
characterFrameHooked = true
|
||||
end
|
||||
|
||||
function EquippedItemLevel:UpdateBagButton(button)
|
||||
if not button or not button.GetBagID then
|
||||
return
|
||||
end
|
||||
|
||||
local label = button.EquippedItemLevelBagLabel
|
||||
if not label then
|
||||
label = button:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
|
||||
label:SetPoint("BOTTOMRIGHT", button, "BOTTOMRIGHT", -2, 2)
|
||||
label:SetFont("Fonts\\FRIZQT__.TTF", 11, "OUTLINE")
|
||||
label:SetJustifyH("RIGHT")
|
||||
button.EquippedItemLevelBagLabel = label
|
||||
end
|
||||
|
||||
local bag, slot = button:GetBagID(), button:GetID()
|
||||
local itemLink = bag and slot and C_Container.GetContainerItemLink(bag, slot)
|
||||
local level = GetItemLevel(itemLink)
|
||||
if not level or not IsGearItem(itemLink) or not equippedLowest or not equippedHighest then
|
||||
label:Hide()
|
||||
return
|
||||
end
|
||||
|
||||
local red, green, blue = GetGradientColor(level, equippedLowest, equippedHighest)
|
||||
label:SetText(string.format("%.0f", level))
|
||||
label:SetTextColor(red, green, blue)
|
||||
label:Show()
|
||||
end
|
||||
|
||||
function EquippedItemLevel:UpdateContainerFrame(frame)
|
||||
if not frame then
|
||||
return
|
||||
end
|
||||
|
||||
if frame.EnumerateValidItems then
|
||||
for _, button in frame:EnumerateValidItems() do
|
||||
self:UpdateBagButton(button)
|
||||
end
|
||||
elseif frame.Items then
|
||||
for _, button in ipairs(frame.Items) do
|
||||
self:UpdateBagButton(button)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function EquippedItemLevel:HookBagFrame(frame)
|
||||
if not frame or bagFrameHooks[frame] or not frame.Update then
|
||||
return
|
||||
end
|
||||
|
||||
hooksecurefunc(frame, "Update", function(updatedFrame)
|
||||
EquippedItemLevel:UpdateContainerFrame(updatedFrame)
|
||||
end)
|
||||
bagFrameHooks[frame] = true
|
||||
end
|
||||
|
||||
function EquippedItemLevel:UpdateBagLabels()
|
||||
local index = 1
|
||||
local frame = _G["ContainerFrame" .. index]
|
||||
while frame do
|
||||
self:HookBagFrame(frame)
|
||||
self:UpdateContainerFrame(frame)
|
||||
index = index + 1
|
||||
frame = _G["ContainerFrame" .. index]
|
||||
end
|
||||
|
||||
if ContainerFrameCombinedBags then
|
||||
self:HookBagFrame(ContainerFrameCombinedBags)
|
||||
self:UpdateContainerFrame(ContainerFrameCombinedBags)
|
||||
end
|
||||
end
|
||||
|
||||
local function AddTooltipItemLevel(tooltip, data)
|
||||
-- The same data is supplied for normal and comparison tooltips, including
|
||||
-- comparison panes that do not implement GameTooltip:GetItem().
|
||||
local itemReference = data and (data.hyperlink or data.id)
|
||||
if not itemReference or not IsGearItem(itemReference) then
|
||||
return
|
||||
end
|
||||
|
||||
local level = GetItemLevel(itemReference)
|
||||
if not level then
|
||||
return
|
||||
end
|
||||
|
||||
local red, green, blue = GetGradientColor(level, equippedLowest or level, equippedHighest or level)
|
||||
tooltip:AddDoubleLine("Item level", string.format("%.0f", level), 1, 0.82, 0, red, green, blue)
|
||||
tooltip:Show()
|
||||
end
|
||||
|
||||
function EquippedItemLevel:OnEvent(event)
|
||||
if event == "PLAYER_LOGIN" then
|
||||
TooltipDataProcessor.AddTooltipPostCall(Enum.TooltipDataType.Item, AddTooltipItemLevel)
|
||||
|
||||
end
|
||||
|
||||
self:HookCharacterFrame()
|
||||
self:QueueUpdate()
|
||||
end
|
||||
|
||||
EquippedItemLevel:RegisterEvent("PLAYER_LOGIN")
|
||||
EquippedItemLevel:RegisterEvent("PLAYER_EQUIPMENT_CHANGED")
|
||||
EquippedItemLevel:RegisterEvent("GET_ITEM_INFO_RECEIVED")
|
||||
EquippedItemLevel:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
EquippedItemLevel:RegisterEvent("ADDON_LOADED")
|
||||
EquippedItemLevel:RegisterEvent("BAG_UPDATE_DELAYED")
|
||||
EquippedItemLevel:SetScript("OnEvent", EquippedItemLevel.OnEvent)
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
## Interface: 11508
|
||||
## Title: Equipped Item Level
|
||||
## Notes: Shows equipped item levels on the character pane and in equipped-item tooltips.
|
||||
## Author: Local
|
||||
## Version: 1.0
|
||||
|
||||
EquippedItemLevel.lua
|
||||
|
|
@ -0,0 +1,216 @@
|
|||
local addonName = ...
|
||||
|
||||
local SetGuard = CreateFrame("Frame")
|
||||
_G[addonName] = SetGuard
|
||||
|
||||
local savedSetItems = {}
|
||||
local hookedFrames = {}
|
||||
local updateQueued = false
|
||||
local merchantOpen = false
|
||||
|
||||
function SetGuard:RebuildSetSlots()
|
||||
wipe(savedSetItems)
|
||||
if not (C_EquipmentSet and C_EquipmentSet.GetEquipmentSetIDs and C_EquipmentSet.GetItemIDs) then
|
||||
return
|
||||
end
|
||||
|
||||
local setIDs = C_EquipmentSet.GetEquipmentSetIDs()
|
||||
if not setIDs then
|
||||
return
|
||||
end
|
||||
|
||||
for _, setID in ipairs(setIDs) do
|
||||
local itemIDs = C_EquipmentSet.GetItemIDs(setID)
|
||||
if itemIDs then
|
||||
for _, itemID in pairs(itemIDs) do
|
||||
if itemID and itemID > 0 then
|
||||
savedSetItems[itemID] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function GetBagItemID(bag, slot)
|
||||
if C_Container and C_Container.GetContainerItemID then
|
||||
return C_Container.GetContainerItemID(bag, slot)
|
||||
end
|
||||
|
||||
if C_Container and C_Container.GetContainerItemInfo then
|
||||
local info = C_Container.GetContainerItemInfo(bag, slot)
|
||||
return info and info.itemID
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
local function GetItemIcon(button)
|
||||
return button.icon or button.Icon or button.ItemIcon or button.IconTexture or button:GetNormalTexture()
|
||||
end
|
||||
|
||||
local function GetItemTextures(button)
|
||||
local textures, seen = {}, {}
|
||||
local candidates = {
|
||||
button.icon,
|
||||
button.Icon,
|
||||
button.ItemIcon,
|
||||
button.IconTexture,
|
||||
button:GetNormalTexture(),
|
||||
}
|
||||
for _, texture in pairs(candidates) do
|
||||
if texture and texture.GetVertexColor and not seen[texture] then
|
||||
seen[texture] = true
|
||||
table.insert(textures, texture)
|
||||
end
|
||||
end
|
||||
return textures
|
||||
end
|
||||
|
||||
local function SetButtonDesaturated(button, desaturated)
|
||||
if SetItemButtonDesaturated then
|
||||
SetItemButtonDesaturated(button, desaturated)
|
||||
return
|
||||
end
|
||||
|
||||
local icon = GetItemIcon(button)
|
||||
if icon and icon.SetDesaturated then
|
||||
icon:SetDesaturated(desaturated)
|
||||
end
|
||||
end
|
||||
|
||||
local function SetButtonBrightness(button, brightness)
|
||||
if not button.SetGuardVertexColors then
|
||||
button.SetGuardVertexColors = {}
|
||||
for _, texture in ipairs(GetItemTextures(button)) do
|
||||
local red, green, blue, alpha = texture:GetVertexColor()
|
||||
button.SetGuardVertexColors[texture] = { red, green, blue, alpha }
|
||||
end
|
||||
end
|
||||
|
||||
for texture, color in pairs(button.SetGuardVertexColors) do
|
||||
texture:SetVertexColor(color[1] * brightness, color[2] * brightness, color[3] * brightness, color[4])
|
||||
end
|
||||
end
|
||||
|
||||
local function RestoreButtonBrightness(button)
|
||||
if not button.SetGuardVertexColors then
|
||||
return
|
||||
end
|
||||
|
||||
for texture, color in pairs(button.SetGuardVertexColors) do
|
||||
texture:SetVertexColor(color[1], color[2], color[3], color[4])
|
||||
end
|
||||
button.SetGuardVertexColors = nil
|
||||
end
|
||||
|
||||
function SetGuard:UpdateBagButton(button)
|
||||
if not button or not button.GetBagID then
|
||||
return
|
||||
end
|
||||
|
||||
local label = button.SetGuardLabel
|
||||
if not label then
|
||||
label = button:CreateFontString(nil, "OVERLAY", "GameFontNormal")
|
||||
label:SetPoint("TOPLEFT", button, "TOPLEFT", 2, -2)
|
||||
label:SetFont("Fonts\\FRIZQT__.TTF", 14, "OUTLINE")
|
||||
label:SetText("S")
|
||||
label:SetTextColor(0.1, 1, 0.1)
|
||||
button.SetGuardLabel = label
|
||||
end
|
||||
|
||||
local bag, slot = button:GetBagID(), button:GetID()
|
||||
local itemID = bag and slot and GetBagItemID(bag, slot)
|
||||
local isSetItem = itemID and savedSetItems[itemID]
|
||||
label:SetShown(isSetItem == true)
|
||||
|
||||
if isSetItem and merchantOpen then
|
||||
if not button.SetGuardDimmed then
|
||||
local icon = GetItemIcon(button)
|
||||
button.SetGuardWasDesaturated = icon and icon.GetDesaturated and icon:GetDesaturated() or false
|
||||
button.SetGuardDimmed = true
|
||||
end
|
||||
SetButtonDesaturated(button, true)
|
||||
SetButtonBrightness(button, 0.25)
|
||||
elseif button.SetGuardDimmed then
|
||||
SetButtonDesaturated(button, button.SetGuardWasDesaturated == true)
|
||||
RestoreButtonBrightness(button)
|
||||
button.SetGuardDimmed = nil
|
||||
button.SetGuardWasDesaturated = nil
|
||||
end
|
||||
end
|
||||
|
||||
function SetGuard:UpdateContainerFrame(frame)
|
||||
if not frame then
|
||||
return
|
||||
end
|
||||
|
||||
if frame.EnumerateValidItems then
|
||||
for _, button in frame:EnumerateValidItems() do
|
||||
self:UpdateBagButton(button)
|
||||
end
|
||||
elseif frame.Items then
|
||||
for _, button in ipairs(frame.Items) do
|
||||
self:UpdateBagButton(button)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function SetGuard:HookContainerFrame(frame)
|
||||
if not frame or hookedFrames[frame] or not frame.Update then
|
||||
return
|
||||
end
|
||||
|
||||
hooksecurefunc(frame, "Update", function(updatedFrame)
|
||||
SetGuard:UpdateContainerFrame(updatedFrame)
|
||||
end)
|
||||
hookedFrames[frame] = true
|
||||
end
|
||||
|
||||
function SetGuard:UpdateVisibleBags()
|
||||
local index = 1
|
||||
local frame = _G["ContainerFrame" .. index]
|
||||
while frame do
|
||||
self:HookContainerFrame(frame)
|
||||
self:UpdateContainerFrame(frame)
|
||||
index = index + 1
|
||||
frame = _G["ContainerFrame" .. index]
|
||||
end
|
||||
|
||||
if ContainerFrameCombinedBags then
|
||||
self:HookContainerFrame(ContainerFrameCombinedBags)
|
||||
self:UpdateContainerFrame(ContainerFrameCombinedBags)
|
||||
end
|
||||
end
|
||||
|
||||
function SetGuard:QueueUpdate(rebuildSets)
|
||||
if rebuildSets then
|
||||
self:RebuildSetSlots()
|
||||
end
|
||||
if updateQueued then
|
||||
return
|
||||
end
|
||||
|
||||
updateQueued = true
|
||||
C_Timer.After(0.1, function()
|
||||
updateQueued = false
|
||||
SetGuard:UpdateVisibleBags()
|
||||
end)
|
||||
end
|
||||
|
||||
function SetGuard:OnEvent(event)
|
||||
if event == "MERCHANT_SHOW" then
|
||||
merchantOpen = true
|
||||
elseif event == "MERCHANT_CLOSED" then
|
||||
merchantOpen = false
|
||||
end
|
||||
|
||||
self:QueueUpdate(event == "PLAYER_LOGIN" or event == "EQUIPMENT_SETS_CHANGED")
|
||||
end
|
||||
|
||||
SetGuard:RegisterEvent("PLAYER_LOGIN")
|
||||
SetGuard:RegisterEvent("EQUIPMENT_SETS_CHANGED")
|
||||
SetGuard:RegisterEvent("BAG_UPDATE_DELAYED")
|
||||
SetGuard:RegisterEvent("MERCHANT_SHOW")
|
||||
SetGuard:RegisterEvent("MERCHANT_CLOSED")
|
||||
SetGuard:RegisterEvent("ADDON_LOADED")
|
||||
SetGuard:SetScript("OnEvent", SetGuard.OnEvent)
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
## Interface: 11508
|
||||
## Title: Set Guard
|
||||
## Notes: Marks bag items saved in equipment sets and dims them at merchants.
|
||||
## Author: Local
|
||||
## Version: 1.0
|
||||
|
||||
SetGuard.lua
|
||||
|
|
@ -0,0 +1,385 @@
|
|||
local addonName = ...
|
||||
|
||||
local SuffixQuery = CreateFrame("Frame")
|
||||
_G[addonName] = SuffixQuery
|
||||
|
||||
local defaults = {
|
||||
mode = "AND",
|
||||
selectedStats = {},
|
||||
minimapAngle = 225,
|
||||
}
|
||||
|
||||
local statOptions = {
|
||||
{ key = "strength", label = "Strength" },
|
||||
{ key = "agility", label = "Agility" },
|
||||
{ key = "stamina", label = "Stamina" },
|
||||
{ key = "intellect", label = "Intellect" },
|
||||
{ key = "spirit", label = "Spirit" },
|
||||
{ key = "attackPower", label = "Attack power" },
|
||||
{ key = "spellPower", label = "Spell power" },
|
||||
{ key = "healing", label = "Healing" },
|
||||
{ key = "crit", label = "Critical strike" },
|
||||
{ key = "hit", label = "Hit" },
|
||||
{ key = "haste", label = "Haste" },
|
||||
{ key = "defense", label = "Defense" },
|
||||
{ key = "dodge", label = "Dodge" },
|
||||
{ key = "parry", label = "Parry" },
|
||||
{ key = "block", label = "Block" },
|
||||
{ key = "expertise", label = "Expertise" },
|
||||
}
|
||||
|
||||
local function Suffix(term, ...)
|
||||
local stats = {}
|
||||
for index = 1, select("#", ...) do
|
||||
stats[select(index, ...)] = true
|
||||
end
|
||||
return { term = term, stats = stats }
|
||||
end
|
||||
|
||||
-- Classic-oriented mappings based on the historical Item suffix and
|
||||
-- ItemRandomSuffix tables. Terms intentionally omit "of the" for AH search.
|
||||
local suffixes = {
|
||||
Suffix("Strength", "strength"),
|
||||
Suffix("Agility", "agility"),
|
||||
Suffix("Stamina", "stamina"),
|
||||
Suffix("Intellect", "intellect"),
|
||||
Suffix("Spirit", "spirit"),
|
||||
Suffix("Tiger", "strength", "agility"),
|
||||
Suffix("Beast", "strength", "agility", "stamina"),
|
||||
Suffix("Grove", "strength", "agility", "stamina"),
|
||||
Suffix("Striking", "strength", "agility", "stamina"),
|
||||
Suffix("Bear", "strength", "stamina"),
|
||||
Suffix("Gorilla", "strength", "intellect"),
|
||||
Suffix("Boar", "strength", "spirit"),
|
||||
Suffix("Monkey", "agility", "stamina"),
|
||||
Suffix("Falcon", "agility", "intellect"),
|
||||
Suffix("Wolf", "agility", "spirit"),
|
||||
Suffix("Eagle", "stamina", "intellect"),
|
||||
Suffix("Whale", "stamina", "spirit"),
|
||||
Suffix("Owl", "intellect", "spirit"),
|
||||
Suffix("Ancestor", "strength", "stamina", "crit"),
|
||||
Suffix("Battle", "strength", "stamina", "crit"),
|
||||
Suffix("Champion", "strength", "stamina", "dodge"),
|
||||
Suffix("Crusade", "strength", "stamina", "dodge"),
|
||||
Suffix("Mercenary", "strength", "stamina", "hit"),
|
||||
Suffix("Soldier", "strength", "stamina", "crit"),
|
||||
Suffix("Squire", "strength", "stamina", "hit"),
|
||||
Suffix("Bandit", "agility", "stamina", "crit"),
|
||||
Suffix("Hunt", "agility", "stamina", "crit"),
|
||||
Suffix("Marksman", "agility", "stamina", "hit"),
|
||||
Suffix("Shadow", "agility", "stamina", "crit"),
|
||||
Suffix("Thief", "agility", "stamina", "haste"),
|
||||
Suffix("Wild", "agility", "stamina", "hit"),
|
||||
Suffix("Elder", "stamina", "intellect", "spirit"),
|
||||
Suffix("Foreseer", "intellect", "crit", "haste"),
|
||||
Suffix("Mind", "intellect", "crit", "haste"),
|
||||
Suffix("Moon", "stamina", "intellect"),
|
||||
Suffix("Necromancer", "stamina", "intellect", "hit"),
|
||||
Suffix("Nightmare", "stamina", "intellect", "crit"),
|
||||
Suffix("Prophet", "stamina", "intellect", "spirit"),
|
||||
Suffix("Restoration", "stamina", "intellect", "spirit"),
|
||||
Suffix("Seer", "stamina", "intellect", "crit"),
|
||||
Suffix("Sorcerer", "stamina", "intellect"),
|
||||
Suffix("Sun", "stamina", "intellect", "crit"),
|
||||
Suffix("Vision", "stamina", "intellect", "crit"),
|
||||
Suffix("Eluding", "agility", "dodge"),
|
||||
Suffix("Blocking", "strength", "block"),
|
||||
Suffix("Knight", "stamina", "expertise"),
|
||||
Suffix("Defender", "stamina", "defense"),
|
||||
Suffix("Healing", "healing"),
|
||||
Suffix("Arcane Wrath", "spellPower"),
|
||||
Suffix("Fiery Wrath", "spellPower"),
|
||||
Suffix("Frozen Wrath", "spellPower"),
|
||||
Suffix("Nature's Wrath", "spellPower"),
|
||||
Suffix("Shadow Wrath", "spellPower"),
|
||||
}
|
||||
|
||||
local function CopyDefaults(target, source)
|
||||
for key, value in pairs(source) do
|
||||
if target[key] == nil then
|
||||
target[key] = type(value) == "table" and CopyDefaults({}, value) or value
|
||||
end
|
||||
end
|
||||
return target
|
||||
end
|
||||
|
||||
local function GetSelectedStats()
|
||||
local selected = {}
|
||||
for _, option in ipairs(statOptions) do
|
||||
if SuffixQuery.db.selectedStats[option.key] then
|
||||
table.insert(selected, option.key)
|
||||
end
|
||||
end
|
||||
return selected
|
||||
end
|
||||
|
||||
local function MatchesSelection(suffix, selected, mode)
|
||||
if mode == "OR" then
|
||||
for _, stat in ipairs(selected) do
|
||||
if suffix.stats[stat] then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
for _, stat in ipairs(selected) do
|
||||
if not suffix.stats[stat] then
|
||||
return false
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function SuffixQuery:BuildQuery()
|
||||
local selected = GetSelectedStats()
|
||||
if #selected == 0 then
|
||||
return nil, "Select at least one stat."
|
||||
end
|
||||
|
||||
local terms = {}
|
||||
for _, suffix in ipairs(suffixes) do
|
||||
if MatchesSelection(suffix, selected, self.db.mode) then
|
||||
table.insert(terms, suffix.term:lower())
|
||||
end
|
||||
end
|
||||
|
||||
if #terms == 0 then
|
||||
return nil, "No mapped suffixes match that selection."
|
||||
end
|
||||
|
||||
return table.concat(terms, "|")
|
||||
end
|
||||
|
||||
local function CreateBackdrop(frame)
|
||||
frame:SetBackdrop({
|
||||
bgFile = "Interface\\DialogFrame\\UI-DialogBox-Background",
|
||||
edgeFile = "Interface\\DialogFrame\\UI-DialogBox-Border",
|
||||
tile = true,
|
||||
tileSize = 32,
|
||||
edgeSize = 32,
|
||||
insets = { left = 11, right = 11, top = 11, bottom = 11 },
|
||||
})
|
||||
frame:SetBackdropColor(0, 0, 0, 0.95)
|
||||
end
|
||||
|
||||
local function CreateText(parent, text, template, point, relativeTo, relativePoint, x, y)
|
||||
local label = parent:CreateFontString(nil, "OVERLAY", template)
|
||||
label:SetText(text)
|
||||
label:SetPoint(point, relativeTo or parent, relativePoint or point, x or 0, y or 0)
|
||||
return label
|
||||
end
|
||||
|
||||
function SuffixQuery:RefreshModeButtons()
|
||||
local isAnd = self.db.mode == "AND"
|
||||
self.andButton:SetText(isAnd and "AND (ON)" or "AND")
|
||||
self.orButton:SetText(isAnd and "OR" or "OR (ON)")
|
||||
end
|
||||
|
||||
function SuffixQuery:ShowQuery()
|
||||
local query, message = self:BuildQuery()
|
||||
if not query then
|
||||
self.status:SetText(message)
|
||||
self.status:SetTextColor(1, 0.25, 0.25)
|
||||
return
|
||||
end
|
||||
|
||||
-- Edit boxes render a literal pipe as "||". The copied value remains a
|
||||
-- single pipe, matching the auction-house search grammar.
|
||||
self.result:SetText(query:gsub("|", "||"))
|
||||
self.result:SetFocus()
|
||||
self.result:HighlightText()
|
||||
self.status:SetText(string.format("%d suffixes — Ctrl+C copies and closes.", select(2, query:gsub("|", "")) + 1))
|
||||
self.status:SetTextColor(0.3, 1, 0.3)
|
||||
end
|
||||
|
||||
function SuffixQuery:ToggleModal()
|
||||
if self.modal:IsShown() then
|
||||
self.modal:Hide()
|
||||
else
|
||||
self:RefreshModeButtons()
|
||||
self.modal:Show()
|
||||
end
|
||||
end
|
||||
|
||||
function SuffixQuery:UpdateMinimapPosition()
|
||||
local angle = math.rad(self.db.minimapAngle)
|
||||
self.minimapButton:ClearAllPoints()
|
||||
self.minimapButton:SetPoint("CENTER", Minimap, "CENTER", math.cos(angle) * 104, math.sin(angle) * 104)
|
||||
end
|
||||
|
||||
function SuffixQuery:CreateMinimapButton()
|
||||
local button = CreateFrame("Button", "SuffixQueryMinimapButton", Minimap)
|
||||
button:SetSize(62, 62)
|
||||
button:SetFrameStrata("MEDIUM")
|
||||
button:SetFrameLevel(8)
|
||||
local icon = button:CreateTexture(nil, "ARTWORK")
|
||||
icon:SetTexture("Interface\\Icons\\INV_Misc_Note_01")
|
||||
icon:SetSize(24, 24)
|
||||
icon:SetPoint("CENTER", button, "CENTER", -12, 12)
|
||||
icon:SetTexCoord(0.12, 0.88, 0.12, 0.88)
|
||||
local iconMask = button:CreateMaskTexture()
|
||||
iconMask:SetTexture("Interface\\Minimap\\UI-Minimap-Background", "CLAMPTOBLACKADDITIVE", "CLAMPTOBLACKADDITIVE")
|
||||
iconMask:SetAllPoints(icon)
|
||||
icon:AddMaskTexture(iconMask)
|
||||
local border = button:CreateTexture(nil, "OVERLAY")
|
||||
border:SetTexture("Interface\\Minimap\\MiniMap-TrackingBorder")
|
||||
border:SetAllPoints(button)
|
||||
local highlight = button:CreateTexture(nil, "HIGHLIGHT")
|
||||
highlight:SetTexture("Interface\\Minimap\\UI-Minimap-ZoomButton-Highlight")
|
||||
highlight:SetBlendMode("ADD")
|
||||
highlight:SetSize(62, 62)
|
||||
highlight:SetPoint("CENTER", button, "CENTER", -12, 12)
|
||||
button.icon = icon
|
||||
button:RegisterForClicks("LeftButtonUp")
|
||||
button:RegisterForDrag("LeftButton")
|
||||
button:SetScript("OnClick", function()
|
||||
SuffixQuery:ToggleModal()
|
||||
end)
|
||||
button:SetScript("OnDragStart", function(self)
|
||||
self:SetScript("OnUpdate", function()
|
||||
local cursorX, cursorY = GetCursorPosition()
|
||||
local scale = Minimap:GetEffectiveScale()
|
||||
local centerX, centerY = Minimap:GetCenter()
|
||||
cursorX, cursorY = cursorX / scale, cursorY / scale
|
||||
SuffixQuery.db.minimapAngle = math.deg(math.atan2(cursorY - centerY, cursorX - centerX))
|
||||
SuffixQuery:UpdateMinimapPosition()
|
||||
end)
|
||||
end)
|
||||
button:SetScript("OnDragStop", function(self)
|
||||
self:SetScript("OnUpdate", nil)
|
||||
end)
|
||||
button:SetScript("OnEnter", function(self)
|
||||
GameTooltip:SetOwner(self, "ANCHOR_LEFT")
|
||||
GameTooltip:AddLine("Suffix Query")
|
||||
GameTooltip:AddLine("Build a random-suffix search string.", 1, 1, 1, true)
|
||||
GameTooltip:Show()
|
||||
end)
|
||||
button:SetScript("OnLeave", GameTooltip_Hide)
|
||||
self.minimapButton = button
|
||||
self:UpdateMinimapPosition()
|
||||
end
|
||||
|
||||
function SuffixQuery:CreateModal()
|
||||
local frame = CreateFrame("Frame", "SuffixQueryModal", UIParent, "BackdropTemplate")
|
||||
frame:SetSize(460, 510)
|
||||
frame:SetPoint("CENTER")
|
||||
frame:SetFrameStrata("DIALOG")
|
||||
frame:SetToplevel(true)
|
||||
frame:EnableMouse(true)
|
||||
frame:SetMovable(true)
|
||||
frame:SetClampedToScreen(true)
|
||||
frame:Hide()
|
||||
CreateBackdrop(frame)
|
||||
table.insert(UISpecialFrames, frame:GetName())
|
||||
|
||||
frame:SetScript("OnMouseDown", function(self, button)
|
||||
if button == "LeftButton" then
|
||||
self:StartMoving()
|
||||
end
|
||||
end)
|
||||
frame:SetScript("OnMouseUp", function(self)
|
||||
self:StopMovingOrSizing()
|
||||
end)
|
||||
|
||||
CreateText(frame, "Suffix Query", "GameFontHighlightLarge", "TOP", frame, "TOP", 0, -22)
|
||||
CreateText(frame, "Select stats, choose matching logic, then generate a search string.", "GameFontNormalSmall", "TOP", frame, "TOP", 0, -48)
|
||||
|
||||
local close = CreateFrame("Button", nil, frame, "UIPanelCloseButton")
|
||||
close:SetPoint("TOPRIGHT", frame, "TOPRIGHT", -4, -4)
|
||||
|
||||
local modeLabel = CreateText(frame, "Match suffixes containing:", "GameFontHighlight", "TOPLEFT", frame, "TOPLEFT", 24, -79)
|
||||
local andButton = CreateFrame("Button", nil, frame, "UIPanelButtonTemplate")
|
||||
andButton:SetSize(92, 24)
|
||||
andButton:SetPoint("TOPLEFT", modeLabel, "BOTTOMLEFT", 0, -7)
|
||||
andButton:SetScript("OnClick", function()
|
||||
SuffixQuery.db.mode = "AND"
|
||||
SuffixQuery:RefreshModeButtons()
|
||||
end)
|
||||
self.andButton = andButton
|
||||
|
||||
local orButton = CreateFrame("Button", nil, frame, "UIPanelButtonTemplate")
|
||||
orButton:SetSize(92, 24)
|
||||
orButton:SetPoint("LEFT", andButton, "RIGHT", 8, 0)
|
||||
orButton:SetScript("OnClick", function()
|
||||
SuffixQuery.db.mode = "OR"
|
||||
SuffixQuery:RefreshModeButtons()
|
||||
end)
|
||||
self.orButton = orButton
|
||||
|
||||
CreateText(frame, "Stats", "GameFontHighlight", "TOPLEFT", frame, "TOPLEFT", 24, -145)
|
||||
for index, option in ipairs(statOptions) do
|
||||
local column = (index - 1) % 2
|
||||
local row = math.floor((index - 1) / 2)
|
||||
local checkbox = CreateFrame("CheckButton", nil, frame, "ChatConfigCheckButtonTemplate")
|
||||
checkbox:SetPoint("TOPLEFT", frame, "TOPLEFT", 28 + column * 205, -168 - row * 28)
|
||||
checkbox:SetChecked(self.db.selectedStats[option.key] == true)
|
||||
checkbox.Text:SetText(option.label)
|
||||
checkbox.Text:ClearAllPoints()
|
||||
checkbox.Text:SetPoint("LEFT", checkbox, "RIGHT", 2, 0)
|
||||
checkbox:SetScript("OnClick", function(self)
|
||||
SuffixQuery.db.selectedStats[option.key] = self:GetChecked() and true or nil
|
||||
end)
|
||||
end
|
||||
|
||||
local generate = CreateFrame("Button", nil, frame, "UIPanelButtonTemplate")
|
||||
generate:SetSize(160, 28)
|
||||
generate:SetPoint("BOTTOM", frame, "BOTTOM", 0, 78)
|
||||
generate:SetText("Generate query")
|
||||
generate:SetScript("OnClick", function()
|
||||
SuffixQuery:ShowQuery()
|
||||
end)
|
||||
|
||||
local result = CreateFrame("EditBox", nil, frame, "InputBoxTemplate")
|
||||
result:SetSize(400, 28)
|
||||
result:SetPoint("BOTTOM", frame, "BOTTOM", 0, 43)
|
||||
result:SetAutoFocus(false)
|
||||
result:SetFontObject("ChatFontNormal")
|
||||
result:SetScript("OnEscapePressed", function()
|
||||
frame:Hide()
|
||||
end)
|
||||
|
||||
local ctrlDown = false
|
||||
result:SetScript("OnKeyDown", function(_, key)
|
||||
if key == "LCTRL" or key == "RCTRL" or key == "LMETA" or key == "RMETA" then
|
||||
ctrlDown = true
|
||||
end
|
||||
end)
|
||||
result:SetScript("OnKeyUp", function(_, key)
|
||||
if key == "LCTRL" or key == "RCTRL" or key == "LMETA" or key == "RMETA" then
|
||||
C_Timer.After(0.2, function()
|
||||
ctrlDown = false
|
||||
end)
|
||||
elseif ctrlDown and (key == "C" or key == "X") then
|
||||
C_Timer.After(0.1, function()
|
||||
frame:Hide()
|
||||
end)
|
||||
end
|
||||
end)
|
||||
self.result = result
|
||||
|
||||
local status = CreateText(frame, "Generate highlights the result for copying.", "GameFontNormalSmall", "BOTTOM", frame, "BOTTOM", 0, 17)
|
||||
status:SetWidth(400)
|
||||
status:SetJustifyH("CENTER")
|
||||
self.status = status
|
||||
self.modal = frame
|
||||
self:RefreshModeButtons()
|
||||
end
|
||||
|
||||
function SuffixQuery:Initialize()
|
||||
SuffixQueryDB = CopyDefaults(SuffixQueryDB or {}, defaults)
|
||||
self.db = SuffixQueryDB
|
||||
self:CreateModal()
|
||||
self:CreateMinimapButton()
|
||||
SLASH_SUFFIXQUERY1 = "/suffixquery"
|
||||
SlashCmdList.SUFFIXQUERY = function()
|
||||
SuffixQuery:ToggleModal()
|
||||
end
|
||||
end
|
||||
|
||||
SuffixQuery:RegisterEvent("PLAYER_LOGIN")
|
||||
SuffixQuery:SetScript("OnEvent", function(self, event)
|
||||
if event == "PLAYER_LOGIN" then
|
||||
self:Initialize()
|
||||
end
|
||||
end)
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
## Interface: 11508
|
||||
## Title: Suffix Query
|
||||
## Notes: Builds auction-house search queries for Classic random item suffixes.
|
||||
## Author: Local
|
||||
## Version: 1.0
|
||||
## SavedVariables: SuffixQueryDB
|
||||
|
||||
SuffixQuery.lua
|
||||
Loading…
Reference in New Issue