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