You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
12081 lines
423 KiB
12081 lines
423 KiB
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 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()
|
|
return 4
|
|
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,
|
|
}
|
|
if MenuUtil then
|
|
MenuUtil.CreateContextMenu(BWInterfaceFrame.bossButtonDropDown, function(ownerRegion, rootDescription)
|
|
for i=1,#fightsList do
|
|
local menu = fightsList[i]
|
|
if menu.isTitle then
|
|
rootDescription:CreateTitle(menu.text)
|
|
else
|
|
rootDescription:CreateButton(menu.text, menu.func)
|
|
end
|
|
end
|
|
end)
|
|
else
|
|
EasyMenu(fightsList, BWInterfaceFrame.bossButtonDropDown, "cursor", 10 , -15, "MENU")
|
|
end
|
|
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,
|
|
}
|
|
if MenuUtil then
|
|
MenuUtil.CreateContextMenu(BWInterfaceFrame.bossButtonDropDown, function(ownerRegion, rootDescription)
|
|
for i=1,#fightsList do
|
|
local menu = fightsList[i]
|
|
if menu.isTitle then
|
|
rootDescription:CreateTitle(menu.text)
|
|
else
|
|
rootDescription:CreateButton(menu.text, menu.func)
|
|
end
|
|
end
|
|
end)
|
|
else
|
|
EasyMenu(fightsList, BWInterfaceFrame.bossButtonDropDown, "cursor", 10 , -15, "MENU")
|
|
end
|
|
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]<b[2] end)
|
|
sort(destTable,function(a,b) return a[2]<b[2] end)
|
|
wipe(BWInterfaceFrame.tab.tabs[1].sourceDropDown.List)
|
|
wipe(BWInterfaceFrame.tab.tabs[1].targetDropDown.List)
|
|
if not isBySpellDamage then
|
|
BWInterfaceFrame.tab.tabs[1].sourceDropDown.List[1] = {text = L.BossWatcherAll,func = DamageTab_DPS_SelectDropDownSource,padding = 16}
|
|
BWInterfaceFrame.tab.tabs[1].targetDropDown.List[1] = {text = L.BossWatcherAll,func = DamageTab_DPS_SelectDropDownDest,padding = 16}
|
|
for i=1,#sourceTable do
|
|
local isPlayer = ExRT.F.GetUnitTypeByGUID(sourceTable[i][1]) == 0
|
|
local classColor = ""
|
|
if isPlayer then
|
|
classColor = "|c"..ExRT.F.classColorByGUID(sourceTable[i][1])
|
|
end
|
|
BWInterfaceFrame.tab.tabs[1].sourceDropDown.List[i+1] = {
|
|
text = classColor..sourceTable[i][2]..GUIDtoText(" [%s]",sourceTable[i][1]),
|
|
arg1 = sourceTable[i][1],
|
|
func = DamageTab_DPS_SelectDropDownSource,
|
|
checkFunc = DamageTab_DPS_CheckDropDownSource,
|
|
checkable = true,
|
|
}
|
|
end
|
|
BWInterfaceFrame.tab.tabs[1].targetDropDown:Show()
|
|
BWInterfaceFrame.tab.tabs[1].targetText:Show()
|
|
else
|
|
for i=1,#sourceTable do
|
|
local spellColorTable = module.db.schoolsColors[ module.db.spellsSchool[ sourceTable[i][1] ] or 0 ] or module.db.schoolsColors[0]
|
|
local spellColor = "|cff"..format("%02x%02x%02x",spellColorTable.r*255,spellColorTable.g*255,spellColorTable.b*255)
|
|
BWInterfaceFrame.tab.tabs[1].sourceDropDown.List[i] = {
|
|
text = spellColor..sourceTable[i][2],
|
|
arg1 = sourceTable[i][1],
|
|
func = DamageTab_DPS_SelectDropDownSource_Spell,
|
|
icon = sourceTable[i][3],
|
|
hoverFunc = DamageTab_HoverDropDownSpell,
|
|
hoverArg = sourceTable[i][1],
|
|
leaveFunc = GameTooltip_Hide,
|
|
}
|
|
end
|
|
BWInterfaceFrame.tab.tabs[1].targetDropDown:Hide()
|
|
BWInterfaceFrame.tab.tabs[1].targetText:Hide()
|
|
wipe(sourceVar)
|
|
wipe(destVar)
|
|
return
|
|
end
|
|
for i=1,#destTable do
|
|
local isPlayer = ExRT.F.GetUnitTypeByGUID(destTable[i][1]) == 0
|
|
local classColor = ""
|
|
if isPlayer then
|
|
classColor = "|c"..ExRT.F.classColorByGUID(destTable[i][1])
|
|
end
|
|
BWInterfaceFrame.tab.tabs[1].targetDropDown.List[i+1] = {
|
|
text = classColor.. date("%M:%S ", timestampToFightTime( CurrentFight.damage_seen[destTable[i][1]] ))..GetGUID(destTable[i][1])..GUIDtoText(" [%s]",destTable[i][1]),
|
|
arg1 = destTable[i][1],
|
|
func = DamageTab_DPS_SelectDropDownDest,
|
|
hoverFunc = DamageTab_ShowArrow,
|
|
leaveFunc = DamageTab_HideArrow,
|
|
hoverArg = timestampToFightTime( CurrentFight.damage_seen[destTable[i][1]] ) / GetFightLength(true),
|
|
checkFunc = DamageTab_DPS_CheckDropDownDest,
|
|
checkable = true,
|
|
}
|
|
end
|
|
if not disableUpdateVars then
|
|
wipe(sourceVar)
|
|
wipe(destVar)
|
|
end
|
|
end
|
|
|
|
function DamageTab_UpdatePage(updateLists,midFunc,disableUpdateVars)
|
|
ExRT.F.dprint("Damage Tab: Update Page",GetTime())
|
|
if updateLists then
|
|
DamageTab_DPS(disableUpdateVars)
|
|
end
|
|
if midFunc then
|
|
midFunc()
|
|
end
|
|
if DamageTab_Variables.state_spellsTarget then
|
|
DamageTab_UpdateLinesSpellToTargets()
|
|
elseif DamageTab_Variables.state_spells then
|
|
DamageTab_UpdateLinesSpells()
|
|
else
|
|
DamageTab_UpdateLinesPlayers()
|
|
end
|
|
end
|
|
|
|
tab.chkFriendly = ELib:Radio(tab,L.BossWatcherFriendly,true):Point(15,-50):AddButton():OnClick(function(self)
|
|
DamageTab_Variables.state_friendly = true
|
|
DamageTab_Variables.state_spellsTarget = false
|
|
BWInterfaceFrame.tab.tabs[1].chkEnemy:SetChecked(false)
|
|
BWInterfaceFrame.tab.tabs[1].chkSpellsTargets:SetChecked(false)
|
|
self:SetChecked(true)
|
|
|
|
DamageTab_UpdatePage(true)
|
|
end)
|
|
tab.chkEnemy = ELib:Radio(tab,L.BossWatcherHostile):Point(15,-65):AddButton():OnClick(function(self)
|
|
DamageTab_Variables.state_friendly = false
|
|
DamageTab_Variables.state_spellsTarget = false
|
|
BWInterfaceFrame.tab.tabs[1].chkFriendly:SetChecked(false)
|
|
BWInterfaceFrame.tab.tabs[1].chkSpellsTargets:SetChecked(false)
|
|
self:SetChecked(true)
|
|
|
|
DamageTab_UpdatePage(true)
|
|
end)
|
|
tab.chkSpellsTargets = ELib:Radio(tab,L.BossWatcherDamageDamageSpellToFriendly):Point(15,-80):AddButton():OnClick(function(self)
|
|
DamageTab_Variables.state_friendly = true
|
|
DamageTab_Variables.state_spellsTarget = true
|
|
BWInterfaceFrame.tab.tabs[1].chkFriendly:SetChecked(false)
|
|
BWInterfaceFrame.tab.tabs[1].chkEnemy:SetChecked(false)
|
|
self:SetChecked(true)
|
|
|
|
DamageTab_UpdatePage(true)
|
|
end)
|
|
|
|
tab.bySource = ELib:Radio(tab,L.BossWatcherBySource,true):Point(200,-50):AddButton():OnClick(function(self)
|
|
DamageTab_Variables.state_byTarget = false
|
|
DamageTab_Variables.state_spells = false
|
|
BWInterfaceFrame.tab.tabs[1].byTarget:SetChecked(false)
|
|
BWInterfaceFrame.tab.tabs[1].bySpell:SetChecked(false)
|
|
self:SetChecked(true)
|
|
|
|
DamageTab_UpdatePage(true)
|
|
end)
|
|
tab.byTarget = ELib:Radio(tab,L.BossWatcherByTarget):Point(200,-65):AddButton():OnClick(function(self)
|
|
DamageTab_Variables.state_byTarget = true
|
|
DamageTab_Variables.state_spells = false
|
|
BWInterfaceFrame.tab.tabs[1].bySource:SetChecked(false)
|
|
BWInterfaceFrame.tab.tabs[1].bySpell:SetChecked(false)
|
|
self:SetChecked(true)
|
|
|
|
DamageTab_UpdatePage(true)
|
|
end)
|
|
tab.bySpell = ELib:Radio(tab,L.BossWatcherBySpell):Point(200,-80):AddButton():OnClick(function(self)
|
|
DamageTab_Variables.state_byTarget = false
|
|
DamageTab_Variables.state_spells = true
|
|
BWInterfaceFrame.tab.tabs[1].bySource:SetChecked(false)
|
|
BWInterfaceFrame.tab.tabs[1].byTarget:SetChecked(false)
|
|
self:SetChecked(true)
|
|
|
|
DamageTab_UpdatePage(true)
|
|
end)
|
|
|
|
|
|
tab.sourceDropDown = ELib:DropDown(tab,250,20):Size(195):Point(430,-50):SetText(L.BossWatcherAll):Tooltip(L.BossWatcherDropdownsHoldShiftSource)
|
|
tab.sourceText = ELib:Text(tab,L.BossWatcherSource..":",12):Size(100,20):Point("RIGHT",tab.sourceDropDown,"LEFT",-6,0):Right():Color():Shadow()
|
|
|
|
tab.targetDropDown = ELib:DropDown(tab,250,20):Size(195):Point(430,-75):SetText(L.BossWatcherAll):Tooltip(L.BossWatcherDropdownsHoldShiftDest)
|
|
tab.targetText = ELib:Text(tab,L.BossWatcherTarget..":",12):Size(100,20):Point("TOPRIGHT",tab.targetDropDown,"TOPLEFT",-6,0):Right():Color():Shadow()
|
|
|
|
|
|
function tab.sourceDropDown:additionalToggle()
|
|
for i=2,#self.List do
|
|
self.List[i].checkState = sourceVar[ self.List[i].arg1 ]
|
|
end
|
|
end
|
|
function tab.targetDropDown:additionalToggle()
|
|
for i=2,#self.List do
|
|
self.List[i].checkState = destVar[ self.List[i].arg1 ]
|
|
end
|
|
end
|
|
|
|
tab.showOverallChk = ELib:Check(tab,"|cffffffff"..L.BossWatcherOverdamage):Point(650,-50):Tooltip(L.BossWatcherDamageShowOver):OnClick(function (self)
|
|
if self:GetChecked() then
|
|
DamageTab_Variables.ShowAll = true
|
|
else
|
|
DamageTab_Variables.ShowAll = false
|
|
end
|
|
DamageTab_UpdatePage()
|
|
end)
|
|
|
|
tab.showGraphChk = ELib:Check(tab,"|cffffffff"..L.BossWatcherTabGraphics.." ",VMRT.BossWatcher.optionsDamageGraph):Point(650,-75):OnClick(function (self)
|
|
local tab1 = BWInterfaceFrame.tab.tabs[1]
|
|
if self:GetChecked() then
|
|
tab1.scroll:Point("TOP",0,-305)
|
|
BWInterfaceFrame.GraphFrame:Show()
|
|
else
|
|
tab1.scroll:Point("TOP",0,-100)
|
|
BWInterfaceFrame.GraphFrame:Hide()
|
|
end
|
|
VMRT.BossWatcher.optionsDamageGraph = self:GetChecked()
|
|
if DamageTab_Variables.graphCache then
|
|
DamageTab_ReloadGraph(unpack(DamageTab_Variables.graphCache))
|
|
end
|
|
end)
|
|
|
|
tab.scroll = ELib:ScrollFrame(tab):Point("TOP",0,VMRT.BossWatcher.optionsDamageGraph and -305 or -100):Point("BOTTOM",0,10):Height(600)
|
|
tab.scroll:SetWidth(835)
|
|
tab.scroll.C:SetWidth(835)
|
|
tab.lines = {}
|
|
|
|
local function DamageTab_RightClick_BackFunction()
|
|
if not DamageTab_Variables.Back_state then
|
|
DamageTab_UpdatePage(true)
|
|
return
|
|
end
|
|
DamageTab_Variables.state_friendly = DamageTab_Variables.Back_state[1]
|
|
DamageTab_Variables.state_spells = DamageTab_Variables.Back_state[2]
|
|
DamageTab_Variables.state_spellsTarget = DamageTab_Variables.Back_state[3]
|
|
DamageTab_Variables.state_byTarget = DamageTab_Variables.Back_state[4]
|
|
|
|
DamageTab_UpdatePage(true,function()
|
|
sourceVar = DamageTab_Variables.Back_state[5]
|
|
destVar = DamageTab_Variables.Back_state[6]
|
|
end)
|
|
DamageTab_Variables.Back_state = nil
|
|
end
|
|
|
|
tab.scroll:SetScript("OnMouseUp",function(self,button)
|
|
if button == "RightButton" then
|
|
DamageTab_RightClick_BackFunction()
|
|
end
|
|
end)
|
|
|
|
local function DamageTab_Line_Check_OnClick(self)
|
|
local spellID = self:GetParent().spellID or self:GetParent().sourceGUID
|
|
if not spellID then
|
|
return
|
|
end
|
|
local graphData = BWInterfaceFrame.GraphFrame.G.data
|
|
if self:GetParent().isDoT and type(spellID) == 'number' then
|
|
spellID = -spellID
|
|
end
|
|
local findPos = ExRT.F.table_find(graphData,spellID,'info_spellID')
|
|
if findPos then
|
|
graphData[ findPos ].hide = not self:GetChecked()
|
|
end
|
|
BWInterfaceFrame.GraphFrame.G:Reload()
|
|
end
|
|
local function DamageTab_Line_OnClick(self,button)
|
|
if button == "RightButton" then
|
|
DamageTab_RightClick_BackFunction()
|
|
return
|
|
end
|
|
local x,y = ExRT.F.GetCursorPos(self)
|
|
if (self.check and self.check:IsShown() or (self:GetParent().check and self:GetParent().check:IsShown())) and x <= 30 then
|
|
return
|
|
end
|
|
local GUID = self.sourceGUID
|
|
local tooltip = self.spellLink
|
|
|
|
local parent = self:GetParent()
|
|
if parent.isMain then
|
|
GUID = parent.sourceGUID
|
|
tooltip = parent.spellLink
|
|
end
|
|
if parent.isMain and IsShiftKeyDown() and tooltip and tooltip:find("spell:") then
|
|
local spellID = tooltip:match("%d+")
|
|
if spellID then
|
|
ExRT.F.LinkSpell(spellID)
|
|
return
|
|
end
|
|
end
|
|
if GUID then
|
|
if not DamageTab_Variables.state_spells then
|
|
DamageTab_Variables.Back_state = {
|
|
DamageTab_Variables.state_friendly,
|
|
DamageTab_Variables.state_spells,
|
|
DamageTab_Variables.state_spellsTarget,
|
|
DamageTab_Variables.state_byTarget,
|
|
ExRT.F.table_copy2(sourceVar),
|
|
ExRT.F.table_copy2(destVar),
|
|
}
|
|
DamageTab_Variables.state_spells = true
|
|
DamageTab_UpdatePage(true,function()
|
|
if DamageTab_Variables.state_byTarget then
|
|
wipe(destVar)
|
|
destVar[GUID] = true
|
|
else
|
|
wipe(sourceVar)
|
|
sourceVar[GUID] = true
|
|
end
|
|
end,true)
|
|
end
|
|
end
|
|
end
|
|
local function DamageTab_LineOnEnter(self)
|
|
if self.tooltip then
|
|
GameTooltip:SetOwner(self,"ANCHOR_LEFT")
|
|
local firstLine = self.tooltip[1]
|
|
if type(firstLine) == "table" then
|
|
firstLine = (firstLine[2] and "|T"..firstLine[2]..":18|t " or "")..firstLine[1]
|
|
end
|
|
GameTooltip:SetText(firstLine)
|
|
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
|
|
end
|
|
local function DamageTab_Line_OnEnter(self)
|
|
local parent = self:GetParent()
|
|
if parent.spellLink then
|
|
GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
|
|
GameTooltip:SetHyperlink(parent.spellLink)
|
|
GameTooltip:Show()
|
|
elseif parent.name:IsTruncated() then
|
|
GameTooltip:SetOwner(self,"ANCHOR_LEFT")
|
|
GameTooltip:SetText(parent.name:GetText())
|
|
GameTooltip:Show()
|
|
elseif parent.tooltip then
|
|
DamageTab_LineOnEnter(parent)
|
|
end
|
|
end
|
|
function DamageTab_SetLine(dataTable)
|
|
local i,icon,name,overall_num,overall,total,dps,class,sourceGUID,spellLink,tooltip,school,overall_black,showCheck,checkState,spellID,isDoT
|
|
|
|
i = dataTable.line
|
|
icon = dataTable.icon or ""
|
|
name = dataTable.name
|
|
total = dataTable.num
|
|
overall_num = dataTable.num / max(dataTable.total,1)
|
|
overall = dataTable.num / max(dataTable.max,1)
|
|
if dataTable.alpha then
|
|
overall_black = dataTable.alpha / max(dataTable.num,1)
|
|
end
|
|
dps = dataTable.dps
|
|
class = dataTable.class
|
|
sourceGUID = dataTable.sourceGUID
|
|
if dataTable.spellID and dataTable.spellID ~= -1 then
|
|
spellLink = "spell:"..dataTable.spellID
|
|
end
|
|
tooltip = dataTable.tooltip
|
|
school = dataTable.school
|
|
showCheck = dataTable.check
|
|
checkState = dataTable.checkState
|
|
spellID = dataTable.spellID
|
|
isDoT = dataTable.isDoT
|
|
|
|
local line = BWInterfaceFrame.tab.tabs[1].lines[i]
|
|
if not line then
|
|
line = CreateFrame("Button",nil,BWInterfaceFrame.tab.tabs[1].scroll.C)
|
|
BWInterfaceFrame.tab.tabs[1].lines[i] = line
|
|
line:SetSize(815,20)
|
|
line:SetPoint("TOPLEFT",0,-(i-1)*20)
|
|
|
|
line.leftSide = CreateFrame("Frame",nil,line)
|
|
line.leftSide:SetSize(1,20)
|
|
line.leftSide:SetPoint("LEFT",5,0)
|
|
|
|
line.check = ELib:Check(line,""):Point("TOPLEFT",5,-2)
|
|
line.check:SetSize(16,16)
|
|
line.check:SetScript("OnClick",DamageTab_Line_Check_OnClick)
|
|
|
|
line.overall_num = ELib:Text(line,"45.76%",12):Size(70,20):Point(250,0):Right():Color():Shadow()
|
|
|
|
line.icon = ELib:Icon(line,nil,18):Point("TOPLEFT",line.leftSide,0,-1)
|
|
line.name = ELib:Text(line,"name",12):Size(0,20):Point("TOPLEFT",line.leftSide,25,0):Point("TOPRIGHT",line.overall_num,"TOPLEFT",0,0):Color():Shadow()
|
|
line.name:SetMaxLines(1)
|
|
|
|
line.name_tooltip = CreateFrame('Button',nil,line)
|
|
line.name_tooltip:SetAllPoints(line.name)
|
|
line.overall = line:CreateTexture(nil, "BACKGROUND")
|
|
line.overall:SetTexture("Interface\\AddOns\\"..GlobalAddonName.."\\media\\bar24.tga")
|
|
line.overall:SetSize(300,16)
|
|
line.overall:SetPoint("TOPLEFT",325,-2)
|
|
line.overall_black = line:CreateTexture(nil, "BACKGROUND")
|
|
line.overall_black:SetTexture("Interface\\AddOns\\"..GlobalAddonName.."\\media\\bar24b.tga")
|
|
line.overall_black:SetSize(300,16)
|
|
line.overall_black:SetPoint("LEFT",line.overall,"RIGHT",0,0)
|
|
|
|
line.total = ELib:Text(line,"125.46M",12):Size(95,20):Point(630,0):Color():Shadow()
|
|
line.dps = ELib:Text(line,"34576.43",12):Size(100,20):Point(725,0):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.name_tooltip:SetScript("OnClick",DamageTab_Line_OnClick)
|
|
line.name_tooltip:SetScript("OnEnter",DamageTab_Line_OnEnter)
|
|
line.name_tooltip:SetScript("OnLeave",GameTooltip_Hide)
|
|
line:SetScript("OnClick",DamageTab_Line_OnClick)
|
|
line:SetScript("OnEnter",DamageTab_LineOnEnter)
|
|
line:SetScript("OnLeave",GameTooltip_Hide)
|
|
line:RegisterForClicks("AnyUp")
|
|
|
|
line.isMain = true
|
|
end
|
|
if type(icon) == "table" then
|
|
line.icon.texture:SetTexture(icon[1] or "Interface\\Icons\\INV_MISC_QUESTIONMARK")
|
|
line.icon.texture:SetTexCoord(unpack(icon,2,5))
|
|
else
|
|
line.icon.texture:SetTexture(icon or "Interface\\Icons\\INV_MISC_QUESTIONMARK")
|
|
line.icon.texture:SetTexCoord(0,1,0,1)
|
|
end
|
|
line.name:SetText(name or "")
|
|
line.overall_num:SetFormattedText("%.2f%%",overall_num and overall_num * 100 or 0)
|
|
if overall_black and overall_black > 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 <Show>: 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
|
|
if MenuUtil then
|
|
MenuUtil.CreateContextMenu(aurasTab.linesRightClickMenuDropDown, function(ownerRegion, rootDescription)
|
|
for i=1,#aurasTab.linesRightClickMenu do
|
|
if aurasTab.linesRightClickMenu[i].isTitle then
|
|
rootDescription:CreateTitle(aurasTab.linesRightClickMenu[i].text)
|
|
else
|
|
rootDescription:CreateButton(aurasTab.linesRightClickMenu[i].text, aurasTab.linesRightClickMenu[i].func)
|
|
end
|
|
end
|
|
end)
|
|
else
|
|
EasyMenu(aurasTab.linesRightClickMenu, aurasTab.linesRightClickMenuDropDown, "cursor", 10 , -15, "MENU")
|
|
end
|
|
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]<b[2] else return a[3] end end)
|
|
sort(destTable,function(a,b) if a[3]==b[3] then return a[2]<b[2] else return a[3] end end)
|
|
|
|
wipe(aurasTab.sourceDropDown.List)
|
|
wipe(aurasTab.targetDropDown.List)
|
|
aurasTab.sourceDropDown.List[1] = {text = L.BossWatcherAll,func = AurasTab_SelectDropDownSource,padding = 16}
|
|
aurasTab.targetDropDown.List[1] = {text = L.BossWatcherAll,func = AurasTab_SelectDropDownDest,padding = 16}
|
|
for i=1,#sourceTable do
|
|
local isPlayer = ExRT.F.GetUnitTypeByGUID(sourceTable[i][1]) == 0
|
|
local classColor = ""
|
|
if isPlayer then
|
|
classColor = "|c"..ExRT.F.classColorByGUID(sourceTable[i][1])
|
|
end
|
|
aurasTab.sourceDropDown.List[i+1] = {
|
|
text = classColor..sourceTable[i][2]..GUIDtoText(" [%s]",sourceTable[i][1]),
|
|
arg1 = sourceTable[i][1],
|
|
func = AurasTab_SelectDropDownSource,
|
|
checkFunc = AurasTab_CheckDropDownSource,
|
|
checkable = true,
|
|
}
|
|
end
|
|
for i=1,#destTable do
|
|
local isPlayer = ExRT.F.GetUnitTypeByGUID(destTable[i][1]) == 0
|
|
local classColor = ""
|
|
if isPlayer then
|
|
classColor = "|c"..ExRT.F.classColorByGUID(destTable[i][1])
|
|
end
|
|
aurasTab.targetDropDown.List[i+1] = {
|
|
text = classColor..destTable[i][2]..GUIDtoText(" [%s]",destTable[i][1]),
|
|
arg1 = destTable[i][1],
|
|
func = AurasTab_SelectDropDownDest,
|
|
checkFunc = AurasTab_CheckDropDownDest,
|
|
checkable = true,
|
|
}
|
|
end
|
|
|
|
--ExRT.F.ScheduleTimer(collectgarbage, 1, "collect")
|
|
end
|
|
|
|
function UpdateBuffsPage()
|
|
local currTab = aurasTab
|
|
if not currTab.db then
|
|
return
|
|
end
|
|
|
|
local minVal = ExRT.F.Round(currTab.scrollBar:GetValue())
|
|
local buffTable2 = currTab.db
|
|
|
|
local linesCount = 0
|
|
for i=1,AurasTab_Variables.TotalLines do
|
|
for j=1,currTab.lines[i].greenCount do
|
|
currTab.lines[i].green[j]:Hide()
|
|
end
|
|
currTab.lines[i].greenCount = 0
|
|
table.wipe(currTab.lines[i].greenTooltips)
|
|
end
|
|
for i=minVal,#buffTable2 do
|
|
local data = buffTable2[i]
|
|
linesCount = linesCount + 1
|
|
local Line = currTab.lines[linesCount]
|
|
Line.spellIcon:SetTexture(data[3])
|
|
Line.spellText:SetText(data[2] or "???")
|
|
Line.spellLink = GetSpellLink(data[1])
|
|
Line.spellName = data[2] or "Spell"
|
|
Line.spellID = data[1]
|
|
Line.lineData = data
|
|
|
|
for j=1,#data[5] do
|
|
Line.greenCount = Line.greenCount + 1
|
|
local n = Line.greenCount
|
|
|
|
if not Line.green[n] then
|
|
CreateBuffGreen(linesCount,n)
|
|
end
|
|
|
|
Line.green[n]:SetPoint("TOPLEFT",data[5][j][1],0)
|
|
Line.green[n]:SetSize(max(data[5][j][2]-data[5][j][1],0.1),18)
|
|
Line.green[n]:Show()
|
|
|
|
Line.greenTooltips[#Line.greenTooltips+1] = data[5][j]
|
|
end
|
|
|
|
Line:Show()
|
|
if linesCount >= 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]<b[1] end)
|
|
end
|
|
local spellToTime = {}
|
|
|
|
local stats = {
|
|
targets = {},
|
|
sources = {},
|
|
}
|
|
|
|
local isSpellsFilterEnabled = ExRT.F.table_len(SpellsTab_Variables.Filter) > 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,
|
|
}
|
|
}
|
|
if MenuUtil then
|
|
MenuUtil.CreateContextMenu(graphsTab.graphZoomDropDown, function(ownerRegion, rootDescription)
|
|
for i=1,#zoomList do
|
|
local menu = zoomList[i]
|
|
if menu.isTitle then
|
|
rootDescription:CreateTitle(menu.text)
|
|
else
|
|
rootDescription:CreateButton(menu.text, menu.func)
|
|
end
|
|
end
|
|
end)
|
|
else
|
|
EasyMenu(zoomList, graphsTab.graphZoomDropDown, "cursor", 10 , -15, "MENU")
|
|
end
|
|
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]<b[1] end)
|
|
myGraphData.name = name
|
|
if IsShiftKeyDown() and type(currTab.graph.data) == "table" and #currTab.graph.data > 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[1] end)
|
|
|
|
graphData[#graphData + 1] = unitGraphData
|
|
end
|
|
end
|
|
table.sort(graphsTab.graphicsTab.dropDown.List,function(a,b)return a._sort < b._sort end)
|
|
|
|
graphsTab.graph.data = graphData
|
|
graphsTab.graph:Reload()
|
|
end
|
|
local function GraphTab_UpdatePage()
|
|
if not CurrentFight.graphData then
|
|
return
|
|
end
|
|
|
|
local units,powers = {},{}
|
|
for sec,data in pairs(CurrentFight.graphData) do
|
|
for sourceName,dataPower in pairs(data) do
|
|
for powerID,powerVal in pairs(dataPower) do
|
|
powerID = tonumber(powerID)
|
|
if powerID then
|
|
powers[powerID] = true
|
|
units[powerID] = units[powerID] or {}
|
|
if not ExRT.F.table_find(units[powerID],sourceName) then
|
|
units[powerID][#units[powerID]+1] = sourceName
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
wipe(graphsTab.graphicsTab.powerDropDown.List)
|
|
for powerID,powerName in pairs(module.db.energyLocale) do
|
|
if powers[powerID] then
|
|
local info = {}
|
|
graphsTab.graphicsTab.powerDropDown.List[ #graphsTab.graphicsTab.powerDropDown.List + 1 ] = info
|
|
info.text = powerName
|
|
info.arg1 = powerID
|
|
info.func = GraphTab_ReloadPage
|
|
end
|
|
end
|
|
table.sort(graphsTab.graphicsTab.powerDropDown.List,function(a,b)return a.arg1 < b.arg1 end)
|
|
|
|
graphsTab.graphicsTab.powerDropDown.Lines = #graphsTab.graphicsTab.powerDropDown.List
|
|
|
|
GraphsTab_Variables.Cache = units
|
|
GraphsTab_Variables.PowerTypeNow = 0
|
|
GraphTab_ReloadPage()
|
|
end
|
|
|
|
tab.graphicsTab.tabs[2]:SetScript("OnShow",function (self)
|
|
if BWInterfaceFrame.nowFightID == self.lastFightID then
|
|
return
|
|
end
|
|
self.lastFightID = BWInterfaceFrame.nowFightID
|
|
graphsTab.graphicsTab.tabs[3].lastFightID = nil
|
|
|
|
GraphTabLoad()
|
|
graphsTab.graphicsTab.healthDropDown:Show()
|
|
if not CurrentFight.graphData then
|
|
return
|
|
end
|
|
local units = {}
|
|
for i,data in pairs(CurrentFight.graphData) do
|
|
for sourceName,_ in pairs(data) do
|
|
if not ExRT.F.table_find(units,sourceName) then
|
|
units[#units+1] = sourceName
|
|
end
|
|
end
|
|
end
|
|
for i=1,#units do
|
|
local info = {}
|
|
graphsTab.graphicsTab.dropDown.List[i] = info
|
|
local unitGUID = ExRT.F.table_find2(CurrentFight.raidguids,units[i])
|
|
info.text = (unitGUID and "|c"..ExRT.F.classColorByGUID(unitGUID) or "")..units[i]
|
|
info.arg1 = units[i]
|
|
info.func = GraphHealthSelect
|
|
info.justifyH = "CENTER"
|
|
info._sort = GraphTab_SpecialUnits[ units[i] ] or units[i]
|
|
end
|
|
table.sort(graphsTab.graphicsTab.dropDown.List,function(a,b)return a._sort < b._sort end)
|
|
end)
|
|
tab.graphicsTab.tabs[3]:SetScript("OnShow",function (self)
|
|
if BWInterfaceFrame.nowFightID ~= self.lastFightID then
|
|
GraphTabLoad()
|
|
graphsTab.graphicsTab.powerDropDown:Show()
|
|
GraphTab_UpdatePage()
|
|
self.lastFightID = BWInterfaceFrame.nowFightID
|
|
graphsTab.graphicsTab.tabs[2].lastFightID = nil
|
|
end
|
|
end)
|
|
|
|
local function GraphPowerSelectHealthType(_,arg)
|
|
GraphsTab_Variables.HealthTypeNow = arg
|
|
if GraphsTab_Variables.HealthLastName then
|
|
GraphHealthSelect(nil,GraphsTab_Variables.HealthLastName)
|
|
end
|
|
local text
|
|
if arg == 1 then
|
|
text = HEALTH
|
|
else
|
|
text = ACTION_SPELL_MISSED_ABSORB
|
|
end
|
|
graphsTab.graphicsTab.healthDropDown:SetText(L.BossWatcherSelectPower..text)
|
|
ELib:DropDownClose()
|
|
end
|
|
tab.graphicsTab.healthDropDown.List[1] = {text = HEALTH,arg1 = 1,func = GraphPowerSelectHealthType}
|
|
tab.graphicsTab.healthDropDown.List[2] = {text = ACTION_SPELL_MISSED_ABSORB,arg1 = 2,func = GraphPowerSelectHealthType}
|
|
|
|
|
|
tab.sourceList = ELib:ScrollList(tab.graphicsTab.tabs[1]):Size(190,365):Point(14,-45)
|
|
tab.powerTypeList = ELib:ScrollList(tab.graphicsTab.tabs[1]):Size(190,136):Point("TOPLEFT",tab.sourceList,"BOTTOMLEFT",0,-8)
|
|
tab.sourceList.IndexToGUID = {}
|
|
tab.powerTypeList.IndexToGUID = {}
|
|
|
|
local function EnergyLineOnEnter(self)
|
|
if self.spellID then
|
|
GameTooltip:SetOwner(self, "ANCHOR_LEFT")
|
|
GameTooltip:SetHyperlink("spell:"..self.spellID)
|
|
GameTooltip:Show()
|
|
end
|
|
end
|
|
|
|
tab.spells = {}
|
|
for i=1,20 do
|
|
tab.spells[i] = CreateFrame("Frame",nil,tab.graphicsTab.tabs[1])
|
|
tab.spells[i]:SetPoint("TOPLEFT",220,-10-28*(i-1))
|
|
tab.spells[i]:SetSize(420,28)
|
|
|
|
tab.spells[i].texture = tab.spells[i]:CreateTexture(nil,"BACKGROUND")
|
|
tab.spells[i].texture:SetSize(24,24)
|
|
tab.spells[i].texture:SetPoint("TOPLEFT",0,-2)
|
|
|
|
tab.spells[i].spellName = ELib:Text(tab.spells[i],"",13):Size(225,28):Point(26,0):Color():Shadow()
|
|
tab.spells[i].amount = ELib:Text(tab.spells[i],"",12):Size(90,28):Point(250,0):Color():Shadow()
|
|
tab.spells[i].count = ELib:Text(tab.spells[i],"",12):Size(80,28):Point(340,0):Color():Shadow()
|
|
|
|
tab.spells[i]:SetScript("OnEnter",EnergyLineOnEnter)
|
|
tab.spells[i]:SetScript("OnLeave",GameTooltip_Hide)
|
|
end
|
|
|
|
local function EnergyClearLines()
|
|
for i=1,#graphsTab.spells do
|
|
graphsTab.spells[i]:Hide()
|
|
end
|
|
end
|
|
|
|
function tab.sourceList:SetListValue(index)
|
|
local tab6 = graphsTab
|
|
table.wipe(tab6.powerTypeList.L)
|
|
table.wipe(tab6.powerTypeList.IndexToGUID)
|
|
|
|
local sourceGUID = tab6.sourceList.IndexToGUID[index]
|
|
tab6.sourceGUID = sourceGUID
|
|
local powerList = {}
|
|
for powerType,powerData in pairs(CurrentFight.power[sourceGUID]) do
|
|
powerList[#powerList + 1] = {powerType,module.db.energyLocale[ powerType ] or L.BossWatcherEnergyTypeUnknown..powerType}
|
|
end
|
|
table.sort(powerList,function (a,b) return a[1] < b[1] end)
|
|
for i,powerData in ipairs(powerList) do
|
|
tab6.powerTypeList.L[i] = powerData[2]
|
|
tab6.powerTypeList.IndexToGUID[i] = powerData[1]
|
|
end
|
|
|
|
--EnergyClearLines()
|
|
tab6.powerTypeList.selected = 1
|
|
tab6.powerTypeList:Update()
|
|
tab6.powerTypeList:SetListValue(1)
|
|
end
|
|
function tab.powerTypeList:SetListValue(index)
|
|
local powerType = graphsTab.powerTypeList.IndexToGUID[index]
|
|
local sourceGUID = graphsTab.sourceGUID
|
|
|
|
local spellList = {
|
|
{nil,L.BossWatcherReportTotal,"",0,0},
|
|
}
|
|
for spellID,spellData in pairs(CurrentFight.power[sourceGUID][powerType]) do
|
|
local spellName,_,spellTexture = GetSpellInfo(spellID)
|
|
spellList[#spellList + 1] = {spellID,spellName,spellTexture,spellData[1],spellData[2]}
|
|
spellList[1][4] = spellList[1][4] + spellData[1]
|
|
spellList[1][5] = spellList[1][5] + spellData[2]
|
|
end
|
|
table.sort(spellList,function (a,b) return a[4] > 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[4] else return a[3]<b[3] end end)
|
|
for i=1,#resultData do
|
|
currTab.events.L[#currTab.events.L + 1] = resultData[i][1]
|
|
currTab.events.DATA[#currTab.events.L] = resultData[i][2]
|
|
end
|
|
end
|
|
currTab.events:Update()
|
|
end
|
|
function tab.events:SetListValue(index)
|
|
self.selected = nil
|
|
self:Update()
|
|
end
|
|
function tab.events:HoverListValue(isHover,index,hoveredObj)
|
|
if not isHover then
|
|
GameTooltip_Hide()
|
|
ELib.Tooltip:HideAdd()
|
|
BWInterfaceFrame.timeLineFrame.timeLine.arrow:Hide()
|
|
else
|
|
local this = hoveredObj
|
|
local line = interruptTab.events.DATA[index]
|
|
|
|
GameTooltip:SetOwner(this or self,"ANCHOR_BOTTOMLEFT")
|
|
GameTooltip:SetHyperlink("spell:"..line[4])
|
|
GameTooltip:Show()
|
|
|
|
if line[3] then
|
|
ELib.Tooltip:Add("spell:"..line[3])
|
|
end
|
|
|
|
if this.text:IsTruncated() then
|
|
ELib.Tooltip:Add(nil,{this.text:GetText()},false,true)
|
|
end
|
|
|
|
local _time = timestampToFightTime(line[5]) / GetFightLength(true)
|
|
|
|
BWInterfaceFrame.timeLineFrame.timeLine.arrow:SetPoint("TOPLEFT",BWInterfaceFrame.timeLineFrame.timeLine,"TOPLEFT",BWInterfaceFrame.timeLineFrame.width*_time,0)
|
|
BWInterfaceFrame.timeLineFrame.timeLine.arrow:Show()
|
|
end
|
|
end
|
|
|
|
function UpdateInterruptPage()
|
|
local currTab = interruptTab
|
|
table.wipe(currTab.list.L)
|
|
table.wipe(currTab.list.GUIDs)
|
|
table.wipe(currTab.events.L)
|
|
|
|
local workTable = CurrentFight.interrupts
|
|
if currTab.tabs.selected == 2 then
|
|
workTable = CurrentFight.dispels
|
|
elseif currTab.tabs.selected == 3 then
|
|
workTable = CurrentFight.aurabroken
|
|
end
|
|
local data = {}
|
|
for i,line in ipairs(workTable) do
|
|
if Intterupt_Type == 1 then
|
|
if not ExRT.F.table_find(data,line[1]) and CurrentFight.segments[ line.s ].e then
|
|
data[#data + 1] = line[1]
|
|
end
|
|
elseif Intterupt_Type == 2 then
|
|
if not ExRT.F.table_find(data,line[2]) and CurrentFight.segments[ line.s ].e then
|
|
data[#data + 1] = line[2]
|
|
end
|
|
else
|
|
if not ExRT.F.table_find(data,line[4]) and CurrentFight.segments[ line.s ].e then
|
|
data[#data + 1] = line[4]
|
|
end
|
|
end
|
|
end
|
|
local data2 = {}
|
|
for i=1,#data do
|
|
if Intterupt_Type ~= 3 then
|
|
data2[i] = {data[i],GetGUID(data[i]),"|c"..ExRT.F.classColorByGUID(data[i])}
|
|
else
|
|
local spellName = GetSpellInfo(data[i])
|
|
data2[i] = {data[i],spellName}
|
|
end
|
|
end
|
|
sort(data2,function(a,b)return a[2]<b[2] end)
|
|
for i=1,#data2 do
|
|
currTab.list.L[i+2] = (data2[i][3] or "")..data2[i][2]
|
|
currTab.list.GUIDs[i+2] = data2[i][1]
|
|
end
|
|
currTab.list.L[1] = L.BossWatcherAll
|
|
currTab.list.L[2] = L.BossWatcherSpellsCount
|
|
|
|
currTab.list.selected = 1
|
|
|
|
currTab.list:Update()
|
|
currTab.list:SetListValue(1)
|
|
--currTab.events:Update()
|
|
end
|
|
|
|
tab:SetScript("OnShow",function (self)
|
|
BWInterfaceFrame.timeLineFrame:ClearAllPoints()
|
|
BWInterfaceFrame.timeLineFrame:SetPoint("TOP",self,"TOP",0,-10)
|
|
BWInterfaceFrame.timeLineFrame:Show()
|
|
if BWInterfaceFrame.nowFightID ~= self.lastFightID then
|
|
UpdateInterruptPage()
|
|
self.lastFightID = BWInterfaceFrame.nowFightID
|
|
end
|
|
end)
|
|
tab:SetScript("OnHide",function (self)
|
|
BWInterfaceFrame.timeLineFrame:Hide()
|
|
end)
|
|
|
|
|
|
|
|
|
|
---- Heal
|
|
tab = BWInterfaceFrame.tab.tabs[2]
|
|
|
|
local HsourceVar,HdestVar = {},{}
|
|
local HealingTab_SetLine = nil
|
|
local HealingTab_Variables = {
|
|
Last_Func = nil,
|
|
Last_doEnemy = nil,
|
|
Last_doReduction = nil,
|
|
ShowOverheal = false,
|
|
Back_Func = nil,
|
|
Back_destVar = nil,
|
|
Back_sourceVar = nil,
|
|
NULLSpellAmount = {
|
|
amount = 0,
|
|
over = 0,
|
|
absorbed = 0,
|
|
count = 0,
|
|
crit = 0,
|
|
critcount = 0,
|
|
critmax = 0,
|
|
critover = 0,
|
|
hitmax = 0,
|
|
absorbs = 0,
|
|
},
|
|
|
|
state_friendly = true,
|
|
state_spells = false,
|
|
state_mitigation = false,
|
|
state_byTarget = false,
|
|
}
|
|
local HealingTab_UpdatePage
|
|
|
|
local function HealingTab_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 = ExRT.F.GetUnitInfoByUnitFlag(flags,1) == 1024
|
|
local isNPC = 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 HealingTab_UpdateDropDowns()
|
|
HealingTab_UpdateDropDown(HsourceVar,BWInterfaceFrame.tab.tabs[2].sourceDropDown)
|
|
HealingTab_UpdateDropDown(HdestVar,BWInterfaceFrame.tab.tabs[2].targetDropDown)
|
|
end
|
|
local function HealingTab_UpdateChecks()
|
|
local tab = BWInterfaceFrame.tab.tabs[2]
|
|
for _,c in pairs({tab.chkFriendly,tab.chkEnemy,tab.chkMitigation,tab.bySource,tab.byTarget,tab.bySpell}) do
|
|
c:SetChecked(false)
|
|
end
|
|
if HealingTab_Variables.state_mitigation then
|
|
tab.chkMitigation:SetChecked(true)
|
|
elseif HealingTab_Variables.state_friendly then
|
|
tab.chkFriendly:SetChecked(true)
|
|
else
|
|
tab.chkEnemy:SetChecked(true)
|
|
end
|
|
if HealingTab_Variables.state_spells then
|
|
tab.bySpell:SetChecked(true)
|
|
end
|
|
if HealingTab_Variables.state_byTarget then
|
|
tab.byTarget:SetChecked(true)
|
|
elseif not HealingTab_Variables.state_spells then
|
|
tab.bySource:SetChecked(true)
|
|
end
|
|
end
|
|
|
|
local function HealingTab_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_healing = 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 and 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_healing = 0,
|
|
hide = true,
|
|
r = r,
|
|
g = g,
|
|
b = b,
|
|
}
|
|
end
|
|
graphData[#graphData+1] = newData
|
|
local totalHealing = 0
|
|
for i=1,fightLength do
|
|
newData[i] = {i,spellData[i] or 0}
|
|
totalHealing = totalHealing + (spellData[i] or 0)
|
|
end
|
|
newData.total_healing = totalHealing
|
|
end
|
|
sort(graphData,function(a,b) return a.total_healing>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]<b[2] end)
|
|
sort(destTable,function(a,b) return a[2]<b[2] end)
|
|
wipe(BWInterfaceFrame.tab.tabs[2].sourceDropDown.List)
|
|
wipe(BWInterfaceFrame.tab.tabs[2].targetDropDown.List)
|
|
BWInterfaceFrame.tab.tabs[2].sourceDropDown.List[1] = {text = L.BossWatcherAll,func = HealingTab_SelectDropDownSource,padding = 16}
|
|
BWInterfaceFrame.tab.tabs[2].targetDropDown.List[1] = {text = L.BossWatcherAll,func = HealingTab_SelectDropDownDest,padding = 16}
|
|
for i=1,#sourceTable do
|
|
local isPlayer = ExRT.F.GetUnitTypeByGUID(sourceTable[i][1]) == 0
|
|
local classColor = ""
|
|
if isPlayer then
|
|
classColor = "|c"..ExRT.F.classColorByGUID(sourceTable[i][1])
|
|
end
|
|
BWInterfaceFrame.tab.tabs[2].sourceDropDown.List[i+1] = {
|
|
text = classColor..sourceTable[i][2]..GUIDtoText(" [%s]",sourceTable[i][1]),
|
|
arg1 = sourceTable[i][1],
|
|
func = HealingTab_SelectDropDownSource,
|
|
checkFunc = HealingTab_CheckDropDownSource,
|
|
checkable = true,
|
|
}
|
|
end
|
|
for i=1,#destTable do
|
|
local isPlayer = ExRT.F.GetUnitTypeByGUID(destTable[i][1]) == 0
|
|
local classColor = ""
|
|
if isPlayer then
|
|
classColor = "|c"..ExRT.F.classColorByGUID(destTable[i][1])
|
|
end
|
|
BWInterfaceFrame.tab.tabs[2].targetDropDown.List[i+1] = {
|
|
text = classColor..destTable[i][2]..GUIDtoText(" [%s]",destTable[i][1]),
|
|
arg1 = destTable[i][1],
|
|
func = HealingTab_SelectDropDownDest,
|
|
checkFunc = HealingTab_CheckDropDownDest,
|
|
checkable = true,
|
|
}
|
|
end
|
|
tinsert(BWInterfaceFrame.tab.tabs[2].targetDropDown.List,2,{
|
|
text = L.BossWatcherHealToTanks,
|
|
padding = 16,
|
|
func = HealingTab_SelectDropDown_OptionTanks,
|
|
arg1 = destTable,
|
|
arg2 = true,
|
|
})
|
|
tinsert(BWInterfaceFrame.tab.tabs[2].targetDropDown.List,2,{
|
|
text = L.BossWatcherHealToNonTanks,
|
|
padding = 16,
|
|
func = HealingTab_SelectDropDown_OptionTanks,
|
|
arg1 = destTable,
|
|
arg2 = false,
|
|
})
|
|
if not disableUpdateVars then
|
|
wipe(HsourceVar)
|
|
wipe(HdestVar)
|
|
end
|
|
end
|
|
|
|
local function HealingTab_RPS(disableUpdateVars)
|
|
local sourceTable = {}
|
|
local destTable = {}
|
|
for destGUID,destData in pairs(CurrentFight.reduction) do
|
|
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
|
|
local isFriendly = ExRT.F.UnitIsFriendlyByUnitFlag2(CurrentFight.reaction[reductorGUID])
|
|
if isFriendly then
|
|
for spellID,spellSegments in pairs(reductorData) 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,reductorGUID,1) then
|
|
sourceTable[#sourceTable + 1] = {reductorGUID,GetGUID(reductorGUID)}
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
sort(sourceTable,function(a,b) return a[2]<b[2] end)
|
|
sort(destTable,function(a,b) return a[2]<b[2] end)
|
|
wipe(BWInterfaceFrame.tab.tabs[2].sourceDropDown.List)
|
|
wipe(BWInterfaceFrame.tab.tabs[2].targetDropDown.List)
|
|
BWInterfaceFrame.tab.tabs[2].sourceDropDown.List[1] = {text = L.BossWatcherAll,func = HealingTab_SelectDropDownSource,padding = 16}
|
|
BWInterfaceFrame.tab.tabs[2].targetDropDown.List[1] = {text = L.BossWatcherAll,func = HealingTab_SelectDropDownDest,padding = 16}
|
|
for i=1,#sourceTable do
|
|
local isPlayer = ExRT.F.GetUnitTypeByGUID(sourceTable[i][1]) == 0
|
|
local classColor = ""
|
|
if isPlayer then
|
|
classColor = "|c"..ExRT.F.classColorByGUID(sourceTable[i][1])
|
|
end
|
|
BWInterfaceFrame.tab.tabs[2].sourceDropDown.List[i+1] = {
|
|
text = classColor..sourceTable[i][2]..GUIDtoText(" [%s]",sourceTable[i][1]),
|
|
arg1 = sourceTable[i][1],
|
|
func = HealingTab_SelectDropDownSource,
|
|
checkFunc = HealingTab_CheckDropDownSource,
|
|
checkable = true,
|
|
}
|
|
end
|
|
for i=1,#destTable do
|
|
local isPlayer = ExRT.F.GetUnitTypeByGUID(destTable[i][1]) == 0
|
|
local classColor = ""
|
|
if isPlayer then
|
|
classColor = "|c"..ExRT.F.classColorByGUID(destTable[i][1])
|
|
end
|
|
BWInterfaceFrame.tab.tabs[2].targetDropDown.List[i+1] = {
|
|
text = classColor..destTable[i][2]..GUIDtoText(" [%s]",destTable[i][1]),
|
|
arg1 = destTable[i][1],
|
|
func = HealingTab_SelectDropDownDest,
|
|
checkFunc = HealingTab_CheckDropDownDest,
|
|
checkable = true,
|
|
}
|
|
end
|
|
if not disableUpdateVars then
|
|
wipe(HsourceVar)
|
|
wipe(HdestVar)
|
|
end
|
|
end
|
|
|
|
function HealingTab_UpdatePage(updateLists,midFunc,disableUpdateVars)
|
|
if HealingTab_Variables.state_mitigation then
|
|
BWInterfaceFrame.tab.tabs[2].showOverhealChk:Tooltip(""):SetText("|cffffffff"..STAT_DODGE.." / "..STAT_PARRY.." / "..ACTION_SPELL_MISSED_IMMUNE)
|
|
|
|
else
|
|
BWInterfaceFrame.tab.tabs[2].showOverhealChk:Tooltip(L.BossWatcherHealShowOver):SetText("|cffffffff"..L.BossWatcherOverhealing)
|
|
end
|
|
if updateLists then
|
|
if not HealingTab_Variables.state_mitigation then
|
|
HealingTab_HPS(disableUpdateVars)
|
|
else
|
|
HealingTab_RPS(disableUpdateVars)
|
|
end
|
|
end
|
|
if midFunc then
|
|
midFunc()
|
|
end
|
|
if IsAltKeyDown() then
|
|
HealingTab_UpdateLinesFromSpells()
|
|
elseif not HealingTab_Variables.state_spells then
|
|
HealingTab_UpdateLinesPlayers()
|
|
else
|
|
HealingTab_UpdateLinesSpell()
|
|
end
|
|
end
|
|
|
|
|
|
tab.chkFriendly = ELib:Radio(tab,L.BossWatcherFriendly,true):Point(15,-50):AddButton():OnClick(function(self)
|
|
HealingTab_Variables.state_friendly = true
|
|
HealingTab_Variables.state_mitigation = false
|
|
BWInterfaceFrame.tab.tabs[2].chkEnemy:SetChecked(false)
|
|
BWInterfaceFrame.tab.tabs[2].chkMitigation:SetChecked(false)
|
|
self:SetChecked(true)
|
|
|
|
HealingTab_UpdatePage(true)
|
|
end)
|
|
tab.chkEnemy = ELib:Radio(tab,L.BossWatcherHostile):Point(15,-65):AddButton():OnClick(function(self)
|
|
HealingTab_Variables.state_friendly = false
|
|
HealingTab_Variables.state_mitigation = false
|
|
BWInterfaceFrame.tab.tabs[2].chkFriendly:SetChecked(false)
|
|
BWInterfaceFrame.tab.tabs[2].chkMitigation:SetChecked(false)
|
|
self:SetChecked(true)
|
|
|
|
HealingTab_UpdatePage(true)
|
|
end)
|
|
tab.chkMitigation = ELib:Radio(tab,L.BossWatcherHealReduction):Point(15,-80):AddButton():OnClick(function(self)
|
|
HealingTab_Variables.state_friendly = true
|
|
HealingTab_Variables.state_mitigation = true
|
|
BWInterfaceFrame.tab.tabs[2].chkFriendly:SetChecked(false)
|
|
BWInterfaceFrame.tab.tabs[2].chkEnemy:SetChecked(false)
|
|
self:SetChecked(true)
|
|
|
|
HealingTab_UpdatePage(true)
|
|
end)
|
|
|
|
tab.bySource = ELib:Radio(tab,L.BossWatcherBySource,true):Point(200,-50):AddButton():OnClick(function(self)
|
|
HealingTab_Variables.state_byTarget = false
|
|
HealingTab_Variables.state_spells = false
|
|
BWInterfaceFrame.tab.tabs[2].byTarget:SetChecked(false)
|
|
BWInterfaceFrame.tab.tabs[2].bySpell:SetChecked(false)
|
|
self:SetChecked(true)
|
|
|
|
HealingTab_UpdatePage(true)
|
|
end)
|
|
tab.byTarget = ELib:Radio(tab,L.BossWatcherByTarget):Point(200,-65):AddButton():OnClick(function(self)
|
|
HealingTab_Variables.state_byTarget = true
|
|
HealingTab_Variables.state_spells = false
|
|
BWInterfaceFrame.tab.tabs[2].bySource:SetChecked(false)
|
|
BWInterfaceFrame.tab.tabs[2].bySpell:SetChecked(false)
|
|
self:SetChecked(true)
|
|
|
|
HealingTab_UpdatePage(true)
|
|
end)
|
|
tab.bySpell = ELib:Radio(tab,L.BossWatcherBySpell):Point(200,-80):AddButton():OnClick(function(self)
|
|
HealingTab_Variables.state_byTarget = false
|
|
HealingTab_Variables.state_spells = true
|
|
BWInterfaceFrame.tab.tabs[2].bySource:SetChecked(false)
|
|
BWInterfaceFrame.tab.tabs[2].byTarget:SetChecked(false)
|
|
self:SetChecked(true)
|
|
|
|
HealingTab_UpdatePage(true)
|
|
end)
|
|
|
|
tab.sourceDropDown = ELib:DropDown(tab,250,20):Size(195):Point(430,-50):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(195):Point(430,-75):SetText(L.BossWatcherAll)
|
|
tab.targetText = ELib:Text(tab,L.BossWatcherTarget..":",12):Size(100,20):Point("TOPRIGHT",tab.targetDropDown,"TOPLEFT",-6,0):Right():Color():Shadow()
|
|
|
|
function tab.sourceDropDown.additionalToggle(self)
|
|
for i=2,#self.List do
|
|
self.List[i].checkState = HsourceVar[self.List[i].arg1]
|
|
end
|
|
end
|
|
function tab.targetDropDown.additionalToggle(self)
|
|
for i=2,#self.List do
|
|
self.List[i].checkState = HdestVar[self.List[i].arg1]
|
|
end
|
|
end
|
|
|
|
tab.showOverhealChk = ELib:Check(tab,"|cffffffff"..L.BossWatcherOverhealing):Point(650,-50):Tooltip(L.BossWatcherHealShowOver):OnClick(function (self)
|
|
if self:GetChecked() then
|
|
HealingTab_Variables.ShowOverheal = true
|
|
else
|
|
HealingTab_Variables.ShowOverheal = false
|
|
end
|
|
HealingTab_UpdatePage()
|
|
end)
|
|
tab.showOverhealChk.text:SetPoint("RIGHT",BWInterfaceFrame,-5,0)
|
|
tab.showOverhealChk.text:SetJustifyH("LEFT")
|
|
|
|
tab.showGraphChk = ELib:Check(tab,"|cffffffff"..L.BossWatcherTabGraphics.." ",VMRT.BossWatcher.optionsHealingGraph):Point(650,-75):OnClick(function (self)
|
|
local tab2 = BWInterfaceFrame.tab.tabs[2]
|
|
if self:GetChecked() then
|
|
tab2.scroll:Point("TOP",0,-305)
|
|
BWInterfaceFrame.GraphFrame:Show()
|
|
else
|
|
tab2.scroll:Point("TOP",0,-100)
|
|
BWInterfaceFrame.GraphFrame:Hide()
|
|
end
|
|
VMRT.BossWatcher.optionsHealingGraph = self:GetChecked()
|
|
if HealingTab_Variables.graphCache then
|
|
HealingTab_ReloadGraph(unpack(HealingTab_Variables.graphCache))
|
|
end
|
|
end)
|
|
|
|
tab.scroll = ELib:ScrollFrame(tab):Point("TOP",0,VMRT.BossWatcher.optionsHealingGraph and -305 or -100):Point("BOTTOM",0,10):Height(600)
|
|
tab.scroll:SetWidth(835)
|
|
tab.scroll.C:SetWidth(835)
|
|
tab.lines = {}
|
|
|
|
local function HealingTab_RightClick_BackFunction()
|
|
if not HealingTab_Variables.Back_state then
|
|
HealingTab_UpdatePage(true)
|
|
return
|
|
end
|
|
HealingTab_Variables.state_friendly = HealingTab_Variables.Back_state[1]
|
|
HealingTab_Variables.state_spells = HealingTab_Variables.Back_state[2]
|
|
HealingTab_Variables.state_mitigation = HealingTab_Variables.Back_state[3]
|
|
HealingTab_Variables.state_byTarget = HealingTab_Variables.Back_state[4]
|
|
|
|
HealingTab_UpdatePage(true,function()
|
|
HsourceVar = HealingTab_Variables.Back_state[5]
|
|
HdestVar = HealingTab_Variables.Back_state[6]
|
|
end)
|
|
HealingTab_Variables.Back_state = nil
|
|
end
|
|
|
|
tab.scroll:SetScript("OnMouseUp",function(self,button)
|
|
if button == "RightButton" then
|
|
HealingTab_RightClick_BackFunction()
|
|
end
|
|
end)
|
|
|
|
local function HealingTab_Line_Check_OnClick(self)
|
|
local spellID = self:GetParent().spellID or self:GetParent().sourceGUID
|
|
if not spellID then
|
|
return
|
|
end
|
|
if self:GetParent().isDoT and type(spellID) == 'number' then
|
|
spellID = -spellID
|
|
end
|
|
local graphData = BWInterfaceFrame.GraphFrame.G.data
|
|
local findPos = ExRT.F.table_find(graphData,spellID,'info_spellID')
|
|
if findPos then
|
|
graphData[ findPos ].hide = not self:GetChecked()
|
|
end
|
|
BWInterfaceFrame.GraphFrame.G:Reload()
|
|
end
|
|
local function HealingTab_Line_OnClick(self,button)
|
|
if button == "RightButton" then
|
|
HealingTab_RightClick_BackFunction()
|
|
return
|
|
end
|
|
local x,y = ExRT.F.GetCursorPos(self)
|
|
if (self.check and self.check:IsShown() or (self:GetParent().check and self:GetParent().check:IsShown())) and x <= 30 then
|
|
return
|
|
end
|
|
local GUID = self.sourceGUID
|
|
local tooltip = self.spellLink
|
|
|
|
local parent = self:GetParent()
|
|
if parent.isMain then
|
|
GUID = parent.sourceGUID
|
|
tooltip = parent.spellLink
|
|
end
|
|
if parent.isMain and IsShiftKeyDown() and tooltip and tooltip:find("spell:") then
|
|
local spellID = tooltip:match("%d+")
|
|
if spellID then
|
|
ExRT.F.LinkSpell(spellID)
|
|
return
|
|
end
|
|
end
|
|
if GUID then
|
|
if not HealingTab_Variables.state_spells then
|
|
HealingTab_Variables.Back_state = {
|
|
HealingTab_Variables.state_friendly,
|
|
HealingTab_Variables.state_spells,
|
|
HealingTab_Variables.state_mitigation,
|
|
HealingTab_Variables.state_byTarget,
|
|
ExRT.F.table_copy2(HsourceVar),
|
|
ExRT.F.table_copy2(HdestVar),
|
|
}
|
|
HealingTab_Variables.state_spells = true
|
|
HealingTab_UpdatePage(true,function()
|
|
if HealingTab_Variables.state_byTarget then
|
|
wipe(HdestVar)
|
|
HdestVar[GUID] = true
|
|
else
|
|
wipe(HsourceVar)
|
|
HsourceVar[GUID] = true
|
|
end
|
|
end,true)
|
|
end
|
|
end
|
|
end
|
|
local function HealingTab_LineOnEnter(self)
|
|
if self.tooltip then
|
|
GameTooltip:SetOwner(self,"ANCHOR_LEFT")
|
|
local firstLine = self.tooltip[1]
|
|
if type(firstLine) == "table" then
|
|
firstLine = (firstLine[2] and "|T"..firstLine[2]..":18|t " or "")..firstLine[1]
|
|
end
|
|
GameTooltip:SetText(firstLine)
|
|
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
|
|
end
|
|
local function HealingTab_Line_OnEnter(self)
|
|
local parent = self:GetParent()
|
|
if parent.spellLink then
|
|
GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
|
|
GameTooltip:SetHyperlink(parent.spellLink)
|
|
GameTooltip:Show()
|
|
elseif parent.name:IsTruncated() then
|
|
GameTooltip:SetOwner(self,"ANCHOR_LEFT")
|
|
GameTooltip:SetText(parent.name:GetText())
|
|
GameTooltip:Show()
|
|
elseif parent.tooltip then
|
|
HealingTab_LineOnEnter(parent)
|
|
end
|
|
end
|
|
function HealingTab_SetLine(dataTable)
|
|
local i,icon,name,overall_num,overall,total,dps,class,sourceGUID,doEnemy,spellLink,tooltip,school,overall_black,isDoT
|
|
local showCheck,checkState,spellID
|
|
|
|
i = dataTable.line
|
|
icon = dataTable.icon or ""
|
|
name = dataTable.name
|
|
total = dataTable.num
|
|
overall_num = dataTable.num / max(dataTable.total,1)
|
|
overall = dataTable.num / max(dataTable.max,1)
|
|
if dataTable.alpha then
|
|
overall_black = dataTable.alpha / max(dataTable.num,1)
|
|
end
|
|
dps = dataTable.dps
|
|
class = dataTable.class
|
|
sourceGUID = dataTable.sourceGUID
|
|
doEnemy = dataTable.doEnemy
|
|
if dataTable.spellID and dataTable.spellID ~= -1 then
|
|
spellLink = "spell:"..dataTable.spellID
|
|
end
|
|
tooltip = dataTable.tooltip
|
|
school = dataTable.school
|
|
isDoT = dataTable.isDoT
|
|
showCheck = dataTable.check
|
|
checkState = dataTable.checkState
|
|
spellID = dataTable.spellID
|
|
|
|
if not BWInterfaceFrame.tab.tabs[2].lines[i] then
|
|
local line = CreateFrame("Button",nil,BWInterfaceFrame.tab.tabs[2].scroll.C)
|
|
BWInterfaceFrame.tab.tabs[2].lines[i] = line
|
|
line:SetSize(815,20)
|
|
line:SetPoint("TOPLEFT",0,-(i-1)*20)
|
|
|
|
line.leftSide = CreateFrame("Frame",nil,line)
|
|
line.leftSide:SetSize(1,20)
|
|
line.leftSide:SetPoint("LEFT",5,0)
|
|
|
|
line.check = ELib:Check(line,""):Point("TOPLEFT",5,-2)
|
|
line.check:SetSize(16,16)
|
|
line.check:SetScript("OnClick",HealingTab_Line_Check_OnClick)
|
|
|
|
line.overall_num = ELib:Text(line,"45.76%",12):Size(70,20):Point(250,0):Right():Color():Shadow()
|
|
|
|
line.icon = ELib:Icon(line,nil,18):Point("TOPLEFT",line.leftSide,0,-1)
|
|
line.name = ELib:Text(line,"name",12):Size(0,20):Point("TOPLEFT",line.leftSide,25,0):Point("TOPRIGHT",line.overall_num,"TOPLEFT",0,0):Color():Shadow()
|
|
line.name:SetMaxLines(1)
|
|
|
|
line.name_tooltip = CreateFrame('Button',nil,line)
|
|
line.name_tooltip:SetAllPoints(line.name)
|
|
line.overall = line:CreateTexture(nil, "BACKGROUND")
|
|
--line.overall:SetColorTexture(0.7, 0.1, 0.1, 1)
|
|
line.overall:SetTexture("Interface\\AddOns\\"..GlobalAddonName.."\\media\\bar24.tga")
|
|
line.overall:SetSize(300,16)
|
|
line.overall:SetPoint("TOPLEFT",325,-2)
|
|
line.overall_black = line:CreateTexture(nil, "BACKGROUND")
|
|
line.overall_black:SetTexture("Interface\\AddOns\\"..GlobalAddonName.."\\media\\bar24b.tga")
|
|
line.overall_black:SetSize(300,16)
|
|
line.overall_black:SetPoint("LEFT",line.overall,"RIGHT",0,0)
|
|
|
|
line.total = ELib:Text(line,"125.46M",12):Size(95,20):Point(630,0):Color():Shadow()
|
|
line.dps = ELib:Text(line,"34576.43",12):Size(100,20):Point(725,0):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.name_tooltip:SetScript("OnClick",HealingTab_Line_OnClick)
|
|
line.name_tooltip:SetScript("OnEnter",HealingTab_Line_OnEnter)
|
|
line.name_tooltip:SetScript("OnLeave",GameTooltip_Hide)
|
|
line:SetScript("OnClick",HealingTab_Line_OnClick)
|
|
line:SetScript("OnEnter",HealingTab_LineOnEnter)
|
|
line:SetScript("OnLeave",GameTooltip_Hide)
|
|
line:RegisterForClicks("AnyUp")
|
|
|
|
line.isMain = true
|
|
end
|
|
local line = BWInterfaceFrame.tab.tabs[2].lines[i]
|
|
if type(icon) == "table" then
|
|
line.icon.texture:SetTexture(icon[1] or "Interface\\Icons\\INV_MISC_QUESTIONMARK")
|
|
line.icon.texture:SetTexCoord(unpack(icon,2,5))
|
|
else
|
|
line.icon.texture:SetTexture(icon or "Interface\\Icons\\INV_MISC_QUESTIONMARK")
|
|
line.icon.texture:SetTexCoord(0,1,0,1)
|
|
end
|
|
line.name:SetText(name or "")
|
|
line.overall_num:SetFormattedText("%.2f%%",overall_num and overall_num * 100 or 0)
|
|
if overall_black and overall_black > 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.P<b.P else return a[3]>b[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]<b[1] end)
|
|
--tinsert(raidFrames,1,{"target",.7,.7,.7})
|
|
if graphData and graphData[1] then
|
|
for i=1,#raidFrames do
|
|
tab.raidFrames[i].Unit = raidFrames[i][1]
|
|
tab.raidFrames[i].text:SetTextColor(raidFrames[i][2],raidFrames[i][3],raidFrames[i][4],1)
|
|
tab.raidFrames[i].text:SetText(raidFrames[i][1])
|
|
tab.raidFrames[i]:Update(1)
|
|
|
|
--- debuffs list
|
|
local debuffsData = {}
|
|
tab.raidFrames[i].debuffsData = debuffsData
|
|
local guid = nil
|
|
for guidNow,nameNow in pairs(CurrentFight.raidguids) do
|
|
if nameNow == raidFrames[i][1] then
|
|
guid = guidNow
|
|
break
|
|
end
|
|
end
|
|
if guid then
|
|
local current = {}
|
|
local aurasData = CurrentFight.auras
|
|
for k=1,#aurasData do
|
|
local aurasLine = aurasData[k]
|
|
if aurasLine[3] == guid and aurasLine[7] == 'DEBUFF' then
|
|
local time_ = floor( timestampToFightTime( aurasLine[1] ) * 2 ) + 1
|
|
local spellID = aurasLine[6]
|
|
if not PositionsTab_Variables.DebuffsBlackList[ spellID ] then
|
|
if aurasLine[8] ~= 2 then
|
|
current[ spellID ] = current[ spellID ] or time_
|
|
elseif aurasLine[8] == 2 then
|
|
local start = current[ spellID ] or 1
|
|
for l = start, time_ do
|
|
debuffsData[l] = debuffsData[l] or {}
|
|
tinsert(debuffsData[l],spellID+((time_ - l)%1000)/1000)
|
|
end
|
|
current[ spellID ] = nil
|
|
end
|
|
end
|
|
end
|
|
end
|
|
local positionsData_len = #positionsData
|
|
for spellID,start in pairs(current) do
|
|
for j=start, positionsData_len do
|
|
debuffsData[j] = debuffsData[j] or {}
|
|
tinsert(debuffsData[j],spellID+((positionsData_len - j)%1000)/1000)
|
|
end
|
|
end
|
|
|
|
tab.raidFrames[i]:Update(1)
|
|
end
|
|
end
|
|
for i=#raidFrames+1,40 do
|
|
tab.raidFrames[i].Unit = nil
|
|
tab.raidFrames[i]:Update()
|
|
end
|
|
for i=1,6 do
|
|
tab.unitFrames[i].Unit = "boss"..i
|
|
tab.unitFrames[i]:Update(1)
|
|
end
|
|
else
|
|
for i=1,40 do
|
|
tab.raidFrames[i].Unit = nil
|
|
tab.raidFrames[i]:Update()
|
|
end
|
|
for i=1,6 do
|
|
tab.unitFrames[i].Unit = nil
|
|
tab.unitFrames[i]:Update()
|
|
end
|
|
end
|
|
|
|
tab.timeSlider:SetMinMaxValues(1,max(.5,#graphData)*2)
|
|
|
|
tab.scroll.C:SetScale(1)
|
|
tab.scroll.scrollH = 0
|
|
tab.scroll.scrollV = 0
|
|
tab.scroll:SetHorizontalScroll(0)
|
|
tab.scroll:SetVerticalScroll(0)
|
|
for i=1,#tab.player do
|
|
tab.player[i].SubDot:SetScale(1)
|
|
end
|
|
|
|
local HPList = PositionsTab_Variables.HPList
|
|
wipe(PositionsTab_Variables.HPList)
|
|
|
|
for destGUID,destData in pairs(CurrentFight.damage) do
|
|
for destReaction,destReactData in pairs(destData) do
|
|
local isEnemy = false
|
|
if GetUnitInfoByUnitFlagFix(destReaction,2) == 512 then
|
|
isEnemy = true
|
|
end
|
|
if isEnemy then
|
|
for sourceGUID,sourceData in pairs(destReactData) do
|
|
local source = sourceGUID
|
|
local dest = destGUID
|
|
|
|
local mobData = HPList[dest]
|
|
if not mobData then
|
|
mobData = {
|
|
maxhp = CurrentFight.maxHP[dest],
|
|
damage = {},
|
|
res = {},
|
|
mark = {},
|
|
}
|
|
HPList[dest] = mobData
|
|
end
|
|
|
|
for spellID,spellSegments in pairs(sourceData) do
|
|
for segment,spellAmount in pairs(spellSegments) do
|
|
mobData.damage[segment] = (mobData.damage[segment] or 0) + spellAmount.amount - spellAmount.overkill
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
for sourceGUID,sourceData in pairs(CurrentFight.heal) do
|
|
for destGUID,destData in pairs(sourceData) do
|
|
for destReact,destReactData in pairs(destData) do
|
|
local isEnemy = not ExRT.F.UnitIsFriendlyByUnitFlag2(destReact)
|
|
if isEnemy then
|
|
local source = sourceGUID
|
|
local dest = destGUID
|
|
|
|
local mobData = HPList[dest]
|
|
if not mobData then
|
|
mobData = {
|
|
maxhp = CurrentFight.maxHP[dest],
|
|
damage = {},
|
|
res = {},
|
|
mark = {},
|
|
}
|
|
HPList[dest] = mobData
|
|
end
|
|
|
|
for spellID,spellSegments in pairs(destReactData) do
|
|
for segment,spellAmount in pairs(spellSegments) do
|
|
mobData.damage[segment] = (mobData.damage[segment] or 0) - (spellAmount.amount - spellAmount.over - spellAmount.absorbs)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
for GUID,dataGUID in pairs(CurrentFight.cast) do
|
|
for i,PlayerCastData in ipairs(dataGUID) do
|
|
local mark, destData
|
|
if HPList[dataGUID] then
|
|
mark = PlayerCastData[6]
|
|
destData = HPList[dataGUID]
|
|
elseif HPList[ PlayerCastData[4] ] then
|
|
mark = PlayerCastData[5]
|
|
destData = HPList[ PlayerCastData[4] ]
|
|
end
|
|
if mark then
|
|
local t = ceil(CurrentFight.segments[ PlayerCastData.s ].t - CurrentFight.encounterStart)
|
|
destData.mark[t] = module.db.raidTargets[ mark ] or 0
|
|
end
|
|
end
|
|
end
|
|
for destGUID,destData in pairs(HPList) do
|
|
local dmgList = {}
|
|
for seg,dmg in pairs(destData.damage) do
|
|
dmgList[#dmgList+1] = {seg,dmg}
|
|
end
|
|
sort(dmgList,function(a,b)return a[1]<b[1] end)
|
|
local now = 0
|
|
for i,d in pairs(dmgList) do
|
|
if not destData.seen then
|
|
destData.seen = d[1]
|
|
end
|
|
now = now + d[2]
|
|
destData.res[ d[1] ] = now
|
|
destData.lastSeen = d[1]
|
|
end
|
|
local last = ceil(CurrentFight.segments[#CurrentFight.segments].t - CurrentFight.encounterStart)
|
|
local prev = nil
|
|
for i=0,last do
|
|
if i < destData.seen then
|
|
destData.res[i] = 0
|
|
elseif i <= (destData.lastSeen + 10) then
|
|
if destData.res[i] then
|
|
prev = destData.res[i]
|
|
else
|
|
destData.res[i] = prev
|
|
end
|
|
end
|
|
end
|
|
|
|
local prev,first = nil
|
|
for i=0,last do
|
|
if destData.mark[i] then
|
|
prev = destData.mark[i]
|
|
if not first then
|
|
for j=0,i-1 do
|
|
destData.mark[j] = prev
|
|
end
|
|
first = true
|
|
end
|
|
else
|
|
destData.mark[i] = prev
|
|
end
|
|
end
|
|
for i=0,last do
|
|
if destData.mark[i] == 0 then
|
|
destData.mark[i] = nil
|
|
end
|
|
end
|
|
end
|
|
|
|
tab.timeSlider:SetValue(1)
|
|
PositionsTab_UpdatePositions(1)
|
|
end
|
|
|
|
tab:SetScript("OnShow",function (self)
|
|
if BWInterfaceFrame.nowFightID ~= self.lastFightID then
|
|
PositionsTab_UpdatePage()
|
|
self.lastFightID = BWInterfaceFrame.nowFightID
|
|
end
|
|
end)
|
|
|
|
|
|
--- Others tab
|
|
tab = BWInterfaceFrame.tab.tabs[10]
|
|
local otherTab = tab
|
|
|
|
tab.DecorationLine = ELib:DecorationLine(tab,true):Point("TOPLEFT",tab,"TOPLEFT",3,-8):Point("RIGHT",tab,-3,0):Size(0,20)
|
|
|
|
tab.tab = ELib:Tabs(tab,0,
|
|
TRACKING,
|
|
L.BossWatcherDamageTaken
|
|
):Size(865,600):Point("TOP",0,-29):SetTo(1)
|
|
|
|
tab.tab:SetBackdropBorderColor(0,0,0,0)
|
|
tab.tab:SetBackdropColor(0,0,0,0)
|
|
|
|
|
|
---- Tracking
|
|
tab = BWInterfaceFrame.tab.tabs[10].tab.tabs[1]
|
|
local trackingTab = tab
|
|
|
|
local TrackingTab_Variables = {
|
|
EncounterOrder = {
|
|
1778,1785,1787,1798,1786,1783,1788,1794,1777,1800,1784,1795,1799,
|
|
1853,1841,1873,1854,1876,1877,1864,
|
|
1958,1962,2008,
|
|
1849,1865,1867,1871,1862,1886,1842,1863,1872,1866,
|
|
},
|
|
}
|
|
|
|
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.BossWatcherTabPlayersSpells,
|
|
L.BossWatcherTabSettings
|
|
):Size(850,555):Point("TOP",0,-29):SetTo(1)
|
|
tab.headerTab:SetBackdropBorderColor(0,0,0,0)
|
|
tab.headerTab:SetBackdropColor(0,0,0,0)
|
|
|
|
tab.spellsList = ELib:ScrollList(tab.headerTab.tabs[1]):Size(190,510):Point(10,-15)
|
|
tab.targetsList = ELib:ScrollTableList(tab.headerTab.tabs[1],80,0):Size(625,510):Point("TOPLEFT",tab.spellsList,"TOPRIGHT",10,0)
|
|
|
|
tab.spellsList.D = {}
|
|
|
|
function tab.spellsList:UpdateAdditional()
|
|
for i=1,#self.List do
|
|
self.List[i]:SetEnabled(true)
|
|
end
|
|
end
|
|
|
|
function tab.spellsList:SetListValue(index)
|
|
wipe(trackingTab.targetsList.L)
|
|
|
|
local L = trackingTab.targetsList.L
|
|
|
|
local prevTime,prevTimeRow = nil
|
|
|
|
local spellID = self.D[index]
|
|
for i,trackingData in ipairs(CurrentFight.tracking) do
|
|
if trackingData[6] == spellID then
|
|
local time = timestampToFightTime(trackingData[1])
|
|
local sourceGUID,destGUID = trackingData[2],trackingData[4]
|
|
local sourceRaidTarget,destRaidTarget = nil
|
|
|
|
if spellID == 185008 then --Archimonde: Unleashed Torment
|
|
for _,auraData in ipairs(CurrentFight.auras) do
|
|
if auraData[2] == sourceGUID and auraData[6] == 184964 then
|
|
sourceGUID = auraData[3]
|
|
break
|
|
end
|
|
end
|
|
elseif spellID == 190399 then --Archimonde: Mark of the Legion
|
|
for _,auraData in ipairs(CurrentFight.auras) do
|
|
if auraData[6] == 187050 and auraData[8] == 2 and abs(trackingData[1]-auraData[1])<0.4 then
|
|
sourceGUID = auraData[3]
|
|
break
|
|
end
|
|
end
|
|
elseif spellID == 182011 then --Mannoroth: Empowered Mannoroth's Gaze
|
|
local newSourceGUID = nil
|
|
for _,auraData in ipairs(CurrentFight.auras) do
|
|
if auraData[6] == 182006 and auraData[8] == 2 and auraData[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[1] else return a[5]>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
|