Files
coa-decursive/Decursive/Dcr_DebuffsFrame.lua
Andrew6810 6988cc52f5 init
2022-10-21 06:52:48 -07:00

1655 lines
63 KiB
Lua

--[[
This file is part of Decursive.
Decursive (v 2.5.1-6-gd3885c5) add-on for World of Warcraft UI
Copyright (C) 2006-2007-2008-2009 John Wellesz (archarodim AT teaser.fr) ( http://www.2072productions.com/to/decursive.php )
Starting from 2009-10-31 and until said otherwise by its author, Decursive
is no longer free software, all rights are reserved to its author (John Wellesz).
The only official and allowed distribution means are www.2072productions.com, www.wowace.com and curse.com.
To distribute Decursive through other means a special authorization is required.
Decursive is inspired from the original "Decursive v1.9.4" by Quu.
The original "Decursive 1.9.4" is in public domain ( www.quutar.com )
Decursive is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
--]]
-------------------------------------------------------------------------------
local addonName, T = ...;
-- big ugly scary fatal error message display function {{{
if not T._FatalError then
-- the beautiful error popup : {{{ -
StaticPopupDialogs["DECURSIVE_ERROR_FRAME"] = {
text = "|cFFFF0000Decursive Error:|r\n%s",
button1 = "OK",
OnAccept = function()
return false;
end,
timeout = 0,
whileDead = 1,
hideOnEscape = 1,
showAlert = 1,
}; -- }}}
T._FatalError = function (TheError) StaticPopup_Show ("DECURSIVE_ERROR_FRAME", TheError); end
end
-- }}}
if not T._LoadedFiles or not T._LoadedFiles["Dcr_lists.xml"] or not T._LoadedFiles["Dcr_lists.lua"] then -- XML are loaded even if LUA syntax errors exixts
if not DecursiveInstallCorrupted then T._FatalError("Decursive installation is corrupted! (Dcr_lists.xml or Dcr_lists.lua not loaded)"); end;
DecursiveInstallCorrupted = true;
return;
end
local D = T.Dcr;
local L = D.L;
local LC = D.LC;
local DC = DcrC;
local DS = DC.DS;
-- NS def
D.MicroUnitF = {};
-- create a shortcut
local MicroUnitF = D.MicroUnitF;
MicroUnitF.prototype = {};
MicroUnitF.metatable ={ __index = MicroUnitF.prototype };
function MicroUnitF:new(...)
local instance = setmetatable({}, self.metatable);
instance:init(...);
return instance;
end
-- since there are tens of thousands of globals defined at all times, lets use some locals!
local BOOKTYPE_PET = BOOKTYPE_PET;
local BOOKTYPE_SPELL = BOOKTYPE_SPELL;
-- Init object factory defaults
--MicroUnitF.ExistingPerID = {};
MicroUnitF.ExistingPerUNIT = {};
MicroUnitF.ExistingPerNum = {};
MicroUnitF.UnitToMUF = {};
MicroUnitF.Number = 0;
MicroUnitF.UnitShown = 0;
MicroUnitF.UnitsDebuffedInRange = 0;
MicroUnitF.DraggingHandle = false;
D.ForLLDebuffedUnitsNum = 0;
-- using power 2 values just to OR them but only CHARMED_STATUS is ORed (it's a C style bitfield)
local NORMAL = DC.NORMAL;
local ABSENT = DC.ABSENT;
local FAR = DC.FAR;
local STEALTHED = DC.STEALTHED;
local BLACKLISTED = DC.BLACKLISTED;
local AFFLICTED = DC.AFFLICTED;
local AFFLICTED_NIR = DC.AFFLICTED_NIR;
local CHARMED_STATUS = DC.CHARMED_STATUS;
local AFFLICTED_AND_CHARMED = DC.AFFLICTED_AND_CHARMED;
-- Those are the different colors used for the MUFs main texture
local MF_colors = { };
local unpack = _G.unpack;
local select = _G.select;
local pairs = _G.pairs;
local ipairs = _G.ipairs;
local GetTime = _G.GetTime;
local PlaySoundFile = _G.PlaySoundFile;
local IsControlKeyDown = _G.IsControlKeyDown;
local floor = _G.math.floor;
local table = _G.table;
local t_insert = _G.table.insert;
local str_format = _G.string.format;
local str_sub = _G.string.gsub;
local table = _G.table;
local string = _G.string;
local UnitExists = _G.UnitExists;
local UnitClass = _G.UnitClass;
local fmod = _G.math.fmod;
local UnitIsUnit = _G.UnitIsUnit;
local str_upper = _G.string.upper;
local InCombatLockdown = _G.InCombatLockdown;
local UnitAura = _G.UnitAura;
local GetRaidTargetIndex= _G.GetRaidTargetIndex;
DC.AvailableButtonsReadable = { -- {{{
["%s1"] = L["HLP_LEFTCLICK"], -- left mouse button
["%s2"] = L["HLP_RIGHTCLICK"], -- right mouse button
["%s3"] = L["HLP_MIDDLECLICK"], -- middle mouse button
["ctrl-%s1"] = L["CTRL"] .. "-" .. L["HLP_LEFTCLICK"],
["ctrl-%s2"] = L["CTRL"] .. "-" .. L["HLP_RIGHTCLICK"],
["ctrl-%s3"] = L["CTRL"] .. "-" .. L["HLP_MIDDLECLICK"],
["shift-%s1"] = L["SHIFT"] .. "-" .. L["HLP_LEFTCLICK"],
["shift-%s2"] = L["SHIFT"] .. "-" .. L["HLP_RIGHTCLICK"],
["shift-%s3"] = L["SHIFT"] .. "-" .. L["HLP_MIDDLECLICK"],
["alt-%s1"] = L["ALT"] .. "-" .. L["HLP_LEFTCLICK"],
["alt-%s2"] = L["ALT"] .. "-" .. L["HLP_RIGHTCLICK"],
["alt-%s3"] = L["ALT"] .. "-" .. L["HLP_MIDDLECLICK"],
-- 3, -- middle mouse button || RESERVED FOR TARGETTING
}; -- }}}
-- modifier for the macro
local AvailableModifier = { -- {{{
"shift","ctrl","alt",
} -- }}}
-- MicroUnitF STATIC methods {{{
-- Updates the color table
function MicroUnitF:RegisterMUFcolors ()
-- MF_colors = D.profile.MF_colors; -- this should be enough but is not because D.profile can change at unexpected times....
D:tcopy(MF_colors, D.profile.MF_colors);
end
-- defines what is printed when the object is read as a string
function MicroUnitF:ToString() -- {{{
return "Decursive Micro Unit Frame Object";
end -- }}}
-- The Factory for MicroUnitF objects
function MicroUnitF:Create(Unit, ID) -- {{{
if InCombatLockdown() then
-- if we are fighting, postpone the call
D:AddDelayedFunctionCall (
"Create"..Unit, self.Create,
Unit, ID);
return false;
end
-- if we attempt to create a MUF that already exists, update it instead
if (self.ExistingPerUNIT[Unit]) then
return self.ExistingPerUNIT[Unit];
end
self.Number = self.Number + 1;
-- create a new MUF object
self.ExistingPerUNIT[Unit] = self:new(D.MFContainer, Unit, self.Number, ID);
self.ExistingPerNum[self.Number] = self.ExistingPerUNIT[Unit];
return self.ExistingPerUNIT[Unit];
end -- }}}
-- return a MUF object if it exists, nil otherwise
--[=[
function MicroUnitF:Exists(IdOrNum) -- {{{
return self.ExistingPerID[IdOrNum] or self.ExistingPerNum[IdOrNum];
end
--]=]
-- }}}
-- return the number MUFs we can use
function MicroUnitF:MFUsableNumber () -- {{{
return ((self.MaxUnit > D.Status.UnitNum) and D.Status.UnitNum or self.MaxUnit);
end -- }}}
-- this is used when a setting influencing MUF's position is changed
function MicroUnitF:ResetAllPositions () -- {{{
if InCombatLockdown() then
D:AddDelayedFunctionCall (
"ResetAllPositions", self.ResetAllPositions,
self);
return false;
end
local MF, i;
D:Debug("Resetting all MF position");
self:Delayed_MFsDisplay_Update ();
local Unit_Array = D.Status.Unit_Array;
for i=1, #Unit_Array do
MF = self.ExistingPerUNIT[ Unit_Array[i] ]
if MF then
MF.Frame:SetPoint(unpack(self:GiveMFAnchor(i)));
end
end
end -- }}}
-- return the anchor of a given MUF depending on its creation ID
do
local Anchor = { "TOPLEFT", x, y, "TOPLEFT" };
function MicroUnitF:GiveMFAnchor (ID) -- {{{
local LineNum = floor( (ID - 1) / D.profile.DebuffsFramePerline);
local NumOnLine = fmod( (ID - 1), D.profile.DebuffsFramePerline);
local x = (D.profile.DebuffsFrameGrowToTop and DC.MFSIZE or 0) + NumOnLine * (DC.MFSIZE + D.profile.DebuffsFrameXSpacing);
local y = (D.profile.DebuffsFrameGrowToTop and -1 or 1) * LineNum * ((-1 * D.profile.DebuffsFrameYSpacing) - DC.MFSIZE);
Anchor[2] = x; Anchor[3] = y;
return Anchor;
end
end-- }}}
function MicroUnitF:Delayed_MFsDisplay_Update ()
if D.profile.ShowDebuffsFrame then
D:ScheduleDelayedCall("Dcr_UpdateMUFsNUM", self.MFsDisplay_Update, 1.5, self);
end
end
-- This update the MUFs display, show and hide MUFs as necessary
function MicroUnitF:MFsDisplay_Update () -- {{{
if (not D.profile.ShowDebuffsFrame) then
return false; -- XXX we will have to call this function again when we show the MUFs
end
-- This function cannot do anything if we are fighting
if InCombatLockdown() then
-- if we are fighting, postpone the call
D:AddDelayedFunctionCall (
"UpdateMicroUnitFrameDisplay", self.MFsDisplay_Update,
self);
return false;
end
-- Get an up to date unit array if necessary
D:GetUnitArray(); -- this is the only place where GetUnitArray() is called directly
-- =======
-- Begin
-- =======
-- get the number of MUFs we should display
local NumToShow = self:MFUsableNumber();
-- if we don't have all the MUFs needed then return, we are not ready
if (self.Number < NumToShow) then
self:Delayed_MFsDisplay_Update ();
return false;
end
local MF = false;
local i = 1;
local Old_UnitShown = self.UnitShown;
D:Debug("Update required: NumToShow = ", NumToShow);
local Unit_Array_UnitToGUID = D.Status.Unit_Array_UnitToGUID;
local Unit_Array = D.Status.Unit_Array;
-- Scan unit array in display order and show the maximum until NumToShow is reached
-- The ID is set for all MUFs present in our unit array
local Updated = 0;
for i, Unit in ipairs(Unit_Array) do
MF = self.ExistingPerUNIT[Unit];
if MF then
MF.ID = i;
if not MF.Shown and i <= NumToShow then -- we got this unit in our group but it's hidden
MF.Shown = true;
self.UnitShown = self.UnitShown + 1;
MF.ToPlace = true;
Updated = Updated + 1;
D:ScheduleDelayedCall("Dcr_Update"..MF.CurrUnit, MF.UpdateWithCS, D.profile.DebuffsFrameRefreshRate * Updated, MF);
--D:Debug("|cFF88AA00Show schedule for MUF", Unit, "UnitShown:", self.UnitShown);
end
else
--D:errln("showhide: no muf for", Unit); -- call delay display up
self:Delayed_MFsDisplay_Update ();
end
end
-- hide remaining units
if self.UnitShown > NumToShow then
for Unit, MF in pairs(self.ExistingPerUNIT) do -- see all the MUF we ever created and show or hide them if there corresponding unit exists
-- show/hide
if MF.Shown and (not Unit_Array_UnitToGUID[Unit] or MF.ID > NumToShow ) then -- we don't have this unit but its MUF is shown
-- clear debuff before hiding to avoid leaving 'ghosts' behind...
if D.UnitDebuffed[MF.CurrUnit] then
D.ForLLDebuffedUnitsNum = D.ForLLDebuffedUnitsNum - 1;
end
MF.Debuffs = false;
MF.IsDebuffed = false;
MF.Debuff1Prio = false;
MF.PrevDebuff1Prio = false;
D.UnitDebuffed[MF.CurrUnit] = false; -- used by the live-list only
D.Stealthed_Units[MF.CurrUnit] = false;
MF.Shown = false;
self.UnitShown = self.UnitShown - 1;
--D:Debug("|cFF88AA00Hiding %d (%s), scheduling update in %f|r", i, MF.CurrUnit, D.profile.DebuffsFrameRefreshRate * i);
Updated = Updated + 1;
D:ScheduleDelayedCall("Dcr_Update"..MF.CurrUnit, MF.Update, D.profile.DebuffsFrameRefreshRate * Updated, MF);
MF.Frame:Hide();
end
end
end
-- manage to get what we show in the screen
if self.UnitShown > 0 and Old_UnitShown ~= self.UnitShown then
MicroUnitF:Place();
end
return true;
end -- }}}
function MicroUnitF:Delayed_Force_FullUpdate ()
if (D.profile.ShowDebuffsFrame) then
D:ScheduleDelayedCall("Dcr_Force_FullUpdate", self.Force_FullUpdate, 0.3, self);
end
end
function MicroUnitF:Force_FullUpdate () -- {{{
if (not D.profile.ShowDebuffsFrame) then
return false;
end
-- This function cannot do anything if we are fighting
if InCombatLockdown() then
-- if we are fighting, postpone the call
D:AddDelayedFunctionCall (
"Force_FullUpdate", self.Force_FullUpdate,
self);
return false;
end
local MF;--, MF_f;
D.Status.SpellsChanged = GetTime(); -- will force an update of all MUFs attributes
local i = 1;
for Unit, MF in pairs(self.ExistingPerUNIT) do
if not MF.IsDebuffed then
MF.UnitStatus = 0; -- reset status to force SetColor to update
end
MF.ChronoFontString:SetTextColor(unpack(MF_colors["COLORCHRONOS"]));
D:ScheduleDelayedCall("Dcr_Update"..MF.CurrUnit, MF.UpdateWithCS, D.profile.DebuffsFrameRefreshRate * i, MF);
i = i + 1;
end
end -- }}}
-- Those set the scalling of the MUF container
-- SACALING FUNCTIONS (MicroUnitF Children) {{{
-- Place the MUFs container according to its scale
function MicroUnitF:Place () -- {{{
if self.UnitShown == 0 or self.DraggingHandle then return end
if InCombatLockdown() then
-- if we are fighting, postpone the call
D:AddDelayedFunctionCall (
"MicroUnitFPlace", self.Place,
self);
return;
end
local UIScale = UIParent:GetEffectiveScale()
local FrameScale = self.Frame:GetEffectiveScale();
local x, y = D.profile.DebuffsFrame_x, D.profile.DebuffsFrame_y;
-- If executed for the very first time, then put it in the top right corner of the screen
if (not x or not y) then
x = (UIParent:GetWidth() * UIScale) - (UIParent:GetWidth() * UIScale) / 4;
y = - (UIParent:GetHeight() * UIScale) / 5;
D.profile.DebuffsFrame_x = x;
D.profile.DebuffsFrame_y = y;
end
local FirstLineNum = 0;
local Handle_x_offset = 0;
local Handle_y_offset = 0;
-- get the number of max unit per line
if self.UnitShown >= D.profile.DebuffsFramePerline then FirstLineNum = D.profile.DebuffsFramePerline; else FirstLineNum = self.UnitShown; end
-- get the offset of the handle we need to apply in order to align the MUFs on the right
if D.profile.DebuffsFrameStickToRight then
Handle_x_offset = -1 * FrameScale * (FirstLineNum * (DC.MFSIZE + D.profile.DebuffsFrameXSpacing) - D.profile.DebuffsFrameXSpacing );
end
Handle_x_offset = Handle_x_offset - FrameScale * (D.profile.DebuffsFrameGrowToTop and DC.MFSIZE or 0);
Handle_y_offset = Handle_y_offset + FrameScale * (D.profile.DebuffsFrameGrowToTop and DC.MFSIZE or 0);
-- check the right edge so it can't be out of the screen
local RightEdge = x + FrameScale * (FirstLineNum * (DC.MFSIZE + D.profile.DebuffsFrameXSpacing) - D.profile.DebuffsFrameXSpacing + (D.profile.DebuffsFrameGrowToTop and DC.MFSIZE or 0)) + Handle_x_offset ;
if (RightEdge > UIParent:GetWidth() * UIScale) then
Handle_x_offset = Handle_x_offset - (RightEdge - UIParent:GetWidth() * UIScale);
D:Debug("put the MUFs on the screen!!! (Right edge out)");
end
-- check the left edge so it can't be out of the screen
local LeftEdge = x + Handle_x_offset;
if (LeftEdge < 0) then
Handle_x_offset = Handle_x_offset - LeftEdge;
D:Debug("put the MUFs on the screen!!! (Left edge out)");
end
-- check the bottom edge
local NumberOfLines = floor( (self.UnitShown - 1) / D.profile.DebuffsFramePerline) + 1;
--D:Debug(NumberOfLines);
local BottomEdge = y - FrameScale * ((D.profile.DebuffsFrameGrowToTop and 1 or NumberOfLines) * (DC.MFSIZE + D.profile.DebuffsFrameYSpacing) - D.profile.DebuffsFrameYSpacing) + Handle_y_offset;
if -BottomEdge > UIParent:GetHeight() * UIScale then
Handle_y_offset = Handle_y_offset - (BottomEdge + UIParent:GetHeight() * UIScale);
D:Debug("put the MUFs on the screen!!! (Bottom edge out)");
end
-- check the top edge
local TopEdge = y + FrameScale * (((D.profile.DebuffsFrameGrowToTop and NumberOfLines > 1) and NumberOfLines - 1 or 1) * (DC.MFSIZE + D.profile.DebuffsFrameYSpacing) - ((not D.profile.DebuffsFrameGrowToTop) and 1 or 0) * D.profile.DebuffsFrameYSpacing) + Handle_y_offset;
if (TopEdge > 0) then
Handle_y_offset = Handle_y_offset - TopEdge;
D:Debug("put the MUFs on the screen!!! (Top edge out)");
end
x = x + Handle_x_offset;
y = y + Handle_y_offset;
-- set to the scaled position
self.Frame:ClearAllPoints();
self.Frame:SetPoint("TOPLEFT", UIParent, "TOPLEFT", x/FrameScale , y/FrameScale);
D:Debug("MUF Window position set");
end -- }}}
-- Save the position of the frame without its scale
function MicroUnitF:SavePos () -- {{{
if self.UnitShown == 0 then return end
local FirstLineNum;
if self.Frame:IsVisible() then
-- We save the unscalled position (no problem if the sacale is changed behind our back)
D.profile.DebuffsFrame_x = self.Frame:GetEffectiveScale() * self.Frame:GetLeft();
D.profile.DebuffsFrame_y = self.Frame:GetEffectiveScale() * self.Frame:GetTop() - UIParent:GetHeight() * UIParent:GetEffectiveScale();
-- if we choosed to align the MUF to the right then we have to add the
-- width of the first line to get the original position of the handle
if D.profile.DebuffsFrameStickToRight then
if self.UnitShown >= D.profile.DebuffsFramePerline then
FirstLineNum = D.profile.DebuffsFramePerline;
else
FirstLineNum = self.UnitShown;
end
D.profile.DebuffsFrame_x = D.profile.DebuffsFrame_x + self.Frame:GetEffectiveScale() * (FirstLineNum * (DC.MFSIZE + D.profile.DebuffsFrameXSpacing) - D.profile.DebuffsFrameXSpacing);
end
D.profile.DebuffsFrame_x = D.profile.DebuffsFrame_x + self.Frame:GetEffectiveScale() * (D.profile.DebuffsFrameGrowToTop and DC.MFSIZE or 0);
D.profile.DebuffsFrame_y = D.profile.DebuffsFrame_y - self.Frame:GetEffectiveScale() * (D.profile.DebuffsFrameGrowToTop and DC.MFSIZE or 0);
-- D:Debug("Frame position saved");
end
end -- }}}
-- set the scaling of the MUFs container according to the user settings
function MicroUnitF:SetScale (NewScale) -- {{{
-- save the current position without any scaling
-- self:SavePos ();
-- Setting the new scale
self.Frame:SetScale(NewScale);
-- Place the frame adapting its position to the news cale
self:Place ();
end -- }}}
-- }}}
-- Update the MUF of a given unitid
function MicroUnitF:UpdateMUFUnit(Unitid, CheckStealth)
if not D.profile.ShowDebuffsFrame then
return;
end
local unit = false;
if (D.Status.Unit_Array_UnitToGUID[Unitid]) then
unit = Unitid;
else
D:Debug("Unit", Unitid, "not in raid or party!" );
return;
end
-- get the MUF object
local MF = self.UnitToMUF[unit];
if (MF and MF.Shown) then
-- The MUF will be updated only every DebuffsFrameRefreshRate seconds at most
-- but we don't miss any event XXX note this can be the cause of slowdown if 25 or 40 players got debuffed at the same instant, DebuffUpdateRequest is here to prevent that since 2008-02-17
if (not D:DelayedCallExixts("Dcr_Update"..unit)) then
D.DebuffUpdateRequest = D.DebuffUpdateRequest + 1;
D:ScheduleDelayedCall("Dcr_Update"..unit, CheckStealth and MF.UpdateWithCS or MF.Update, D.profile.DebuffsFrameRefreshRate * (1 + floor(D.DebuffUpdateRequest / (D.profile.DebuffsFramePerUPdate / 2))), MF);
D:Debug("Update scheduled for, ", unit, MF.ID);
return true; -- return value used to aknowledge that the function actually did something
end
else
D:Debug("No MUF found for ", unit, Unitid);
end
end
-- Event management functions
-- MUF EVENTS (MicroUnitF children) (OnEnter, OnLeave, OnLoad, OnPreClick) {{{
-- It's outside the function to avoid creating and discarding this table at each call
local DefaultTTAnchor = {"ANCHOR_TOPLEFT", 0, 6};
local UnitGUID = _G.UnitGUID;
local TooltipButtonsInfo = {}; -- help tooltip text table
local TooltipUpdate = 0; -- help tooltip change update check
-- This function is responsible for showing the tooltip when the mouse pointer is over a MUF
-- it also handles Unstable Affliction detection and warning.
function MicroUnitF:OnEnter(frame) -- {{{
D.Status.MouseOveringMUF = true;
local MF = frame.Object;
local Status;
local Unit = MF.CurrUnit; -- shortcut
local TooltipText = "";
local GUIDwasFixed = false;
local unitguid = UnitGUID(Unit);
if unitguid ~= D.Status.Unit_Array_UnitToGUID[Unit] or Unit ~= D.Status.Unit_Array_GUIDToUnit[unitguid] then
if unitguid then
D.Status.Unit_Array_UnitToGUID[Unit] = unitguid;
D.Status.Unit_Array_GUIDToUnit[unitguid] = Unit;
GUIDwasFixed = true;
end
end
MF:Update(false, false, true); -- will reset the color early and set the current status of the MUF
MF:SetClassBorder(); -- set the border if it wasn't possible at the time the unit was discovered
if not Unit then
return; -- If the user overs the MUF befor it's completely initialized
end
--Test for unstable affliction
if MF.Debuffs then
for i, Debuff in ipairs(MF.Debuffs) do
if Debuff.Type then
-- Create a warning if the Unstable Affliction is detected
if Debuff.Name == DS["Unstable Affliction"] then
--if Debuff.Name == "Malédiction de Stalvan" then -- to test easily
D:Println("|cFFFF0000 ==> %s !!|r (%s)", DS["Unstable Affliction"], D:MakePlayerName((D:PetUnitName( Unit, true ))));
PlaySoundFile("Sound\\Doodad\\G_NecropolisWound.wav");
end
end
end
end
if D.profile.AfflictionTooltips then
-- removes the CHARMED_STATUS bit from Status, we don't need it
Status = bit.band(MF.UnitStatus, bit.bnot(CHARMED_STATUS));
-- First, write the name of the unit in its class color
if UnitExists(MF.CurrUnit) then
TooltipText =
((DC.RAID_ICON_LIST[GetRaidTargetIndex(Unit)]) and (DC.RAID_ICON_LIST[GetRaidTargetIndex(Unit)] .. "0:0:0:0|t ") or "" ) ..
-- Colored unit name
D:ColorText( (D:PetUnitName( Unit, true ))
, "FF" .. ((UnitClass(Unit)) and DC.HexClassColor[ (select(2, UnitClass(Unit))) ] or "AAAAAA")) .. " |cFF3F3F3F(".. Unit .. ")|r";
else
TooltipText = MF.CurrUnit;
end
-- set UnitStatus text
local StatusText = "";
-- set the status text, just translate the bitfield to readable text
if Status == NORMAL then
StatusText = L["NORMAL"];
elseif Status == ABSENT then
StatusText = str_format(L["ABSENT"], Unit);
elseif Status == FAR then
StatusText = L["TOOFAR"];
elseif Status == STEALTHED then
StatusText = L["STEALTHED"];
elseif Status == BLACKLISTED then
StatusText = L["BLACKLISTED"];
elseif MF.Debuffs and (Status == AFFLICTED or Status == AFFLICTED_NIR) then
local DebuffType = MF.Debuffs[1].Type;
StatusText = str_format(L["AFFLICTEDBY"], D:ColorText( L[str_upper(DC.TypeNames[DebuffType])], "FF" .. DC.TypeColors[DebuffType]) );
end
-- Unit Status
--TooltipText = TooltipText .. "\n" .. L["UNITSTATUS"] .. StatusText .. "\n";
TooltipText = TooltipText .. "\n" .. StatusText;
-- list the debuff(s) names
if MF.Debuffs then
for i, Debuff in ipairs(MF.Debuffs) do
if Debuff.Type then
local DebuffApps = Debuff.Applications;
TooltipText = TooltipText .. "\n" .. str_format("%s", D:ColorText(Debuff.Name, "FF" .. DC.TypeColors[Debuff.Type])) .. (DebuffApps>0 and str_format(" (%d)", DebuffApps) or "");
end
end
end
local VerticalMUF = floor((self.UnitShown - 1) / D.profile.DebuffsFramePerline ) * D.profile.DebuffsFramePerline + 1;
-- The tooltip is anchored above the top first MUF
if not D.profile.DebuffsFrameGrowToTop then
local FirstMUFAnchor = self:GiveMFAnchor(1);
DefaultTTAnchor[2] = FirstMUFAnchor[2];
DefaultTTAnchor[3] = FirstMUFAnchor[3];
else
local TopMUFAnchor = self:GiveMFAnchor(VerticalMUF);
DefaultTTAnchor[2] = TopMUFAnchor[2];
DefaultTTAnchor[3] = TopMUFAnchor[3];
end
-- Display the tooltip
D:DisplayTooltip( TooltipText , self.Frame, DefaultTTAnchor);
-- if the tooltip is at the top of the screen it means it's overlaping the MUF, let's move the tooltip somewhere else.
if floor(DcrDisplay_Tooltip:GetTop()) == floor(UIParent:GetTop()) then
local RefMUF = 1;
if not D.profile.DebuffsFrameGrowToTop then
RefMUF = VerticalMUF;
end
DcrDisplay_Tooltip:ClearAllPoints();
DcrDisplay_Tooltip:SetPoint("TOPLEFT", self.ExistingPerNum[RefMUF].Frame, "BOTTOMLEFT");
end
end
-- show a help text in the Game default tooltip
if D.profile.DebuffsFrameShowHelp then
-- if necessary we will update the help tooltip text
if (D.Status.SpellsChanged ~= TooltipUpdate) then
TooltipButtonsInfo = {};
local AvailableButtons = D.db.global.AvailableButtons;
for Spell, Prio in pairs(D.Status.CuringSpellsPrio) do
TooltipButtonsInfo[Prio] =
str_format("%s: %s", D:ColorText(DC.AvailableButtonsReadable[AvailableButtons[Prio]], D:NumToHexColor(MF_colors[Prio])), Spell);
end
t_insert(TooltipButtonsInfo, str_format("%s: %s", DC.AvailableButtonsReadable[AvailableButtons[#AvailableButtons - 1]], L["TARGETUNIT"]));
t_insert(TooltipButtonsInfo, str_format("%s: %s", DC.AvailableButtonsReadable[AvailableButtons[#AvailableButtons ]], L["FOCUSUNIT"]));
TooltipButtonsInfo = table.concat(TooltipButtonsInfo, "\n");
TooltipUpdate = D.Status.SpellsChanged;
end
GameTooltip_SetDefaultAnchor(GameTooltip, frame);
GameTooltip:SetText(TooltipButtonsInfo);
GameTooltip:Show();
end
end -- }}}
-- No longer used
--[===[
function MicroUnitF:LateAnalysis(From, Debuffs, MF, Status, GUIDwasFixed)
local Unit = MF.CurrUnit; -- shortcut
local unitguid = UnitGUID(Unit);
local RegisteredUnitguid = MF.UnitGUID;
-- search for detection by combat event manager
local foundcblevents = {}; local highestever = 0; local _; local i=1; local dname; local DebuffApplyTime = false;
local debuffname = Debuffs and Debuffs[1].Name or "No debuff, charmed?";
-- find the debuff on the unit and find out when it was applyed
while 1 do
dname = UnitAura(Unit, i, "HARMFUL");
if not dname then break end;
if dname == debuffname then
local Dduration, DexpireTime;
dname, _, _, _, _, Dduration, DexpireTime = UnitAura(Unit, i, "HARMFUL");
DebuffApplyTime = DexpireTime - Dduration;
break;
end
i = i + 1;
end
if not DebuffApplyTime or DebuffApplyTime == 0 then
D:AddDebugText("DebuffApplyTime could not be found for ", dname, "on", Unit, "DebuffApplyTime set to now - 3s");
DebuffApplyTime = GetTime() - 3;
end
local DetectHistoryIndex = 1;
local halfrange = 5; local RangeMatch = false; local latestever = "none"; local latesttime = 0;
local tconcat = _G.table.concat
D:Debug("Looking for events on", Unit, "between", DebuffApplyTime - halfrange, "and", DebuffApplyTime + halfrange);
-- look in the history of the combat log event handler
while D.DetectHistory[DetectHistoryIndex] do
-- take all the events related to this unit or debuffname around the time the missed debuff was applyed
if D.DetectHistory[DetectHistoryIndex][8] == unitguid or D.DetectHistory[DetectHistoryIndex][12] == debuffname then
if D.DetectHistory[DetectHistoryIndex][1] > DebuffApplyTime - halfrange and D.DetectHistory[DetectHistoryIndex][1] < DebuffApplyTime + halfrange then
t_insert(foundcblevents, tconcat(D.DetectHistory[DetectHistoryIndex],", "));
t_insert(foundcblevents, "\n");
RangeMatch = true;
elseif D.DetectHistory[DetectHistoryIndex][1] > latesttime then-- find the latest event concerning this unit
latesttime = D.DetectHistory[DetectHistoryIndex][1];
latestever = DetectHistoryIndex;
end
end
DetectHistoryIndex = DetectHistoryIndex + 1;
end
if latestever ~= "none" then
latestever = tconcat(D.DetectHistory[latestever],", ");
end
D:AddDebugText("Debuff late detection:", From, debuffname, "Type:", Debuffs[1].TypeName, "on unit:", Unit, unitguid, "_AppT_:", DebuffApplyTime, "DFRR:", D.profile.DebuffsFrameRefreshRate, "Status:", Status, "DT:", GetTime(), "LGU:", D.Status.GroupUpdatedOn, "LGuEr", D.Status.GroupUpdateEvent, "JFGUID:", GUIDwasFixed, "DbUreq:", D.DebuffUpdateRequest, "MFGuid~:", RegisteredUnitguid ~= unitguid, "Z:", GetZoneText(), "DTI:", DetectHistoryIndex);
-- trigger a dcr diag if DetectHistoryIndex is 1 :/
if DetectHistoryIndex == 1 then
T._SelfDiagnostic(true, true);
end
if #foundcblevents == 0 then
D:AddDebugText("No event in range at all for ", Unit, "or debuff:", debuffname, "latest found:", latestever);
else
D:AddDebugText(#foundcblevents / 2, "events for ", Unit, "or debuff:", debuffname, "Status:", Status, "Events:\n", unpack(foundcblevents));
end
end
--]===]
function MicroUnitF:OnLeave() -- {{{
D.Status.MouseOveringMUF = false;
--D:Debug("Micro unit Hidden");
DcrDisplay_Tooltip:Hide();
if (D.profile.DebuffsFrameShowHelp) then
GameTooltip:Hide();
end
end -- }}}
function D.MicroUnitF:OnCornerEnter(frame)
if (D.profile.DebuffsFrameShowHelp) then
D:DisplayGameTooltip(frame,
str_format(
"|cFF11FF11%s|r-|cFF11FF11%s|r: %s\n\n"..
--"|cFF11FF11%s|r: %s\n"..
"|cFF11FF11%s|r-|cFF11FF11%s|r: %s\n\n"..
"|cFF11FF11%s|r-|cFF11FF11%s|r: %s\n"..
"|cFF11FF11%s|r-|cFF11FF11%s|r: %s\n\n"..
"|cFF11FF11%s|r-|cFF11FF11%s|r: %s",
D.L["ALT"], D.L["HLP_LEFTCLICK"], D.L["HANDLEHELP"],
--D.L["HLP_RIGHTCLICK"], D.L["STR_OPTIONS"],
D.L["ALT"], D.L["HLP_RIGHTCLICK"], D.L["BINDING_NAME_DCRSHOWOPTION"],
D.L["CTRL"], D.L["HLP_LEFTCLICK"], D.L["BINDING_NAME_DCRPRSHOW"],
D.L["SHIFT"], D.L["HLP_LEFTCLICK"], D.L["BINDING_NAME_DCRSKSHOW"],
D.L["SHIFT"], D.L["HLP_RIGHTCLICK"], D.L["BINDING_NAME_DCRSHOW"]
));
end;
end
function MicroUnitF:OnLoad(frame) -- {{{
frame:SetScript("PreClick", self.OnPreClick);
frame:SetScript("PostClick", self.OnPostClick);
end
-- }}}
function MicroUnitF.OnPreClick(frame, Button) -- {{{
D:Debug("Micro unit Preclicked: ", Button);
local Unit = frame.Object.CurrUnit; -- shortcut
if (frame.Object.UnitStatus == NORMAL and (Button == "LeftButton" or Button == "RightButton")) then
D:Println(L["HLP_NOTHINGTOCURE"]);
elseif (frame.Object.UnitStatus == AFFLICTED) then
local NeededPrio = D:GiveSpellPrioNum(frame.Object.Debuffs[1].Type);
local RequestedPrio = false;
local ButtonsString = "";
if IsControlKeyDown() then
ButtonsString = "ctrl-";
elseif IsAltKeyDown() then
ButtonsString = "alt-";
elseif IsShiftKeyDown() then
ButtonsString = "shift-";
end
if Button == "LeftButton" then
ButtonsString = ButtonsString .. "%s1";
elseif Button == "RightButton" then
ButtonsString = ButtonsString .. "%s2";
end
RequestedPrio = D:tGiveValueIndex(D.db.global.AvailableButtons, ButtonsString);
if RequestedPrio and NeededPrio ~= RequestedPrio then
D:errln(L["HLP_WRONGMBUTTON"]);
if NeededPrio and MF_colors[NeededPrio] then
D:Println(L["HLP_USEXBUTTONTOCURE"], D:ColorText(DC.AvailableButtonsReadable[ D.db.global.AvailableButtons[NeededPrio] ], D:NumToHexColor(MF_colors[NeededPrio])));
--[===[@debug@
else
D:AddDebugText("Button wrong click info bug: NeededPrio:", NeededPrio, "Unit:", Unit, "RequestedPrio:", RequestedPrio, "Button clicked:", Button, "MF_colors:", unpack(MF_colors), "Debuff Type:", frame.Object.Debuffs[1].Type);
--@end-debug@]===]
end
elseif RequestedPrio and D.Status.HasSpell then
-- D:Print("XXX ClickedMF SET");
D.Status.ClickedMF = frame.Object; -- used to update the MUF on cast success and failure to know which unit is being cured
D.Status.ClickedMF.SPELL_CAST_SUCCESS = false;
D:Debuff_History_Add(frame.Object.Debuffs[1].Name, frame.Object.Debuffs[1].TypeName);
end
end
end -- }}}
-- }}}
-- }}}
-- MicroUnitF NON STATIC METHODS {{{
-- init a new micro frame (Call internally by :new() only)
function MicroUnitF.prototype:init(Container, Unit, FrameNum, ID) -- {{{
D:Debug("Initializing MicroUnit object", Unit, "with FrameNum=", FrameNum, " and ID", ID);
-- set object default variables
self.Parent = Container;
self.ID = ID; -- is set by te roaming updater
self.FrameNum = FrameNum;
self.ToPlace = true;
self.Debuffs = false;
self.Debuff1Prio = false;
self.PrevDebuff1Prio = false;
self.IsDebuffed = false;
self.CurrUnit = false;
self.UnitName = false;
self.UnitGUID = false;
self.UnitClass = false;
self.UnitStatus = 0;
self.FirstDebuffType = 0;
self.NormalAlpha = false;
self.BorderAlpha = false;
self.Color = {};
self.IsCharmed = false;
self.UpdateCountDown = 3;
self.LastAttribUpdate = 0;
self.LitTime = false;
self.Chrono = false;
self.PrevChrono = false;
self.Shown = false; -- Setting this to true will broke the stick to right option
self.UpdateCD = 0;
self.RaidTargetIcon = false;
self.PrevRaidTargetIndex= false;
-- create the frame
self.Frame = CreateFrame ("Button", "DcrMicroUnit"..Unit, self.Parent, "DcrMicroUnitTemplateSecure");
self.CooldownFrame = CreateFrame ("Cooldown", "DcrMicroUnitCD"..Unit, self.Frame, "DcrMicroUnitCDTemplate");
-- outer texture (the class border)
-- Bottom side
self.OuterTexture1 = self.Frame:CreateTexture("DcrMicroUnit"..Unit.."Bottom", "BORDER");
self.OuterTexture1:SetPoint("BOTTOMLEFT", self.Frame, "BOTTOMLEFT", 0, 0);
self.OuterTexture1:SetPoint("TOPRIGHT", self.Frame, "BOTTOMRIGHT", 0, 2);
-- left side
self.OuterTexture2 = self.Frame:CreateTexture("DcrMicroUnit"..Unit.."Left", "BORDER");
self.OuterTexture2:SetPoint("TOPLEFT", self.Frame, "TOPLEFT", 0, -2);
self.OuterTexture2:SetPoint("BOTTOMRIGHT", self.Frame, "BOTTOMLEFT", 2, 2);
-- top side
self.OuterTexture3 = self.Frame:CreateTexture("DcrMicroUnit"..Unit.."Top", "BORDER");
self.OuterTexture3:SetPoint("TOPLEFT", self.Frame, "TOPLEFT", 0, 0);
self.OuterTexture3:SetPoint("BOTTOMRIGHT", self.Frame, "TOPRIGHT", 0, -2);
-- right side
self.OuterTexture4 = self.Frame:CreateTexture("DcrMicroUnit"..Unit.."Right", "BORDER");
self.OuterTexture4:SetPoint("TOPRIGHT", self.Frame, "TOPRIGHT", 0, -2);
self.OuterTexture4:SetPoint("BOTTOMLEFT", self.Frame, "BOTTOMRIGHT", -2, 2);
-- global texture
self.Texture = self.Frame:CreateTexture("DcrMicroUnit"..Unit.."Back", "ARTWORK");
self.Texture:SetPoint("CENTER",self.Frame ,"CENTER",0,0)
self.Texture:SetHeight(16);
self.Texture:SetWidth(16);
-- inner Texture (Charmed special texture)
self.InnerTexture = self.Frame:CreateTexture("DcrMicroUnit"..Unit.."Charmed", "OVERLAY");
self.InnerTexture:SetPoint("CENTER",self.Frame ,"CENTER",0,0)
self.InnerTexture:SetHeight(7);
self.InnerTexture:SetWidth(7);
self.InnerTexture:SetTexture(unpack(MF_colors[CHARMED_STATUS]));
-- Chrono Font string
self.ChronoFontString = self.Frame:CreateFontString("DcrMicroUnit"..Unit.."Chrono", "ARTWORK", "DcrMicroUnitChronoFont");
self.ChronoFontString:SetTextColor(unpack(MF_colors["COLORCHRONOS"]));
-- raid target icon
self.RaidIconTexture = self.Frame:CreateTexture("DcrMicroUnit"..Unit.."RaidTargetIcon", "OVERLAY");
self.RaidIconTexture:SetPoint("CENTER",self.Frame ,"CENTER",0,8)
self.RaidIconTexture:SetHeight(13);
self.RaidIconTexture:SetWidth(13);
-- a reference to this object
self.Frame.Object = self;
-- register events
self.Frame:RegisterForClicks("AnyUp");
self.Frame:SetFrameStrata("MEDIUM");
-- set the frame attributes
self:UpdateAttributes(Unit);
-- once the MF frame is set up, schedule an event to show it
MicroUnitF:Delayed_MFsDisplay_Update();
end -- }}}
function MicroUnitF.prototype:Update(SkipSetColor, SkipDebuffs, CheckStealth)
local MF = self;
local ActionsDone = 0;
local Unit = MF.CurrUnit;
-- The unit is the same but the name isn't... (check for class change)
if MF.CurrUnit == Unit and D.Status.Unit_Array_UnitToGUID[self.CurrUnit] ~= self.UnitGUID then
if MF:SetClassBorder() then
ActionsDone = ActionsDone + 1; -- count expensive things done
end
-- if the guid changed we really need to rescan the unit!
SkipSetColor = false; SkipDebuffs = false; CheckStealth = true;
--[===[@debug@
D:Debug("|cFF00CC00MUF:Update(): Guid change rescanning", Unit, "|r");
--@end-debug@]===]
end
-- Update the frame attributes if necessary (Spells priority or unit id changes)
if (D.Status.SpellsChanged ~= MF.LastAttribUpdate ) then
--D:Debug("Attributes update required: ", MF.ID);
if (MF:UpdateAttributes(Unit, true)) then
ActionsDone = ActionsDone + 1; -- count expensive things done
SkipSetColor = false; SkipDebuffs = false; -- if some attributes were updated then update the rest
end
end
if (not SkipSetColor) then
if (not SkipDebuffs) then
-- get the manageable debuffs of this unit
MF:SetDebuffs();
D:Debug("Debuff set for ", MF.ID);
if CheckStealth then
D.Stealthed_Units[MF.CurrUnit] = D:CheckUnitStealth(MF.CurrUnit); -- update stealth status
-- D:Debug("MF:Update(): Stealth status checked as requested.");
end
end
if (MF:SetColor()) then
ActionsDone = ActionsDone + 1; -- count expensive things done
end
end
return ActionsDone;
end
function MicroUnitF.prototype:UpdateWithCS()
self:Update(false, false, true);
end
function MicroUnitF.prototype:UpdateSkippingSetBuf()
self:Update(false, true);
end
-- UPDATE attributes (Spells and Unit) {{{
do
-- used to tell if we changed something to improve performances.
-- Each attribute change trigger an event...
local ReturnValue = false;
-- this updates the sttributes of a MUF's frame object
function MicroUnitF.prototype:UpdateAttributes(Unit, DoNotDelay)
-- Delay the call if we are fighting
if InCombatLockdown() then
if not DoNotDelay then
D:AddDelayedFunctionCall (
"MicroUnit_" .. Unit, -- UID
self.UpdateAttributes, self, Unit); -- function call
end
return false;
end
ReturnValue = false;
-- if the unit is not set
if not self.CurrUnit then
self.Frame:SetAttribute("unit", Unit);
-- UnitToMUF[] can only be set when out of fight so it remains
-- coherent with what is displayed when groups are changed during a
-- fight
MicroUnitF.UnitToMUF[Unit] = self;
self.CurrUnit = Unit;
self:SetClassBorder();
-- set the return value because we did something expensive
ReturnValue = self;
end
if (D.Status.SpellsChanged == self.LastAttribUpdate) then
return ReturnValue; -- nothing changed
end
-- D:Debug("UpdateAttributes() executed");
if self.LastAttribUpdate == 0 then -- only once
-- set the mouse left-button actions on all modifiers
self.Frame:SetAttribute("type1", "macro");
self.Frame:SetAttribute("ctrl-type1", "macro");
self.Frame:SetAttribute("alt-type1", "macro");
self.Frame:SetAttribute("shift-type1", "macro");
-- set the mouse right-button actions on all modifiers
self.Frame:SetAttribute("type2", "macro");
self.Frame:SetAttribute("ctrl-type2", "macro");
self.Frame:SetAttribute("alt-type2", "macro");
self.Frame:SetAttribute("shift-type2", "macro");
-- set the mouse middle-button actions on all modifiers
self.Frame:SetAttribute("type3", "macro");
self.Frame:SetAttribute("ctrl-type3", "macro");
self.Frame:SetAttribute("alt-type3", "macro");
self.Frame:SetAttribute("shift-type3", "macro");
end
local AvailableButtons = D.db.global.AvailableButtons;
self.Frame:SetAttribute(str_format(AvailableButtons[#AvailableButtons - 1], "macrotext"), str_format("/target %s", Unit));
self.Frame:SetAttribute(str_format(AvailableButtons[#AvailableButtons ], "macrotext"), str_format("/focus %s", Unit));
-- set the spells attributes using the lookup tables above
for Spell, Prio in pairs(D.Status.CuringSpellsPrio) do
--the [target=%s, help][target=%s, harm] prevents the 'please select a unit' cursor problem (Blizzard should fix this...)
self.Frame:SetAttribute(str_format(AvailableButtons[Prio], "macrotext"), str_format("%s/cast [target=%s, help][target=%s, harm] %s%s",
((not D.Status.FoundSpells[Spell][1]) and "/stopcasting\n" or ""),
Unit,Unit,
Spell,
(DC.SpellsToUse[Spell].Rank and "(" .. (str_sub(DC.RANKNUMTRANS, '%d+', DC.SpellsToUse[Spell].Rank)) .. ")" or "") ));
--[[
D:Debug("XX-> macro: ",str_format(AvailableButtons[Prio], "macrotext"), str_format("%s/cast [target=%s, help][target=%s, harm] %s%s",
((not D.Status.FoundSpells[Spell][1]) and "/stopcasting\n" or ""),
Unit,Unit,
Spell,
(DC.SpellsToUse[Spell].Rank and "(" .. (str_sub(DC.RANKNUMTRANS, '%d+', DC.SpellsToUse[Spell].Rank)) .. ")" or "") ));
--]]
end
self.Debuff1Prio = false;
-- the update timestamp
self.LastAttribUpdate = D.Status.SpellsChanged;
return self;
end
end -- }}}
function MicroUnitF.prototype:SetDebuffs() -- {{{
self.Debuffs, self.IsCharmed = D:UnitCurableDebuffs(self.CurrUnit);
if D.UnitDebuffed[self.CurrUnit] then
D.ForLLDebuffedUnitsNum = D.ForLLDebuffedUnitsNum - 1;
end
if (self.Debuffs and self.Debuffs[1] and self.Debuffs[1].Type) then
--D:Debug("A debuff was found"); -- XXX
self.IsDebuffed = true;
self.Debuff1Prio = D:GiveSpellPrioNum( self.Debuffs[1].Type );
D.UnitDebuffed[self.CurrUnit] = true;
D.ForLLDebuffedUnitsNum = D.ForLLDebuffedUnitsNum + 1;
else
--D:Debug("No debuff found"); -- XXX
self.IsDebuffed = false;
self.Debuff1Prio = false;
self.PrevDebuff1Prio = false;
D.UnitDebuffed[self.CurrUnit] = false; -- used by the live-list only
end
end -- }}}
do
--[=[
-- This function is responsible for setting all the textures of a MUF object:
-- - The main color
-- - Showing/Hiding the charmed alert square
-- - The Alpha of the center and borders
-- This function also set the Status of the MUF that will be used in the tooltip
--]=]
local DebuffType, Unit, PreviousStatus, BorderAlpha, Class, ClassColor, ReturnValue, RangeStatus, Alpha, PrioChanged, PrevChrono, Time, Status;
local profile = {};
-- global access optimization
local IsSpellInRange = _G.IsSpellInRange;
local UnitClass = _G.UnitClass;
local UnitExists = _G.UnitExists;
local UnitIsVisible = _G.UnitIsVisible;
local UnitLevel = _G.UnitLevel;
local unpack = _G.unpack;
local select = _G.select;
local GetTime = _G.GetTime;
local floor = _G.math.floor;
local fmod = _G.math.fmod;
local CooldownFrame_SetTimer = _G.CooldownFrame_SetTimer;
local GetSpellCooldown = _G.GetSpellCooldown;
local GetRaidTargetIndex= _G.GetRaidTargetIndex;
local bor = _G.bit.bor;
function MicroUnitF.prototype:SetColor() -- {{{
profile = D.profile;
Status = D.Status;
-- register default alpha of the border
BorderAlpha = profile.DebuffsFrameElemBorderAlpha;
-- register local variables
DebuffType = false;
ReturnValue = false;
Unit = self.CurrUnit;
PreviousStatus = self.UnitStatus;
-- if unit not available, if a unit cease to exist (this happen often for pets)
if not UnitExists(Unit) then
if PreviousStatus ~= ABSENT then
self.Color = MF_colors[ABSENT];
self.UnitStatus = ABSENT;
if self.LitTime then
self.LitTime = false;
self.ChronoFontString:SetText(" ");
end
end
-- UnitIsVisible() behavior is not 100% reliable so we also use UnitLevel() that will return -1 when the Unit is too far...
elseif not UnitIsVisible(Unit) or UnitLevel(Unit) < 1 then
if PreviousStatus ~= FAR then
self.Color = MF_colors[FAR];
self.UnitStatus = FAR;
if self.LitTime then
self.LitTime = false;
self.ChronoFontString:SetText(" ");
end
end
else
-- If the Unit is invisible
if profile.Show_Stealthed_Status and D.Stealthed_Units[Unit] then
if PreviousStatus ~= STEALTHED then
self.Color = MF_colors[STEALTHED];
self.UnitStatus = STEALTHED;
if self.LitTime then
self.LitTime = false;
self.ChronoFontString:SetText(" ");
end
end
-- if unit is blacklisted
elseif Status.Blacklisted_Array[Unit] then
if PreviousStatus ~= BLACKLISTED then
self.Color = MF_colors[BLACKLISTED];
self.UnitStatus = BLACKLISTED;
if self.LitTime then
self.LitTime = false;
self.ChronoFontString:SetText(" ");
end
end
-- if the unit has some debuffs we can handle
elseif (self.IsDebuffed) then
DebuffType = self.Debuffs[1].Type;
if self.PrevDebuff1Prio ~= self.Debuff1Prio then
self.Color = MF_colors[self.Debuff1Prio];
self.PrevDebuff1Prio = self.Debuff1Prio;
PrioChanged = true;
end
-- Test if the spell we are going to use is in range
-- Some time can elaps between the instant the debuff is detected and the instant it is shown.
-- Between those instants, a reconfiguration can happen (pet dies or some spells become unavailable)
-- So we test before calling this api that we can still cure this debuff type
if Status.CuringSpells[DebuffType] then
RangeStatus = IsSpellInRange(Status.CuringSpells[DebuffType], Unit);
else
RangeStatus = false;
end
Time = GetTime();
if RangeStatus and self.UpdateCD < Status.UpdateCooldown then
CooldownFrame_SetTimer (self.CooldownFrame, GetSpellCooldown(Status.CuringSpells[DebuffType]) );
self.UpdateCD = Time;
end
-- update the chrono
if profile.DebuffsFrameChrono then
if self.LitTime then
PrevChrono = self.Chrono;
if not profile.DebuffsFrameTimeLeft then
self.Chrono = floor(Time - self.LitTime);
if self.Chrono ~= PrevChrono then
self.ChronoFontString:SetText( ((self.Chrono < 60) and self.Chrono or (floor(self.Chrono / 60) .. "\'") ));
end
else
self.Chrono = floor(self.Debuffs[1].expirationTime - Time);
if self.Chrono ~= PrevChrono then
self.ChronoFontString:SetText( ((self.Chrono < 60) and (self.Chrono + 1) or (floor(self.Chrono / 60 + 1) .. "\'") ));
end
end
else
self.LitTime = Time;
end
end
self.RaidTargetIcon = GetRaidTargetIndex(Unit);
if self.PrevRaidTargetIndex ~= self.RaidTargetIcon then
self.RaidIconTexture:SetTexture(self.RaidTargetIcon and DC.RAID_ICON_TEXTURE_LIST[self.RaidTargetIcon] or nil);
self.PrevRaidTargetIndex = self.RaidTargetIcon;
end
-- set the status according to RangeStatus
if (not RangeStatus or RangeStatus == 0) then
Alpha = 0.40;
self.UnitStatus = AFFLICTED_NIR;
else
Alpha = 1;
self.UnitStatus = AFFLICTED;
BorderAlpha = 1;
MicroUnitF.UnitsDebuffedInRange = MicroUnitF.UnitsDebuffedInRange + 1;
if (not Status.SoundPlayed) then
D:PlaySound (self.CurrUnit, "SetColor()" );
end
end
elseif PreviousStatus ~= NORMAL then
-- the unit has nothing special, set the status to normal
self.Color = MF_colors[NORMAL];
self.UnitStatus = NORMAL;
if self.LitTime then
self.LitTime = false;
self.ChronoFontString:SetText(" ");
end
if self.RaidTargetIcon then
self.RaidIconTexture:SetTexture(nil);
self.RaidTargetIcon = false;
self.PrevRaidTargetIndex = false;
end
-- if the previous status was FAR, trigger a full rescan of the unit (combat log event does not report events for far away units)
if PreviousStatus == FAR then
D.MicroUnitF:UpdateMUFUnit(self.CurrUnit, true); -- this is able to deal when a lot of update queries
end
end
end
if PreviousStatus == AFFLICTED or PreviousStatus == AFFLICTED_AND_CHARMED then
MicroUnitF.UnitsDebuffedInRange = MicroUnitF.UnitsDebuffedInRange - 1;
if MicroUnitF.UnitsDebuffedInRange == 0 and profile.Hide_LiveList then
D:Debug("SetColor(): No more unit, sound re-enabled");
Status.SoundPlayed = false;
end
end
if not profile.DebuffsFrameElemBorderShow then
BorderAlpha = 0;
end
-- set the class border color when needed (the class is unknown and the unit exists or the unit name changed)
--self:SetClassBorder();
-- set the alpha of the border if necessary
if self.BorderAlpha ~= BorderAlpha then
self.OuterTexture1:SetAlpha(BorderAlpha);
self.OuterTexture2:SetAlpha(BorderAlpha);
self.OuterTexture3:SetAlpha(BorderAlpha);
self.OuterTexture4:SetAlpha(BorderAlpha);
self.BorderAlpha = BorderAlpha;
-- set this to true because we just did something expensive...
ReturnValue = true;
--D:Debug("border alpha set");
end
-- Add the charm status to the bitfield (note that it's treated separatly because it's shown even if the unit is not afflicetd by anything we can cure)
if self.IsCharmed then
self.UnitStatus = bor(self.UnitStatus, CHARMED_STATUS);
end
-- if the unit is not afflicted or too far set the color to a lower alpha
if not DebuffType then -- if DebuffType was not set, it means that the unit is too far
Alpha = self.Color[4] * profile.DebuffsFrameElemAlpha;
self.PrevDebuff1Prio = false;
end
-- Apply the colors and alphas only if necessary
-- The MUF status changed
-- The user changed the defaultAlpha
-- The priority (and thus the color) of the first affliction changed
if (self.UnitStatus ~= PreviousStatus or self.NormalAlpha ~= profile.DebuffsFrameElemAlpha or PrioChanged) then-- or self.FirstDebuffType ~= DebuffType) then
if PrioChanged then PrioChanged = false; end
-- Set the main texture
self.Texture:SetTexture(self.Color[1], self.Color[2], self.Color[3], Alpha);
--self.Texture:SetAlpha(Alpha);
-- Show the charmed alert square
if self.IsCharmed then
self.InnerTexture:Show();
else
self.InnerTexture:Hide();
end
--D:Debug("Color Applied, MUF Status:", self.UnitStatus);
-- save the current global status
self.NormalAlpha = profile.DebuffsFrameElemAlpha;
self.FirstDebuffType = DebuffType;
-- set this to true because we just did something expensive...
ReturnValue = true;
end
return ReturnValue;
end -- }}}
function MicroUnitF.prototype:SetClassBorder()
--D:Debug("SetClassBorder called ", D.Status.Unit_Array_UnitToGUID[self.CurrUnit] , self.UnitGUID);
ReturnValue = false;
if (D.profile.DebuffsFrameElemBorderShow and (D.Status.Unit_Array_UnitToGUID[self.CurrUnit] ~= self.UnitGUID or (not self.UnitClass and UnitExists(self.CurrUnit)))) then
-- Get the GUID of this unit
self.UnitGUID = D.Status.Unit_Array_UnitToGUID[self.CurrUnit];
if self.UnitGUID then -- can be nil because of focus...
-- Get its class
Class = (select(2, UnitClass(self.CurrUnit)));
else
Class = false;
end
-- if the class changed
if (Class and Class ~= self.UnitClass) then
ClassColor = DC.ClassesColors[Class];
-- update the border color (the four borders)
self.OuterTexture1:SetTexture( unpack(ClassColor) );
self.OuterTexture2:SetTexture( unpack(ClassColor) );
self.OuterTexture3:SetTexture( unpack(ClassColor) );
self.OuterTexture4:SetTexture( unpack(ClassColor) );
-- save the class for futur reference
self.UnitClass = Class;
-- set this to true because we just did something expensive...
ReturnValue = true;
--D:Debug("Class '%s' set for '%s'", Class, self.CurrUnit);
elseif not Class and self.UnitClass then
-- if the class is not available, set it to false so this test will be done again and again until a class is found
self.UnitClass = false;
BorderAlpha = 0;
self.OuterTexture1:SetAlpha(BorderAlpha);
self.OuterTexture2:SetAlpha(BorderAlpha);
self.OuterTexture3:SetAlpha(BorderAlpha);
self.OuterTexture4:SetAlpha(BorderAlpha);
self.BorderAlpha = BorderAlpha;
ReturnValue = true;
--D:Debug("Class of unit %s is Nil", self.CurrUnit);
end
end
return ReturnValue;
end
end
-- }}}
do
local MicroFrameUpdateIndex = 1; -- MUFs are not updated all together
local NumToShow, ActionsDone, Unit, MF, pass, UnitNum;
-- updates the micro frames if needed (called regularly by ACE event, changed in the option menu)
function D:DebuffsFrame_Update() -- {{{
local Unit_Array = self.Status.Unit_Array;
local UnitToGUID = self.Status.Unit_Array_UnitToGUID;
UnitNum = self.Status.UnitNum; -- we need to go through all the units to set MF.ID properly
NumToShow = ((MicroUnitF.MaxUnit > UnitNum) and UnitNum or MicroUnitF.MaxUnit);
ActionsDone = 0; -- used to limit the maximum number of consecutive UI actions
-- we don't check all the MUF at each call, only some of them (changed in the options)
for pass = 1, self.profile.DebuffsFramePerUPdate do
-- When all frames have been updated, go back to the first
if (MicroFrameUpdateIndex > UnitNum) then
MicroFrameUpdateIndex = 1;
-- self:Debug("last micro frame updated,,:: %d", #self.Status.Unit_Array);
end
-- get a unit
Unit = Unit_Array[MicroFrameUpdateIndex];
-- should never fire unless the player choosed to ignore everything or something is wrong somewhere in the code
if not Unit then
--self:Debug("Unit is nil :/");
return false;
end
-- get its MUF
MF = MicroUnitF.ExistingPerUNIT[Unit];
-- if no MUF then create it (All MUFs are created here)
if (not MF) then
if not InCombatLockdown() then
MF = MicroUnitF:Create(Unit, MicroFrameUpdateIndex);
ActionsDone = ActionsDone + 1;
end
end
-- place the MUF ~right where it belongs~
if MF and MF.ToPlace ~= MicroFrameUpdateIndex and not InCombatLockdown() then
--sanity check
--[[
if MicroFrameUpdateIndex ~= MF.ID then -- XXX to remove for release
D:AddDebugText("DebuffsFrame_Update(): MicroFrameUpdateIndex ~= MF.ID", MicroFrameUpdateIndex, MF.ID, Unit, MF.CurrUnit, "ToPlace:", MF.ToPlace);
end
--]]
MF.ToPlace = MicroFrameUpdateIndex;
MF.Frame:SetPoint(unpack(MicroUnitF:GiveMFAnchor(MicroFrameUpdateIndex)));
if MF.Shown then
MF.Frame:Show();
end
-- test for GUID change and force a debuff update in this case
if UnitToGUID[MF.CurrUnit] ~= MF.UnitGUID then
MF.UpdateCountDown = 0; -- will force MF:Update() to be called
--[===[@debug@
D:Println("|cFFFFAA55GUID change detected while placing for |r", MicroFrameUpdateIndex, UnitToGUID[MF.CurrUnit], MF.UnitGUID );
--@end-debug@]===]
end
ActionsDone = ActionsDone + 1;
end
-- update the MUF attributes and its colors -- this is done by an event handler now (buff/debuff received...) except when the unit has a debuff and is in range
if MF and MicroFrameUpdateIndex <= NumToShow then
if not (MF.IsDebuffed or MF.IsCharmed) and MF.UpdateCountDown ~= 0 then
MF.UpdateCountDown = MF.UpdateCountDown - 1;
else -- if MF.IsDebuffed or MF.IsCharmed or MF.UpdateCountDown == 0
ActionsDone = ActionsDone + MF:Update(false, true);--, not ((MF.IsDebuffed or MF.IsCharmed) and MF.UnitStatus ~= AFFLICTED)); -- we rescan debuffs if the unit is not in spell range XXX useless now since we rescan everyone every second
MF.UpdateCountDown = 3;
end
end
-- we are done for this frame, go to te next
MicroFrameUpdateIndex = MicroFrameUpdateIndex + 1;
-- don't update more than 5 MUF in a row
-- don't loop when reaching the end, wait for the next call (useful when less MUFs than PerUpdate)
if (ActionsDone > 5 or pass == UnitNum) then
--self:Debug("Max actions count reached");
break;
end
end
-- end
end -- }}}
end
-- This little function returns the priority of the spell corresponding to an affliction type (one spell can be used for several types)
function D:GiveSpellPrioNum (Type) -- {{{
return D.Status.CuringSpellsPrio[D.Status.CuringSpells[Type]];
end -- }}}
-- old unused variables and functions
-- UNUSED STUFF {{{
-- Micro Frame Events, useless for now
function MicroUnitF:OnPostClick(frame, button)
-- D:Debug("Micro unit PostClicked");
end
function MicroUnitF:OnAttributeChanged(self, name, value)
D:Debug("Micro unit", name, "AttributeChanged to", value);
end
local MUF_Status = { -- unused
[1] = "normal";
[2] = "absent";
[3] = "far";
[4] = "stealthed";
[5] = "blacklist";
[6] = "afflicted";
[7] = "afflicted-far";
[8] = "afflicted-charmed";
[9] = "afflicted-charmed-far";
}
local MF_Textures = { -- unused
"Interface/AddOns/Decursive/Textures/BackDrop-red", -- red
"Interface/AddOns/Decursive/Textures/BackDrop-blue", -- blue
"Interface/AddOns/Decursive/Textures/BackDrop-orange", -- orange
["grey"] = "Interface\\AddOns\\Decursive\\Textures\\BackDrop-grey-medium",
["black"] = "Interface/AddOns/Decursive/Textures/BackDrop",
};
-- }}}
T._LoadedFiles["Dcr_DebuffsFrame.lua"] = "2.5.1-6-gd3885c5";
-- Heresy