| 
Nenue@6
 | 
     1 --- Combat
 | 
| 
Nenue@6
 | 
     2 -- @file-author@
 | 
| 
Nenue@6
 | 
     3 -- @project-revision@ @project-hash@
 | 
| 
Nenue@6
 | 
     4 -- @file-revision@ @file-hash@
 | 
| 
Nenue@6
 | 
     5 -- Created: 1/11/2016 4:27 PM
 | 
| 
Nenue@6
 | 
     6 --- Data collector for API Combat Log Event
 | 
| 
Nenue@6
 | 
     7 local T, _G = Turok, _G
 | 
| 
Nenue@6
 | 
     8 local mod = T:NewModule("Combat")
 | 
| 
Nenue@6
 | 
     9 local pairs, ipairs, select, concat, format, tinsert, wipe = pairs, ipairs, select, table.concat, string.format, tinsert, table.wipe
 | 
| 
Nenue@6
 | 
    10 local GetSpellTexture, CreateFrame, GetTime, unpack, floor = GetSpellTexture, CreateFrame, GetTime, unpack, floor
 | 
| 
Nenue@6
 | 
    11 local db
 | 
| 
Nenue@6
 | 
    12 local cText, cNum, cKey, cWord, cPink, cType, cBool = cText, cNum, cKey, cWord, cPink, cType, cBool
 | 
| 
Nenue@6
 | 
    13 local print = function(...)
 | 
| 
Nenue@6
 | 
    14   if _G.Devian and _G.DevianDB.workspace ~= 1 then
 | 
| 
Nenue@6
 | 
    15     _G.print('CombatText', ...)
 | 
| 
Nenue@6
 | 
    16   end
 | 
| 
Nenue@6
 | 
    17 end
 | 
| 
Nenue@6
 | 
    18 
 | 
| 
Nenue@6
 | 
    19 
 | 
| 
Nenue@6
 | 
    20 --- performance constants
 | 
| 
Nenue@6
 | 
    21 local SCT_NAME = 'TurokSCT%s'
 | 
| 
Nenue@6
 | 
    22 local SCT_TEMPLATE = 'TurokSCTAnchorTemplate'
 | 
| 
Nenue@6
 | 
    23 local SCT_PREGAME = 5
 | 
| 
Nenue@6
 | 
    24 local SCT_MESSAGE_TEMPLATE = 'TurokCombatMessageTemplate'
 | 
| 
Nenue@6
 | 
    25 local SCT_MESSAGE_NAME = 'CombatString%d'
 | 
| 
Nenue@6
 | 
    26 local LOG_TEMPLATE =  'TurokCombatLogAnchorTemplate'
 | 
| 
Nenue@6
 | 
    27 
 | 
| 
Nenue@6
 | 
    28 --- UX structures
 | 
| 
Nenue@6
 | 
    29 T.defaults.CombatText = {
 | 
| 
Nenue@6
 | 
    30   width = 400, height = 800,
 | 
| 
Nenue@6
 | 
    31    parent = 'UIParent',
 | 
| 
Nenue@6
 | 
    32   Outgoing = {
 | 
| 
Nenue@6
 | 
    33     anchor = 'RIGHT', anchorTo = 'RIGHT',
 | 
| 
Nenue@6
 | 
    34     x = -400, y = -200,
 | 
| 
Nenue@6
 | 
    35   },
 | 
| 
Nenue@6
 | 
    36   Incoming = {
 | 
| 
Nenue@6
 | 
    37     anchor = 'LEFT', anchorTo = 'LEFT',parent = 'UIParent',
 | 
| 
Nenue@6
 | 
    38     x = 400, y = -200,
 | 
| 
Nenue@6
 | 
    39   },
 | 
| 
Nenue@6
 | 
    40   DoTTracker = {
 | 
| 
Nenue@6
 | 
    41     anchor = 'BOTTOMLEFT', anchorTo = 'BOTTOM', parent = 'UIParent',
 | 
| 
Nenue@6
 | 
    42     x = 80, y = 400,
 | 
| 
Nenue@6
 | 
    43   },
 | 
| 
Nenue@6
 | 
    44   defaultFont = {"Interface\\Addons\\Turok\\Media\\font\\ArchivoNarrow-Regular.ttf", 18, 'OUTLINE'},
 | 
| 
Nenue@6
 | 
    45   defaultAnimation = 'slide',
 | 
| 
Nenue@6
 | 
    46 
 | 
| 
Nenue@6
 | 
    47   textFonts = {
 | 
| 
Nenue@6
 | 
    48     font1 = {"Interface\\Addons\\Turok\\Media\\font\\ArchivoNarrow-Regular.ttf" , 30, 'OUTLINE'},
 | 
| 
Nenue@6
 | 
    49     font2 = {"Interface\\Addons\\Turok\\Media\\font\\ArchivoNarrow-Regular.ttf" , 24, 'OUTLINE'},
 | 
| 
Nenue@6
 | 
    50     font3 = {"Interface\\Addons\\Turok\\Media\\font\\ArchivoNarrow-Bold.ttf" , 20, 'OUTLINE'},
 | 
| 
Nenue@6
 | 
    51   },
 | 
| 
Nenue@6
 | 
    52 
 | 
| 
Nenue@6
 | 
    53   --- [(string) combatEvent] = {[1] = (string) format [, [2] = fontKey]}
 | 
| 
Nenue@6
 | 
    54   --- Substitution values
 | 
| 
Nenue@6
 | 
    55   -- %d [1] amount [2] overkill [3] absorbed [4] blocked
 | 
| 
Nenue@6
 | 
    56   -- %s [5] spell [6] caster [7] school
 | 
| 
Nenue@6
 | 
    57   textFormat = {
 | 
| 
Nenue@6
 | 
    58     ['SWING_DAMAGE'] = {'%d'},
 | 
| 
Nenue@6
 | 
    59     ['SPELL_DAMAGE'] = {'%d'},
 | 
| 
Nenue@6
 | 
    60     ['RANGE_DAMAGE'] = {'%d'},
 | 
| 
Nenue@6
 | 
    61     Incoming = {
 | 
| 
Nenue@6
 | 
    62       ['SWING_DAMAGE'] = {'-%d'},
 | 
| 
Nenue@6
 | 
    63       ['SPELL_DAMAGE'] = {'-%d (%s)'},
 | 
| 
Nenue@6
 | 
    64       ['RANGE_DAMAGE'] = {'-%d'},
 | 
| 
Nenue@6
 | 
    65     },
 | 
| 
Nenue@6
 | 
    66   },
 | 
| 
Nenue@6
 | 
    67   textModifiers ={
 | 
| 
Nenue@6
 | 
    68     critical = {'%s!', 'pop'},
 | 
| 
Nenue@6
 | 
    69     overKill = {'%s |cFF0088FFKilling Blow!|r', 'slide'},
 | 
| 
Nenue@6
 | 
    70     multistrike = {'<%s>', 'lateralSlide', 'font3'},
 | 
| 
Nenue@6
 | 
    71     absorbed = {'%s (%d)', 'slide'},
 | 
| 
Nenue@6
 | 
    72     blocked = {'%s {%d}', 'slide'},
 | 
| 
Nenue@6
 | 
    73     pet = {'(%s)', 'lateralSlide'},
 | 
| 
Nenue@6
 | 
    74     grouped = {'%s (%d hit)', 'slide'},
 | 
| 
Nenue@6
 | 
    75   },
 | 
| 
Nenue@6
 | 
    76   --- [AnimationGroup key] = {[XML attrib] = [value], d[x/y] = (number) 0-1 }
 | 
| 
Nenue@6
 | 
    77    -- d[x/y] indicates the proportional relevance of each FontString dimension when frames are displaced by a new event
 | 
| 
Nenue@6
 | 
    78    -- x/y    indicates the ranges of movement made by an animation, and are also considered when calculating displacement
 | 
| 
Nenue@6
 | 
    79   animation = {
 | 
| 
Nenue@6
 | 
    80     slide = {
 | 
| 
Nenue@6
 | 
    81       x = 0,   dx = 0,
 | 
| 
Nenue@6
 | 
    82       y = 300, dy = 1,
 | 
| 
Nenue@6
 | 
    83       duration = 2
 | 
| 
Nenue@6
 | 
    84     },
 | 
| 
Nenue@6
 | 
    85     lateralSlide = {
 | 
| 
Nenue@6
 | 
    86       x = 300, dx = 1,
 | 
| 
Nenue@6
 | 
    87       y = 0,   dy = 0,
 | 
| 
Nenue@6
 | 
    88       duration = 2
 | 
| 
Nenue@6
 | 
    89     },
 | 
| 
Nenue@6
 | 
    90     pop = {
 | 
| 
Nenue@6
 | 
    91       toScale = 1.4,
 | 
| 
Nenue@6
 | 
    92       fromScale = 0.1,
 | 
| 
Nenue@6
 | 
    93       duration = 0.14,
 | 
| 
Nenue@6
 | 
    94       dx = 0, dy = 1,
 | 
| 
Nenue@6
 | 
    95     },
 | 
| 
Nenue@6
 | 
    96     fadeOut = {change = -1, duration = 0.5},
 | 
| 
Nenue@6
 | 
    97   },
 | 
| 
Nenue@6
 | 
    98 }
 | 
