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.
1661 lines
69 KiB
1661 lines
69 KiB
local COMPAT, _, T = select(4,GetBuildInfo()), ...
|
|
if T.SkipLocalActionBook then return end
|
|
if T.TenEnv then T.TenEnv() end
|
|
|
|
local MODERN, CF_CLASSIC, CI_ERA = COMPAT >= 10e4 or nil, COMPAT < 10e4 or nil, COMPAT < 2e4 or nil
|
|
local CF_WRATH, CF_CATA, CF_MISTS = COMPAT < 10e4 and COMPAT > 3e4 or nil, COMPAT < 10e4 and COMPAT > 4e4 or nil, COMPAT < 10e4 and COMPAT > 5e4 or nil
|
|
local MODERN_MOUNTS, MODERN_BATTLEPETS = MODERN or CF_WRATH, MODERN or CF_MISTS
|
|
local EV = T.Evie
|
|
local AB = T.ActionBook:compatible(2,43)
|
|
local RW = T.ActionBook:compatible("Rewire", 1,27)
|
|
local KR = T.ActionBook:compatible("Kindred", 1,14)
|
|
local IM = T.ActionBook:compatible("Imp", 1,0)
|
|
assert(EV and AB and RW and KR and IM and 1, "Incompatible library bundle")
|
|
local L = T.ActionBook.L
|
|
local FORCED_MOUNT_SPELLS = {}
|
|
local spellFeedback, itemHint, toyHint, mountHint
|
|
|
|
local NormalizeInRange = {[0]=0, 1, [true]=1, [false]=0}
|
|
local _, CLASS = UnitClass("player")
|
|
local lowered = setmetatable({}, {__index=function(t,k)
|
|
if k ~= nil then
|
|
local r = type(k) == "string" and k:lower() or k
|
|
t[k] = r
|
|
return r
|
|
end
|
|
end})
|
|
local callMethod = setmetatable({}, {__index=function(t,k)
|
|
t[k] = function(self, ...)
|
|
return self[k](self, ...)
|
|
end
|
|
return t[k]
|
|
end})
|
|
local function newWidgetName(prefix)
|
|
local bni, bn = 0 repeat
|
|
bn, bni = prefix .. bni, bni + 1
|
|
until GetClickFrame(bn) == nil
|
|
return bn
|
|
end
|
|
local function getSpellMountID(sid)
|
|
return sid and (FORCED_MOUNT_SPELLS[sid] or MODERN_MOUNTS and C_MountJournal.GetMountFromSpell(sid)) or false
|
|
end
|
|
local GetCachedItemName, PeekCachedItemName do
|
|
local itemNames = {}
|
|
function EV:GET_ITEM_INFO_RECEIVED(iid, ok)
|
|
if itemNames[iid] == false and ok then
|
|
itemNames[iid] = C_Item.GetItemInfo(iid) or false
|
|
end
|
|
end
|
|
function GetCachedItemName(ident)
|
|
local iid = tonumber(ident) or tonumber(type(ident) == "string" and ident:match("item:(%d+)"))
|
|
if iid then
|
|
local c, f = itemNames[iid], C_Item.GetItemInfo(iid)
|
|
itemNames[iid] = f or c or false
|
|
return f or c or nil
|
|
end
|
|
end
|
|
function PeekCachedItemName(ident)
|
|
return itemNames[tonumber(ident) or tonumber(type(ident) == "string" and ident:match("item:(%d+)"))]
|
|
end
|
|
end
|
|
local function toCooldown(now, start, duration, enabled)
|
|
if start and start > now then
|
|
start = start - 2^32/1000
|
|
end
|
|
duration = duration or 0
|
|
return duration > 0 and enabled ~= 0 and start+duration-now or 0, duration, enabled
|
|
end
|
|
|
|
securecall(function() -- mount: mount ID
|
|
if not MODERN_MOUNTS then
|
|
function mountHint()
|
|
end
|
|
return
|
|
end
|
|
local function callSummonMount(mountID)
|
|
C_MountJournal.SummonByID(mountID)
|
|
end
|
|
local function summonAction(mountID)
|
|
return "func", callSummonMount, mountID
|
|
end
|
|
if CLASS == "DRUID" then
|
|
local actType, clickPrefix do
|
|
local bn = newWidgetName("AB:M!")
|
|
local b = CreateFrame("Button", bn, nil)
|
|
b:SetScript("OnClick", function(_, btn)
|
|
btn = tonumber(btn)
|
|
if btn then
|
|
C_MountJournal.SummonByID(btn)
|
|
end
|
|
end)
|
|
actType, clickPrefix = "macrotext", SLASH_CLICK1 .. " " .. bn .. " "
|
|
if MODERN then
|
|
actType, clickPrefix = "retext", SLASH_CANCELFORM1 .. " [form,noform:moonkin,nocombat]\n" .. clickPrefix
|
|
end
|
|
end
|
|
summonAction = function(mountID)
|
|
return actType, clickPrefix .. mountID
|
|
end
|
|
end
|
|
|
|
local function checkUsableMountID(mid)
|
|
local _1, sid, _3, _4, _5, _6, _7, factionLocked, factionId, hide, have = C_MountJournal.GetMountInfoByID(mid)
|
|
return (have and sid ~= 0 and not hide
|
|
and (not factionLocked or factionId == (UnitFactionGroup("player") == "Horde" and 0 or 1)) and sid ~= 0
|
|
and RW:IsSpellCastable(sid, 2)) and mid or nil, sid
|
|
end
|
|
function mountHint(id)
|
|
local usable = (not (InCombatLockdown() or IsIndoors())) and HasFullControl() and not UnitIsDeadOrGhost("player")
|
|
local cname, sid, icon, active, usable2 = C_MountJournal.GetMountInfoByID(id)
|
|
local cdLeft, cdLength = toCooldown(GetTime(), GetSpellCooldown(sid))
|
|
return usable and cdLeft == 0 and usable2, active and 1 or 0, icon, cname, 0, cdLeft, cdLength, callMethod.SetMountBySpellID, sid
|
|
end
|
|
local actionMap = {}
|
|
local function createMount(id)
|
|
if type(id) == "number" and not actionMap[id] and checkUsableMountID(id) then
|
|
actionMap[id] = AB:CreateActionSlot(mountHint, id, summonAction(id))
|
|
end
|
|
return actionMap[id]
|
|
end
|
|
local function describeMount(id)
|
|
local name, sid, icon, _4, _5, _6, _7, factionLocked, factionId = C_MountJournal.GetMountInfoByID(id)
|
|
if name and factionLocked then
|
|
name = name .. (factionId == 0 and "|A:QuestPortraitIcon-Horde-small:14:14:0:-1|a" or "|A:QuestPortraitIcon-Alliance-small:15:13:-1:-1|a")
|
|
end
|
|
return L"Mount", name, icon, nil, callMethod.SetMountBySpellID, sid
|
|
end
|
|
AB:RegisterActionType("mount", createMount, describeMount, 1)
|
|
if MODERN then -- random
|
|
local mjID, rname, _, ricon = C_MountJournal.GetMountFromSpell(150544), GetSpellInfo(150544)
|
|
actionMap[0] = AB:CreateActionSlot(function()
|
|
return HasFullControl() and not IsIndoors(), IsMounted() and 1 or 0, ricon, rname, 0, 0, 0, callMethod.SetMountBySpellID, 150544
|
|
end, nil, summonAction(0))
|
|
FORCED_MOUNT_SPELLS[150544], actionMap[mjID or 0] = 0, 0
|
|
RW:SetCastEscapeAction(rname, actionMap[0])
|
|
RW:SetCastEscapeAction("spell:150544", actionMap[0])
|
|
end
|
|
local function mountSync()
|
|
AB:NotifyObservers("mount")
|
|
end
|
|
EV.NEW_MOUNT_ADDED, EV.PLAYER_ENTERING_WORLD, EV.COMPANION_LEARNED = mountSync, mountSync, mountSync
|
|
end)
|
|
securecall(function() -- spell: spell ID + mount spell ID
|
|
local actionMap, spellMap = {}, {}
|
|
local function isCurrentForm(q, qsid)
|
|
local id = GetShapeshiftForm()
|
|
if id == 0 then return end
|
|
local _, _, _, sid = GetShapeshiftFormInfo(id)
|
|
return q == sid or qsid == sid or q == GetSpellInfo(sid or 0) or (sid and q and ("" .. sid) == q)
|
|
end
|
|
local SetSpellBookItem, SetSpellByID, SetSpellByExactID do
|
|
if MODERN then
|
|
function SetSpellBookItem(self, id)
|
|
return self:SetSpellBookItem(id, BOOKTYPE_SPELL)
|
|
end
|
|
function SetSpellByID(self, ...)
|
|
return self:SetSpellByID(...)
|
|
end
|
|
function SetSpellByExactID(self, id)
|
|
return self:SetSpellByID(id, false, true, true)
|
|
end
|
|
else
|
|
local tr1 = {}
|
|
local function SetRankText(self, sid, ...)
|
|
if sid then
|
|
local tr = tr1[self]
|
|
if tr == nil and self then
|
|
local n = self:GetName()
|
|
tr = n and _G[n .. "TextRight1"]
|
|
tr = type(tr) == "table" and type(tr.IsObjectType) == "function" and tr:IsObjectType("FontString") and tr
|
|
tr1[self] = tr or false
|
|
end
|
|
local sr = tr and not tr:IsShown() and GetSpellSubtext(sid) or ""
|
|
if sr ~= "" then
|
|
tr:SetText(sr)
|
|
tr:SetTextColor(0.5, 0.5, 0.5)
|
|
tr:Show()
|
|
self:Show()
|
|
end
|
|
end
|
|
return ...
|
|
end
|
|
function SetSpellBookItem(self, id)
|
|
local st, sid = GetSpellBookItemInfo(id, "spell")
|
|
return SetRankText(self, st == "SPELL" and sid, self:SetSpellBookItem(id, "spell"))
|
|
end
|
|
function SetSpellByID(self, ...)
|
|
return SetRankText(self, (...), self:SetSpellByID(...))
|
|
end
|
|
function SetSpellByExactID(self, sid)
|
|
return SetRankText(self, sid, self:SetSpellByID(sid, nil, nil, true))
|
|
end
|
|
end
|
|
end
|
|
local getSpellIDFromName = CI_ERA and function(n)
|
|
return (select(7, GetSpellInfo(n or "")))
|
|
end or function(n)
|
|
return tonumber(((GetSpellLink(n) or ""):match("spell:(%d+)")))
|
|
end
|
|
local RUNE_BASESPELL_CACHE, RUNE_SPELLS = {}, {} if CI_ERA then
|
|
for sid in ("399967 417346 399954 417347 415450 417345 399966 417348 415449"):gmatch("%d+") do
|
|
RUNE_SPELLS[sid+0] = GetSpellInfo(sid+0)
|
|
end
|
|
setmetatable(RUNE_BASESPELL_CACHE, {__index=function(t, k)
|
|
t[k] = RUNE_SPELLS[FindBaseSpellByID(k)] or false
|
|
return t[k]
|
|
end})
|
|
end
|
|
local iconOverrideHandlers = {}
|
|
local function spellHint(n, _modState, target)
|
|
if not n then return end
|
|
local sname, _, _, _, _, _, sid = GetSpellInfo(n)
|
|
local mjID = sid and getSpellMountID(sid)
|
|
if mjID then return mountHint(mjID) end
|
|
if not sname then return end
|
|
local now, msid = GetTime(), spellMap[lowered[n]] or sid
|
|
local inRange, usable, nomana, hasRange = NormalizeInRange[IsSpellInRange(sid and RUNE_BASESPELL_CACHE[sid] or n, target or "target")], IsUsableSpell(n)
|
|
inRange, hasRange = inRange ~= 0, inRange ~= nil
|
|
local cdLeft, cdLength, enabled = toCooldown(now, GetSpellCooldown(n))
|
|
local count, charges, maxCharges, ccdStart, ccdLength = GetSpellCount(n), GetSpellCharges(n)
|
|
local state = ((IsSelectedSpellBookItem(n) or IsCurrentSpell(n) or isCurrentForm(n, sid) or enabled == 0) and 1 or 0) +
|
|
(MODERN and IsSpellOverlayed(msid or 0) and 2 or 0) + (nomana and 8 or 0) + (inRange and 0 or 16) + (charges and charges > 0 and 64 or 0) +
|
|
(hasRange and 512 or 0) + (usable and 0 or 1024) + (enabled == 0 and 2048 or 0)
|
|
usable = not not (usable and inRange and (cdLeft == 0 or enabled == 0))
|
|
if charges and maxCharges and charges < maxCharges and cdLeft == 0 then
|
|
cdLeft, cdLength = toCooldown(now, ccdStart, ccdLength, 1)
|
|
end
|
|
local ih, ico, ohUsable = iconOverrideHandlers[msid], nil
|
|
if ih then
|
|
ico, ohUsable = ih(msid, n)
|
|
if ohUsable ~= nil then
|
|
usable = ohUsable == true
|
|
end
|
|
end
|
|
local sbslot = msid and msid ~= 161691 and FindSpellBookSlotBySpellID(msid)
|
|
return usable, state, ico or GetSpellTexture(n), sname or n, count <= 1 and charges or count, cdLeft, cdLength, sbslot and SetSpellBookItem or msid and SetSpellByID, sbslot or msid
|
|
end
|
|
function spellFeedback(sname, target, spellId)
|
|
spellMap[sname] = spellId or spellMap[sname] or getSpellIDFromName(sname)
|
|
return spellHint(sname, nil, target)
|
|
end
|
|
local function createSpell(id, flags)
|
|
if type(id) ~= "number" then return end
|
|
local mjID = getSpellMountID(id)
|
|
if mjID then
|
|
return AB:GetActionSlot("mount", mjID)
|
|
end
|
|
|
|
local laxRank, action = CF_CLASSIC and flags ~= 16 and "lax-rank"
|
|
local castable, rwCastType = RW:IsSpellCastable(id, nil, laxRank)
|
|
if not castable then
|
|
return
|
|
elseif rwCastType == "forced-id-cast" then
|
|
action = id
|
|
elseif rwCastType == "rewire-escape" then
|
|
return AB:GetActionSlot("macrotext", SLASH_CAST1 .. " " .. GetSpellInfo(id))
|
|
else
|
|
local s0, r0 = GetSpellInfo(id), GetSpellSubtext(id)
|
|
local o, s = pcall(GetSpellInfo, s0, r0)
|
|
if laxRank and not (o and s) then
|
|
o, s = pcall(GetSpellInfo, s0)
|
|
end
|
|
if not (o and s and s0) then
|
|
return
|
|
end
|
|
local r1 = laxRank and r0 or GetSpellSubtext(s0)
|
|
action = (r0 and r1 ~= r0 and (CF_CLASSIC or FindSpellBookSlotBySpellID(id))) and (s0 .. "(" .. r0 .. ")") or s0
|
|
id = CF_CLASSIC and select(7, GetSpellInfo(action)) or id
|
|
end
|
|
|
|
if action then
|
|
if not actionMap[action] then
|
|
actionMap[action] = AB:CreateActionSlot(spellHint, action, "attribute", "type","spell", "spell",action, "checkselfcast",true, "checkfocuscast",true)
|
|
end
|
|
if type(action) == "string" and spellMap[action] ~= id then
|
|
spellMap[lowered[action]] = id
|
|
end
|
|
end
|
|
return actionMap[action]
|
|
end
|
|
local function describeSpell(q, id, flags)
|
|
local name2, sid2, icon2, rank, name, _, icon, _, _, _, _, icon1 = nil, nil, nil, GetSpellSubtext(id), GetSpellInfo(id)
|
|
local laxRank = CF_CLASSIC and flags ~= 16 and "lax-rank"
|
|
local _, castType = RW:IsSpellCastable(id, nil, laxRank)
|
|
if castType == "rune-ability-spell" then
|
|
_, icon2 = GetSpellTexture(id)
|
|
elseif name and castType ~= "forced-id-cast" then
|
|
local qRank = (MODERN or q == "list-query" or not laxRank) and rank or nil
|
|
rank, name2, _, icon2, _, _, _, sid2 = GetSpellSubtext(name, rank), GetSpellInfo(name, qRank)
|
|
if MODERN and sid2 and IsPassiveSpell(sid2) or RUNE_SPELLS[id] then
|
|
icon, name2, icon2 = icon1 or icon, nil, nil
|
|
end
|
|
end
|
|
local srank = rank and rank ~= "" and (rank ~= GetSpellSubtext(name) or (CF_CLASSIC and rank ~= GetSpellSubtext(name, (rank:gsub("%d+", "1"))))) and " (" .. rank .. ")" or ""
|
|
local ts, ns = q == "list-query" and srank or "", (laxRank or q == "list-query") and "" or srank
|
|
local mjID = getSpellMountID(id)
|
|
return mjID and L"Mount" or (L"Spell" .. ts), (name2 or name or "?") .. ns, icon2 or icon, nil, SetSpellByExactID, id
|
|
end
|
|
AB:RegisterActionType("spell", createSpell, describeSpell, 2, true)
|
|
if MODERN then -- specials
|
|
local gab = GetSpellInfo(161691)
|
|
actionMap[gab] = AB:CreateActionSlot(spellHint, gab, "conditional", "[outpost]", "attribute", "type","spell", "spell",gab)
|
|
spellMap[lowered[gab]] = 161691
|
|
actionMap[150544] = AB:GetActionSlot("mount", 0)
|
|
end
|
|
|
|
function EV.SPELLS_CHANGED()
|
|
wipe(RUNE_BASESPELL_CACHE)
|
|
for k, v in pairs(spellMap) do
|
|
if v ~= 161691 then
|
|
spellMap[k] = nil
|
|
end
|
|
end
|
|
AB:NotifyObservers("spell")
|
|
end
|
|
function AB.HUM:SetSpellIconOverride(id, f)
|
|
if not (type(id) == "number" and (f == nil or type(f) == "function")) then
|
|
return error('SetSpellIconOverride: invalid arguments', 2)
|
|
end
|
|
iconOverrideHandlers[id] = f
|
|
end
|
|
end)
|
|
securecall(function() -- item: items ID/inventory slot
|
|
local actionMap, itemIdMap, LAST_EQUIP_SLOT = {}, {}, INVSLOT_LAST_EQUIPPED
|
|
local countOverrideHandlers = {}
|
|
local function containerTip(self, bagslot)
|
|
local slot = bagslot % 100
|
|
self:SetBagItem((bagslot-slot)/100, slot)
|
|
end
|
|
local function playerInventoryTip(self, slot)
|
|
self:SetInventoryItem("player", slot)
|
|
end
|
|
local function GetItemLocation(iid, name, name2)
|
|
local name2, cb, cs, n = name2 and lowered[name2]
|
|
for i=1, LAST_EQUIP_SLOT do
|
|
if GetInventoryItemID("player", i) == iid then
|
|
n = C_Item.GetItemInfo(GetInventoryItemLink("player", i))
|
|
if n == name or n and name2 and lowered[n] == name2 then
|
|
return nil, i
|
|
elseif not cs then
|
|
cb, cs = nil, i
|
|
end
|
|
end
|
|
end
|
|
local ns, giid, gil = C_Container.GetContainerNumSlots, C_Container.GetContainerItemID, C_Container.GetContainerItemLink
|
|
for i=0,4 do
|
|
for j=1, ns(i) do
|
|
if iid == giid(i, j) then
|
|
n = C_Item.GetItemInfo(gil(i, j))
|
|
if n == name or n and name2 and lowered[n] == name2 then
|
|
return i, j
|
|
elseif not cs then
|
|
cb, cs = i, j
|
|
end
|
|
end
|
|
end
|
|
end
|
|
return cb, cs
|
|
end
|
|
function itemHint(ident, _modState, target, purpose, ibag, islot)
|
|
local name, link, icon, _, bag, slot, tip, tipArg
|
|
if type(ident) == "number" and ident <= LAST_EQUIP_SLOT then
|
|
local invid = GetInventoryItemID("player", ident)
|
|
if invid == nil then return end
|
|
bag, slot, name, link = nil, invid, C_Item.GetItemInfo(GetInventoryItemLink("player", ident) or invid)
|
|
ident = name or ident
|
|
elseif ident then
|
|
name, link, _, _, _, _, _, _, _, icon = C_Item.GetItemInfo(ident)
|
|
else
|
|
return
|
|
end
|
|
local iid, cdLeft, cdLength, enabled = (link and tonumber(link:match("item:([x%x]+)"))) or itemIdMap[ident]
|
|
if MODERN and iid and PlayerHasToy(iid) and C_Item.GetItemCount(iid) == 0 then
|
|
return toyHint(iid, nil, target)
|
|
elseif iid then
|
|
cdLeft, cdLength, enabled = toCooldown(GetTime(), C_Container.GetItemCooldown(iid))
|
|
end
|
|
target = target or "target"
|
|
local canRange = not (InCombatLockdown() and (UnitIsFriend("player", target) or not UnitExists(target))) or nil
|
|
local inRange, hasRange = canRange and NormalizeInRange[C_Item.IsItemInRange(ident, target)]
|
|
inRange, hasRange = inRange ~= 0, inRange ~= nil
|
|
if ibag and islot then
|
|
bag, slot = ibag, islot
|
|
elseif iid then
|
|
bag, slot = GetItemLocation(iid, name, ident)
|
|
end
|
|
if bag and slot then
|
|
tip, tipArg = containerTip, bag * 100 + slot
|
|
elseif slot then
|
|
tip, tipArg = playerInventoryTip, slot
|
|
elseif iid then
|
|
tip, tipArg = callMethod.SetItemByID, iid
|
|
end
|
|
local nCharge = C_Item.GetItemCount(ident, false, true) or 0
|
|
local usable = nCharge > 0 and (C_Item.GetItemSpell(ident) == nil or C_Item.IsUsableItem(ident))
|
|
local qual = MODERN and ident and (C_TradeSkillUI.GetItemReagentQualityByItemInfo(ident) or C_TradeSkillUI.GetItemCraftedQualityByItemInfo(ident))
|
|
qual = qual and qual > 0 and qual < 8 and (qual * 16384) or 0
|
|
local state = (C_Item.IsCurrentItem(ident) and 1 or 0) + (inRange and 0 or 16) + (slot and C_Item.IsEquippableItem(ident) and (bag and (purpose == "equip" and 128 or 0) or (slot and 256 or 0)) or 0) + (hasRange and 512 or 0) + (usable and 0 or 1024) + (enabled == 0 and 2048 or 0) + qual
|
|
usable = not not (usable and inRange and cdLeft == 0)
|
|
icon = icon or C_Item.GetItemIconByID(ident)
|
|
local oh = countOverrideHandlers[iid]
|
|
if oh then
|
|
local ohCharge, ohUsable = oh(iid, nCharge)
|
|
nCharge = ohCharge or nCharge
|
|
if ohUsable == true or ohUsable == false then
|
|
usable = ohUsable
|
|
end
|
|
end
|
|
return usable, state, icon, name or ident, nCharge, cdLeft, cdLength or 0, tip, tipArg
|
|
end
|
|
local function createItem(id, flags)
|
|
local byName, forceShow, onlyEquipped
|
|
if type(id) ~= "number" then return end
|
|
if type(flags) == "number" then
|
|
byName, forceShow, onlyEquipped = flags % 4 >= 2, flags % 2 >= 1, flags % 8 >= 4
|
|
end
|
|
local name = id <= LAST_EQUIP_SLOT and id or (byName and GetCachedItemName(id) or ("item:" .. id))
|
|
if not forceShow and onlyEquipped and not ((id > LAST_EQUIP_SLOT and C_Item.IsEquippedItem(name)) or (id <= LAST_EQUIP_SLOT and GetInventoryItemLink("player", id))) then return end
|
|
if not forceShow and C_Item.GetItemCount(name) == 0 then return end
|
|
if not actionMap[name] then
|
|
actionMap[name], itemIdMap[name] = AB:CreateActionSlot(itemHint, name, "attribute", "type","item", "item",name, "checkselfcast",true, "checkfocuscast",true), id
|
|
end
|
|
return actionMap[name]
|
|
end
|
|
local function describeItem(id, _flags)
|
|
local cat, cq = L"Item", MODERN and id and (C_TradeSkillUI.GetItemReagentQualityByItemInfo(id) or C_TradeSkillUI.GetItemCraftedQualityByItemInfo(id))
|
|
cat = cq and cat .. "|A:Professions-Icon-Quality-Tier" .. cq .. "-Small:0:0:2:0|a" or cat
|
|
return cat, C_Item.GetItemNameByID(id) or PeekCachedItemName(id), C_Item.GetItemIconByID(id), nil, callMethod.SetItemByID, tonumber(id)
|
|
end
|
|
AB:RegisterActionType("item", createItem, describeItem, 2)
|
|
function EV.BAG_UPDATE()
|
|
AB:NotifyObservers("item")
|
|
end
|
|
RW:SetCommandHint(SLASH_EQUIP1, 70, function(_, _, clause, target)
|
|
if clause and clause ~= "" and C_Item.GetItemNameByID(clause) then
|
|
return true, itemHint(clause, nil, target, "equip")
|
|
end
|
|
end)
|
|
RW:SetCommandHint(SLASH_EQUIP_TO_SLOT1, 70, function(_, _, clause)
|
|
local item = clause and clause:match("^%s*%d+%s+(.*)")
|
|
if item then
|
|
return RW:GetCommandAction(SLASH_EQUIP1, item)
|
|
end
|
|
end)
|
|
function AB.HUM:SetItemCountOverride(id, f)
|
|
if not (type(id) == "number" and (f == nil or type(f) == "function")) then
|
|
error('SetItemCountOverride: invalid arguments', 2)
|
|
end
|
|
countOverrideHandlers[id] = f
|
|
end
|
|
end)
|
|
securecall(function() -- peq: slot token
|
|
local slots = {
|
|
head="HEADSLOT", neck="NECKSLOT", shoulders="SHOULDERSLOT", shirt="SHIRTSLOT", chest="CHESTSLOT",
|
|
waist="WAISTSLOT", legs="LEGSSLOT", feet="FEETSLOT", wrist="WRISTSLOT", hands="HANDSSLOT",
|
|
finger1="FINGER0SLOT", finger2="FINGER1SLOT", trinket1="TRINKET0SLOT", trinket2="TRINKET1SLOT",
|
|
back="BACKSLOT", tabard="TABARDSLOT",
|
|
}
|
|
for tk, sk in pairs(slots) do
|
|
local sn, suf, ok, slot = _G[sk], tk:match("%d+$"), pcall(GetInventorySlotInfo, sk)
|
|
slots[tk] = ok and slot and {sk, sn and suf and (sn .. " " .. suf) or sn or sk} or nil
|
|
if ok and slot then
|
|
RW:SetCastAlias(tk, tostring(slot), false)
|
|
end
|
|
end
|
|
local function describePlayerEquipmentSlot(tk)
|
|
local si = slots[tk]
|
|
if si then
|
|
local _, tex = GetInventorySlotInfo(si[1])
|
|
return L"Equipment Slot", si[2], tex
|
|
end
|
|
end
|
|
local function createPlayerEquipmentSlot(tk)
|
|
local si = slots[tk]
|
|
if si and not si[3] then
|
|
local slot = GetInventorySlotInfo(si[1])
|
|
si[3] = AB:CreateActionSlot(itemHint, slot, "conditional","[uslot:" .. tk .. "]", "attribute", "type","item", "item",slot)
|
|
end
|
|
return si and si[3]
|
|
end
|
|
AB:RegisterActionType("peq", createPlayerEquipmentSlot, describePlayerEquipmentSlot, 1)
|
|
end)
|
|
securecall(function() -- macrotext
|
|
local map = {}
|
|
local function macroHint(mtext, modLockState)
|
|
return RW:GetMacroAction(mtext, modLockState)
|
|
end
|
|
local function createMacrotext(macrotext)
|
|
if type(macrotext) ~= "string" then return end
|
|
if not map[macrotext] then
|
|
map[macrotext] = AB:CreateActionSlot(macroHint, macrotext, "retext", macrotext, false, true)
|
|
end
|
|
return map[macrotext]
|
|
end
|
|
local function describeMacrotext(macrotext)
|
|
if macrotext == "" then return L"Custom Macro", L"New Macro", "Interface/Icons/INV_Misc_Note_03" end
|
|
local _, _, ico = RW:GetMacroAction(macrotext)
|
|
return L"Custom Macro", "", ico
|
|
end
|
|
AB:RegisterActionType("macrotext", createMacrotext, describeMacrotext, 1)
|
|
local function checkReturn(pri, ...)
|
|
if select("#", ...) > 0 then return pri, ... end
|
|
end
|
|
local function checkCountReturn(pri, ...)
|
|
if select("#", ...) > 0 then
|
|
local _, _, _, _, nc = ...
|
|
return nc == 0 and pri - 5 or pri, ...
|
|
end
|
|
end
|
|
local function canUseViaSCUI(clause)
|
|
if (tonumber(clause) or 0) > INVSLOT_LAST_EQUIPPED then
|
|
-- SCUI will pass to UseInventoryItem
|
|
return false
|
|
end
|
|
return true
|
|
end
|
|
RW:SetCommandHint("/use", 100, function(_, _, clause, target, _, _, msg)
|
|
if not clause or clause == "" then return end
|
|
local isItemReturn, link, bag, slot = false, SecureCmdItemParse(clause)
|
|
if (bag and slot) or (link and C_Item.GetItemInfoInstant(link)) then
|
|
if msg == "castrandom-fallback" or canUseViaSCUI(clause) then
|
|
isItemReturn = true
|
|
end
|
|
end
|
|
if isItemReturn then
|
|
return checkCountReturn(90, itemHint(link, nil, target, nil, bag, slot))
|
|
end
|
|
local sid = clause:match("^spell:(%d+)$")
|
|
if sid or not tonumber(clause, 10) then
|
|
return checkReturn(true, spellFeedback(sid or clause, target))
|
|
end
|
|
end)
|
|
RW:SetCommandHint("/cast", 100, function(_, _, clause, target, _, _, msg)
|
|
if not clause or clause == "" then return end
|
|
local sex = DoesSpellExist(clause) and not tonumber(clause, 10)
|
|
local sid = not sex and clause:match("^spell:(%d+)$")
|
|
if sex or sid then
|
|
return checkReturn(true, spellFeedback(sid or clause, target))
|
|
else
|
|
local link, bag, slot = SecureCmdItemParse(clause)
|
|
if ((bag and slot) or (link and C_Item.GetItemInfoInstant(link))) and
|
|
(msg == "castrandom-fallback" or canUseViaSCUI(clause)) then
|
|
return checkCountReturn(90, itemHint(link, nil, target, nil, bag, slot))
|
|
end
|
|
end
|
|
end)
|
|
RW:SetCommandHint(SLASH_CASTSEQUENCE1, 100, function(_, _, clause, target)
|
|
if not clause or clause == "" then return end
|
|
local _, item, spell = QueryCastSequence(clause)
|
|
clause = (item or spell)
|
|
if clause then
|
|
return RW:GetCommandAction("/use", clause, target)
|
|
end
|
|
end)
|
|
do -- /userandom + /qsequence
|
|
local f = CreateFrame("Frame", nil, nil, "SecureHandlerBaseTemplate")
|
|
f:SetFrameRef("RW", RW:seclib())
|
|
f:SetFrameRef("KR", KR:seclib())
|
|
f:Execute([[-- AB_userandom_init
|
|
seed, crState, qsState = math.random(2^30), newtable(), newtable()
|
|
RW = self:GetFrameRef('RW'), self:SetAttribute('frameref-RW', nil)
|
|
KR = self:GetFrameRef('KR'), self:SetAttribute('frameref-KR', nil)
|
|
]])
|
|
f:SetAttribute("RunSlashCmd", [=[-- AB_userandom
|
|
local cmd, v, target, s, q = ...
|
|
local isRand = cmd ~= "/qsequence"
|
|
local tv, i, vt, _ = (isRand and crState or qsState)[v]
|
|
if v == "" or not v then
|
|
return
|
|
elseif not tv then
|
|
local iv, tn, np = newtable(), 1, 1 --@init_clause_start
|
|
while np do
|
|
local sp, spc, ev, eo = np, np
|
|
repeat
|
|
sp, spc = spc, v:match("^%s*<[^<]->()%s*", sp)
|
|
until not spc
|
|
eo = sp > np and v:sub(np, sp-1):gsub("<(.-)>", "[%1]") or nil
|
|
ev, np = v:match("^%s*([^%s,][^,]*),?%s*()", sp)
|
|
ev = ev and ev:match("^%s*(.-)%s*$") or ""
|
|
if ev ~= "" then
|
|
iv[-tn], iv[tn], tn = eo, ev, tn + 1
|
|
end
|
|
end
|
|
tv, (isRand and crState or qsState)[v], iv[0] = iv, iv, isRand and 1 + seed % #iv or 1 --@init_clause_end
|
|
end
|
|
i = tv[0]
|
|
v, vt, tv[0] = tv[i], tv[-i], isRand and math.random(#tv) or (1 + i % #tv)
|
|
if v then
|
|
if vt then
|
|
_, vt = KR:RunAttribute("EvaluateCmdOptions", vt)
|
|
end
|
|
return RW:RunAttribute("RunSlashCmd", "/cast", v, vt or target, isRand and "opt-into-cr-fallback")
|
|
end
|
|
]=])
|
|
local getNextCast do -- (kind, v, target) -> (v, target)
|
|
local senv = GetManagedEnvironment(f)
|
|
local uenv = setmetatable({qsState={}, crState={}, newtable=function() return {} end}, {__index=senv})
|
|
local initF = setfenv(loadstring("return function(isRand, v)\n" .. f:GetAttribute("RunSlashCmd"):match("[^\n]+@init_clause_start.-@init_clause_end") .. "\nreturn iv end"), {})
|
|
initF = setfenv(initF(), uenv)
|
|
function getNextCast(k, c, target)
|
|
local t1, ucache, tv, i, v, vt = senv[k][c], uenv[k]
|
|
tv = t1 or ucache[c] or initF(k == "crState", c)
|
|
if t1 then
|
|
ucache[c] = nil
|
|
end
|
|
i = tv[0]
|
|
v, vt = tv[i], tv[-i]
|
|
if vt then
|
|
_, vt = KR:EvaluateCmdOptions(vt)
|
|
end
|
|
return v, vt or target
|
|
end
|
|
end
|
|
RW:RegisterCommand(SLASH_USERANDOM1, true, true, f)
|
|
local function hintCastRandom(_, _, clause, target)
|
|
if (clause or "") == "" then return end
|
|
local v, vt = getNextCast("crState", clause, target)
|
|
if v then
|
|
local nextN = tonumber(v)
|
|
if nextN and nextN > 20 and C_Item.GetItemNameByID(nextN) then
|
|
v = "item:" .. v
|
|
end
|
|
return RW:GetCommandAction("/use", v, vt or target, nil, "castrandom-fallback")
|
|
end
|
|
end
|
|
local function hintQuickSequence(_slash, _unparsed, clause, target)
|
|
if (clause or "") == "" then return end
|
|
local v, vt = getNextCast("qsState", clause, target)
|
|
if v then
|
|
return RW:GetCommandAction("/cast", v, vt)
|
|
end
|
|
end
|
|
RW:SetCommandHint(SLASH_USERANDOM1, 50, hintCastRandom)
|
|
SLASH_ACTIONBOOK_QSEQUENCE1, SLASH_ACTIONBOOK_QSEQUENCE2 = "/qsequence", "/quicksequence"
|
|
RW:RegisterCommand(SLASH_ACTIONBOOK_QSEQUENCE1, true, true, f)
|
|
RW:AddCommandAliases(SLASH_ACTIONBOOK_QSEQUENCE1, SLASH_ACTIONBOOK_QSEQUENCE2)
|
|
RW:SetCommandHint(SLASH_ACTIONBOOK_QSEQUENCE1, 100, hintQuickSequence)
|
|
IM:AddTokenizableCommand("ACTIONBOOK_QSEQUENCE", SLASH_CASTRANDOM1)
|
|
SLASH_ACTIONBOOK_QSEQUENCE1, SLASH_ACTIONBOOK_QSEQUENCE2 = nil, nil
|
|
end
|
|
end)
|
|
securecall(function() -- macro: name
|
|
local map, sm = {}, {} do
|
|
local wmSynced, owner = true, RW:RegisterNamedMacroTextOwner("ab-macro-wrapper", 10)
|
|
local function syncWMacros()
|
|
local notify, numGlobal, numChar = false, GetNumMacros()
|
|
for k in pairs(sm) do
|
|
if not GetMacroInfo(k) then
|
|
notify, sm[k] = RW:SetNamedMacroText(k, nil, owner, true) or notify, nil
|
|
end
|
|
end
|
|
local ofs = MAX_ACCOUNT_MACROS - numGlobal
|
|
for i=1,numGlobal + numChar do
|
|
local k, _, text = GetMacroInfo((i > numGlobal and ofs or 0)+i)
|
|
if k and text ~= sm[k] then
|
|
notify, sm[k] = RW:SetNamedMacroText(k, "#abmacrowrap " .. k .. "\n" .. text, owner, true) or notify, text
|
|
end
|
|
end
|
|
if notify then
|
|
AB:NotifyObservers("macro")
|
|
end
|
|
wmSynced = true
|
|
return "remove"
|
|
end
|
|
function EV.UPDATE_MACROS()
|
|
if not InCombatLockdown() then
|
|
syncWMacros()
|
|
elseif wmSynced then
|
|
EV.PLAYER_REGEN_ENABLED, wmSynced = syncWMacros, nil
|
|
end
|
|
end
|
|
end
|
|
RW:SetMetaHintFilter("abmacrowrap", "macroFallback", false, function(_meta, v)
|
|
if sm[v] then
|
|
local n, ico = GetMacroInfo(v)
|
|
return true, not not n, ico, v
|
|
end
|
|
end)
|
|
local function namedMacroHint(name, cndState)
|
|
return RW:GetNamedMacroAction(name, cndState)
|
|
end
|
|
local function createNamedMacro(name, flags)
|
|
local forceShow = flags == 1
|
|
if type(name) == "string" and (forceShow or RW:IsNamedMacroKnown(name)) then
|
|
if not map[name] then
|
|
map[name] = AB:CreateActionSlot(namedMacroHint, name, "reslash", "/runmacro", name)
|
|
end
|
|
return map[name]
|
|
end
|
|
end
|
|
local function describeMacro(name)
|
|
local _, ico
|
|
if RW:IsNamedMacroKnown(name) then
|
|
_, _, ico = RW:GetNamedMacroAction(name)
|
|
end
|
|
return L"Macro", name, ico
|
|
end
|
|
AB:RegisterActionType("macro", createNamedMacro, describeMacro, 2)
|
|
end)
|
|
securecall(function() -- battlepet: pet ID, species ID
|
|
if not (MODERN_BATTLEPETS or CF_WRATH) then
|
|
return
|
|
end
|
|
local petAction, special = {}, {}
|
|
local BPET_ATYPE_NAME, SummonCompanion = not MODERN_BATTLEPETS and COMPANIONS or L"Battle Pet"
|
|
local function SetBattlePetByID(self, id)
|
|
local sid, cname, lvl, _, _, _, _, name, _, ptype, _, _, desc, _, cb = C_PetJournal.GetPetInfoByPetID(id)
|
|
if not sid then return false end
|
|
local hp, mhp, ap, spd, rarity = C_PetJournal.GetPetStats(id)
|
|
local qc, hc, icof = ITEM_QUALITY_COLORS[rarity-1], HIGHLIGHT_FONT_COLOR, "|TInterface\\PetBattles\\PetBattle-StatIcons:0:0:0:0:32:32:%d:%d:%d:%d|t %s"
|
|
self:AddLine(cname or name, qc.r, qc.g, qc.b)
|
|
if cb then
|
|
self:AddLine(UNIT_TYPE_LEVEL_TEMPLATE:format(lvl, _G["BATTLE_PET_NAME_".. ptype]), hc.r, hc.g, hc.b)
|
|
self:AddLine(icof:format(0, 16, 0, 16, ap) .. " " .. icof:format(0, 16, 16, 32, spd) .. " " .. icof:format(16,32,16,32, hp < mhp and (hp .. "/" .. mhp) or hp), hc.r, hc.g, hc.b)
|
|
elseif desc then
|
|
local nc = NORMAL_FONT_COLOR
|
|
self:AddLine(desc, nc.r, nc.g, nc.b, 1)
|
|
end
|
|
end
|
|
if not MODERN_BATTLEPETS then
|
|
SetBattlePetByID = callMethod.SetCompanionPet
|
|
end
|
|
if not MODERN then
|
|
function SummonCompanion(guid)
|
|
if C_PetJournal.IsCurrentlySummoned(guid) then
|
|
C_PetJournal.DismissSummonedPet(guid)
|
|
else
|
|
DoEmote("STAND")
|
|
C_PetJournal.SummonPetByGUID(guid)
|
|
end
|
|
end
|
|
end
|
|
local function battlepetHint(pid)
|
|
local sid, cn, _, _, _, _, _, n, tex = C_PetJournal.GetPetInfoByPetID(pid)
|
|
local cdLeft, cdLength, enabled = toCooldown(GetTime(), C_PetJournal.GetPetCooldownByGUID(pid))
|
|
local state = (CF_WRATH and (C_PetJournal.IsCurrentlySummoned(pid) and 1 or 0) or strcmputf8i(C_PetJournal.GetSummonedPetGUID() or "", pid) == 0 and 1 or 0)
|
|
+ (enabled == 0 and 2048 or 0)
|
|
return sid and cdLeft == 0 and C_PetJournal.PetIsSummonable(pid), state, tex, cn or n or "", 0, cdLeft, cdLength, SetBattlePetByID, pid
|
|
end
|
|
if MODERN_BATTLEPETS then -- random favorite pet
|
|
local rname, _, ricon = GetSpellInfo(243819)
|
|
local function randFaveHint()
|
|
return HasFullControl(), C_PetJournal.GetSummonedPetGUID() and 1 or 0, ricon, rname, 0, 0, 0, callMethod.SetSpellByID, 243819
|
|
end
|
|
petAction.fave = AB:CreateActionSlot(randFaveHint, nil, "macrotext", SLASH_RANDOMFAVORITEPET1)
|
|
RW:ImportSlashCmd("RANDOMFAVORITEPET", true, false, 20, function(_, _, clause, _target)
|
|
if clause then
|
|
return true, randFaveHint()
|
|
end
|
|
end)
|
|
RW:SetCastEscapeAction(rname, petAction.fave)
|
|
RW:SetCastEscapeAction("spell:243819", petAction.fave)
|
|
function special.fave()
|
|
return BPET_ATYPE_NAME, rname, ricon, nil, callMethod.SetSpellByID, 243819
|
|
end
|
|
end
|
|
local GetBattlePetInfo do -- (petID[, speciesID])
|
|
local function checkInfoReturn(pid, spid, ok, ...)
|
|
if ok and ... then
|
|
return pid, ...
|
|
elseif spid then
|
|
local sn = C_PetJournal.GetPetInfoBySpeciesID(spid)
|
|
local _, pid = C_PetJournal.FindPetIDByName(sn ~= "" and sn or "---")
|
|
if pid then
|
|
return GetBattlePetInfo(pid)
|
|
end
|
|
end
|
|
end
|
|
function GetBattlePetInfo(pid, spid)
|
|
return checkInfoReturn(pid, spid, pcall(C_PetJournal.GetPetInfoByPetID, pid))
|
|
end
|
|
end
|
|
local function createBattlePet(pid, spid)
|
|
if special[pid] then return petAction[pid] end
|
|
local rpid = GetBattlePetInfo(pid, spid)
|
|
if not rpid then return end
|
|
local pk = rpid:upper()
|
|
if not petAction[pk] then
|
|
if MODERN then
|
|
petAction[pk] = AB:CreateActionSlot(battlepetHint, rpid, "macrotext", EMOTE143_CMD1 .. "\n" .. SLASH_SUMMON_BATTLE_PET1 .. " " .. rpid)
|
|
else -- no /summonbattlepet implementation in 4.4.0, 5.5.0
|
|
petAction[pk] = AB:CreateActionSlot(battlepetHint, rpid, "func", SummonCompanion, rpid)
|
|
end
|
|
end
|
|
return petAction[pk]
|
|
end
|
|
local function describeBattlePet(pid, spid)
|
|
if special[pid] then return special[pid]() end
|
|
local rpid, _, cn, lvl, _, _, _, _, n, tex = GetBattlePetInfo(pid, spid)
|
|
if not rpid then return BPET_ATYPE_NAME, "?" end
|
|
if (cn or n) and ((lvl or 0) > 1) then cn = "[" .. lvl .. "] " .. (cn or n) end
|
|
return BPET_ATYPE_NAME, cn or n or ("#" .. tostring(rpid)), tex, nil, SetBattlePetByID, rpid
|
|
end
|
|
AB:RegisterActionType("battlepet", createBattlePet, describeBattlePet, 2)
|
|
RW:SetCommandHint(SLASH_SUMMON_BATTLE_PET1, 60, function(_, _, clause)
|
|
if clause and clause ~= "" then
|
|
local _, petID = C_PetJournal.FindPetIDByName(clause:trim())
|
|
if petID then
|
|
return true, battlepetHint(petID)
|
|
end
|
|
end
|
|
end)
|
|
end)
|
|
securecall(function() -- equipmentset: equipment sets by name
|
|
if not (MODERN or CF_WRATH) then
|
|
return
|
|
end
|
|
local setMap = {}
|
|
local function resolveIcon(fid)
|
|
return type(fid) == "number" and fid or ("Interface/Icons/" .. (fid or "INV_Misc_QuestionMark"))
|
|
end
|
|
local function equipmentsetHint(name)
|
|
local esid = name and C_EquipmentSet.GetEquipmentSetID(name) or -1
|
|
local _, icon, _, active, total, equipped, available = C_EquipmentSet.GetEquipmentSetInfo(esid)
|
|
if icon then
|
|
return total == equipped or (available > 0), active and 1 or 0, resolveIcon(icon), name, nil, 0, 0, callMethod.SetEquipmentSet, esid
|
|
end
|
|
end
|
|
local function wrapCommandHint(...)
|
|
local _, state = ...
|
|
if state then
|
|
return true, ...
|
|
end
|
|
end
|
|
function EV.EQUIPMENT_SETS_CHANGED()
|
|
AB:NotifyObservers("equipmentset")
|
|
end
|
|
local function equipSetActionSpec(name)
|
|
return "attribute", "type","equipmentset", "equipmentset",name
|
|
end
|
|
local function equipSetActionSpec_SLASH(name)
|
|
-- [3.4.2] [4.4.1] /equipset exists but the SABT action type does not
|
|
return "macrotext", SLASH_EQUIP_SET1 .. " " .. name
|
|
end
|
|
equipSetActionSpec, equipSetActionSpec_SLASH = MODERN and equipSetActionSpec or equipSetActionSpec_SLASH, nil
|
|
local function createEquipSet(name)
|
|
local sid = type(name) == "string" and C_EquipmentSet.GetEquipmentSetID(name)
|
|
if not sid then return end
|
|
if not setMap[name] and (MODERN or name:match("^[^%[;%]]*$")) then
|
|
setMap[name] = AB:CreateActionSlot(equipmentsetHint, name, equipSetActionSpec(name))
|
|
end
|
|
return setMap[name]
|
|
end
|
|
local function describeEquipSet(name)
|
|
local esid = name and C_EquipmentSet.GetEquipmentSetID(name) or -1
|
|
local _, ico = C_EquipmentSet.GetEquipmentSetInfo(esid)
|
|
return L"Equipment Set", name, ico and resolveIcon(ico) or "Interface/Icons/INV_Misc_QuestionMark", nil, callMethod.SetEquipmentSet, esid
|
|
end
|
|
AB:RegisterActionType("equipmentset", createEquipSet, describeEquipSet, 1)
|
|
RW:SetCommandHint(SLASH_EQUIP_SET1, 80, function(_, _, clause)
|
|
if clause and clause ~= "" then
|
|
return wrapCommandHint(equipmentsetHint(clause))
|
|
end
|
|
end)
|
|
end)
|
|
securecall(function() -- raidmark
|
|
local map, waitingToClearSelf = {}
|
|
local function CanChangeRaidTargets(unit)
|
|
return not not ((not IsInRaid() or UnitIsGroupLeader("player") or UnitIsGroupAssistant("player")) and not (unit and UnitIsPlayer(unit) and UnitIsEnemy("player", unit)))
|
|
end
|
|
local function setRaidTarget(id)
|
|
SetRaidTarget("target", GetRaidTargetIndex("target") == id and 0 or id)
|
|
end
|
|
local function raidmarkHint(i, _, target)
|
|
local target = target or "target"
|
|
return CanChangeRaidTargets(target), GetRaidTargetIndex(target) == i and 1 or 0, "Interface/TargetingFrame/UI-RaidTargetingIcon_" .. i, _G["RAID_TARGET_" .. i], 0, 0, 0
|
|
end
|
|
local function removeHint()
|
|
return CanChangeRaidTargets(), 0, "Interface/Icons/INV_Gauntlets_02", REMOVE_WORLD_MARKERS, 0, 0, 0
|
|
end
|
|
local function FinishClearRaidTargets()
|
|
if waitingToClearSelf and GetRaidTargetIndex("player") == 1 then
|
|
waitingToClearSelf = nil
|
|
if CanChangeRaidTargets() then
|
|
SetRaidTarget("player", 0)
|
|
end
|
|
return "remove"
|
|
end
|
|
end
|
|
map[0] = AB:CreateActionSlot(removeHint, nil, "func", function()
|
|
if not CanChangeRaidTargets() then return end
|
|
local pt = GetRaidTargetIndex("player")
|
|
for i=8, 0, -1 do
|
|
SetRaidTarget("player", i == pt and 1 or i == 1 and pt or i)
|
|
end
|
|
if not (pt or waitingToClearSelf) and IsInGroup() then
|
|
waitingToClearSelf, EV.RAID_TARGET_UPDATE = 1, FinishClearRaidTargets
|
|
end
|
|
end)
|
|
for i=1,8 do
|
|
map[i] = AB:CreateActionSlot(raidmarkHint, i, "func", setRaidTarget, i)
|
|
end
|
|
local function createRaidMark(id)
|
|
return map[id]
|
|
end
|
|
local function describeRaidMark(id)
|
|
if id == 0 then return L"Raid Marker", REMOVE_WORLD_MARKERS, "Interface/Icons/INV_Gauntlets_02" end
|
|
return L"Raid Marker", _G["RAID_TARGET_" .. id], "Interface/TargetingFrame/UI-RaidTargetingIcon_" .. id
|
|
end
|
|
AB:RegisterActionType("raidmark", createRaidMark, describeRaidMark, 1)
|
|
RW:ImportSlashCmd("TARGET_MARKER", true, false, 40, function(_, _, clause, target)
|
|
clause = tonumber(clause)
|
|
if clause == 0 then
|
|
return true, removeHint()
|
|
elseif clause then
|
|
return true, raidmarkHint(clause, nil, target)
|
|
end
|
|
end)
|
|
end)
|
|
securecall(function() -- worldmarker
|
|
if not (MODERN or CF_CATA) then
|
|
return
|
|
end
|
|
local NUM_WORLD_MARKERS = CF_CATA and NUM_WORLD_RAID_MARKERS_CATA == 5 and 5 or 8
|
|
local map, icons = {}, {[0]="Interface/Icons/INV_Misc_PunchCards_White",
|
|
"Interface/Icons/INV_Misc_QirajiCrystal_04","Interface/Icons/INV_Misc_QirajiCrystal_03",
|
|
"Interface/Icons/INV_Misc_QirajiCrystal_05","Interface/Icons/INV_Misc_QirajiCrystal_02",
|
|
"Interface/Icons/INV_Misc_QirajiCrystal_01",
|
|
MODERN and "Interface/Icons/INV_Elemental_Primal_Fire" or 'Interface/TargetingFrame/UI-RaidTargetingIcon_2',
|
|
MODERN and "Interface/Icons/INV_jewelcrafting_taladiterecrystal" or 'Interface/TargetingFrame/UI-RaidTargetingIcon_5',
|
|
MODERN and "Interface/Icons/INV_jewelcrafting_taladitecrystal" or 'Interface/TargetingFrame/UI-RaidTargetingIcon_8'
|
|
}
|
|
local function Tooltip_SetWorldMark(tip, i)
|
|
tip:SetText(i == 0 and REMOVE_WORLD_MARKERS or _G["WORLD_MARKER" .. i])
|
|
if not IsInGroup() then
|
|
tip:AddLine(ERR_NOT_IN_GROUP, 0.95, 0.15, 0, 1)
|
|
elseif IsInRaid() and not (UnitIsGroupLeader("player") or UnitIsGroupAssistant("player") or IsEveryoneAssistant()) then
|
|
tip:AddLine(ERR_NOT_LEADER, 0.95, 0.15, 0, 1)
|
|
end
|
|
end
|
|
local function worldmarkHint(i)
|
|
local canMark = not not (IsInGroup() and (not IsInRaid() or UnitIsGroupLeader("player") or UnitIsGroupAssistant("player") or IsEveryoneAssistant()))
|
|
return canMark, i > 0 and IsRaidMarkerActive(i) and 1 or 0, icons[i], i == 0 and REMOVE_WORLD_MARKERS or _G["WORLD_MARKER" .. i], 0, 0, 0, Tooltip_SetWorldMark, i
|
|
end
|
|
for i=1, NUM_WORLD_MARKERS do
|
|
map[i] = AB:CreateActionSlot(worldmarkHint, i, "attribute", "type","worldmarker", "action","toggle", "marker",i)
|
|
end
|
|
map[0] = AB:CreateActionSlot(worldmarkHint, 0, "macrotext", SLASH_CLEAR_WORLD_MARKER1 .. " " .. ALL)
|
|
local function createWorldmark(id)
|
|
return map[id]
|
|
end
|
|
local function describeWorldmark(id)
|
|
if map[id] == nil then return L"Raid World Marker", "?" end
|
|
return L"Raid World Marker", id == 0 and REMOVE_WORLD_MARKERS or _G["WORLD_MARKER" .. id], icons[id]
|
|
end
|
|
AB:RegisterActionType("worldmark", createWorldmark, describeWorldmark, 1)
|
|
RW:SetCommandHint(SLASH_WORLD_MARKER1, 40, function(_, _, clause)
|
|
clause = tonumber(clause)
|
|
if map[clause] and clause > 0 then
|
|
return true, worldmarkHint(clause)
|
|
end
|
|
end)
|
|
end)
|
|
securecall(function() -- extrabutton
|
|
local slot = (MODERN or CF_CATA) and GetExtraBarIndex and (GetExtraBarIndex()*12 - 11)
|
|
local function extrabuttonHint()
|
|
if not HasExtraActionBar() then
|
|
return false, 0, "Interface/Icons/temp", "", 0, 0, 0
|
|
end
|
|
local now, at, aid = GetTime(), GetActionInfo(slot)
|
|
local inRange, usable, nomana, hasRange = NormalizeInRange[IsActionInRange(slot)], IsUsableAction(slot)
|
|
inRange, hasRange = inRange ~= 0, inRange ~= nil
|
|
local cdLeft, cdLength, enabled = toCooldown(now, GetActionCooldown(slot))
|
|
local count, charges, maxCharges, ccdStart, ccdLength = GetActionCount(slot), GetActionCharges(slot)
|
|
local state = ((IsCurrentAction(slot) or enabled == 0) and 1 or 0) +
|
|
(at == "spell" and IsSpellOverlayed(aid) and 2 or 0) +
|
|
(nomana and 8 or 0) + (inRange and 0 or 16) + (charges and charges > 0 and 64 or 0) + (hasRange and 512 or 0) + (usable and 0 or 1024) + (enabled == 0 and 2048 or 0)
|
|
if charges and maxCharges and charges < maxCharges and cdLeft == 0 then
|
|
cdLeft, cdLength = toCooldown(now, ccdStart, ccdLength, 1)
|
|
end
|
|
usable = not not (usable and inRange and (cdLeft == 0 or enabled == 0 or charges > 0))
|
|
return usable, state, GetActionTexture(slot), GetActionText(slot) or (at == "spell" and GetSpellInfo(aid)), count <= 1 and charges or count, cdLeft, cdLength, callMethod.SetAction, slot
|
|
end
|
|
local aid = slot and AB:CreateActionSlot(extrabuttonHint, nil, "conditional", "[extrabar]", "attribute", "type","action", "action",slot)
|
|
local aid2 = slot and AB:CreateActionSlot(extrabuttonHint, nil, "attribute", "type","action", "action",slot)
|
|
local function createExtraButton(id, flags)
|
|
local forceShow = flags == 1
|
|
return id == 1 and (forceShow and aid2 or aid) or nil
|
|
end
|
|
local function describeExtraButton(_id)
|
|
local name, tex = L"Extra Action Button", "Interface/Icons/Spell_Shadow_Teleport"
|
|
if slot and HasExtraActionBar() then
|
|
local at, aid = GetActionInfo(slot)
|
|
name, tex = GetActionText(slot) or (at == "spell" and GetSpellInfo(aid)) or name, GetActionTexture(slot) or tex
|
|
end
|
|
return L"Extra Action Button", name, tex
|
|
end
|
|
AB:RegisterActionType("extrabutton", createExtraButton, describeExtraButton, 2)
|
|
if slot then
|
|
RW:SetClickHint("ExtraActionButton1", 95, function()
|
|
if HasExtraActionBar() then
|
|
return true, extrabuttonHint()
|
|
end
|
|
end)
|
|
end
|
|
end)
|
|
securecall(function() -- zoneability auto-collection
|
|
local skipZoneAbilities = {
|
|
[436521]=1, [436524]=1, -- Pandaria remix: Extract Gem + Unraveling Sands
|
|
}
|
|
local col, tpos, colId = MODERN and {__embed=true} or nil, {}
|
|
local function createZoneAbility(id)
|
|
return id == 0 and colId or nil
|
|
end
|
|
local function describeZoneAbility(id)
|
|
if id == 0 then
|
|
return L"Zone Abilities", L"Zone Abilities", MODERN and [[Interface\Icons\Icon_TreasureMap]] or "Interface/Icons/Spell_Shadow_Teleport", nil, nil, nil, "collection"
|
|
end
|
|
end
|
|
local function onZoneCollectionOpen(_, event, cid)
|
|
if event ~= "internal.collection.preopen" or cid ~= colId then return end
|
|
local changed, ni, za = nil, 1, C_ZoneAbility and C_ZoneAbility.GetActiveAbilities()
|
|
for i=1, za and #za or 0 do
|
|
local asid = za[i].spellID
|
|
if asid and not (skipZoneAbilities[asid] or IsPassiveSpell(asid)) then
|
|
local tk, aid = "INTZAs" .. asid, AB:GetActionSlot("spell", asid)
|
|
if aid and not ((tpos[tk] or ni) < ni and col[tpos[tk]] == tk) then
|
|
changed = changed or col[ni] ~= tk or col[tk] ~= aid
|
|
col[ni], col[tk], tpos[tk], ni = tk, aid, ni, ni + 1
|
|
end
|
|
end
|
|
end
|
|
for i=ni, #col do
|
|
local tk = col[i]
|
|
changed, col[i], col[tpos[tk] == i and tk or i] = 1, nil, nil
|
|
end
|
|
if changed then
|
|
AB:UpdateActionSlot(colId, col)
|
|
end
|
|
end
|
|
colId = col and AB:CreateActionSlot(nil,nil, "collection",col)
|
|
AB:RegisterActionType("zoneability", createZoneAbility, describeZoneAbility, 1)
|
|
if col then
|
|
AB:AddObserver("internal.collection.preopen", onZoneCollectionOpen)
|
|
function EV:PLAYER_REGEN_DISABLED()
|
|
onZoneCollectionOpen(nil, "internal.collection.preopen", colId)
|
|
end
|
|
end
|
|
end)
|
|
securecall(function() -- petspell: spell ID
|
|
local actionInfo = {
|
|
stay={"Interface\\Icons\\Spell_Nature_TimeStop", "PET_ACTION_WAIT"},
|
|
move={"Interface\\Icons\\Ability_Hunter_Pet_Goto", "PET_ACTION_MOVE_TO", 1},
|
|
follow={"Interface\\Icons\\Ability_Tracking", "PET_ACTION_FOLLOW"},
|
|
attack={"Interface\\Icons\\Ability_GhoulFrenzy", "PET_ACTION_ATTACK"},
|
|
defend={"Interface\\Icons\\Ability_Defend", "PET_MODE_DEFENSIVE"},
|
|
assist={"Interface\\Icons\\Ability_Hunter_Pet_Assist", "PET_MODE_ASSIST"},
|
|
passive={"Interface\\Icons\\Ability_Seal", "PET_MODE_PASSIVE"},
|
|
dismiss={CLASS == "WARLOCK" and "Interface\\Icons\\spell_shadow_sacrificialshield" or "Interface\\Icons\\spell_nature_spiritwolf"}
|
|
}
|
|
local actionID = {}
|
|
local petTip = MODERN and function(self, slot)
|
|
return self:SetSpellBookItem(slot, BOOKTYPE_PET)
|
|
end or function(self, slot)
|
|
return self:SetPetAction(slot)
|
|
end
|
|
local petCommandFeedback = MODERN and function(info)
|
|
local ico, name, slot = info[1], info[2], info[3]
|
|
if GetSpellBookItemTexture(slot or 0, "pet") ~= ico then
|
|
slot = nil
|
|
for i=1,HasPetSpells() or 0 do
|
|
if GetSpellBookItemTexture(i, "pet") == ico and GetSpellBookItemInfo(i, "pet") == "PETACTION" then
|
|
info[3], slot = i, i
|
|
break
|
|
end
|
|
end
|
|
end
|
|
return not not slot, slot and IsSelectedSpellBookItem(slot, "pet") and 1 or 0, ico, _G[name] or (slot and GetSpellBookItemName(slot, "pet")) or "", 0, 0, 0, slot and petTip or nil, slot
|
|
end or function(info)
|
|
local ico, name, slot = info[1], info[2], info[3]
|
|
local sname, _icokey, _isToken, isActive, _autoCastAllowed, _autoCastEnabled, _spellID, hasRange, inRange = GetPetActionInfo(slot or 0)
|
|
if sname ~= name then
|
|
info[3], slot = nil
|
|
for i=1,10 do
|
|
sname, _icokey, _isToken, isActive, _autoCastAllowed, _autoCastEnabled, _spellID, hasRange, inRange = GetPetActionInfo(i)
|
|
if sname == name then
|
|
info[3], slot = i, i
|
|
break
|
|
end
|
|
end
|
|
end
|
|
local flags = slot and (isActive and 1 or 0 + (hasRange and not inRange and 16 or 0) + (hasRange and 512 or 0)) or 0
|
|
return not not slot and (inRange or not hasRange), flags, ico, _G[name] or name, 0, 0, 0, slot and petTip or nil, slot
|
|
end
|
|
local function petHint(sid)
|
|
local info = actionInfo[sid]
|
|
if sid == "dismiss" then
|
|
if CLASS == "HUNTER" and PetCanBeAbandoned() then
|
|
return spellFeedback(2641, nil, 2641)
|
|
end
|
|
return HasFullControl() and UnitExists("pet") and PetCanBeDismissed(), 0, info[1], PET_ACTION_DISMISS
|
|
elseif info then
|
|
return petCommandFeedback(info)
|
|
elseif sid then
|
|
return spellFeedback(sid, nil, sid)
|
|
end
|
|
end
|
|
local function createPetAction(id)
|
|
if type(id) == "number" and id > 0 and not actionID[id] and not IsPassiveSpell(id) and GetSpellInfo(id) then
|
|
actionID[id] = AB:CreateActionSlot(petHint, id, "conditional","[petcontrol,known:" .. GetSpellInfo(id) .. "];hide", "attribute", "type","spell", "spell",id)
|
|
end
|
|
return actionID[id]
|
|
end
|
|
local function describePetAction(id)
|
|
if type(id) == "number" then
|
|
local name, _, icon = GetSpellInfo(id)
|
|
return L"Pet Ability", name, icon, nil, callMethod.SetSpellByID, id
|
|
elseif actionID[id] then
|
|
local st, _, _, icon, name, _, _, _, tipf, tipa = nil, petHint(id)
|
|
_, st = GetSpellBookItemName(tipa or 0, "pet")
|
|
return MODERN and st or L"Pet Ability", name, icon, nil, tipf, tipa
|
|
end
|
|
end
|
|
AB:RegisterActionType("petspell", createPetAction, describePetAction, 1)
|
|
do
|
|
local cnd, macroMap = "[petcontrol,@pet,help,novehicleui]", {}
|
|
local function check(...)
|
|
if ... ~= nil then
|
|
return true, ...
|
|
end
|
|
end
|
|
local function petmacroHint(slash, _, clause, _target)
|
|
local aid = clause and macroMap[slash]
|
|
if aid then
|
|
return check(petHint(aid))
|
|
end
|
|
end
|
|
local function addPetCommand(cmd, key)
|
|
actionID[key] = AB:CreateActionSlot(petHint, key, "conditional", cnd, "macrotext", cmd)
|
|
RW:SetCommandHint(cmd, 75, petmacroHint)
|
|
macroMap[cmd:lower()] = key
|
|
end
|
|
addPetCommand(SLASH_PET_STAY1, "stay")
|
|
addPetCommand(SLASH_PET_FOLLOW1, "follow")
|
|
addPetCommand(SLASH_PET_ATTACK1, "attack")
|
|
addPetCommand(SLASH_PET_DEFENSIVE1, "defend")
|
|
addPetCommand(SLASH_PET_PASSIVE1, "passive")
|
|
actionID.dismiss = AB:CreateActionSlot(petHint, "dismiss", "conditional", cnd, "macrotext", SLASH_PET_DISMISS1)
|
|
if MODERN then
|
|
addPetCommand(SLASH_PET_MOVE_TO1, "move")
|
|
addPetCommand(SLASH_PET_ASSIST1, "assist")
|
|
else
|
|
actionInfo.assist = {"Interface/Icons/Ability_Racial_BloodRage", "PET_MODE_AGGRESSIVE"}
|
|
addPetCommand(SLASH_PET_AGGRESSIVE1, "assist")
|
|
end
|
|
end
|
|
end)
|
|
securecall(function() -- toy: item ID, flags[FORCE_SHOW]
|
|
if not (MODERN or CF_WRATH) then
|
|
return
|
|
end
|
|
local map, lastUsability, uq, whinedAboutGIIR = {}, {}, {}
|
|
local OVERRIDE_TOY_ACQUIRED, IGNORE_TOY_USABILITY = {}, {
|
|
[129149]=1, [129279]=1, [129367]=1, [130157]="[in:broken isles]", [130158]=1, [130170]=1,
|
|
[130191]=1, [130199]=1, [130232]=1, [131812]=1, [131814]=1, [140325]=1, [147708]=1,
|
|
[165021]=1,
|
|
[153039]=1, [119421]=1, [128462]="[alliance]", [128471]="[horde]", [95589]="[alliance]", [95590]="[horde]",
|
|
[89222]=1, [63141]="[alliance]", [64997]="[horde]", [66888]=1, [89869]=1, [90175]=1,
|
|
[103685]=1, [115468]="[horde]", [115472]="[alliance]", [119160]="[horde]", [119182]="[alliance]",
|
|
[122283]=1, [142531]=1, [142532]=1, [163211]=1,
|
|
[85500]="[fish5]",
|
|
[182773]="[coven:necro][acoven80:necro]", [184353]="[coven:kyrian][acoven80:kyrian]", [180290]="[coven:fae][acoven80:fae]", [183716]="[coven:venthyr][acoven80:venthyr]", [190237] = 1,
|
|
}
|
|
local function playerHasToy(id)
|
|
local f = OVERRIDE_TOY_ACQUIRED[id]
|
|
if f then
|
|
return f == true or f(id)
|
|
end
|
|
return f == nil and PlayerHasToy(id)
|
|
end
|
|
function toyHint(iid, _modState, target)
|
|
local state, count, hasUsableCharge, now = 0, 0, false, GetTime()
|
|
local _, name, icon = C_ToyBox.GetToyInfo(iid)
|
|
local cdLeft, cdLength, enabled = toCooldown(now, C_Container.GetItemCooldown(iid))
|
|
local ignUse, usable = IGNORE_TOY_USABILITY[iid]
|
|
local _, sid = C_Item.GetItemSpell(iid)
|
|
if not playerHasToy(iid) then
|
|
usable = false
|
|
elseif ignUse == nil then
|
|
usable = C_ToyBox.IsToyUsable(iid) ~= false
|
|
else
|
|
usable = ignUse == 1 or (not not KR:EvaluateCmdOptions(ignUse))
|
|
end
|
|
target = target or "target"
|
|
local canRange = not (InCombatLockdown() and (UnitIsFriend("player", target) or not UnitExists(target))) or nil
|
|
local inRange, hasRange = canRange and NormalizeInRange[C_Item.IsItemInRange(iid, target)]
|
|
inRange, hasRange = inRange ~= 0, inRange ~= nil
|
|
state = state + (inRange and 0 or 16) + (hasRange and 512 or 0) + (enabled == 0 and 2048 or 0)
|
|
if sid then
|
|
local charges, maxCharges, ccdStart, ccdLength = GetSpellCharges(sid)
|
|
-- BUG[11.0.2/2409]: GetSpellCharges[The Innkeeper's Daughter] returns the unified hearthstone state,
|
|
-- but the *item cooldown* is actually enforced (longer + no second charge for Humans).
|
|
count = charges and charges > 0 and cdLength == 0 and charges or count
|
|
if charges and maxCharges and charges < maxCharges and cdLength == 0 then
|
|
hasUsableCharge, cdLeft, cdLength = charges > 0, toCooldown(now, ccdStart, ccdLength, 1)
|
|
state = state + (hasUsableCharge and 64 or 0)
|
|
end
|
|
end
|
|
icon = icon or C_Item.GetItemIconByID(iid)
|
|
usable = name and (hasUsableCharge or cdLeft == 0) and inRange and usable or false
|
|
return usable, state, icon, name, count, cdLeft, cdLength, callMethod.SetToyByItemID, iid
|
|
end
|
|
function EV:GET_ITEM_INFO_RECEIVED(iid, ok)
|
|
if not (ok and uq[iid]) then
|
|
return
|
|
end
|
|
local iu = C_ToyBox.IsToyUsable(iid)
|
|
if iu ~= nil then
|
|
lastUsability[iid], uq[iid] = iu, nil
|
|
elseif not whinedAboutGIIR then
|
|
whinedAboutGIIR = true
|
|
error("Curse your sudden but inevitable betrayal [" .. iid .. "]")
|
|
end
|
|
end
|
|
local function wrapCondition(cnd, ...)
|
|
if (cnd or 1) == 1 then
|
|
return ...
|
|
else
|
|
return "conditional", cnd, ...
|
|
end
|
|
end
|
|
local function createToy(id, flags)
|
|
local forceShow = flags == 1
|
|
local mid, ignUse = map[id], IGNORE_TOY_USABILITY[id]
|
|
if not (mid or ignUse or type(id) == "number") or not (forceShow or playerHasToy(id)) then
|
|
return
|
|
end
|
|
local isUsable = ignUse or C_ToyBox.IsToyUsable(id)
|
|
if isUsable == nil then
|
|
isUsable, uq[id] = lastUsability[id], 1
|
|
C_Item.GetItemInfo(id)
|
|
elseif not ignUse then
|
|
lastUsability[id] = isUsable
|
|
end
|
|
if not (forceShow or isUsable) then
|
|
mid = nil
|
|
elseif mid == nil then
|
|
mid = AB:CreateActionSlot(toyHint, id, wrapCondition(ignUse, "attribute", "type","toy", "toy",id))
|
|
map[id] = mid
|
|
end
|
|
return mid
|
|
end
|
|
local function describeToy(id)
|
|
if type(id) ~= "number" then return end
|
|
local _, name, tex = C_ToyBox.GetToyInfo(id)
|
|
return L"Toy", name, tex or C_Item.GetItemIconByID(id), nil, callMethod.SetToyByItemID, id
|
|
end
|
|
AB:RegisterActionType("toy", createToy, describeToy, 2)
|
|
RW:SetCommandHint(SLASH_USE_TOY1, 60, function(_, _, clause, target)
|
|
if clause and clause ~= "" then
|
|
local iid = C_Item.GetItemIDForItemInfo(clause)
|
|
if iid then
|
|
return true, toyHint(iid, nil, target)
|
|
end
|
|
end
|
|
end)
|
|
function AB.HUM:SetPlayerHasToyOverride(id, filter)
|
|
local tf = type(filter)
|
|
if not (type(id) == "number" and (tf == "nil" or tf == "boolean" or tf == "function")) then
|
|
return error('SetPlayerHasToyOverride: invalid arguments', 2)
|
|
end
|
|
OVERRIDE_TOY_ACQUIRED[id] = filter
|
|
end
|
|
end)
|
|
securecall(function() -- disenchant: iid
|
|
local map, DISENCHANT_SID = {}, 13262
|
|
local DISENCHANT_SN = GetSpellInfo(DISENCHANT_SID)
|
|
local ICON_PREFIX = "|TInterface/Buttons/UI-GroupLoot-DE-Up:0:0|t "
|
|
local SLASH_SPELL_TARGET_ITEM1 = '/spelltargetitem' do
|
|
local wn = newWidgetName("AB:I!")
|
|
local w = CreateFrame("Button", wn, nil, "SecureActionButtonTemplate")
|
|
w:SetAttribute("pressAndHoldAction", 1)
|
|
w:Hide()
|
|
SecureHandlerWrapScript(w, "OnClick", w, [[return nil, 'post']], [[self:SetAttribute("target-item", nil)]])
|
|
local er = {u="\\117", ["{"]="\\123", ["}"]="\\125"}
|
|
local function escape(s)
|
|
return ("%q"):format(s):gsub('[{u}]', er):sub(2,-2)
|
|
end
|
|
w:SetAttribute("RunSlashCmd", ([[-- AB_SPELLTARGET_ITEM_RUN
|
|
local cmd, v = ...
|
|
if cmd == "%s" and v then
|
|
self:SetAttribute("target-item", v)
|
|
return "%s"
|
|
end
|
|
]]):format(escape(SLASH_SPELL_TARGET_ITEM1), escape(SLASH_CLICK1 .. " " .. wn .. " 1")))
|
|
RW:RegisterCommand(SLASH_SPELL_TARGET_ITEM1, true, false, w)
|
|
end
|
|
local function disenchantTip(self, iid)
|
|
self:SetItemByID(iid)
|
|
self:AddLine(ICON_PREFIX .. DISENCHANT_SN, 0, 1, 0)
|
|
self:Show()
|
|
end
|
|
local function disenchantHint(ident)
|
|
local count = C_Item.GetItemCount(ident, false, false, false)
|
|
local usable = IsPlayerSpell(DISENCHANT_SID) and count > 0
|
|
local name = C_Item.GetItemNameByID(ident)
|
|
local qual = MODERN and ident and (C_TradeSkillUI.GetItemReagentQualityByItemInfo(ident) or C_TradeSkillUI.GetItemCraftedQualityByItemInfo(ident))
|
|
qual = qual and qual > 0 and qual < 8 and (qual * 16384) or 0
|
|
local cdLeft, cdLength, enabled = toCooldown(GetTime(), GetSpellCooldown(DISENCHANT_SID))
|
|
local state = (C_Item.IsCurrentItem(ident) and 1 or 0) + (usable and 0 or 1024) + qual + 131072 + (enabled == 0 and 2048 or 0)
|
|
local disName = ICON_PREFIX .. (name or ("item:" .. ident))
|
|
return not not (usable and cdLeft == 0), state, C_Item.GetItemIconByID(ident), disName, count,
|
|
cdLeft or 0, cdLength or 0, disenchantTip, ident
|
|
end
|
|
local function createDisenchant(iid)
|
|
if not (IsPlayerSpell(13262) and type(iid) == "number" and C_Item.GetItemCount(iid) > 0) then
|
|
return
|
|
end
|
|
local mid = map[iid]
|
|
if not mid then
|
|
local macrotext = ("%s\n%s [@none] spell:%d\n%s item:%d\n%1$s"):format(SLASH_STOPSPELLTARGET1, SLASH_CAST1, DISENCHANT_SID, SLASH_SPELL_TARGET_ITEM1, iid)
|
|
mid = AB:CreateActionSlot(disenchantHint, iid, "retext", macrotext)
|
|
map[iid] = mid
|
|
end
|
|
return mid
|
|
end
|
|
local function describeDisenchant(iid)
|
|
if type(iid) ~= "number" then return end
|
|
local icon, name = C_Item.GetItemIconByID(iid), C_Item.GetItemNameByID(iid)
|
|
return DISENCHANT_SN, name or ("item:" .. iid), icon, nil, disenchantTip, iid
|
|
end
|
|
AB:RegisterActionType("disenchant", createDisenchant, describeDisenchant, 1)
|
|
end)
|
|
securecall(function() -- /ping
|
|
if not MODERN then
|
|
return
|
|
end
|
|
local TOKENS, INFO = {}, {
|
|
{PING, "Ping_Marker_Icon_NonThreat"},
|
|
{PING, "Ping_Marker_Icon_Threat"},
|
|
assist={PING_TYPE_ASSIST, "Ping_Wheel_Icon_Assist"},
|
|
attack={PING_TYPE_ATTACK, "Ping_Wheel_Icon_Attack"},
|
|
onmyway={PING_TYPE_ON_MY_WAY, "Ping_Marker_Icon_OnMyWay"},
|
|
warning={PING_TYPE_WARNING, "Ping_Wheel_Icon_Warning"},
|
|
}
|
|
for k,v in pairs(INFO) do
|
|
if type(k) == "string" then
|
|
TOKENS[v[1]:lower()] = k
|
|
end
|
|
end
|
|
RW:SetCommandHint(SLASH_PING1, 40, function(_, _, clause, target)
|
|
if clause then
|
|
clause = lowered[clause]
|
|
local ci = INFO[TOKENS[clause] or clause] or INFO[C_Ping.GetContextualPingTypeForUnit(UnitGUID(target ~= "cursor" and target or "mouseover") or nil) == 4 and 2 or 1]
|
|
local perm = (not IsInRaid() or UnitIsGroupLeader("player") or UnitIsGroupAssistant("player") or not C_PartyInfo.GetRestrictPings())
|
|
local cdInfo, nowMs = C_Ping.GetCooldownInfo(), GetTime()*1000
|
|
local cd = cdInfo.endTimeMs > nowMs and (cdInfo.endTimeMs-nowMs)/1000 or 0
|
|
return true, perm and cd == 0 or false, 262144, ci[2], ci[1], 0, cd, cd > 0 and (cdInfo.endTimeMs-cdInfo.startTimeMs)/1000 or 0
|
|
end
|
|
end)
|
|
end)
|
|
securecall(function() -- uipanel: token
|
|
local CLICK, pyCLICK, widgetClickCommand, closeButton = SLASH_CLICK1 .. " " do
|
|
local pyName = newWidgetName("AB:PY!")
|
|
local py = CreateFrame("Button", pyName, nil, "SecureActionButtonTemplate")
|
|
py:SetAttribute("type", "click")
|
|
py:SetAttribute("pressAndHoldAction", 1)
|
|
pyCLICK = CLICK .. pyName .. " "
|
|
function widgetClickCommand(k, w)
|
|
if w == nil then return "" end
|
|
local tn = type(w) == "string" and w or w.GetName and w:GetName()
|
|
if tn == nil then
|
|
local w1 = py:GetAttribute("clickbutton-" .. k)
|
|
k = (w1 and w1 ~= w) and k .. "2" or k
|
|
py:SetAttribute("clickbutton-" .. k, w)
|
|
tn = pyName .. " " .. k
|
|
end
|
|
return CLICK .. tn .. " 1\n"
|
|
end
|
|
function closeButton(p, reg)
|
|
local r = CreateFrame("Button", nil, p, "UIPanelCloseButton")
|
|
r:Hide()
|
|
return r, reg and widgetClickCommand(reg, r)
|
|
end
|
|
end
|
|
local ShowVaultTip
|
|
if MODERN then
|
|
local unlockedRewards, needRefresh
|
|
function EV:WEEKLY_REWARDS_UPDATE()
|
|
unlockedRewards = nil
|
|
end
|
|
local function genRewardPreview()
|
|
needRefresh = nil
|
|
if C_WeeklyRewards.HasGeneratedRewards() then
|
|
local a, ni = C_WeeklyRewards.GetActivities(), 1
|
|
local getRewardLink, getItemLevel = C_WeeklyRewards.GetItemHyperlink, C_Item.GetDetailedItemLevelInfo
|
|
for i=1, a and #a or 0 do
|
|
local ai = a[i]
|
|
if ai and ai.rewards and ai.rewards[1] then
|
|
local ilink, q = getRewardLink(ai.rewards[1].itemDBID), ai.rewards[1].quantity
|
|
local ilvl = (ilink and getItemLevel(ilink) or -1)
|
|
local suf = (q or 1) > 1 and " (x" .. q .. ")" or (" (" .. ITEM_LEVEL_ABBR .. " " .. ilvl .. ")")
|
|
a[ni], ni, needRefresh = ilink .. suf, ni + 1, needRefresh or ilvl == -1
|
|
end
|
|
end
|
|
unlockedRewards = table.concat(a, "\n", 1, ni-1) or ""
|
|
return
|
|
end
|
|
local getExampleReward, getItemLevel = C_WeeklyRewards.GetExampleRewardItemHyperlinks, C_Item.GetDetailedItemLevelInfo
|
|
local a, ni = not C_WeeklyRewards.HasAvailableRewards() and C_WeeklyRewards.GetActivities(), 1
|
|
for i=1,a and #a or 0 do
|
|
local ai = a[i]
|
|
if ai and ai.progress >= ai.threshold then
|
|
local ilink = getExampleReward(ai.id)
|
|
local ilvl = ilink and getItemLevel(ilink) or -1
|
|
a[ni], ni, needRefresh = ilvl, ni + 1, needRefresh or ilvl == -1
|
|
end
|
|
end
|
|
unlockedRewards = ""
|
|
if ni > 1 then
|
|
for i=#a, ni, -1 do
|
|
a[i] = nil
|
|
end
|
|
table.sort(a)
|
|
local ec = ITEM_QUALITY_COLORS[4].hex
|
|
local si, j, ilvl, sj = ni, ni-1
|
|
while j > 0 do
|
|
ilvl, sj = a[j], j
|
|
repeat j = j - 1 until a[j] ~= ilvl
|
|
a[ni], ni = ec .. ITEM_LEVEL:format(ilvl) .. (sj > j+1 and "|r (x" .. (sj-j) .. ")" or "|r"), ni + 1
|
|
end
|
|
unlockedRewards = table.concat(a, "\n", si, ni-1)
|
|
end
|
|
end
|
|
function ShowVaultTip(GameTooltip)
|
|
local hc = HIGHLIGHT_FONT_COLOR
|
|
GameTooltip:SetText(DELVES_GREAT_VAULT_LABEL)
|
|
if unlockedRewards == nil or needRefresh then
|
|
genRewardPreview()
|
|
end
|
|
if (unlockedRewards or "") ~= "" then
|
|
GameTooltip:AddLine(unlockedRewards, hc.r, hc.g, hc.b)
|
|
elseif C_WeeklyRewards.HasAvailableRewards() then
|
|
GameTooltip:AddLine(GREAT_VAULT_REWARDS_WAITING, 0.1, 0.9, 0.1, 1)
|
|
else
|
|
GameTooltip:AddLine(WEEKLY_REWARDS_ADD_ITEMS, 0.75, 0.75, 0.75, 1)
|
|
end
|
|
if InCombatLockdown() and not WeeklyRewardsFrame:IsShown() then
|
|
GameTooltip:AddLine("|A:gmchat-icon-blizz:0:0|a " .. ERR_NOT_IN_COMBAT, 1, 0, 0, 1)
|
|
end
|
|
GameTooltip:Show()
|
|
end
|
|
end
|
|
local function openPanelFallback(panel)
|
|
return not InCombatLockdown() and ShowUIPanel(panel)
|
|
end
|
|
local panelMap, panels = {}, {
|
|
character={CHARACTER, icon="Interface/PVPFrame/Icons/prestige-icon-7-3", gw=PaperDollFrame, tw=CharacterFrameTab1},
|
|
reputation={REPUTATION, icon="Interface/Icons/Achievement_Reputation_01", gw=ReputationFrame, tw=MODERN and CharacterFrameTab2 or CharacterFrameTab3},
|
|
currency={CURRENCY, icon="Interface/Icons/INV_Misc_Coin_17", gw=TokenFrame, tw=MODERN and CharacterFrameTab3 or CF_WRATH and CharacterFrameTab5},
|
|
spellbook={SPELLBOOK, icon="Interface/Icons/INV_Misc_Book_09", gw=CF_CLASSIC and SpellBookFrame, tmt="/click SpellbookMicroButton\n/click SpellBookFrameCloseButton", cw=SpellBookFrameCloseButton},
|
|
talents={TALENTS_BUTTON, icon="Interface/Icons/Ability_Marksmanship", gn=CF_CLASSIC and "PlayerTalentFrame", tw=CF_CLASSIC and TalentMicroButton, req=function() return (UnitLevel("player") or 0) >= 10 end},
|
|
achievements={ACHIEVEMENTS, atlas="UI-HUD-MicroMenu-Achievements-Up", gn="AchievementFrame", tw=AchievementMicroButton, tcr=1},
|
|
quests={QUESTLOG_BUTTON, icon="Interface/Icons/INV_Misc_Book_08", gw=MODERN and QuestMapFrame or QuestLogFrame, tw=QuestLogMicroButton},
|
|
groupfinder={DUNGEONS_BUTTON, icon=MODERN and "Interface/Icons/LEVELUPICON-LFD" or "Interface/LFGFrame/BattlenetWorking0", gw=PVEFrame, tw=LFDMicroButton},
|
|
collections=MODERN and {COLLECTIONS, icon="Interface/Icons/INV_Box_01", gn="CollectionsJournal", tw=CollectionsMicroButton},
|
|
adventureguide=MODERN and {ADVENTURE_JOURNAL, icon="Interface/EncounterJournal/UI-EJ-PortraitIcon", gn="EncounterJournal", tw=EJMicroButton},
|
|
guild=MODERN and {GUILD_AND_COMMUNITIES, icon="Interface/Icons/INV_Shirt_GuildTabard_01", gn="CommunitiesFrame", tw=GuildMicroButton}
|
|
or {title=GUILD, icon="Interface/Icons/INV_Shirt_GuildTabard_01", gw=GuildFrame, ow=FriendsFrameTab3, cw=FriendsFrameCloseButton, req=IsInGuild},
|
|
map={WORLD_MAP, icon=CI_ERA and "Interface/Worldmap/WorldMap-Icon" or "Interface/Icons/Inv_Misc_Map08", gw=WorldMapFrame, tw=MODERN and MinimapCluster.ZoneTextButton or MiniMapWorldMapButton},
|
|
social={SOCIAL_BUTTON, icon=MODERN and "Interface/Icons/UI_Chat" or "Interface/Icons/INV_Scroll_03", gw=FriendsFrame, tw=MODERN and QuickJoinToastButton or FriendsMicroButton},
|
|
calendar={L"Calendar", icon="Interface/Icons/Spell_Holy_BorrowedTime", gn="CalendarFrame", tw=GameTimeFrame},
|
|
options={OPTIONS, icon=MODERN and "Interface/Icons/Misc_RnRWrenchButtonRight" or "Interface/Icons/INV_Misc_Wrench_01", gw=SettingsPanel, noduck=1, open=function() Settings.OpenToCategory(nil) end},
|
|
macro={MACROS, icon="Interface/Icons/INV_Misc_Note_06", gn="MacroFrame", tmt=SLASH_MACRO1, cw=closeButton(MacroFrame), postmt=pyCLICK .. "csp 1\n" .. pyCLICK .. "cgm 1"},
|
|
profs=MODERN and {TRADE_SKILLS, icon="interface/icons/inv_pick_02", tw=ProfessionMicroButton},
|
|
gamemenu={MAINMENU_BUTTON, icon=CF_CLASSIC and "Interface/Icons/INV_Misc_PunchCards_Red", atlas="UI-HUD-MicroMenu-GameMenu-Up", gw=GameMenuFrame, tmt="/click GameMenuButtonContinue", noduck=1, pre=function() return not GameMenuFrame:IsShown() or nil end, post=function() RatingMenuFrame:Show() RatingMenuFrame:Hide() PlaySound(SOUNDKIT.IG_MAINMENU_OPEN) end},
|
|
vault=MODERN and {DELVES_GREAT_VAULT_LABEL, icon="Interface/Icons/INV_Cape_Special_Treasure_C_01", gn="WeeklyRewardsFrame", skipCloseSound=169062, req=function() return UnitLevel("player") == 80 end, tip=ShowVaultTip, open=openPanelFallback},
|
|
csp={gw=SettingsPanel, cpreamble=true, cw=closeButton(SettingsPanel, "csp")},
|
|
cgm={gw=GameMenuFrame, cpreamble=true, cw=closeButton(GameMenuFrame, "cgm")},
|
|
csf={pre=function() return StoreFrame_IsShown and StoreFrame_SetShown and StoreFrame_IsShown() and StoreFrame_SetShown(false) end, cpreamble=true},
|
|
}
|
|
do
|
|
local exName = newWidgetName("AB:PX!")
|
|
local clickEx = CLICK .. " " .. exName .. " "
|
|
local cmdPrefix = clickEx .. "csf 1\n" .. clickEx
|
|
local cmdDuckPrefix = cmdPrefix .. "csp 1\n" .. clickEx .. "cgm 1\n" .. clickEx
|
|
local ex = CreateFrame("Button", exName, nil, "SecureActionButtonTemplate")
|
|
ex:SetAttribute("type", "macro")
|
|
ex:SetAttribute("pressAndHoldAction", 1)
|
|
local function prerun(k)
|
|
local i, r = panels[k], 0
|
|
local tw, gw, cw, cw2, ow, ofun, scs = i.tw, i.gw, i.cw, i.cw2, i.ow, i.open, i.skipCloseSound
|
|
if tw and not tw:IsEnabled() then
|
|
r = i.tcr and r + 1 or r; tw:Enable()
|
|
end
|
|
if cw or ow or scs or ofun then
|
|
local gh, cd, od = not (gw and gw:IsShown()), not (cw and cw:IsEnabled()), not (ow and ow:IsEnabled())
|
|
if cw and gh ~= cd then
|
|
r = r + (gh and 6 or 2); cw:SetEnabled(not gh)
|
|
end
|
|
if cw2 then
|
|
r = r + (gh and 64 or 0); cw2:SetEnabled(not gh)
|
|
end
|
|
if ow and gh == od then
|
|
r = r + (gh and 8 or 24); ow:SetEnabled(gh)
|
|
end
|
|
if scs and not gh then
|
|
local ok, sh = PlaySound(scs)
|
|
if ok and sh then
|
|
r, i.stopSoundHandle = r + 32, sh
|
|
end
|
|
end
|
|
if ofun and gh == od then
|
|
securecall(ofun, gw, k)
|
|
end
|
|
end
|
|
return r ~= 0 and r or nil
|
|
end
|
|
local function postrun(k, m)
|
|
local i, m1, m3, m5 = panels[k], m % 2, m % 8, m % 32
|
|
if m5 >= 8 then i.ow:SetEnabled(m5 > 8) end
|
|
if m3 >= 2 then i.cw:SetEnabled(m3 > 2) end
|
|
if m >= 64 then i.cw2:SetEnabled(true) end
|
|
if m1 >= 1 then i.tw:Disable() end
|
|
local ssh = i.stopSoundHandle
|
|
i.stopSoundHandle = ssh and StopSound(ssh) and nil
|
|
end
|
|
ex:SetScript("PreClick", function(_, b)
|
|
local i = panels[b]
|
|
if i and i.pre then
|
|
i.postMessage = i.pre(b, i, prerun)
|
|
end
|
|
end)
|
|
ex:SetScript("PostClick", function(_, b)
|
|
local i, bp, pm = panels[b]
|
|
bp = i and i.cpreamble and b or b:match("^post%-(.*)")
|
|
i = panels[bp]
|
|
pm = i and i.postMessage
|
|
if pm ~= nil and i.post then
|
|
i.postMessage = nil
|
|
i.post(bp, pm, postrun)
|
|
end
|
|
end)
|
|
local function prepareMacroText(k, v)
|
|
local tmt = v.tmt
|
|
if tmt then
|
|
tmt = tmt:gsub("/click ", CLICK)
|
|
elseif v.tw then
|
|
tmt = widgetClickCommand(k, v.tw)
|
|
elseif v.cw or v.ow then
|
|
tmt = widgetClickCommand(k, v.ow) .. widgetClickCommand(k, v.cw)
|
|
if v.cw2 then
|
|
tmt = tmt .. widgetClickCommand(k, v.cw2)
|
|
end
|
|
end
|
|
if v.tw or v.cw or v.ow or v.open or v.cwrap then
|
|
v.pre, v.post = v.pre or prerun, v.post or postrun
|
|
end
|
|
if tmt and v.premt then
|
|
tmt = v.premt .. "\n" .. tmt
|
|
end
|
|
if tmt and v.postmt then
|
|
tmt = tmt .. "\n" .. v.postmt
|
|
end
|
|
if v.post then
|
|
tmt = tmt .. (tmt:sub(-1) ~= "\n" and "\n" or "") .. clickEx .. "post-" .. k
|
|
end
|
|
tmt = tmt and ((v.noduck and cmdPrefix or cmdDuckPrefix) .. k .. " 1\n" .. tmt)
|
|
return tmt
|
|
end
|
|
local pmeta = {__index=function(t, k)
|
|
local n, r = k == "gw" and t.gn
|
|
if n then
|
|
r = _G[n]
|
|
elseif k == "mainText" then
|
|
r = prepareMacroText(t.pk, t)
|
|
end
|
|
if k ~= nil then
|
|
t[k] = r
|
|
end
|
|
return r
|
|
end}
|
|
for k,v in pairs(panels) do
|
|
v.pk = k
|
|
setmetatable(v, pmeta)
|
|
if v.cpreamble and v.cw then
|
|
ex:SetAttribute("type-" .. k, "click")
|
|
ex:SetAttribute("clickbutton-" .. k, v.cw)
|
|
v.pre, v.post = v.pre or prerun, v.post or postrun
|
|
end
|
|
end
|
|
end
|
|
do -- further panels init
|
|
panels.options.cw, panels.options.cw2 = panels.csp.cw, panels.cgm.cw
|
|
panels.macro.postmt = widgetClickCommand("cmf", panels.macro.cw)
|
|
if MODERN then
|
|
panels.gamemenu.cw, panels.gamemenu.tmt = panels.cgm.cw, nil
|
|
panels.spellbook.tmt, panels.spellbook.cwrap, panels.spellbook.cw, panels.spellbook.ow = "/click PlayerSpellsFrameCloseButton\n/click PlayerSpellsMicroButton\n" .. pyCLICK .. " spelltab 1", 1, nil
|
|
panels.talents.tmt, panels.talents.cwrap, panels.talents.tw = "/click PlayerSpellsFrameCloseButton\n/click PlayerSpellsMicroButton\n" .. pyCLICK .. " talenttab 1", 1, nil
|
|
function EV.PLAYER_LOGIN()
|
|
pcall(C_AddOns.LoadAddOn, "Blizzard_PlayerSpells")
|
|
panels.spellbook.gw = PlayerSpellsFrame.SpellBookFrame
|
|
panels.talents.gw, panels.talents.gn = PlayerSpellsFrame.TalentsFrame, nil
|
|
panels.spellbook.ow, panels.talents.ow = PlayerSpellsFrameCloseButton, PlayerSpellsFrameCloseButton
|
|
if PlayerSpellsFrame and PlayerSpellsFrame.tabSystem and PlayerSpellsFrame.tabSystem.tabs then
|
|
widgetClickCommand("spelltab", PlayerSpellsFrame.tabSystem.tabs[PlayerSpellsFrame.spellBookTabID])
|
|
widgetClickCommand("talenttab", PlayerSpellsFrame.tabSystem.tabs[PlayerSpellsFrame.talentTabID])
|
|
end
|
|
if (UnitLevel("player") or 0) < 80 then
|
|
function EV:PLAYER_LEVEL_UP(nlvl)
|
|
if nlvl == 80 then
|
|
AB:NotifyObservers("uipanel")
|
|
return "remove"
|
|
end
|
|
end
|
|
end
|
|
return "remove"
|
|
end
|
|
pcall(C_AddOns.LoadAddOn, "Blizzard_WeeklyRewards")
|
|
panels.vault.cw = closeButton(panels.vault.gw)
|
|
elseif CF_WRATH then
|
|
panels.achievements.icon = "Interface/PvPFrame/Icons/prestige-icon-4"
|
|
local gfp = panels.groupfinder
|
|
gfp.tw, gfp.cw, gfp.skipCloseSound = nil, closeButton(gfp.gw), 839
|
|
gfp.open, gfp.premt = openPanelFallback, CLICK .. "GroupFinderFrameGroupButton1"
|
|
function panels.currency.req()
|
|
return GetCurrencyListSize() > 0
|
|
end
|
|
else -- era
|
|
panels.achievements = nil
|
|
panels.groupfinder = nil
|
|
panels.currency = nil
|
|
panels.calendar = nil
|
|
panels.reputation.icon = "Interface/Icons/INV_MISC_NOTE_02"
|
|
end
|
|
function EV.ADDON_LOADED()
|
|
if MacroFrame then
|
|
panels.macro.cw:SetParent(MacroFrame)
|
|
return "remove"
|
|
end
|
|
end
|
|
end
|
|
local function panelHint(tk)
|
|
local i = panels[tk]
|
|
if not i then return end
|
|
local gw, icon, s = i.gw, i.icon, 0
|
|
s = (gw and gw:IsVisible()) and s + 1 or s
|
|
if icon == nil then
|
|
icon, s = i.atlas, s + 262144
|
|
end
|
|
local tf = i.tip or nil
|
|
local willFail = MODERN and AreAllPanelsDisallowed() or (gw and i.open == openPanelFallback and InCombatLockdown() and not gw:IsVisible())
|
|
return tk == "gamemenu" or not willFail, s, icon, i[1], nil, nil, nil, tf, tf and tk
|
|
end
|
|
local function createPanel(tk)
|
|
local r = panelMap[tk]
|
|
local pi = r == nil and panels[tk]
|
|
if pi and pi[1] and pi.mainText and (pi.req == nil or pi.req()) then
|
|
r = AB:CreateActionSlot(panelHint, tk, "macrotext", pi.mainText)
|
|
panelMap[tk] = r
|
|
end
|
|
return r
|
|
end
|
|
local function describePanel(tk)
|
|
local i = panels[tk]
|
|
if i and i[1] then
|
|
return L"Interface Panel", i[1], i.icon or i.atlas
|
|
end
|
|
return L"Interface Panel"
|
|
end
|
|
AB:RegisterActionType("uipanel", createPanel, describePanel, 1)
|
|
end)
|