--[[ Created by Mayron - Twisting Nether (EU) ]]--
--[[ USER OPTIONS BELOW ]]--
----------------------------
local options = {
["Bar Texture"] = "Interface\\AddOns\\EarthShieldTracker\\EST_StatusBarTexture.tga",
["Gap Between Bars"] = 2,
["Bar Height"] = 20,
["Window Width"] = 210,
["Caster Text Color"] = { 1.0, 1.0, 1.0 }, -- red, green, blue (0.0 - 1.0 )
}
--[[ END OF USER OPTIONS ]]--
-----------------------------
local ES = GetSpellInfo(974); -- "Earth Shield"
local tracking = {} -- Holds the Tracking info
-- Setup Display Window:
local window = CreateFrame("Frame", "EST_Window", UIParent, "HelpPlateBox");
window:SetWidth(options["Window Width"]);
window:SetFrameStrata("HIGH");
if (not EST_DB) then
window:SetPoint("CENTER", UIParent, "CENTER");
else
window:SetPoint(EST_DB[1], UIParent, EST_DB[3], EST_DB[4], EST_DB[5]);
end
for i = 1, #window.Textures do
window.Textures[i]:SetVertexColor( 0, 0, 0 );
end
-- Make Movable:
window:SetMovable(true);
window:EnableMouse(true)
window:SetClampedToScreen(true);
window:RegisterForDrag("LeftButton")
window:SetScript("OnDragStart", function(self)
self:StartMoving();
end)
window:SetScript("OnDragStop", function(self)
self:StopMovingOrSizing();
EST_DB = { self:GetPoint("CENTER") };
end)
-- Setup Font Strings for Window:
window.dest = window:CreateFontString(nil, "ARTWORK", "GameFontHighlight");
window.dest:SetPoint("TOPLEFT", window, "TOPLEFT", 5, -4)
window.dest:SetJustifyH("LEFT");
window.dest:SetText("Target (stacks)");
window.source = window:CreateFontString(nil, "ARTWORK", "GameFontHighlight");
window.source:SetPoint("TOPRIGHT", window, "TOPRIGHT", -5, -5)
window.source:SetJustifyH("RIGHT");
window.source:SetText("Caster");
local popBar, pushBar; -- "static" functions
do
local stack = {};
local activeBars = 0;
local createdBars = 0;
function popBar(tracker) -- Returns a bar to be used.
--[[if (tracker.bar and tracking[tracker.sourceName]) then
return -- This unfortunately did not stop bars from being duplicated
end]]
local bar;
activeBars = activeBars + 1;
for id, b in next, stack do
if (b and b.id) then
bar = b;
stack[bar.id] = nil;
break;
end
end
if (not bar) then
-- if no bars have been created yet then create a new one
createdBars = createdBars + 1;
bar = CreateFrame("StatusBar", "EST_Bar"..createdBars, EST_Window, "TrackingBarTemplate");
bar.id = createdBars;
bar:SetMinMaxValues(0, 9);
bar.barFlash:Hide()
bar.border:Hide();
bar.barSpark:Hide();
-- User preferences:
bar.source:SetTextColor(options["Caster Text Color"][1], options["Caster Text Color"][2], options["Caster Text Color"][3]);
end
if (bar.id == 1) then
bar:SetPoint("BOTTOMLEFT", EST_Window, "BOTTOMLEFT", 2, 2);
bar:SetPoint("BOTTOMRIGHT", EST_Window, "BOTTOMRIGHT", -2, 2);
else
bar:SetPoint("BOTTOMLEFT", _G["EST_Bar"..(bar.id - 1)], "TOPLEFT", 0, options["Gap Between Bars"]);
bar:SetPoint("BOTTOMRIGHT", _G["EST_Bar"..(bar.id - 1)], "TOPRIGHT", 0, options["Gap Between Bars"]);
end
window:SetHeight(2 + (activeBars * (options["Bar Height"] + options["Gap Between Bars"])) + 20);
return bar;
end
function pushBar(bar)
activeBars = activeBars - 1;
stack[bar.id] = bar;
window:SetHeight(2 + (activeBars * (options["Bar Height"] + options["Gap Between Bars"])) + 20);
end
end
--[[
When a bar is removed, bars ontop of it should be moved down by removing the tracker from the bar
and readding the tracker object to the bar below rather than using SetPoint to move the bars position in the list.
--]]
local reordering;
local function ReorderTrackers(removedBarID)
reordering = true;
if (not next(tracking)) then
window:Hide();
reordering = nil;
return;
end
local newOrder = {};
for sourceName, tracker in pairs(tracking) do
if (tracker.bar) then -- only remove ones above! --(tracker.bar.id > removedBarID)
--if (tracker.bar.id and (tracker.bar.id > removedBarID)) then -- this did not stop the problem either
tracker:RemoveBar(); -- remove all active bars
table.insert(newOrder, tracker);
--end
else
tracking[sourceName] = nil; -- Should be removed
end
end
for id, tracker in ipairs(newOrder) do
tracker.bar = popBar(tracker);
tracker:SetTrackingInfo();
end
reordering = nil;
end
-- Creating Trackers:
local trackerPrototype = {} -- metatable for Tracker objects to inherit
trackerPrototype.__index = trackerPrototype;
--[[
Removes the Bar from the tracker object and pushes it back onto the stack for later use
--]]
function trackerPrototype:RemoveBar()
-- reset bar:
if (self.bar) then
self.bar:Hide();
self.bar:SetStatusBarColor(0, 0, 0, 0);
self.bar.dest:SetText("");
self.bar.source:SetText("");
self.bar:SetValue(0);
pushBar(self.bar);
self.bar = nil;
end
end
--[[
Checks all Raid members for ES and returns a table of found source/dest pairs
--]]
local function ScanRaid()
local prefix = (IsInRaid() and "raid") or (IsInGroup() and "party");
local tbl = {};
-- Scans to check if a group member has ES applied:
if (prefix and GetNumGroupMembers() > 0) then
for i = 1, GetNumGroupMembers() do
local hasES = UnitBuff(prefix..i, ES);
if (hasES) then
local destGUID = UnitGUID(prefix..i);
local sourceID = (select(8, UnitBuff(prefix..i, ES)));
if (sourceID) then
local sourceGUID = UnitGUID(sourceID); -- Gets the ES Caster (the Shaman)
tbl[sourceGUID] = destGUID;
end
end
end
-- for testing only:
--[[else
local hasES = UnitBuff("player", ES);
if (hasES) then
local destGUID = UnitGUID("player");
local sourceGUID = UnitGUID( (select(8, UnitBuff("player", ES))) ); -- Gets the ES Caster (the Shaman)
tbl[sourceGUID] = destGUID;
end]]
end
return tbl;
end
do
local function Tracker_OnUpdate(self)
if (self and tracking[self.sourceName]) then
self:SetTrackingInfo();
if (IsInRaid() or IsInGroup()) then
C_Timer.After(2, function() Tracker_OnUpdate(self) end)
end
end
end
--[[
Returns the UnitID of the destination player (the player who has ES on them) within the group
--]]
function trackerPrototype:GetUnitID(destName)
-- If the unitID has not changed then there is no need to recalculate it!
if (self.destUnitID) then
local foundName = UnitName(self.destUnitID);
if (foundName and foundName == destName) then
return self.destUnitID;
end
end
if (destName == UnitName("player")) then
return "player";
end
local prefix = (IsInRaid() and "raid") or (IsInGroup() and "party");
if (GetNumGroupMembers() > 0) then
for i = 1, GetNumGroupMembers() do
local foundName = UnitName(prefix..i);
if (foundName and foundName == destName) then
return (prefix..i);
end
end
end
end
--[[
Updates the destGUID or remove tracker if not found.
--]]
function trackerPrototype:GetNewDest()
local foundPairs = ScanRaid();
if (foundPairs[self.sourceGUID]) then -- source Shaman has ES on new target
self.destGUID = foundPairs[self.sourceGUID];
_, self.destClass, _, _, _, self.destName, _ = GetPlayerInfoByGUID(self.destGUID);
local destUnitID = self:GetUnitID(self.destName);
_, _, _, self.stacks = UnitBuff(destUnitID, ES);
return true;
else
if (self.bar) then
local removedBarID = self.bar.id;
self:RemoveBar();
tracking[self.sourceName] = nil;
ReorderTrackers(removedBarID);
end
return false;
end
end
--[[
Refreshes/Generates the details required and attaches a StatusBar to represent the
details of the tracker objects and sets up the bar to then be displayed on the window.
--]]
function trackerPrototype:SetTrackingInfo(firstTime)
if (reordering) then return end
_, self.sourceClass, _, _, _, self.sourceName, _ = GetPlayerInfoByGUID(self.sourceGUID);
_, self.destClass, _, _, _, self.destName, _ = GetPlayerInfoByGUID(self.destGUID);
local destUnitID = self:GetUnitID(self.destName);
local stacks, sourceUnitID;
if (destUnitID) then
_, _, _, stacks, _, _, _, sourceUnitID = UnitBuff(destUnitID, ES);
end
if (stacks and sourceUnitID) then -- if ES found
local sourceGUID = UnitGUID(sourceUnitID);
if (self.sourceGUID == sourceGUID) then -- if same shaman applied it
self.stacks = stacks;
else -- different shaman has ES on same dest
local successful = self:GetNewDest();
if (not successful) then return end
end
else
local successful = self:GetNewDest();
if (not successful) then return end
end
-- continue once stacks, dest info and sourc info is correct:
if (not self.bar) then
self.bar = popBar(self);
-- User Appearance Preferences:
self.bar:SetStatusBarTexture(options["Bar Texture"]);
self.bar:SetHeight(options["Bar Height"]);
end
local c = (CUSTOM_CLASS_COLORS or RAID_CLASS_COLORS)[self.destClass];
self.bar:SetStatusBarColor(c.r, c.g, c.b, 1);
self.bar.dest:SetText( (self.destName)..' ('..(self.stacks)..')' );
self.bar.source:SetText(self.sourceName);
self.bar:SetValue(self.stacks);
self.bar:Show();
window:Show();
if (firstTime) then
C_Timer.After(2, function() Tracker_OnUpdate(self) end)
end
end
end
--[[
Constructor: Constructs the Tracker Object which inherits trackerPrototype functions.
Fills out details of Tracker into the tracking table.
--]]
local function CreateTracker(sourceGUID, destGUID)
local sourceName = select(6, GetPlayerInfoByGUID(sourceGUID));
if (not sourceName) then return end -- source/caster is not a player!
local destName = select(6, GetPlayerInfoByGUID(destGUID));
if (not destName) then return end -- destination/target is not a player!
-- If found already in the tracking table then the Shaman has applied ES onto a new target
local skip;
if (not tracking[sourceName]) then
tracking[sourceName] = setmetatable({}, trackerPrototype);
else
skip = true;
end
local self = tracking[sourceName];
self.sourceGUID = self.sourceGUID or sourceGUID;
self.destGUID = destGUID;
if (not skip) then self:SetTrackingInfo(true); end
end
--[[
Scripts and Event:
--]]
local function Window_OnUpdate(self)
local tbl = ScanRaid();
for sourceGUID, destGUID in pairs(tbl) do
CreateTracker(sourceGUID, destGUID);
end
if ( IsInRaid() or IsInGroup() ) then
C_Timer.After(1, function() Window_OnUpdate(self) end);
end
end
window:SetScript("OnEvent", function(self, event, _, subevent, _, sourceGUID, sourceName, _, _, destGUID, destName, _, _, ...)
if ( event == "COMBAT_LOG_EVENT_UNFILTERED" ) then
if (subevent == "SPELL_AURA_REFRESH" or subevent == "SPELL_AURA_APPLIED") then
if ( not ( UnitInRaid(sourceName) or UnitInParty(sourceName) ) ) then return end
if ( not ( UnitInRaid(destName) or UnitInParty(destName) ) ) then return end
local spellName = GetSpellInfo((...));
if (spellName == ES) then
CreateTracker(sourceGUID, destGUID);
end
elseif(subevent == "SPELL_AURA_REMOVED") then
if (tracking[sourceName]) then
tracking[sourceName]:SetTrackingInfo();
end
end
elseif ( IsInRaid() or IsInGroup() ) then
Window_OnUpdate(window);
else
window:Hide();
end
end)
window:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED");
window:RegisterEvent("GROUP_ROSTER_UPDATE");
window:RegisterEvent("GROUP_JOINED");