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.
204 lines
5.6 KiB
204 lines
5.6 KiB
local _, addon = ...
|
|
|
|
--[[ namespace:header
|
|
In each example `namespace` refers to the 2nd value of the addon vararg, e.g:
|
|
|
|
```lua
|
|
local _, namespace = ...
|
|
```
|
|
--]]
|
|
|
|
-- game version API
|
|
local _, _, _, interfaceVersion = GetBuildInfo()
|
|
--[[ namespace:IsRetail()
|
|
Checks if the current client is running the "retail" version.
|
|
--]]
|
|
function addon:IsRetail()
|
|
return interfaceVersion > 100000
|
|
end
|
|
|
|
--[[ namespace:IsClassicEra()
|
|
Checks if the current client is running the "classic era" version (e.g. vanilla).
|
|
--]]
|
|
function addon:IsClassicEra()
|
|
return interfaceVersion < 20000
|
|
end
|
|
|
|
--[[ namespace:IsClassic()
|
|
Checks if the current client is running the "classic" version.
|
|
--]]
|
|
function addon:IsClassic()
|
|
return not addon:IsRetail() and not addon:IsClassicEra()
|
|
end
|
|
|
|
--[[ namespace:ArgCheck(arg, argIndex, type[, type...])
|
|
Checks if the argument `arg` at position `argIndex` is of type(s).
|
|
--]]
|
|
function addon:ArgCheck(arg, argIndex, ...)
|
|
assert(type(argIndex) == 'number', 'Bad argument #2 to \'ArgCheck\' (number expected, got ' .. type(argIndex) .. ')')
|
|
|
|
for index = 1, select('#', ...) do
|
|
if type(arg) == select(index, ...) then
|
|
return
|
|
end
|
|
end
|
|
|
|
local types = string.join(', ', ...)
|
|
local name = debugstack(2, 2, 0):match(': in function [`<](.-)[\'>]')
|
|
error(string.format('Bad argument #%d to \'%s\' (%s expected, got %s)', argIndex, name, types, type(arg)), 3)
|
|
end
|
|
|
|
-- easy frame "removal"
|
|
local hidden = CreateFrame('Frame')
|
|
hidden:Hide()
|
|
|
|
--[[ namespace:Hide(_object_[, _child_,...])
|
|
Forcefully hide an `object`, or its `child`.
|
|
It will recurse down to the last child if provided.
|
|
--]]
|
|
function addon:Hide(object, ...)
|
|
if type(object) == 'string' then
|
|
object = _G[object]
|
|
end
|
|
|
|
if ... then
|
|
-- iterate through arguments, they're children referenced by key
|
|
for index = 1, select('#', ...) do
|
|
object = object[select(index, ...)]
|
|
end
|
|
end
|
|
|
|
if object then
|
|
object:SetParent(hidden)
|
|
object.SetParent = nop
|
|
|
|
if object.UnregisterAllEvents then
|
|
object:UnregisterAllEvents()
|
|
end
|
|
end
|
|
end
|
|
|
|
-- random utilities
|
|
do
|
|
local GUID_PATTERN = '%w+%-.-%-.-%-.-%-.-%-(.-)%-'
|
|
--[[ namespace:ExtractIDFromGUID(_guid_)
|
|
Returns the integer `id` from the given [`guid`](https://warcraft.wiki.gg/wiki/GUID).
|
|
--]]
|
|
function addon:ExtractIDFromGUID(guid)
|
|
return guid and tonumber(guid:match(GUID_PATTERN))
|
|
end
|
|
end
|
|
|
|
--[[ namespace:GetNPCID(_unit_)
|
|
Returns the integer `id` of the given [`unit`](https://warcraft.wiki.gg/wiki/UnitId).
|
|
--]]
|
|
function addon:GetNPCID(unit)
|
|
if unit and UnitExists(unit) then
|
|
local npcGUID = UnitGUID(unit)
|
|
return npcGUID and addon:ExtractIDFromGUID(npcGUID), npcGUID
|
|
end
|
|
end
|
|
|
|
do
|
|
local ITEM_LINK_FORMAT = '|Hitem:%d|h'
|
|
--[[ namespace:GetItemLinkFromID(_itemID_)
|
|
Generates an [item link](https://warcraft.wiki.gg/wiki/ItemLink) from an `itemID`.
|
|
This is a crude generation and won't have valid data for complex items.
|
|
--]]
|
|
function addon:GetItemLinkFromID(itemID)
|
|
return ITEM_LINK_FORMAT:format(itemID)
|
|
end
|
|
end
|
|
|
|
--[[ namespace:GetPlayerMapID()
|
|
Returns the ID of the current map the zone the player is located in.
|
|
--]]
|
|
function addon:GetPlayerMapID()
|
|
-- TODO: maybe use HBD data if it's available
|
|
return C_Map.GetBestMapForUnit('player') or -1
|
|
end
|
|
|
|
--[[ namespace:GetPlayerPosition(_mapID_)
|
|
Returns the `x` and `y` coordinates for the player in the given `mapID` (if they are valid).
|
|
--]]
|
|
function addon:GetPlayerPosition(mapID)
|
|
local pos = C_Map.GetPlayerMapPosition(mapID, 'player')
|
|
if pos then
|
|
return pos:GetXY()
|
|
end
|
|
end
|
|
|
|
--[[ namespace:tsize(_table_)
|
|
Returns the number of entries in the `table`.
|
|
Works for associative tables as opposed to `#table`.
|
|
--]]
|
|
function addon:tsize(tbl)
|
|
-- would really like Lua 5.2 for this
|
|
local size = 0
|
|
for _ in next, tbl do
|
|
size = size + 1
|
|
end
|
|
return size
|
|
end
|
|
|
|
do
|
|
local function auraSlotsWrapper(unit, spellID, token, ...)
|
|
local slot, data
|
|
for index = 1, select('#', ...) do
|
|
slot = select(index, ...)
|
|
data = C_UnitAuras.GetAuraDataBySlot(unit, slot)
|
|
if spellID == data.spellId and data.sourceUnit then
|
|
return nil, data
|
|
end
|
|
end
|
|
|
|
return token
|
|
end
|
|
|
|
--[[ namespace:GetUnitAura(_unit_, _spellID_, _filter_)
|
|
Returns the aura by `spellID` on the [`unit`](https://warcraft.wiki.gg/wiki/UnitId), if it exists.
|
|
|
|
* [`unitID`](https://warcraft.wiki.gg/wiki/UnitId)
|
|
* `spellID` - spell ID to check for
|
|
* `filter` - aura filter, see [UnitAura](https://warcraft.wiki.gg/wiki/API_C_UnitAuras.GetAuraDataByIndex#Filters)
|
|
--]]
|
|
function addon:GetUnitAura(unit, spellID, filter)
|
|
local token, data
|
|
repeat
|
|
token, data = auraSlotsWrapper(unit, spellID, C_UnitAuras.GetAuraSlots(unit, filter, nil, token))
|
|
until token == nil
|
|
|
|
return data
|
|
end
|
|
end
|
|
|
|
--[[ namespace:CreateColor(r, g, b[, a])
|
|
Wrapper for CreateColor that can handle >1-255 range as well.
|
|
Alpha (`a`) will always be in the 0-1 range.
|
|
--]]
|
|
--[[ namespace:CreateColor(hex)
|
|
Wrapper for CreateColor that can handle hex colors (both `RRGGBB` and `AARRGGBB`).
|
|
--]]
|
|
function addon:CreateColor(r, g, b, a)
|
|
if type(r) == 'string' then
|
|
-- load from hex
|
|
local hex = r:gsub('#', '')
|
|
if #hex == 8 then
|
|
-- prefixed with alpha
|
|
a = tonumber(hex:sub(1, 2), 16) / 255
|
|
r = tonumber(hex:sub(3, 4), 16) / 255
|
|
g = tonumber(hex:sub(5, 6), 16) / 255
|
|
b = tonumber(hex:sub(7, 8), 16) / 255
|
|
elseif #hex == 6 then
|
|
r = tonumber(hex:sub(1, 2), 16) / 255
|
|
g = tonumber(hex:sub(3, 4), 16) / 255
|
|
b = tonumber(hex:sub(5, 6), 16) / 255
|
|
end
|
|
elseif r > 1 or g > 1 or b > 1 then
|
|
r = r / 255
|
|
g = g / 255
|
|
b = b / 255
|
|
end
|
|
|
|
return CreateColor(r, g, b, a)
|
|
end
|
|
|