| 
Nenue@6
 | 
    99 --- [1] text wrapper [2] animation type
 | 
| 
Nenue@6
 | 
   100 local dotEvents = {
 | 
| 
Nenue@6
 | 
   101   SPELL_AURA_APPLIED = true,
 | 
| 
Nenue@6
 | 
   102   SPELL_PERIODIC_DAMAGE = true,
 | 
| 
Nenue@6
 | 
   103   SPELL_AURA_REMOVED = true,
 | 
| 
Nenue@6
 | 
   104   SPELL_AURA_REFRESHED = true,
 | 
| 
Nenue@6
 | 
   105 }
 | 
| 
Nenue@6
 | 
   106 local petGUID = {}
 | 
| 
Nenue@6
 | 
   107 
 | 
| 
Nenue@6
 | 
   108 ------------- data structures
 | 
| 
Nenue@6
 | 
   109 local defaultAnimation, defaultFont
 | 
| 
Nenue@6
 | 
   110 local criticalModifier, absorbedModifier, blockedModifier, overKillModifier, multistrikeModifier, groupedModifier
 | 
| 
Nenue@6
 | 
   111 local criticalAnimation, absorbedAnimation, blockedAnimation, overKillAnimation, multistrikeAnimation, groupedAnimation
 | 
| 
Nenue@6
 | 
   112 local criticalFont, absorbedFont, blockedFont, overKillFont,multistrikeFont, groupedFont
 | 
| 
Nenue@6
 | 
   113 local textFonts = {}
 | 
| 
Nenue@6
 | 
   114 local textFormat = {}
 | 
| 
Nenue@6
 | 
   115 local animation = {}
 | 
| 
Nenue@6
 | 
   116 local spellCache = {}
 | 
| 
Nenue@6
 | 
   117 local DoTFrames = {}
 | 
| 
Nenue@6
 | 
   118 
 | 
| 
Nenue@6
 | 
   119 local dotTrackingEvents = {
 | 
| 
Nenue@6
 | 
   120   ['SPELL_AURA_APPLIED'] = true,
 | 
| 
Nenue@6
 | 
   121   ['SPELL_AURA_REMOVED'] = true,
 | 
| 
Nenue@6
 | 
   122   ['SPELL_AURA_REFRESHED'] = true,
 | 
| 
Nenue@6
 | 
   123 }
 | 
| 
Nenue@6
 | 
   124 
 | 
| 
Nenue@6
 | 
   125 --- multi-hit events data
 | 
| 
Nenue@6
 | 
   126 -- [(string) localized name] = {
 | 
| 
Nenue@6
 | 
   127 --   [1] = (bool)   controlled channel
 | 
| 
Nenue@6
 | 
   128 --   [2] = (number) minimum time to wait for next damage tick
 | 
| 
Nenue@6
 | 
   129 --   [3] = (number) maximum time to log grouped spell hits
 | 
| 
Nenue@6
 | 
   130 --   [4] = (string) combatEvent full string
 | 
| 
Nenue@6
 | 
   131 --   [5] = (string) combatEvent short prefix (as opposed to SPELL_PERIODIC)
 | 
| 
Nenue@6
 | 
   132 --   [6] = (string) combatEvent short suffix (as opposed to AURA_APPLIED)
 | 
| 
Nenue@6
 | 
   133 -- }
 | 
| 
Nenue@6
 | 
   134 -- tailing fields are set to reduce the number of arguments passed around
 | 
| 
Nenue@6
 | 
   135 local groupedSpells = {
 | 
| 
Nenue@6
 | 
   136   global = {false, 1, 'SPELL_PERIODIC_DAMAGE', 'SPELL', 'DAMAGE'},
 | 
| 
Nenue@6
 | 
   137   ['Crimson Tempest'] = {false,   0.3,  'SPELL_DAMAGE', 'SPELL', 'DAMAGE'}, -- shockwave dot
 | 
| 
Nenue@6
 | 
   138   ['Mind Sear'] =       {false,   1,  'SPELL_DAMAGE', 'SPELL', 'DAMAGE'}, -- scanning aoe
 | 
| 
Nenue@6
 | 
   139   ['Searing Insanity'] =       {false,   1,  'SPELL_DAMAGE', 'SPELL', 'DAMAGE'}, -- scanning aoe
 | 
| 
Nenue@6
 | 
   140   ['Ice Nova'] =        {false, 0.4,  'SPELL_DAMAGE', 'SPELL', 'DAMAGE'}, -- shockwave aoe
 | 
| 
Nenue@6
 | 
   141   ['Blizzard'] =        {false,   1,  'SPELL_DAMAGE', 'SPELL', 'DAMAGE'}, -- scanning aoe
 | 
| 
Nenue@6
 | 
   142   ['Frozen Orb'] =      {false,   1,  'SPELL_DAMAGE', 'SPELL', 'DAMAGE'},
 | 
| 
Nenue@6
 | 
   143   ['Ice Bomb'] =        {false, 0.4,  'SPELL_DAMAGE', 'SPELL', 'DAMAGE'}, -- shockwave aoe
 | 
| 
Nenue@6
 | 
   144   ['Comet Storm'] =     {false, 2.5,  'SPELL_DAMAGE', 'SPELL', 'DAMAGE'}, -- staggered multi-hit
 | 
| 
Nenue@6
 | 
   145   ['Barrage'] =         {false, 1.1,  'SPELL_DAMAGE', 'SPELL', 'DAMAGE'}, -- projectile vommit
 | 
| 
Nenue@6
 | 
   146   ['Glaive Toss'] =     {false,   3,  'SPELL_DAMAGE', 'SPELL', 'DAMAGE'}, -- path projectile,
 | 
| 
Nenue@6
 | 
   147   ['Doom Nova'] =       {false, 0.4,  'SPELL_DAMAGE', 'SPELL', 'DAMAGE'}
 | 
| 
Nenue@6
 | 
   148 }
 | 
| 
Nenue@6
 | 
   149 
 | 
| 
Nenue@6
 | 
   150 local dotSpellIndex = {
 | 
| 
Nenue@6
 | 
   151   ['Shadow Word: Pain'] = {'HARMFUL|PLAYER'},
 | 
| 
Nenue@6
 | 
   152   ['Vampiric Touch'] = {'HARMFUL|PLAYER'}
 | 
| 
Nenue@6
 | 
   153 }
 | 
| 
Nenue@6
 | 
   154 
 | 
| 
Nenue@6
 | 
   155 local offhandSpellIndex = {
 | 
| 
Nenue@6
 | 
   156   ['Execute'] = {'Execute Off-Hand'},
 | 
| 
Nenue@6
 | 
   157   ['Mutilate'] = {'Mutilate Off-Hand'},
 | 
| 
Nenue@6
 | 
   158   ['Stormstrike'] = {'Stormstrike Off-Hand'},
 | 
| 
Nenue@6
 | 
   159 }
 | 
| 
Nenue@6
 | 
   160 
 | 
| 
Nenue@6
 | 
   161 --- stored as a list of field representations to be concatenated together within a loadstring def
 | 
| 
Nenue@6
 | 
   162 
 | 
| 
Nenue@6
 | 
   163 
 | 
| 
Nenue@6
 | 
   164 
 | 
| 
Nenue@6
 | 
   165 --- Tracking tables
 | 
| 
Nenue@6
 | 
   166 local groupedQueue = {}
 | 
| 
Nenue@6
 | 
   167 local groupedEvents = {}
 | 
| 
Nenue@6
 | 
   168 local offhandQueue = {}
 | 
| 
Nenue@6
 | 
   169 local sct_format = {}
 | 
| 
Nenue@6
 | 
   170 
 | 
| 
Nenue@6
 | 
   171 
 | 
| 
Nenue@6
 | 
   172 local function GetGUIDInfo(guid)
 | 
| 
Nenue@6
 | 
   173   local unitType, flags1, flags2 = guid:match('(%a+)\-(%x+)\-(%x+)')
 | 
| 
Nenue@6
 | 
   174   return concat({unitType, flags1, flags2},'|r::|cFF0088FF')..'|r'
 | 
| 
Nenue@6
 | 
   175 end
 | 
| 
Nenue@6
 | 
   176 
 | 
