Mercurial > wow > turok
view Turok/Modules/Combat/CombatLog.lua @ 9:9400a0ff8540
Ugh
Timer:
- container update directionality
- talent update iterates over a non-volatile table to carry out updates
- index management steps organized
- talentRow status implemented, returns the spell associated with the talent chosen from that row
CombatLog:
- sort out font controls and unbork arguments
author | Nenue |
---|---|
date | Sun, 21 Feb 2016 13:08:30 -0500 |
parents | a9b8b0866ece |
children | 0b1a2f3dbfc4 |
line wrap: on
line source
--- 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('CT', ...) 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, }, defaultAnimation = 'slide', defaultFont = 'font0', textFonts = { font0 = {"Interface\\Addons\\Turok\\Media\\font\\ArchivoNarrow-Regular.ttf", 18, 'OUTLINE'}, 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-Regular.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', 'slide', 'font3'}, absorbed = {'%s (%d)', 'slide'}, blocked = {'%s {%d}', 'slide'}, pet = {'(%s)', 'slide'}, 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' ct.string:SetFont(unpack(textFonts[defaultFont])) ct.fontKey = 'font0' 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 and fontKey ~= currentFrame.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('swapping font to "'..cWord(fontKey)..')":'), path, size, flags) currentFrame.string:SetFont(path, size, flags) 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 = defaultFont 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 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 if ticks then text = groupedModifier:format(text, ticks) animationType = groupedAnimation fontKey = groupedFont or fontKey end print(cText('** AddCombatMessage(')..cKey(frame:GetName())..cText('):'), text, cWord(tostring(icon):sub(-20,-1)), cWord(animationType), cKey(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('OnDamage'), 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 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, ...) 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 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 defaultFont = mod.db.defaultFont 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) local fontPath, fontSize, fontFlags = unpack(db.textFonts.font0) --- 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", }, } } }