/trunk
NeedToKnow = {}; |
-- NEEDTOKNOW = {} is defined in the localization file, which must be loaded before this file |
NEEDTOKNOW.VERSION = "2.5.1"; |
NEEDTOKNOW.VERSION = "2.8.2"; |
NEEDTOKNOW.MAXGROUPS = 4; |
NEEDTOKNOW.MAXBARS = 6; |
NEEDTOKNOW.UPDATE_INTERVAL = 0.05; |
NEEDTOKNOW.BAR_DEFAULTS = { |
Enabled = true, |
AuraName = "", |
-- AuraName = {""}, |
-- Priority = "list" or "maxtime" or "mintime", |
Unit = "player", |
BuffOrDebuff = "HELPFUL", |
OnlyMine = true, |
BarColor = { r=0.6, g=0.6, b=0.6, a=1.0 }, |
TimeFormat = "Fmt_SingleUnit", |
vct_enabled = false, |
vct_color = { r=0.6, g=0.6, b=0.0, a=0.3 }, |
vct_spell = "", |
vct_extra = 0, |
bDetectExtends = false, |
}; |
NEEDTOKNOW.GROUP_DEFAULTS = { |
Enabled = false, |
-- EXECUTIVE FRAME |
-- --------------- |
function NeedToKnow.ExecutiveFrame_OnEvent(self, event, arg1) |
if ( event == "ADDON_LOADED" and arg1 == "NeedToKnow") then |
function NeedToKnow.ExecutiveFrame_OnEvent(self, event, ...) |
if ( event == "ADDON_LOADED" and select(1,...) == "NeedToKnow") then |
if ( not NeedToKnow_Settings ) then |
NeedToKnow_Settings = CopyTable(NEEDTOKNOW.DEFAULTS); |
NeedToKnow_Settings["Spec"][1]["Groups"][1]["Enabled"] = true; |
NeedToKnow_Settings["Spec"][2]["Groups"][1]["Enabled"] = true; |
elseif ( NeedToKnow_Settings["Version"] < NEEDTOKNOW.VERSION and not NEEDTOKNOW.UPGRADED) then |
else |
NeedToKnow.SafeUpgrade(); |
end |
if ( not NeedToKnow_Visible ) then |
NeedToKnow_Visible = true; |
end |
NeedToKnow.last_cast = {}; -- [spell][guidTarget] = { time, dur } |
NeedToKnow.nSent = 0; |
NeedToKnow.totem_drops = {}; -- array 1-4 of precise times the totems appeared |
SlashCmdList["NEEDTOKNOW"] = NeedToKnow.SlashCommand; |
SLASH_NEEDTOKNOW1 = "/needtoknow"; |
SLASH_NEEDTOKNOW2 = "/ntk"; |
else |
local fnName = "ExecutiveFrame_"..event; |
local fn = NeedToKnow[fnName]; |
if ( fn ) then |
fn(...); |
end |
end |
end |
elseif ( event == "PLAYER_LOGIN") then |
-- save group positions if upgrading from version that used layout-local.txt |
if ( NeedToKnow_Settings["Version"] < "2.1" ) then |
for groupID = 1, NEEDTOKNOW.MAXGROUPS do |
NeedToKnow.SavePosition(_G["NeedToKnow_Group"..groupID], groupID); |
end |
NeedToKnow_Settings["Version"] = NEEDTOKNOW.VERSION; |
function NeedToKnow.ExecutiveFrame_UNIT_SPELLCAST_SUCCEEDED(unit, spell, rank) |
if unit == "player" then |
local r = NeedToKnow.last_cast[spell]; |
if ( r and r.state == 1 ) then |
r.state = 2; |
elseif (NeedToKnow_Settings["Version"] < "2.4") then |
local tempSettings = CopyTable(NeedToKnow_Settings); |
NeedToKnow.Reset(); |
-- A little extra safety just in case we get two SUCCEEDED entries |
-- before we get the combat log events for them (though I don't |
-- think this is possible.) |
NeedToKnow.last_success = spell; |
NeedToKnow_Settings = CopyTable(tempSettings); |
NeedToKnow_Settings["Version"] = NEEDTOKNOW.VERSION; |
NeedToKnow.Update(); |
NeedToKnow.UIPanel_Update(); |
-- We need the actual target, which we can only get from the combat log. |
-- Thankfully, the combat log event always comes after this one, so we |
-- don't need to register for the combat log for long at all. |
NeedToKnow_ExecutiveFrame:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED"); |
if ( NeedToKnow.nSent > 1 ) then |
NeedToKnow.nSent = NeedToKnow.nSent - 1; |
else |
NeedToKnow.nSent = 0; |
NeedToKnow_ExecutiveFrame:UnregisterEvent("UNIT_SPELLCAST_SUCCEEDED"); |
end |
end |
NeedToKnow.Update(); |
end |
end |
elseif ( event == "PLAYER_TALENT_UPDATE" ) then |
NEEDTOKNOW.CURRENTSPEC = GetActiveTalentGroup(); |
NeedToKnow.Update(); |
NeedToKnow.UIPanel_Update(); |
function NeedToKnow.ExecutiveFrame_COMBAT_LOG_EVENT_UNFILTERED(time, event, guidCaster, ...) |
-- the time that's passed in appears to be time of day, not game time like everything else. |
time = GetTime(); |
-- TODO: Is checking r.state sufficient or must event be checked instead? |
if ( guidCaster == NeedToKnow.guidPlayer ) then |
local guidTarget, _, _, _, spell = select(3, ...); |
local r = NeedToKnow.last_cast[spell]; |
if ( r and r.state == 2) then |
r.state = 0; |
-- record this spellcast |
if ( not r[guidTarget] ) then |
r[guidTarget] = { time = time, dur = 0 }; |
else |
r[guidTarget].time = time; |
r[guidTarget].dur = 0; |
end |
-- Use this event to expire some targets. This should limit us to |
-- two combats' worth of targets (since GC doesn't happen in combat) |
for kG, vG in pairs(r) do |
if ( type(vG) == "table" and ( vG.time + 300 < time ) ) then |
r[kG] = nil; |
end |
end |
end |
if ( spell == NeedToKnow.last_success ) then |
-- We saw our spell, we can disconnect from the spam hose |
NeedToKnow_ExecutiveFrame:UnregisterEvent("COMBAT_LOG_EVENT_UNFILTERED"); |
end |
end |
end |
function NeedToKnow.ExecutiveFrame_PLAYER_LOGIN() |
-- save group positions if upgrading from version that used layout-local.txt |
if ( not NeedToKnow_Settings.OldVersion or |
NeedToKnow_Settings.OldVersion < "2.1" ) then |
for groupID = 1, NEEDTOKNOW.MAXGROUPS do |
NeedToKnow.SavePosition(_G["NeedToKnow_Group"..groupID], groupID); |
end |
elseif (NeedToKnow_Settings["Version"] < "2.4") then |
NeedToKnow.UIPanel_Update(); |
end |
NeedToKnow.guidPlayer = UnitGUID("player"); |
NEEDTOKNOW.CURRENTSPEC = GetActiveTalentGroup(); |
NeedToKnow.Update(); |
NeedToKnow.UIPanel_Update(); |
end |
function NeedToKnow.ExecutiveFrame_PLAYER_TALENT_UPDATE() |
NEEDTOKNOW.CURRENTSPEC = GetActiveTalentGroup(); |
NeedToKnow.Update(); |
NeedToKnow.UIPanel_Update(); |
end |
function NeedToKnow.SafeUpgrade() |
if ( NeedToKnow_Settings["Version"] < "2.0" ) then -- total settings clear if v1.x |
NeedToKnow_Settings = CopyTable(NEEDTOKNOW.DEFAULTS); |
NeedToKnow.Update(); |
NeedToKnow.UIPanel_Update(); |
else |
NeedToKnow_Settings = NeedToKnow.AddNewSettings(NeedToKnow_Settings, NEEDTOKNOW.DEFAULTS); |
NeedToKnow_Settings["Version"] = NEEDTOKNOW.VERSION; |
end |
NeedToKnow_Settings.OldVersion = NeedToKnow_Settings["Version"]; |
NeedToKnow_Settings["Version"] = NEEDTOKNOW.VERSION; |
-- Add any new settings |
for iS,vS in ipairs(NeedToKnow_Settings["Spec"]) do |
for kD, vD in pairs(NEEDTOKNOW.SPEC_DEFAULTS) do |
if nil == vS[kD] then |
vS[kD] = NeedToKnow.deepcopy(vD); |
end |
end |
for iG,vG in ipairs(vS["Groups"]) do |
for kD, vD in pairs(NEEDTOKNOW.GROUP_DEFAULTS) do |
if nil == vG[kD] then |
vG[kD] = NeedToKnow.deepcopy(vD); |
end |
end |
for iB, vB in ipairs(vG["Bars"]) do |
for kD, vD in pairs(NEEDTOKNOW.BAR_DEFAULTS) do |
if nil == vB[kD] then |
vB[kD] = NeedToKnow.deepcopy(vD); |
end |
end |
end |
end |
end |
end |
function NeedToKnow.AddNewSettings(settings, defaults) |
for k, v in pairs(defaults) do |
if ( not settings[k] ) then |
if ( type(v) == "table" ) then |
settings[k] = {}; |
settings[k] = NeedToKnow.AddNewSettings(settings[k], defaults[k]); |
else |
settings[k] = v; |
end |
--[[ |
elseif ( type(v) == "table" ) and ( type(settings[k]) ~= "table" ) then |
local oldSetting = settings[k]; |
settings[k] = {}; |
settings[k][1] = oldSetting; |
-- settings[k] = {settings[k]}; |
--]] |
elseif ( type(v) == "table" ) then |
settings[k] = NeedToKnow.AddNewSettings(settings[k], defaults[k]); |
-- Copies anything (int, table, whatever). Unlike CopyTable, deepcopy can |
-- recreate a recursive reference structure (CopyTable will stack overflow.) |
-- Copied from http://lua-users.org/wiki/CopyTable |
function NeedToKnow.deepcopy(object) |
local lookup_table = {} |
local function _copy(object) |
if type(object) ~= "table" then |
return object |
elseif lookup_table[object] then |
return lookup_table[object] |
end |
local new_table = {} |
lookup_table[object] = new_table |
for index, value in pairs(object) do |
new_table[_copy(index)] = _copy(value) |
end |
return setmetatable(new_table, getmetatable(object)) |
end |
return settings; |
return _copy(object) |
end |
function NeedToKnow.Update() |
-- BARS |
-- ---- |
-- Called when the configuration of the bar has changed, when the addon |
-- is loaded or when ntk is locked and unlocked |
function NeedToKnow.Bar_Update(groupID, barID) |
local barName = "NeedToKnow_Group"..groupID.."Bar"..barID; |
local bar = _G[barName]; |
local groupSettings = NeedToKnow_Settings["Spec"][NEEDTOKNOW.CURRENTSPEC]["Groups"][groupID]; |
local barSettings = groupSettings["Bars"][barID]; |
bar.settings = barSettings; |
bar.auraName = barSettings.AuraName; |
bar.buffOrDebuff = barSettings.BuffOrDebuff; |
bar.unit = barSettings.Unit; |
bar.onlyMine = barSettings.OnlyMine; |
if ( barSettings.Unit == "mhand" or barSettings.Unit == "ohand" ) then |
bar.unit = "player"; |
end |
bar.nextUpdate = GetTime() + NEEDTOKNOW.UPDATE_INTERVAL; |
bar.fixedDuration = groupSettings.FixedDuration; |
background:SetHeight(bar:GetHeight() + 2*NeedToKnow_Settings["BarPadding"]); |
background:SetVertexColor(unpack(NeedToKnow_Settings["BkgdColor"])); |
bar.text:SetText(bar.auraName); |
-- Set up the Visual Cast Time overlay. It isn't a part of the template |
-- because most bars won't use it and thus don't need to pay the cost of |
-- a hidden frame |
if ( barSettings.vct_enabled ) then |
if ( nil == bar.vct ) then |
bar.vct = bar:CreateTexture(barName.."VisualCast", "ARTWORK"); |
bar.vct:SetPoint("TOPLEFT", bar, "TOPLEFT"); |
end |
local argb = barSettings.vct_color; |
bar.vct:SetTexture(argb.r, argb.g, argb.b, argb.a ); |
bar.vct:SetBlendMode("ADD"); |
bar.vct:SetHeight(bar:GetHeight()); |
elseif (nil ~= bar.vct) then |
bar.vct:Hide(); |
end |
if ( NeedToKnow_Settings["Locked"] ) then |
-- Set up the bar to be functional |
bar:EnableMouse(0); |
bar:Hide(); |
bar.spark:Show(); |
bar.time:Show(); |
if ( barSettings.Enabled ) then |
NeedToKnow.SetScripts(bar); |
-- Events were cleared while unlocked, so need to check the bar again n ow |
NeedToKnow.Bar_AuraCheck(bar); |
else |
NeedToKnow.ClearScripts(bar); |
bar:Hide(); |
end |
else |
NeedToKnow.ClearScripts(bar); |
-- Set up the bar to be configured |
bar:EnableMouse(1); |
bar:Show(); |
bar.spark:Hide(); |
bar.time:Hide(); |
end |
if ( bar.vct ) then |
bar.vct:SetWidth( bar:GetWidth() / 16); |
bar.vct:Show(); |
end |
local txt = bar.auraName; |
if ( barSettings.bDetectExtends == true ) then |
txt = txt .. " + 3s"; |
end |
bar.text:SetText(txt); |
if ( barSettings.Enabled ) then |
bar:SetAlpha(1); |
else |
bar:SetAlpha(0.4); |
if ( barSettings.Enabled ) then |
bar:SetAlpha(1); |
else |
bar:SetAlpha(0.4); |
end |
end |
if ( NeedToKnow_Settings["Locked"] and barSettings.Enabled ) then |
NeedToKnow.SetScripts(bar); |
NeedToKnow.Bar_AuraCheck(bar); -- a check for when the addon gets locked |
else |
NeedToKnow.ClearScripts(bar); |
end |
end |
function NeedToKnow.SetScripts(bar) |
bar:SetScript("OnEvent", NeedToKnow.Bar_OnEvent); |
bar:SetScript("OnUpdate", NeedToKnow.Bar_OnUpdate); |
bar:RegisterEvent("PLAYER_TARGET_CHANGED"); |
bar:RegisterEvent("PLAYER_FOCUS_CHANGED"); |
if bar.unit == "targettarget" then |
if ( "TOTEM" == bar.settings.BuffOrDebuff ) then |
bar:RegisterEvent("PLAYER_TOTEM_UPDATE"); |
elseif ( "mhand" == bar.settings.Unit or "ohand" == bar.settings.Unit ) then |
bar:RegisterEvent("UNIT_INVENTORY_CHANGED"); |
elseif ( bar.unit == "targettarget" ) then |
-- WORKAROUND: PLAYER_TARGET_CHANGED happens immediately, UNIT_TARGET every couple seconds |
bar:RegisterEvent("PLAYER_TARGET_CHANGED"); |
bar:RegisterEvent("UNIT_TARGET"); |
-- WORKAROUND: Don't get UNIT_AURA for targettarget |
bar:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED"); |
else |
bar:RegisterEvent("UNIT_AURA"); |
if ( bar.unit == "focus" ) then |
bar:RegisterEvent("PLAYER_FOCUS_CHANGED"); |
elseif ( bar.unit == "target" ) then |
bar:RegisterEvent("PLAYER_TARGET_CHANGED"); |
end |
end |
if "TOTEM" == bar.buffOrDebuff then |
bar:RegisterEvent("PLAYER_TOTEM_UPDATE"); |
if bar.settings.bDetectExtends then |
bar:RegisterEvent("UNIT_SPELLCAST_SENT"); |
end |
bar:RegisterEvent("UNIT_AURA"); |
bar:RegisterEvent("UNIT_TARGET"); |
end |
function NeedToKnow.ClearScripts(bar) |
bar:UnregisterEvent("COMBAT_LOG_EVENT_UNFILTERED"); |
bar:UnregisterEvent("PLAYER_TOTEM_UPDATE"); |
bar:UnregisterEvent("UNIT_AURA"); |
bar:UnregisterEvent("UNIT_INVENTORY_CHANGED"); |
bar:UnregisterEvent("UNIT_TARGET"); |
bar:UnregisterEvent("UNIT_SPELLCAST_SENT"); |
end |
function NeedToKnow.Bar_OnEvent(self, event, ...) |
if ( NEEDTOKNOW.AURAEVENTS[combatEvent] ) then |
local guidTarget = select(6, ...) |
if ( guidTarget == UnitGUID(self.unit) ) then |
local idSpell = select(9, ...) |
local nameSpell = select(10, ...) |
local idSpell, nameSpell = select(9, ...) |
if (self.auraName:find(idSpell) or |
self.auraName:find(nameSpell)) |
then |
end |
elseif ( combatEvent == "UNIT_DIED" ) and ( select(6, ...) == UnitGUID(self.unit) ) then |
NeedToKnow.Bar_AuraCheck(self); |
-- elseif ((combatEvent == "ENCHANT_APPLIED") or ( combatEvent == "ENCHANT_REMOVED")) and ( self.BuffOrDebuff == "WEAPON" ) and ( string.find( self.auraName, select (9, ...)) ) and ( UnitGUID(self.unit) == select (6, ...) ) then |
end |
elseif ( event == "PLAYER_TOTEM_UPDATE" ) then |
NeedToKnow.Bar_AuraCheck(self); |
elseif ( event == "UNIT_AURA" ) and ( select(1, ...) == self.unit ) then |
NeedToKnow.Bar_AuraCheck(self); |
elseif ( event == "UNIT_INVENTORY_CHANGED" and select(1, ...) == self.unit ) then |
NeedToKnow.Bar_AuraCheck(self); |
elseif ( event == "PLAYER_TARGET_CHANGED" ) or ( event == "PLAYER_FOCUS_CHANGED" ) then |
NeedToKnow.Bar_AuraCheck(self); |
elseif ( event == "UNIT_TARGET" and select(1, ...) == "target" ) then |
elseif ( event == "UNIT_TARGET" and select(1, ...) == "target" ) then |
NeedToKnow.Bar_AuraCheck(self); |
elseif ( event == "UNIT_SPELLCAST_SENT" ) then |
local unit, spell = select(1, ...); |
if unit == "player" and self.buffName == spell then |
local r = NeedToKnow.last_cast[spell]; |
if ( r and r.state == 0 ) then |
r.state = 1; |
NeedToKnow.nSent = NeedToKnow.nSent + 1; |
NeedToKnow_ExecutiveFrame:RegisterEvent("UNIT_SPELLCAST_SUCCEEDED"); |
end |
end |
end |
end |
-- AuraCheck calls on this to compute the "text" of the bar |
-- It is separated out like this in part to be hooked by other addons |
function NeedToKnow.ComputeBarText(buffName, count, extended) |
local text; |
if ( count > 1 ) then |
text = buffName.." ["..count.."]"; |
else |
text = buffName; |
end |
if ( extended and extended > 1 ) then |
text = text .. string.format(" + %.0fs", extended); |
end |
return text; |
end |
-- Called by NeedToKnow.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) |
local vct_duration = 0; |
local spellToTime = bar.settings.vct_spell |
if ( nil == spellToTime or "" == spellToTime ) then |
spellToTime = bar.buffName; |
end |
local _, _, _, _, _, _, castTime = GetSpellInfo(spellToTime); |
if ( castTime ) then |
vct_duration = castTime / 1000; |
bar.vct_refresh = true; |
else |
bar.vct_refresh = false; |
end |
if ( bar.settings.vct_extra ) then |
vct_duration = vct_duration + bar.settings.vct_extra; |
end |
return vct_duration; |
end |
function NeedToKnow.UpdateVCT(bar) |
local vct_duration = NeedToKnow.ComputeVCTDuration(bar); |
if ( bar.duration ) then |
vct_width = (vct_duration * bar:GetWidth()) / bar.duration; |
if (vct_width > bar:GetWidth()) then |
vct_width = bar:GetWidth(); |
end |
else |
vct_width = 0; |
end |
if ( vct_width > 1 ) then |
bar.vct:SetWidth(vct_width); |
bar.vct:Show(); |
else |
bar.vct:Hide(); |
end |
end |
function NeedToKnow.ConfigureVisibleBar(bar, count, extended) |
local text = NeedToKnow.ComputeBarText(bar.buffName, count, extended); |
bar.text:SetText(text); |
-- Is this an aura with a finite duration? |
local vct_width = 0; |
if ( bar.duration > 0 ) then |
bar:SetMinMaxValues(0, bar.duration); |
local timeLeft = bar.expirationTime - GetTime(); |
bar:SetValue(timeLeft); |
-- Hide the spark if it's "off the bar" |
if ( timeLeft > bar.duration ) then |
bar.spark:Hide(); |
else |
bar.spark:SetPoint("CENTER", bar, "LEFT", bar:GetWidth()*timeLeft/bar.duration, 0); |
bar.spark:Show(); |
end |
-- Determine the size of the visual cast bar |
if ( bar.settings.vct_enabled ) then |
NeedToKnow.UpdateVCT(bar); |
end |
bar.time:Show(); |
else |
-- Hide the time text and spark for auras with "infinite" duration |
bar:SetMinMaxValues(0, 1); |
bar:SetValue(1); |
bar.time:Hide(); |
bar.spark:Hide(); |
if ( bar.vct ) then |
bar.vct:Hide(); |
end |
end |
end |
function NeedToKnow.GetUtilityTooltips() |
if ( not NeedToKnow_Tooltip1 ) then |
for idxTip = 1,2 do |
local ttname = "NeedToKnow_Tooltip"..idxTip; |
local tt = CreateFrame("GameTooltip", ttname) |
tt:SetOwner(UIParent, "ANCHOR_NONE") |
tt.left = {} |
tt.right = tt:CreateFontString() |
tt.right:SetFontObject(GameFontNormal) |
for i = 1, 30 do |
tt.left[i] = tt:CreateFontString() |
tt.left[i]:SetFontObject(GameFontNormal) |
tt:AddFontStrings(tt.left[i], tt.right) |
end |
end |
end |
local tt1,tt2 = NeedToKnow_Tooltip1, NeedToKnow_Tooltip2; |
tt1:ClearLines(); |
tt2:ClearLines(); |
return tt1,tt2; |
end |
function NeedToKnow.DetermineTempEnchantFromTooltip(i_invID) |
local tt1,tt2 = NeedToKnow.GetUtilityTooltips(); |
tt1:SetInventoryItem("player", i_invID); |
local n,h = tt1:GetItem(); |
tt2:SetHyperlink(h); |
-- Look for green lines present in tt1 that are missing from tt2 |
local nLines1, nLines2 = tt1:NumLines(), tt2:NumLines(); |
local i1, i2 = 1,1; |
while ( i1 <= nLines1 ) do |
local txt1 = tt1.left[i1]; |
if ( txt1:GetTextColor() ~= 0 ) then |
i1 = i1 + 1; |
elseif ( i2 <= nLines2 ) then |
local txt2 = tt2.left[i2]; |
if ( txt2:GetTextColor() ~= 0 ) then |
i2 = i2 + 1; |
elseif (txt1:GetText() == txt2:GetText()) then |
i1 = i1 + 1; |
i2 = i2 + 1; |
else |
break; |
end |
else |
break; |
end |
end |
if ( i1 <= nLines1 ) then |
local line = tt1.left[i1]:GetText(); |
local iter = line:gmatch("[^%s]+"); |
local name, rank = iter(), iter(); |
return name, rank; |
end |
end |
-- Called whenever the state of auras on the bar's unit may have changed |
function NeedToKnow.Bar_AuraCheck(bar) |
local buffName, count, duration, expirationTime, caster, buffIconPath |
local buffName, count, duration, expirationTime, caster |
local settings = bar.settings; |
if ( UnitExists(bar.unit) ) then |
-- Determine if the bar should be showing anything |
if ( UnitExists(bar.unit) ) then |
local isWeapon; |
if ( "mhand" == settings.Unit or |
"ohand" == settings.Unit ) |
then |
isWeapon = true; |
end |
local mainHandEnchantName, mainHandEnchantRank, offHandEnchantName, offHandEnchantRank; |
local hasMainHandEnchant, mainHandExpiration, mainHandCharges, hasOffHandEnchant, offHandExpiration, offHandCharges; |
if ( isWeapon ) then |
hasMainHandEnchant, mainHandExpiration, mainHandCharges, hasOffHandEnchant, offHandExpiration, offHandCharges = GetWeaponEnchantInfo(); |
if ( hasMainHandEnchant and "mhand" == settings.Unit ) then |
mainHandEnchantName, mainHandEnchantRank = NeedToKnow.DetermineTempEnchantFromTooltip(16); |
end |
if ( hasOffHandEnchant and "ohand" == settings.Unit ) then |
offHandEnchantName, offHandEnchantRank = NeedToKnow.DetermineTempEnchantFromTooltip(17); |
end |
end |
local startIndex=1; |
local numBuffs; |
_ , numBuffs = string.gsub(bar.auraName, "," , ",") |
for idxBuff=0, numBuffs do |
local endIndex = bar.auraName:find(",",startIndex,true) or (string.len(bar.auraName)+1) |
local barSpell = strtrim(string.sub(bar.auraName, startIndex, endIndex-1)) |
local filter = bar.buffOrDebuff |
local filter = settings.BuffOrDebuff |
local _, nDigits = barSpell:find("^[0-9]+") |
local spellName, spellRank, spellIconPath; |
local isSpellID = nDigits == barSpell:len(); |
if isSpellID then |
spellName, spellRank , spellIconPath = GetSpellInfo(barSpell); |
end |
local isSpellID = ( nDigits == barSpell:len() ); |
if ( bar.buffOrDebuff == "TOTEM" ) then |
if ( settings.BuffOrDebuff == "TOTEM" ) then |
local spellName, spellRank, spellIconPath; |
if ( isSpellID ) then |
spellName, spellRank, spellIconPath = GetSpellInfo(barSpell); |
end |
for iSlot=1, 4 do |
local haveTotem, totemName, startTime, totemDuration = GetTotemInfo(iSlot); |
local sComp = barSpell |
local sComp = barSpell; |
if isSpellID then sComp = spellName end |
if totemName and |
totemName:find(sComp) |
then |
buffName = totemName |
duration = totemDuration |
expirationTime = startTime + duration |
count = 1 |
caster = "player"; |
break; |
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 |
-- 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(); |
if ( precise - startTime > 1 ) then |
precise = startTime + 1; |
end |
NeedToKnow.totem_drops[iSlot] = precise; |
end |
buffName = totemName; |
duration = totemDuration; |
expirationTime = NeedToKnow.totem_drops[iSlot] + duration; |
count = 1; |
caster = "player"; |
break; |
end |
end |
elseif isWeapon then |
-- todo: use substring instead? |
if ( "mhand" == settings.Unit and mainHandEnchantName == barSpell ) then |
buffName = mainHandEnchantName |
count = mainHandCharges; |
if ( count < 1 ) then count = 1 end |
duration = 1800 |
expirationTime = GetTime() + mainHandExpiration/1000 |
caster = "player" |
elseif ( "ohand" == settings.Unit and offHandEnchantName == barSpell ) then |
buffName = mainHandEnchantName |
count = offHandCharges; |
if ( count < 1 ) then count = 1 end |
duration = 1800 |
expirationTime = GetTime() + offHandExpiration/1000 |
caster = "player" |
end |
else |
if bar.onlyMine then |
filter = filter .. "|PLAYER" |
if settings.OnlyMine then |
filter = filter .. "|PLAYER"; |
end |
if isSpellID then |
local j = 1 |
-- WORKAROUND: The second parameter to UnitAura can't be a spellid, so I have |
-- to walk them all |
local barID = tonumber(barSpell); |
local j = 1; |
while true do |
local buffRank |
buffName, buffRank, buffIconPath, count, _, duration, expirationTime, caster = UnitAura(bar.unit, j, filter); |
local buffRank, spellID, buffIconPath; |
buffName, buffRank, buffIconPath, count, _, duration, expirationTime, caster, _, _, spellID = UnitAura(bar.unit, j, filter); |
if (not buffName) then |
break; |
end |
if (spellName == buffName and |
spellIconPath == buffIconPath and |
spellRank == buffRank ) |
then |
break |
if (spellID == barID) then |
break; |
else |
buffName, count, duration, expirationTime, caster, buffIconPath = nil; |
buffName, count, duration, expirationTime, caster = nil; |
end |
j=j+1; |
end |
startIndex=endIndex+1; |
end |
end |
if ( not duration ) then |
bar:Hide(); |
return; |
else --if ( ( caster == "player" ) or ( caster == "pet" ) or ( caster == "vehicle" ) or not bar.onlyMine ) then |
bar.duration = bar.fixedDuration or duration; |
local timeLeft = expirationTime - GetTime(); |
bar.expirationTime = expirationTime; |
bar:SetMinMaxValues(0, bar.duration); |
bar:SetValue(timeLeft); |
bar.spark:SetPoint("CENTER", bar, "LEFT", bar:GetWidth()*min(1, timeLeft/bar.duration), 0); |
if ( count > 1 ) then |
bar.text:SetText(buffName.." ["..count.."]"); |
end |
-- There is an aura this bar is watching! Set it up |
duration = tonumber(duration); |
if ( duration ) then |
-- Handle duration increases |
local extended; |
if (settings.bDetectExtends) then |
local curStart = expirationTime - duration; |
local guidTarget = UnitGUID(bar.unit); |
if ( not NeedToKnow.last_cast[buffName] ) then |
NeedToKnow.last_cast[buffName] = { state=0 }; |
end |
local r = NeedToKnow.last_cast[buffName]; |
if ( not r[guidTarget] ) then |
r[guidTarget] = { time=curStart, dur=duration }; |
elseif ( r[guidTarget].dur == 0 ) then |
r[guidTarget].dur = duration |
else |
bar.text:SetText(buffName); |
local rStart = r[guidTarget]; |
extended = expirationTime - rStart.time - rStart.dur; |
if ( extended > 1 ) then |
duration = rStart.dur; |
end |
end |
bar:Show(); |
return; |
end |
bar:Hide(); -- move along. these aren't the buffs you're looking for. |
else |
bar.duration = tonumber(bar.fixedDuration) or duration; |
bar.expirationTime = expirationTime; |
bar.buffName = buffName; |
NeedToKnow.ConfigureVisibleBar(bar, count, extended); |
bar:Show(); |
else |
bar:Hide(); |
end |
end |
--[[function NeedToKnow.Bar_WeaponCheck(bar) |
local buffName, count, duration, expirationTime, caster, numBuffs |
local startIndex=1; |
_ , numBuffs = string.gsub(bar.auraName, "," , ",") |
for i=0, numBuffs do |
local endIndex = bar.auraName:find(",",startIndex,true) or (string.len(bar.auraName)+1); |
function NeedToKnow.Fmt_SingleUnit(i_fSeconds) |
return string.format(SecondsToTimeAbbrev(i_fSeconds)); |
end |
buffName, _, _, count, _, duration, expirationTime, caster = UnitAura(bar.unit, strtrim(string.sub(bar.auraName, startIndex, endIndex-1)), nil, bar.buffOrDebuff); |
if ( duration ) then |
break; |
else |
startIndex=endIndex+1; |
end |
end |
function NeedToKnow.Fmt_TwoUnits(i_fSeconds) |
if ( i_fSeconds < 6040 ) then |
local nMinutes, nSeconds; |
nMinutes = floor(i_fSeconds / 60); |
nSeconds = floor(i_fSeconds - nMinutes*60); |
return string.format("%02d:%02d", nMinutes, nSeconds); |
else |
string.format(SecondsToTimeAbbrev(i_fSeconds)); |
end |
end |
]]-- |
function NeedToKnow.Fmt_Float(i_fSeconds) |
return string.format("%0.1f", i_fSeconds); |
end |
function NeedToKnow.Bar_OnUpdate(self, elapsed) |
local now = GetTime(); |
if ( now > self.nextUpdate ) then |
self.nextUpdate = now + NEEDTOKNOW.UPDATE_INTERVAL; |
local timeLeft = self.expirationTime - GetTime(); |
timeLeft = max(0, timeLeft); |
self:SetValue(timeLeft); |
self.time:SetFormattedText(SecondsToTimeAbbrev(timeLeft)); |
self.spark:SetPoint("CENTER", self, "LEFT", self:GetWidth()*min(1, timeLeft/self.duration), 0); |
-- WORKAROUND: Although the existence of the enchant is correct at UNIT_INVENTORY_CHANGED |
-- the expiry time is not yet correct. So we update the expiration every update :( |
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() |
self.expirationTime = GetTime() + mhExpire/1000; |
elseif ( origUnit == "ohand" ) then |
local _, _, _, ohEnchant, ohExpire = GetWeaponEnchantInfo() |
self.expirationTime = GetTime() + ohExpire/1000; |
end |
if ( self.duration > 0 ) then |
local timeLeft = self.expirationTime - GetTime(); |
timeLeft = max(0, timeLeft); |
self:SetValue(timeLeft); |
local fn = NeedToKnow[self.settings.TimeFormat]; |
local oldText = self.time:GetText(); |
local newText; |
if ( fn ) then |
newText = fn(timeLeft); |
else |
newText = string.format(SecondsToTimeAbbrev(timeLeft)); |
end |
if ( newText ~= oldText ) then |
self.time:SetText(newText); |
end |
if ( timeLeft <= self.duration ) then |
self.spark:SetPoint("CENTER", self, "LEFT", self:GetWidth()*timeLeft/self.duration, 0); |
self.spark:Show(); |
end |
if ( self.vct_refresh ) then |
NeedToKnow.UpdateVCT(self); |
end |
end |
end |
end |
## Interface: 30200 |
## Title: NeedToKnow |
## Author: Bilt of Lightninghoof US |
## Version: 2.6.0 |
## Interface: 30300 |
## Title: NeedToKnow - Updated! |
## Author: Kitjan |
## Version: 2.8.2 |
## Notes: Buff/debuff timer bars, original code by Nephthys of Hyjal-US Added support for multiple buffs per bar. |
## OptionalDeps: LibStub,LibSharedMedia-3.0 |
## DefaultState: Enabled |
NEEDTOKNOW.BARMENU_TOTEM = "Totem"; |
NEEDTOKNOW.CMD_HIDE = "hide"; |
NEEDTOKNOW.CMD_SHOW = "show"; |
NEEDTOKNOW.BARMENU_TIMEFORMAT = "Time Format"; |
NEEDTOKNOW.FMT_SINGLEUNIT = "Single unit (2 m)"; |
NEEDTOKNOW.FMT_TWOUNITS = "Minutes and seconds (01:10)"; |
NEEDTOKNOW.FMT_FLOAT = "Fractional Seconds (70.1)"; |
NEEDTOKNOW.BARMENU_VISUALCASTTIME = "Visual Cast Time"; |
NEEDTOKNOW.BARMENU_VCT_ENABLE = "Enable for this bar"; |
NEEDTOKNOW.BARMENU_VCT_COLOR = "Overlay color"; |
NEEDTOKNOW.BARMENU_VCT_SPELL = "Choose cast time by spell"; |
NEEDTOKNOW.BARMENU_VCT_EXTRA = "Set additional time"; |
NEEDTOKNOW.BARMENU_MAIN_HAND = "Main Hand"; |
NEEDTOKNOW.BARMENU_OFF_HAND = "Off Hand"; |
NEEDTOKNOW.CHOOSE_VCT_SPELL_DIALOG = "Enter the name of a spell (in your spellbook) whose cast time will determine the base length of the visual cast time. If left blank, the aura name will be used as the spell name. To force this to be 0, type 0."; |
NEEDTOKNOW.CHOOSE_VCT_EXTRA_DIALOG = "Enter an amount of seconds that will be added to the cast time of the spell. Ex: 1.5"; |
if ( GetLocale() == "deDE" ) then |
-- Thanks, sp00n & Fxfighter EU-Echsenkessel! |
NEEDTOKNOW.BAR_TOOLTIP1 = "NeedToKnow"; |
info.swatchFunc = NeedToKnow.SetColor; |
info.cancelFunc = NeedToKnow.CancelColor; |
info.extraInfo = variable; |
-- Not sure if I should leave this state around or not. It seems like the |
-- correct strata to have it at anyway, so I'm going to leave it there for now |
ColorPickerFrame:SetFrameStrata("FULLSCREEN_DIALOG"); |
OpenColorPicker(info); |
end |
end |
NeedToKnow.Show(true); |
PlaySound("UChatScrollButton"); |
NeedToKnow.last_cast = {}; |
NeedToKnow.Update(); |
end |
button2 = CANCEL, |
hasEditBox = 1, |
maxLetters = 255, |
OnShow = function() |
local edit = getglobal(this:GetName().."EditBox"); |
local groupID = NeedToKnow.CurrentBar["groupID"]; |
local barID = NeedToKnow.CurrentBar["barID"]; |
local curval = NeedToKnow_Settings["Spec"][NEEDTOKNOW.CURRENTSPEC]["Groups"][groupID]["Bars"][barID]["AuraName"]; |
edit:SetFocus(); |
edit:SetText(curval); |
end, |
OnAccept = function(iconNumber) |
local text = getglobal(this:GetParent():GetName().."EditBox"):GetText(); |
NeedToKnow.BarMenu_ChooseName(text); |
local variable = this:GetParent().variable; |
if ( nil ~= variable ) then |
NeedToKnow.BarMenu_ChooseName(text, variable); |
end |
end, |
EditBoxOnEnterPressed = function(iconNumber) |
local text = getglobal(this:GetParent():GetName().."EditBox"):GetText(); |
NeedToKnow.BarMenu_ChooseName(text); |
local variable = this:GetParent().variable; |
if ( nil ~= variable ) then |
NeedToKnow.BarMenu_ChooseName(text, variable); |
end |
this:GetParent():Hide(); |
end, |
EditBoxOnEscapePressed = function() |
}; |
NeedToKnow.BarMenu_MoreOptions = { |
{ VariableName = "BuffOrDebuff", MenuText = NEEDTOKNOW.BARMENU_BUFFORDEBUFF, HasSubmenu = true }, |
{ VariableName = "Unit", MenuText = NEEDTOKNOW.BARMENU_CHOOSEUNIT, HasSubmenu = true }, |
}; |
{ VariableName = "Enabled", MenuText = NEEDTOKNOW.BARMENU_ENABLE }, |
{ VariableName = "AuraName", MenuText = NEEDTOKNOW.BARMENU_CHOOSENAME, Type = "Dialog", DialogText = "CHOOSENAME_DIALOG" }, |
{ VariableName = "BuffOrDebuff", MenuText = NEEDTOKNOW.BARMENU_BUFFORDEBUFF, Type = "Submenu" }, |
{ VariableName = "Unit", MenuText = NEEDTOKNOW.BARMENU_CHOOSEUNIT, Type = "Submenu" }, |
{ VariableName = "TimeFormat", MenuText = NEEDTOKNOW.BARMENU_TIMEFORMAT, Type = "Submenu" }, |
{ VariableName = "VisualCastTime", MenuText = NEEDTOKNOW.BARMENU_VISUALCASTTIME, Type = "Submenu" }, |
{ VariableName = "bDetectExtends", MenuText = "Track duration increases" }, -- FIXME: Localization |
{ VariableName = "OnlyMine", MenuText = NEEDTOKNOW.BARMENU_ONLYMINE }, |
{ VariableName = "BarColor", MenuText = NEEDTOKNOW.BARMENU_BARCOLOR, Type = "Color" }, |
} |
NeedToKnow.BarMenu_SubMenus = { |
-- the keys on this table need to match the settings variable names |
{ Setting = "HARMFUL", MenuText = NEEDTOKNOW.BARMENU_DEBUFF }, |
{ Setting = "TOTEM", MenuText = NEEDTOKNOW.BARMENU_TOTEM }, |
}, |
TimeFormat = { |
{ Setting = "Fmt_SingleUnit", MenuText = NEEDTOKNOW.FMT_SINGLEUNIT }, |
{ Setting = "Fmt_TwoUnits", MenuText = NEEDTOKNOW.FMT_TWOUNITS }, |
{ Setting = "Fmt_Float", MenuText = NEEDTOKNOW.FMT_FLOAT }, |
}, |
Unit = { |
{ Setting = "player", MenuText = NEEDTOKNOW.BARMENU_PLAYER }, |
{ Setting = "target", MenuText = NEEDTOKNOW.BARMENU_TARGET }, |
{ Setting = "focus", MenuText = NEEDTOKNOW.BARMENU_FOCUS }, |
{ Setting = "pet", MenuText = NEEDTOKNOW.BARMENU_PET }, |
{ Setting = "vehicle", MenuText = NEEDTOKNOW.BARMENU_VEHICLE }, |
{ Setting = "mhand", MenuText = NEEDTOKNOW.BARMENU_MAIN_HAND }, |
{ Setting = "ohand", MenuText = NEEDTOKNOW.BARMENU_OFF_HAND }, |
}, |
VisualCastTime = { |
{ VariableName = "vct_enabled", MenuText = NEEDTOKNOW.BARMENU_VCT_ENABLE }, |
{ VariableName = "vct_color", MenuText = NEEDTOKNOW.BARMENU_VCT_COLOR, Type = "Color" }, |
{ VariableName = "vct_spell", MenuText = NEEDTOKNOW.BARMENU_VCT_SPELL, Type = "Dialog", DialogText = "CHOOSE_VCT_SPELL_DIALOG" }, |
{ VariableName = "vct_extra", MenuText = NEEDTOKNOW.BARMENU_VCT_EXTRA, Type = "Dialog", DialogText = "CHOOSE_VCT_SPELL_DIALOG", Numeric=true }, |
}, |
}; |
function NeedToKnow.Bar_OnEnter(self) |
end |
end |
function NeedToKnow.BarMenu_AddButton(barSettings, i_desc, i_parent) |
info = UIDropDownMenu_CreateInfo(); |
local type = i_desc["Type"]; |
info.text = i_desc["MenuText"]; |
info.value = i_desc["VariableName"]; |
if ( nil == info.value and nil ~= i_desc["Setting"]) then |
info.value = i_parent; |
type = "SetVar"; |
end; |
local varSettings = barSettings[info.value]; |
if ( not varSettings and (type == "Check" or type == "Color") ) then |
print (string.format("NTK: Could not find %s in", info.value), barSettings); |
return |
end |
info.hasArrow = false; |
info.checked = false; |
--info.notCheckable = true; -- Doesn't prevent checking, just formats the line differently |
info.keepShownOnClick = true; |
if ( nil == type or type == "Check" ) then |
info.func = NeedToKnow.BarMenu_ToggleSetting; |
info.checked = (nil ~= varSettings and varSettings); |
info.notCheckable = false; |
elseif ( type == "SetVar" ) then |
info.func = NeedToKnow.BarMenu_ChooseSetting; |
info.value = i_desc["Setting"]; |
info.checked = (varSettings == info.value); |
info.notCheckable = false; |
info.keepShownOnClick = false; |
elseif ( type == "Submenu" ) then |
info.hasArrow = true; |
elseif ( type == "Dialog" ) then |
info.func = NeedToKnow.BarMenu_ShowNameDialog; |
info.keepShownOnClick = false; |
info.value = {variable = i_desc.VariableName, text = i_desc.DialogText, numeric = i_desc.Numeric }; |
elseif ( type == "Color" ) then |
info.hasColorSwatch = 1; |
info.hasOpacity = true; |
info.r = varSettings.r; |
info.g = varSettings.g; |
info.b = varSettings.b; |
info.opacity = 1 - varSettings.a; |
info.swatchFunc = NeedToKnow.BarMenu_SetColor; |
info.opacityFunc = NeedToKnow.BarMenu_SetOpacity; |
info.cancelFunc = NeedToKnow.BarMenu_CancelColor; |
info.func = UIDropDownMenuButton_OpenColorPicker; |
info.keepShownOnClick = false; |
end |
UIDropDownMenu_AddButton(info, UIDROPDOWNMENU_MENU_LEVEL); |
if ( type == "Color" ) then |
-- Sadly, extraInfo isn't a field propogated to the button |
-- Code to get the button copied from UIDropDownMenu_AddButton |
local level = UIDROPDOWNMENU_MENU_LEVEL; |
local listFrame = _G["DropDownList"..level]; |
local index = listFrame and (listFrame.numButtons) or 1; |
local listFrameName = listFrame:GetName(); |
local button = getglobal(listFrameName.."Button"..index); |
button.extraInfo = info.value; |
end |
end |
function NeedToKnow.BarMenu_Initialize() |
local groupID = NeedToKnow.CurrentBar["groupID"]; |
local barID = NeedToKnow.CurrentBar["barID"]; |
local barSettings = NeedToKnow_Settings["Spec"][NEEDTOKNOW.CURRENTSPEC]["Groups"][groupID]["Bars"][barID]; |
if ( UIDROPDOWNMENU_MENU_LEVEL == 2 ) then |
if ( UIDROPDOWNMENU_MENU_VALUE == "VisualCastTime" ) then |
-- Create a summary title for the visual cast time submenu |
local title = ""; |
if ( barSettings.vct_spell and "" ~= barSettings.vct_spell ) then |
title = title .. barSettings.vct_spell; |
end |
local fExtra = tonumber(barSettings.vct_extra); |
if ( fExtra and fExtra > 0 ) then |
if ("" ~= title) then |
title = title .. " + "; |
end |
title = title .. string.format("%0.1fs", fExtra); |
end |
if ( "" ~= title ) then |
local info = UIDropDownMenu_CreateInfo(); |
info.text = title; |
info.isTitle = true; |
UIDropDownMenu_AddButton(info, UIDROPDOWNMENU_MENU_LEVEL); |
end |
end |
local subMenus = NeedToKnow.BarMenu_SubMenus; |
for index, value in ipairs(subMenus[UIDROPDOWNMENU_MENU_VALUE]) do |
-- here, UIDROPDOWNMENU_MENU_VALUE is the setting name |
local info = UIDropDownMenu_CreateInfo(); |
info.text = subMenus[UIDROPDOWNMENU_MENU_VALUE][index]["MenuText"]; |
info.value = subMenus[UIDROPDOWNMENU_MENU_VALUE][index]["Setting"]; |
info.checked = ( info.value == NeedToKnow_Settings["Spec"][NEEDTOKNOW.CURRENTSPEC]["Groups"][groupID]["Bars"][barID][UIDROPDOWNMENU_MENU_VALUE] ); |
info.func = NeedToKnow.BarMenu_ChooseSetting; |
UIDropDownMenu_AddButton(info, UIDROPDOWNMENU_MENU_LEVEL); |
NeedToKnow.BarMenu_AddButton(barSettings, value, UIDROPDOWNMENU_MENU_VALUE); |
end |
return; |
end |
UIDropDownMenu_AddButton(info); |
end |
-- enable bar |
info = UIDropDownMenu_CreateInfo(); |
info.value = "Enabled"; |
info.text = NEEDTOKNOW.BARMENU_ENABLE; |
info.checked = barSettings.Enabled; |
info.func = NeedToKnow.BarMenu_ToggleSetting; |
info.keepShownOnClick = true; |
UIDropDownMenu_AddButton(info); |
-- choose aura name |
info = UIDropDownMenu_CreateInfo(); |
info.value = "NEEDTOKNOW.BARMENU_CHOOSENAME"; |
info.text = NEEDTOKNOW.BARMENU_CHOOSENAME; |
info.func = NeedToKnow.BarMenu_ShowNameDialog; |
UIDropDownMenu_AddButton(info); |
local moreOptions = NeedToKnow.BarMenu_MoreOptions; |
for index, value in ipairs(moreOptions) do |
info = UIDropDownMenu_CreateInfo(); |
info.text = moreOptions[index]["MenuText"]; |
info.value = moreOptions[index]["VariableName"]; |
info.hasArrow = moreOptions[index]["HasSubmenu"]; |
if not info.hasArrow then |
info.func = NeedToKnow.BarMenu_ToggleSetting; |
info.checked = NeedToKnow_Settings["Spec"][NEEDTOKNOW.CURRENTSPEC]["Groups"][groupID]["bars"][barID][info.value]; |
end |
info.keepShownOnClick = true; |
UIDropDownMenu_AddButton(info); |
NeedToKnow.BarMenu_AddButton(barSettings, moreOptions[index]); |
end |
-- only show if cast by self? |
info = UIDropDownMenu_CreateInfo(); |
info.value = "OnlyMine"; |
info.text = NEEDTOKNOW.BARMENU_ONLYMINE; |
info.checked = barSettings.OnlyMine; |
info.func = NeedToKnow.BarMenu_ToggleSetting; |
info.keepShownOnClick = true; |
UIDropDownMenu_AddButton(info); |
-- choose bar color |
info = UIDropDownMenu_CreateInfo(); |
info.text = NEEDTOKNOW.BARMENU_BARCOLOR; |
info.hasColorSwatch = 1; |
info.hasOpacity = true; |
info.r = barSettings.BarColor.r; |
info.g = barSettings.BarColor.g; |
info.b = barSettings.BarColor.b; |
info.opacity = 1 - barSettings.BarColor.a; |
info.func = UIDropDownMenuButton_OpenColorPicker; |
info.swatchFunc = NeedToKnow.BarMenu_SetColor; |
info.opacityFunc = NeedToKnow.BarMenu_SetOpacity; |
info.cancelFunc = NeedToKnow.BarMenu_CancelColor; |
UIDropDownMenu_AddButton(info); |
info = UIDropDownMenu_CreateInfo(); |
info.disabled = true; |
UIDropDownMenu_AddButton(info); |
info.func = NeedToKnow.BarMenu_ClearSettings; |
UIDropDownMenu_AddButton(info); |
if ( false == barSettings.OnlyMine ) then |
NeedToKnow.BarMenu_UncheckAndDisable(groupID, barID, "bDetectExtends"); |
end |
end |
function NeedToKnow.BarMenu_ToggleSetting() |
function NeedToKnow.BarMenu_ToggleSetting(self, a1, a2, checked) |
local groupID = NeedToKnow.CurrentBar["groupID"]; |
local barID = NeedToKnow.CurrentBar["barID"]; |
NeedToKnow_Settings["Spec"][NEEDTOKNOW.CURRENTSPEC]["Groups"][groupID]["Bars"][barID][this.value] = this.checked; |
if ( this.value == "OnlyMine" ) then |
if ( false == this.checked ) then |
NeedToKnow.BarMenu_UncheckAndDisable(groupID, barID, "bDetectExtends"); |
else |
NeedToKnow.BarMenu_EnableItem(groupID, barID, "bDetectExtends"); |
end |
end |
NeedToKnow.Bar_Update(groupID, barID); |
end |
function NeedToKnow.BarMenu_ChooseSetting() |
function NeedToKnow.BarMenu_UncheckAndDisable(groupID, barID, i_valueName) |
NeedToKnow_Settings["Spec"][NEEDTOKNOW.CURRENTSPEC]["Groups"][groupID]["Bars"][barID][i_valueName] = false; |
local listFrame = _G["DropDownList"..1]; |
local listFrameName = listFrame:GetName(); |
local n = listFrame.numButtons; |
for index=1,n do |
local button = getglobal(listFrameName.."Button"..index); |
local txt = button.value; |
if ( txt == i_valueName ) then |
getglobal(listFrameName.."Button"..index.."Check"):Hide(); |
button.checked = false; |
button:Disable(); |
break; |
end; |
end |
end |
function NeedToKnow.BarMenu_EnableItem(groupID, barID, i_valueName) |
local listFrame = _G["DropDownList"..1]; |
local listFrameName = listFrame:GetName(); |
local n = listFrame.numButtons; |
for index=1,n do |
local button = getglobal(listFrameName.."Button"..index); |
local txt = button.value; |
if ( txt == i_valueName ) then |
button:Enable(); |
break; |
end; |
end |
end |
function NeedToKnow.BarMenu_ChooseSetting(self, a1, a2, checked) |
local groupID = NeedToKnow.CurrentBar["groupID"]; |
local barID = NeedToKnow.CurrentBar["barID"]; |
NeedToKnow_Settings["Spec"][NEEDTOKNOW.CURRENTSPEC]["Groups"][groupID]["Bars"][barID][UIDROPDOWNMENU_MENU_VALUE] = this.value; |
NeedToKnow.Bar_Update(groupID, barID); |
end |
function NeedToKnow.BarMenu_ShowNameDialog() |
-- TODO: There has to be a better way to do this, this has pretty bad user feel |
function NeedToKnow.EditBox_Numeric_OnTextChanged(self, isUserInput) |
if ( isUserInput ) then |
local txt = self:GetText(); |
local culled = txt:gsub("[^0-9.]",""); -- Remove non-digits |
local iPeriod = culled:find("[.]"); |
if ( nil ~= iPeriod ) then |
local before = culled:sub(1, iPeriod); |
local after = string.gsub( culled:sub(iPeriod+1), "[.]", "" ); |
culled = before .. after; |
end |
if ( txt ~= culled ) then |
self:SetText(culled); |
end |
end |
if ( NeedToKnow.EditBox_Original_OnTextChanged ) then |
NeedToKnow.EditBox_Original_OnTextChanged(self, isUserInput); |
end |
end |
function NeedToKnow.BarMenu_ShowNameDialog(self, a1, a2, checked) |
StaticPopupDialogs["NEEDTOKNOW.CHOOSENAME_DIALOG"].text = NEEDTOKNOW[this.value.text]; |
local dialog = StaticPopup_Show("NEEDTOKNOW.CHOOSENAME_DIALOG"); |
dialog.variable = this.value.variable; |
local edit = getglobal(dialog:GetName().."EditBox"); |
local groupID = NeedToKnow.CurrentBar["groupID"]; |
local barID = NeedToKnow.CurrentBar["barID"]; |
local curval = NeedToKnow_Settings["Spec"][NEEDTOKNOW.CURRENTSPEC]["Groups"][groupID]["Bars"][barID][dialog.variable]; |
local numeric = this.value.numeric or false; |
-- TODO: There has to be a better way to do this, this has pretty bad user feel |
if ( nil == NeedToKnow.EditBox_Original_OnTextChanged ) then |
NeedToKnow.EditBox_Original_OnTextChanged = edit:GetScript("OnTextChanged"); |
end |
if ( numeric ) then |
edit:SetScript("OnTextChanged", NeedToKnow.EditBox_Numeric_OnTextChanged); |
else |
edit:SetScript("OnTextChanged", NeedToKnow.EditBox_Original_OnTextChanged); |
end |
edit:SetFocus(); |
edit:SetText(curval); |
end |
function NeedToKnow.BarMenu_ChooseName(text) |
function NeedToKnow.BarMenu_ChooseName(text, variable) |
local groupID = NeedToKnow.CurrentBar["groupID"]; |
local barID = NeedToKnow.CurrentBar["barID"]; |
NeedToKnow_Settings["Spec"][NEEDTOKNOW.CURRENTSPEC]["Groups"][groupID]["Bars"][barID]["AuraName"] = text; |
NeedToKnow_Settings["Spec"][NEEDTOKNOW.CURRENTSPEC]["Groups"][groupID]["Bars"][barID][variable] = text; |
NeedToKnow.Bar_Update(groupID, barID); |
end |
function MemberDump(v, bIndex) |
print("members") |
for index, value in pairs(v) do |
print(" ", index, value); |
end |
local mt = getmetatable(v) |
if ( mt ) then |
print("metatable") |
for index, value in pairs(mt) do |
print(" ", index, value); |
end |
if ( mt.__index and bIndex) then |
print("__index") |
for index, value in pairs(mt.__index) do |
print(" ", index, value); |
end |
end |
end |
end |
function NeedToKnow.BarMenu_SetColor() |
local groupID = NeedToKnow.CurrentBar["groupID"]; |
local barID = NeedToKnow.CurrentBar["barID"]; |
local r,g,b = ColorPickerFrame:GetColorRGB(); |
NeedToKnow_Settings["Spec"][NEEDTOKNOW.CURRENTSPEC]["Groups"][groupID]["Bars"][barID]["BarColor"]["r"] = r; |
NeedToKnow_Settings["Spec"][NEEDTOKNOW.CURRENTSPEC]["Groups"][groupID]["Bars"][barID]["BarColor"]["g"] = g; |
NeedToKnow_Settings["Spec"][NEEDTOKNOW.CURRENTSPEC]["Groups"][groupID]["Bars"][barID]["BarColor"]["b"] = b; |
local varSettings = NeedToKnow_Settings["Spec"][NEEDTOKNOW.CURRENTSPEC]["Groups"][groupID]["Bars"][barID][ColorPickerFrame.extraInfo]; |
varSettings.r,varSettings.g,varSettings.b = ColorPickerFrame:GetColorRGB(); |
NeedToKnow.Bar_Update(groupID, barID); |
end |
function NeedToKnow.BarMenu_SetOpacity() |
local groupID = NeedToKnow.CurrentBar["groupID"]; |
local barID = NeedToKnow.CurrentBar["barID"]; |
NeedToKnow_Settings["Spec"][NEEDTOKNOW.CURRENTSPEC]["Groups"][groupID]["Bars"][barID]["BarColor"]["a"] = 1 - OpacitySliderFrame:GetValue(); |
local varSettings = NeedToKnow_Settings["Spec"][NEEDTOKNOW.CURRENTSPEC]["Groups"][groupID]["Bars"][barID][ColorPickerFrame.extraInfo]; |
varSettings.a = 1 - OpacitySliderFrame:GetValue(); |
NeedToKnow.Bar_Update(groupID, barID); |
end |
if ( previousValues.r ) then |
local groupID = NeedToKnow.CurrentBar["groupID"]; |
local barID = NeedToKnow.CurrentBar["barID"]; |
NeedToKnow_Settings["Spec"][NEEDTOKNOW.CURRENTSPEC]["Groups"][groupID]["Bars"][barID]["BarColor"]["r"] = previousValues.r; |
NeedToKnow_Settings["Spec"][NEEDTOKNOW.CURRENTSPEC]["Groups"][groupID]["Bars"][barID]["BarColor"]["g"] = previousValues.g; |
NeedToKnow_Settings["Spec"][NEEDTOKNOW.CURRENTSPEC]["Groups"][groupID]["Bars"][barID]["BarColor"]["b"] = previousValues.b; |
NeedToKnow_Settings["Spec"][NEEDTOKNOW.CURRENTSPEC]["Groups"][groupID]["Bars"][barID]["BarColor"]["a"] = 1 - previousValues.opacity; |
local varSettings = NeedToKnow_Settings["Spec"][NEEDTOKNOW.CURRENTSPEC]["Groups"][groupID]["Bars"][barID][ColorPickerFrame.extraInfo]; |
varSettings.r = previousValues.r; |
varSettings.g = previousValues.g; |
varSettings.b = previousValues.b; |
varSettings.a = 1 - previousValues.opacity; |
NeedToKnow.Bar_Update(groupID, barID); |
end |
end |
-------------------- |
NeedToKnow |
by Nephthys of Hyjal |
by Kitjan |
-------------------- |
NeedToKnow allows you to monitor specific buffs and debuffs of your choosing as timer bars that always appear in a consistent place on your screen in a consistent color. It's especially useful for monitoring frequently used short-duration buffs and debuffs. For example, a rogue could configure NeedToKnow to show timer bars for Slice and Dice, Rupture, and their own stack of Deadly Poison VII. A death knight could use it to track their own diseases on a mob. NeedToKnow also works with procs and on-use trinkets. The number, size, position, and appearance of timer bars are all customizable. |
--------------------------- |
New in latest version (2.2) |
--------------------------- |
- Added option to show bars with a fixed maximum duration |
- Fixed an issue with targetoftarget |
- Added koKR and deDE localization |
------------ |
Instructions |
------------ |
---------- |
To do list |
Change log |
---------- |
- Add option to track multiple buffs/debuffs with a single bar |
- Add options to customize text display |
2.8.0 |
- Added the ability to track increases in spell duration, especially useful for dps druids |
- Marked as being a 3.3 addon |
- Fixed: Took advantage of a new 3.3 API to get the spell id of active buffs and debuffs. Bars that check spellid should be much more reliable and, for example, be able to tell the difference between the two different Death's Verdict procs |
- Fixed: Totem timing is much more accurate |
- Fixed: Visual cast times now updates based on changes in haste and other casting-time-affecting abilities |
2.7.1 |
- Fixed: Accidentally removed the background color picker |
---------- |
Change log |
---------- |
2.7.0 |
- Added options for how the time text is formatted. The current style is the default, with mm:ss and ss.t as other options |
- Added "visual cast time" overlay which can be used to tell when there's less than some critical amount of time left on an aura |
- Hid the spark when the aura lasts longer than the bar (either an infinite duration, or using the Max duration feature.) |
- Hid the time text when the aura has an infinite duration |
2.6.0 |
- Added support for a new "Buff or Debuff" type: Totem. Type in the name of the totem to watch for (can be a partial string.) |
- Fixed a parse error in the DE localization |
- Slightly improved performance of "target of target" |
- Added two new /ntk options: show and hide. They can be used to temporarily show and hide the ntk groups. |
2.5.2 |
-Changed event parsing to try to be more robust (see autobot's errors) |
2.5.1 |
-Trying a different strategy for identifying "only cast by me" spells |
-When editing the watched auras, the edit field starts with the current value |
-Configuring by SpellID is automatically detected and does not need a menu item checked |
2.5 |
-Fixed ToT issue |
-Added support for SpellID |
2.4.3 |
-Added SharedMedia support, uses LibSharedMedia-3.0 |
-Greatly improved performance |
2.4.2 |
-Fixed a bug with the multiple buffs per line |
-Fixed a small bug with resize button showing |
-Optimized performance slightly |
2.4.1 |
-Fixed character restriction on buff names, no accepts up to 255 characters. |
-Added Russian localization |
2.4 |
-Brought up to 3.2 API standards |
-Added multiple buffs/debuffs per bar |
-Dual-Specialization support |
Version 2.2 |
- Added option to show bars with a fixed maximum duration |
- Fixed an issue with targetoftarget |