| 
Nenue@6
 | 
   177 local SpellSchoolColors = {
 | 
| 
Nenue@6
 | 
   178   [1] = {255, 255,0},     -- physical
 | 
| 
Nenue@6
 | 
   179   [2] = {255, 230, 128},  -- holy
 | 
| 
Nenue@6
 | 
   180   [3] = {255, 255, 128},  -- holy+phys
 | 
| 
Nenue@6
 | 
   181   [4] = {255, 128, 0},    -- fire
 | 
| 
Nenue@6
 | 
   182   [8] = {77, 255, 77},    -- nature
 | 
| 
Nenue@6
 | 
   183   [16] = {128, 255, 255}, -- frost
 | 
| 
Nenue@6
 | 
   184   [20] = {255, 192, 128},   -- frostfire
 | 
| 
Nenue@6
 | 
   185   [32] = {128, 128, 255}, -- shadow
 | 
| 
Nenue@6
 | 
   186   [48] = {128, 192, 255}, -- shadow+frost
 | 
| 
Nenue@6
 | 
   187   [64] = {255, 128, 255}, -- arcane,
 | 
| 
Nenue@6
 | 
   188   [72] = {255, 128, 128}, -- spellstorm,
 | 
| 
Nenue@6
 | 
   189 }
 | 
| 
Nenue@6
 | 
   190 
 | 
| 
Nenue@6
 | 
   191 local h = '|cFF%02X%02X%02X'
 | 
| 
Nenue@6
 | 
   192 local SpellSchoolColor = function (flags, spellName)
 | 
| 
Nenue@6
 | 
   193   local i = 64
 | 
| 
Nenue@6
 | 
   194   local rA, gA, bA
 | 
| 
Nenue@6
 | 
   195   if SpellSchoolColors[flags] then
 | 
| 
Nenue@6
 | 
   196     print(flags, 'match')
 | 
| 
Nenue@6
 | 
   197     print(format('%02X%02X%02X', unpack(SpellSchoolColors[flags])))
 | 
| 
Nenue@6
 | 
   198     return format(h, unpack(SpellSchoolColors[flags]))
 | 
| 
Nenue@6
 | 
   199   end
 | 
| 
Nenue@6
 | 
   200 
 | 
| 
Nenue@6
 | 
   201   repeat
 | 
| 
Nenue@6
 | 
   202     local rB, gB, bB = unpack(SpellSchoolColors[i])
 | 
| 
Nenue@6
 | 
   203     if i <= flags then
 | 
| 
Nenue@6
 | 
   204       if not rA then
 | 
| 
Nenue@6
 | 
   205         rA = rB
 | 
| 
Nenue@6
 | 
   206         gA = gB
 | 
| 
Nenue@6
 | 
   207         bA = bB
 | 
| 
Nenue@6
 | 
   208       else
 | 
| 
Nenue@6
 | 
   209         rA = (rA+rB)/2
 | 
| 
Nenue@6
 | 
   210         gA = (gA+gB)/2
 | 
| 
Nenue@6
 | 
   211         bA = (bA+bB)/2
 | 
| 
Nenue@6
 | 
   212       end
 | 
| 
Nenue@6
 | 
   213       print('test:', cWord(i), '<=', cKey(flags), '=', (i <= flags), cPink(rA), cNum(gA), cText(bA))
 | 
| 
Nenue@6
 | 
   214       flags = flags - i
 | 
| 
Nenue@6
 | 
   215     else
 | 
| 
Nenue@6
 | 
   216       print(i, 'skip')
 | 
| 
Nenue@6
 | 
   217     end
 | 
| 
Nenue@6
 | 
   218     i = i/2
 | 
| 
Nenue@6
 | 
   219   until (i == 1)
 | 
| 
Nenue@6
 | 
   220   SpellSchoolColors[flags] = {rA, gA, bA }
 | 
| 
Nenue@6
 | 
   221   --print(string.format('%02X%02X%02X', unpack(SpellSchoolColors[flags])))
 | 
| 
Nenue@6
 | 
   222   return format(h, unpack(SpellSchoolColors[flags]))
 | 
| 
Nenue@6
 | 
   223 end
 | 
| 
Nenue@6
 | 
   224 local myGUID
 | 
| 
Nenue@6
 | 
   225 
 | 
| 
Nenue@6
 | 
   226 mod.NewCombatMessage = function(frame, index)
 | 
| 
Nenue@6
 | 
   227   local ct = CreateFrame('Frame', SCT_MESSAGE_NAME:format(frame.lineID), frame, SCT_MESSAGE_TEMPLATE)
 | 
| 
Nenue@6
 | 
   228   index = index and 'active' or 'expired'
 | 
| 
Nenue@6
 | 
   229   frame.lineID = frame.lineID + 1
 | 
