Mercurial > wow > turok
diff Turok/Modules/Combat/CombatLog.lua @ 6:a9b8b0866ece
clear out log jam
author | Nenue |
---|---|
date | Sun, 21 Feb 2016 08:32:53 -0500 |
parents | |
children | 9400a0ff8540 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Turok/Modules/Combat/CombatLog.lua Sun Feb 21 08:32:53 2016 -0500 @@ -0,0 +1,758 @@ +--- Combat +-- @file-author@ +-- @project-revision@ @project-hash@ +-- @file-revision@ @file-hash@ +-- Created: 1/11/2016 4:27 PM +--- Data collector for API Combat Log Event +local T, _G = Turok, _G +local mod = T:NewModule("Combat") +local pairs, ipairs, select, concat, format, tinsert, wipe = pairs, ipairs, select, table.concat, string.format, tinsert, table.wipe +local GetSpellTexture, CreateFrame, GetTime, unpack, floor = GetSpellTexture, CreateFrame, GetTime, unpack, floor +local db +local cText, cNum, cKey, cWord, cPink, cType, cBool = cText, cNum, cKey, cWord, cPink, cType, cBool +local print = function(...) + if _G.Devian and _G.DevianDB.workspace ~= 1 then + _G.print('CombatText', ...) + end +end + + +--- performance constants +local SCT_NAME = 'TurokSCT%s' +local SCT_TEMPLATE = 'TurokSCTAnchorTemplate' +local SCT_PREGAME = 5 +local SCT_MESSAGE_TEMPLATE = 'TurokCombatMessageTemplate' +local SCT_MESSAGE_NAME = 'CombatString%d' +local LOG_TEMPLATE = 'TurokCombatLogAnchorTemplate' + +--- UX structures +T.defaults.CombatText = { + width = 400, height = 800, + parent = 'UIParent', + Outgoing = { + anchor = 'RIGHT', anchorTo = 'RIGHT', + x = -400, y = -200, + }, + Incoming = { + anchor = 'LEFT', anchorTo = 'LEFT',parent = 'UIParent', + x = 400, y = -200, + }, + DoTTracker = { + anchor = 'BOTTOMLEFT', anchorTo = 'BOTTOM', parent = 'UIParent', + x = 80, y = 400, + }, + defaultFont = {"Interface\\Addons\\Turok\\Media\\font\\ArchivoNarrow-Regular.ttf", 18, 'OUTLINE'}, + defaultAnimation = 'slide', + + textFonts = { + font1 = {"Interface\\Addons\\Turok\\Media\\font\\ArchivoNarrow-Regular.ttf" , 30, 'OUTLINE'}, + font2 = {"Interface\\Addons\\Turok\\Media\\font\\ArchivoNarrow-Regular.ttf" , 24, 'OUTLINE'}, + font3 = {"Interface\\Addons\\Turok\\Media\\font\\ArchivoNarrow-Bold.ttf" , 20, 'OUTLINE'}, + }, + + --- [(string) combatEvent] = {[1] = (string) format [, [2] = fontKey]} + --- Substitution values + -- %d [1] amount [2] overkill [3] absorbed [4] blocked + -- %s [5] spell [6] caster [7] school + textFormat = { + ['SWING_DAMAGE'] = {'%d'}, + ['SPELL_DAMAGE'] = {'%d'}, + ['RANGE_DAMAGE'] = {'%d'}, + Incoming = { + ['SWING_DAMAGE'] = {'-%d'}, + ['SPELL_DAMAGE'] = {'-%d (%s)'}, + ['RANGE_DAMAGE'] = {'-%d'}, + }, + }, + textModifiers ={ + critical = {'%s!', 'pop'}, + overKill = {'%s |cFF0088FFKilling Blow!|r', 'slide'}, + multistrike = {'<%s>', 'lateralSlide', 'font3'}, + absorbed = {'%s (%d)', 'slide'}, + blocked = {'%s {%d}', 'slide'}, + pet = {'(%s)', 'lateralSlide'}, + grouped = {'%s (%d hit)', 'slide'}, + }, + --- [AnimationGroup key] = {[XML attrib] = [value], d[x/y] = (number) 0-1 } + -- d[x/y] indicates the proportional relevance of each FontString dimension when frames are displaced by a new event + -- x/y indicates the ranges of movement made by an animation, and are also considered when calculating displacement + animation = { + slide = { + x = 0, dx = 0, + y = 300, dy = 1, + duration = 2 + }, + lateralSlide = { + x = 300, dx = 1, + y = 0, dy = 0, + duration = 2 + }, + pop = { + toScale = 1.4, + fromScale = 0.1, + duration = 0.14, + dx = 0, dy = 1, + }, + fadeOut = {change = -1, duration = 0.5}, + }, +} +--- [1] text wrapper [2] animation type +local dotEvents = { + SPELL_AURA_APPLIED = true, + SPELL_PERIODIC_DAMAGE = true, + SPELL_AURA_REMOVED = true, + SPELL_AURA_REFRESHED = true, +} +local petGUID = {} + +------------- data structures +local defaultAnimation, defaultFont +local criticalModifier, absorbedModifier, blockedModifier, overKillModifier, multistrikeModifier, groupedModifier +local criticalAnimation, absorbedAnimation, blockedAnimation, overKillAnimation, multistrikeAnimation, groupedAnimation +local criticalFont, absorbedFont, blockedFont, overKillFont,multistrikeFont, groupedFont +local textFonts = {} +local textFormat = {} +local animation = {} +local spellCache = {} +local DoTFrames = {} + +local dotTrackingEvents = { + ['SPELL_AURA_APPLIED'] = true, + ['SPELL_AURA_REMOVED'] = true, + ['SPELL_AURA_REFRESHED'] = true, +} + +--- multi-hit events data +-- [(string) localized name] = { +-- [1] = (bool) controlled channel +-- [2] = (number) minimum time to wait for next damage tick +-- [3] = (number) maximum time to log grouped spell hits +-- [4] = (string) combatEvent full string +-- [5] = (string) combatEvent short prefix (as opposed to SPELL_PERIODIC) +-- [6] = (string) combatEvent short suffix (as opposed to AURA_APPLIED) +-- } +-- tailing fields are set to reduce the number of arguments passed around +local groupedSpells = { + global = {false, 1, 'SPELL_PERIODIC_DAMAGE', 'SPELL', 'DAMAGE'}, + ['Crimson Tempest'] = {false, 0.3, 'SPELL_DAMAGE', 'SPELL', 'DAMAGE'}, -- shockwave dot + ['Mind Sear'] = {false, 1, 'SPELL_DAMAGE', 'SPELL', 'DAMAGE'}, -- scanning aoe + ['Searing Insanity'] = {false, 1, 'SPELL_DAMAGE', 'SPELL', 'DAMAGE'}, -- scanning aoe + ['Ice Nova'] = {false, 0.4, 'SPELL_DAMAGE', 'SPELL', 'DAMAGE'}, -- shockwave aoe + ['Blizzard'] = {false, 1, 'SPELL_DAMAGE', 'SPELL', 'DAMAGE'}, -- scanning aoe + ['Frozen Orb'] = {false, 1, 'SPELL_DAMAGE', 'SPELL', 'DAMAGE'}, + ['Ice Bomb'] = {false, 0.4, 'SPELL_DAMAGE', 'SPELL', 'DAMAGE'}, -- shockwave aoe + ['Comet Storm'] = {false, 2.5, 'SPELL_DAMAGE', 'SPELL', 'DAMAGE'}, -- staggered multi-hit + ['Barrage'] = {false, 1.1, 'SPELL_DAMAGE', 'SPELL', 'DAMAGE'}, -- projectile vommit + ['Glaive Toss'] = {false, 3, 'SPELL_DAMAGE', 'SPELL', 'DAMAGE'}, -- path projectile, + ['Doom Nova'] = {false, 0.4, 'SPELL_DAMAGE', 'SPELL', 'DAMAGE'} +} + +local dotSpellIndex = { + ['Shadow Word: Pain'] = {'HARMFUL|PLAYER'}, + ['Vampiric Touch'] = {'HARMFUL|PLAYER'} +} + +local offhandSpellIndex = { + ['Execute'] = {'Execute Off-Hand'}, + ['Mutilate'] = {'Mutilate Off-Hand'}, + ['Stormstrike'] = {'Stormstrike Off-Hand'}, +} + +--- stored as a list of field representations to be concatenated together within a loadstring def + + + +--- Tracking tables +local groupedQueue = {} +local groupedEvents = {} +local offhandQueue = {} +local sct_format = {} + + +local function GetGUIDInfo(guid) + local unitType, flags1, flags2 = guid:match('(%a+)\-(%x+)\-(%x+)') + return concat({unitType, flags1, flags2},'|r::|cFF0088FF')..'|r' +end + +local SpellSchoolColors = { + [1] = {255, 255,0}, -- physical + [2] = {255, 230, 128}, -- holy + [3] = {255, 255, 128}, -- holy+phys + [4] = {255, 128, 0}, -- fire + [8] = {77, 255, 77}, -- nature + [16] = {128, 255, 255}, -- frost + [20] = {255, 192, 128}, -- frostfire + [32] = {128, 128, 255}, -- shadow + [48] = {128, 192, 255}, -- shadow+frost + [64] = {255, 128, 255}, -- arcane, + [72] = {255, 128, 128}, -- spellstorm, +} + +local h = '|cFF%02X%02X%02X' +local SpellSchoolColor = function (flags, spellName) + local i = 64 + local rA, gA, bA + if SpellSchoolColors[flags] then + print(flags, 'match') + print(format('%02X%02X%02X', unpack(SpellSchoolColors[flags]))) + return format(h, unpack(SpellSchoolColors[flags])) + end + + repeat + local rB, gB, bB = unpack(SpellSchoolColors[i]) + if i <= flags then + if not rA then + rA = rB + gA = gB + bA = bB + else + rA = (rA+rB)/2 + gA = (gA+gB)/2 + bA = (bA+bB)/2 + end + print('test:', cWord(i), '<=', cKey(flags), '=', (i <= flags), cPink(rA), cNum(gA), cText(bA)) + flags = flags - i + else + print(i, 'skip') + end + i = i/2 + until (i == 1) + SpellSchoolColors[flags] = {rA, gA, bA } + --print(string.format('%02X%02X%02X', unpack(SpellSchoolColors[flags]))) + return format(h, unpack(SpellSchoolColors[flags])) +end +local myGUID + +mod.NewCombatMessage = function(frame, index) + local ct = CreateFrame('Frame', SCT_MESSAGE_NAME:format(frame.lineID), frame, SCT_MESSAGE_TEMPLATE) + index = index and 'active' or 'expired' + frame.lineID = frame.lineID + 1 + frame.active[#frame.active+1] = ct + ct.index = #frame.active + ct.x = 0 + ct.y = 0 + ct.point = 'BOTTOMLEFT' + return ct +end + +--- frame interaction logic +mod.AddCombatMessage = function(frame, text, icon, animationType, fontKey) + local line + print(fontKey) + + local expired = frame.expired + local active = frame.active + local a = 1 + local shiftY, shiftX + local lastFrame + --- If animation has overlap delta values, find the last active frame of that animation type and check for overlaps. + --- Frames are considered overlapping when the last member's ((height - distance traveled) * delta) is over 0. + --- This assumes the same string height for the upcoming frame since wordwrap isn't enabled. + print(animationType) + if animation[animationType].dx or animation[animationType].dy then + print('animation has displacement') + if frame.last then + lastFrame = frame.last + local dp = lastFrame[animationType]:GetProgress() + if animation[animationType].dx then + local dx = dp * animation[animationType].x + shiftX = (lastFrame.string:GetStringWidth() - dx) * animation[animationType].dx + end + if animation[animationType].dy then + local dy = dp * animation[animationType].y + shiftY = (lastFrame.string:GetStringHeight() - dy) * animation[animationType].dy + print(' ', 'h=', floor(lastFrame.string:GetStringHeight()), 'dY=', dy, 'offsetY=', shiftY) + end + print(cWord('lastFrame hit:'), lastFrame and lastFrame:GetName(), cNum(shiftX), cNum(shiftY)) + end + end + + -- find a usable frame + local currentFrame + for i, ct in ipairs(frame.active) do + if not currentFrame then + if ct.discard then + ct.discard= nil + currentFrame = ct + end + end + + if lastFrame and ct.animationType == animationType and not ct.discard then + --print('lastFrame defined, check for overlap') + if shiftY > 0 then + print(cWord(' * vertical shift'), cNum('+'..floor(shiftY))) + ct.y = ct.y + shiftY + end + if shiftX > 0 then + print(cWord(' * horizontal shift'), cNum('+'..floor(shiftX))) + ct.x = ct.x + shiftX + end + ct:SetPoint(ct.point, frame, ct.point, ct.x, ct.y) + end + end + -- if no expired frames became available, make a new one (should max at 20 or so if groupings are right) + if not currentFrame then + currentFrame = mod.NewCombatMessage(frame) + print(cNum(' creating new string object for the heap')) + end + + print(cText(' * Starting'), cPink(animationType), 'on', cKey(currentFrame:GetName()), ' ['..cNum(currentFrame.index)..']') + + if icon then + currentFrame.icon:Show() + currentFrame.icon:SetTexture(icon) + else + currentFrame.icon:Hide() + end + if fontKey then + local newFont = fontKey and textFonts[fontKey] or defaultFont + + local path, size, flags = currentFrame.string:GetFont() + path = newFont[1] or path + size = newFont[2] or size + flags = newFont[3] or flags + print(cText('font ('..cWord(fontKey)..'):'), path, size, flags) + local result = currentFrame.string:SetFont(path, size, flags) + print(cNum(' result:'), cNum(result), currentFrame.string:GetFont()) + --currentFrame.fontKey = fontKey + end + + currentFrame.animationType = animationType + currentFrame.string:SetText(text) + local cHeight = currentFrame.string:GetStringHeight() + currentFrame:SetSize(currentFrame.string:GetStringWidth(), cHeight) + currentFrame.icon:SetSize(cHeight, cHeight) + currentFrame.y = 0 + currentFrame.x = 0 + currentFrame:SetPoint(currentFrame.point, frame, currentFrame.point, currentFrame.x, currentFrame.y) + currentFrame[animationType]:Play() + frame.last = currentFrame +end + +local GetTextFormatFunc = function(amount, overKill, absorbed, blocked, multistrike, spellName, sourceName, destName) + local sub_text = { + ['%d'] = amount, + ['%o'] = overKill, + ['%a'] = absorbed, + ['%b'] = blocked, + ['%m'] = multistrike, + ['%s'] = spellName, + ['%n'] = sourceName, + ['%t'] = destName, + } + local func = function(token) + print(cPink('gsub run:'), token) + return sub_text[token] + end + return func +end + +--- builds CT messages from combat log event data +-- separated from frame interaction for auxiliary events such as combat/loot/etc +local CreateDamageText = function(frame, timestamp, combatEvent, sourceName, destName, spellID, spellName, effectSchool, amount, overKill, resisted, blocked, absorbed, critical, glancing, crushing, isOffHand, multistrike, ticks) + if combatEvent:match('^SWING') then + spellID = 1 + spellName = 'Attack' + elseif combatEvent:match('^RANGE') then + spellID = 2 + spellName = 'Auto Shot' + end + if not combatEvent:match('DAMAGE') then + return + end + + + local fontKey = 'font1' + local icon = GetSpellTexture(spellID) + local animationType = defaultAnimation + local text = amount + if textFormat[combatEvent] then + local tString, fontKey = unpack(textFormat[combatEvent]) + if amount > 1000000 then + amount = (floor(amount/100000)/10) ..'M' + elseif amount > 1000 then + amount = (floor(amount/100)/10) ..'k' + end + + text = tString:gsub('%%[doabsnt]', GetTextFormatFunc(amount, overKill, absorbed, blocked, multistrike, spellName, sourceName, destName)) + print("** font override:", '"'..cText(tString)..'",', cPink(font), cNum(size), cWord(outline)) + end + + print('** getting color data', cText(spellName)) + if type(effectSchool) == 'number' then + text = SpellSchoolColor(effectSchool) ..text..'|r' + end + + if overKill > 0 then + text = overKillModifier:format(text, overKill) + animationType = overKillAnimation + fontKey = overKillFont or fontKey + end + if critical then + text = criticalModifier:format(text) + animationType = criticalAnimation + fontKey = criticalFont or fontKey + end + if absorbed then + text = absorbedModifier:format(text, absorbed) + animationType = absorbedAnimation + fontKey = absorbedFont or fontKey + end + if blocked then + text = blockedModifier:format(text, blocked) + animationType = blockedAnimation + fontKey = blockedFont or fontKey + end + if multistrike then + text = multistrikeModifier:format(text, multistrike) + animationType = multistrikeAnimation + fontKey = multistrikeFont or fontKey + end + + print(ticks) + if ticks then + text = groupedModifier:format(text, ticks) + animationType = groupedAnimation + fontKey = groupedFont or fontKey + end + + + print('** sending format to SCT:', text, icon, animationType, fontKey) + + + mod.AddCombatMessage(frame, text .. '|r', icon, animationType, fontKey) +end + +local CT_ShowConsolidated = function() + for spellName, queuedSpell in pairs(groupedQueue) do + local isChannel, minDelay, combatEvent, sourceName, destName, spellID, school, SCT, timestamp = unpack(queuedSpell) + for i, v in ipairs(queuedSpell) do + print(' ', i, '=', v) + end + + print(spellName, 'vars:', spellID, spellName, school, SCT, timestamp) + if groupedEvents[spellName] and #groupedEvents[spellName] ~= 0 then + local amount, overKill, resisted, blocked, absorbed, critical, glancing, crushing, multistrike, hit = 0, -1, 0, 0, 0, 0, 0, 0, 0, 0 + for i, line in ipairs(groupedEvents[spellName]) do + -- extra strike? + --{amount, overKill, resisted, blocked, absorbed, critical, glancing, crushing, multistrike} + local amount_n, overKill_n, resisted_n, blocked_n, absorbed_n, critical_n, glancing_n, crushing_n, multi_n, sourceGUID, destGUID, sourceName, destName = unpack (line) + + amount = amount + amount_n + if overKill_n > 0 then overKill = overKill + overKill_n end + if blocked_n then blocked = blocked + line[4] end + if absorbed_n then absorbed = absorbed + line[5] end + if critical_n then critical = critical + 1 end + if multi_n then multistrike = multistrike + 1 end + + hit = hit + 1 + end + if overKill == -1 then + overKill = overKill + 1 + end + wipe(groupedEvents[spellName]) + groupedQueue[spellName] = nil + print(' expelling', spellName, cText('A:'), cNum(amount), cText('O:'), cNum(overKill), cText('Ticks:'), cNum(hit), cText('Crits:'), cNum(critical), cText(school)) + print(SCT, timestamp, combatEvent, sourceName, destName, spellID, spellName, school, amount, overKill, resisted, blocked, absorbed, critical, glancing, crushing, multistrike, hit) + CreateDamageText(SCT, timestamp, combatEvent, sourceName, destName, spellID, spellName, school, amount, -1, nil, nil, nil, false, glancing, crushing, false, multistrike, hit) + end + end +end + +local CT_ConsolidateText = function (self, timestamp, sourceGUID, destGUID, sourceName, destName, spellID, spellName, school, amount, overKill, resisted, blocked, absorbed, critical, glancing, crushing, isOffHand, multistrike) + local archive = groupedSpells[spellName] and groupedSpells[spellName] or groupedSpells.global + local isChannel, minDelay, combatEvent = unpack(archive) + if not groupedEvents[spellName] then + groupedEvents[spellName] = {} + end + + if #groupedEvents[spellName] == 0 then + groupedQueue[spellName] = {isChannel, minDelay, combatEvent, sourceName, destName, spellID, school, self, timestamp} + T:ScheduleTimer(CT_ShowConsolidated, minDelay) + print(' starting archive for', select(7, unpack(archive))) + else + print(' recording into archive', cText(spellName)) + end + tinsert(groupedEvents[spellName], {amount, overKill, resisted, blocked, absorbed, critical, glancing, crushing, multistrike, sourceGUID, destGUID, sourceName, destName, sourceName, destName,}) +end + +local CT_OnCast = function(self, event, unit, spellName, lineID, spellID) + if unit ~= 'player' and unit ~= 'pet' then + return + end + + if groupedSpells[spellName] then + print(cText('** Spell casting info received')) + local isChannel, delay = unpack(groupedSpells[spellName]) + if isChannel and event == 'CHANNEL_STOP' and groupedEvents[spellName] then + T:ScheduleTimer(CT_ShowConsolidated, delay) + else + if not groupedEvents[spellName] then + groupedEvents[spellName] = {} + end + end + end +end + +local CT_Unlock = function(str) + for i, CT in pairs(mod.CombatTextFrames) do + local frame = CT.frame + frame.configMode = (frame.configMode == nil) and true or nil + frame:RegisterForDrag(frame.configMode and 'LeftButton' or nil) + frame:EnableMouse(frame.configMode and true or false) + print(i, frame.configMode and 'ON' or 'OFF') + for _, reg in ipairs(frame.configRegions) do + if frame.configMode then + reg:Show() + else + reg:Hide() + end + end + + if frame.configMode then + CT.configTimer = T:ScheduleRepeatingTimer(function() + print(i, 'config tick') + for i, s in ipairs(CT.sample) do + CT.OnEvent(frame, unpack(s)) + end + end, 2) + else + print(CT.configTimer) + T:CancelTimer(CT.configTimer) + end + + end +end + +--- check for sectors +local queue = {} +mod.OnDamage = function(self, event, ...) + local isVisible + + local timestamp, combatEvent = ... + print(cText('* CT'), cKey(combatEvent)) + if combatEvent == 'UNIT_DIED' then + return + end + local sourceGUID, sourceName, sourceFlags, _, destGUID, destName, destFlags, _, spellID, spellName, spellSchool = select(4, ...) + print(' from', cPink(sourceGUID), 'spell', cNum(spellID), cText(spellName), '->', cKey(destGUID)) + + -- SWING starts at arg 10, SPELL/RANGE start at arg 13, ENVIRONMENTAL starts at arg 8 + local offset = 15 + print(combatEvent:sub(0,3)) + if combatEvent:sub(0,3) == 'SWI' then + offset = 10 + elseif combatEvent:sub(0,3) == 'ENV' then + offset = 8 + end + local amount, overKill, effectSchool, resisted, blocked, absorbed, critical, glancing, crushing, isOffHand, multistrike = select(offset, ...) + + print(' dmg', amount, overKill) + + local sc = SpellSchoolColors[effectSchool] + if groupedSpells[spellName] then + print('* ', cText(spellName), 'to consolidator') + CT_ConsolidateText(self, timestamp, sourceGUID, destGUID, sourceName, destName, spellID, spellName, effectSchool, amount, overKill, resisted, blocked, absorbed, critical, glancing, crushing, isOffHand, multistrike) + return + end + + print('displaying on', cWord(self:GetName())) + CreateDamageText(self, timestamp, combatEvent, sourceName, destName, spellID, spellName, effectSchool, amount, overKill, resisted, blocked, absorbed, critical, glancing, crushing, isOffHand, multistrike) +end + +mod.maxDotFrame = 1 +mod.OnDotEvent = function (self, event, timestamp, combatEvent, ...) + print(cWord('DOT'), combatEvent) + local sourceGUID, sourceName, sourceFlags, _, destGUID, destName, destFlags, destExtraFlags, spellID, spellName, castSchool, amount, overKill, resisted, blocked, absorbed, critical, glancing, crushing, isOffHand, multistrike = select(2, ...) + print(cText('dot from'), cWord(sourceGUID), 'to', cKey(destGUID)) + if sourceGUID == T.GUID then + local p = mod.PeriodicTable + p[destGUID] = p[destGUID] or {} + local unit = p[destGUID] + if not p[spellID] then + if not p.frames[#p.frames] then + unit[spellID] = CreateFrame('Frame', 'DotBar'..mod.maxDotFrame, p, 'TkDotBarTemplate') + mod.maxDotFrame = mod.maxDotFrame + 1 + unit[spellID].dotID = mod.maxDotFrame + + print(' create new frame ['..cKey(unit[spellID]:GetID(), '] for'), cWord(spellName), cKey(destGUID)) + else + unit[spellID] = p.frames[#p.frames] + p.frames[#p.frames] = nil -- remove that entry + print(' recycling frame ['..cKey(unit[spellID].dotID, '] for'), cWord(spellName), cKey(destGUID)) + end + + unit[spellID]:Show() + end + local dot = unit[spellID] + local time = GetTime() + end +end + + + + +function mod:OnInitialize() + print('This is a thing.') + self.UNIT_SPELLCAST_SUCCEEDED = CT_OnCast + self.UNIT_SPELLCAST_CHANNEL_START = CT_OnCast + self.UNIT_SPELLCAST_CHANNEL_STOP = CT_OnCast +end + +function mod:OnInitialize() + mod.db = TurokData.CombatText + db = TurokData.CombatText + myGUID = T.GUID + mod.PeriodicTable = mod.PeriodicTable or CreateFrame('Frame', 'TurokPeriodicFrame', UIParent) + mod.PeriodicTable.frames = {} + + local m = mod.db.textModifiers + --- These values are going to be looked up a lot, so cache as close as possible + criticalModifier = m.critical[1] or '%s CRIT' + criticalAnimation = m.critical[2] or 'slide' + criticalFont = m.critical[3] + + absorbedModifier = m.absorbed[1] or '%s ABS' + absorbedAnimation = m.absorbed[2] or 'slide' + absorbedFont = m.absorbed[3] + + blockedModifier = m.blocked[1] or '%s BLK' + blockedAnimation = m.blocked[2] or 'slide' + blockedFont = m.blocked[3] + + overKillModifier = m.overKill[1] or '%s KILL' + overKillAnimation = m.overKill[2] or 'slide' + overKillFont = m.overKill[3] + + multistrikeModifier = m.multistrike[1] or '%s MS' + multistrikeAnimation = m.multistrike[2] or 'slide' + multistrikeFont = m.multistrike[3] + + groupedModifier = m.grouped[1] or '%s' + groupedAnimation = m.grouped[2] or 'slide' + groupedFont = m.grouped[3] + + --- Same as above, but for specific table values, key is determined by the combat event + defaultAnimation = mod.db.defaultAnimation + defaultFont = mod.db.defaultFont + for k,v in pairs(mod.db.textFormat) do + textFormat[k] = {v[1], v[2]} + print('imported textFormat.'..k, cText(textFormat[k][1]), cNum(textFormat[k][2])) + end + for k,v in pairs(mod.db.textFonts) do + textFonts[k] = {v[1] or defaultFont[1], v[2] or defaultFont[2], v[3] or defaultFont[3]} + print('imported font.'..k, cText(textFonts[k][1]), cNum(textFonts[k][2]), cWord(textFonts[k][3])) + end + + for k,v in pairs(mod.db.animation) do + animation[k] = {} + animation[k].x = v.x + animation[k].y = v.y + animation[k].dx = v.dx + animation[k].dy = v.dy + animation[k].fromScale = v.fromScale + animation[k].toScale = v.toScale + animation[k].deviation = v.deviation + animation[k].change = v.change + animation[k].fromAlpha = v.fromAlpha + animation[k].toAlpha = v.toAlpha + animation[k].duration = v.duration + end +end + +function mod:OnEnable() + T:RegisterChatCommand('tkc', CT_Unlock) + + --- Populate CT frames + for name, CT in pairs(mod.CombatTextFrames) do + print('create CT', name) + -- make frame + CT.frame = CT.frame or CreateFrame('Frame', SCT_NAME:format(name), UIParent, SCT_TEMPLATE) + + -- local vars + local db = db[name] or db + local frame = CT.frame + + -- script defs + frame.IsFrameEvent = CT.trigger + frame.name = name + frame.lineID = 0 + frame.expired = {} + frame.active = {} + + -- frame defs + frame:SetPoint(db.anchor, db.parent, db.anchorTo, db.x, db.y) + frame:RegisterEvent('COMBAT_LOG_EVENT_UNFILTERED') + print('bound ', name, 'with', CT.OnEvent) + db.sample = {} + local logged = {} + frame:SetScript('OnEvent', function(self, e,...) + if CT.trigger(e, ...) then + print('event trigger fired', name) + if #db.sample < 5 and not logged[select(2,...)] then + logged[select(2,...)] = true + tinsert(db.sample, {e, ...}) + end + + CT.OnEvent(frame, e, ...) + end + end) + + -- configurators + frame.configHeader:SetText(name) + frame:EnableMouse(false) + + -- pre-pop some text frames + for i = (CT.lineID or 1), SCT_PREGAME do + print(frame:GetName(), 'pre-pop #'..i) + mod.NewCombatMessage(frame) + end + end +end + + +local damageEvents = { + ['SPELL_DAMAGE'] = true, + ['SPELL_PERIODIC_DAMAGE'] = true, + ['SWING_DAMAGE'] = true, + ['RANGE_DAMAGE'] = true, + ['SPELL_HEAL'] = true, +} +mod.CombatTextFrames = { + Incoming = { + trigger = function(e, _, c, _, _, _, _, _, d) return (d == T.GUID and damageEvents[c]) end, + OnEvent = mod.OnDamage, + sample = { + {"COMBAT_LOG_EVENT_UNFILTERED", 1455887795.271, "SPELL_HEAL", false, "Player-3684-07235A4E", "Klakyn", 1297, 0, "Player-3684-07235A4E", "Klakyn", 1297, 0, 143924, "Leech", 1, 93, 93, 0, false, false, }, + } + }, + Outgoing = { + trigger = function(e, _,c,_, s) return (s == T.GUID and damageEvents[c]) end, + OnEvent = mod.OnDamage, + sample = { + { + "COMBAT_LOG_EVENT_UNFILTERED", 1455887795.006, "SPELL_DAMAGE", false, "Player-3684-07235A4E", "Klakyn", 1297, 0, "Creature-0-3684-1116-7-87761-0000C32119", "Dungeoneer's Training Dummy", 68136, 0, 589, "Shadow Word: Pain", 32, 3944, -1, 32, nil, nil, nil, false, false, false, false, false, }, { + "COMBAT_LOG_EVENT_UNFILTERED", 1455887795.271, "SPELL_HEAL", false, "Player-3684-07235A4E", "Klakyn", 1297, 0, "Player-3684-07235A4E", "Klakyn", 1297, 0, 143924, "Leech", 1, 93, 93, 0, false, false, }, { + "COMBAT_LOG_EVENT_UNFILTERED", 1455887797.377, "SPELL_PERIODIC_DAMAGE", false, "Player-3684-07235A4E", "Klakyn", 1297, 0, "Creature-0-3684-1116-7-87761-0000C32119", "Dungeoneer's Training Dummy", 68136, 0, 589, "Shadow Word: Pain", 32, 3944, -1, 32, nil, nil, nil, false, false, false, false, false, }, { + "COMBAT_LOG_EVENT_UNFILTERED", 1455887812.702, "SWING_DAMAGE", false, "Player-3684-07235A4E", "Klakyn", 1297, 0, "Creature-0-3684-1116-7-87761-0000C32119", "Dungeoneer's Training Dummy", 68136, 0, 279, -1, 1, nil, 120, nil, false, false, false, false, false, }, -- [4] + } + }, + DoTTracker = { + trigger = function(e, _, c, _, s) return (s == T.GUID and dotEvents[c]) end, + OnEvent = mod.OnDotEvent, + sample = { + {"COMBAT_LOG_EVENT_UNFILTERED", 1455887795.006, "SPELL_AURA_APPLIED", false, + "Player-3684-07235A4E", "Klakyn", 1297, 0, + "Creature-0-3684-1116-7-87761-0000C32119", "Dungeoneer's Training Dummy", 68136, 0, + 589, "Shadow Word: Pain", 32, "DEBUFF", }, + {"COMBAT_LOG_EVENT_UNFILTERED", 1455887797.377, "SPELL_PERIODIC_DAMAGE", false, + "Player-3684-07235A4E", "Klakyn", 1297, 0, + "Creature-0-3684-1116-7-87761-0000C32119", "Dungeoneer's Training Dummy", 68136, 0, + 589, "Shadow Word: Pain", 32, 3944, -1, 32, nil, nil, nil, false, false, false, false, false, }, + {"COMBAT_LOG_EVENT_UNFILTERED", 1455887807.199, "SPELL_AURA_REMOVED", false, + "Player-3684-07235A4E", "Klakyn", 1297, 0, + "Player-3684-07235A4E", "Klakyn", 1297, 0, + 15473, "Shadowform", 32, "BUFF", }, + } + } +} \ No newline at end of file