1
0
Bifurcation 0
Ce dépôt a été archivé le 2020-03-15. Vous pouvez voir ses fichiers ou le cloner, mais pas ouvrir de ticket ou de demandes d'ajout, ni soumettre de changements.
questhelperredux/libs/LibRangeCheck-2.0/LibRangeCheck-2.0.lua

1034 lignes
34 Kio
Lua
Brut Vue normale Historique

2010-10-24 23:17:33 +02:00
--[[
Name: LibRangeCheck-2.0
Revision: $Revision: 98 $
Author(s): mitch0
Website: http://www.wowace.com/projects/librangecheck-2-0/
Description: A range checking library based on interact distances and spell ranges
Dependencies: LibStub
License: Public Domain
]]
--- LibRangeCheck-2.0 provides an easy way to check for ranges and get suitable range checking functions for specific ranges.\\
-- The checkers use spell and item range checks, or interact based checks for special units where those two cannot be used.\\
-- The lib handles the refreshing of checker lists in case talents / spells / glyphs change and in some special cases when equipment changes (for example some of the mage pvp gloves change the range of the Fire Blast spell), and also handles the caching of items used for item-based range checks.\\
-- A callback is provided for those interested in checker changes.
-- @usage
-- local rc = LibStub("LibRangeCheck-2.0")
--
-- rc.RegisterCallback(self, rc.CHECKERS_CHANGED, function() print("need to refresh my stored checkers") end)
--
-- local minRange, maxRange = rc:GetRange('target')
-- if not minRange then
-- print("cannot get range estimate for target")
-- elseif not maxRange then
-- print("target is over " .. minRange .. " yards")
-- else
-- print("target is between " .. minRange .. " and " .. maxRange .. " yards")
-- end
--
-- local meleeChecker = rc:GetFriendMaxChecker(rc.MeleeRange) -- 5 yds
-- for i = 1, 4 do
-- -- TODO: check if unit is valid, etc
-- if meleeChecker("party" .. i) then
-- print("Party member " .. i .. " is in Melee range")
-- end
-- end
--
-- local safeDistanceChecker = rc:GetHarmMinChecker(30)
-- -- negate the result of the checker!
-- local isSafelyAway = not safeDistanceChecker('target')
--
-- @class file
-- @name LibRangeCheck-2.0
local MAJOR_VERSION = "LibRangeCheck-2.0"
local MINOR_VERSION = tonumber(("$Revision: 98 $"):match("%d+")) + 100000
local lib, oldminor = LibStub:NewLibrary(MAJOR_VERSION, MINOR_VERSION)
if not lib then
return
end
-- << STATIC CONFIG
local UpdateDelay = .5
local ItemRequestTimeout = 10.0
-- interact distance based checks. ranges are based on my own measurements (thanks for all the folks who helped me with this)
local DefaultInteractList = {
[3] = 8,
[2] = 9,
[4] = 28,
}
-- interact list overrides for races
local InteractLists = {
["Tauren"] = {
[3] = 6,
[2] = 7,
[4] = 25,
},
["Scourge"] = {
[3] = 7,
[2] = 8,
[4] = 27,
},
}
local MeleeRange = 5
-- list of friendly spells that have different ranges
local FriendSpells = {}
-- list of harmful spells that have different ranges
local HarmSpells = {}
FriendSpells["DRUID"] = {
5185, -- ["Healing Touch"], -- 40
467, -- ["Thorns"], -- 30 (Nature's Reach: 33, 36)
1126, -- ["Mark of the Wild"], -- 30
}
HarmSpells["DRUID"] = {
16979, -- ["Feral Charge"], -- 8-25
5176, -- ["Wrath"], -- 30 (Nature's Reach: 33, 36)
33786, -- ["Cyclone"], -- 20 (Nature's Reach: 22, 24; Gale Winds: +10/20%)
6795, -- ["Growl"], -- 20
5211, -- ["Bash"], -- 5
}
FriendSpells["HUNTER"] = {}
HarmSpells["HUNTER"] = {
1130, -- ["Hunter's Mark"] -- 100
53351, -- ["Kill Shot"] -- 5-45 (Hawk Eye: 47, 49, 51)
75, -- ["Auto Shot"], -- 5-35 (Hawk Eye: 37, 39, 41)
2764, -- ["Throw"], -- 30
19503, -- ["Scatter Shot"], -- 15 (Hawk Eye: 17, 19, 21; Glyph of Scatter Shot: +3)
2974, -- ["Wing Clip"], -- 5
}
FriendSpells["MAGE"] = {
475, -- ["Remove Curse"], -- 40 (Magic Attunement: 43, 46)
1459, -- ["Arcane Intellect"], -- 30 (Magic Attunement: 33, 36)
}
HarmSpells["MAGE"] = {
44614, -- ["Frostfire Bolt"], -- 40
133, -- ["Fireball"], -- 35 (Flame Throwing: 38, 41)
116, -- ["Frostbolt"], -- 30 (Arctic Reach: 33, 36)
30455, -- ["Ice Lance"], -- 30 (Arctic Reach: 33, 36, Glyph of Ice Lance: +5)
5143, -- ["Arcane Missiles"], -- 30 (Magic Attunement: 33, 36; Glyph of Arcane Missiles: +5)
30451, -- ["Arcane Blast"], -- 30 (Magic Attunement: 33, 36)
2948, -- ["Scorch"], -- 30 (Flame Throwing: 33, 36)
5019, -- ["Shoot"], -- 30
2136, -- ["Fire Blast"], -- 20 (Flame Throwing: 23, 26; Gladiator Gloves: +5)
}
FriendSpells["PALADIN"] = {
635, -- ["Holy Light"], -- 40
19740, -- ["Blessing of Might"], -- 30
20473, -- ["Holy Shock"], -- 20
}
HarmSpells["PALADIN"] = {
24275, -- ["Hammer of Wrath"], -- 30 (Glyph of Hammer of Wrath: +5)
20473, -- ["Holy Shock"], -- 20
20271, -- ["Judgement"], -- 10
35395, -- ["Crusader Strike"], -- 5
}
FriendSpells["PRIEST"] = {
2050, -- ["Lesser Heal"], -- 40
1243, -- ["Power Word: Fortitude"], -- 30
}
HarmSpells["PRIEST"] = {
585, -- ["Smite"], -- 30 (Holy Reach: 33, 36)
589, -- ["Shadow Word: Pain"], -- 30 (Shadow Reach: 33, 36)
5019, -- ["Shoot"], -- 30
15407, -- ["Mind Flay"], -- 20 (Shadow Reach: 22, 24, Glyph of Mind Flay: +10)
}
FriendSpells["ROGUE"] = {}
HarmSpells["ROGUE"] = {
2764, -- ["Throw"], -- 30
26679, -- ["Deadly Throw"], -- 30 (Glyph of Deadly Throw: +5)
2094, -- ["Blind"], -- 10 (Dirty Tricks: 12, 15)
2098, -- ["Eviscerate"], -- 5
}
FriendSpells["SHAMAN"] = {
331, -- ["Healing Wave"], -- 40
526, -- ["Cure Poison"], -- 30
}
HarmSpells["SHAMAN"] = {
403, -- ["Lightning Bolt"], -- 30 (Storm Reach: 33, 36)
370, -- ["Purge"], -- 30
8050, -- ["Flame Shock"], -- 20 (Elemental Reach: 27, 35; Gladiator Gloves: +5)
-- 8042, -- ["Earth Shock"], -- 20 (Storm, Earth and Fire: 21-25; Gladiator Gloves: +5)
8056, -- ["Frost Shock"], -- 20 (Gladiator Gloves: +5)
}
FriendSpells["WARRIOR"] = {}
HarmSpells["WARRIOR"] = {
100, -- ["Charge"], -- 8-25 (Glyph of Charge: +5)
3018, -- ["Shoot"], -- 30
2764, -- ["Throw"], -- 30
355, -- ["Taunt"], -- 30
5246, -- ["Intimidating Shout"], -- 8
772, -- ["Rend"], -- 5
}
FriendSpells["WARLOCK"] = {
5697, -- ["Unending Breath"], -- 30 (demo)
}
HarmSpells["WARLOCK"] = {
5019, -- ["Shoot"], -- 30
348, -- ["Immolate"], -- 30 (Destructive Reach: 33, 36)
172, -- ["Corruption"], -- 30 (Grim Reach: 33, 36)
18223, -- ["Curse of Exhaustion"], -- 30 (Grim Reach: 33, 36, Glyph of Curse of Exhaustion: +5)
5782, -- ["Fear"], -- 20 (Grim Reach: 22, 24)
17877, -- ["Shadowburn"], -- 20 (Destructive Reach: 22, 24)
}
FriendSpells["DEATHKNIGHT"] = {
}
HarmSpells["DEATHKNIGHT"] = {
47541, -- ["Death Coil"], -- 30
47476, -- ["Strangulate"], -- 30 (Glyph of Strangulate: +20)
45477, -- ["Icy Touch"], -- 20 (Icy Reach: 25, 30)
56222, -- ["Dark Command"], -- 20
50842, -- ["Pestilence"], -- 5
45902, -- ["Blood Strike"], -- 5, but requires weapon, use Pestilence if possible, so keep it after Pestilence in this list
}
-- Items [Special thanks to Maldivia for the nice list]
local FriendItems = {
[5] = {
37727, -- Ruby Acorn
},
[8] = {
34368, -- Attuned Crystal Cores
33278, -- Burning Torch
},
[10] = {
32321, -- Sparrowhawk Net
},
[15] = {
1251, -- Linen Bandage
2581, -- Heavy Linen Bandage
3530, -- Wool Bandage
3531, -- Heavy Wool Bandage
6450, -- Silk Bandage
6451, -- Heavy Silk Bandage
8544, -- Mageweave Bandage
8545, -- Heavy Mageweave Bandage
14529, -- Runecloth Bandage
14530, -- Heavy Runecloth Bandage
21990, -- Netherweave Bandage
21991, -- Heavy Netherweave Bandage
34721, -- Frostweave Bandage
34722, -- Heavy Frostweave Bandage
-- 38643, -- Thick Frostweave Bandage
-- 38640, -- Dense Frostweave Bandage
},
[20] = {
21519, -- Mistletoe
},
[25] = {
31463, -- Zezzak's Shard
},
[30] = {
1180, -- Scroll of Stamina
1478, -- Scroll of Protection II
3012, -- Scroll of Agility
1712, -- Scroll of Spirit II
2290, -- Scroll of Intellect II
1711, -- Scroll of Stamina II
34191, -- Handful of Snowflakes
},
[35] = {
18904, -- Zorbin's Ultra-Shrinker
},
[40] = {
34471, -- Vial of the Sunwell
},
[45] = {
32698, -- Wrangling Rope
},
[60] = {
32825, -- Soul Cannon
37887, -- Seeds of Nature's Wrath
},
[80] = {
35278, -- Reinforced Net
},
}
local HarmItems = {
[5] = {
37727, -- Ruby Acorn
},
[8] = {
34368, -- Attuned Crystal Cores
33278, -- Burning Torch
},
[10] = {
32321, -- Sparrowhawk Net
},
[15] = {
33069, -- Sturdy Rope
},
[20] = {
10645, -- Gnomish Death Ray
},
[25] = {
24268, -- Netherweave Net
41509, -- Frostweave Net
31463, -- Zezzak's Shard
},
[30] = {
835, -- Large Rope Net
7734, -- Six Demon Bag
34191, -- Handful of Snowflakes
},
[35] = {
24269, -- Heavy Netherweave Net
18904, -- Zorbin's Ultra-Shrinker
},
[40] = {
28767, -- The Decapitator
},
[45] = {
32698, -- Wrangling Rope
},
[60] = {
32825, -- Soul Cannon
37887, -- Seeds of Nature's Wrath
},
[80] = {
35278, -- Reinforced Net
},
}
-- This could've been done by checking player race as well and creating tables for those, but it's easier like this
for k, v in pairs(FriendSpells) do
tinsert(v, 28880) -- ["Gift of the Naaru"]
end
for k, v in pairs(HarmSpells) do
tinsert(v, 28734) -- ["Mana Tap"]
end
-- >> END OF STATIC CONFIG
-- cache
local setmetatable = setmetatable
local tonumber = tonumber
local pairs = pairs
local tostring = tostring
local print = print
local next = next
local type = type
local wipe = wipe
local tinsert = tinsert
local tremove = tremove
local BOOKTYPE_SPELL = BOOKTYPE_SPELL
local GetSpellInfo = GetSpellInfo
local GetSpellBookItemName = GetSpellBookItemName
local GetItemInfo = GetItemInfo
local UnitCanAttack = UnitCanAttack
local UnitCanAssist = UnitCanAssist
local UnitExists = UnitExists
local UnitIsDeadOrGhost = UnitIsDeadOrGhost
local CheckInteractDistance = CheckInteractDistance
local IsSpellInRange = IsSpellInRange
local IsItemInRange = IsItemInRange
local UnitClass = UnitClass
local UnitRace = UnitRace
local GetInventoryItemLink = GetInventoryItemLink
local GetTime = GetTime
local HandSlotId = GetInventorySlotInfo("HandsSlot")
local TT = ItemRefTooltip
-- temporary stuff
local itemRequestTimeoutAt
local foundNewItems
local cacheAllItems
local friendItemRequests
local harmItemRequests
local lastUpdate = 0
-- minRangeCheck is a function to check if spells with minimum range are really out of range, or fail due to range < minRange. See :init() for its setup
local minRangeCheck = function(unit) return CheckInteractDistance(unit, 2) end
local checkers_Spell = setmetatable({}, {
__index = function(t, spellIdx)
local func = function(unit)
if IsSpellInRange(spellIdx, BOOKTYPE_SPELL, unit) == 1 then
return true
end
end
t[spellIdx] = func
return func
end
})
local checkers_SpellWithMin = setmetatable({}, {
__index = function(t, spellIdx)
local func = function(unit)
if IsSpellInRange(spellIdx, BOOKTYPE_SPELL, unit) == 1 then
return true
elseif minRangeCheck(unit) then
return true, true
end
end
t[spellIdx] = func
return func
end
})
local checkers_Item = setmetatable({}, {
__index = function(t, item)
local func = function(unit)
if IsItemInRange(item, unit) == 1 then
return true
end
end
t[item] = func
return func
end
})
local checkers_Interact = setmetatable({}, {
__index = function(t, index)
local func = function(unit)
if CheckInteractDistance(unit, index) then
return true
end
end
t[index] = func
return func
end
})
-- helper functions
local function copyTable(src, dst)
if type(dst) ~= "table" then dst = {} end
if type(src) == "table" then
for k, v in pairs(src) do
if type(v) == "table" then
v = copyTable(v, dst[k])
end
dst[k] = v
end
end
return dst
end
local function initItemRequests(cacheAll)
friendItemRequests = copyTable(FriendItems)
harmItemRequests = copyTable(HarmItems)
cacheAllItems = cacheAll
foundNewItems = nil
end
local function requestItemInfo(itemId)
if not itemId then return end
TT:SetHyperlink(string.format("item:%d", itemId))
end
-- return the spellIndex of the given spell by scanning the spellbook
local function findSpellIdx(spellName)
local i = 1
while true do
local spell, rank = GetSpellBookItemName(i, BOOKTYPE_SPELL)
if not spell then return nil end
if spell == spellName then return i end
i = i + 1
end
return nil
end
-- minRange should be nil if there's no minRange, not 0
local function addChecker(t, range, minRange, checker)
local rc = { ["range"] = range, ["minRange"] = minRange, ["checker"] = checker }
for i = 1, #t do
local v = t[i]
if rc.range == v.range then return end
if rc.range > v.range then
tinsert(t, i, rc)
return
end
end
tinsert(t, rc)
end
local function createCheckerList(spellList, itemList, interactList)
local res = {}
if spellList then
for i = 1, #spellList do
local sid = spellList[i]
local name, _, _, _, _, _, _, minRange, range = GetSpellInfo(sid)
local spellIdx = findSpellIdx(name)
if spellIdx and range then
minRange = math.floor(minRange + 0.5)
range = math.floor(range + 0.5)
-- print("### spell: " .. tostring(name) .. ", " .. tostring(minRange) .. " - " .. tostring(range))
if minRange == 0 then -- getRange() expects minRange to be nil in this case
minRange = nil
end
if range == 0 then
range = MeleeRange
end
if minRange then
addChecker(res, range, minRange, checkers_SpellWithMin[spellIdx])
else
addChecker(res, range, minRange, checkers_Spell[spellIdx])
end
end
end
end
if itemList then
for range, items in pairs(itemList) do
for i = 1, #items do
local item = items[i]
if GetItemInfo(item) then
addChecker(res, range, nil, checkers_Item[item])
break
end
end
end
end
if interactList and not next(res) then
for index, range in pairs(interactList) do
addChecker(res, range, nil, checkers_Interact[index])
end
end
return res
end
-- returns minRange, maxRange or nil
local function getRange(unit, checkerList)
local min, max = 0, nil
for i = 1, #checkerList do
local rc = checkerList[i]
if not max or max > rc.range then
if rc.minRange then
local inRange, inMinRange = rc.checker(unit)
if inMinRange then
max = rc.minRange
elseif inRange then
min, max = rc.minRange, rc.range
elseif min > rc.range then
return min, max
else
return rc.range, max
end
elseif rc.checker(unit) then
max = rc.range
elseif min > rc.range then
return min, max
else
return rc.range, max
end
end
end
return min, max
end
local function updateCheckers(origList, newList)
if #origList ~= #newList then
wipe(origList)
copyTable(newList, origList)
return true
end
for i = 1, #origList do
if origList[i].range ~= newList[i].range or origList[i].checker ~= newList[i].checker then
wipe(origList)
copyTable(newList, origList)
return true
end
end
end
local function rcIterator(checkerList)
local curr = #checkerList
return function()
local rc = checkerList[curr]
if not rc then
return nil
end
curr = curr - 1
return rc.range, rc.checker
end
end
local function getMinChecker(checkerList, range)
local checker, checkerRange
for i = 1, #checkerList do
local rc = checkerList[i]
if rc.range < range then
return checker, checkerRange
end
checker, checkerRange = rc.checker, rc.range
end
return checker, checkerRange
end
local function getMaxChecker(checkerList, range)
for i = 1, #checkerList do
local rc = checkerList[i]
if rc.range <= range then
return rc.checker, rc.range
end
end
end
local function getChecker(checkerList, range)
for i = 1, #checkerList do
local rc = checkerList[i]
if rc.range == range then
return rc.checker
end
end
end
local function null()
end
local function createSmartChecker(friendChecker, harmChecker, miscChecker)
miscChecker = miscChecker or null
friendChecker = friendChecker or miscChecker
harmChecker = harmChecker or miscChecker
return function(unit)
if not UnitExists(unit) then
return nil
end
if UnitIsDeadOrGhost(unit) then
return miscChecker(unit)
end
if UnitCanAttack("player", unit) then
return harmChecker(unit)
elseif UnitCanAssist("player", unit) then
return friendChecker(unit)
else
return miscChecker(unit)
end
end
end
-- OK, here comes the actual lib
-- pre-initialize the checkerLists here so that we can return some meaningful result even if
-- someone manages to call us before we're properly initialized. miscRC should be independent of
-- race/class/talents, so it's safe to initialize it here
-- friendRC and harmRC will be properly initialized later when we have all the necessary data for them
lib.checkerCache_Spell = lib.checkerCache_Spell or {}
lib.checkerCache_Item = lib.checkerCache_Item or {}
lib.miscRC = createCheckerList(nil, nil, DefaultInteractList)
lib.friendRC = createCheckerList(nil, nil, DefaultInteractList)
lib.harmRC = createCheckerList(nil, nil, DefaultInteractList)
lib.failedItemRequests = {}
-- << Public API
--- The callback name that is fired when checkers are changed.
-- @field
lib.CHECKERS_CHANGED = "CHECKERS_CHANGED"
-- "export" it, maybe someone will need it for formatting
--- Constant for Melee range (5yd).
-- @field
lib.MeleeRange = MeleeRange
function lib:findSpellIndex(spell)
if type(spell) == 'number' then
spell = GetSpellInfo(spell)
end
if not spell then return nil end
return findSpellIdx(spell)
end
-- returns the range estimate as a string
-- deprecated, use :getRange(unit) instead and build your own strings
-- (checkVisible is not used any more, kept for compatibility only)
function lib:getRangeAsString(unit, checkVisible, showOutOfRange)
local minRange, maxRange = self:getRange(unit)
if not minRange then return nil end
if not maxRange then
return showOutOfRange and minRange .. " +" or nil
end
return minRange .. " - " .. maxRange
end
-- initialize RangeCheck if not yet initialized or if "forced"
function lib:init(forced)
if self.initialized and (not forced) then return end
self.initialized = true
local _, playerClass = UnitClass("player")
local _, playerRace = UnitRace("player")
minRangeCheck = nil
-- first try to find a nice item we can use for minRangeCheck
if HarmItems[15] then
local items = HarmItems[15]
for i = 1, #items do
local item = items[i]
if GetItemInfo(item) then
minRangeCheck = function(unit)
return (IsItemInRange(item, unit) == 1)
end
break
end
end
end
if not minRangeCheck then
-- ok, then try to find some class specific spell
if playerClass == "WARRIOR" then
-- for warriors, use Intimidating Shout if available
local name = GetSpellInfo(5246) -- ["Intimidating Shout"]
local spellIdx = findSpellIdx(name)
if spellIdx then
minRangeCheck = function(unit)
return (IsSpellInRange(spellIdx, BOOKTYPE_SPELL, unit) == 1)
end
end
elseif playerClass == "ROGUE" then
-- for rogues, use Blind if available
local name = GetSpellInfo(2094) -- ["Blind"]
local spellIdx = findSpellIdx(name)
if spellIdx then
minRangeCheck = function(unit)
return (IsSpellInRange(spellIdx, BOOKTYPE_SPELL, unit) == 1)
end
end
end
end
if not minRangeCheck then
-- fall back to interact distance checks
if playerClass == "HUNTER" or playerRace == "Tauren" then
-- for hunters, use interact4 as it's safer
-- for Taurens interact4 is actually closer than 25yd and interact2 is closer than 8yd, so we can't use that
minRangeCheck = checkers_Interact[4]
else
minRangeCheck = checkers_Interact[2]
end
end
local interactList = InteractLists[playerRace] or DefaultInteractList
self.handSlotItem = GetInventoryItemLink("player", HandSlotId)
local changed = false
if updateCheckers(self.friendRC, createCheckerList(FriendSpells[playerClass], FriendItems, interactList)) then
changed = true
end
if updateCheckers(self.harmRC, createCheckerList(HarmSpells[playerClass], HarmItems, interactList)) then
changed = true
end
if updateCheckers(self.miscRC, createCheckerList(nil, nil, interactList)) then
changed = true
end
if changed and self.callbacks then
self.callbacks:Fire(self.CHECKERS_CHANGED)
end
end
--- Return an iterator for checkers usable on friendly units as (**range**, **checker**) pairs.
function lib:GetFriendCheckers()
return rcIterator(self.friendRC)
end
--- Return an iterator for checkers usable on enemy units as (**range**, **checker**) pairs.
function lib:GetHarmCheckers()
return rcIterator(self.harmRC)
end
--- Return an iterator for checkers usable on miscellaneous units as (**range**, **checker**) pairs. These units are neither enemy nor friendly, such as people in sanctuaries or corpses.
function lib:GetMiscCheckers()
return rcIterator(self.miscRC)
end
--- Return a checker suitable for out-of-range checking on friendly units, that is, a checker whose range is equal or larger than the requested range.
-- @param range the range to check for.
-- @return **checker**, **range** pair or **nil** if no suitable checker is available. **range** is the actual range the returned **checker** checks for.
function lib:GetFriendMinChecker(range)
return getMinChecker(self.friendRC, range)
end
--- Return a checker suitable for out-of-range checking on enemy units, that is, a checker whose range is equal or larger than the requested range.
-- @param range the range to check for.
-- @return **checker**, **range** pair or **nil** if no suitable checker is available. **range** is the actual range the returned **checker** checks for.
function lib:GetHarmMinChecker(range)
return getMinChecker(self.harmRC, range)
end
--- Return a checker suitable for out-of-range checking on miscellaneous units, that is, a checker whose range is equal or larger than the requested range.
-- @param range the range to check for.
-- @return **checker**, **range** pair or **nil** if no suitable checker is available. **range** is the actual range the returned **checker** checks for.
function lib:GetMiscMinChecker(range)
return getMinChecker(self.miscRC, range)
end
--- Return a checker suitable for in-range checking on friendly units, that is, a checker whose range is equal or smaller than the requested range.
-- @param range the range to check for.
-- @return **checker**, **range** pair or **nil** if no suitable checker is available. **range** is the actual range the returned **checker** checks for.
function lib:GetFriendMaxChecker(range)
return getMaxChecker(self.friendRC, range)
end
--- Return a checker suitable for in-range checking on enemy units, that is, a checker whose range is equal or smaller than the requested range.
-- @param range the range to check for.
-- @return **checker**, **range** pair or **nil** if no suitable checker is available. **range** is the actual range the returned **checker** checks for.
function lib:GetHarmMaxChecker(range)
return getMaxChecker(self.harmRC, range)
end
--- Return a checker suitable for in-range checking on miscellaneous units, that is, a checker whose range is equal or smaller than the requested range.
-- @param range the range to check for.
-- @return **checker**, **range** pair or **nil** if no suitable checker is available. **range** is the actual range the returned **checker** checks for.
function lib:GetMiscMaxChecker(range)
return getMaxChecker(self.miscRC, range)
end
--- Return a checker for the given range for friendly units.
-- @param range the range to check for.
-- @return **checker** function or **nil** if no suitable checker is available.
function lib:GetFriendChecker(range)
return getChecker(self.friendRC, range)
end
--- Return a checker for the given range for enemy units.
-- @param range the range to check for.
-- @return **checker** function or **nil** if no suitable checker is available.
function lib:GetHarmChecker(range)
return getChecker(self.harmRC, range)
end
--- Return a checker for the given range for miscellaneous units.
-- @param range the range to check for.
-- @return **checker** function or **nil** if no suitable checker is available.
function lib:GetMiscChecker(range)
return getChecker(self.miscRC, range)
end
--- Return a checker suitable for out-of-range checking that checks the unit type and calls the appropriate checker (friend/harm/misc).
-- @param range the range to check for.
-- @return **checker** function.
function lib:GetSmartMinChecker(range)
return createSmartChecker(
getMinChecker(self.friendRC, range),
getMinChecker(self.harmRC, range),
getMinChecker(self.miscRC, range))
end
--- Return a checker suitable for in-of-range checking that checks the unit type and calls the appropriate checker (friend/harm/misc).
-- @param range the range to check for.
-- @return **checker** function.
function lib:GetSmartMaxChecker(range)
return createSmartChecker(
getMaxChecker(self.friendRC, range),
getMaxChecker(self.harmRC, range),
getMaxChecker(self.miscRC, range))
end
--- Return a checker for the given range that checks the unit type and calls the appropriate checker (friend/harm/misc).
-- @param range the range to check for.
-- @param fallback optional fallback function that gets called as fallback(unit) if a checker is not available for the given type (friend/harm/misc) at the requested range. The default fallback function return nil.
-- @return **checker** function.
function lib:GetSmartChecker(range, fallback)
return createSmartChecker(
getChecker(self.friendRC, range) or fallback,
getChecker(self.harmRC, range) or fallback,
getChecker(self.miscRC, range) or fallback)
end
--- Get a range estimate as **minRange**, **maxRange**.
-- @param unit the target unit to check range to.
-- @return **minRange**, **maxRange** pair if a range estimate could be determined, **nil** otherwise. **maxRange** is **nil** if **unit** is further away than the highest possible range we can check.
-- Includes checks for unit validity and friendly/enemy status.
-- @usage
-- local rc = LibStub("LibRangeCheck-2.0")
-- local minRange, maxRange = rc:GetRange('target')
function lib:GetRange(unit)
if not UnitExists(unit) then
return nil
end
if UnitIsDeadOrGhost(unit) then
return getRange(unit, self.miscRC)
end
if UnitCanAttack("player", unit) then
return getRange(unit, self.harmRC)
elseif UnitCanAssist("player", unit) then
return getRange(unit, self.friendRC)
else
return getRange(unit, self.miscRC)
end
end
-- keep this for compatibility
lib.getRange = lib.GetRange
-- >> Public API
function lib:OnEvent(event, ...)
if type(self[event]) == 'function' then
self[event](self, event, ...)
end
end
function lib:LEARNED_SPELL_IN_TAB()
self:scheduleInit()
end
function lib:CHARACTER_POINTS_CHANGED()
self:scheduleInit()
end
function lib:PLAYER_TALENT_UPDATE()
self:scheduleInit()
end
function lib:GLYPH_ADDED()
self:scheduleInit()
end
function lib:GLYPH_REMOVED()
self:scheduleInit()
end
function lib:GLYPH_UPDATED()
self:scheduleInit()
end
function lib:UNIT_INVENTORY_CHANGED(event, unit)
if self.initialized and unit == "player" and self.handSlotItem ~= GetInventoryItemLink("player", HandSlotId) then
self:scheduleInit()
end
end
function lib:processItemRequests(itemRequests)
while true do
local range, items = next(itemRequests)
if not range then return end
while true do
local i, item = next(items)
if not i then
itemRequests[range] = nil
break
elseif self.failedItemRequests[item] then
tremove(items, i)
elseif GetItemInfo(item) then
if itemRequestTimeoutAt then
foundNewItems = true
itemRequestTimeoutAt = nil
end
if not cacheAllItems then
itemRequests[range] = nil
break
end
tremove(items, i)
elseif not itemRequestTimeoutAt then
requestItemInfo(item)
itemRequestTimeoutAt = GetTime() + ItemRequestTimeout
return true
elseif GetTime() > itemRequestTimeoutAt then
if cacheAllItems then
print(MAJOR_VERSION .. ": timeout for item: " .. tostring(item))
end
self.failedItemRequests[item] = true
itemRequestTimeoutAt = nil
tremove(items, i)
else
return true -- still waiting for server response
end
end
end
end
function lib:initialOnUpdate()
self:init()
if friendItemRequests then
if self:processItemRequests(friendItemRequests) then return end
friendItemRequests = nil
end
if harmItemRequests then
if self:processItemRequests(harmItemRequests) then return end
harmItemRequests = nil
end
if foundNewItems then
self:init(true)
foundNewItems = nil
end
if cacheAllItems then
print(MAJOR_VERSION .. ": finished cache")
cacheAllItems = nil
end
self.frame:Hide()
end
function lib:scheduleInit()
self.initialized = nil
lastUpdate = 0
self.frame:Show()
end
-- << load-time initialization
function lib:activate()
if not self.frame then
local frame = CreateFrame("Frame")
self.frame = frame
frame:RegisterEvent("LEARNED_SPELL_IN_TAB")
frame:RegisterEvent("CHARACTER_POINTS_CHANGED")
frame:RegisterEvent("PLAYER_TALENT_UPDATE")
frame:RegisterEvent("GLYPH_ADDED")
frame:RegisterEvent("GLYPH_REMOVED")
frame:RegisterEvent("GLYPH_UPDATED")
local _, playerClass = UnitClass("player")
if playerClass == "MAGE" or playerClass == "SHAMAN" then
-- Mage and Shaman gladiator gloves modify spell ranges
frame:RegisterEvent("UNIT_INVENTORY_CHANGED")
end
end
initItemRequests()
self.frame:SetScript("OnEvent", function(frame, ...) self:OnEvent(...) end)
self.frame:SetScript("OnUpdate", function(frame, elapsed)
lastUpdate = lastUpdate + elapsed
if lastUpdate < UpdateDelay then
return
end
lastUpdate = 0
self:initialOnUpdate()
end)
self:scheduleInit()
end
--- BEGIN CallbackHandler stuff
do
local lib = lib -- to keep a ref even though later we nil lib
--- Register a callback to get called when checkers are updated
-- @class function
-- @name lib.RegisterCallback
-- @usage
-- rc.RegisterCallback(self, rc.CHECKERS_CHANGED, "myCallback")
-- -- or
-- rc.RegisterCallback(self, "CHECKERS_CHANGED", someCallbackFunction)
-- @see CallbackHandler-1.0 documentation for more details
lib.RegisterCallback = lib.RegisterCallback or function(...)
local CBH = LibStub("CallbackHandler-1.0")
lib.RegisterCallback = nil -- extra safety, we shouldn't get this far if CBH is not found, but better an error later than an infinite recursion now
lib.callbacks = CBH:New(lib)
-- ok, CBH hopefully injected or new shiny RegisterCallback
return lib.RegisterCallback(...)
end
end
--- END CallbackHandler stuff
lib:activate()
lib = nil