mirror of
https://github.com/brues-code/pfUI.git
synced 2026-09-21 23:26:56 +00:00
75c4657ab9
* nameplates: source totem icons from UnitCreatedBySpell
Read the totem's icon from the totem-drop spell (UnitCreatedBySpell +
GetSpellTexture) instead of the self-aura at index 1 plus a
UNIT_SPELLCAST_SUCCEEDED capture for active totems. The drop spell is a
broadcast descriptor field present for every summoned unit in range, so
it resolves immediately for passive and active totems alike and gives the
totem's own icon rather than the attack-spell proxy. Drops the
UNIT_SPELLCAST_SUCCEEDED registration and handler.
Re-read the spell each render and key the cached texture on the spell id
so an in-place totem swap (same unit, new drop spell -- no plate re-add)
refreshes the icon without needing the plate to leave and re-enter view.
* CAPI min bumped to 1.13.1
* Gracefully disable pfUI when ClassicAPI is missing
Add API_Check.lua as the first TOC entry. When the ClassicAPI DLL is
absent or below the minimum version it sets pfUI_disabled and stands up
an inert stub so the rest of the addon no-ops instead of flooding load
errors: modules and skins register their bodies into no-ops, and the
setfenv'd api/lib files run inside an environment where CreateFrame and
any missing global resolve to a null object -- so no real frames or live
handlers are created and missing API calls just return null. pfUI.lua
bails early on pfUI_disabled.
* auras: scan through GetAuraSlots instead of by-index loops
Every aura scan loop (unit frame buffs/debuffs, dispel indicators, buff and
custom indicators, player buff frame, buffwatch bars, tooltip buff row) now
enumerates a unit's auras once with C_UnitAuras.GetAuraSlots and reads each
aura by slot id via the positional C_UnitAuras.UnitAuraBySlot. The by-index
getters re-walk the aura array from slot 0 on every call, so a per-button
loop over them was quadratic in the aura count; one enumeration plus a
by-slot read per aura is linear.
pfUI.api.ScanAuraSlots(unit, filter, buf, max) wraps GetAuraSlots' fill-a-
table form (table as the 5th argument) so no vararg Lua frame is involved:
Lua 5.0 allocates an `arg` table for every vararg call, which showed up as
nameplate OnUpdate/OnEvent memory growth in the first cut of this change.
Single by-index reads in tooltip and click handlers are unchanged (one call
each, and SetUnitAura takes the same index).
Requires the ClassicAPI build that adds GetAuraSlots' fill form; on an older
DLL the 5th argument is ignored and ScanAuraSlots would read the first slot
id as the count.
* Show Faction/Race icons in chat
* Load pfUI through ClassicAPI's flavor TOCs
ClassicAPI redirects the read of pfUI\pfUI.toc to a flavored file whenever
the DLL is installed, so which TOC the client opens already answers whether
ClassicAPI is there. Split the manifest three ways and let that do the work:
pfUI.toc fallback, reached only when ClassicAPI is missing;
loads API_Check.lua and nothing else
pfUI_ClassicAPI.toc full addon, every non-Turtle client
pfUI_Turtle.toc full addon plus init\turtle.xml, on Turtle
The fallback TOC declares no SavedVariables. It used to, while API_Check.lua
reset pfUI_profiles to an empty table on the disabled path -- which truncated
the player's profiles on logout.
With the missing-DLL case handled by TOC selection, API_Check.lua drops the
null object stub that kept the other ~140 files quiet, along with
pfUI_disabled and the now unreachable early return in pfUI.lua. It keeps the
version gate, which still matters: the flavor redirect landed in ClassicAPI
v1.11.0, below the v1.13.1 pfUI needs, so an old DLL still gets served a
flavor TOC. pfUI.lua also loses a verbatim duplicate of the whole check.
Turtle-only files move to init\turtle.xml: modules\turtle-wow.lua (its
TURTLE_WOW_VERSION guard is now redundant) and the lft, turtle_shop,
barbershop, transmog and ebc skins. turtle-wow registers last instead of
75th of 84; the only ordering it relies on is pfUI.chat, registered 8th.
pfSellData moves to env\selldata.lua, listed only in pfUI_ClassicAPI.toc,
since turtle-wow.lua replaces the table wholesale on Turtle. env\tables.lua
keeps an empty declaration so sellvalue.lua has something to index when the
turtle-wow module is disabled.
The release workflow pinned PFUI_CLASSIC_API_LATEST in pfUI.lua, which has
not held that constant since it moved to API_Check.lua, so the pin was
silently doing nothing. It also switches to brues-code/packager@vCAPI, which
recognizes the _ClassicAPI and _Turtle suffixes and applies the TOC build
type filters to them.
* Split the vendor price tables into their own manifests
Turtle's pfSellData moves out of modules\turtle-wow.lua into
env\selldata_turtle.lua, matching env\selldata.lua for the stock list, and
each is pulled in by the manifest for its client: init\stock.xml from
pfUI_ClassicAPI.toc, init\turtle.xml from pfUI_Turtle.toc. Either way it
loads after init\env.xml and replaces the empty pfSellData declared there.
Turtle's prices used to be assigned inside the turtle-wow module body, which
put them on pfUI.env and skipped them entirely when that module was
disabled. At file scope they land on _G and apply either way.
* Drop the vanilla compat layer
compat\vanilla.lua named the handful of things that differed between clients
back when pfUI targeted several. Only one client remains, so every constant
had exactly one value. Inline each at its use site and delete the file,
init\compat.xml, and both TOC entries.
COOLDOWN_FRAME_TYPE -> "Model"
LOOT_BUTTON_FRAME_TYPE -> "LootButton"
MINIMAP_TRACKING_FRAME -> _G.MiniMapTrackingFrame
FRIENDS_NAME_LOCATION -> "ButtonTextNameLocation"
EVENTS_MINIMAP_ZONE_UPDATE -> the event list, in panel.lua
MICRO_BUTTONS -> a local in panel.lua
NAMEPLATE_OBJECTORDER -> a local in nameplates.lua
ACTIONBAR_SECURE_TEMPLATE_BAR/_BUTTON -> nil, so the argument goes away
NAMEPLATE_FRAMETYPE and PLAYER_BUFF_START_ID had no readers left.
RunMacroText moves to pfUI.lua. compat\vanilla.lua was setfenv'd into the
pfUI environment, so the function only ever existed on pfUI.env; at file
scope it lands on _G as a real export instead. Nothing in pfUI calls it, and
ClassicAPI neither defines nor looks for a RunMacroText global -- it does
the same throwaway edit box natively in src/macro/Execute.cpp and only
defers to a global RunMacro.
* bump CAPI min to 11303
* auras: uncap the self-debuff tooltip lookup
With selfdebuff on, the displayed debuff list is PLAYER-filtered while
GameTooltip:SetUnitAura indexes the unfiltered HARMFUL list, so both
handlers map one to the other by matching name + sourceGUID. That mapping
scanned slots 1..16 only.
The unfiltered harmful list is not capped at 16. Once a unit's 16 debuff
slots are full the server parks further debuffs in buff slots, and
C_UnitAuras classifies by the aura's polarity flag rather than its slot
range, so it reports those as harmful too -- verified live at 18 harmful on
a 20-aura target. Past the sixteenth the lookup found nothing and fell
through to the raw filtered index, opening the wrong tooltip or none.
Both now enumerate however many harmful auras the unit actually has, via
ScanAuraSlots, which also drops the per-index rescan the by-index accessor
was doing. Each handler gets its own slot buffer: OnEnter can fire while a
refresh is showing/hiding frames under the cursor, so sharing the refresh
buffer could clobber a scan mid-walk.
The nameplate module still collects at most 16 debuffs per plate. That one
is a display cap matching its 16 configured icon frames, not an aura-count
assumption, so it is left alone.
* bump CAPI min to 11304
1609 lines
50 KiB
Lua
1609 lines
50 KiB
Lua
pfUI:RegisterModule("actionbar", function ()
|
||
local class = UnitClassBase("player")
|
||
local _, cr, cg, cb = GetUnitColor('player')
|
||
local er, eg, eb, ea = GetStringColor(pfUI_config.appearance.border.color)
|
||
|
||
local backdrop_highlight = { edgeFile = pfUI.media["img:glow"], edgeSize = 8 }
|
||
local showgrid = 0
|
||
local showgrid_pet = 0
|
||
local rawborder, border = GetBorderSize("actionbars")
|
||
local bpad = rawborder > 1 and border - GetPerfectPixel() or GetPerfectPixel()
|
||
|
||
local eventcache = { } -- contains a list of events that shall be processed later -> [event] = true
|
||
local updatecache = { } -- contains a list of buttons slots that shall be refreshed later -> [slot] = true
|
||
local buttoncache = { } -- contains a list of all buttons ever created -> [slot] = frame
|
||
|
||
-- try to assume based on the current mouse positions if a button drag
|
||
-- should happen even if action-on-key-down is used. By that replace the
|
||
-- cast events by reverting the active buttons to the old mouse-up state.
|
||
local drag_await
|
||
local drag_active
|
||
local function AssumeButtonDrag()
|
||
-- skip if keydown press is not enabled
|
||
if C.bars.keydown ~= "1" then return end
|
||
|
||
-- skip if always shift-drag is not enabled
|
||
if C.bars.shiftdrag ~= "1" then return end
|
||
|
||
if drag_await and not drag_active and IsShiftKeyDown() then
|
||
drag_active = true
|
||
-- set all buttons to regular on release clicks
|
||
for id, button in pairs(buttoncache) do
|
||
button:RegisterForClicks("LeftButtonUp", "RightButtonUp")
|
||
end
|
||
elseif drag_active and not IsShiftKeyDown() then
|
||
drag_active = nil
|
||
-- set all buttons back to their defaults
|
||
for id, button in pairs(buttoncache) do
|
||
button:RegisterForClicks("LeftButtonDown", "RightButtonDown")
|
||
end
|
||
end
|
||
end
|
||
|
||
-- hide blizzard bars
|
||
local function kill(f, killshow)
|
||
if f.Show and killshow then f.Show = function() return end end
|
||
if f.UnregisterAllEvents then f:UnregisterAllEvents() end
|
||
if f.Hide then f:Hide() end
|
||
end
|
||
|
||
-- also abbreviate mouse buttons
|
||
local OrigGetBindingText = GetBindingText
|
||
local function GetBindingText(msg, mod, abbrev)
|
||
local txt = OrigGetBindingText(msg, mod, abbrev)
|
||
if abbrev then
|
||
txt = string.gsub(txt, _G[string.format("%s%s", mod, "BUTTON3")], "MB3")
|
||
txt = string.gsub(txt, _G[string.format("%s%s", mod, "BUTTON4")], "MB4")
|
||
txt = string.gsub(txt, _G[string.format("%s%s", mod, "BUTTON5")], "MB5")
|
||
txt = string.gsub(txt, _G[string.format("%s%s", mod, "MOUSEWHEELDOWN")], "MWD")
|
||
txt = string.gsub(txt, _G[string.format("%s%s", mod, "MOUSEWHEELUP")], "MWU")
|
||
end
|
||
return txt
|
||
end
|
||
|
||
kill(MainMenuBar)
|
||
|
||
local blizzard_elements = { MultiBarBottomLeft,
|
||
MultiBarBottomRight, MultiBarLeft, MultiBarRight }
|
||
for _, f in pairs(blizzard_elements) do
|
||
kill(f, true)
|
||
end
|
||
|
||
-- enable all possible actionbar pages
|
||
SetActionBarToggles(0, 0, 0, 0)
|
||
_G.SHOW_MULTI_ACTIONBAR_1 = nil
|
||
_G.SHOW_MULTI_ACTIONBAR_2 = nil
|
||
_G.SHOW_MULTI_ACTIONBAR_3 = nil
|
||
_G.SHOW_MULTI_ACTIONBAR_4 = nil
|
||
|
||
-- events that provide special updater functions
|
||
local special_events = {
|
||
["ACTIONBAR_UPDATE_COOLDOWN"] = true,
|
||
["ACTIONBAR_UPDATE_USABLE"] = true,
|
||
["ACTIONBAR_UPDATE_STATE"] = true,
|
||
}
|
||
|
||
-- events that are shared across all buttons
|
||
local global_events = {
|
||
-- slot/button updates
|
||
["PLAYER_ENTERING_WORLD"] = true,
|
||
["ACTIONBAR_SLOT_CHANGED"] = true,
|
||
["UPDATE_BINDINGS"] = true,
|
||
["ACTIONBAR_PAGE_CHANGED"] = true,
|
||
["UPDATE_BONUS_ACTIONBAR"] = true,
|
||
["CRAFT_SHOW"] = true,
|
||
["CRAFT_CLOSE"] = true,
|
||
["TRADE_SKILL_SHOW"] = true,
|
||
["TRADE_SKILL_CLOSE"] = true,
|
||
["PLAYER_ENTER_COMBAT"] = true,
|
||
["PLAYER_LEAVE_COMBAT"] = true,
|
||
-- cooldown updates
|
||
["UNIT_INVENTORY_CHANGED"] = true,
|
||
-- auto repeat action
|
||
["START_AUTOREPEAT_SPELL"] = true,
|
||
["STOP_AUTOREPEAT_SPELL"] = true,
|
||
}
|
||
|
||
-- events that are used for the aura/shapeshift bar
|
||
local aura_events = {
|
||
["UPDATE_SHAPESHIFT_FORMS"] = true,
|
||
["PLAYER_AURAS_CHANGED"] = true,
|
||
}
|
||
|
||
-- events that are used for the pet bar
|
||
local pet_events = {
|
||
["PLAYER_CONTROL_LOST"] = true,
|
||
["PLAYER_CONTROL_GAINED"] = true,
|
||
["PLAYER_FARSIGHT_FOCUS_CHANGED"] = true,
|
||
["UNIT_PET"] = true,
|
||
["PET_BAR_UPDATE"] = true,
|
||
["PET_BAR_UPDATE_COOLDOWN"] = true,
|
||
}
|
||
|
||
local buttontypes = {
|
||
-- blizzard keybinds
|
||
[3] = "MULTIACTIONBAR3BUTTON",
|
||
[4] = "MULTIACTIONBAR4BUTTON",
|
||
[5] = "MULTIACTIONBAR2BUTTON",
|
||
[6] = "MULTIACTIONBAR1BUTTON",
|
||
[11] = "SHAPESHIFTBUTTON",
|
||
[12] = "BONUSACTIONBUTTON",
|
||
-- additional keybinds
|
||
[2] = "PFPAGING",
|
||
[7] = "PFSTANCEONE",
|
||
[8] = "PFSTANCETWO",
|
||
[9] = "PFSTANCETHREE",
|
||
[10] = "PFSTANCEFOUR",
|
||
}
|
||
|
||
local blizzbarmapping = {
|
||
["MultiBarRight"] = 3,
|
||
["MultiBarLeft"] = 4,
|
||
["MultiBarBottomRight"] = 5,
|
||
["MultiBarBottomLeft"] = 6,
|
||
["ShapeShiftBar"] = 11,
|
||
["BonusActionBar"] = 12,
|
||
}
|
||
|
||
local barnames = {
|
||
[1] = "Main",
|
||
[2] = "Paging",
|
||
[3] = "Right",
|
||
[4] = "Vertical",
|
||
[5] = "Left",
|
||
[6] = "Top",
|
||
[7] = "StanceBar1",
|
||
[8] = "StanceBar2",
|
||
[9] = "StanceBar3",
|
||
[10] = "StanceBar4",
|
||
[11] = "Stances",
|
||
[12] = "Pet",
|
||
}
|
||
|
||
local button_animations = {
|
||
["none"] = function()
|
||
this.active = nil
|
||
this:Hide()
|
||
end,
|
||
["zoomfade"] = function()
|
||
if this.active == 0 then
|
||
-- init animation
|
||
this:SetSize(this.parent:GetSize())
|
||
this:SetScale(this.parent:GetScale())
|
||
this.tex:SetTexture(this.parent.icon:GetTexture())
|
||
this.tex:SetVertexColor(this.parent.icon:GetVertexColor())
|
||
this:SetAlpha(1)
|
||
this.active = 1
|
||
return
|
||
elseif this.active == 1 then
|
||
-- run animation
|
||
local fade = 30/GetFramerate()*0.05
|
||
this:SetAlpha(this:GetAlpha() - fade)
|
||
this:SetScale(this:GetScale() + fade)
|
||
if this:GetAlpha() > 0 then return end
|
||
end
|
||
|
||
-- stop animation
|
||
this.active = nil
|
||
this:Hide()
|
||
end,
|
||
["shrinkreturn"] = function()
|
||
if this.active == 0 then
|
||
-- init animation
|
||
this:SetSize(this.parent:GetSize())
|
||
this:SetScale(this.parent:GetScale())
|
||
this.tex:SetTexture(this.parent.icon:GetTexture())
|
||
this.tex:SetVertexColor(this.parent.icon:GetVertexColor())
|
||
this:SetAlpha(1)
|
||
this.parent.icon:Hide()
|
||
this.active = 1
|
||
this.time = 0
|
||
return
|
||
elseif this.active == 1 then
|
||
-- run animation
|
||
this.time = this.time + 30/GetFramerate()*0.1
|
||
local fade = 1 -math.exp(-this.time)*math.sin(this.time*math.pi)
|
||
this:SetAlpha(fade)
|
||
this:SetScale(fade)
|
||
if this.time < 1 then return end
|
||
end
|
||
|
||
-- stop animation
|
||
this.active = nil
|
||
this:Hide()
|
||
this.parent.icon:Show()
|
||
end,
|
||
["elasticzoom"] = function()
|
||
if this.active == 0 then
|
||
-- init animation
|
||
this:SetSize(this.parent:GetSize())
|
||
this:SetScale(this.parent:GetScale())
|
||
this.tex:SetTexture(this.parent.icon:GetTexture())
|
||
this.tex:SetVertexColor(this.parent.icon:GetVertexColor())
|
||
this:SetAlpha(1)
|
||
this.parent.icon:Hide()
|
||
this.active = 1
|
||
this.time = 0
|
||
return
|
||
elseif this.active == 1 then
|
||
-- run animation
|
||
this.time = this.time + 30/GetFramerate()*0.03
|
||
local fade = 1 -math.exp(-this.time*6)*math.sin(this.time*4*math.pi)
|
||
this:SetAlpha(fade)
|
||
this:SetScale(fade)
|
||
if this.time < 1 then return end
|
||
end
|
||
|
||
-- stop animation
|
||
this.active = nil
|
||
this:Hide()
|
||
this.parent.icon:Show()
|
||
end,
|
||
["wobblezoom"] = function()
|
||
if this.active == 0 then
|
||
-- init animation
|
||
this:SetSize(this.parent:GetSize())
|
||
this:SetScale(this.parent:GetScale())
|
||
this.tex:SetTexture(this.parent.icon:GetTexture())
|
||
this.tex:SetVertexColor(this.parent.icon:GetVertexColor())
|
||
this:SetAlpha(1)
|
||
this.parent.icon:Hide()
|
||
this.active = 1
|
||
this.time = 0
|
||
return
|
||
elseif this.active == 1 then
|
||
-- run animation
|
||
this.time = this.time + 30/GetFramerate()*0.02
|
||
local fade = 1 -math.exp(-this.time*6)*math.sin(this.time*10*math.pi)
|
||
this:SetAlpha(fade)
|
||
this:SetScale(fade)
|
||
if this.time < 1 then return end
|
||
end
|
||
|
||
-- stop animation
|
||
this.active = nil
|
||
this:Hide()
|
||
this.parent.icon:Show()
|
||
end,
|
||
}
|
||
|
||
local function GetActiveBar()
|
||
if CURRENT_ACTIONBAR_PAGE == 1 and GetBonusBarOffset() ~= 0 then
|
||
return NUM_ACTIONBAR_PAGES + GetBonusBarOffset()
|
||
else
|
||
return CURRENT_ACTIONBAR_PAGE
|
||
end
|
||
end
|
||
|
||
local function ButtonDrag(self)
|
||
local self = self or this
|
||
|
||
if _G.LOCK_ACTIONBAR == "1" and not (pfUI_config.bars.shiftdrag == "1" and IsShiftKeyDown()) then return end
|
||
|
||
if self.bar == 12 then
|
||
PickupPetAction(self.id)
|
||
else
|
||
PickupAction(self.id)
|
||
end
|
||
end
|
||
|
||
local function ButtonDragStop(self)
|
||
local self = self or this
|
||
|
||
if MacroFrame_SaveMacro then
|
||
MacroFrame_SaveMacro()
|
||
end
|
||
|
||
if self.bar == 12 then
|
||
PickupPetAction(self.id)
|
||
else
|
||
PlaceAction(self.id)
|
||
end
|
||
end
|
||
|
||
local mouse
|
||
local function ButtonAnimate(self)
|
||
local self = self or this
|
||
mouse = arg1 and not keystate
|
||
|
||
-- trigger action animation
|
||
if ( pfUI_config.bars.keydown == "1" and keystate == "down" ) or (pfUI_config.bars.keydown == "0" and keystate == "up" ) or self.bar == 11 or mouse then
|
||
if C.bars.animmode == "keypress" and ( self:GetAlpha() > .1 or C.bars.animalways == "1" ) then
|
||
self.animation.active = 0
|
||
self.animation:Show()
|
||
end
|
||
end
|
||
|
||
-- handle button highlight
|
||
if keystate == "down" then
|
||
self.highlight:Show()
|
||
elseif not MouseIsOver(self) then
|
||
self.highlight:Hide()
|
||
end
|
||
end
|
||
|
||
local function ButtonClick(self)
|
||
local self = self or this
|
||
|
||
local grid = self.bar == 12 and showgrid_pet or showgrid
|
||
local mouse = arg1 and not keystate
|
||
local keystate = keystate
|
||
local slfcast = C.bars.altself == "1" and IsAltKeyDown() and true or self.slfcast
|
||
slfcast = C.bars.rightself == "1" and arg1 and arg1 == "RightButton" and true or slfcast
|
||
self.slfcast = nil
|
||
if ( C.bars.keydown == "1" and keystate == "down" and not drag_active ) or (C.bars.keydown == "0" and keystate == "up" or drag_active ) or self.bar == 11 or mouse then
|
||
if self.bar == 11 then
|
||
CastShapeshiftForm(self.id)
|
||
elseif grid == 1 then
|
||
if self.bar == 12 then
|
||
PickupPetAction(self.id)
|
||
else
|
||
PickupAction(self.id)
|
||
end
|
||
elseif self.bar == 12 then
|
||
if arg1 == "LeftButton" then
|
||
if IsPetAttackActive(self.id) then
|
||
PetStopAttack()
|
||
else
|
||
CastPetAction(self.id)
|
||
end
|
||
else
|
||
TogglePetAutocast(self.id)
|
||
end
|
||
else
|
||
if MacroFrame_SaveMacro then
|
||
MacroFrame_SaveMacro()
|
||
end
|
||
|
||
UseAction(self.id, nil, slfcast)
|
||
end
|
||
end
|
||
end
|
||
|
||
local function ButtonMacroScan(self)
|
||
if self.bar > 10 then return end
|
||
if not self.scanmacro then return end
|
||
if pfUI.bars.skip_macro then return end
|
||
|
||
-- SuperCleveRoidMacros: for macros it manages, leave spellslot/booktype unset
|
||
-- so the button's icon, cooldown, and tooltip flow through the hooked
|
||
-- GetActionTexture / GetActionCooldown / GameTooltip:SetAction and follow the
|
||
-- active conditional dynamically, instead of being frozen to the first
|
||
-- statically-scanned spell.
|
||
if CleveRoids and CleveRoids.IsManagedAction and CleveRoids.IsManagedAction(self.id) then
|
||
self.spellslot, self.booktype, self.spellID = nil, nil, nil
|
||
return
|
||
end
|
||
|
||
local kind, slot = GetActionInfo(self.id)
|
||
self.spellslot, self.booktype, self.spellID = nil, nil, nil
|
||
if kind == 'macro' then
|
||
local name, _, body = GetMacroInfo(slot)
|
||
|
||
if name and body then
|
||
local match
|
||
|
||
for line in gfind(body, "[^%\n]+") do
|
||
_, _, match = string.find(line, '^#showtooltip (.+)')
|
||
|
||
-- allow the user to disable the scan
|
||
if match and strfind(match, "disable") then
|
||
return
|
||
end
|
||
|
||
if not match then
|
||
-- add support to specify custom tooltips via:
|
||
-- /run --showtooltip SPELLNAME
|
||
_, _, match = string.find(line, '%-%-showtooltip (.+)')
|
||
end
|
||
|
||
if not match then
|
||
_, _, match = string.find(line, '^/cast (.+)')
|
||
end
|
||
|
||
if not match then
|
||
_, _, match = string.find(line, '^/pfcast (.+)')
|
||
end
|
||
|
||
if not match then
|
||
_, _, match = string.find(line, '^/pfmouse (.+)')
|
||
end
|
||
|
||
if not match then
|
||
_, _, match = string.find(line, 'CastSpellByName%(%"(.+)%"%)')
|
||
end
|
||
|
||
if match then
|
||
self.spellslot, self.booktype, self.spellID = select(7, libspell.GetSpellInfo(match))
|
||
if self.spellslot and self.spellslot > 0 then return end
|
||
end
|
||
end
|
||
end
|
||
end
|
||
end
|
||
|
||
local function ButtonEnter(self)
|
||
self = self or this
|
||
|
||
-- indicate that dragging could get enabled
|
||
drag_await = true
|
||
|
||
GameTooltip:ClearLines()
|
||
GameTooltip_SetDefaultAnchor(GameTooltip, self)
|
||
|
||
if self.bar == 11 then
|
||
GameTooltip:SetShapeshift(self.id)
|
||
elseif self.bar == 12 then
|
||
local name, _, _, token = GetPetActionInfo(self.id)
|
||
if token then
|
||
GameTooltip:AddLine(_G[name])
|
||
GameTooltip:Show()
|
||
else
|
||
GameTooltip:SetPetAction(self.id)
|
||
end
|
||
elseif self.spellID then
|
||
GameTooltip:SetSpellByID(self.spellID)
|
||
elseif self.spellslot and self.booktype then
|
||
GameTooltip:SetSpell(self.spellslot, self.booktype)
|
||
else
|
||
GameTooltip:SetAction(self.id)
|
||
end
|
||
|
||
self.highlight:Show()
|
||
end
|
||
|
||
local function ButtonLeave(self)
|
||
self = self or this
|
||
|
||
-- no longer wait for a drag event
|
||
drag_await = nil
|
||
|
||
self.highlight:Hide()
|
||
GameTooltip:Hide()
|
||
end
|
||
|
||
local start, duration, enable, castable, autocast, token
|
||
local grid, sid, id, bar, active, texture, _
|
||
local function ButtonSlotUpdate(self)
|
||
if not self then return end
|
||
self = self or this
|
||
sid = self.id -- 1 to 120
|
||
|
||
-- reset shared variables
|
||
castable, autocast, token = nil, nil, nil
|
||
|
||
-- set the own ID for compatibility to some vanilla addons
|
||
self:SetID(self.id)
|
||
|
||
grid = self.bar == 12 and showgrid_pet or showgrid
|
||
|
||
if self.bar == 11 then
|
||
-- stance button
|
||
bar = self.bar
|
||
id = sid
|
||
texture, _, active = GetShapeshiftFormInfo(id)
|
||
elseif self.bar == 12 then
|
||
-- pet button
|
||
bar = self.bar
|
||
id = sid
|
||
_, _, texture, token, active, castable, autocast = GetPetActionInfo(id)
|
||
texture = token and _G[texture] or texture
|
||
else
|
||
active = IsCurrentAction(sid) or IsAutoRepeatAction(sid)
|
||
texture = GetActionTexture(sid)
|
||
bar = GetActiveBar()
|
||
id = self.bar == 1 and self.slot or sid-((self.bar)-1)*12
|
||
end
|
||
|
||
-- overwrite with spell macro texture where possible
|
||
if self.spellslot and self.booktype then
|
||
texture = GetSpellTexture(self.spellslot, self.booktype)
|
||
end
|
||
|
||
if not self.showempty and self.backdrop and not texture and grid == 0 then
|
||
self.backdrop:Hide()
|
||
self.hide = true
|
||
else
|
||
self.backdrop:Show()
|
||
self.hide = nil
|
||
end
|
||
|
||
-- active border
|
||
if active then
|
||
if C.bars.animmode == "statechange" and not self.active:IsShown() then
|
||
self.animation.active = 0
|
||
self.animation:Show()
|
||
end
|
||
|
||
self.backdrop:SetBackdropBorderColor(cr,cg,cb,1)
|
||
self.active:Show()
|
||
else
|
||
self.backdrop:SetBackdropBorderColor(er,eg,eb,ea)
|
||
self.active:Hide()
|
||
end
|
||
|
||
if self.bar ~= 11 and self.bar ~= 12 then
|
||
-- update consumables
|
||
if IsConsumableAction(sid) then
|
||
self.count:SetText(GetActionCount(sid))
|
||
elseif IsReagentAction and IsReagentAction(sid) then
|
||
self.count:SetText(GetReagentCount(sid))
|
||
else
|
||
self.count:SetText(nil)
|
||
end
|
||
|
||
-- equipped item
|
||
if IsEquippedAction(sid) and C.bars.showequipped == "1" then
|
||
self.equipped:Show()
|
||
else
|
||
self.equipped:Hide()
|
||
end
|
||
|
||
-- update macro text
|
||
if C.bars["bar"..self.bar] and C.bars["bar"..self.bar].showmacro == "1" then
|
||
self.macro:SetText(GetActionText(sid))
|
||
else
|
||
self.macro:SetText("")
|
||
end
|
||
end
|
||
|
||
-- icon
|
||
if texture ~= self.texture then
|
||
self.icon:SetTexture(texture)
|
||
self.texture = texture
|
||
end
|
||
|
||
-- handle pet bar quirks
|
||
if self.bar == 12 then
|
||
-- desaturate disabled petbar
|
||
self.icon:SetDesaturated(not GetPetActionsUsable())
|
||
|
||
-- display autocast
|
||
if autocast then
|
||
self.autocast:Show()
|
||
self.autocast:SetAlpha(self:GetAlpha() * 0.10)
|
||
else
|
||
self.autocast:Hide()
|
||
end
|
||
|
||
if castable and C.bars.showcastable == "1" then
|
||
self.autocastable:Show()
|
||
else
|
||
self.autocastable:Hide()
|
||
end
|
||
end
|
||
|
||
-- keybinds
|
||
if not self.hide and self.bar == 1 and self.bar ~= 11 and self.bar ~= 12 then
|
||
self.keybind:SetText(GetBindingText(GetBindingKey("ACTIONBUTTON"..id), "KEY_", 1))
|
||
elseif not self.hide and buttontypes[self.bar] then
|
||
self.keybind:SetText(GetBindingText(GetBindingKey(buttontypes[self.bar]..id), "KEY_", 1))
|
||
else
|
||
self.keybind:SetText("")
|
||
end
|
||
end
|
||
|
||
local sid, usable, oom, _
|
||
local function ButtonUsableUpdate(self)
|
||
self = self or this
|
||
sid = self.id -- 1 to 120
|
||
|
||
if self.bar == 11 then
|
||
_, _, _, usable = GetShapeshiftFormInfo(sid)
|
||
elseif self.bar == 12 then
|
||
usable = true
|
||
else
|
||
usable, oom = IsUsableAction(sid)
|
||
end
|
||
|
||
-- update usable [out-of-range = 1, oom = 2, not-usable = 3, default = 0]
|
||
if self.outofrange and C.bars.glowrange == "1" then
|
||
if self.vertexstate ~= 1 then
|
||
self.icon:SetVertexColor(self.rangeColor:GetRGBA())
|
||
self.vertexstate = 1
|
||
end
|
||
elseif oom and C.bars.showoom == "1" then
|
||
if self.vertexstate ~= 2 then
|
||
self.icon:SetVertexColor(self.oomColor:GetRGBA())
|
||
self.vertexstate = 2
|
||
end
|
||
elseif not usable and C.bars.showna == "1" then
|
||
if self.vertexstate ~= 3 then
|
||
self.icon:SetVertexColor(self.naColor:GetRGBA())
|
||
self.vertexstate = 3
|
||
end
|
||
else
|
||
if self.vertexstate ~= 0 then
|
||
self.icon:SetVertexColor(1, 1, 1, 1)
|
||
self.vertexstate = 0
|
||
end
|
||
end
|
||
end
|
||
|
||
local function ButtonRangeUpdate(self)
|
||
self = self or this
|
||
|
||
-- update range display
|
||
if C.bars.glowrange == "1" and self.bar ~= 11 and self.bar ~= 12 and HasAction(self.id) and ActionHasRange(self.id) and IsActionInRange(self.id) == 0 then
|
||
if not self.outofrange then
|
||
self.outofrange = true
|
||
ButtonUsableUpdate(self)
|
||
end
|
||
elseif self.outofrange then
|
||
self.outofrange = nil
|
||
ButtonUsableUpdate(self)
|
||
end
|
||
end
|
||
|
||
local start, duration, enable
|
||
local function ButtonCooldownUpdate(button)
|
||
if not button then return end
|
||
|
||
if button.bar == 11 then
|
||
start, duration, enable = GetShapeshiftFormCooldown(button.id)
|
||
elseif button.bar == 12 then
|
||
start, duration, enable = GetPetActionCooldown(button.id)
|
||
elseif button.spellslot and button.booktype then
|
||
start, duration = GetSpellCooldown(button.spellslot, button.booktype)
|
||
enable = 1
|
||
else
|
||
start, duration, enable = GetActionCooldown(button.id)
|
||
end
|
||
|
||
-- Nil-protect: GetActionCooldown can return nil during macro parsing/indexing
|
||
if start and duration then
|
||
CooldownFrame_SetTimer(button.cd, start, duration, enable or 1)
|
||
end
|
||
end
|
||
|
||
local _, active
|
||
local function ButtonIsActiveUpdate(button)
|
||
if not button then return end
|
||
|
||
if button.bar == 11 then
|
||
_, _, active, _ = GetShapeshiftFormInfo(button.id)
|
||
elseif button.bar == 12 then
|
||
_, _, _, _, active, _, _ = GetPetActionInfo(button.id)
|
||
else
|
||
active = IsCurrentAction(button.id) or IsAutoRepeatAction(button.id)
|
||
end
|
||
|
||
-- active border
|
||
if active then
|
||
if not button.border_active then
|
||
button.backdrop:SetBackdropBorderColor(cr,cg,cb,1)
|
||
button.border_active = true
|
||
end
|
||
|
||
button.active:Show()
|
||
else
|
||
if button.border_active then
|
||
button.backdrop:SetBackdropBorderColor(er,eg,eb,ea)
|
||
button.border_active = nil
|
||
end
|
||
|
||
button.active:Hide()
|
||
end
|
||
end
|
||
|
||
|
||
local function ButtonFullUpdate(button)
|
||
if not button then return end
|
||
|
||
ButtonMacroScan(button)
|
||
ButtonSlotUpdate(button)
|
||
ButtonRangeUpdate(button)
|
||
ButtonUsableUpdate(button)
|
||
ButtonCooldownUpdate(button)
|
||
ButtonIsActiveUpdate(button)
|
||
end
|
||
|
||
local function BarsEvent(self)
|
||
self = self or this
|
||
|
||
-- refresh only specific slots
|
||
if event == "ACTIONBAR_SLOT_CHANGED" and arg1 and arg1 ~= 0 then
|
||
updatecache[arg1] = true
|
||
return
|
||
end
|
||
|
||
-- run special refresh functions on next update
|
||
if special_events[event] then
|
||
eventcache[event] = true
|
||
return
|
||
end
|
||
|
||
-- handle aura events
|
||
if aura_events[event] then
|
||
for j=1,12 do
|
||
if self[11] and self[11][j] then
|
||
updatecache[self[11][j].slot] = true
|
||
end
|
||
end
|
||
return
|
||
end
|
||
|
||
-- handle pet events
|
||
if pet_events[event] then
|
||
for j=1,12 do
|
||
if self[12] and self[12][j] then
|
||
updatecache[self[12][j].slot] = true
|
||
end
|
||
end
|
||
return
|
||
end
|
||
|
||
-- handle global events
|
||
for id in pairs(buttoncache) do
|
||
updatecache[id] = true
|
||
end
|
||
end
|
||
|
||
local self, button, unlock
|
||
-- Main update loop with throttle for performance optimization
|
||
local function BarsUpdate(self)
|
||
self = self or this
|
||
|
||
-- Throttle for performance
|
||
if (this.tick_main or 0) > GetTime() then return end
|
||
this.tick_main = GetTime() + 0.025
|
||
|
||
-- update buttons whenever a button drag is assumed
|
||
AssumeButtonDrag()
|
||
|
||
if pfUI.unlock then
|
||
-- update all bars when entering unlock
|
||
if pfUI.unlock:IsShown() ~= unlock then
|
||
pfUI.bars:UpdateConfig()
|
||
unlock = pfUI.unlock:IsShown()
|
||
end
|
||
end
|
||
|
||
-- run cached usable usable actions
|
||
if eventcache["ACTIONBAR_UPDATE_USABLE"] then
|
||
eventcache["ACTIONBAR_UPDATE_USABLE"] = nil
|
||
for id, button in pairs(buttoncache) do
|
||
ButtonUsableUpdate(button)
|
||
end
|
||
end
|
||
|
||
-- run cached cooldown events
|
||
if eventcache["ACTIONBAR_UPDATE_COOLDOWN"] then
|
||
eventcache["ACTIONBAR_UPDATE_COOLDOWN"] = nil
|
||
for id, button in pairs(buttoncache) do
|
||
ButtonCooldownUpdate(button)
|
||
end
|
||
end
|
||
|
||
-- run cached action state events
|
||
if eventcache["ACTIONBAR_UPDATE_STATE"] then
|
||
eventcache["ACTIONBAR_UPDATE_STATE"] = nil
|
||
for id, button in pairs(buttoncache) do
|
||
ButtonIsActiveUpdate(button)
|
||
end
|
||
end
|
||
|
||
for id in pairs(updatecache) do
|
||
-- run updates based on slot
|
||
pfUI.bars.ButtonFullUpdate(buttoncache[id])
|
||
|
||
-- run updates on paging actionbar if required
|
||
for i=1,12 do
|
||
if pfUI.bars[1][i].id == id then
|
||
pfUI.bars.ButtonFullUpdate(pfUI.bars[1][i])
|
||
end
|
||
end
|
||
|
||
-- clear update cache
|
||
updatecache[id] = nil
|
||
end
|
||
|
||
if ( this.tick or .2) > GetTime() then return else this.tick = GetTime() + .2 end
|
||
|
||
for id, button in pairs(buttoncache) do
|
||
if button:IsShown() then ButtonRangeUpdate(button) end
|
||
end
|
||
end
|
||
|
||
-- create the main event and update handler for pfUI actionbars
|
||
local bars = CreateFrame("Frame", "pfActionBar", UIParent)
|
||
for event in pairs(special_events) do bars:RegisterEvent(event) end
|
||
for event in pairs(global_events) do bars:RegisterEvent(event) end
|
||
for event in pairs(aura_events) do bars:RegisterEvent(event) end
|
||
for event in pairs(pet_events) do bars:RegisterEvent(event) end
|
||
|
||
-- refresh actionbar buttons on event
|
||
bars:SetScript("OnEvent", BarsEvent)
|
||
|
||
-- update actionbar buttons
|
||
bars:SetScript("OnUpdate", BarsUpdate)
|
||
|
||
-- enable bar paging via secure functions
|
||
local function EnablePaging(bar)
|
||
if not bar.pager then
|
||
bar.pager = CreateFrame("Frame")
|
||
bar.pager:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||
bar.pager:RegisterEvent("UPDATE_BONUS_ACTIONBAR")
|
||
bar.pager:RegisterEvent("ACTIONBAR_PAGE_CHANGED")
|
||
bar.pager:SetScript("OnEvent", function()
|
||
for i=1, 10 do -- reload pageable bars
|
||
local pageable = C.bars["bar"..i] and C.bars["bar"..i].pageable == "1" and true or nil
|
||
_G.VIEWABLE_ACTION_BAR_PAGES[i] = pageable
|
||
end
|
||
|
||
local active = GetActiveBar()
|
||
for i=1,12 do
|
||
local id = i + (active-1)*12
|
||
bar[i].id = id
|
||
updatecache[i] = true
|
||
end
|
||
end)
|
||
end
|
||
end
|
||
|
||
local function SwitchBar(bar)
|
||
if _G.CURRENT_ACTIONBAR_PAGE ~= bar then
|
||
_G.CURRENT_ACTIONBAR_PAGE = bar
|
||
ChangeActionBarPage(bar)
|
||
end
|
||
end
|
||
|
||
local inCatForm = nil
|
||
local prowlActive = nil
|
||
|
||
-- Form IDs per vanilla 1.12 SpellShapeshiftForm.dbc.
|
||
local CAT_FORM = 1
|
||
local SHADOWFORM = 28
|
||
local function HasCatForm()
|
||
return GetShapeshiftFormID() == CAT_FORM and true or nil
|
||
end
|
||
|
||
local function InShadowform()
|
||
return GetShapeshiftFormID() == SHADOWFORM and true or nil
|
||
end
|
||
|
||
local function FullScan()
|
||
if class ~= "DRUID" then return nil end
|
||
inCatForm = HasCatForm()
|
||
prowlActive = inCatForm and IsStealthed() or nil
|
||
return prowlActive
|
||
end
|
||
|
||
-- pagemaster / meta page switch
|
||
do
|
||
local formpage, shift, ctrl, alt, default = 8, 6, 5, 3, 1
|
||
|
||
-- set temporary pagemaster bindings keybinds
|
||
if C.bars.pagemaster == "1" then
|
||
local modifier = { "ALT", "SHIFT", "CTRL" }
|
||
local buttons = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "+", "=", "´" }
|
||
local current = CURRENT_ACTIONBAR_PAGE
|
||
bars.pagemaster = bars.pagemaster or CreateFrame("Frame", "pfPageMaster", UIParent)
|
||
bars.pagemaster:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||
bars.pagemaster:SetScript("OnEvent", function()
|
||
for _,mod in pairs(modifier) do
|
||
for _,but in pairs(buttons) do
|
||
SetBinding(mod.."-"..but)
|
||
end
|
||
end
|
||
end)
|
||
end
|
||
|
||
-- setup page switch frame. `formpaging` is the shared "auto-page is
|
||
-- active" flag; druid prowl and priest shadowform each drive it, and
|
||
-- no character is ever both, so one flag covers both features.
|
||
local formpaging = nil
|
||
local pageswitch = CreateFrame("Frame", "pfActionBarPageSwitch", UIParent)
|
||
pageswitch:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||
pageswitch:RegisterEvent("UPDATE_SHAPESHIFT_FORM")
|
||
pageswitch:RegisterEvent("PLAYER_AURAS_CHANGED")
|
||
pageswitch:RegisterEvent("PLAYER_LOGOUT")
|
||
pageswitch:SetScript("OnEvent", function()
|
||
if event == "PLAYER_LOGOUT" then
|
||
this:UnregisterAllEvents()
|
||
this:SetScript("OnEvent", nil)
|
||
this:SetScript("OnUpdate", nil)
|
||
return
|
||
end
|
||
|
||
if class == "PRIEST" then
|
||
if event == "UPDATE_SHAPESHIFT_FORM" or event == "PLAYER_ENTERING_WORLD" then
|
||
formpaging = InShadowform()
|
||
end
|
||
return
|
||
end
|
||
|
||
if class ~= "DRUID" then return end
|
||
|
||
if event == "PLAYER_ENTERING_WORLD" then
|
||
formpaging = FullScan()
|
||
return
|
||
end
|
||
|
||
if event == "UPDATE_SHAPESHIFT_FORM" then
|
||
-- Authoritative form-change signal — recompute and short-circuit.
|
||
inCatForm = HasCatForm()
|
||
if not inCatForm then
|
||
prowlActive = nil
|
||
formpaging = nil
|
||
end
|
||
return
|
||
end
|
||
|
||
-- PLAYER_AURAS_CHANGED: form changes already went through
|
||
-- UPDATE_SHAPESHIFT_FORM above; this handler only watches prowl on/off.
|
||
if inCatForm then
|
||
if IsStealthed() then
|
||
prowlActive = true
|
||
formpaging = true
|
||
else
|
||
prowlActive = nil
|
||
formpaging = nil
|
||
end
|
||
end
|
||
end)
|
||
|
||
-- Eager prowl detection via Nampower SPELL_GO_SELF — zero-latency vs.
|
||
-- PLAYER_AURAS_CHANGED, which can land a frame or two behind the cast.
|
||
-- Form changes are no longer hooked here; UPDATE_SHAPESHIFT_FORM covers
|
||
-- those without needing a per-spellID watchlist.
|
||
local PROWL_IDS = { [5215] = true, [6783] = true, [9913] = true }
|
||
pfUI.libdebuff_spell_go_hooks = pfUI.libdebuff_spell_go_hooks or {}
|
||
pfUI.libdebuff_spell_go_hooks["actionbar_prowl"] = function(spellId)
|
||
if class ~= "DRUID" then return end
|
||
if PROWL_IDS[spellId] then
|
||
inCatForm = true
|
||
prowlActive = true
|
||
formpaging = true
|
||
end
|
||
end
|
||
pageswitch:SetScript("OnUpdate", function()
|
||
-- switch actionbar page depending on meta key that is pressed
|
||
if C.bars.pagemastershift == "1" and IsShiftKeyDown() then
|
||
SwitchBar(shift)
|
||
return
|
||
elseif C.bars.pagemasterctrl == "1" and IsControlKeyDown() then
|
||
SwitchBar(ctrl)
|
||
return
|
||
elseif C.bars.pagemasteralt == "1" and IsAltKeyDown() then
|
||
SwitchBar(alt)
|
||
return
|
||
elseif C.bars.pagemasteralt == "1" or C.bars.pagemasterctrl == "1" or C.bars.pagemastershift == "1" then
|
||
SwitchBar(default)
|
||
end
|
||
|
||
-- switch actionbar page while druid stealth / priest shadowform is active
|
||
if (class == "DRUID" and C.bars.druidstealth == "1")
|
||
or (class == "PRIEST" and C.bars.priestshadow == "1") then
|
||
if formpaging and _G.CURRENT_ACTIONBAR_PAGE == 1 then
|
||
SwitchBar(formpage)
|
||
elseif not formpaging and _G.CURRENT_ACTIONBAR_PAGE == 8 then
|
||
SwitchBar(default)
|
||
end
|
||
end
|
||
end)
|
||
end
|
||
|
||
local function CreateActionButton(parent, bar, button)
|
||
-- load config
|
||
local size = C.bars["bar"..bar].icon_size
|
||
local font = pfUI.media[C.bars.font]
|
||
local font_offset = tonumber(C.bars.font_offset)
|
||
|
||
local macro_size = tonumber(C.bars.macro_size)
|
||
local macro_color = { GetStringColor(C.bars.macro_color) }
|
||
|
||
local count_size = tonumber(C.bars.count_size)
|
||
local count_color = { GetStringColor(C.bars.count_color) }
|
||
|
||
local bind_size = tonumber(C.bars.bind_size)
|
||
local bind_color = { GetStringColor(C.bars.bind_color) }
|
||
|
||
local cd_size = tonumber(C.bars.cd_size)
|
||
|
||
local showempty = C.bars["bar"..bar].showempty
|
||
local showmacro = C.bars["bar"..bar].showmacro
|
||
local showkybind = C.bars["bar"..bar].showkeybind
|
||
local showcount = C.bars["bar"..bar].showcount
|
||
|
||
-- sanitize font sizes
|
||
if macro_size == 0 then macro_size = 1 end
|
||
if count_size == 0 then macro_size = 1 end
|
||
if bind_size == 0 then macro_size = 1 end
|
||
if cd_size == 0 then cd_size = nil end
|
||
|
||
local button_name = "pfActionBar" .. barnames[bar] .. "Button" .. button
|
||
|
||
local id = (bar-1)*12+button
|
||
local exists = _G[button_name] and true or nil
|
||
local f = _G[button_name] or CreateFrame("Button", button_name, parent)
|
||
|
||
-- no button available, create a new one
|
||
if not exists then
|
||
-- prepare the button for vanilla
|
||
if not f.HookScript then f.HookScript = HookScript end
|
||
f:SetScript("OnClick", ButtonClick)
|
||
|
||
if bar ~= 11 then
|
||
f:RegisterForDrag("LeftButton", "RightButton")
|
||
f:SetScript("OnDragStart", ButtonDrag)
|
||
f:SetScript("OnReceiveDrag", ButtonDragStop)
|
||
end
|
||
|
||
-- add mouseovers
|
||
f:SetScript("OnEnter", ButtonEnter)
|
||
f:SetScript("OnLeave", ButtonLeave)
|
||
|
||
-- add click animation handler
|
||
f:HookScript("OnClick", ButtonAnimate)
|
||
f.id = id
|
||
|
||
-- set a static slot
|
||
f.slot = id
|
||
|
||
-- cooldown
|
||
f.cd = CreateFrame("Model", f:GetName() .. "Cooldown", f, "CooldownFrameTemplate")
|
||
f.cd.pfCooldownStyleAnimation = 1
|
||
f.cd.pfCooldownType = "NOGCD"
|
||
f.cd.pfCooldownSize = cd_size
|
||
|
||
-- icon
|
||
f.icon = f:CreateTexture(button_name .. "Icon", "BACKGROUND")
|
||
f.icon:SetTexCoord(.08, .92, .08, .92)
|
||
f.icon:SetAllPoints()
|
||
|
||
-- animation
|
||
f.animation = CreateFrame("Frame", button_name .. "Animation", f)
|
||
f.animation.parent = f
|
||
f.animation:SetPoint("CENTER", 0, 0)
|
||
f.animation:Hide()
|
||
f.animation.tex = f.animation:CreateTexture(button_name .. "AnimationTexture", "BACKGROUND")
|
||
f.animation.tex:SetTexCoord(.08, .92, .08, .92)
|
||
f.animation.tex:SetAllPoints()
|
||
|
||
if bar ~= 11 and bar ~= 12 then
|
||
-- equipped item
|
||
f.equipped = f:CreateTexture(nil, "BORDER")
|
||
f.equipped:SetAllPoints()
|
||
f.equipped:Hide()
|
||
elseif bar == 12 then
|
||
f.autocast = CreateFrame("Model", nil, f)
|
||
f.autocast:SetAllPoints()
|
||
f.autocast:SetModel("Interface\\Buttons\\UI-AutoCastButton.mdx")
|
||
f.autocast:SetSequence(0)
|
||
f.autocast:SetSequenceTime(0, 0)
|
||
f.autocast:Hide()
|
||
|
||
f.autocastable = f:CreateTexture(nil, "BORDER")
|
||
f.autocastable:SetAllPoints()
|
||
f.autocastable:SetTexture("Interface\\Buttons\\UI-AutoCastableOverlay")
|
||
f.autocastable:SetTexCoord(.25, .75, .25, .75)
|
||
end
|
||
|
||
-- macro
|
||
f.macro = f:CreateFontString(button_name .. "Name", "LOW", "GameFontNormal")
|
||
|
||
-- keybind
|
||
f.keybind = f:CreateFontString(nil, "LOW", "GameFontNormal")
|
||
|
||
-- itemcount
|
||
f.count = f:CreateFontString(button_name .. "Count", "LOW", "GameFontNormal")
|
||
|
||
-- highlight
|
||
f.highlight = CreateFrame("Frame", nil, f)
|
||
f.highlight:SetBackdrop(backdrop_highlight)
|
||
f.highlight:SetBackdropBorderColor(1,1,1,.8)
|
||
f.highlight:SetAllPoints()
|
||
f.highlight:Hide()
|
||
|
||
-- active
|
||
f.active = CreateFrame("Frame", nil, f)
|
||
f.active:SetBackdrop(backdrop_highlight)
|
||
f.active:SetBackdropBorderColor(1,1,.5,1)
|
||
f.active:SetAllPoints()
|
||
f.active:Hide()
|
||
|
||
-- add to buttoncache
|
||
buttoncache[id] = f
|
||
end
|
||
|
||
-- set keydown option
|
||
if C.bars.keydown == "1" then
|
||
f:RegisterForClicks("LeftButtonDown", "RightButtonDown")
|
||
else
|
||
f:RegisterForClicks("LeftButtonUp", "RightButtonUp")
|
||
end
|
||
|
||
-- set animation
|
||
f.animation:SetScript("OnUpdate", button_animations[C.bars.animation])
|
||
|
||
-- pet autocast
|
||
if bar == 12 then
|
||
f.autocast:SetScale(C.bars["bar"..bar].icon_size / 25)
|
||
f.autocast:SetAlpha(.10)
|
||
end
|
||
|
||
-- macro options
|
||
if showmacro == "1" then f.macro:Show() else f.macro:Hide() end
|
||
SetAllPointsOffset(f.macro, f, font_offset, -font_offset)
|
||
f.macro:SetFont(font, macro_size, "OUTLINE")
|
||
f.macro:SetTextColor(unpack(macro_color))
|
||
f.macro:SetJustifyH("LEFT")
|
||
f.macro:SetJustifyV("BOTTOM")
|
||
|
||
-- keybind options
|
||
if showkybind == "1" then f.keybind:Show() else f.keybind:Hide() end
|
||
SetAllPointsOffset(f.keybind, f, font_offset, -font_offset)
|
||
f.keybind:SetFont(font, bind_size, "OUTLINE")
|
||
f.keybind:SetTextColor(unpack(bind_color))
|
||
f.keybind:SetJustifyH("RIGHT")
|
||
f.keybind:SetJustifyV("TOP")
|
||
f.keybind:SetNonSpaceWrap(false)
|
||
|
||
-- item count options
|
||
if showcount == "1" then f.count:Show() else f.count:Hide() end
|
||
SetAllPointsOffset(f.count, f, font_offset, -font_offset)
|
||
f.count:SetFont(font, count_size, "OUTLINE")
|
||
f.count:SetTextColor(unpack(count_color))
|
||
f.count:SetJustifyH("RIGHT")
|
||
f.count:SetJustifyV("BOTTOM")
|
||
|
||
-- macro spell scan (disabled when macro addons are loaded)
|
||
if C.bars.macroscan == "0" or pfUI:MacroAddonsLoaded() then
|
||
f.scanmacro, f.spellslot, f.booktype = nil, nil, nil
|
||
else
|
||
f.scanmacro = true
|
||
end
|
||
|
||
-- range glow color
|
||
f.rangeColor = GetStringColorObject(C.bars.rangecolor)
|
||
|
||
-- out of mana color
|
||
f.oomColor = GetStringColorObject(C.bars.oomcolor)
|
||
|
||
-- not usable color
|
||
f.naColor = GetStringColorObject(C.bars.nacolor)
|
||
|
||
-- equipped color
|
||
if f.equipped then
|
||
f.equipped:SetTexture(GetStringColor(C.bars.eqcolor))
|
||
end
|
||
|
||
-- general appearance
|
||
f.showempty = showempty == "1" and true or nil
|
||
f:SetSize(size, size)
|
||
CreateBackdrop(f, border)
|
||
|
||
return f
|
||
end
|
||
|
||
local function CreateActionBar(i)
|
||
-- load config
|
||
local buttonbasename = "pfActionBar" .. barnames[i] .. "Button"
|
||
local enable = C.bars["bar"..i].enable
|
||
local size = C.bars["bar"..i].icon_size
|
||
local spacing = C.bars["bar"..i].spacing
|
||
local background = C.bars["bar"..i].background
|
||
local formfactor = C.bars["bar"..i].formfactor
|
||
local uneven = C.bars["bar"..i].uneven or "Down"
|
||
local fillmode = C.bars["bar"..i].fillmode or "auto"
|
||
-- validate uneven against bar shape and reset if incompatible
|
||
local _, _, fcols2, frows2 = string.find(tostring(formfactor or ""), "(%d+)%s*x%s*(%d+)")
|
||
fcols2, frows2 = tonumber(fcols2), tonumber(frows2)
|
||
local f_is_square = fcols2 == 3 and frows2 == 3
|
||
local f_is_vertical = fcols2 and fcols2 <= 3 and not f_is_square
|
||
local f_is_horizontal = frows2 and frows2 <= 3 and not f_is_square
|
||
if f_is_vertical then
|
||
if uneven ~= "Up" and uneven ~= "Down" then uneven = "Down" end
|
||
elseif f_is_horizontal then
|
||
if uneven ~= "Left" and uneven ~= "Right" then uneven = "Down" end
|
||
end
|
||
C.bars["bar"..i].uneven = uneven
|
||
local autohide = C.bars["bar"..i].autohide
|
||
local hide_time = C.bars["bar"..i].hide_time
|
||
local hide_combat = C.bars["bar"..i].hide_combat == "1" and true or nil
|
||
|
||
local buttons = tonumber(C.bars["bar"..i].buttons) or 12
|
||
if i == 11 and bars[i] then -- shapeshift buttons
|
||
buttons = GetNumShapeshiftForms()
|
||
elseif i == 12 and bars[i] then -- pet buttons
|
||
buttons = NUM_PET_ACTION_SLOTS
|
||
elseif i == 12 or i == 11 then
|
||
-- Fallback to 10 buttons on shapeshift and petbar during initialization.
|
||
-- This lets us load bars that aren't yet available for your class or level.
|
||
buttons = 10
|
||
end
|
||
|
||
-- don't process empty bars
|
||
if buttons == 0 then
|
||
bars[i]:Hide()
|
||
return
|
||
end
|
||
|
||
-- the stored layout is invalid, temporary fallback
|
||
local _, _, fcols, frows = string.find(tostring(formfactor or ""), "(%d+)%s*x%s*(%d+)")
|
||
if not ((fcols and frows) or pfGridmath[buttons][BarLayoutFormfactor(formfactor)]) then
|
||
formfactor = BarLayoutOptions(buttons)[1]
|
||
end
|
||
|
||
local font = pfUI.font_unit
|
||
local font_size = C.global.font_unit_size
|
||
|
||
local realsize = size+border*2
|
||
|
||
-- create frame
|
||
local init = not bars[i]
|
||
bars[i] = bars[i] or CreateFrame("Frame", "pfActionBar" .. barnames[i], UIParent)
|
||
bars[i]:SetID(i)
|
||
|
||
-- autohide
|
||
if autohide == "1" then
|
||
EnableAutohide(bars[i], tonumber(hide_time), hide_combat)
|
||
else
|
||
DisableAutohide(bars[i])
|
||
end
|
||
|
||
-- apply visible settings
|
||
if enable == "1" then
|
||
-- handle pet bar
|
||
if i == 12 then
|
||
-- only show when pet actions exists
|
||
if PetHasActionBar() or pfUI.unlock and pfUI.unlock:IsShown() then
|
||
bars[i]:Show()
|
||
else
|
||
bars[i]:Hide()
|
||
end
|
||
|
||
-- show/hide petbar on petbar updates
|
||
if init then
|
||
bars[i]:RegisterEvent("PET_BAR_UPDATE")
|
||
bars[i]:SetScript("OnEvent", function()
|
||
-- hide obsolete buttons
|
||
for i=1, NUM_PET_ACTION_SLOTS do
|
||
if not PetHasActionBar() and bars[12][i] then
|
||
bars[12][i]:Hide()
|
||
end
|
||
end
|
||
-- refresh layout
|
||
CreateActionBar(12)
|
||
end)
|
||
end
|
||
|
||
-- handle shapeshift bar
|
||
elseif i == 11 then
|
||
-- only show when shapeshifts exist
|
||
if GetNumShapeshiftForms() > 0 then
|
||
bars[i]:Show()
|
||
else
|
||
bars[i]:Hide()
|
||
end
|
||
|
||
-- update shapeshift bar when amount of spells changes
|
||
if init then
|
||
bars[i]:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||
bars[i]:RegisterEvent("UPDATE_BONUS_ACTIONBAR")
|
||
bars[i]:RegisterEvent("UPDATE_SHAPESHIFT_FORMS")
|
||
bars[i]:SetScript("OnEvent", function()
|
||
local count = GetNumShapeshiftForms()
|
||
if count ~= this.lastCount then
|
||
this.lastCount = count
|
||
|
||
-- hide obsolete buttons
|
||
for i=1, NUM_SHAPESHIFT_SLOTS do
|
||
if i > GetNumShapeshiftForms() and bars[11][i] then
|
||
bars[11][i]:Hide()
|
||
end
|
||
end
|
||
|
||
-- create new buttons and refresh layout
|
||
CreateActionBar(11)
|
||
end
|
||
end)
|
||
end
|
||
|
||
-- regular bars
|
||
else
|
||
bars[i]:Show()
|
||
end
|
||
|
||
elseif enable == "0" then
|
||
bars[i]:UnregisterAllEvents()
|
||
bars[i]:Hide()
|
||
end
|
||
|
||
-- create action buttons
|
||
local maxbuttons = (i == 11 or i == 12) and 10 or 12
|
||
for j=1,maxbuttons do
|
||
bars[i][j] = CreateActionButton(bars[i], i, j)
|
||
bars[i][j].bar = i
|
||
|
||
BarButtonAnchor(bars[i][j], buttonbasename, j, buttons, formfactor, size, border, spacing, uneven, fillmode)
|
||
bars[i][j]:ClearAllPoints()
|
||
bars[i][j]:SetPoint(unpack(bars[i][j]._anchor))
|
||
bars[i][j]:Show()
|
||
|
||
if i > 10 then
|
||
bars[i][j].id = j
|
||
end
|
||
|
||
-- refresh button
|
||
updatecache[bars[i][j].slot] = true
|
||
end
|
||
|
||
for j=buttons+1,12 do
|
||
if bars[i][j] then
|
||
bars[i][j]:Hide()
|
||
end
|
||
end
|
||
|
||
-- enable paging for the first actionbar
|
||
if i == 1 then
|
||
EnablePaging(bars[i])
|
||
end
|
||
|
||
-- adjust actionbar size
|
||
BarLayoutSize(bars[i], buttons, formfactor, size, border, spacing, uneven, fillmode)
|
||
bars[i]:SetSize(bars[i]._size[1], bars[i]._size[2])
|
||
bars[i]:ClearAllPoints()
|
||
if i == 1 then -- main
|
||
bars[i]:SetPoint("BOTTOM", 0, 2*border)
|
||
elseif i == 3 then -- left
|
||
bars[i]:SetPoint("BOTTOMLEFT", bars[1], "BOTTOMRIGHT", 3*border, 0)
|
||
elseif i == 4 then -- vertical
|
||
bars[i]:SetPoint("RIGHT", -2*border, 0)
|
||
elseif i == 5 then -- right
|
||
bars[i]:SetPoint("BOTTOMRIGHT", bars[1], "BOTTOMLEFT", -3*border, 0)
|
||
elseif i == 6 then -- top
|
||
bars[i]:SetPoint("BOTTOM", bars[1], "TOP", 0, -spacing)
|
||
elseif i == 11 then -- stances
|
||
bars[i]:SetPoint("BOTTOM", bars[6], "TOP", 0, 3*border)
|
||
elseif i == 12 then -- pet
|
||
bars[i]:SetPoint("BOTTOM", bars[6], "TOP", 0, 3*border)
|
||
|
||
-- make stance bar dodge by default
|
||
bars[i]:SetScript("OnShow", function()
|
||
if pfUI.unlock and pfUI.unlock:IsShown() then return end
|
||
if bars[11] and bars[11]:IsShown() then
|
||
bars[11]:ClearAllPoints()
|
||
bars[11]:SetPoint("BOTTOM", bars[12], "TOP", 0, 3*border)
|
||
UpdateMovable(bars[11], true)
|
||
end
|
||
end)
|
||
|
||
-- restore old stance bar position
|
||
bars[i]:SetScript("OnHide", function()
|
||
if pfUI.unlock and pfUI.unlock:IsShown() then return end
|
||
if bars[11] and bars[11]:IsShown() then
|
||
bars[11]:ClearAllPoints()
|
||
bars[11]:SetPoint("BOTTOM", bars[6], "TOP", 0, 3*border)
|
||
UpdateMovable(bars[11], true)
|
||
end
|
||
end)
|
||
else -- others
|
||
bars[i]:SetPoint("TOP", 0, -i*50)
|
||
end
|
||
|
||
UpdateMovable(bars[i])
|
||
|
||
-- apply backdrop settings
|
||
if background == "1" then
|
||
CreateBackdrop(bars[i], border)
|
||
CreateBackdropShadow(bars[i])
|
||
bars[i].backdrop:Show()
|
||
elseif bars[i].backdrop then
|
||
bars[i].backdrop:Hide()
|
||
end
|
||
|
||
-- share backdrop of main and top actionbar
|
||
if bars[6] and bars[1] then
|
||
bars[6].OnMove = bars[6].OnMove or function()
|
||
bars[1].mergedBackdrop = bars[1].mergedBackdrop or CreateFrame("Frame", nil, UIParent)
|
||
bars[1].mergedBackdrop:SetPoint("TOPLEFT", bars[6], "TOPLEFT", 0, 0)
|
||
bars[1].mergedBackdrop:SetPoint("BOTTOMRIGHT", bars[1], "BOTTOMRIGHT", 0, 0)
|
||
CreateBackdrop(bars[1].mergedBackdrop)
|
||
|
||
local _, anchor, _ = bars[6]:GetPoint()
|
||
if anchor == bars[1] and C.bars.bar1.enable == "1"
|
||
and C.bars.bar1.enable == "1" and C.bars.bar6.enable == "1"
|
||
and C.bars.bar1.background == "1" and C.bars.bar6.background == "1"
|
||
and C.bars.bar1.autohide == "0" and C.bars.bar6.autohide == "0"
|
||
and C.bars.bar1.icon_size == C.bars.bar6.icon_size
|
||
and C.bars.bar1.spacing == C.bars.bar6.spacing
|
||
and C.bars.bar1.formfactor == C.bars.bar6.formfactor
|
||
and C.bars.bar1.buttons == C.bars.bar6.buttons
|
||
then
|
||
bars[1].mergedBackdrop:Show()
|
||
|
||
if C.bars.bar1.background == "1" and bars[1].backdrop then
|
||
bars[1].backdrop:Hide()
|
||
end
|
||
|
||
if C.bars.bar6.background == "1" and bars[6].backdrop then
|
||
bars[6].backdrop:Hide()
|
||
end
|
||
else
|
||
bars[1].mergedBackdrop:Hide()
|
||
|
||
if C.bars.bar1.background == "1" and bars[1].backdrop then
|
||
bars[1].backdrop:Show()
|
||
end
|
||
|
||
if C.bars.bar6.background == "1" and bars[6].backdrop then
|
||
bars[6].backdrop:Show()
|
||
end
|
||
end
|
||
end
|
||
bars[6].OnMove()
|
||
end
|
||
end
|
||
|
||
-- create actionbars
|
||
pfUI.bars = bars
|
||
pfUI.bars.update = updatecache
|
||
pfUI.bars.buttons = buttoncache
|
||
pfUI.bars.ButtonFullUpdate = ButtonFullUpdate
|
||
pfUI.bars.ButtonEnter = ButtonEnter
|
||
pfUI.bars.ButtonLeave = ButtonLeave
|
||
|
||
pfUI.bars.UpdateGrid = function(self, state, typ)
|
||
if not typ then
|
||
showgrid = state
|
||
|
||
for id in pairs(buttoncache) do
|
||
updatecache[id] = true
|
||
end
|
||
elseif typ == "PET" then
|
||
showgrid_pet = state
|
||
for slot=133,142 do
|
||
updatecache[slot] = true
|
||
end
|
||
end
|
||
end
|
||
|
||
pfUI.bars.UpdateConfig = function(self)
|
||
for i=1,12 do
|
||
CreateActionBar(i)
|
||
end
|
||
end
|
||
|
||
pfUI.bars:UpdateConfig()
|
||
|
||
-- Localize custom keybinds for additional actionbars (see Bindings.xml)
|
||
local names = {
|
||
["PAGING"] = T["Paging Actionbar"],
|
||
["STANCEONE"] = T["Stance Bar 1"],
|
||
["STANCETWO"] = T["Stance Bar 2"],
|
||
["STANCETHREE"] = T["Stance Bar 3"],
|
||
["STANCEFOUR"] = T["Stance Bar 4"],
|
||
}
|
||
|
||
for name, loc in pairs(names) do
|
||
_G["BINDING_HEADER_PFBAR"..name] = T["Action Bar"] .. " " .. loc
|
||
for i=1,12 do
|
||
_G["BINDING_NAME_PF" .. name .. i] = loc .. " " .. T["Button"] .. " " .. i
|
||
end
|
||
end
|
||
|
||
-- Map Keybinds to button clicks
|
||
function _G.pfActionButton(slot, slfcast, opt)
|
||
if ChatFrameEditBox:IsShown() then return end
|
||
|
||
local bar, button = 1, slot
|
||
|
||
-- determine the proper bar and button
|
||
if opt and blizzbarmapping[opt] then
|
||
bar = blizzbarmapping[opt]
|
||
elseif slot > 12 then
|
||
bar, button = ceil(slot/12), mod(slot, 12)
|
||
button = button == 0 and 12 or button
|
||
end
|
||
|
||
local frame = bars[bar][button]
|
||
if frame then
|
||
frame.slfcast = slfcast
|
||
frame:Click()
|
||
end
|
||
end
|
||
|
||
-- Set keybinds to all actionbuttons
|
||
-- In order to be able to reuse already defined keybinds, we need to remap
|
||
-- existing button functions to pfUI. We need to get rid of the blizzard calls
|
||
-- to avoid having them call texture changes and errors due to missing buttons
|
||
_G.ActionButtonDown = pfActionButton
|
||
_G.ActionButtonUp = pfActionButton
|
||
_G.BonusActionButtonDown = function(slot) pfActionButton(slot, nil, "BonusActionBar") end
|
||
_G.BonusActionButtonUp = function(slot) pfActionButton(slot, nil, "BonusActionBar") end
|
||
_G.MultiActionButtonDown = function(bar, slot, slf) pfActionButton(slot, slf, bar) end
|
||
_G.MultiActionButtonUp = function(bar, slot, slf) pfActionButton(slot, slf, bar) end
|
||
_G.ShapeshiftBar_ChangeForm = function(slot) pfActionButton(slot, nil, "ShapeShiftBar") end
|
||
|
||
-- handle drag-drop grid
|
||
local grid = CreateFrame("Frame")
|
||
grid:RegisterEvent("ACTIONBAR_SHOWGRID")
|
||
grid:RegisterEvent("ACTIONBAR_HIDEGRID")
|
||
grid:RegisterEvent("PET_BAR_SHOWGRID")
|
||
grid:RegisterEvent("PET_BAR_HIDEGRID")
|
||
|
||
grid:SetScript("OnEvent", function()
|
||
if event == "ACTIONBAR_SHOWGRID" then
|
||
pfUI.bars:UpdateGrid(1)
|
||
elseif event == "ACTIONBAR_HIDEGRID" then
|
||
pfUI.bars:UpdateGrid(0)
|
||
elseif event == "PET_BAR_SHOWGRID" then
|
||
pfUI.bars:UpdateGrid(1, "PET")
|
||
elseif event == "PET_BAR_HIDEGRID" then
|
||
pfUI.bars:UpdateGrid(0, "PET")
|
||
end
|
||
end)
|
||
|
||
-- reagent counter
|
||
if C.bars.reagents == "1" then
|
||
local reagent_slots = { } -- action slot -> first reagent itemID
|
||
local reagent_counts = { } -- itemID -> count in player's inventory
|
||
|
||
-- For a given action slot, resolve the spell's first reagent itemID
|
||
-- via GetActionInfo + C_Spell.GetSpellReagents. Macro actions and
|
||
-- bag-item actions are skipped (their reagent resolution would need a
|
||
-- macro-body parse / item-effect lookup that we don't bother with).
|
||
local function UpdateSlot(slot)
|
||
local newID = nil
|
||
if HasAction(slot) then
|
||
local kind, spellID = GetActionInfo(slot)
|
||
if kind == "spell" and spellID then
|
||
local reagents = C_Spell.GetSpellReagents(spellID)
|
||
if reagents and reagents[1] then
|
||
newID = reagents[1].itemID
|
||
end
|
||
end
|
||
end
|
||
|
||
if reagent_slots[slot] ~= newID then
|
||
reagent_slots[slot] = newID
|
||
if newID then
|
||
reagent_counts[newID] = reagent_counts[newID] or C_Item.GetItemCount(newID)
|
||
end
|
||
updatecache[slot] = true
|
||
end
|
||
end
|
||
|
||
-- Recount every tracked reagent and flag its buttons for a refresh.
|
||
local RecountReagents = function()
|
||
for itemID in pairs(reagent_counts) do
|
||
reagent_counts[itemID] = C_Item.GetItemCount(itemID)
|
||
end
|
||
for slot in pairs(reagent_slots) do
|
||
updatecache[slot] = true
|
||
end
|
||
end
|
||
|
||
local reagentcounter = CreateFrame("Frame", "pfReagentCounter", UIParent)
|
||
reagentcounter:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||
reagentcounter:RegisterEvent("ACTIONBAR_SLOT_CHANGED")
|
||
reagentcounter:RegisterEvent("BAG_UPDATE_DELAYED")
|
||
reagentcounter:SetScript("OnEvent", function()
|
||
if event == "ACTIONBAR_SLOT_CHANGED" then
|
||
-- arg1 is the changed slot; 0 (or nil) means "all slots"
|
||
if arg1 and arg1 > 0 then
|
||
UpdateSlot(arg1)
|
||
else
|
||
for slot = 1, 120 do UpdateSlot(slot) end
|
||
end
|
||
elseif event == "BAG_UPDATE_DELAYED" then
|
||
-- inventory changed: refresh the counts we already track
|
||
RecountReagents()
|
||
else
|
||
-- PLAYER_ENTERING_WORLD: seed the full reagent map (UpdateSlot seeds
|
||
-- each new reagent's count; a BAG_UPDATE_DELAYED follows during login)
|
||
for slot = 1, 120 do UpdateSlot(slot) end
|
||
end
|
||
end)
|
||
|
||
function IsReagentAction(slot)
|
||
return reagent_slots[slot] and true or nil
|
||
end
|
||
|
||
function GetReagentCount(slot)
|
||
return reagent_counts[reagent_slots[slot]]
|
||
end
|
||
end
|
||
end) |