You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2392 lines
66 KiB

local GetItemInfo = GetItemInfo;
local GetSpellInfo = GetSpellInfo;
local GetSpellCooldown = GetSpellCooldown;
local GetSpellCharges = GetSpellCharges;
local GetTime = GetTime;
local GetItemCooldown = GetItemCooldown;
local GetRuneCooldown = GetRuneCooldown;
local GetRuneType = GetRuneType;
local GetTotemInfo = GetTotemInfo;
local GetWeaponEnchantInfo = GetWeaponEnchantInfo;
local GetInventoryItemTexture = GetInventoryItemTexture;
local GetGlyphSocketInfo = GetGlyphSocketInfo;
local GetSpecialization = GetSpecialization;
local GetSpecializationInfo = GetSpecializationInfo;
local GetTalentInfo = GetTalentInfo;
local UnitExists = UnitExists;
local UnitPower = UnitPower;
local UnitPowerMax = UnitPowerMax;
local UnitHealth = UnitHealth;
local UnitHealthMax = UnitHealthMax;
local UnitAlternatePowerInfo = UnitAlternatePowerInfo;
local UnitAlternatePowerTextureInfo = UnitAlternatePowerTextureInfo;
local UnitGetIncomingHeals = UnitGetIncomingHeals;
local select = select;
local pairs = pairs;
local ipairs = ipairs;
local wipe = wipe;
local tonumber = tonumber;
local string_find = string.find;
local string_match = string.match;
local string_gmatch = string.gmatch;
local string_trim = strtrim;
local string_gsub = string.gsub;
local string_len = strlenutf8;
local string_lower = string.lower;
local string_split = strsplit;
local strconcat = strconcat;
local table_insert = table.insert;
-- boolean operators for timer entries
local BOOLOP_NONE, BOOLOP_AND, BOOLOP_OR, BOOLOP_RELAXEDAND = 0, 1, 2, 3;
-- local variables
local _;
-- mainline or classic
local wowmainline = (WOW_PROJECT_ID == WOW_PROJECT_MAINLINE);
local wowclassic = (WOW_PROJECT_ID == WOW_PROJECT_CLASSIC);
local wowbcc = (WOW_PROJECT_ID == WOW_PROJECT_BURNING_CRUSADE_CLASSIC);
-- WOW classic support
local AuraUtil_FindAuraByName = AuraUtil.FindAuraByName;
local UnitAura = UnitAura;
local UnitCastingInfo = UnitCastingInfo;
local UnitChannelInfo = UnitChannelInfo;
if (wowclassic and Gnosis.libcldur) then
UnitAura = function(...)
return Gnosis.libcldur:UnitAura(...);
end
AuraUtil_FindAuraByName = function(spellname, unit, filter)
local i = 1;
repeat
local name, ic, sta, _, d, s, _, _, _, id, _, _, _, _, _, eff1, eff2, eff3 =
UnitAura(unit, i, filter);
if (spellname == name) then
return name, ic, sta, _, d, s, _, _, _, id, _, _, _, _, _, eff1, eff2, eff3;
end
i = i + 1;
until id == nil or i > 40;
return nil;
end
end
if (wowclassic and Gnosis.libclcno) then
UnitCastingInfo = function(unit)
return Gnosis.libclcno:UnitCastingInfo(unit);
end
UnitChannelInfo = function(unit)
return Gnosis.libclcno:UnitChannelInfo(unit);
end
end
if (wowbcc) then
UnitCastingInfo = function(unit)
local name, text, texture, startTimeMS, endTimeMS, isTradeSkill, castID, spellId = UnitCastingInfo(unit)
return name, text, texture, startTimeMS, endTimeMS, isTradeSkill, castID, nil, spellId
end
end
if (wowclassic) or (wowbcc) then
GetSpecializationInfo = function()
return nil, "";
end
GetTalentInfo = function()
return 1, "";
end
end
-- string helper functions
function Gnosis:ParseTimer_TrimCmd(line)
return string_gsub(line, "^[%s%.,]*(.-)%s*$", "%1");
end
local function ParseTimer_IsComment(line)
local comment = string_find(line, "^[%s%.,]*(%-%-)");
if (comment) then
return true;
else
return false;
end
end
function Gnosis:ParseTimer_GetCommand(bnwlist, linetostart)
local curcmd, boolop;
-- is valid table?
if (type(bnwlist) ~= "table") then
return;
end
-- remove comments
while (#bnwlist >= linetostart and ParseTimer_IsComment(bnwlist[linetostart])) do
linetostart = linetostart + 1;
end
if (linetostart > #bnwlist) then
return;
end
-- grab first non comment (and remove tokens)
-- remove tokens '\\'. '&', '+'. '*'
-- '\\': append line; '&': previous and current line have to be valid, same as 'and'
-- '?': either previous or current line has to be valid (or both)
-- '*': simply compute line, same as 'or', belongs to previous '&' or '+'
curcmd = string_match(bnwlist[linetostart], "^[%s%.,\\&%?%*]*(.*)")
linetostart = linetostart + 1;
if (not curcmd) then
return;
end
-- append lines beginning with '\\'
while (linetostart <= #bnwlist) do
if (ParseTimer_IsComment(bnwlist[linetostart])) then
-- comment, next line
linetostart = linetostart + 1;
else
local token, append = string_match(bnwlist[linetostart], "^[%s%.,]*([\\&%?%*])[%s%.,\\&%?%*]*(.*)");
if (token) then
if (token == "\\") then
-- append line
curcmd = curcmd .. " " .. append;
linetostart = linetostart + 1;
elseif (token == "&") then
boolop = BOOLOP_AND;
break;
elseif (token == "?") then
boolop = BOOLOP_RELAXEDAND;
break;
elseif (token == "*") then
boolop = BOOLOP_OR;
break;
else
break;
end
else
break;
end
end
end
return curcmd, linetostart, boolop;
end
local function in_value_range(cur_val, cur_val_perc, range_tab)
--[[ range_tab structure
[1] == value lower bound (>=)
[2] == value upper bound (<=)
[3] == stacks lower bound (>=)
[4] == stacks upper bound (<=)
[5] == value lower bound is in percent (true, nil)
[6] == value upper bound is in percent (true, nil) ]]
if(range_tab[1]) then
if(range_tab[5]) then
if(cur_val_perc < range_tab[1]) then
return false;
end
elseif(cur_val < range_tab[1]) then
return false;
end
end
if(range_tab[2]) then
if(range_tab[6]) then
if(cur_val_perc > range_tab[2]) then
return false;
end
elseif(cur_val > range_tab[2]) then
return false;
end
end
return true;
end
local function in_stacks_range(stacks, range_tab)
if(range_tab[3]) then
if(not stacks or stacks < range_tab[3]) then
return false;
end
end
if(range_tab[4]) then
if(not stacks or stacks > range_tab[4]) then
return false;
end
end
return true;
end
local function set_not(ti)
ti.bChannel = true;
ti.dur = 1;
ti.fin = 1;
ti.bSpecial = true;
ti.valIsStatic = true;
ti.ok = true;
end
local function set_times(timer, ti, dur, fin, ischannel)
if (ti.ok) then
if (timer.bNot) then
ti.ok = nil;
else
if (ti.valIsStatic or timer.bExists) then
set_not(ti);
else
ti.bChannel = ischannel;
ti.dur = dur;
ti.fin = fin;
end
end
elseif (timer.bNot) then
set_not(ti);
end
end
function Gnosis:Timers_Spell(bar, timer, ti)
-- cast
local spell, _, icon, s, d, _, _, notInterruptible = UnitCastingInfo(timer.unit);
if(d and d > 0) then
if(timer.spell == "all" or timer.spell == "any" or timer.spell == spell) then
ti.cname = spell;
ti.icon = icon;
ti.unit = timer.unit;
ti.notInterruptible = notInterruptible or nil;
local dur, fin = d-s, d;
if(timer.brange) then
local rem = fin/1000-GetTime();
ti.ok = in_value_range(rem, rem*100000/dur, timer.range_tab);
else
ti.ok = true;
end
set_times(timer, ti, dur, fin, false);
end
else
spell, _, icon, s, d, _, _, notInterruptible = UnitChannelInfo(timer.unit);
if(d and d > 0) then
if(timer.spell == "all" or timer.spell == "any" or timer.spell == spell) then
ti.cname = spell;
ti.icon = icon;
ti.unit = timer.unit;
ti.notInterruptible = notInterruptible or nil;
local dur, fin = d-s, d;
if(timer.brange) then
local rem = fin/1000-GetTime();
ti.ok = in_value_range(rem, rem*100000/dur, timer.range_tab);
else
ti.ok = true;
end
set_times(timer, ti, dur, fin, true);
end
end
end
end
function Gnosis:Timers_SpellCD(bar, timer, ti)
-- cooldown, player only
ti.unit = "player";
local s, d = GetSpellCooldown(timer.spell);
local dur, fin;
local cd_info = Gnosis.timer_cds[timer.spell];
local curtime = GetTime();
if (d and d <= 1.5 and cd_info and curtime < cd_info.fin and (s+d) >= cd_info.fin) then
-- duration of the global cooldown
dur, fin = cd_info.dur, cd_info.fin;
elseif (d and d > 1.5) then
-- duration greater than global cd
dur, fin = d, s + d;
if (cd_info) then
cd_info.dur = dur;
cd_info.fin = fin;
else
Gnosis.timer_cds[timer.spell] = { dur = dur, fin = fin };
end
end
if (dur) then
ti.cname = timer.spell;
ti.icon = timer.icon or select(3, GetSpellInfo(timer.spell));
if(timer.brange) then
local rem = fin - curtime;
ti.ok = in_value_range(rem, rem*100/dur, timer.range_tab);
else
ti.ok = true;
end
set_times(timer, ti, dur*1000, fin*1000, true);
elseif (timer.bNot) then
ti.cname = timer.spell;
ti.icon = timer.icon or select(3, GetSpellInfo(timer.spell));
set_not(ti);
end
end
function Gnosis:Timers_Counter(bar, timer, ti)
ti.unit = "player";
local cnter = Gnosis.counters[timer.spell];
if (cnter and cnter.endtime < GetTime()) then
cnter = nil;
end
if (cnter) then
ti.cname = timer.spell;
if (timer.brange) then
local curcnt = GetTime() - cnter.starttime;
local totcnt = cnter.endtime - cnter.starttime;
ti.ok = in_value_range(curcnt, curcnt*100/totcnt, timer.range_tab);
else
ti.ok = true;
end
set_times(timer, ti,
(cnter.endtime-cnter.starttime)*1000,
cnter.endtime*1000,
true
);
elseif (timer.bNot) then
ti.cname = timer.spell;
set_not(ti);
end
end
local function GetAura(timer, unit)
if (timer.spellid) then
-- aura id
local _, name, ic, sta, d, s, eff1, eff2, eff3, id;
local i = 1;
repeat
name, ic, sta, _, d, s, _, _, _, id, _, _, _, _, _, eff1, eff2, eff3 =
UnitAura(unit, i, timer.filter);
if (id and id == timer.spellid) then
timer.spell = name;
if (timer.auraeffect3) then
if (eff3 and eff3 > 0) then
return ic, sta, timer.auraeffect3, eff3, eff3, true;
end
elseif (timer.auraeffect2) then
if (eff2 and eff2 > 0) then
return ic, sta, timer.auraeffect2, eff2, eff2, true;
end
elseif (timer.auraeffect1) then
if (eff1 and eff1 > 0) then
return ic, sta, timer.auraeffect1, eff1, eff1, true;
end
elseif (timer.aurastacks) then
if (sta and sta > 0) then
return ic, sta, timer.aurastacks, sta, eff1, true;
end
else
-- timer bar
return ic, sta, d, s, eff, false;
end
-- nothing to return
return ic;
end
i = i + 1;
until id == nil or i > 40;
return;
else
-- aura name
local _, ic, sta, _, d, s, _, _, _, _, _, _, _, _, _, eff1, eff2, eff3 =
AuraUtil_FindAuraByName(timer.spell, unit, timer.filter);
if (timer.auraeffect3) then
if (eff3 and eff3 > 0) then
return ic, sta, timer.auraeffect3, eff3, eff3, true;
end
elseif (timer.auraeffect2) then
if (eff2 and eff2 > 0) then
return ic, sta, timer.auraeffect2, eff2, eff2, true;
end
elseif (timer.auraeffect1) then
if (eff1 and eff1 > 0) then
return ic, sta, timer.auraeffect1, eff1, eff1, true;
end
elseif (timer.aurastacks) then
if (sta and sta > 0) then
return ic, sta, timer.aurastacks, sta, eff1, true;
end
else
-- timer bar
return ic, sta, d, s, eff, false;
end
-- nothing to return
return ic;
end
end
function Gnosis:Timers_Aura(bar, timer, ti)
-- aura == buff or debuff (== hot or dot)
ti.unit = timer.unit;
local ic, sta, d, s, effect, isspecial = GetAura(timer, timer.unit);
if (s) then
ti.cname = timer.spell;
ti.stacks = (sta and sta > 0) and sta or nil;
ti.icon = ic;
if (isspecial) then
ti.unit = timer.unit;
ti.bSpecial = true;
if (timer.brange) then
ti.ok = in_value_range(s, s*100/d, timer.range_tab);
else
ti.ok = true;
end
set_times(timer, ti, d, s, true);
else
local rem = 0;
if (s > 0) then
rem = s - GetTime();
end
if (rem > 0) then
-- dynamic aura
if (timer.brange) then
if (in_value_range(rem, rem*100/d, timer.range_tab) and
in_stacks_range(sta, timer.range_tab)) then
ti.ok = true;
end
else
ti.ok = true;
end
set_times(timer, ti, d * 1000, s * 1000, true);
elseif (s == 0 and d == 0 and not timer.bNot) then
-- static aura
if (timer.brange) then
if (in_stacks_range(sta, timer.range_tab)) then
ti.ok = true;
end
else
ti.ok = true;
end
ti.valIsStatic = true;
set_times(timer, ti);
end
end
elseif (timer.bNot) then
ti.cname = timer.spell;
ti.icon = timer.icon or select(3, GetSpellInfo(timer.spell));
set_not(ti);
end
end
function Gnosis:Timers_GroupAura(bar, timer, ti)
ti.unit = nil;
-- scan current group for aura, also scan existing pets
local _, ic, sta, d, s, effect, isspecial;
local n = GetNumGroupMembers();
if (IsInRaid() and n >= 2) then
-- scan raid
for i = 1, n do
local curunit = "raid" .. i;
ic, sta, d, s, effect, isspecial = GetAura(timer, curunit);
if (s) then
ti.unit = curunit;
break;
end
curunit = "raidpet" .. i;
if (UnitExists(curunit)) then
ic, sta, d, s, effect, isspecial = GetAura(timer, curunit);
if (s) then
ti.unit = curunit;
break;
end
end
end
elseif (n >= 2) then
-- scan player and group members
ic, sta, d, s, effect, isspecial = GetAura(timer, "player");
if (s) then
ti.unit = "player";
else
if (UnitExists("playerpet")) then
ic, sta, d, s, effect, isspecial = GetAura(timer, "playerpet");
end
if (s) then
ti.unit = "playerpet";
else
for i = 1, (n - 1) do
local curunit = "party" .. i;
ic, sta, d, s, effect, isspecial = GetAura(timer, curunit);
if (s) then
ti.unit = curunit;
break;
end
curunit = "partypet" .. i;
if (UnitExists(curunit)) then
ic, sta, d, s, effect, isspecial = GetAura(timer, curunit);
if (s) then
ti.unit = curunit;
break;
end
end
end
end
end
else
-- scan player (player not in group)
ic, sta, d, s, effect, isspecial = GetAura(timer, "player");
if (s) then
ti.unit = "player";
else
if (UnitExists("playerpet")) then
ic, sta, d, s, effect, isspecial = GetAura(timer, "playerpet");
if (s) then
ti.unit = "playerpet";
end
end
end
end
if (ti.unit) then
ti.cname = timer.spell;
ti.stacks = (sta and sta > 0) and sta or nil;
ti.effect = effect;
ti.icon = ic;
if (isspecial) then
ti.unit = timer.unit;
ti.bSpecial = true;
if (timer.brange) then
ti.ok = in_value_range(s, s*100/d, timer.range_tab);
else
ti.ok = true;
end
set_times(timer, ti, d, s, true);
else
local rem = 0;
if (s > 0) then
rem = s - GetTime();
end
if (rem > 0) then
-- dynamic aura
if (timer.brange) then
if (in_value_range(rem, rem*100/d, timer.range_tab) and
in_stacks_range(sta, timer.range_tab)) then
ti.ok = true;
end
else
ti.ok = true;
end
set_times(timer, ti, d * 1000, s * 1000, true);
elseif (s == 0 and d == 0 and not timer.bNot) then
-- static aura
if (timer.brange) then
if (in_stacks_range(sta, timer.range_tab)) then
ti.ok = true;
end
else
ti.ok = true;
end
ti.valIsStatic = true;
set_times(timer, ti);
end
end
elseif (timer.bNot) then
ti.unit = timer.unit;
ti.cname = timer.spell;
ti.icon = timer.icon or select(3, GetSpellInfo(timer.spell));
set_not(ti);
end
end
function Gnosis:Timers_SpellRecharge(bar, timer, ti)
-- spell charges, player only
ti.unit = "player";
local curcharges, maxcharges, cdstart, cddur = GetSpellCharges(timer.spell);
if (curcharges == nil) then
return;
end
if (timer.chargecnt) then
-- display amount of charges
ti.cname = timer.spell;
ti.icon = timer.icon;
ti.bSpecial = true;
if (timer.brange) then
ti.ok = in_value_range(curcharges, curcharges*100/maxcharges, timer.range_tab);
else
ti.ok = true;
end
set_times(timer, ti, maxcharges, curcharges);
else
-- recharge cooldown
if (curcharges ~= maxcharges) then
if (timer.brange) then
local rem = cddur-(GetTime()-cdstart);
ti.ok = in_value_range(rem, rem*100/cddur, timer.range_tab) and
in_stacks_range(curcharges, timer.range_tab);
else
ti.ok = true;
end
ti.cname = timer.spell;
ti.stacks = curcharges;
ti.icon = timer.icon;
set_times(timer, ti, cddur*1000, (cdstart+cddur)*1000, true);
elseif (timer.bNot) then
ti.cname = timer.spell;
ti.stacks = curcharges;
ti.icon = timer.icon;
set_not(ti);
end
end
end
function Gnosis:Timers_ItemCD(bar, timer, ti)
-- itemcd, player only
if (not timer.iid) then
local itemname, link, _, _, _, _, _, _, _, itex = GetItemInfo(timer.spell);
if (link) then
timer.iid = string_match(link, "|Hitem:(%d+):");
timer.itex = itex;
timer.iname = itemname;
end
end
if (timer.iid) then
ti.unit = "player";
local s, d = GetItemCooldown(timer.iid);
if (d and d > 1.5) then -- duration greater than global cd
ti.cname = timer.iname;
ti.icon = timer.itex;
local dur, fin = d, s+d;
if (timer.brange) then
local rem = fin - GetTime();
ti.ok = in_value_range(rem, rem*100/dur, timer.range_tab);
else
ti.ok = true;
end
set_times(timer, ti, dur*1000, fin*1000, true);
elseif (timer.bNot) then
ti.cname = timer.iname;
ti.icon = timer.itex;
set_not(ti);
end
end
end
function Gnosis:Timers_ItemEquipped(bar, timer, ti)
-- item equipped? player only
if (not timer.iid) then
local itemname, link, _, _, _, _, _, _, _, itex = GetItemInfo(timer.spell);
if (link) then
timer.iid = string_match(link, "|Hitem:(%d+):");
timer.itex = itex;
timer.iname = itemname;
end
end
if (timer.iname) then
ti.unit = "player";
if (IsEquippedItem(timer.iname)) then
ti.cname = timer.iname;
ti.icon = timer.itex;
ti.ok = true;
ti.valIsStatic = true;
set_times(timer, ti);
elseif (timer.bNot) then
ti.cname = timer.iname;
ti.icon = timer.itex;
set_not(ti);
end
end
end
function Gnosis:Timers_RuneCD(bar, timer, ti)
-- rune cooldown, player only
ti.unit = "player";
-- check for runetype
local rune = GetRuneType(timer.spell);
if (rune and timer.runetype and timer.runetype ~= rune) then
return;
end
-- check cooldown
local s, d, rdy = GetRuneCooldown(timer.spell);
if (s and (not rdy) and (s+d) >= GetTime()) then
if (rune) then
ti.cname = Gnosis.tRuneName[rune];
ti.icon = Gnosis.tRuneTexture[rune];
else
ti.cname = "";
ti.icon = nil;
end
local dur, fin = d, s+d;
if (timer.brange) then
local rem = fin - GetTime();
ti.ok = in_value_range(rem, rem*100/dur, timer.range_tab);
else
ti.ok = true;
end
set_times(timer, ti, dur*1000, fin*1000, true);
elseif (timer.bNot) then
if (rune) then
ti.cname = Gnosis.tRuneName[rune];
ti.icon = Gnosis.tRuneTexture[rune];
else
ti.cname = "";
ti.icon = nil;
end
set_not(ti);
end
end
function Gnosis:Timers_TotemDuration(bar, timer, ti)
-- totem duration
ti.unit = "player";
local bExist, name, s, d, icon = GetTotemInfo(timer.spell);
if(bExist and name and s and s > 0) then
ti.cname = name;
ti.icon = icon;
local dur, fin = d, s+d;
if(timer.brange) then
local rem = fin - GetTime();
ti.ok = in_value_range(rem, rem*100/dur, timer.range_tab);
else
ti.ok = true;
end
set_times(timer, ti, dur*1000, fin*1000, true);
elseif(timer.bNot) then
ti.cname = "";
ti.icon = nil;
set_not(ti);
end
end
function Gnosis:Timers_InnerCD(bar, timer, ti)
ti.unit = "player";
local bExist = false;
if (Gnosis.ti_icd_active[timer.spell]) then
if (GetTime() * 1000 >= Gnosis.ti_icd_active[timer.spell]) then
-- inner cd expired
Gnosis.ti_icd_active[timer.spell] = nil;
else
bExist = true;
end
end
if (bExist) then
ti.cname = timer.spell;
ti.icon = timer.icon;
local dur, fin = Gnosis.ti_icd[timer.spell].duration, Gnosis.ti_icd_active[timer.spell];
if(timer.brange) then
local rem = fin / 1000 - GetTime();
ti.ok = in_value_range(rem, rem*100000/dur, timer.range_tab);
else
ti.ok = true;
end
set_times(timer, ti, dur, fin, true);
elseif (timer.bNot) then
ti.cname = timer.spell;
ti.icon = timer.icon;
set_not(ti);
end
end
function Gnosis:Timers_WeaponEnchant(bar, timer, ti, exists, expires, charges)
-- weapon enchant (player only)
if(exists) then
ti.cname = timer.spell;
local dur = expires;
local fin = expires + GetTime()*1000;
local bardur = (bar.dur or bar.duration);
if(timer.spell == bar.castname and bardur and bardur > dur) then
dur = bardur;
end
if(timer.brange) then
local rem = expires;
ti.ok = in_value_range(rem/1000, rem*100/dur, timer.range_tab);
else
ti.ok = true;
end
set_times(timer, ti, dur, fin, true);
end
end
function Gnosis:Timers_WeaponEnchantMain(bar, timer, ti)
ti.unit = "player";
local exists, expires, charges = select(1, GetWeaponEnchantInfo());
if(exists) then
local tt = Gnosis.tooltip;
tt:ClearLines();
tt:SetInventoryItem("player", 16);
local num = tt:NumLines();
for i = 1, num do
if(string_find(_G["GnosisGameTooltipTextLeft"..i]:GetText(), timer.spell)) then
ti.icon = GetInventoryItemTexture("player", 16);
Gnosis:Timers_WeaponEnchant(bar, timer, ti, exists, expires, charges);
end
end
elseif(timer.bNot) then
ti.cname = timer.spell;
ti.icon = GetInventoryItemTexture("player", 16);
set_not(ti);
end
end
function Gnosis:Timers_WeaponEnchantOff(bar, timer, ti)
ti.unit = "player";
local exists, expires, charges = select(4, GetWeaponEnchantInfo());
if(exists) then
local tt = Gnosis.tooltip;
tt:ClearLines();
tt:SetInventoryItem("player", 17);
local num = tt:NumLines();
for i = 1, num do
if(string_find(_G["GnosisGameTooltipTextLeft"..i]:GetText(), timer.spell)) then
ti.icon = GetInventoryItemTexture("player", 17);
Gnosis:Timers_WeaponEnchant(bar, timer, ti, exists, expires, charges);
end
end
elseif(timer.bNot) then
ti.cname = timer.spell;
ti.icon = GetInventoryItemTexture("player", 17);
set_not(ti);
end
end
function Gnosis:Timers_Range(bar, timer, ti)
-- range between player and selected unit
local minRange, maxRange;
if (UnitExists(timer.unit)) then
minRange, maxRange = Gnosis.range:GetRange(timer.unit);
end
if (minRange and maxRange) then
ti.unit = timer.unit;
ti.bSpecial = true;
if (timer.brange) then
--[[ range_tab structure
[1] == value lower bound (>=)
[2] == value upper bound (<=)
[3] == stacks lower bound (>=)
[4] == stacks upper bound (<=)
[5] == value lower bound is in percent (true, nil)
[6] == value upper bound is in percent (true, nil) ]]
if (timer.range_tab[5] or timer.range_tab[6]) then
-- percentages of what??? (not allowed)
ti.ok = false;
else
if (minRange <= (timer.range_tab[2] or 10000) and
maxRange >= (timer.range_tab[1] or 0)) then
ti.ok = true;
end
end
else
ti.ok = true;
end
set_times(timer, ti, maxRange, minRange, true);
elseif (timer.bNot) then
ti.cname = "";
ti.icon = nil;
ti.unit = timer.unit;
set_not(ti);
end
end
function Gnosis:Timers_Power(bar, timer, ti)
-- mana, rage, focus, energy
local s, d = UnitPower(timer.unit), UnitPowerMax(timer.unit);
if(d and d > 0) then
local pts = select(2, UnitPowerType(timer.unit));
ti.cname = pts and _G[pts] or "";
ti.unit = timer.unit;
ti.bSpecial = true;
if(timer.brange) then
ti.ok = in_value_range(s, s*100/d, timer.range_tab);
else
ti.ok = true;
end
set_times(timer, ti, d, s, true);
elseif(timer.bNot) then
ti.cname = "";
ti.icon = nil;
ti.unit = timer.unit;
set_not(ti);
end
end
function Gnosis:Timers_PowerGeneric(bar, timer, ti)
-- soul shards, eclipse, holy power, dark force, light force (chi)
-- shadow orbs, burning embers and demonic fury
-- combo points (6.0)
local idx = timer.type - 2000;
local s, d = UnitPower(timer.unit, idx, timer.resource_decimals), UnitPowerMax(timer.unit, idx, timer.resource_decimals);
if (d and d > 0) then
-- values including decimals?
if (timer.resource_decimals) then
s = s / 10;
d = d / 10;
end
-- get name and icon of the effect
if (not ti.cname or ti.cname == "") then
ti.cname =
(idx == 4 and _G["COMBO_POINTS"]) or
(idx == 7 and _G["SOUL_SHARDS"]) or
(idx == 8 and _G["ECLIPSE"]) or
(idx == 9 and _G["HOLY_POWER"]) or
(idx == 12 and _G["LIGHT_FORCE"]) or
(idx == 13 and _G["SHADOW_ORBS"]) or
(idx == 14 and _G["BURNING_EMBERS"]) or
(idx == 15 and _G["DEMONIC_FURY"]) or
"";
ti.icon = select(3, GetSpellInfo(
(idx == 4 and 108209) or -- combo points, no exact icon match
(idx == 7 and 117198) or -- soul shards
(idx == 8 and 79577) or -- eclipse
(idx == 9 and 85247) or -- holy power
(idx == 12 and 157411) or -- chi, no exact icon match
(idx == 13 and 95740) or -- shadow orbs
(idx == 14 and 108647) or -- burning embers
(idx == 15 and 104315) or -- demonic fury
nil
));
end
ti.unit = timer.unit;
ti.bSpecial = true;
if (timer.brange) then
ti.ok = in_value_range(s, s*100/d, timer.range_tab);
else
ti.ok = true;
end
set_times(timer, ti, d, s, true);
elseif (timer.bNot) then
ti.cname = "";
ti.icon = nil;
ti.unit = timer.unit;
set_not(ti);
end
end
function Gnosis:Timers_Experience(bar, timer, ti)
local xp = UnitXP("player");
local xpmax = UnitXPMax("player");
if (xp and xpmax) then
ti.icon = nil;
ti.unit = "player";
ti.bSpecial = true;
if (timer.brange) then
ti.ok = in_value_range(xp, xp*100/xpmax, timer.range_tab);
else
ti.ok = true;
end
set_times(timer, ti, xpmax, xp, true);
end
end
function Gnosis:Timers_RestedXP(bar, timer, ti)
local rested = GetXPExhaustion();
local xpmax = UnitXPMax("player");
if (rested and xpmax) then
ti.icon = nil;
ti.unit = "player";
ti.bSpecial = true;
if (timer.brange) then
ti.ok = in_value_range(rested, min(rested*100/xpmax, 100), timer.range_tab);
else
ti.ok = true;
end
set_times(timer, ti, xpmax, rested, true);
end
end
function Gnosis:Timers_Health(bar, timer, ti)
local s, d = UnitHealth(timer.unit), UnitHealthMax(timer.unit);
if(d and d > 0) then
ti.cname = _G["HEALTH"];
ti.unit = timer.unit;
ti.bSpecial = true;
if(timer.brange) then
ti.ok = in_value_range(s, s*100/d, timer.range_tab);
else
ti.ok = true;
end
set_times(timer, ti, d, s, true);
elseif(timer.bNot) then
ti.cname = "";
ti.icon = nil;
ti.unit = timer.unit;
set_not(ti);
end
end
function Gnosis:Timers_PowerAlternate(bar, timer, ti)
local s, d = UnitPower(timer.unit, ALTERNATE_POWER_INDEX), UnitPowerMax(timer.unit, ALTERNATE_POWER_INDEX);
if(d and d > 0) then
local altpowerinfo_cname = select(11, UnitAlternatePowerInfo(timer.unit));
ti.cname = altpowerinfo_cname or "";
ti.icon = UnitAlternatePowerTextureInfo(timer.unit, 2);
ti.unit = timer.unit;
ti.bSpecial = true;
if(timer.brange) then
ti.ok = in_value_range(s, s*100/d, timer.range_tab);
else
ti.ok = true;
end
set_times(timer, ti, d, s, true);
elseif(timer.bNot) then
ti.cname = "";
ti.icon = nil;
ti.unit = timer.unit;
set_not(ti);
end
end
function Gnosis:Timers_IncomingHealth(bar, timer, ti)
local s, d = UnitGetIncomingHeals(timer.unit), UnitHealthMax(timer.unit);
if(s and d and d > 0) then
ti.cname = "";
ti.unit = timer.unit;
ti.bSpecial = true;
if(timer.brange) then
ti.ok = in_value_range(s, s*100/d, timer.range_tab);
else
ti.ok = true;
end
set_times(timer, ti, d, s, true);
elseif(timer.bNot) then
ti.cname = "";
ti.icon = nil;
ti.unit = timer.unit;
set_not(ti);
end
end
function Gnosis:Timers_TargetThreat(bar, timer, ti)
local _, status, pct, _, val = UnitDetailedThreatSituation(timer.unit, "target");
if(status and pct > 0) then
val = val / 100;
ti.cname = "";
ti.unit = timer.unit;
ti.bSpecial = true;
ti.stacks = status;
if(timer.brange) then
if(in_value_range(val, pct, timer.range_tab) and
in_stacks_range(status, timer.range_tab)) then
ti.ok = true;
end
else
ti.ok = true;
end
set_times(timer, ti, val * 100 / pct, val, true);
elseif(timer.bNot) then
ti.cname = "";
ti.icon = nil;
ti.unit = timer.unit;
set_not(ti);
end
end
function Gnosis:Timers_Fixed(bar, timer, ti)
-- show fixed bar (pass text in nfs)
ti.unit = "player";
ti.cname = "";
ti.icon = timer.icon or select(3, GetSpellInfo(timer.spell));
set_not(ti);
end
function Gnosis:Timers_SpellKnown(bar, timer, ti)
ti.unit = "player";
if (GetSpellLink(timer.spell)) then
ti.cname = timer.spell;
ti.icon = timer.icon or select(3, GetSpellInfo(timer.spell));
ti.ok = true;
ti.valIsStatic = true;
set_times(timer, ti);
elseif (timer.bNot) then
ti.cname = timer.spell;
ti.icon = timer.icon or select(3, GetSpellInfo(timer.spell));
set_not(ti);
end
end
function Gnosis:Timers_UnitName(bar, timer, ti)
local n = UnitName(timer.unit);
if (n and (timer.spell == "any" or n == timer.spell)) then
ti.cname = n;
ti.unit = timer.unit;
ti.ok = true;
ti.valIsStatic = true;
set_times(timer, ti);
return;
end
if (timer.bNot) then
ti.cname = timer.spell;
ti.unit = timer.unit;
set_not(ti);
end
end
function Gnosis:Timers_Npc(bar, timer, ti)
local guid = UnitGUID(timer.unit);
if (guid) then
local _, _, _, _, _, npc_id, _ = string_split("-", guid);
if (npc_id and (timer.spell == "any" or npc_id == timer.spell)) then
ti.cname = npc_id;
ti.unit = timer.unit;
ti.ok = true;
ti.valIsStatic = true;
set_times(timer, ti);
return;
end
end
if (timer.bNot) then
ti.cname = timer.spell;
ti.unit = timer.unit;
set_not(ti);
end
end
function Gnosis:Timers_Charspec(bar, timer, ti)
local current_spec = self:SafeGetSpecialization();
if (current_spec) then
-- spec active
local spec_id, spec_name = GetSpecializationInfo(current_spec);
local spell_number = tonumber(timer.spell);
local matched = false;
-- try to compare to specialization id first
-- http://www.wowwiki.com/Specialization_IDs
if (spell_number and spell_number == spec_id) then
matched = true;
-- otherwise compare to string
elseif (timer.spell == spec_name) then
matched = true;
end
if (matched) then
ti.cname = spec_name;
ti.unit = "player";
ti.ok = true;
ti.valIsStatic = true;
set_times(timer, ti);
elseif (timer.bNot) then
ti.cname = spec_name;
ti.unit = "player";
set_not(ti);
end
end
end
function Gnosis:Timers_Talent(bar, timer, ti)
if (Gnosis.iCurSpec) then
local tier, column = string_split("-", timer.spell);
if (tier and column) then
local tier_num = tonumber(tier);
local column_num = tonumber(column);
if (tier_num and column_num) then
--local _, tname, ttex, tsel = GetTalentInfo(tier_num, column_num, Gnosis.iCurSpec);
-- bandaid...
local _, tname, ttex, tsel = GetTalentInfo(tier_num, column_num, 1);
-- talent selected?
if (tsel) then
ti.cname = tname;
ti.unit = "player";
ti.icon = ttex;
ti.ok = true;
ti.valIsStatic = true;
set_times(timer, ti);
elseif (timer.bNot and tname) then
ti.cname = tname;
ti.unit = "player";
ti.icon = ttex;
set_not(ti);
end
end
end
end
end
function Gnosis:Timers_Glyph(bar, timer, ti)
-- check for glyph match
local matched, glyph_name, glyph_icon;
local spellnum = tonumber(timer.spell);
for i=1, NUM_GLYPH_SLOTS do
local valid, _, _, id = GetGlyphSocketInfo(i);
if (valid) then
glyph_name, _, glyph_icon = GetSpellInfo(id);
if (spellnum) then
if (spellnum == id) then
matched = true;
break;
end
elseif (glyph_name == timer.spell) then
matched = true;
break;
end
end
end
-- glyph found
if (matched) then
ti.cname = glyph_name;
ti.unit = "player";
ti.icon = glyph_icon;
ti.ok = true;
ti.valIsStatic = true;
set_times(timer, ti);
-- glyph not found
elseif (timer.bNot) then
ti.unit = "player";
if (spellnum) then
local sname, _, stex = GetSpellInfo(spellnum);
if (sname) then
ti.cname = sname;
ti.icon = stex;
set_not(ti);
end
else
ti.cname = timer.spell;
set_not(ti);
end
end
end
function Gnosis:Timers_GlobalCD(bar, timer, ti)
local gcd = Gnosis.current_gcd;
local rem = gcd and (gcd.finish - GetTime()) or 0;
if (rem > 0 and (timer.spell == "any" or gcd.spell == timer.spell)) then
ti.cname = gcd.spell;
ti.unit = "player";
ti.icon = timer.icon or select(3, GetSpellInfo(gcd.spellid));
if (timer.brange) then
ti.ok = in_value_range(rem, rem*100/gcd.cd, timer.range_tab);
else
ti.ok = true;
end
set_times(timer, ti, gcd.cd*1000, gcd.finish*1000);
elseif (timer.bNot) then
ti.cname = timer.spell;
ti.unit = "player";
ti.icon = timer.icon or select(3, GetSpellInfo(timer.spell));
set_not(ti);
end
end
function Gnosis:Timers_Exit(bar, timer, ti)
ti.ok = true;
end
function Gnosis:ExtractRegex(str, regex_a, regex_b, dotrim)
local res = string_match(str, regex_a);
if (res) then
if (dotrim) then
res = string_trim(res);
end
return res, string_gsub(str, regex_a, "");
elseif (regex_b) then
res = string_match(str, regex_b);
if (res) then
if (dotrim) then
res = string_trim(res);
end
return res, string_gsub(str, regex_b, "");
else
return nil, str;
end
else
return nil, str;
end
end
-- local functions
local function validate_value(val, isperc)
if(val and tonumber(val)) then
val = tonumber(val);
if(isperc) then
if(val > 100) then
return 100;
elseif(val < 0) then
return 0;
else
return val;
end
elseif(val < 0) then
return 0;
else
return val;
end
end
return nil;
end
local function validate_value(val, isperc)
if(val and tonumber(val)) then
val = tonumber(val);
if(isperc) then
if(val > 100) then
return 100;
elseif(val < 0) then
return 0;
else
return val;
end
elseif(val < 0) then
return 0;
else
return val;
end
end
return nil;
end
local function get_valid_range_table(spell_string)
local res, str = Gnosis:ExtractRegex(spell_string, "<([^>]*)>", nil, true);
str = string_trim(str);
local cnt = 1;
if(res) then
res = strconcat(res, ",,,");
local low_val, up_val, low_st, up_st, blow_val_perc, bup_val_perc;
low_val, up_val, low_st, up_st = string_match(res, "([^,]*),([^,]*),([^,]*),([^,]*)");
low_st = tonumber(low_st);
up_st = tonumber(up_st);
low_val, cnt = string_gsub(low_val, "%%", "");
if(cnt > 0) then
blow_val_perc = true;
end
up_val, cnt = string_gsub(up_val, "%%", "");
if(cnt > 0) then
bup_val_perc = true;
end
-- values valid?
low_val = validate_value(low_val, blow_val_perc);
up_val = validate_value(up_val, bup_val_perc);
low_st = validate_value(low_st);
up_st = validate_value(up_st);
if(low_val or up_val or low_st or up_st) then
local t = { low_val, up_val, low_st, up_st, blow_val_perc, bup_val_perc };
return str, true, t;
end
end
return str, nil, nil;
end
local TimerInfo = {
};
local SelectedTimerInfo = {
};
function Gnosis:CreateSingleTimerTable()
-- wipe tables
wipe(self.ti_fl);
wipe(self.ti_icd);
wipe(self.counters);
for key, value in pairs(self.castbars) do
local conf = Gnosis.s.cbconf[key];
local timer_id = 0;
if (conf.bEn and conf.bartype == "ti" and conf.spectab[self.iCurSpec]) then
value.timers = {};
value.iTimerSort = nil;
local curline = 1;
while (curline) do
-- timer id
timer_id = timer_id + 1;
-- copy of timer command string
local str, cmd_boolop;
str, curline, cmd_boolop = self:ParseTimer_GetCommand(conf.bnwlist, curline);
if (not str) then
break;
end
local unit, recast, staticdur, zoom, specstr, iconoverride, portraitunit,
shown, hidden, plays, playm, playf, mcnt, msize, tooltipvalue,
aurastacks, auraeffect1, auraeffect2, auraeffect3, startcnt, startcntcpy, stopcnt, runetype,
resource_decimals, chargecnt, spellid;
-- extract commands from current line
unit, str = self:ExtractRegex(str, "unit=(%w+)", "unit=\"([^\"]+)\"", true);
iconoverride, str = self:ExtractRegex(str, "icon=(%w+)", "icon=\"([^\"]+)\"", true);
shown, str = self:ExtractRegex(str, "shown=(%w+)", "shown=\"([^\"]+)\"", true);
hidden, str = self:ExtractRegex(str, "hidden=(%w+)", "hidden=\"([^\"]+)\"", true);
portraitunit, str = self:ExtractRegex(str, "portrait=(%w+)", "portrait=\"([^\"]+)\"", true);
plays, str = self:ExtractRegex(str, "plays=\"([^\"]+)\"", nil, true);
playm, str = self:ExtractRegex(str, "playm=\"([^\"]+)\"", nil, true);
playf, str = self:ExtractRegex(str, "playf=\"([^\"]+)\"", nil, true);
startcnt, str = self:ExtractRegex(str, "startcnt=(%w+)", "startcnt=\"([^\"]+)\"", true);
startcntcpy, str = self:ExtractRegex(str, "startcntcpy=(%w+)", "startcntcpy=\"([^\"]+)\"", true);
stopcnt, str = self:ExtractRegex(str, "stopcnt=(%w+)", "stopcnt=\"([^\"]+)\"", true);
mcnt, str = self:ExtractRegex(str, "mcnt=(%w+)", "mcnt=\"([^\"]+)\"", true);
msize, str = self:ExtractRegex(str, "msize=([+-]?[0-9]*%.?[0-9]*)", "msize=\"([+-]?[0-9]*%.?[0-9]*)\""); -- floating point regex
recast, str = self:ExtractRegex(str, "recast=([+-]?[0-9]*%.?[0-9]*)", "recast=\"([+-]?[0-9]*%.?[0-9]*)\"");
staticdur, str = self:ExtractRegex(str, "staticdur=([+-]?[0-9]*%.?[0-9]*)", "staticdur=\"([+-]?[0-9]*%.?[0-9]*)\"");
zoom, str = self:ExtractRegex(str, "zoom=([+-]?[0-9]*%.?[0-9]*)", "zoom=\"([+-]?[0-9]*%.?[0-9]*)\"");
aurastacks, str = self:ExtractRegex(str, "auravalue=([+-]?[0-9]*%.?[0-9]*)", "auravalue=\"([+-]?[0-9]*%.?[0-9]*)\"");
if (not aurastacks) then
aurastacks, str = self:ExtractRegex(str, "aurastacks=([+-]?[0-9]*%.?[0-9]*)", "aurastacks=\"([+-]?[0-9]*%.?[0-9]*)\"");
end
auraeffect1, str = self:ExtractRegex(str, "auraeffect1=([+-]?[0-9]*%.?[0-9]*)", "auraeffect1=\"([+-]?[0-9]*%.?[0-9]*)\"");
if (not auraeffect1) then
auraeffect1, str = self:ExtractRegex(str, "auraeffect=([+-]?[0-9]*%.?[0-9]*)", "auraeffect=\"([+-]?[0-9]*%.?[0-9]*)\"");
end
auraeffect2, str = self:ExtractRegex(str, "auraeffect2=([+-]?[0-9]*%.?[0-9]*)", "auraeffect2=\"([+-]?[0-9]*%.?[0-9]*)\"");
auraeffect3, str = self:ExtractRegex(str, "auraeffect3=([+-]?[0-9]*%.?[0-9]*)", "auraeffect3=\"([+-]?[0-9]*%.?[0-9]*)\"");
specstr, str = self:ExtractRegex(str, "spec=([0-4])", "spec=\"([^\"]+)\"");
runetype, str = self:ExtractRegex(str, "runetype=(%d+)", "runetype=\"(%d+)\"");
spellid, str = self:ExtractRegex(str, "spellid=(%d+)", "spellid=\"(%d+)\"");
recast, staticdur, zoom, spellid =
recast and (tonumber(recast) * 1000),
staticdur and (tonumber(staticdur) * 1000),
zoom and (tonumber(zoom) * 1000),
spellid and tonumber(spellid);
local spectab;
if (specstr) then
spectab = self:CommaSeparatedNumbersToTable(specstr, 1, 4);
end
-- stack/effect value display variable
if (aurastacks and tonumber(aurastacks)) then
aurastacks = tonumber(aurastacks);
else
aurastacks = nil;
end
if (auraeffect1 and tonumber(auraeffect1)) then
auraeffect1 = tonumber(auraeffect1);
else
auraeffect1 = nil;
end
if (auraeffect2 and tonumber(auraeffect2)) then
auraeffect2 = tonumber(auraeffect2);
else
auraeffect2 = nil;
end
if (auraeffect3 and tonumber(auraeffect3)) then
auraeffect3 = tonumber(auraeffect3);
else
auraeffect3 = nil;
end
-- runetype
if (runetype and tonumber(runetype) and tonumber(runetype) > 0) then
runetype = tonumber(runetype);
else
runetype = nil;
end
-- marker count/size (tick markers for power bars)
if (mcnt and tonumber(mcnt)) then
mcnt = tonumber(mcnt);
if (mcnt > 10) then
mcnt = 10;
elseif (mcnt < 1) then
mcnt = 1;
end
if (not msize or not tonumber(msize)) then
msize = "1.0";
end
else
msize = nil;
end
if (msize) then
msize = tonumber(msize);
if (msize > 1.0 or msize < 0.0) then
msize = 1.0;
end
end
-- get play interval time
local playinterval;
if (plays) then
local s, f;
s, f, playinterval, plays = string_find(plays, "([+-]?[0-9]*%.?[0-9]*)%-(.+)");
if (playinterval) then playinterval = tonumber(playinterval); end
playm = nil; playf = nil;
elseif (playm) then
s, f, playinterval, playm = string_find(playm, "([+-]?[0-9]*%.?[0-9]*)%-(.+)");
if (playinterval) then playinterval = tonumber(playinterval); end
plays = nil; playf = nil;
elseif (playf) then
s, f, playinterval, playf = string_find(playf, "([+-]?[0-9]*%.?[0-9]*)%-(.+)");
if (playinterval) then playinterval = tonumber(playinterval); end
plays = nil; playm = nil;
end
-- check if playinterval is too short or too long
if (playinterval and (playinterval < 0.5 or playinterval > 600)) then
playinterval = nil;
end
local fplay, tplay, toplay;
if (playinterval) then
if (plays) then
fplay = PlaySound;
tplay = self.played.s;
toplay = plays;
elseif (playm and self.lsm:Fetch("sound", playm)) then
fplay = PlaySoundFile;
tplay = self.played.m;
toplay = self.lsm:Fetch("sound", playm);
elseif (playf) then
fplay = PlaySoundFile;
tplay = self.played.f;
toplay = playf;
end
end
-- start/stop count
local countstart, countinterval, countstop, countcpy;
if (startcnt) then
countinterval, countstart =
string_match(startcnt, "([+-]?[0-9]*%.?[0-9]*)%-(.+)");
if (countstart) then
countinterval = tonumber(countinterval);
else
countinterval = nil;
end
end
if (startcntcpy) then
countcpy = startcntcpy;
end
if (stopcnt) then
countstop = stopcnt;
end
-- icon override, portrait unit
local iconoverride = select(3, GetSpellInfo(iconoverride));
local ptun = portraitunit;
local nfs, tfs, colstr, tsbcol, tbcol;
-- name format string
nfs, str = self:ExtractRegex(str, "nfs=\"([^\"]*)\"", "nfs=(%w+)");
-- time format string
tfs, str = self:ExtractRegex(str, "tfs=\"([^\"]*)\"", "tfs=(%w+)");
-- status bar color
colstr, str = self:ExtractRegex(str, "sbcol=\"([^\"]+)\"");
if (colstr) then
local r,g,b,a = self:GetColorsFromString(colstr);
if(r) then
tsbcol = { r, g, b, a };
end
end
-- border color
colstr, str = self:ExtractRegex(str, "bcol=\"([^\"]+)\"");
if (colstr) then
local r,g,b,a = self:GetColorsFromString(colstr);
if(r) then
tbcol = { r, g, b, a };
end
end
-- command and spellname
local tiType, bSelf, bHarm, bHelp, bShowLag, bShowCasttime, iSort, bExists, bNot, bHideSpark, bHideIcon, cfinit, brange, range_tab, icon__;
local norefresh = false;
local boolop = cmd_boolop or BOOLOP_NONE;
local cmd, spell = string_match(str, "(.-):(.+)");
if(spell) then
spell, brange, range_tab = get_valid_range_table(spell);
end
cmd = cmd and string_trim(cmd);
if (cmd and string_len(cmd) > 0 and spell and string_len(spell) > 0) then
for w in string_gmatch(cmd, "%w+") do
w = string_lower(w);
if (w == "exit") then
tiType = -1;
cfinit = Gnosis.Timers_Exit;
elseif (w == "cast") then
tiType = 0;
cfinit = Gnosis.Timers_Spell;
elseif (w == "cd") then
tiType = 1;
unit = "player";
cfinit = Gnosis.Timers_SpellCD;
elseif (w == "dot" or w == "debuff") then
bHarm = true;
tiType = 2;
cfinit = Gnosis.Timers_Aura;
elseif (w == "hot" or w == "buff") then
bHelp = true;
tiType = 2;
cfinit = Gnosis.Timers_Aura;
elseif (w == "aura") then
tiType = 2;
cfinit = Gnosis.Timers_Aura;
elseif (w == "itemcd") then
tiType = 3;
unit = "player";
cfinit = Gnosis.Timers_ItemCD;
elseif (w == "runecd") then
unit = "player";
if (tonumber(spell) and tonumber(spell) > 0 and tonumber(spell) <= 6) then
tiType = 4;
cfinit = Gnosis.Timers_RuneCD;
end
elseif (w == "totemdur") then
unit = "player";
if(tonumber(spell) and tonumber(spell) > 0 and tonumber(spell) <= MAX_TOTEMS) then
tiType = 5;
cfinit = Gnosis.Timers_TotemDuration;
end
elseif (w == "enchmh") then
tiType = 6;
unit = "player";
cfinit = Gnosis.Timers_WeaponEnchantMain;
elseif (w == "enchoh") then
tiType = 7;
unit = "player";
cfinit = Gnosis.Timers_WeaponEnchantOff;
elseif (w == "icd" or w == "innercd" or w == "proc") then
-- valid spell or spell id given? (name of spell passed for icd does not
-- necessarily have to be a valid spell)
local spell_, _, icon_ = GetSpellInfo(spell);
if (spell_) then
spell = spell_;
icon__ = icon_;
end
if (spell) then
tiType = 8;
cfinit = Gnosis.Timers_InnerCD;
-- staticdur given? otherwise set duration to 5s
self.ti_icd[spell] = {
duration = staticdur or 5.0,
norefresh = false
};
end
unit = "player";
elseif (w == "recharge") then
local spell_, _, icon_ = GetSpellInfo(spell);
if (spell_) then
spell = spell_;
icon__ = icon_;
end
if (spell) then
tiType = 9;
cfinit = Gnosis.Timers_SpellRecharge;
end
unit = "player";
elseif (w == "fixed") then
tiType = 10;
unit = "player";
cfinit = Gnosis.Timers_Fixed;
elseif (w == "spellknown") then
tiType = 11;
unit = "player";
cfinit = Gnosis.Timers_SpellKnown;
elseif (w == "unitname") then
tiType = 12;
cfinit = Gnosis.Timers_UnitName;
elseif (w == "gcd") then
tiType = 13;
unit = "player";
cfinit = Gnosis.Timers_GlobalCD;
elseif (w == "equipped") then
tiType = 14;
unit = "player";
cfinit = Gnosis.Timers_ItemEquipped;
elseif (w == "npc") then
tiType = 15;
cfinit = Gnosis.Timers_Npc;
elseif (w == "charspec") then
tiType = 16;
unit = "player";
cfinit = Gnosis.Timers_Charspec;
elseif (w == "talent") then
tiType = 17;
unit = "player";
cfinit = Gnosis.Timers_Talent;
elseif (w == "glyph") then
tiType = 18;
unit = "player";
cfinit = Gnosis.Timers_Glyph;
elseif (w == "groupdot" or w == "groupdebuff") then
bHarm = true;
tiType = 21;
cfinit = Gnosis.Timers_GroupAura;
elseif (w == "grouphot" or w == "groupbuff") then
bHelp = true;
tiType = 21;
cfinit = Gnosis.Timers_GroupAura;
elseif (w == "groupaura") then
tiType = 21;
cfinit = Gnosis.Timers_GroupAura;
elseif (w == "counter") then
tiType = 22;
cfinit = Gnosis.Timers_Counter;
elseif (w == "resource") then
if (spell == "power") then
tiType = 1000;
cfinit = Gnosis.Timers_Power;
elseif (spell == "health") then
tiType = 1001;
cfinit = Gnosis.Timers_Health;
elseif (spell == "altpower") then
tiType = 1002;
cfinit = Gnosis.Timers_PowerAlternate;
elseif (spell == "heal") then
tiType = 1003;
cfinit = Gnosis.Timers_IncomingHealth;
elseif (spell == "threat") then
tiType = 1004;
cfinit = Gnosis.Timers_TargetThreat;
elseif (spell == "range") then
tiType = 1006;
cfinit = Gnosis.Timers_Range;
elseif (spell == "combopoints") then
tiType = 2004;
cfinit = Gnosis.Timers_PowerGeneric;
elseif (spell == "soulshards") then
tiType = 2007;
cfinit = Gnosis.Timers_PowerGeneric;
elseif (spell == "eclipse") then
tiType = 2008;
cfinit = Gnosis.Timers_PowerGeneric;
elseif (spell == "holypower") then
tiType = 2009;
cfinit = Gnosis.Timers_PowerGeneric;
elseif (spell == "chi") then
tiType = 2012;
cfinit = Gnosis.Timers_PowerGeneric;
elseif (spell == "shadoworbs") then
tiType = 2013;
cfinit = Gnosis.Timers_PowerGeneric;
elseif (spell == "burningembers") then
tiType = 2014;
cfinit = Gnosis.Timers_PowerGeneric;
elseif (spell == "burningembers_decimals") then
tiType = 2014;
cfinit = Gnosis.Timers_PowerGeneric;
resource_decimals = true;
elseif (spell == "demonicfury") then
tiType = 2015;
cfinit = Gnosis.Timers_PowerGeneric;
elseif (spell == "xp" or spell == "experience") then
tiType = 2016;
cfinit = Gnosis.Timers_Experience;
elseif (spell == "rested" or spell == "restedxp") then
tiType = 2017;
cfinit = Gnosis.Timers_RestedXP;
end
elseif (w == "mine") then
bSelf = true;
elseif (w == "helpful" or w == "help") then
bHelp = true;
elseif (w == "harmful" or w == "harm") then
bHarm = true;
elseif (w == "lag") then
bShowLag = true;
elseif (w == "casttime") then
bShowCasttime = true;
elseif (w == "exists") then
bExists = true;
elseif (w == "not") then
bNot = true;
elseif (w == "hidespark" or w == "nospark") then
bHideSpark = true;
elseif (w == "hideicon" or w == "noicon") then
bHideIcon = true;
elseif (w == "and") then
if (boolop == BOOLOP_NONE) then
boolop = BOOLOP_AND;
end
elseif (w == "or") then
if (boolop == BOOLOP_NONE) then
boolop = BOOLOP_OR;
end
elseif (w == "sort") then
if (spell == "minrem") then
iSort = 1;
elseif (spell == "maxrem") then
iSort = 2;
elseif (spell == "mindur") then
iSort = 3;
elseif (spell == "maxdur") then
iSort = 4;
elseif (spell == "first") then
iSort = 5;
end
elseif (w == "norefresh") then
norefresh = true;
elseif (w == "chargecnt") then
chargecnt = true;
end
end
end
local strFilter = "";
strFilter = strFilter .. (bSelf and "PLAYER" or "");
strFilter = strFilter .. (bHarm and (string_len(strFilter) > 0 and "|HARMFUL" or "HARMFUL") or "");
strFilter = strFilter .. (bHelp and (string_len(strFilter) > 0 and "|HELPFUL" or "HELPFUL") or "");
if (tiType) then
local tTimer = {
type = tiType,
spectab = spectab,
filter = strFilter,
spell = spell,
showlag = bShowLag,
showcasttime = bShowCasttime,
nfs = nfs,
tfs = tfs,
recast = recast,
staticdur = staticdur,
zoom = zoom,
bExists = bExists,
bNot = bNot,
cfinit = cfinit,
bcolor = tbcol,
sbcolor = tsbcol,
cbs = not bHideSpark and conf.bShowCBS or false,
hideicon = bHideIcon,
id = timer_id,
brange = brange,
range_tab = range_tab,
boolop = boolop,
icon = icon__,
icov = iconoverride,
ptun = portraitunit,
shown = shown,
hidden = hidden,
playinterval = playinterval,
fplay = fplay,
tplay = tplay,
toplay = toplay,
mcnt = mcnt,
msize = msize,
aurastacks = aurastacks,
auraeffect1 = auraeffect1,
auraeffect2 = auraeffect2,
auraeffect3 = auraeffect3,
countstart = countstart,
countinterval = countinterval,
countcpy = countcpy,
countstop = countstop,
runetype = runetype,
resource_decimals = resource_decimals,
chargecnt = chargecnt,
spellid = spellid,
};
-- targeted unit
tTimer.unit = unit and unit or conf.unit;
-- get name and icon if cast/aura and passed as spellid
if ((tiType <= 2 or tiType == 10 or tiType == 11 or tiType == 21) and tonumber(spell)) then
local name_, _, icon_, _, _, _, spellid_ = GetSpellInfo(tonumber(spell));
if(name_ and icon_) then
tTimer.spell = name_;
tTimer.icon = icon_;
tTimer.spellid = spellid_;
end
end
-- if itemcd try to get item id and texture
if (tiType == 3 or tiType == 14) then
local itemname, link, _, _, _, _, _, _, _, itex = GetItemInfo(spell);
if(link) then
tTimer.iid = string.match(link, "|Hitem:(%d+):");
tTimer.itex = itex;
tTimer.iname = itemname;
end
end
-- inner cooldown/proc (norefresh command)
if (tiType == 8) then
self.ti_icd[spell].norefresh = norefresh;
end
-- special handling for auras with
-- aurastacks/auraeffect commands
if (tiType == 2 or tiType == 21) then
if (aurastacks) then
tTimer.type = tiType + 5000;
elseif (auraeffect1 or auraeffect2 or auraeffect3) then
tTimer.type = tiType + 5001;
end
end
-- special handling for recharge command with
-- the chargecnt option
if (tiType == 9 and chargecnt) then
tTimer.type = 5001;
end
-- do not check if unit exists for unitname/npc command
if (not(tiType == 12 or tiType == 15 or tiType == -1)) then
tTimer.unitexistscheck = true;
end
-- insert entry
table_insert(value.timers, tTimer);
elseif (iSort) then
-- sorting criterion
value.iTimerSort = iSort;
end
end
if (#value.timers > 0) then
table_insert(self.ti_fl, value);
end
end
end
end
function Gnosis:InjectTimer(barname, text, cnt, spell, isCast)
local fCurTime = GetTime() * 1000;
if (self.castbars and self.castbars[barname]) then
local cb = self.castbars[barname];
local cfg = cb.conf;
-- castbar values
cb.channel = not isCast;
cb.icon:SetTexture(nil);
cb.id = 0;
if (spell) then
local name, _, icon = GetSpellInfo(spell);
if (name and icon) then
cb.castname = name;
cb.icon:SetTexture(icon);
end
end
-- show castbar text
cb.ctext:SetText(text);
cb.castname = nil;
cb.duration = cnt * 1000;
cb.endTime = fCurTime + cb.duration;
-- set statusbar value
local val = (cb.endTime - fCurTime) / (cb.duration);
val = (cb.channel and (not cfg.bChanAsNorm)) and val or (1 - val);
cb.bar:SetValue(val);
cb:SetAlpha(cfg.alpha);
cb:Show();
-- castbar spark
if(cfg.bShowCBS) then
cb.cbs:SetPoint("CENTER", cb.bar, "LEFT", val * cb.barwidth, 0);
cb.cbs:Show();
end
-- pushback (also vital for clipping test)
cb.pushback = 0;
-- set bar active
cb.bActive = true;
self.activebars[barname] = cb;
else
self:Print("bar " .. barname .. " unknown");
end
end
function Gnosis:CheckCounter(v, ti)
-- stop counter
if (v.countstop) then
if (self.counters) then
self.counters[v.countstop] = nil;
end
end
-- start counter if it's not already running
if (v.countinterval) then
if (self.counters[v.countstart] == nil) then
self.counters[v.countstart] = { starttime = GetTime(), endtime = GetTime() + v.countinterval };
elseif (self.counters[v.countstart].endtime < GetTime()) then
self.counters[v.countstart].starttime = GetTime();
self.counters[v.countstart].endtime = self.counters[v.countstart].starttime + v.countinterval;
end
end
-- start counter (copying timer duration)
if (v.countcpy and ti) then
if (self.counters[v.countcpy] == nil) then
self.counters[v.countcpy] = { starttime = (ti.fin - ti.dur) / 1000, endtime = ti.fin / 1000 };
elseif (self.counters[v.countcpy].endtime < GetTime()) then
self.counters[v.countcpy].starttime = (ti.fin - ti.dur) / 1000;
self.counters[v.countcpy].endtime = ti.fin / 1000;
end
end
end
function Gnosis:ScanTimerbar(bar, fCurTime)
local bUpdateText = false;
local bDelayedShow = false;
-- hide bar in/out of combat
if (bar.conf.incombatsel == 1 or bar.conf.incombatsel == self.curincombattype or bar.conf.bUnlocked) then
if (bar.bBarHidden) then
bDelayedShow = true;
end
else
if (not bar.bBarHidden) then
bar:Hide();
bar.bBarHidden = true;
end
return;
end
-- valid group layout? valid instance type?
if (not self:CheckGroupLayout(bar.conf) or not self:CheckInstanceType(bar.conf)) then
if (not bar.bBarHidden) then
bar:Hide();
bar.bBarHidden = true;
end
return;
end
local relaxed_and = nil;
local boolop_complete = false;
SelectedTimerInfo.duration = nil;
for k, v in ipairs(bar.timers) do
if (boolop_complete) then
-- unmark relaxed and request
relaxed_and = nil;
-- search for first timer entry without boolop
if (v.boolop == BOOLOP_NONE) then
boolop_complete = false;
end
else
-- compute command?
local checkentry = true;
-- check current spec
if (v.spectab and v.spectab[self.iCurSpec] == false) then
checkentry = false;
else
-- selected unit exists?
if (v.unitexistscheck and (not UnitExists(v.unit))) then
checkentry = false;
else
-- "shown" command? is given bar actually shown?
if (v.shown and Gnosis.castbars[v.shown] and not Gnosis.castbars[v.shown].bActive) then
checkentry = false;
else
-- "hidden" command? is given bar actually hidden?
if (v.hidden and Gnosis.castbars[v.hidden] and Gnosis.castbars[v.hidden].bActive) then
checkentry = false;
end
end
end
end
-- check entry
if (checkentry) then
wipe(TimerInfo);
-- call related timer function (Timers.lua)
v:cfinit(bar, v, TimerInfo);
-- related timer info valid
if (TimerInfo.ok and self:UnitRelationSelect(bar.conf.relationsel, TimerInfo.unit)) then
-- start or stop counter?
self:CheckCounter(v, TimerInfo);
-- boolop?
if (v.boolop == BOOLOP_RELAXEDAND) then
-- '?' (relaxed and, only one match necessary)
relaxed_and = 2; -- store as valid
elseif (v.boolop == BOOLOP_AND) then
-- timer is condition for next one(s), next please
elseif (relaxed_and == 1) then
-- relaxed and requested but no match found
relaxed_and = nil;
if (v.boolop == BOOLOP_OR) then
boolop_complete = true;
end
else
-- exit?
if (v.type == -1) then
-- play sound/music/file
self:PlayBarAudio(v, bar.name);
-- exit, do not compute further
break;
end
if (v.boolop == BOOLOP_OR) then
boolop_complete = true;
end
-- check if cooldown is gcd
local bTakeover = false;
if (TimerInfo.bSpecial) then
bTakeover = true;
SelectedTimerInfo.bSpecial = TimerInfo.bSpecial;
SelectedTimerInfo.valIsStatic = TimerInfo.valIsStatic;
else
-- sorting
SelectedTimerInfo.bSpecial = false;
if (not bar.iTimerSort or not SelectedTimerInfo.duration) then
bTakeover = true;
elseif (bar.iTimerSort == 1 and SelectedTimerInfo.endTime > TimerInfo.fin) then -- min remaining
bTakeover = true;
elseif (bar.iTimerSort == 2 and SelectedTimerInfo.endTime < TimerInfo.fin) then -- max remaining
bTakeover = true;
elseif (bar.iTimerSort == 3 and SelectedTimerInfo.duration > TimerInfo.dur) then -- min duration
bTakeover = true;
elseif (bar.iTimerSort == 4 and SelectedTimerInfo.duration < TimerInfo.dur) then -- max duration
bTakeover = true;
end
end
if (bTakeover) then
SelectedTimerInfo.castname = TimerInfo.cname;
SelectedTimerInfo.endTime = TimerInfo.fin;
SelectedTimerInfo.duration = TimerInfo.dur;
SelectedTimerInfo.icon = TimerInfo.icon;
SelectedTimerInfo.stacks = TimerInfo.stacks;
SelectedTimerInfo.effect = TimerInfo.effect;
SelectedTimerInfo.tiunit = TimerInfo.unit;
SelectedTimerInfo.bChannel = TimerInfo.bChannel;
SelectedTimerInfo.notInterruptible = TimerInfo.notInterruptible;
SelectedTimerInfo.curtimer = v;
end
if (SelectedTimerInfo.bSpecial or not bar.iTimerSort) then
-- break if no sorting criterion given or if bar was durationless,
-- i.e. it couldn't be sorted anyway
break;
end
end
elseif (v.boolop == BOOLOP_RELAXEDAND) then
-- '?' (relaxed and, only one match necessary)
relaxed_and = relaxed_and or 1; -- relaxed ok requested
elseif (v.boolop == BOOLOP_AND) then
-- "and"/'&' but invalid entry, skip to next combined "and"/"or" block
boolop_complete = true;
end
elseif (v.boolop == BOOLOP_RELAXEDAND) then
-- '?' (relaxed and, only one match necessary)
relaxed_and = 1; -- relaxed ok requested
elseif (v.boolop == BOOLOP_AND) then
-- "and"/'&' but invalid entry, skip to next combined "and"/"or" block
boolop_complete = true;
else
relaxed_and = nil;
end
end
end
if (SelectedTimerInfo.duration) then
if (bDelayedShow) then
bar.bBarHidden = nil;
bar:Show();
end
-- play sound/music/file
self:PlayBarAudio(SelectedTimerInfo.curtimer, bar.name);
-- only minor changes to bar necessary?
if (bar.bActive and bar.timer_id == SelectedTimerInfo.curtimer.id and
bar.castname == SelectedTimerInfo.castname and bar.notInterruptible == SelectedTimerInfo.notInterruptible) then
local dur = bar.dur and bar.dur or bar.duration;
local bRecalcTick = (dur ~= SelectedTimerInfo.duration);
-- redo name text
-- stacks; effect value and name of targeted unit (added in 4.01)
if (bar.stacks ~= SelectedTimerInfo.stacks or bar.effect ~= SelectedTimerInfo.effect or SelectedTimerInfo.tiunit ~= bar.tiUnit or bar.tiUnitName ~= UnitName(bar.tiUnit)) then
bar.stacks = SelectedTimerInfo.stacks;
bar.effect = SelectedTimerInfo.effect;
bar.tiUnit = SelectedTimerInfo.tiunit;
bar.tiUnitName = UnitName(bar.tiUnit);
bar.ctext:SetText(self:CreateCastname(bar, bar.conf, SelectedTimerInfo.castname, ""));
end
if (SelectedTimerInfo.bSpecial) then
if (not SelectedTimerInfo.valIsStatic) then
-- power
self:SetPowerbarValue(bar, SelectedTimerInfo.endTime, SelectedTimerInfo.duration, SelectedTimerInfo.curtimer.cbs);
if (SelectedTimerInfo.curtimer.mcnt) then
self:SetPowerbarValueMarkers(bar, SelectedTimerInfo.endTime,
SelectedTimerInfo.duration, SelectedTimerInfo.curtimer.mcnt,
SelectedTimerInfo.curtimer.msize);
end
end
return;
end
-- zoom?
local bZoom = SelectedTimerInfo.curtimer.zoom and (SelectedTimerInfo.curtimer.zoom >= (SelectedTimerInfo.endTime - fCurTime));
-- staticdur?
local bStatic = SelectedTimerInfo.curtimer.staticdur and true;
bar.dur = (bStatic or bZoom) and SelectedTimerInfo.duration or nil;
bar.duration = bZoom and SelectedTimerInfo.curtimer.zoom or (bStatic and SelectedTimerInfo.curtimer.staticdur or SelectedTimerInfo.duration);
bar.endTime = SelectedTimerInfo.endTime;
if (bar.cbs_check) then
local bShowCBS = bar.duration >= (bar.endTime - fCurTime);
if (bShowCBS) then
if (bar.cbs_hidden) then
bar.cbs:Show();
bar.cbs_hidden = false;
end
else
if (not bar.cbs_hidden) then
bar.cbs:Hide();
bar.cbs_hidden = true;
end
end
end
self:SetupTimerLagBox(bar, SelectedTimerInfo.curtimer.showlag,
SelectedTimerInfo.curtimer.showcasttime, SelectedTimerInfo.castname,
SelectedTimerInfo.curtimer.recast, bRecalcTick);
else -- create bar
-- id
bar.timer_id = SelectedTimerInfo.curtimer.id;
-- name and time format strings
bar.nfs = SelectedTimerInfo.curtimer.nfs and SelectedTimerInfo.curtimer.nfs or bar.conf.strNameFormat;
bar.tfs = SelectedTimerInfo.curtimer.tfs and SelectedTimerInfo.curtimer.tfs or bar.conf.strTimeFormat;
-- not interruptible status for 'cast' command
bar.notInterruptible = SelectedTimerInfo.notInterruptible;
if (SelectedTimerInfo.bSpecial) then
bar.bSpecial = true;
self:SetupPowerbar(bar, SelectedTimerInfo);
if (SelectedTimerInfo.curtimer.mcnt) then
self:SetPowerbarValueMarkers(bar, SelectedTimerInfo.endTime,
SelectedTimerInfo.duration, SelectedTimerInfo.curtimer.mcnt,
SelectedTimerInfo.curtimer.msize);
end
else
bar.bSpecial = false;
self:SetupTimerbar(bar, fCurTime, SelectedTimerInfo);
end
end
elseif (self.activebars[bar.name] or bar.forcecleanup) then
local conf = bar.conf;
-- bar active, fadeout or cleanup
if (conf.bUnlocked or conf.bShowWNC or bDelayedShow) then
self:CleanupCastbar(bar);
bar.forcecleanup = false;
else
self:PrepareCastbarForFadeout(bar, fCurTime, bar.forcecleanup);
bar.forcecleanup = false;
end
end
end
function Gnosis:PlayBarAudio(curtimer, barname)
local playinterval = curtimer.playinterval;
if (playinterval) then
local fplay = curtimer.fplay;
local tplay = curtimer.tplay;
local toplay = curtimer.toplay;
if (not tplay[barname]) then
tplay[barname] = {};
end
local tp = tplay[barname];
if ((not tp[toplay]) or tp[toplay].timer <= GetTime()) then
if (tp[toplay] and tp[toplay].handle) then
StopSound(tp[toplay].handle);
end
local willPlay, handle = fplay(toplay, self.s.ct.channel and self.tSoundChannels[self.s.ct.channel] or self.tSoundChannels[1]);
if (willPlay) then
if (tp[toplay]) then
tp[toplay].handle = handle;
tp[toplay].timer = GetTime() + playinterval;
else
tp[toplay] = {
["handle"] = handle,
["timer"] = GetTime() + playinterval,
};
end
else
if (tp[toplay]) then
wipe(tp[toplay]);
end
tp[toplay] = nil;
end
end
end
end