| 
Nenue@6
 | 
   230   frame.active[#frame.active+1] = ct
 | 
| 
Nenue@6
 | 
   231   ct.index = #frame.active
 | 
| 
Nenue@6
 | 
   232   ct.x = 0
 | 
| 
Nenue@6
 | 
   233   ct.y = 0
 | 
| 
Nenue@6
 | 
   234   ct.point = 'BOTTOMLEFT'
 | 
| 
Nenue@6
 | 
   235   return ct
 | 
| 
Nenue@6
 | 
   236 end
 | 
| 
Nenue@6
 | 
   237 
 | 
| 
Nenue@6
 | 
   238 --- frame interaction logic
 | 
| 
Nenue@6
 | 
   239 mod.AddCombatMessage = function(frame, text, icon, animationType, fontKey)
 | 
| 
Nenue@6
 | 
   240   local line
 | 
| 
Nenue@6
 | 
   241   print(fontKey)
 | 
| 
Nenue@6
 | 
   242 
 | 
| 
Nenue@6
 | 
   243   local expired = frame.expired
 | 
| 
Nenue@6
 | 
   244   local active = frame.active
 | 
| 
Nenue@6
 | 
   245   local a = 1
 | 
| 
Nenue@6
 | 
   246   local shiftY, shiftX
 | 
| 
Nenue@6
 | 
   247   local lastFrame
 | 
| 
Nenue@6
 | 
   248   --- If animation has overlap delta values, find the last active frame of that animation type and check for overlaps.
 | 
| 
Nenue@6
 | 
   249   --- Frames are considered overlapping when the last member's ((height - distance traveled) * delta) is over 0.
 | 
| 
Nenue@6
 | 
   250   --- This assumes the same string height for the upcoming frame since wordwrap isn't enabled.
 | 
| 
Nenue@6
 | 
   251   print(animationType)
 | 
| 
Nenue@6
 | 
   252   if animation[animationType].dx or animation[animationType].dy then
 | 
| 
Nenue@6
 | 
   253     print('animation has displacement')
 | 
| 
Nenue@6
 | 
   254     if frame.last then
 | 
| 
Nenue@6
 | 
   255       lastFrame = frame.last
 | 
| 
Nenue@6
 | 
   256       local dp = lastFrame[animationType]:GetProgress()
 | 
| 
Nenue@6
 | 
   257       if animation[animationType].dx then
 | 
| 
Nenue@6
 | 
   258         local dx = dp * animation[animationType].x
 | 
| 
Nenue@6
 | 
   259         shiftX = (lastFrame.string:GetStringWidth() - dx) * animation[animationType].dx
 | 
| 
Nenue@6
 | 
   260       end
 | 
| 
Nenue@6
 | 
   261       if animation[animationType].dy then
 | 
| 
Nenue@6
 | 
   262         local dy = dp * animation[animationType].y
 | 
| 
Nenue@6
 | 
   263         shiftY = (lastFrame.string:GetStringHeight() - dy) * animation[animationType].dy
 | 
| 
Nenue@6
 | 
   264         print('  ', 'h=', floor(lastFrame.string:GetStringHeight()), 'dY=', dy, 'offsetY=', shiftY)
 | 
| 
Nenue@6
 | 
   265       end
 | 
| 
Nenue@6
 | 
   266       print(cWord('lastFrame hit:'), lastFrame and lastFrame:GetName(), cNum(shiftX), cNum(shiftY))
 | 
| 
Nenue@6
 | 
   267     end
 | 
| 
Nenue@6
 | 
   268   end
 | 
| 
Nenue@6
 | 
   269 
 | 
| 
Nenue@6
 | 
   270   -- find a usable frame
 | 
| 
Nenue@6
 | 
   271   local currentFrame
 | 
| 
Nenue@6
 | 
   272   for i, ct in ipairs(frame.active) do
 | 
| 
Nenue@6
 | 
   273     if not currentFrame then
 | 
| 
Nenue@6
 | 
   274       if ct.discard then
 | 
| 
Nenue@6
 | 
   275         ct.discard=  nil
 | 
| 
Nenue@6
 | 
   276         currentFrame = ct
 | 
| 
Nenue@6
 | 
   277       end
 | 
| 
Nenue@6
 | 
   278     end
 | 
| 
Nenue@6
 | 
   279 
 | 
| 
Nenue@6
 | 
   280     if lastFrame and ct.animationType == animationType and not ct.discard then
 | 
| 
Nenue@6
 | 
   281       --print('lastFrame defined, check for overlap')
 | 
| 
Nenue@6
 | 
   282       if shiftY > 0 then
 | 
| 
Nenue@6
 | 
   283         print(cWord('  * vertical shift'), cNum('+'..floor(shiftY)))
 | 
| 
Nenue@6
 | 
   284         ct.y = ct.y + shiftY
 | 
| 
Nenue@6
 | 
   285       end
 | 
| 
Nenue@6
 | 
   286       if shiftX > 0 then
 | 
| 
Nenue@6
 | 
   287         print(cWord('  * horizontal shift'), cNum('+'..floor(shiftX)))
 | 
| 
Nenue@6
 | 
   288         ct.x = ct.x + shiftX
 | 
| 
Nenue@6
 | 
   289       end
 | 
| 
Nenue@6
 | 
   290       ct:SetPoint(ct.point, frame, ct.point, ct.x, ct.y)
 | 
| 
Nenue@6
 | 
   291     end
 | 
| 
Nenue@6
 | 
   292   end
 | 
| 
Nenue@6
 | 
   293   -- if no expired frames became available, make a new one (should max at 20 or so if groupings are right)
 | 
| 
Nenue@6
 | 
   294   if  not currentFrame then
 | 
| 
Nenue@6
 | 
   295     currentFrame = mod.NewCombatMessage(frame)
 | 
| 
Nenue@6
 | 
   296     print(cNum('     creating new string object for the heap'))
 | 
| 
Nenue@6
 | 
   297   end
 | 
| 
Nenue@6
 | 
   298 
 | 
| 
Nenue@6
 | 
   299   print(cText(' * Starting'), cPink(animationType), 'on', cKey(currentFrame:GetName()), ' ['..cNum(currentFrame.index)..']')
 | 
| 
Nenue@6
 | 
   300 
 | 
| 
Nenue@6
 | 
   301   if icon then
 | 
| 
Nenue@6
 | 
   302     currentFrame.icon:Show()
 | 
| 
Nenue@6
 | 
   303     currentFrame.icon:SetTexture(icon)
 | 
| 
Nenue@6
 | 
   304   else
 | 
| 
Nenue@6
 | 
   305     currentFrame.icon:Hide()
 | 
| 
Nenue@6
 | 
   306   end
 | 
| 
Nenue@6
 | 
   307   if fontKey  then
 | 
| 
Nenue@6
 | 
   308     local newFont = fontKey and textFonts[fontKey] or defaultFont
 | 
| 
Nenue@6
 | 
   309 
 | 
| 
Nenue@6
 | 
   310     local path, size, flags = currentFrame.string:GetFont()
 | 
| 
Nenue@6
 | 
   311     path = newFont[1] or path
 | 
| 
Nenue@6
 | 
   312     size = newFont[2] or size
 | 
| 
Nenue@6
 | 
   313     flags = newFont[3] or flags
 | 
| 
Nenue@6
 | 
   314     print(cText('font ('..cWord(fontKey)..'):'), path, size, flags)
 | 
| 
Nenue@6
 | 
   315     local result = currentFrame.string:SetFont(path, size, flags)
 | 
| 
Nenue@6
 | 
   316     print(cNum('     result:'), cNum(result), currentFrame.string:GetFont())
 | 
| 
Nenue@6
 | 
   317     --currentFrame.fontKey = fontKey
 | 
| 
Nenue@6
 | 
   318   end
 | 
| 
Nenue@6
 | 
   319 
 | 
| 
Nenue@6
 | 
   320   currentFrame.animationType = animationType
 | 
| 
Nenue@6
 | 
   321   currentFrame.string:SetText(text)
 | 
| 
Nenue@6
 | 
   322   local cHeight = currentFrame.string:GetStringHeight()
 | 
| 
Nenue@6
 | 
   323   currentFrame:SetSize(currentFrame.string:GetStringWidth(), cHeight)
 | 
| 
Nenue@6
 | 
   324   currentFrame.icon:SetSize(cHeight, cHeight)
 | 
| 
Nenue@6
 | 
   325   currentFrame.y = 0
 | 
| 
Nenue@6
 | 
   326   currentFrame.x = 0
 | 
| 
Nenue@6
 | 
   327   currentFrame:SetPoint(currentFrame.point, frame, currentFrame.point, currentFrame.x, currentFrame.y)
 | 
| 
Nenue@6
 | 
   328   currentFrame[animationType]:Play()
 | 
| 
Nenue@6
 | 
   329   frame.last = currentFrame
 | 
| 
Nenue@6
 | 
   330 end
 | 
| 
Nenue@6
 | 
   331 
 | 
| 
Nenue@6
 | 
   332 local GetTextFormatFunc = function(amount, overKill, absorbed, blocked, multistrike, spellName, sourceName, destName)
 | 
| 
Nenue@6
 | 
   333   local sub_text = {
 | 
| 
Nenue@6
 | 
   334     ['%d'] = amount,
 | 
| 
Nenue@6
 | 
   335     ['%o'] = overKill,
 | 
| 
Nenue@6
 | 
   336     ['%a'] = absorbed,
 | 
| 
Nenue@6
 | 
   337     ['%b'] = blocked,
 | 
| 
Nenue@6
 | 
   338     ['%m'] = multistrike,
 | 
| 
Nenue@6
 | 
   339     ['%s'] = spellName,
 | 
| 
Nenue@6
 | 
   340     ['%n'] = sourceName,
 | 
| 
Nenue@6
 | 
   341     ['%t'] = destName,
 | 
| 
Nenue@6
 | 
   342   }
 | 
| 
Nenue@6
 | 
   343   local func = function(token)
 | 
| 
Nenue@6
 | 
   344     print(cPink('gsub run:'), token)
 | 
| 
Nenue@6
 | 
   345     return sub_text[token]
 | 
| 
Nenue@6
 | 
   346   end
 | 
| 
Nenue@6
 | 
   347   return func
 | 
| 
Nenue@6
 | 
   348 end
 | 
| 
Nenue@6
 | 
   349 
 | 
| 
Nenue@6
 | 
   350 --- builds CT messages from combat log event data
 | 
| 
Nenue@6
 | 
   351 --  separated from frame interaction for auxiliary events such as combat/loot/etc
 | 
| 
Nenue@6
 | 
   352 local CreateDamageText = function(frame, timestamp, combatEvent, sourceName, destName, spellID, spellName, effectSchool, amount, overKill, resisted, blocked, absorbed, critical, glancing, crushing, isOffHand, multistrike, ticks)
 | 
| 
Nenue@6
 | 
   353   if combatEvent:match('^SWING') then
 | 
| 
Nenue@6
 | 
   354     spellID = 1
 | 
| 
Nenue@6
 | 
   355     spellName = 'Attack'
 | 
| 
Nenue@6
 | 
   356   elseif combatEvent:match('^RANGE') then
 | 
| 
Nenue@6
 | 
   357     spellID = 2
 | 
| 
Nenue@6
 | 
   358     spellName = 'Auto Shot'
 | 
| 
Nenue@6
 | 
   359   end
 | 
| 
Nenue@6
 | 
   360   if not combatEvent:match('DAMAGE') then
 | 
| 
Nenue@6
 | 
   361     return
 | 
| 
Nenue@6
 | 
   362   end
 | 
| 
Nenue@6
 | 
   363 
 | 
| 
Nenue@6
 | 
   364 
 | 
| 
Nenue@6
 | 
   365   local fontKey = 'font1'
 | 
| 
Nenue@6
 | 
   366   local icon = GetSpellTexture(spellID)
 | 
| 
Nenue@6
 | 
   367   local animationType = defaultAnimation
 | 
| 
Nenue@6
 | 
   368   local text = amount
 | 
| 
Nenue@6
 | 
   369   if textFormat[combatEvent] then
 | 
| 
Nenue@6
 | 
   370     local tString, fontKey = unpack(textFormat[combatEvent])
 | 
| 
Nenue@6
 | 
   371     if amount > 1000000 then
 | 
| 
Nenue@6
 | 
   372       amount = (floor(amount/100000)/10) ..'M'
 | 
| 
Nenue@6
 | 
   373     elseif amount > 1000 then
 | 
| 
Nenue@6
 | 
   374       amount = (floor(amount/100)/10) ..'k'
 | 
| 
Nenue@6
 | 
   375     end
 | 
| 
Nenue@6
 | 
   376 
 | 
| 
Nenue@6
 | 
   377     text = tString:gsub('%%[doabsnt]', GetTextFormatFunc(amount, overKill, absorbed, blocked, multistrike, spellName, sourceName, destName))
 | 
| 
Nenue@6
 | 
   378     print("** font override:", '"'..cText(tString)..'",', cPink(font), cNum(size), cWord(outline))
 | 
| 
Nenue@6
 | 
   379   end
 | 
| 
Nenue@6
 | 
   380 
 | 
| 
Nenue@6
 | 
   381   print('** getting color data', cText(spellName))
 | 
| 
Nenue@6
 | 
   382   if type(effectSchool) == 'number' then
 | 
| 
Nenue@6
 | 
   383     text = SpellSchoolColor(effectSchool) ..text..'|r'
 | 
| 
Nenue@6
 | 
   384   end
 | 
| 
Nenue@6
 | 
   385 
 | 
| 
Nenue@6
 | 
   386   if overKill > 0 then
 | 
| 
Nenue@6
 | 
   387     text = overKillModifier:format(text, overKill)
 | 
| 
Nenue@6
 | 
   388     animationType = overKillAnimation
 | 
| 
Nenue@6
 | 
   389     fontKey = overKillFont or fontKey
 | 
| 
Nenue@6
 | 
   390   end
 | 
| 
Nenue@6
 | 
   391   if critical then
 | 
| 
Nenue@6
 | 
   392     text = criticalModifier:format(text)
 | 
| 
Nenue@6
 | 
   393     animationType = criticalAnimation
 | 
| 
Nenue@6
 | 
   394     fontKey = criticalFont or fontKey
 | 
| 
Nenue@6
 | 
   395   end
 | 
| 
Nenue@6
 | 
   396   if absorbed then
 | 
| 
Nenue@6
 | 
   397     text = absorbedModifier:format(text, absorbed)
 | 
| 
Nenue@6
 | 
   398     animationType = absorbedAnimation
 | 
| 
Nenue@6
 | 
   399     fontKey = absorbedFont or fontKey
 | 
| 
Nenue@6
 | 
   400   end
 | 
| 
Nenue@6
 | 
   401   if blocked then
 | 
| 
Nenue@6
 | 
   402     text = blockedModifier:format(text, blocked)
 | 
| 
Nenue@6
 | 
   403     animationType = blockedAnimation
 | 
| 
Nenue@6
 | 
   404     fontKey = blockedFont or fontKey
 | 
| 
Nenue@6
 | 
   405   end
 | 
| 
Nenue@6
 | 
   406   if multistrike then
 | 
| 
Nenue@6
 | 
   407     text = multistrikeModifier:format(text, multistrike)
 | 
| 
Nenue@6
 | 
   408     animationType = multistrikeAnimation
 | 
| 
Nenue@6
 | 
   409     fontKey = multistrikeFont or fontKey
 | 
| 
Nenue@6
 | 
   410   end
 | 
| 
Nenue@6
 | 
   411 
 | 
| 
Nenue@6
 | 
   412   print(ticks)
 | 
| 
Nenue@6
 | 
   413   if ticks then
 | 
| 
Nenue@6
 | 
   414     text = groupedModifier:format(text, ticks)
 | 
| 
Nenue@6
 | 
   415     animationType = groupedAnimation
 | 
| 
Nenue@6
 | 
   416     fontKey = groupedFont or fontKey
 | 
| 
Nenue@6
 | 
   417   end
 | 
| 
Nenue@6
 | 
   418 
 | 
| 
Nenue@6
 | 
   419 
 | 
| 
Nenue@6
 | 
   420   print('** sending format to SCT:', text, icon, animationType, fontKey)
 | 
| 
Nenue@6
 | 
   421 
 | 
| 
Nenue@6
 | 
   422 
 | 
| 
Nenue@6
 | 
   423   mod.AddCombatMessage(frame, text .. '|r', icon, animationType, fontKey)
 | 
| 
Nenue@6
 | 
   424 end
 | 
| 
Nenue@6
 | 
   425 
 | 
| 
Nenue@6
 | 
   426 local CT_ShowConsolidated = function()
 | 
| 
Nenue@6
 | 
   427   for spellName, queuedSpell in pairs(groupedQueue) do
 | 
| 
Nenue@6
 | 
   428     local isChannel, minDelay, combatEvent, sourceName, destName, spellID, school, SCT, timestamp = unpack(queuedSpell)
 | 
| 
Nenue@6
 | 
   429     for i, v  in ipairs(queuedSpell) do
 | 
| 
Nenue@6
 | 
   430       print('   ', i, '=', v)
 | 
| 
Nenue@6
 | 
   431     end
 | 
| 
Nenue@6
 | 
   432 
 | 
| 
Nenue@6
 | 
   433     print(spellName, 'vars:', spellID, spellName, school, SCT, timestamp)
 | 
| 
Nenue@6
 | 
   434     if groupedEvents[spellName] and #groupedEvents[spellName] ~= 0 then
 | 
| 
Nenue@6
 | 
   435       local amount, overKill, resisted, blocked, absorbed, critical, glancing, crushing, multistrike, hit = 0, -1, 0, 0, 0, 0, 0, 0, 0, 0
 | 
| 
Nenue@6
 | 
   436       for i, line in ipairs(groupedEvents[spellName]) do
 | 
| 
Nenue@6
 | 
   437         -- extra strike?
 | 
| 
Nenue@6
 | 
   438         --{amount, overKill, resisted, blocked, absorbed, critical, glancing, crushing, multistrike}
 | 
| 
Nenue@6
 | 
   439         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)
 | 
