local GlobalAddonName, ExRT = ... local max = max local ceil = ceil local UnitCombatlogname = ExRT.F.UnitCombatlogname local UnitHealth = UnitHealth local UnitHealthMax = UnitHealthMax local UnitGetTotalAbsorbs = UnitGetTotalAbsorbs or ExRT.NULLfunc local UnitPower = UnitPower local UnitPowerMax = UnitPowerMax local UnitGUID = UnitGUID local UnitName = UnitName local C_UnitAuras = C_UnitAuras local AntiSpam = ExRT.F.AntiSpam local GetUnitInfoByUnitFlag = ExRT.F.GetUnitInfoByUnitFlag local UnitInRaid = UnitInRaid local UnitIsPlayerOrPet = ExRT.F.UnitIsPlayerOrPet local GUIDtoID = ExRT.F.GUIDtoID local GetUnitTypeByGUID = ExRT.F.GetUnitTypeByGUID local pairs = pairs local GetTime = GetTime local UnitIsFriendlyByUnitFlag = ExRT.F.UnitIsFriendlyByUnitFlag local wipe = wipe local bit_band = bit.band local tremove = tremove local strsplit = strsplit local type = type local UnitGroupRolesAssigned = UnitGroupRolesAssigned or ExRT.NULLfunc local GetSpellTexture = C_Spell and C_Spell.GetSpellTexture or GetSpellTexture local VMRT = nil local module = ExRT:New("BossWatcher",ExRT.L.BossWatcher) local ELib,L = ExRT.lib,ExRT.L module.db.data = {} module.db.nowNum = 1 local fightData,guidData,graphData,reactionData,segmentsData = nil module.db.lastFightID = 0 module.db.timeFix = nil module.db.spellsSchool = {} local spellsSchool = module.db.spellsSchool local deathLog = {} module.db.deathLog = deathLog local raidGUIDs = {} local damageTakenLog = {} module.db.damageTakenLog = damageTakenLog local spellFix_LotM = {} local spellFix_SM = {} module.db.raidTargets = { [0x1] = 1, [0x2] = 2, [0x4] = 3, [0x8] = 4, [0x10] = 5, [0x20] = 6, [0x40] = 7, [0x80] = 8, [0x100] = 9, [0x200] = 10, [0x400] = 11, [0x800] = 12, [0x1000] = 13, [0x2000] = 14, [0x4000] = 15, [0x8000] = 16, [0x10000] = 17, [0x20000] = 18, } module.db.energyLocale = { [0] = "|cff69ccf0"..L.BossWatcherEnergyType0, [1] = "|cffedc294"..L.BossWatcherEnergyType1, [2] = "|cffd1fa99"..L.BossWatcherEnergyType2, [3] = "|cffffff8f"..L.BossWatcherEnergyType3, [4] = "|cfffff569"..L.BossWatcherEnergyType4, [5] = "|cffeb4561"..L.BossWatcherEnergyType5, [6] = "|cffeb4561"..L.BossWatcherEnergyType6, [7] = "|cff9482c9"..L.BossWatcherEnergyType7, [8] = "|cff"..format("%02x%02x%02x",113,0,197)..L.BossWatcherEnergyType8, [9] = "|cffffb3e0"..L.BossWatcherEnergyType9, [10] = "|cffffffff"..L.BossWatcherEnergyType10, [11] = "|cff"..format("%02x%02x%02x",0,143,255)..L.BossWatcherEnergyType11, [12] = "|cff4DbB98"..L.BossWatcherEnergyType12, [13] = "|cff"..format("%02x%02x%02x",51,0,102)..L.BossWatcherEnergyType13, [16] = "|cff"..format("%02x%02x%02x",0,255,255)..L.BossWatcherEnergyType16, [17] = "|cff"..format("%02x%02x%02x",209,76,223)..L.BossWatcherEnergyType17, [18] = "|cff"..format("%02x%02x%02x",255,147,0)..L.BossWatcherEnergyType18, [19] = "|cff"..format("%02x%02x%02x",170,169,53)..(L.BossWatcherEnergyType19 or "Essence"), } module.db.energyPerClass = { --class --for all --for player ["WARRIOR"] = {{1,10}, {1,10}}, ["PALADIN"] = {{0,10}, {0,9,10}}, ["HUNTER"] = {{2,10}, {2,10}}, ["ROGUE"] = {{3,10}, {2,4,10}}, ["PRIEST"] = {{0,10}, {0,10,13}}, ["DEATHKNIGHT"]={{6,10}, {5,6,10}}, ["SHAMAN"] = {{0,10}, {0,10,11}}, ["MAGE"] = {{0,10}, {0,10,16}}, ["WARLOCK"] = {{0,10}, {0,10,7,14,15}}, ["MONK"] = {{0,3,10}, {0,3,10,12}}, ["DRUID"] = {{0,1,3,10}, {0,3,4,8,10}}, ["DEMONHUNTER"]={{0,17,18,10}, {0,17,18,10}}, ["EVOKER"] = {{0,19,10}, {0,19,10}}, ["NO"] = {{0,1,2,3,6,10},{0,1,2,3,6,10,5,7,8,9,11,12,13,14,15,16,17,18,19}}, ["ALL"] = {{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25},{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25}}, } local energyPerClass = module.db.energyPerClass module.db.schoolsDefault = {0x1,0x2,0x4,0x8,0x10,0x20,0x40} module.db.schoolsColors = { [SCHOOL_MASK_NONE or Enum.Damageclass.MaskNone] = {r=.8,g=.8,b=.8}, [SCHOOL_MASK_PHYSICAL or Enum.Damageclass.MaskPhysical] = {r=1,g=.64,b=.19}, [SCHOOL_MASK_HOLY or Enum.Damageclass.MaskHoly] = {r=1,g=1,b=.56}, [SCHOOL_MASK_FIRE or Enum.Damageclass.MaskFire] = {r=.92,g=.27,b=.38}, [SCHOOL_MASK_NATURE or Enum.Damageclass.MaskNature] = {r=.6,g=1,b=.4}, --r=.82,g=.98,b=.6 [SCHOOL_MASK_FROST or Enum.Damageclass.MaskFrost] = {r=.29,g=.50,b=1}, [SCHOOL_MASK_SHADOW or Enum.Damageclass.MaskShadow] = {r=.72,g=.66,b=.94}, [SCHOOL_MASK_ARCANE or Enum.Damageclass.MaskArcane] = {r=.56,g=.95,b=1}, [0x1C] = {r=1,g=.3,b=1}, --Elemental [0x7C] = {r=.6,g=0,b=0}, --Chromatic [0x7E] = {r=1,g=0,b=0}, --Magic [0x7F] = {r=.25,g=.25,b=.25}, --Chaos } module.db.schoolsColorsGradient = { [0x3] = {SCHOOL_MASK_HOLY or Enum.Damageclass.MaskHoly,SCHOOL_MASK_PHYSICAL or Enum.Damageclass.MaskPhysical}, [0x5] = {SCHOOL_MASK_FIRE or Enum.Damageclass.MaskFire,SCHOOL_MASK_PHYSICAL or Enum.Damageclass.MaskPhysical}, [0x6] = {SCHOOL_MASK_FIRE or Enum.Damageclass.MaskFire,SCHOOL_MASK_HOLY or Enum.Damageclass.MaskHoly}, [0x9] = {SCHOOL_MASK_NATURE or Enum.Damageclass.MaskNature,SCHOOL_MASK_PHYSICAL or Enum.Damageclass.MaskPhysical}, [0xA] = {SCHOOL_MASK_NATURE or Enum.Damageclass.MaskNature,SCHOOL_MASK_HOLY or Enum.Damageclass.MaskHoly}, [0xC] = {SCHOOL_MASK_NATURE or Enum.Damageclass.MaskNature,SCHOOL_MASK_FIRE or Enum.Damageclass.MaskFire}, [0x11] = {SCHOOL_MASK_FROST or Enum.Damageclass.MaskFrost,SCHOOL_MASK_PHYSICAL or Enum.Damageclass.MaskPhysical}, [0x12] = {SCHOOL_MASK_FROST or Enum.Damageclass.MaskFrost,SCHOOL_MASK_HOLY or Enum.Damageclass.MaskHoly}, [0x14] = {SCHOOL_MASK_FROST or Enum.Damageclass.MaskFrost,SCHOOL_MASK_FIRE or Enum.Damageclass.MaskFire}, [0x18] = {SCHOOL_MASK_FROST or Enum.Damageclass.MaskFrost,SCHOOL_MASK_NATURE or Enum.Damageclass.MaskNature}, [0x21] = {SCHOOL_MASK_SHADOW or Enum.Damageclass.MaskShadow,SCHOOL_MASK_PHYSICAL or Enum.Damageclass.MaskPhysical}, [0x22] = {SCHOOL_MASK_SHADOW or Enum.Damageclass.MaskShadow,SCHOOL_MASK_HOLY or Enum.Damageclass.MaskHoly}, [0x24] = {SCHOOL_MASK_SHADOW or Enum.Damageclass.MaskShadow,SCHOOL_MASK_FIRE or Enum.Damageclass.MaskFire}, [0x28] = {SCHOOL_MASK_SHADOW or Enum.Damageclass.MaskShadow,SCHOOL_MASK_NATURE or Enum.Damageclass.MaskNature}, [0x30] = {SCHOOL_MASK_SHADOW or Enum.Damageclass.MaskShadow,SCHOOL_MASK_FROST or Enum.Damageclass.MaskFrost}, [0x41] = {SCHOOL_MASK_ARCANE or Enum.Damageclass.MaskArcane,SCHOOL_MASK_PHYSICAL or Enum.Damageclass.MaskPhysical}, [0x42] = {SCHOOL_MASK_ARCANE or Enum.Damageclass.MaskArcane,SCHOOL_MASK_HOLY or Enum.Damageclass.MaskHoly}, [0x44] = {SCHOOL_MASK_ARCANE or Enum.Damageclass.MaskArcane,SCHOOL_MASK_FIRE or Enum.Damageclass.MaskFire}, [0x48] = {SCHOOL_MASK_ARCANE or Enum.Damageclass.MaskArcane,SCHOOL_MASK_NATURE or Enum.Damageclass.MaskNature}, [0x50] = {SCHOOL_MASK_ARCANE or Enum.Damageclass.MaskArcane,SCHOOL_MASK_FROST or Enum.Damageclass.MaskFrost}, [0x60] = {SCHOOL_MASK_ARCANE or Enum.Damageclass.MaskArcane,SCHOOL_MASK_SHADOW or Enum.Damageclass.MaskShadow}, } module.db.schoolsNames = { [SCHOOL_MASK_NONE or Enum.Damageclass.MaskNone] = L.BossWatcherSchoolUnknown, [SCHOOL_MASK_PHYSICAL or Enum.Damageclass.MaskPhysical] = L.BossWatcherSchoolPhysical, [SCHOOL_MASK_HOLY or Enum.Damageclass.MaskHoly] = L.BossWatcherSchoolHoly, [SCHOOL_MASK_FIRE or Enum.Damageclass.MaskFire] = L.BossWatcherSchoolFire, [SCHOOL_MASK_NATURE or Enum.Damageclass.MaskNature] = L.BossWatcherSchoolNature, [SCHOOL_MASK_FROST or Enum.Damageclass.MaskFrost] = L.BossWatcherSchoolFrost, [SCHOOL_MASK_SHADOW or Enum.Damageclass.MaskShadow] = L.BossWatcherSchoolShadow, [SCHOOL_MASK_ARCANE or Enum.Damageclass.MaskArcane] = L.BossWatcherSchoolArcane, [0x1C] = L.BossWatcherSchoolElemental, --Elemental [0x7C] = L.BossWatcherSchoolChromatic, --Chromatic [0x7E] = L.BossWatcherSchoolMagic, --Magic [0x7F] = L.BossWatcherSchoolChaos, --Chaos } local ReductionAurasFunctions = { physical = 1, magic = 2, feintCheck = 3, dampenHarmCheck = 4, } module.db.reductionAuras = { --Paladin --[465] = {0.8,nil,function(auraVar) return (100+auraVar)/100 end}, --Devotion Aura [498] = 0.8, --Divine Protection [86659] = 0.5, --Guardian of Ancient Kings [31850] = 0.8, --Ardent Defender --Priest [45242] = 0.85, --Focused Will [33206] = 0.6, --Pain Suppression [81782] = 0.75, --Power Word: Barrier [47585] = 0.4, --Dispersion [194384] = 1, --Atonement talent --Druid [22812] = {0.8,nil,function(_,auraVar) return (100+auraVar)/100 end}, --Barkskin [102342] = {0.8,nil,function(auraVar) return (100+auraVar)/100 end}, --Ironbark --DH [212800] = {0.65,nil,function(_,_,auraVar) return (100+auraVar)/100 end}, --Blur --Shaman [260881] = 0.95, --Ghost in the Mist [108271] = 0.6, --Astral Shift --Warlock [104773] = {0.6,nil,function(_,_,auraVar) return (100+auraVar)/100 end}, --Unending Resolve --DK [48792] = {0.7,nil,function(_,_,auraVar) return (100+auraVar)/100 end}, --Icebound Fortitude --Monk [122278] = 0.75, --Dampen Harm [120954] = {0.8,nil,function(_,auraVar) return (100+auraVar)/100 end}, --Fortifying Brew --Warrior [871] = 0.6, --Shield Wall [184364] = 0.7, --Enraged Regeneration --Mage [113862] = 0.4, --Greater Invisibility --Other [303350] = 0.9, --EP weapon [296003] = {1,nil,function(_,auraVar) return (100+auraVar)/100 end}, --Essence: Unwavering Ward } module.db.reductionBySpec = { [63] = {30482, 0.94, ReductionAurasFunctions.physical, 0x4}, --Mage fire; 16% with artifact trait, 6% without [104] = {16931, 0.94}, --Druid bear [581] = {203513,0.9}, --Demonic Wards } module.db.reductionCurrent = {} module.db.reductionPowerWordBarrierCaster = nil module.db.reductionIsNotAoe = {} module.db.def_trackingDamageSpells = { [209471]=1873, --Il'gynoth: Nightmare Explosion [198099]=1841, --Ursoc: Barreling Momentum [199237]=1841, --Ursoc: Barreling Momentum > Crushing Impact [211073]=1877, --Cenarius: Desiccating Stomp [210619]=1877, --Cenarius: Destructive Nightmares [206369]=1864, --Xavius: Corruption Meteor [228162]=1958, --Odyn: Shield of Light [210074]=1849, --Crystal Scorpion: Shockwave [204733]=1849, --Crystal Scorpion: Volatile Chitin [207328]=1867, --Trillax: Cleansing Destruction [206376]=1842, --Krosus: Burning Pitch [206938]=1863, --Etraeus: Shatter [210546]=1872, --Elisande: orb [206370]=1866, --Guldan: [317066]=2344, --N'Zoth the Corruptor [326707]=2407, --Sire Denathrius: Cleansing Pain [339728]=2417, --Stone Legion Generals: Pulverizing Meteor [330712]=2398, --Shriekwing: Bloodcurdling Shriek [334948]=2412, --The Council of Blood: Unstoppable Charge [331708]=2412, --The Council of Blood: Scarlet Letter --[2812]=true, --Test } local var_reductionAuras,var_reductionCurrent = module.db.reductionAuras,module.db.reductionCurrent local var_trackingDamageSpells = nil local encounterSpecial = {} local _graphSectionTimer,_graphSectionTimerRounded,_graphRaidSnapshot = 0,0,{} local _graphRaidEnergy = {} module.db.graphRaidEnergy = _graphRaidEnergy local _BW_Start,_BW_End = nil local fightData_damage,fightData_damage_seen,fightData_heal,fightData_healFrom,fightData_switch,fightData_cast,fightData_auras,fightData_power,fightData_deathLog,fightData_maxHP,fightData_reduction local active_segment local active_phase local deathMaxEvents = 100 do local function CheckForCombat() if UnitAffectingCombat("player") or IsEncounterInProgress() then _BW_Start() end end function module:Enable() VMRT.BossWatcher.enabled = true module:RegisterEvents('ZONE_CHANGED_NEW_AREA','PLAYER_REGEN_DISABLED','PLAYER_REGEN_ENABLED','ENCOUNTER_START','ENCOUNTER_END','CHALLENGE_MODE_START','CHALLENGE_MODE_COMPLETED','CHALLENGE_MODE_RESET') module.main:ZONE_CHANGED_NEW_AREA() module:RegisterSlash() if select(3,GetInstanceInfo()) == 8 and C_ChallengeMode.IsChallengeModeActive() then module.main:CHALLENGE_MODE_START() elseif UnitAffectingCombat("player") then _BW_Start() else ExRT.F.Timer(CheckForCombat,3) end end end function module:Disable() VMRT.BossWatcher.enabled = nil if fightData then _BW_End() end module.main:UnregisterAllEvents() module:UnregisterSlash() end local BWInterfaceFrame = nil local BWInterfaceFrameLoad,isBWInterfaceFrameLoaded,BWInterfaceFrameLoadFunc = nil do local isAdded = nil function BWInterfaceFrameLoadFunc() if not isBWInterfaceFrameLoaded then BWInterfaceFrameLoad() end if isBWInterfaceFrameLoaded then if SettingsPanel then SettingsPanel:Hide() else InterfaceOptionsFrame:Hide() end BWInterfaceFrame:Show() end CloseDropDownMenus() ELib.ScrollDropDown.Close() end function module:miniMapMenu() if isAdded then return end local subMenu = { {text = L.BossWatcher, func = BWInterfaceFrameLoadFunc, notCheckable = true}, {text = L.BossWatcherTabMobs, func = ExRT.F.FightLog_OpenTab, arg1 = 1, notCheckable = true}, {text = L.BossWatcherTabHeal, func = ExRT.F.FightLog_OpenTab, arg1 = 2, notCheckable = true}, {text = L.BossWatcherTabBuffsAndDebuffs, func = ExRT.F.FightLog_OpenTab, arg1 = 3, notCheckable = true}, {text = L.BossWatcherTabEnemy, func = ExRT.F.FightLog_OpenTab, arg1 = 4, notCheckable = true}, {text = L.BossWatcherTabPlayersSpells, func = ExRT.F.FightLog_OpenTab, arg1 = 5, notCheckable = true}, {text = L.BossWatcherTabEnergy, func = ExRT.F.FightLog_OpenTab, arg1 = 6, notCheckable = true}, {text = L.BossWatcherTabInterruptAndDispel, func = ExRT.F.FightLog_OpenTab, arg1 = 7, notCheckable = true}, {text = TRACKING, func = ExRT.F.FightLog_OpenTab, arg1 = 8, notCheckable = true}, {text = L.BossWatcherDeath, func = ExRT.F.FightLog_OpenTab, arg1 = 9, notCheckable = true}, {text = L.BossWatcherPositions, func = ExRT.F.FightLog_OpenTab, arg1 = 10, notCheckable = true}, } ExRT.F.MinimapMenuAdd(L.BossWatcher, BWInterfaceFrameLoadFunc, 1, "FightLog_Navigation", subMenu) end module:RegisterMiniMapMenu() end ExRT.F.FightLog_Open = BWInterfaceFrameLoadFunc function ExRT.F:FightLog_OpenTab(tabID) if not isBWInterfaceFrameLoaded then BWInterfaceFrameLoad() end BWInterfaceFrame.tab:SelectTab(tabID) BWInterfaceFrame:Show() CloseDropDownMenus() ELib.ScrollDropDown.Close() end function module.options:Load() self:CreateTilte() self.checkEnabled = ELib:Check(self,L.Enable,VMRT.BossWatcher.enabled):Point(15,-35):AddColorState():OnClick(function(self) if self:GetChecked() then module:Enable() else module:Disable() end end) self.sliderNum = ELib:Slider(self,L.BossWatcherOptionsFightsSave):Size(300):Point(20,-125):Range(1,25):SetTo(VMRT.BossWatcher.fightsNum or 1):OnChange(function(self,event) event = ExRT.F.Round(event) VMRT.BossWatcher.fightsNum = event self.tooltipText = event self:tooltipReload(self) end) self.warningText = ELib:Text(self,L.BossWatcherOptionsFightsWarning,12):Size(570,25):Point(15,-150):Top():Color():Shadow() if ExRT.isDev then self.saveVariables = ELib:Check(self,L.BossWatcherSaveVariables):Point(260,-35):Tooltip(L.BossWatcherSaveVariablesWarring):OnClick(function(self) VMRT.BossWatcher.saveVariables = true VMRT.BossWatcher.SAVED_DATA = module.db.data print('Saved') end) end self.showButton = ELib:Button(self,L.BossWatcherGoToBossWatcher):Size(550,20):Point("TOP",0,-200):OnClick(function () if SettingsPanel then SettingsPanel:Hide() else InterfaceOptionsFrame:Hide() end ExRT.Options.Frame:Hide() BWInterfaceFrameLoadFunc() end) self.buttonChecker = CreateFrame("Frame",nil,self) self.buttonChecker:SetScript("OnShow",function (self) if not ExRT.Options.Frame:IsShown() then module.options.showButton:Hide() else module.options.showButton:Show() end end) self.chatText = ELib:Text(self,L.BossWatcherOptionsHelp,12):Size(600,250):Point(15,-235):Top():Color():Shadow() function module.options:AdditionalOnShow() if ExRT.Options.Frame:IsShown() then module.options:SetParent(ExRT.Options.Frame) module.options:ClearAllPoints() module.options:SetPoint("TOPLEFT",ExRT.Options.Frame.ListWidth,-16) end end end local function UpdateTrackingDamageSpellsTable() var_trackingDamageSpells = ExRT.F.table_copy2(module.db.def_trackingDamageSpells) for spellID,_ in pairs(VMRT.BossWatcher.trackingDamageSpells) do var_trackingDamageSpells[spellID] = true end end function module.main:ADDON_LOADED() VMRT = _G.VMRT VMRT.BossWatcher = VMRT.BossWatcher or { optionsDamageGraph = true, optionsHealingGraph = true, optionsPositionsDist = true, fightsNum = 2, } if VMRT.BossWatcher.enabled then module:Enable(true) end VMRT.BossWatcher.fightsNum = VMRT.BossWatcher.fightsNum or 2 VMRT.BossWatcher.trackingDamageSpells = VMRT.BossWatcher.trackingDamageSpells or {} UpdateTrackingDamageSpellsTable() if VMRT.BossWatcher.saveVariables and VMRT.BossWatcher.SAVED_DATA then module.db.data = VMRT.BossWatcher.SAVED_DATA for i=1,#module.db.data do if module.db.data[i] then module.db.data[i].fightID = -i end end else VMRT.BossWatcher.SAVED_DATA = nil end VMRT.BossWatcher.saveVariables = nil if VMRT.Addon.Version < 3596 then VMRT.BossWatcher.optionsDamageGraph = true VMRT.BossWatcher.optionsHealingGraph = true VMRT.BossWatcher.optionsPositionsDist = true end end local negateHealing = {} local SLTReductionAuraSpellID = 98007 local SLTReductionAuraData = {} local SLTReductionSourceGUID = nil local SLTReductionFrame = CreateFrame("Frame") SLTReductionFrame:SetScript("OnEvent",function(_,_,unit) local findEm = nil for i=1,60 do local auraData = C_UnitAuras.GetAuraDataByIndex(unit, i, "HELPFUL") if not auraData then break elseif auraData.spellId == SLTReductionAuraSpellID then findEm = true break end end local guid = UnitGUID(unit) if not guid then return elseif findEm and not SLTReductionAuraData[ guid ] then if not fightData_auras then return end local destData = var_reductionCurrent[ guid ] if not destData then destData = {} var_reductionCurrent[ guid ] = destData end local destCount = #destData local from = 1 for i=1,destCount do from = from * destData[i].r end local currReduction = 1 / (1 - (from - from * 0.9)) destData[destCount + 1] = { s = SLTReductionAuraSpellID, r = 0.9, c = (currReduction - 1), g = SLTReductionSourceGUID, } SLTReductionAuraData[ guid ] = true fightData_auras[ #fightData_auras + 1 ] = {GetTime() - module.db.timeFix[1] + module.db.timeFix[2],SLTReductionSourceGUID,UnitGUID(unit),true,true,SLTReductionAuraSpellID,"BUFF",1,1} elseif not findEm and SLTReductionAuraData[ guid ] then if not fightData_auras then return end local destData = var_reductionCurrent[ guid ] if not destData then return end for i=1,#destData do if destData[i] and destData[i].s == SLTReductionAuraSpellID then tremove(destData,i) end end local from,fromPhysical,fromMagic = 1,1,1 for i=1,#destData do local spellData = destData[i] local currReduction = nil if spellData.f == ReductionAurasFunctions.magic then currReduction = 1 / (1 - (fromMagic - fromMagic * spellData.r)) fromMagic = fromMagic * spellData.r elseif spellData.f == ReductionAurasFunctions.physical then currReduction = 1 / (1 - (fromPhysical - fromPhysical * spellData.r)) fromPhysical = fromPhysical * spellData.r else currReduction = 1 / (1 - (from - from * spellData.r)) fromPhysical = fromPhysical * spellData.r fromMagic = fromMagic * spellData.r end from = from * spellData.r spellData.c = currReduction - 1 end SLTReductionAuraData[ guid ] = nil fightData_auras[ #fightData_auras + 1 ] = {GetTime() - module.db.timeFix[1] + module.db.timeFix[2],SLTReductionSourceGUID,UnitGUID(unit),true,true,SLTReductionAuraSpellID,"BUFF",2,1} end end) local SLTReductionUnregTimer = nil local function SLTReductionUnreg() SLTReductionUnregTimer = nil SLTReductionFrame:UnregisterAllEvents() wipe(SLTReductionAuraData) end local function SLTReductionReg(sourceGUID) SLTReductionSourceGUID = sourceGUID SLTReductionFrame:RegisterEvent("UNIT_AURA") end local reductionAtonement = {} local function addReductionOnPull(unit,destGUID) --------------> Add passive reductions --- Note: this is first reduction check ever and I must don't care about any existens data local unitInspectData = ExRT.A.ExCD2 and ExRT.A.ExCD2.db and ExRT.A.ExCD2.db.inspectDB and ExRT.A.ExCD2.db.inspectDB[unit] local specID = unitInspectData and unitInspectData.spec or 0 local reductionSpec = module.db.reductionBySpec[ specID ] if reductionSpec then var_reductionCurrent[ destGUID ] = { { s = reductionSpec[1], r = reductionSpec[2], c = (1 / reductionSpec[2] - 1), g = destGUID, f = reductionSpec[3], } } spellsSchool[ reductionSpec[1] ] = reductionSpec[4] or 0x1 end if unitInspectData and unitInspectData.class == "PRIEST" and specID == 256 and unitInspectData.GUID then if (unitInspectData[7] == 1) then reductionAtonement[ unitInspectData.GUID ] = 0.97 else reductionAtonement[ unitInspectData.GUID ] = nil end end if unitInspectData and unitInspectData.class == "DRUID" and specID ~= 104 then --Guardian affinity if (unitInspectData[3] == 2 and specID ~= 105) or (unitInspectData[3] == 3 and specID == 105) then var_reductionCurrent[ destGUID ] = { { s = 16931, r = 0.94, c = (1 / 0.94 - 1), g = destGUID, } } end end --------------> Add active reductions from current auras for i=1,60 do local auraData = C_UnitAuras.GetAuraDataByIndex(unit,i) if not auraData then return end --------------> Add reduction local reduction = var_reductionAuras[auraData.spellId] if reduction then local sourceGUID = nil if auraData.sourceUnit then sourceGUID = UnitGUID(auraData.sourceUnit) end sourceGUID = sourceGUID or "" local destData = var_reductionCurrent[ destGUID ] if not destData then destData = {} var_reductionCurrent[ destGUID ] = destData end local destCount = #destData local func,funcAura,reductionTable = nil if type(reduction)=="table" then reductionTable = reduction funcAura = reduction[3] func = reduction[2] reduction = reduction[1] end if funcAura then if auraData.points then reduction, func = funcAura(auraData.points[1] or 0,auraData.points[2] or 0,auraData.points[3] or 0,auraData.points[4] or 0,auraData.points[5] or 0) if not reduction then reduction = reductionTable[1] func = reductionTable[2] funcAura = nil end --ExRT.F.dprint(format("%s > %s: %s [%d%%]",sourceName,destName,spellName,(reduction or 0)*100)) else funcAura = nil end end if reduction ~= 1 then local from = 1 if func == ReductionAurasFunctions.magic then for i=1,destCount do if destData[i].f ~= ReductionAurasFunctions.physical then from = from * destData[i].r end end elseif func == ReductionAurasFunctions.physical then for i=1,destCount do if destData[i].f ~= ReductionAurasFunctions.magic then from = from * destData[i].r end end else for i=1,destCount do from = from * destData[i].r end end local currReduction = 1 / (1 - (from - from * reduction)) destData[destCount + 1] = { s = auraData.spellId, r = reduction, c = (currReduction - 1), g = sourceGUID, f = func, } if not spellsSchool[auraData.spellId] then spellsSchool[auraData.spellId] = 0x1 end end end end end local BossPhasesData = {} local BossPhasesFrame = CreateFrame("Frame") local BossPhasesBossmodPhaseCounter, BossPhasesBossmodPhase, BossPhasesBossmodEnabled = 1 local BossPhasesBossmodCL local BossPhasesBossmod function BossPhasesBossmod() if BigWigsLoader and type(BigWigsLoader)=='table' and BigWigsLoader.RegisterMessage then local r = {} function r:BigWigs_Message (event, module, key, text, ...) if (key == "stages") then if not text or not BossPhasesBossmodEnabled then return elseif BossPhasesBossmodPhase ~= text then if BossPhasesBossmodCL or BigWigsAPI then local CL = BossPhasesBossmodCL or BigWigsAPI:GetLocale("BigWigs: Common") BossPhasesBossmodCL = CL if CL and CL.soon and type(text)=='string' then local patt = CL.soon:gsub("%%s","") if CL.soon:find("^%%s") then patt = patt .. "$" else patt = "^" .. patt end if text:find( patt ) then return end end end local phaseNumInDB = nil for index,phase in pairs(segmentsData.phaseNames) do if text == phase then phaseNumInDB = index break end end if not phaseNumInDB then BossPhasesBossmodPhaseCounter = BossPhasesBossmodPhaseCounter + 1 phaseNumInDB = BossPhasesBossmodPhaseCounter segmentsData.phaseNames[BossPhasesBossmodPhaseCounter] = text end BossPhasesBossmodPhase = text active_phase = phaseNumInDB end end end BigWigsLoader.RegisterMessage (r, "BigWigs_Message") BossPhasesBossmod = nil end end function _BW_Start(encounterID,encounterName) module.db.lastFightID = module.db.lastFightID + 1 local maxFights = (VMRT.BossWatcher.fightsNum or 5) for i=maxFights+1,2,-1 do module.db.data[i] = module.db.data[i-1] end fightData_damage = {} fightData_damage_seen = {} fightData_heal = {} fightData_healFrom = {} fightData_switch = {} fightData_cast = {} fightData_auras = {} fightData_power = {} fightData_deathLog = {} fightData_maxHP = {} fightData_reduction = {} fightData = { guids = {}, raidguids = {}, reaction = {}, pets = {}, encounterName = encounterName, encounterID = encounterID, encounterStartGlobal = time(), encounterStart = GetTime(), encounterEnd = 0, graphData = {}, positionsData = {}, fightID = module.db.lastFightID, segments = { [1] = { e=true, t=GetTime(), p=1, }, }, damage = fightData_damage, damage_seen = fightData_damage_seen, heal = fightData_heal, healFrom = fightData_healFrom, switch = fightData_switch, cast = fightData_cast, interrupts = {}, dispels = {}, auras = fightData_auras, power = fightData_power, dies = {}, chat = {}, resurrests = {}, summons = {}, aurabroken = {}, deathLog = fightData_deathLog, maxHP = fightData_maxHP, reduction = fightData_reduction, tracking = {}, other = { roles = {}, rolesGUID = {}, }, } module.db.data[1] = fightData wipe(deathLog) wipe(damageTakenLog) wipe(encounterSpecial) raidGUIDs = fightData.raidguids guidData = fightData.guids graphData = fightData.graphData reactionData = fightData.reaction segmentsData = fightData.segments active_segment = 1 active_phase = 1 wipe(negateHealing) module:RegisterEvents('COMBAT_LOG_EVENT_UNFILTERED','UNIT_TARGET','RAID_BOSS_EMOTE','RAID_BOSS_WHISPER','UPDATE_MOUSEOVER_UNIT') BossPhasesBossmodEnabled = nil local bossPhaseReg = BossPhasesData[encounterID or -1] if bossPhaseReg then BossPhasesFrame:SetScript("OnEvent",bossPhaseReg.func) for i=1,#bossPhaseReg.events do local event = bossPhaseReg.events[i] if type(event)=='table' then BossPhasesFrame:RegisterUnitEvent(event[1],event[2]) else BossPhasesFrame:RegisterEvent(event) end end segmentsData.phaseNames = bossPhaseReg.names elseif encounterID then BossPhasesBossmodPhase = nil BossPhasesBossmodEnabled = true BossPhasesBossmodPhaseCounter = 1 if BossPhasesBossmod then BossPhasesBossmod() end segmentsData.phaseNames = {[1] = 1,} if BossPhasesBossmodCL and BossPhasesBossmodCL.stage then segmentsData.phaseNames[1] = BossPhasesBossmodCL.stage:format(1) end end _graphSectionTimer = 0 _graphSectionTimerRounded = 0 _graphRaidSnapshot = {"boss1","boss2","boss3","boss4","boss5","boss6","target","focus"} local energyPerClass_general = energyPerClass["NO"][1] wipe(_graphRaidEnergy) for i=1,#_graphRaidSnapshot do _graphRaidEnergy[i] = energyPerClass_general end wipe(var_reductionCurrent) if not var_trackingDamageSpells then var_trackingDamageSpells = {} end module:RegisterTimer() for _, name, subgroup, class, guid, rank, level, online, isDead, combatRole in ExRT.F.IterateRoster, ExRT.F.GetRaidDiffMaxGroup() do _graphRaidSnapshot[#_graphRaidSnapshot + 1] = name if module.db.allEnergyMode then class = "ALL" end local energy = energyPerClass[class or "NO"] if name == ExRT.SDB.charName then energy = energy and energy[2] else energy = energy and energy[1] end _graphRaidEnergy[#_graphRaidSnapshot] = energy or energyPerClass_general if guid then raidGUIDs[ guid ] = name addReductionOnPull(name,guid) fightData.other.rolesGUID[guid] = combatRole end fightData.other.roles[name] = combatRole end end function _BW_End(encounterID) if fightData then fightData.encounterEnd = GetTime() fightData.timeFix = module.db.timeFix fightData.ExRTver = ExRT.V fightData.isEnded = true if not fightData.encounterName then local minSeen,minGUID = nil for GUID,seen in pairs(fightData_damage_seen) do if (not minSeen or minSeen > seen) and ExRT.F.GetUnitInfoByUnitFlag(fightData.reaction[GUID],3) == 64 then minGUID = GUID minSeen = seen end end if minGUID and fightData.guids[minGUID] and fightData.guids[minGUID] ~= "nil" then fightData.encounterName = fightData.guids[minGUID] end end local GLOBALpets = ExRT.F.Pets:getPetsDB() for GUID,name in pairs(fightData.guids) do local petData = GLOBALpets[GUID] if petData then fightData.pets[GUID] = petData end end end wipe(encounterSpecial) module:UnregisterEvents('COMBAT_LOG_EVENT_UNFILTERED','UNIT_TARGET','RAID_BOSS_EMOTE','RAID_BOSS_WHISPER','UPDATE_MOUSEOVER_UNIT') module:UnregisterTimer() BossPhasesFrame:UnregisterAllEvents() fightData = nil guidData = nil graphData = nil reactionData = nil active_phase = nil if BossPhasesBossmodEnabled and segmentsData.phaseNames and ExRT.F.table_len(segmentsData.phaseNames) == 1 then segmentsData.phaseNames = nil end BossPhasesBossmodEnabled = nil segmentsData = nil wipe(deathLog) wipe(var_reductionCurrent) wipe(damageTakenLog) local maxFights = (VMRT.BossWatcher.fightsNum or 5) for i=25,1,-1 do local data = module.db.data[i] if data and data.encounterStart and data.encounterEnd and data.encounterID and (data.encounterEnd - data.encounterStart) < 20 then local c = 0 for k,v in pairs(data.raidguids) do c = c + 1 if c > 1 then break end end if c > 1 then tremove(module.db.data,i) end end end for i=(maxFights+1),25 do if module.db.data[i] then wipe(module.db.data[i]) end module.db.data[i] = nil end fightData_damage = nil fightData_damage_seen = nil fightData_heal = nil fightData_healFrom = nil fightData_switch = nil fightData_cast = nil fightData_auras = nil fightData_power = nil fightData_deathLog = nil fightData_maxHP = nil fightData_reduction = nil end do local segment_tmr,segment_checker = 0,-1 function module:timer(elapsed) --------------> Graphs _graphSectionTimer = _graphSectionTimer + elapsed local nowTimer = _graphSectionTimer - _graphSectionTimer % 1 + 1 if _graphSectionTimerRounded ~= nowTimer then _graphSectionTimerRounded = nowTimer local data = {} graphData[_graphSectionTimerRounded] = data for i=1,#_graphRaidSnapshot do local name = _graphRaidSnapshot[i] local _name = i <= 8 and UnitCombatlogname(name) if i > 8 or _name then local health = UnitHealth(name) local hpmax = UnitHealthMax(name) local absorbs = UnitGetTotalAbsorbs(name) local power = UnitPower(name) local powerMax = UnitPowerMax(name) local currData = { name = _name or nil, health = health ~= 0 and health or nil, hpmax = hpmax ~= 0 and hpmax or nil, absorbs = absorbs ~= 0 and absorbs or nil, power = power ~= 0 and power or nil, pmax = powerMax ~=0 and powerMax or nil, } data[name] = currData local energy = _graphRaidEnergy[i] for j=1,#energy do local powerID = energy[j] local power = UnitPower(name,powerID) if power ~= 0 then currData[powerID] = power end end end end end segment_tmr = segment_tmr + elapsed local nowSegment = segment_tmr - segment_tmr % 1 if nowSegment ~= segment_checker then segment_checker = nowSegment active_segment = active_segment + 1 segmentsData[active_segment] = { e = true, t = GetTime(), p = active_phase, } end end end local function GetCurrentZoneID() local zoneID = 0 local zoneName, zoneType, difficulty, _, _, _, _, mapID = GetInstanceInfo() if difficulty == 8 then zoneID = 3 elseif zoneType == 'raid' and ((tonumber(mapID) and mapID >= 603) or mapID == 533) then zoneID = 1 elseif zoneType == 'arena' or zoneType == 'pvp' then zoneID = 2 elseif zoneType == 'scenario' and (difficulty == 38 or difficulty == 39 or difficulty == 40 or difficulty == 45) then --islands zoneID = 4 elseif mapID == 2274 or mapID == 2212 or mapID == 2213 or mapID == 2275 then --horrific vision zoneID = 4 elseif difficulty == 167 then --torghast zoneID = 4 end return zoneID, zoneName end function module.main:ENCOUNTER_START(encounterID,encounterName) local zoneID = GetCurrentZoneID() if zoneID == 1 then _BW_Start(encounterID,encounterName) end end function module.main:PLAYER_REGEN_DISABLED() local zoneID = GetCurrentZoneID() if zoneID == 0 then _BW_Start() end end function module.main:CHALLENGE_MODE_START() local _,zoneName = GetCurrentZoneID() _BW_Start(nil,zoneName) end function module.main:ENCOUNTER_END(encounterID) local zoneID = GetCurrentZoneID() if zoneID == 1 then _BW_End(encounterID) end end function module.main:PLAYER_REGEN_ENABLED() local zoneID = GetCurrentZoneID() if zoneID == 0 then _BW_End() end end function module.main:CHALLENGE_MODE_RESET() if fightData then _BW_End() end end module.main.CHALLENGE_MODE_COMPLETED = module.main.CHALLENGE_MODE_RESET do local function ZoneCheck() local zoneID, zoneName = GetCurrentZoneID() if zoneID == 2 or zoneID == 4 then _BW_Start(nil,zoneName) end end function module.main:ZONE_CHANGED_NEW_AREA() if fightData then _BW_End() end ExRT.F.ScheduleTimer(ZoneCheck,2) end end local function AddNotRealDeath(destGUID,timestamp,spellID) local destData = deathLog[destGUID] if not destData then destData = {c = 0} deathLog[destGUID] = destData end local destTable = { {5,destGUID,timestamp,active_segment,spellID}, } destTable.h = destTable[1] local destTableLen = 1 fightData_deathLog[#fightData_deathLog + 1] = destTable local c = destData.c for i=c,1,-1 do local copyTable = destData[i] if copyTable and copyTable.t then destTableLen = destTableLen + 1 destTable[destTableLen] = { copyTable.t, copyTable.s, copyTable.ti, copyTable.sp, copyTable.a, copyTable.o, copyTable.sc, copyTable.b, copyTable.ab, copyTable.c, false, copyTable.h, copyTable.hm, copyTable.ia, copyTable.sm, copyTable.dm, } end end for i=deathMaxEvents,c+1,-1 do local copyTable = destData[i] if copyTable and copyTable.t then destTableLen = destTableLen + 1 destTable[destTableLen] = { copyTable.t, copyTable.s, copyTable.ti, copyTable.sp, copyTable.a, copyTable.o, copyTable.sc, copyTable.b, copyTable.ab, copyTable.c, false, copyTable.h, copyTable.hm, copyTable.ia, copyTable.sm, copyTable.dm, } end end destData.schFunc = function() local tinsert = tinsert if destData.c ~= c then local d = {} if destData.c < c then for i=destData.c,1,-1 do local copyTable = destData[i] if copyTable and copyTable.t and (copyTable.ti - timestamp) <= 0.25 then d[#d+1] = { copyTable.t, copyTable.s, copyTable.ti, copyTable.sp, copyTable.a, copyTable.o, copyTable.sc, copyTable.b, copyTable.ab, copyTable.c, false, copyTable.h, copyTable.hm, copyTable.ia, copyTable.sm, copyTable.dm, } end end for i=deathMaxEvents,c+1,-1 do local copyTable = destData[i] if copyTable and copyTable.t and (copyTable.ti - timestamp) <= 0.25 then d[#d+1] = { copyTable.t, copyTable.s, copyTable.ti, copyTable.sp, copyTable.a, copyTable.o, copyTable.sc, copyTable.b, copyTable.ab, copyTable.c, false, copyTable.h, copyTable.hm, copyTable.ia, copyTable.sm, copyTable.dm, } end end for i=#d,1,-1 do tinsert(destTable,1,d[i]) end else for i=destData.c,c+1,-1 do local copyTable = destData[i] if copyTable and copyTable.t and (copyTable.ti - timestamp) <= 0.25 then d[#d+1] = { copyTable.t, copyTable.s, copyTable.ti, copyTable.sp, copyTable.a, copyTable.o, copyTable.sc, copyTable.b, copyTable.ab, copyTable.c, false, copyTable.h, copyTable.hm, copyTable.ia, copyTable.sm, copyTable.dm, } end end for i=#d,1,-1 do tinsert(destTable,1,d[i]) end end end destData.schFunc = nil destData.sch = nil end destData.sch = C_Timer.NewTimer(1,destData.schFunc) end module.AddNotRealDeath = AddNotRealDeath local EnvironmentalTypeToSpellID = { ["Falling"] = 110122, ["Drowning"] = 68730, ["Fatigue"] = 125024, ["Fire"] = 103795, ["Lava"] = 119741, ["Slime"] = 16456, -- UnkEnvDamage = 48360, } local DeathLogBlackList = { [154420] = true, --Azshara encounter [157452] = true, --Nzoth spine } local CLEUParser, CLEU function module.main.SPELL_DAMAGE(timestamp,event,hideCaster,sourceGUID,sourceName,sourceFlags,sourceFlags2,destGUID,destName,destFlags,destFlags2,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,val11,val12,val13,val14) --[[ val1 spellID val2 _ val3 _ val4 amount val5 overkill val6 school val7 resisted val8 blocked val9 absorbed val10 critical val11 glancing val12 crushing val13 isOffHand val14 missType --Note, missType param added by myself for tracking function ]] --------------> Add damage local destTable = fightData_damage[destGUID] --destTable if not destTable then fightData_damage_seen[destGUID] = timestamp destTable = { [destFlags]={ [sourceGUID]={ [event == "SPELL_PERIODIC_DAMAGE" and -val1 or val1]={ [active_segment] = { amount = 0, count = 0, overkill = 0, blocked = 0, absorbed = 0, crit = 0, critcount = 0, critmax = 0, critover = 0, hitmax = 0, parry = 0, dodge = 0, miss = 0, } } } } } fightData_damage[destGUID] = destTable if val6 then spellsSchool[val1] = val6 end end local destReaction = destTable[destFlags] --destReaction if not destReaction then destReaction = { [sourceGUID]={ [event == "SPELL_PERIODIC_DAMAGE" and -val1 or val1]={ [active_segment] = { amount = 0, count = 0, overkill = 0, blocked = 0, absorbed = 0, crit = 0, critcount = 0, critmax = 0, critover = 0, hitmax = 0, parry = 0, dodge = 0, miss = 0, } } } } destTable[destFlags] = destReaction if val6 then spellsSchool[val1] = val6 end end local sourceTable = destReaction[sourceGUID] --sourceTable if not sourceTable then sourceTable = { [event == "SPELL_PERIODIC_DAMAGE" and -val1 or val1]={ [active_segment] = { amount = 0, count = 0, overkill = 0, blocked = 0, absorbed = 0, crit = 0, critcount = 0, critmax = 0, critover = 0, hitmax = 0, parry = 0, dodge = 0, miss = 0, } } } destReaction[sourceGUID] = sourceTable if val6 then spellsSchool[val1] = val6 end end local segmentsTable = sourceTable[event == "SPELL_PERIODIC_DAMAGE" and -val1 or val1] --segmentsTable if not segmentsTable then segmentsTable = { [active_segment] = { amount = 0, count = 0, overkill = 0, blocked = 0, absorbed = 0, crit = 0, critcount = 0, critmax = 0, critover = 0, hitmax = 0, parry = 0, dodge = 0, miss = 0, } } sourceTable[event == "SPELL_PERIODIC_DAMAGE" and -val1 or val1] = segmentsTable if val6 then spellsSchool[val1] = val6 end end local spellTable = segmentsTable[active_segment] --spellTable if not spellTable then spellTable = { amount = 0, count = 0, overkill = 0, blocked = 0, absorbed = 0, crit = 0, critcount = 0, critmax = 0, critover = 0, hitmax = 0, parry = 0, dodge = 0, miss = 0, } segmentsTable[active_segment] = spellTable end spellTable.amount = spellTable.amount + val4 spellTable.count = spellTable.count + 1 if val5 > 0 then spellTable.overkill = spellTable.overkill + val5 end if val8 then spellTable.blocked = spellTable.blocked + val8 end if val9 then spellTable.absorbed = spellTable.absorbed + val9 end if val10 then spellTable.crit = spellTable.crit + val4 spellTable.critcount = spellTable.critcount + 1 if spellTable.critmax < val4 then spellTable.critmax = val4 end spellTable.critover = spellTable.critover + (val5 > 0 and val5 or 0) + (val8 and val8 or 0) + (val9 and val9 or 0) elseif spellTable.hitmax < val4 then spellTable.hitmax = val4 end --------------> Add death --local mobID = GUIDtoID(destGUID) --if not DeathLogBlackList[mobID] then do local destData = deathLog[destGUID] --destData if not destData then destData = {c=0} deathLog[destGUID] = destData end local tmpVar = destData.c + 1 if tmpVar > deathMaxEvents then tmpVar = 1 end destData.c = tmpVar local activeTable = destData[tmpVar] if not activeTable then activeTable = {} destData[tmpVar] = activeTable end activeTable.t = 1 activeTable.s = sourceGUID activeTable.ti = timestamp activeTable.sp = val1 activeTable.a = val4 activeTable.o = val5 activeTable.sc = val6 activeTable.b = val8 activeTable.ab = val9 activeTable.c = val10 activeTable.ia = nil activeTable.sm = sourceFlags2 activeTable.dm = destFlags2 local raidunit = raidGUIDs[ destGUID ] if raidunit then activeTable.h = UnitHealth( raidunit ) activeTable.hm = UnitHealthMax( raidunit ) end end --------------> Add reduction local reductionTable = var_reductionCurrent[destGUID] if reductionTable then local reduction = fightData_reduction[destGUID] if not reduction then reduction = {} fightData_reduction[destGUID] = reduction end local reduction2 = reduction[sourceGUID] if not reduction2 then reduction2 = {} reduction[sourceGUID] = reduction2 end reduction = reduction2[val1] if not reduction then reduction = {} reduction2[val1] = reduction end for i=1,#reductionTable do local reductionSubtable = reductionTable[i] local amount2 = val4+(val9 or 0)+(val8 or 0)+val5 local isCheck = reductionSubtable.f if not isCheck then isCheck = true elseif isCheck == 1 then --physical isCheck = val6 == 1 elseif isCheck == 2 then --magic isCheck = bit_band(val6 or 0,1) == 0 elseif isCheck == 3 then --feintCheck isCheck = module.db.reductionIsNotAoe[val1] elseif isCheck == 4 then --dampenHarmCheck local unitHealthMax = UnitHealthMax(destName or "?") unitHealthMax = unitHealthMax == 0 and 1400000 or unitHealthMax isCheck = (amount2 / unitHealthMax) >= 0.15 end if isCheck then local reductionGUID = reductionSubtable.g reduction2 = reduction[reductionGUID] if not reduction2 then reduction2 = {} reduction[reductionGUID] = reduction2 end local reductionSpell = reductionSubtable.s local amount = amount2 * reductionSubtable.c local reductionSpellData = reduction2[reductionSpell] if not reductionSpellData then reductionSpellData = {} reduction2[reductionSpell] = reductionSpellData end reductionSpellData[active_segment] = (reductionSpellData[active_segment] or 0)+amount end end end --------------> Add switch local switchTable = fightData_switch[destGUID] if not switchTable then switchTable = { [1]={}, --cast [2]={}, --target seen=timestamp, } fightData_switch[destGUID] = switchTable end local switchTableCLEU = switchTable[1] if not switchTableCLEU[sourceGUID] then switchTableCLEU[sourceGUID] = {timestamp,val1,1} end --------------> Add special spells [tracking] if var_trackingDamageSpells[val1] then fightData.tracking[#fightData.tracking + 1] = {timestamp,sourceGUID,sourceFlags2,destGUID,destFlags2,val1,val4,val5,val6,val8,val9,val10,val14,s = active_segment} end --------------> Other if val1 == 196917 then -- Light of the Martyr: effective healing fix local lotmData = spellFix_LotM[sourceGUID] if lotmData then local damageTaken = val4 + val5 + (val9 or 0) if damageTaken < (lotmData[1] + lotmData[2]) and (timestamp - lotmData[5]) < 0.2 then local spellTable = lotmData[3] if lotmData[2] == 0 then spellTable.amount = spellTable.amount - damageTaken elseif lotmData[2] >= damageTaken then spellTable.absorbed = spellTable.absorbed - damageTaken else spellTable.absorbed = spellTable.absorbed - lotmData[2] spellTable.amount = spellTable.amount - damageTaken + lotmData[2] end if lotmData[4] then spellTable.crit = spellTable.crit - damageTaken end end end --[[ elseif val1 == 186439 then -- Shadow Mend local spellTable = spellFix_SM[destGUID] if spellTable then local damageTaken = val4 + val5 + (val9 or 0) local amount = spellTable.amount if amount < damageTaken then spellTable.amount = 0 else spellTable.amount = amount - damageTaken end end ]] end if val14 then return spellTable end end function module.main.SPELL_HEAL(timestamp,event,hideCaster,sourceGUID,sourceName,sourceFlags,sourceFlags2,destGUID,destName,destFlags,destFlags2,val1,val2,val3,val4,val5,val6,val7) --[[ Note about healing: amount = healing + overhealing absorbed = if spell absorbed by ability (ex. DK's egg, Koragh shadow phase) absorbs = if spell is absorb (ex. PW:S, HPally mastery, BloodDK mastery) ]] --[[ val1 spellID val2 _ val3 school val4 amount val5 overhealing val6 absorbed val7 critical ]] if val6 < 0 then return end --Fix 8.2 boss --------------> Add heal local sourceTable = fightData_heal[sourceGUID] --sourceTable if not sourceTable then sourceTable = { [destGUID] = { [destFlags] = { [event == "SPELL_PERIODIC_HEAL" and -val1 or val1] = { [active_segment] = { amount = 0, over = 0, absorbed = 0, count = 0, crit = 0, critcount = 0, critmax = 0, critover = 0, hitmax = 0, absorbs = 0, } } } } } fightData_heal[sourceGUID] = sourceTable if val3 then spellsSchool[val1] = val3 end end local destTable = sourceTable[destGUID] --destTable if not destTable then destTable = { [destFlags] = { [event == "SPELL_PERIODIC_HEAL" and -val1 or val1] = { [active_segment] = { amount = 0, over = 0, absorbed = 0, count = 0, crit = 0, critcount = 0, critmax = 0, critover = 0, hitmax = 0, absorbs = 0, } } } } sourceTable[destGUID] = destTable if val3 then spellsSchool[val1] = val3 end end local destReactTable = destTable[destFlags] --destReactTable if not destReactTable then destReactTable = { [event == "SPELL_PERIODIC_HEAL" and -val1 or val1] = { [active_segment] = { amount = 0, over = 0, absorbed = 0, count = 0, crit = 0, critcount = 0, critmax = 0, critover = 0, hitmax = 0, absorbs = 0, } } } destTable[destFlags] = destReactTable if val3 then spellsSchool[val1] = val3 end end local segmentsTable = destReactTable[event == "SPELL_PERIODIC_HEAL" and -val1 or val1] --segmentsTable if not segmentsTable then segmentsTable = { [active_segment] = { amount = 0, over = 0, absorbed = 0, count = 0, crit = 0, critcount = 0, critmax = 0, critover = 0, hitmax = 0, absorbs = 0, } } destReactTable[event == "SPELL_PERIODIC_HEAL" and -val1 or val1] = segmentsTable if val3 then spellsSchool[val1] = val3 end end local spellTable = segmentsTable[active_segment] if not spellTable then spellTable = { amount = 0, over = 0, absorbed = 0, count = 0, crit = 0, critcount = 0, critmax = 0, critover = 0, hitmax = 0, absorbs = 0, } segmentsTable[active_segment] = spellTable if val3 then spellsSchool[val1] = val3 end end spellTable.amount = spellTable.amount + val4 spellTable.over = spellTable.over + val5 spellTable.absorbed = spellTable.absorbed + val6 spellTable.count = spellTable.count + 1 if val7 then spellTable.crit = spellTable.crit + val4 + val6 spellTable.critcount = spellTable.critcount + 1 if spellTable.critmax < val4 then spellTable.critmax = val4 end spellTable.critover = spellTable.critover + val5 elseif spellTable.hitmax < val4 then spellTable.hitmax = val4 end --------------> Add death local destData = deathLog[destGUID] if not destData then destData = {c=0} deathLog[destGUID] = destData end local tmpVar = destData.c + 1 if tmpVar > deathMaxEvents then tmpVar = 1 end destData.c = tmpVar local activeTable = destData[tmpVar] if not activeTable then activeTable = {} destData[tmpVar] = activeTable end activeTable.t = 2 activeTable.s = sourceGUID activeTable.ti = timestamp activeTable.sp = val1 activeTable.a = val4 activeTable.o = val5 activeTable.sc = val3 activeTable.b = nil activeTable.ab = val6 activeTable.c = val7 activeTable.ia = nil activeTable.sm = sourceFlags2 activeTable.dm = destFlags2 local raidunit = raidGUIDs[ destGUID ] if raidunit then activeTable.h = UnitHealth( raidunit ) activeTable.hm = UnitHealthMax( raidunit ) end --------------> Other --[[ if negateHealing[destGUID] then spellTable.amount = spellTable.amount - val4 spellTable.over = spellTable.over + val4 + val6 spellTable.absorbed = spellTable.absorbed - val6 if val7 then spellTable.crit = spellTable.crit - val4 - val6 spellTable.critcount = spellTable.critcount - 1 spellTable.critover = spellTable.critover - val5 end end ]] if val1 == 183998 then --Light of the Martyr: effective healing fix local lotmData = spellFix_LotM[sourceGUID] if not lotmData then lotmData = {} spellFix_LotM[sourceGUID] = lotmData end lotmData[1] = val4 - val5 lotmData[2] = val6 lotmData[3] = spellTable lotmData[4] = val7 lotmData[5] = timestamp elseif val1 == 186263 then --Shadow Mend: effective healing fix, remove damage from debuff spellFix_SM[destGUID] = spellTable elseif val1 == 213313 then --Paladin: Divine intervention AddNotRealDeath(destGUID,timestamp,val1) end end function module.main.SPELL_AURA_APPLIED(timestamp,event,hideCaster,sourceGUID,sourceName,sourceFlags,sourceFlags2,destGUID,destName,destFlags,destFlags2,spellID,spellName,school,auraType) if spellID == 465 then --HPal devo is broken and can spam 2000 events / sec return end --fightData_auras[ #fightData_auras + 1 ] = {timestamp,sourceGUID,destGUID,UnitIsFriendlyByUnitFlag(sourceFlags),UnitIsFriendlyByUnitFlag(destFlags),spellID,auraType,1,1,s = active_segment} fightData_auras[ #fightData_auras + 1 ] = {timestamp,sourceGUID,destGUID,bit_band(sourceFlags or 0,240) == 16,bit_band(destFlags or 0,240) == 16,spellID,auraType,1,1,s = active_segment} --------------> Add reduction local reduction = var_reductionAuras[spellID] if reduction then if spellID == 81782 then sourceGUID = module.db.reductionPowerWordBarrierCaster or sourceGUID elseif spellID == 194384 then reduction = reductionAtonement[sourceGUID] or 1 --[[ if ExRT.A.Inspect then local db = ExRT.A.Inspect.db.inspectDB for k,v in pairs(db) do if v.GUID == sourceGUID then if v[7] == 1 then reduction = 0.97 end break end end end ]] end local destData = var_reductionCurrent[ destGUID ] if not destData then destData = {} var_reductionCurrent[ destGUID ] = destData end local destCount = #destData local func,funcAura,reductionTable = nil if type(reduction)=="table" then reductionTable = reduction funcAura = reduction[3] func = reduction[2] reduction = reduction[1] end if funcAura then for i=1,60 do local auraData = C_UnitAuras.GetAuraDataByIndex(destName or "?",i) if not auraData then break elseif auraData.spellId == spellID then if auraData.points then reduction, func = funcAura(auraData.points[1] or 0,auraData.points[2] or 0,auraData.points[3] or 0,auraData.points[4] or 0,auraData.points[5] or 0) if not reduction then reduction = reductionTable[1] func = reductionTable[2] funcAura = nil end --ExRT.F.dprint(format("%s > %s: %s [%d%%]",sourceName,destName,spellName,(reduction or 0)*100)) else funcAura = nil end break end end end if reduction == 1 then return end for i=1,destCount do if destData[i].s == spellID then local destSpell = destData[i] destSpell.r = reduction local from,fromMagic,fromPhysical = 1,1,1 for j=1,i-1 do if destData[j].f == ReductionAurasFunctions.magic then fromMagic = fromMagic * destData[j].r elseif destData[j].f == ReductionAurasFunctions.physical then fromPhysical = fromPhysical * destData[j].r else from = from * destData[j].r end end for j=i,destCount do local currReduction if destData[j].f == ReductionAurasFunctions.magic then currReduction = 1 / (1 - (fromMagic - fromMagic * destData[j].r)) fromMagic = fromMagic * destData[j].r elseif destData[j].f == ReductionAurasFunctions.physical then currReduction = 1 / (1 - (fromPhysical - fromPhysical * destData[j].r)) fromPhysical = fromPhysical * destData[j].r else currReduction = 1 / (1 - (from - from * destData[j].r)) from = from * destData[j].r end destData[j].c = currReduction end return end end local from = 1 if func == ReductionAurasFunctions.magic then for i=1,destCount do if destData[i].f ~= ReductionAurasFunctions.physical then from = from * destData[i].r end end elseif func == ReductionAurasFunctions.physical then for i=1,destCount do if destData[i].f ~= ReductionAurasFunctions.magic then from = from * destData[i].r end end else for i=1,destCount do from = from * destData[i].r end end local currReduction = 1 / (1 - (from - from * reduction)) destData[destCount + 1] = { s = spellID, r = reduction, c = (currReduction - 1), g = sourceGUID, f = func, } --debug_CurrentReductionToChat(destData) if school then spellsSchool[spellID] = school end end if spellID == 45181 or spellID == 211319 or spellID == 87024 or spellID == 344907 or spellID == 116888 or spellID == 209261 then --Cheated Death, Archbishop Benedictus' Restitution, Cauterize, Sands of Time (Trinket), Shroud of Purgatory, Uncontained Fel AddNotRealDeath(destGUID,timestamp,spellID) --elseif spellID == 243961 then --Varimatras Disable Healing Debuff -- negateHealing[destGUID] = true end end function module.main.SPELL_AURA_REMOVED(timestamp,event,hideCaster,sourceGUID,sourceName,sourceFlags,sourceFlags2,destGUID,destName,destFlags,destFlags2,spellID,_,school,auraType,amount) if spellID == 465 then --HPal devo is broken and can spam 2000 events / sec return end --fightData_auras[ #fightData_auras + 1 ] = {timestamp,sourceGUID,destGUID,UnitIsFriendlyByUnitFlag(sourceFlags),UnitIsFriendlyByUnitFlag(destFlags),spellID,auraType,2,1,s = active_segment} fightData_auras[ #fightData_auras + 1 ] = {timestamp,sourceGUID,destGUID,bit_band(sourceFlags or 0,240) == 16,bit_band(destFlags or 0,240) == 16,spellID,auraType,2,1,s = active_segment} if amount and amount > 0 and spellID ~= 284663 --BfD: Conclave: Bwonsamdi then CLEU["SPELL_HEAL"](timestamp,"SPELL_HEAL",hideCaster,sourceGUID,sourceName,sourceFlags,sourceFlags2,destGUID,destName,destFlags,destFlags2,spellID,nil,school,amount,amount,0) end --------------> Add reduction local reduction = var_reductionAuras[spellID] if reduction then local destData = var_reductionCurrent[ destGUID ] if not destData then return end for i=1,#destData do if destData[i] and destData[i].s == spellID and (destData[i].g == sourceGUID or spellID == 81782) then tremove(destData,i) end end local from,fromPhysical,fromMagic = 1,1,1 for i=1,#destData do local spellData = destData[i] local currReduction = nil if spellData.f == ReductionAurasFunctions.magic then currReduction = 1 / (1 - (fromMagic - fromMagic * spellData.r)) fromMagic = fromMagic * spellData.r elseif spellData.f == ReductionAurasFunctions.physical then currReduction = 1 / (1 - (fromPhysical - fromPhysical * spellData.r)) fromPhysical = fromPhysical * spellData.r else currReduction = 1 / (1 - (from - from * spellData.r)) fromPhysical = fromPhysical * spellData.r fromMagic = fromMagic * spellData.r end from = from * spellData.r spellData.c = currReduction - 1 end --debug_CurrentReductionToChat(destData) end --------------> Other if spellID == 187464 then --Shadow Mend spellFix_SM[destGUID] = nil --elseif spellID == 243961 then --Varimatras Disable Healing Debuff -- negateHealing[destGUID] = nil end end function module.main.SPELL_CAST_SUCCESS(timestamp,event,hideCaster,sourceGUID,sourceName,sourceFlags,sourceFlags2,destGUID,destName,destFlags,destFlags2,spellID,spellName,spellSchool,empoweredRank) --------------> Add cast local sourceTable = fightData_cast[sourceGUID] if not sourceTable then sourceTable = {} fightData_cast[sourceGUID] = sourceTable end sourceTable[ #sourceTable + 1 ] = {timestamp,spellID,1,destGUID,destFlags2,sourceFlags2,sourceFlags,empoweredRank,s = active_segment} if spellSchool then spellsSchool[spellID] = spellSchool end --------------> Add switch local targetTable = fightData_switch[destGUID] if not targetTable then targetTable = { [1]={}, --cast [2]={}, --target seen=timestamp, } fightData_switch[destGUID] = targetTable end local targetCastTable = targetTable[1] if not targetCastTable[sourceGUID] then targetCastTable[sourceGUID] = {timestamp,spellID,2} end --------------> Other if spellID == 62618 then --PW:B caster fix module.db.reductionPowerWordBarrierCaster = sourceGUID elseif spellID == 98008 then --SLT Totem if SLTReductionUnregTimer then SLTReductionUnregTimer:Cancel() end SLTReductionUnregTimer = C_Timer.NewTimer(8,SLTReductionUnreg) SLTReductionReg(sourceGUID) elseif spellID == 209471 then --Illgynoth blossom death-cast return CLEU["UNIT_DIED"](timestamp,"UNIT_DIED",true,"",nil,0,0,sourceGUID,sourceName,sourceFlags,sourceFlags2) end end function module.main.SPELL_ENERGIZE(timestamp,event,hideCaster,sourceGUID,sourceName,sourceFlags,sourceFlags2,destGUID,destName,destFlags,destFlags2,spellID, _, _, amount, overEnergize, powerType, alternatePowerType) local sourceData = fightData_power[destGUID] if not sourceData then sourceData = {} fightData_power[destGUID] = sourceData end local powerData = sourceData[powerType or 0] if not powerData then powerData = {} sourceData[powerType or 0] = powerData end local spellData = powerData[spellID] if not spellData then spellData = {0,0} powerData[spellID] = spellData end spellData[1] = spellData[1] + amount spellData[2] = spellData[2] + 1 end function module.main.SWING_DAMAGE(timestamp,event,hideCaster,sourceGUID,sourceName,sourceFlags,sourceFlags2,destGUID,destName,destFlags,destFlags2,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10) return CLEU["SPELL_DAMAGE"](timestamp,"SPELL_DAMAGE",hideCaster,sourceGUID,sourceName,sourceFlags,sourceFlags2,destGUID,destName,destFlags,destFlags2,6603,nil,1,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10) end function module.main.SPELL_AURA_APPLIED_DOSE(timestamp,event,hideCaster,sourceGUID,sourceName,sourceFlags,sourceFlags2,destGUID,destName,destFlags,destFlags2,spellID,_,_,type,stack) --fightData_auras[ #fightData_auras + 1 ] = {timestamp,sourceGUID,destGUID,UnitIsFriendlyByUnitFlag(sourceFlags),UnitIsFriendlyByUnitFlag(destFlags),spellID,type,3,stack,s = active_segment} fightData_auras[ #fightData_auras + 1 ] = {timestamp,sourceGUID,destGUID,bit_band(sourceFlags or 0,240) == 16,bit_band(destFlags or 0,240) == 16,spellID,type,3,stack,s = active_segment} end function module.main.SPELL_CAST_START(timestamp,event,hideCaster,sourceGUID,sourceName,sourceFlags,sourceFlags2,destGUID,destName,destFlags,destFlags2,spellID,spellName,spellSchool) --------------> Add cast local sourceTable = fightData_cast[sourceGUID] if not sourceTable then sourceTable = {} fightData_cast[sourceGUID] = sourceTable end sourceTable[ #sourceTable + 1 ] = {timestamp,spellID,2,destGUID,destFlags2,sourceFlags2,sourceFlags,s = active_segment} if spellSchool then spellsSchool[spellID] = spellSchool end --------------> Add switch if sourceName and GetUnitInfoByUnitFlag(sourceFlags,1) == 1024 then local unitID = UnitInRaid(sourceName) if unitID then unitID = "raid"..unitID local targetGUID = UnitGUID(unitID.."target") if targetGUID and not UnitIsPlayerOrPet(targetGUID) then -- Switch code local targetTable = fightData_switch[targetGUID] if not targetTable then targetTable = { [1]={}, --cast [2]={}, --target seen=timestamp, } fightData_switch[targetGUID] = targetTable end local targetCastTable = targetTable[1] if not targetCastTable[sourceGUID] then targetCastTable[sourceGUID] = {timestamp,spellID,3} end -- / Switch code end end end end function module.main.SPELL_ABSORBED(timestamp,event,hideCaster,sourceGUID,sourceName,sourceFlags,sourceFlags2,destGUID,destName,destFlags,destFlags2,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,val11) --[[ SPELL_ABSORBED event info: for SWING timestamp,attackerGUID,attackerName,attackerFlags,attackerFlags2,destGUID,destName,destFlags,destFlags2,sourceGUID,sourceName,sourceFlags,sourceFlags2,spellID,spellName,school,amount OR for SPELL timestamp,attackerGUID,attackerName,attackerFlags,attackerFlags2,destGUID,destName,destFlags,destFlags2,attackerSpellId,attackerSpellName,attackerSchool,sourceGUID,sourceName,sourceFlags,sourceFlags2,spellID,spellName,school,amount ]] local attackerSpellId,attackerSpellName,attackerSchool,sourceGUID,sourceName,sourceFlags,sourceFlags2,spellID,spellName,school,amount if not val11 then sourceGUID,sourceName,sourceFlags,sourceFlags2,spellID,spellName,school,amount = val1,val2,val3,val4,val5,val6,val7,val8 attackerSpellId = 6603 else attackerSpellId,attackerSpellName,attackerSchool,sourceGUID,sourceName,sourceFlags,sourceFlags2,spellID,spellName,school,amount = val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,val11 end if spellID == 20711 or spellID == 115069 or spellID == 157533 then --Not real absorbs spells return end --------------> Add heal local sourceTable = fightData_heal[sourceGUID] if not sourceTable then sourceTable = {} fightData_heal[sourceGUID] = sourceTable end local destTable = sourceTable[destGUID] if not destTable then destTable = {} sourceTable[destGUID] = destTable end local destReactTable = destTable[destFlags] if not destReactTable then destReactTable = {} destTable[destFlags] = destReactTable end local segmentsTable = destReactTable[spellID] if not segmentsTable then segmentsTable = {} destReactTable[spellID] = segmentsTable end local spellTable = segmentsTable[active_segment] if not spellTable then spellTable = { amount = 0, over = 0, absorbed = 0, count = 0, crit = 0, critcount = 0, critmax = 0, critover = 0, hitmax = 0, absorbs = 0, } segmentsTable[active_segment] = spellTable spellsSchool[spellID] = school end spellTable.amount = spellTable.amount + amount spellTable.absorbs = spellTable.absorbs + amount spellTable.count = spellTable.count + 1 if spellTable.hitmax < amount then spellTable.hitmax = amount end --------------> Add death local destData = deathLog[destGUID] if not destData then destData = {c=0} deathLog[destGUID] = destData end local pos = destData.c + 1 if pos > deathMaxEvents then pos = 1 end local deathLine = destData[pos] if not deathLine then deathLine = {} destData[pos] = deathLine end deathLine.t = 2 deathLine.s = sourceGUID deathLine.ti = timestamp deathLine.sp = spellID deathLine.a = amount deathLine.o = 0 deathLine.sc = school deathLine.b = nil deathLine.ab = nil deathLine.c = nil deathLine.ia = amount deathLine.sm = sourceFlags2 deathLine.dm = destFlags2 local player = raidGUIDs[ destGUID ] if player then deathLine.h = UnitHealth( player ) deathLine.hm = UnitHealthMax( player ) end destData.c = pos end function module.main.SPELL_MISSED(timestamp,event,hideCaster,sourceGUID,sourceName,sourceFlags,sourceFlags2,destGUID,destName,destFlags,destFlags2,spellID,_,school,missType,isOffHand,amountMissed,critical) local newEvent = event == "SPELL_PERIODIC_MISSED" and "SPELL_PERIODIC_DAMAGE" or "SPELL_DAMAGE" if missType == "ABSORB" then return CLEU[newEvent](timestamp,newEvent,hideCaster,sourceGUID,sourceName,sourceFlags,sourceFlags2,destGUID,destName,destFlags,destFlags2,spellID,nil,nil,0,0,school,nil,nil,amountMissed,critical,nil,nil,isOffHand) elseif missType == "BLOCK" then return CLEU[newEvent](timestamp,newEvent,hideCaster,sourceGUID,sourceName,sourceFlags,sourceFlags2,destGUID,destName,destFlags,destFlags2,spellID,nil,nil,0,0,school,nil,amountMissed,nil,critical,nil,nil,isOffHand) elseif missType == "PARRY" then local spellTable = CLEU[newEvent](timestamp,newEvent,hideCaster,sourceGUID,sourceName,sourceFlags,sourceFlags2,destGUID,destName,destFlags,destFlags2,spellID,nil,nil,0,0,school,nil,nil,nil,critical,nil,nil,isOffHand,missType) spellTable.parry = spellTable.parry + 1 elseif missType == "DODGE" then local spellTable = CLEU[newEvent](timestamp,newEvent,hideCaster,sourceGUID,sourceName,sourceFlags,sourceFlags2,destGUID,destName,destFlags,destFlags2,spellID,nil,nil,0,0,school,nil,nil,nil,critical,nil,nil,isOffHand,missType) spellTable.dodge = spellTable.dodge + 1 else local spellTable = CLEU[newEvent](timestamp,newEvent,hideCaster,sourceGUID,sourceName,sourceFlags,sourceFlags2,destGUID,destName,destFlags,destFlags2,spellID,nil,nil,0,0,school,nil,nil,nil,critical,nil,nil,isOffHand,missType) spellTable.miss = spellTable.miss + 1 end end function module.main.SWING_MISSED(timestamp,event,hideCaster,sourceGUID,sourceName,sourceFlags,sourceFlags2,destGUID,destName,destFlags,destFlags2,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,val11,val12,val13,val14) return CLEU["SPELL_MISSED"](timestamp,"SPELL_MISSED",hideCaster,sourceGUID,sourceName,sourceFlags,sourceFlags2,destGUID,destName,destFlags,destFlags2,6603,nil,0x1,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,val11,val12,val13,val14) end function module.main.RANGE_DAMAGE(timestamp,event,hideCaster,sourceGUID,sourceName,sourceFlags,sourceFlags2,destGUID,destName,destFlags,destFlags2,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,val11,val12,val13,val14) return CLEU["SPELL_DAMAGE"](timestamp,"SPELL_DAMAGE",hideCaster,sourceGUID,sourceName,sourceFlags,sourceFlags2,destGUID,destName,destFlags,destFlags2,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,val11,val12,val13,val14) end function module.main.SPELL_AURA_REMOVED_DOSE(timestamp,event,hideCaster,sourceGUID,sourceName,sourceFlags,sourceFlags2,destGUID,destName,destFlags,destFlags2,spellID,_,_,type,stack) fightData_auras[ #fightData_auras + 1 ] = {timestamp,sourceGUID,destGUID,bit_band(sourceFlags or 0,240) == 16,bit_band(destFlags or 0,240) == 16,spellID,type,4,stack,s = active_segment} end function module.main.UNIT_DIED(timestamp,event,hideCaster,sourceGUID,sourceName,sourceFlags,sourceFlags2,destGUID,destName,destFlags,destFlags2) fightData.dies[#fightData.dies+1] = {destGUID,destFlags,timestamp,destFlags2,s = active_segment} --if DeathLogBlackList[GUIDtoID(destGUID)] then return end --[[ Death line type: 1: damage 2: heal 3: death 4: insta kill 5: death simulation (fd, sor proc) ]] local destData = deathLog[destGUID] if not destData then destData = {c=0} deathLog[destGUID] = destData end if destData.sch then destData.sch:Cancel() destData.schFunc() destData.sch = nil destData.schFunc = nil end fightData_deathLog[#fightData_deathLog + 1] = destData deathLog[destGUID] = nil destData.h = {3,destGUID,timestamp,active_segment} return destData end function module.main.SPELL_SUMMON(timestamp,event,hideCaster,sourceGUID,sourceName,sourceFlags,sourceFlags2,destGUID,destName,destFlags,destFlags2,spellID) fightData.summons[#fightData.summons+1]={sourceGUID,destGUID,spellID,timestamp,sourceFlags2,destFlags2,s = active_segment} end function module.main.SPELL_DISPEL(timestamp,event,hideCaster,sourceGUID,sourceName,sourceFlags,sourceFlags2,destGUID,destName,destFlags,destFlags2,spellID,_,_,destSpell) fightData.dispels[#fightData.dispels+1]={sourceGUID,destGUID,spellID,destSpell,timestamp,sourceFlags2,destFlags2,s = active_segment} end function module.main.SPELL_AURA_BROKEN_SPELL(timestamp,event,hideCaster,sourceGUID,sourceName,sourceFlags,sourceFlags2,destGUID,destName,destFlags,destFlags2,spellID,_,_,extraSpellId,_,_,auraType) if not auraType then auraType = extraSpellId --SPELL_AURA_BROKEN instead SPELL_AURA_BROKEN_SPELL extraSpellId = 6603 end fightData.aurabroken[#fightData.aurabroken+1]={sourceGUID,destGUID,spellID,extraSpellId,timestamp,auraType,sourceFlags2,destFlags2,s = active_segment} end function module.main.ENVIRONMENTAL_DAMAGE(timestamp,event,hideCaster,sourceGUID,sourceName,sourceFlags,sourceFlags2,destGUID,destName,destFlags,destFlags2,environmentalType,amount,overkill,school,resisted,blocked,absorbed,critical,glancing,crushing,isOffHand) local environmentalSpellID = environmentalType and EnvironmentalTypeToSpellID[environmentalType] or 48360 return CLEU["SPELL_DAMAGE"](timestamp,"SPELL_DAMAGE",hideCaster,sourceGUID,sourceName,sourceFlags,sourceFlags2,destGUID,destName,destFlags,destFlags2,environmentalSpellID,nil,nil,amount,overkill,school,resisted,blocked,absorbed,critical,glancing,crushing,isOffHand) end function module.main.SPELL_INTERRUPT(timestamp,event,hideCaster,sourceGUID,sourceName,sourceFlags,sourceFlags2,destGUID,destName,destFlags,destFlags2,spellID,_,_,destSpell) fightData.interrupts[#fightData.interrupts+1]={sourceGUID,destGUID,spellID,destSpell,timestamp,sourceFlags2,destFlags2,s = active_segment} end function module.main.DAMAGE_SPLIT(timestamp,event,hideCaster,sourceGUID,sourceName,sourceFlags,sourceFlags2,destGUID,destName,destFlags,destFlags2,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,val11,val12,val13,val14) return CLEU["SPELL_DAMAGE"](timestamp,"SPELL_DAMAGE",hideCaster,sourceGUID,sourceName,sourceFlags,sourceFlags2,destGUID,destName,destFlags,destFlags2,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,val11,val12,val13,val14) end function module.main.SPELL_RESURRECT(timestamp,event,hideCaster,sourceGUID,sourceName,sourceFlags,sourceFlags2,destGUID,destName,destFlags,destFlags2,spellID) fightData.resurrests[#fightData.resurrests+1]={sourceGUID,destGUID,spellID,timestamp,s = active_segment} end function module.main.SPELL_DRAIN(timestamp,event,hideCaster,sourceGUID,sourceName,sourceFlags,sourceFlags2,destGUID,destName,destFlags,destFlags2,spellID,_,_,amount,powerType) return CLEU["SPELL_ENERGIZE"](timestamp,"SPELL_ENERGIZE",hideCaster,sourceGUID,sourceName,sourceFlags,sourceFlags2,destGUID,destName,destFlags,destFlags2,spellID,nil,nil,-amount,powerType) end function module.main.SPELL_LEECH(timestamp,event,hideCaster,sourceGUID,sourceName,sourceFlags,sourceFlags2,destGUID,destName,destFlags,destFlags2,spellID,_,_,amount,powerType,extraAmount) if extraAmount then CLEU["SPELL_ENERGIZE"](timestamp,"SPELL_ENERGIZE",hideCaster,sourceGUID,sourceName,sourceFlags,sourceFlags2,destGUID,destName,destFlags,destFlags2,spellID,nil,nil,extraAmount,powerType) end return CLEU["SPELL_ENERGIZE"](timestamp,"SPELL_ENERGIZE",hideCaster,sourceGUID,sourceName,sourceFlags,sourceFlags2,destGUID,destName,destFlags,destFlags2,spellID,nil,nil,-amount,powerType) end function module.main.SPELL_INSTAKILL(timestamp,event,hideCaster,sourceGUID,sourceName,sourceFlags,sourceFlags2,destGUID,destName,destFlags,destFlags2,spellID,_,school) local destData = deathLog[destGUID] --destData if not destData then destData = {c=0} deathLog[destGUID] = destData end local tmpVar = destData.c + 1 if tmpVar > deathMaxEvents then tmpVar = 1 end destData.c = tmpVar local destLine = destData[tmpVar] if not destLine then destLine = {} destData[tmpVar] = destLine end destLine.t = 4 destLine.s = sourceGUID destLine.ti = timestamp destLine.sp = spellID destLine.a = nil destLine.o = nil destLine.sc = nil destLine.b = nil destLine.ab = nil destLine.c = nil destLine.ia = nil destLine.sm = sourceFlags2 destLine.dm = destFlags2 end CLEU = { SPELL_DAMAGE = module.main.SPELL_DAMAGE, SPELL_PERIODIC_DAMAGE = module.main.SPELL_DAMAGE, SPELL_HEAL = module.main.SPELL_HEAL, SPELL_PERIODIC_HEAL = module.main.SPELL_HEAL, SPELL_AURA_APPLIED = module.main.SPELL_AURA_APPLIED, SPELL_AURA_REMOVED = module.main.SPELL_AURA_REMOVED, SPELL_CAST_SUCCESS = module.main.SPELL_CAST_SUCCESS, SPELL_EMPOWER_END = module.main.SPELL_CAST_SUCCESS, SPELL_ENERGIZE = module.main.SPELL_ENERGIZE, SPELL_PERIODIC_ENERGIZE = module.main.SPELL_ENERGIZE, SWING_DAMAGE = module.main.SWING_DAMAGE, SPELL_AURA_APPLIED_DOSE = module.main.SPELL_AURA_APPLIED_DOSE, SPELL_CAST_START = module.main.SPELL_CAST_START, SPELL_EMPOWER_START = module.main.SPELL_CAST_START, SPELL_ABSORBED = module.main.SPELL_ABSORBED, SPELL_MISSED = module.main.SPELL_MISSED, RANGE_MISSED = module.main.SPELL_MISSED, SPELL_PERIODIC_MISSED = module.main.SPELL_MISSED, SWING_MISSED = module.main.SWING_MISSED, RANGE_DAMAGE = module.main.RANGE_DAMAGE, SPELL_AURA_REMOVED_DOSE = module.main.SPELL_AURA_REMOVED_DOSE, UNIT_DIED = module.main.UNIT_DIED, UNIT_DESTROYED = module.main.UNIT_DIED, SPELL_SUMMON = module.main.SPELL_SUMMON, SPELL_CREATE = module.main.SPELL_SUMMON, SPELL_DISPEL = module.main.SPELL_DISPEL, SPELL_STOLEN = module.main.SPELL_DISPEL, SPELL_AURA_BROKEN_SPELL = module.main.SPELL_AURA_BROKEN_SPELL, SPELL_AURA_BROKEN = module.main.SPELL_AURA_BROKEN_SPELL, ENVIRONMENTAL_DAMAGE = module.main.ENVIRONMENTAL_DAMAGE, SPELL_INTERRUPT = module.main.SPELL_INTERRUPT, DAMAGE_SPLIT = module.main.DAMAGE_SPLIT, SPELL_RESURRECT = module.main.SPELL_RESURRECT, SPELL_DRAIN = module.main.SPELL_DRAIN, SPELL_PERIODIC_DRAIN = module.main.SPELL_DRAIN, SPELL_LEECH = module.main.SPELL_LEECH, SPELL_PERIODIC_LEECH = module.main.SPELL_LEECH, SPELL_INSTAKILL = module.main.SPELL_INSTAKILL, } CLEUParser = function(timestamp,event,hideCaster,sourceGUID,sourceName,sourceFlags,sourceFlags2,destGUID,destName,destFlags,destFlags2,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,val11,val12,val13) if not guidData[sourceGUID] then guidData[sourceGUID] = sourceName or "nil" end if not guidData[destGUID] then guidData[destGUID] = destName or "nil" end reactionData[sourceGUID] = sourceFlags reactionData[destGUID] = destFlags local func = CLEU[event] if func then return func(timestamp,event,hideCaster,sourceGUID,sourceName,sourceFlags,sourceFlags2,destGUID,destName,destFlags,destFlags2,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,val11,val12,val13) else return end -- DAMAGE_SHIELD -- DAMAGE_SHIELD_MISSED -- UNIT_DISSIPATES -- SPELL_BUILDING_DAMAGE -- SPELL_BUILDING_HEAL end if ExRT.isClassic and not ExRT.isCata then CLEUParser = function(timestamp,event,hideCaster,sourceGUID,sourceName,sourceFlags,sourceFlags2,destGUID,destName,destFlags,destFlags2,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,val11,val12,val13) if event ~= "SWING_DAMAGE" and event ~= "SWING_MISSED" then val1 = val2 if event == "SPELL_PERIODIC_DAMAGE" then event = "SPELL_DAMAGE" elseif event == "SPELL_PERIODIC_HEAL" then event = "SPELL_HEAL" elseif event == "SPELL_PERIODIC_MISSED" then event = "SPELL_MISSED" end end if not guidData[sourceGUID] then guidData[sourceGUID] = sourceName or "nil" end if not guidData[destGUID] then guidData[destGUID] = destName or "nil" end reactionData[sourceGUID] = sourceFlags reactionData[destGUID] = destFlags local func = CLEU[event] if func then return func(timestamp,event,hideCaster,sourceGUID,sourceName,sourceFlags,sourceFlags2,destGUID,destName,destFlags,destFlags2,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,val11,val12,val13) else return end end end module.main.COMBAT_LOG_EVENT_UNFILTERED2 = CLEUParser function module.main.COMBAT_LOG_EVENT_UNFILTERED(timestamp,...) if not module.db.timeFix then module.db.timeFix = {GetTime(),timestamp} end module.main.COMBAT_LOG_EVENT_UNFILTERED = CLEUParser module.main.COMBAT_LOG_EVENT_UNFILTERED2 = nil CLEUParser(timestamp,...) module:RegisterEvents('COMBAT_LOG_EVENT_UNFILTERED') end function module.main:RAID_BOSS_EMOTE(msg,sender) local spellID = msg:match("spell:(%d+)") if spellID then fightData.chat[ #fightData.chat + 1 ] = {sender,msg,spellID,GetTime(),s = active_segment} end end module.main.RAID_BOSS_WHISPER = module.main.RAID_BOSS_EMOTE function module.main:UNIT_TARGET(unitID) local targetGUID = UnitGUID(unitID.."target") if targetGUID and not UnitIsPlayerOrPet(targetGUID) then local sourceGUID = UnitGUID(unitID) if GetUnitTypeByGUID(sourceGUID) == 0 then local targetTable = fightData_switch[targetGUID] if not targetTable then targetTable = { [1]={}, --cast [2]={}, --target seen=module.db.timeFix and (GetTime() - module.db.timeFix[1] + module.db.timeFix[2]) or GetTime(), } fightData_switch[targetGUID] = targetTable end local targetCastTable = targetTable[2] if not targetCastTable[sourceGUID] then targetCastTable[sourceGUID] = {GetTime()} end end if not fightData_maxHP[targetGUID] then fightData_maxHP[targetGUID] = UnitHealthMax(unitID.."target") end end end function module.main:UPDATE_MOUSEOVER_UNIT() local sourceGUID = UnitGUID("mouseover") if sourceGUID and not fightData_maxHP[sourceGUID] then fightData_maxHP[sourceGUID] = UnitHealthMax("mouseover") if not guidData[sourceGUID] then guidData[sourceGUID] = UnitName("mouseover") or "nil" end end end local function GlobalRecordStart() if not VMRT.BossWatcher.enabled then return end module:UnregisterEvents('PLAYER_REGEN_DISABLED','PLAYER_REGEN_ENABLED','ENCOUNTER_START','ENCOUNTER_END') if fightData then _BW_End() end _BW_Start() print(L.BossWatcherRecordStart) end local function GlobalRecordEnd() if not VMRT.BossWatcher.enabled then return end _BW_End() module:RegisterEvents('PLAYER_REGEN_DISABLED','PLAYER_REGEN_ENABLED','ENCOUNTER_START','ENCOUNTER_END') print(L.BossWatcherRecordStop) end function module:slash(arg) if arg == "bw s" or arg == "bw start" or arg == "fl s" or arg == "fl start" then GlobalRecordStart() print( ExRT.F.CreateChatLink("BWGlobalRecordEnd",GlobalRecordEnd,L.BossWatcherStopRecord), L.BossWatcherStopRecord2 ) elseif arg == "bw e" or arg == "bw end" or arg == "fl e" or arg == "fl end" then GlobalRecordEnd() elseif arg == "bw" or arg == "fl" then BWInterfaceFrameLoadFunc() elseif arg == "bw clear" or arg == "fl clear" or arg == "bw c" or arg == "fl c" then module:ClearData() print('Cleared') elseif arg == "bw reset" or arg == "fl reset" then VMRT.BossWatcher.SAVED_DATA = nil print('Cleared') elseif arg == "bw save" or arg == "fl save" then VMRT.BossWatcher.saveVariables = true VMRT.BossWatcher.SAVED_DATA = module.db.data print('Saved') elseif arg == "bw allenergy" or arg == "fl allenergy" then module.db.allEnergyMode = not module.db.allEnergyMode print('All Energy Mode',module.db.allEnergyMode) elseif arg == "bw phase" or arg == "fl phase" then if active_phase then active_phase = active_phase + 1 print('Jump to phase',active_phase) end elseif arg:find("^bw ") or arg:find("^fl ") then ExRT.F:FightLog_Open() elseif arg == "help" then print("|cff00ff00/rt fl|r - open fight log tab") print("|cff00ff00/rt fl s|r - manual start logging fight for fight log") print("|cff00ff00/rt fl e|r - manual stop logging fight for fight log") end end function module:ClearData(isFirstLoad) module.db.lastFightID = module.db.lastFightID + 1 module.db.nowNum = 1 module.db.data = { { guids = {}, raidguids = {}, reaction = {}, pets = {}, encounterStartGlobal = time(), encounterStart = GetTime(), encounterEnd = GetTime()+1, isEnded = true, graphData = {}, positionsData = {}, segments = { [1] = { e=true, t=GetTime(), }, }, damage = {}, damage_seen = {}, heal = {}, healFrom = {}, switch = {}, cast = {}, interrupts = {}, dispels = {}, auras = {}, power = {}, dies = {}, chat = {}, resurrests = {}, summons = {}, aurabroken = {}, deathLog = {}, maxHP = {}, reduction = {}, tracking = {}, fightID = module.db.lastFightID, other = { roles = {}, rolesGUID = {}, }, }, } if isFirstLoad then return end ExRT.F.ScheduleTimer(collectgarbage, 1, "collect") if BWInterfaceFrame and BWInterfaceFrame:IsShown() then BWInterfaceFrame:Hide() BWInterfaceFrame:Show() end end function BWInterfaceFrameLoad() --if InCombatLockdown() then -- print(L.SetErrorInCombat) -- return --end isBWInterfaceFrameLoaded = true if not module.db.data[1] then module:ClearData(true) end -- Some upvaules local ipairs,pairs,tonumber,tostring,format,date,min,sort,table = ipairs,pairs,tonumber,tostring,format,date,min,sort,table local GetSpellInfo = ExRT.F.GetSpellInfo or GetSpellInfo if ExRT.isClassic and not ExRT.isCata then local _GetSpellInfo = GetSpellInfo function GetSpellInfo(spellID) if type(spellID) == 'string' then local sN = _GetSpellInfo(spellID) if sN then return _GetSpellInfo(spellID) else return spellID,false,"Interface\\Icons\\INV_MISC_QUESTIONMARK" end else return _GetSpellInfo(spellID) end end end local CurrentFight = nil local BWInterfaceFrame_Name = 'GMRTBWInterfaceFrame' BWInterfaceFrame = ELib:Template("ExRTBWInterfaceFrame",UIParent) _G[BWInterfaceFrame_Name] = BWInterfaceFrame BWInterfaceFrame:SetPoint("CENTER",0,0) BWInterfaceFrame.HeaderText:SetText(L.BossWatcher) BWInterfaceFrame.backToInterface:SetText("<<") BWInterfaceFrame:SetMovable(true) BWInterfaceFrame:RegisterForDrag("LeftButton") BWInterfaceFrame:SetScript("OnDragStart", function(self) self:StartMoving() end) BWInterfaceFrame:SetScript("OnDragStop", function(self) self:StopMovingOrSizing() end) BWInterfaceFrame:SetDontSavePosition(true) ELib:ShadowInside(BWInterfaceFrame) module.options.SecondFrame = BWInterfaceFrame BWInterfaceFrame.border = ELib:Shadow(BWInterfaceFrame,20) BWInterfaceFrame.DecorationLine = ELib:Frame(BWInterfaceFrame):Point("TOPLEFT",BWInterfaceFrame,0,-40):Point("BOTTOMRIGHT",BWInterfaceFrame,"TOPRIGHT",0,-60):Texture(1,1,1,1):TexturePoint('x') BWInterfaceFrame.DecorationLine.texture:SetGradient("VERTICAL",CreateColor(.24,.25,.30,1), CreateColor(.27,.28,.33,1)) BWInterfaceFrame.backToInterface.tooltipText = L.BossWatcherBackToInterface BWInterfaceFrame.buttonClose.tooltipText = L.BossWatcherButtonClose BWInterfaceFrame:Hide() BWInterfaceFrame.bossButton:SetText(L.BossWatcherLastFight) BWInterfaceFrame.bossButton:SetWidth(BWInterfaceFrame.bossButton:GetTextWidth()+30) BWInterfaceFrame.phaseButton = ELib:Template("ExRTButtonTransparentTemplate",BWInterfaceFrame) BWInterfaceFrame.phaseButton:SetHeight(18) BWInterfaceFrame.phaseButton:SetPoint("LEFT",BWInterfaceFrame.bossButton,"RIGHT",10,0) BWInterfaceFrame.phaseButton:SetText(L.BossWatcherAllPhases.." |TInterface\\AddOns\\"..GlobalAddonName.."\\media\\DiesalGUIcons16x256x128:16:16:0:0:256:128:64:80:64:80|t") BWInterfaceFrame.phaseButton:SetWidth(BWInterfaceFrame.phaseButton:GetTextWidth()+30) local reportData = {{},{},{},{ {},{},{} },{},{},{},{},{}} local reportOptions = {} BWInterfaceFrame.report = ELib:Button(BWInterfaceFrame,L.BossWatcherCreateReport):Size(150,18):Point("TOPRIGHT",BWInterfaceFrame,"TOPRIGHT",-4,-18):Tooltip(L.BossWatcherCreateReportTooltip):OnClick(function () local activeTab = BWInterfaceFrame.tab.selected ---Tab with mobs fix if activeTab == 4 then local activeTabOnPage = BWInterfaceFrame.tab.tabs[4].infoTabs.selected for i=1,#reportData[4][activeTabOnPage] do reportData[4][activeTabOnPage][i] = reportData[4][activeTabOnPage][i]:gsub("||","") end ExRT.F:ToChatWindow(reportData[4][activeTabOnPage]) return end for i=1,#reportData[activeTab] do reportData[activeTab][i] = reportData[activeTab][i]:gsub("||","") end ExRT.F:ToChatWindow(reportData[activeTab],nil,reportOptions[activeTab]) end) BWInterfaceFrame.report:Hide() ---- Helpful functions local function GetGUID(GUID) if GUID and CurrentFight.guids[GUID] and CurrentFight.guids[GUID] ~= "nil" then return CurrentFight.guids[GUID] else return L.BossWatcherUnknown end end local function GetPetsDB() return CurrentFight.pets end local function CloseDropDownMenus_fix() CloseDropDownMenus() end local function timestampToFightTime(time) if not time then return 0 end local fixTable = CurrentFight.timeFix if not fixTable and module.db.timeFix then fixTable = module.db.timeFix elseif not fixTable and not module.db.timeFix then return 0 end local res = time - (fixTable[2] - fixTable[1] + CurrentFight.encounterStart) return max(res,0) end local function GUIDtoText(patt,GUID) if GUID and GUID ~= "" then patt = patt or "%s" local _type = ExRT.F.GetUnitTypeByGUID(GUID) if _type == 0 then return "" elseif _type == 3 or _type == 5 then local mobSpawnID,mobSpawnID1,mobSpawnID2,mobSpawnID3 = nil local spawnID = GUID:match("%-([^%-]+)$") if spawnID then mobSpawnID = tonumber(spawnID, 16) mobSpawnID1 = tonumber(spawnID:sub(1,5), 16) or 0 mobSpawnID2 = tonumber(spawnID:sub(6,8), 16) or 0 mobSpawnID3 = tonumber(spawnID:sub(9), 16) or 0 local unitType,_,serverID,instanceID,zoneUID,id,spawnID = strsplit("-", GUID or "") if id and unitType == "Creature" or unitType == "Vehicle" then --local spawnEpoch = GetServerTime() - (GetServerTime() % 2^23) local spawnEpochOffset = bit.band(tonumber(string.sub(spawnID, 5), 16), 0x7fffff) local spawnIndex = bit.rshift(bit.band(tonumber(string.sub(spawnID, 1, 5), 16), 0xffff8), 3) --local spawnTime = spawnEpoch + spawnEpochOffset --if spawnTime > GetServerTime() then -- spawnTime = spawnTime - ((2^23) - 1) --end --mobSpawnID2 = format("%X",spawnEpochOffset) mobSpawnID2 = spawnEpochOffset mobSpawnID3 = spawnIndex end end if mobSpawnID then --return format(patt,tostring(mobSpawnID)..", "..mobSpawnID2.."-"..mobSpawnID1) --return format(patt,mobSpawnID2.."-"..mobSpawnID3.."-"..mobSpawnID1) return format(patt,mobSpawnID2..":"..mobSpawnID3) else return format(patt,GUID) end else return format(patt,GUID) end else return "" end end local function SetSchoolColorsToLine(self,school) local isNotGradient = ExRT.F.table_find(module.db.schoolsDefault,school) or school == 0 or module.db.schoolsColors[school or -1] local isConfirmedGradient = module.db.schoolsColorsGradient[ school or -1 ] if isNotGradient and module.db.schoolsColors[school] then self:SetVertexColor(module.db.schoolsColors[school].r,module.db.schoolsColors[school].g,module.db.schoolsColors[school].b, 1) elseif isConfirmedGradient then local school1,school2 = isConfirmedGradient[1],isConfirmedGradient[2] self:SetVertexColor(1,1,1,1) self:SetGradient("HORIZONTAL",CreateColor(module.db.schoolsColors[school1].r,module.db.schoolsColors[school1].g,module.db.schoolsColors[school1].b,1), CreateColor(module.db.schoolsColors[school2].r,module.db.schoolsColors[school2].g,module.db.schoolsColors[school2].b,1)) else local school1,school2 = nil for i=1,#module.db.schoolsDefault do local isSchool = bit.band(school,module.db.schoolsDefault[i]) > 0 if isSchool and not school1 then school1 = module.db.schoolsDefault[i] elseif isSchool and not school2 then school2 = module.db.schoolsDefault[i] end end if school1 and school2 then self:SetVertexColor(1,1,1,1) self:SetGradient("HORIZONTAL",CreateColor(module.db.schoolsColors[school1].r,module.db.schoolsColors[school1].g,module.db.schoolsColors[school1].b,1), CreateColor(module.db.schoolsColors[school2].r,module.db.schoolsColors[school2].g,module.db.schoolsColors[school2].b,1)) elseif school1 and not school2 then self:SetVertexColor(module.db.schoolsColors[school1].r,module.db.schoolsColors[school1].g,module.db.schoolsColors[school1].b, 1) else self:SetVertexColor(0.8,0.8,0.8, 1) end end end local function GetSchoolName(school) if not school or module.db.schoolsNames[school] then return module.db.schoolsNames[school or 0] else local school1,school2 = nil for i=1,#module.db.schoolsDefault do local isSchool = bit.band(school,module.db.schoolsDefault[i]) > 0 if isSchool and not school1 then school1 = module.db.schoolsDefault[i] elseif isSchool and not school2 then school2 = module.db.schoolsDefault[i] end end if school1 and school2 then return module.db.schoolsNames[school1] .. "-" .. module.db.schoolsNames[school2] elseif school1 and not school2 then return module.db.schoolsNames[school1] else return module.db.schoolsNames[0] end end end local function GetUnitInfoByUnitFlagFix(unitFlag,infoType) if not unitFlag then return end return GetUnitInfoByUnitFlag(unitFlag,infoType) end local function GetFightLength(full) if full then if not CurrentFight.isEnded then return GetTime() - CurrentFight.encounterStart end return (CurrentFight.encounterEnd - CurrentFight.encounterStart) end local length = 0 for i=1,#CurrentFight.segments do if CurrentFight.segments[i].e then length = length - CurrentFight.segments[i].t + (CurrentFight.segments[i+1] and CurrentFight.segments[i+1].t or (CurrentFight.isEnded and CurrentFight.encounterEnd or GetTime()) ) end end if length == 0 then length = 0.01 end return length end local function SubUTF8String(str,len) local strlen = ExRT.F.utf8len(str) if strlen > len then str = ExRT.F.utf8sub(str,1,len) .. "..." end return str end local function GetFightID(data,short) local r = tostring(data.encounterStart) if short then r = r .. "-" .. GetFightLength(true) else for i=1,#data.segments do if data.segments[i].e then r = r .. "," .. i end end end return r end local GetTargetIconText = ExRT.F.GetRaidTargetText ---- Locals for functions in code below local SpellsPage_GetCastsNumber,AurasPage_IsAuraOn,Graph_AutoUpdateStep = nil ---- Bugfix functions local _GetSpellLink = C_Spell and C_Spell.GetSpellLink or GetSpellLink local function GetSpellLink(spellID) local link = _GetSpellLink(spellID) if link then return link end local spellName = GetSpellInfo(spellID) return spellName or "Unk" end ---- Update functions local function UpdateSegments(start,ending,isAdd,disableReload) if isAdd and start and ending then for i=1,#CurrentFight.segments do if (i>=start and i<=ending) then CurrentFight.segments[i].e = true end end else for i=1,#CurrentFight.segments do CurrentFight.segments[i].e = not start or not ending or (i>=start and i<=ending) end end if not disableReload then BWInterfaceFrame:Hide() BWInterfaceFrame:Show() end end local function UpdateSegments_SelectPhase(phase) local minX,maxX = #CurrentFight.segments,1 for i=1,#CurrentFight.segments do CurrentFight.segments[i].e = CurrentFight.segments[i].p == phase if CurrentFight.segments[i].p == phase then if i < minX then minX = i end if i > maxX then maxX = i end end end if minX ~= #CurrentFight.segments or maxX ~= 1 then BWInterfaceFrame.GraphFrame.G.ZoomMinX = minX - 1 BWInterfaceFrame.GraphFrame.G.ZoomMaxX = maxX + 1 end BWInterfaceFrame:Hide() BWInterfaceFrame:Show() end local function UpdateSegmentsTime(start,ending,isAdd,disableReload) local s,e = nil,nil local totalSegments = #CurrentFight.segments for j=1,totalSegments - 1 do if CurrentFight.segments[j].t <= start and CurrentFight.segments[j+1].t > start and not s then s = j e = j elseif CurrentFight.segments[j].t <= ending and CurrentFight.segments[j+1].t > ending and s then e = j break end end if ending > CurrentFight.segments[totalSegments].t then e = totalSegments end if s and e then UpdateSegments(s,e,isAdd,disableReload) end end BWInterfaceFrame:SetScript("OnShow",function (self) local fightData = module.db.data[module.db.nowNum] local fightID = GetFightID(fightData) if self.nowFightID ~= fightID then CurrentFight = fightData local isInRecording = not fightData.isEnded if isInRecording then fightData.encounterEnd = GetTime() --print(L.BossWatcherCombatError) --return end self.nowFightID = fightID fightData.fightID = fightID local nowFightIDShort = GetFightID(CurrentFight,true) if nowFightIDShort ~= self.nowFightIDShort then BWInterfaceFrame.phaseButton:SetText(L.BossWatcherAllPhases.." |TInterface\\AddOns\\"..GlobalAddonName.."\\media\\DiesalGUIcons16x256x128:16:16:0:0:256:128:64:80:64:80|t") BWInterfaceFrame.phaseButton:SetWidth(BWInterfaceFrame.phaseButton:GetTextWidth()+30) end self.nowFightIDShort = nowFightIDShort local _time = ((isInRecording and GetTime() or fightData.encounterEnd) - fightData.encounterStart) self.bossButton:SetText( (fightData.encounterName or L.BossWatcherLastFight)..date(": %H:%M - ", fightData.encounterStartGlobal )..date("%H:%M", fightData.encounterStartGlobal + _time )..format(" (%dm%02ds)",floor(_time/60),_time%60 )..(isInRecording and " *" or "").." |TInterface\\AddOns\\"..GlobalAddonName.."\\media\\DiesalGUIcons16x256x128:16:16:0:0:256:128:64:80:64:80|t" ) self.bossButton:SetWidth(self.bossButton:GetTextWidth()+30) for i=1,#reportData do if i ~= 4 then wipe(reportData[i]) else wipe(reportData[4][1]) wipe(reportData[4][2]) wipe(reportData[4][3]) end end if CurrentFight.segments.phaseNames then BWInterfaceFrame.phaseButton:Show() else BWInterfaceFrame.phaseButton:Hide() end end BWInterfaceFrame.tab:Show() end) BWInterfaceFrame:SetScript("OnHide",function (self) BWInterfaceFrame.tab:Hide() end) BWInterfaceFrame.bossButtonDropDown = CreateFrame("Frame", BWInterfaceFrame_Name.."BossButtonDropDown", nil, "UIDropDownMenuTemplate") BWInterfaceFrame.bossButton:SetScript("OnClick",function (self) local fightsList = { { text = L.BossWatcherSelectFight, isTitle = true, notCheckable = true, notClickable = true }, } for i=1,#module.db.data do local colorCode = "" if i == module.db.nowNum then colorCode = "|cff00ff00" end local fightData = module.db.data[i] local _time = (fightData.isEnded and fightData.encounterEnd or GetTime()) - fightData.encounterStart fightsList[#fightsList + 1] = { text = i..". "..colorCode..(fightData.encounterName or L.BossWatcherLastFight)..date(": %H:%M - ", fightData.encounterStartGlobal )..date("%H:%M", fightData.encounterStartGlobal + _time )..format(" (%dm%02ds)",floor(_time/60),_time%60 )..(fightData.isEnded and "" or " *"), notCheckable = true, func = function() module.db.nowNum = i self:SetText( (fightData.encounterName or L.BossWatcherLastFight)..date(": %H:%M - ", fightData.encounterStartGlobal )..date("%H:%M", fightData.encounterStartGlobal + _time )..format(" (%dm%02ds)",floor(_time/60),_time%60 )..(fightData.isEnded and "" or " *").." |TInterface\\AddOns\\"..GlobalAddonName.."\\media\\DiesalGUIcons16x256x128:16:16:0:0:256:128:64:80:64:80|t" ) self:SetWidth(self:GetTextWidth()+30) if not fightData.isEnded then BWInterfaceFrame.nowFightID = -1 end BWInterfaceFrame:Hide() BWInterfaceFrame:Show() end, } end fightsList[#fightsList + 1] = { text = L.cd2HistoryClear, notCheckable = true, func = function() StaticPopupDialogs["EXRT_FIGHTLOG_CLEAR"] = { text = L.cd2HistoryClear, button1 = L.YesText, button2 = L.NoText, OnAccept = function() module:ClearData() end, timeout = 0, whileDead = true, hideOnEscape = true, preferredIndex = 3, } StaticPopup_Show("EXRT_FIGHTLOG_CLEAR") end, } fightsList[#fightsList + 1] = { text = L.BossWatcherSelectFightClose, notCheckable = true, func = CloseDropDownMenus_fix, } EasyMenu(fightsList, BWInterfaceFrame.bossButtonDropDown, "cursor", 10 , -15, "MENU") end) BWInterfaceFrame.bossButton.tooltipText = L.BossWatcherSelectFight BWInterfaceFrame.phaseButton:SetScript("OnClick",function (self) local fightsList = { { text = L.BossWatcherAllPhases, notCheckable = true, func = function() self:SetText( L.BossWatcherAllPhases.." |TInterface\\AddOns\\"..GlobalAddonName.."\\media\\DiesalGUIcons16x256x128:16:16:0:0:256:128:64:80:64:80|t" ) self:SetWidth(self:GetTextWidth()+30) UpdateSegments() end, } } for i=1,#CurrentFight.segments.phaseNames do local name = CurrentFight.segments.phaseNames[i] if type(name)=='number' then if name < 0 then local ej_name = C_EncounterJournal.GetSectionInfo(-name) if ej_name then name = ej_name.title else name = L.BossWatcherPhase.." "..i end else name = L.BossWatcherPhase.." "..i end end fightsList[#fightsList + 1] = { text = name, notCheckable = true, func = function() self:SetText( name.." |TInterface\\AddOns\\"..GlobalAddonName.."\\media\\DiesalGUIcons16x256x128:16:16:0:0:256:128:64:80:64:80|t" ) self:SetWidth(self:GetTextWidth()+30) UpdateSegments_SelectPhase(i) end, } end fightsList[#fightsList + 1] = { text = L.BossWatcherSelectFightClose, notCheckable = true, func = CloseDropDownMenus_fix, } EasyMenu(fightsList, BWInterfaceFrame.bossButtonDropDown, "cursor", 10 , -15, "MENU") end) ---- Tabs BWInterfaceFrame.tab = ELib:Tabs(BWInterfaceFrame,0, L.BossWatcherTabMobs, L.BossWatcherTabHeal, L.BossWatcherTabBuffsAndDebuffs, L.BossWatcherTabEnemy, L.BossWatcherTabPlayersSpells, L.BossWatcherTabEnergy, L.BossWatcherTabInterruptAndDispelShort, L.BossWatcherDeath, L.BossWatcherFrames, OTHER, L.BossWatcherTabSettings ):Size(865,600):Point("TOP",0,-60):SetTo(1) BWInterfaceFrame.tab.tabs[7].button.tooltip = L.BossWatcherTabInterruptAndDispel BWInterfaceFrame.tab:SetBackdropBorderColor(0,0,0,0) BWInterfaceFrame.tab:SetBackdropColor(0,0,0,0) for i=1,#BWInterfaceFrame.tab.tabs do BWInterfaceFrame.tab.tabs[i].button.Left:SetWidth(11) BWInterfaceFrame.tab.tabs[i].button.Right:SetWidth(11) end BWInterfaceFrame.tab:Hide() ---- Settings tab-button BWInterfaceFrame.tab.tabs[11]:SetScript("OnShow",function (self) if not module.options.isLoaded then module.options:GetScript("OnShow")(module.options) end module.options:SetParent(self) module.options:ClearAllPoints() module.options:SetPoint("TOPLEFT",5,-10) module.options:Show() end) ---- TimeLine Frame BWInterfaceFrame.timeLineFrame = CreateFrame('Frame',nil,BWInterfaceFrame.tab) BWInterfaceFrame.timeLineFrame.width = 858 BWInterfaceFrame.timeLineFrame:SetSize(BWInterfaceFrame.timeLineFrame.width,60) local TimeLine_Pieces = 60 local function TimeLinePieceOnEnter(self) if self.tooltip and #self.tooltip > 0 then ELib.Tooltip.Show(self,"ANCHOR_RIGHT",L.BossWatcherTimeLineTooltipTitle..":",unpack(self.tooltip)) end end local UpdateTimeLine, TimeLineFrame_ImprovedSelectSegment_GetSelected, TimeLineFrame_ImprovedSelectSegment_OnUpdate, TimeLineFrame_ImprovedSelectSegment_OnUpdate_Passive = nil do local TLframe = CreateFrame("Frame",nil,BWInterfaceFrame.timeLineFrame) BWInterfaceFrame.timeLineFrame.timeLine = TLframe TLframe:SetSize(BWInterfaceFrame.timeLineFrame.width,30) TLframe:SetPoint("TOP",0,0) do local tlWidth = BWInterfaceFrame.timeLineFrame.width/TimeLine_Pieces for i=1,TimeLine_Pieces do TLframe[i] = CreateFrame("Frame",nil,TLframe) TLframe[i]:SetSize(tlWidth,30) TLframe[i]:SetPoint("TOPLEFT",(i-1)*tlWidth,0) TLframe[i]:SetScript("OnEnter",TimeLinePieceOnEnter) TLframe[i]:SetScript("OnLeave",ELib.Tooltip.Hide) end end TLframe.texture = TLframe:CreateTexture(nil, "BACKGROUND",nil,0) --TLframe.texture:SetTexture("Interface\\AddOns\\"..GlobalAddonName.."\\media\\bar9.tga") --TLframe.texture:SetVertexColor(0.3, 1, 0.3, 1) TLframe.texture:SetColorTexture(1, 1, 1, 1) TLframe.texture:SetGradient("VERTICAL",CreateColor(1,0.82,0,.7), CreateColor(0.95,0.65,0,.7)) TLframe.texture:SetAllPoints() TLframe.textLeft = ELib:Text(TLframe,"",12):Size(200,16):Point("BOTTOMLEFT",TLframe,"BOTTOMLEFT", 2, 2):Top():Color():Shadow() TLframe.textCenter = ELib:Text(TLframe,"",12):Size(200,16):Point("BOTTOM",TLframe,"BOTTOM", 0, 2):Top():Center():Color():Shadow() TLframe.textRight = ELib:Text(TLframe,"",12):Size(200,16):Point("BOTTOMRIGHT",TLframe,"BOTTOMRIGHT", -2, 2):Top():Right():Color():Shadow() TLframe.lifeUnderLine = TLframe:CreateTexture(nil, "BACKGROUND") TLframe.lifeUnderLine:SetColorTexture(1,1,1,1) TLframe.lifeUnderLine:SetGradient("VERTICAL",CreateColor(1,0.2,0.2,0), CreateColor(1,0.2,0.2, 0.7)) TLframe.lifeUnderLine._SetPoint = TLframe.lifeUnderLine.SetPoint TLframe.lifeUnderLine.SetPoint = function(self,start,_end) self:ClearAllPoints() self:_SetPoint("TOPLEFT",self:GetParent(),"BOTTOMLEFT",start*BWInterfaceFrame.timeLineFrame.width,0) self:SetSize((_end-start)*BWInterfaceFrame.timeLineFrame.width,16) self:Show() end TLframe.arrow = CreateFrame("Frame",nil,TLframe) TLframe.arrow:SetSize(21,30) TLframe.arrow.G1 = TLframe.arrow:CreateTexture(nil, "BACKGROUND",nil,4) TLframe.arrow.G1:SetSize(4,30) TLframe.arrow.G1:SetPoint("LEFT",2,0) TLframe.arrow.G1:SetColorTexture(0,1,0) TLframe.arrow.G2 = TLframe.arrow:CreateTexture(nil, "BACKGROUND",nil,5) TLframe.arrow.G2:SetSize(2,30) TLframe.arrow.G2:SetPoint("LEFT",0,0) TLframe.arrow.G2:SetColorTexture(1,1,1) TLframe.arrow.G3 = TLframe.arrow:CreateTexture(nil, "BACKGROUND",nil,4) TLframe.arrow.G3:SetSize(15,30) TLframe.arrow.G3:SetPoint("LEFT",6,0) TLframe.arrow.G3:SetColorTexture(1,1,1) TLframe.arrow.G3:SetGradient("HORIZONTAL",CreateColor(0,1,0,1), CreateColor(0,1,0,0)) TLframe.arrow:Hide() TLframe.timeSegments = {} local function TimeLine_UpdateSegments() local count = 0 local totalSegments = #CurrentFight.segments for i=1,totalSegments do if CurrentFight.segments[i].e then count = count + 1 end end if count == totalSegments then for i=1,#TLframe.timeSegments do TLframe.timeSegments[i]:Hide() end TLframe.ImprovedSelectSegment.ResetZoom:Hide() else local fightDuration = GetFightLength(true) local tlWidth,tlHeight = TLframe:GetWidth(),TLframe:GetHeight() for i=1,totalSegments do if not TLframe.timeSegments[i] then TLframe.timeSegments[i] = TLframe:CreateTexture(nil, "BACKGROUND",nil,1) TLframe.timeSegments[i]:SetTexture("Interface\\AddOns\\"..GlobalAddonName.."\\media\\bar9.tga") TLframe.timeSegments[i]:SetVertexColor(0.8, 0.8, 0.8, 1) end if CurrentFight.segments[i].e then TLframe.timeSegments[i]:Hide() else local timeStart = CurrentFight.segments[i].t - CurrentFight.encounterStart local timeEnd = (CurrentFight.segments[i+1] and CurrentFight.segments[i+1].t or (not CurrentFight.isEnded and GetTime() or CurrentFight.encounterEnd)) - CurrentFight.encounterStart local startPos = timeStart/fightDuration*tlWidth local endPos = timeEnd/fightDuration*tlWidth TLframe.timeSegments[i]:SetPoint("TOPLEFT",startPos,0) TLframe.timeSegments[i]:SetSize(max(endPos-startPos,0.5),tlHeight) TLframe.timeSegments[i]:Show() end end for i=totalSegments + 1,#TLframe.timeSegments do TLframe.timeSegments[i]:Hide() end TLframe.ImprovedSelectSegment.ResetZoom:Show() end end TLframe.labels = {} function TLframe:AddLabel(i,pos,type) local label = TLframe.labels[i] if not label then label = TLframe:CreateTexture(nil, "BACKGROUND") TLframe.labels[i] = label label:SetSize(3,25) end label:SetPoint("TOP",TLframe,"BOTTOMLEFT",BWInterfaceFrame.timeLineFrame.width*pos,0) if not type then label:SetColorTexture(.35,.38,1,.7) elseif type == 1 then label:SetColorTexture(.2,1,0,1) elseif type == 2 then label:SetColorTexture(1,.25,.3,.7) end label:Show() end function TLframe:HideLabels() for i=1,#TLframe.labels do TLframe.labels[i]:Hide() end end local EncountersPhases = {} TLframe.redLine = {} local function CreateRedLine(i) TLframe.redLine[i] = TLframe:CreateTexture(nil, "BACKGROUND",nil,3) TLframe.redLine[i]:SetColorTexture(0.7, 0.1, 0.1, 0.5) TLframe.redLine[i]:SetSize(2,30) end TLframe.blueLine = {} local function CreateBlueLine(i) TLframe.blueLine[i] = TLframe:CreateTexture(nil, "BACKGROUND",nil,4) TLframe.blueLine[i]:SetColorTexture(0.1, 0.1, 0.7, 0.5) TLframe.blueLine[i]:SetSize(3,30) end TLframe.phaseMarker = {} TLframe.phaseMarkerNum = 0 TLframe.phaseMarkerMax = 0 local function AddPhase(pos,phase) local currNum = TLframe.phaseMarkerNum + 1 TLframe.phaseMarkerNum = currNum local l,l2,t = TLframe.phaseMarker[currNum],TLframe.phaseMarker[currNum+0.5],TLframe.phaseMarker[-currNum] if not l then l = TLframe:CreateTexture(nil, "BACKGROUND",nil,5) TLframe.phaseMarker[currNum] = l l:SetSize(2,30) l:SetColorTexture(1, 1, 1, 0.8) l2 = TLframe:CreateTexture(nil, "BACKGROUND",nil,5) TLframe.phaseMarker[currNum+0.5] = l2 l2:SetSize(1,30) l2:SetColorTexture(0, 0, 0, 0.8) l2:SetPoint("TOPLEFT",l,"TOPRIGHT",0,0) t = ELib:Text(TLframe,"",10):Point("TOPLEFT",l,"TOPRIGHT",2,-1):Point("TOPRIGHT",TLframe,"TOPRIGHT",0,-1):Point("BOTTOM",TLframe):Left():Top():Color(1,1,1,1) t:SetMaxLines(1) TLframe.phaseMarker[-currNum] = t TLframe.phaseMarkerMax = max(currNum,TLframe.phaseMarkerMax) end local t_pos = BWInterfaceFrame.timeLineFrame.width * pos l:SetPoint("TOPLEFT",t_pos,0) t.pos = t_pos local phaseText = nil if type(phase)=='number' and phase < 0 then local name = C_EncounterJournal.GetSectionInfo(-phase) if name then phaseText = name.title end elseif type(phase)=='string' then phaseText = phase end local secondString if currNum > 1 then local prev = TLframe.phaseMarker[-(currNum-1)] if prev.pos + prev:GetStringWidth() > t_pos then secondString = "|n" end end t:SetText((secondString or "")..(phaseText or (L.BossWatcherPhase.." "..phase))) if secondString then t:SetMaxLines(2) else t:SetMaxLines(1) end l:Show() l2:Show() t:Show() end function UpdateTimeLine() local fight_dur = GetFightLength(true) if fightData and fight_dur < 1.5 then fight_dur = GetTime() - CurrentFight.encounterStart end TLframe.textLeft:SetText( date("%H:%M:%S", CurrentFight.encounterStartGlobal) ) TLframe.textRight:SetText( date("%M:%S", fight_dur) ) TLframe.textCenter:SetText( date("%M:%S", fight_dur / 2) ) TLframe.ImprovedSelectSegment:Show() for i=1,TimeLine_Pieces do TLframe[i]:Hide() end TLframe.phaseMarkerNum = 0 for i=1,TLframe.phaseMarkerMax do TLframe.phaseMarker[i]:Hide() TLframe.phaseMarker[i+0.5]:Hide() TLframe.phaseMarker[-i]:Hide() end local CurrentEncountersPhases = CurrentFight.encounterID and EncountersPhases[CurrentFight.encounterID] or ExRT.NULL if CurrentFight.segments.phaseNames then local prev = CurrentFight.segments[1].p for i=2,#CurrentFight.segments do local sData = CurrentFight.segments[i] if prev ~= sData.p then local _time = sData.t - CurrentFight.encounterStart prev = sData.p local name = CurrentFight.segments.phaseNames[prev] if type(name)=='number' then if name < 0 then local ej_name = C_EncounterJournal.GetSectionInfo(-name) if ej_name then name = ej_name.title else name = L.BossWatcherPhase.." "..i end else name = L.BossWatcherPhase.." "..i end end AddPhase(_time / fight_dur,name or prev) end end CurrentEncountersPhases = ExRT.NULL end local redLineNum = 0 for i=1,TimeLine_Pieces do if not TLframe[i].tooltip then TLframe[i].tooltip = {} end wipe(TLframe[i].tooltip) end if CurrentEncountersPhases.aura then local phasesAuras = CurrentEncountersPhases.aura for i,data in ipairs(CurrentFight.auras) do if phasesAuras[ data[6] ] then local phaseData = phasesAuras[ data[6] ] if (phaseData.isFade and data[8] == 2) or (not phaseData.isFade and data[8] == 1) then local _time = timestampToFightTime(data[1]) AddPhase(_time / fight_dur,phaseData.phase) end end end end local addToToolipTable = {} local bossHpPerSegment = {} if CurrentFight.graphData then local lastSec = 0 for sec,data in pairs(CurrentFight.graphData) do lastSec = max(lastSec,sec) end local antiSpam = {} local phaseHpStart = 1 for sec=1,lastSec do local data = CurrentFight.graphData[sec] if data then local boss1 = data["boss1"] if boss1 then local hpMax = boss1.hpmax if hpMax ~= 0 and boss1.health then local tooltipIndex = sec / fight_dur tooltipIndex = min( floor( (TimeLine_Pieces - 0.01)*tooltipIndex + 1 ) , TimeLine_Pieces) local hp = boss1.health/hpMax if not bossHpPerSegment[tooltipIndex] then bossHpPerSegment[tooltipIndex] = (boss1.name or "boss1").."'s hp: "..format("%.1f%%",hp*100) end if CurrentEncountersPhases.hp then for i=phaseHpStart,#CurrentEncountersPhases.hp do local data = CurrentEncountersPhases.hp[i] if hp <= data.hp then AddPhase(sec / fight_dur,data.phase) phaseHpStart = i+1 break end end end end end for i=2,5 do local boss = data["boss"..i] if boss then local hpMax = boss.hpmax if hpMax ~= 0 and boss.health then local hp = boss.health/hpMax local str = (boss.name or "boss"..i).."'s hp: "..format("%.1f%%",hp*100) local tooltipIndex = sec / fight_dur tooltipIndex = min( floor( (TimeLine_Pieces - 0.01)*tooltipIndex + 1 ) , TimeLine_Pieces) antiSpam[tooltipIndex] = antiSpam[tooltipIndex] or {} if not antiSpam[tooltipIndex]["boss"..i] then if not bossHpPerSegment[tooltipIndex] then bossHpPerSegment[tooltipIndex] = str else bossHpPerSegment[tooltipIndex] = bossHpPerSegment[tooltipIndex] .. "|n" .. str end antiSpam[tooltipIndex]["boss"..i] = true end end end end end end end for mobGUID,mobData in pairs(CurrentFight.cast) do for i=1,#mobData do local castData = mobData[i] if castData[7] and ExRT.F.GetUnitInfoByUnitFlag(castData[7],3) ~= 16 then local _time = timestampToFightTime(castData[1]) local tooltipIndex = _time / fight_dur if CurrentEncountersPhases.cast then local phaseData = CurrentEncountersPhases.cast[ castData[2] ] if phaseData and ((phaseData.isCastStart and castData[3] == 2) or (not phaseData.isCastStart and castData[3] == 1)) then AddPhase(tooltipIndex,phaseData.phase) end if phaseData and phaseData.next and ((phaseData.next.isCastStart and castData[3] == 2) or (not phaseData.next.isCastStart and castData[3] == 1)) then local newTime = _time + phaseData.next.time if newTime < fight_dur then AddPhase(newTime / fight_dur,phaseData.next.phase) end end end if CurrentFight.segments[castData.s].e then redLineNum = redLineNum + 1 if not TLframe.redLine[redLineNum] then CreateRedLine(redLineNum) end TLframe.redLine[redLineNum]:SetPoint("TOPLEFT",TLframe,"TOPLEFT",BWInterfaceFrame.timeLineFrame.width*tooltipIndex,0) TLframe.redLine[redLineNum]:Show() tooltipIndex = min( floor( (TimeLine_Pieces - 0.01)*tooltipIndex + 1 ) , TimeLine_Pieces) local spellName,_,spellTexture = GetSpellInfo(castData[2]) local targetInfo = "" if castData[4] and castData[4] ~= "" then targetInfo = " "..L.BossWatcherTimeLineOnText.." |c"..ExRT.F.classColorByGUID(castData[4])..GetGUID(castData[4]).."|r" end addToToolipTable[#addToToolipTable + 1] = {tooltipIndex,_time,"[" .. date("%M:%S", _time ) .. "] |c"..ExRT.F.classColorByGUID(mobGUID) .. GetGUID(mobGUID) .."|r" .. GUIDtoText("(%s)",mobGUID) .. ( castData[3] == 1 and " "..L.BossWatcherTimeLineCast.." " or " "..L.BossWatcherTimeLineCastStart.." " ) .. format("%s%s%s",spellTexture and "|T"..spellTexture..":0|t " or "",spellName or "???"," ["..castData[2].."]") .. targetInfo } end end end end for _,chatData in ipairs(CurrentFight.chat) do local _time = min( max(chatData[4] - CurrentFight.encounterStart,0) , CurrentFight.encounterEnd) local tooltipIndex = _time / fight_dur if CurrentEncountersPhases.chat then local phaseData = CurrentEncountersPhases.chat[ chatData[3] ] if phaseData then AddPhase(tooltipIndex,phaseData.phase) end end if CurrentFight.segments[chatData.s].e then redLineNum = redLineNum + 1 if not TLframe.redLine[redLineNum] then CreateRedLine(redLineNum) end TLframe.redLine[redLineNum]:SetPoint("TOPLEFT",TLframe,"TOPLEFT",BWInterfaceFrame.timeLineFrame.width*tooltipIndex,0) TLframe.redLine[redLineNum]:Show() tooltipIndex = min( floor( (TimeLine_Pieces - 0.01)*tooltipIndex + 1 ) , TimeLine_Pieces) local spellName,_,spellTexture = GetSpellInfo(chatData[3]) addToToolipTable[#addToToolipTable + 1] = {tooltipIndex,_time,"[" .. date("%M:%S", _time ) .. "] ".. L.BossWatcherChatSpellMsg .. " " .. format("%s%s%s",spellTexture and "|T"..spellTexture..":0|t " or "",spellName or "???"," ["..chatData[3].."]") } end end for _,resData in ipairs(CurrentFight.resurrests) do if CurrentFight.segments[resData.s].e then local _time = timestampToFightTime(resData[4]) local tooltipIndex = _time / fight_dur redLineNum = redLineNum + 1 if not TLframe.redLine[redLineNum] then CreateRedLine(redLineNum) end TLframe.redLine[redLineNum]:SetPoint("TOPLEFT",TLframe,"TOPLEFT",BWInterfaceFrame.timeLineFrame.width*tooltipIndex,0) TLframe.redLine[redLineNum]:Show() tooltipIndex = min( floor( (TimeLine_Pieces - 0.01)*tooltipIndex + 1 ) , TimeLine_Pieces) local spellName,_,spellTexture = GetSpellInfo(resData[3]) addToToolipTable[#addToToolipTable + 1] = {tooltipIndex,_time,"[" .. date("%M:%S", _time ) .. "] |c"..ExRT.F.classColorByGUID(resData[1]) .. GetGUID(resData[1]) .."|r" .. GUIDtoText("(%s)",resData[1]) .. " ".. L.BossWatcherTimeLineCast.. " " .. format("%s%s%s",spellTexture and "|T"..spellTexture..":0|t " or "",spellName or "???"," ["..resData[3].."]") .. " "..L.BossWatcherTimeLineOnText.." |c"..ExRT.F.classColorByGUID(resData[2])..GetGUID(resData[2]).."|r" } end end for i=(redLineNum+1),#TLframe.redLine do TLframe.redLine[i]:Hide() end local blueLineNum = 0 for i=1,#CurrentFight.dies do if CurrentFight.segments[CurrentFight.dies[i].s].e and ExRT.F.GetUnitInfoByUnitFlag(CurrentFight.dies[i][2],1) == 1024 then local _time = timestampToFightTime(CurrentFight.dies[i][3]) local tooltipIndex = _time / fight_dur blueLineNum = blueLineNum + 1 if not TLframe.blueLine[blueLineNum] then CreateBlueLine(blueLineNum) end TLframe.blueLine[blueLineNum]:SetPoint("TOPLEFT",TLframe,"TOPLEFT",BWInterfaceFrame.timeLineFrame.width*tooltipIndex,0) TLframe.blueLine[blueLineNum]:Show() tooltipIndex = min ( floor( (TimeLine_Pieces - 0.01)*tooltipIndex + 1 ) , TimeLine_Pieces) addToToolipTable[#addToToolipTable + 1] = {tooltipIndex,_time,"[" .. date("%M:%S", _time ) .. "] |cffee5555" .. GetGUID(CurrentFight.dies[i][1]) .. GUIDtoText("(%s)",CurrentFight.dies[i][1]) .. " "..L.BossWatcherTimeLineDies.."|r"} end end for i=(blueLineNum+1),#TLframe.blueLine do TLframe.blueLine[i]:Hide() end for i=1,TimeLine_Pieces do if bossHpPerSegment[i] then table.insert(TLframe[i].tooltip,{bossHpPerSegment[i],1,1,1}) end end table.sort(addToToolipTable,function (a,b) return a[2] < b[2] end) for i=1,#addToToolipTable do if TLframe[ addToToolipTable[i][1] ] then table.insert(TLframe[ addToToolipTable[i][1] ].tooltip,{addToToolipTable[i][3],1,1,1}) end end TimeLine_UpdateSegments() end TLframe.ImprovedSelectSegment = CreateFrame("Button",nil,TLframe) TLframe.ImprovedSelectSegment:SetAllPoints() TLframe.ImprovedSelectSegment:Hide() TLframe.ImprovedSelectSegment.ResetZoom = CreateFrame("Button",nil,TLframe.ImprovedSelectSegment) TLframe.ImprovedSelectSegment.ResetZoom:SetSize(200,13) TLframe.ImprovedSelectSegment.ResetZoom:SetPoint("TOPRIGHT",TLframe.ImprovedSelectSegment,"BOTTOMRIGHT",-1,4) TLframe.ImprovedSelectSegment.ResetZoom.Text = ELib:Text(TLframe.ImprovedSelectSegment.ResetZoom,"["..L.BossWatcherGraphZoomReset.."]",11):Size(200,13):Point("RIGHT",0,0):Right():Top():Color():Outline() TLframe.ImprovedSelectSegment.ResetZoom:SetWidth( TLframe.ImprovedSelectSegment.ResetZoom.Text:GetStringWidth() ) TLframe.ImprovedSelectSegment.ResetZoom:SetScript("OnClick",function (self) BWInterfaceFrame.GraphFrame.G.ZoomMinX = nil BWInterfaceFrame.GraphFrame.G.ZoomMaxX = nil UpdateSegments() end) TLframe.ImprovedSelectSegment.ResetZoom:Hide() TLframe.ImprovedSelectSegment.hoverTime = ELib:Text(TLframe.ImprovedSelectSegment,"",11):Size(200,16):Center():Top():Color():Outline() TLframe.ImprovedSelectSegment.Texture = TLframe:CreateTexture(nil, "BACKGROUND",nil,2) --TLframe.ImprovedSelectSegment.Texture:SetTexture("Interface\\AddOns\\"..GlobalAddonName.."\\media\\bar9.tga") --TLframe.ImprovedSelectSegment.Texture:SetVertexColor(0, 0.65, 0.9, .7) TLframe.ImprovedSelectSegment.Texture:SetColorTexture(1, 1, 1, 1) TLframe.ImprovedSelectSegment.Texture:SetGradient("VERTICAL",CreateColor(0.3,0.75,0.90,.7), CreateColor(0,0.62,0.90,.7)) TLframe.ImprovedSelectSegment.Texture:SetHeight(30) TLframe.ImprovedSelectSegment.Texture:Hide() function TimeLineFrame_ImprovedSelectSegment_GetSelected(self) local fightDuration = GetFightLength(true) local timeLineWidth = self:GetWidth() local start = self.mouseDowned / timeLineWidth local ending = ExRT.F.GetCursorPos(self) / timeLineWidth if ending > start then ending = min(ending,1) start = max(start,0) else start = min(start,1) ending = max(ending,0) end start = ExRT.F.Round(start * fightDuration) ending = ExRT.F.Round(ending * fightDuration) return start,ending end function TimeLineFrame_ImprovedSelectSegment_OnUpdate(self) local x = ExRT.F.GetCursorPos(self) local width = x - self.mouseDowned if width > 0 then width = min(width,self:GetWidth()-self.mouseDowned) self.Texture:SetWidth(width) self.Texture:SetPoint("TOPLEFT",TLframe,"TOPLEFT", self.mouseDowned ,0) local start,ending = TimeLineFrame_ImprovedSelectSegment_GetSelected(self) ELib.Tooltip.Show(self,"ANCHOR_CURSOR",format("%d:%02d - %d:%02d",start / 60,start % 60,ending / 60,ending % 60)) elseif width < 0 then width = -width width = min(width,self.mouseDowned) self.Texture:SetWidth(width) self.Texture:SetPoint("TOPLEFT",TLframe,"TOPLEFT", self.mouseDowned-width,0) local start,ending = TimeLineFrame_ImprovedSelectSegment_GetSelected(self) ELib.Tooltip.Show(self,"ANCHOR_CURSOR",format("%d:%02d - %d:%02d",ending / 60,ending % 60,start / 60,start % 60)) else self.Texture:SetWidth(1) ELib.Tooltip:Hide() end end TLframe.ImprovedSelectSegment:SetScript("OnMouseDown",function (self) self.mouseDowned = ExRT.F.GetCursorPos(self) self.Texture:SetPoint("TOPLEFT",TLframe,"TOPLEFT", self.mouseDowned ,0) self.Texture:SetWidth(1) self.Texture:Show() self:SetScript("OnUpdate",TimeLineFrame_ImprovedSelectSegment_OnUpdate) self.hoverTime:Hide() end) TLframe.ImprovedSelectSegment:SetScript("OnMouseUp",function (self) self:SetScript("OnUpdate",nil) self.Texture:Hide() ELib.Tooltip:Hide() if not self.mouseDowned then return end local start,ending = TimeLineFrame_ImprovedSelectSegment_GetSelected(self) self.mouseDowned = nil if ending < start then start,ending = ending,start end start = start + 1 ending = ending + 1 UpdateSegments(start,ending,IsShiftKeyDown()) end) function TimeLineFrame_ImprovedSelectSegment_OnUpdate_Passive(self) local timeLineWidth = self:GetWidth() local fightDuration = GetFightLength(true) local x = ExRT.F.GetCursorPos(self) local time = ExRT.F.Round(x / timeLineWidth * fightDuration) self.hoverTime:SetFormattedText("%d:%02d",time / 60,time % 60) self.hoverTime:SetPoint("TOP",self,"TOPLEFT",x,-2) self.hoverTime:Show() local segmentNow = ceil(x / timeLineWidth * 60 + 0.01) if segmentNow ~= self.lastSegment then self.lastSegment = segmentNow ELib.Tooltip:Hide() else return end local frame = TLframe[segmentNow] if frame then TimeLinePieceOnEnter(frame) end end TLframe.ImprovedSelectSegment:SetScript("OnEnter",function (self) if self.mouseDowned then return end self.lastSegment = nil self:SetScript("OnUpdate",TimeLineFrame_ImprovedSelectSegment_OnUpdate_Passive) end) TLframe.ImprovedSelectSegment:SetScript("OnLeave",function (self) self.hoverTime:Hide() if self.mouseDowned then return end ELib.Tooltip:Hide() self:SetScript("OnUpdate",nil) end) end BWInterfaceFrame.timeLineFrame:SetScript("OnShow",function (self) if BWInterfaceFrame.nowFightID ~= self.lastFightID then UpdateTimeLine() self.lastFightID = BWInterfaceFrame.nowFightID end end) ---- Graph Frame BWInterfaceFrame.GraphFrame = CreateFrame('Frame',nil,BWInterfaceFrame.tab) BWInterfaceFrame.GraphFrame:SetSize(860,200) BWInterfaceFrame.GraphFrame:SetPoint("TOP",0,-80) BWInterfaceFrame.GraphFrame:Hide() BWInterfaceFrame.GraphFrame.G = ExRT.lib.CreateGraph(BWInterfaceFrame.GraphFrame,790,180,"TOPLEFT",50,-5,true) BWInterfaceFrame.GraphFrame.G.fixMissclickZoom = true BWInterfaceFrame.GraphFrame.G.DisableReloadOnResetZoom = true BWInterfaceFrame.GraphFrame.G.backgroundHighlight = {} BWInterfaceFrame.GraphFrame.G.ResetZoom:Point("TOPRIGHT",-100,10) BWInterfaceFrame.GraphFrame.G.highlightHideButton = ELib:Button(BWInterfaceFrame.GraphFrame.G,"",1) BWInterfaceFrame.GraphFrame.G.highlightHideButton:SetSize(16,16) BWInterfaceFrame.GraphFrame.G.highlightHideButton.background = BWInterfaceFrame.GraphFrame.G.highlightHideButton:CreateTexture(nil, "BACKGROUND") BWInterfaceFrame.GraphFrame.G.highlightHideButton.background:SetAllPoints() BWInterfaceFrame.GraphFrame.G.highlightHideButton.background:SetTexture("Interface\\AddOns\\"..GlobalAddonName.."\\media\\DiesalGUIcons16x256x128") BWInterfaceFrame.GraphFrame.G.highlightHideButton.background:SetTexCoord(0.5,0.5625,0.5,0.625) BWInterfaceFrame.GraphFrame.G.highlightHideButton:SetScript("OnEnter",function(self) self.background:SetVertexColor(.7,0,0,1) end) BWInterfaceFrame.GraphFrame.G.highlightHideButton:SetScript("OnLeave",function(self) self.background:SetVertexColor(1,1,1,.7) end) BWInterfaceFrame.GraphFrame.G.highlightHideButton:SetScript("OnClick",function(self) local parent = self:GetParent() parent.highlight = nil parent:OnAfterReload() end) BWInterfaceFrame.GraphFrame.G.highlightHideButton:Hide() function BWInterfaceFrame.GraphFrame.G:OnBeforeReload() if not self.data[1] then return end local tooltipX = {} for i=1,#self.data[1] do tooltipX[i] = format("%d:%02d",i / 60,i % 60) end self.data.tooltipX = tooltipX end function BWInterfaceFrame.GraphFrame.G:OnAfterReload() if not self.highlight or not self.range or not self.range.maxX then for i=1,#self.backgroundHighlight do self.backgroundHighlight[i]:Hide() end self.highlightHideButton:Hide() return end local count = 0 for i=1,#self.highlight,2 do local posStart,posEnd = self.highlight[i],self.highlight[i+1] if not (posStart > self.range.maxX or posEnd < self.range.minX) then if posStart < self.range.minX then posStart = self.range.minX end if posEnd > self.range.maxX then posEnd = self.range.maxX end count = count + 1 local background = self.backgroundHighlight[count] if not background then background = self:CreateTexture(nil, "BACKGROUND",nil,2) self.backgroundHighlight[count] = background background:SetPoint("TOP",0,0) background:SetPoint("BOTTOM",0,0) background:SetColorTexture(0.7, 0.65, 0.9, .3) if count == 1 then self.highlightHideButton:SetPoint("TOPRIGHT",background,2,-2) end end local posX = (posStart - self.range.minX) / (self.range.maxX - self.range.minX) * self:GetWidth() local width = (posEnd - posStart) / (self.range.maxX - self.range.minX) * self:GetWidth() background:SetPoint("LEFT",posX,0) background:SetWidth(width) background:Show() if count == 1 then self.highlightHideButton:Show() end end end for i=count+1,#self.backgroundHighlight do self.backgroundHighlight[i]:Hide() end if count == 0 then self.highlightHideButton:Hide() end end function BWInterfaceFrame.GraphFrame.G:Zoom(start,ending) if ending == start then self.ZoomMinX = nil self.ZoomMaxX = nil else self.ZoomMinX = start self.ZoomMaxX = ending end UpdateSegments(start,ending) end function BWInterfaceFrame.GraphFrame.G:OnResetZoom() UpdateSegments() end BWInterfaceFrame.GraphFrame.stepSlider = ELib:Slider(BWInterfaceFrame.GraphFrame,"",true):Point("BOTTOMRIGHT",BWInterfaceFrame.GraphFrame.G,"BOTTOMLEFT",-10,20):Size(100):Range(1,10):SetTo(1):OnChange(function(self) local graph = self:GetParent().G self:Tooltip(L.BossWatcherGraphicsStep.."\n"..floor(self:GetValue() + 0.5)) self:tooltipReload() if graph.IgnoreStepSliderFix then return end graph.step = floor(self:GetValue() + 0.5) graph:Reload() end):Tooltip(L.BossWatcherGraphicsStep.."\n1") function Graph_AutoUpdateStep() local fightDuration = GetFightLength() local step = 1 if fightDuration >= 360 then step = 3 elseif fightDuration >= 180 then step = 2 end local graph = BWInterfaceFrame.GraphFrame.G graph.IgnoreStepSliderFix = true graph.step = step BWInterfaceFrame.GraphFrame.stepSlider:SetValue(step) graph.IgnoreStepSliderFix = nil end local tab = nil ---- Damage Tab tab = BWInterfaceFrame.tab.tabs[1] local sourceVar,destVar = {},{} local DamageTab_SetLine = nil local DamageTab_Variables = { Last_Func = nil, Last_doEnemy = nil, ShowAll = false, Back_Func = nil, Back_destVar = nil, Back_sourceVar = nil, state_friendly = true, state_spells = false, state_byTarget = false, state_spellsTarget = false, } local DamageTab_UpdatePage local function DamageTab_GetGUIDsReport(list,isDest) local result = "" for GUID,_ in pairs(list) do if result ~= "" then result = result..", " end local time = "" if isDest and ExRT.F.GetUnitTypeByGUID(GUID) ~= 0 and CurrentFight.damage_seen[GUID] then time = date(" (%M:%S)", timestampToFightTime( CurrentFight.damage_seen[GUID] )) end result = result .. GetGUID(GUID) .. time end if result ~= "" then return result end end local function DamageTab_UpdateDropDown(arr,dropDown) local count = ExRT.F.table_len(arr) if count == 0 then dropDown:SetText(L.BossWatcherAll) elseif count == 1 then local GUID = nil for g,_ in pairs(arr) do GUID = g end local name = GetGUID(GUID) local flags = CurrentFight.reaction[GUID] local isPlayer = GetUnitInfoByUnitFlagFix(flags,1) == 1024 local isNPC = GetUnitInfoByUnitFlagFix(flags,2) == 512 if isPlayer then name = "|c"..ExRT.F.classColorByGUID(GUID)..name elseif isNPC then name = name .. date(" %M:%S", timestampToFightTime( CurrentFight.damage_seen[GUID] )) .. GUIDtoText(" [%s]",GUID) end dropDown:SetText(name) else dropDown:SetText(L.BossWatcherSeveral) end end local function DamageTab_UpdateDropDowns() DamageTab_UpdateDropDown(sourceVar,BWInterfaceFrame.tab.tabs[1].sourceDropDown) DamageTab_UpdateDropDown(destVar,BWInterfaceFrame.tab.tabs[1].targetDropDown) end local function DamageTab_UpdateChecks() local tab = BWInterfaceFrame.tab.tabs[1] for _,c in pairs({tab.chkFriendly,tab.chkEnemy,tab.chkSpellsTargets,tab.bySource,tab.byTarget,tab.bySpell}) do c:SetChecked(false) end if DamageTab_Variables.state_spellsTarget then tab.chkSpellsTargets:SetChecked(true) elseif DamageTab_Variables.state_friendly then tab.chkFriendly:SetChecked(true) else tab.chkEnemy:SetChecked(true) end if DamageTab_Variables.state_spells then tab.bySpell:SetChecked(true) end if DamageTab_Variables.state_byTarget then tab.byTarget:SetChecked(true) elseif not DamageTab_Variables.state_spells then tab.bySource:SetChecked(true) end end local function DamageTab_Temp_SortingBy2Param(a,b) return a[2] > b[2] end local function DamageTab_ReloadGraph(data,fightLength,linesData,isSpell) local graphData = {} for key,spellData in pairs(data) do local newData if isSpell then local spellID = key local isPet = 1 if (not ExRT.isClassic or ExRT.isCata) and spellID < -1 then isPet = -1 spellID = -spellID end local spellName,_,spellIcon = GetSpellInfo(spellID) if spellID == -1 then spellName = L.BossWatcherReportTotal elseif spellName then spellName = "|T"..spellIcon..":0|t "..spellName else spellName = spellID end newData = { info_spellID = type(spellID)=='number' and spellID*isPet or spellID, name = spellName, total_damage = 0, hide = true, } else local sourceGUID = key local name,r,g,b = nil if sourceGUID == -1 then name = L.BossWatcherReportTotal else local class name = GetGUID(sourceGUID) if sourceGUID ~= "" then class = select(2,GetPlayerInfoByGUID(sourceGUID)) end name = "|c".. ExRT.F.classColor(class) .. name .. "|r" if class then local classColorArray = type(CUSTOM_CLASS_COLORS)=="table" and CUSTOM_CLASS_COLORS[class] or RAID_CLASS_COLORS[class] r,g,b = classColorArray.r,classColorArray.g,classColorArray.b end end newData = { info_spellID = sourceGUID, name = name, total_damage = 0, hide = true, r = r, g = g, b = b, } end graphData[#graphData+1] = newData local total_damage = 0 for i=1,fightLength do newData[i] = {i,spellData[i] or 0} total_damage = total_damage + (spellData[i] or 0) end newData.total_damage = total_damage end sort(graphData,function(a,b) return a.total_damage>b.total_damage end) local findPos = ExRT.F.table_find(graphData,-1,'info_spellID') if findPos then graphData[ findPos ].hide = nil graphData[ findPos ].specialLine = true end for i=1,3 do if linesData[i] then findPos = ExRT.F.table_find(graphData,linesData[i][isSpell and "spell" or "guid"],'info_spellID') if findPos then graphData[ findPos ].hide = nil end end end BWInterfaceFrame.GraphFrame.G.data = graphData BWInterfaceFrame.GraphFrame.G:Reload() end local function DamageTab_UpdateLines_GetUnit(damage,graph,source,dest,header,secondHeader) header = header or "guid" local sourceDamage for i=1,#damage do if damage[i][header] == source and (not secondHeader or damage[i].info == secondHeader) then sourceDamage = i break end end if not sourceDamage then sourceDamage = #damage + 1 damage[sourceDamage] = { [header] = source, info = secondHeader, eff = 0, total = 0, count = 0, overkill = 0, blocked = 0, absorbed = 0, crit = 0, critcount = 0, critmax = 0, critover = 0, hitmax = 0, parry = 0, dodge = 0, miss = 0, targets = {}, } end sourceDamage = damage[sourceDamage] local destPos local targets = sourceDamage.targets for i=1,#targets do if targets[i][1] == dest then destPos = i break end end if not destPos then destPos = #sourceDamage.targets + 1 sourceDamage.targets[destPos] = {dest,0} end if not graph[ source ] then graph[ source ] = {} end return sourceDamage, sourceDamage.targets[destPos] end local function DamageTab_UpdateLinesPlayers() ExRT.F.dprint("Damage Update: Players",GetTime()) local doEnemy = DamageTab_Variables.state_friendly local isReverse = DamageTab_Variables.state_byTarget DamageTab_UpdateDropDowns() DamageTab_UpdateChecks() local damage = {} local total = 0 local totalOver = 0 local graph = {[-1]={}} for destGUID,destData in pairs(CurrentFight.damage) do if ExRT.F.table_len(destVar) == 0 or destVar[destGUID] then for destReaction,destReactData in pairs(destData) do local isEnemy = false if GetUnitInfoByUnitFlagFix(destReaction,2) == 512 then isEnemy = true end if (isEnemy and doEnemy) or (not isEnemy and not doEnemy) then for sourceGUID,sourceData in pairs(destReactData) do local owner = ExRT.F.Pets:getOwnerGUID(sourceGUID,GetPetsDB()) if owner then sourceGUID = owner end if ExRT.F.table_len(sourceVar) == 0 or sourceVar[sourceGUID] then local source = isReverse and destGUID or sourceGUID local dest = isReverse and sourceGUID or destGUID local sourceDamage, destPos = DamageTab_UpdateLines_GetUnit(damage,graph,source,dest,"guid") for spellID,spellSegments in pairs(sourceData) do for segment,spellAmount in pairs(spellSegments) do if CurrentFight.segments[segment].e then sourceDamage.eff = sourceDamage.eff + spellAmount.amount - spellAmount.overkill sourceDamage.total = sourceDamage.total + spellAmount.amount + spellAmount.blocked + spellAmount.absorbed sourceDamage.overkill = sourceDamage.overkill + spellAmount.overkill sourceDamage.blocked = sourceDamage.blocked + spellAmount.blocked sourceDamage.absorbed = sourceDamage.absorbed + spellAmount.absorbed sourceDamage.crit = sourceDamage.crit + spellAmount.crit total = total + spellAmount.amount - spellAmount.overkill totalOver = totalOver + spellAmount.overkill + spellAmount.blocked + spellAmount.absorbed local damgeCount = spellAmount.amount + (DamageTab_Variables.ShowAll and (spellAmount.blocked+spellAmount.absorbed) or -spellAmount.overkill) destPos[2] = destPos[2] + damgeCount if not graph[ source ][segment] then graph[ source ][segment] = 0 end if not graph[ -1 ][segment] then graph[ -1 ][segment] = 0 end graph[ source ][segment] = graph[ source ][segment] + damgeCount graph[ -1 ][segment] = graph[ -1 ][segment] + damgeCount end end end end end end end end end local totalIsFull = 1 total = max(total,1) if total == 1 and #damage == 0 then total = 0 totalIsFull = 0 end local _max = nil reportOptions[1] = L.BossWatcherReportDPS wipe(reportData[1]) reportData[1][1] = (DamageTab_GetGUIDsReport(sourceVar) or L.BossWatcherAllSources).." > "..(DamageTab_GetGUIDsReport(destVar,true) or L.BossWatcherAllTargets) local activeFightLength = GetFightLength() if DamageTab_Variables.ShowAll then total = total + totalOver sort(damage,function(a,b) return a.total>b.total end) _max = damage[1] and damage[1].total or 0 else sort(damage,function(a,b) return a.eff>b.eff end) _max = damage[1] and damage[1].eff or 0 end reportData[1][2] = L.BossWatcherReportTotal.." - "..ExRT.F.shortNumber(total).."@1@ ("..floor(total / activeFightLength)..")@1#" DamageTab_SetLine({ line = 1, name = L.BossWatcherReportTotal, num = total, total = total, max = total, alpha = DamageTab_Variables.ShowAll and totalOver, dps = total / activeFightLength, spellID = -1, check = BWInterfaceFrame.GraphFrame:IsShown(), checkState = true, }) for i=#damage,1,-1 do if damage[i].total == 0 then tremove(damage,i) end end for i=1,#damage do local damageLine = damage[i] local class = nil if damageLine.guid and damageLine.guid ~= "" then class = select(2,GetPlayerInfoByGUID(damageLine.guid)) end local icon = "" if class and CLASS_ICON_TCOORDS[class] then icon = {"Interface\\GLUES\\CHARACTERCREATE\\UI-CHARACTERCREATE-CLASSES",unpack(CLASS_ICON_TCOORDS[class])} end local tooltipData = {GetGUID(damageLine.guid), {L.BossWatcherDamageTooltipOverkill,ExRT.F.shortNumber(damageLine.overkill)}, {L.BossWatcherDamageTooltipBlocked,ExRT.F.shortNumber(damageLine.blocked)}, {L.BossWatcherDamageTooltipAbsorbed,ExRT.F.shortNumber(damageLine.absorbed)}, {L.BossWatcherDamageTooltipTotal,ExRT.F.shortNumber(damageLine.total)}, {" "," "}, {L.BossWatcherDamageTooltipFromCrit,format("%s (%.1f%%)",ExRT.F.shortNumber(damageLine.crit),max(damageLine.crit/max(1,damageLine.total)*100))}, } sort(damageLine.targets,DamageTab_Temp_SortingBy2Param) if #damageLine.targets > 0 then tooltipData[#tooltipData + 1] = {" "," "} tooltipData[#tooltipData + 1] = {isReverse and L.BossWatcherDamageTooltipSources or L.BossWatcherDamageTooltipTargets," "} end for j=1,min(5,#damageLine.targets) do tooltipData[#tooltipData + 1] = {SubUTF8String(GetGUID(damageLine.targets[j][1]),20)..GUIDtoText(" [%s]",damageLine.targets[j][1]),format("%s (%.1f%%)",ExRT.F.shortNumber(damageLine.targets[j][2]),min(damageLine.targets[j][2] / max(1,(DamageTab_Variables.ShowAll and damageLine.total or damageLine.eff))*100,100))} end local currDamage = DamageTab_Variables.ShowAll and damageLine.total or damageLine.eff local dps = currDamage/activeFightLength DamageTab_SetLine({ line = i+1, icon = icon, name = GetGUID(damageLine.guid)..GUIDtoText(" [%s]",damageLine.guid), num = currDamage, alpha = DamageTab_Variables.ShowAll and (damageLine.total - damageLine.eff), total = total, max = _max, dps = dps, class = class, sourceGUID = damageLine.guid, tooltip = tooltipData, check = BWInterfaceFrame.GraphFrame:IsShown(), checkState = i <= 3, }) reportData[1][#reportData[1]+1] = i..". "..GetGUID(damageLine.guid).." - "..ExRT.F.shortNumber(currDamage).."@1@ ("..floor(dps)..")@1#" end for i=#damage+2,#BWInterfaceFrame.tab.tabs[1].lines do BWInterfaceFrame.tab.tabs[1].lines[i]:Hide() end BWInterfaceFrame.tab.tabs[1].scroll:Height((#damage+1) * 20) DamageTab_Variables.graphCache = {graph,#CurrentFight.segments,damage,false} if BWInterfaceFrame.GraphFrame:IsShown() then DamageTab_ReloadGraph(graph,#CurrentFight.segments,damage,false) end end local function DamageTab_UpdateLinesSpells() local doEnemy = DamageTab_Variables.state_friendly DamageTab_UpdateDropDowns() DamageTab_UpdateChecks() local damage = {} local total = 0 local totalOver = 0 local graph = {[-1]={}} for destGUID,destData in pairs(CurrentFight.damage) do if ExRT.F.table_len(destVar) == 0 or destVar[destGUID] then for destReaction,destReactData in pairs(destData) do local isEnemy = false if GetUnitInfoByUnitFlagFix(destReaction,2) == 512 then isEnemy = true end if (isEnemy and doEnemy) or (not isEnemy and not doEnemy) then for sourceGUID,sourceData in pairs(destReactData) do local owner = ExRT.F.Pets:getOwnerGUID(sourceGUID,GetPetsDB()) if owner then sourceGUID = owner end if ExRT.F.table_len(sourceVar) == 0 or sourceVar[sourceGUID] then for spellID,spellSegments in pairs(sourceData) do local sourceDamage, destPos = DamageTab_UpdateLines_GetUnit(damage,graph,spellID,destGUID,"spell",owner and "pet") for segment,spellAmount in pairs(spellSegments) do if CurrentFight.segments[segment].e then sourceDamage.total = sourceDamage.total + spellAmount.amount + spellAmount.blocked + spellAmount.absorbed sourceDamage.eff = sourceDamage.eff + spellAmount.amount - spellAmount.overkill sourceDamage.count = sourceDamage.count + spellAmount.count sourceDamage.overkill = sourceDamage.overkill + spellAmount.overkill sourceDamage.blocked = sourceDamage.blocked + spellAmount.blocked sourceDamage.absorbed = sourceDamage.absorbed + spellAmount.absorbed sourceDamage.crit = sourceDamage.crit + spellAmount.crit sourceDamage.critcount = sourceDamage.critcount + spellAmount.critcount if sourceDamage.critmax < spellAmount.critmax then sourceDamage.critmax = spellAmount.critmax end sourceDamage.critover = sourceDamage.critover + spellAmount.critover if sourceDamage.hitmax < spellAmount.hitmax then sourceDamage.hitmax = spellAmount.hitmax end sourceDamage.parry = sourceDamage.parry + spellAmount.parry sourceDamage.dodge = sourceDamage.dodge + spellAmount.dodge sourceDamage.miss = sourceDamage.miss + spellAmount.miss total = total + spellAmount.amount - spellAmount.overkill totalOver = totalOver + spellAmount.overkill + spellAmount.blocked + spellAmount.absorbed local damgeCount = spellAmount.amount + (DamageTab_Variables.ShowAll and (spellAmount.blocked+spellAmount.absorbed) or -spellAmount.overkill) destPos[2] = destPos[2] + damgeCount if not graph[ spellID ][segment] then graph[ spellID ][segment] = 0 end if not graph[ -1 ][segment] then graph[ -1 ][segment] = 0 end graph[ spellID ][segment] = graph[ spellID ][segment] + damgeCount graph[ -1 ][segment] = graph[ -1 ][segment] + damgeCount end end end end end end end end end local totalIsFull = 1 total = max(total,1) if total == 1 and #damage == 0 then total = 0 totalIsFull = 0 end local _max = nil reportOptions[1] = L.BossWatcherReportDPS wipe(reportData[1]) reportData[1][1] = (DamageTab_GetGUIDsReport(sourceVar) or L.BossWatcherAllSources).." > "..(DamageTab_GetGUIDsReport(destVar,true) or L.BossWatcherAllTargets) local activeFightLength = GetFightLength() if DamageTab_Variables.ShowAll then total = total + totalOver sort(damage,function(a,b) return a.total>b.total end) _max = damage[1] and damage[1].total or 0 else sort(damage,function(a,b) return a.eff>b.eff end) _max = damage[1] and damage[1].eff or 0 end reportData[1][2] = L.BossWatcherReportTotal.." - "..ExRT.F.shortNumber(total).."@1@ ("..floor(total / activeFightLength)..")@1#" DamageTab_SetLine({ line = 1, name = L.BossWatcherReportTotal, num = total, total = total, max = total, alpha = DamageTab_Variables.ShowAll and totalOver, dps = total / activeFightLength, spellID = -1, check = BWInterfaceFrame.GraphFrame:IsShown(), checkState = true, }) for i=#damage,1,-1 do if damage[i].total == 0 then tremove(damage,i) end end local castsCount = SpellsPage_GetCastsNumber(ExRT.F.table_len(sourceVar) > 0 and sourceVar,ExRT.F.table_len(destVar) > 0 and destVar) for i=1,#damage do local damageLine = damage[i] local isPetAbility = damageLine.info == "pet" local spellID = damageLine.spell local isDoT = (not ExRT.isClassic or ExRT.isCata) and spellID < 0 if isDoT then spellID = -spellID end local spellName,_,spellIcon = GetSpellInfo(spellID) local defSpellName = spellName if isPetAbility then spellName = L.BossWatcherPetText..": "..spellName end if isDoT then spellName = spellName .. " ["..L.BossWatcherDoT.."]" end if damageLine.info and damageLine.info ~= "pet" then spellName = GetGUID(damageLine.info)..": "..spellName end local school = module.db.spellsSchool[ spellID ] or 0 local tooltipData = { {spellName,spellIcon}, {L.BossWatcherDamageTooltipCount,damageLine.count}, {L.BossWatcherDamageTooltipMaxHit,damageLine.hitmax}, {L.BossWatcherDamageTooltipMidHit,ExRT.F.Round((damageLine.eff-damageLine.crit+damageLine.critover)/max(damageLine.count-damageLine.critcount,1))}, {L.BossWatcherDamageTooltiCritCount,format("%d (%.1f%%)",damageLine.critcount,damageLine.critcount/damageLine.count*100)}, {L.BossWatcherDamageTooltiCritAmount,ExRT.F.shortNumber(damageLine.crit - damageLine.critover)}, {L.BossWatcherDamageTooltiMaxCrit,damageLine.critmax}, {L.BossWatcherDamageTooltiMidCrit,ExRT.F.Round((damageLine.crit - damageLine.critover)/max(damageLine.critcount,1))}, {L.BossWatcherDamageTooltipParry,format("%d (%.1f%%)",damageLine.parry,damageLine.parry/damageLine.count*100)}, {L.BossWatcherDamageTooltipDodge,format("%d (%.1f%%)",damageLine.dodge,damageLine.dodge/damageLine.count*100)}, {L.BossWatcherDamageTooltipMiss,format("%d (%.1f%%)",damageLine.miss,damageLine.miss/damageLine.count*100)}, {L.BossWatcherDamageTooltipOverkill,ExRT.F.shortNumber(damageLine.overkill)}, {L.BossWatcherDamageTooltipBlocked,ExRT.F.shortNumber(damageLine.blocked)}, {L.BossWatcherDamageTooltipAbsorbed,ExRT.F.shortNumber(damageLine.absorbed)}, {L.BossWatcherDamageTooltipTotal,ExRT.F.shortNumber(damageLine.total)}, {L.BossWatcherSchool,GetSchoolName(school)}, } local casts = castsCount[ spellID ] or castsCount[ defSpellName ] if casts then tinsert(tooltipData,2,{L.BossWatcherDamageTooltipCastsCount,casts}) tinsert(tooltipData,3,{L.BossWatcherPerCast,ExRT.F.shortNumber(damageLine.eff / casts)}) end sort(damageLine.targets,DamageTab_Temp_SortingBy2Param) if #damageLine.targets > 0 then tooltipData[#tooltipData + 1] = {" "," "} tooltipData[#tooltipData + 1] = {L.BossWatcherDamageTooltipTargets," "} end for j=1,min(5,#damageLine.targets) do tooltipData[#tooltipData + 1] = {SubUTF8String(GetGUID(damageLine.targets[j][1]),20)..GUIDtoText(" [%s]",damageLine.targets[j][1]),format("%s (%.1f%%)",ExRT.F.shortNumber(damageLine.targets[j][2]),min(damageLine.targets[j][2] / max(1,DamageTab_Variables.ShowAll and damageLine.total or damageLine.eff)*100,100))} end local currDamage = DamageTab_Variables.ShowAll and damageLine.total or damageLine.eff local dps = currDamage/activeFightLength DamageTab_SetLine({ line = i+1, icon = spellIcon, name = spellName, num = currDamage, alpha = DamageTab_Variables.ShowAll and (damageLine.total - damageLine.eff), total = total, max = _max, dps = dps, spellID = spellID, school = school, isDoT = isDoT, tooltip = tooltipData, check = BWInterfaceFrame.GraphFrame:IsShown(), checkState = i <= 3, }) reportData[1][#reportData[1]+1] = i..". "..(isPetAbility and L.BossWatcherPetText..": " or "")..(damageLine.info and damageLine.info ~= "pet" and GetGUID(damageLine.info)..": " or "")..GetSpellLink(spellID).." - "..ExRT.F.shortNumber(currDamage).."@1@ ("..floor(dps)..")@1#" end for i=#damage+2,#BWInterfaceFrame.tab.tabs[1].lines do BWInterfaceFrame.tab.tabs[1].lines[i]:Hide() end BWInterfaceFrame.tab.tabs[1].scroll:Height((#damage+1) * 20) DamageTab_Variables.graphCache = {graph,#CurrentFight.segments,damage,true} if BWInterfaceFrame.GraphFrame:IsShown() then DamageTab_ReloadGraph(graph,#CurrentFight.segments,damage,true) end end local function DamageTab_UpdateLinesSpellToTargets() local doEnemy = false DamageTab_UpdateDropDowns() DamageTab_UpdateChecks() local spellIDnow,spellIDnow_Name = nil,"" for spellID,_ in pairs(sourceVar) do spellIDnow = spellID end if spellIDnow then spellIDnow_Name = GetSpellInfo(spellIDnow) or "" BWInterfaceFrame.tab.tabs[1].sourceDropDown:SetText(spellIDnow_Name) else BWInterfaceFrame.tab.tabs[1].sourceDropDown:SetText(L.BossWatcherSelect) end local damage = {} local total = 0 local totalOver = 0 local totalCount = 0 local graph = {[-1]={}} for destGUID,destData in pairs(CurrentFight.damage) do for destReaction,destReactData in pairs(destData) do local isEnemy = GetUnitInfoByUnitFlagFix(destReaction,2) == 512 if (doEnemy and isEnemy) or (not doEnemy and not isEnemy) then for sourceGUID,sourceData in pairs(destReactData) do for spellID,spellSegments in pairs(sourceData) do if sourceVar[spellID] or sourceVar[-spellID] then local inDamagePos = ExRT.F.table_find(damage,destGUID,1) if not inDamagePos then inDamagePos = #damage + 1 damage[inDamagePos] = {destGUID,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,guid=destGUID,total=0,sources={}} end if not graph[ destGUID ] then graph[ destGUID ] = {} end for segment,spellAmount in pairs(spellSegments) do if CurrentFight.segments[segment].e then damage[inDamagePos].total = damage[inDamagePos].total + spellAmount.amount + spellAmount.blocked + spellAmount.absorbed damage[inDamagePos][2] = damage[inDamagePos][2] + spellAmount.amount - spellAmount.overkill --amount damage[inDamagePos][3] = damage[inDamagePos][3] + spellAmount.count --count damage[inDamagePos][4] = damage[inDamagePos][4] + spellAmount.overkill --overkill damage[inDamagePos][5] = damage[inDamagePos][5] + spellAmount.blocked --blocked damage[inDamagePos][6] = damage[inDamagePos][6] + spellAmount.absorbed --absorbed damage[inDamagePos][7] = damage[inDamagePos][7] + spellAmount.crit --crit damage[inDamagePos][8] = damage[inDamagePos][8] + spellAmount.critcount --crit count damage[inDamagePos][9] = max(damage[inDamagePos][9],spellAmount.critmax)--crit max damage[inDamagePos][13] = max(damage[inDamagePos][13],spellAmount.hitmax)--hit max damage[inDamagePos][14] = damage[inDamagePos][14] + spellAmount.parry --parry damage[inDamagePos][15] = damage[inDamagePos][15] + spellAmount.dodge --dodge damage[inDamagePos][16] = damage[inDamagePos][16] + spellAmount.miss --other miss total = total + spellAmount.amount - spellAmount.overkill totalOver = totalOver + spellAmount.overkill + spellAmount.blocked + spellAmount.absorbed totalCount = totalCount + spellAmount.count if not graph[ destGUID ][segment] then graph[ destGUID ][segment] = 0 end if not graph[ -1 ][segment] then graph[ -1 ][segment] = 0 end local damgeCount = spellAmount.count graph[ destGUID ][segment] = graph[ destGUID ][segment] + damgeCount graph[ -1 ][segment] = graph[ -1 ][segment] + damgeCount local source for i=1,#damage[inDamagePos].sources do if damage[inDamagePos].sources[i][1] == sourceGUID then source = damage[inDamagePos].sources[i] break end end if not source then source = {sourceGUID,0} damage[inDamagePos].sources[ #damage[inDamagePos].sources+1 ] = source end source[2] = source[2] + spellAmount.amount + (DamageTab_Variables.ShowAll and (spellAmount.blocked + spellAmount.absorbed) or -spellAmount.overkill) end end end end end end end end local totalIsFull = 1 total = max(total,1) if total == 1 and #damage == 0 then total = 0 totalIsFull = 0 end local _max = nil reportOptions[1] = L.BossWatcherReportCount wipe(reportData[1]) reportData[1][1] = GetSpellLink(spellIDnow).." > "..L.BossWatcherAllTargets if DamageTab_Variables.ShowAll then total = total + totalOver sort(damage,function(a,b) return (a[2]+a[4]+a[5]+a[6])>(b[2]+b[4]+b[5]+b[6]) end) _max = damage[1] and (damage[1][2]+damage[1][4]+damage[1][5]+damage[1][6]) or 0 else sort(damage,function(a,b) return a[2]>b[2] end) _max = damage[1] and damage[1][2] or 0 end reportData[1][2] = L.BossWatcherReportTotal.." - "..ExRT.F.shortNumber(total).."@1@ ("..floor(totalCount)..")@1#" DamageTab_SetLine({ line = 1, name = L.BossWatcherReportTotal, num = total, total = total, max = total, alpha = DamageTab_Variables.ShowAll and totalOver, dps = totalCount, spellID = -1, check = BWInterfaceFrame.GraphFrame:IsShown(), checkState = true, }) for i=#damage,1,-1 do if damage[i].total == 0 then tremove(damage,i) end end for i=1,#damage do local class = nil if damage[i][1] and damage[i][1] ~= "" then class = select(2,GetPlayerInfoByGUID(damage[i][1])) end local icon = "" if class and CLASS_ICON_TCOORDS[class] then icon = {"Interface\\GLUES\\CHARACTERCREATE\\UI-CHARACTERCREATE-CLASSES",unpack(CLASS_ICON_TCOORDS[class])} end local tooltipData = {GetGUID(damage[i][1]), {L.BossWatcherDamageTooltipCount,damage[i][3]}, {L.BossWatcherDamageTooltipMaxHit,damage[i][13]}, {L.BossWatcherDamageTooltipMidHit,ExRT.F.Round((damage[i][2]-damage[i][7]+damage[i][4])/max(damage[i][3]-damage[i][8],1))}, {L.BossWatcherDamageTooltiCritCount,format("%d (%.1f%%)",damage[i][8],damage[i][8]/damage[i][3]*100)}, {L.BossWatcherDamageTooltiCritAmount,ExRT.F.shortNumber(damage[i][7])}, {L.BossWatcherDamageTooltiMaxCrit,damage[i][9]}, {L.BossWatcherDamageTooltiMidCrit,ExRT.F.Round(damage[i][7]/max(damage[i][8],1))}, {L.BossWatcherDamageTooltipParry,format("%d (%.1f%%)",damage[i][14],damage[i][14]/damage[i][3]*100)}, {L.BossWatcherDamageTooltipDodge,format("%d (%.1f%%)",damage[i][15],damage[i][15]/damage[i][3]*100)}, {L.BossWatcherDamageTooltipMiss,format("%d (%.1f%%)",damage[i][16],damage[i][16]/damage[i][3]*100)}, {L.BossWatcherDamageTooltipOverkill,ExRT.F.shortNumber(damage[i][4])}, {L.BossWatcherDamageTooltipBlocked,ExRT.F.shortNumber(damage[i][5])}, {L.BossWatcherDamageTooltipAbsorbed,ExRT.F.shortNumber(damage[i][6])}, {L.BossWatcherDamageTooltipTotal,ExRT.F.shortNumber(damage[i][4]+damage[i][5]+damage[i][6]+damage[i][2])}, } sort(damage[i].sources,DamageTab_Temp_SortingBy2Param) if #damage[i].sources > 0 then tooltipData[#tooltipData + 1] = {" "," "} tooltipData[#tooltipData + 1] = {L.BossWatcherDamageTooltipSources," "} end for j=1,min(5,#damage[i].sources) do tooltipData[#tooltipData + 1] = {SubUTF8String(GetGUID(damage[i].sources[j][1]),20)..GUIDtoText(" [%s]",damage[i].sources[j][1]),format("%s (%.1f%%)",ExRT.F.shortNumber(damage[i].sources[j][2]),min(damage[i].sources[j][2] / max(1,(DamageTab_Variables.ShowAll and damage[i].total or damage[i][2]))*100,100))} end local currDamage = damage[i][2]+(DamageTab_Variables.ShowAll and (damage[i][4]+damage[i][5]+damage[i][6]) or 0) DamageTab_SetLine({ line = i+1, icon = icon, name = GetGUID(damage[i][1])..GUIDtoText(" [%s]",damage[i][1]), num = currDamage, alpha = DamageTab_Variables.ShowAll and (damage[i][4]+damage[i][5]+damage[i][6]), total = total, max = _max, dps = damage[i][3], class = class, sourceGUID = damage[i][1], doEnemy = doEnemy, tooltip = tooltipData, check = BWInterfaceFrame.GraphFrame:IsShown(), checkState = i <= 3, }) reportData[1][#reportData[1]+1] = i..". "..GetGUID(damage[i][1]).." - "..ExRT.F.shortNumber(currDamage).."@1@ ("..(damage[i][3]-damage[i][11])..")@1#" end for i=#damage+2,#BWInterfaceFrame.tab.tabs[1].lines do BWInterfaceFrame.tab.tabs[1].lines[i]:Hide() end BWInterfaceFrame.tab.tabs[1].scroll:Height((#damage+1) * 20) DamageTab_Variables.graphCache = {graph,#CurrentFight.segments,damage,false} if BWInterfaceFrame.GraphFrame:IsShown() then DamageTab_ReloadGraph(graph,#CurrentFight.segments,damage,false) end end local function DamageTab_ShowDamageToTarget(GUID) local button = BWInterfaceFrame.tab.tabs[1].button local func = button:GetScript("OnClick") DamageTab_Variables.state_friendly = true DamageTab_Variables.state_spells = false DamageTab_Variables.state_byTarget = false DamageTab_Variables.state_spellsTarget = false func(button) DamageTab_UpdatePage(true,function() destVar[GUID] = true end) end local function DamageTab_DPS_SelectDropDownSource(self,arg) ELib:DropDownClose() wipe(sourceVar) if arg then sourceVar[arg] = true if IsShiftKeyDown() then local name = CurrentFight.guids[arg] if name then for GUID,GUIDname in pairs(CurrentFight.guids) do if GUIDname == name then sourceVar[GUID] = true end end end elseif IsControlKeyDown() then local name = CurrentFight.guids[arg] if name then sourceVar[arg] = nil for GUID,GUIDname in pairs(CurrentFight.guids) do if GUIDname ~= name then sourceVar[GUID] = true end end end end end DamageTab_UpdatePage() end local function DamageTab_DPS_SelectDropDownDest(self,arg) ELib:DropDownClose() wipe(destVar) if arg then destVar[arg] = true if IsShiftKeyDown() then local name = CurrentFight.guids[arg] if name then for GUID,GUIDname in pairs(CurrentFight.guids) do if GUIDname == name then destVar[GUID] = true end end end elseif IsControlKeyDown() then local name = CurrentFight.guids[arg] if name then destVar[arg] = nil for GUID,GUIDname in pairs(CurrentFight.guids) do if GUIDname ~= name then destVar[GUID] = true end end end end end DamageTab_UpdatePage() end local function DamageTab_DPS_SelectDropDownSource_Spell(self,spellID) wipe(sourceVar) sourceVar[spellID] = true DamageTab_UpdatePage() ELib:DropDownClose() end local function DamageTab_DPS_CheckDropDownSource(self,checked) if checked then sourceVar[self.arg1] = true else sourceVar[self.arg1] = nil end DamageTab_UpdatePage() end local function DamageTab_DPS_CheckDropDownDest(self,checked) if checked then destVar[self.arg1] = true else destVar[self.arg1] = nil end DamageTab_UpdatePage() end local function DamageTab_HideArrow() BWInterfaceFrame.timeLineFrame.timeLine.arrow:Hide() end local function DamageTab_ShowArrow(self,pos) if pos then BWInterfaceFrame.timeLineFrame.timeLine.arrow:SetPoint("TOPLEFT",BWInterfaceFrame.timeLineFrame.timeLine,"TOPLEFT",BWInterfaceFrame.timeLineFrame.width*pos,0) BWInterfaceFrame.timeLineFrame.timeLine.arrow:Show() end end local function DamageTab_HoverDropDownSpell(self,spellID) if not spellID then return end ELib.Tooltip.Link(self,"spell:"..spellID) end local function DamageTab_DPS(disableUpdateVars) local doEnemy = DamageTab_Variables.state_friendly local isBySpellDamage = DamageTab_Variables.state_spellsTarget if isBySpellDamage then doEnemy = false end local reaction = 512 if not doEnemy then reaction = 256 end if not CurrentFight.damage then --First load fix return end local sourceTable = {} local destTable = {} for destGUID,destData in pairs(CurrentFight.damage) do for destReaction,destReactData in pairs(destData) do if GetUnitInfoByUnitFlagFix(destReaction,2) == reaction then for sourceGUID,sourceData in pairs(destReactData) do local owner = ExRT.F.Pets:getOwnerGUID(sourceGUID,GetPetsDB()) if owner then sourceGUID = owner end for spellID,spellSegments in pairs(sourceData) do for segment,spellAmount in pairs(spellSegments) do if CurrentFight.segments[segment].e then if not ExRT.F.table_find(destTable,destGUID,1) then destTable[#destTable + 1] = {destGUID,CurrentFight.damage_seen[destGUID] or 0} end if not isBySpellDamage and not ExRT.F.table_find(sourceTable,sourceGUID,1) then sourceTable[#sourceTable + 1] = {sourceGUID,GetGUID(sourceGUID)} elseif isBySpellDamage then if spellID < 0 then spellID = -spellID end if not ExRT.F.table_find(sourceTable,spellID,1) then local spellName,_,spellIcon = GetSpellInfo(spellID) sourceTable[#sourceTable + 1] = {spellID,spellName,spellIcon} end end end end end end end end end sort(sourceTable,function(a,b) return a[2] 0 then local width = 300*(overall or 1) local normal_width = width * (1 - overall_black) line.overall:SetWidth(max(normal_width,1)) line.overall_black:SetWidth(max(width-normal_width,1)) line.overall_black:Show() if normal_width == 0 then line.overall:Hide() line.overall_black:SetPoint("TOPLEFT",325,-2) else line.overall:Show() line.overall_black:ClearAllPoints() line.overall_black:SetPoint("LEFT",line.overall,"RIGHT",0,0) end else line.overall:Show() line.overall_black:Hide() line.overall:SetWidth(max(300*(overall or 1),1)) end line.total:SetText(total and ExRT.F.shortNumber(total) or "") do dps = dps or 0 line.dps:SetFormattedText("%s.%s",FormatLargeNumber(floor(dps)),format("%.2f",dps % 1):gsub("^.-%.","")) end line.overall:SetGradient("HORIZONTAL",CreateColor(0,0,0,0), CreateColor(0,0,0,0)) line.overall_black:SetGradient("HORIZONTAL",CreateColor(0,0,0,0), CreateColor(0,0,0,0)) if class then local classColorArray = type(CUSTOM_CLASS_COLORS)=="table" and CUSTOM_CLASS_COLORS[class] or RAID_CLASS_COLORS[class] if classColorArray then line.overall:SetVertexColor(classColorArray.r,classColorArray.g,classColorArray.b, 1) line.overall_black:SetVertexColor(classColorArray.r,classColorArray.g,classColorArray.b, 1) else line.overall:SetVertexColor(0.8,0.8,0.8, 1) line.overall_black:SetVertexColor(0.8,0.8,0.8, 1) end else line.overall:SetVertexColor(0.8,0.8,0.8, 1) line.overall_black:SetVertexColor(0.8,0.8,0.8, 1) end if school then SetSchoolColorsToLine(line.overall,school) SetSchoolColorsToLine(line.overall_black,school) end if showCheck then line.leftSide:SetPoint("LEFT",25,0) line.check:SetChecked(checkState) line.check:Show() else line.leftSide:SetPoint("LEFT",5,0) line.check:Hide() end line.sourceGUID = sourceGUID line.spellID = spellID line.spellLink = spellLink line.tooltip = tooltip line.isDoT = isDoT line:Show() end tab:SetScript("OnShow",function (self) BWInterfaceFrame.timeLineFrame:ClearAllPoints() BWInterfaceFrame.timeLineFrame:SetPoint("TOP",self,"TOP",0,-10) BWInterfaceFrame.timeLineFrame:Show() BWInterfaceFrame.report:Show() BWInterfaceFrame.GraphFrame:SetPoint("TOP",0,-105) if BWInterfaceFrame.tab.tabs[1].showGraphChk:GetChecked() then BWInterfaceFrame.GraphFrame:Show() end if BWInterfaceFrame.nowFightID ~= self.lastFightID then local currFightIDShort = GetFightID(CurrentFight,true) local disableUpdateVars = nil if currFightIDShort == self.lastFightShortID then disableUpdateVars = true end self.lastFightShortID = currFightIDShort DamageTab_Variables.graphCache = nil Graph_AutoUpdateStep() DamageTab_UpdatePage(true,nil,disableUpdateVars) self.lastFightID = BWInterfaceFrame.nowFightID elseif DamageTab_Variables.graphCache then ExRT.F.dprint("Damage Page : Update Graph",GetTime()) DamageTab_ReloadGraph(unpack(DamageTab_Variables.graphCache)) end end) tab:SetScript("OnHide",function (self) BWInterfaceFrame.timeLineFrame:Hide() BWInterfaceFrame.report:Hide() BWInterfaceFrame.GraphFrame:Hide() end) ---- Auras Tab tab = BWInterfaceFrame.tab.tabs[3] local aurasTab = tab local AurasTab_Variables = { FilterSource = 0x0111, FilterSourceGUID = {}, FilterDest = 0x0110, FilterDestGUID = {}, NameWidth = 188, WorkWidth = 650, TotalLines = 28, IsFriendly = true, buffsFilters = { [1] = {[-1]=L.BossWatcherFilterOnlyBuffs,}, --> Only buffs [2] = {[-1]=L.BossWatcherFilterOnlyDebuffs,}, --> Only debuffs [3] = {[-1]=L.BossWatcherFilterBySpellID,}, --> By spellID [4] = {[-1]=L.BossWatcherFilterBySpellName,}, --> By spellName [5] = { [-1]=L.BossWatcherFilterTaunts, [-2]={62124,17735,97827,56222,51399,49560,6795,355,115546,116189,185245}, }, [6] = { [-1]=L.BossWatcherFilterStun, [-2]={853,105593,91797,408,119381,89766,118345,46968,107570,5211,44572,119392,122057,113656,108200,108194,30283,118905,20549,119072,115750}, }, [7] = { [-1]=L.BossWatcherFilterPersonal, [-2]={148467,31224,110788,55694,47585,31850,115610,122783,642,5277,118038,104773,115176,48707,1966,61336,120954,871,106922,30823,6229,22812,498}, }, [8] = { [-1]=L.BossWatcherFilterRaidSaves, [-2]={145629,114192,114198,81782,108281,97463,31821,15286,115213,44203,64843,76577}, }, [9] = { [-1]=L.BossWatcherFilterPotions, [-2]={279152,279153,279151,269853,250878,252753,251316,251231,300714,298225,298317,300741,298146,298152,298153,298154,298155, 307159,307162,307163,307164,307160,307161,307497,307494,307496,307495,322302,344314,307199,342890,307196,307195}, }, [10] = { [-1]="DPS CD", [-2]={31884,162264,26297,266091,1719,47568,193530,80353,190319,113860,188592,194223,295840,296962}, }, }, buffsFilterStatus = { [1] = true, }, } AurasTab_Variables.TotalWidth = AurasTab_Variables.NameWidth + AurasTab_Variables.WorkWidth --[[ 0x0001 - hostile 0x0010 - friendly 0x0100 - pets & guards 0x1000 - by GUID ]] for i=5,#AurasTab_Variables.buffsFilters do for _,sID in ipairs(AurasTab_Variables.buffsFilters[i][-2]) do AurasTab_Variables.buffsFilters[i][sID] = true end end local UpdateBuffsPage,UpdateBuffPageDB tab.DecorationLine = ELib:DecorationLine(tab,true):Point("TOPLEFT",tab,"TOPLEFT",3,-9):Point("RIGHT",tab,-3,0):Size(0,20) tab.headerTab = ELib:Tabs(tab,0, L.BossWatcherBuff, L.BossWatcherDebuff ):Size(850,555):Point("TOP",0,-29):SetTo(1) tab.headerTab:SetBackdropBorderColor(0,0,0,0) tab.headerTab:SetBackdropColor(0,0,0,0) tab.headerTab.tabs[1].button.additionalFunc = function() AurasTab_Variables.buffsFilterStatus[1] = true AurasTab_Variables.buffsFilterStatus[2] = false UpdateBuffPageDB() UpdateBuffsPage() end tab.headerTab.tabs[2].button.additionalFunc = function() AurasTab_Variables.buffsFilterStatus[1] = false AurasTab_Variables.buffsFilterStatus[2] = true UpdateBuffPageDB() UpdateBuffsPage() end tab.timeLine = {} do local scheduledUpdate tab.nameFilterEditBox = ELib:Edit(tab.DecorationLine:GetParent()):Point("RIGHT",tab.DecorationLine,"RIGHT",-50,0):Size(150,16):AddSearchIcon():OnChange(function (self,isUser) if not isUser then return end local text = self:GetText() for key,val in pairs(AurasTab_Variables.buffsFilters[4]) do if key ~= -1 then AurasTab_Variables.buffsFilters[4][key] = nil end end for key,val in pairs(AurasTab_Variables.buffsFilters[3]) do if key ~= -1 then AurasTab_Variables.buffsFilters[3][key] = nil end end local lines = {strsplit(",", text)} for i=1,#lines do if lines[i] ~= "" then local s = lines[i] if tonumber(s) then AurasTab_Variables.buffsFilters[3][ tonumber(s) ] = true else AurasTab_Variables.buffsFilters[4][ strlower(s) ] = true end end end if (ExRT.F.table_len(AurasTab_Variables.buffsFilters[4]) + ExRT.F.table_len(AurasTab_Variables.buffsFilters[3])) > 2 then AurasTab_Variables.buffsFilterStatus[4] = true if not scheduledUpdate then scheduledUpdate = C_Timer.NewTimer(1,function() scheduledUpdate = nil UpdateBuffPageDB() UpdateBuffsPage() end) end else AurasTab_Variables.buffsFilterStatus[4] = false if scheduledUpdate then scheduledUpdate:Cancel() end scheduledUpdate = nil UpdateBuffPageDB() UpdateBuffsPage() return end end) end tab.chkFriendly = ELib:Radio(tab,L.BossWatcherFriendly,true):Point(15,-35):AddButton():OnClick(function(self) aurasTab.chkEnemy:SetChecked(false) self:SetChecked(true) AurasTab_Variables.IsFriendly = true AurasTab_Variables.FilterDest = 0x0110 UpdateBuffPageDB() UpdateBuffsPage() end) tab.chkEnemy = ELib:Radio(tab,L.BossWatcherHostile):Point(15,-50):AddButton():OnClick(function(self) aurasTab.chkFriendly:SetChecked(false) self:SetChecked(true) AurasTab_Variables.IsFriendly = false AurasTab_Variables.FilterDest = 0x0101 UpdateBuffPageDB() UpdateBuffsPage() end) local function AurasTab_ActivateAnyPage(tab,check) local f1,f2 = UpdateBuffsPage,UpdateBuffPageDB UpdateBuffsPage,UpdateBuffPageDB = ExRT.NULLfunc, ExRT.NULLfunc aurasTab.headerTab.tabs[tab].button:Click() aurasTab[check == 1 and "chkFriendly" or "chkEnemy"]:Click() UpdateBuffsPage,UpdateBuffPageDB = f1,f2 end tab.sourceDropDown = ELib:DropDown(tab,250,20):Size(190):Point(240,-38):SetText(L.BossWatcherAll) tab.sourceText = ELib:Text(tab,L.BossWatcherSource..":",12):Size(100,20):Point("TOPRIGHT",tab.sourceDropDown,"TOPLEFT",-6,0):Right():Color():Shadow() tab.targetDropDown = ELib:DropDown(tab,250,20):Size(190):Point(520,-38):SetText(L.BossWatcherAll) tab.targetText = ELib:Text(tab,L.BossWatcherTarget..":",12):Size(100,20):Point("TOPRIGHT",tab.targetDropDown,"TOPLEFT",-6,0):Right():Color():Shadow() tab.filterDropDown = ELib:DropDown(tab,150,#AurasTab_Variables.buffsFilters-3):Size(125):Point(725,-38):SetText(L.BossWatcherBuffsAndDebuffsFilterFilter) tab.filterDropDown.List[1] = {text = RESET,func = function() ELib:DropDownClose() for i=5,#AurasTab_Variables.buffsFilters do AurasTab_Variables.buffsFilterStatus[i] = false end UpdateBuffPageDB(true) UpdateBuffsPage() end,padding = 16} do local Activate = { [5] = {2,2}, [7] = {1,1}, [8] = {1,1}, [9] = {1,1}, } local function OnClick(_,arg) ELib:DropDownClose() for i=5,#AurasTab_Variables.buffsFilters do AurasTab_Variables.buffsFilterStatus[i] = nil end AurasTab_Variables.buffsFilterStatus[arg] = not AurasTab_Variables.buffsFilterStatus[arg] if Activate[arg] then AurasTab_ActivateAnyPage(unpack(Activate[arg])) end UpdateBuffPageDB(true) UpdateBuffsPage() end local function OnEnter(self,i) local sList = AurasTab_Variables.buffsFilters[i][-2] if not sList then sList = {} for sid,_ in pairs(AurasTab_Variables.buffsFilters[i]) do if sid > 0 then sList[#sList + 1] = sid end end end if #sList == 0 then return end local sList2 = {} if #sList <= 35 then for j=1,#sList do local sID,_,sT=GetSpellInfo(sList[j]) if sID then sList2[#sList2 + 1] = "|T"..sT..":0|t |cffffffff"..sID.."|r" end end else local count = 1 for j=1,#sList do local sID,_,sT=GetSpellInfo(sList[j]) if sID then if not sList2[count] then sList2[count] = {"|T"..sT..":0|t |cffffffff"..sID.."|r"} elseif not sList2[count].right then sList2[count].right = "|cffffffff"..sID.."|r |T"..sT..":0|t" count = count + 1 end end end end ELib.Tooltip.Show(self,"ANCHOR_LEFT",L.BossWatcherFilterTooltip..":",unpack(sList2)) end for i=5,#AurasTab_Variables.buffsFilters do tab.filterDropDown.List[i-3] = { text = AurasTab_Variables.buffsFilters[i][-1], arg1 = i, func = OnClick, checkFunc = function(self,checked) AurasTab_Variables.buffsFilterStatus[i] = checked UpdateBuffPageDB(true) UpdateBuffsPage() end, checkable = true, hoverFunc = OnEnter, leaveFunc = ELib.Tooltip.Hide, hoverArg = i, } end end function tab.filterDropDown.additionalToggle(self) for i=2,#self.List do self.List[i].checkState = AurasTab_Variables.buffsFilterStatus[3+i] end end function tab.sourceDropDown.additionalToggle(self) for i=2,#self.List do self.List[i].checkState = AurasTab_Variables.FilterSourceGUID[self.List[i].arg1] end end function tab.targetDropDown.additionalToggle(self) for i=2,#self.List do self.List[i].checkState = AurasTab_Variables.FilterDestGUID[self.List[i].arg1] end end local function AurasTab_UpdateDropDownText(dropDown,arr) local count = ExRT.F.table_len(arr) if count == 0 then dropDown:SetText(L.BossWatcherAll) elseif count == 1 then local GUID = nil for g,_ in pairs(arr) do GUID = g end local name = GetGUID(GUID) local flags = CurrentFight.reaction[GUID] local isPlayer = flags and ExRT.F.GetUnitInfoByUnitFlag(flags,1) == 1024 local isNPC = flags and ExRT.F.GetUnitInfoByUnitFlag(flags,2) == 512 if isPlayer then name = "|c"..ExRT.F.classColorByGUID(GUID)..name elseif isNPC then name = name .. GUIDtoText(" [%s]",GUID) end dropDown:SetText(name) else dropDown:SetText(L.BossWatcherSeveral) end end local function AurasTab_UpdateDropDownsTexts() AurasTab_UpdateDropDownText(aurasTab.sourceDropDown,AurasTab_Variables.FilterSourceGUID) AurasTab_UpdateDropDownText(aurasTab.targetDropDown,AurasTab_Variables.FilterDestGUID) local anyFilter = nil for i=5,#AurasTab_Variables.buffsFilters do if AurasTab_Variables.buffsFilterStatus[i] then anyFilter = true break end end aurasTab.filterDropDown:SetText((anyFilter and "|cff00ff00" or "")..L.BossWatcherBuffsAndDebuffsFilterFilter) end local function AurasTab_SelectDropDownSource(self,arg) ELib:DropDownClose() wipe(AurasTab_Variables.FilterSourceGUID) if not arg then AurasTab_Variables.FilterSource = 0x0111 elseif IsShiftKeyDown() then AurasTab_Variables.FilterSource = 0x1000 AurasTab_Variables.FilterSourceGUID[arg] = true local name = CurrentFight.guids[arg] if name then for GUID,GUIDname in pairs(CurrentFight.guids) do if GUIDname == name then AurasTab_Variables.FilterSourceGUID[arg] = true end end end else AurasTab_Variables.FilterSource = 0x1000 AurasTab_Variables.FilterSourceGUID[arg] = true end UpdateBuffPageDB(true) UpdateBuffsPage() end local function AurasTab_SelectDropDownDest(self,arg) ELib:DropDownClose() wipe(AurasTab_Variables.FilterDestGUID) if not arg then if AurasTab_Variables.IsFriendly then AurasTab_Variables.FilterDest = 0x0110 else AurasTab_Variables.FilterDest = 0x0101 end elseif IsShiftKeyDown() then AurasTab_Variables.FilterDest = 0x1000 AurasTab_Variables.FilterDestGUID[arg] = true local name = CurrentFight.guids[arg] if name then for GUID,GUIDname in pairs(CurrentFight.guids) do if GUIDname == name then AurasTab_Variables.FilterDestGUID[arg] = true end end end else AurasTab_Variables.FilterDest = 0x1000 AurasTab_Variables.FilterDestGUID[arg] = true end UpdateBuffPageDB(true) UpdateBuffsPage() end local function AurasTab_CheckDropDownSource(self,checked) if checked then AurasTab_Variables.FilterSource = 0x1000 AurasTab_Variables.FilterSourceGUID[self.arg1] = true else AurasTab_Variables.FilterSourceGUID[self.arg1] = nil if ExRT.F.table_len(AurasTab_Variables.FilterSourceGUID) == 0 then AurasTab_Variables.FilterSource = 0x0111 end end UpdateBuffPageDB(true) UpdateBuffsPage() end local function AurasTab_CheckDropDownDest(self,checked) if checked then AurasTab_Variables.FilterDest = 0x1000 AurasTab_Variables.FilterDestGUID[self.arg1] = true else AurasTab_Variables.FilterDestGUID[self.arg1] = nil if ExRT.F.table_len(AurasTab_Variables.FilterDestGUID) == 0 then if AurasTab_Variables.IsFriendly then AurasTab_Variables.FilterDest = 0x0110 else AurasTab_Variables.FilterDest = 0x0101 end end end UpdateBuffPageDB(true) UpdateBuffsPage() end for i=1,11 do local line = CreateFrame("Frame",nil,tab) tab.timeLine[i] = line line:SetPoint("TOPLEFT",AurasTab_Variables.NameWidth+(i-1)*(AurasTab_Variables.WorkWidth/10)-1,-72) line:SetSize(2,AurasTab_Variables.TotalLines * 18 + 14) line.texture = line:CreateTexture(nil, "BACKGROUND") line.texture:SetColorTexture(1, 1, 1, 0.3) line.texture:SetAllPoints() line.timeText = ELib:Text(line,"",11):Size(200,12):Point("TOPRIGHT",line,"TOPLEFT",-1,-1):Right():Top():Color() end tab.redDeathLine = {} local function CreateRedDeathLine(i) if not aurasTab.redDeathLine[i] then local line = aurasTab:CreateTexture(nil, "BACKGROUND",0,-4) aurasTab.redDeathLine[i] = line line:SetColorTexture(1, 0.3, 0.3, 1) line:SetSize(2,AurasTab_Variables.TotalLines * 18 + 14) line:Hide() end end tab.linesRightClickMenu = { { text = "Spell", isTitle = true, notCheckable = true, notClickable = true }, { text = L.BossWatcherSendToChat, func = function() if aurasTab.linesRightClickMenuData then local chat_type = ExRT.F.chatType(true) SendChatMessage(aurasTab.linesRightClickMenuData[1],chat_type) for i=2,#aurasTab.linesRightClickMenuData do SendChatMessage(ExRT.F.clearTextTag(aurasTab.linesRightClickMenuData[i]),chat_type) end end CloseDropDownMenus() end, notCheckable = true }, { text = L.BossWatcherOnlySegmentsWithAura, func = function() CloseDropDownMenus() local buffData = aurasTab.linesRightClickLineData if not buffData then return end local table1 = {} for i=1,#buffData[5] do table1[#table1+1] = buffData[5][i][3] table1[#table1+1] = buffData[5][i][4] end local len = #table1 for i=1,len,2 do local time_start,time_end = table1[i],table1[i+1] if time_start then for j=i+2,len,2 do local new_start,new_end = table1[j],table1[j+1] if new_start then if new_start <= time_start and new_end > time_start then time_start = new_start time_end = max(time_end,new_end) table1[j] = nil table1[j+1] = nil elseif new_start > time_start and new_start <= time_end then time_end = max(time_end,new_end) table1[j] = nil table1[j+1] = nil end end end table1[i] = time_start table1[i+1] = time_end end end UpdateSegments(-1,-1,false,true) for i=1,len,2 do if table1[i] then UpdateSegmentsTime(table1[i]+CurrentFight.encounterStart,table1[i+1]+CurrentFight.encounterStart,true,true) end end UpdateSegments(-1,-1,true,false) end, notCheckable = true }, { text = L.BossWatcherAddToGraph, func = function() CloseDropDownMenus() local buffData = aurasTab.linesRightClickLineData if not buffData then return end local table1 = {} for i=1,#buffData[5] do table1[#table1+1] = buffData[5][i][3] table1[#table1+1] = buffData[5][i][4] end local len = #table1 for i=1,len,2 do local time_start,time_end = table1[i],table1[i+1] if time_start then for j=i+2,len,2 do local new_start,new_end = table1[j],table1[j+1] if new_start then if new_start <= time_start and new_end > time_start then time_start = new_start time_end = max(time_end,new_end) table1[j] = nil table1[j+1] = nil elseif new_start > time_start and new_start <= time_end then time_end = max(time_end,new_end) table1[j] = nil table1[j+1] = nil end end end table1[i] = time_start table1[i+1] = time_end end end local table2 = {} for i=1,len,2 do if table1[i] then table2[#table2 + 1] = table1[i] table2[#table2 + 1] = table1[i + 1] end end BWInterfaceFrame.GraphFrame.G.highlight = table2 end, notCheckable = true }, { text = L.BossWatcherAurasMoreInfoText, func = function() if aurasTab.linesRightClickMoreInfoData then aurasTab.linesRightClickMoreInfo:Update() aurasTab.linesRightClickMoreInfo:ShowClick() end CloseDropDownMenus() end, notCheckable = true }, { text = L.minimapmenuclose, func = function() CloseDropDownMenus() end, notCheckable = true }, } tab.linesRightClickMenuDropDown = CreateFrame("Frame", BWInterfaceFrame_Name.."AurasTab".."LinesRightClickMenuDropDown", nil, "UIDropDownMenuTemplate") tab.linesRightClickMoreInfo = ELib:Popup(L.BossWatcherAurasMoreInfoText):Size(300,375) tab.linesRightClickMoreInfo.ScrollFrame = ELib:ScrollFrame(tab.linesRightClickMoreInfo):Size(285,320):Point("TOP",0,-25):Height(400) --tab.linesRightClickMoreInfo.ScrollFrame.backdrop:Hide() tab.linesRightClickMoreInfo.lines = {} tab.linesRightClickMoreInfo.anchor = "TOPRIGHT" tab.linesRightClickMoreInfo.reportButton = ELib:Button(tab.linesRightClickMoreInfo,L.BossWatcherCreateReport):Size(292,20):Point("BOTTOM",0,5):Tooltip(L.BossWatcherCreateReportTooltip):OnClick(function (self) ExRT.F:ToChatWindow(aurasTab.linesRightClickMoreInfo.report) aurasTab.linesRightClickMoreInfo:Hide() end) do local function LineOnEnter(self) if self.name:IsTruncated() then GameTooltip:SetOwner(self,"ANCHOR_LEFT") GameTooltip:SetText(self.name:GetText()) GameTooltip:Show() end end local function SetLine(i,name,count) if not aurasTab.linesRightClickMoreInfo.lines[i] then local line = CreateFrame("Button",nil,aurasTab.linesRightClickMoreInfo.ScrollFrame.C) aurasTab.linesRightClickMoreInfo.lines[i] = line line:SetSize(270,20) line:SetPoint("TOPLEFT",0,-(i-1)*20) line.name = ELib:Text(line,"name",11):Size(210,20):Point(10,0):Color():Shadow() line.count = ELib:Text(line,"name",12):Size(40,20):Point(220,0):Center():Color():Shadow() line.back = line:CreateTexture(nil, "BACKGROUND") line.back:SetAllPoints() if i%2==0 then line.back:SetColorTexture(0.3, 0.3, 0.3, 0.1) end line:SetScript("OnEnter",LineOnEnter) line:SetScript("OnLeave",GameTooltip_Hide) end aurasTab.linesRightClickMoreInfo.lines[i].name:SetText(i..". "..name) aurasTab.linesRightClickMoreInfo.lines[i].count:SetText(count) aurasTab.linesRightClickMoreInfo.lines[i]:Show() end tab.linesRightClickMoreInfo.Update = function(self) local spellID = aurasTab.linesRightClickMoreInfoData self.title:SetText(GetSpellInfo(spellID) or "?") local data = {} for i,sourceData in ipairs(CurrentFight.auras) do if sourceData[6] == spellID and (sourceData[8] == 1 or sourceData[8] == 3) and (not aurasTab.filterS or (aurasTab.filterS == 1 and sourceData[4]) or (aurasTab.filterS == 2 and not sourceData[4]) or aurasTab.filterS == sourceData[2]) then local inPos = ExRT.F.table_find(data,sourceData[3],1) if not inPos then inPos = #data + 1 data[inPos] = {sourceData[3],0,0} end data[inPos][2] = data[inPos][2] + 1 end end for destGUID,destData in pairs(CurrentFight.damage) do for destReaction,destReactData in pairs(destData) do for sourceGUID,sourceData in pairs(destReactData) do if not aurasTab.filterS or aurasTab.filterS == sourceGUID then if sourceData[spellID] then for segment,spellAmount in pairs(sourceData[spellID]) do local missed = spellAmount.parry + spellAmount.dodge + spellAmount.miss if missed > 0 then local inPos = ExRT.F.table_find(data,destGUID,1) if not inPos then inPos = #data + 1 data[inPos] = {destGUID,0,0} end data[inPos][3] = data[inPos][3] + 1 end end end end end end end sort(data,function(a,b)return a[2]>b[2]end) local report = {} aurasTab.linesRightClickMoreInfo.report = report report[1] = GetSpellLink(spellID) for i=1,#data do local isPlayer = ExRT.F.GetUnitTypeByGUID(data[i][1]) == 0 local classColor = "" if isPlayer then classColor = "|c"..ExRT.F.classColorByGUID(data[i][1]) end SetLine(i,classColor..GetGUID(data[i][1])..GUIDtoText(" [%s]",data[i][1]),data[i][2]..(data[i][3] > 0 and "+"..data[i][3] or "")) report[#report+1] = i..". "..GetGUID(data[i][1]).." - "..data[i][2]..(data[i][3] > 0 and "+"..data[i][3] or "") end for i=#data+1,#aurasTab.linesRightClickMoreInfo.lines do aurasTab.linesRightClickMoreInfo.lines[i]:Hide() end aurasTab.linesRightClickMoreInfo.ScrollFrame:SetNewHeight(#data * 20) end end local BuffsLineUptimeTempTable = {} local function BuffsLinesOnUpdate(self) local x,y = ExRT.F.GetCursorPos(self) if ExRT.F.IsInFocus(self,x,y) then for j=1,AurasTab_Variables.TotalLines do if aurasTab.lines[j] ~= self then aurasTab.lines[j].hl:Hide() end end self.hl:Show() if x <= AurasTab_Variables.NameWidth then if GameTooltip:IsShown() then local _,spellID = GameTooltip:GetSpell() if spellID == self.spellID then return end end GameTooltip:SetOwner(self, "ANCHOR_RIGHT", -AurasTab_Variables.WorkWidth, 0) if type(self.spellLink) == "string" and not self.spellLink:find("spell:") then GameTooltip:AddLine(self.spellLink) else GameTooltip:SetHyperlink(self.spellLink) end local greenCount = #self.greenTooltips for i=1,greenCount do BuffsLineUptimeTempTable[(i-1)*2+1] = self.greenTooltips[i][1] BuffsLineUptimeTempTable[(i-1)*2+2] = self.greenTooltips[i][2] end for i=1,greenCount do local iPos = (i-1)*2+1 if BuffsLineUptimeTempTable[iPos] then for j=1,greenCount do local jPos = (j-1)*2+1 if i~=j and BuffsLineUptimeTempTable[jPos] then if BuffsLineUptimeTempTable[jPos] <= BuffsLineUptimeTempTable[iPos] and BuffsLineUptimeTempTable[jPos+1] > BuffsLineUptimeTempTable[iPos] then BuffsLineUptimeTempTable[iPos] = BuffsLineUptimeTempTable[jPos] BuffsLineUptimeTempTable[iPos+1] = max(BuffsLineUptimeTempTable[jPos+1],BuffsLineUptimeTempTable[iPos+1]) BuffsLineUptimeTempTable[jPos] = nil BuffsLineUptimeTempTable[jPos+1] = nil end if BuffsLineUptimeTempTable[jPos] and BuffsLineUptimeTempTable[jPos+1] >= BuffsLineUptimeTempTable[iPos+1] and BuffsLineUptimeTempTable[jPos] < BuffsLineUptimeTempTable[iPos+1] then BuffsLineUptimeTempTable[iPos] = min(BuffsLineUptimeTempTable[jPos],BuffsLineUptimeTempTable[iPos]) BuffsLineUptimeTempTable[iPos+1] = BuffsLineUptimeTempTable[jPos+1] BuffsLineUptimeTempTable[jPos] = nil BuffsLineUptimeTempTable[jPos+1] = nil end end end end end local uptime = 0 for i=1,greenCount do local iPos = (i-1)*2+1 if BuffsLineUptimeTempTable[iPos] then uptime = uptime + (BuffsLineUptimeTempTable[iPos+1] - BuffsLineUptimeTempTable[iPos]) end end uptime = uptime / AurasTab_Variables.WorkWidth GameTooltip:AddLine(L.BossWatcherBuffsAndDebuffsTooltipUptimeText..": "..format("%.2f%% (%.1f %s)",uptime*100,uptime*GetFightLength(true),L.BossWatcherBuffsAndDebuffsSecondsText)) GameTooltip:AddLine(L.BossWatcherBuffsAndDebuffsTooltipCountText..": "..(self.greenCount or 0)) GameTooltip:AddLine("Spell ID: "..(self.spellID or 0)) GameTooltip:Show() else if not self.tooltip then self.tooltip = {} end table.wipe(self.tooltip) local owner = nil local _min,_max = AurasTab_Variables.NameWidth+AurasTab_Variables.WorkWidth,AurasTab_Variables.NameWidth for j = 1,#self.greenTooltips do local greenTooltip = self.greenTooltips[j] local rightPos = greenTooltip[2] local leftPos = greenTooltip[1] if rightPos - leftPos < 2 then rightPos = leftPos + 2 end if x >= leftPos and x <= rightPos then local sourceClass = ExRT.F.classColorByGUID(greenTooltip[5]) local destClass = ExRT.F.classColorByGUID(greenTooltip[6]) local duration = (greenTooltip[4] - greenTooltip[3]) table.insert(self.tooltip, date("[%M:%S", greenTooltip[3] ) .. format(".%03d",(greenTooltip[3]*1000)%1000).. " - "..date("%M:%S", greenTooltip[3]+duration ).. format(".%03d",((greenTooltip[3]+duration)*1000)%1000).."] " .. "|c" .. sourceClass .. GetGUID(greenTooltip[5])..GUIDtoText(" (%s)",greenTooltip[5]).."|r "..L.BossWatcherBuffsAndDebuffsTextOn.." |c".. destClass .. GetGUID(greenTooltip[6])..GUIDtoText(" (%s)",greenTooltip[6]).."|r") if greenTooltip[7] and greenTooltip[7] ~= 1 then self.tooltip[#self.tooltip] = self.tooltip[#self.tooltip] .. " (".. greenTooltip[7] ..")" end self.tooltip[#self.tooltip] = self.tooltip[#self.tooltip] .. format(" <%.1f%s>",duration,L.BossWatcherBuffsAndDebuffsSecondsText) owner = greenTooltip[1] _min = min(_min,leftPos) _max = max(_max,rightPos) end end if #self.tooltip > 0 then table.sort(self.tooltip,function(a,b) return a < b end) ELib.Tooltip.Show(self,{"ANCHOR_LEFT",owner or 0,0},L.BossWatcherBuffsAndDebuffsTooltipTitle..":",unpack(self.tooltip)) else GameTooltip_Hide() end end end end local function BuffsLinesOnLeave(self) GameTooltip_Hide() self.hl:Hide() end local function BuffsLinesOnClick(self,button) local x,y = ExRT.F.GetCursorPos(self) if x > 0 and x < AurasTab_Variables.TotalWidth and y > 0 and y < 18 then if x <= AurasTab_Variables.NameWidth then ExRT.F.LinkSpell(nil,self.spellLink) elseif button == "RightButton" then if GameTooltip:IsShown() then if aurasTab.linesRightClickMenuData then wipe(aurasTab.linesRightClickMenuData) else aurasTab.linesRightClickMenuData = {} end table.insert(aurasTab.linesRightClickMenuData , self.spellLink) for j=2, GameTooltip:NumLines() do table.insert(aurasTab.linesRightClickMenuData , _G["GameTooltipTextLeft"..j]:GetText()) end aurasTab.linesRightClickMenu[1].text = self.spellName else aurasTab.linesRightClickMenuData = nil end aurasTab.linesRightClickMoreInfoData = self.spellID aurasTab.linesRightClickLineData = self.lineData EasyMenu(aurasTab.linesRightClickMenu, aurasTab.linesRightClickMenuDropDown, "cursor", 10 , -15, "MENU") end end end tab.lines = {} for i=1,AurasTab_Variables.TotalLines do local line = CreateFrame("Button",nil,tab) tab.lines[i] = line line:SetSize(AurasTab_Variables.TotalWidth,18) line:SetPoint("TOPLEFT", 0, -18*(i-1)-84) line.spellIcon = line:CreateTexture(nil, "BACKGROUND") line.spellIcon:SetSize(16,16) line.spellIcon:SetPoint("TOPLEFT", 5, -1) line.spellText = ELib:Text(line,"",11):Size(AurasTab_Variables.NameWidth-23,18):Point(23,0):Color() line.green = {} line.greenFrame = {} line.greenCount = 0 line.greenTooltips = {} ExRT.lib.CreateHoverHighlight(line) line.hl:SetAlpha(.5) line:SetScript("OnUpdate", BuffsLinesOnUpdate) line:SetScript("OnLeave", BuffsLinesOnLeave) line:RegisterForClicks("RightButtonUp","LeftButtonUp") line:SetScript("OnClick", BuffsLinesOnClick) end tab.scrollBar = ELib:ScrollBar(tab):Size(16,AurasTab_Variables.TotalLines*18):Point("TOPRIGHT",-5,-84):Range(1,2) local function CreateBuffGreen(i,j) aurasTab.lines[i].green[j] = aurasTab.lines[i]:CreateTexture(nil, "BACKGROUND",nil,5) aurasTab.lines[i].green[j]:SetColorTexture(1, 0.82, 0, 0.7) aurasTab.lines[i].greenFrame[j] = CreateFrame("Frame",nil,aurasTab.lines[i]) end local function buffsFunc_isPetOrGuard(flag) if not flag then return false end local res = ExRT.F.GetUnitInfoByUnitFlag(flag,1) if res == 4096 or res == 8192 then return true end end local function buffsFunc_findStringInArray(array,str) for array_str,_ in pairs(array) do if type(array_str) == "string" and (array_str == str or str:find(array_str)) then return true end end end function UpdateBuffPageDB(notUpdateTargetsList) --upvaules local currFight = CurrentFight local buffsFilterStatus = AurasTab_Variables.buffsFilterStatus AurasTab_UpdateDropDownsTexts() local fightDuration = GetFightLength(true) for i=1,10 do aurasTab.timeLine[i+1].timeText:SetText( date("%M:%S", fightDuration*(i/10) ) ) end local _F_sourceGUID = bit.band(AurasTab_Variables.FilterSource,0xF000) > 0 local _F_sourceFriendly = bit.band(AurasTab_Variables.FilterSource,0x00F0) > 0 local _F_sourceHostile = bit.band(AurasTab_Variables.FilterSource,0x000F) > 0 local _F_sourcePets = bit.band(AurasTab_Variables.FilterSource,0x0F00) > 0 local _F_destGUID = bit.band(AurasTab_Variables.FilterDest,0xF000) > 0 local _F_destFriendly = bit.band(AurasTab_Variables.FilterDest,0x00F0) > 0 local _F_destHostile = bit.band(AurasTab_Variables.FilterDest,0x000F) > 0 local _F_destPets = bit.band(AurasTab_Variables.FilterDest,0x0F00) > 0 local sourceList,destList = {},{} local buffTable = {} for i,sourceData in ipairs(CurrentFight.auras) do local spellID = sourceData[6] local spellName,_,spellTexture = GetSpellInfo(spellID) local filterStatus = true for j=5,#AurasTab_Variables.buffsFilters do filterStatus = filterStatus and (not buffsFilterStatus[j] or AurasTab_Variables.buffsFilters[j][spellID]) end if ((not _F_sourceGUID and ((_F_sourcePets or not buffsFunc_isPetOrGuard(currFight.reaction[ sourceData[2] ])) and ((_F_sourceFriendly and sourceData[4]) or (_F_sourceHostile and not sourceData[4])))) or (sourceData[2] and AurasTab_Variables.FilterSourceGUID[ sourceData[2] ])) and ((not _F_destGUID and ((_F_destPets or not buffsFunc_isPetOrGuard(currFight.reaction[ sourceData[3] ])) and ((_F_destFriendly and sourceData[5]) or (_F_destHostile and not sourceData[5])))) or (sourceData[3] and AurasTab_Variables.FilterDestGUID[ sourceData[3] ])) and (not buffsFilterStatus[1] or sourceData[7] == 'BUFF') and (not buffsFilterStatus[2] or sourceData[7] == 'DEBUFF') and (not buffsFilterStatus[4] or (AurasTab_Variables.buffsFilters[3][spellID] or buffsFunc_findStringInArray(AurasTab_Variables.buffsFilters[4],strlower(spellName)))) and filterStatus then local time_ = timestampToFightTime( sourceData[1] ) local time_postion = time_ / fightDuration local type_ = sourceData[8] local buffTablePos for j=1,#buffTable do if buffTable[j][1] == spellID then buffTablePos = j break end end if not buffTablePos then buffTablePos = #buffTable + 1 buffTable[buffTablePos] = {spellID,spellName,spellTexture,{},{}} end local sourceGUID = sourceData[2] or 0 local destGUID = sourceData[3] or 0 local sourceDest = sourceGUID .. destGUID local buffTableBuffPos for j=1,#buffTable[buffTablePos][4] do if buffTable[buffTablePos][4][j][1] == sourceDest then buffTableBuffPos = j break end end if not buffTableBuffPos then buffTableBuffPos = #buffTable[buffTablePos][4] + 1 buffTable[buffTablePos][4][buffTableBuffPos] = {sourceDest,sourceGUID,destGUID,{}} end local eventPos = #buffTable[buffTablePos][4][buffTableBuffPos][4] + 1 if type_ == 3 or type_ == 4 then buffTable[buffTablePos][4][buffTableBuffPos][4][eventPos] = {0,time_,time_postion,sourceData[9] or 1} type_ = 1 eventPos = eventPos + 1 end buffTable[buffTablePos][4][buffTableBuffPos][4][eventPos] = {type_ % 2,time_,time_postion,sourceData[9] or 1} sourceList[sourceGUID] = true destList[destGUID] = true end if ((_F_sourceFriendly and sourceData[4]) or (_F_sourceHostile and not sourceData[4])) and ((_F_destFriendly and sourceData[5]) or (_F_destHostile and not sourceData[5])) and (not buffsFilterStatus[1] or sourceData[7] == 'BUFF') and (not buffsFilterStatus[2] or sourceData[7] == 'DEBUFF') and (not buffsFilterStatus[4] or (AurasTab_Variables.buffsFilters[3][spellID] or buffsFunc_findStringInArray(AurasTab_Variables.buffsFilters[4],strlower(spellName)))) and filterStatus then local sourceGUID = sourceData[2] or 0 local destGUID = sourceData[3] or 0 sourceList[sourceGUID] = true destList[destGUID] = true end end sort(buffTable,function(a,b) return a[2] < b[2] end) for i=1,#buffTable do local buffTableI = buffTable[i] for j=1,#buffTableI[4] do local buffTableJ = buffTableI[4][j] local maxEvents = #buffTableJ[4] if maxEvents > 0 and buffTableJ[4][1][1] == 0 then local newLine = #buffTableI[5] + 1 buffTableI[5][newLine] = { AurasTab_Variables.NameWidth, AurasTab_Variables.NameWidth+AurasTab_Variables.WorkWidth*buffTableJ[4][1][3], 0, buffTableJ[4][1][2], buffTableJ[2], buffTableJ[3], 1, } end for k=1,maxEvents do if buffTableJ[4][k][1] == 1 then local endOfTime = nil for n=(k+1),maxEvents do if buffTableJ[4][n][1] == 0 and not endOfTime then endOfTime = n --break end end local newLine = #buffTableI[5] + 1 buffTableI[5][newLine] = { AurasTab_Variables.NameWidth+AurasTab_Variables.WorkWidth*buffTableJ[4][k][3], AurasTab_Variables.NameWidth+AurasTab_Variables.WorkWidth*(endOfTime and buffTableJ[4][endOfTime][3] or 1), buffTableJ[4][k][2], endOfTime and buffTableJ[4][endOfTime][2] or fightDuration, buffTableJ[2], buffTableJ[3], buffTableJ[4][k][4], } --startPos,endPos,startTime,endTime,sourceGUID,destGUID,stacks end end end end --> Death Line for i=1,#aurasTab.redDeathLine do aurasTab.redDeathLine[i]:Hide() end if _F_destGUID and ExRT.F.table_len(AurasTab_Variables.FilterDestGUID) > 0 then local j = 0 for i=1,#CurrentFight.dies do if AurasTab_Variables.FilterDestGUID[ CurrentFight.dies[i][1] ] then j = j + 1 CreateRedDeathLine(j) local time_ = timestampToFightTime( CurrentFight.dies[i][3] ) local pos = AurasTab_Variables.NameWidth + time_/fightDuration*AurasTab_Variables.WorkWidth - 1 aurasTab.redDeathLine[j]:SetPoint("TOPLEFT",pos,-42-25) aurasTab.redDeathLine[j]:Show() end end end aurasTab.scrollBar:Range(1,max(#buffTable-AurasTab_Variables.TotalLines+1,1)) aurasTab.db = buffTable if notUpdateTargetsList then return end local sourceTable,destTable = {},{} for sourceGUID,_ in pairs(sourceList) do sourceTable[#sourceTable+1] = {sourceGUID,GetGUID(sourceGUID),sourceGUID:find("^Player")} end for destGUID,_ in pairs(destList) do destTable[#destTable+1] = {destGUID,GetGUID(destGUID),destGUID:find("^Player")} end sort(sourceTable,function(a,b) if a[3]==b[3] then return a[2]= AurasTab_Variables.TotalLines then break end end for i=(linesCount+1),AurasTab_Variables.TotalLines do local line = currTab.lines[i] line:Hide() line.lineData = nil end currTab.scrollBar:UpdateButtons() end tab.scrollBar:SetScript("OnValueChanged",UpdateBuffsPage) tab:SetScript("OnMouseWheel",function (self,delta) if delta > 0 then aurasTab.scrollBar.buttonUP:Click("LeftButton") else aurasTab.scrollBar.buttonDown:Click("LeftButton") end end) function AurasPage_IsAuraOn(destGUID,auraSpellID,fightTime) local isOnNow = false local aurasTable = CurrentFight.auras for j=1,#aurasTable do if aurasTable[j][6] == auraSpellID and aurasTable[j][3] == destGUID then if aurasTable[j][8] == 1 or aurasTable[j][8] == 2 then isOnNow = true else isOnNow = false end end local thisTime = timestampToFightTime( aurasTable[j][1] ) if thisTime > fightTime then return isOnNow end end return isOnNow end tab:SetScript("OnShow",function (self) if BWInterfaceFrame.nowFightIDShort ~= self.lastFightID then UpdateBuffPageDB() self.lastFightID = BWInterfaceFrame.nowFightIDShort end UpdateBuffsPage() end) ---- Mobs Info & Switch tab = BWInterfaceFrame.tab.tabs[4] local mobsTab = tab local Enemy_GUIDnow,Enemy_LifeTime = nil tab.targetsList = ELib:ScrollList(tab):Point(14,-76):Size(282,513) tab.targetsList.GUIDs = {} tab.DecorationLine = ELib:DecorationLine(tab,true):Point("TOPLEFT",tab.targetsList,"TOPRIGHT",0,2):Point("RIGHT",tab,-5,0):Size(0,37) tab.selectedMob = ELib:Text(tab.DecorationLine:GetParent(),"",11):Size(530,12):Point("TOPLEFT",tab.DecorationLine,"TOPLEFT",5,-5):Color():Top() tab.infoTabs = ELib:Tabs(tab,0, L.BossWatcherSwitchBySpell, L.BossWatcherSwitchByTarget, L.BossWatcherDamageSwitchTabInfo ):Size(530,465):Point(295,-111):SetTo(1) tab.infoTabs:SetBackdropBorderColor(0,0,0,0) tab.infoTabs:SetBackdropColor(0,0,0,0) tab.switchSpellBox = ELib:MultiEdit(tab.infoTabs.tabs[1]):Size(540,415):Point(13,-10):Hyperlinks() tab.switchTargetBox = ELib:MultiEdit(tab.infoTabs.tabs[2]):Size(540,415):Point(13,-10) tab.infoBoxText = ELib:Text(tab.infoTabs.tabs[3],L.BossWatcherDamageSwitchTabInfoNoInfo,12):Size(540,415):Point(13,-13):Top():Color() tab.toDamageButton = ELib:Button(tab.infoTabs,L.BossWatcherShowDamageToTarget):Size(548,20):Point("BOTTOMLEFT",tab.targetsList,"BOTTOMRIGHT",8,-2):OnClick(function (self) if not Enemy_GUIDnow then return end DamageTab_ShowDamageToTarget(Enemy_GUIDnow) end) tab.toSegmentsButton = ELib:Button(tab.infoTabs,L.BossWatcherOnlySegmentsWithEnemy):Size(548,20):Point("BOTTOM",tab.toDamageButton,"TOP",0,5):OnClick(function (self) if not Enemy_LifeTime then return end UpdateSegmentsTime(unpack(Enemy_LifeTime)) end) function tab.targetsList:SetListValue(index) local destGUID = self.GUIDs[index] Enemy_GUIDnow = destGUID Enemy_LifeTime = {timestampToFightTime( CurrentFight.damage_seen[destGUID] )+CurrentFight.encounterStart,CurrentFight.encounterEnd or GetTime()} mobsTab.toSegmentsButton:SetEnabled(true) mobsTab.toDamageButton:SetEnabled(true) wipe(reportData[4][1]) wipe(reportData[4][2]) wipe(reportData[4][3]) local _time = timestampToFightTime(CurrentFight.damage_seen[destGUID]) local fight_dur = GetFightLength(true) mobsTab.selectedMob:SetText(GetGUID(destGUID).." "..date("%M:%S", _time )..GUIDtoText(" (%s)",destGUID)) _time = _time / fight_dur local textResult = "" local textResult2 = "" if CurrentFight.switch[destGUID] then local switchTable = {} if CurrentFight.switch[destGUID][1] then for sourceGUID,sourceData in pairs(CurrentFight.switch[destGUID][1]) do if ExRT.F.GetUnitTypeByGUID(sourceGUID) == 0 and not ExRT.F.table_find(switchTable,sourceGUID,3) then table.insert(switchTable,{GetGUID(sourceGUID),timestampToFightTime(sourceData[1]),sourceGUID,sourceData[2],sourceData[3]}) end end end table.sort(switchTable,function(a,b) return a[2] < b[2] end) if #switchTable > 0 then textResult = L.BossWatcherReportCast.." [" .. date("%M:%S", switchTable[1][2] ) .."]:|n" reportData[4][1][1] = GetGUID(destGUID).." > ".. L.BossWatcherReportCast.." [" .. date("%M:%S", switchTable[1][2] ) .."]:" for i=1,#switchTable do local spellName = GetSpellInfo(switchTable[i][4] or 0) textResult = textResult ..i..". ".."|c".. ExRT.F.classColorByGUID(switchTable[i][3]).. switchTable[i][1] .. GUIDtoText(" <%s>",switchTable[i][3]) .. "|r (".. format("%.3f",switchTable[i][2]-switchTable[1][2])..", |Hspell:"..(switchTable[i][4] or 0).."|h"..(spellName or "?").."|h, "..((switchTable[i][5] == 1 and DAMAGE:lower()) or (switchTable[i][5] == 2 and ACTION_SPELL_CAST_SUCCESS:lower()) or (switchTable[i][5] == 3 and ACTION_SPELL_CAST_START:lower()) or "unk")..")" reportData[4][1][#reportData[4][1]+1] = i..". "..switchTable[i][1] .. "(" .. format("%.3f",switchTable[i][2]-switchTable[1][2])..", "..GetSpellLink(switchTable[i][4] or 0)..")" if i ~= #switchTable then textResult = textResult .. "|n" end end textResult = textResult .. "\n\n" end wipe(switchTable) if CurrentFight.switch[destGUID][2] then for sourceGUID,sourceData in pairs(CurrentFight.switch[destGUID][2]) do if ExRT.F.GetUnitTypeByGUID(sourceGUID) == 0 and not ExRT.F.table_find(switchTable,sourceGUID,3) then table.insert(switchTable,{GetGUID(sourceGUID),sourceData[1] - CurrentFight.encounterStart,sourceGUID,sourceData[2]}) end end end table.sort(switchTable,function(a,b) return a[2] < b[2] end) if #switchTable > 0 then textResult2 = textResult2 .. L.BossWatcherReportSwitch.." [" .. date("%M:%S", switchTable[1][2] ) .."]:|n" reportData[4][2][1] = GetGUID(destGUID).." > ".. L.BossWatcherReportSwitch.." [" .. date("%M:%S", switchTable[1][2] ) .."]:" for i=1,#switchTable do textResult2 = textResult2 ..i..". ".. "|c".. ExRT.F.classColorByGUID(switchTable[i][3]).. switchTable[i][1] .. GUIDtoText(" <%s>",switchTable[i][3]) .. "|r (".. format("%.3f",switchTable[i][2]-switchTable[1][2])..")" reportData[4][2][#reportData[4][2]+1] = i..". ".. switchTable[i][1].."(" .. format("%.3f",switchTable[i][2]-switchTable[1][2])..")" if i ~= #switchTable then textResult2 = textResult2 .. "|n" end end end end mobsTab.switchSpellBox:SetText(textResult):ToTop() mobsTab.switchTargetBox:SetText(textResult2):ToTop() --> Other Info textResult = "" reportData[4][3][1] = GetGUID(destGUID)..":" for i=1,#CurrentFight.dies do if CurrentFight.dies[i][1]==destGUID then textResult = textResult .. L.BossWatcherDamageSwitchTabInfoRIP..": ".. date("%M:%S", timestampToFightTime(CurrentFight.dies[i][3]) ) .. date(" (%H:%M:%S)", CurrentFight.dies[i][3] ) .. "\n" reportData[4][3][#reportData[4][3]+1] = L.BossWatcherDamageSwitchTabInfoRIP..": ".. date("%M:%S", timestampToFightTime(CurrentFight.dies[i][3]) ) .. date(" (%H:%M:%S)", CurrentFight.dies[i][3] ) local raidTarget = module.db.raidTargets[ CurrentFight.dies[i][4] or 0 ] if raidTarget then textResult = textResult .. L.BossWatcherMarkOnDeath..": "..GetTargetIconText(raidTarget).." ".. string.gsub( L["raidtargeticon"..raidTarget] , "[{}]", "" ) .."\n" reportData[4][3][#reportData[4][3]+1] = L.BossWatcherMarkOnDeath..": "..string.gsub( L["raidtargeticon"..raidTarget] , "[{}]", "" ) end Enemy_LifeTime[2] = timestampToFightTime(CurrentFight.dies[i][3])+CurrentFight.encounterStart end end local mobID = ExRT.F.GUIDtoID(destGUID) local mobSpawnID1,mobSpawnID2,mobSpawnID3,mobSpawnID4 = 0,0,0,0 do local spawnString = destGUID:match("%-([^%-]+)$") or "0" if spawnString then mobSpawnID1 = tonumber(spawnString:sub(1,5), 16) or 0 mobSpawnID2 = tonumber(spawnString:sub(6), 16) or 0 mobSpawnID3 = tonumber(spawnString, 16) or 0 local copyStr = spawnString while copyStr:find("^0") do copyStr = copyStr:gsub("^0","") end mobSpawnID4 = tonumber(string.reverse(copyStr), 16) or 0 end end textResult = textResult .. "Mob ID: ".. mobID .. "\n" local unitType,_,serverID,instanceID,zoneUID,id,spawnID = strsplit("-", destGUID or "") if id and unitType == "Creature" or unitType == "Vehicle" then local spawnEpoch = GetServerTime() - (GetServerTime() % 2^23) local spawnEpochOffset = bit.band(tonumber(string.sub(spawnID, 5), 16), 0x7fffff) local spawnIndex = bit.rshift(bit.band(tonumber(string.sub(spawnID, 1, 5), 16), 0xffff8), 3) local spawnTime = spawnEpoch + spawnEpochOffset if spawnTime > GetServerTime() then spawnTime = spawnTime - ((2^23) - 1) end textResult = textResult .. "Spawned at: ".. date("%d-%m-%Y %H:%M:%S", spawnTime) .. "\n" textResult = textResult .. "Spawn index: ".. spawnIndex .. "\n" else textResult = textResult .. "Spawn ID: ".. mobSpawnID1 .. "-" .. mobSpawnID2 .." ("..mobSpawnID3..", "..mobSpawnID4..")".. "\n" end textResult = textResult .. "GUID: ".. destGUID .. "\n" reportData[4][3][#reportData[4][3]+1] = "Mob ID: ".. mobID reportData[4][3][#reportData[4][3]+1] = "Spawn ID: ".. mobSpawnID1 .. "-" .. mobSpawnID2 reportData[4][3][#reportData[4][3]+1] = "GUID: ".. destGUID if CurrentFight.maxHP[destGUID] then textResult = textResult .. "Max Health: ".. CurrentFight.maxHP[destGUID] .. "\n" reportData[4][3][#reportData[4][3]+1] = "Max Health: ".. CurrentFight.maxHP[destGUID] end mobsTab.infoBoxText:SetText(textResult) end function tab.targetsList:HoverListValue(isHover,index,hoveredObj) if not isHover then BWInterfaceFrame.timeLineFrame.timeLine.arrow:Hide() BWInterfaceFrame.timeLineFrame.timeLine.lifeUnderLine:Hide() GameTooltip_Hide() else local mobGUID = self.GUIDs[index] local mobSeen = timestampToFightTime( CurrentFight.damage_seen[mobGUID] ) local fight_dur = GetFightLength(true) local _time = mobSeen / fight_dur BWInterfaceFrame.timeLineFrame.timeLine.arrow:SetPoint("TOPLEFT",BWInterfaceFrame.timeLineFrame.timeLine,"TOPLEFT",BWInterfaceFrame.timeLineFrame.width*_time,0) BWInterfaceFrame.timeLineFrame.timeLine.arrow:Show() local dieTime = 1 for i=1,#CurrentFight.dies do if CurrentFight.dies[i][1]==mobGUID then dieTime = timestampToFightTime(CurrentFight.dies[i][3]) / fight_dur break end end BWInterfaceFrame.timeLineFrame.timeLine.lifeUnderLine:SetPoint(_time,dieTime) GameTooltip:SetOwner(self,"ANCHOR_CURSOR") GameTooltip:AddLine(GUIDtoText("%s",mobGUID)) if hoveredObj.text:IsTruncated() then GameTooltip:AddLine(GetGUID(mobGUID) .. date(" %M:%S", mobSeen) ) end GameTooltip:Show() end end local function UpdateMobsPage() table.wipe(mobsTab.targetsList.L) table.wipe(mobsTab.targetsList.GUIDs) wipe(reportData[4][1]) wipe(reportData[4][2]) wipe(reportData[4][3]) local mobsList = {} for mobGUID,mobData in pairs(CurrentFight.damage) do for destReaction,destReactData in pairs(mobData) do if (ExRT.F.GetUnitInfoByUnitFlag(destReaction,2) == 512) and not ExRT.F.table_find(mobsList,mobGUID,3) then mobsList[#mobsList+1] = {GetGUID(mobGUID),CurrentFight.damage_seen[mobGUID],mobGUID} for i=1,#CurrentFight.dies do if CurrentFight.dies[i][1]==mobGUID then local raidTarget = module.db.raidTargets[ CurrentFight.dies[i][4] or 0 ] if raidTarget then mobsList[#mobsList][1] = GetTargetIconText(raidTarget).." "..mobsList[#mobsList][1] end break end end end end end for mobGUID,mobData in pairs(CurrentFight.switch) do if (not CurrentFight.reaction[mobGUID] or ExRT.F.GetUnitInfoByUnitFlag(CurrentFight.reaction[mobGUID],2) == 512) and not ExRT.F.table_find(mobsList,mobGUID,3) then mobsList[#mobsList+1] = {GetGUID(mobGUID),mobData.seen or 0,mobGUID} for i=1,#CurrentFight.dies do if CurrentFight.dies[i][1]==mobGUID then local raidTarget = module.db.raidTargets[ CurrentFight.dies[i][4] or 0 ] if raidTarget then mobsList[#mobsList][1] = GetTargetIconText(raidTarget).." "..mobsList[#mobsList][1] end break end end end end table.sort(mobsList,function(a,b) return a[2] < b[2] end) for i=1,#mobsList do mobsTab.targetsList.L[i] = date("%M:%S ", timestampToFightTime(mobsList[i][2]))..mobsList[i][1] mobsTab.targetsList.GUIDs[i] = mobsList[i][3] end mobsTab.targetsList:Update() Enemy_GUIDnow = nil mobsTab.toDamageButton:SetEnabled(false) mobsTab.toSegmentsButton:SetEnabled(false) end tab:SetScript("OnShow",function (self) BWInterfaceFrame.timeLineFrame:ClearAllPoints() BWInterfaceFrame.timeLineFrame:SetPoint("TOP",self,"TOP",0,-10) BWInterfaceFrame.timeLineFrame:Show() BWInterfaceFrame.report:Show() if BWInterfaceFrame.nowFightID ~= self.lastFightID then self.targetsList.selected = nil UpdateMobsPage() self.lastFightID = BWInterfaceFrame.nowFightID end end) tab:SetScript("OnHide",function (self) BWInterfaceFrame.timeLineFrame:Hide() BWInterfaceFrame.report:Hide() end) ---- Spells tab = BWInterfaceFrame.tab.tabs[5] local tab5 = tab local SpellsTab_Variables = { Type = 1, --1 - friendly 2 - hostile 3 - spells count, 4 - summons TypeP2 = 1, --1 - friendly 2 - hostile Filter = {}, FilterByTarget = {}, TimeLineBlackList = { [147193] = true, --Shadowy Apparition [341263] = true, --Shadowy Apparition [272790] = true, --Frenzy [27576] = true, --Mutilate [124506] = true, --Gift of the Ox [124503] = true, --Gift of the Ox [336463] = true, --Shadowcore Oil Blast [324748] = true, --Celestial Guidance [139546] = true, --Combo Point [328917] = true, --Combat Meditation [328913] = true, --Combat Meditation [228537] = true, --Shattered Souls [324202] = true, --Eternal Grace [324226] = true, --Ascended Vigor [209788] = true, --Soul Fragment [204255] = true, --Soul Fragment [204062] = true, --Soul Fragment [203795] = true, --Soul Fragment }, } tab.SpellsTab_Variables = SpellsTab_Variables tab.DecorationLine = ELib:DecorationLine(tab,true):Point("TOPLEFT",tab,"TOPLEFT",3,-8):Point("RIGHT",tab,-3,0):Size(0,20) tab.page = ELib:Tabs(tab,0, L.BossWatcherTimeLineTooltipTitle, L.BossWatcherTimelineText ):Size(865,600):Point("TOP",0,-29):SetTo(1) tab.page:SetBackdropBorderColor(0,0,0,0) tab.page:SetBackdropColor(0,0,0,0) tab.playersList = ELib:ScrollList(tab.page.tabs[1]):Size(190,420):Point(14,-140) tab.playersCastsList = ELib:ScrollList(tab.page.tabs[1]):Size(637,440):Point(214,-95) tab.playersList.IndexToGUID = {} tab.playersCastsList.IndexToGUID = {} tab.summaryTooltip = ELib:Button(tab.page.tabs[1],STATISTICS):Point("TOP",tab.playersCastsList,"BOTTOM",0,-5):Size(637,20) tab.summaryTooltip:SetScript("OnEnter",function(self) if not self.tooltip then return end GameTooltip:SetOwner(self,"ANCHOR_LEFT") GameTooltip:SetText(self.tooltip[1]) for i=2,#self.tooltip do if type(self.tooltip[i]) == "table" then GameTooltip:AddDoubleLine(self.tooltip[i][1],self.tooltip[i][2],1,1,1,1,1,1,1,1) else GameTooltip:AddLine(self.tooltip[i]) end end GameTooltip:Show() end) tab.summaryTooltip:SetScript("OnLeave",ELib.Tooltip.Hide) local function SpellsTab_ReloadSpells() local selected = tab5.playersList.selected if selected then tab5.playersList:SetListValue(selected) end end local SpellsTab_UpdateFilterHeader = nil local function SpellsTab_UpdateFilter(text) SpellsTab_UpdateFilterHeader = nil wipe(SpellsTab_Variables.Filter) wipe(SpellsTab_Variables.FilterByTarget) if text:find("^target!?[=~]") or text:find("^!?[=~]") then text = text:gsub("^target","") local filterType = 1 if text:find("^!=") then filterType = 3 elseif text:find("^!~") then filterType = 4 elseif text:find("^~") then filterType = 2 end local targetsStr = text:match("^!?[=~](.+)") if targetsStr then local targets = {strsplit(";",targetsStr)} for i=1,#targets do if tonumber(targets[i]) then SpellsTab_Variables.FilterByTarget[ tonumber(targets[i]) ] = filterType else SpellsTab_Variables.FilterByTarget[ targets[i] ] = filterType end end end SpellsTab_ReloadSpells() return end local spells = {strsplit(";",text)} for i=1,#spells do if tonumber(spells[i]) then spells[i] = tonumber(spells[i]) end SpellsTab_Variables.Filter[spells[i]] = true end SpellsTab_ReloadSpells() end tab.filterEditBox = ELib:Edit(tab.page.tabs[1]):Size(639,16):Point(213,-73):Tooltip(L.BossWatcherSpellsFilterTooltip..'|n'..L.BossWatcherBySpell..': "|cffffffff774;Multi-shot;105809|r" '..OR_CAPS:lower()..' "|cffffffffFlash Heal|r"|n'..L.BossWatcherByTarget..': "|cfffffffftarget=Ragnaros;The Lich King;Lei Shen|r" '..OR_CAPS:lower()..' "|cffffffff=Garrosh|r" '..OR_CAPS:lower()..' "|cffffffff~illi|r"'):OnChange(function (self) local text = self:GetText() if text == "" then ExRT.F.CancelTimer(SpellsTab_UpdateFilterHeader) wipe(SpellsTab_Variables.Filter) wipe(SpellsTab_Variables.FilterByTarget) SpellsTab_ReloadSpells() return end SpellsTab_UpdateFilterHeader = ExRT.F.ScheduleETimer(SpellsTab_UpdateFilterHeader,SpellsTab_UpdateFilter,0.8,text) end) function tab.playersList:HoverListValue(isHover,index) if not isHover then GameTooltip_Hide() else GameTooltip:SetOwner(self,"ANCHOR_CURSOR") GameTooltip:AddLine(GUIDtoText("%s",self.IndexToGUID[index])) GameTooltip:Show() end end function tab.playersCastsList:HoverListValue(isHover,index,hoveredObj) if not isHover then GameTooltip_Hide() ELib.Tooltip:HideAdd() BWInterfaceFrame.timeLineFrame.timeLine.arrow:Hide() BWInterfaceFrame.timeLineFrame.timeLine:HideLabels() else local data = self.IndexToGUID[index] GameTooltip:SetOwner(hoveredObj or self,"ANCHOR_BOTTOMLEFT") GameTooltip:SetHyperlink(data[1]) GameTooltip:AddLine("Spell ID: "..(data[3] or 0)) GameTooltip:Show() if hoveredObj.text:IsTruncated() then ELib.Tooltip:Add(nil,{hoveredObj.text:GetText()},false,true) end if data[2] then if not data[5] then BWInterfaceFrame.timeLineFrame.timeLine.arrow:SetPoint("TOPLEFT",BWInterfaceFrame.timeLineFrame.timeLine,"TOPLEFT",BWInterfaceFrame.timeLineFrame.width*data[2],0) BWInterfaceFrame.timeLineFrame.timeLine.arrow:Show() else local count = 0 for i=1,#data[5] do local pos = data[5][i] if pos < 0 and data[6] then pos = -pos count = count + 1 BWInterfaceFrame.timeLineFrame.timeLine:AddLabel(count,pos,data[2] == pos and 1 or 2) elseif pos >= 0 then count = count + 1 BWInterfaceFrame.timeLineFrame.timeLine:AddLabel(count,pos,data[2] == pos and 1 or nil) end end end end if self.redTime and data[4] then local diff = data[4] - self.redTime local isNegative = diff < 0 and -diff ELib.Tooltip:Add(nil,{format("%s%d:%06.3f (%.3f %s)",isNegative and "-" or "",(isNegative or diff) / 60,(isNegative or diff) % 60,diff,SECONDS)},false,true) end end end local function SpellsTab_FindInWord(haystack,needle) if not haystack or not needle then return end needle = needle:lower() haystack = haystack:lower() if haystack:find(needle) then return true end return false end function tab.playersList:SetListValue(index) local playersCastsList = tab5.playersCastsList table.wipe(playersCastsList.L) table.wipe(playersCastsList.IndexToGUID) local selfGUID = self.IndexToGUID[index] local fight_dur = GetFightLength(true) local SpellsTab_isFriendly = SpellsTab_Variables.Type == 1 tab5.summaryTooltip.tooltip = nil tab5.summaryTooltip:Disable() if SpellsTab_Variables.Type == 4 then for i,data in ipairs(CurrentFight.summons) do if (not selfGUID or selfGUID == data[1]) and CurrentFight.segments[ data.s ].e then local spellName,_,spellTexture = GetSpellInfo(data[3]) local time_ = timestampToFightTime(data[4]) local sourceName= "|c"..ExRT.F.classColorByGUID(data[1])..GetGUID( data[1] )..GUIDtoText(" <%s>",data[1]).."|r " local destName= "|c"..ExRT.F.classColorByGUID(data[2])..GetGUID( data[2] )..GUIDtoText(" <%s>",data[2]).."|r " local sourceMarker = module.db.raidTargets[ data[5] or 0 ] local destMarker = module.db.raidTargets[ data[6] or 0 ] playersCastsList.L[#playersCastsList.L + 1] = format("[%02d:%06.3f] ",time_ / 60,time_ % 60)..(sourceMarker and GetTargetIconText(sourceMarker) or "")..sourceName.." "..ACTION_SPELL_SUMMON.." "..(destMarker and GetTargetIconText(destMarker) or "")..destName..L.BossWatcherByText..format(" %s%s",spellTexture and "|T"..spellTexture..":0|t " or "",spellName or "???") playersCastsList.IndexToGUID[#playersCastsList.IndexToGUID + 1] = {"spell:"..data[3],time_ / fight_dur,data[3],time_} end end elseif SpellsTab_Variables.Type ~= 3 then local spells = {} if selfGUID then for i,PlayerCastData in ipairs(CurrentFight.cast[selfGUID]) do if CurrentFight.segments[ PlayerCastData.s ].e then spells[#spells + 1] = {PlayerCastData[1],PlayerCastData[2],PlayerCastData[3],PlayerCastData[4],PlayerCastData[5],PlayerCastData[6],nil,PlayerCastData[8]} end end else local reaction = SpellsTab_isFriendly and 256 or 512 for GUID,dataGUID in pairs(CurrentFight.cast) do for i,PlayerCastData in ipairs(dataGUID) do if ExRT.F.GetUnitInfoByUnitFlag(PlayerCastData[7],2) == reaction and CurrentFight.segments[ PlayerCastData.s ].e then spells[#spells + 1] = {PlayerCastData[1],PlayerCastData[2],PlayerCastData[3],PlayerCastData[4],PlayerCastData[5],PlayerCastData[6],GUID,PlayerCastData[8]} end end end sort(spells,function(a,b) return a[1] 0 local isTargetsFilterEnabled = ExRT.F.table_len(SpellsTab_Variables.FilterByTarget) > 0 for i,data in ipairs(spells) do local spellID = data[2] local spellName,_,spellTexture = GetSpellInfo(spellID) local time_ = timestampToFightTime(data[1]) local isCast = "" if data[3] == 2 then isCast = L.BossWatcherBeginCasting.." " end local sourceName = "" if data[7] then sourceName = "|c"..ExRT.F.classColorByGUID(data[7])..GetGUID( data[7] )..GUIDtoText(" <%s>",data[7]).."|r " end local isMustBeAdded = true if isSpellsFilterEnabled then isMustBeAdded = false for filterSource,_ in pairs(SpellsTab_Variables.Filter) do if (type(filterSource) == "number" and filterSource == spellID) or (type(filterSource) ~= "number" and spellName and string.find(strlower(spellName),strlower(filterSource))) then isMustBeAdded = true break end end elseif isTargetsFilterEnabled then isMustBeAdded = false for filterSource,filterType in pairs(SpellsTab_Variables.FilterByTarget) do if (filterType == 2 and SpellsTab_FindInWord( GetGUID( data[4] ),filterSource )) or (filterType == 3 and not (GetGUID( data[4] ) == filterSource)) or (filterType == 4 and not SpellsTab_FindInWord( GetGUID( data[4] ),filterSource )) or (filterType == 1 and (GetGUID( data[4] ) == filterSource)) or (filterType == 1 and type(filterSource)=='number' and module.db.raidTargets[ data[5] or 0 ] == filterSource) then isMustBeAdded = true break end end end if isMustBeAdded then spellToTime[spellID] = spellToTime[spellID] or {} spellToTime[spellID][#spellToTime[spellID] + 1] = time_ / fight_dur * (data[3] == 2 and -1 or 1) local sourceMarker = module.db.raidTargets[ data[6] or 0 ] playersCastsList.L[#playersCastsList.L + 1] = format("[%02d:%06.3f] ",time_ / 60,time_ % 60)..(sourceMarker and GetTargetIconText(sourceMarker) or "")..sourceName..isCast..format("%s%s",spellTexture and "|T"..spellTexture..":0|t " or "",spellName or "???")..(data[8] and " ("..EMPOWERS..": "..data[8]..")" or "") playersCastsList.IndexToGUID[#playersCastsList.IndexToGUID + 1] = {"spell:"..spellID,time_ / fight_dur,spellID,time_,spellToTime[spellID],data[3] == 2} if data[4] and data[4] ~= "" then local destMarker = module.db.raidTargets[ data[5] or 0 ] playersCastsList.L[#playersCastsList.L] = playersCastsList.L[#playersCastsList.L] .. " > |c"..ExRT.F.classColorByGUID(data[4])..(destMarker and GetTargetIconText(destMarker) or "")..GetGUID( data[4] )..GUIDtoText(" <%s>",data[4]).."|r" if data[3] == 1 then stats.targets[ data[4] ] = (stats.targets[ data[4] ] or 0) + 1 end elseif data[3] == 1 then stats.targets[ "-" ] = (stats.targets[ "-" ] or 0) + 1 end if data[3] == 1 then stats.sources[spellID] = (stats.sources[spellID] or 0) + 1 end end end local ss,ts = {},{} local cs,ct = 0,0 for q,w in pairs(stats.sources) do ss[ #ss+1 ] = {q,w} cs=cs+w end for q,w in pairs(stats.targets) do if q~="-" then ts[ #ts+1 ] = {q,w} end ct=ct+w end sort(ss,DamageTab_Temp_SortingBy2Param) sort(ts,DamageTab_Temp_SortingBy2Param) local s = {TARGET..":"} for i=1,min(#ts,10) do s[#s+1]= { (ts[i][1] ~= "-" and GetGUID( ts[i][1] )..GUIDtoText(" <%s>",ts[i][1]) or NONE) , format("%d (%.2f%%)",ts[i][2],ts[i][2] / max(1,ct) * 100) } end if #ts > 10 then local o = 0 for i=11,#ts do o = o + ts[i][2] end s[#s+1]= { OTHER , format("%d (%.2f%%)",o,o / max(1,ct) * 100) } end s[#s+1]= " " s[#s+1]= SOURCES .. ":" for i=1,min(#ss,10) do local spellName,_,spellTexture = GetSpellInfo(ss[i][1]) s[#s+1]= { "|T" .. (spellTexture or "") .. ":0|t " ..(spellName or ss[i][1]) , format("%d (%.2f%%)",ss[i][2],ss[i][2] / max(1,cs) * 100) } end if #ss > 10 then local o = 0 for i=11,#ss do o = o + ss[i][2] end s[#s+1]= { OTHER , format("%d (%.2f%%)",o,o / max(1,ct) * 100) } end tab5.summaryTooltip.tooltip = s tab5.summaryTooltip:Enable() else local spells,spellToTime = {},{} for GUID,dataGUID in pairs(CurrentFight.cast) do if not selfGUID or selfGUID == GUID then for i,PlayerCastData in ipairs(CurrentFight.cast[GUID]) do if PlayerCastData[3] ~= 2 and CurrentFight.segments[ PlayerCastData.s ].e then local spellID = PlayerCastData[2] local inTable = ExRT.F.table_find(spells,spellID,1) if not inTable then inTable = #spells + 1 spells[inTable] = {spellID,0} end spells[inTable][2] = spells[inTable][2] + 1 spellToTime[spellID] = spellToTime[spellID] or {} spellToTime[spellID][#spellToTime[spellID] + 1] = timestampToFightTime(PlayerCastData[1]) / fight_dur * (PlayerCastData[3] == 2 and -1 or 1) end end end end sort(spells,function(a,b)return a[2]>b[2] end) for i,data in ipairs(spells) do local spellName,_,spellTexture = GetSpellInfo(data[1]) playersCastsList.L[#playersCastsList.L + 1] = data[2].." "..format("%s%s",spellTexture and "|T"..spellTexture..":0|t " or "",spellName or "???") playersCastsList.IndexToGUID[#playersCastsList.IndexToGUID + 1] = {"spell:"..data[1],0,data[1],nil,spellToTime[ data[1] ]} end end playersCastsList:Update() end function tab.playersCastsList:SetListValue(index,button) self.selected = nil if button == "RightButton" then if self.redTime or not self.IndexToGUID[index][4] then self.redTime = nil self.redIndex = nil else self.redTime = self.IndexToGUID[index][4] self.redIndex = index end self:Update() return end self.redTime = nil self.redIndex = nil local sID = self.IndexToGUID[index][3] if self.redSpell == sID then self.redSpell = nil else self.redSpell = sID end self:Update() end function tab.playersCastsList:UpdateAdditional(scrollPos) for j=1,#self.List do local index = self.List[j].index if self.redSpell and index and self.IndexToGUID[index] and self.IndexToGUID[index][3] == self.redSpell then self.List[j].text:SetTextColor(1,0.2,0.2,1) elseif self.redIndex and self.redIndex == index then self.List[j].text:SetTextColor(0.6,1,0.2,1) else self.List[j].text:SetTextColor(1,1,1,1) end end end local function UpdateSpellsPage() table.wipe(tab5.playersList.L) table.wipe(tab5.playersList.IndexToGUID) table.wipe(tab5.playersCastsList.L) table.wipe(tab5.playersCastsList.IndexToGUID) local playersListTable = {} if SpellsTab_Variables.Type ~= 4 then local SpellsTab_isFriendly = SpellsTab_Variables.Type == 1 for sourceGUID,sourceData in pairs(CurrentFight.cast) do if not ExRT.F.table_find(playersListTable,sourceGUID,1) then for i=1,#sourceData do if CurrentFight.segments[ sourceData[i].s ].e and (SpellsTab_Variables.Type == 3 or (SpellsTab_isFriendly and ExRT.F.GetUnitInfoByUnitFlag(sourceData[i][7],1) == 1024) or (not SpellsTab_isFriendly and ExRT.F.GetUnitInfoByUnitFlag(sourceData[i][7],2) == 512)) then playersListTable[#playersListTable + 1] = {sourceGUID,GetGUID( sourceGUID ),"|c"..ExRT.F.classColorByGUID(sourceGUID),sourceGUID:find("^Player%-")} break end end end end else for _,data in pairs(CurrentFight.summons) do local sourceGUID = data[1] if CurrentFight.segments[ data.s ].e and not ExRT.F.table_find(playersListTable,sourceGUID,1) then playersListTable[#playersListTable + 1] = {sourceGUID,GetGUID( sourceGUID ),"|c"..ExRT.F.classColorByGUID(sourceGUID)} end end end if SpellsTab_Variables.Type == 3 then table.sort(playersListTable,function (a,b) if a[4] == b[4] then return a[2] < b[2] else return a[4] end end) else table.sort(playersListTable,function (a,b) return a[2] < b[2] end) end tab5.playersList.L[1] = L.BossWatcherAll for i,playersListTableData in ipairs(playersListTable) do tab5.playersList.L[i+1] = playersListTableData[3]..playersListTableData[2] tab5.playersList.IndexToGUID[i+1] = playersListTableData[1] end tab5.playersList.selected = 1 tab5.playersCastsList.redTime = nil tab5.playersCastsList.redIndex = nil tab5.playersList:Update() tab5.playersList:SetListValue(1) --tab5.playersCastsList:Update() end tab.chkFriendly = ELib:Radio(tab.page.tabs[1],L.BossWatcherFriendly,true):Point(15,-75):AddButton():OnClick(function(self) self:SetChecked(true) tab5.chkEnemy:SetChecked(false) tab5.chkSpellsCount:SetChecked(false) tab5.chkSpellsSummons:SetChecked(false) SpellsTab_Variables.Type = 1 UpdateSpellsPage() end) tab.chkEnemy = ELib:Radio(tab.page.tabs[1],L.BossWatcherHostile):Point(15,-90):AddButton():OnClick(function(self) self:SetChecked(true) tab5.chkFriendly:SetChecked(false) tab5.chkSpellsCount:SetChecked(false) tab5.chkSpellsSummons:SetChecked(false) SpellsTab_Variables.Type = 2 UpdateSpellsPage() end) tab.chkSpellsCount = ELib:Radio(tab.page.tabs[1],L.BossWatcherSpellsCount):Point(15,-105):AddButton():OnClick(function(self) self:SetChecked(true) tab5.chkFriendly:SetChecked(false) tab5.chkEnemy:SetChecked(false) tab5.chkSpellsSummons:SetChecked(false) SpellsTab_Variables.Type = 3 UpdateSpellsPage() end) tab.chkSpellsSummons = ELib:Radio(tab.page.tabs[1],SUMMONS):Point(15,-120):AddButton():OnClick(function(self) self:SetChecked(true) tab5.chkFriendly:SetChecked(false) tab5.chkEnemy:SetChecked(false) tab5.chkSpellsCount:SetChecked(false) SpellsTab_Variables.Type = 4 UpdateSpellsPage() end) tab.chkFriendlyTab2 = ELib:Radio(tab.page.tabs[2],L.BossWatcherFriendly,true):Point(10,-55):AddButton():OnClick(function(self) self:SetChecked(true) tab5.chkEnemyTab2:SetChecked(false) SpellsTab_Variables.TypeP2 = 1 tab5.timelineFrame:UpdateDB() tab5.timelineFrame:Update() end) tab.chkEnemyTab2 = ELib:Radio(tab.page.tabs[2],L.BossWatcherHostile):Point(10,-70):AddButton():OnClick(function(self) self:SetChecked(true) tab5.chkFriendlyTab2:SetChecked(false) SpellsTab_Variables.TypeP2 = 2 tab5.timelineFrame:UpdateDB() tab5.timelineFrame:Update() end) do local scheduledUpdate tab.filterEditBox = ELib:Edit(tab.page.tabs[2]):Point("TOPLEFT",130,-46):Size(200,18):AddSearchIcon():Tooltip(FILTER.."\nplayer1,player2,55342,30451,s:innervate,s:hymn\nt:targetname\nft:targetname"):OnChange(function (self,isUser) if not isUser then return end local text = self:GetText() if text == "" then text = nil end SpellsTab_Variables.TLTargetFilter = nil if not text then SpellsTab_Variables.TLFilter = nil if scheduledUpdate then scheduledUpdate:Cancel() scheduledUpdate = nil end tab5.timelineFrame:UpdateDB() tab5.timelineFrame:Update() return end text = text:lower() SpellsTab_Variables.TLFilter = {strsplit(",",text)} for i=#SpellsTab_Variables.TLFilter,1,-1 do if strtrim(SpellsTab_Variables.TLFilter[i]) == "" then tremove(SpellsTab_Variables.TLFilter,i) elseif SpellsTab_Variables.TLFilter[i]:find("^[tT]:") then local t = strtrim(SpellsTab_Variables.TLFilter[i]:sub(3)):lower() if t ~= "" then SpellsTab_Variables.TLTargetFilter = SpellsTab_Variables.TLTargetFilter or {} SpellsTab_Variables.TLTargetFilter[#SpellsTab_Variables.TLTargetFilter+1] = t end tremove(SpellsTab_Variables.TLFilter,i) end end for i=1,#SpellsTab_Variables.TLFilter do SpellsTab_Variables.TLFilter[i] = tonumber(SpellsTab_Variables.TLFilter[i]) or SpellsTab_Variables.TLFilter[i] end if #SpellsTab_Variables.TLFilter == 0 then SpellsTab_Variables.TLFilter = nil end if not scheduledUpdate then scheduledUpdate = C_Timer.NewTimer(1,function() scheduledUpdate = nil tab5.timelineFrame:UpdateDB() tab5.timelineFrame:Update() end) end end) end tab.timelineFrame = ELib:ScrollFrame(tab.page.tabs[2]):Point("TOPLEFT",130,-90):Size(858-130,495-15-18):AddHorizontal(true) ELib:Border(tab.timelineFrame,0) ELib:DecorationLine(tab.page.tabs[2]):Point("BOTTOM",tab.timelineFrame,"TOP",0,0):Point("LEFT",tab.page.tabs[2]):Point("RIGHT",tab.page.tabs[2]):Size(0,1) tab.timelineFrame.LINE_HEIGHT = 24 tab.timelineFrame.PIX_PER_SEC = 50 tab.timelineFrameNames = ELib:ScrollFrame(tab.page.tabs[2]):Point("TOPLEFT",0,-90):Size(130,495-15-18) ELib:Border(tab.timelineFrameNames,0) tab.timelineFrameNames.ScrollBar:Hide() tab.timelineFrameNames.lines = {} tab.timelineFrame.ScrollNames = tab.timelineFrameNames tab.timelineFrameNames.mouseWheelRange = 0 tab.timelineFrameTimes = ELib:ScrollFrame(tab.page.tabs[2]):Point("TOPLEFT",130,-70):Size(858-130,20) ELib:Border(tab.timelineFrameTimes,0) tab.timelineFrameTimes.ScrollBar:Hide() tab.timelineFrameTimes.texts = {} tab.timelineFrameTimes:Height(20) tab.timelineFrame.ScrollTimes = tab.timelineFrameTimes tab.timelineFrame.lines = {} for i=1,ceil(495/tab.timelineFrame.LINE_HEIGHT)+2 do local line = CreateFrame("Frame",nil,tab.timelineFrame.C) tab.timelineFrame.lines[i] = line line:SetPoint("TOPLEFT",0,-(i-1)*tab.timelineFrame.LINE_HEIGHT) line:SetPoint("RIGHT",0,0) line:SetHeight(tab.timelineFrame.LINE_HEIGHT) ELib:DecorationLine(line):Point("TOP",line,"BOTTOM",0,0):Point("LEFT",tab.timelineFrame):Point("RIGHT",tab.timelineFrame):Size(0,1) line.icons = {} line:Hide() local lineN = CreateFrame("Frame",nil,tab.timelineFrameNames.C) tab.timelineFrameNames.lines[i] = lineN lineN:SetPoint("TOPLEFT",0,-(i-1)*tab.timelineFrame.LINE_HEIGHT) lineN:SetPoint("RIGHT",0,0) lineN:SetHeight(tab.timelineFrame.LINE_HEIGHT) ELib:DecorationLine(lineN):Point("TOP",lineN,"BOTTOM",0,0):Point("LEFT",tab.timelineFrameNames):Point("RIGHT",tab.timelineFrameNames):Size(0,1) lineN.text = ELib:Text(lineN,"Name"):Point("LEFT",5,0):Point("RIGHT",-5,0):Color():Tooltip():MaxLines(2) lineN:Hide() line.LineName = lineN end tab.stepSlider = ELib:Slider(tab.page.tabs[2],""):Point("BOTTOMRIGHT",tab.timelineFrameTimes,"TOPRIGHT",-10,10):Size(100):Range(3,100):SetTo(50):SetObey(true):OnChange(function(self) local val = floor(self:GetValue() + 0.5) tab5.timelineFrame.PIX_PER_SEC = val self:Tooltip(val) self:tooltipReload() tab5.timelineFrame:UpdateDB() tab5.timelineFrame:Update() end) tab.stepSlider.Low:Hide() tab.stepSlider.High:Hide() local function SpellPage_TimeLine_Spell_OnEnter(self) local data = self.data local spellID = data.spellID local spellName,_,spellTexture = GetSpellInfo(spellID) GameTooltip:SetOwner(self,"ANCHOR_LEFT") GameTooltip:SetText("|T"..(spellTexture or 134400)..":20|t "..spellName) local t = data.time local castTime if data.time_end or data.failed then GameTooltip:AddLine(format("%d:%02d.%03d %s",t/60,t%60,t%1*1000,L.BossWatcherTimeLineCastStart)) if data.time_end then castTime = data.time_end - t t = data.time_end end if data.interrupt then t = data.interrupt - data.time GameTooltip:AddLine(format("<%d.%03ds> %s",t,t%1*1000,L.BossWatcherInterruptText)) local ispellName,_,ispellTexture = GetSpellInfo(data.interrupt_data[3]) GameTooltip:AddLine(format("%s %s %s %s", "|c"..ExRT.F.classColorByGUID(data.interrupt_data[1])..GetGUID(data.interrupt_data[1]) .. GUIDtoText(" <%s>",data.interrupt_data[1]).."|r", L.BossWatcherInterruptText,L.BossWatcherByText, "|T"..(ispellTexture or 134400)..":16|t "..ispellName )) end end if not data.failed then GameTooltip:AddLine(format("%d:%02d.%03d %s %s %s",t/60,t%60,t%1*1000,L.BossWatcherTimeLineCast,L.BossWatcherBuffsAndDebuffsTextOn,"|c"..ExRT.F.classColorByGUID(data.target)..GetGUID(data.target) .. GUIDtoText(" <%s>",data.target))) if castTime then GameTooltip:AddLine(format("<%d.%03ds>",castTime,castTime%1*1000)) end end if data.prevTime then t = data.time - data.prevTime GameTooltip:AddLine(format("-%d.%03ds",t,t%1*1000)) end GameTooltip:AddSpellByID(spellID) GameTooltip:AddLine("SpellID: "..spellID) GameTooltip:Show() end local function SpellPage_TimeLine_Spell_OnLeave(self) GameTooltip_Hide() end function tab.timelineFrame:GetIcon(line,tdata) local firstHidden for i=1,#line.icons do if line.icons[i].data == tdata then return line.icons[i], true elseif not firstHidden and not line.icons[i]:IsShown() then firstHidden = line.icons[i] end end if firstHidden then return firstHidden end local icon = CreateFrame("Frame",nil,line) line.icons[#line.icons+1] = icon icon:SetSize(22,22) icon.t = ELib:Texture(icon):Point("LEFT",0,0):Size(22,22) icon.l = ELib:Texture(icon,1,1,1,1,"BACKGROUND"):Point("LEFT",0,0):Size(22,22):Shown(false) icon:SetScript("OnEnter",SpellPage_TimeLine_Spell_OnEnter) icon:SetScript("OnLeave",SpellPage_TimeLine_Spell_OnLeave) return icon end function tab.timelineFrame:DBRecordSetTargetFilter(db) if SpellsTab_Variables.TLTargetFilter then local filterNotTarget for j=1,#SpellsTab_Variables.TLTargetFilter do local name = GetGUID(db.target)..GUIDtoText(" <%s>",db.target) if name:lower():find(SpellsTab_Variables.TLTargetFilter[j]) then filterNotTarget = true break end end if not filterNotTarget then db.filterNotTarget = true else db.filterNotTarget = false end end end function tab.timelineFrame:UpdateDB() local data = {} local SpellsTab_isFriendly = SpellsTab_Variables.TypeP2 == 1 local interruptGUIDs = {} for _,data in pairs(CurrentFight.interrupts) do local d = interruptGUIDs[ data[2] ] if not d then d = {} interruptGUIDs[ data[2] ] = d end d[#d+1] = data end local reaction = SpellsTab_isFriendly and 256 or 512 for sourceGUID,sourceData in pairs(CurrentFight.cast) do if ExRT.F.GetUnitInfoByUnitFlag(CurrentFight.reaction[sourceGUID],2) == reaction then local guidData = { guid = sourceGUID, name = GetGUID(sourceGUID) .. GUIDtoText(" <%s>",sourceGUID), nameUnmod = GetGUID(sourceGUID):lower(), isPlayer = sourceGUID:find("^Player") and 1 or 0, } local castStarted,castStartedPrev local prevTime = 0 for i=1,#sourceData do local cast = sourceData[i] local spellID = cast[2] local toAdd = false if SpellsTab_Variables.TLFilter then for j=1,#SpellsTab_Variables.TLFilter do local f = SpellsTab_Variables.TLFilter[j] if type(f) == "number" then if spellID == f then toAdd = true end elseif f:find("^[sS]:") then f = f:sub(3) if #f > 0 and GetSpellInfo(spellID):lower():find(f) then toAdd = true end elseif f:find("^[fF][tT]:") then local targetGUID = cast[4] if targetGUID then local tarName = GetGUID(targetGUID)..GUIDtoText(" <%s>",targetGUID) f = f:sub(4) if #f > 0 and tarName:lower():find(f) then toAdd = true end end else if #f > 0 and guidData.nameUnmod:find(f) then toAdd = true end end end else toAdd = true end if SpellsTab_Variables.TimeLineBlackList[spellID] then toAdd = false end if toAdd then local new if cast[3] == 1 and castStarted and castStarted.spellID == spellID then castStarted.time_end = timestampToFightTime(cast[1]) castStarted.end_data = cast castStarted.target = cast[4] self:DBRecordSetTargetFilter(castStarted) prevTime = castStarted.time_end elseif cast[3] == 1 and castStartedPrev and castStartedPrev.spellID == spellID then castStartedPrev.time_end = timestampToFightTime(cast[1]) castStartedPrev.end_data = cast castStartedPrev.target = cast[4] castStartedPrev.failed = nil self:DBRecordSetTargetFilter(castStartedPrev) prevTime = castStartedPrev.time_end else if castStarted then castStarted.failed = true end new = { time = timestampToFightTime(cast[1]), spellID = spellID, data = cast, prevTime = prevTime, target = cast[4], } self:DBRecordSetTargetFilter(new) guidData[#guidData+1] = new prevTime = new.time end if cast[3] == 2 then castStarted = new castStartedPrev = nil else castStartedPrev = castStarted castStarted = nil end end end if castStarted then castStarted.failed = true end local interrupts = interruptGUIDs[sourceGUID] if interrupts then local start = 1 for i=1,#interrupts do local data = interrupts[i] local t = timestampToFightTime(data[5]) local spellID = data[4] local prevdata for j=start,#guidData do local cdata = guidData[j] if cdata.time > t then start = j break end prevdata = cdata end if prevdata and prevdata.failed and (prevdata.spellID == spellID) then prevdata.interrupt = t prevdata.interrupt_data = data end end end if #guidData > 0 then data[#data+1] = guidData end end end self:Height(self.LINE_HEIGHT * #data + 20) self.ScrollNames:Height(self.LINE_HEIGHT * #data + 20) local fight_dur = GetFightLength(true) self:Width(max(900,self.PIX_PER_SEC * (fight_dur+2))) self.ScrollTimes.C:SetWidth(max(900,self.PIX_PER_SEC * (fight_dur+2))) local timeTextCount = ceil((858-130)/self.PIX_PER_SEC)+3 local itCount = 1 if timeTextCount < 25 then itCount = 1 elseif timeTextCount < 50 then itCount = 2 timeTextCount = ceil(timeTextCount/2) elseif timeTextCount < 75 then itCount = 3 timeTextCount = ceil(timeTextCount/3) elseif timeTextCount < 100 then itCount = 5 timeTextCount = ceil(timeTextCount/5) else itCount = 10 timeTextCount = ceil(timeTextCount/10) end self.ScrollTimes.MAX = timeTextCount self.ScrollTimes.ITcount = itCount do local index = 0 while index < timeTextCount do index = index + 1 local text = self.ScrollTimes.texts[index] if not text then text = ELib:Frame(self.ScrollTimes.C):Size(100,20) text.t = ELib:Text(text,"Time"):Color():Point('x'):Middle():Left() text.l = ELib:DecorationLine(text):Point("LEFT",text.t,"LEFT",0,0):Point("BOTTOM",self,"TOP"):Size(2,7) end text:Show() self.ScrollTimes.texts[index] = text end for i=index+1,#self.ScrollTimes.texts do self.ScrollTimes.texts[i]:Hide() end end sort(data,function(a,b) if a.isPlayer == b.isPlayer then return a.name < b.name else return a.isPlayer > b.isPlayer end end) self.data = data end local function SetSchoolColorToTexture(self,school) if school == -1 then self:Color(1,.3,.3,1) return elseif school == -2 then self:Color(.3,.3,1,1) return end local isNotGradient = ExRT.F.table_find(module.db.schoolsDefault,school) or school == 0 or module.db.schoolsColors[school or -1] local isConfirmedGradient = module.db.schoolsColorsGradient[ school or -1 ] if isNotGradient and module.db.schoolsColors[school] then self:Color(1,1,1,1) self:SetColorTexture(module.db.schoolsColors[school].r,module.db.schoolsColors[school].g,module.db.schoolsColors[school].b, 1) elseif isConfirmedGradient then local school1,school2 = isConfirmedGradient[1],isConfirmedGradient[2] self:SetColorTexture(1,1,1,1) self:SetGradient("HORIZONTAL",CreateColor(module.db.schoolsColors[school1].r,module.db.schoolsColors[school1].g,module.db.schoolsColors[school1].b,1), CreateColor(module.db.schoolsColors[school2].r,module.db.schoolsColors[school2].g,module.db.schoolsColors[school2].b,1)) else local school1,school2 = nil for i=1,#module.db.schoolsDefault do local isSchool = bit.band(school,module.db.schoolsDefault[i]) > 0 if isSchool and not school1 then school1 = module.db.schoolsDefault[i] elseif isSchool and not school2 then school2 = module.db.schoolsDefault[i] end end if school1 and school2 then self:SetColorTexture(1,1,1,1) self:SetGradient("HORIZONTAL",CreateColor(module.db.schoolsColors[school1].r,module.db.schoolsColors[school1].g,module.db.schoolsColors[school1].b,1), CreateColor(module.db.schoolsColors[school2].r,module.db.schoolsColors[school2].g,module.db.schoolsColors[school2].b,1)) elseif school1 and not school2 then self:Color(1,1,1,1) self:SetColorTexture(module.db.schoolsColors[school1].r,module.db.schoolsColors[school1].g,module.db.schoolsColors[school1].b, 1) else self:Color(1,1,1,1) self:SetColorTexture(1,1,1,1) end end end function tab.timelineFrame:Update() local scroll = self.ScrollBar:GetValue() self:SetVerticalScroll(scroll % self.LINE_HEIGHT) self.ScrollNames:SetVerticalScroll(scroll % self.LINE_HEIGHT) local start = floor(scroll / self.LINE_HEIGHT) + 1 local scrollH = self.ScrollBarHorizontal:GetValue() self:SetHorizontalScroll(scrollH % self.PIX_PER_SEC) self.ScrollTimes:SetHorizontalScroll(scrollH % self.PIX_PER_SEC) local startH = floor(scrollH / self.PIX_PER_SEC) local needRepos = (startH ~= self.prevStartH) or (start ~= self.prevStart) or (self.PIX_PER_SEC ~= self.prevPIX_PER_SEC) local visMaxTime = startH + (858-130) / self.PIX_PER_SEC + 1 local DATA = self.data local lineCount = 1 for i=start,#DATA do local data = DATA[i] local line = self.lines[lineCount] lineCount = lineCount + 1 if not line then break end for j=1,#line.icons do line.icons[j].iter = nil end line.posnow = nil for j=1,#data do local tdata = data[j] if ((tdata.time >= startH) or (tdata.time_end and tdata.time_end >= startH) or (tdata.interrupt and tdata.interrupt >= startH)) and (tdata.time < visMaxTime) then local icon,isExist = self:GetIcon(line,tdata) local width = 22 local pos = (tdata.time - startH) * self.PIX_PER_SEC if needRepos or not isExist then if line.posnow and pos < line.posnow and self.PIX_PER_SEC > 25 then pos = line.posnow end icon:SetPoint("LEFT",pos,0,0) if not isExist then local spellName,_,spellTexture = GetSpellInfo(tdata.spellID) icon.t:Texture(spellTexture or 134400) if tdata.failed then icon.t:Color(1,.3,.3,1) elseif tdata.filterNotTarget then icon.t:Color(.3,.3,1,1) else icon.t:Color(1,1,1,1) end if tdata.time_end then local school = module.db.spellsSchool[ tdata.spellID ] or 0 SetSchoolColorToTexture(icon.l,school) if tdata.filterNotTarget then SetSchoolColorToTexture(icon.l,-2) end local w = (tdata.time_end - tdata.time) * self.PIX_PER_SEC icon.l:SetWidth(w) icon.l:Show() width = max(22,w) icon:SetWidth(width) elseif tdata.interrupt then SetSchoolColorToTexture(icon.l,-1) local w = (tdata.interrupt - tdata.time) * self.PIX_PER_SEC icon.l:SetWidth(w) icon.l:Show() width = max(22,w) icon:SetWidth(width) else icon.l:Hide() icon:SetWidth(22) end icon.data = tdata end icon:Show() end icon.iter = true line.posnow = pos + width - 6 end end local color = "|c"..ExRT.F.classColorByGUID(data.guid) if data.isPlayer == 1 then line.LineName.text.TooltipOverwrite = color..data.name line.LineName.text:SetText(color..strsplit("-",data.name)) line.LineName.text:FontSize(12) else line.LineName.text.TooltipOverwrite = nil line.LineName.text:SetText(color..data.name) line.LineName.text:FontSize(10) end for j=1,#line.icons do local icon = line.icons[j] if not icon.iter then icon.data = nil icon:Hide() end end line:Show() line.LineName:Show() end for i=lineCount,#self.lines do local line = self.lines[i] line:Hide() line.LineName:Hide() end if needRepos then local limit = self.ScrollTimes.MAX local itCount = self.ScrollTimes.ITcount local index = 0 local t_pos = startH local t_fix = 0 while (startH + t_fix) % itCount ~= 0 do t_fix = t_fix - 1 end t_pos = t_pos + t_fix while index < limit do index = index + 1 local text = self.ScrollTimes.texts[index] text.t:SetFormattedText("%d:%02d",t_pos / 60, t_pos % 60) text:Point("LEFT",self.ScrollTimes.C,"TOPLEFT",(t_pos - startH)*self.PIX_PER_SEC,-8) t_pos = t_pos + itCount end end self.prevStartH = startH self.prevStart = start self.prevPIX_PER_SEC = self.PIX_PER_SEC end tab.timelineFrame.ScrollBar.slider:SetScript("OnValueChanged", function(self) self:GetParent():GetParent():Update() self:UpdateButtons() end) tab.timelineFrame.ScrollBarHorizontal.slider:SetScript("OnValueChanged", function(self) self:GetParent():GetParent():Update() self:UpdateButtons() end) function SpellsPage_GetCastsNumber(guidsSourceTable,guidsDestTable,spellID) local allData = not spellID and {} local count = 0 local spellName = GetSpellInfo(spellID) for GUID,dataGUID in pairs(CurrentFight.cast) do if not guidsSourceTable or guidsSourceTable[GUID] then for i,PlayerCastData in ipairs(dataGUID) do if allData and PlayerCastData[3] ~= 2 and CurrentFight.segments[ PlayerCastData.s ].e and (not guidsDestTable or not PlayerCastData[4] or guidsDestTable[ PlayerCastData[4] ]) then allData[ PlayerCastData[2] ] = (allData[ PlayerCastData[2] ] or 0) + 1 elseif not allData and PlayerCastData[3] ~= 2 and (PlayerCastData[2] == spellID or (spellName and spellName == GetSpellInfo(PlayerCastData[2]))) and CurrentFight.segments[ PlayerCastData.s ].e then count = count + 1 end end end end if allData then local byName = {} for spellID,count in pairs(allData) do byName[ GetSpellInfo(spellID) or "?" ] = count end for name,count in pairs(byName) do allData[name] = count end end return allData or count end tab:SetScript("OnShow",function (self) BWInterfaceFrame.timeLineFrame:ClearAllPoints() BWInterfaceFrame.timeLineFrame:SetPoint("TOP",self.page.tabs[1],"TOP",0,-10) BWInterfaceFrame.timeLineFrame:Show() if BWInterfaceFrame.nowFightID ~= self.lastFightID then UpdateSpellsPage() tab5.timelineFrame:UpdateDB() tab5.timelineFrame:Update() self.lastFightID = BWInterfaceFrame.nowFightID end end) tab:SetScript("OnHide",function (self) BWInterfaceFrame.timeLineFrame:Hide() end) ---- Power; HP, Powers Graphs tab = BWInterfaceFrame.tab.tabs[6] local graphsTab = tab local PowerTab_isFriendly = true local GraphsTab_Variables = { DestTable = {}, LastGUID = nil, LastDoEnemy = nil, LastDoSpell = nil, PowerTypeNow = 0, PowerLastName = nil, HealthTypeNow = 1, HealthLastName = nil, Cache = {}, } tab.DecorationLine = ELib:DecorationLine(tab,true):Point("TOPLEFT",tab,"TOPLEFT",3,-9):Point("RIGHT",tab,-3,0):Size(0,20) tab.graphicsTab = ELib:Tabs(tab,0, POWER_GAINS, L.BossWatcherTabGraphics..": "..L.BossWatcherGraphicsHealth, L.BossWatcherTabGraphics..": "..L.BossWatcherGraphicsPower ):Size(850,555):Point("TOP",0,-29):SetTo(1) tab.graphicsTab:SetBackdropBorderColor(0,0,0,0) tab.graphicsTab:SetBackdropColor(0,0,0,0) function tab.graphicsTab:buttonAdditionalFunc() local tab = graphsTab if self.selected == 1 then tab.graphicsTab.dropDown:Hide() tab.graph:Hide() tab.graphicsTab.powerDropDown:Hide() tab.graphicsTab.healthDropDown:Hide() tab.graphicsTab.bySpellDropDown:Hide() tab.graphicsTab.byTargetDropDown:Hide() tab.graphicsTab.stepSlider:Hide() else tab.graphicsTab.dropDown:Show() tab.graph:Show() tab.graphicsTab.stepSlider:Show() end end tab.graphicsTab.dropDown = ELib:DropDown(tab.graphicsTab,220,10):Size(220):Point(15,-10) tab.graph = ExRT.lib.CreateGraph(tab.graphicsTab,760,485,"TOP",0,-50,true) tab.graph.axisXisTime = true function tab.graphicsTab.dropDown:additionalToggle() local graphData = graphsTab.graph.data for i=1,#self.List do if self.List[i].checkable then local findPos = ExRT.F.table_find(graphData,self.List[i].arg1,'private_name') if findPos then self.List[i].checkState = not graphData[ findPos ].hide end end end end tab.graphicsTab.powerDropDown = ELib:DropDown(tab.graphicsTab,200,ExRT.F.table_len(module.db.energyLocale)):Size(220):Point(560,-10):SetText(L.BossWatcherSelectPower..module.db.energyLocale[0]) tab.graphicsTab.healthDropDown = ELib:DropDown(tab.graphicsTab,200,2):Size(220):Point(560,-10):SetText(L.BossWatcherSelectPower..(HEALTH or "Health")) tab.graphicsTab.bySpellDropDown = ELib:DropDown(tab.graphicsTab,250,10):Size(145):Point(540,-10):SetText(L.BossWatcherTabPlayersSpells):Tooltip(L.BossWatcherGraphicsHoldShift) tab.graphicsTab.byTargetDropDown = ELib:DropDown(tab.graphicsTab,250,10):Size(145):Point(695,-10):SetText(L.BossWatcherGraphicsTargets) tab.graphicsTab.stepSlider = ELib:Slider(tab.graphicsTab,L.BossWatcherGraphicsStep):Size(250):Point(270,-15):Range(1,1):OnChange(function (self,value) value = ExRT.F.Round(value) self.tooltipText = value self:tooltipReload(self) if self.disableUpdateFix then return end graphsTab.graph.step = value graphsTab.graph:Reload() end) tab.graphicsTab.stepSlider:SetScript("OnMinMaxChanged",function (self) local _min,_max = self:GetMinMaxValues() self.Low:SetText(_min) self.High:SetText(_max) end) tab.graph:SetScript("OnLeave",function () GameTooltip_Hide() end) tab.graphZoomDropDown = CreateFrame("Frame", BWInterfaceFrame_Name.."GraphZoomDropDown", nil, "UIDropDownMenuTemplate") function tab.graph:Zoom(start,ending) local zoomList = { { text = L.BossWatcherGraphZoom, isTitle = true, notCheckable = true, notClickable = true }, { text = L.BossWatcherGraphZoomOnlyGraph, notCheckable = true, func = function() self.ZoomMinX = start self.ZoomMaxX = ending self:Reload() end, }, { text = L.BossWatcherGraphZoomGlobal, notCheckable = true, func = function() self.ZoomMinX = start self.ZoomMaxX = ending self:Reload() UpdateSegments(start,ending,nil,true) end, }, { text = L.BossWatcherSelectFightClose, notCheckable = true, func = CloseDropDownMenus_fix, } } EasyMenu(zoomList, graphsTab.graphZoomDropDown, "cursor", 10 , -15, "MENU") end local function GraphGetFightMax() local i = 0 for sec,data in pairs(CurrentFight.graphData) do i=max(sec,i) end return i end local function GraphHealthSelect(_,name) GraphsTab_Variables.HealthLastName = name local currTab = graphsTab local graphTypeName = GraphsTab_Variables.HealthTypeNow == 1 and "health" or "absorbs" local myGraphData = {} local maxFight = GraphGetFightMax() for sec,data in pairs(CurrentFight.graphData) do local health = data[name] and data[name][graphTypeName] or 0 local maxHP = data[name] and data[name].hpmax or 0 local maxHPtext = "" if maxHP > 0 then maxHPtext = format(" [%.1f%%]",(health or 0) / maxHP * 100) end local comment = (data[name] and data[name].name or "") .. maxHPtext if comment == "" then comment = nil end health = health or 0 myGraphData[#myGraphData + 1] = {sec,health,format("%d:%02d",sec/60,sec%60),nil,comment} end table.sort(myGraphData,function(a,b)return a[1] 0 then currTab.graph.data[#currTab.graph.data + 1] = myGraphData else currTab.graph.data = {myGraphData} end currTab.graph:Reload() currTab.graphicsTab.stepSlider:SetMinMaxValues(1,max(1,maxFight)) currTab.graphicsTab.dropDown:SetText(name) ELib:DropDownClose() end function tab.graphicsTab.byTargetDropDown.additionalToggle(self) local tabNow = graphsTab.graphicsTab.selected for i=2,#self.List do self.List[i].checkState = GraphsTab_Variables.DestTable[self.List[i].arg1] if tabNow == 1 then self.List[i].isHidden = (self.List[i].isEnemy and GraphsTab_Variables.LastDoEnemy) or (not self.List[i].isEnemy and not GraphsTab_Variables.LastDoEnemy) end end end local function GraphTabLoad() ELib:DropDownClose() local currTab = graphsTab currTab.graph.data = {} currTab.graph:Reload() currTab.graphicsTab.dropDown:SetText(L.BossWatcherGraphicsSelect) table.wipe(currTab.graphicsTab.dropDown.List) currTab.graphicsTab.stepSlider:SetMinMaxValues(1,1) currTab.graphicsTab.stepSlider:SetValue(1) currTab.graphicsTab.dropDown.Lines = 10 currTab.graphicsTab.dropDown.tooltip = L.BossWatcherGraphicsHoldShift currTab.graphicsTab.powerDropDown:Hide() currTab.graphicsTab.healthDropDown:Hide() currTab.graphicsTab.bySpellDropDown:Hide() currTab.graphicsTab.byTargetDropDown:Hide() GraphsTab_Variables.LastGUID = nil GraphsTab_Variables.LastDoEnemy = nil GraphsTab_Variables.LastDoSpell = nil end local GraphTab_SpecialUnits = { ["_total"] = "*1", ["boss1"] = "*2", ["boss2"] = "*3", ["boss3"] = "*4", ["boss4"] = "*5", ["boss5"] = "*6", ["boss6"] = "*7", ["target"] = "*8", ["focus"] = "*9", } local function GraphTab_UnitDropDown_Check(self,checked) local graphData = graphsTab.graph.data local findPos = ExRT.F.table_find(graphData,self.arg1,'private_name') if findPos then graphData[ findPos ].hide = not checked end graphsTab.graph:Reload() end local function GraphTab_UnitDropDown_Click(self,arg1) ELib:DropDownClose() local graphData = graphsTab.graph.data for _,data in pairs(graphData) do data.hide = data.private_name ~= arg1 end graphsTab.graph:Reload() end local function GraphTab_ReloadPage(_,newPowerID) local powerID = newPowerID or GraphsTab_Variables.PowerTypeNow GraphsTab_Variables.PowerTypeNow = powerID ELib:DropDownClose() graphsTab.graphicsTab.powerDropDown:SetText(L.BossWatcherSelectPower..module.db.energyLocale[powerID]) wipe(graphsTab.graphicsTab.dropDown.List) local graphData = {} local maxFight = GraphGetFightMax() local units = GraphsTab_Variables.Cache[powerID] if units then for i=1,#units do local name = units[i] local info = {} graphsTab.graphicsTab.dropDown.List[i] = info local unitGUID = ExRT.F.table_find2(CurrentFight.raidguids,name) info.text = (unitGUID and "|c"..ExRT.F.classColorByGUID(unitGUID) or "")..name info.arg1 = name info.func = GraphTab_UnitDropDown_Click info.checkFunc = GraphTab_UnitDropDown_Check info.checkable = true --info.justifyH = "CENTER" info._sort = GraphTab_SpecialUnits[ name ] or name local class,r,g,b = nil if unitGUID and unitGUID ~= "" then class = select(2,GetPlayerInfoByGUID(unitGUID)) end if class then local classColorArray = type(CUSTOM_CLASS_COLORS)=="table" and CUSTOM_CLASS_COLORS[class] or RAID_CLASS_COLORS[class] r,g,b = classColorArray.r,classColorArray.g,classColorArray.b end local unitGraphData = { name = info.text, private_name = name, hide = false, r = r, g = g, b = b, } if powerID == 0 then unitGraphData.hide = CurrentFight.other.roles[name] ~= "HEALER" end for sec,data in pairs(CurrentFight.graphData) do local dataPower = data[name] if dataPower and dataPower[powerID] then unitGraphData[#unitGraphData + 1] = {sec,dataPower[powerID],format("%d:%02d",sec/60,sec%60),nil,data[name].name} else unitGraphData[#unitGraphData + 1] = {sec,0,format("%d:%02d",sec/60,sec%60),nil,dataPower and dataPower.name} end end table.sort(unitGraphData,function(a,b)return a[1] b[4] end) EnergyClearLines() if #spellList > 1 then for i,spellData in ipairs(spellList) do local line = graphsTab.spells[i] if line then line.texture:SetTexture(spellData[3]) line.spellName:SetText(spellData[2]) line.amount:SetText(spellData[4]) line.count:SetText(spellData[5].." |4"..L.BossWatcherEnergyOnce1..":"..L.BossWatcherEnergyOnce2..":"..L.BossWatcherEnergyOnce1) line.spellID = spellData[1] line:Show() end end end end local function UpdatePowerPage() table.wipe(graphsTab.sourceList.L) table.wipe(graphsTab.sourceList.IndexToGUID) table.wipe(graphsTab.powerTypeList.L) table.wipe(graphsTab.powerTypeList.IndexToGUID) local sourceListTable = {} for sourceGUID,sourceData in pairs(CurrentFight.power) do if (PowerTab_isFriendly and ExRT.F.GetUnitInfoByUnitFlag(CurrentFight.reaction[sourceGUID],1) == 1024) or (not PowerTab_isFriendly and ExRT.F.GetUnitInfoByUnitFlag(CurrentFight.reaction[sourceGUID],2) == 512) then sourceListTable[#sourceListTable + 1] = {sourceGUID,GetGUID( sourceGUID ),"|c"..ExRT.F.classColorByGUID(sourceGUID)} end end table.sort(sourceListTable,function (a,b) return a[2] < b[2] end) for i,sourceData in ipairs(sourceListTable) do graphsTab.sourceList.L[i] = sourceData[3]..sourceData[2] graphsTab.sourceList.IndexToGUID[i] = sourceData[1] end graphsTab.sourceList.selected = nil graphsTab.powerTypeList.selected = nil graphsTab.sourceList:Update() graphsTab.powerTypeList:Update() EnergyClearLines() end tab.chkFriendly = ELib:Radio(tab.graphicsTab.tabs[1],L.BossWatcherFriendly,true):Point(15,-10):AddButton():OnClick(function(self) self:SetChecked(true) graphsTab.chkEnemy:SetChecked(false) PowerTab_isFriendly = true UpdatePowerPage() end) tab.chkEnemy = ELib:Radio(tab.graphicsTab.tabs[1],L.BossWatcherHostile):Point(15,-25):AddButton():OnClick(function(self) self:SetChecked(true) graphsTab.chkFriendly:SetChecked(false) PowerTab_isFriendly = nil UpdatePowerPage() end) do local firstLoad = nil tab:SetScript("OnShow",function (self) if BWInterfaceFrame.nowFightID ~= self.lastFightID then if not firstLoad then firstLoad = true graphsTab.graphicsTab:buttonAdditionalFunc() end GraphsTab_Variables.PowerLastName = nil GraphsTab_Variables.HealthLastName = nil self.lastFightID = BWInterfaceFrame.nowFightID UpdatePowerPage() end end) end ---- Interrupt & dispels tab = BWInterfaceFrame.tab.tabs[7] local interruptTab = tab tab.DecorationLine = ELib:DecorationLine(tab,true):Point("TOPLEFT",tab,"TOPLEFT",3,-80):Point("RIGHT",tab,-3,0):Size(0,20) do local broke = ACTION_SPELL_AURA_BROKEN broke = (ExRT.F.utf8sub(broke,1,1):upper())..ExRT.F.utf8sub(broke,2,-1) tab.tabs = ELib:Tabs(tab,0, L.BossWatcherInterrupts, L.BossWatcherDispels, broke ):Size(845,485):Point("TOP",0,-100) end tab.tabs.tabs[3].button.tooltip = L.BossWatcherBrokeTooltip tab.tabs:SetBackdropBorderColor(0,0,0,0) tab.tabs:SetBackdropColor(0,0,0,0) local Intterupt_Type = 1 local UpdateInterruptPage = nil function tab.tabs:buttonAdditionalFunc() UpdateInterruptPage() end tab.bySource = ELib:Radio(tab.tabs,L.BossWatcherBySource,true):Point(10,-3):AddButton():OnClick(function(self) self:SetChecked(true) interruptTab.byTarget:SetChecked(false) interruptTab.bySpell:SetChecked(false) Intterupt_Type = 1 UpdateInterruptPage() end) tab.byTarget = ELib:Radio(tab.tabs,L.BossWatcherByTarget):Point(10,-18):AddButton():OnClick(function(self) self:SetChecked(true) interruptTab.bySource:SetChecked(false) interruptTab.bySpell:SetChecked(false) Intterupt_Type = 2 UpdateInterruptPage() end) tab.bySpell = ELib:Radio(tab.tabs,L.BossWatcherBySpell):Point(10,-33):AddButton():OnClick(function(self) self:SetChecked(true) interruptTab.byTarget:SetChecked(false) interruptTab.bySource:SetChecked(false) Intterupt_Type = 3 UpdateInterruptPage() end) tab.list = ELib:ScrollList(tab):Size(190,434):Point(14,-155) tab.list.GUIDs = {} tab.events = ELib:ScrollList(tab):Size(637,481):Point(214,-108) tab.events.DATA = {} function tab.list:SetListValue(index) local currTab = interruptTab local filter = currTab.list.GUIDs[index] local isInterrupt = currTab.tabs.selected == 1 local isDispel = currTab.tabs.selected == 2 local isBroke = currTab.tabs.selected == 3 local workTable = CurrentFight.interrupts if isBroke then workTable = CurrentFight.aurabroken elseif not isInterrupt then workTable = CurrentFight.dispels end table.wipe(currTab.events.L) table.wipe(currTab.events.DATA) if index == 2 then local data = {} for i,line in ipairs(workTable) do local toAdd = nil if Intterupt_Type == 1 then toAdd = line[1] elseif Intterupt_Type == 2 then toAdd = line[2] elseif Intterupt_Type == 3 then toAdd = line[4] end if toAdd and CurrentFight.segments[ line.s ].e then local pos = ExRT.F.table_find(data,toAdd,1) if pos then data[pos][2] = data[pos][2] + 1 else data[#data + 1] = {toAdd,1,line} end end end sort(data,function(a,b) return a[2]>b[2] end) for i=1,#data do local name = data[i][1] if Intterupt_Type == 3 then local spellName,_,spellTexture = GetSpellInfo(name) name = "|T"..spellTexture..":0|t ".. spellName else name = "|c".. ExRT.F.classColorByGUID(name) .. GetGUID(name) .. GUIDtoText(" (%s)",name).."|r" end currTab.events.L[#currTab.events.L + 1] = name .. ": " ..data[i][2] currTab.events.DATA[#currTab.events.L] = data[i][3] end else local SpellCanBeKicked = {} local SpellDispel = {} local resultData = {} for i,line in ipairs(workTable) do local isOkay = false if (Intterupt_Type == 1 and (not filter or line[1] == filter)) or (Intterupt_Type == 2 and (not filter or line[2] == filter)) or (Intterupt_Type == 3 and (not filter or line[4] == filter)) then isOkay = true end if isOkay then if isInterrupt and not line[2]:find("^Player%-") then SpellCanBeKicked[ line[4] ] = true elseif isDispel then SpellDispel[ line[3] ] = true end if CurrentFight.segments[ line.s ].e then local spellSourceName,_,spellSourceTexture = GetSpellInfo(line[3]) local spellDestName,_,spellDestTexture = GetSpellInfo(line[4]) local dispelOrInterrupt = L.BossWatcherDispelText local brokeType = nil if isInterrupt then dispelOrInterrupt = L.BossWatcherInterruptText elseif isBroke then dispelOrInterrupt = ACTION_SPELL_AURA_BROKEN brokeType = line[6] and " ("..line[6]:lower()..")" spellSourceName,spellSourceTexture,spellDestName,spellDestTexture = spellDestName,spellDestTexture,spellSourceName,spellSourceTexture end local sourceMarker = module.db.raidTargets[ isBroke and line[7] or line[6] or 0 ] local destMarker = module.db.raidTargets[ isBroke and line[8] or line[7] or 0 ] resultData[#resultData+1] = { "[".. date("%M:%S", timestampToFightTime(line[5])) .. format(".%03d",timestampToFightTime(line[5]) * 1000 % 1000).."] |c".. ExRT.F.classColorByGUID(line[1]) ..(sourceMarker and GetTargetIconText(sourceMarker) or "") .. GetGUID(line[1]) .. GUIDtoText(" (%s)",line[1]) .. "|r "..dispelOrInterrupt.." |c" .. ExRT.F.classColorByGUID(line[2])..(destMarker and GetTargetIconText(destMarker) or "").. GetGUID(line[2]) .. "'s" .. GUIDtoText(" (%s)",line[2]) .. "|r |Hspell:" .. (line[4] or 0) .. "|h" .. format("%s%s",spellDestTexture and "|T"..spellDestTexture..":0|t " or "",spellDestName or "???") .. "|h"..(brokeType or "").." "..L.BossWatcherByText.." |Hspell:" .. (line[3] or 0) .. "|h" .. format("%s%s",spellSourceTexture and "|T"..spellSourceTexture..":0|t " or "",spellSourceName or "???") .. "|h", line, line[5], i, } end end end if isInterrupt and not filter then local subCount = 0 for GUID,dataGUID in pairs(CurrentFight.cast) do for i,PlayerCastData in ipairs(dataGUID) do if CurrentFight.segments[ PlayerCastData.s ].e and SpellCanBeKicked[ PlayerCastData[2] ] and PlayerCastData[3] == 1 then local spellSourceName,_,spellSourceTexture = GetSpellInfo(PlayerCastData[2]) local sourceMarker = module.db.raidTargets[ PlayerCastData[6] or 0 ] local destMarker = module.db.raidTargets[ PlayerCastData[5] or 0 ] subCount = subCount +1 resultData[#resultData+1] = { "|cffff9999[".. date("%M:%S", timestampToFightTime(PlayerCastData[1])).. format(".%03d",timestampToFightTime(PlayerCastData[1]) * 1000 % 1000).."] |TInterface\\AddOns\\"..GlobalAddonName.."\\media\\DiesalGUIcons16x256x128:16:16:0:0:256:128:128:144:64:80|t |c".. ExRT.F.classColorByGUID(GUID) ..(sourceMarker and GetTargetIconText(sourceMarker) or "") .. GetGUID(GUID) .. GUIDtoText(" (%s)",GUID) .. "|r "..L.BossWatcherTimeLineCast.." |Hspell:" .. (PlayerCastData[3] or 0) .. "|h" .. format("%s%s",spellSourceTexture and "|T"..spellSourceTexture..":0|t " or "",spellSourceName or "???") .. "|h"..(PlayerCastData[4] and PlayerCastData[4] ~= "" and (" > |c".. ExRT.F.classColorByGUID(PlayerCastData[4]) ..(destMarker and GetTargetIconText(destMarker) or "") .. GetGUID(PlayerCastData[4]) .. GUIDtoText(" (%s)",PlayerCastData[4]) .. "|r") or ""), {nil,nil,nil,PlayerCastData[2],PlayerCastData[1]}, PlayerCastData[1], subCount, } end end end end if isDispel and not filter then local subCount = 0 for GUID,dataGUID in pairs(CurrentFight.cast) do for i,PlayerCastData in ipairs(dataGUID) do if CurrentFight.segments[ PlayerCastData.s ].e and SpellDispel[ PlayerCastData[2] ] and PlayerCastData[3] == 1 then local spellSourceName,_,spellSourceTexture = GetSpellInfo(PlayerCastData[2]) local sourceMarker = module.db.raidTargets[ PlayerCastData[6] or 0 ] local destMarker = module.db.raidTargets[ PlayerCastData[5] or 0 ] local findEm = nil for j=1,#resultData do if GUID == resultData[j][2][1] and abs(resultData[j][3] - PlayerCastData[1]) < 0.5 then findEm = true break elseif (resultData[j][3] - PlayerCastData[1]) > 2 then break end end if not findEm then subCount = subCount + 1 resultData[#resultData+1] = { "|cffff9999[".. date("%M:%S", timestampToFightTime(PlayerCastData[1])).. format(".%03d",timestampToFightTime(PlayerCastData[1]) * 1000 % 1000).."] |TInterface\\AddOns\\"..GlobalAddonName.."\\media\\DiesalGUIcons16x256x128:16:16:0:0:256:128:128:144:64:80|t |c".. ExRT.F.classColorByGUID(GUID) ..(sourceMarker and GetTargetIconText(sourceMarker) or "") .. GetGUID(GUID) .. GUIDtoText(" (%s)",GUID) .. "|r "..L.BossWatcherTimeLineCast.." |Hspell:" .. (PlayerCastData[3] or 0) .. "|h" .. format("%s%s",spellSourceTexture and "|T"..spellSourceTexture..":0|t " or "",spellSourceName or "???") .. "|h" ..(PlayerCastData[4] and PlayerCastData[4] ~= "" and (" > |c".. ExRT.F.classColorByGUID(PlayerCastData[4]) ..(destMarker and GetTargetIconText(destMarker) or "") .. GetGUID(PlayerCastData[4]) .. GUIDtoText(" (%s)",PlayerCastData[4]) .. "|r") or ""), {nil,nil,nil,PlayerCastData[2],PlayerCastData[1]}, PlayerCastData[1], subCount, } end end end end end sort(resultData,function(a,b)if a[3]==b[3] then return a[4]b.total_healing end) local findPos = ExRT.F.table_find(graphData,-1,'info_spellID') if findPos then graphData[ findPos ].hide = nil graphData[ findPos ].specialLine = true end for i=1,3 do if linesData[i] then findPos = ExRT.F.table_find(graphData,linesData[i][isSpell and "spell" or "guid"],'info_spellID') if findPos then graphData[ findPos ].hide = nil end end end BWInterfaceFrame.GraphFrame.G.data = graphData BWInterfaceFrame.GraphFrame.G:Reload() end local function HealingTab_UpdateLines_GetUnit(heal,graph,source,dest,header,secondHeader) header = header or "guid" local sourceHeal for i=1,#heal do if heal[i][header] == source and (not secondHeader or heal[i].info == secondHeader) then sourceHeal = i break end end if not sourceHeal then sourceHeal = #heal + 1 heal[sourceHeal] = { [header] = source, info = secondHeader, eff = 0, total = 0, count = 0, overheal = 0, absorbed = 0, absorbs = 0, crit = 0, critcount = 0, critmax = 0, critover = 0, hitmax = 0, targets = {}, from = {}, } end sourceHeal = heal[sourceHeal] local destPos local targets = sourceHeal.targets for i=1,#targets do if targets[i][1] == dest then destPos = i break end end if not destPos then destPos = #sourceHeal.targets + 1 sourceHeal.targets[destPos] = {dest,0} end if not graph[ source ] then graph[ source ] = {} end return sourceHeal, sourceHeal.targets[destPos] end local function HealingTab_UpdateLinesPlayers() ExRT.F.dprint("Healing Update: Players",GetTime()) HealingTab_UpdateDropDowns() HealingTab_UpdateChecks() local doEnemy = not HealingTab_Variables.state_friendly local doReduction = HealingTab_Variables.state_mitigation local isReverse = HealingTab_Variables.state_byTarget local onlyReduction = doReduction local heal = {} local total = 0 local totalOver = 0 local graph = {[-1]={}} if not onlyReduction then for sourceGUID,sourceData in pairs(CurrentFight.heal) do local owner = ExRT.F.Pets:getOwnerGUID(sourceGUID,GetPetsDB()) if owner then sourceGUID = owner end if ExRT.F.table_len(HsourceVar) == 0 or HsourceVar[sourceGUID] then for destGUID,destData in pairs(sourceData) do for destReact,destReactData in pairs(destData) do local isEnemy = not ExRT.F.UnitIsFriendlyByUnitFlag2(destReact) if ExRT.F.table_len(HdestVar) == 0 or HdestVar[destGUID] then if (isEnemy and doEnemy) or (not isEnemy and not doEnemy) then local source = isReverse and destGUID or sourceGUID local dest = isReverse and sourceGUID or destGUID local sourceHeal, destPos = HealingTab_UpdateLines_GetUnit(heal,graph,source,dest,"guid") for spellID,spellSegments in pairs(destReactData) do for segment,spellAmount in pairs(spellSegments) do if CurrentFight.segments[segment].e then if spellID == 98021 then --Spirit Link spellAmount = HealingTab_Variables.NULLSpellAmount end sourceHeal.eff = sourceHeal.eff + spellAmount.amount - spellAmount.over + spellAmount.absorbed sourceHeal.total = sourceHeal.total + spellAmount.amount + spellAmount.absorbed --total sourceHeal.overheal = sourceHeal.overheal + spellAmount.over --overheal sourceHeal.absorbed = sourceHeal.absorbed + spellAmount.absorbed --absorbed sourceHeal.crit = sourceHeal.crit + spellAmount.crit - (HealingTab_Variables.ShowOverheal and 0 or spellAmount.critover) sourceHeal.absorbs = sourceHeal.absorbs + spellAmount.absorbs --absorbs total = total + spellAmount.amount - spellAmount.over + spellAmount.absorbed totalOver = totalOver + spellAmount.over destPos[2] = destPos[2] + spellAmount.amount + spellAmount.absorbed + (HealingTab_Variables.ShowOverheal and 0 or -spellAmount.over) if not graph[ source ][segment] then graph[ source ][segment] = 0 end if not graph[ -1 ][segment] then graph[ -1 ][segment] = 0 end local healCount = spellAmount.amount - (HealingTab_Variables.ShowOverheal and 0 or spellAmount.over) + spellAmount.absorbed graph[ source ][segment] = graph[ source ][segment] + healCount graph[ -1 ][segment] = graph[ -1 ][segment] + healCount end end end end end end end end end end if doReduction and not doEnemy then for destGUID,destData in pairs(CurrentFight.reduction) do if ExRT.F.table_len(HdestVar) == 0 or HdestVar[destGUID] then for sourceGUID,sourceData in pairs(destData) do for spellID,spellData in pairs(sourceData) do for reductorGUID,reductorData in pairs(spellData) do local owner = ExRT.F.Pets:getOwnerGUID(reductorGUID,GetPetsDB()) if owner then reductorGUID = owner end if ExRT.F.table_len(HsourceVar) == 0 or HsourceVar[reductorGUID] then local isFriendly = ExRT.F.UnitIsFriendlyByUnitFlag2(CurrentFight.reaction[reductorGUID]) if isFriendly then local source = isReverse and destGUID or reductorGUID local dest = isReverse and reductorGUID or destGUID local sourceHeal, destPos = HealingTab_UpdateLines_GetUnit(heal,graph,source,dest,"guid") for reductionSpellID,spellSegments in pairs(reductorData) do for segment,reductionSpellAmount in pairs(spellSegments) do if CurrentFight.segments[segment].e then sourceHeal.eff = sourceHeal.eff + reductionSpellAmount sourceHeal.total = sourceHeal.total + reductionSpellAmount if not onlyReduction then sourceHeal.absorbs = sourceHeal.absorbs + reductionSpellAmount end total = total + reductionSpellAmount destPos[2] = destPos[2] + reductionSpellAmount if not graph[ source ][segment] then graph[ source ][segment] = 0 end if not graph[ -1 ][segment] then graph[ -1 ][segment] = 0 end graph[ source ][segment] = graph[ source ][segment] + reductionSpellAmount graph[ -1 ][segment] = graph[ -1 ][segment] + reductionSpellAmount end end end end end end end end end end if HealingTab_Variables.ShowOverheal and onlyReduction then local missData = {} local avgDamage = {} for destGUID,destData in pairs(CurrentFight.damage) do for destReaction,destReactData in pairs(destData) do for sourceGUID,sourceData in pairs(destReactData) do local avgData = avgDamage[sourceGUID] if not avgData then avgData = {} avgDamage[sourceGUID] = avgData end for spellID,spellSegments in pairs(sourceData) do local avgSpell = avgData[spellID] if not avgSpell then avgSpell = {0,0} avgData[spellID] = avgSpell end for segment,spellAmount in pairs(spellSegments) do avgSpell[1] = avgSpell[1] + spellAmount.count avgSpell[2] = avgSpell[2] + spellAmount.amount + spellAmount.blocked + spellAmount.absorbed if spellAmount.parry > 0 or spellAmount.dodge > 0 or spellAmount.miss > 0 then local missDestData = missData[destGUID] if not missDestData then missDestData = {} missData[destGUID] = missDestData end local missSpell = missDestData[sourceGUID] if not missSpell then missSpell = {} missDestData[sourceGUID] = missSpell end local missSpellData = missSpell[spellID] if not missSpellData then missSpellData = { parry = 0, dodge = 0, miss = 0, parry_target = 0, dodge_target = 0, miss_target = 0, segments = {}, } missSpell[spellID] = missSpellData end missSpellData.parry = missSpellData.parry+spellAmount.parry missSpellData.dodge = missSpellData.dodge+spellAmount.dodge missSpellData.miss = missSpellData.miss+spellAmount.miss if CurrentFight.segments[segment].e then missSpellData.parry_target = missSpellData.parry_target+spellAmount.parry missSpellData.dodge_target = missSpellData.dodge_target+spellAmount.dodge missSpellData.miss_target = missSpellData.miss_target+spellAmount.miss missSpellData.segments[#missSpellData.segments+1] = segment end end end end end end end local reductionMissToSpell = { dodge = 81, parry = 82243, miss = 154592, } for destGUID,destData in pairs(missData) do local isFriendly = ExRT.F.UnitIsFriendlyByUnitFlag2(CurrentFight.reaction[destGUID] or 0) if isFriendly and (ExRT.F.table_len(HsourceVar) == 0 or HsourceVar[destGUID]) then for sourceGUID,sourceData in pairs(destData) do for spellID,spellAmount in pairs(sourceData) do local avgData = avgDamage[ sourceGUID ][ spellID ] local avg = avgData[3] if not avg then if avgData[1] > 0 then avg = avgData[2] / avgData[1] else avg = 0 end avgData[3] = avg end if avg > 0 then for reductionName,reductionSpellID in pairs(reductionMissToSpell) do if spellAmount[reductionName] > 0 then local sourceHeal, destPos = HealingTab_UpdateLines_GetUnit(heal,graph,destGUID,destGUID,"guid") local fromSpellPos = ExRT.F.table_find(sourceHeal.from,spellID,1) if not fromSpellPos then fromSpellPos = #sourceHeal.from + 1 sourceHeal.from[fromSpellPos] = {spellID,0} end fromSpellPos = sourceHeal.from[fromSpellPos] local amount = avg * spellAmount[reductionName.."_target"] sourceHeal.eff = sourceHeal.eff + amount sourceHeal.total = sourceHeal.total + amount sourceHeal.absorbs = sourceHeal.absorbs + amount total = total + amount destPos[2] = destPos[2] + amount fromSpellPos[2] = fromSpellPos[2] + amount for i=1,#spellAmount.segments do local segment = spellAmount.segments[i] if not graph[ destGUID ] then graph[ destGUID ] = {} end if not graph[ destGUID ][segment] then graph[ destGUID ][segment] = 0 end if not graph[ -1 ][segment] then graph[ -1 ][segment] = 0 end graph[ destGUID ][segment] = graph[ destGUID ][segment] + amount graph[ -1 ][segment] = graph[ -1 ][segment] + amount end end end end end end end end end end if not isReverse and not onlyReduction then for _,healData in pairs(heal) do for sourceGUID,sourceData in pairs(CurrentFight.healFrom) do if healData.guid == sourceGUID or ExRT.F.Pets:getOwnerGUID(sourceGUID,GetPetsDB()) == healData.guid then for destGUID,destData in pairs(sourceData) do local isEnemy = not ExRT.F.UnitIsFriendlyByUnitFlag2(CurrentFight.reaction[destGUID]) if ExRT.F.table_len(HdestVar) == 0 or HdestVar[destGUID] then if (isEnemy and doEnemy) or (not isEnemy and not doEnemy) then for spellID,spellData in pairs(destData) do for fromSpellID,fromSpellSegments in pairs(spellData) do for segment,fromSpellAmount in pairs(fromSpellSegments) do if CurrentFight.segments[segment].e then local destPos = ExRT.F.table_find(healData.from,fromSpellID,1) if not destPos then destPos = #healData.from + 1 healData.from[destPos] = {fromSpellID,0} end destPos = healData.from[destPos] destPos[2] = destPos[2] + fromSpellAmount end end end end end end end end end end end local totalIsFull = 1 total = max(total,1) if total == 1 and #heal == 0 then total = 0 totalIsFull = 0 end local _max = nil reportOptions[2] = L.BossWatcherReportHPS wipe(reportData[2]) reportData[2][1] = (DamageTab_GetGUIDsReport(HsourceVar) or L.BossWatcherAllSources).." > "..(DamageTab_GetGUIDsReport(HdestVar) or L.BossWatcherAllTargets) local activeFightLength = GetFightLength() if HealingTab_Variables.ShowOverheal then total = total + totalOver sort(heal,function(a,b) return a.total>b.total end) _max = heal[1] and heal[1].total or 0 else sort(heal,function(a,b) return a.eff>b.eff end) _max = heal[1] and heal[1].eff or 0 end reportData[2][2] = L.BossWatcherReportTotal.." - "..ExRT.F.shortNumber(total).."@1@ ("..floor(total / activeFightLength)..")@1#" HealingTab_SetLine({ line = 1, name = L.BossWatcherReportTotal, num = total, total = total, max = total, alpha = HealingTab_Variables.ShowOverheal and totalOver, dps = total / activeFightLength, spellID = -1, check = BWInterfaceFrame.GraphFrame:IsShown(), checkState = true, }) for i=1,#heal do local healLine = heal[i] local class = nil if healLine.guid and healLine.guid ~= "" then class = select(2,ExRT.F:SafeCall(GetPlayerInfoByGUID,healLine.guid)) end local icon = "" if class and CLASS_ICON_TCOORDS[class] then icon = {"Interface\\GLUES\\CHARACTERCREATE\\UI-CHARACTERCREATE-CLASSES",unpack(CLASS_ICON_TCOORDS[class])} end local tooltipData = {GetGUID(healLine.guid), {L.BossWatcherHealTooltipOver,format("%s (%.1f%%)",ExRT.F.shortNumber(healLine.overheal),healLine.overheal/max(healLine.total,1)*100)}, {L.BossWatcherHealTooltipAbsorbed,ExRT.F.shortNumber(healLine.absorbed)}, {L.BossWatcherHealTooltipTotal,ExRT.F.shortNumber(healLine.total)}, {" "," "}, {L.BossWatcherHealTooltipFromCrit,format("%s (%.1f%%)",ExRT.F.shortNumber(healLine.crit),healLine.crit/max(1,healLine.eff+(HealingTab_Variables.ShowOverheal and healLine.overheal or 0))*100)}, {ACTION_SPELL_MISSED_ABSORB,format("%s (%.1f%%)",ExRT.F.shortNumber(healLine.absorbs),healLine.absorbs/max(healLine.eff+(HealingTab_Variables.ShowOverheal and healLine.overheal or 0),1)*100)}, } sort(healLine.targets,DamageTab_Temp_SortingBy2Param) if #healLine.targets > 0 then tooltipData[#tooltipData + 1] = {" "," "} tooltipData[#tooltipData + 1] = {L.BossWatcherHealTooltipTargets," "} end for j=1,min(5,#healLine.targets) do tooltipData[#tooltipData + 1] = {SubUTF8String(GetGUID(healLine.targets[j][1]),20)..GUIDtoText(" [%s]",healLine.targets[j][1]),format("%s (%.1f%%)",ExRT.F.shortNumber(healLine.targets[j][2]),min(healLine.targets[j][2] / max(1,healLine.eff+(HealingTab_Variables.ShowOverheal and healLine.overheal or 0))*100,100))} end sort(healLine.from,DamageTab_Temp_SortingBy2Param) if #healLine.from > 0 then tooltipData[#tooltipData + 1] = {" "," "} tooltipData[#tooltipData + 1] = {L.BossWatcherFromSpells," "} end for j=1,min(5,#healLine.from) do local spellName,_,spellTexture = GetSpellInfo(healLine.from[j][1]) tooltipData[#tooltipData + 1] = {(spellTexture and "|T"..spellTexture..":0|t" or "")..(spellName or "spell:"..(healLine.from[j][1] or -1)),ExRT.F.shortNumber(healLine.from[j][2])} end local currHealing = healLine.eff+(HealingTab_Variables.ShowOverheal and healLine.overheal or 0) local hps = currHealing/activeFightLength HealingTab_SetLine({ line = i+1, icon = icon, name = GetGUID(healLine.guid)..GUIDtoText(" [%s]",healLine.guid), num = currHealing, total = total, max = _max, alpha = (HealingTab_Variables.ShowOverheal and not onlyReduction) and healLine.overheal or healLine.absorbs, dps = hps, class = class, sourceGUID = healLine.guid, tooltip = tooltipData, check = BWInterfaceFrame.GraphFrame:IsShown(), checkState = i <= 3, }) reportData[2][#reportData[2]+1] = i..". "..GetGUID(healLine.guid).." - "..ExRT.F.shortNumber(currHealing).."@1@ ("..floor(hps)..")@1#" end for i=#heal+2,#BWInterfaceFrame.tab.tabs[2].lines do BWInterfaceFrame.tab.tabs[2].lines[i]:Hide() end BWInterfaceFrame.tab.tabs[2].scroll:Height((#heal+1) * 20) HealingTab_Variables.graphCache = {graph,#CurrentFight.segments,heal,false} if BWInterfaceFrame.GraphFrame:IsShown() then HealingTab_ReloadGraph(graph,#CurrentFight.segments,heal,false) end end local function HealingTab_UpdateLinesSpell() ExRT.F.dprint("Healing Update: Spells",GetTime()) HealingTab_UpdateDropDowns() HealingTab_UpdateChecks() local doEnemy = not HealingTab_Variables.state_friendly local doReduction = HealingTab_Variables.state_mitigation local onlyReduction = doReduction local heal = {} local total = 0 local totalOver = 0 local graph = {[-1]={}} if not onlyReduction then for sourceGUID,sourceData in pairs(CurrentFight.heal) do local owner = ExRT.F.Pets:getOwnerGUID(sourceGUID,GetPetsDB()) if owner then sourceGUID = owner end if ExRT.F.table_len(HsourceVar) == 0 or HsourceVar[sourceGUID] then for destGUID,destData in pairs(sourceData) do for destReact,destReactData in pairs(destData) do local isEnemy = not ExRT.F.UnitIsFriendlyByUnitFlag2(destReact) if ExRT.F.table_len(HdestVar) == 0 or HdestVar[destGUID] then if (isEnemy and doEnemy) or (not isEnemy and not doEnemy) then for spellID,spellSegments in pairs(destReactData) do local sourceHeal, destPos = HealingTab_UpdateLines_GetUnit(heal,graph,spellID,destGUID,"spell",owner and "pet") for segment,spellAmount in pairs(spellSegments) do if CurrentFight.segments[segment].e then if spellID == 98021 and not HealingTab_Variables.ShowOverheal then --Spirit Link spellAmount = HealingTab_Variables.NULLSpellAmount end sourceHeal.eff = sourceHeal.eff + spellAmount.amount - spellAmount.over + spellAmount.absorbed --ef sourceHeal.total = sourceHeal.total + spellAmount.amount + spellAmount.absorbed --total sourceHeal.overheal = sourceHeal.overheal + spellAmount.over --overheal sourceHeal.absorbed = sourceHeal.absorbed + spellAmount.absorbed --absorbed sourceHeal.count = sourceHeal.count + spellAmount.count --count sourceHeal.crit = sourceHeal.crit + spellAmount.crit --crit sourceHeal.critcount = sourceHeal.critcount + spellAmount.critcount --crit-count sourceHeal.critmax = max(sourceHeal.critmax,spellAmount.critmax) --crit-max sourceHeal.hitmax = max(sourceHeal.hitmax,spellAmount.hitmax) --hit-max sourceHeal.critover = sourceHeal.critover + spellAmount.critover --crit overheal sourceHeal.absorbs = sourceHeal.absorbs + spellAmount.absorbs --absorbs total = total + spellAmount.amount - spellAmount.over + spellAmount.absorbed totalOver = totalOver + spellAmount.over destPos[2] = destPos[2] + spellAmount.amount + spellAmount.absorbed + (HealingTab_Variables.ShowOverheal and 0 or -spellAmount.over) if not graph[ spellID ][segment] then graph[ spellID ][segment] = 0 end if not graph[ -1 ][segment] then graph[ -1 ][segment] = 0 end local healCount = spellAmount.amount - (HealingTab_Variables.ShowOverheal and 0 or spellAmount.over) + spellAmount.absorbed graph[ spellID ][segment] = graph[ spellID ][segment] + healCount graph[ -1 ][segment] = graph[ -1 ][segment] + healCount end end end end end end end end end end if doReduction and not doEnemy then for destGUID,destData in pairs(CurrentFight.reduction) do if ExRT.F.table_len(HdestVar) == 0 or HdestVar[destGUID] then for sourceGUID,sourceData in pairs(destData) do for spellID,spellData in pairs(sourceData) do for reductorGUID,reductorData in pairs(spellData) do local owner = ExRT.F.Pets:getOwnerGUID(reductorGUID,GetPetsDB()) if ExRT.F.table_len(HsourceVar) == 0 or HsourceVar[reductorGUID] then local isFriendly = ExRT.F.UnitIsFriendlyByUnitFlag2(CurrentFight.reaction[reductorGUID]) if isFriendly then for reductionSpellID,spellSegments in pairs(reductorData) do local sourceHeal, destPos = HealingTab_UpdateLines_GetUnit(heal,graph,reductionSpellID,destGUID,"spell",owner and "pet") for segment,reductionSpellAmount in pairs(spellSegments) do if CurrentFight.segments[segment].e then sourceHeal.eff = sourceHeal.eff + reductionSpellAmount sourceHeal.total = sourceHeal.total + reductionSpellAmount if not onlyReduction then sourceHeal.absorbs = sourceHeal.absorbs + reductionSpellAmount end total = total + reductionSpellAmount destPos[2] = destPos[2] + reductionSpellAmount if not graph[ reductionSpellID ][segment] then graph[ reductionSpellID ][segment] = 0 end if not graph[ -1 ][segment] then graph[ -1 ][segment] = 0 end graph[ reductionSpellID ][segment] = graph[ reductionSpellID ][segment] + reductionSpellAmount graph[ -1 ][segment] = graph[ -1 ][segment] + reductionSpellAmount end end end end end end end end end end if HealingTab_Variables.ShowOverheal and onlyReduction then local missData = {} local avgDamage = {} for destGUID,destData in pairs(CurrentFight.damage) do for destReaction,destReactData in pairs(destData) do for sourceGUID,sourceData in pairs(destReactData) do local avgData = avgDamage[sourceGUID] if not avgData then avgData = {} avgDamage[sourceGUID] = avgData end for spellID,spellSegments in pairs(sourceData) do local avgSpell = avgData[spellID] if not avgSpell then avgSpell = {0,0} avgData[spellID] = avgSpell end for segment,spellAmount in pairs(spellSegments) do avgSpell[1] = avgSpell[1] + spellAmount.count avgSpell[2] = avgSpell[2] + spellAmount.amount + spellAmount.blocked + spellAmount.absorbed if spellAmount.parry > 0 or spellAmount.dodge > 0 or spellAmount.miss > 0 then local missDestData = missData[destGUID] if not missDestData then missDestData = {} missData[destGUID] = missDestData end local missSpell = missDestData[sourceGUID] if not missSpell then missSpell = {} missDestData[sourceGUID] = missSpell end local missSpellData = missSpell[spellID] if not missSpellData then missSpellData = { parry = 0, dodge = 0, miss = 0, parry_target = 0, dodge_target = 0, miss_target = 0, segments = {}, } missSpell[spellID] = missSpellData end missSpellData.parry = missSpellData.parry+spellAmount.parry missSpellData.dodge = missSpellData.dodge+spellAmount.dodge missSpellData.miss = missSpellData.miss+spellAmount.miss if CurrentFight.segments[segment].e then missSpellData.parry_target = missSpellData.parry_target+spellAmount.parry missSpellData.dodge_target = missSpellData.dodge_target+spellAmount.dodge missSpellData.miss_target = missSpellData.miss_target+spellAmount.miss missSpellData.segments[#missSpellData.segments+1] = segment end end end end end end end local reductionMissToSpell = { dodge = 81, parry = 82243, miss = 154592, } for destGUID,destData in pairs(missData) do local isFriendly = ExRT.F.UnitIsFriendlyByUnitFlag2(CurrentFight.reaction[destGUID] or 0) if isFriendly and (ExRT.F.table_len(HsourceVar) == 0 or HsourceVar[destGUID]) then for sourceGUID,sourceData in pairs(destData) do for spellID,spellAmount in pairs(sourceData) do local avgData = avgDamage[ sourceGUID ][ spellID ] local avg = avgData[3] if not avg then if avgData[1] > 0 then avg = avgData[2] / avgData[1] else avg = 0 end avgData[3] = avg end if avg > 0 then for reductionName,reductionSpellID in pairs(reductionMissToSpell) do if spellAmount[reductionName] > 0 then local sourceHeal, destPos = HealingTab_UpdateLines_GetUnit(heal,graph,reductionSpellID,destGUID,"spell") local fromSpellPos = ExRT.F.table_find(sourceHeal.from,spellID,1) if not fromSpellPos then fromSpellPos = #sourceHeal.from + 1 sourceHeal.from[fromSpellPos] = {spellID,0} end fromSpellPos = sourceHeal.from[fromSpellPos] local amount = avg * spellAmount[reductionName.."_target"] sourceHeal.eff = sourceHeal.eff + amount sourceHeal.total = sourceHeal.total + amount sourceHeal.absorbs = sourceHeal.absorbs + amount total = total + amount destPos[2] = destPos[2] + amount fromSpellPos[2] = fromSpellPos[2] + amount for i=1,#spellAmount.segments do local segment = spellAmount.segments[i] if not graph[ reductionSpellID ] then graph[ reductionSpellID ] = {} end if not graph[ reductionSpellID ][segment] then graph[ reductionSpellID ][segment] = 0 end if not graph[ -1 ][segment] then graph[ -1 ][segment] = 0 end graph[ reductionSpellID ][segment] = graph[ reductionSpellID ][segment] + amount graph[ -1 ][segment] = graph[ -1 ][segment] + amount end end end end end end end end end end if not onlyReduction then for _,healData in pairs(heal) do for sourceGUID,sourceData in pairs(CurrentFight.healFrom) do for destGUID,destData in pairs(sourceData) do local isEnemy = not ExRT.F.UnitIsFriendlyByUnitFlag2(CurrentFight.reaction[destGUID]) if ExRT.F.table_len(HdestVar) == 0 or HdestVar[destGUID] then if (isEnemy and doEnemy) or (not isEnemy and not doEnemy) then for spellID,spellData in pairs(destData) do if healData.spell == spellID then for fromSpellID,fromSpellSegments in pairs(spellData) do for segment,fromSpellAmount in pairs(fromSpellSegments) do if CurrentFight.segments[segment].e then local destPos = ExRT.F.table_find(healData.from,fromSpellID,1) if not destPos then destPos = #healData.from + 1 healData.from[destPos] = {fromSpellID,0} end destPos = healData.from[destPos] destPos[2] = destPos[2] + fromSpellAmount end end end end end end end end end end end local totalIsFull = 1 total = max(total,1) if total == 1 and #heal == 0 then total = 0 totalIsFull = 0 end local _max = nil reportOptions[2] = L.BossWatcherReportHPS wipe(reportData[2]) reportData[2][1] = (DamageTab_GetGUIDsReport(HsourceVar) or L.BossWatcherAllSources).." > "..(DamageTab_GetGUIDsReport(HdestVar) or L.BossWatcherAllTargets) local activeFightLength = GetFightLength() if HealingTab_Variables.ShowOverheal then total = total + totalOver sort(heal,function(a,b) return a.total>b.total end) _max = heal[1] and heal[1].total or 0 else sort(heal,function(a,b) return a.eff>b.eff end) _max = heal[1] and heal[1].eff or 0 end reportData[2][2] = L.BossWatcherReportTotal.." - "..ExRT.F.shortNumber(total).."@1@ ("..floor(total / activeFightLength)..")@1#" HealingTab_SetLine({ line = 1, name = L.BossWatcherReportTotal, total = total, num = total, max = total, dps = total / activeFightLength, spellID = -1, alpha = HealingTab_Variables.ShowOverheal and totalOver, check = BWInterfaceFrame.GraphFrame:IsShown(), checkState = true, }) _max = max(_max,1) local castsCount = SpellsPage_GetCastsNumber(ExRT.F.table_len(HsourceVar) > 0 and HsourceVar,ExRT.F.table_len(HdestVar) > 0 and HdestVar) for i=1,#heal do local healLine = heal[i] local isPetAbility = healLine.info == "pet" local spellID = healLine.spell local isHoT = (not ExRT.isClassic or ExRT.isCata) and spellID < 0 if isHoT then spellID = -spellID end local spellName,_,spellIcon = GetSpellInfo(spellID) local defSpellName = spellName if isPetAbility then spellName = L.BossWatcherPetText..": "..spellName end if isHoT then spellName = spellName .. " ["..L.BossWatcherHoT.."]" end local school = module.db.spellsSchool[ spellID ] or 0 local tooltipData = { {spellName,spellIcon}, {L.BossWatcherHealTooltipCount,healLine.count}, {L.BossWatcherHealTooltipHitMax,floor(healLine.hitmax)}, {L.BossWatcherHealTooltipHitMid,ExRT.F.Round(max(healLine.total-healLine.crit-(healLine.overheal-healLine.critover),0)/max(healLine.count-healLine.critcount,1))}, {L.BossWatcherHealTooltipCritCount,format("%d (%.1f%%)",healLine.critcount,healLine.critcount/max(1,healLine.count)*100)}, {L.BossWatcherHealTooltipCritAmount,ExRT.F.shortNumber(healLine.crit-healLine.critover)}, {L.BossWatcherHealTooltipCritMax,healLine.critmax}, {L.BossWatcherHealTooltipCritMid,ExRT.F.Round((healLine.crit-healLine.critover)/max(healLine.critcount,1))}, {L.BossWatcherHealTooltipOver,format("%s (%.1f%%)",ExRT.F.shortNumber(healLine.overheal),healLine.overheal/max(healLine.total,1)*100)}, {L.BossWatcherHealTooltipAbsorbed,ExRT.F.shortNumber(healLine.absorbed)}, {L.BossWatcherHealTooltipTotal,ExRT.F.shortNumber(healLine.total)}, {L.BossWatcherSchool,GetSchoolName(school)}, } local casts = castsCount[ spellID ] or castsCount[ defSpellName ] if casts then tinsert(tooltipData,2,{L.BossWatcherDamageTooltipCastsCount,casts}) tinsert(tooltipData,3,{L.BossWatcherPerCast,ExRT.F.shortNumber(healLine.eff / casts)}) end sort(healLine.targets,DamageTab_Temp_SortingBy2Param) if #healLine.targets > 0 then tooltipData[#tooltipData + 1] = {" "," "} tooltipData[#tooltipData + 1] = {L.BossWatcherHealTooltipTargets," "} end for j=1,min(5,#healLine.targets) do tooltipData[#tooltipData + 1] = {SubUTF8String(GetGUID(healLine.targets[j][1]),20)..GUIDtoText(" [%s]",healLine.targets[j][1]),format("%s (%.1f%%)",ExRT.F.shortNumber(healLine.targets[j][2]),min(healLine.targets[j][2] / max(1,healLine.eff+(HealingTab_Variables.ShowOverheal and healLine.overheal or 0))*100,100))} end sort(healLine.from,DamageTab_Temp_SortingBy2Param) if #healLine.from > 0 then tooltipData[#tooltipData + 1] = {" "," "} tooltipData[#tooltipData + 1] = {L.BossWatcherFromSpells," "} end for j=1,min(5,#healLine.from) do local spellName,_,spellTexture = GetSpellInfo(healLine.from[j][1]) tooltipData[#tooltipData + 1] = {(spellTexture and "|T"..spellTexture..":0|t" or "")..(spellName or "spell:"..spellID),ExRT.F.shortNumber(healLine.from[j][2])} end local currHealing = healLine.eff+(HealingTab_Variables.ShowOverheal and healLine.overheal or 0) local hps = currHealing/activeFightLength HealingTab_SetLine({ line = i+1, icon = spellIcon, name = spellName, total = total, num = currHealing, alpha = (HealingTab_Variables.ShowOverheal and not onlyReduction) and healLine.overheal or healLine.absorbs, max = _max, dps = hps, spellID = spellID, tooltip = tooltipData, school = school, isDoT = isHoT, check = BWInterfaceFrame.GraphFrame:IsShown(), checkState = i <= 3, }) reportData[2][#reportData[2]+1] = i..". "..(isPetAbility and L.BossWatcherPetText..": " or "")..GetSpellLink(spellID).." - "..ExRT.F.shortNumber(currHealing).."@1@ ("..floor(hps)..")@1#" end for i=#heal+2,#BWInterfaceFrame.tab.tabs[2].lines do BWInterfaceFrame.tab.tabs[2].lines[i]:Hide() end BWInterfaceFrame.tab.tabs[2].scroll:Height((#heal+1) * 20) HealingTab_Variables.graphCache = {graph,#CurrentFight.segments,heal,true} if BWInterfaceFrame.GraphFrame:IsShown() then HealingTab_ReloadGraph(graph,#CurrentFight.segments,heal,true) end end local function HealingTab_UpdateLinesFromSpells() HealingTab_UpdateDropDowns() HealingTab_UpdateChecks() local doEnemy = nil local heal = {} local total = 0 local graph = {[-1]={}} for sourceGUID,sourceData in pairs(CurrentFight.healFrom) do local owner = ExRT.F.Pets:getOwnerGUID(sourceGUID,GetPetsDB()) if owner then sourceGUID = owner end if ExRT.F.table_len(HsourceVar) == 0 or HsourceVar[sourceGUID] then for destGUID,destData in pairs(sourceData) do local isEnemy = not ExRT.F.UnitIsFriendlyByUnitFlag2(CurrentFight.reaction[destGUID]) if ExRT.F.table_len(HdestVar) == 0 or HdestVar[destGUID] then if (isEnemy and doEnemy) or (not isEnemy and not doEnemy) then for spellID,spellData in pairs(destData) do for fromSpellID,fromSpellSegments in pairs(spellData) do local inDamagePos = ExRT.F.table_find(heal,fromSpellID,1) if not inDamagePos then inDamagePos = #heal + 1 heal[inDamagePos] = {fromSpellID,0,{},{},spell=fromSpellID} end local destPos = ExRT.F.table_find(heal[inDamagePos][3],destGUID,1) if not destPos then destPos = #heal[inDamagePos][3] + 1 heal[inDamagePos][3][destPos] = {destGUID,0} end destPos = heal[inDamagePos][3][destPos] local sourcePos = ExRT.F.table_find(heal[inDamagePos][4],sourceGUID,1) if not sourcePos then sourcePos = #heal[inDamagePos][4] + 1 heal[inDamagePos][4][sourcePos] = {sourceGUID,0} end sourcePos = heal[inDamagePos][4][sourcePos] if not graph[ fromSpellID ] then graph[ fromSpellID ] = {} end for segment,fromSpellAmount in pairs(fromSpellSegments) do if CurrentFight.segments[segment].e then heal[inDamagePos][2] = heal[inDamagePos][2] + fromSpellAmount total = total + fromSpellAmount destPos[2] = destPos[2] + fromSpellAmount sourcePos[2] = sourcePos[2] + fromSpellAmount if not graph[ fromSpellID ][segment] then graph[ fromSpellID ][segment] = 0 end if not graph[ -1 ][segment] then graph[ -1 ][segment] = 0 end graph[ fromSpellID ][segment] = graph[ fromSpellID ][segment] + fromSpellAmount graph[ -1 ][segment] = graph[ -1 ][segment] + fromSpellAmount end end end end end end end end end local totalIsFull = 1 total = max(total,1) if total == 1 and #heal == 0 then total = 0 totalIsFull = 0 end local _max = nil reportOptions[2] = L.BossWatcherReportHPS wipe(reportData[2]) reportData[2][1] = (DamageTab_GetGUIDsReport(HsourceVar) or L.BossWatcherAllSources).." > "..(DamageTab_GetGUIDsReport(HdestVar) or L.BossWatcherAllTargets) local activeFightLength = GetFightLength() do local hps = total / activeFightLength reportData[2][2] = L.BossWatcherReportTotal.." - "..ExRT.F.shortNumber(total).."@1@ ("..floor(hps)..")@1#" sort(heal,function(a,b) return a[2]>b[2] end) _max = heal[1] and heal[1][2] or 0 HealingTab_SetLine({ line = 1, name = L.BossWatcherReportTotal, num = total, total = total, max = total, dps = hps, spellID = -1, check = BWInterfaceFrame.GraphFrame:IsShown(), checkState = true, }) end for i=1,#heal do local spellName,_,spellIcon = GetSpellInfo(heal[i][1]) local school = module.db.spellsSchool[ heal[i][1] ] or 0 local tooltipData = { {spellName,spellIcon}, {L.BossWatcherHealTooltipTotal,ExRT.F.shortNumber(heal[i][2])}, {L.BossWatcherSchool,GetSchoolName(school)}, } sort(heal[i][3],DamageTab_Temp_SortingBy2Param) if #heal[i][3] > 0 then tooltipData[#tooltipData + 1] = {" "," "} tooltipData[#tooltipData + 1] = {L.BossWatcherHealTooltipTargets," "} end for j=1,min(5,#heal[i][3]) do tooltipData[#tooltipData + 1] = {SubUTF8String(GetGUID(heal[i][3][j][1]),20)..GUIDtoText(" [%s]",heal[i][3][j][1]),format("%s (%.1f%%)",ExRT.F.shortNumber(heal[i][3][j][2]),min(heal[i][3][j][2] / max(1,heal[i][2])*100,100))} end sort(heal[i][4],DamageTab_Temp_SortingBy2Param) if #heal[i][4] > 0 then tooltipData[#tooltipData + 1] = {" "," "} tooltipData[#tooltipData + 1] = {L.BossWatcherHealTooltipSources," "} end for j=1,min(5,#heal[i][4]) do tooltipData[#tooltipData + 1] = {SubUTF8String(GetGUID(heal[i][4][j][1]),20)..GUIDtoText(" [%s]",heal[i][4][j][1]),format("%s (%.1f%%)",ExRT.F.shortNumber(heal[i][4][j][2]),min(heal[i][4][j][2] / max(1,heal[i][2])*100,100))} end do local hps = heal[i][2]/activeFightLength HealingTab_SetLine({ line = i+1, icon = spellIcon, name = spellName, total = total, num = heal[i][2], max = _max, dps = hps, spellID = heal[i][1], tooltip = tooltipData, school = school, check = BWInterfaceFrame.GraphFrame:IsShown(), checkState = i <= 3, }) reportData[2][#reportData[2]+1] = i..". "..GetSpellLink(heal[i][1]).." - "..ExRT.F.shortNumber(heal[i][2]).."@1@ ("..floor(hps)..")@1#" end end for i=#heal+2,#BWInterfaceFrame.tab.tabs[2].lines do BWInterfaceFrame.tab.tabs[2].lines[i]:Hide() end BWInterfaceFrame.tab.tabs[2].scroll:Height((#heal+1) * 20) HealingTab_Variables.graphCache = {graph,#CurrentFight.segments,heal,true} if BWInterfaceFrame.GraphFrame:IsShown() then HealingTab_ReloadGraph(graph,#CurrentFight.segments,heal,true) end end local function HealingTab_SelectDropDownSource(self,arg) ELib:DropDownClose() local Back_destVar = ExRT.F.table_copy2(HdestVar) local Back_sourceVar = ExRT.F.table_copy2(HsourceVar) wipe(HsourceVar) if arg then HsourceVar[arg] = true if IsShiftKeyDown() then local name = CurrentFight.guids[arg] if name then for GUID,GUIDname in pairs(CurrentFight.guids) do if GUIDname == name then HsourceVar[GUID] = true end end end end end HealingTab_UpdatePage() end local function HealingTab_SelectDropDownDest(self,arg) ELib:DropDownClose() local Back_destVar = ExRT.F.table_copy2(HdestVar) local Back_sourceVar = ExRT.F.table_copy2(HsourceVar) wipe(HdestVar) if arg then HdestVar[arg] = true if IsShiftKeyDown() then local name = CurrentFight.guids[arg] if name then for GUID,GUIDname in pairs(CurrentFight.guids) do if GUIDname == name then HdestVar[GUID] = true end end end end end HealingTab_UpdatePage() end local function HealingTab_SelectDropDown_OptionTanks(_,destTable,onlyTanks) ELib:DropDownClose() local Back_destVar = ExRT.F.table_copy2(HdestVar) local Back_sourceVar = ExRT.F.table_copy2(HsourceVar) wipe(HdestVar) for i=1,#destTable do local isTank if CurrentFight.other.rolesGUID[ destTable[i][1] ] == "TANK" then isTank = true end if (isTank and onlyTanks) or (not isTank and not onlyTanks) then HdestVar[ destTable[i][1] ] = true end end HealingTab_UpdatePage() end local function HealingTab_CheckDropDownSource(self,checked) if checked then HsourceVar[self.arg1] = true else HsourceVar[self.arg1] = nil end HealingTab_UpdatePage() end local function HealingTab_CheckDropDownDest(self,checked) if checked then HdestVar[self.arg1] = true else HdestVar[self.arg1] = nil end HealingTab_UpdatePage() end local function HealingTab_HPS(disableUpdateVars) local doEnemy = not HealingTab_Variables.state_friendly local sourceTable = {} local destTable = {} for sourceGUID,sourceData in pairs(CurrentFight.heal) do local owner = ExRT.F.Pets:getOwnerGUID(sourceGUID,GetPetsDB()) if owner then sourceGUID = owner end for destGUID,destData in pairs(sourceData) do for destReact,destReactData in pairs(destData) do local isFriendly = ExRT.F.UnitIsFriendlyByUnitFlag2(destReact) if (isFriendly and not doEnemy) or (not isFriendly and doEnemy) then for spellID,spellSegments in pairs(destReactData) do for segment,spellAmount in pairs(spellSegments) do if CurrentFight.segments[segment].e then if not ExRT.F.table_find(destTable,destGUID,1) then destTable[#destTable + 1] = {destGUID,GetGUID(destGUID)} end if not ExRT.F.table_find(sourceTable,sourceGUID,1) then sourceTable[#sourceTable + 1] = {sourceGUID,GetGUID(sourceGUID)} end end end end end end end end sort(sourceTable,function(a,b) return a[2] 0 then local width = 300*(overall or 1) local normal_width = width * (1 - overall_black) line.overall:SetWidth(max(normal_width,1)) line.overall_black:SetWidth(max(width-normal_width,1)) line.overall_black:Show() if normal_width == 0 then line.overall:Hide() line.overall_black:SetPoint("TOPLEFT",325,-2) else line.overall:Show() line.overall_black:ClearAllPoints() line.overall_black:SetPoint("LEFT",line.overall,"RIGHT",0,0) end else line.overall:Show() line.overall_black:Hide() line.overall:SetWidth(max(300*(overall or 1),1)) end line.total:SetText(total and ExRT.F.shortNumber(total) or "") do dps = dps or 0 line.dps:SetFormattedText("%s.%s",FormatLargeNumber(floor(dps)),format("%.2f",dps % 1):gsub("^.-%.","")) end line.overall:SetGradient("HORIZONTAL",CreateColor(0,0,0,0), CreateColor(0,0,0,0)) line.overall_black:SetGradient("HORIZONTAL",CreateColor(0,0,0,0), CreateColor(0,0,0,0)) if class then local classColorArray = type(CUSTOM_CLASS_COLORS)=="table" and CUSTOM_CLASS_COLORS[class] or RAID_CLASS_COLORS[class] if classColorArray then line.overall:SetVertexColor(classColorArray.r,classColorArray.g,classColorArray.b, 1) line.overall_black:SetVertexColor(classColorArray.r,classColorArray.g,classColorArray.b, 1) else line.overall:SetVertexColor(0.8,0.8,0.8, 1) line.overall_black:SetVertexColor(0.8,0.8,0.8, 1) end else line.overall:SetVertexColor(0.8,0.8,0.8, 1) line.overall_black:SetVertexColor(0.8,0.8,0.8, 1) end if school then SetSchoolColorsToLine(line.overall,school) SetSchoolColorsToLine(line.overall_black,school) end if showCheck then line.leftSide:SetPoint("LEFT",25,0) line.check:SetChecked(checkState) line.check:Show() else line.leftSide:SetPoint("LEFT",5,0) line.check:Hide() end line.sourceGUID = sourceGUID line.spellID = spellID line.doEnemy = doEnemy line.spellLink = spellLink line.tooltip = tooltip line.isDoT = isDoT line:Show() end local function HealingTab_AddSpecialInfo(text) local infoFrame = BWInterfaceFrame.tab.tabs[2].specialInfoFrame if not text then if infoFrame then infoFrame:Hide() end return end if not infoFrame then local sframe = CreateFrame("Frame",nil,BWInterfaceFrame.tab.tabs[2].scroll) BWInterfaceFrame.tab.tabs[2].specialInfoFrame = sframe sframe:SetSize(24,24) sframe:SetPoint("CENTER",BWInterfaceFrame.tab.tabs[2].scroll,"TOPLEFT",0,0) sframe.text = ELib:Text(sframe,"?",22):Size(24,24):Point("CENTER",0,0):Center():Color():Shadow() sframe.text:SetShadowColor(0,0,0,0) local circle = sframe:CreateTexture(nil, "OVERLAY") circle:SetPoint("CENTER",-1,2) circle:SetSize(26,26) circle:SetTexture("Interface\\Addons\\"..GlobalAddonName.."\\media\\radioModern") circle:SetTexCoord(0,0.25,0,1) sframe:SetScript("OnEnter",function(self) self.text:SetShadowColor(1,1,1,1) ELib.Tooltip.Show(self,nil,self.tooltip) end) sframe:SetScript("OnLeave",function(self) self.text:SetShadowColor(1,1,1,0) ELib.Tooltip:Hide() end) infoFrame = sframe end infoFrame.tooltip = text infoFrame:Show() end tab:SetScript("OnShow",function (self) BWInterfaceFrame.timeLineFrame:ClearAllPoints() BWInterfaceFrame.timeLineFrame:SetPoint("TOP",self,"TOP",0,-10) BWInterfaceFrame.timeLineFrame:Show() BWInterfaceFrame.GraphFrame:SetPoint("TOP",0,-105) if BWInterfaceFrame.tab.tabs[2].showGraphChk:GetChecked() then BWInterfaceFrame.GraphFrame:Show() end BWInterfaceFrame.report:Show() if BWInterfaceFrame.nowFightID ~= self.lastFightID then local currFightIDShort = GetFightID(CurrentFight,true) local disableUpdateVars = nil if currFightIDShort == self.lastFightShortID then disableUpdateVars = true end self.lastFightShortID = currFightIDShort HealingTab_Variables.graphCache = nil Graph_AutoUpdateStep() HealingTab_UpdatePage(true,nil,disableUpdateVars) self.lastFightID = BWInterfaceFrame.nowFightID HealingTab_AddSpecialInfo() elseif HealingTab_Variables.graphCache then HealingTab_ReloadGraph(unpack(HealingTab_Variables.graphCache)) end end) tab:SetScript("OnHide",function (self) BWInterfaceFrame.timeLineFrame:Hide() BWInterfaceFrame.report:Hide() BWInterfaceFrame.GraphFrame:Hide() end) ---- Death Tab tab = BWInterfaceFrame.tab.tabs[8] local deathTab = tab local DeathTab_Variables = { --Use this because limit 200 local vars isEnemy = false, onlyPlayers = true, isBuffs = false, isDebuffs = false, isBlack = false, SetDeath_Last_Arg = nil, SetDeath_Last_Arg2 = nil, aurasBlackList = { [116956]=true, [167188]=true, [113742]=true, [6673]=true, [19740]=true, [109773]=true, [1126]=true, [21562]=true, [24907]=true, [166928]=true, [1459]=true, [93435]=true, [19506]=true, [167187]=true, [166916]=true, [166646]=true, [77747]=true, [67330]=true, [116781]=true, [24604]=true, [115921]=true, [51470]=true, [24932]=true, [117666]=true, [128432]=true, [155522]=true, [20217]=true, [469]=true, [57330]=true, } } local DeathTab_SetLine = nil function DeathTab_Variables:ConvertDataToOld(data) if not data.c then return data end local destTable = {data.h} local destTableLen = 1 for i=data.c,1,-1 do local copyTable = data[i] if copyTable and copyTable.t then destTableLen = destTableLen + 1 destTable[destTableLen] = { copyTable.t, copyTable.s, copyTable.ti, copyTable.sp, copyTable.a, copyTable.o, copyTable.sc, copyTable.b, copyTable.ab, copyTable.c, false, copyTable.h, copyTable.hm, copyTable.ia, copyTable.sm, copyTable.dm, } end end for i=deathMaxEvents,data.c+1,-1 do local copyTable = data[i] if copyTable and copyTable.t then destTableLen = destTableLen + 1 destTable[destTableLen] = { copyTable.t, copyTable.s, copyTable.ti, copyTable.sp, copyTable.a, copyTable.o, copyTable.sc, copyTable.b, copyTable.ab, copyTable.c, false, copyTable.h, copyTable.hm, copyTable.ia, copyTable.sm, copyTable.dm, } end end return destTable end local function DeathTab_ClearPage() for i=1,#deathTab.lines do deathTab.lines[i]:Hide() end deathTab.scroll:SetNewHeight(0) deathTab.sourceDropDown:SetText(L.BossWatcherSelect) end local function DeathTab_SetDeath(_,arg,arg2) DeathTab_Variables.SetDeath_Last_Arg = arg DeathTab_Variables.SetDeath_Last_Arg2 = arg2 ELib:DropDownClose() DeathTab_ClearPage() deathTab.sourceDropDown:SetText( arg2 ) local _data = CurrentFight.deathLog[arg] _data = DeathTab_Variables:ConvertDataToOld(_data) local data = {} local minTime,maxTime = _data[1][3]-20,_data[1][3] local GUID = _data[1][2] local deathTime = nil wipe(reportData[8]) reportData[8][1] = date("%H:%M:%S",_data[1][3])..date(" %Mm%Ss",timestampToFightTime(_data[1][3])).." "..GetGUID(_data[1][2]) for i=1,#_data do if _data[i][3] then _data[i].P = i data[#data + 1] = _data[i] minTime = min(minTime,_data[i][3]) end end if DeathTab_Variables.isBuffs or DeathTab_Variables.isDebuffs then local DataDefLen = #_data for i,auraData in ipairs(CurrentFight.auras) do if auraData[3] == GUID and auraData[1] >= minTime and auraData[1] <= maxTime and ((DeathTab_Variables.isBuffs and auraData[7]=='BUFF') or (DeathTab_Variables.isDebuffs and auraData[7]=='DEBUFF')) and (not DeathTab_Variables.isBlack or not DeathTab_Variables.aurasBlackList[ auraData[6] ]) then data[#data + 1] = {6,auraData[2],auraData[1],auraData[6],auraData[8],auraData[9],P=(DataDefLen + i)} end end sort(data,function(a,b) if a[3]==b[3] then return a.Pb[3] end end) end for i=1,#data do if data[i][1] then local _time = timestampToFightTime(data[i][3]) local diffTime = deathTime and format("%.2f",_time - deathTime) or "" if diffTime == "0.00" then diffTime = "-0.00" end local timeText = date("%M:%S.",_time)..format("%03d",_time * 1000 % 1000)..(diffTime~="" and " " or "")..diffTime if data[i][1] == 3 then local text = GetGUID(data[i][2])..GUIDtoText(" [%s]",data[i][2]) .. " ".. L.BossWatcherDeathDeath DeathTab_SetLine(i,timeText,text,0,0,0) reportData[8][#reportData[8] + 1] = "-0.0s "..L.BossWatcherDeathDeath deathTime = _time elseif data[i][1] == 4 then local spellName,_,spellTexture = GetSpellInfo(data[i][4]) local sourceMarker = module.db.raidTargets[ data[i][15] or 0 ] local text = (sourceMarker and GetTargetIconText(sourceMarker) or "")..GetGUID(data[i][2])..GUIDtoText(" [%s]",data[i][2]).." "..L.BossWatcherInstaKill.." ".. L.BossWatcherByText .. " |T"..spellTexture..":0|t"..spellName DeathTab_SetLine(i,timeText,text,0,0,0,data[i][4]) reportData[8][#reportData[8] + 1] = "-0.0s instakill "..GetSpellLink(data[i][4]) deathTime = _time elseif data[i][1] == 5 then local spellName,_,spellTexture = GetSpellInfo(data[i][5]) local text = format(GUILD_NEWS_FORMAT3,GetGUID(data[i][2])..GUIDtoText(" [%s]",data[i][2]),"|T"..spellTexture..":0|t"..spellName) DeathTab_SetLine(i,timeText,text,0,0,0,data[i][5]) reportData[8][#reportData[8] + 1] = "-0.0s "..spellName deathTime = _time elseif data[i][1] == 1 then local spellName,_,spellTexture = GetSpellInfo(data[i][4]) local name = GetGUID(data[i][2])..GUIDtoText(" [%s]",data[i][2]) local overkill = data[i][6] and data[i][6] > 0 and " ("..L.BossWatcherDeathOverKill..":"..data[i][6]..")" or "" local blocked = data[i][8] and data[i][8] > 0 and " ("..L.BossWatcherDeathBlocked..":"..data[i][8]..")" or "" local absorbed = data[i][9] and data[i][9] > 0 and " ("..L.BossWatcherDeathAbsorbed..":"..data[i][9]..")" or "" local isCrit = data[i][10] and "*" or "" local school = " ("..GetSchoolName(data[i][7])..")" local amount = data[i][5] - (data[i][6] or 0) local HP = "" if data[i][12] and data[i][13]~=0 then HP = format("%d%% ",data[i][12]/data[i][13]*100) --HP = format("%d%% > %d%% ",min((data[i][12]+amount)/data[i][13],1)*100,data[i][12]/data[i][13]*100) end if ExRT.F.GetUnitTypeByGUID(data[i][2]) == 0 then name = "|c"..ExRT.F.classColorByGUID(data[i][2])..name.."|r" end local sourceMarker = module.db.raidTargets[ data[i][15] or 0 ] local destMarker = module.db.raidTargets[ data[i][16] or 0 ] local text = HP..(sourceMarker and GetTargetIconText(sourceMarker) or "")..name.." "..L.BossWatcherDeathDamage.." |T"..spellTexture..":0|t"..spellName.." "..L.BossWatcherDeathOn.." "..isCrit..amount..isCrit .. blocked .. absorbed .. overkill .. school DeathTab_SetLine(i,timeText,text,1,0,0,data[i][4]) reportData[8][#reportData[8] + 1] = diffTime.."s."..HP.." -"..isCrit..amount..isCrit ..blocked .. absorbed .. overkill .." ["..GetGUID(data[i][2]).." - "..GetSpellLink(data[i][4]).."]" elseif data[i][1] == 2 then local spellName,_,spellTexture = GetSpellInfo(data[i][4]) local name = GetGUID(data[i][2])..GUIDtoText(" [%s]",data[i][2]) local overheal = data[i][6] and data[i][6] > 0 and " ("..L.BossWatcherDeathOverHeal..":"..data[i][6]..")" or "" local absorbed = data[i][9] and data[i][9] > 0 and " ("..L.BossWatcherDeathAbsorbed..":"..data[i][9]..")" or "" local isCrit = data[i][10] and "*" or "" local school = " ("..GetSchoolName(data[i][7])..")" local amount = data[i][5] - (data[i][6] or 0) local HP = "" if data[i][12] and data[i][13]~=0 then HP = format("%d%% ",data[i][12]/data[i][13]*100) --if not data[i][14] then HP = format("%d%% > ",(data[i][12]-amount)/data[i][13]*100) .. HP end end if ExRT.F.GetUnitTypeByGUID(data[i][2]) == 0 then name = "|c"..ExRT.F.classColorByGUID(data[i][2])..name.."|r" end local sourceMarker = module.db.raidTargets[ data[i][15] or 0 ] local text = HP .. (sourceMarker and GetTargetIconText(sourceMarker) or "")..name.." "..L.BossWatcherDeathHeal..(data[i][14] and (" ("..(ACTION_SPELL_MISSED_ABSORB and strlower(ACTION_SPELL_MISSED_ABSORB) or "absorbed")..")") or "").." |T"..spellTexture..":0|t"..spellName.." "..L.BossWatcherDeathOn.." "..isCrit..amount..isCrit .. absorbed .. overheal .. school DeathTab_SetLine(i,timeText,text,0,1,0,data[i][4]) reportData[8][#reportData[8] + 1] = diffTime.."s."..HP.." +"..isCrit..amount..isCrit .. absorbed .. overheal .." ["..GetGUID(data[i][2]).." - "..GetSpellLink(data[i][4]).."]" elseif data[i][1] == 6 then local spellName,_,spellTexture = GetSpellInfo(data[i][4]) local name = GetGUID(data[i][2])..GUIDtoText(" [%s]",data[i][2]) local isApplied = (data[i][5]==1 or data[i][5]==3) if ExRT.F.GetUnitTypeByGUID(data[i][2]) == 0 then name = "|c"..ExRT.F.classColorByGUID(data[i][2])..name.."|r" end local sourceMarker = module.db.raidTargets[ data[i][15] or 0 ] local text = (sourceMarker and GetTargetIconText(sourceMarker) or "")..name.." "..(isApplied and L.BossWatcherDeathAuraAdd or L.BossWatcherDeathAuraRemove).." |T"..spellTexture..":0|t"..spellName if data[i][6] and data[i][6] > 1 then text = text .. "<"..data[i][6]..">" end DeathTab_SetLine(i,timeText,text,1,1,0,data[i][4]) reportData[8][#reportData[8] + 1] = diffTime.."s. ["..GetGUID(data[i][2]).." "..(isApplied and "+" or "-")..GetSpellLink(data[i][4]).."]" end deathTab.lines[i]:Show() end end deathTab.scroll:SetNewHeight(#data * 18) end local function DeathTab_SetDeathList() local counter = 0 for i,deathData in ipairs(CurrentFight.deathLog) do deathData = DeathTab_Variables:ConvertDataToOld(deathData) local header = deathData.header or deathData[1] local GUID = header[2] local isFriendly = ExRT.F.UnitIsFriendlyByUnitFlag2(CurrentFight.reaction[GUID]) local isPlayer = ExRT.F.GetUnitTypeByGUID(GUID) == 0 if ((isFriendly and not DeathTab_Variables.isEnemy and (not DeathTab_Variables.onlyPlayers or isPlayer)) or (not isFriendly and DeathTab_Variables.isEnemy)) and (deathData[2] and deathData[2][1]) then counter = counter + 1 local classColor = "|cffbbbbbb" if isPlayer then classColor = "|c"..ExRT.F.classColorByGUID(GUID) end local text = classColor..GetGUID(GUID)..GUIDtoText(" [%s]",GUID).."|r" local spellID = nil for j=1,#deathData do if deathData[j] ~= header and ((deathData[j][1] == 1 and deathData[j][6] > 0) or (deathData[j][1] == 4)) then local sourceColor = "|cffbbbbbb" if ExRT.F.GetUnitTypeByGUID(deathData[j][2]) == 0 then sourceColor = "|c"..ExRT.F.classColorByGUID(deathData[j][2]) end local spellName,_,spellTexture = GetSpellInfo(deathData[j][4]) text = text .." < " ..sourceColor .. GetGUID(deathData[j][2])..GUIDtoText(" [%s]",deathData[j][2]).."|r (|T"..spellTexture..":0|t"..spellName..")" spellID = deathData[j][4] break end end local cR,cG,cB = 0,0,0 if header[1] == 5 then local spellName,_,spellTexture = GetSpellInfo(header[5]) spellID = header[5] text = text .." (|T"..spellTexture..":0|t"..spellName..")" cR,cG,cB = .8,.8,0 end local _time = timestampToFightTime( deathData[1][3] ) local timeText = date("%M:%S.",_time)..format("%03d",_time * 1000 % 1000) local arrowPos = _time / GetFightLength(true) DeathTab_SetLine(counter,timeText,text,cR,cG,cB,spellID,i,arrowPos) deathTab.lines[counter]:Show() end end deathTab.scroll:SetNewHeight(counter * 18) end local function DeathTab_UpdateData() local list = deathTab.sourceDropDown.List wipe(list) for i,deathData in ipairs(CurrentFight.deathLog) do deathData = DeathTab_Variables:ConvertDataToOld(deathData) local header = deathData.header or deathData[1] local GUID = header[2] local isFriendly = ExRT.F.UnitIsFriendlyByUnitFlag2(CurrentFight.reaction[GUID]) local isPlayer = ExRT.F.GetUnitTypeByGUID(GUID) == 0 if ((isFriendly and not DeathTab_Variables.isEnemy and (not DeathTab_Variables.onlyPlayers or isPlayer)) or (not isFriendly and DeathTab_Variables.isEnemy)) and (deathData[2] and deathData[2][1]) then local classColor = "" if isPlayer then classColor = "|c"..ExRT.F.classColorByGUID(GUID) elseif isFriendly then classColor = "|cffbbbbbb" end local text = date("%M:%S"..(header[1] == 5 and "*" or "").." ",timestampToFightTime(header[3]))..classColor..GetGUID(GUID)..GUIDtoText(" [%s]",GUID) list[#list+1] = { text = text, arg1 = i, arg2 = text, func = DeathTab_SetDeath, hoverFunc = DamageTab_ShowArrow, leaveFunc = DamageTab_HideArrow, hoverArg = timestampToFightTime( header[3] ) / GetFightLength(true), } end end end local function DeathTab_UpdatePage() DeathTab_UpdateData() DeathTab_ClearPage() DeathTab_SetDeathList() DeathTab_Variables.SetDeath_Last_Arg = nil end tab.chkFriendlyOnlyPlayers = ELib:Radio(tab,L.BossWatcherFriendly.." ("..TUTORIAL_TITLE19..")",true):Point(15,-50):AddButton():OnClick(function(self) DeathTab_Variables.isEnemy = false DeathTab_Variables.onlyPlayers = true self:SetChecked(true) deathTab.chkFriendly:SetChecked(false) deathTab.chkEnemy:SetChecked(false) DeathTab_UpdatePage() end) tab.chkFriendly = ELib:Radio(tab,L.BossWatcherFriendly):Point("LEFT",tab.chkFriendlyOnlyPlayers,"LEFT",200,0):AddButton():OnClick(function(self) DeathTab_Variables.isEnemy = false DeathTab_Variables.onlyPlayers = false deathTab.chkFriendlyOnlyPlayers:SetChecked(false) self:SetChecked(true) deathTab.chkEnemy:SetChecked(false) DeathTab_UpdatePage() end) tab.chkEnemy = ELib:Radio(tab,L.BossWatcherHostile):Point("LEFT",tab.chkFriendly,"LEFT",200,0):AddButton():OnClick(function(self) DeathTab_Variables.isEnemy = true DeathTab_Variables.onlyPlayers = false deathTab.chkFriendlyOnlyPlayers:SetChecked(false) deathTab.chkFriendly:SetChecked(false) self:SetChecked(true) DeathTab_UpdatePage() end) tab.sourceDropDown = ELib:DropDown(tab,250,20):Size(180):Point("TOPLEFT",tab.chkEnemy,"TOPLEFT",200,0):SetText(L.BossWatcherSelect):AddText(L.BossWatcherTarget..":") tab.showBuffsChk = ELib:Check(tab,L.BossWatcherDeathBuffsShow):Point(15,-75):OnClick(function (self) if self:GetChecked() then DeathTab_Variables.isBuffs = true else DeathTab_Variables.isBuffs = false end if DeathTab_Variables.SetDeath_Last_Arg then DeathTab_SetDeath(nil,DeathTab_Variables.SetDeath_Last_Arg,DeathTab_Variables.SetDeath_Last_Arg2) end end) tab.showDebuffsChk = ELib:Check(tab,L.BossWatcherDeathDebuffsShow):Point("LEFT",tab.showBuffsChk,"LEFT",200,0):OnClick(function (self) if self:GetChecked() then DeathTab_Variables.isDebuffs = true else DeathTab_Variables.isDebuffs = false end if DeathTab_Variables.SetDeath_Last_Arg then DeathTab_SetDeath(nil,DeathTab_Variables.SetDeath_Last_Arg,DeathTab_Variables.SetDeath_Last_Arg2) end end) tab.buffsblacklistChk = ELib:Check(tab,L.BossWatcherDeathBlacklist):Point("LEFT",tab.showDebuffsChk,"LEFT",200,0):OnClick(function (self) if self:GetChecked() then DeathTab_Variables.isBlack = true else DeathTab_Variables.isBlack = false end if DeathTab_Variables.SetDeath_Last_Arg then DeathTab_SetDeath(nil,DeathTab_Variables.SetDeath_Last_Arg,DeathTab_Variables.SetDeath_Last_Arg2) end end) local function DeathTab_LineOnEnter(self) if self.spellLink then GameTooltip:SetOwner(self, "ANCHOR_LEFT",120,0) GameTooltip:SetHyperlink(self.spellLink) GameTooltip:Show() end if self.text:IsTruncated() then ELib.Tooltip:Add(nil,{self.text:GetText()},false,true) end if self.arrowPos then BWInterfaceFrame.timeLineFrame.timeLine.arrow:SetPoint("TOPLEFT",BWInterfaceFrame.timeLineFrame.timeLine,"TOPLEFT",BWInterfaceFrame.timeLineFrame.width*self.arrowPos,0) BWInterfaceFrame.timeLineFrame.timeLine.arrow:Show() end end local function DeathTab_LineOnLeave(self) GameTooltip_Hide() ELib.Tooltip:HideAdd() BWInterfaceFrame.timeLineFrame.timeLine.arrow:Hide() end local function DeathTab_LineOnClick(self,button) if not self.clickToLog then if button == "RightButton" then DeathTab_UpdatePage() end return end if button == "RightButton" then return end local name = self.text:GetText() DeathTab_SetDeath(nil,self.clickToLog,name:match("(.-)|r"),nil) end tab.scroll = ELib:ScrollFrame(tab):Size(835,508-25):Point("TOP",0,-105) tab.lines = {} function DeathTab_SetLine(i,textTime,textText,gradientR,gradientG,gradientB,spellID,clickToLog,arrowPos) local line = deathTab.lines[i] if not line then line = CreateFrame("Button",nil,deathTab.scroll.C) deathTab.lines[i] = line line:SetPoint("TOP",0,-(i-1)*18) line:SetSize(810,18) line.time = ELib:Text(line,"00:02."..format("%02d",i),12):Size(150,16):Point("LEFT",10,0):Color():Shadow() line.text = ELib:Text(line,"00:02."..format("%02d",i),12):Size(810-125-20,16):Point("LEFT",125,0):Color():Shadow() line.back = line:CreateTexture(nil, "BACKGROUND") line.back:SetAllPoints() line.back:SetColorTexture( 1, 1, 1, 1) line.back:SetGradient("HORIZONTAL",CreateColor(0,0,0,0), CreateColor(0,0,0,0)) line:SetScript("OnEnter",DeathTab_LineOnEnter) line:SetScript("OnLeave",DeathTab_LineOnLeave) line:SetScript("OnClick",DeathTab_LineOnClick) line:RegisterForClicks("LeftButtonDown","RightButtonDown") end line.time:SetText(textTime) line.text:SetText(textText) line.back:SetGradient("HORIZONTAL",CreateColor(gradientR,gradientG,gradientB, 0.3), CreateColor(gradientR,gradientG,gradientB, 0)) line.spellLink = spellID and "spell:"..spellID line.clickToLog = clickToLog line.arrowPos = arrowPos line:Show() end tab:SetScript("OnShow",function (self) BWInterfaceFrame.timeLineFrame:ClearAllPoints() BWInterfaceFrame.timeLineFrame:SetPoint("TOP",self,"TOP",0,-10) BWInterfaceFrame.timeLineFrame:Show() BWInterfaceFrame.report:Show() if BWInterfaceFrame.nowFightID ~= self.lastFightID then DeathTab_UpdatePage() self.lastFightID = BWInterfaceFrame.nowFightID end end) tab:SetScript("OnHide",function (self) BWInterfaceFrame.timeLineFrame:Hide() BWInterfaceFrame.report:Hide() end) ---- Positions Tab tab = BWInterfaceFrame.tab.tabs[9] local posTab = tab local PositionsTab_UpdatePage = nil local PositionsTab_Variables = { --Use this because limit 200 local vars DebuffsBlackList = { [160029] = true, --Resurrecting; haven't CLEU event for removing }, UnitsBlackList = { ["boss1"]=true, ["boss2"]=true, ["boss3"]=true, ["boss4"]=true, ["boss5"]=true, ["boss6"]=true, ["target"]=true, ["focus"]=true, }, HPList = {}, } BWInterfaceFrame.PositionsTab_Variables = PositionsTab_Variables PositionsTab_Variables.BossToMap = {} local function PositionsTab_UpdatePositions(segment) local time = ceil(segment / 2) local tab = posTab for i=1,40 do tab.raidFrames[i]:Update(segment) end for i=1,6 do tab.unitFrames[i]:Update(time) end local text = "" for destGUID,destData in pairs(PositionsTab_Variables.HPList) do local curHP = destData.res[time] if curHP and curHP ~= 0 and curHP ~= destData.maxhp and (not destData.maxhp or curHP < destData.maxhp) then local color1,color2 if destData.lastSeen and time > destData.lastSeen then color1,color2 = "|cffaaaaaa", "|r" end text = text .. (color1 or "") .. (destData.mark[time] and GetTargetIconText(destData.mark[time]) or "") .. GetGUID(destGUID) .. GUIDtoText(" (%s)",destGUID) .. " "..(destData.maxhp and (destData.maxhp-curHP).."/"..destData.maxhp..format(" <%.1f%%>",(1-curHP/destData.maxhp)*100) or "-"..curHP) .. (color2 or "") .. "|n" end end tab.scroll.mobsText:SetText(text) end tab.timeSlider = ELib:Slider(tab,L.BossWatcherPositionsSlider):Size(800):Point("TOP",0,-20):Range(1,1):OnChange(function (self,value) value = ExRT.F.Round(value) local time = floor(value / 2) self.tooltipText = format("%d:%02d",time / 60,time % 60) self:tooltipReload(self) PositionsTab_UpdatePositions(value) end) tab.timeSlider.tooltipText = "0:00" tab.timeSlider:SetScript("OnMinMaxChanged",function (self) local _min,_max = self:GetMinMaxValues() self.Low:SetText("0:00") _max = max(1,_max / 2) self.High:SetText(format("%d:%02d",_max / 60,_max % 60)) end) tab.timeSlider:SetObeyStepOnDrag(true) tab.scroll = ELib:ScrollFrame(tab):Size(835,540):Point("TOP",0,-50):Height(540) tab.scroll.ScrollBar:Hide() tab.scroll.C:SetWidth( tab.scroll:GetWidth() - 4 ) tab.player = {} tab.scroll.player = tab.player local function PositionsTab_RaidFrame_UpdateHP(self,segment) if not self.Unit then self:Hide() return end local sec = ceil(segment / 2) local data = CurrentFight.graphData[sec] if not data then self:Hide() return end self:Show() local hpNow = data[self.Unit] and data[self.Unit].health or 0 local hpMax = data[self.Unit] and data[self.Unit].hpmax or 0 if hpMax == 0 or hpNow == 0 then self.hp:Hide() else self.hp:Show() self.hp:SetWidth(max(hpNow / hpMax * self.WIDTH,1)) end local data = self.debuffsData and self.debuffsData[segment] if not data then for i=1,#self.debuffs do self.debuffs[i]:Hide() end else for i=1,#data do if i > #self.debuffs then break end local spellID = floor( data[i] ) local texture = GetSpellTexture(data[i]) self.debuffs[i]:Texture(texture) self.debuffs[i].spellID = spellID local timeLeft = (data[i] % 1) * 1000 self.debuffs[i].timeLeft = floor(timeLeft) self.debuffs[i]:Show() end for i=#data+1,#self.debuffs do self.debuffs[i]:Hide() end end local data = CurrentFight.graphData[sec] local powerNow = data[self.Unit] and data[self.Unit].power or 0 local powerMax = data[self.Unit] and data[self.Unit].pmax or 0 if powerMax == 0 then self.power:Hide() self.power_text:SetText("") else self.power:SetShown(powerNow ~= 0) self.power:SetWidth(max(0,min(powerNow / powerMax * self.WIDTH,self.WIDTH))) if powerMax >= 1000 then self.power_text:SetFormattedText("%.1f%%",powerNow/powerMax*100) else self.power_text:SetFormattedText("%d/%d",powerNow,powerMax) end end end local function PositionsTab_UnitFrame_UpdateHP(self,sec) if not self.Unit then self:Hide() return end local data = CurrentFight.graphData[sec] if not data then self:Hide() return end self.text:SetText(data[self.Unit] and data[self.Unit].name or self.Unit) local hpNow = data[self.Unit] and data[self.Unit].health or 0 local hpMax = data[self.Unit] and data[self.Unit].hpmax or 0 if hpMax == 0 then self:Hide() return end self:Show() if hpMax == 0 or hpNow == 0 then self.hp:Hide() return end self.hp:Show() self.hp:SetWidth(max(hpNow / hpMax * self.WIDTH,1)) self.hp_text:SetFormattedText("%.1f",hpNow / hpMax * 100) local powerNow = data[self.Unit] and data[self.Unit].power or 0 local powerMax = data[self.Unit] and data[self.Unit].pmax or 0 if powerMax == 0 then self.power:Hide() self.power_text:SetText("") else self.power:SetShown(powerNow ~= 0) self.power:SetWidth(max(0,min(powerNow / powerMax * self.WIDTH,self.WIDTH))) if powerMax >= 1000 then self.power_text:SetFormattedText("%.1f%%",powerNow/powerMax*100) else self.power_text:SetFormattedText("%d/%d",powerNow,powerMax) end end end local function PositionsTab_RaidFrame_DebuffOnEnter(self) posTab.scroll.disableTooltip = true if self.spellID then ELib.Tooltip.Link(self,"spell:"..self.spellID) if self.stacks then GameTooltip:AddLine(L.BossWatcherBuffsAndDebuffsTooltipCountText..": "..self.stacks) GameTooltip:Show() end if self.timeLeft then GameTooltip:AddLine(format(PET_TIME_LEFT_SECONDS,self.timeLeft + 1)) GameTooltip:Show() end end end local function PositionsTab_RaidFrame_DebuffOnLeave(self) posTab.scroll.disableTooltip = nil ELib.Tooltip:Hide() end tab.raidFrames = {} for i=1,8 do for j=1,5 do local frame = CreateFrame("Button",nil,tab.scroll) tab.raidFrames[(i-1)*5+j] = frame local WIDTH,HEIGHT = 100,35 frame.WIDTH = WIDTH frame:SetSize(WIDTH,HEIGHT) frame:SetPoint("BOTTOMLEFT",tab.scroll,5+(i-1)*(WIDTH+3),5+(j-1)*(HEIGHT+3)) frame.text = ELib:Text(frame,UnitName('player'),12):Size(WIDTH-10,14):Point("BOTTOMLEFT",5,6):Left():Color() frame.text:SetDrawLayer("ARTWORK", 0) ELib:Border(frame,1,0,0,0,1,1) frame.back = frame:CreateTexture(nil, "BACKGROUND",nil,-5) frame.back:SetSize(WIDTH,HEIGHT) frame.back:SetPoint("LEFT",0,0) frame.back:SetColorTexture(.05,.05,.05,1) frame.hp = frame:CreateTexture(nil, "BACKGROUND",nil,0) frame.hp:SetSize(WIDTH,HEIGHT) frame.hp:SetPoint("LEFT",0,0) frame.hp:SetColorTexture(.3,.3,.3,1) frame.power = frame:CreateTexture(nil, "BACKGROUND",nil,3) frame.power:SetSize(WIDTH,3) frame.power:SetPoint("BOTTOMLEFT",0,0) frame.power:SetColorTexture(.3,.3,1,1) frame.power_text = ELib:Text(frame,"99%",8):Point("BOTTOMRIGHT",-2,1):Right():Color() frame.power_text:SetDrawLayer("ARTWORK", 0) frame.Update = PositionsTab_RaidFrame_UpdateHP frame.debuffs = {} for i=1,6 do frame.debuffs[i] = ELib:Frame(frame):Point("TOP",frame,"TOPLEFT",7 + (i-1)*(14+1),1):Size(14,14):Texture(GetSpellTexture(25771),nil):TexturePoint('x') frame.debuffs[i]:SetScript("OnEnter",PositionsTab_RaidFrame_DebuffOnEnter) frame.debuffs[i]:SetScript("OnLeave",PositionsTab_RaidFrame_DebuffOnLeave) end frame:Hide() end end tab.unitFrames = {} for i=1,6 do local frame = CreateFrame("Button",nil,tab.scroll) tab.unitFrames[i] = frame local WIDTH,HEIGHT = 195,30 frame:SetSize(WIDTH,HEIGHT) frame.WIDTH = WIDTH frame:SetPoint("TOPLEFT",tab.scroll,5,-50-(i-1)*(HEIGHT+3)) frame.text = ELib:Text(frame,UnitName('player'),11):Size(WIDTH-40,20):Point("LEFT",2,0):Color() frame.text:SetDrawLayer("ARTWORK", 0) frame.hp_text = ELib:Text(frame,"99.9%",10):Size(36,20):Point("TOPRIGHT",-2,-3):Right():Color() frame.hp_text:SetDrawLayer("ARTWORK", 0) ELib:Border(frame,1,0,0,0,1,1) frame.back = frame:CreateTexture(nil, "BACKGROUND",nil,-5) frame.back:SetSize(WIDTH,HEIGHT) frame.back:SetPoint("LEFT",0,0) frame.back:SetColorTexture(.05,.05,.05,1) frame.hp = frame:CreateTexture(nil, "BACKGROUND",nil,0) frame.hp:SetSize(WIDTH,HEIGHT) frame.hp:SetPoint("LEFT",0,0) frame.hp:SetColorTexture(.3,.3,.3,1) frame.power = frame:CreateTexture(nil, "BACKGROUND",nil,3) frame.power:SetSize(WIDTH,4) frame.power:SetPoint("BOTTOMLEFT",0,0) frame.power:SetColorTexture(.3,.3,1,1) frame.power_text = ELib:Text(frame,"99/120",8):Point("BOTTOMRIGHT",-2,1):Right():Color() frame.power_text:SetDrawLayer("ARTWORK", 0) frame.Update = PositionsTab_UnitFrame_UpdateHP frame:Hide() end tab.scroll.mobsText = ELib:Text(tab.scroll,"",10):Size(400,0):Point("TOPRIGHT",-5,-3):Left():Color() function PositionsTab_UpdatePage() local tab = posTab local positionsData = CurrentFight.positionsData local minX,maxX,minY,maxY = 0,1,0,1 local knownMap = nil local encounterID = CurrentFight.encounterID for i=1,#tab.player do tab.player[i]:Hide() end local dotCount = 0 local raidFrames = {} local graphData = CurrentFight.graphData if graphData and graphData[1] then for name,data in pairs(graphData[1]) do if not PositionsTab_Variables.UnitsBlackList[name] then local unitGUID = ExRT.F.table_find2(CurrentFight.raidguids,name) local cR,cG,cB = .8,.8,.8 if unitGUID then local class = select(2,GetPlayerInfoByGUID(unitGUID)) if class then cR,cG,cB = ExRT.F.classColorNum(class) end end raidFrames[#raidFrames + 1] = {name,cR,cG,cB} end end end sort(raidFrames,function(a,b) return a[1]trackingData[1] then break elseif auraData[6] == 182006 and auraData[8] == 2 and auraData[1]<=trackingData[1] then newSourceGUID = auraData[3] end end sourceGUID = newSourceGUID or sourceGUID elseif spellID == 181617 then --Mannoroth: Mannoroth's Gaze local newSourceGUID = nil for _,auraData in ipairs(CurrentFight.auras) do if auraData[6] == 181597 and auraData[8] == 2 and auraData[1]>trackingData[1] then break elseif auraData[6] == 181597 and auraData[8] == 2 and auraData[1]<=trackingData[1] then newSourceGUID = auraData[3] end end sourceGUID = newSourceGUID or sourceGUID elseif spellID == 180161 then --Tyrant Velhari: Edict of Condemnation local newSourceGUID = nil for _,auraData in ipairs(CurrentFight.auras) do if auraData[6] == 185241 and auraData[8] == 1 and auraData[1]<=trackingData[1] then newSourceGUID = auraData[3] elseif auraData[6] == 185241 and auraData[8] == 1 and auraData[1]>trackingData[1] then break end end sourceGUID = newSourceGUID or sourceGUID elseif spellID == 198099 then --Ursoc: Barreling Momentum local newSourceGUID = nil for _,auraData in ipairs(CurrentFight.auras) do if auraData[6] == 198006 and auraData[8] == 1 and auraData[1]<=trackingData[1] then newSourceGUID = auraData[3] elseif auraData[1]>trackingData[1] then break end end sourceGUID = newSourceGUID or sourceGUID elseif spellID == 228162 then --Odyn: Shield of Light local newSourceGUID = nil if CurrentFight.cast[sourceGUID] then for _,castData in ipairs(CurrentFight.cast[sourceGUID]) do if castData[2] == 228162 and castData[3] == 1 and castData[1]<=trackingData[1] then newSourceGUID = castData[4] elseif castData[1]>trackingData[1] then break end end end sourceGUID = newSourceGUID or sourceGUID end sourceRaidTarget = module.db.raidTargets[ trackingData[3] or 0 ] destRaidTarget = module.db.raidTargets[ trackingData[5] or 0 ] local diff = prevTime and (time-prevTime) or 999 if diff <= 0.05 then prevTimeRow = prevTimeRow or prevTime else if prevTimeRow then for j=#L,1,-1 do if L[j][3] and L[j][3] < prevTimeRow then tinsert(L,j+1,{" "," "}) break elseif L[j][1] == " " then break end end L[#L+1] = {" "," "} end prevTimeRow = nil end prevTime = time --{timestamp,sourceGUID,sourceFlags2,destGUID,destFlags2,spellID,amount,overkill,school,blocked,absorbed,critical,multistrike,missType} local amountString if not trackingData[14] then local overkill = trackingData[8] and trackingData[8] > 0 and " ("..ExRT.L.BossWatcherDeathOverKill..": "..ExRT.F.shortNumber(trackingData[8])..") " or "" local blocked = trackingData[10] and trackingData[10] > 0 and " ("..ExRT.L.BossWatcherDeathBlocked..": "..ExRT.F.shortNumber(trackingData[10])..") " or "" local absorbed = trackingData[11] and trackingData[11] > 0 and " ("..ExRT.L.BossWatcherDeathAbsorbed..": "..ExRT.F.shortNumber(trackingData[11])..") " or "" amountString = (trackingData[12] and "*" or "")..ExRT.F.shortNumber(trackingData[7] - trackingData[8])..(trackingData[12] and "*" or "").." "..overkill..blocked..absorbed amountString = strtrim(amountString) else amountString = "~"..trackingData[13].."~" end L[#L+1] = {date("%M:%S.", time)..format("%03d",time%1*1000),(sourceRaidTarget and GetTargetIconText(sourceRaidTarget) or "").."|c".. ExRT.F.classColorByGUID(sourceGUID)..GetGUID(sourceGUID)..GUIDtoText(" [%s]",sourceGUID).."|r > "..(destRaidTarget and GetTargetIconText(destRaidTarget) or "").."|c".. ExRT.F.classColorByGUID(destGUID)..GetGUID(destGUID)..GUIDtoText(" [%s]",destGUID).."|r: "..amountString,time} end end local prevTimeText = nil for i=1,#L do if L[i][1] == prevTimeText then prevTimeText = L[i][1] L[i][1] = "" else prevTimeText = L[i][1] end end trackingTab.targetsList:Update() end function tab.targetsList:HoverListValue(isHover,index,hoveredObj) if not isHover then GameTooltip_Hide() else if hoveredObj.text2:IsTruncated() then GameTooltip:SetOwner(self,"ANCHOR_CURSOR") GameTooltip:AddLine(hoveredObj.text2:GetText() ) GameTooltip:Show() end end end local function UpdateTrackingPage() wipe(trackingTab.targetsList.L) wipe(trackingTab.spellsList.L) wipe(trackingTab.spellsList.D) local L,D = trackingTab.spellsList.L,trackingTab.spellsList.D for i,trackingData in ipairs(CurrentFight.tracking) do if not ExRT.F.table_find(D,trackingData[6]) then D[#D+1] = trackingData[6] end end for i=1,#D do local spellName,_,spellTexture = GetSpellInfo(D[i]) L[i] = "|T"..spellTexture..":0|t "..spellName end trackingTab.spellsList:Update() trackingTab.targetsList:Update() end tab.optionsSpellsList = ELib:ScrollTableList(tab.headerTab.tabs[2],80,20,0,20):Size(650,400):Point(10,-15) local function UpdateTrackingOptionsPage() wipe(trackingTab.optionsSpellsList.L) local L = trackingTab.optionsSpellsList.L for spellID,encounterID in pairs(var_trackingDamageSpells) do local spellName,_,spellTexture = GetSpellInfo(spellID) spellName = spellName or '???' spellTexture = spellTexture or "Interface\\Icons\\INV_MISC_QUESTIONMARK" local encounterName = nil if type(encounterID)=='number' then encounterName = ExRT.L.bossName[encounterID] end L[#L+1] = {spellID,"|T"..spellTexture..":0|t",(encounterName and encounterName..": " or "")..spellName,module.db.def_trackingDamageSpells[spellID] and "" or "|TInterface\\AddOns\\"..GlobalAddonName.."\\media\\DiesalGUIcons16x256x128:16:16:0:0:256:128:128:144:64:80|t",type(encounterID)=='number' and ExRT.F.table_find(TrackingTab_Variables.EncounterOrder,encounterID) or -1} end sort(L,function(a,b) if a[5]==b[5] then return a[1]b[5] end end) trackingTab.optionsSpellsList:Update() end function tab.optionsSpellsList:AdditionalLineClick() local x,y = ExRT.F.GetCursorPos(self) local pos = self:GetWidth()-x if pos <= 25 and pos > 7 then local parent = self.mainFrame local i = parent.selected if parent.L[i][4] ~= "" then local spellID = parent.L[i][1] VMRT.BossWatcher.trackingDamageSpells[ spellID ] = nil UpdateTrackingDamageSpellsTable() UpdateTrackingOptionsPage() end end end UpdateTrackingOptionsPage() tab.optionsEditAddSpell = ELib:Edit(tab.headerTab.tabs[2],6,true):Size(200,20):Point("TOPLEFT",tab.optionsSpellsList,"BOTTOMLEFT",0,-5) tab.optionsButtonAddSpell = ELib:Button(tab.headerTab.tabs[2],L.cd2TextAdd):Size(150,20):Point("LEFT",tab.optionsEditAddSpell,"RIGHT",5,0):OnClick(function() local tab = trackingTab local spellID = tonumber(tab.optionsEditAddSpell:GetText()) if not spellID then return end tab.optionsEditAddSpell:SetText("") VMRT.BossWatcher.trackingDamageSpells[ spellID ] = true UpdateTrackingDamageSpellsTable() UpdateTrackingOptionsPage() end) tab:SetScript("OnShow",function (self) if BWInterfaceFrame.nowFightID ~= self.lastFightID then self.lastFightID = BWInterfaceFrame.nowFightID UpdateTrackingPage() end end) ---- Tracking tab = BWInterfaceFrame.tab.tabs[10].tab.tabs[2] local damageTakenTab = tab local DamageTakenTab_Variables = { graph_step = 5, check_byrole = true, check_absorbs = false, check_enemy = false, } tab.graph = ELib:GraphCol(tab):Point("TOP",0,-35):Size(780,400) tab.graph.TooltipText = function(self,tip) local t = tip.dataX local val = tip.dataY if val >= 1000000 then val = format("%.2fm",val/1000000) elseif val >= 1000 then val = format("%dk",val/1000) else val = format("%d",val) end return (tip.dataT and tip.dataT.."\n" or "") .. format("%d:%02d",t / 60,t % 60) .. "\n" ..val end tab.graph.TextX = function(self,x,data) x = data.x return format("%d:%02d",x / 60,x % 60) end tab.graph.media.colors = { {1,.3,0,1}, {0.5,.8,0,1}, {1,.3,0,.55} } tab.graph.stepSlider = ELib:Slider(tab.graph,"",true):Point("RIGHT",tab.graph,"LEFT",-10,0):Size(100):Range(1,60):SetTo(DamageTakenTab_Variables.graph_step):OnChange(function(self) local step = floor(self:GetValue() + 0.5) if DamageTakenTab_Variables.graph_step == step then return end DamageTakenTab_Variables.graph_step = step self:Tooltip(L.BossWatcherGraphicsStep.."\n"..step) self:tooltipReload() damageTakenTab:Update() end):Tooltip(L.BossWatcherGraphicsStep.."\n"..DamageTakenTab_Variables.graph_step) tab.chkByRole = ELib:Radio(tab,"Non-tanks/Tanks",DamageTakenTab_Variables.check_byrole):Point("TOPLEFT",tab.graph,"BOTTOMLEFT",0,-15):AddButton():OnClick(function(self) damageTakenTab.chkAbsorbs:SetChecked(false) damageTakenTab.chkEnemies:SetChecked(false) self:SetChecked(true) DamageTakenTab_Variables.check_byrole = true DamageTakenTab_Variables.check_absorbs = false DamageTakenTab_Variables.check_enemy = false damageTakenTab:Update() end) tab.chkAbsorbs = ELib:Radio(tab,"Non-absorbed/Absorbed",DamageTakenTab_Variables.check_absorbs):Point("TOPLEFT",tab.chkByRole,"BOTTOMLEFT",0,-5):AddButton():OnClick(function(self) damageTakenTab.chkByRole:SetChecked(false) damageTakenTab.chkEnemies:SetChecked(false) self:SetChecked(true) DamageTakenTab_Variables.check_byrole = false DamageTakenTab_Variables.check_absorbs = true DamageTakenTab_Variables.check_enemy = false damageTakenTab:Update() end) tab.chkEnemies = ELib:Radio(tab,"Enemies",DamageTakenTab_Variables.check_absorbs):Point("TOPLEFT",tab.chkAbsorbs,"BOTTOMLEFT",0,-5):AddButton():OnClick(function(self) damageTakenTab.chkByRole:SetChecked(false) damageTakenTab.chkAbsorbs:SetChecked(false) self:SetChecked(true) DamageTakenTab_Variables.check_byrole = false DamageTakenTab_Variables.check_absorbs = false DamageTakenTab_Variables.check_enemy = true damageTakenTab:Update() end) function tab:Update() local result = {} local max_t = 0 local doEnemy = DamageTakenTab_Variables.check_enemy local isReverse = false for destGUID,destData in pairs(CurrentFight.damage) do if ExRT.F.table_len(destVar) == 0 or destVar[destGUID] then for destReaction,destReactData in pairs(destData) do local isEnemy = false if GetUnitInfoByUnitFlagFix(destReaction,2) == 512 then isEnemy = true end if (isEnemy and doEnemy) or (not isEnemy and not doEnemy) then for sourceGUID,sourceData in pairs(destReactData) do local owner = ExRT.F.Pets:getOwnerGUID(sourceGUID,GetPetsDB()) if owner then sourceGUID = owner end if ExRT.F.table_len(sourceVar) == 0 or sourceVar[sourceGUID] then local source = isReverse and destGUID or sourceGUID local dest = isReverse and sourceGUID or destGUID for spellID,spellSegments in pairs(sourceData) do for segment,spellAmount in pairs(spellSegments) do local t = floor(CurrentFight.segments[segment].t - CurrentFight.encounterStart) if not result[t] then result[t] = {0,0} end if DamageTakenTab_Variables.check_byrole then local c = CurrentFight.other.rolesGUID[dest] == "TANK" and 2 or 1 --result[t][c] = result[t][c] + spellAmount.amount + spellAmount.absorbed - spellAmount.overkill result[t][c] = result[t][c] + spellAmount.amount - spellAmount.overkill elseif DamageTakenTab_Variables.check_absorbs then result[t][1] = result[t][1] + spellAmount.amount - spellAmount.overkill result[t][2] = result[t][2] + spellAmount.absorbed elseif DamageTakenTab_Variables.check_enemy then result[t][1] = result[t][1] + spellAmount.amount + spellAmount.absorbed - spellAmount.overkill end if t > max_t then max_t = t end end end end end end end end end --result={} max_t=600 for i=0,600 do result[i] = {math.random(100000),math.random(50000)} end local data = {} local step = DamageTakenTab_Variables.graph_step local now = 1 local total = 0 for i=0,max_t do if i % step == 0 and i > 0 then now = now + 1 end data[now] = (data[now] or {{0,0},x=i}) data[now][1][1] = data[now][1][1] + (result[i] and result[i][1] or 0) data[now][1][2] = data[now][1][2] + (result[i] and result[i][2] or 0) end if DamageTakenTab_Variables.check_byrole then data.c1_1 = tab.graph.media.colors[1] data.c1_2 = tab.graph.media.colors[2] data.t1_1 = "Non-tanks" data.t1_2 = "Tanks" elseif DamageTakenTab_Variables.check_absorbs then data.c1_1 = tab.graph.media.colors[1] data.c1_2 = tab.graph.media.colors[3] data.t1_1 = "Non-absorbed" data.t1_2 = "Absorbed" elseif DamageTakenTab_Variables.check_enemy then data.c1_1 = tab.graph.media.colors[1] for i=1,#data do for j=2,#data[i][1] do data[i][1][j] = nil end end end self.graph.data = data self.graph:Update() end tab:SetScript("OnShow",function (self) if BWInterfaceFrame.nowFightID ~= self.lastFightID then self.lastFightID = BWInterfaceFrame.nowFightID damageTakenTab:Update() end end) BWInterfaceFrame:GetScript("OnShow")(BWInterfaceFrame) end