PHP код:
local addon, ns = ...
-- новый модуль
local cd = ns:NewModule("CooldownTracker")
-- начало роста иконок
local initialAnchor = "BOTTOMLEFT"
local size = 19 -- размер обычных кд
local interruptsize = 35 -- размер интераптов
local spacingX = 20 -- отступы между иконками по оси Х
local spacingY = 2 -- отступы по оси У
local growthX = "RIGHT" -- направление роста
local growthY = "DOWN" -- куда будет рости новый ряд после преодоления cols
local cols = 3 -- количество иконок в ряду
-- пилим якорь для обычных иконок
local anchor = CreateFrame("frame", nil, UIParent)
anchor:SetSize(size, size)
anchor:SetPoint("CENTER", -165, 80) -- позиции якоря , отсюда начинается рост иконок
-- текстурка якоря , закомментировать после установки в нужное место
local tt = anchor:CreateTexture()
tt:SetAllPoints()
tt:SetTexture(1,1,1)
-- якорь для интераптов
local interrupt = CreateFrame("frame", nil, UIParent)
interrupt:SetSize(size, size)
interrupt:SetPoint("CENTER", -70, 170)
-- текстурка интераптов
local tt = interrupt:CreateTexture()
tt:SetAllPoints()
tt:SetTexture(0,0,1)
-- база спелов которые хотим отслеживать [айди][кд][интерапт]
local spells = {
--хх Рыцарь смерти хх--
-- Интеррапты
[47476] = {120}, -- Удушение
[47528] = {10, true}, -- Заморозка разума
-- Дэф
[48707] = {45}, -- Антимагический панцирь
[48743] = {120}, -- Смертельный союз
[48792] = {120}, -- Незыблемость льда
[49039] = {120}, -- Перерождение
[51052] = {45}, -- Зона антимагии
-- Бурст
[49016] = {180}, -- Истерия
[49028] = {90}, -- Танцующее руническое оружие
[49206] = {180}, -- Призыв горгульи
-- Остальное
[49576] = {25}, -- Хватка смерти
[49203] = {60}, -- Ненасытная стужа
[47481] = {60}, -- Отгрызть(пет)
--xx Воин хх--
-- Интеррапты
[72] = {12, true}, -- Удар щитом
[6552] = {10, true}, -- Зуботычина
[20252] = {25}, -- Перехват
[100] = {20}, -- Рывок
[6178] = {20}, -- Рывок
[11578] = {20}, -- Рывок
-- Дэф
[18499] = {30}, -- Ярость берсерка
[3411] = {30}, -- Вмешательство
[871] = {300}, -- Глухая оборона
[23920] = {10}, -- Отражение заклинания
[2565] = {60}, -- Блок щитом
[12975] = {180}, -- Ни шагу назад
[676] = {60},
-- Бурст
[46924] = {90}, -- Вихрь клинков
[1719] = {300}, -- Безрассудство
[20230] = {300}, -- Возмездие
-- Остальное
[12809] = {30}, -- Оглушающий удар
[46968] = {20}, -- Ударная волна
[5246] = {180}, -- Устрашающий крик
--хх Черножопник хх--
-- Интеррапты
[54785] = {45}, -- Демонический прыжок
[19244] = {24, true}, --Запрет чар
[19647] = {24, true}, -- Запрет чар
-- Дэф
[48020] = {30}, -- Демонический круг: телепортация
[18708] = {180}, -- Господство скверны
-- Бурст
[30283] = {20}, -- Неистовство Тьмы
[30413] = {20}, -- Неистовство Тьмы
[30414] = {20}, -- Неистовство Тьмы
[47846] = {20}, -- Неистовство Тьмы
[47847] = {20}, -- Неистовство Тьмы
[59672] = {126}, -- Метаморфоза
-- Остальное
[6789] = {120}, -- Лик смерти
[17925] = {120}, -- Лик смерти
[17926] = {120}, -- Лик смерти
[27223] = {120}, -- Лик смерти
[47859] = {120}, -- Лик смерти
[47860] = {120}, -- Лик смерти
[5484] = {40}, -- Вой ужаса
[17928] = {40}, -- Вой ужаса
--хх Шамэн хх--
-- Интеррапты
[57994] = {6, true}, -- Пронизывающий ветер
-- Дэф
[8177] = {15}, -- Тотем заземления
[16188] = {180}, -- Природная стремительность
[30823] = {120}, -- Ярость шамана
-- Бурст
[51533] = {180}, -- Дух дикого волка
[16166] = {180}, -- Покорение стихий
[2825] = {300}, -- Жажда крови
[32182] = {300}, -- Героизм
-- Остальное
[51514] = {45}, -- Сглаз
[59156] = {45}, -- Гром и молния
[59158] = {45}, -- Гром и молния
[59159] = {45}, -- Гром и молния
--хх розовыйбоник хх--
-- Интеррапты
[1766] = {10, true}, -- Пинок
[36554] = {30}, -- Шаг сквозь тень
[408] = {20}, -- Удар по почкам
[8643] = {20}, -- Удар по почкам
-- Дэф
[45182] = {60}, -- Обман смерти
[31224] = {90}, -- Плащ Теней
[39666] = {90}, -- Плащ Теней
[65961] = {90}, -- Плащ Теней
[5277] = {180}, -- Ускользание
[26669] = {180}, -- Ускользание
[1856] = {120}, -- Исчезновение
[1857] = {120}, -- Исчезновение
[26889] = {180}, -- Исчезновение
-- Бурст
[51713] = {60}, -- Танец теней
[51690] = {120}, -- Череда убийств
[14177] = {180}, -- Хладнокровие
-- Остальное
[14185] = {300, resets = {14177, 36554, 2983, 8696, 11305, 5277, 26669, 1856, 1857, 26889}}, -- Подготовка {14177,36554,26889,11305,26669}
[2094] = {120}, -- Ослепление
[51722] = {60}, -- Долой оружие
--хх дрыст хх--
-- Интеррапты
[15487] = {45}, -- Безмолвие
-- Дэф
[33206] = {180}, -- Подавление боли
[47585] = {120}, -- Слияние с Тьмой
-- Бурст
[64044] = {120}, -- Глубинный ужас
-- Остальное
[8122] = {24}, -- Ментальный крик
[8124] = {24}, -- Ментальный крик
[10888] = {24}, -- Ментальный крик
[10890] = {24}, -- Ментальный крик
[34433] = {300}, -- Исчадие тьмы
--хх Гей хх--
[31935] = {25}, -- Длань свободы
-- Дэф
[1044] = {25}, -- Длань свободы
[6940] = {120}, -- Длань жертвенности
[10278] = {180}, -- Длань защиты
[642] = {300}, -- Божественный щит
[498] = {180}, -- Божественная защита
-- Бурст
[31884] = {180}, -- Гнев карателя
-- Остальное
[853] = {45}, -- Молот правосудия
[5588] = {45}, -- Молот правосудия
[5589] = {45}, -- Молот правосудия
[10308] = {45}, -- Молот правосудия
[20066] = {60}, -- Покаяние
[54428] = {60}, -- Святая клятва
--хх Маг хх--
-- Интеррапты
[2139] = {24, true}, -- Антимагия
[44572] = {30}, -- Глубокая заморозка
-- Дэф
[66] = {180}, -- Невидимость
[1953] = {15}, -- Скачок
[45438] = {300}, -- Ледяная глыба
-- Бурст
[12042] = {120}, -- Мощь тайной магии
[12043] = {120}, -- Величие разума
-- Остальное
[11958] = {384, resets = {45438, 44572, 31687}}, -- Холодная хватка ???
[12051] = {240}, -- Прилив сил
[11113] = {30}, -- Взрывная волна
[13018] = {30}, -- Взрывная волна
[13019] = {30}, -- Взрывная волна
[13020] = {30}, -- Взрывная волна
[13021] = {30}, -- Взрывная волна
[27133] = {30}, -- Взрывная волна
[33933] = {30}, -- Взрывная волна
[42944] = {30}, -- Взрывная волна
[42945] = {30}, -- Взрывная волна
[31661] = {20}, -- Дыхание дракона
[33041] = {20}, -- Дыхание дракона
[33042] = {20}, -- Дыхание дракона
[33043] = {20}, -- Дыхание дракона
[42949] = {20}, -- Дыхание дракона
[42950] = {20}, -- Дыхание дракона
-- хх Хунтер хх--
-- Интеррапты
[19503] = {30}, -- Дезориентирующий выстрел
[34490] = {20, true}, -- Глушащий выстрел
-- Дэф
[53271] = {60}, -- Приказ хозяина
[781] = {25}, -- Отрыв
-- Остальное
[19577] = {42}, -- Устрашение
-- Bestial Wrath
[19574] = {120},
-- Wyvern Sting
[27068] = 49012,
[49011] = 49012,
[49012] = {60},
-- Readiness
[23989] = {180, resets={49012,34600,63670,19263,3034,34490}},
-- Pin (Crab)
[53547] = 53548,
[53548] = {40},
-- Pummel (Gorilla)
[26090] = {30},
-- Frost Trap
[13809] = 14311,
-- Freezing Arrow
[60202] = 14311,
-- Freezing Trap
[14311] = {30},
-- Snake Trap
[34600] = {30},
-- Deterrence
[19263] = {90},
--хх Друид хх--
-- Интеррапты
[49376] = {30}, -- Звериная атака - кошка
[16979] = {15, true}, -- Звериная атака - медведь кик
[5211] = {30}, -- Оглушить
[6798] = {30}, -- Оглушить
[8983] = {30}, -- Оглушить
[50516] = {20}, -- Тайфун
[53223] = {20}, -- Тайфун
[53225] = {20}, -- Тайфун
[53226] = {20}, -- Тайфун
[61384] = {20}, -- Тайфун
-- Дэф
[22812] = {60}, -- Дубовая кожа
[29166] = {180}, -- Озарение
[17116] = {120}, -- Природная стремительность
[22842] = {180}, -- Неистовое восстановление
[61336] = {180}, -- Инстинкты выживания
-- Бурст
[48505] = {90}, -- Звездопад
[53199] = {90}, -- Звездопад
[53200] = {90}, -- Звездопад
[53201] = {90}, -- Звездопад
[50334] = {180}, -- Берсерк
-- Остальное
[16689] = {60}, -- Хватка природы
[16810] = {60}, -- Хватка природы
[16811] = {60}, -- Хватка природы
[16821] = {60}, -- Хватка природы
[16813] = {60}, -- Хватка природы
[17329] = {60}, -- Хватка природы
[27009] = {60}, -- Хватка природы
[53312] = {60}, -- Хватка природы
}
-- массив с иконками и активными кд
local Icons, ActiveCD = {}, {}
-- нужен небольшой фикс для трап ханта , незачем выводить иконку разных трап
local GetSpell = function(spellID)
if( tonumber(spells[spellID]) ) then
return spells[spells[spellID]]
end
return spells[spellID]
end
-- очищаем иконки по айди гуид-спеллид
local RemoveTimerByID = function(id)
for i = 1, #ActiveCD do
local cd = ActiveCD[i]
if cd and id==cd.id then
table.remove(ActiveCD, i):Hide()
end
end
end
-- обновляем позиции
local UpdatePosition = function()
-- обработка небольшого конфига который писал в начале
local sizex = size + spacingX
local sizey = size + spacingY
local growthx = (growthX == "LEFT" and -1) or 1
local growthy = (growthY == "DOWN" and -1) or 1
local col1, col2 = 1, 1 -- счетчик активных иконок
for i = 1, #ActiveCD do
local button = ActiveCD[i]
if(not button) then break end -- выходим из цикла если активных иконок нет
button:ClearAllPoints()
if button.interrupt then -- позиции интерапта
button:SetPoint("TOPLEFT", interrupt, "TOPLEFT", 0, (col2-1) * interruptsize + spacingY)
col2 = col2 + 1
else
local col = (col1 - 1) % cols -- предыдущая иконка
local row = math.floor((col1 - 1) / cols) -- начало и конец новой строчки
button:SetPoint(initialAnchor, anchor, initialAnchor, col * sizex * growthx, row * sizey * growthy)
col1 = col1 + 1
end
end
end
-- выводим таймер и прячем "отработаные иконки"
local OnUpdate
do
local FormatTime = ns.FormatTime
OnUpdate = function(self)
local endTime = self.timeLeft - GetTime()
if endTime > 0 then
local text = self.text
if endTime > 2 then -- меняем цвет текста на красный если до конца кд осталось меньше 2 секунд
text:SetTextColor(1,1,1)
else
text:SetTextColor(1,0,0)
end
text:SetText(FormatTime(endTime))
else
for i = 1, #ActiveCD do
if ActiveCD[i].id==self.id then
table.remove(ActiveCD, i) -- кд больше не активно, удаляем его
break
end
end
UpdatePosition()
self:Hide()
end
end
end
-- создаем иконку
local createIcon = function()
local frame = CreateFrame("Frame", nil, anchor)
frame:SetScript("OnUpdate", OnUpdate)
frame.icon = frame:CreateTexture(nil, "BACKGROUND")
frame.icon:SetAllPoints()
frame.text = frame:CreateFontString(nil, "BACKGROUND")
frame.text:SetFontObject(GameFontHighlight)
frame.text:SetPoint("LEFT", frame, "RIGHT", 0, 0)
return frame
end
-- создаем таймер
local CreateTimer = function(sourceGUID, sourceName, duration, spellID, interrupt)
local id = sourceGUID .. spellID
local icon = Icons[#ActiveCD+1] -- не будем загажвать память создавая новую иконку только в том случае если для таймера нет неактивной иконки
if( not icon ) then
icon = createIcon()
end
-- необходимо для фильтрации интераптов и сброса кд спелами типа препы
icon.id = id
icon.spellID = spellID
icon.sourceGUID = sourceGUID
icon.sourceName = sourceName
icon.interrupt = interrupt
icon.timeLeft = GetTime() + duration
icon.icon:SetTexture(select(3, GetSpellInfo(spellID)))
if interrupt then
icon:SetSize(interruptsize, interruptsize) -- разные размеры для интераптов и спелов
else
icon:SetSize(size, size)
end
icon:Show()
table.insert(ActiveCD, icon)
UpdatePosition()
end
local COMBATLOG_OBJECT_REACTION_HOSTILE = COMBATLOG_OBJECT_REACTION_HOSTILE
local COMBATLOG_OBJECT_AFFILIATION_MINE = COMBATLOG_OBJECT_AFFILIATION_MINE -- ниже небольшой фильтр, мы хотим отслеживать только врагов
local COMBAT_LOG_EVENT_UNFILTERED = function(self, event, timestamp, eventType, sourceGUID, sourceName, sourceFlags, destGUID, destName, destFlags, ...)
if( eventType == "SPELL_CAST_SUCCESS" and bit.band(sourceFlags, COMBATLOG_OBJECT_REACTION_HOSTILE) == COMBATLOG_OBJECT_REACTION_HOSTILE ) then
local spellID = ...
local cd = GetSpell(spellID) -- можно и напрямую из массива , эхх чертовы охотники с их трапами ....
if not cd then return end
local resets = cd.resets
if resets then
for i = 1, #resets do
RemoveTimerByID(sourceGUID .. resets[i]) -- сбрасываем иконки , например после использования препы
end
end
CreateTimer(sourceGUID, sourceName, cd[1], spellID, cd[2] or false)
end
end
-- сбрасываем иконки при каждой загрузке экрана (например заход на арену)
local PLAYER_ENTERING_WORLD = function()
for i = 1, #ActiveCD do
local cd = ActiveCD[i]
if cd then
-- cd:Hide()
-- ActiveCD[i] = nil
table.remove(ActiveCD, i):Hide() -- ну да эту чудо возвращает элемент массива который собирается удалить))
end
end
UpdatePosition()
end
-- регистрируем ивенты
function cd:Init()
self:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED", COMBAT_LOG_EVENT_UNFILTERED)
self:RegisterEvent("PLAYER_ENTERING_WORLD", PLAYER_ENTERING_WORLD)
end