| 
Nenue@6
 | 
   440 
 | 
| 
Nenue@6
 | 
   441         amount = amount + amount_n
 | 
| 
Nenue@6
 | 
   442         if overKill_n > 0   then overKill = overKill + overKill_n end
 | 
| 
Nenue@6
 | 
   443         if blocked_n        then blocked = blocked + line[4] end
 | 
| 
Nenue@6
 | 
   444         if absorbed_n       then absorbed = absorbed + line[5] end
 | 
| 
Nenue@6
 | 
   445         if critical_n       then critical = critical + 1 end
 | 
| 
Nenue@6
 | 
   446         if multi_n          then multistrike = multistrike + 1  end
 | 
| 
Nenue@6
 | 
   447 
 | 
| 
Nenue@6
 | 
   448         hit = hit + 1
 | 
| 
Nenue@6
 | 
   449       end
 | 
| 
Nenue@6
 | 
   450       if overKill == -1 then
 | 
| 
Nenue@6
 | 
   451         overKill = overKill + 1
 | 
| 
Nenue@6
 | 
   452       end
 | 
| 
Nenue@6
 | 
   453       wipe(groupedEvents[spellName])
 | 
| 
Nenue@6
 | 
   454       groupedQueue[spellName] = nil
 | 
| 
Nenue@6
 | 
   455       print('   expelling', spellName, cText('A:'), cNum(amount), cText('O:'), cNum(overKill), cText('Ticks:'), cNum(hit), cText('Crits:'), cNum(critical), cText(school))
 | 
| 
Nenue@6
 | 
   456       print(SCT, timestamp, combatEvent, sourceName, destName, spellID, spellName, school, amount, overKill, resisted, blocked, absorbed, critical, glancing, crushing, multistrike, hit)
 | 
| 
Nenue@6
 | 
   457       CreateDamageText(SCT, timestamp, combatEvent, sourceName, destName, spellID, spellName, school, amount, -1, nil, nil, nil, false, glancing, crushing, false, multistrike, hit)
 | 
| 
Nenue@6
 | 
   458     end
 | 
| 
Nenue@6
 | 
   459   end
 | 
| 
Nenue@6
 | 
   460 end
 | 
| 
Nenue@6
 | 
   461 
 | 
| 
Nenue@6
 | 
   462 local CT_ConsolidateText = function (self, timestamp, sourceGUID, destGUID, sourceName, destName, spellID, spellName, school, amount, overKill, resisted, blocked, absorbed, critical, glancing, crushing, isOffHand, multistrike)
 | 
| 
Nenue@6
 | 
   463   local archive = groupedSpells[spellName] and groupedSpells[spellName] or groupedSpells.global
 | 
| 
Nenue@6
 | 
   464   local isChannel, minDelay, combatEvent = unpack(archive)
 | 
| 
Nenue@6
 | 
   465   if not groupedEvents[spellName] then
 | 
| 
Nenue@6
 | 
   466     groupedEvents[spellName] = {}
 | 
| 
Nenue@6
 | 
   467   end
 | 
| 
Nenue@6
 | 
   468 
 | 
| 
Nenue@6
 | 
   469   if #groupedEvents[spellName] == 0 then
 | 
| 
Nenue@6
 | 
   470     groupedQueue[spellName] = {isChannel, minDelay, combatEvent, sourceName, destName, spellID, school, self, timestamp}
 | 
