/
}, |
total = 0 |
} |
NeedToKnow.scratch.bar_entry = |
{ |
idxName = 0, |
barSpell = "", |
isSpellID = false, |
} |
-- NEEDTOKNOW = {} is defined in the localization file, which must be loaded before this file |
NEEDTOKNOW.VERSION = "4.0.02" |
local last = NeedToKnow.last_cast_tail-1 |
local i |
for i = last,NeedToKnow.last_cast_head,-1 do |
if t[i].spell == spell then |
if t[i].spell == spell then |
found = i |
break |
end |
-- This may fail for valid names if the client doesn't have the data for |
-- that spell yet (just logged in or changed talent specs), in which case |
-- we mark that spell to try again later |
function NeedToKnow.SetupSpellCooldown(bar, idx, barSpell) |
local isSpellID = barSpell:match("%d+") == barSpell |
if ( barSpell == "Auto Shot" or |
barSpell == NEEDTOKNOW.AUTO_SHOT or |
barSpell == "75" ) |
then |
bar.settings.bAutoShot = true |
bar.cd_functions[idx] = NeedToKnow.GetAutoShotCooldown |
elseif not isSpellID then |
local item_id = NeedToKnow.GetItemIDString(barSpell) |
if item_id then |
bar.spells[idx] = item_id |
bar.cd_functions[idx] = NeedToKnow.GetItemCooldown |
function NeedToKnow.SetupSpellCooldown(bar, entry) |
local id = entry.id |
local name = entry.name |
local idx = entry.idxName |
if not id then |
if ( name == "Auto Shot" or |
name == NEEDTOKNOW.AUTO_SHOT ) |
then |
bar.settings.bAutoShot = true |
bar.cd_functions[idx] = NeedToKnow.GetAutoShotCooldown |
else |
local betterSpell = barSpell |
betterSpell = NeedToKnow.TryToFindSpellWithCD(barSpell) |
if nil ~= betterSpell then |
bar.spells[idx] = betterSpell |
bar.cd_functions[idx] = NeedToKnow.GetSpellCooldown |
elseif not GetSpellCooldown(barSpell) then |
bar.cd_functions[idx] = NeedToKnow.GetUnresolvedCooldown |
local item_id = NeedToKnow.GetItemIDString(name) |
if item_id then |
entry.id = item_id |
entry.name = nil |
bar.cd_functions[idx] = NeedToKnow.GetItemCooldown |
else |
local betterSpellID |
betterSpellID = NeedToKnow.TryToFindSpellWithCD(name) |
if nil ~= betterSpell then |
entry.id = betterSpell |
entry.name = nil |
bar.cd_functions[idx] = NeedToKnow.GetSpellCooldown |
elseif not GetSpellCooldown(name) then |
bar.cd_functions[idx] = NeedToKnow.GetUnresolvedCooldown |
end |
end |
end |
end |
-- Split the spell names |
bar.spells = {} |
bar.cd_functions = {} |
local iSpell = 0 |
for barSpell in bar.auraName:gmatch("([^,]+)") do |
barSpell = strtrim(barSpell) |
table.insert(bar.spells, barSpell) |
local _, nDigits = barSpell:find("^%d+") |
if ( nDigits == barSpell:len() ) then |
table.insert(bar.spells, { idxName=iSpell, id=tonumber(barSpell) } ) |
else |
table.insert(bar.spells, { idxName=iSpell, name=barSpell } ) |
end |
iSpell = iSpell+1 |
end |
-- split the user name overrides |
if barSettings.buffcd_reset_spells and barSettings.buffcd_reset_spells ~= "" then |
bar.reset_spells = {} |
bar.reset_start = {} |
iSpell = 0 |
for resetSpell in barSettings.buffcd_reset_spells:gmatch("([^,]+)") do |
resetSpell = strtrim(resetSpell) |
table.insert(bar.reset_spells, resetSpell) |
local _, nDigits = resetSpell:find("^%d+") |
if ( nDigits == resetSpell:len() ) then |
table.insert(bar.reset_spells, { idxName = iSpell, id=tonumber(resetSpell) } ) |
else |
table.insert(bar.reset_spells, { idxName = iSpell, name=resetSpell} ) |
end |
table.insert(bar.reset_start, 0) |
end |
else |
bar.fnCheck = NeedToKnow.AuraCheck_EQUIPSLOT |
elseif "CASTCD" == barSettings.BuffOrDebuff then |
bar.fnCheck = NeedToKnow.AuraCheck_CASTCD |
for idx, barSpell in ipairs(bar.spells) do |
for idx, entry in ipairs(bar.spells) do |
table.insert(bar.cd_functions, NeedToKnow.GetSpellCooldown) |
NeedToKnow.SetupSpellCooldown(bar, idx, barSpell) |
NeedToKnow.SetupSpellCooldown(bar, entry) |
end |
elseif "mhand" == barSettings.Unit or "ohand" == barSettings.Unit then |
bar.fnCheck = NeedToKnow.AuraCheck_Weapon |
if bar.settings.bDetectExtends then |
local idx,barSpell |
for idx, barSpell in ipairs(bar.spells) do |
local _, nDigits = barSpell:find("^%d+") |
local spell |
if ( nDigits == barSpell:len() ) then |
spell = GetSpellInfo(to_number(barSpell)) |
local idx,entry |
for idx, entry in ipairs(bar.spells) do |
local spellName |
if ( entry.id ) then |
spellName = GetSpellInfo(entry.id) |
else |
spell = barSpell |
spellName = entry.name |
end |
if spell then |
local r = NeedToKnow.last_guid[spell] |
if spellName then |
local r = NeedToKnow.last_guid[spellName] |
if not r then |
NeedToKnow.last_guid[spell] = { time=0, dur=0, expiry=0 } |
NeedToKnow.last_guid[spellName] = { time=0, dur=0, expiry=0 } |
end |
else |
print("Warning! NTK could not get name for ", barSpell) |
print("Warning! NTK could not get name for ", entry.id) |
end |
end |
NeedToKnow.RegisterSpellcastSent() |
NeedToKnow.Bar_AuraCheck(self) |
elseif ( event == "UNIT_PET" and unit == "player" ) then |
NeedToKnow.Bar_AuraCheck(self) |
--elseif ( event == "PLAYER_SPELLCAST_SENT" ) then |
elseif ( event == "PLAYER_SPELLCAST_SUCCEEDED" ) then |
--if ( self.settings.Unit == "lastcast" ) then |
-- |
--end |
elseif ( event == "START_AUTOREPEAT_SPELL" ) then |
self:RegisterEvent("UNIT_SPELLCAST_SUCCEEDED") |
-- Helper for NeedToKnow.AuraCheck_CASTCD for names we haven't figured out yet |
function NeedToKnow.GetUnresolvedCooldown(bar, barSpell, idxName) |
NeedToKnow.SetupSpellCooldown(bar, idxName, barSpell) |
local fn = bar.cd_functions[idxName] |
function NeedToKnow.GetUnresolvedCooldown(bar, entry) |
NeedToKnow.SetupSpellCooldown(bar, entry) |
local fn = bar.cd_functions[entry.idxName] |
if NeedToKnow.GetUnresolvedCooldown ~= fn then |
-- Have to re-evaluate barSpell since SetupSpellCooldown may have changed bar.spells |
return fn(bar, bar.spells[idxName], idxName) |
return fn(bar, entry) |
end |
end |
-- Wrapper around GetSpellCooldown with extra sauce |
-- Expected to return start, cd_len, enable, buffName, iconpath |
function NeedToKnow.GetSpellCooldown(bar, barSpell) |
function NeedToKnow.GetSpellCooldown(bar, entry) |
local barSpell = entry.id or entry.name |
local start, cd_len, enable = GetSpellCooldown(barSpell) |
if start and start > 0 then |
local spellName, spellRank, spellIconPath, _, _, spellPower = GetSpellInfo(barSpell) |
NeedToKnow.GSIBroken = {} |
end |
if not NeedToKnow.GSIBroken[barSpell] then |
print("NeedToKnow: Warning! Unable to get spell info for "..barSpell..". Try using Spell ID instead.") |
print("NeedToKnow: Warning! Unable to get spell info for",barSpell,". Try using Spell ID instead.") |
NeedToKnow.GSIBroken[barSpell] = true; |
end |
spellName = barSpell |
spellName = tostring(barSpell) |
end |
if 0 == enable then |
-- We've already seen the correct CD for this; keep using it |
start = bar.expirationTime - bar.duration |
cd_len = bar.duration |
elseif NeedToKnow.last_sent and NeedToKnow.last_sent[barSpell] and NeedToKnow.last_sent[barSpell] > (tNow - 1.5) then |
elseif NeedToKnow.last_sent and NeedToKnow.last_sent[spellName] and NeedToKnow.last_sent[spellName] > (tNow - 1.5) then |
-- We think the spell was just cast, and a CD just started but it's short. |
-- Look at the tooltip to tell what the correct CD should be. If it's supposed |
-- to be short (Ghoul Frenzy, Howling Blast), then start a CD bar |
-- Wrapper around GetItemCooldown |
-- Expected to return start, cd_len, enable, buffName, iconpath |
function NeedToKnow.GetItemCooldown(bar, item_id, idx) |
local start, cd_len, enable = GetItemCooldown(item_id) |
function NeedToKnow.GetItemCooldown(bar, entry) |
local start, cd_len, enable = GetItemCooldown(entry.id) |
if start then |
local name, _, _, _, _, _, _, _, icon = GetItemInfo(item_id) |
local name, _, _, _, _, _, _, _, icon = GetItemInfo(entry.id) |
return start, cd_len, enable, name, icon |
end |
end |
-- Bar_AuraCheck helper for Totem bars, this returns data if |
-- a totem matching barSpell is currently out. |
-- a totem matching bar_entry is currently out. |
function NeedToKnow.AuraCheck_TOTEM(bar, bar_entry, all_stacks) |
local idxName, barSpell, isSpellID = bar_entry.idxName, bar_entry.barSpell, bar_entry.isSpellID |
local spellName, spellRank, spellIconPath |
if ( isSpellID ) then |
spellName, spellRank, spellIconPath = GetSpellInfo(barSpell) |
end |
local idxName = bar_entry.idxName |
local sComp = bar_entry.name or GetSpellInfo(bar_entry.id) |
for iSlot=1, 4 do |
local haveTotem, totemName, startTime, totemDuration, totemIcon = GetTotemInfo(iSlot) |
local sComp = barSpell |
if isSpellID then sComp = spellName end |
if ( totemName and totemName:find(sComp) ) then |
-- WORKAROUND: The startTime reported here is both cast to an int and off by |
-- a latency meaning it can be significantly low. So we cache the GetTime |
-- Bar_AuraCheck helper for tracking usable gear based on the slot its in |
-- rather than the equipment name |
function NeedToKnow.AuraCheck_EQUIPSLOT(bar, bar_entry, all_stacks) |
local idxName, barSpell, isSpellID = bar_entry.idxName, bar_entry.barSpell, bar_entry.isSpellID |
local spellName, spellRank, spellIconPath |
if ( isSpellID ) then |
local slot = tonumber(bar_entry.barSpell) |
local id = GetInventoryItemID("player",slot) |
if ( bar_entry.id ) then |
local id = GetInventoryItemID("player",bar_entry.id) |
if id then |
local start, cd_len, enable, name, icon = NeedToKnow.GetItemCooldown(bar, id, idx) |
local item_entry = NeedToKnow.scratch.bar_entry |
item_entry.id = id |
local start, cd_len, enable, name, icon = NeedToKnow.GetItemCooldown(bar, item_entry) |
if ( start and start > 0 ) then |
AddInstanceToStacks(all_stacks, bar_entry, |
-- (computed by UpdateWeaponEnchants) for the given spell. |
-- FIXME: this is the only bar type that does not work with spell ids. |
function NeedToKnow.AuraCheck_Weapon(bar, bar_entry, all_stacks) |
local idxName, barSpell, isSpellID = bar_entry.idxName, bar_entry.barSpell, bar_entry.isSpellID; |
local data = NeedToKnow.weapon_enchants[bar.settings.Unit] |
if ( data.present and data.name and data.name:find(barSpell) ) then |
if ( data.present and data.name and data.name:find(bar_entry.name) ) then |
AddInstanceToStacks( all_stacks, bar_entry, |
1800, -- duration TODO: Get real duration? |
data.name, -- name |
-- Bar_AuraCheck helper that checks for spell/item use cooldowns |
-- Relies on NeedToKnow.GetAutoShotCooldown, NeedToKnow.GetSpellCooldown |
-- and NeedToKnow.GetItemCooldown. Bar_Update will have already pre-processed |
-- this list so that bar.cd_functions[idxName] can do something with barSpell |
-- this list so that bar.cd_functions[idxName] can do something with bar_entry |
function NeedToKnow.AuraCheck_CASTCD(bar, bar_entry, all_stacks) |
local idxName, barSpell, isSpellID = bar_entry.idxName, bar_entry.barSpell, bar_entry.isSpellID; |
local idxName = bar_entry.idxName |
local func = bar.cd_functions[idxName] |
local start, cd_len, should_cooldown, buffName, iconPath = func(bar, barSpell, idxName) |
if ( not func ) then |
trace("ERROR setting up index",idxName,"on bar",bar:GetName(),bar.settings.AuraName); |
return; |
end |
local start, cd_len, should_cooldown, buffName, iconPath = func(bar, bar_entry) |
-- filter out the GCD, we only care about actual spell CDs |
if start and cd_len <= 1.5 and func ~= NeedToKnow.GetAutoShotCooldown then |
-- Bar_AuraCheck helper for watching "Is Usable", which means that the action |
-- bar button for the spell lights up. This is mostly useful for Victory Rush |
function NeedToKnow.AuraCheck_USABLE(bar, bar_entry, all_stacks) |
local idxName, barSpell, isSpellID = bar_entry.idxName, bar_entry.barSpell, bar_entry.isSpellID; |
local key |
local key = bar_entry.id or bar_entry.name |
local settings = bar.settings |
if ( isSpellID ) then key = tonumber(barSpell) else key = barSpell end |
if ( not key ) then key = "" end |
local spellName, _, iconPath = GetSpellInfo(key) |
if ( spellName ) then |
-- cooldown for spells cast automatically (procs). The "reset on buff" logic |
-- is still handled by |
function NeedToKnow.AuraCheck_BUFFCD(bar, bar_entry, all_stacks) |
local idxName, barSpell, isSpellID = bar_entry.idxName, bar_entry.barSpell, bar_entry.isSpellID; |
local buff_stacks = NeedToKnow.scratch.buff_stacks |
buff_stacks.total = 0 |
NeedToKnow.AuraCheck_Single(bar, bar_entry, buff_stacks) |
-- Bar_AuraCheck helper that looks for the first instance of a buff |
-- Uses the UnitAura filters exclusively if it can |
function NeedToKnow.AuraCheck_Single(bar, bar_entry, all_stacks) |
local idxName, barSpell, isSpellID = bar_entry.idxName, bar_entry.barSpell, bar_entry.isSpellID; |
local settings = bar.settings |
local filter = settings.BuffOrDebuff |
if settings.OnlyMine then |
filter = filter .. "|PLAYER" |
end |
if isSpellID then |
if bar_entry.id then |
-- WORKAROUND: The second parameter to UnitAura can't be a spellid, so I have |
-- to walk them all |
local barID = tonumber(barSpell) |
local barID = bar_entry.id |
local j = 1 |
while true do |
local buffName, _, iconPath, count, _, duration, expirationTime, caster, _, _, spellID |
end |
else |
local buffName, _ , iconPath, count, _, duration, expirationTime, caster |
= UnitAura(bar.unit, barSpell, nil, filter) |
= UnitAura(bar.unit, bar_entry.name, nil, filter) |
AddInstanceToStacks( all_stacks, bar_entry, |
duration, -- duration |
-- Bar_AuraCheck helper that updates bar.all_stacks (but returns nil) |
-- by scanning all the auras on the unit |
function NeedToKnow.AuraCheck_AllStacks(bar, bar_entry, all_stacks) |
local idxName, barSpell, isSpellID = bar_entry.idxName, bar_entry.barSpell, bar_entry.isSpellID; |
local j = 1 |
local matchID |
if isSpellID then matchID = tonumber(barSpell) end |
local settings = bar.settings |
local filter = settings.BuffOrDebuff |
break |
end |
if (isSpellID and spellID == matchID) or (not isSpellID and barSpell == buffName) then |
AddInstanceToStacks(all_stacks, bar_entry, |
duration, |
buffName, |
count, |
expirationTime, |
iconPath, |
caster ) |
if (spellID == bar_entry.id) or (bar_entry.name == buffName) |
then |
AddInstanceToStacks(all_stacks, bar_entry, |
duration, |
buffName, |
count, |
expirationTime, |
iconPath, |
caster ) |
end |
j = j+1 |
if ( bUnitExists ) then |
all_stacks = NeedToKnow.scratch.all_stacks |
all_stacks.total = 0 |
local bar_entry = NeedToKnow.scratch.bar_entry |
-- Call the helper function for each of the spells in the list |
for idx, barSpell in ipairs(bar.spells) do |
local _, nDigits = barSpell:find("^%d+") |
bar_entry.idxName = idx |
bar_entry.barSpell = barSpell |
bar_entry.isSpellID = ( nDigits == barSpell:len() ) |
bar.fnCheck(bar, bar_entry, all_stacks); |
for idx, entry in ipairs(bar.spells) do |
bar.fnCheck(bar, entry, all_stacks); |
if all_stacks.total > 0 and not settings.show_all_stacks then |
idxName = idx |
buff_stacks.total = 0 |
-- Keep track of when the reset auras were last applied to the player |
for idx, resetSpell in ipairs(bar.reset_spells) do |
local _, nDigits = resetSpell:find("^%d+") |
local bar_entry = NeedToKnow.scratch.bar_entry |
bar_entry.idxName = idx |
bar_entry.barSpell = resetSpell |
bar_entry.isSpellID = ( nDigits == resetSpell:len() ) |
-- Note this relies on BUFFCD setting the target to player, and that the onlyMine will work either way |
local resetDuration, _, _, resetExpiration |
= NeedToKnow.AuraCheck_Single(bar, bar_entry, buff_stacks) |
= NeedToKnow.AuraCheck_Single(bar, resetSpell, buff_stacks) |
local tStart |
if buff_stacks.total > 0 then |
if 0 == buff_stacks.max.duration then |