890 lines
30 KiB
Lua
890 lines
30 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_Raid.lua"] then
|
|
if not DecursiveInstallCorrupted then T._FatalError("Decursive installation is corrupted! (Dcr_Raid.lua not loaded)"); end;
|
|
DecursiveInstallCorrupted = true;
|
|
return;
|
|
end
|
|
|
|
local D = Dcr;
|
|
--D:SetDateAndRevision("$Date: 2008-09-16 00:25:13 +0200 (mar., 16 sept. 2008) $", "$Revision: 81755 $");
|
|
|
|
local L = D.L;
|
|
local LC = D.LC;
|
|
local DC = DcrC;
|
|
local DS = DC.DS;
|
|
-------------------------------------------------------------------------------
|
|
|
|
local pairs = _G.pairs;
|
|
local ipairs = _G.ipairs;
|
|
local type = _G.type;
|
|
local table = _G.table;
|
|
local t_sort = _G.table.sort;
|
|
local PlaySoundFile = _G.PlaySoundFile;
|
|
local UnitName = _G.UnitName;
|
|
local UnitDebuff = _G.UnitDebuff;
|
|
local UnitBuff = _G.UnitBuff;
|
|
local UnitIsCharmed = _G.UnitIsCharmed;
|
|
local UnitCanAttack = _G.UnitCanAttack;
|
|
local UnitClass = _G.UnitClass;
|
|
local UnitExists = _G.UnitExists;
|
|
local GetNetStats = _G.GetNetStats;
|
|
local _;
|
|
|
|
-------------------------------------------------------------------------------
|
|
-- The UI functions {{{
|
|
-------------------------------------------------------------------------------
|
|
|
|
-------------------------------------------------------------------------------
|
|
-- The printing functions {{{
|
|
-------------------------------------------------------------------------------
|
|
|
|
function D:Show_Cure_Order() --{{{
|
|
self:Println("printing cure order:");
|
|
for index, unit in ipairs(self.Status.Unit_Array) do
|
|
self:Println( unit, " - ", self:MakePlayerName((self:UnitName(unit))) , " Index: ", index);
|
|
end
|
|
end --}}}
|
|
|
|
-- }}}
|
|
-------------------------------------------------------------------------------
|
|
|
|
-- Show Hide FUNCTIONS -- {{{
|
|
|
|
function D:ShowHideLiveList(hide) --{{{
|
|
|
|
if not D.DcrFullyInitialized then
|
|
return;
|
|
end
|
|
|
|
-- if hide is requested or if hide is not set and the live-list is shown
|
|
if (hide==1 or (not hide and DcrLiveList:IsVisible())) then
|
|
D.profile.Hide_LiveList = true;
|
|
DcrLiveList:Hide();
|
|
D:CancelDelayedCall("Dcr_LLupdate");
|
|
else
|
|
D.profile.Hide_LiveList = false;
|
|
DcrLiveList:ClearAllPoints();
|
|
DcrLiveList:SetPoint("TOPLEFT", "DecursiveMainBar", "BOTTOMLEFT");
|
|
DcrLiveList:Show();
|
|
|
|
D:ScheduleRepeatedCall("Dcr_LLupdate", D.LiveList.Update_Display, D.profile.ScanTime, D.LiveList);
|
|
end
|
|
|
|
end --}}}
|
|
|
|
-- This functions hides or shows the "Decursive" bar depending on its current
|
|
-- state, it's also able hide/show the live-list if the "tie live-list" option is active
|
|
function D:HideBar(hide) --{{{
|
|
|
|
if not D.DcrFullyInitialized then
|
|
return;
|
|
end
|
|
|
|
if (hide==1 or (not hide and DecursiveMainBar:IsVisible())) then
|
|
if (D.profile.LiveListTied) then
|
|
D:ShowHideLiveList(1);
|
|
end
|
|
D.profile.Hidden = true;
|
|
DecursiveMainBar:Hide();
|
|
else
|
|
if (D.profile.LiveListTied) then
|
|
D:ShowHideLiveList(0);
|
|
end
|
|
D.profile.Hidden = false;
|
|
DecursiveMainBar:Show();
|
|
end
|
|
|
|
if DecursiveMainBar:IsVisible() and DcrLiveList:IsVisible() then
|
|
DcrLiveList:ClearAllPoints();
|
|
DcrLiveList:SetPoint("TOPLEFT", "DecursiveMainBar", "BOTTOMLEFT");
|
|
else
|
|
D:ColorPrint(0.3, 0.5, 1, L["SHOW_MSG"]);
|
|
end
|
|
|
|
LibStub("AceConfigRegistry-3.0"):NotifyChange(D.name);
|
|
end --}}}
|
|
|
|
function D:ShowHidePriorityListUI() --{{{
|
|
|
|
if not D.DcrFullyInitialized then
|
|
return;
|
|
end
|
|
|
|
if (DecursivePriorityListFrame:IsVisible()) then
|
|
DecursivePriorityListFrame:Hide();
|
|
else
|
|
DecursivePriorityListFrame:Show();
|
|
end
|
|
end --}}}
|
|
|
|
function D:ShowHideSkipListUI() --{{{
|
|
|
|
if not D.DcrFullyInitialized then
|
|
return;
|
|
end
|
|
|
|
if (DecursiveSkipListFrame:IsVisible()) then
|
|
DecursiveSkipListFrame:Hide();
|
|
else
|
|
DecursiveSkipListFrame:Show();
|
|
end
|
|
end --}}}
|
|
|
|
-- This shows/hides the buttons near the "Decursive" bar
|
|
function D:ShowHideButtons(UseCurrentValue) --{{{
|
|
|
|
if not D.DcrFullyInitialized then
|
|
return;
|
|
end
|
|
|
|
if not D.profile then
|
|
return;
|
|
end
|
|
|
|
|
|
local DcrFrame = "DecursiveMainBar";
|
|
local buttons = {
|
|
DcrFrame .. "Priority",
|
|
DcrFrame .. "Skip",
|
|
DcrFrame .. "Hide",
|
|
}
|
|
|
|
local DCRframeObject = _G[DcrFrame];
|
|
|
|
if (not UseCurrentValue) then
|
|
D.profile.HideButtons = (not D.profile.HideButtons);
|
|
end
|
|
|
|
for _, ButtonName in pairs(buttons) do
|
|
local Button = _G[ButtonName];
|
|
|
|
if (D.profile.HideButtons) then
|
|
Button:Hide();
|
|
DCRframeObject.isLocked = 1;
|
|
else
|
|
Button:Show();
|
|
DCRframeObject.isLocked = 0;
|
|
end
|
|
|
|
end
|
|
|
|
end --}}}
|
|
|
|
-- }}}
|
|
|
|
|
|
-- this resets the location of the windows
|
|
function D:ResetWindow() --{{{
|
|
|
|
DecursiveMainBar:ClearAllPoints();
|
|
DecursiveMainBar:SetPoint("CENTER", UIParent);
|
|
DecursiveMainBar:Show();
|
|
|
|
DcrLiveList:ClearAllPoints();
|
|
DcrLiveList:SetPoint("TOPLEFT", DecursiveMainBar, "BOTTOMLEFT");
|
|
DcrLiveList:Show();
|
|
|
|
DecursivePriorityListFrame:ClearAllPoints();
|
|
DecursivePriorityListFrame:SetPoint("CENTER", UIParent);
|
|
|
|
DecursiveSkipListFrame:ClearAllPoints();
|
|
DecursiveSkipListFrame:SetPoint("CENTER", UIParent);
|
|
|
|
DecursivePopulateListFrame:ClearAllPoints();
|
|
DecursivePopulateListFrame:SetPoint("CENTER", UIParent);
|
|
|
|
D.MFContainer:ClearAllPoints();
|
|
D.MFContainer:SetPoint("CENTER", UIParent, "CENTER", 0, 0);
|
|
|
|
DecursiveAnchor:ClearAllPoints();
|
|
DecursiveAnchor:SetPoint("TOP", UIErrorsFrame, "BOTTOM", 0, 0);
|
|
|
|
end --}}}
|
|
|
|
|
|
function D:PlaySound (UnitID, Caller) --{{{
|
|
if self.profile.PlaySound and not self.Status.SoundPlayed then
|
|
local Debuffs = self:UnitCurableDebuffs(UnitID, true);
|
|
if Debuffs and Debuffs[1] and Debuffs[1].Type then
|
|
|
|
-- good sounds: Sound\\Doodad\\BellTollTribal.wav
|
|
-- Sound\\interface\\AuctionWindowOpen.wav
|
|
-- Sound\\interface\\AlarmClockWarning3.wav
|
|
PlaySoundFile(self.profile.SoundFile);
|
|
self:Debug("Sound Played! by %s", Caller);
|
|
self.Status.SoundPlayed = true;
|
|
else
|
|
self.UnitDebuffed[UnitID] = false;
|
|
end
|
|
end
|
|
end --}}}
|
|
|
|
-- LIVE-LIST DISPLAY functions {{{
|
|
|
|
|
|
|
|
-- Those set the scalling of the LIVELIST container
|
|
-- SACALING FUNCTIONS {{{
|
|
-- Place the LIVELIST container according to its scale
|
|
function D:PlaceLL () -- {{{
|
|
local UIScale = UIParent:GetEffectiveScale()
|
|
local FrameScale = DecursiveMainBar:GetEffectiveScale();
|
|
local x, y = D.profile.MainBarX, D.profile.MainBarY;
|
|
|
|
-- check if the coordinates are correct
|
|
if x and y and (x + 10 > UIParent:GetWidth() * UIScale or x < 0 or (-1 * y + 10) > UIParent:GetHeight() * UIScale or y > 0) then
|
|
x = false; -- reset to default position
|
|
T._FatalError("Decursive's bar position reset to default");
|
|
end
|
|
|
|
-- 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) / 2;
|
|
y = - (UIParent:GetHeight() * UIScale) / 8;
|
|
|
|
D.profile.MainBarX = x;
|
|
D.profile.MainBarY = y;
|
|
end
|
|
|
|
-- set to the scaled position
|
|
DecursiveMainBar:ClearAllPoints();
|
|
DecursiveMainBar:SetPoint("TOPLEFT", UIParent, "TOPLEFT", x/FrameScale , y/FrameScale);
|
|
DcrLiveList:ClearAllPoints();
|
|
DcrLiveList:SetPoint("TOPLEFT", DecursiveMainBar, "BOTTOMLEFT");
|
|
end -- }}}
|
|
|
|
-- Save the position of the frame without its scale
|
|
function D:SaveLLPos () -- {{{
|
|
if self.profile and DecursiveMainBar:IsVisible() then
|
|
-- We save the unscalled position (no problem if the sacale is changed behind our back)
|
|
self.profile.MainBarX = DecursiveMainBar:GetEffectiveScale() * DecursiveMainBar:GetLeft();
|
|
self.profile.MainBarY = DecursiveMainBar:GetEffectiveScale() * DecursiveMainBar:GetTop() - UIParent:GetHeight() * UIParent:GetEffectiveScale();
|
|
|
|
|
|
if self.profile.MainBarX < 0 then
|
|
self.profile.MainBarX = 0;
|
|
end
|
|
|
|
if self.profile.MainBarY > 0 then
|
|
self.profile.MainBarY = 0;
|
|
end
|
|
|
|
end
|
|
end -- }}}
|
|
|
|
-- set the scaling of the LIVELIST container according to the user settings
|
|
function D:SetLLScale (NewScale) -- {{{
|
|
|
|
-- save the current position without any scaling
|
|
D:SaveLLPos ();
|
|
-- Set the new scale
|
|
DecursiveMainBar:SetScale(NewScale);
|
|
DcrLiveList:SetScale(NewScale);
|
|
-- Place the frame adapting its position to the news cale
|
|
D:PlaceLL ();
|
|
|
|
end -- }}}
|
|
-- }}}
|
|
|
|
|
|
-- }}}
|
|
|
|
-- // }}}
|
|
-------------------------------------------------------------------------------
|
|
|
|
do
|
|
local iterator = 1;
|
|
local DebuffHistHashTable = {};
|
|
|
|
function D:Debuff_History_Add( DebuffName, DebuffType )
|
|
|
|
if not DebuffHistHashTable[DebuffName] then
|
|
|
|
-- reset iterator if out of boundaries
|
|
if iterator > DC.DebuffHistoryLength then
|
|
iterator = 1;
|
|
end
|
|
|
|
-- clean hastable if necessary before adding a new entry
|
|
if DebuffHistHashTable[D.DebuffHistory[iterator]] then
|
|
DebuffHistHashTable[D.DebuffHistory[iterator]] = nil;
|
|
end
|
|
|
|
-- Register the name in the HashTable using the debuff type
|
|
DebuffHistHashTable[DebuffName] = (DebuffType and DC.NameToTypes[DebuffType] or DC.NOTYPE);
|
|
--D:Debug(DebuffName, DebuffHistHashTable[DebuffName]);
|
|
|
|
-- Put this debuff in our history
|
|
D.DebuffHistory[iterator] = DebuffName;
|
|
|
|
-- This is a useless comment
|
|
iterator = iterator + 1;
|
|
end
|
|
|
|
end
|
|
|
|
function D:Debuff_History_Get (Index, Colored)
|
|
|
|
local HumanIndex = iterator - Index;
|
|
|
|
if HumanIndex < 1 then
|
|
HumanIndex = HumanIndex + DC.DebuffHistoryLength;
|
|
end
|
|
|
|
if not D.DebuffHistory[HumanIndex] then
|
|
return "|cFF777777Empty|r", false;
|
|
end
|
|
|
|
if Colored then
|
|
--D:Debug(D.DebuffHistory[HumanIndex], DebuffHistHashTable[D.DebuffHistory[HumanIndex]]);
|
|
return D:ColorText(D.DebuffHistory[HumanIndex], "FF" .. DC.TypeColors[DebuffHistHashTable[D.DebuffHistory[HumanIndex]]]), true;
|
|
else
|
|
return D.DebuffHistory[HumanIndex], true;
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
-- Scanning functionalities {{{
|
|
-------------------------------------------------------------------------------
|
|
|
|
do
|
|
|
|
local Name, rank, Texture, Applications, TypeName, Duration, expirationTime;
|
|
local D = _G.Dcr;
|
|
local UnitAura = _G.UnitAura;
|
|
|
|
-- This function only returns interesting values of UnitDebuff()
|
|
local function GetUnitDebuff (Unit, i) --{{{
|
|
|
|
if D.LiveList.TestItemDisplayed and i == 1 and Unit ~= "target" and Unit ~= "mouseover" and UnitExists(Unit) then
|
|
D:Debug("|cFFFF0000Setting test debuff for %s (debuff %d)|r", Unit, i);
|
|
return "Test item", DC.TypeNames[D.Status.ReversedCureOrder[1]], 2, "Interface\\AddOns\\Decursive\\iconON.tga", D.LiveList.TestItemDisplayed + 70;
|
|
end
|
|
|
|
--D:Debug("|cFFFF0000Getting debuffs for %s , id = %d|r", Unit, i);
|
|
|
|
|
|
-- Name, rank, Texture, Applications, TypeName, duration, expirationTime, unitCaster, isStealable = UnitAura("unit", index or ["name", "rank"][, "filter"])
|
|
|
|
local Name, rank, Texture, Applications, TypeName, Duration, expirationTime = UnitAura(Unit, i, "HARMFUL");
|
|
|
|
--local Name, rank, Texture, Applications, TypeName, Duration = UnitDebuff(Unit, i);
|
|
|
|
if Name then
|
|
return Name, TypeName, Applications, Texture, expirationTime;
|
|
else
|
|
return false, false, false, false, false;
|
|
end
|
|
end --}}}
|
|
|
|
-- there is a known maximum number of unit and a known maximum debuffs per unit so lets allocate the memory needed only once. Memory will be allocated when needed and re-used...
|
|
local DebuffUnitCache = {};
|
|
|
|
-- Variables are declared outside so that Lua doesn't initialize them at each call
|
|
local Name, Type, i, StoredDebuffIndex, CharmFound, IsCharmed;
|
|
|
|
local DcrC = DcrC; -- for faster access
|
|
|
|
local UnitIsCharmed = _G.UnitIsCharmed;
|
|
local UnitCanAttack = _G.UnitCanAttack;
|
|
local GetTime = _G.GetTime;
|
|
local IsSpellInRange = _G.IsSpellInRange;
|
|
|
|
-- This is the core debuff scanning function of Decursive
|
|
-- This function does more than just reporting Debuffs. it also detects charmed units
|
|
|
|
function D:GetUnitDebuffAll (Unit) --{{{
|
|
|
|
-- create a Debuff table for this unit if there is not already one
|
|
if (not DebuffUnitCache[Unit]) then
|
|
DebuffUnitCache[Unit] = {};
|
|
end
|
|
|
|
-- This is just a shortcut for easier readability
|
|
local ThisUnitDebuffs = DebuffUnitCache[Unit];
|
|
|
|
i = 1; -- => to index all debuffs
|
|
StoredDebuffIndex = 1; -- => this index only debuffs with a type
|
|
CharmFound = false; -- => avoid to find that the unit is charmed again and again...
|
|
|
|
|
|
-- test if the unit is mind controlled once
|
|
if (UnitIsCharmed(Unit) and (UnitCanAttack("player", Unit) or UnitIsCharmed("player"))) then
|
|
IsCharmed = true;
|
|
else
|
|
IsCharmed = false;
|
|
end
|
|
|
|
-- iterate all available debuffs
|
|
while (true) do
|
|
Name, TypeName, Applications, Texture, expirationTime = GetUnitDebuff(Unit, i);
|
|
|
|
if not Name then
|
|
break;
|
|
end
|
|
|
|
|
|
-- test for a type (Magic Curse Disease or Poison)
|
|
if (TypeName and TypeName ~= "") then
|
|
Type = DC.NameToTypes[TypeName];
|
|
else
|
|
Type = false;
|
|
end
|
|
|
|
-- implement the test for DominateMind I HATE stupid exceptions like this one... so many hours lost because of this :/
|
|
--[=[
|
|
if Name == DS["YOGGG_DOMINATE_MIND"] and Type == DC.MAGIC then
|
|
|
|
if DC.MyClass == "PALADIN" then
|
|
IsCharmed = false;
|
|
CharmFound = false;
|
|
else
|
|
IsCharmed = true;
|
|
end
|
|
|
|
if DC.MyClass == "PALADIN" or DC.MyClass == "SHAMAN" then
|
|
|
|
YoggReport = true;
|
|
|
|
D:AddDebugText("|cFFFF9955Decursive Yoggy Debug (try 6):|r", Unit, TypeName, Applications,
|
|
"PC:",DC.MyClass,"IsOvering:", self.Status.MouseOveringMUF, "DN",i, "SDi",StoredDebuffIndex,
|
|
"UICp:",UnitIsCharmed( "player"), "UICu:",UnitIsCharmed(Unit),
|
|
"UCApu:",UnitCanAttack("player", Unit), "IsCharmed:", IsCharmed
|
|
-- "UCAup:",UnitCanAttack(Unit, "player"), "UCApu:",UnitCanAttack("player", Unit), "UIFpu:",UnitIsFriend( "player", Unit), "UIEpu:",UnitIsEnemy( "player", Unit)
|
|
);
|
|
end
|
|
|
|
end
|
|
--]=]
|
|
|
|
-- if the unit is charmed and we didn't took care of this information yet
|
|
if IsCharmed and (not CharmFound or Type == DC.MAGIC) then
|
|
-- If the unit has a magical debuff and we can cure it
|
|
-- (note that the target is not friendly in that case)
|
|
if (Type == DC.MAGIC and self.Status.CuringSpells[DC.ENEMYMAGIC]) then
|
|
Type = DC.ENEMYMAGIC;
|
|
|
|
-- NOTE: if a unit is charmed and has another magical debuff
|
|
-- this block will be executed...
|
|
else -- the unit doesn't have a magical debuff or we can't remove magical debuffs
|
|
Type = DC.CHARMED; -- The player can't remove it anyway so just say the unit is afflicted by a charming effect
|
|
TypeName = DC.TypeNames[DC.CHARMED];
|
|
end
|
|
CharmFound = true;
|
|
end
|
|
|
|
--[=[
|
|
if YoggReport then
|
|
local IsInRange;
|
|
if self.Status.CuringSpells[Type] then
|
|
IsInRange = IsSpellInRange(self.Status.CuringSpells[Type], Unit)
|
|
end
|
|
D:AddDebugText("CharmFound:", CharmFound, "TN:", DC.TypeNames[Type], "ISIR", IsInRange);
|
|
end
|
|
--]=]
|
|
|
|
|
|
-- If we found a type, register the Debuff
|
|
if (Type) then
|
|
-- Create a Debuff index entry if necessary
|
|
if (not ThisUnitDebuffs[StoredDebuffIndex]) then
|
|
ThisUnitDebuffs[StoredDebuffIndex] = {};
|
|
end
|
|
|
|
ThisUnitDebuffs[StoredDebuffIndex].expirationTime = expirationTime;
|
|
ThisUnitDebuffs[StoredDebuffIndex].Texture = Texture;
|
|
ThisUnitDebuffs[StoredDebuffIndex].Applications = Applications;
|
|
ThisUnitDebuffs[StoredDebuffIndex].TypeName = TypeName;
|
|
ThisUnitDebuffs[StoredDebuffIndex].Type = Type;
|
|
ThisUnitDebuffs[StoredDebuffIndex].Name = Name;
|
|
ThisUnitDebuffs[StoredDebuffIndex].index = i;
|
|
|
|
-- we can't use i, else we wouldn't have contiguous indexes in the table
|
|
StoredDebuffIndex = StoredDebuffIndex + 1;
|
|
end
|
|
|
|
i = i + 1;
|
|
end
|
|
|
|
-- erase remaining unused entries without freeing the memory (less garbage)
|
|
while (ThisUnitDebuffs[StoredDebuffIndex]) do
|
|
ThisUnitDebuffs[StoredDebuffIndex].Type = false;
|
|
StoredDebuffIndex = StoredDebuffIndex + 1;
|
|
end
|
|
|
|
-- if no debuff on the unit then it can't be charmed... FUCKING LAG!!
|
|
if i == 1 then
|
|
IsCharmed = false;
|
|
end
|
|
|
|
return ThisUnitDebuffs, IsCharmed;
|
|
end --}}}
|
|
end
|
|
|
|
|
|
do
|
|
-- see the comment about DebuffUnitCache
|
|
local ManagedDebuffUnitCache = D.ManagedDebuffUnitCache;
|
|
|
|
|
|
local D = D;
|
|
local _ = false;
|
|
local CureOrder;
|
|
local sorting = function (a, b)
|
|
|
|
CureOrder = D.classprofile.CureOrder; -- LUA is too simple, lets do the access optimization...
|
|
|
|
local cura = (a.Type and CureOrder[a.Type] and CureOrder[a.Type] > 0) and CureOrder[a.Type] or 1024;
|
|
local curb = (b.Type and CureOrder[b.Type] and CureOrder[b.Type] > 0) and CureOrder[b.Type] or 1024;
|
|
|
|
return cura < curb;
|
|
end
|
|
|
|
-- This function will return a table containing only the Debuffs we can cure excepts the one we have to ignore
|
|
-- in different conditions.
|
|
function D:UnitCurableDebuffs (Unit, JustOne) -- {{{
|
|
|
|
if not Unit then
|
|
self:Debug("No unit supplied to UnitCurableDebuffs()");
|
|
return false;
|
|
end
|
|
|
|
if (not ManagedDebuffUnitCache[Unit]) then
|
|
ManagedDebuffUnitCache[Unit] = {};
|
|
end
|
|
|
|
local AllUnitDebuffs, IsCharmed = self:GetUnitDebuffAll(Unit); -- always return a table, may be empty though
|
|
|
|
if not (AllUnitDebuffs[1] and AllUnitDebuffs[1].Type ) then -- if there is no curable debuff (a debuff with a type)
|
|
return false, IsCharmed;
|
|
end
|
|
|
|
local Spells = self.Status.CuringSpells; -- shortcut to available spells by debuff type
|
|
|
|
|
|
local ManagedDebuffs = ManagedDebuffUnitCache[Unit]; -- shortcut for readability
|
|
|
|
--self:Debug("Debuffs were found");
|
|
|
|
local DebuffNum = 1; -- number of found debuff (used for indexing)
|
|
|
|
local continue_ = true; -- if we have to ignore a debuff, this will become false
|
|
|
|
|
|
|
|
for _, Debuff in ipairs(AllUnitDebuffs) do
|
|
|
|
if (not Debuff.Type) then -- useless test, only debuffs with a type are returned...
|
|
break;
|
|
end
|
|
continue_ = true;
|
|
|
|
-- test if we have to ignore this debuf {{{ --
|
|
|
|
if self.Status.PlayerOnlyTypes[Debuff.Type] and Unit ~= "player" then -- if this type is curable on the player only
|
|
continue_ = false;
|
|
end
|
|
|
|
if (self.profile.DebuffsToIgnore[Debuff.Name]) then
|
|
-- these are the BAD ones... the ones that make the target immune... abort this unit
|
|
--D:Debug("UnitCurableDebuffs(): %s is ignored", Debuff.Name);
|
|
break; -- exit here
|
|
end
|
|
|
|
if (self.profile.BuffDebuff[Debuff.Name]) then
|
|
-- these are just ones you don't care about (sleepless deam etc...)
|
|
continue_ = false;
|
|
--D:Debug("UnitCurableDebuffs(): %s is not a real debuff", Debuff.Name);
|
|
end
|
|
|
|
if (self.Status.Combat or self.profile.DebuffAlwaysSkipList[Debuff.Name]) then
|
|
local _, EnUClass = UnitClass(Unit);
|
|
if (self.profile.skipByClass[EnUClass]) then
|
|
if (self.profile.skipByClass[EnUClass][Debuff.Name]) then
|
|
-- these are just ones you don't care about by class while in combat
|
|
|
|
-- This lead to a problem because once the fight is finished there are no event to trigger
|
|
-- a rescan of this unit, so the debuff does not appear...
|
|
|
|
-- solution to the above problem:
|
|
|
|
if not self.profile.DebuffAlwaysSkipList[Debuff.Name] then
|
|
self:AddDelayedFunctionCall("ReScan"..Unit, D.MicroUnitF.UpdateMUFUnit, D.MicroUnitF, Unit);
|
|
end
|
|
|
|
D:Debug("UnitCurableDebuffs(): %s is configured to be skipped", Debuff.Name);
|
|
continue_ = false;
|
|
end
|
|
end
|
|
end
|
|
|
|
-- }}}
|
|
|
|
|
|
if continue_ then
|
|
-- self:Debug("Debuffs matters");
|
|
-- If we are still here it means that this Debuff is something not to be ignored...
|
|
|
|
|
|
-- We have a match for this type and we decided (checked) to
|
|
-- cure it NOTE: self.classprofile.CureOrder[DEBUFF_TYPE] is set
|
|
-- to FALSE when the type is unchecked and to < 0 when there is
|
|
-- no spell available for the type or when the spell is gone
|
|
-- (it happens for warlocks or when using the same profile with
|
|
-- several characters)
|
|
--if (self.classprofile.CureOrder[Debuff.Type] and self.classprofile.CureOrder[Debuff.Type] > 0) then
|
|
if (self:GetCureCheckBoxStatus(Debuff.Type)) then
|
|
|
|
|
|
-- self:Debug("we can cure it");
|
|
|
|
-- if we do have a spell to cure
|
|
if (Spells[Debuff.Type]) then
|
|
-- The user doesn't want to cure a unit afllicted by poison or disease if the unit
|
|
-- is beeing cured by an abolish spell
|
|
|
|
if (self.profile.Check_For_Abolish and (Debuff.Type == DC.POISON and self:CheckUnitForBuffs(Unit, DS["SPELL_ABOLISH_POISON"]) or Debuff.Type == DC.DISEASE and self:CheckUnitForBuffs(Unit, DS["SPELL_ABOLISH_DISEASE"]))) then
|
|
self:Debug("Abolish buff found, skipping");
|
|
else
|
|
-- self:Debug("It's managed");
|
|
|
|
-- create an entry for this debuff index if necessary
|
|
if (not ManagedDebuffs[DebuffNum]) then
|
|
ManagedDebuffs[DebuffNum] = {};
|
|
end
|
|
|
|
-- copy the debuff information to this table.
|
|
self:tcopy(ManagedDebuffs[DebuffNum], Debuff);
|
|
|
|
DebuffNum = DebuffNum + 1;
|
|
|
|
-- the live-list only reports the first debuf found and set JustOne to true
|
|
if (JustOne) then
|
|
break;
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end -- for END
|
|
|
|
-- erase unused entries without freeing the memory (less garbage)
|
|
if (not JustOne or DebuffNum == 1) then -- if JustOne is set don't clear anything except if we found nothing
|
|
while (ManagedDebuffs[DebuffNum]) do
|
|
ManagedDebuffs[DebuffNum].Type = false;
|
|
-- ManagedDebuffs[DebuffNum].TimeStamp = false;
|
|
DebuffNum = DebuffNum + 1;
|
|
end
|
|
end
|
|
|
|
-- sort the table only if it's not 'empty' and only if there is at least two debuffs
|
|
if (ManagedDebuffs[1] and ManagedDebuffs[1].Type) then
|
|
|
|
-- order Debuffs according to type priority order
|
|
if (not JustOne and ManagedDebuffs[2] and ManagedDebuffs[2].Type) then
|
|
t_sort(ManagedDebuffs, sorting); -- uses memory..
|
|
end
|
|
return ManagedDebuffs, IsCharmed;
|
|
else
|
|
return false, IsCharmed;
|
|
end
|
|
|
|
end -- // }}}
|
|
|
|
local GetTime = _G.GetTime;
|
|
local Debuffs = {}; local IsCharmed = false; local Unit; local MUF; local IsDebuffed= false; local CheckStealth = false;
|
|
local NoScanStatuses = false;
|
|
function D:ScanEveryBody()
|
|
|
|
if not NoScanStatuses then
|
|
NoScanStatuses = {[DC.ABSENT] = true, [DC.FAR] = true, [DC.BLACKLISTED] = true};
|
|
end
|
|
|
|
local UnitArray = self.Status.Unit_Array; local i = 1;
|
|
local CheckStealth = self.profile.Show_Stealthed_Status;
|
|
|
|
--[===[@debug@
|
|
local start = GetTime();
|
|
--@end-debug@]===]
|
|
|
|
while UnitArray[i] do
|
|
Unit = UnitArray[i];
|
|
MUF = self.MicroUnitF.UnitToMUF[Unit];
|
|
|
|
if MUF and not NoScanStatuses[MUF.UnitStatus] then
|
|
Debuffs, IsCharmed = self:UnitCurableDebuffs(Unit, true);
|
|
|
|
if CheckStealth then
|
|
self.Stealthed_Units[Unit] = self:CheckUnitStealth(Unit); -- update stealth status
|
|
end
|
|
|
|
IsDebuffed = (Debuffs and true) or IsCharmed;
|
|
-- If MUF disagrees
|
|
if IsDebuffed ~= MUF.IsDebuffed and not D:DelayedCallExixts("Dcr_Update" .. Unit) then
|
|
--[===[@debug@
|
|
if IsDebuffed then
|
|
self:AddDebugText("delayed debuff found by scaneveryone, scheduling analysis in 1s");
|
|
--D:ScheduleDelayedCall("Dcr_lateanalysis" .. Unit, self.MicroUnitF.LateAnalysis, 1, self.MicroUnitF, "ScanEveryone", Debuffs, MUF, MUF.UnitStatus);
|
|
else
|
|
self:AddDebugText("delayed UNdebuff found by scaneveryone on", Unit);
|
|
end
|
|
--@end-debug@]===]
|
|
|
|
self.MicroUnitF:UpdateMUFUnit(Unit, true);
|
|
|
|
--[===[@debug@
|
|
D:Println("HAAAAAAA!!!!!");
|
|
--@end-debug@]===]
|
|
end
|
|
end
|
|
|
|
i = i + 1;
|
|
end
|
|
--[===[@debug@
|
|
--D:Debug("|cFF777777Scanning everybody...", i - 1, "units scanned in ", GetTime() - start, "seconds|r");
|
|
--@end-debug@]===]
|
|
end
|
|
|
|
|
|
-- a little test... the ".." way wins (6x faster than the format solution) when both sides are strings
|
|
function D:tests()
|
|
|
|
local test = "test1";
|
|
local start = GetTime();
|
|
local strings = {"string1", "string2", "strring3"};
|
|
local teststring = "unitraid5"
|
|
for i =1, 1000000 do
|
|
teststring = strings[i%3 + 1];
|
|
test = "test_"..teststring;
|
|
end
|
|
D:Debug("pass (\"\".. completed in:", GetTime() - start, test);
|
|
|
|
start = GetTime();
|
|
for i =1, 1000000 do
|
|
local t = strings[i%3 + 1];
|
|
test = ("test_%s"):format(teststring);
|
|
end
|
|
D:Debug("pass format completed in:", GetTime() - start, test);
|
|
|
|
end
|
|
|
|
end
|
|
|
|
local UnitBuffsCache = {};
|
|
|
|
-- this function returns true if one of the debuff(s) passed to it is found on the specified unit
|
|
function D:CheckUnitForBuffs(Unit, BuffNamesToCheck) --{{{
|
|
|
|
-- --[=[
|
|
if (not UnitBuffsCache[Unit]) then
|
|
UnitBuffsCache[Unit] = {};
|
|
end
|
|
|
|
local UnitBuffs = UnitBuffsCache[Unit];
|
|
local i = 1;
|
|
local buff_name = "";
|
|
|
|
-- Get all the unit's buffs
|
|
while true do
|
|
|
|
buff_name = UnitBuff(Unit, i)
|
|
|
|
if not buff_name then
|
|
break;
|
|
end
|
|
|
|
UnitBuffs[i] = buff_name;
|
|
i = i + 1;
|
|
end
|
|
|
|
while UnitBuffs[i] do -- clean the rest of the cache
|
|
UnitBuffs[i] = false;
|
|
i = i + 1;
|
|
end
|
|
--]=]
|
|
|
|
if type(BuffNamesToCheck) ~= "table" then
|
|
|
|
if self:tcheckforval(UnitBuffs, BuffNamesToCheck) then
|
|
return true;
|
|
else
|
|
return false;
|
|
end
|
|
else
|
|
local Buff;
|
|
for _, Buff in pairs(BuffNamesToCheck) do
|
|
|
|
if self:tcheckforval(UnitBuffs, Buff) then
|
|
return true;
|
|
end
|
|
|
|
end
|
|
end
|
|
|
|
return false;
|
|
|
|
end --}}}
|
|
|
|
|
|
D.Stealthed_Units = {};
|
|
|
|
do
|
|
local Stealthed = {DS["Prowl"], DS["Stealth"], DS["Shadowmeld"], DS["Invisibility"], DS["Lesser Invisibility"]}; --, DS["Ice Armor"],};
|
|
|
|
DC.IsStealthBuff = D:tReverse(Stealthed);
|
|
|
|
function D:CheckUnitStealth(Unit)
|
|
if self:CheckUnitForBuffs(Unit, Stealthed) then
|
|
-- self:Debug("Sealth found !");
|
|
return true;
|
|
end
|
|
return false;
|
|
end
|
|
end
|
|
-- }}}
|
|
|
|
|
|
|
|
T._LoadedFiles["Decursive.lua"] = "2.5.1-6-gd3885c5";
|
|
|
|
-- Sin
|