| 
Nenue@6
 | 
   471     T:ScheduleTimer(CT_ShowConsolidated, minDelay)
 | 
| 
Nenue@6
 | 
   472     print('  starting archive for', select(7, unpack(archive)))
 | 
| 
Nenue@6
 | 
   473   else
 | 
| 
Nenue@6
 | 
   474     print('  recording into archive', cText(spellName))
 | 
| 
Nenue@6
 | 
   475   end
 | 
| 
Nenue@6
 | 
   476   tinsert(groupedEvents[spellName], {amount, overKill, resisted, blocked, absorbed, critical, glancing, crushing, multistrike, sourceGUID, destGUID, sourceName, destName, sourceName, destName,})
 | 
| 
Nenue@6
 | 
   477 end
 | 
| 
Nenue@6
 | 
   478 
 | 
| 
Nenue@6
 | 
   479 local CT_OnCast = function(self, event, unit, spellName, lineID, spellID)
 | 
| 
Nenue@6
 | 
   480   if unit ~= 'player' and unit ~= 'pet' then
 | 
| 
Nenue@6
 | 
   481     return
 | 
| 
Nenue@6
 | 
   482   end
 | 
| 
Nenue@6
 | 
   483 
 | 
| 
Nenue@6
 | 
   484   if groupedSpells[spellName] then
 | 
| 
Nenue@6
 | 
   485     print(cText('** Spell casting info received'))
 | 
| 
Nenue@6
 | 
   486     local isChannel, delay = unpack(groupedSpells[spellName])
 | 
| 
Nenue@6
 | 
   487     if isChannel and event == 'CHANNEL_STOP' and groupedEvents[spellName] then
 | 
| 
Nenue@6
 | 
   488       T:ScheduleTimer(CT_ShowConsolidated, delay)
 | 
| 
Nenue@6
 | 
   489     else
 | 
| 
Nenue@6
 | 
   490       if not groupedEvents[spellName] then
 | 
| 
Nenue@6
 | 
   491         groupedEvents[spellName] = {}
 | 
| 
Nenue@6
 | 
   492       end
 | 
| 
Nenue@6
 | 
   493     end
 | 
| 
Nenue@6
 | 
   494   end
 | 
| 
Nenue@6
 | 
   495 end
 | 
| 
Nenue@6
 | 
   496 
 | 
| 
Nenue@6
 | 
   497 local CT_Unlock = function(str)
 | 
| 
Nenue@6
 | 
   498   for i, CT in pairs(mod.CombatTextFrames) do
 | 
| 
Nenue@6
 | 
   499       local frame = CT.frame
 | 
| 
Nenue@6
 | 
   500       frame.configMode = (frame.configMode == nil) and true or nil
 | 
| 
Nenue@6
 | 
   501       frame:RegisterForDrag(frame.configMode and 'LeftButton' or nil)
 | 
| 
Nenue@6
 | 
   502       frame:EnableMouse(frame.configMode and true or false)
 | 
| 
Nenue@6
 | 
   503       print(i, frame.configMode and 'ON' or 'OFF')
 | 
| 
Nenue@6
 | 
   504       for _, reg in ipairs(frame.configRegions) do
 | 
| 
Nenue@6
 | 
   505         if frame.configMode then
 | 
| 
Nenue@6
 | 
   506           reg:Show()
 | 
| 
Nenue@6
 | 
   507         else
 | 
| 
Nenue@6
 | 
   508           reg:Hide()
 | 
| 
Nenue@6
 | 
   509         end
 | 
| 
Nenue@6
 | 
   510       end
 | 
| 
Nenue@6
 | 
   511 
 | 
| 
Nenue@6
 | 
   512       if frame.configMode then
 | 
| 
Nenue@6
 | 
   513         CT.configTimer = T:ScheduleRepeatingTimer(function()
 | 
| 
Nenue@6
 | 
   514           print(i, 'config tick')
 | 
| 
Nenue@6
 | 
   515           for i, s in ipairs(CT.sample) do
 | 
| 
Nenue@6
 | 
   516             CT.OnEvent(frame, unpack(s))
 | 
| 
Nenue@6
 | 
   517           end
 | 
| 
Nenue@6
 | 
   518         end, 2)
 | 
| 
Nenue@6
 | 
   519       else
 | 
| 
Nenue@6
 | 
   520         print(CT.configTimer)
 | 
| 
Nenue@6
 | 
   521         T:CancelTimer(CT.configTimer)
 | 
| 
Nenue@6
 | 
   522       end
 | 
| 
Nenue@6
 | 
   523 
 | 
| 
Nenue@6
 | 
   524   end
 | 
| 
Nenue@6
 | 
   525 end
 | 
| 
Nenue@6
 | 
   526 
 | 
| 
Nenue@6
 | 
   527 --- check for sectors
 | 
| 
Nenue@6
 | 
   528 local queue = {}
 | 
| 
Nenue@6
 | 
   529 mod.OnDamage = function(self, event, ...)
 | 
| 
Nenue@6
 | 
   530   local isVisible
 | 
| 
Nenue@6
 | 
   531 
 | 
| 
Nenue@6
 | 
   532   local timestamp, combatEvent = ...
 | 
| 
Nenue@6
 | 
   533   print(cText('* CT'), cKey(combatEvent))
 | 
| 
Nenue@6
 | 
   534   if combatEvent == 'UNIT_DIED' then
 | 
| 
Nenue@6
 | 
   535     return
 | 
| 
Nenue@6
 | 
   536   end
 | 
| 
Nenue@6
 | 
   537   local sourceGUID, sourceName, sourceFlags, _, destGUID, destName, destFlags, _, spellID, spellName, spellSchool = select(4, ...)
 | 
| 
Nenue@6
 | 
   538   print('  from', cPink(sourceGUID), 'spell', cNum(spellID), cText(spellName), '->', cKey(destGUID))
 | 
| 
Nenue@6
 | 
   539 
 | 
| 
Nenue@6
 | 
   540   -- SWING starts at arg 10, SPELL/RANGE start at arg 13, ENVIRONMENTAL starts at arg 8
 | 
| 
Nenue@6
 | 
   541   local offset = 15
 | 
| 
Nenue@6
 | 
   542   print(combatEvent:sub(0,3))
 | 
| 
Nenue@6
 | 
   543   if combatEvent:sub(0,3) == 'SWI' then
 | 
| 
Nenue@6
 | 
   544     offset = 10
 | 
| 
Nenue@6
 | 
   545   elseif combatEvent:sub(0,3) == 'ENV' then
 | 
| 
Nenue@6
 | 
   546     offset = 8
 | 
| 
Nenue@6
 | 
   547   end
 | 
| 
Nenue@6
 | 
   548   local amount, overKill, effectSchool, resisted, blocked, absorbed, critical, glancing, crushing, isOffHand, multistrike = select(offset, ...)
 | 
| 
Nenue@6
 | 
   549 
 | 
| 
Nenue@6
 | 
   550   print('  dmg', amount, overKill)
 | 
| 
Nenue@6
 | 
   551 
 | 
| 
Nenue@6
 | 
   552   local sc = SpellSchoolColors[effectSchool]
 | 
| 
Nenue@6
 | 
   553   if groupedSpells[spellName] then
 | 
| 
Nenue@6
 | 
   554     print('* ', cText(spellName), 'to consolidator')
 | 
| 
Nenue@6
 | 
   555     CT_ConsolidateText(self, timestamp, sourceGUID, destGUID, sourceName, destName, spellID, spellName, effectSchool, amount, overKill, resisted, blocked, absorbed, critical, glancing, crushing, isOffHand, multistrike)
 | 
| 
Nenue@6
 | 
   556     return
 | 
| 
Nenue@6
 | 
   557   end
 | 
| 
Nenue@6
 | 
   558 
 | 
| 
Nenue@6
 | 
   559   print('displaying on', cWord(self:GetName()))
 | 
| 
Nenue@6
 | 
   560   CreateDamageText(self, timestamp, combatEvent, sourceName, destName, spellID, spellName, effectSchool, amount, overKill, resisted, blocked, absorbed, critical, glancing, crushing, isOffHand, multistrike)
 | 
| 
Nenue@6
 | 
   561 end
 | 
| 
Nenue@6
 | 
   562 
 | 
| 
Nenue@6
 | 
   563 mod.maxDotFrame = 1
 | 
| 
Nenue@6
 | 
   564 mod.OnDotEvent = function (self, event, timestamp, combatEvent, ...)
 | 
| 
Nenue@6
 | 
   565   print(cWord('DOT'), combatEvent)
 | 
| 
Nenue@6
 | 
   566   local sourceGUID, sourceName, sourceFlags, _, destGUID, destName, destFlags, destExtraFlags, spellID, spellName, castSchool, amount, overKill, resisted, blocked, absorbed, critical, glancing, crushing, isOffHand, multistrike = select(2, ...)
 | 
