/
-- ADDON GLOBALS |
-- ------------- |
local GetActiveTalentGroup = _G.GetActiveSpecGroup |
NeedToKnow = {} |
NeedToKnowLoader = {} |
NeedToKnow.scratch = {} |
NeedToKnow.scratch.all_stacks = |
-- ------------- |
-- ADDON MEMBERS |
-- ------------- |
local g_GetActiveTalentGroup = _G.GetActiveSpecGroup |
local g_UnitAffectingCombat = UnitAffectingCombat |
local g_UnitIsFriend = UnitIsFriend |
local g_UnitGUID = UnitGUID |
local g_GetTime = GetTime |
local g_GetWeaponEnchantInfo = GetWeaponEnchantInfo |
local m_last_guid, m_last_cast, m_last_sent, m_last_cast_head, m_last_cast_tail |
local m_bInCombat, m_bCombatWithBoss |
local mfn_Bar_AuraCheck |
local mfn_AuraCheck_Single |
local mfn_AuraCheck_TOTEM |
local mfn_AuraCheck_BUFFCD |
local mfn_AuraCheck_USABLE |
local mfn_AuraCheck_EQUIPSLOT |
local mfn_AuraCheck_CASTCD |
local mfn_AuraCheck_Weapon |
local mfn_AuraCheck_AllStacks |
local mfn_GetUnresolvedCooldown |
local mfn_GetAutoShotCooldown |
local mfn_GetSpellCooldown |
local mfn_AddInstanceToStacks |
local mfn_SetStatusBarValue |
local mfn_ResetScratchStacks |
local mfn_UpdateVCT |
local m_scratch = {} |
m_scratch.all_stacks = |
{ |
min = |
{ |
total = 0, |
total_ttn = { 0, 0, 0 } |
} |
NeedToKnow.scratch.buff_stacks = |
m_scratch.buff_stacks = |
{ |
min = |
{ |
total = 0, |
total_ttn = { 0, 0, 0 } |
} |
NeedToKnow.scratch.bar_entry = |
m_scratch.bar_entry = |
{ |
idxName = 0, |
barSpell = "", |
-- NEEDTOKNOW = {} is defined in the localization file, which must be loaded before this file |
NEEDTOKNOW.VERSION = "4.0.08" |
NEEDTOKNOW.UPDATE_INTERVAL = 0.05 |
NEEDTOKNOW.MAXBARS = 20 |
local c_UPDATE_INTERVAL = 0.05 |
local c_MAXBARS = 20 |
-- Get the localized name of spell 75, which is "Auto Shot" in US English |
NEEDTOKNOW.AUTO_SHOT = GetSpellInfo(75) |
local c_AUTO_SHOT_NAME = GetSpellInfo(75) |
-- COMBAT_LOG_EVENT_UNFILTERED events where select(6,...) is the caster, 9 is the spellid, and 10 is the spell name |
-- (used for Target-of-target monitoring) |
NEEDTOKNOW.AURAEVENTS = { |
local c_AURAEVENTS = { |
SPELL_AURA_APPLIED = true, |
SPELL_AURA_REMOVED = true, |
SPELL_AURA_APPLIED_DOSE = true, |
-- TODO: I hate to pay this memory cost for every "spell" ever cast. |
-- Would be nice to at least garbage collect this data at some point, but that |
-- may add more overhead than just keeping track of 100 spells. |
if not NeedToKnow.last_sent then |
NeedToKnow.last_sent = {} |
if not m_last_sent then |
m_last_sent = {} |
end |
NeedToKnow.last_sent[spell] = GetTime() |
m_last_sent[spell] = g_GetTime() |
-- How expensive a second check do we need? |
if ( NeedToKnow.last_guid[spell] or NeedToKnow.BarsForPSS ) then |
local r = NeedToKnow.last_cast[NeedToKnow.last_cast_tail] |
if ( m_last_guid[spell] or NeedToKnow.BarsForPSS ) then |
local r = m_last_cast[m_last_cast_tail] |
if not r then |
r = { spell=spell, target=tgt, serial=serialno } |
NeedToKnow.last_cast[NeedToKnow.last_cast_tail] = r |
m_last_cast[m_last_cast_tail] = r |
else |
r.spell = spell |
r.target = tgt |
r.serial = serialno |
end |
NeedToKnow.last_cast_tail = NeedToKnow.last_cast_tail + 1 |
if ( NeedToKnow.last_cast_tail == 2 ) then |
NeedToKnow.last_cast_head = 1 |
if ( NeedToKnow.last_guid[spell] ) then |
m_last_cast_tail = m_last_cast_tail + 1 |
if ( m_last_cast_tail == 2 ) then |
m_last_cast_head = 1 |
if ( m_last_guid[spell] ) then |
NeedToKnow_ExecutiveFrame:UnregisterEvent("UNIT_SPELLCAST_SUCCEEDED") |
NeedToKnow_ExecutiveFrame:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED") |
else |
function NeedToKnow.ExecutiveFrame_UNIT_SPELLCAST_SUCCEEDED(unit, spell, rank_str, serialno, spellid) |
if unit == "player" then |
local found |
local t = NeedToKnow.last_cast |
local last = NeedToKnow.last_cast_tail-1 |
local t = m_last_cast |
local last = m_last_cast_tail-1 |
local i |
for i = last,NeedToKnow.last_cast_head,-1 do |
for i = last,m_last_cast_head,-1 do |
if t[i].spell == spell and t[i].serial == serialno then |
found = i |
break |
end |
if ( found == last ) then |
NeedToKnow.last_cast_tail = 1 |
NeedToKnow.last_cast_head = 1 |
m_last_cast_tail = 1 |
m_last_cast_head = 1 |
NeedToKnow_ExecutiveFrame:UnregisterEvent("UNIT_SPELLCAST_SUCCEEDED") |
else |
NeedToKnow.last_cast_head = found+1 |
m_last_cast_head = found+1 |
end |
end |
end |
function NeedToKnow.ExecutiveFrame_COMBAT_LOG_EVENT_UNFILTERED(tod, event, hideCaster, guidCaster, ...) |
-- the time that's passed in appears to be time of day, not game time like everything else. |
local time = GetTime() |
local time = g_GetTime() |
-- TODO: Is checking r.state sufficient or must event be checked instead? |
if ( guidCaster == NeedToKnow.guidPlayer and event=="SPELL_CAST_SUCCESS") then |
local guidTarget, nameTarget, _, _, spellid, spell = select(4, ...) -- source_name, source_flags, source_flags2, |
local found |
local t = NeedToKnow.last_cast |
local last = NeedToKnow.last_cast_tail-1 |
local t = m_last_cast |
local last = m_last_cast_tail-1 |
local i |
for i = last,NeedToKnow.last_cast_head,-1 do |
for i = last,m_last_cast_head,-1 do |
if t[i].spell == spell then |
found = i |
break |
end |
end |
local rBySpell = NeedToKnow.last_guid[spell] |
local rBySpell = m_last_guid[spell] |
if ( rBySpell ) then |
local rByGuid = rBySpell[guidTarget] |
if not rByGuid then |
end |
if ( found == last ) then |
NeedToKnow.last_cast_tail = 1 |
NeedToKnow.last_cast_head = 1 |
m_last_cast_tail = 1 |
m_last_cast_head = 1 |
NeedToKnow_ExecutiveFrame:UnregisterEvent("COMBAT_LOG_EVENT_UNFILTERED") |
else |
NeedToKnow.last_cast_head = found+1 |
m_last_cast_head = found+1 |
end |
end |
end |
NeedToKnow_Visible = true |
end |
NeedToKnow.last_cast = {} -- [n] = { spell, target, serial } |
NeedToKnow.last_cast_head = 1 |
NeedToKnow.last_cast_tail = 1 |
NeedToKnow.last_guid = {} -- [spell][guidTarget] = { time, dur, expiry } |
m_last_cast = {} -- [n] = { spell, target, serial } |
m_last_cast_head = 1 |
m_last_cast_tail = 1 |
m_last_guid = {} -- [spell][guidTarget] = { time, dur, expiry } |
NeedToKnow.totem_drops = {} -- array 1-4 of precise times the totems appeared |
NeedToKnow.weapon_enchants = { mhand = {}, ohand = {} } |
function NeedToKnow.ExecutiveFrame_PLAYER_TALENT_UPDATE() |
if NeedToKnow.CharSettings then |
local spec = GetActiveTalentGroup() |
local spec = g_GetActiveTalentGroup() |
local profile_key = NeedToKnow.CharSettings.Specs[spec] |
if not profile_key then |
function NeedToKnow.ExecutiveFrame_UNIT_TARGET(unitTargeting) |
if NeedToKnow.bInCombat and not NeedToKnow.bCombatWithBoss then |
if m_bInCombat and not m_bCombatWithBoss then |
if UnitLevel(unitTargeting .. 'target') == -1 then |
NeedToKnow.bCombatWithBoss = true |
m_bCombatWithBoss = true |
if NeedToKnow.BossStateBars then |
for bar, unused in pairs(NeedToKnow.BossStateBars) do |
NeedToKnow.Bar_AuraCheck(bar) |
mfn_Bar_AuraCheck(bar) |
end |
end |
end |
function NeedToKnow.ExecutiveFrame_PLAYER_REGEN_DISABLED(unitTargeting) |
NeedToKnow.bInCombat = true |
NeedToKnow.bCombatWithBoss = false |
m_bInCombat = true |
m_bCombatWithBoss = false |
if IsInRaid() then |
for i = 1, 40 do |
if UnitLevel("raid"..i.."target") == -1 then |
NeedToKnow.bCombatWithBoss = true; |
m_bCombatWithBoss = true; |
break; |
end |
end |
elseif IsInGroup() then |
for i = 1, 5 do |
if UnitLevel("party"..i.."target") == -1 then |
NeedToKnow.bCombatWithBoss = true; |
m_bCombatWithBoss = true; |
break; |
end |
end |
elseif UnitLevel("target") == -1 then |
NeedToKnow.bCombatWithBoss = true |
m_bCombatWithBoss = true |
end |
if NeedToKnow.BossStateBars then |
for bar, unused in pairs(NeedToKnow.BossStateBars) do |
NeedToKnow.Bar_AuraCheck(bar) |
mfn_Bar_AuraCheck(bar) |
end |
end |
end |
function NeedToKnow.ExecutiveFrame_PLAYER_REGEN_ENABLED(unitTargeting) |
NeedToKnow.bInCombat = false |
NeedToKnow.bCombatWithBoss = false |
m_bInCombat = false |
m_bCombatWithBoss = false |
if NeedToKnow.BossStateBars then |
for bar, unused in pairs(NeedToKnow.BossStateBars) do |
NeedToKnow.Bar_AuraCheck(bar) |
mfn_Bar_AuraCheck(bar) |
end |
end |
end |
-- Switch to the new profile |
NeedToKnow.ProfileSettings = NeedToKnow_Profiles[profile_key] |
local spec = GetActiveTalentGroup() |
local spec = g_GetActiveTalentGroup() |
NeedToKnow.CharSettings.Specs[spec] = profile_key |
-- fill in any missing defaults |
end |
local function SetStatusBarValue(bar,texture,value,value0) |
mfn_SetStatusBarValue = function (bar,texture,value,value0) |
local pct0 = 0 |
if value0 then |
pct0 = value0 / bar.max_value |
local idx = entry.idxName |
if not id then |
if ( name == "Auto Shot" or |
name == NEEDTOKNOW.AUTO_SHOT ) |
name == c_AUTO_SHOT_NAME ) |
then |
bar.settings.bAutoShot = true |
bar.cd_functions[idx] = NeedToKnow.GetAutoShotCooldown |
bar.cd_functions[idx] = mfn_GetAutoShotCooldown |
else |
local item_id = NeedToKnow.GetItemIDString(name) |
if item_id then |
if nil ~= betterSpell then |
entry.id = betterSpell |
entry.name = nil |
bar.cd_functions[idx] = NeedToKnow.GetSpellCooldown |
bar.cd_functions[idx] = mfn_GetSpellCooldown |
elseif not GetSpellCooldown(name) then |
bar.cd_functions[idx] = NeedToKnow.GetUnresolvedCooldown |
bar.cd_functions[idx] = mfn_GetUnresolvedCooldown |
else |
bar.cd_functions[idx] = NeedToKnow.GetSpellCooldown |
bar.cd_functions[idx] = mfn_GetSpellCooldown |
end |
end |
end |
bar.settings = barSettings |
bar.unit = barSettings.Unit |
bar.nextUpdate = GetTime() + NEEDTOKNOW.UPDATE_INTERVAL |
bar.nextUpdate = g_GetTime() + c_UPDATE_INTERVAL |
bar.fixedDuration = tonumber(groupSettings.FixedDuration) |
if ( not bar.fixedDuration or 0 >= bar.fixedDuration ) then |
end |
bar.max_value = 1 |
SetStatusBarValue(bar,bar.bar1,1) |
mfn_SetStatusBarValue(bar,bar.bar1,1) |
bar.bar1:SetTexture(NeedToKnow.LSM:Fetch("statusbar", NeedToKnow.ProfileSettings["BarTexture"])) |
if ( bar.bar2 ) then |
bar.bar2:SetTexture(NeedToKnow.LSM:Fetch("statusbar", NeedToKnow.ProfileSettings["BarTexture"])) |
-- Determine which helper functions to use |
if "BUFFCD" == barSettings.BuffOrDebuff then |
bar.fnCheck = NeedToKnow.AuraCheck_BUFFCD |
bar.fnCheck = mfn_AuraCheck_BUFFCD |
elseif "TOTEM" == barSettings.BuffOrDebuff then |
bar.fnCheck = NeedToKnow.AuraCheck_TOTEM |
bar.fnCheck = mfn_AuraCheck_TOTEM |
elseif "USABLE" == barSettings.BuffOrDebuff then |
bar.fnCheck = NeedToKnow.AuraCheck_USABLE |
bar.fnCheck = mfn_AuraCheck_USABLE |
elseif "EQUIPSLOT" == barSettings.BuffOrDebuff then |
bar.fnCheck = NeedToKnow.AuraCheck_EQUIPSLOT |
bar.fnCheck = mfn_AuraCheck_EQUIPSLOT |
elseif "CASTCD" == barSettings.BuffOrDebuff then |
bar.fnCheck = NeedToKnow.AuraCheck_CASTCD |
bar.fnCheck = mfn_AuraCheck_CASTCD |
for idx, entry in ipairs(bar.spells) do |
table.insert(bar.cd_functions, NeedToKnow.GetSpellCooldown) |
table.insert(bar.cd_functions, mfn_GetSpellCooldown) |
NeedToKnow.SetupSpellCooldown(bar, entry) |
end |
elseif "mhand" == barSettings.Unit or "ohand" == barSettings.Unit then |
bar.fnCheck = NeedToKnow.AuraCheck_Weapon |
bar.fnCheck = mfn_AuraCheck_Weapon |
elseif barSettings.show_all_stacks then |
bar.fnCheck = NeedToKnow.AuraCheck_AllStacks |
bar.fnCheck = mfn_AuraCheck_AllStacks |
else |
bar.fnCheck = NeedToKnow.AuraCheck_Single |
bar.fnCheck = mfn_AuraCheck_Single |
end |
if ( barSettings.BuffOrDebuff == "BUFFCD" ) then |
NeedToKnow.SetScripts(bar) |
-- Events were cleared while unlocked, so need to check the bar again now |
NeedToKnow.Bar_AuraCheck(bar) |
mfn_Bar_AuraCheck(bar) |
else |
NeedToKnow.ClearScripts(bar) |
bar:Hide() |
spellName = entry.name |
end |
if spellName then |
local r = NeedToKnow.last_guid[spellName] |
local r = m_last_guid[spellName] |
if not r then |
NeedToKnow.last_guid[spellName] = { time=0, dur=0, expiry=0 } |
m_last_guid[spellName] = { time=0, dur=0, expiry=0 } |
end |
else |
print("Warning! NTK could not get name for ", entry.id) |
end |
function NeedToKnow.Bar_OnSizeChanged(self) |
if (self.bar1.cur_value) then SetStatusBarValue(self, self.bar1, self.bar1.cur_value) end |
if (self.bar2 and self.bar2.cur_value) then SetStatusBarValue(self, self.bar2, self.bar2.cur_value, self.bar1.cur_value) end |
if (self.bar1.cur_value) then mfn_SetStatusBarValue(self, self.bar1, self.bar1.cur_value) end |
if (self.bar2 and self.bar2.cur_value) then mfn_SetStatusBarValue(self, self.bar2, self.bar2.cur_value, self.bar1.cur_value) end |
end |
function NeedToKnow.Bar_OnEvent(self, event, unit, ...) |
if ( event == "COMBAT_LOG_EVENT_UNFILTERED") then |
local combatEvent = select(1, ...) |
if ( NEEDTOKNOW.AURAEVENTS[combatEvent] ) then |
if ( c_AURAEVENTS[combatEvent] ) then |
local guidTarget = select(7, ...) |
if ( guidTarget == UnitGUID(self.unit) ) then |
if ( guidTarget == g_UnitGUID(self.unit) ) then |
local idSpell, nameSpell = select(11, ...) |
if (self.auraName:find(idSpell) or |
self.auraName:find(nameSpell)) |
then |
NeedToKnow.Bar_AuraCheck(self) |
mfn_Bar_AuraCheck(self) |
end |
end |
elseif ( combatEvent == "UNIT_DIED" ) then |
local guidDeceased = select(7, ...) |
if ( guidDeceased == UnitGUID(self.unit) ) then |
NeedToKnow.Bar_AuraCheck(self) |
mfn_Bar_AuraCheck(self) |
end |
end |
elseif ( event == "PLAYER_TOTEM_UPDATE" ) or |
( event == "SPELL_UPDATE_COOLDOWN" ) or |
( event == "SPELL_UPDATE_USABLE" ) |
then |
NeedToKnow.Bar_AuraCheck(self) |
mfn_Bar_AuraCheck(self) |
elseif ( event == "UNIT_AURA" ) and ( unit == self.unit ) then |
NeedToKnow.Bar_AuraCheck(self) |
mfn_Bar_AuraCheck(self) |
elseif ( event == "UNIT_INVENTORY_CHANGED" and unit == "player" ) then |
NeedToKnow.UpdateWeaponEnchants() |
NeedToKnow.Bar_AuraCheck(self) |
mfn_Bar_AuraCheck(self) |
elseif ( event == "PLAYER_TARGET_CHANGED" ) or ( event == "PLAYER_FOCUS_CHANGED" ) then |
if self.unit == "targettarget" then |
NeedToKnow.CheckCombatLogRegistration(self) |
end |
NeedToKnow.Bar_AuraCheck(self) |
mfn_Bar_AuraCheck(self) |
elseif ( event == "UNIT_TARGET" and unit == "target" ) then |
if self.unit == "targettarget" then |
NeedToKnow.CheckCombatLogRegistration(self) |
end |
NeedToKnow.Bar_AuraCheck(self) |
mfn_Bar_AuraCheck(self) |
elseif ( event == "UNIT_PET" and unit == "player" ) then |
NeedToKnow.Bar_AuraCheck(self) |
mfn_Bar_AuraCheck(self) |
elseif ( event == "PLAYER_SPELLCAST_SUCCEEDED" ) then |
local spellName, spellID, tgt = select(1,...) |
local i,entry |
if entry.id == spellID or entry.name == spellName then |
self.unit = tgt or "unknown" |
--trace("Updating",self:GetName(),"since it was recast on",self.unit) |
NeedToKnow.Bar_AuraCheck(self) |
mfn_Bar_AuraCheck(self) |
break; |
end |
end |
self:UnregisterEvent("UNIT_SPELLCAST_SUCCEEDED") |
elseif ( event == "UNIT_SPELLCAST_SUCCEEDED" ) then |
local spell = select(1,...) |
if ( self.settings.bAutoShot and unit == "player" and spell == NEEDTOKNOW.AUTO_SHOT ) then |
if ( self.settings.bAutoShot and unit == "player" and spell == c_AUTO_SHOT_NAME ) then |
local interval = UnitRangedDamage("player") |
self.tAutoShotCD = interval |
self.tAutoShotStart = GetTime() |
NeedToKnow.Bar_AuraCheck(self) |
self.tAutoShotStart = g_GetTime() |
mfn_Bar_AuraCheck(self) |
end |
end |
end |
return text |
end |
-- Called by NeedToKnow.UpdateVCT, which is called from AuraCheck and possibly |
-- Called by mfn_UpdateVCT, which is called from AuraCheck and possibly |
-- by Bar_Update depending on vct_refresh. In addition to refactoring out some |
-- code from the long AuraCheck, this also provides a convenient hook for other addons |
function NeedToKnow.ComputeVCTDuration(bar) |
return vct_duration |
end |
function NeedToKnow.UpdateVCT(bar) |
mfn_UpdateVCT = function (bar) |
local vct_duration = NeedToKnow.ComputeVCTDuration(bar) |
local dur = bar.fixedDuration or bar.duration |
-- Determine the size of the visual cast bar |
if ( bar.settings.vct_enabled ) then |
NeedToKnow.UpdateVCT(bar) |
mfn_UpdateVCT(bar) |
end |
-- Force an update to get all the bars to the current position (sharing code) |
-- This will call UpdateVCT again, but that seems ok |
bar.nextUpdate = -NEEDTOKNOW.UPDATE_INTERVAL |
if bar.expirationTime > GetTime() then |
bar.nextUpdate = -c_UPDATE_INTERVAL |
if bar.expirationTime > g_GetTime() then |
NeedToKnow.Bar_OnUpdate(bar, 0) |
end |
else |
-- Hide the time text and spark for auras with "infinite" duration |
bar.max_value = 1 |
SetStatusBarValue(bar,bar.bar1,1) |
if bar.bar2 then SetStatusBarValue(bar,bar.bar2,1) end |
mfn_SetStatusBarValue(bar,bar.bar1,1) |
if bar.bar2 then mfn_SetStatusBarValue(bar,bar.bar2,1) end |
bar.time:Hide() |
bar.spark:Hide() |
bar.time:Hide() |
bar.spark:Hide() |
bar.max_value = 1 |
SetStatusBarValue(bar,bar.bar1,1) |
mfn_SetStatusBarValue(bar,bar.bar1,1) |
if ( bar.icon ) then |
bar.icon:Hide() |
end |
-- Helper for NeedToKnow.AuraCheck_CASTCD which gets the autoshot cooldown |
function NeedToKnow.GetAutoShotCooldown(bar) |
local tNow = GetTime() |
-- Helper for mfn_AuraCheck_CASTCD which gets the autoshot cooldown |
mfn_GetAutoShotCooldown = function(bar) |
local tNow = g_GetTime() |
if ( bar.tAutoShotStart and bar.tAutoShotStart + bar.tAutoShotCD > tNow ) then |
local n, icon = GetSpellInfo(75) |
return bar.tAutoShotStart, bar.tAutoShotCD, 1, NEEDTOKNOW.AUTO_SHOT, icon |
return bar.tAutoShotStart, bar.tAutoShotCD, 1, c_AUTO_SHOT_NAME, icon |
else |
bar.tAutoShotStart = nil |
end |
end |
-- Helper for NeedToKnow.AuraCheck_CASTCD for names we haven't figured out yet |
function NeedToKnow.GetUnresolvedCooldown(bar, entry) |
-- Helper for mfn_AuraCheck_CASTCD for names we haven't figured out yet |
mfn_GetUnresolvedCooldown = function(bar, entry) |
NeedToKnow.SetupSpellCooldown(bar, entry) |
local fn = bar.cd_functions[entry.idxName] |
if NeedToKnow.GetUnresolvedCooldown ~= fn then |
if mfn_GetUnresolvedCooldown ~= fn then |
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, entry) |
mfn_GetSpellCooldown = function(bar, entry) |
local barSpell = entry.id or entry.name |
local start, cd_len, enable = GetSpellCooldown(barSpell) |
if start and start > 0 then |
elseif spellPower == 5 then -- Rune |
-- Filter out rune cooldown artificially extending the cd |
if cd_len <= 10 then |
local tNow = GetTime() |
local tNow = g_GetTime() |
if bar.expirationTime and tNow < bar.expirationTime 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[spellName] and NeedToKnow.last_sent[spellName] > (tNow - 1.5) then |
elseif m_last_sent and m_last_sent[spellName] and m_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 |
data.icon = nil |
--trace("Warning: NTK couldn't figure out what enchant is on weapon slot",slot) |
end |
data.expiration = GetTime() + data.expiration/1000 |
data.expiration = g_GetTime() + data.expiration/1000 |
if oldname ~= data.name then |
local _ |
_,_,data.icon = GetSpellInfo(data.name) |
mdata.present, mdata.expiration, mdata.charges, |
odata.present, odata.expiration, odata.charges |
= GetWeaponEnchantInfo() |
= g_GetWeaponEnchantInfo() |
if ( mdata.present ) then |
NeedToKnow.UpdateWeaponEnchantData(mdata, 16) |
end |
local function AddInstanceToStacks(all_stacks, bar_entry, duration, name, count, expirationTime, iconPath, caster, tt1, tt2, tt3) |
mfn_AddInstanceToStacks = function (all_stacks, bar_entry, duration, name, count, expirationTime, iconPath, caster, tt1, tt2, tt3) |
if duration then |
if (not count or count < 1) then count = 1 end |
if ( 0 == all_stacks.total or all_stacks.min.expirationTime > expirationTime ) then |
-- Bar_AuraCheck helper for Totem bars, this returns data if |
-- a totem matching bar_entry is currently out. |
function NeedToKnow.AuraCheck_TOTEM(bar, bar_entry, all_stacks) |
mfn_AuraCheck_TOTEM = function(bar, bar_entry, all_stacks) |
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) |
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 |
-- that the totem actually appeared, so long as GetTime is reasonably close to |
-- a latency meaning it can be significantly low. So we cache the g_GetTime |
-- that the totem actually appeared, so long as g_GetTime is reasonably close to |
-- startTime (since the totems may have been out for awhile before this runs.) |
if ( not NeedToKnow.totem_drops[iSlot] or |
NeedToKnow.totem_drops[iSlot] < startTime ) |
then |
local precise = GetTime() |
local precise = g_GetTime() |
if ( precise - startTime > 1 ) then |
precise = startTime + 1 |
end |
NeedToKnow.totem_drops[iSlot] = precise |
end |
AddInstanceToStacks(all_stacks, bar_entry, |
mfn_AddInstanceToStacks(all_stacks, bar_entry, |
totemDuration, -- duration |
totemName, -- name |
1, -- count |
-- 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) |
mfn_AuraCheck_EQUIPSLOT = function (bar, bar_entry, all_stacks) |
local spellName, spellRank, spellIconPath |
if ( bar_entry.id ) then |
local id = GetInventoryItemID("player",bar_entry.id) |
if id then |
local item_entry = NeedToKnow.scratch.bar_entry |
local item_entry = m_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, |
mfn_AddInstanceToStacks(all_stacks, bar_entry, |
cd_len, -- duration |
name, -- name |
1, -- count |
-- Bar_AuraCheck helper that checks the bar.weapon_enchants |
-- (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) |
mfn_AuraCheck_Weapon = function (bar, bar_entry, all_stacks) |
local data = NeedToKnow.weapon_enchants[bar.settings.Unit] |
if ( data.present and data.name and data.name:find(bar_entry.name) ) then |
AddInstanceToStacks( all_stacks, bar_entry, |
mfn_AddInstanceToStacks( all_stacks, bar_entry, |
1800, -- duration TODO: Get real duration? |
data.name, -- name |
data.charges, -- count |
-- Bar_AuraCheck helper that checks for spell/item use cooldowns |
-- Relies on NeedToKnow.GetAutoShotCooldown, NeedToKnow.GetSpellCooldown |
-- Relies on mfn_GetAutoShotCooldown, mfn_GetSpellCooldown |
-- and NeedToKnow.GetItemCooldown. Bar_Update will have already pre-processed |
-- this list so that bar.cd_functions[idxName] can do something with bar_entry |
function NeedToKnow.AuraCheck_CASTCD(bar, bar_entry, all_stacks) |
mfn_AuraCheck_CASTCD = function(bar, bar_entry, all_stacks) |
local idxName = bar_entry.idxName |
local func = bar.cd_functions[idxName] |
if ( not func ) then |
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 |
if start and cd_len <= 1.5 and func ~= mfn_GetAutoShotCooldown then |
if bar.expirationTime and bar.expirationTime <= (start + cd_len) then |
start = bar.expirationTime - bar.duration |
cd_len = bar.duration |
end |
if start and cd_len then |
local tNow = GetTime() |
local tNow = g_GetTime() |
local tEnd = start + cd_len |
if ( tEnd > tNow + 0.1 ) then |
AddInstanceToStacks( all_stacks, bar_entry, |
mfn_AddInstanceToStacks( all_stacks, bar_entry, |
cd_len, -- duration |
buffName, -- name |
1, -- count |
-- 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) |
mfn_AuraCheck_USABLE = function (bar, bar_entry, all_stacks) |
local key = bar_entry.id or bar_entry.name |
local settings = bar.settings |
if ( not key ) then key = "" end |
if (isUsable or notEnoughMana) then |
local duration = settings.usable_duration |
local expirationTime |
local tNow = GetTime() |
local tNow = g_GetTime() |
if ( not bar.expirationTime or |
(bar.expirationTime > 0 and bar.expirationTime < tNow - 0.01) ) |
then |
expirationTime = bar.expirationTime |
end |
AddInstanceToStacks( all_stacks, bar_entry, |
mfn_AddInstanceToStacks( all_stacks, bar_entry, |
duration, -- duration |
spellName, -- name |
1, -- count |
end |
function NeedToKnow.ResetScratchStacks(buff_stacks) |
mfn_ResetScratchStacks = function (buff_stacks) |
buff_stacks.total = 0; |
buff_stacks.total_ttn[1] = 0; |
buff_stacks.total_ttn[2] = 0; |
-- Bar_AuraCheck helper for watching "internal cooldowns", which is like a spell |
-- 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 buff_stacks = NeedToKnow.scratch.buff_stacks |
NeedToKnow.ResetScratchStacks(buff_stacks); |
NeedToKnow.AuraCheck_Single(bar, bar_entry, buff_stacks) |
local tNow = GetTime() |
mfn_AuraCheck_BUFFCD = function (bar, bar_entry, all_stacks) |
local buff_stacks = m_scratch.buff_stacks |
mfn_ResetScratchStacks(buff_stacks); |
mfn_AuraCheck_Single(bar, bar_entry, buff_stacks) |
local tNow = g_GetTime() |
if ( buff_stacks.total > 0 ) then |
if buff_stacks.max.expirationTime == 0 then |
-- TODO: This really doesn't work very well as a substitute for telling when the aura was applied |
if not bar.expirationTime then |
local nDur = tonumber(bar.settings.buffcd_duration) |
AddInstanceToStacks( all_stacks, bar_entry, |
mfn_AddInstanceToStacks( all_stacks, bar_entry, |
nDur, buff_stacks.min.buffName, 1, nDur+tNow, buff_stacks.min.iconPath, buff_stacks.min.caster ) |
else |
AddInstanceToStacks( all_stacks, bar_entry, |
mfn_AddInstanceToStacks( all_stacks, bar_entry, |
bar.duration, -- duration |
bar.buffName, -- name |
1, -- count |
local duration = tonumber(bar.settings.buffcd_duration) |
local expiration = tStart + duration |
if ( expiration > tNow ) then |
AddInstanceToStacks( all_stacks, bar_entry, |
mfn_AddInstanceToStacks( all_stacks, bar_entry, |
duration, -- duration |
buff_stacks.min.buffName, -- name |
-- Seeing the charges on the CD bar violated least surprise for me |
buff_stacks.min.caster ) -- caster |
end |
elseif ( bar.expirationTime and bar.expirationTime > tNow + 0.1 ) then |
AddInstanceToStacks( all_stacks, bar_entry, |
mfn_AddInstanceToStacks( all_stacks, bar_entry, |
bar.duration, -- duration |
bar.buffName, -- name |
1, -- count |
-- 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) |
mfn_AuraCheck_Single = function(bar, bar_entry, all_stacks) |
local settings = bar.settings |
local filter = settings.BuffOrDebuff |
if settings.OnlyMine then |
end |
if (spellID == barID) then |
AddInstanceToStacks( all_stacks, bar_entry, |
mfn_AddInstanceToStacks( all_stacks, bar_entry, |
duration, -- duration |
buffName, -- name |
count, -- count |
else |
local buffName, iconPath, count, duration, expirationTime, caster, _, tt1, tt2, tt3 |
= UnitAuraWrapper(bar.unit, bar_entry.name, nil, filter) |
AddInstanceToStacks( all_stacks, bar_entry, |
mfn_AddInstanceToStacks( all_stacks, bar_entry, |
duration, -- duration |
buffName, -- name |
count, -- count |
-- 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) |
mfn_AuraCheck_AllStacks = function (bar, bar_entry, all_stacks) |
local j = 1 |
local settings = bar.settings |
local filter = settings.BuffOrDebuff |
if (spellID == bar_entry.id) or (bar_entry.name == buffName) |
then |
AddInstanceToStacks(all_stacks, bar_entry, |
mfn_AddInstanceToStacks(all_stacks, bar_entry, |
duration, |
buffName, |
count, |
-- Called whenever the state of auras on the bar's unit may have changed |
function NeedToKnow.Bar_AuraCheck(bar) |
local g_UnitExists = UnitExists |
mfn_Bar_AuraCheck = function (bar) |
local settings = bar.settings |
local bUnitExists, isWeapon |
if "mhand" == settings.Unit or |
elseif "lastraid" == settings.Unit then |
bUnitExists = bar.unit and UnitExists(bar.unit) |
else |
bUnitExists = UnitExists(settings.Unit) |
bUnitExists = g_UnitExists(settings.Unit) |
end |
-- Determine if the bar should be showing anything |
local all_stacks |
local idxName, duration, buffName, count, expirationTime, iconPath, caster |
if ( bUnitExists ) then |
all_stacks = NeedToKnow.scratch.all_stacks |
NeedToKnow.ResetScratchStacks(all_stacks); |
all_stacks = m_scratch.all_stacks |
mfn_ResetScratchStacks(all_stacks); |
-- Call the helper function for each of the spells in the list |
for idx, entry in ipairs(bar.spells) do |
-- (reset_spells will only be set for BUFFCD) |
if ( bar.reset_spells ) then |
local maxStart = 0 |
local tNow = GetTime() |
local buff_stacks = NeedToKnow.scratch.buff_stacks |
NeedToKnow.ResetScratchStacks(buff_stacks); |
local tNow = g_GetTime() |
local buff_stacks = m_scratch.buff_stacks |
mfn_ResetScratchStacks(buff_stacks); |
-- Keep track of when the reset auras were last applied to the player |
for idx, resetSpell in ipairs(bar.reset_spells) do |
-- 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, resetSpell, buff_stacks) |
= mfn_AuraCheck_Single(bar, resetSpell, buff_stacks) |
local tStart |
if buff_stacks.total > 0 then |
if 0 == buff_stacks.max.duration then |
if (settings.bDetectExtends) then |
local curStart = expirationTime - duration |
local guidTarget = UnitGUID(bar.unit) |
local r = NeedToKnow.last_guid[buffName] |
local r = m_last_guid[buffName] |
if ( not r[guidTarget] ) then -- Should only happen from /reload or /ntk while the aura is active |
-- This went off for me, but I don't know a repro yet. I suspect it has to do with bear/cat switching |
bar:Show() |
else |
if (settings.bDetectExtends and bar.buffName) then |
local r = NeedToKnow.last_guid[bar.buffName] |
local r = m_last_guid[bar.buffName] |
if ( r ) then |
local guidTarget = UnitGUID(bar.unit) |
if guidTarget then |
end |
end |
if ( bBlink and not settings.blink_ooc ) then |
if not UnitAffectingCombat("player") then |
if not g_UnitAffectingCombat("player") then |
bBlink = false |
end |
end |
if ( bBlink and settings.blink_boss ) then |
if UnitIsFriend(bar.unit, "player") then |
bBlink = NeedToKnow.bCombatWithBoss |
if g_UnitIsFriend(bar.unit, "player") then |
bBlink = m_bCombatWithBoss |
else |
bBlink = (UnitLevel(bar.unit) == -1) |
end |
end |
function NeedToKnow.Bar_OnUpdate(self, elapsed) |
local now = GetTime() |
local now = g_GetTime() |
if ( now > self.nextUpdate ) then |
self.nextUpdate = now + NEEDTOKNOW.UPDATE_INTERVAL |
self.nextUpdate = now + c_UPDATE_INTERVAL |
if ( self.blink ) then |
self.blink_phase = self.blink_phase + NEEDTOKNOW.UPDATE_INTERVAL |
self.blink_phase = self.blink_phase + c_UPDATE_INTERVAL |
if ( self.blink_phase >= 2 ) then |
self.blink_phase = 0 |
end |
local origUnit = self.settings.Unit |
if ( origUnit == "mhand" ) then |
-- The expiry time doesn't update right away, so we have to poll it |
local mhEnchant, mhExpire = GetWeaponEnchantInfo() |
local mhEnchant, mhExpire = g_GetWeaponEnchantInfo() |
if ( mhExpire ) then |
self.expirationTime = GetTime() + mhExpire/1000 |
self.expirationTime = g_GetTime() + mhExpire/1000 |
end |
elseif ( origUnit == "ohand" ) then |
local _, _, _, ohEnchant, ohExpire = GetWeaponEnchantInfo() |
local _, _, _, ohEnchant, ohExpire = g_GetWeaponEnchantInfo() |
if ( ohExpire ) then |
self.expirationTime = GetTime() + ohExpire/1000 |
self.expirationTime = g_GetTime() + ohExpire/1000 |
end |
end |
-- others fire the event too soon. So we have to keep checking. |
if ( self.duration and self.duration > 0 ) then |
local duration = self.fixedDuration or self.duration |
local bar1_timeLeft = self.expirationTime - GetTime() |
local bar1_timeLeft = self.expirationTime - g_GetTime() |
if ( bar1_timeLeft < 0 ) then |
if ( self.settings.BuffOrDebuff == "CASTCD" or |
self.settings.BuffOrDebuff == "BUFFCD" or |
self.settings.BuffOrDebuff == "EQUIPSLOT" ) |
then |
NeedToKnow.Bar_AuraCheck(self) |
mfn_Bar_AuraCheck(self) |
return |
end |
bar1_timeLeft = 0 |
end |
SetStatusBarValue(self, self.bar1, bar1_timeLeft); |
mfn_SetStatusBarValue(self, self.bar1, bar1_timeLeft); |
if ( self.settings.show_time ) then |
local fn = NeedToKnow[self.settings.TimeFormat] |
local oldText = self.time:GetText() |
end |
if ( self.max_expirationTime ) then |
local bar2_timeLeft = self.max_expirationTime - GetTime() |
SetStatusBarValue(self, self.bar2, bar2_timeLeft, bar1_timeLeft) |
local bar2_timeLeft = self.max_expirationTime - g_GetTime() |
mfn_SetStatusBarValue(self, self.bar2, bar2_timeLeft, bar1_timeLeft) |
end |
if ( self.vct_refresh ) then |
NeedToKnow.UpdateVCT(self) |
mfn_UpdateVCT(self) |
end |
end |
end |