| 
Nenue@6
 | 
   567   print(cText('dot from'), cWord(sourceGUID), 'to', cKey(destGUID))
 | 
| 
Nenue@6
 | 
   568   if sourceGUID == T.GUID then
 | 
| 
Nenue@6
 | 
   569     local p = mod.PeriodicTable
 | 
| 
Nenue@6
 | 
   570     p[destGUID] = p[destGUID] or {}
 | 
| 
Nenue@6
 | 
   571     local unit = p[destGUID]
 | 
| 
Nenue@6
 | 
   572     if not p[spellID] then
 | 
| 
Nenue@6
 | 
   573       if not p.frames[#p.frames] then
 | 
| 
Nenue@6
 | 
   574         unit[spellID] = CreateFrame('Frame', 'DotBar'..mod.maxDotFrame, p, 'TkDotBarTemplate')
 | 
| 
Nenue@6
 | 
   575         mod.maxDotFrame = mod.maxDotFrame + 1
 | 
| 
Nenue@6
 | 
   576         unit[spellID].dotID = mod.maxDotFrame
 | 
| 
Nenue@6
 | 
   577 
 | 
| 
Nenue@6
 | 
   578         print('  create new frame ['..cKey(unit[spellID]:GetID(), '] for'), cWord(spellName), cKey(destGUID))
 | 
| 
Nenue@6
 | 
   579       else
 | 
| 
Nenue@6
 | 
   580         unit[spellID] = p.frames[#p.frames]
 | 
| 
Nenue@6
 | 
   581         p.frames[#p.frames] = nil -- remove that entry
 | 
| 
Nenue@6
 | 
   582         print('  recycling frame ['..cKey(unit[spellID].dotID, '] for'), cWord(spellName), cKey(destGUID))
 | 
| 
Nenue@6
 | 
   583       end
 | 
| 
Nenue@6
 | 
   584 
 | 
| 
Nenue@6
 | 
   585       unit[spellID]:Show()
 | 
| 
Nenue@6
 | 
   586     end
 | 
| 
Nenue@6
 | 
   587     local dot = unit[spellID]
 | 
| 
Nenue@6
 | 
   588     local time = GetTime()
 | 
| 
Nenue@6
 | 
   589   end
 | 
| 
Nenue@6
 | 
   590 end
 | 
| 
Nenue@6
 | 
   591 
 | 
| 
Nenue@6
 | 
   592 
 | 
| 
Nenue@6
 | 
   593 
 | 
| 
Nenue@6
 | 
   594 
 | 
| 
Nenue@6
 | 
   595 function mod:OnInitialize()
 | 
| 
Nenue@6
 | 
   596   print('This is a thing.')
 | 
| 
Nenue@6
 | 
   597   self.UNIT_SPELLCAST_SUCCEEDED = CT_OnCast
 | 
| 
Nenue@6
 | 
   598   self.UNIT_SPELLCAST_CHANNEL_START = CT_OnCast
 | 
| 
Nenue@6
 | 
   599   self.UNIT_SPELLCAST_CHANNEL_STOP = CT_OnCast
 | 
| 
Nenue@6
 | 
   600 end
 | 
| 
Nenue@6
 | 
   601 
 | 
| 
Nenue@6
 | 
   602 function mod:OnInitialize()
 | 
| 
Nenue@6
 | 
   603   mod.db = TurokData.CombatText
 | 
| 
Nenue@6
 | 
   604   db = TurokData.CombatText
 | 
| 
Nenue@6
 | 
   605   myGUID = T.GUID
 | 
| 
Nenue@6
 | 
   606   mod.PeriodicTable = mod.PeriodicTable or CreateFrame('Frame', 'TurokPeriodicFrame', UIParent)
 | 
| 
Nenue@6
 | 
   607   mod.PeriodicTable.frames = {}
 | 
| 
Nenue@6
 | 
   608 
 | 
| 
Nenue@6
 | 
   609   local m = mod.db.textModifiers
 | 
| 
Nenue@6
 | 
   610   --- These values are going to be looked up a lot, so cache as close as possible
 | 
| 
Nenue@6
 | 
   611   criticalModifier = m.critical[1] or '%s CRIT'
 | 
| 
Nenue@6
 | 
   612   criticalAnimation = m.critical[2] or 'slide'
 | 
| 
Nenue@6
 | 
   613   criticalFont = m.critical[3]
 | 
| 
Nenue@6
 | 
   614 
 | 
| 
Nenue@6
 | 
   615   absorbedModifier = m.absorbed[1] or '%s ABS'
 | 
| 
Nenue@6
 | 
   616   absorbedAnimation = m.absorbed[2] or 'slide'
 | 
| 
Nenue@6
 | 
   617   absorbedFont = m.absorbed[3]
 | 
| 
Nenue@6
 | 
   618 
 | 
| 
Nenue@6
 | 
   619   blockedModifier = m.blocked[1] or '%s BLK'
 | 
| 
Nenue@6
 | 
   620   blockedAnimation = m.blocked[2] or 'slide'
 | 
| 
Nenue@6
 | 
   621   blockedFont = m.blocked[3]
 | 
| 
Nenue@6
 | 
   622 
 | 
| 
Nenue@6
 | 
   623   overKillModifier = m.overKill[1] or '%s KILL'
 | 
| 
Nenue@6
 | 
   624   overKillAnimation = m.overKill[2] or 'slide'
 | 
| 
Nenue@6
 | 
   625   overKillFont = m.overKill[3]
 | 
| 
Nenue@6
 | 
   626 
 | 
| 
Nenue@6
 | 
   627   multistrikeModifier = m.multistrike[1] or '%s MS'
 | 
| 
Nenue@6
 | 
   628   multistrikeAnimation = m.multistrike[2] or 'slide'
 | 
| 
Nenue@6
 | 
   629   multistrikeFont = m.multistrike[3]
 | 
| 
Nenue@6
 | 
   630 
 | 
| 
Nenue@6
 | 
   631   groupedModifier = m.grouped[1] or '%s'
 | 
| 
Nenue@6
 | 
   632   groupedAnimation = m.grouped[2] or 'slide'
 | 
| 
Nenue@6
 | 
   633   groupedFont = m.grouped[3]
 | 
| 
Nenue@6
 | 
   634 
 | 
| 
Nenue@6
 | 
   635   --- Same as above, but for specific table values, key is determined by the combat event
 | 
| 
Nenue@6
 | 
   636   defaultAnimation = mod.db.defaultAnimation
 | 
| 
Nenue@6
 | 
   637   defaultFont = mod.db.defaultFont
 | 
| 
Nenue@6
 | 
   638   for k,v in pairs(mod.db.textFormat) do
 | 
| 
Nenue@6
 | 
   639     textFormat[k] = {v[1], v[2]}
 | 
| 
Nenue@6
 | 
   640     print('imported textFormat.'..k, cText(textFormat[k][1]), cNum(textFormat[k][2]))
 | 
| 
Nenue@6
 | 
   641   end
 | 
| 
Nenue@6
 | 
   642   for k,v in pairs(mod.db.textFonts) do
 | 
| 
Nenue@6
 | 
   643     textFonts[k] = {v[1] or defaultFont[1], v[2] or defaultFont[2], v[3] or defaultFont[3]}
 | 
| 
Nenue@6
 | 
   644     print('imported font.'..k, cText(textFonts[k][1]), cNum(textFonts[k][2]), cWord(textFonts[k][3]))
 | 
| 
Nenue@6
 | 
   645   end
 | 
| 
Nenue@6
 | 
   646 
 | 
| 
Nenue@6
 | 
   647   for k,v in pairs(mod.db.animation) do
 | 
| 
Nenue@6
 | 
   648     animation[k] = {}
 | 
| 
Nenue@6
 | 
   649     animation[k].x = v.x
 | 
| 
Nenue@6
 | 
   650     animation[k].y = v.y
 | 
| 
Nenue@6
 | 
   651     animation[k].dx = v.dx
 | 
| 
Nenue@6
 | 
   652     animation[k].dy = v.dy
 | 
| 
Nenue@6
 | 
   653     animation[k].fromScale = v.fromScale
 | 
| 
Nenue@6
 | 
   654     animation[k].toScale = v.toScale
 | 
| 
Nenue@6
 | 
   655     animation[k].deviation = v.deviation
 | 
| 
Nenue@6
 | 
   656     animation[k].change = v.change
 | 
| 
Nenue@6
 | 
   657     animation[k].fromAlpha = v.fromAlpha
 | 
| 
Nenue@6
 | 
   658     animation[k].toAlpha = v.toAlpha
 | 
| 
Nenue@6
 | 
   659     animation[k].duration = v.duration
 | 
| 
Nenue@6
 | 
   660   end
 | 
| 
Nenue@6
 | 
   661 end
 | 
| 
Nenue@6
 | 
   662 
 | 
| 
Nenue@6
 | 
   663 function mod:OnEnable()
 | 
| 
Nenue@6
 | 
   664   T:RegisterChatCommand('tkc', CT_Unlock)
 | 
| 
Nenue@6
 | 
   665 
 | 
| 
Nenue@6
 | 
   666   --- Populate CT frames
 | 
| 
Nenue@6
 | 
   667   for name, CT in pairs(mod.CombatTextFrames) do
 | 
| 
Nenue@6
 | 
   668     print('create CT', name)
 | 
| 
Nenue@6
 | 
   669     -- make frame
 | 
| 
Nenue@6
 | 
   670     CT.frame = CT.frame or CreateFrame('Frame', SCT_NAME:format(name), UIParent, SCT_TEMPLATE)
 | 
| 
Nenue@6
 | 
   671 
 | 
| 
Nenue@6
 | 
   672     -- local vars
 | 
| 
Nenue@6
 | 
   673     local db = db[name] or db
 | 
| 
Nenue@6
 | 
   674     local frame = CT.frame
 | 
| 
Nenue@6
 | 
   675 
 | 
| 
Nenue@6
 | 
   676     -- script defs
 | 
| 
Nenue@6
 | 
   677     frame.IsFrameEvent = CT.trigger
 | 
| 
Nenue@6
 | 
   678     frame.name = name
 | 
| 
Nenue@6
 | 
   679     frame.lineID = 0
 | 
| 
Nenue@6
 | 
   680     frame.expired = {}
 | 
| 
Nenue@6
 | 
   681     frame.active = {}
 | 
| 
Nenue@6
 | 
   682 
 | 
| 
Nenue@6
 | 
   683     -- frame defs
 | 
| 
Nenue@6
 | 
   684     frame:SetPoint(db.anchor, db.parent, db.anchorTo, db.x, db.y)
 | 
| 
Nenue@6
 | 
   685     frame:RegisterEvent('COMBAT_LOG_EVENT_UNFILTERED')
 | 
| 
Nenue@6
 | 
   686     print('bound ', name, 'with', CT.OnEvent)
 | 
| 
Nenue@6
 | 
   687     db.sample = {}
 | 
| 
Nenue@6
 | 
   688     local logged = {}
 | 
| 
Nenue@6
 | 
   689     frame:SetScript('OnEvent', function(self, e,...)
 | 
| 
Nenue@6
 | 
   690       if CT.trigger(e, ...) then
 | 
| 
Nenue@6
 | 
   691         print('event trigger fired', name)
 | 
| 
Nenue@6
 | 
   692           if #db.sample < 5 and not logged[select(2,...)] then
 | 
| 
Nenue@6
 | 
   693             logged[select(2,...)] = true
 | 
| 
Nenue@6
 | 
   694             tinsert(db.sample, {e, ...})
 | 
| 
Nenue@6
 | 
   695           end
 | 
| 
Nenue@6
 | 
   696 
 | 
| 
Nenue@6
 | 
   697         CT.OnEvent(frame, e, ...)
 | 
| 
Nenue@6
 | 
   698       end
 | 
| 
Nenue@6
 | 
   699     end)
 | 
| 
Nenue@6
 | 
   700 
 | 
| 
Nenue@6
 | 
   701     -- configurators
 | 
| 
Nenue@6
 | 
   702     frame.configHeader:SetText(name)
 | 
| 
Nenue@6
 | 
   703     frame:EnableMouse(false)
 | 
| 
Nenue@6
 | 
   704 
 | 
| 
Nenue@6
 | 
   705     -- pre-pop some text frames
 | 
| 
Nenue@6
 | 
   706     for i = (CT.lineID or 1), SCT_PREGAME do
 | 
| 
Nenue@6
 | 
   707       print(frame:GetName(), 'pre-pop #'..i)
 | 
| 
Nenue@6
 | 
   708       mod.NewCombatMessage(frame)
 | 
| 
Nenue@6
 | 
   709     end
 | 
| 
Nenue@6
 | 
   710   end
 | 
| 
Nenue@6
 | 
   711 end
 | 
| 
Nenue@6
 | 
   712 
 | 
| 
Nenue@6
 | 
   713 
 | 
| 
Nenue@6
 | 
   714 local damageEvents = {
 | 
| 
Nenue@6
 | 
   715   ['SPELL_DAMAGE'] = true,
 | 
| 
Nenue@6
 | 
   716   ['SPELL_PERIODIC_DAMAGE'] = true,
 | 
| 
Nenue@6
 | 
   717   ['SWING_DAMAGE'] = true,
 | 
| 
Nenue@6
 | 
   718   ['RANGE_DAMAGE'] = true,
 | 
| 
Nenue@6
 | 
   719   ['SPELL_HEAL'] = true,
 | 
| 
Nenue@6
 | 
   720 }
 | 
| 
Nenue@6
 | 
   721 mod.CombatTextFrames = {
 | 
| 
Nenue@6
 | 
   722   Incoming = {
 | 
| 
Nenue@6
 | 
   723     trigger = function(e, _, c, _, _, _, _, _, d) return (d == T.GUID and damageEvents[c]) end,
 | 
| 
Nenue@6
 | 
   724     OnEvent = mod.OnDamage,
 | 
| 
Nenue@6
 | 
   725     sample = {
 | 
| 
Nenue@6
 | 
   726       {"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, },
 | 
| 
Nenue@6
 | 
   727     }
 | 
| 
Nenue@6
 | 
   728   },
 | 
| 
Nenue@6
 | 
   729   Outgoing = {
 | 
| 
Nenue@6
 | 
   730     trigger = function(e, _,c,_, s) return (s == T.GUID and damageEvents[c]) end,
 | 
| 
Nenue@6
 | 
   731     OnEvent = mod.OnDamage,
 | 
| 
Nenue@6
 | 
   732     sample = {
 | 
| 
Nenue@6
 | 
   733     {
 | 
| 
Nenue@6
 | 
   734         "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, }, {
 | 
| 
Nenue@6
 | 
   735         "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, }, {
 | 
| 
Nenue@6
 | 
   736         "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, }, {
 | 
| 
Nenue@6
 | 
   737         "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]
 | 
| 
Nenue@6
 | 
   738     }
 | 
| 
Nenue@6
 | 
   739   },
 | 
| 
Nenue@6
 | 
   740   DoTTracker = {
 | 
| 
Nenue@6
 | 
   741     trigger = function(e, _, c, _, s) return (s == T.GUID and dotEvents[c]) end,
 | 
| 
Nenue@6
 | 
   742     OnEvent = mod.OnDotEvent,
 | 
| 
Nenue@6
 | 
   743     sample = {
 | 
| 
Nenue@6
 | 
   744       {"COMBAT_LOG_EVENT_UNFILTERED", 1455887795.006, "SPELL_AURA_APPLIED",    false,
 | 
| 
Nenue@6
 | 
   745         "Player-3684-07235A4E", "Klakyn", 1297, 0,
 | 
| 
Nenue@6
 | 
   746         "Creature-0-3684-1116-7-87761-0000C32119", "Dungeoneer's Training Dummy", 68136, 0,
 | 
| 
Nenue@6
 | 
   747         589, "Shadow Word: Pain", 32, "DEBUFF", },
 | 
| 
Nenue@6
 | 
   748       {"COMBAT_LOG_EVENT_UNFILTERED", 1455887797.377, "SPELL_PERIODIC_DAMAGE", false,
 | 
| 
Nenue@6
 | 
   749         "Player-3684-07235A4E", "Klakyn", 1297, 0,
 | 
| 
Nenue@6
 | 
   750         "Creature-0-3684-1116-7-87761-0000C32119", "Dungeoneer's Training Dummy", 68136, 0,
 | 
| 
Nenue@6
 | 
   751         589, "Shadow Word: Pain", 32, 3944, -1, 32, nil, nil, nil, false, false, false, false, false, },
 | 
| 
Nenue@6
 | 
   752       {"COMBAT_LOG_EVENT_UNFILTERED", 1455887807.199, "SPELL_AURA_REMOVED",    false,
 | 
| 
Nenue@6
 | 
   753         "Player-3684-07235A4E", "Klakyn", 1297, 0,
 | 
| 
Nenue@6
 | 
   754         "Player-3684-07235A4E", "Klakyn", 1297, 0,
 | 
| 
Nenue@6
 | 
   755         15473, "Shadowform", 32, "BUFF", },
 | 
| 
Nenue@6
 | 
   756     }
 | 
| 
Nenue@6
 | 
   757   }
 | 
| 
Nenue@6
 | 
   758 } |