----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- local Details = _G.Details local addonName, Details222 = ... local Loc = LibStub("AceLocale-3.0"):GetLocale("Details") local detailsFramework = DetailsFramework ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --local pointers local UnitAffectingCombat = UnitAffectingCombat local UnitHealth = UnitHealth local UnitHealthMax = UnitHealthMax local UnitGUID = UnitGUID local IsInGroup = IsInGroup local CombatLogGetCurrentEventInfo = CombatLogGetCurrentEventInfo local GetTime = GetTime local tonumber = tonumber local tinsert = table.insert local select = select local bitBand = bit.band local floor = math.floor local ipairs = ipairs local type = type local meleeString = _G["MELEE"] local _UnitGroupRolesAssigned = detailsFramework.UnitGroupRolesAssigned local _GetSpellInfo = Details.getspellinfo local GetSpellInfo = Details222.GetSpellInfo local isWOTLK = detailsFramework.IsWotLKWow() local isERA = detailsFramework.IsClassicWow() local isCATA = detailsFramework.IsCataWow() local _tempo = time() _ = nil local shield_cache = Details.ShieldCache --details local local parser = Details.parser --details local local crowdControlSpells = LIB_OPEN_RAID_CROWDCONTROL local spellContainerClass = Details.container_habilidades --details local --localize the cooldown table from the framework local defensive_cooldowns = {} if (LIB_OPEN_RAID_COOLDOWNS_INFO) then --check if the cooldown is type 2 or 3 or 4 and add to the defensive_cooldowns table for spellId, spellTable in pairs(LIB_OPEN_RAID_COOLDOWNS_INFO) do if (spellTable.type == 2 or spellTable.type == 3 or spellTable.type == 4) then defensive_cooldowns[spellId] = spellTable end end end --cache the addition functions for each attribute local _spell_damage_func = Details.habilidade_dano.Add local _spell_damageMiss_func = Details.habilidade_dano.AddMiss local _spell_heal_func = Details.habilidade_cura.Add local _spell_energy_func = Details.habilidade_e_energy.Add local _spell_utility_func = Details.habilidade_misc.Add ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --cache --cache current combat local _current_combat = Details.tabela_vigente or {} --placeholder table --cache total table local _current_total = _current_combat.totals local _current_gtotal = _current_combat.totals_grupo --cache actors containers local _current_damage_container = _current_combat [1] local _current_heal_container = _current_combat [2] local _current_energy_container = _current_combat [3] local _current_misc_container = _current_combat [4] --pet container cache ---@type petcontainer local petContainer = Details222.PetContainer local names_cache = {} --damage local damage_cache = setmetatable({}, Details.weaktable) local damage_cache_pets = setmetatable({}, Details.weaktable) local damage_cache_petsOwners = setmetatable({}, Details.weaktable) --heaing local healing_cache = setmetatable({}, Details.weaktable) local banned_healing_spells = { [326514] = true, --remove on 10.0 - Forgeborne Reveries - necrolords ability } --energy local energy_cache = setmetatable({}, Details.weaktable) --misc local misc_cache = setmetatable({}, Details.weaktable) local misc_cache_pets = setmetatable({}, Details.weaktable) local misc_cache_petsOwners = setmetatable({}, Details.weaktable) --party & raid members local raid_members_cache = setmetatable({}, Details.weaktable) --tanks local tanks_members_cache = setmetatable({}, Details.weaktable) --auto regen local auto_regen_cache = setmetatable({}, Details.weaktable) --bitfield swap cache local bitfield_swap_cache = {} --damage and heal last events local last_events_cache = {} --just initialize table (placeholder) --hunter pet frenzy cache local pet_frenzy_cache = {} --npcId cache local npcid_cache = {} --enemy cast cache local enemy_cast_cache = {} --shield spellid cache local shield_spellid_cache = {} --pets local petCache = petContainer.Pets --ignore deaths local ignore_death_cache = {} --cache local cacheAnything = { arenaHealth = {}, paladin_vivaldi_blessings = {}, track_hunter_frenzy = false, rampage_cast_amount = {}, } --store the gear of each player local gearCache = {} --cache the data for passive trinkets procs local _trinket_data_cache = {} --spell containers for special cases local monk_guard_talent = {} --guard talent for bm monks --spell reflection local reflection_damage = {} --self-inflicted damage local reflection_debuffs = {} --self-inflicted debuffs local reflection_events = {} --spell_missed reflected events local reflection_auras = {} --active reflecting auras local reflection_dispels = {} --active reflecting dispels local reflection_spellid = { --we can track which spell caused the reflection --this is used to credit this aura as the one doing the damage [23920] = true, --warrior spell reflection [216890] = true, --warrior spell reflection (pvp talent) [213915] = true, --warrior mass spell reflection [212295] = true, --warlock nether ward --check pally legendary } local reflection_dispelid = { --some dispels also reflect, and we can track them [122783] = true, --monk diffuse magic --[205604] = true, --demon hunter reverse magic --this last one is an odd one, like most dh spells is kindy buggy combatlog wise --for now it doesn't fire SPELL_DISPEL events even when dispelling stuff (thanks blizzard) --maybe someone can figure out something to track it... but for now it doesnt work } local reflection_ignore = { --common self-harm spells that we know weren't reflected --this list can be expanded [111400] = true, --warlock burning rush [124255] = true, --monk stagger [196917] = true, --paladin light of the martyr [217979] = true, --warlock health funnel } --army od the dead cache local dk_pets_cache = { army = {}, apoc = {}, } --list of buffs that should be credited to the target of the buff local buffs_to_other_players = { --[10060] = true, --power infusion [413426] = true, --rippling anthem (trinket 10.1) [405734] = true, --spore tender [406785] = true, --invigorating spore cloud } ---@class evokerinfo : table ---@field key1 serial ---@field key2 actorname ---@field key3 controlflags ---@field key4 valueamount ---@class evokereonsbreathinfo : table ---@field key1 serial ---@field key2 actorname ---@field key3 controlflags ---@field key4 unit ---@field key5 unixtime ---@field key6 number ---@field key7 number local augmentation_aura_list = { [395152] = true, --ebon might (evoker 10.1.5) 395296 = the evoker buff on it self [413984] = true, --Shifting Sands [410089] = true, --prescience (evoker 10.1.5) [409560] = true, --Temporal Wound [360827] = true, --Blistering Scales [410263] = true, --Inferno's Blessing } --list of buffs given by another player but should also be credited to the which received it local buffs_on_target = { --[395152] = true, --ebon might (evoker 10.1.5) [395152] = true, --ebon might (evoker 10.1.5) 395296 = the evoker buff on it self [410089] = true, --prescience (evoker 10.1.5) [10060] = true, --power infusion [194384] = true, --atonement uptime [378134] = true, --rallied to victory } ---@type table local ignoredWorldAuras = Details222.IgnoredWorldAuras Details.CreditBuffToTarget = buffs_on_target --store all information about augmentation evokers ~roskash local augmentation_cache = { ebon_might = {}, prescience = {}, prescience_stacks = {}, ---@type table breath_targets = {}, flyaway = {}, flyaway_timer = {}, shield = {}, ss = {}, infernobless = {}, } Details.augmentation_cache = augmentation_cache Details222.SpecHelpers[1473].augmentation_cache = augmentation_cache local empower_cache = {} local scale_factors = { [256] = 3.80,--disc priest [254] = 9.73, --hunter mm } ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --constants local container_misc = Details.container_type.CONTAINER_MISC_CLASS local OBJECT_TYPE_ENEMY = 0x00000040 local OBJECT_TYPE_PLAYER = 0x00000400 local OBJECT_TYPE_PETS = 0x00003000 local AFFILIATION_GROUP = 0x00000007 local REACTION_FRIENDLY = 0x00000010 local ENVIRONMENTAL_FALLING_NAME = Loc ["STRING_ENVIRONMENTAL_FALLING"] local ENVIRONMENTAL_DROWNING_NAME = Loc ["STRING_ENVIRONMENTAL_DROWNING"] local ENVIRONMENTAL_FATIGUE_NAME = Loc ["STRING_ENVIRONMENTAL_FATIGUE"] local ENVIRONMENTAL_FIRE_NAME = Loc ["STRING_ENVIRONMENTAL_FIRE"] local ENVIRONMENTAL_LAVA_NAME = Loc ["STRING_ENVIRONMENTAL_LAVA"] local ENVIRONMENTAL_SLIME_NAME = Loc ["STRING_ENVIRONMENTAL_SLIME"] local RAID_TARGET_FLAGS = { [128] = true, --0x80 skull [64] = true, --0x40 cross [32] = true, --0x20 square [16] = true, --0x10 moon [8] = true, --0x8 triangle [4] = true, --0x4 diamond [2] = true, --0x2 circle [1] = true, --0x1 star } --spellIds override local override_spellId = {} if (isWOTLK) then override_spellId = { --Scourge Strike [55090] = 55271, [55265] = 55271, [55270] = 55271, [70890] = 55271, --shadow --Frost Strike [49143] = 55268, [51416] = 55268, [51417] = 55268, [51418] = 55268, [51419] = 55268, [66962] = 55268, --offhand --Obliterate [49020] = 51425, [51423] = 51425, [51424] = 51425, [66974] = 51425, --offhand --Death Strike [49998] = 49924, [49999] = 49924, [45463] = 49924, [49923] = 49924, [66953] = 49924, --offhand --Blood Strike [45902] = 49930, [49926] = 49930, [49927] = 49930, [49928] = 49930, [49929] = 49930, [66979] = 49930, --offhand --Rune Strike [6621] = 56815, --offhand --Plague Strike [45462] = 49921, [49917] = 49921, [49918] = 49921, [49919] = 49921, [49920] = 49921, [66992] = 49921, --offhand --Seal of Command [20424] = 69403, --53739 and 53733 --odyn's fury warrior [385062] = 385060, [385061] = 385060, --crushing blow [335098] = 335097, [335100] = 335097, --charge warrior [105771] = 126664, --elemental stances [377458] = 377459, [377461] = 377459, [382133] = 377459, } else --retail override_spellId = { --~merge [184707] = 218617, --warrior rampage [184709] = 218617, --warrior rampage [201364] = 218617, --warrior rampage [201363] = 218617, --warrior rampage [85384] = 96103, --warrior raging blow [85288] = 96103, --warrior raging blow [280849] = 5308, --warrior execute [163558] = 5308, --warrior execute [217955] = 5308, --warrior execute [217956] = 5308, --warrior execute [217957] = 5308, --warrior execute [224253] = 5308, --warrior execute [199850] = 199658, --warrior whirlwind [190411] = 199658, --warrior whirlwind [44949] = 199658, --warrior whirlwind [199667] = 199658, --warrior whirlwind [199852] = 199658, --warrior whirlwind [199851] = 199658, --warrior whirlwind [411547] = 199658, --arms warrior whirlwind [385228] = 199658, --arms warrior whirlwind [105771] = 126664, --warrior charge [222031] = 199547, --deamonhunter ChaosStrike [200685] = 199552, --deamonhunter Blade Dance [391378] = 199552, --^ [391374] = 199552, --^ [210155] = 210153, --deamonhunter Death Sweep [393055] = 210153, --^ [393054] = 210153, --^ [393035] = 337819, --demonhunter throw glaive [227518] = 201428, --deamonhunter Annihilation [187727] = 178741, --deamonhunter Immolation Aura [201789] = 201628, --deamonhunter Fury of the Illidari [225921] = 225919, --deamonhunter Fracture talent [205164] = 205165, --death knight Crystalline Swords [193315] = 197834, --rogue Saber Slash [202822] = 202823, --rogue greed [280720] = 282449, --rogue Secret Technique [280719] = 282449, --rogue Secret Technique [27576] = 5374, --rogue mutilate [385897] = 8676, --rogue Ambush [430023] = 8676, --rogue Ambush [233496] = 233490, --warlock Unstable Affliction [233497] = 233490, --warlock Unstable Affliction [233498] = 233490, --warlock Unstable Affliction [233499] = 233490, --warlock Unstable Affliction [261947] = 261977, --monk fist of the white tiger talent [32175] = 17364, -- shaman Stormstrike (from Turkar on github) [32176] = 17364, -- shaman Stormstrike [45284] = 188196, --shaman lightining bolt overloaded [45297] = 188443, -- shaman chain lightning overload [120588] = 117014, -- shaman elemental blast overload [285466] = 285452, -- shaman lava burst overload [298765] = 77478, -- shaman earthquake overload [219271] = 210714, -- shaman icefury overload [228361] = 228360, --shadow priest void erruption [401422] = 401428, --vessel of searing shadow (trinket) [417134] = 414532, --rage of Fyr'alath [413584] = 414532, [424094] = 414532, [228649] = 100784, --monk blackout kick } --all totem --377461 382133 --377458 377459 end local override_aura_spellid = { [426672] = { --Pip's Emerald Friendship Badge Urctos CanOverride = function(auraName, texture, count, auraType, duration, expirationTime, sourceUnit, isStealable, nameplateShowPersonal, spellId, canApplyAura, isBossAura, isFromPlayerOrPlayerPet, nameplateShowAll, timeMod, ...) if (duration and duration >= 1 and duration <= 60) then return true end end, NewSpellId = 426674, }, [426676] = { --Pip's Emerald Friendship Badge Aerwynn CanOverride = function(auraName, texture, count, auraType, duration, expirationTime, sourceUnit, isStealable, nameplateShowPersonal, spellId, canApplyAura, isBossAura, isFromPlayerOrPlayerPet, nameplateShowAll, timeMod, ...) if (duration and duration >= 1 and duration <= 60) then return true end end, NewSpellId = 426677, }, [426647] = { --Pip's Emerald Friendship Badge Pip CanOverride = function(auraName, texture, count, auraType, duration, expirationTime, sourceUnit, isStealable, nameplateShowPersonal, spellId, canApplyAura, isBossAura, isFromPlayerOrPlayerPet, nameplateShowAll, timeMod, ...) if (duration and duration >= 1 and duration <= 60) then return true end end, NewSpellId = 426648 }, } local bitfield_debuffs = {} for _, spellid in ipairs(Details.BitfieldSwapDebuffsIDs) do local spellname = GetSpellInfo(spellid) if (spellname) then bitfield_debuffs[spellname] = true else bitfield_debuffs[spellid] = true end end for spellId in pairs(Details.BitfieldSwapDebuffsSpellIDs) do bitfield_debuffs [spellId] = true end Details.bitfield_debuffs_table = bitfield_debuffs --tbc spell caches local TBC_PrayerOfMendingCache = {} local TBC_EarthShieldCache = {} --expose the override spells table to external scripts Details.OverridedSpellIds = override_spellId --list of ignored npcs by the user Details.default_ignored_npcs = { --DH Havoc Talent Fodder to the Flame [169421] = true, [169425] = true, [168932] = true, [169426] = true, [169429] = true, [169428] = true, [169430] = true, --Volatile Spark on razga'reth [194999] = true, --Ozumat - Throne of Tides [44566] = true, --Smoldering Seedling trinket [212590] = true, } local ignored_npcids = {} --ignore soul link (damage from the warlock on his pet - current to demonology only) local SPELLID_WARLOCK_SOULLINK = 108446 --brewmaster monk guard talent local SPELLID_MONK_GUARD = 115295 --shaman earth shield (bcc) local SPELLID_SHAMAN_EARTHSHIELD_HEAL = 379 local SPELLID_SHAMAN_EARTHSHIELD_BUFF_RANK1 = 974 local SPELLID_SHAMAN_EARTHSHIELD_BUFF_RANK2 = 32593 local SPELLID_SHAMAN_EARTHSHIELD_BUFF_RANK3 = 32594 local SHAMAN_EARTHSHIELD_BUFF = { [SPELLID_SHAMAN_EARTHSHIELD_BUFF_RANK1] = true, [SPELLID_SHAMAN_EARTHSHIELD_BUFF_RANK2] = true, [SPELLID_SHAMAN_EARTHSHIELD_BUFF_RANK3] = true, } --holy priest prayer of mending (bcc) local SPELLID_PRIEST_POM_BUFF = 41635 local SPELLID_PRIEST_POM_HEAL = 33110 local SPELLID_SANGUINE_HEAL = 226510 --spells with special treatment local special_damage_spells = { [98021] = true, --spirit link toten [124255] = true, --stagger [282449] = true, --akaari's soul rogue [196917] = true, --light of the martyr [388009] = true, --blessing of spring [388012] = true, --blessing of summer [384601] = true, --Anti Magic Bomb [392171] = true, --Rose of the Vale [392166] = true, --Azure Stone of Might [379020] = true, --Wand of Negation [372824] = true, --Burning Chains } --damage spells to ignore local damage_spells_to_ignore = { --the damage that the warlock apply to its pet through soullink is ignored --it is not useful for damage done or friendly fire [SPELLID_WARLOCK_SOULLINK] = true, } --expose the ignore spells table to external scripts Details.SpellsToIgnore = damage_spells_to_ignore --is parser allowed to replace spellIDs? local is_using_spellId_override = false --cache data for fast access during parsing local _in_combat = false local _current_encounter_id local _in_resting_zone = false local _global_combat_counter = 0 ---amount of events allowed to store in the table which records the latest events that happened to a player before his death, this value can also be retrieved with Details.deadlog_events local _amount_of_last_events = 16 --map type local _is_in_instance = false --overheal for shields local _use_shield_overheal = false --hooks local _hook_cooldowns = false local _hook_deaths = false local _hook_battleress = false local _hook_interrupt = false local _hook_cooldowns_container = Details.hooks ["HOOK_COOLDOWN"] local _hook_deaths_container = Details.hooks ["HOOK_DEATH"] local _hook_battleress_container = Details.hooks ["HOOK_BATTLERESS"] local _hook_interrupt_container = Details.hooks ["HOOK_INTERRUPT"] --regen overflow local auto_regen_power_specs = { [103] = Enum.PowerType.Energy, --druid feral [259] = Enum.PowerType.Energy, --rogue ass [260] = Enum.PowerType.Energy, --rogue outlaw [261] = Enum.PowerType.Energy, --rogue sub [254] = Enum.PowerType.Focus, --hunter mm [253] = Enum.PowerType.Focus, --hunter bm [255] = Enum.PowerType.Focus, --hunter survival [268] = Enum.PowerType.Energy, --monk brewmaster [269] = Enum.PowerType.Energy, --monk windwalker } local AUTO_REGEN_PRECISION = 2 --todo: replace the amount of wasted resource by the amount of time the player "sitted" at max power --Neltharus Weapons in Neltharus dungeon --Remove on 11.0 --these detect the weapon actor by the damage spellId Details.NeltharusWeaponSpellIds = { [384601] = true, --Anti Magic Bomb [392171] = true, --Rose of the Vale [392166] = true, --Azure Stone of Might [379020] = true, --Wand of Negation [372824] = true, --Burning Chains } Details.NeltharusWeaponActorName = "Neltharus Weapons" Details.NeltharusWeaponActorSpellId = 377176 --for the icon: Blazing Aegis --sanguine affix for m+ Details.SanguineHealActorName = GetSpellInfo(SPELLID_SANGUINE_HEAL) --cache a spellName and the value is the spellId --the container actor will use this name to create a fake player actor where its name is the spellName and the specIcon is the spellIcon Details.SpecialSpellActorsName = {} --add sanguine affix if (not isWOTLK and not isCATA and not isERA) then if (Details.SanguineHealActorName) then Details.SpecialSpellActorsName[Details.SanguineHealActorName] = SPELLID_SANGUINE_HEAL end --add Neltharus weapons Details.SpecialSpellActorsName[Details.NeltharusWeaponActorName] = Details.NeltharusWeaponActorSpellId end --Damage spells that trigger outside of combat, which we don't want to have start a combat. --387846 Fel Armor --352561 Undulating Maneuvers --111400 warlock's burning rush --368637 is buff from trinket "Scars of Fraternal Strife" which make the player bleed even out-of-combat --371070 is "Iced Phial of Corrupting Rage" effect triggers randomly, even out-of-combat --401394 is "Vessel of Seared Shadows" trinket --146739 is corruption that doesn't expire local spells_cant_start_combat = { [368637] = true, --The Third Rune [371070] = true, --Rotting from Within [146739] = true, --Corruption [387846] = true, --Fel Armor [352561] = true, --Undulating Maneuvers [401394] = true, --Unstable Flames [111400] = true, --Burning Rush [12654] = true, --Ignite [419800] = true, --Intensifying Flame [448744] = true, --Authority of Radiant Power } ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --internal functions ----------------------------------------------------------------------------------------------------------------------------------------- --DAMAGE serach key: ~damage | ----------------------------------------------------------------------------------------------------------------------------------------- local whoAggro = function(self) if ((Details.LastPullMsg or 0) + 30 > time()) then Details.WhoAggroTimer = nil return end Details.LastPullMsg = time() local hitLine = self.HitBy or "|cFFFFBB00First Hit|r: *?*" local targetLine = "" if (Details.bossTargetAtPull) then targetLine = " |cFFFFBB00Boss First Target|r: " .. Details.bossTargetAtPull else for i = 1, 5 do local boss = UnitExists("boss" .. i) if (boss) then local target = UnitName ("boss" .. i .. "target") if (target and type(target) == "string") then targetLine = " |cFFFFBB00Boss First Target|r: " .. target break end end end end Details:Msg(hitLine .. targetLine) Details.WhoAggroTimer = nil Details.bossTargetAtPull = nil end local lastRecordFound = {id = 0, diff = 0, combatTime = 0} Details.PrintEncounterRecord = function(self) --this block won't execute if the storage isn't loaded --self is a timer reference from C_Timer local diffNumberToName = Details222.storage.DiffIdToName local encounterID = self.Boss local diff = self.Diff if (diff == 15 or diff == 16) then --might give errors local value, rank, combatTime = 0, 0, 0 if (encounterID == lastRecordFound.id and diff == lastRecordFound.diff) then --is the same encounter, no need to find the value again. value, rank, combatTime = lastRecordFound.value, lastRecordFound.rank, lastRecordFound.combatTime else local db = Details.GetStorage() local role = _UnitGroupRolesAssigned("player") local isDamage = (role == "DAMAGER") or (role == "TANK") --or true ---@type details_storage_unitresult, details_encounterkillinfo local bestRank, encounterTable = Details222.storage.GetBestFromPlayer(diffNumberToName[diff], encounterID, isDamage and "DAMAGER" or "HEALER", Details.playername, true) if (bestRank) then ---@type number local rankPosition = Details222.storage.GetUnitGuildRank(diffNumberToName[diff], encounterID, isDamage and "DAMAGER" or "HEALER", Details.playername, true) value = bestRank.total or 0 rank = rankPosition or 0 combatTime = encounterTable.elapsed --if found the result, cache the values so no need to search again next pull lastRecordFound.value = value lastRecordFound.rank = rank lastRecordFound.id = encounterID lastRecordFound.diff = diff lastRecordFound.combatTime = combatTime else --if didn't found, no reason to search again on next pull lastRecordFound.value = 0 lastRecordFound.rank = 0 lastRecordFound.combatTime = 0 lastRecordFound.id = encounterID lastRecordFound.diff = diff end end if (value and combatTime and value > 0 and combatTime > 0) then Details:Msg("|cFFFFBB00Your Best Score|r:", Details:ToK2 ((value) / combatTime) .. " [|cFFFFFF00Guild Rank: " .. rank .. "|r]") --localize-me end if ((not combatTime or combatTime == 0) and not Details.SyncWarning) then Details:Msg("|cFFFF3300you may need sync the rank within the guild, type '|cFFFFFF00/details rank|r'|r") --localize-me Details.SyncWarning = true end end end --~spell ~spelldamage function parser:spell_dmg(token, time, sourceSerial, sourceName, sourceFlags, targetSerial, targetName, targetFlags, targetRaidFlags, spellId, spellName, spellType, amount, overkill, school, resisted, blocked, absorbed, critical, glacing, crushing, isoffhand, isreflected) --early checks and fixes if (sourceSerial == "") then if (sourceFlags and bitBand(sourceFlags, OBJECT_TYPE_PETS) ~= 0) then --pets must have a serial return end end --melee if (token == "SWING_DAMAGE") then spellId, spellName, spellType, amount, overkill, school, resisted, blocked, absorbed, critical, glacing, crushing, isoffhand = 1, meleeString, 00000001, spellId, spellName, spellType, amount, overkill, school, resisted, blocked, absorbed, critical end if (not targetName) then --no target name, just quit return elseif (not sourceName) then --no actor name, use spell name instead sourceName = names_cache[spellName] if (not sourceName) then sourceName = "[*] " .. spellName names_cache[spellName] = sourceName end sourceFlags = 0xa48 sourceSerial = "" end --check if the spell is in the backlist and return if true if (damage_spells_to_ignore[spellId]) then return end --> spell reflection code by github user @m4tjz if (sourceSerial == targetSerial and not reflection_ignore[spellId]) then --~reflect --this spell could've been reflected, check it if (reflection_events[sourceSerial] and reflection_events[sourceSerial][spellId] and time-reflection_events[sourceSerial][spellId].time > 3.5 and (not reflection_debuffs[sourceSerial] or (reflection_debuffs[sourceSerial] and not reflection_debuffs[sourceSerial][spellId]))) then --here we check if we have to filter old reflection data --we check for two conditions --the first is to see if this is an old reflection --if more than 3.5 seconds have past then we can say that it is old... but! --the second condition is to see if there is an active debuff with the same spellid --if there is one then we ignore the timer and skip this --this should be cleared afterwards somehow... don't know how... reflection_events[sourceSerial][spellId] = nil if (next(reflection_events[sourceSerial]) == nil) then --there should be some better way of handling this kind of filtering, any suggestion? reflection_events[sourceSerial] = nil end end local reflection = reflection_events[sourceSerial] and reflection_events[sourceSerial][spellId] if (reflection) then --if we still have the reflection data then we conclude it was reflected --extend the duration of the timer to catch the rare channelling spells reflection_events[sourceSerial][spellId].time = time --crediting the source of the reflection aura sourceSerial = reflection.who_serial sourceName = reflection.who_name sourceFlags = reflection.who_flags --data of the aura that caused the reflection isreflected = spellId --which spell was reflected spellId = reflection.spellid --which spell made the reflection spellName = reflection.spellname spellType = reflection.spelltype return parser:spell_dmg(token,time,sourceSerial,sourceName,sourceFlags,targetSerial,targetName,targetFlags,targetRaidFlags,spellId,spellName,0x400,amount,-1,nil,nil,nil,nil,false,false,false,false, isreflected) else --saving information about this damage because it may occurred before a reflect event reflection_damage[sourceSerial] = reflection_damage[sourceSerial] or {} reflection_damage[sourceSerial][spellId] = { amount = amount, time = time, } end end --> if the parser are allowed to replace spellIDs if (is_using_spellId_override) then spellId = override_spellId[spellId] or spellId end --> npcId check for ignored npcs --> get the npcId from the cache, if it's not there then get it from the serial and add it to the cache local npcId = npcid_cache[targetSerial] --target npc if (not npcId) then --this string manipulation is running on every event npcId = tonumber(select(6, strsplit("-", targetSerial)) or 0) npcid_cache[targetSerial] = npcId end if (ignored_npcids[npcId]) then return end npcId = npcid_cache[sourceSerial] --source npc if (not npcId) then npcId = tonumber(select(6, strsplit("-", sourceSerial)) or 0) npcid_cache[sourceSerial] = npcId end if (ignored_npcids[npcId]) then return end if (npcId == 24207) then --army of the dead --check if this is a army or apoc pet if (dk_pets_cache.army[sourceSerial]) then local cachedName = names_cache[24207001] if (not cachedName) then sourceName = sourceName .. "|T237511:0|t" names_cache[24207001] = sourceName else sourceName = cachedName end else local cachedName = names_cache[24207002] if (not cachedName) then sourceName = sourceName .. "|T1392565:0|t" names_cache[24207002] = sourceName else sourceName = cachedName end end end --check if the spellId has an especial treatment if (special_damage_spells[spellId]) then --stagger if (spellId == 124255) then return parser:MonkStagger_damage(token, time, sourceSerial, sourceName, sourceFlags, targetSerial, targetName, targetFlags, spellId, spellName, spellType, amount, overkill, school, resisted, blocked, absorbed, critical, glacing, crushing, isoffhand) --spirit link toten elseif (spellId == 98021) then return parser:SLT_damage(token, time, sourceSerial, sourceName, sourceFlags, targetSerial, targetName, targetFlags, spellId, spellName, spellType, amount, overkill, school, resisted, blocked, absorbed, critical, glacing, crushing, isoffhand) --rogue's secret technique | when akari's soul gives damage | dragonflight | --REMOVE ON 11.0 - maybe elseif (spellId == 282449) then --npcID if (npcId == 144961) then local ownerName, ownerGUID, ownerFlags = Details222.Pets.AkaarisSoulOwner(sourceSerial, sourceName) if (ownerName and ownerGUID) then sourceSerial = ownerGUID sourceName = ownerName sourceFlags = ownerFlags end end --Light of the Martyr - paladin spell which causes damage to the caster it self elseif (spellId == 196917) then -- or spellid == 183998 < healing part return parser:LOTM_damage(token, time, sourceSerial, sourceName, sourceFlags, targetSerial, targetName, targetFlags, spellId, spellName, spellType, amount, overkill, school, resisted, blocked, absorbed, critical, glacing, crushing, isoffhand) elseif (spellId == 388009 or spellId == 388012) then --damage from the paladin blessings of the seasons local blessingSource = cacheAnything.paladin_vivaldi_blessings[sourceSerial] if (blessingSource) then sourceSerial, sourceName, sourceFlags = unpack(blessingSource) end elseif (Details.NeltharusWeaponSpellIds[spellId]) then sourceName = Details.NeltharusWeaponActorName sourceFlags = 0x514 sourceSerial = "Creature-0-3134-2289-28065-" .. spellId .. "-000164C698" end end ------------------------------------------------------------------------------------------------ --check if need start an combat if (not _in_combat) then --~startcombat ~combatstart if ( token ~= "SPELL_PERIODIC_DAMAGE" and ( (sourceFlags and bitBand(sourceFlags, AFFILIATION_GROUP) ~= 0 and UnitAffectingCombat(sourceName)) --error here, need to remove the realm from sourceName or (targetFlags and bitBand(targetFlags, AFFILIATION_GROUP) ~= 0 and UnitAffectingCombat(targetName)) or (not Details.in_group and sourceFlags and bitBand(sourceFlags, AFFILIATION_GROUP) ~= 0) ) ) then if (spells_cant_start_combat[spellId] and sourceName == Details.playername) then return end if (Details.encounter_table.id and Details.encounter_table["start"] >= GetTime() - 3 and Details.announce_firsthit.enabled) then local link = _GetSpellInfo(spellId) --Removed check for the id being <= 10, both branches were the same. if (Details.WhoAggroTimer) then Details.WhoAggroTimer:Cancel() end Details.WhoAggroTimer = C_Timer.NewTimer(0.1, whoAggro) Details.WhoAggroTimer.HitBy = "|cFFFFFF00First Hit|r: " .. (link or "") .. " from " .. (sourceName or "Unknown") if (Details.announce_firsthit.enabled) then Details:Msg("", Details.WhoAggroTimer.HitBy) end end --local spellInfo = C_Spell.GetSpellInfo(spellId) --print("1 spell:", spellId, spellInfo.name) Details222.StartCombat(sourceSerial, sourceName, sourceFlags, targetSerial, targetName, targetFlags) else --entrar em combate se for dot e for do jogador e o ultimo combate ter sido a mais de 10 segundos atr�s if (token == "SPELL_PERIODIC_DAMAGE" and sourceName == Details.playername) then if (spells_cant_start_combat[spellId]) then return end --can't start a combat with a dot with the latest combat finished less than 10 seconds ago if (Details.last_combat_time + 10 < _tempo) then --print("2 spell:", spellId) Details222.StartCombat(sourceSerial, sourceName, sourceFlags, targetSerial, targetName, targetFlags) end end end end --[[statistics]]-- _detalhes.statistics.damage_calls = _detalhes.statistics.damage_calls + 1 _current_damage_container.need_refresh = true ------------------------------------------------------------------------------------------------ --get actors ---@type actor, actor local sourceActor, ownerActor = damage_cache[sourceSerial] or damage_cache_pets[sourceSerial] or damage_cache[sourceName], damage_cache_petsOwners[sourceSerial] if (not sourceActor) then sourceActor, ownerActor, sourceName = _current_damage_container:GetOrCreateActor(sourceSerial, sourceName, sourceFlags, true) if (ownerActor) then --the actor is a pet if (sourceSerial ~= "") then --insert in the pet cache damage_cache_pets[sourceSerial] = sourceActor damage_cache_petsOwners[sourceSerial] = ownerActor end --check if the pet owner is already in the cache if (not damage_cache[ownerActor.serial] and ownerActor.serial ~= "") then damage_cache[ownerActor.serial] = ownerActor end if (ownerActor) then if (Details222.Debug.DebugPets) then Details:Msg("Parser|DebugPets|ActorCreated|PetName:", sourceActor:Name(), "sourceName:", sourceName, "ownerName:", ownerActor:Name()) elseif (Details222.Debug.DebugPlayerPets and sourceName == Details.playername) then Details:Msg("Parser|DebugPets|ActorCreated|PetName:", sourceActor:Name(), "sourceName:", sourceName, "ownerName:", ownerActor:Name()) end end else --there's no owner actor if (sourceFlags) then if (sourceSerial ~= "") then --insert the sourceActor into the cache damage_cache[sourceSerial] = sourceActor else if (names_cache[spellName]) then damage_cache[sourceName] = sourceActor local _, _, spellIcon = _GetSpellInfo(spellId or 1) sourceActor.spellicon = spellIcon end end end end --if a owner actor isn't found and debug pets is enabled and this is a pet or guardian if (not ownerActor and (Details222.Debug.DebugPets or Details222.Debug.DebugPlayerPets) and bitBand(sourceFlags, 0x00003000) ~= 0) then --OBJECT_TYPE_PETGUARDIAN --note: the actor wasn't found in the cache and got created Details:Msg("DebugPets|Parser|Owner Actor Not Found|", sourceName, sourceSerial, sourceFlags, bitBand(sourceFlags, 0x00003000) ~= 0) ---@type petdata? local petData = petContainer.GetPetInfo(sourceSerial) if (sourceName == "Fire Spirit") then Details:Msg("DebugPets|Parser|PetData|Exists?", petData, sourceName, "Dumping petData on dumpt.") dumpt(petData) end end elseif (ownerActor) then --has (sourceActor and ownerActor) --sourceName is the name of the pet local cachedPetName = names_cache[sourceSerial] if (not cachedPetName) then --add the owner name into the sourceName sourceName = sourceName .. " <" .. ownerActor.nome .. ">" names_cache[sourceSerial] = sourceName else sourceName = cachedPetName end end if (not sourceActor) then return end ---@type actor, actor local targetActor, targetOwner = damage_cache[targetSerial] or damage_cache_pets[targetSerial] or damage_cache[targetName], damage_cache_petsOwners[targetSerial] if (not targetActor) then targetActor, targetOwner, targetName = _current_damage_container:GetOrCreateActor(targetSerial, targetName, targetFlags, true) if (targetOwner) then if (targetSerial ~= "") then --insert in the pet cache damage_cache_pets[targetSerial] = targetActor damage_cache_petsOwners[targetSerial] = targetOwner end --check if the pet owner is already in the cache if (not damage_cache[targetOwner.serial] and targetOwner.serial ~= "") then damage_cache[targetOwner.serial] = targetOwner end else if (targetFlags and targetSerial ~= "") then --ter certeza que n�o � um pet damage_cache [targetSerial] = targetActor end end elseif (targetOwner) then --sourceName is the name of the pet local cachedPetName = names_cache[targetSerial] if (not cachedPetName) then --add the owner name into the sourceName targetName = targetName .. " <" .. targetOwner.nome .. ">" names_cache[targetSerial] = targetName else targetName = cachedPetName end end if (not targetActor) then local instanceName, _, _, _, _, _, _, instanceId = GetInstanceInfo() Details:Msg("D! Report 0x885488", targetName, instanceName, instanceId, damage_cache[targetSerial] and "true") return end --last event sourceActor.last_event = _tempo ------------------------------------------------------------------------------------------------ --group checks and avoidance if (absorbed) then amount = absorbed + (amount or 0) end if (_is_in_instance) then if (overkill and overkill > 0) then overkill = overkill + 1 --if enabled it'll cut the amount of overkill from the last hit (which killed the actor) --when disabled it'll show the total damage done for the latest hit amount = amount - overkill end end if (sourceActor.grupo and not sourceActor.arena_enemy and not sourceActor.enemy and not targetActor.arena_enemy) then --source = friendly player and not an enemy player --dano to adversario estava caindo aqui por nao estar checando .enemy _current_gtotal[1] = _current_gtotal[1] + amount elseif (targetActor.grupo) then --source = arena enemy or friendly player if (targetActor.arena_enemy) then _current_gtotal[1] = _current_gtotal[1] + amount end --record avoidance only for tank actors if (tanks_members_cache[targetSerial]) then --monk's stagger if (targetActor.classe == "MONK") then if (absorbed) then --the absorbed amount was staggered and should not be count as damage taken now --this absorbed will hit the player with the stagger debuff amount = (amount or 0) - absorbed end else --advanced damage taken --if advanced damage taken is enabled, the damage taken to tanks acts like the monk stuff above if (Details.damage_taken_everything) then if (absorbed) then amount = (amount or 0) - absorbed end end end --avoidance local avoidance = targetActor.avoidance if (not avoidance) then targetActor.avoidance = Details:CreateActorAvoidanceTable() avoidance = targetActor.avoidance end local overall = avoidance.overall local mob = avoidance[sourceName] if (not mob) then --if isn't in the table, build on the fly mob = Details:CreateActorAvoidanceTable(true) avoidance[sourceName] = mob end overall["ALL"] = overall["ALL"] + 1 --qualtipo de hit ou absorb mob["ALL"] = mob["ALL"] + 1 --qualtipo de hit ou absorb if (not isERA and spellId < 3) then --overall overall["HITS"] = overall["HITS"] + 1 mob["HITS"] = mob["HITS"] + 1 end if (blocked and blocked > 0) then overall["BLOCKED_HITS"] = overall["BLOCKED_HITS"] + 1 mob["BLOCKED_HITS"] = mob["BLOCKED_HITS"] + 1 overall["BLOCKED_AMT"] = overall["BLOCKED_AMT"] + blocked mob["BLOCKED_AMT"] = mob["BLOCKED_AMT"] + blocked end --absorbs status if (absorbed) then --aqui pode ser apenas absorb parcial overall["ABSORB"] = overall["ABSORB"] + 1 overall["PARTIAL_ABSORBED"] = overall["PARTIAL_ABSORBED"] + 1 overall["PARTIAL_ABSORB_AMT"] = overall["PARTIAL_ABSORB_AMT"] + absorbed overall["ABSORB_AMT"] = overall["ABSORB_AMT"] + absorbed mob["ABSORB"] = mob["ABSORB"] + 1 mob["PARTIAL_ABSORBED"] = mob["PARTIAL_ABSORBED"] + 1 mob["PARTIAL_ABSORB_AMT"] = mob["PARTIAL_ABSORB_AMT"] + absorbed mob["ABSORB_AMT"] = mob["ABSORB_AMT"] + absorbed else --add aos hits sem absorbs overall["FULL_HIT"] = overall["FULL_HIT"] + 1 overall["FULL_HIT_AMT"] = overall["FULL_HIT_AMT"] + amount mob["FULL_HIT"] = mob["FULL_HIT"] + 1 mob["FULL_HIT_AMT"] = mob["FULL_HIT_AMT"] + amount end end --record death log local actorLatestEvents = last_events_cache[targetName] if (not actorLatestEvents) then actorLatestEvents = _current_combat:CreateLastEventsTable(targetName) end local i = actorLatestEvents.n local thisEvent = actorLatestEvents[i] thisEvent[1] = true --true if this is a damage || false for healing thisEvent[2] = spellId --spellid || false if this is a battle ress line thisEvent[3] = amount --amount of damage or healing thisEvent[4] = time --parser time --current unit heal if (targetActor.arena_enemy) then --this is an arena enemy, get the heal with the unit Id local unitId = Details.arena_enemies[targetName] if (not unitId) then unitId = Details:GuessArenaEnemyUnitId(targetName) end if (unitId) then thisEvent[5] = UnitHealth(unitId) / UnitHealthMax(unitId) else thisEvent[5] = cacheAnything.arenaHealth[targetName] or 100000 end cacheAnything.arenaHealth[targetName] = thisEvent[5] else thisEvent[5] = UnitHealth(targetName) / UnitHealthMax(targetName) end thisEvent[6] = sourceName --source name thisEvent[7] = absorbed thisEvent[8] = spellType or school thisEvent[9] = false thisEvent[10] = overkill thisEvent[11] = critical thisEvent[12] = crushing i = i + 1 if (i == _amount_of_last_events + 1) then actorLatestEvents.n = 1 else actorLatestEvents.n = i end end ------------------------------------------------------------------------------------------------ --~activity time if (not sourceActor.dps_started) then --register on time machine sourceActor:GetOrChangeActivityStatus(true) if (ownerActor and not ownerActor.dps_started) then ownerActor:GetOrChangeActivityStatus(true) if (ownerActor.end_time) then ownerActor.end_time = nil else ownerActor.start_time = _tempo end end if (sourceActor.end_time) then sourceActor.end_time = nil else sourceActor.start_time = _tempo end --'player' if (sourceActor.nome == Details.playername and token ~= "SPELL_PERIODIC_DAMAGE") then if (UnitAffectingCombat("player")) then Details:SendEvent("COMBAT_PLAYER_TIMESTARTED", nil, _current_combat, sourceActor) end end end ------------------------------------------------------------------------------------------------ --firendly fire ~friendlyfire local is_friendly_fire = false if (_is_in_instance) then local npcId = npcid_cache[targetSerial] if (npcId ~= 207341) then if (bitfield_swap_cache [sourceSerial] or ownerActor and bitfield_swap_cache [ownerActor.serial]) then if (targetActor.grupo or targetOwner and targetOwner.grupo) then is_friendly_fire = true end else if (bitfield_swap_cache [targetSerial] or targetOwner and bitfield_swap_cache [targetOwner.serial]) then else --Astral Nova explosion from Astral Bomb (Spectral Invoker - Algeth'ar Academy) should get friend zone here if ((targetActor.grupo or targetOwner and targetOwner.grupo) and (sourceActor.grupo or ownerActor and ownerActor.grupo)) then is_friendly_fire = true end end end end else if ( (bitBand(targetFlags, REACTION_FRIENDLY) ~= 0 and bitBand(sourceFlags, REACTION_FRIENDLY) ~= 0) or --ajdt d' brx (raid_members_cache [targetSerial] and raid_members_cache [sourceSerial] and targetSerial:find("Player") and sourceSerial:find("Player")) --amrl ) then is_friendly_fire = true end end --double check for Astral Nova explosion (only inside AA dungeon) if (spellId == 387848 and not is_friendly_fire) then if ((targetActor.grupo or targetOwner and targetOwner.grupo) and (sourceActor.grupo or ownerActor and ownerActor.grupo)) then is_friendly_fire = true end end if (is_friendly_fire) then if (sourceActor.grupo) then --se tiver ele n�o adiciona o evento l� em cima local t = last_events_cache[targetName] if (not t) then t = _current_combat:CreateLastEventsTable(targetName) end local i = t.n local thisEvent = t [i] thisEvent[1] = true --true if this is a damage || false for healing thisEvent[2] = spellId --spellid || false if this is a battle ress line thisEvent[3] = amount --amount of damage or healing thisEvent[4] = time --parser time thisEvent[5] = UnitHealth(targetName) / UnitHealthMax(targetName) --current unit heal thisEvent[6] = sourceName --source name thisEvent[7] = absorbed thisEvent[8] = spellType or school thisEvent[9] = true thisEvent[10] = overkill i = i + 1 if (i == _amount_of_last_events+1) then t.n = 1 else t.n = i end end sourceActor.friendlyfire_total = sourceActor.friendlyfire_total + amount local friend = sourceActor.friendlyfire[targetName] or sourceActor:CreateFFTable(targetName) friend.total = friend.total + amount friend.spells[spellId] = (friend.spells[spellId] or 0) + amount ------------------------------------------------------------------------------------------------ --damage taken --target targetActor.damage_taken = targetActor.damage_taken + amount - (absorbed or 0) --adiciona o dano tomado if (not targetActor.damage_from[sourceName]) then --adiciona a pool de dano tomado de quem targetActor.damage_from[sourceName] = true end return true else _current_total[1] = _current_total[1] + amount ------------------------------------------------------------------------------------------------ --damage taken --target targetActor.damage_taken = targetActor.damage_taken + amount --adiciona o dano tomado if (not targetActor.damage_from[sourceName]) then --adiciona a pool de dano tomado de quem targetActor.damage_from[sourceName] = true end end ------------------------------------------------------------------------------------------------ --amount add --~roskash - augmentation evoker damage buff if (augmentation_cache.ebon_might[sourceSerial] or (ownerActor and augmentation_cache.ebon_might[ownerActor.serial])) then --get the serial number of the player who did the damage, in case of a pet or minion use the owner serial local thisSourceSerial = (augmentation_cache.ebon_might[sourceSerial] and sourceSerial) or ownerActor.serial ---actor buffed with ebonmight -> list of evokers whose buffed ---@type table local evokersWhoBuffedThisPlayer = augmentation_cache.ebon_might[thisSourceSerial] for i, evokerInfo in ipairs(evokersWhoBuffedThisPlayer) do ---@cast evokerInfo evokerinfo ---@type serial, actorname, controlflags local evokerSourceSerial, evokerSourceName, evokerSourceFlags, attributedGained = unpack(evokerInfo) if (evokerSourceSerial ~= thisSourceSerial) then ---@type actor local evokerActor = damage_cache[evokerSourceSerial] if (not evokerActor) then evokerActor = _current_damage_container:GetOrCreateActor(evokerSourceSerial, evokerSourceName, evokerSourceFlags, true) end if (evokerActor) then local extraSpellId = 395152 evokerActor.augmentedSpellsContainer = evokerActor.augmentedSpellsContainer or spellContainerClass:CreateSpellContainer(Details.container_type.CONTAINER_DAMAGE_CLASS) local augmentedSpell = evokerActor.augmentedSpellsContainer._ActorTable[extraSpellId] if (not augmentedSpell) then augmentedSpell = evokerActor.augmentedSpellsContainer:GetOrCreateSpell(extraSpellId, true, token) end --> calculate tier and ilevel bonuses; this values could be cached at the start of the combat --local bHasFourPieces = gearCache[evokerSourceSerial] and gearCache[evokerSourceSerial].tierAmount >= 4 local tierPieceMultiplier = 1 --bHasFourPieces and 1.08 or 1 local evokerItemLevel = gearCache[evokerSourceSerial] and gearCache[evokerSourceSerial].ilevel or 400 evokerItemLevel = max(evokerItemLevel, 400) local itemLevelMultiplier = 1 -- + ((evokerItemLevel - 400) * 0.006) local predictedAmount = 0 if (Details.zone_type == "raid") then --0x410b predictedAmount = amount * (0.06947705 * tierPieceMultiplier * itemLevelMultiplier) else predictedAmount = amount * (0.07590444 * tierPieceMultiplier * itemLevelMultiplier) end --local damageSpellName = GetSpellInfo(spellId) --print("EbonMight Cache:", Details.augmentation_cache, evokerSourceSerial, floor(amount), floor(predictedAmount), floor(predictedAmount/amount*100) .. "%", damageSpellName) evokerActor.total_extra = evokerActor.total_extra + predictedAmount augmentedSpell.total = augmentedSpell.total + predictedAmount augmentedSpell.targets[sourceName] = (augmentedSpell.targets[sourceName] or 0) + predictedAmount if (Details.debug) then DetailsParserDebugFrame:BlinkIcon(extraSpellId, 1) DetailsParserDebugFrame.AllTexts[1]:SetText("Evokers Buffed: " .. #evokersWhoBuffedThisPlayer .. " (" .. floor(predictedAmount / amount * 100) .. "%)") end end end end end if (augmentation_cache.ss[sourceSerial] or (ownerActor and augmentation_cache.ss[ownerActor.serial])) then --actor buffed with ss --get the serial number of the player who did the damage, in case of a pet or minion use the owner serial local thisSourceSerial = augmentation_cache.ss[sourceSerial] and sourceSerial or ownerActor.serial ---@type table local currentlyBuffedWithSS = augmentation_cache.ss[thisSourceSerial] for i, evokerInfo in ipairs(currentlyBuffedWithSS) do ---@cast evokerInfo evokerinfo ---@type serial, actorname, controlflags local evokerSourceSerial, evokerSourceName, evokerSourceFlags, versaBuff = unpack(evokerInfo) if (evokerSourceSerial ~= thisSourceSerial) then ---@type actor local evokerActor = damage_cache[evokerSourceSerial] if (not evokerActor) then evokerActor = _current_damage_container:GetOrCreateActor(evokerSourceSerial, evokerSourceName, evokerSourceFlags, true) end if (evokerActor) then local extraSpellId = 413984 evokerActor.augmentedSpellsContainer = evokerActor.augmentedSpellsContainer or spellContainerClass:CreateSpellContainer(Details.container_type.CONTAINER_DAMAGE_CLASS) local augmentedSpell = evokerActor.augmentedSpellsContainer._ActorTable[extraSpellId] if (not augmentedSpell) then augmentedSpell = evokerActor.augmentedSpellsContainer:GetOrCreateSpell(extraSpellId, true, token) end versaBuff = versaBuff / 100 local predictedAmount = amount * versaBuff * 0.73548755 --0x410f evokerActor.total_extra = evokerActor.total_extra + predictedAmount augmentedSpell.total = augmentedSpell.total + predictedAmount augmentedSpell.targets[sourceName] = (augmentedSpell.targets[sourceName] or 0) + predictedAmount if (Details.debug) then --DetailsParserDebugFrame:BlinkIcon(extraSpellId, 2) end end end end end if (spellId == 360828 and augmentation_cache.shield[sourceSerial]) then --shield ---actor buffed with the shield -> list of evokers whose buffed ---@type table local currentlyBuffedWithShield = augmentation_cache.shield[sourceSerial] for i, evokerInfo in ipairs(currentlyBuffedWithShield) do ---@cast evokerInfo evokerinfo ---@type serial, actorname, controlflags local evokerSourceSerial, evokerSourceName, evokerSourceFlags = unpack(evokerInfo) if (evokerSourceSerial ~= sourceSerial) then ---@type actor local evokerActor = damage_cache[evokerSourceSerial] if (not evokerActor) then evokerActor = _current_damage_container:GetOrCreateActor(evokerSourceSerial, evokerSourceName, evokerSourceFlags, true) end if (evokerActor) then local extraSpellId = 360828 evokerActor.augmentedSpellsContainer = evokerActor.augmentedSpellsContainer or spellContainerClass:CreateSpellContainer(Details.container_type.CONTAINER_DAMAGE_CLASS) local augmentedSpell = evokerActor.augmentedSpellsContainer._ActorTable[extraSpellId] if (not augmentedSpell) then augmentedSpell = evokerActor.augmentedSpellsContainer:GetOrCreateSpell(extraSpellId, true, token) end local damageSplitted = amount / #currentlyBuffedWithShield evokerActor.total_extra = evokerActor.total_extra + damageSplitted augmentedSpell.total = augmentedSpell.total + damageSplitted augmentedSpell.targets[sourceName] = (augmentedSpell.targets[sourceName] or 0) + damageSplitted if (Details.debug) then --DetailsParserDebugFrame:BlinkIcon(extraSpellId, 3) end end end end end if (spellId == 404908 and augmentation_cache.prescience[sourceSerial]) then --fate mirror ---actor buffed with prescience -> list of evokers whose buffed ---@type table local currentlyBuffedWithPrescience = augmentation_cache.prescience[sourceSerial] for i, evokerInfo in ipairs(currentlyBuffedWithPrescience) do ---@cast evokerInfo evokerinfo ---@type serial, actorname, controlflags local evokerSourceSerial, evokerSourceName, evokerSourceFlags = unpack(evokerInfo) if (evokerSourceSerial ~= sourceSerial) then ---@type actor local evokerActor = damage_cache[evokerSourceSerial] if (not evokerActor) then evokerActor = _current_damage_container:GetOrCreateActor(evokerSourceSerial, evokerSourceName, evokerSourceFlags, true) end if (evokerActor) then local extraSpellId = 404908 evokerActor.augmentedSpellsContainer = evokerActor.augmentedSpellsContainer or spellContainerClass:CreateSpellContainer(Details.container_type.CONTAINER_DAMAGE_CLASS) local augmentedSpell = evokerActor.augmentedSpellsContainer._ActorTable[extraSpellId] if (not augmentedSpell) then augmentedSpell = evokerActor.augmentedSpellsContainer:GetOrCreateSpell(extraSpellId, true, token) end local fateMirror_plus_Prescience = amount + amount * 0.58782001 evokerActor.total_extra = evokerActor.total_extra + fateMirror_plus_Prescience augmentedSpell.total = augmentedSpell.total + fateMirror_plus_Prescience augmentedSpell.targets[sourceName] = (augmentedSpell.targets[sourceName] or 0) + fateMirror_plus_Prescience if (Details.debug) then --DetailsParserDebugFrame:BlinkIcon(extraSpellId, 4) end end end end end if (spellId == 410265 and augmentation_cache.infernobless[sourceSerial]) then ---@type table local currentlyBuffedWithInfernoBless = augmentation_cache.infernobless[sourceSerial] for i, evokerInfo in ipairs(currentlyBuffedWithInfernoBless) do ---@cast evokerInfo evokerinfo ---@type serial, actorname, controlflags local evokerSourceSerial, evokerSourceName, evokerSourceFlags = unpack(evokerInfo) if (evokerSourceSerial ~= sourceSerial) then ---@type actor local evokerActor = damage_cache[evokerSourceSerial] if (not evokerActor) then evokerActor = _current_damage_container:GetOrCreateActor(evokerSourceSerial, evokerSourceName, evokerSourceFlags, true) end if (evokerActor) then local extraSpellId = 410265 evokerActor.augmentedSpellsContainer = evokerActor.augmentedSpellsContainer or spellContainerClass:CreateSpellContainer(Details.container_type.CONTAINER_DAMAGE_CLASS) local augmentedSpell = evokerActor.augmentedSpellsContainer._ActorTable[extraSpellId] if (not augmentedSpell) then augmentedSpell = evokerActor.augmentedSpellsContainer:GetOrCreateSpell(extraSpellId, true, token) end evokerActor.total_extra = evokerActor.total_extra + amount augmentedSpell.total = augmentedSpell.total + amount augmentedSpell.targets[sourceName] = (augmentedSpell.targets[sourceName] or 0) + amount if (Details.debug) then --DetailsParserDebugFrame:BlinkIcon(extraSpellId, 5) end end end end end if (spellId == 409632) then local breathTargets = augmentation_cache.breath_targets ---@type evokereonsbreathinfo[] local evokerWithEonsApplications = breathTargets[targetSerial] if (evokerWithEonsApplications) then --this table consists in a list of evokers who applied eon's breath on the target for i = 1, #evokerWithEonsApplications do ---@type evokereonsbreathinfo local evokerInfo = evokerWithEonsApplications[i] ---@type guid, actorname, controlflags, unit, unixtime, auraduration, gametime local evokerSerial, evokerName, evokerFlags, unitIDAffected, appliedTime, duration, expirationTime = unpack(evokerInfo) if (evokerSerial ~= sourceSerial) then if (detailsFramework:IsNearlyEqual(time, appliedTime + duration, 0.12)) then ---@type actor local evokerActor = damage_cache[evokerSerial] if (not evokerActor) then evokerActor = _current_damage_container:GetOrCreateActor(evokerSerial, evokerName, evokerFlags, true) end if (evokerActor) then local extraSpellId = 409632 evokerActor.augmentedSpellsContainer = evokerActor.augmentedSpellsContainer or spellContainerClass:CreateSpellContainer(Details.container_type.CONTAINER_DAMAGE_CLASS) local augmentedSpell = evokerActor.augmentedSpellsContainer._ActorTable[extraSpellId] if (not augmentedSpell) then augmentedSpell = evokerActor.augmentedSpellsContainer:GetOrCreateSpell(extraSpellId, true, token) end evokerActor.total_extra = evokerActor.total_extra + amount augmentedSpell.total = augmentedSpell.total + amount augmentedSpell.targets[sourceName] = (augmentedSpell.targets[sourceName] or 0) + amount end end end end end end --actor owner (if any) if (ownerActor) then --se for dano de um Pet ownerActor.total = ownerActor.total + amount --e adiciona o dano ao pet --add owner targets ownerActor.targets[targetName] = (ownerActor.targets[targetName] or 0) + amount ownerActor.last_event = _tempo if (RAID_TARGET_FLAGS[targetRaidFlags]) then --add the amount done for the owner ownerActor.raid_targets [targetRaidFlags] = (ownerActor.raid_targets [targetRaidFlags] or 0) + amount end end --raid targets if (RAID_TARGET_FLAGS[targetRaidFlags]) then sourceActor.raid_targets[targetRaidFlags] = (sourceActor.raid_targets[targetRaidFlags] or 0) + amount end --actor sourceActor.total = sourceActor.total + amount --actor without pets sourceActor.total_without_pet = sourceActor.total_without_pet + amount --actor targets sourceActor.targets[targetName] = (sourceActor.targets[targetName] or 0) + amount --actor spells table local spellTable = sourceActor.spells._ActorTable[spellId] if (not spellTable) then spellTable = sourceActor.spells:GetOrCreateSpell(spellId, true, token) spellTable.spellschool = spellType or school if (_current_combat.is_boss and sourceFlags and bitBand(sourceFlags, OBJECT_TYPE_ENEMY) ~= 0) then Details.spell_school_cache[spellName] = spellType or school end if (isreflected) then spellTable.isReflection = true end end --empowerment data if (empower_cache[sourceSerial]) then local empowerSpellInfo = empower_cache[sourceSerial][spellName] if (empowerSpellInfo) then if (not empowerSpellInfo.counted_healing) then --total of empowerment spellTable.e_total = (spellTable.e_total or 0) + empowerSpellInfo.empowerLevel --usado para calcular o average empowerment --total amount of empowerment spellTable.e_amt = (spellTable.e_amt or 0) + 1 --usado para calcular o average empowerment --amount of casts on each level spellTable.e_lvl = spellTable.e_lvl or {} spellTable.e_lvl[empowerSpellInfo.empowerLevel] = (spellTable.e_lvl[empowerSpellInfo.empowerLevel] or 0) + 1 empowerSpellInfo.counted_healing = true end --damage bracket spellTable.e_dmg = spellTable.e_dmg or {} spellTable.e_dmg[empowerSpellInfo.empowerLevel] = (spellTable.e_dmg[empowerSpellInfo.empowerLevel] or 0) + amount end end if (_trinket_data_cache[spellId] and _in_combat) then --~trinket ---@type trinketdata local thisData = _trinket_data_cache[spellId] if (thisData.lastCombatId == _global_combat_counter) then if (thisData.lastPlayerName == sourceName) then if (thisData.lastActivation < (time - 40)) then local cooldownTime = time - thisData.lastActivation thisData.totalCooldownTime = thisData.totalCooldownTime + cooldownTime thisData.activations = thisData.activations + 1 thisData.lastActivation = time thisData.averageTime = floor(thisData.totalCooldownTime / thisData.activations) if (cooldownTime < thisData.minTime) then thisData.minTime = cooldownTime end if (cooldownTime > thisData.maxTime) then thisData.maxTime = cooldownTime end end end else thisData.lastCombatId = _global_combat_counter thisData.lastActivation = time thisData.lastPlayerName = sourceName end if (_current_combat.trinketProcs) then ---@type table local playerTrinketData = _current_combat.trinketProcs[sourceName] or {} _current_combat.trinketProcs[sourceName] = playerTrinketData ---@type trinketprocdata local trinketData = playerTrinketData[spellId] or {cooldown = 0, total = 0} playerTrinketData[spellId] = trinketData if (trinketData.cooldown < time) then trinketData.cooldown = time + 20 trinketData.total = trinketData.total + 1 end end end return _spell_damage_func(spellTable, targetSerial, targetName, targetFlags, amount, sourceName, resisted, blocked, absorbed, critical, glacing, token, isoffhand, isreflected) end --special behavior for monk stagger debuff periodic damage --using it as a separate function to avoid the overhead of checking if the spell is stagger on every damage event function parser:MonkStagger_damage(token, time, sourceSerial, sourceName, sourceFlags, alvo_serial, alvo_name, alvo_flags, spellId, spellname, spelltype, amount, overkill, school, resisted, blocked, absorbed, critical, glacing, crushing, isoffhand) --tag the container to refresh _current_damage_container.need_refresh = true --get the monk damage object local sourceActor, ownerActor = damage_cache[sourceSerial] or damage_cache_pets[sourceSerial] or damage_cache[sourceName], damage_cache_petsOwners[sourceSerial] if (not sourceActor) then sourceActor, ownerActor, sourceName = _current_damage_container:GetOrCreateActor(sourceSerial, sourceName, sourceFlags, true) if (ownerActor) then --� um pet if (sourceSerial ~= "") then damage_cache_pets[sourceSerial] = sourceActor damage_cache_petsOwners[sourceSerial] = ownerActor end --conferir se o dono j� esta no cache if (not damage_cache[ownerActor.serial] and ownerActor.serial ~= "") then damage_cache[ownerActor.serial] = ownerActor end else if (sourceFlags) then --ter certeza que n�o � um pet if (sourceSerial ~= "") then damage_cache[sourceSerial] = sourceActor else if (sourceName:find("%[")) then --need to use the cache here damage_cache[sourceName] = sourceActor local _, _, icon = _GetSpellInfo(spellId or 1) sourceActor.spellicon = icon end end end end elseif (ownerActor) then --has (sourceActor and ownerActor) --sourceName is the name of the pet local cachedPetName = names_cache[sourceSerial] if (not cachedPetName) then --add the owner name into the sourceName sourceName = sourceName .. " <" .. ownerActor.nome .. ">" names_cache[sourceSerial] = sourceName else sourceName = cachedPetName end end --last event sourceActor.last_event = _tempo --amount amount = (amount or 0) --damage taken sourceActor.damage_taken = sourceActor.damage_taken + amount if (not sourceActor.damage_from[sourceName]) then sourceActor.damage_from[sourceName] = true end --friendly fire total sourceActor.friendlyfire_total = sourceActor.friendlyfire_total + amount --friendly fire from who local friend = sourceActor.friendlyfire[sourceName] or sourceActor:CreateFFTable(sourceName) friend.total = friend.total + amount friend.spells[spellId] = (friend.spells[spellId] or 0) + amount --record death log local t = last_events_cache[sourceName] if (not t) then t = _current_combat:CreateLastEventsTable(sourceName) end local i = t.n local this_event = t[i] if (not this_event) then return Details:Msg("Parser Event Error -> Set to 16 DeathLogs and /reload", i, _amount_of_last_events) end this_event [1] = true --true if this is a damage || false for healing this_event [2] = spellId --spellid || false if this is a battle ress line this_event [3] = amount --amount of damage or healing this_event [4] = time --parser time this_event [5] = UnitHealth(sourceName) / UnitHealthMax(sourceName) --current unit heal this_event [6] = sourceName --source name this_event [7] = absorbed this_event [8] = school this_event [9] = true --friendly fire this_event [10] = overkill i = i + 1 if (i == _amount_of_last_events+1) then t.n = 1 else t.n = i end --avoidance local avoidance = sourceActor.avoidance if (not avoidance) then sourceActor.avoidance = Details:CreateActorAvoidanceTable() avoidance = sourceActor.avoidance end local overall = avoidance.overall local mob = avoidance [sourceName] if (not mob) then --if isn't in the table, build on the fly mob = Details:CreateActorAvoidanceTable (true) avoidance [sourceName] = mob end overall ["ALL"] = overall ["ALL"] + 1 --qualtipo de hit ou absorb mob ["ALL"] = mob ["ALL"] + 1 --qualtipo de hit ou absorb if (blocked and blocked > 0) then overall ["BLOCKED_HITS"] = overall ["BLOCKED_HITS"] + 1 mob ["BLOCKED_HITS"] = mob ["BLOCKED_HITS"] + 1 overall ["BLOCKED_AMT"] = overall ["BLOCKED_AMT"] + blocked mob ["BLOCKED_AMT"] = mob ["BLOCKED_AMT"] + blocked end --absorbs status if (absorbed) then --aqui pode ser apenas absorb parcial overall ["ABSORB"] = overall ["ABSORB"] + 1 overall ["PARTIAL_ABSORBED"] = overall ["PARTIAL_ABSORBED"] + 1 overall ["PARTIAL_ABSORB_AMT"] = overall ["PARTIAL_ABSORB_AMT"] + absorbed overall ["ABSORB_AMT"] = overall ["ABSORB_AMT"] + absorbed mob ["ABSORB"] = mob ["ABSORB"] + 1 mob ["PARTIAL_ABSORBED"] = mob ["PARTIAL_ABSORBED"] + 1 mob ["PARTIAL_ABSORB_AMT"] = mob ["PARTIAL_ABSORB_AMT"] + absorbed mob ["ABSORB_AMT"] = mob ["ABSORB_AMT"] + absorbed else --add aos hits sem absorbs overall ["FULL_HIT"] = overall ["FULL_HIT"] + 1 overall ["FULL_HIT_AMT"] = overall ["FULL_HIT_AMT"] + amount mob ["FULL_HIT"] = mob ["FULL_HIT"] + 1 mob ["FULL_HIT_AMT"] = mob ["FULL_HIT_AMT"] + amount end end --special rule for LOTM function parser:LOTM_damage (token, time, who_serial, who_name, who_flags, alvo_serial, alvo_name, alvo_flags, spellid, spellname, spelltype, amount, overkill, school, resisted, blocked, absorbed, critical, glacing, crushing, isoffhand) if (absorbed) then amount = absorbed + (amount or 0) end local healingActor = healing_cache [who_serial] if (healingActor and healingActor.spells) then healingActor.total = healingActor.total - (amount or 0) local spellTable = healingActor.spells:GetSpell (183998) if (spellTable) then spellTable.anti_heal = (spellTable.anti_heal or 0) + amount end end local t = last_events_cache [who_name] if (not t) then t = _current_combat:CreateLastEventsTable (who_name) end local i = t.n local this_event = t [i] if (not this_event) then return Details:Msg("Parser Event Error -> Set to 16 DeathLogs and /reload", i, _amount_of_last_events) end this_event [1] = true --true if this is a damage || false for healing this_event [2] = spellid --spellid || false if this is a battle ress line this_event [3] = amount --amount of damage or healing this_event [4] = time --parser time this_event [5] = UnitHealth(who_name) / UnitHealthMax(who_name) --current unit heal this_event [6] = who_name --source name this_event [7] = absorbed this_event [8] = school this_event [9] = true --friendly fire this_event [10] = overkill i = i + 1 if (i == _amount_of_last_events+1) then t.n = 1 else t.n = i end local damageActor = damage_cache [who_serial] if (damageActor) then --damage taken damageActor.damage_taken = damageActor.damage_taken + amount if (not damageActor.damage_from [who_name]) then --adiciona a pool de dano tomado de quem damageActor.damage_from [who_name] = true end --friendly fire damageActor.friendlyfire_total = damageActor.friendlyfire_total + amount local friend = damageActor.friendlyfire [who_name] or damageActor:CreateFFTable (who_name) friend.total = friend.total + amount friend.spells [spellid] = (friend.spells [spellid] or 0) + amount end end --special rule of SLT function parser:SLT_damage (token, time, who_serial, who_name, who_flags, alvo_serial, alvo_name, alvo_flags, spellid, spellname, spelltype, amount, overkill, school, resisted, blocked, absorbed, critical, glacing, crushing, isoffhand) --damager local este_jogador, meu_dono = damage_cache [who_serial] or damage_cache_pets [who_serial] or damage_cache [who_name], damage_cache_petsOwners [who_serial] if (not este_jogador) then --pode ser um desconhecido ou um pet este_jogador, meu_dono, who_name = _current_damage_container:GetOrCreateActor (who_serial, who_name, who_flags, true) if (meu_dono) then --� um pet if (who_serial ~= "") then damage_cache_pets [who_serial] = este_jogador damage_cache_petsOwners [who_serial] = meu_dono end --conferir se o dono j� esta no cache if (not damage_cache [meu_dono.serial] and meu_dono.serial ~= "") then damage_cache [meu_dono.serial] = meu_dono end else if (who_flags) then --ter certeza que n�o � um pet if (who_serial ~= "") then damage_cache [who_serial] = este_jogador else if (who_name:find("%[")) then damage_cache [who_name] = este_jogador local _, _, icon = _GetSpellInfo(spellid or 1) este_jogador.spellicon = icon else --_detalhes:Msg("Unknown actor with unknown serial ", spellname, who_name) end end end end elseif (meu_dono) then --� um pet who_name = who_name .. " <" .. meu_dono.nome .. ">" end --his target local jogador_alvo, alvo_dono = damage_cache [alvo_serial] or damage_cache_pets [alvo_serial] or damage_cache [alvo_name], damage_cache_petsOwners [alvo_serial] if (not jogador_alvo) then jogador_alvo, alvo_dono, alvo_name = _current_damage_container:GetOrCreateActor (alvo_serial, alvo_name, alvo_flags, true) if (alvo_dono) then if (alvo_serial ~= "") then damage_cache_pets [alvo_serial] = jogador_alvo damage_cache_petsOwners [alvo_serial] = alvo_dono end --conferir se o dono j� esta no cache if (not damage_cache [alvo_dono.serial] and alvo_dono.serial ~= "") then damage_cache [alvo_dono.serial] = alvo_dono end else if (alvo_flags and alvo_serial ~= "") then --ter certeza que n�o � um pet damage_cache [alvo_serial] = jogador_alvo end end elseif (alvo_dono) then --� um pet alvo_name = alvo_name .. " <" .. alvo_dono.nome .. ">" end --last event este_jogador.last_event = _tempo --record death log local t = last_events_cache [alvo_name] if (not t) then t = _current_combat:CreateLastEventsTable (alvo_name) end local i = t.n local this_event = t [i] if (not this_event) then return Details:Msg("Parser Event Error -> Set to 16 DeathLogs and /reload", i, _amount_of_last_events) end this_event [1] = true --true if this is a damage || false for healing this_event [2] = spellid --spellid || false if this is a battle ress line this_event [3] = amount --amount of damage or healing this_event [4] = time --parser time this_event [5] = UnitHealth(alvo_name) / UnitHealthMax(alvo_name) --current unit heal this_event [6] = who_name --source name this_event [7] = absorbed this_event [8] = spelltype or school this_event [9] = false this_event [10] = overkill i = i + 1 if (i == _amount_of_last_events+1) then t.n = 1 else t.n = i end end --extra attacks - disabled function parser:spell_dmg_extra_attacks(token, time, who_serial, who_name, who_flags, _, _, _, _, spellid, spellName, spelltype, arg1) --print("this is even exists on ingame cleu?") local este_jogador = damage_cache [who_serial] if (not este_jogador) then local meu_dono este_jogador, meu_dono, who_name = _current_damage_container:GetOrCreateActor (who_serial, who_name, who_flags, true) if (not este_jogador) then return --just return if actor doen't exist yet end end --actor spells table local spell = este_jogador.spells._ActorTable[1] --melee damage if (not spell) then return end spell.extra["extra_attack"] = (spell.extra["extra_attack"] or 0) + 1 end --function parser:swingmissed (token, time, who_serial, who_name, who_flags, alvo_serial, alvo_name, alvo_flags, missType, isOffHand, amountMissed) function parser:swingmissed(token, time, who_serial, who_name, who_flags, alvo_serial, alvo_name, alvo_flags, alvo_flags2, missType, isOffHand, amountMissed) --, isOffHand, amountMissed, arg1 return parser:missed(token, time, who_serial, who_name, who_flags, alvo_serial, alvo_name, alvo_flags, alvo_flags2, 1, "Corpo-a-Corpo", 00000001, missType, isOffHand, amountMissed) --, isOffHand, amountMissed, arg1 end function parser:rangemissed(token, time, who_serial, who_name, who_flags, alvo_serial, alvo_name, alvo_flags, alvo_flags2, spellid, spellname, spelltype, missType, isOffHand, amountMissed) --, isOffHand, amountMissed, arg1 return parser:missed(token, time, who_serial, who_name, who_flags, alvo_serial, alvo_name, alvo_flags, alvo_flags2, 2, "Tiro-Autom�tico", 00000001, missType, isOffHand, amountMissed) --, isOffHand, amountMissed, arg1 end -- ~miss function parser:missed(token, time, sourceSerial, sourceName, sourceFlags, targetSerial, targetName, targetFlags, targetFlags2, spellId, spellName, spellType, missType, isOffHand, amountMissed) if (not targetName) then --no target name, just quit return elseif (not sourceName) then --no actor name, use spell name instead sourceName = "[*] " .. spellName sourceFlags = 0xa48 sourceSerial = "" end ------------------------------------------------------------------------------------------------ --get actors --todo tbc seems to not have misses? need further investigation --print("MISS", "|", missType, "|", isOffHand, "|", amountMissed, "|", arg1) --print(missType, who_name, spellname, amountMissed) --'misser' ---@type actor local sourceActor = damage_cache[sourceSerial] if (not sourceActor) then local ownerActor sourceActor, ownerActor, sourceName = _current_damage_container:GetOrCreateActor(sourceSerial, sourceName, sourceFlags, true) if (not sourceActor) then return --just return if actor doen't exist yet end end sourceActor.last_event = _tempo if (tanks_members_cache[targetSerial]) then --only track tanks for avoidance local targetActor = damage_cache[targetSerial] if (targetActor) then local avoidance = targetActor.avoidance if (not avoidance) then targetActor.avoidance = Details:CreateActorAvoidanceTable() avoidance = targetActor.avoidance end --not to confuse with overall data, this is the overall miss table counting avoidance for all mobs local overallMissTable = avoidance.overall[missType] if (overallMissTable) then local overall = avoidance.overall overall[missType] = overallMissTable + 1 --add to the amount of misses --avoidance for this mob only local missTableMob = avoidance[sourceName] if (not missTableMob) then --if isn't in the table, build on the fly missTableMob = Details:CreateActorAvoidanceTable(true) avoidance[sourceName] = missTableMob end missTableMob[missType] = missTableMob[missType] + 1 if (missType == "ABSORB") then --full absorb overall["ALL"] = overall["ALL"] + 1 --qualtipo de hit ou absorb overall["FULL_ABSORBED"] = overall["FULL_ABSORBED"] + 1 --amount overall["ABSORB_AMT"] = overall["ABSORB_AMT"] + (amountMissed or 0) overall["FULL_ABSORB_AMT"] = overall["FULL_ABSORB_AMT"] + (amountMissed or 0) missTableMob["ALL"] = missTableMob["ALL"] + 1 --qualtipo de hit ou absorb missTableMob["FULL_ABSORBED"] = missTableMob["FULL_ABSORBED"] + 1 --amount missTableMob["ABSORB_AMT"] = missTableMob["ABSORB_AMT"] + (amountMissed or 0) missTableMob["FULL_ABSORB_AMT"] = missTableMob["FULL_ABSORB_AMT"] + (amountMissed or 0) end end end end ------------------------------------------------------------------------------------------------ --amount add if (missType == "ABSORB") then if (token == "SWING_MISSED") then sourceActor.totalabsorbed = sourceActor.totalabsorbed + amountMissed return parser:spell_dmg("SWING_DAMAGE", time, sourceSerial, sourceName, sourceFlags, targetSerial, targetName, targetFlags, targetFlags2, amountMissed, -1, 1, nil, nil, nil, false, false, false, false) elseif (token == "RANGE_MISSED") then sourceActor.totalabsorbed = sourceActor.totalabsorbed + amountMissed return parser:spell_dmg("RANGE_DAMAGE", time, sourceSerial, sourceName, sourceFlags, targetSerial, targetName, targetFlags, targetFlags2, spellId, spellName, spellType, amountMissed, -1, 1, nil, nil, nil, false, false, false, false) else sourceActor.totalabsorbed = sourceActor.totalabsorbed + amountMissed return parser:spell_dmg(token, time, sourceSerial, sourceName, sourceFlags, targetSerial, targetName, targetFlags, targetFlags2, spellId, spellName, spellType, amountMissed, -1, 1, nil, nil, nil, false, false, false, false) end ------------------------------------------------------------------------------------------------ --spell reflection elseif (missType == "REFLECT" and reflection_auras[targetSerial]) then --~reflect --a reflect event and we have the reflecting aura data if (reflection_damage[sourceSerial] and reflection_damage[sourceSerial][spellId] and time-reflection_damage[sourceSerial][spellId].time > 3.5 and (not reflection_debuffs[sourceSerial] or (reflection_debuffs[sourceSerial] and not reflection_debuffs[sourceSerial][spellId]))) then --here we check if we have to filter old damage data --we check for two conditions --the first is to see if this is an old damage --if more than 3.5 seconds have past then we can say that it is old... but! --the second condition is to see if there is an active debuff with the same spellid --if there is one then we ignore the timer and skip this --this should be cleared afterwards somehow... don't know how... reflection_damage[sourceSerial][spellId] = nil if (next(reflection_damage[sourceSerial]) == nil) then --there should be some better way of handling this kind of filtering, any suggestion? reflection_damage[sourceSerial] = nil end end local damage = reflection_damage[sourceSerial] and reflection_damage[sourceSerial][spellId] local reflection = reflection_auras[targetSerial] if (damage) then --damage ocurred first, so we have its data local amount = reflection_damage[sourceSerial][spellId].amount local isreflected = spellId --which spell was reflected targetSerial = reflection.who_serial targetName = reflection.who_name targetFlags = reflection.who_flags spellId = reflection.spellid spellName = reflection.spellname spellType = reflection.spelltype --crediting the source of the aura that caused the reflection --also saying that the damage came from the aura that reflected the spell reflection_damage[sourceSerial][spellId] = nil if next(reflection_damage[sourceSerial]) == nil then --this is so bad at clearing, there should be a better way of handling this reflection_damage[sourceSerial] = nil end return parser:spell_dmg(token, time, targetSerial, targetName, targetFlags, sourceSerial, sourceName, sourceFlags, nil, spellId, spellName, spellType, amount, -1, nil, nil, nil, nil, false, false, false, false, isreflected) else --saving information about this reflect because it occurred before the damage event reflection_events[sourceSerial] = reflection_events[sourceSerial] or {} reflection_events[sourceSerial][spellId] = reflection reflection_events[sourceSerial][spellId].time = time end else --colocando aqui apenas pois ele confere o override dentro do damage if (is_using_spellId_override) then spellId = override_spellId[spellId] or spellId end --actor spells table local spell = sourceActor.spells._ActorTable[spellId] if (not spell) then spell = sourceActor.spells:GetOrCreateSpell(spellId, true, token) spell.spellschool = spellType if (_current_combat.is_boss and sourceFlags and bitBand(sourceFlags, OBJECT_TYPE_ENEMY) ~= 0) then Details.spell_school_cache[spellName] = spellType end end return _spell_damageMiss_func(spell, targetSerial, targetName, targetFlags, sourceName, missType) end end ----------------------------------------------------------------------------------------------------------------------------------------- --SPELL_EMPOWER ----------------------------------------------------------------------------------------------------------------------------------------- function parser:spell_empower(token, time, sourceSerial, sourceName, sourceFlags, targetSerial, targetName, targetFlags, targetRaidFlags, spellId, spellName, spellSchool, empowerLevel) --empowerLevel only exists on _END and _INTERRUPT if (token == "SPELL_EMPOWER_START" or token == "SPELL_EMPOWER_INTERRUPT") then return end if (not empowerLevel) then return end --early checks if (not sourceSerial or not sourceName or not sourceFlags) then return end --source damager, should this only register for Players? if (sourceFlags and bitBand(sourceFlags, OBJECT_TYPE_PLAYER) == 0) then return end local sourceObject = damage_cache[sourceSerial] or damage_cache[sourceName] if (not sourceObject) then sourceObject = _current_damage_container:GetOrCreateActor(sourceSerial, sourceName, sourceFlags, true) end if (not sourceObject) then return end --add to the amount of spell casts --nop, SPELL_EMPOWER_END does not trigger when Tip The Scales is enabled --perhaps UNIT_SPELLCAST_SUCCEEDED checking if tip the scales buff is enabled and checking if the spell is an empowered spell --parser:spellcast(token, time, sourceSerial, sourceName, sourceFlags, targetSerial, targetName, targetFlags, targetRaidFlags, spellId, spellName) empower_cache[sourceSerial] = empower_cache[sourceSerial] or {} local empowerTable = { spellName = spellName, empowerLevel = empowerLevel, time = time, counted_healing = false, counted_damage = false, } empower_cache[sourceSerial][spellName] = empowerTable end --parser.spell_empower --10/30 15:32:11.515 SPELL_EMPOWER_START,Player-4184-00242A35,"Isodrak-Valdrakken",0x514,0x0,Player-4184-00242A35,"Isodrak-Valdrakken",0x514,0x0,382266,"Fire Breath",0x4 --10/30 15:32:12.433 SPELL_EMPOWER_END,Player-4184-00242A35,"Isodrak-Valdrakken",0x514,0x0,0000000000000000,nil,0x80000000,0x80000000,382266,"Fire Breath",0x4,1 --10/30 15:33:45.970 SPELL_EMPOWER_INTERRUPT,Player-4184-00218B4F,"Minng-Valdrakken",0x512,0x0,0000000000000000,nil,0x80000000,0x80000000,382266,"Fire Breath",0x4,1 --10/30 15:34:47.249 SPELL_EMPOWER_START,Player-4184-0048EE5B,"Nezaland-Valdrakken",0x514,0x0,Player-4184-0048EE5B,"Nezaland-Valdrakken",0x514,0x0,382266,"Fire Breath",0x4 --357209 damage spell is different from the spell cast ----------------------------------------------------------------------------------------------------------------------------------------- --SUMMON serach key: ~summon | ----------------------------------------------------------------------------------------------------------------------------------------- function parser:summon(token, time, sourceSerial, sourceName, sourceFlags, petGuid, petName, petFlags, petRaidFlags, summonSpellId, summonSpellName) if (not sourceName) then sourceName = "[*] " .. summonSpellName end local npcId = tonumber(select(6, strsplit("-", petGuid)) or 0) if (Details222.Debug.DebugPets) then end --differenciate army and apoc pets for DK if (summonSpellId == 42651) then --army of the dead dk_pets_cache.army[petGuid] = sourceName end --If fire elemental totem on Wrath, then ignore the summon of the fire elemental totem itself and instead create the Greater Fire Elemental early. --Greater Fire Elemental and Fire Elemental Totem have the same serial besides the npc id. --There are cases where the Greater Fire Elemental could attack and the SWING_DAMAGE event happens before the spell_summon for it. Same frame. --[[12/14 21:14:44.545 SPELL_SUMMON,Player-4384-03852552,"Toekruh-Mankrik",0x512,0x0,Creature-0-4391-615-3107-15439-00001A8313,"Fire Elemental Totem",0xa28,0x0,2894,"Fire Elemental Totem",0x1 12/14 21:14:44.545 SWING_DAMAGE,Creature-0-4391-615-3107-15438-00001A8313,"Greater Fire Elemental",0x2112,0x0,Creature-0-4391-615-3107-28860-00001A8258,"Sartharion",0xa48,0x0,Creature-0-4391-615-3107-15438-00001A8313,Creature-0-4391-615-3107-15439-00001A8313,4274,4274,0,0,0,-1,0,0,0,3261.68,530.04,155,3.3324,208,188,187,-1,4,0,0,0,nil,nil,nil 12/14 21:14:44.545 SPELL_CAST_SUCCESS,Creature-0-4391-615-3107-15438-00001A8313,"Greater Fire Elemental",0x2112,0x0,Creature-0-4391-615-3107-28860-00001A8258,"Sartharion",0xa48,0x0,57984,"Fire Blast",0x4,Creature-0-4391-615-3107-15438-00001A8313,Creature-0-4391-615-3107-15439-00001A8313,4274,4274,0,0,0,-1,0,0,0,3261.68,530.04,155,3.3324,208 12/14 21:14:44.545 SPELL_CAST_SUCCESS,Creature-0-4391-615-3107-15439-00001A8313,"Fire Elemental Totem",0x2112,0x0,0000000000000000,nil,0x80000000,0x80000000,32982,"Fire Elemental Totem",0x1,Creature-0-4391-615-3107-15439-00001A8313,Player-4384-03852552,3888,3888,0,0,0,-1,0,0,0,3257.01,531.82,155,5.1330,208 12/14 21:14:44.545 SPELL_SUMMON,Creature-0-4391-615-3107-15439-00001A8313,"Fire Elemental Totem",0x2112,0x0,Creature-0-4391-615-3107-15438-00001A8313,"Greater Fire Elemental",0x2112,0x0,32982,"Fire Elemental Totem",0x1 ]] if (isWOTLK or isCATA) then if (npcId == 15439) then petContainer.AddPet(petGuid:gsub("%-15439%-", "%-15438%-"), "Greater Fire Elemental", petFlags, sourceSerial, sourceName, sourceFlags, summonSpellId) elseif (npcId == 15438) then return end end --send the summonSpellId to spellcache in order to identify if the pet is from an item, for instance: a trinket local newPetName = Details222.Pets.GetPetNameFromCustomSpells(petName, summonSpellId, npcId) if (newPetName ~= petName) then --print("Details! debug trinket summon| player:", sourceName, "| old pet name:", petName, "| new pet name:", newPetName, "| spellId:", summonSpellId) petName = newPetName end --if the caster is inside the petCache, it means this is a pet summoning another pet ---@type petdata local petTable = petCache[sourceSerial] if (petTable) then sourceName, sourceSerial, sourceFlags = petTable.ownerName, petTable.ownerGuid, petTable.ownerFlags end petTable = petCache[petGuid] if (petTable) then sourceName, sourceSerial, sourceFlags = petTable[1], petTable[2], petTable[3] end petContainer.AddPet(petGuid, petName, petFlags, sourceSerial, sourceName, sourceFlags, summonSpellId) end ----------------------------------------------------------------------------------------------------------------------------------------- --HEALING serach key: ~healing ~heal | ----------------------------------------------------------------------------------------------------------------------------------------- local ignored_shields = { [142862] = true, -- Ancient Barrier (Malkorok) [114556] = true, -- Purgatory (DK) [115069] = true, -- Stance of the Sturdy Ox (Monk) [20711] = true, -- Spirit of Redemption (Priest) [184553] = true, --Soul Capacitor } local ignored_overheal = { --during refresh, some shield does not replace the old value for the new one [47753] = true, -- Divine Aegis [86273] = true, -- Illuminated Healing [114908] = true, --Spirit Shell [152118] = true, --Clarity of Will } function parser:heal_denied(token, time, sourceSerial, sourceName, sourceFlags, targetSerial, targetName, targetFlags, targetFlags2, spellIdAbsorb, spellNameAbsorb, spellSchoolAbsorb, serialHealer, nameHealer, flagsHealer, flags2Healer, spellIdHeal, spellNameHeal, typeHeal, amountDenied) if (not _in_combat) then return end --check invalid serial against pets if (sourceSerial == "") then if (sourceFlags and bitBand(sourceFlags, OBJECT_TYPE_PETS) ~= 0) then --is pet return end end --no name, use spellname if (not sourceName) then sourceName = "[*] " .. (spellNameHeal or "--unknown spell--") end --no target, just ignore if (not targetName) then return end --if no spellid if (not spellIdAbsorb) then spellIdAbsorb = 1 spellNameAbsorb = "unknown" spellSchoolAbsorb = 1 end if (is_using_spellId_override) then spellIdAbsorb = override_spellId[spellIdAbsorb] or spellIdAbsorb spellIdHeal = override_spellId[spellIdHeal] or spellIdHeal end --source actor ---@type actor local sourceActor, ownerActor = healing_cache[sourceSerial] if (not sourceActor) then sourceActor, ownerActor, sourceName = _current_heal_container:GetOrCreateActor(sourceSerial, sourceName, sourceFlags, true) if (not ownerActor and sourceFlags and sourceSerial ~= "") then --add to cache if isn't a pet healing_cache[sourceSerial] = sourceActor end end local targetActor, targetOwner = healing_cache[targetSerial] if (not targetActor) then targetActor, targetOwner, targetName = _current_heal_container:GetOrCreateActor(targetSerial, targetName, targetFlags, true) if (not targetOwner and targetFlags and targetSerial ~= "") then healing_cache[targetSerial] = targetActor end end sourceActor.last_event = _tempo ------------------------------------------------ sourceActor.totaldenied = sourceActor.totaldenied + amountDenied --actor spells table local spell = sourceActor.spells._ActorTable[spellIdAbsorb] if (not spell) then spell = sourceActor.spells:GetOrCreateSpell(spellIdAbsorb, true, token) if (_current_combat.is_boss and sourceFlags and bitBand(sourceFlags, OBJECT_TYPE_ENEMY) ~= 0) then Details.spell_school_cache[spellNameAbsorb] = spellSchoolAbsorb or 1 end end return _spell_heal_func(spell, targetSerial, targetName, targetFlags, amountDenied, spellIdHeal, token, nameHealer) --, overhealing end function parser:heal_absorb(token, time, sourceSerial, sourceName, sourceFlags, targetSerial, targetName, targetFlags, targetFlags2, spellId, spellName, spellSchool, shieldOwnerSerial, shieldOwnerName, shieldOwnerFlags, shieldOwnerFlags2, shieldSpellId, shieldName, shieldType, amount) if (isCATA or isWOTLK or isERA) then if (not amount) then --melee shieldOwnerSerial, shieldOwnerName, shieldOwnerFlags, shieldOwnerFlags2, shieldSpellId, shieldName, shieldType, amount = spellId, spellName, spellSchool, shieldOwnerSerial, shieldOwnerName, shieldOwnerFlags, shieldOwnerFlags2, shieldSpellId end return parser:heal(token, time, shieldOwnerSerial, shieldOwnerName, shieldOwnerFlags, targetSerial, targetName, targetFlags, targetFlags2, shieldSpellId, shieldName, shieldType, amount, 0, 0, nil, true) else --retail if (type(shieldName) == "boolean") then shieldOwnerSerial, shieldOwnerName, shieldOwnerFlags, shieldOwnerFlags2, shieldSpellId, shieldName, shieldType, amount = spellId, spellName, spellSchool, shieldOwnerSerial, shieldOwnerName, shieldOwnerFlags, shieldOwnerFlags2, shieldSpellId end end if (ignored_shields[shieldSpellId]) then return elseif (shieldSpellId == 110913) then --dark bargain local max_health = UnitHealthMax(shieldOwnerName) if ((amount or 0) > (max_health or 1) * 4) then return end end --diminuir o escudo nas tabelas de ShieldCache if (_use_shield_overheal) then local shieldsOnTarget = shield_cache[targetName] if (shieldsOnTarget) then local shieldsBySpellId = shieldsOnTarget[shieldSpellId] if (shieldsBySpellId) then local shieldAmount = shieldsBySpellId[shieldOwnerName] if (shieldAmount) then shieldsBySpellId[shieldOwnerName] = shieldAmount - amount end end end shield_spellid_cache[shieldSpellId] = true end return parser:heal(token, time, shieldOwnerSerial, shieldOwnerName, shieldOwnerFlags, targetSerial, targetName, targetFlags, targetFlags2, shieldSpellId, shieldName, shieldType, amount, 0, 0, nil, true) end function parser:heal(token, time, sourceSerial, sourceName, sourceFlags, targetSerial, targetName, targetFlags, targetFlags2, spellId, spellName, spellType, amount, overHealing, absorbed, critical, bIsShield) --only capture heal if is in combat if (not _in_combat) then if (not _in_resting_zone) then --and not in a resting zone return end end --check invalid serial against pets if (sourceSerial == "") then if (sourceFlags and bitBand(sourceFlags, OBJECT_TYPE_PETS) ~= 0) then --it's a pet without a serial number, ignore return end end --no target, no heal if (not targetName) then return end --check for banned spells if (banned_healing_spells[spellId]) then return end --> npcId check for ignored npcs --> get the npcId from the cache, if it's not there then get it from the serial and add it to the cache local npcId = npcid_cache[targetSerial] --target npc if (not npcId) then --this string manipulation is running on every event npcId = tonumber(select(6, strsplit("-", targetSerial)) or 0) npcid_cache[targetSerial] = npcId end if (ignored_npcids[npcId]) then return end if (not sourceName) then --no actor name, use spell name instead sourceName = names_cache[spellName] if (not sourceName) then sourceName = "[*] " .. spellName --cache the string manipulation names_cache[spellName] = sourceName end end if (spellId == 98021) then --spirit link toten return parser:SLT_healing(token, time, sourceSerial, sourceName, sourceFlags, targetSerial, targetName, targetFlags, spellId, spellName, spellType, amount, overHealing, absorbed, critical, bIsShield) end if (is_using_spellId_override) then spellId = override_spellId[spellId] or spellId end --sanguine ichor mythic dungeon affix (heal enemies) if (spellId == SPELLID_SANGUINE_HEAL) then sourceName = Details.SanguineHealActorName sourceFlags = 0x518 sourceSerial = "Creature-0-3134-2289-28065-" .. SPELLID_SANGUINE_HEAL .. "-000164C698" end local effectiveHeal = absorbed if (bIsShield) then --shield has the correct amount of 'healing done' effectiveHeal = amount else effectiveHeal = effectiveHeal + amount - overHealing end if (isWOTLK or isCATA) then --earth shield if (spellId == SPELLID_SHAMAN_EARTHSHIELD_HEAL) then --get the information of who placed the buff into this actor local sourceData = TBC_EarthShieldCache[sourceName] if (sourceData) then sourceSerial, sourceName, sourceFlags = unpack(sourceData) end --prayer of mending elseif (spellId == SPELLID_PRIEST_POM_HEAL) then local sourceData = TBC_PrayerOfMendingCache[sourceName] if (sourceData) then sourceSerial, sourceName, sourceFlags = unpack(sourceData) TBC_PrayerOfMendingCache[sourceName] = nil end elseif (spellId == 27163) then --Judgement of Light (paladin), reattribute healing to the person getting healed. Stops 'sniping' the JoL to parse --Removed old version 10/27/23 Flamanis sourceSerial, sourceName, sourceFlags = targetSerial, targetName, targetFlags end end _current_heal_container.need_refresh = true ------------------------------------------------------------------------------------------------ --get actors --healer local sourceActor, ownerActor = healing_cache[sourceSerial], nil if (not sourceActor) then sourceActor, ownerActor, sourceName = _current_heal_container:GetOrCreateActor(sourceSerial, sourceName, sourceFlags, true) if (not ownerActor and sourceFlags and sourceSerial ~= "") then --if isn't a pet, add to the cache healing_cache[sourceSerial] = sourceActor end end --target local targetActor, targetOwner = healing_cache[targetSerial], nil if (not targetActor) then targetActor, targetOwner, targetName = _current_heal_container:GetOrCreateActor(targetSerial, targetName, targetFlags, true) if (not targetOwner and targetFlags and targetSerial ~= "") then --if isn't a pet, add to the cache healing_cache[targetSerial] = targetActor end end sourceActor.last_event = _tempo ------------------------------------------------------------------------------------------------ --an enemy healing enemy or an player actor healing a enemy if (spellId == SPELLID_SANGUINE_HEAL) then --sanguine ichor (heal enemies) sourceActor.grupo = true elseif (bitBand(targetFlags, REACTION_FRIENDLY) == 0 and not Details.is_in_arena and not Details.is_in_battleground) then if (not sourceActor.heal_enemy[spellId]) then sourceActor.heal_enemy[spellId] = effectiveHeal else sourceActor.heal_enemy[spellId] = sourceActor.heal_enemy[spellId] + effectiveHeal end sourceActor.heal_enemy_amt = sourceActor.heal_enemy_amt + effectiveHeal return true end --check if this is a mythic dungeon run if (false) then if (Details222.MythicPlus.IsMythicPlus()) then if (bitBand(targetFlags, REACTION_FRIENDLY) == 0 and bitBand(sourceFlags, REACTION_FRIENDLY) == 0) then --this is a enemy healing another enemy --create or get an actor which the actor name is the spell name local actorName = GetSpellInfo(spellId) local spellActor = _current_heal_container:GetOrCreateActor(spellId, actorName, 0x514, true) spellActor.grupo = true spellActor.last_event = _tempo spellActor.total = spellActor.total + effectiveHeal spellActor.spellicon = GetSpellTexture(spellId) spellActor.customColor = {0.5, 0.953, 0.082} end end end ------------------------------------------------------------------------------------------------ --group checks if (sourceActor.grupo and not targetActor.arena_enemy) then _current_gtotal[2] = _current_gtotal[2] + effectiveHeal end if (targetActor.grupo) then local t = last_events_cache[targetName] if (not t) then t = _current_combat:CreateLastEventsTable(targetName) end local i = t.n --consolidate if the spellId is the same and the time is the same as well local previousEvent = t[i-1] if (previousEvent and previousEvent[1] == false and previousEvent[2] == spellId and floor(previousEvent[4]) == floor(time)) then previousEvent[3] = previousEvent[3] + amount if (absorbed) then previousEvent[8] = (previousEvent[8] or 0) + absorbed end previousEvent[7] = previousEvent[7] or bIsShield previousEvent[1] = false --true if this is a damage || false for healing previousEvent[5] = UnitHealth(targetName) / UnitHealthMax(targetName) previousEvent[11] = (previousEvent[11] or 0) + 1 --attempt to perform arithmetic on a boolean value (during battlegrounds - fix 02 Nov 2023) else local thisEvent = t[i] thisEvent[1] = false --true if this is a damage || false for healing thisEvent[2] = spellId --spellid || false if this is a battle ress line thisEvent[3] = amount --amount of damage or healing thisEvent[4] = time --parser time thisEvent[11] = nil --current unit heal if (targetActor.arena_enemy) then --this is an arena enemy, get the heal with the unit Id local unitId = Details.arena_enemies[targetName] if (not unitId) then unitId = Details:GuessArenaEnemyUnitId(targetName) end if (unitId) then thisEvent[5] = UnitHealth(unitId) / UnitHealthMax(unitId) else thisEvent[5] = 0 end else thisEvent[5] = UnitHealth(targetName) / UnitHealthMax(targetName) end thisEvent[6] = sourceName thisEvent[7] = bIsShield thisEvent[8] = absorbed i = i + 1 if (i == _amount_of_last_events + 1) then t.n = 1 else t.n = i end end end ------------------------------------------------------------------------------------------------ --~activity time if (not sourceActor.iniciar_hps) then sourceActor:GetOrChangeActivityStatus(true) if (ownerActor and not ownerActor.iniciar_hps) then ownerActor:GetOrChangeActivityStatus(true) if (ownerActor.end_time) then ownerActor.end_time = nil else ownerActor.start_time = _tempo end end if (sourceActor.end_time) then --o combate terminou, reabrir o tempo sourceActor.end_time = nil else sourceActor.start_time = _tempo end end ------------------------------------------------------------------------------------------------ --add amount --actor target if (effectiveHeal > 0) then --combat total _current_total[2] = _current_total[2] + effectiveHeal --actor healing amount sourceActor.total = sourceActor.total + effectiveHeal sourceActor.total_without_pet = sourceActor.total_without_pet + effectiveHeal --healing taken targetActor.healing_taken = targetActor.healing_taken + effectiveHeal --adiciona o dano tomado if (not targetActor.healing_from[sourceName]) then --adiciona a pool de dano tomado de quem targetActor.healing_from[sourceName] = true end if (bIsShield) then sourceActor.totalabsorb = sourceActor.totalabsorb + effectiveHeal sourceActor.targets_absorbs[targetName] = (sourceActor.targets_absorbs[targetName] or 0) + effectiveHeal end --pet if (ownerActor) then ownerActor.total = ownerActor.total + effectiveHeal --heal do pet ownerActor.targets[targetName] = (ownerActor.targets[targetName] or 0) + effectiveHeal end --target amount sourceActor.targets[targetName] = (sourceActor.targets[targetName] or 0) + effectiveHeal end if (ownerActor) then ownerActor.last_event = _tempo end if (overHealing > 0) then sourceActor.totalover = sourceActor.totalover + overHealing sourceActor.targets_overheal[targetName] = (sourceActor.targets_overheal[targetName] or 0) + overHealing if (ownerActor) then ownerActor.totalover = ownerActor.totalover + overHealing end end --actor spells table local spellTable = sourceActor.spells._ActorTable[spellId] if (not spellTable) then spellTable = sourceActor.spells:GetOrCreateSpell(spellId, true, token) if (bIsShield) then spellTable.is_shield = true end spellTable.spellschool = spellType if (_current_combat.is_boss and sourceFlags and bitBand(sourceFlags, OBJECT_TYPE_ENEMY) ~= 0) then Details.spell_school_cache[spellName] = spellType end end --empowerment data if (empower_cache[sourceSerial]) then local empowerSpellInfo = empower_cache[sourceSerial][spellName] if (empowerSpellInfo) then if (not empowerSpellInfo.counted_damage) then --total of empowerment spellTable.e_total = (spellTable.e_total or 0) + empowerSpellInfo.empowerLevel --used to calculate the average empowerment --total amount of empowerment spellTable.e_amt = (spellTable.e_amt or 0) + 1 --used to calculate the average empowerment --amount of casts on each level spellTable.e_lvl = spellTable.e_lvl or {} spellTable.e_lvl[empowerSpellInfo.empowerLevel] = (spellTable.e_lvl[empowerSpellInfo.empowerLevel] or 0) + 1 empowerSpellInfo.counted_damage = true end --healing bracket spellTable.e_heal = spellTable.e_heal or {} spellTable.e_heal[empowerSpellInfo.empowerLevel] = (spellTable.e_heal[empowerSpellInfo.empowerLevel] or 0) + effectiveHeal end end if (bIsShield) then return _spell_heal_func(spellTable, targetSerial, targetName, targetFlags, effectiveHeal, sourceName, 0, nil, overHealing, true) else return _spell_heal_func(spellTable, targetSerial, targetName, targetFlags, effectiveHeal, sourceName, absorbed, critical, overHealing) end end function parser:SLT_healing (token, time, who_serial, who_name, who_flags, alvo_serial, alvo_name, alvo_flags, spellid, spellname, spelltype, amount, overhealing, absorbed, critical, is_shield) --get actors local este_jogador, meu_dono = healing_cache [who_serial] if (not este_jogador) then --pode ser um desconhecido ou um pet este_jogador, meu_dono, who_name = _current_heal_container:GetOrCreateActor (who_serial, who_name, who_flags, true) if (not meu_dono and who_flags and who_serial ~= "") then --se n�o for um pet, add no cache healing_cache [who_serial] = este_jogador end end local jogador_alvo, alvo_dono = healing_cache [alvo_serial] if (not jogador_alvo) then jogador_alvo, alvo_dono, alvo_name = _current_heal_container:GetOrCreateActor (alvo_serial, alvo_name, alvo_flags, true) if (not alvo_dono and alvo_flags and alvo_serial ~= "") then healing_cache [alvo_serial] = jogador_alvo end end este_jogador.last_event = _tempo local t = last_events_cache [alvo_name] if (not t) then t = _current_combat:CreateLastEventsTable (alvo_name) end local i = t.n local this_event = t [i] this_event [1] = false --true if this is a damage || false for healing this_event [2] = spellid --spellid || false if this is a battle ress line this_event [3] = amount --amount of damage or healing this_event [4] = time --parser time this_event [5] = UnitHealth(alvo_name) / UnitHealthMax(alvo_name) --current unit heal this_event [6] = who_name --source name this_event [7] = is_shield this_event [8] = absorbed i = i + 1 if (i == _amount_of_last_events+1) then t.n = 1 else t.n = i end local spell = este_jogador.spells._ActorTable [spellid] if (not spell) then spell = este_jogador.spells:GetOrCreateSpell(spellid, true, token) spell.neutral = true end return _spell_heal_func (spell, alvo_serial, alvo_name, alvo_flags, absorbed + amount - overhealing, who_name, absorbed, critical, overhealing, nil) end ----------------------------------------------------------------------------------------------------------------------------------------- --BUFFS & DEBUFFS search key: ~buff ~aura ~shield | ----------------------------------------------------------------------------------------------------------------------------------------- function parser:buff(token, time, sourceSerial, sourceName, sourceFlags, targetSerial, targetName, targetFlags, targetFlags2, spellId, spellName, spellschool, auraType, amount, arg1, arg2, arg3) if (ignoredWorldAuras[spellId]) then return end --not yet well know about unnamed buff casters if (not targetName) then targetName = "[*] Unknown shield target" elseif (not sourceName) then sourceName = names_cache[spellName] if (not sourceName) then sourceName = "[*] " .. spellName names_cache[spellName] = sourceName end sourceFlags = 0xa48 sourceSerial = "" end if (augmentation_aura_list[spellId]) then Details222.SpecHelpers[1473].BuffIn(token, time, sourceSerial, sourceName, sourceFlags, targetSerial, targetName, targetFlags, targetFlags2, spellId, spellName, spellschool, auraType, amount) end ------------------------------------------------------------------------------------------------ --spell reflection if (reflection_spellid[spellId]) then --~reflect --this is a spell reflect aura --we save the info on who received this aura and from whom --this will be used to credit this spell as the one doing the damage reflection_auras[targetSerial] = { who_serial = sourceSerial, who_name = sourceName, who_flags = sourceFlags, spellid = spellId, spellname = spellName, spelltype = spellschool, } end if (auraType == "BUFF") then if (LIB_OPEN_RAID_BLOODLUST[spellId]) then --~bloodlust if (Details.playername == targetName) then _current_combat.bloodlust = _current_combat.bloodlust or {} _current_combat.bloodlust[#_current_combat.bloodlust+1] = _current_combat:GetCombatTime() end end if (override_aura_spellid[spellId] and sourceName == Details.playername) then local auraName, texture, count, auraType, duration, expirationTime, sourceUnit, isStealable, nameplateShowPersonal, spellId, canApplyAura, isBossAura, isFromPlayerOrPlayerPet, nameplateShowAll, timeMod, v1, v2, v3, v4, v5 = Details:FindBuffCastedByUnitName(sourceName, spellId, sourceName) if (auraName) then local overrideTable = override_aura_spellid[spellId] if (overrideTable.CanOverride(auraName, texture, count, auraType, duration, expirationTime, sourceUnit, isStealable, nameplateShowPersonal, spellId, canApplyAura, isBossAura, isFromPlayerOrPlayerPet, nameplateShowAll, timeMod, v1, v2, v3, v4, v5)) then --proc parser:add_buff_uptime(token, time, sourceSerial, sourceName, sourceFlags, targetSerial, targetName, targetFlags, targetFlags2, overrideTable.NewSpellId, spellName, "BUFF_UPTIME_IN") local bAddToTarget, bOverrideTime = false, true parser:add_buff_uptime(token, time+duration, sourceSerial, sourceName, sourceFlags, targetSerial, targetName, targetFlags, targetFlags2, overrideTable.NewSpellId, spellName, "BUFF_UPTIME_OUT", bAddToTarget, bOverrideTime) --standard buff (not discounting the time with proc, this give the player how much time the buff was active overall) parser:add_buff_uptime(token, time, sourceSerial, sourceName, sourceFlags, targetSerial, targetName, targetFlags, targetFlags2, spellId, spellName, "BUFF_UPTIME_IN") return end end elseif (spellId == 388007 or spellId == 388011) then --buff: bleesing of the summer and winter cacheAnything.paladin_vivaldi_blessings[targetSerial] = {sourceSerial, sourceName, sourceFlags} elseif (spellId == 27827) then --spirit of redemption (holy ~priest) ~spirit local deathLog = last_events_cache[targetName] if (not deathLog) then deathLog = _current_combat:CreateLastEventsTable(targetName) end local i = deathLog.n local thisEvent = deathLog[i] if (not thisEvent) then return Details:Msg("Parser Event Error -> Set to 16 DeathLogs and /reload", i, _amount_of_last_events) end thisEvent[1] = 5 --5 = buff aplication thisEvent[2] = spellId --spellid thisEvent[3] = 1 thisEvent[4] = time --parser time thisEvent[5] = UnitHealth(targetName) / UnitHealthMax(targetName) --current unit heal thisEvent[6] = sourceName --source name thisEvent[7] = false thisEvent[8] = false thisEvent[9] = false thisEvent[10] = false i = i + 1 if (i == _amount_of_last_events+1) then deathLog.n = 1 else deathLog.n = i end C_Timer.After(0.05, function() --25/12/2022: enabled the delay to wait the combatlog dump damage events which will happen after the buff is applied parser:dead("UNIT_DIED", time, sourceSerial, sourceName, sourceFlags, targetSerial, targetName, targetFlags) ignore_death_cache [sourceName] = true end) return elseif (spellId == SPELLID_MONK_GUARD) then --BfA monk talent monk_guard_talent [sourceSerial] = amount elseif (spellId == 272790 and cacheAnything.track_hunter_frenzy) then --hunter pet Frenzy quick fix for show the Frenzy uptime if (pet_frenzy_cache[sourceName]) then if (detailsFramework:IsNearlyEqual(pet_frenzy_cache[sourceName], time, 0.2)) then return end end if (not Details.in_combat) then C_Timer.After(1, function() if (Details.in_combat) then if (pet_frenzy_cache[sourceName]) then if (detailsFramework:IsNearlyEqual(pet_frenzy_cache[sourceName], time, 0.2)) then return end end return parser:add_buff_uptime(token, time, sourceSerial, sourceName, sourceFlags, sourceSerial, sourceName, sourceFlags, 0x0, spellId, spellName, "BUFF_UPTIME_IN") end end) return end pet_frenzy_cache[sourceName] = time --when the buffIN happened return parser:add_buff_uptime(token, time, sourceSerial, sourceName, sourceFlags, sourceSerial, sourceName, sourceFlags, 0x0, spellId, spellName, "BUFF_UPTIME_IN") end if (isWOTLK or isCATA) then if (SHAMAN_EARTHSHIELD_BUFF[spellId]) then TBC_EarthShieldCache[targetName] = {sourceSerial, sourceName, sourceFlags} elseif (spellId == SPELLID_PRIEST_POM_BUFF) then TBC_PrayerOfMendingCache [targetName] = {sourceSerial, sourceName, sourceFlags} end end if (buffs_on_target[spellId]) then parser:add_buff_uptime(token, time, sourceSerial, sourceName, sourceFlags, targetSerial, targetName, targetFlags, targetFlags2, spellId, spellName, "BUFF_UPTIME_IN", true) elseif (sourceName == targetName and raid_members_cache[sourceSerial] and _in_combat) then --player itself parser:add_buff_uptime(token, time, sourceSerial, sourceName, sourceFlags, targetSerial, targetName, targetFlags, targetFlags2, spellId, spellName, "BUFF_UPTIME_IN") elseif (petCache[sourceSerial] and petCache[sourceSerial][2] == targetSerial) then --pet putting an aura on its owner parser:add_buff_uptime(token, time, targetSerial, targetName, targetFlags, targetSerial, targetName, targetFlags, targetFlags2, spellId, spellName, "BUFF_UPTIME_IN") elseif (buffs_to_other_players[spellId]) then --e.g. power infusion parser:add_buff_uptime(token, time, targetSerial, targetName, targetFlags, targetSerial, targetName, targetFlags, targetFlags2, spellId, spellName, "BUFF_UPTIME_IN") end --healing done absorbs if (_use_shield_overheal) then if (shield_spellid_cache[spellId] and amount) then if (not shield_cache[targetName]) then shield_cache[targetName] = {} shield_cache[targetName][spellId] = {} shield_cache[targetName][spellId][sourceName] = amount elseif (not shield_cache[targetName][spellId]) then shield_cache[targetName][spellId] = {} shield_cache[targetName][spellId][sourceName] = amount else shield_cache[targetName][spellId][sourceName] = amount end end end ------------------------------------------------------------------------------------------------ --recording debuffs applied by player elseif (auraType == "DEBUFF") then ------------------------------------------------------------------------------------------------ --spell reflection if (sourceSerial == targetSerial and not reflection_ignore[spellId]) then --self-inflicted debuff that could've been reflected --just saving it as a boolean to check for reflections reflection_debuffs[sourceSerial] = reflection_debuffs[sourceSerial] or {} reflection_debuffs[sourceSerial][spellId] = true end if (_in_combat) then ------------------------------------------------------------------------------------------------ --buff uptime if (crowdControlSpells[spellId]) then parser:add_cc_done (token, time, sourceSerial, sourceName, sourceFlags, targetSerial, targetName, targetFlags, targetFlags2, spellId, spellName) end if ((bitfield_debuffs[spellName] or bitfield_debuffs[spellId]) and raid_members_cache[targetSerial]) then bitfield_swap_cache[targetSerial] = true end if (raid_members_cache [sourceSerial]) then --call record debuffs uptime parser:add_debuff_uptime (token, time, sourceSerial, sourceName, sourceFlags, targetSerial, targetName, targetFlags, targetFlags2, spellId, spellName, "DEBUFF_UPTIME_IN") elseif (raid_members_cache [targetSerial] and not raid_members_cache [sourceSerial]) then --alvo � da raide e who � alguem de fora da raide parser:add_bad_debuff_uptime (token, time, sourceSerial, sourceName, sourceFlags, targetSerial, targetName, targetFlags, targetFlags2, spellId, spellName, spellschool, "DEBUFF_UPTIME_IN") end end end end --~refresh function parser:buff_refresh(token, time, sourceSerial, sourceName, sourceFlags, targetSerial, targetName, targetFlags, targetFlags2, spellId, spellName, spellschool, tipo, amount) if (ignoredWorldAuras[spellId]) then return end if (not sourceName) then sourceName = names_cache[spellName] if (not sourceName) then sourceName = "[*] " .. spellName names_cache[spellName] = sourceName end sourceFlags = 0xa48 sourceSerial = "" end if (augmentation_aura_list[spellId]) then Details222.SpecHelpers[1473].BuffRefresh(token, time, sourceSerial, sourceName, sourceFlags, targetSerial, targetName, targetFlags, targetFlags2, spellId, spellName, spellschool, tipo, amount) end if (tipo == "BUFF") then if (spellId == 272790 and cacheAnything.track_hunter_frenzy) then --hunter pet Frenzy spellid local miscActorObject = misc_cache[sourceName] if (miscActorObject) then --fastest way to query utility spell data local spellTable = miscActorObject.buff_uptime_spells and miscActorObject.buff_uptime_spells._ActorTable[spellId] if (spellTable) then if (spellTable.actived and pet_frenzy_cache[sourceName]) then if (detailsFramework:IsNearlyEqual(pet_frenzy_cache[sourceName], time, 0.2)) then return end end end end parser:add_buff_uptime(token, time, sourceSerial, sourceName, sourceFlags, sourceSerial, sourceName, sourceFlags, 0x0, spellId, spellName, "BUFF_UPTIME_REFRESH") pet_frenzy_cache[sourceName] = time return end if (buffs_on_target[spellId]) then parser:add_buff_uptime(token, time, sourceSerial, sourceName, sourceFlags, targetSerial, targetName, targetFlags, targetFlags2, spellId, spellName, "BUFF_UPTIME_REFRESH", true) elseif (sourceName == targetName and raid_members_cache [sourceSerial] and _in_combat) then --call record buffs uptime parser:add_buff_uptime (token, time, sourceSerial, sourceName, sourceFlags, targetSerial, targetName, targetFlags, targetFlags2, spellId, spellName, "BUFF_UPTIME_REFRESH") elseif (petCache [sourceSerial] and petCache [sourceSerial][2] == targetSerial) then --um pet colocando uma aura do dono parser:add_buff_uptime (token, time, targetSerial, targetName, targetFlags, targetSerial, targetName, targetFlags, targetFlags2, spellId, spellName, "BUFF_UPTIME_REFRESH") elseif (buffs_to_other_players[spellId]) then parser:add_buff_uptime(token, time, targetSerial, targetName, targetFlags, targetSerial, targetName, targetFlags, targetFlags2, spellId, spellName, "BUFF_UPTIME_REFRESH") end if (_use_shield_overheal) then if (shield_spellid_cache[spellId] and amount) then if (shield_cache[targetName] and shield_cache[targetName][spellId] and shield_cache[targetName][spellId][sourceName]) then if (ignored_overheal[spellId]) then shield_cache[targetName][spellId][sourceName] = amount --refresh gives the updated amount return end --get the shield overheal local overhealAmount = shield_cache[targetName][spellId][sourceName] --set the new shield amount shield_cache[targetName][spellId][sourceName] = amount if (overhealAmount > 0) then return parser:heal(token, time, sourceSerial, sourceName, sourceFlags, targetSerial, targetName, targetFlags, targetFlags2, spellId, spellName, nil, 0, ceil (overhealAmount), 0, nil, true) end end end end ------------------------------------------------------------------------------------------------ --recording debuffs applied by player elseif (tipo == "DEBUFF") then if (_in_combat) then ------------------------------------------------------------------------------------------------ --buff uptime if (raid_members_cache [sourceSerial]) then --call record debuffs uptime parser:add_debuff_uptime (token, time, sourceSerial, sourceName, sourceFlags, targetSerial, targetName, targetFlags, targetFlags2, spellId, spellName, "DEBUFF_UPTIME_REFRESH") elseif (raid_members_cache [targetSerial] and not raid_members_cache [sourceSerial]) then --alvo � da raide e o caster � inimigo parser:add_bad_debuff_uptime (token, time, sourceSerial, sourceName, sourceFlags, targetSerial, targetName, targetFlags, targetFlags2, spellId, spellName, spellschool, "DEBUFF_UPTIME_REFRESH", amount) end end end end -- ~unbuff function parser:unbuff(token, time, sourceSerial, sourceName, sourceFlags, targetSerial, targetName, targetFlags, targetFlags2, spellId, spellName, spellSchool, tipo, amount) if (ignoredWorldAuras[spellId]) then return end if (not sourceName) then sourceName = names_cache[spellName] if (not sourceName) then sourceName = "[*] " .. spellName names_cache[spellName] = sourceName end sourceFlags = 0xa48 sourceSerial = "" end if (augmentation_aura_list[spellId]) then Details222.SpecHelpers[1473].BuffOut(token, time, sourceSerial, sourceName, sourceFlags, targetSerial, targetName, targetFlags, targetFlags2, spellId, spellName, spellSchool, tipo, amount) end if (tipo == "BUFF") then if (spellId == 272790 and cacheAnything.track_hunter_frenzy) then --hunter pet Frenzy spellid if (not pet_frenzy_cache[sourceName]) then return end parser:add_buff_uptime(token, time, sourceSerial, sourceName, sourceFlags, sourceSerial, sourceName, sourceFlags, 0x0, spellId, spellName, "BUFF_UPTIME_OUT") pet_frenzy_cache[sourceName] = nil return end if (buffs_on_target[spellId]) then parser:add_buff_uptime(token, time, sourceSerial, sourceName, sourceFlags, targetSerial, targetName, targetFlags, targetFlags2, spellId, spellName, "BUFF_UPTIME_OUT", true) elseif (sourceName == targetName and raid_members_cache[sourceSerial] and _in_combat) then --call record buffs uptime parser:add_buff_uptime(token, time, sourceSerial, sourceName, sourceFlags, targetSerial, targetName, targetFlags, targetFlags2, spellId, spellName, "BUFF_UPTIME_OUT") elseif (petCache[sourceSerial] and petCache[sourceSerial][2] == targetSerial) then --um pet colocando uma aura do dono parser:add_buff_uptime(token, time, targetSerial, targetName, targetFlags, targetSerial, targetName, targetFlags, targetFlags2, spellId, spellName, "BUFF_UPTIME_OUT") elseif (buffs_to_other_players[spellId]) then parser:add_buff_uptime(token, time, targetSerial, targetName, targetFlags, targetSerial, targetName, targetFlags, targetFlags2, spellId, spellName, "BUFF_UPTIME_OUT") end if (spellId == SPELLID_MONK_GUARD) then --BfA monk talent if (monk_guard_talent[sourceSerial]) then local damage_prevented = monk_guard_talent[sourceSerial] - (amount or 0) parser:heal(token, time, sourceSerial, sourceName, sourceFlags, targetSerial, targetName, targetFlags, targetFlags2, spellId, spellName, spellSchool, damage_prevented, ceil (amount or 0), 0, 0, true) end elseif (spellId == 388007 or spellId == 388011) then --buff: bleesing of the summer cacheAnything.paladin_vivaldi_blessings[targetSerial] = nil end ------------------------------------------------------------------------------------------------ --shield overheal if (_use_shield_overheal) then if (shield_spellid_cache[spellId]) then if (shield_cache [targetName] and shield_cache [targetName][spellId] and shield_cache [targetName][spellId][sourceName]) then if (amount) then -- o amount � o que sobrou do escudo --local overheal = escudo [alvo_name][spellid][who_name] --usando o 'amount' passado pela função --overheal não esta dando refresh quando um valor é adicionado ao escudo shield_cache [targetName][spellId][sourceName] = 0 --can't use monk guard since its overheal is computed inside the unbuff if (amount > 0 and spellId ~= SPELLID_MONK_GUARD) then --removing the nil at the end before true for is_shield, I have no documentation change about it, not sure the reason why it was addded return parser:heal (token, time, sourceSerial, sourceName, sourceFlags, targetSerial, targetName, targetFlags, targetFlags2, spellId, spellName, nil, 0, ceil (amount), 0, 0, true) --0, 0, nil, true else return end end shield_cache [targetName][spellId][sourceName] = 0 end end end ------------------------------------------------------------------------------------------------ --recording debuffs applied by player elseif (tipo == "DEBUFF") then ------------------------------------------------------------------------------------------------ --spell reflection if (reflection_dispels[targetSerial] and reflection_dispels[targetSerial][spellId]) then --debuff was dispelled by a reflecting dispel and could've been reflected --save the data about whom dispelled who and the spell that was dispelled local reflection = reflection_dispels[targetSerial][spellId] reflection_events[sourceSerial] = reflection_events[sourceSerial] or {} reflection_events[sourceSerial][spellId] = { who_serial = reflection.who_serial, who_name = reflection.who_name, who_flags = reflection.who_flags, spellid = reflection.spellid, spellname = reflection.spellname, spelltype = reflection.spelltype, time = time, } reflection_dispels[targetSerial][spellId] = nil if (next(reflection_dispels[targetSerial]) == nil) then --suggestion on how to make this better? reflection_dispels[targetSerial] = nil end end ------------------------------------------------------------------------------------------------ --spell reflection if (reflection_debuffs[sourceSerial] and reflection_debuffs[sourceSerial][spellId]) then --self-inflicted debuff was removed, so we just clear this data reflection_debuffs[sourceSerial][spellId] = nil if (next(reflection_debuffs[sourceSerial]) == nil) then --better way of doing this? accepting suggestions reflection_debuffs[sourceSerial] = nil end end if (_in_combat) then ------------------------------------------------------------------------------------------------ --buff uptime if (raid_members_cache [sourceSerial]) then --call record debuffs uptime parser:add_debuff_uptime (token, time, sourceSerial, sourceName, sourceFlags, targetSerial, targetName, targetFlags, targetFlags2, spellId, spellName, "DEBUFF_UPTIME_OUT") elseif (raid_members_cache [targetSerial] and not raid_members_cache [sourceSerial]) then --alvo � da raide e o caster � inimigo parser:add_bad_debuff_uptime (token, time, sourceSerial, sourceName, sourceFlags, targetSerial, targetName, targetFlags, targetFlags2, spellId, spellName, spellSchool, "DEBUFF_UPTIME_OUT") end if ((bitfield_debuffs[spellName] or bitfield_debuffs[spellId]) and targetSerial) then bitfield_swap_cache[targetSerial] = nil end end end end --~crowd control ~ccdone function parser:add_cc_done(token, time, sourceSerial, sourceName, sourceFlags, targetSerial, targetName, targetFlags, targetFlags2, spellId, spellName) _current_misc_container.need_refresh = true ---@type actor local sourceActor, ownerActor = misc_cache[sourceName] if (not sourceActor) then sourceActor, ownerActor, sourceName = _current_misc_container:GetOrCreateActor(sourceSerial, sourceName, sourceFlags, true) if (not ownerActor) then misc_cache[sourceName] = sourceActor end end sourceActor.last_event = _tempo if (not sourceActor.cc_done) then sourceActor.cc_done = Details:GetOrderNumber() sourceActor.cc_done_spells = spellContainerClass:CreateSpellContainer(container_misc) sourceActor.cc_done_targets = {} end --add amount sourceActor.cc_done = sourceActor.cc_done + 1 sourceActor.cc_done_targets[targetName] = (sourceActor.cc_done_targets[targetName] or 0) + 1 --actor spells table local spellTable = sourceActor.cc_done_spells._ActorTable[spellId] if (not spellTable) then spellTable = sourceActor.cc_done_spells:GetOrCreateSpell(spellId, true) end spellTable.targets[targetName] = (spellTable.targets[targetName] or 0) + 1 spellTable.counter = spellTable.counter + 1 --add the crowd control for the pet owner if (ownerActor) then if (not ownerActor.cc_done) then ownerActor.cc_done = Details:GetOrderNumber() ownerActor.cc_done_spells = spellContainerClass:CreateSpellContainer(container_misc) ownerActor.cc_done_targets = {} end --add amount ownerActor.cc_done = ownerActor.cc_done + 1 ownerActor.cc_done_targets[targetName] = (ownerActor.cc_done_targets[targetName] or 0) + 1 --actor spells table local ownerSpellTable = ownerActor.cc_done_spells._ActorTable[spellId] if (not ownerSpellTable) then ownerSpellTable = ownerActor.cc_done_spells:GetOrCreateSpell(spellId, true) end ownerSpellTable.targets[targetName] = (ownerSpellTable.targets[targetName] or 0) + 1 ownerSpellTable.counter = ownerSpellTable.counter + 1 end if (not sourceActor.classe) then if (sourceFlags and bitBand(sourceFlags, OBJECT_TYPE_PLAYER) ~= 0) then if (sourceActor.classe == "UNKNOW" or sourceActor.classe == "UNGROUPPLAYER") then ---@type actor local damageActor = damage_cache [sourceSerial] if (damageActor and (damageActor.classe ~= "UNKNOW" and damageActor.classe ~= "UNGROUPPLAYER")) then sourceActor.classe = damageActor.classe else ---@type actor local healingActor = healing_cache[sourceSerial] if (healingActor and (healingActor.classe ~= "UNKNOW" and healingActor.classe ~= "UNGROUPPLAYER")) then sourceActor.classe = healingActor.classe end end end end end end ----------------------------------------------------------------------------------------------------------------------------------------- --MISC search key: ~buffuptime ~buffsuptime | ----------------------------------------------------------------------------------------------------------------------------------------- function parser:add_bad_debuff_uptime(token, time, sourceGuid, sourceName, sourceFlags, targetGuid, targetName, targetFlags, targetsFlags2, spellId, spellName, spellSchool, sInOrOut, stackSize) if (ignoredWorldAuras[spellId]) then return elseif (not targetName) then --no target name, just quit return elseif (not sourceName) then --no actor name, use spell name instead sourceName = "[*] "..spellName end ------------------------------------------------------------------------------------------------ --get actors --nome do debuff ser� usado para armazenar o nome do ator local sourceActor = misc_cache[spellName] if (not sourceActor) then --pode ser um desconhecido ou um pet sourceActor = _current_misc_container:GetOrCreateActor (sourceGuid, spellName, sourceFlags, true) misc_cache[spellName] = sourceActor end ------------------------------------------------------------------------------------------------ --build containers on the fly if (not sourceActor.debuff_uptime) then sourceActor.boss_debuff = true sourceActor.damage_twin = sourceName sourceActor.spellschool = spellSchool sourceActor.damage_spellid = spellId sourceActor.debuff_uptime = 0 sourceActor.debuff_uptime_spells = spellContainerClass:CreateSpellContainer (container_misc) sourceActor.debuff_uptime_targets = {} end ------------------------------------------------------------------------------------------------ --add amount --update last event sourceActor.last_event = _tempo --actor target local este_alvo = sourceActor.debuff_uptime_targets [targetName] if (not este_alvo) then este_alvo = Details.atributo_misc:CreateBuffTargetObject() sourceActor.debuff_uptime_targets [targetName] = este_alvo end if (sInOrOut == "DEBUFF_UPTIME_IN") then este_alvo.actived = true este_alvo.activedamt = este_alvo.activedamt + 1 if (este_alvo.actived_at and este_alvo.actived) then este_alvo.uptime = este_alvo.uptime + _tempo - este_alvo.actived_at sourceActor.debuff_uptime = sourceActor.debuff_uptime + _tempo - este_alvo.actived_at end este_alvo.actived_at = _tempo --death log --record death log local t = last_events_cache[targetName] if (not t) then t = _current_combat:CreateLastEventsTable(targetName) end local i = t.n local thisEvent = t[i] if (not thisEvent) then return Details:Msg("Parser Event Error -> Set to 16 DeathLogs and /reload", i, _amount_of_last_events) end thisEvent[1] = 4 --4 = debuff aplication thisEvent[2] = spellId --spellid thisEvent[3] = 1 thisEvent[4] = time --parser time thisEvent[5] = UnitHealth(targetName) / UnitHealthMax(targetName) --current unit heal thisEvent[6] = sourceName --source name thisEvent[7] = false thisEvent[8] = false thisEvent[9] = false thisEvent[10] = false i = i + 1 if (i == _amount_of_last_events+1) then t.n = 1 else t.n = i end elseif (sInOrOut == "DEBUFF_UPTIME_REFRESH") then if (este_alvo.actived_at and este_alvo.actived) then este_alvo.uptime = este_alvo.uptime + _tempo - este_alvo.actived_at sourceActor.debuff_uptime = sourceActor.debuff_uptime + _tempo - este_alvo.actived_at end este_alvo.actived_at = _tempo este_alvo.actived = true --death log --local name, texture, count, debuffType, duration, expirationTime, caster, canStealOrPurge, nameplateShowPersonal, spellId = UnitAura (alvo_name, spellname, nil, "HARMFUL") --UnitAura ("Kastfall", "Gulp Frog Toxin", nil, "HARMFUL") --6/27 15:06:18.113 SPELL_AURA_APPLIED_DOSE,Creature-0-2085-2657-20918-227617-0000FDAA05,"Cosmic Simulacrum",0xa48,0x0,Player-4184-005CFB2D,"nil",0x511,0x0,459273,"Cosmic Shards",0x20,DEBUFF,4 --6/27 15:06:18.114 SPELL_AURA_REFRESH,Creature-0-2085-2657-20918-227617-0000FDAA05,"Cosmic Simulacrum",0xa48,0x0,Player-4184-005CFB2D,"nil",0x511,0x0,459273,"Cosmic Shards",0x20,DEBUFF --record death log local t = last_events_cache[targetName] if (not t) then t = _current_combat:CreateLastEventsTable(targetName) end local i = t.n local bCanAdd = true local previousEvent = t[i-1] if (previousEvent) then if (previousEvent[1] == 4 and previousEvent[2] == spellId and detailsFramework.Math.IsNearlyEqual(time, previousEvent[4], 0.01)) then --don't repeat the application of the same debuff bCanAdd = false end end if (bCanAdd) then local thisEvent = t[i] if (not thisEvent) then return Details:Msg("Parser Event Error -> Set to 16 DeathLogs and /reload", i, _amount_of_last_events) end thisEvent[1] = 4 --4 = debuff aplication thisEvent[2] = spellId --spellid thisEvent[3] = stackSize or 1 thisEvent[4] = time --parser time thisEvent[5] = UnitHealth(targetName) / UnitHealthMax(targetName) --current unit heal thisEvent[6] = sourceName --source name thisEvent[7] = false thisEvent[8] = false thisEvent[9] = false thisEvent[10] = false i = i + 1 if (i == _amount_of_last_events+1) then t.n = 1 else t.n = i end end elseif (sInOrOut == "DEBUFF_UPTIME_OUT") then if (este_alvo.actived_at and este_alvo.actived) then este_alvo.uptime = este_alvo.uptime + Details._tempo - este_alvo.actived_at sourceActor.debuff_uptime = sourceActor.debuff_uptime + _tempo - este_alvo.actived_at --token = actor misc object end este_alvo.activedamt = este_alvo.activedamt - 1 if (este_alvo.activedamt == 0) then este_alvo.actived = false este_alvo.actived_at = nil else este_alvo.actived_at = _tempo end end end -- ~debuff ---@param token string ---@param time unixtime ---@param sourceSerial guid ---@param sourceName actorname ---@param sourceFlags controlflags ---@param targetSerial guid ---@param targetName actorname ---@param targetFlags controlflags ---@param targetFlags2 number ---@param spellId spellid ---@param spellName spellname ---@param sAuraInOrOut string ---@param bAddToTarget boolean|nil not in use on debuffs at the moment function parser:add_debuff_uptime(token, time, sourceSerial, sourceName, sourceFlags, targetSerial, targetName, targetFlags, targetFlags2, spellId, spellName, sAuraInOrOut, bAddToTarget) if (ignoredWorldAuras[spellId]) then return end _current_misc_container.need_refresh = true local sourceActor = misc_cache[sourceName] if (not sourceActor) then sourceActor = _current_misc_container:GetOrCreateActor(sourceSerial, sourceName, sourceFlags, true) misc_cache[sourceName] = sourceActor end if (not sourceActor.debuff_uptime) then sourceActor.debuff_uptime = 0 sourceActor.debuff_uptime_spells = spellContainerClass:CreateSpellContainer(container_misc) sourceActor.debuff_uptime_targets = {} end sourceActor.last_event = _tempo --actor spells table do local spellTable = sourceActor.debuff_uptime_spells._ActorTable[spellId] if (not spellTable) then spellTable = sourceActor.debuff_uptime_spells:GetOrCreateSpell(spellId, true, "DEBUFF_UPTIME") end return _spell_utility_func(spellTable, targetName, sourceActor, "BUFF_OR_DEBUFF", sAuraInOrOut) end end --~buff ---@param token string ---@param time unixtime ---@param sourceSerial guid ---@param sourceName actorname ---@param sourceFlags controlflags ---@param targetSerial guid ---@param targetName actorname ---@param targetFlags controlflags ---@param targetFlags2 number ---@param spellId spellid ---@param spellName spellname ---@param sAuraInOrOut string ---@param bAddToTarget boolean? --special auras which has to be added to the caster and target ---@param bOverrideTime boolean? function parser:add_buff_uptime(token, time, sourceSerial, sourceName, sourceFlags, targetSerial, targetName, targetFlags, targetFlags2, spellId, spellName, sAuraInOrOut, bAddToTarget, bOverrideTime) if (ignoredWorldAuras[spellId]) then return end _current_misc_container.need_refresh = true local sourceActor = misc_cache[sourceName] if (not sourceActor) then sourceActor = _current_misc_container:GetOrCreateActor(sourceSerial, sourceName, sourceFlags, true) misc_cache[sourceName] = sourceActor end sourceActor.last_event = _tempo --build containers on the fly if not exist if (not sourceActor.buff_uptime) then sourceActor.buff_uptime = 0 sourceActor.buff_uptime_spells = spellContainerClass:CreateSpellContainer(container_misc) sourceActor.buff_uptime_targets = {} end --actor spells table do ---@type spelltable local spellTable = sourceActor.buff_uptime_spells._ActorTable[spellId] if (not spellTable) then spellTable = sourceActor.buff_uptime_spells:GetOrCreateSpell(spellId, true, "BUFF_UPTIME") end _spell_utility_func(spellTable, targetName, sourceActor, "BUFF_OR_DEBUFF", sAuraInOrOut, bOverrideTime and floor(time)) end if (bAddToTarget and sourceSerial ~= targetSerial) then local targetActor = misc_cache[targetName] if (not targetActor) then targetActor = _current_misc_container:GetOrCreateActor(targetSerial, targetName, targetFlags, true) misc_cache[targetName] = targetActor end targetActor.last_event = _tempo --build containers on the fly if not exist if (not targetActor.buff_uptime) then targetActor.buff_uptime = 0 targetActor.buff_uptime_spells = spellContainerClass:CreateSpellContainer(container_misc) targetActor.buff_uptime_targets = {} end --build containers on the fly if not exist if (not targetActor.received_buffs_spells) then targetActor.received_buffs_spells = spellContainerClass:CreateSpellContainer(container_misc) end local nameWithSpellId = sourceName .. "@" .. spellId ---@type spelltable local spellTable = targetActor.received_buffs_spells._ActorTable[nameWithSpellId] if (not spellTable) then spellTable = targetActor.received_buffs_spells:GetOrCreateSpell(nameWithSpellId, true, "BUFF_UPTIME") spellTable.id = spellId end _spell_utility_func(spellTable, sourceName, targetActor, "BUFF_OR_DEBUFF", sAuraInOrOut, bOverrideTime and floor(time)) end end ----------------------------------------------------------------------------------------------------------------------------------------- --ENERGY serach key: ~energy | ----------------------------------------------------------------------------------------------------------------------------------------- local PowerEnum = Enum and Enum.PowerType local SPELL_POWER_MANA = SPELL_POWER_MANA or (PowerEnum and PowerEnum.Mana) or 0 local SPELL_POWER_RAGE = SPELL_POWER_RAGE or (PowerEnum and PowerEnum.Rage) or 1 local SPELL_POWER_FOCUS = SPELL_POWER_FOCUS or (PowerEnum and PowerEnum.Focus) or 2 local SPELL_POWER_ENERGY = SPELL_POWER_ENERGY or (PowerEnum and PowerEnum.Energy) or 3 local SPELL_POWER_COMBO_POINTS2 = SPELL_POWER_COMBO_POINTS or (PowerEnum and PowerEnum.ComboPoints) or 4 local SPELL_POWER_RUNES = SPELL_POWER_RUNES or (PowerEnum and PowerEnum.Runes) or 5 local SPELL_POWER_RUNIC_POWER = SPELL_POWER_RUNIC_POWER or (PowerEnum and PowerEnum.RunicPower) or 6 local SPELL_POWER_SOUL_SHARDS = SPELL_POWER_SOUL_SHARDS or (PowerEnum and PowerEnum.SoulShards) or 7 local SPELL_POWER_LUNAR_POWER = SPELL_POWER_LUNAR_POWER or (PowerEnum and PowerEnum.LunarPower) or 8 local SPELL_POWER_HOLY_POWER = SPELL_POWER_HOLY_POWER or (PowerEnum and PowerEnum.HolyPower) or 9 local SPELL_POWER_ALTERNATE_POWER = SPELL_POWER_ALTERNATE_POWER or (PowerEnum and PowerEnum.Alternate) or 10 local SPELL_POWER_MAELSTROM = SPELL_POWER_MAELSTROM or (PowerEnum and PowerEnum.Maelstrom) or 11 local SPELL_POWER_CHI = SPELL_POWER_CHI or (PowerEnum and PowerEnum.Chi) or 12 local SPELL_POWER_INSANITY = SPELL_POWER_INSANITY or (PowerEnum and PowerEnum.Insanity) or 13 local SPELL_POWER_OBSOLETE = SPELL_POWER_OBSOLETE or (PowerEnum and PowerEnum.Obsolete) or 14 local SPELL_POWER_OBSOLETE2 = SPELL_POWER_OBSOLETE2 or (PowerEnum and PowerEnum.Obsolete2) or 15 local SPELL_POWER_ARCANE_CHARGES = SPELL_POWER_ARCANE_CHARGES or (PowerEnum and PowerEnum.ArcaneCharges) or 16 local SPELL_POWER_FURY = SPELL_POWER_FURY or (PowerEnum and PowerEnum.Fury) or 17 local SPELL_POWER_PAIN = SPELL_POWER_PAIN or (PowerEnum and PowerEnum.Pain) or 18 local energy_types = { [SPELL_POWER_MANA] = true, [SPELL_POWER_RAGE] = true, [SPELL_POWER_ENERGY] = true, [SPELL_POWER_RUNIC_POWER] = true, } local resource_types = { [SPELL_POWER_INSANITY] = true, --shadow priest [SPELL_POWER_CHI] = true, --monk [SPELL_POWER_HOLY_POWER] = true, --paladins [SPELL_POWER_LUNAR_POWER] = true, --balance druids [SPELL_POWER_SOUL_SHARDS] = true, --warlock affliction [SPELL_POWER_COMBO_POINTS2] = true, --combo points [SPELL_POWER_MAELSTROM] = true, --shamans [SPELL_POWER_PAIN] = true, --demonhunter tank [SPELL_POWER_RUNES] = true, --dk [SPELL_POWER_ARCANE_CHARGES] = true, --mage [SPELL_POWER_FURY] = true, --warrior demonhunter dps } local resourcePowerType = { [SPELL_POWER_COMBO_POINTS2] = SPELL_POWER_ENERGY, --combo points [SPELL_POWER_SOUL_SHARDS] = SPELL_POWER_MANA, --warlock [SPELL_POWER_LUNAR_POWER] = SPELL_POWER_MANA, --druid [SPELL_POWER_HOLY_POWER] = SPELL_POWER_MANA, --paladin [SPELL_POWER_INSANITY] = SPELL_POWER_MANA, --shadowpriest [SPELL_POWER_MAELSTROM] = SPELL_POWER_MANA, --shaman [SPELL_POWER_CHI] = SPELL_POWER_MANA, --monk [SPELL_POWER_PAIN] = SPELL_POWER_ENERGY, --demonhuinter [SPELL_POWER_RUNES] = SPELL_POWER_RUNIC_POWER, --dk [SPELL_POWER_ARCANE_CHARGES] = SPELL_POWER_MANA, --mage [SPELL_POWER_FURY] = SPELL_POWER_RAGE, --warrior } Details.resource_strings = { [SPELL_POWER_COMBO_POINTS2] = "Combo Point", [SPELL_POWER_SOUL_SHARDS] = "Soul Shard", [SPELL_POWER_LUNAR_POWER] = "Lunar Power", [SPELL_POWER_HOLY_POWER] = "Holy Power", [SPELL_POWER_INSANITY] = "Insanity", [SPELL_POWER_MAELSTROM] = "Maelstrom", [SPELL_POWER_CHI] = "Chi", [SPELL_POWER_PAIN] = "Pain", [SPELL_POWER_RUNES] = "Runes", [SPELL_POWER_ARCANE_CHARGES] = "Arcane Charge", [SPELL_POWER_FURY] = "Rage", } Details.resource_icons = { [SPELL_POWER_COMBO_POINTS2] = {file = [[Interface\PLAYERFRAME\ClassOverlayComboPoints]], coords = {58/128, 74/128, 25/64, 39/64}}, [SPELL_POWER_SOUL_SHARDS] = {file = [[Interface\PLAYERFRAME\UI-WARLOCKSHARD]], coords = {3/64, 17/64, 2/128, 16/128}}, [SPELL_POWER_LUNAR_POWER] = {file = [[Interface\PLAYERFRAME\DruidEclipse]], coords = {117/256, 140/256, 83/128, 115/128}}, [SPELL_POWER_HOLY_POWER] = {file = [[Interface\PLAYERFRAME\PALADINPOWERTEXTURES]], coords = {75/256, 94/256, 87/128, 100/128}}, [SPELL_POWER_INSANITY] = {file = [[Interface\PLAYERFRAME\Priest-ShadowUI]], coords = {119/256, 150/256, 61/128, 94/128}}, [SPELL_POWER_MAELSTROM] = {file = [[Interface\PLAYERFRAME\MonkNoPower]], coords = {0, 1, 0, 1}}, [SPELL_POWER_CHI] = {file = [[Interface\PLAYERFRAME\MonkLightPower]], coords = {0, 1, 0, 1}}, [SPELL_POWER_PAIN] = {file = [[Interface\PLAYERFRAME\Deathknight-Energize-Blood]], coords = {0, 1, 0, 1}}, [SPELL_POWER_RUNES] = {file = [[Interface\PLAYERFRAME\UI-PlayerFrame-Deathknight-SingleRune]], coords = {0, 1, 0, 1}}, [SPELL_POWER_ARCANE_CHARGES] = {file = [[Interface\PLAYERFRAME\MageArcaneCharges]], coords = {68/256, 90/256, 68/128, 91/128}}, [SPELL_POWER_FURY] = {file = [[Interface\PLAYERFRAME\UI-PlayerFrame-Deathknight-Blood-On]], coords = {0, 1, 0, 1}}, } local alternatePowerEnableFrame = CreateFrame("frame", "DetailsAlternatePowerEventHandler") local alternatePowerMonitorFrame = CreateFrame("frame", "DetailsAlternatePowerMonitor") alternatePowerEnableFrame:RegisterEvent("UNIT_POWER_BAR_SHOW") alternatePowerEnableFrame:RegisterEvent("ENCOUNTER_END") alternatePowerEnableFrame.IsRunning = false --alternate power will only run when the encounter has a alternate power bar alternatePowerEnableFrame:SetScript("OnEvent", function(self, event) if (event == "UNIT_POWER_BAR_SHOW") then alternatePowerMonitorFrame:RegisterEvent("UNIT_POWER_UPDATE") -- 8.0 alternatePowerEnableFrame.IsRunning = true elseif (alternatePowerEnableFrame.IsRunning and (event == "ENCOUNTER_END" or event == "PLAYER_REGEN_ENABLED")) then alternatePowerMonitorFrame:UnregisterEvent("UNIT_POWER_UPDATE") alternatePowerEnableFrame.IsRunning = false end end) local onUnitPowerUpdate = function(self, event, unitID, powerType) if (powerType == "ALTERNATE") then local actorName = Details:GetFullName(unitID) if (actorName) then --weird bug on cata as described below if (not _current_combat.alternate_power) then _current_combat.alternate_power = {} end local power = _current_combat.alternate_power[actorName] --cata: 120x Details/core/parser.lua:3694: attempt to index field 'alternate_power' (a nil value) if (not power) then power = _current_combat:CreateAlternatePowerTable(actorName) end local currentPower = UnitPower(unitID, 10) if (currentPower and currentPower > power.last) then local addPower = currentPower - power.last power.total = power.total + addPower --main actor local actorObject = energy_cache[actorName] if (not actorObject) then --as alternate power bars does not trigger for pets, this is guaranteed to be a player actor actorObject = _current_energy_container:GetOrCreateActor(UnitGUID(unitID), actorName, 0x514, true) energy_cache[actorName] = actorObject end actorObject.alternatepower = actorObject.alternatepower + addPower _current_energy_container.need_refresh = true end power.last = currentPower or 0 end end end alternatePowerMonitorFrame:SetScript("OnEvent", onUnitPowerUpdate) ---this function captures when the energy of a unit is at its max capacity on classes which auto regenerates it's power such like Rogues ---staying at max capacity prevents it to generate more energy and causes a power overflow local regen_power_overflow_check = function() if (not _in_combat) then return end for playerName, powerType in pairs(auto_regen_cache) do local currentPower = UnitPower(playerName, powerType) or 0 local maxPower = UnitPowerMax(playerName, powerType) or 1 if (currentPower == maxPower) then --power overflow local energyObject = energy_cache[playerName] if (energyObject) then energyObject.passiveover = energyObject.passiveover + AUTO_REGEN_PRECISION end end end end -- ~energy ~resource function parser:energize (token, time, sourceSerial, sourceName, sourceFlags, targetSerial, targetName, targetFlags, targetFlags2, spellId, spellName, spellType, amount, overpower, powerType, altpower) if (not sourceName) then sourceName = "[*] " .. spellName elseif (not targetName) then return end --get resource type local bIsResource, resourceAmount, resourceId = resourcePowerType[powerType], amount, powerType --check if is valid if (not energy_types[powerType] and not bIsResource) then return elseif (bIsResource) then powerType = bIsResource amount = 0 end overpower = overpower or 0 _current_energy_container.need_refresh = true --get actors ---@type actor local sourceActor = energy_cache[sourceName] local ownerActor if (not sourceActor) then sourceActor, ownerActor, sourceName = _current_energy_container:GetOrCreateActor(sourceSerial, sourceName, sourceFlags, true) sourceActor.powertype = powerType if (ownerActor) then ownerActor.powertype = powerType end if (not ownerActor) then energy_cache[sourceName] = sourceActor end end if (not sourceActor.powertype) then sourceActor.powertype = powerType end ---@type actor local targetActor = energy_cache[targetName] local ownerTarget if (not targetActor) then targetActor, ownerTarget, targetName = _current_energy_container:GetOrCreateActor(targetSerial, targetName, targetFlags, true) targetActor.powertype = powerType if (ownerTarget) then ownerTarget.powertype = powerType end if (not ownerTarget) then energy_cache[targetName] = targetActor end end if (targetActor.powertype ~= sourceActor.powertype) then return end sourceActor.last_event = _tempo --amount add if (not bIsResource) then --add to targets sourceActor.targets[targetName] = (sourceActor.targets[targetName] or 0) + amount --add to combat total _current_total[3][powerType] = _current_total[3][powerType] + amount if (sourceActor.grupo) then _current_gtotal [3] [powerType] = _current_gtotal [3] [powerType] + amount end --regen produced amount sourceActor.total = sourceActor.total + amount sourceActor.totalover = sourceActor.totalover + overpower --target regenerated amount targetActor.received = targetActor.received + amount --owner if (ownerActor) then ownerActor.total = ownerActor.total + amount end --actor spells table local spellTable = sourceActor.spells._ActorTable[spellId] if (not spellTable) then spellTable = sourceActor.spells:GetOrCreateSpell(spellId, true, token) end --return spell:Add (alvo_serial, alvo_name, alvo_flags, amount, who_name, powertype) return _spell_energy_func (spellTable, targetSerial, targetName, targetFlags, amount, sourceName, powerType, overpower) else --is a resource sourceActor.resource = sourceActor.resource + resourceAmount sourceActor.resource_type = resourceId end end ----------------------------------------------------------------------------------------------------------------------------------------- --MISC search key: ~cooldown | ----------------------------------------------------------------------------------------------------------------------------------------- function parser:add_defensive_cooldown(token, time, sourceSerial, sourceName, sourceFlags, targetSerial, targetName, targetFlags, targetFlags2, spellId, spellName) _current_misc_container.need_refresh = true local sourceActor, ownerActor = misc_cache[sourceName], nil if (not sourceActor) then sourceActor, ownerActor, sourceName = _current_misc_container:GetOrCreateActor(sourceSerial, sourceName, sourceFlags, true) if (not ownerActor) then misc_cache[sourceName] = sourceActor end end if (not sourceActor.cooldowns_defensive) then sourceActor.cooldowns_defensive = Details:GetOrderNumber(sourceName) sourceActor.cooldowns_defensive_targets = {} sourceActor.cooldowns_defensive_spells = spellContainerClass:CreateSpellContainer(container_misc) end --actor cooldowns used sourceActor.cooldowns_defensive = sourceActor.cooldowns_defensive + 1 --combat totals _current_total[4].cooldowns_defensive = _current_total[4].cooldowns_defensive + 1 if (sourceActor.grupo) then _current_gtotal[4].cooldowns_defensive = _current_gtotal[4].cooldowns_defensive + 1 if (sourceName == targetName) then --last events local t = last_events_cache[sourceName] if (not t) then t = _current_combat:CreateLastEventsTable(sourceName) end local i = t.n local thisEvent = t [i] thisEvent[1] = 1 --true if this is a damage || false for healing || 1 for cooldown thisEvent[2] = spellId --spellid || false if this is a battle ress line thisEvent[3] = 1 --amount of damage or healing thisEvent[4] = time thisEvent[5] = UnitHealth(sourceName) / UnitHealthMax(sourceName) thisEvent[6] = sourceName i = i + 1 if (i == _amount_of_last_events+1) then t.n = 1 else t.n = i end sourceActor.last_cooldown = {time, spellId} end end --update last event sourceActor.last_event = _tempo --actor targets sourceActor.cooldowns_defensive_targets[targetName] = (sourceActor.cooldowns_defensive_targets [targetName] or 0) + 1 --actor spells table local spellTable = sourceActor.cooldowns_defensive_spells._ActorTable[spellId] if (not spellTable) then spellTable = sourceActor.cooldowns_defensive_spells:GetOrCreateSpell(spellId, true, token) end if (_hook_cooldowns) then --send event to registred functions for i = 1, #_hook_cooldowns_container do local successful, errorText = pcall(_hook_cooldowns_container[i], nil, token, time, sourceSerial, sourceName, sourceFlags, targetSerial, targetName, targetFlags, spellId, spellName) if (not successful) then Details:Msg("error occurred on a cooldown hook function:", errorText) end end end return _spell_utility_func(spellTable, targetName, token, "BUFF_OR_DEBUFF", "COOLDOWN") end --serach key: ~interrupt ---comment: this function is called when a spell is interrupted ---@param token string ---@param time unixtime ---@param sourceSerial guid ---@param sourceName actorname ---@param sourceFlags controlflags ---@param targetSerial guid ---@param targetName actorname ---@param targetFlags controlflags ---@param targetFlags2 number ---@param spellId spellid ---@param spellName spellname ---@param spellType spellschool ---@param extraSpellID spellid ---@param extraSpellName spellname ---@param extraSchool spellschool function parser:interrupt(token, time, sourceSerial, sourceName, sourceFlags, targetSerial, targetName, targetFlags, targetFlags2, spellId, spellName, spellType, extraSpellID, extraSpellName, extraSchool) --quake affix from mythic+ if (spellId == 240448) then return end if (not sourceName) then sourceName = "[*] " .. spellName elseif (not targetName) then return end _current_misc_container.need_refresh = true ------------------------------------------------------------------------------------------------ --get actors local petName, ownerName, ownerGUID, ownerFlags ---@type actorutility, actorutility local sourceActor, ownerActor = misc_cache[sourceName], nil if (not sourceActor) then sourceActor, ownerActor, sourceName = _current_misc_container:GetOrCreateActor(sourceSerial, sourceName, sourceFlags, true) if (not ownerActor) then misc_cache[sourceName] = sourceActor end end if (not ownerActor) then petName, ownerName, ownerGUID, ownerFlags = petContainer.GetOwner(sourceSerial, targetName) if (petName) then ownerActor = _current_misc_container:GetOrCreateActor(ownerGUID, ownerName, ownerFlags, true) end end ------------------------------------------------------------------------------------------------ --build containers on the fly if (not sourceActor.interrupt) then sourceActor.interrupt = Details:GetOrderNumber() sourceActor.interrupt_targets = {} sourceActor.interrupt_spells = spellContainerClass:CreateSpellContainer(container_misc) sourceActor.interrompeu_oque = {} end ------------------------------------------------------------------------------------------------ --add amount --actor interrupt amount sourceActor.interrupt = sourceActor.interrupt + 1 --combat totals _current_total[4].interrupt = _current_total[4].interrupt + 1 if (sourceActor.grupo) then _current_gtotal[4].interrupt = _current_gtotal[4].interrupt + 1 end --update last event sourceActor.last_event = _tempo --spells interrupted sourceActor.interrompeu_oque[extraSpellID] = (sourceActor.interrompeu_oque[extraSpellID] or 0) + 1 --actor targets sourceActor.interrupt_targets[targetName] = (sourceActor.interrupt_targets[targetName] or 0) + 1 --actor spells table local spell = sourceActor.interrupt_spells._ActorTable[spellId] if (not spell) then spell = sourceActor.interrupt_spells:GetOrCreateSpell(spellId, true, token) end _spell_utility_func(spell, targetName, token, extraSpellID, extraSpellName) if (spellId == 19647) then --spell lock (warlock pet) --Details:Msg("warlock pet interrupt, owner:", (ownerActor and ownerActor.nome or "no owner")) end --if the interrupt is from a pet, then we need to add the interrupt to the owner if (ownerActor) then if (not ownerActor.interrupt) then ownerActor.interrupt = Details:GetOrderNumber(sourceName) ownerActor.interrupt_targets = {} ownerActor.interrupt_spells = spellContainerClass:CreateSpellContainer(container_misc) ownerActor.interrompeu_oque = {} end ownerActor.last_event = _tempo --total interrupts ownerActor.interrupt = ownerActor.interrupt + 1 --add to interrupt targets ownerActor.interrupt_targets[targetName] = (ownerActor.interrupt_targets[targetName] or 0) + 1 --which spells this actor interrupted ownerActor.interrompeu_oque[extraSpellID] = (ownerActor.interrompeu_oque[extraSpellID] or 0) + 1 --pet interrupt if (_hook_interrupt) then for _, func in ipairs(_hook_interrupt_container) do func(nil, token, time, ownerActor.serial, ownerActor.nome, ownerActor.flag_original, targetSerial, targetName, targetFlags, spellId, spellName, spellType, extraSpellID, extraSpellName, extraSchool) end end else --player interrupt if (_hook_interrupt) then for _, func in ipairs(_hook_interrupt_container) do func(nil, token, time, sourceSerial, sourceName, sourceFlags, targetSerial, targetName, targetFlags, spellId, spellName, spellType, extraSpellID, extraSpellName, extraSchool) end end end end ---search key: ~spellcast ~castspell ~cast ---comment: this function is called when a spell is casted ---@param token string ---@param time number ---@param sourceSerial string ---@param sourceName string ---@param sourceFlags number ---@param targetSerial string ---@param targetName string ---@param targetFlags number ---@param targetRaidFlags number ---@param spellId number ---@param spellName string ---@param spellType number? function parser:spellcast(token, time, sourceSerial, sourceName, sourceFlags, targetSerial, targetName, targetFlags, targetRaidFlags, spellId, spellName, spellType) --only capture if is in combat if (not _in_combat) then return end if (not sourceName) then sourceName = "[*] " .. spellName end ---@type actor, actor local sourceActor, ownerActor = misc_cache[sourceSerial] or misc_cache_pets[sourceSerial] or misc_cache[sourceName], misc_cache_petsOwners[sourceSerial] if (not sourceActor) then sourceActor, ownerActor, sourceName = _current_misc_container:GetOrCreateActor(sourceSerial, sourceName, sourceFlags, true) if (ownerActor) then if (sourceSerial ~= "") then misc_cache_pets [sourceSerial] = sourceActor misc_cache_petsOwners [sourceSerial] = ownerActor end if (not misc_cache[ownerActor.serial] and ownerActor.serial ~= "") then misc_cache[ownerActor.serial] = ownerActor end else if (sourceFlags) then misc_cache[sourceName] = sourceActor end end end --check if this is an item usage spellId = Details222.OnUseItem.Trinkets[spellId] or spellId ------------------------------------------------------------------------------------------------ --build containers on the fly --amount of casts by actors ~casts local castsByPlayer = _current_combat.amountCasts[sourceName] if (not castsByPlayer) then castsByPlayer = {} _current_combat.amountCasts[sourceName] = castsByPlayer end --rampage cast spam if (spellId == 184367 or spellId == 184707 or spellId == 201364) then --rampage spellIds (IDs from Retail - wow patch 10.1.0) local latestRampageCastByPlayer = (cacheAnything.rampage_cast_amount[sourceName] or 0) if (latestRampageCastByPlayer > time - 0.8) then return end cacheAnything.rampage_cast_amount[sourceName] = time end local amountOfCasts = _current_combat.amountCasts[sourceName][spellName] or 0 amountOfCasts = amountOfCasts + 1 _current_combat.amountCasts[sourceName][spellName] = amountOfCasts ------------------------------------------------------------------------------------------------ --record cooldowns cast which can't track with buff applyed --a player is the caster if (raid_members_cache[sourceSerial]) then --check if is a cooldown local cooldownInfo = defensive_cooldowns[spellId] if (cooldownInfo) then if (not targetName) then if (cooldownInfo.type == 2 or cooldownInfo.type == 3) then targetName = sourceName elseif (cooldownInfo.type == 4) then targetName = Loc ["STRING_RAID_WIDE"] else targetName = "--x--x--" end end return parser:add_defensive_cooldown(token, time, sourceSerial, sourceName, sourceFlags, targetSerial, targetName, targetFlags, targetRaidFlags, spellId, spellName) end else --enemy successful casts (not interrupted) if (bitBand(sourceFlags, 0x00000040) ~= 0 and sourceName) then --byte 2 = 4 (enemy) --damager ---@type actor local enemyActorObject = damage_cache[sourceSerial] if (not enemyActorObject) then enemyActorObject = _current_damage_container:GetOrCreateActor(sourceSerial, sourceName, sourceFlags, true) end if (enemyActorObject) then --actor spells table ---@type spelltable local spellTable = enemyActorObject.spells._ActorTable[spellId] if (not spellTable) then spellTable = enemyActorObject.spells:GetOrCreateSpell(spellId, true, token) end spellTable.successful_casted = spellTable.successful_casted + 1 end --add the spellId in the enemy_cast_cache table to store the time the enemy successfully cast a spell --check if the spell is in the table local enemyName = sourceName if (not enemy_cast_cache[time]) then enemy_cast_cache[time] = {enemyName, spellId, 1} else enemy_cast_cache[time][3] = enemy_cast_cache[time][3] + 1 end end end end --serach key: ~dispel ---@param token string ---@param time unixtime ---@param sourceSerial string ---@param sourceName string ---@param sourceFlags number ---@param targetSerial string ---@param targetName string ---@param targetFlags number ---@param targetFlags2 number ---@param spellId number ---@param spellName string ---@param spellType number ---@param extraSpellID number ---@param extraSpellName string ---@param extraSchool number function parser:dispell(token, time, sourceSerial, sourceName, sourceFlags, targetSerial, targetName, targetFlags, targetFlags2, spellId, spellName, spellType, extraSpellID, extraSpellName, extraSchool, auraType) if (not sourceName) then sourceName = "[*] " .. extraSpellName end if (not targetName) then targetName = "[*] " .. spellId end _current_misc_container.need_refresh = true ---@type actor, actor local sourceActor, ownerActor = misc_cache[sourceName] if (not sourceActor) then sourceActor, ownerActor, sourceName = _current_misc_container:GetOrCreateActor(sourceSerial, sourceName, sourceFlags, true) if (not ownerActor) then misc_cache[sourceName] = sourceActor end end --build containers on the fly if (not sourceActor.dispell) then ---@type number sourceActor.dispell = Details:GetOrderNumber(sourceName) ---@type table sourceActor.dispell_targets = {} ---@type spellcontainer sourceActor.dispell_spells = spellContainerClass:CreateSpellContainer(container_misc) ---@type table sourceActor.dispell_oque = {} end --spell reflection if (reflection_dispelid[spellId]) then --this aura could've been reflected to the caster after the dispel --save data about whom was dispelled by who and what spell it was reflection_dispels[targetSerial] = reflection_dispels[targetSerial] or {} reflection_dispels[targetSerial][extraSpellID] = { who_serial = sourceSerial, who_name = sourceName, who_flags = sourceFlags, spellid = spellId, spellname = spellName, spelltype = spellType, } end --last event update sourceActor.last_event = _tempo --total dispells in combat _current_total[4].dispell = _current_total[4].dispell + 1 if (sourceActor.grupo) then _current_gtotal[4].dispell = _current_gtotal[4].dispell + 1 end --actor dispell amount sourceActor.dispell = sourceActor.dispell + 1 --dispelled what if (extraSpellID) then sourceActor.dispell_oque[extraSpellID] = (sourceActor.dispell_oque[extraSpellID] or 0) + 1 end --actor targets sourceActor.dispell_targets[targetName] = (sourceActor.dispell_targets[targetName] or 0) + 1 --actor spells table ---@type spelltable local spellTable = sourceActor.dispell_spells._ActorTable[spellId] if (not spellTable) then spellTable = sourceActor.dispell_spells:GetOrCreateSpell(spellId, true, token) end _spell_utility_func(spellTable, targetName, token, extraSpellID, extraSpellName) --is has an owner, add the dispel to the owner as well if (ownerActor) then if (not ownerActor.dispell) then ownerActor.dispell = Details:GetOrderNumber(sourceName) ownerActor.dispell_targets = {} ownerActor.dispell_spells = spellContainerClass:CreateSpellContainer(container_misc) ownerActor.dispell_oque = {} end ownerActor.dispell = ownerActor.dispell + 1 ownerActor.dispell_targets[targetName] = (ownerActor.dispell_targets[targetName] or 0) + 1 ownerActor.last_event = _tempo if (extraSpellID) then ownerActor.dispell_oque[extraSpellID] = (ownerActor.dispell_oque[extraSpellID] or 0) + 1 end end end --serach key: ~ress function parser:ress(token, time, sourceSerial, sourceName, sourceFlags, targetSerial, targetName, targetFlags, targetFlags2, spellId, spellName) if (bitBand(sourceFlags, AFFILIATION_GROUP) == 0) then return end --do not register ress if not in combat if (not Details.in_combat) then return end _current_misc_container.need_refresh = true --main actor local sourceActor, ownerActor = misc_cache[sourceName] if (not sourceActor) then sourceActor, ownerActor, sourceName = _current_misc_container:GetOrCreateActor(sourceSerial, sourceName, sourceFlags, true) if (not ownerActor) then --if not a pet, add no cache misc_cache[sourceName] = sourceActor end end --update last event sourceActor.last_event = _tempo --build containers on the fly if (not sourceActor.ress) then sourceActor.ress = Details:GetOrderNumber(sourceName) sourceActor.ress_targets = {} sourceActor.ress_spells = spellContainerClass:CreateSpellContainer(container_misc) --cria o container das habilidades usadas para interromper end --add amount _current_total[4].ress = _current_total[4].ress + 1 if (sourceActor.grupo) then _current_combat.totals_grupo[4].ress = _current_combat.totals_grupo[4].ress + 1 end --add ress amount sourceActor.ress = sourceActor.ress + 1 --add battle ress if (UnitAffectingCombat(sourceName)) then --search for the latest death of the target actor for i = 1, #_current_combat.last_events_tables do if (_current_combat.last_events_tables[i][3] == targetName) then local deathLog = _current_combat.last_events_tables[i][1] --deathinfo index 1 = a table with the death log local jaTem = false for _, evento in ipairs(deathLog) do if (evento[1] and not evento[3]) then jaTem = true end end if (not jaTem) then tinsert(_current_combat.last_events_tables[i][1], 1, { 2, spellId, 1, time, UnitHealth(targetName) / UnitHealthMax(targetName), sourceName }) break end end end if (_hook_battleress) then for _, func in ipairs(_hook_battleress_container) do func (nil, token, time, sourceSerial, sourceName, sourceFlags, targetSerial, targetName, targetFlags, spellId, spellName) end end end --actor targets sourceActor.ress_targets[targetName] = (sourceActor.ress_targets[targetName] or 0) + 1 --actor spells table local spellTable = sourceActor.ress_spells._ActorTable[spellId] if (not spellTable) then spellTable = sourceActor.ress_spells:GetOrCreateSpell(spellId, true, token) end return _spell_utility_func(spellTable, targetName, token) end --serach key: ~cc function parser:break_cc(token, time, sourceSerial, sourceName, sourceFlags, targetSerial, targetName, targetFlags, targetFlags2, spellId, spellName, spellType, extraSpellID, extraSpellName, extraSchool, auraType) if (not crowdControlSpells[spellId]) then return elseif (bitBand(sourceFlags, AFFILIATION_GROUP) == 0) then return elseif (not targetName) then return --no target name, just quit end if (not spellName) then spellName = "Melee" end if (not sourceName) then sourceName = "[*] " .. spellName --if there's no sourceName, use spellName instead sourceFlags = 0xa48 sourceSerial = "" end _current_misc_container.need_refresh = true ---@type actorutility, actorutility local sourceActor, ownerActor = misc_cache[sourceName], nil if (not sourceActor) then --unknown if is a pet or player sourceActor, ownerActor, sourceName = _current_misc_container:GetOrCreateActor(sourceSerial, sourceName, sourceFlags, true) if (not ownerActor) then --not a pet: add to cache misc_cache[sourceName] = sourceActor end end --create the spell container on the fly if (not sourceActor.cc_break) then sourceActor.cc_break = Details:GetOrderNumber() sourceActor.cc_break_targets = {} sourceActor.cc_break_oque = {} ---@type spellcontainer sourceActor.cc_break_spells = spellContainerClass:CreateSpellContainer(container_misc) end sourceActor.last_event = _tempo --add amount _current_total[4].cc_break = _current_total[4].cc_break + 1 if (sourceActor.grupo) then _current_combat.totals_grupo[4].cc_break = _current_combat.totals_grupo[4].cc_break + 1 end --add amount sourceActor.cc_break = sourceActor.cc_break + 1 --broke what sourceActor.cc_break_oque[spellId] = (sourceActor.cc_break_oque[spellId] or 0) + 1 --actor targets sourceActor.cc_break_targets[targetName] = (sourceActor.cc_break_targets[targetName] or 0) + 1 ---@type spelltable local spellTable = sourceActor.cc_break_spells._ActorTable[extraSpellID] if (not spellTable) then spellTable = sourceActor.cc_break_spells:GetOrCreateSpell(extraSpellID, true, token) end return _spell_utility_func(spellTable, targetName, token, spellId, spellName) end --serach key: ~dead ~death ~morte ---when a player dies, save the events that lead to his death ---this is used to show the last events before the player died under the Deaths display ---the first index of the table which hold a single event tells the type of event happened, there are the types: ---boolean true: the player took damage ---boolean false: the player received heal from someone ---number 1: the player used a cooldown ---number 2: the player received a battle res ---number 3: tell which was the latest cooldown used by the player ---number 4: debuff the player received ---number 5: buff the player received ---number 6: emeny casted a spell ---@param token string ---@param time number ---@param sourceSerial string ---@param sourceName string ---@param sourceFlags number ---@param targetSerial string ---@param targetName string ---@param targetFlags number function parser:dead(token, time, sourceSerial, sourceName, sourceFlags, targetSerial, targetName, targetFlags) ---@cast Details222 details222 if (petContainer.Pets[targetSerial]) then petContainer.RemovePet(targetSerial) end --early checks and fixes if (not targetName) then return end ---@type actordamage local damageActor = _current_damage_container:GetActor(targetName) --check if the dead actor is an actor outside the player group, for instance a pvp player or a npc if (_in_combat and targetFlags and (not damageActor or (bitBand(targetFlags, 0x00000008) ~= 0 and not damageActor.grupo))) then --frags if (Details.only_pvp_frags and (bitBand(targetFlags, 0x00000400) == 0 or (bitBand(targetFlags, 0x00000040) == 0 and bitBand(targetFlags, 0x00000020) == 0))) then --byte 2 = 4 (HOSTILE) byte 3 = 4 (OBJECT_TYPE_PLAYER) return end if (not _current_combat.frags[targetName]) then _current_combat.frags[targetName] = 1 else _current_combat.frags[targetName] = _current_combat.frags[targetName] + 1 end _current_combat.frags_need_refresh = true --player death elseif (not UnitIsFeignDeath(targetName)) then if ( --player in your group (bitBand(targetFlags, AFFILIATION_GROUP) ~= 0 or (damageActor and damageActor.grupo)) and --must be a player bitBand(targetFlags, OBJECT_TYPE_PLAYER) ~= 0 and --must be in combat _in_combat ) then if (ignore_death_cache[targetName]) then ignore_death_cache[targetName] = nil return end local bIsMythicRun = false --check if this is a mythic+ run for overall deaths local mythicLevel = C_ChallengeMode and C_ChallengeMode.GetActiveKeystoneInfo() --classic wow doesn't not have C_ChallengeMode API if (mythicLevel and type(mythicLevel) == "number" and mythicLevel >= 2) then --several checks to be future proof bIsMythicRun = true end _current_misc_container.need_refresh = true --combat totals _current_total[4].dead = _current_total[4].dead + 1 _current_gtotal[4].dead = _current_gtotal[4].dead + 1 --main actor no container de misc que ir� armazenar a morte local thisPlayer, meu_dono = misc_cache [targetName] if (not thisPlayer) then --pode ser um desconhecido ou um pet thisPlayer, meu_dono, sourceName = _current_misc_container:GetOrCreateActor (targetSerial, targetName, targetFlags, true) if (not meu_dono) then --se n�o for um pet, add no cache misc_cache [targetName] = thisPlayer end end --table where the events will be placed in order, other events will also be added, for example, the last cooldown used by the player local eventsBeforePlayerDeath = {} --get the table where is registered the last events before the player died local recordedEvents = last_events_cache[targetName] if (not recordedEvents) then recordedEvents = _current_combat:CreateLastEventsTable(targetName) end --during a regular combat, 99.9% of the events aren't used by the death log --hence the process of getting data for the death log is made as fast as it can be --when a death occurs, the death log data is then parsed and built, the next 200 lines does this processing --lesses index = older / higher index = newer --[=[ eventTable [1] = type of the event eventTable [2] = spellId --spellid or false if this is a battle ress event eventTable [3] = amount --amount of damage or healing eventTable [4] = time --unix time eventTable [5] = player health when the event happened eventTable [6] = name of the actor which caused this event eventTable [7] = absorbed eventTable [8] = spell school eventTable [9] = friendly fire eventTable [10] = amount of overkill damage --]=] --get the index of the last event recorded local lastIndex = recordedEvents.n --first, remove all healing events where the player was at full health --here the event log gets reordered as in the parser it work with index recycling if (lastIndex < _amount_of_last_events+1 and not recordedEvents[lastIndex][4]) then --the last events table amount of indexes is less than the amount of events to store for i = 1, lastIndex-1 do if (recordedEvents[i][4] and recordedEvents[i][4]+_amount_of_last_events > time) then tinsert(eventsBeforePlayerDeath, recordedEvents[i]) end end else --go from the index where the last event was stored to the end of the table for i = lastIndex, _amount_of_last_events do if (recordedEvents[i][4] and recordedEvents[i][4]+_amount_of_last_events > time) then tinsert(eventsBeforePlayerDeath, recordedEvents[i]) end end --go from the start of the table to the index where the last event minus 1 was stored for i = 1, lastIndex-1 do if (recordedEvents[i][4] and recordedEvents[i][4]+_amount_of_last_events > time) then tinsert(eventsBeforePlayerDeath, recordedEvents[i]) end end end local bHadDeathEvent = false local firstEventTime local lastEventTime if (eventsBeforePlayerDeath[1]) then bHadDeathEvent = true firstEventTime = eventsBeforePlayerDeath[1][4] lastEventTime = eventsBeforePlayerDeath[#eventsBeforePlayerDeath][4] end --enemy_cast_cache store the time of the event as key and a table as value --the value has [1] = enemyName, [2] = spellid, [3] = amount of casts on that time (in case many enemies casted the same spell at the same time) --enemy_cast_cache[time] = {enemyName, spellId, 1} local enemyCastCache = enemy_cast_cache --as multiple enemies can have casted the same spell at the same time, iterate over the enemyCastCache and merge the casts that happened really close to each other --transfer the casts that happened within the event window of the player death to a new indexed table local enemyCastCacheIndexed = {} if (bHadDeathEvent) then for time, enemyCastTable in pairs(enemyCastCache) do if (time >= firstEventTime and time <= lastEventTime) then enemyCastCacheIndexed[#enemyCastCacheIndexed+1] = {time, unpack(enemyCastTable)} --time, enemyName, spellId, amount of casts end end end --sort enemy casts events to place earlier casts in the first indexes of the table table.sort(enemyCastCacheIndexed, function(t1, t2) return t1[1] < t2[1] end) --iterate among the enemy cast events and remove cast events that are too close to each other for i = #enemyCastCacheIndexed, 1, -1 do local previousEnemyCastEvent = enemyCastCacheIndexed[i-1] if (previousEnemyCastEvent) then local nextEnemyCastEvent = enemyCastCacheIndexed[i] if (previousEnemyCastEvent[1]+0.1 > nextEnemyCastEvent[1]) then if (previousEnemyCastEvent[3] == nextEnemyCastEvent[3]) then enemyCastCacheIndexed[i] = nil --as the event got removed, add a cast event to the previous event previousEnemyCastEvent[4] = previousEnemyCastEvent[4] + 1 end end end end --iterage among eventsBeforePlayerDeath and add the enemy casts events that happened within the last events time window local currentEnemyCastIndex = 1 for i = 1, #eventsBeforePlayerDeath do local eventTable = eventsBeforePlayerDeath[i] local eventTime = eventTable[4] for enemyCastEventIndex = currentEnemyCastIndex, #enemyCastCacheIndexed do local enemyCastEvent = enemyCastCacheIndexed[enemyCastEventIndex] if (enemyCastEvent) then local enemyCastTime = enemyCastEvent[1] local enemyName = enemyCastEvent[2] local spellId = enemyCastEvent[3] local castAmount = enemyCastEvent[4] if (enemyCastTime+0.1 > eventTime and enemyCastTime+0.1 - eventTime < 0.3) then --create a new event to show the cast and add it to the list of events before death local eventType = 6 --cast local newEventTable = {} newEventTable[1] = eventType newEventTable[2] = spellId --spellId newEventTable[3] = castAmount --amount of casts newEventTable[4] = enemyCastTime --when the event happened using unix time newEventTable[5] = 0 --player health when the event happened newEventTable[6] = enemyName --source name tinsert(eventsBeforePlayerDeath, i, newEventTable) currentEnemyCastIndex = enemyCastEventIndex + 1 break end end end end --verify if a cooldown near the death event was used if (thisPlayer.last_cooldown) then --create a new event to show the latest cooldown the player used before death and add it to the list of events before death local eventType = 3 --last cooldown used local eventTable = {} eventTable[1] = eventType eventTable[2] = thisPlayer.last_cooldown[2] --spellId eventTable[3] = 0 --amount of damage or healing but in this case is 0 eventTable[4] = thisPlayer.last_cooldown[1] --when the event happened using unix time eventTable[5] = 0 --player health when the event happened eventTable[6] = targetName --source name eventsBeforePlayerDeath[#eventsBeforePlayerDeath+1] = eventTable else --no last cooldown found so just add a last cooldown used event with no spellId and time 0 local eventTable = {} eventTable[1] = 3 --event type eventTable[2] = 0 --spellId eventTable[3] = 0 --amount of damage or healing but in this case is 0 eventTable[4] = 0 --when the event happened using unix time eventTable[5] = 0 --player health when the event happened eventTable[6] = targetName --source name eventsBeforePlayerDeath[#eventsBeforePlayerDeath+1] = eventTable end local maxHealth if (thisPlayer.arena_enemy) then --this is an arena enemy, get the heal with the unit Id local unitId = Details.arena_enemies[thisPlayer.nome] if (not unitId) then unitId = Details:GuessArenaEnemyUnitId(thisPlayer.nome) end if (unitId) then maxHealth = UnitHealthMax(unitId) end if (not maxHealth) then maxHealth = 0 end else maxHealth = UnitHealthMax(thisPlayer.nome) end local playerDeathTable local combatElapsedTime = GetTime() - _current_combat:GetStartTime() do local minutes, seconds = floor(combatElapsedTime / 60), floor(combatElapsedTime % 60) playerDeathTable = { eventsBeforePlayerDeath, --table time, --number unix time thisPlayer.nome, --string player name thisPlayer.classe, --string player class maxHealth, --number max health minutes .. "m " .. seconds .. "s", --time of death as string ["dead"] = true, ["last_cooldown"] = thisPlayer.last_cooldown, ["dead_at"] = combatElapsedTime, ["spec"] = thisPlayer.spec, } end tinsert(_current_combat.last_events_tables, #_current_combat.last_events_tables+1, playerDeathTable) --check if this is a mythic+ run for overall deaths if (bIsMythicRun) then --more checks for integrity if (Details.tabela_overall and Details.tabela_overall.last_events_tables) then --this is a mythic dungeon run, add the death to overall data --need to adjust the time of death, since this will show all deaths in the mythic run --first copy the table local overallDeathTable = detailsFramework.table.copy({}, playerDeathTable) --get the elapsed time local mythicPlusElapsedTime = GetTime() - Details.tabela_overall:GetStartTime() local minutes, seconds = floor(mythicPlusElapsedTime/60), floor(mythicPlusElapsedTime % 60) overallDeathTable[6] = minutes .. "m " .. seconds .. "s" overallDeathTable.dead_at = mythicPlusElapsedTime --save data about the mythic run in the deathTable which goes in the regular segment --confused? 'playerDeathTable' is added into the '_current_combat.last_events_tables' ~20 above on a tinsert playerDeathTable["mythic_plus"] = true playerDeathTable["mythic_plus_dead_at"] = mythicPlusElapsedTime playerDeathTable["mythic_plus_dead_at_string"] = overallDeathTable[6] --now add the death table into the overall data (this is the regular overall data, not the mythic plus overall data) tinsert(Details.tabela_overall.last_events_tables, #Details.tabela_overall.last_events_tables + 1, overallDeathTable) end end if (_hook_deaths) then --send event to registred functions for _, func in ipairs(_hook_deaths_container) do local successful, errortext = pcall(func, nil, token, time, sourceSerial, sourceName, sourceFlags, targetSerial, targetName, targetFlags, playerDeathTable, thisPlayer.last_cooldown, combatElapsedTime, maxHealth, playerDeathTable["mythic_plus_dead_at"] or 0) if (not successful) then Details:Msg("error occurred on a death hook function:", errortext) end end end --remove the player death events from the cache last_events_cache[targetName] = nil end end end --local WA_OnPlayerDeath = function(isFakeDeath, token, time, sourceSerial, sourceName, sourceFlags, targetSerial, targetName, targetFlags, deathLog, lastCooldown, combatElapsedTime, maxHealth, mythicPlusElapsedTime) --check auras with details! death log enabled --run a script in the aura which receives interesting data from the WA_OnPlayerDeath() --end --Details:InstallHook("HOOK_DEATH", WA_OnPlayerDeath) function parser:environment(token, time, sourceSerial, who_name, who_flags, alvo_serial, alvo_name, alvo_flags, alvo_flags2, env_type, amount) local spelId if (env_type == "Falling") then who_name = ENVIRONMENTAL_FALLING_NAME spelId = 3 elseif (env_type == "Drowning") then who_name = ENVIRONMENTAL_DROWNING_NAME spelId = 4 elseif (env_type == "Fatigue") then who_name = ENVIRONMENTAL_FATIGUE_NAME spelId = 5 elseif (env_type == "Fire") then who_name = ENVIRONMENTAL_FIRE_NAME spelId = 6 elseif (env_type == "Lava") then who_name = ENVIRONMENTAL_LAVA_NAME spelId = 7 elseif (env_type == "Slime") then who_name = ENVIRONMENTAL_SLIME_NAME spelId = 8 end return parser:spell_dmg(token, time, sourceSerial, who_name, who_flags, alvo_serial, alvo_name, alvo_flags, alvo_flags2, spelId or 1, env_type, 00000003, amount, -1, 1) end ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --core function parser:WipeSourceCache() Details:Destroy(monk_guard_talent) end local token_list = { -- neutral ["SPELL_SUMMON"] = parser.summon, --["SPELL_CAST_FAILED"] = parser.spell_fail } --[==[@debug@ Details.token_list = token_list --@end-debug@]==] --serach key: ~capture Details.capture_types = {"damage", "heal", "energy", "miscdata", "aura", "spellcast"} Details.capture_schedules = {} function Details:CaptureIsAllEnabled() for _, thisType in ipairs(Details.capture_types) do if (not Details.capture_real[thisType]) then return false end end return true end function Details:CaptureIsEnabled(capture) if (Details.capture_real[capture]) then return true end return false end function Details:CaptureRefresh() for _, thisType in ipairs(Details.capture_types) do if (Details.capture_current[thisType]) then Details:CaptureEnable(thisType) else Details:CaptureDisable(thisType) end end end function Details:CaptureGet(captureType) return Details.capture_real[captureType] end function Details:CaptureReset() Details:CancelAllCaptureSchedules() for _, thisType in ipairs(Details.capture_types) do Details:CaptureSet(true, thisType, true) end end function Details:CaptureSet(onOff, captureType, real, time) if (onOff == nil) then onOff = Details.capture_real[captureType] end if (real) then --hard switch Details.capture_real[captureType] = onOff Details.capture_current[captureType] = onOff else --soft switch Details.capture_current[captureType] = onOff if (time) then local scheduleId = math.random(1, 10000000) local new_schedule = Details:ScheduleTimer("CaptureTimeout", time, {captureType, scheduleId}) --todo: use Details.Schedule tinsert(Details.capture_schedules, {new_schedule, scheduleId}) end end Details:CaptureRefresh() end function Details:CancelAllCaptureSchedules() for i = 1, #Details.capture_schedules do local schedule_table, schedule_id = unpack(Details.capture_schedules[i]) Details:CancelTimer(schedule_table) end Details:Destroy(Details.capture_schedules) end function Details:CaptureTimeout (table) local capture_type, schedule_id = unpack(table) Details.capture_current [capture_type] = Details.capture_real [capture_type] Details:CaptureRefresh() for index, table in ipairs(Details.capture_schedules) do local id = table [2] if (schedule_id == id) then tremove(Details.capture_schedules, index) break end end end function Details:CaptureDisable (capture_type) capture_type = string.lower(capture_type) if (capture_type == "damage") then token_list ["SPELL_PERIODIC_DAMAGE"] = nil token_list ["SPELL_EXTRA_ATTACKS"] = nil token_list ["SPELL_DAMAGE"] = nil token_list ["SWING_DAMAGE"] = nil token_list ["RANGE_DAMAGE"] = nil token_list ["DAMAGE_SHIELD"] = nil token_list ["DAMAGE_SPLIT"] = nil token_list ["RANGE_MISSED"] = nil token_list ["SWING_MISSED"] = nil token_list ["SPELL_MISSED"] = nil token_list ["SPELL_BUILDING_MISSED"] = nil token_list ["SPELL_PERIODIC_MISSED"] = nil token_list ["DAMAGE_SHIELD_MISSED"] = nil token_list ["ENVIRONMENTAL_DAMAGE"] = nil token_list ["SPELL_BUILDING_DAMAGE"] = nil token_list ["SPELL_EMPOWER_START"] = nil token_list ["SPELL_EMPOWER_END"] = nil token_list ["SPELL_EMPOWER_INTERRUPT"] = nil elseif (capture_type == "heal") then token_list ["SPELL_HEAL"] = nil token_list ["SPELL_PERIODIC_HEAL"] = nil token_list ["SPELL_HEAL_ABSORBED"] = nil token_list ["SPELL_ABSORBED"] = nil elseif (capture_type == "aura") then token_list ["SPELL_AURA_APPLIED"] = parser.buff token_list ["SPELL_AURA_REMOVED"] = parser.unbuff token_list ["SPELL_AURA_REFRESH"] = parser.buff_refresh token_list ["SPELL_AURA_APPLIED_DOSE"] = parser.buff_refresh elseif (capture_type == "energy") then token_list ["SPELL_ENERGIZE"] = nil token_list ["SPELL_PERIODIC_ENERGIZE"] = nil elseif (capture_type == "spellcast") then token_list ["SPELL_CAST_SUCCESS"] = nil elseif (capture_type == "miscdata") then -- dispell token_list ["SPELL_DISPEL"] = nil token_list ["SPELL_STOLEN"] = nil -- cc broke token_list ["SPELL_AURA_BROKEN"] = nil token_list ["SPELL_AURA_BROKEN_SPELL"] = nil -- ress token_list ["SPELL_RESURRECT"] = nil -- interrupt token_list ["SPELL_INTERRUPT"] = nil -- dead token_list ["UNIT_DIED"] = nil token_list ["UNIT_DESTROYED"] = nil end end --SPELL_DRAIN --need research --SPELL_LEECH --need research --SPELL_PERIODIC_DRAIN --need research --SPELL_PERIODIC_LEECH --need research --SPELL_DISPEL_FAILED --need research --SPELL_BUILDING_HEAL --need research function Details:CaptureEnable (capture_type) capture_type = string.lower(capture_type) --retail if (capture_type == "damage") then token_list ["SPELL_PERIODIC_DAMAGE"] = parser.spell_dmg token_list ["SPELL_EXTRA_ATTACKS"] = nil --parser.spell_dmg_extra_attacks token_list ["SPELL_DAMAGE"] = parser.spell_dmg token_list ["SPELL_BUILDING_DAMAGE"] = parser.spell_dmg token_list ["SWING_DAMAGE"] = parser.spell_dmg --parser.swing token_list ["RANGE_DAMAGE"] = parser.spell_dmg --parser.range token_list ["DAMAGE_SHIELD"] = parser.spell_dmg token_list ["DAMAGE_SPLIT"] = parser.spell_dmg token_list ["RANGE_MISSED"] = parser.rangemissed token_list ["SWING_MISSED"] = parser.swingmissed token_list ["SPELL_MISSED"] = parser.missed token_list ["SPELL_PERIODIC_MISSED"] = parser.missed token_list ["SPELL_BUILDING_MISSED"] = parser.missed token_list ["DAMAGE_SHIELD_MISSED"] = parser.missed token_list ["ENVIRONMENTAL_DAMAGE"] = parser.environment token_list ["SPELL_EMPOWER_START"] = parser.spell_empower --evoker only token_list ["SPELL_EMPOWER_END"] = parser.spell_empower --evoker only token_list ["SPELL_EMPOWER_INTERRUPT"] = parser.spell_empower --evoker only elseif (capture_type == "heal") then token_list ["SPELL_HEAL"] = parser.heal token_list ["SPELL_PERIODIC_HEAL"] = parser.heal token_list ["SPELL_HEAL_ABSORBED"] = parser.heal_denied token_list ["SPELL_ABSORBED"] = parser.heal_absorb elseif (capture_type == "aura") then token_list ["SPELL_AURA_APPLIED"] = parser.buff token_list ["SPELL_AURA_REMOVED"] = parser.unbuff token_list ["SPELL_AURA_REFRESH"] = parser.buff_refresh token_list ["SPELL_AURA_APPLIED_DOSE"] = parser.buff_refresh elseif (capture_type == "energy") then token_list ["SPELL_ENERGIZE"] = parser.energize token_list ["SPELL_PERIODIC_ENERGIZE"] = parser.energize elseif (capture_type == "spellcast") then token_list ["SPELL_CAST_SUCCESS"] = parser.spellcast elseif (capture_type == "miscdata") then -- dispell token_list ["SPELL_DISPEL"] = parser.dispell token_list ["SPELL_STOLEN"] = parser.dispell -- cc broke token_list ["SPELL_AURA_BROKEN"] = parser.break_cc token_list ["SPELL_AURA_BROKEN_SPELL"] = parser.break_cc -- ress token_list ["SPELL_RESURRECT"] = parser.ress -- interrupt token_list ["SPELL_INTERRUPT"] = parser.interrupt -- dead token_list ["UNIT_DIED"] = parser.dead token_list ["UNIT_DESTROYED"] = parser.dead end end parser.original_functions = { ["spell_dmg"] = parser.spell_dmg, ["spell_dmg_extra_attacks"] = nil, --parser.spell_dmg_extra_attacks, ["swing"] = parser.spell_dmg, --parser.swing, ["range"] = parser.spell_dmg, --parser.range, ["rangemissed"] = parser.rangemissed, ["swingmissed"] = parser.swingmissed, ["missed"] = parser.missed, ["environment"] = parser.environment, ["heal"] = parser.heal, ["heal_absorb"] = parser.heal_absorb, ["heal_denied"] = parser.heal_denied, ["buff"] = parser.buff, ["unbuff"] = parser.unbuff, ["buff_refresh"] = parser.buff_refresh, ["energize"] = parser.energize, ["spellcast"] = parser.spellcast, ["dispell"] = parser.dispell, ["break_cc"] = parser.break_cc, ["ress"] = parser.ress, ["interrupt"] = parser.interrupt, ["dead"] = parser.dead, ["spell_empower"] = parser.spell_empower, } function parser:SetParserFunction (token, func) if (parser.original_functions [token]) then if (type(func) == "function") then parser [token] = func else parser [token] = parser.original_functions [token] end parser:RefreshFunctions() else return Details:Msg("Invalid Token for SetParserFunction.") end end local all_parser_tokens = { ["SPELL_PERIODIC_DAMAGE"] = "spell_dmg", ["SPELL_EXTRA_ATTACKS"] = nil, --"spell_dmg_extra_attacks", ["SPELL_DAMAGE"] = "spell_dmg", ["SPELL_BUILDING_DAMAGE"] = "spell_dmg", ["SWING_DAMAGE"] = "spell_dmg", --"swing" ["RANGE_DAMAGE"] = "spell_dmg", --"range", ["DAMAGE_SHIELD"] = "spell_dmg", ["DAMAGE_SPLIT"] = "spell_dmg", ["RANGE_MISSED"] = "rangemissed", ["SWING_MISSED"] = "swingmissed", ["SPELL_MISSED"] = "missed", ["SPELL_PERIODIC_MISSED"] = "missed", ["SPELL_BUILDING_MISSED"] = "missed", ["DAMAGE_SHIELD_MISSED"] = "missed", ["ENVIRONMENTAL_DAMAGE"] = "environment", ["SPELL_HEAL"] = "heal", ["SPELL_PERIODIC_HEAL"] = "heal", ["SPELL_HEAL_ABSORBED"] = "heal_denied", ["SPELL_ABSORBED"] = "heal_absorb", ["SPELL_AURA_APPLIED"] = "buff", ["SPELL_AURA_REMOVED"] = "unbuff", ["SPELL_AURA_REFRESH"] = "buff_refresh", ["SPELL_AURA_APPLIED_DOSE"] = "buff_refresh", ["SPELL_ENERGIZE"] = "energize", ["SPELL_PERIODIC_ENERGIZE"] = "energize", ["SPELL_CAST_SUCCESS"] = "spellcast", ["SPELL_DISPEL"] = "dispell", ["SPELL_STOLEN"] = "dispell", ["SPELL_AURA_BROKEN"] = "break_cc", ["SPELL_AURA_BROKEN_SPELL"] = "break_cc", ["SPELL_RESURRECT"] = "ress", ["SPELL_INTERRUPT"] = "interrupt", ["UNIT_DIED"] = "dead", ["UNIT_DESTROYED"] = "dead", } function parser:RefreshFunctions() for CLUE_ID, token in pairs(all_parser_tokens) do if (token_list [CLUE_ID]) then --not disabled token_list [CLUE_ID] = parser [token] end end end function Details:CallWipe (from_slash) Details:Msg("Wipe has been called by your raid leader.") if (Details.wipe_called) then if (from_slash) then return Details:Msg(Loc ["STRING_WIPE_ERROR1"]) else return end elseif (not Details.encounter_table.id) then if (from_slash) then return Details:Msg(Loc ["STRING_WIPE_ERROR2"]) else return end end local eTable = Details.encounter_table --finish the encounter local successful_ended = Details.parser_functions:ENCOUNTER_END (eTable.id, eTable.name, eTable.diff, eTable.size, 0) if (successful_ended) then --we wiped Details.wipe_called = true --cancel the on going captures schedules Details:CancelAllCaptureSchedules() --disable it Details:CaptureSet (false, "damage", false) Details:CaptureSet (false, "energy", false) Details:CaptureSet (false, "aura", false) Details:CaptureSet (false, "energy", false) Details:CaptureSet (false, "spellcast", false) if (from_slash) then if (UnitIsGroupLeader ("player")) then Details:SendHomeRaidData ("WI") end end local lower_instance = Details:GetLowerInstanceNumber() if (lower_instance) then lower_instance = Details:GetInstance(lower_instance) lower_instance:InstanceAlert (Loc ["STRING_WIPE_ALERT"], {[[Interface\CHARACTERFRAME\UI-StateIcon]], 18, 18, false, 0.5, 1, 0, 0.5}, 4) end else if (from_slash) then return Details:Msg(Loc ["STRING_WIPE_ERROR3"]) else return end end end -- PARSER --serach key: ~parser ~events ~start ~inicio function Details:FlagNewCombat_PVPState() if (Details.is_in_battleground) then Details.tabela_vigente.pvp = true Details.tabela_vigente.is_pvp = {name = Details.zone_name, mapid = Details.zone_id} elseif (Details.is_in_arena) then Details.tabela_vigente.arena = true Details.tabela_vigente.is_arena = {name = Details.zone_name, zone = Details.zone_name, mapid = Details.zone_id} end end function Details:GetZoneType() return Details.zone_type end local gotAggro = false function Details.parser_functions:UNIT_FLAGS(...) if (gotAggro) then return end if (Details:GetZoneType() ~= "raid" and Details:GetZoneType() ~= "party") then return end local unitId = ... if (UnitExists(unitId)) then if (UnitAffectingCombat(unitId) and not UnitAffectingCombat("player")) then Details.LastAggro = UnitName(unitId) gotAggro = true C_Timer.After(1, function() gotAggro = false end) end end end function Details.parser_functions:ZONE_CHANGED_NEW_AREA(...) return Details.Schedules.After(1, Details.Check_ZONE_CHANGED_NEW_AREA) end --~zone ~area function Details:Check_ZONE_CHANGED_NEW_AREA() local zoneName, zoneType, _, _, _, _, _, zoneMapID = GetInstanceInfo() Details.zone_type = zoneType Details.zone_id = zoneMapID Details.zone_name = zoneName _in_resting_zone = IsResting() if (_in_resting_zone) then Details:CaptureReset() end parser:WipeSourceCache() Details.listener:UnregisterEvent("UNIT_FLAGS") _is_in_instance = false if (zoneType == "party" or zoneType == "raid") then _is_in_instance = true end if (Details.last_zone_type ~= zoneType) then Details:SendEvent("ZONE_TYPE_CHANGED", nil, zoneType) Details.last_zone_type = zoneType for index, instancia in ipairs(Details.tabela_instancias) do if (instancia.ativa) then instancia:AdjustAlphaByContext(true) end end Details222.Cache.ClearAugmentationCache() end Details.time_type = Details.time_type_original if (Details.is_in_arena and zoneType ~= "arena") then Details:LeftArena() end --check if the player left a battleground if (Details.is_in_battleground and zoneType ~= "pvp") then Details.pvp_parser_frame:StopBgUpdater() Details.is_in_battleground = nil Details.time_type = Details.time_type_original end if (zoneType == "pvp") then --battlegrounds if (Details.debug) then Details:Msg("(debug) zone type is now 'pvp'.") end if(not Details.is_in_battleground and Details.overall_clear_pvp) then Details.tabela_historico:ResetOverallData() end Details.is_in_battleground = true if (_in_combat and not _current_combat.pvp) then Details:SairDoCombate() end if (not _in_combat) then Details222.StartCombat() end _current_combat.pvp = true _current_combat.is_pvp = {name = zoneName, mapid = zoneMapID} if (Details.use_battleground_server_parser) then if (Details.time_type == 1) then Details.time_type_original = 1 Details.time_type = 2 end Details.pvp_parser_frame:StartBgUpdater() else if (Details.force_activity_time_pvp) then Details.time_type_original = Details.time_type Details.time_type = 1 end end Details.lastBattlegroundStartTime = GetTime() elseif (zoneType == "arena") then if (Details.debug) then Details:Msg("(debug) zone type is now 'arena'.") end if (Details.force_activity_time_pvp) then Details.time_type_original = Details.time_type Details.time_type = 1 end if (not Details.is_in_arena) then if (Details.overall_clear_pvp) then Details.tabela_historico:ResetOverallData() end --reset spec cache if broadcaster requested if (Details.streamer_config.reset_spec_cache) then Details:Destroy(Details.cached_specs) end end Details.is_in_arena = true Details:EnteredInArena() else local inInstance = IsInInstance() if ((zoneType == "raid" or zoneType == "party") and inInstance) then Details:CheckForAutoErase(zoneMapID) --if the current raid is current tier raid, pre-load the storage database if (zoneType == "raid") then if (Details:IsZoneIdFromCurrentExpansion(zoneMapID)) then Details.ScheduleLoadStorage() end end Details.listener:RegisterEvent("UNIT_FLAGS") end if (Details:IsInInstance()) then Details.last_instance = zoneMapID end --if (_current_combat.pvp) then -- _current_combat.pvp = false --end end Details222.AutoRunCode.DispatchAutoRunCode("on_zonechanged") Details:SchedulePetUpdate(7) Details:CheckForPerformanceProfile() end function Details.parser_functions:PLAYER_ENTERING_WORLD () return Details.parser_functions:ZONE_CHANGED_NEW_AREA() end -- ~encounter --ENCOUNTER START function Details.parser_functions:ENCOUNTER_START(...) if (Details.debug) then Details:Msg("(debug) |cFFFFFF00ENCOUNTER_START|r event triggered.") end Details222.Perf.WindowUpdate = 0 Details222.Perf.WindowUpdateC = true Details.latest_ENCOUNTER_END = Details.latest_ENCOUNTER_END or 0 if (Details.latest_ENCOUNTER_END + 10 > GetTime()) then return end --leave the current combat when the encounter start, if is doing a mythic plus dungeons, check if the options allows to create a dedicated segment for the boss fight if ((_in_combat and not Details.tabela_vigente.is_boss) and (not Details.MythicPlus.Started or Details.mythic_plus.boss_dedicated_segment)) then Details:SairDoCombate() end local encounterID, encounterName, difficultyID, raidSize = select(1, ...) local zoneName, zoneType, _, _, _, _, _, zoneMapID = GetInstanceInfo() if (zoneType == "party") then local openRaidLib = LibStub:GetLibrary("LibOpenRaid-1.0", true) if (openRaidLib) then openRaidLib.KeystoneInfoManager.SendPlayerKeystoneInfoToParty() end end if (Details:IsZoneIdFromCurrentExpansion(zoneMapID)) then Details.current_exp_raid_encounters[encounterID] = true end Details222.DebugMsg("|cFFFFFF00Who Aggro by UNIT_FLAGS:", Details.LastAggro) if (not Details.WhoAggroTimer and Details.announce_firsthit.enabled) then Details.WhoAggroTimer = C_Timer.NewTimer(0.1, whoAggro) for i = 1, 5 do local boss = UnitExists("boss" .. i) if (boss) then local targetName = UnitName("boss" .. i .. "target") if (targetName and type(targetName) == "string") then Details.bossTargetAtPull = targetName break end end end end if (IsInGuild() and IsInRaid() and Details.announce_damagerecord.enabled and Details222.storageLoaded) then Details.TellDamageRecord = C_Timer.NewTimer(0.6, Details.PrintEncounterRecord) Details.TellDamageRecord.Boss = encounterID Details.TellDamageRecord.Diff = difficultyID end _current_encounter_id = encounterID Details.boss1_health_percent = 1 local DBM_MOD, DBM_TIME = Details.encounter_table.DBM_Mod, Details.encounter_table.DBM_ModTime Details:Destroy(Details.encounter_table) Details.encounter_table.phase = 1 --store the encounter time inside the encounter table for the encounter plugin Details.encounter_table.start = GetTime() Details.encounter_table["end"] = nil Details.encounter_table.id = encounterID Details.encounter_table.name = encounterName Details.encounter_table.diff = difficultyID Details.encounter_table.size = raidSize Details.encounter_table.zone = zoneName Details.encounter_table.mapid = zoneMapID if (DBM_MOD and DBM_TIME == time()) then Details.encounter_table.DBM_Mod = DBM_MOD end local encounterTable, bossIndex = Details:GetBossEncounterDetailsFromEncounterId(zoneMapID, encounterID) if (encounterTable) then Details.encounter_table.index = bossIndex end Details:SendEvent("COMBAT_ENCOUNTER_START", nil, ...) Details222.CacheKeystoneForAllGroupMembers() end --ENCOUNRTER_END function Details.parser_functions:ENCOUNTER_END(...) if (Details.debug) then Details:Msg("(debug) |cFFFFFF00ENCOUNTER_END|r event triggered.") end Details222.Perf.WindowUpdateC = false _current_encounter_id = nil local encounterID, encounterName, difficultyID, raidSize, endStatus = select(1, ...) if (not Details.encounter_table.start) then Details:Msg("encounter table without start time.") return end Details.latest_ENCOUNTER_END = Details.latest_ENCOUNTER_END or 0 if (Details.latest_ENCOUNTER_END + 15 > GetTime()) then return end Details.latest_ENCOUNTER_END = GetTime() Details.encounter_table["end"] = GetTime() local bossIcon = Details:GetBossEncounterTexture(encounterName) _current_combat.bossIcon = bossIcon _current_combat.EncounterName = encounterName if (_in_combat) then if (endStatus == 1) then Details.encounter_table.kill = true Details:SairDoCombate(true, {encounterID, encounterName, difficultyID, raidSize, endStatus}) --killed else Details.encounter_table.kill = false Details:SairDoCombate(false, {encounterID, encounterName, difficultyID, raidSize, endStatus}) --wipe end else if ((Details.tabela_vigente:GetEndTime() or 0) + 2 >= Details.encounter_table ["end"]) then Details.tabela_vigente:SetStartTime(Details.encounter_table ["start"]) Details.tabela_vigente:SetEndTime(Details.encounter_table ["end"]) Details:RefreshMainWindow(-1, true) end end petContainer.Reset() C_Timer.After(1, function() petContainer.PetScan("ENCOUNTER_END") end) --tag item level of all players local openRaidLib = LibStub:GetLibrary("LibOpenRaid-1.0", true) local allPlayersGear = openRaidLib and openRaidLib.GetAllUnitsGear() local status = xpcall(function() for actorIndex, actorObject in Details:GetCurrentCombat():GetContainer(DETAILS_ATTRIBUTE_DAMAGE):ListActors() do local gearInfo = allPlayersGear and allPlayersGear[actorObject:Name()] if (gearInfo) then actorObject.ilvl = gearInfo.ilevel end end end, geterrorhandler()) if (not status) then Details:Msg("ilvl error:", status) end Details:SendEvent("COMBAT_ENCOUNTER_END", nil, ...) Details222.Cache.ClearAugmentationCache() Details:Destroy(Details.encounter_table) Details:Destroy(dk_pets_cache.army) Details:Destroy(dk_pets_cache.apoc) Details:Destroy(empower_cache) return true end function Details.parser_functions:UNIT_PET(unitId) petContainer.UNIT_PET(unitId) Details:SchedulePetUpdate(1) end local autoSwapDynamicOverallData = function(instance, inCombat) local mainDisplayGroup, subDisplay = instance:GetDisplay() local customDisplayAttributeId = 5 --entering in combat, swap to dynamic overall damage if (inCombat) then if (mainDisplayGroup == DETAILS_ATTRIBUTE_DAMAGE and subDisplay == DETAILS_SUBATTRIBUTE_DAMAGEDONE) then local segment = instance:GetSegment() if (segment == DETAILS_SEGMENTID_OVERALL) then local dynamicOverallDataCustomID = Details222.GetCustomDisplayIDByName(Loc["STRING_CUSTOM_DYNAMICOVERAL"]) instance:SetDisplay(segment, customDisplayAttributeId, dynamicOverallDataCustomID) end end else --leaving combat if (mainDisplayGroup == customDisplayAttributeId) then local dynamicOverallDataCustomID = Details222.GetCustomDisplayIDByName(Loc["STRING_CUSTOM_DYNAMICOVERAL"]) if (subDisplay == dynamicOverallDataCustomID) then local segment = instance:GetSegment() if (segment == DETAILS_SEGMENTID_OVERALL) then instance:SetDisplay(true, DETAILS_ATTRIBUTE_DAMAGE, DETAILS_SUBATTRIBUTE_DAMAGEDONE) end end end end end function Details.parser_functions:PLAYER_REGEN_DISABLED(...) C_Timer.After(0, function() if (not Details.bossTargetAtPull) then if (UnitExists("boss1")) then local bossTarget = UnitName("boss1target") if (bossTarget) then Details.bossTargetAtPull = bossTarget end end end end) if (Details.auto_swap_to_dynamic_overall) then Details:InstanceCall(autoSwapDynamicOverallData, true) end Details.combat_id_global = Details.combat_id_global + 1 _global_combat_counter = Details.combat_id_global _trinket_data_cache = Details:GetTrinketData() if (Details.zone_type == "pvp" and not Details.use_battleground_server_parser) then if (_in_combat) then Details:SairDoCombate() end Details222.StartCombat() end if (not Details:CaptureGet("damage")) then Details222.StartCombat() end --essa parte do solo mode ainda sera usada? if (Details.solo and Details.PluginCount.SOLO > 0) then --solo mode local esta_instancia = Details.tabela_instancias[Details.solo] esta_instancia.atualizando = true end for index, instancia in ipairs(Details.tabela_instancias) do if (instancia.ativa) then --1 = none, we doesn't need to call instancia:AdjustAlphaByContext(true) end end Details222.AutoRunCode.DispatchAutoRunCode("on_entercombat") Details.tabela_vigente.CombatStartedAt = GetTime() local bSilentOnError = true local openRaidLib = LibStub:GetLibrary("LibOpenRaid-1.0", bSilentOnError) --isWOTLK isERA if (openRaidLib) then wipe(gearCache) local bNeedPlayerGear = true if (IsInRaid()) then local unitIdCache = Details222.UnitIdCache.Raid bNeedPlayerGear = false for i = 1, 40 do local unitId = unitIdCache[i] local guid = UnitGUID(unitId) if (guid) then local unitGearInfo = openRaidLib.GetUnitGear(unitId) if (unitGearInfo) then gearCache[guid] = { tierAmount = unitGearInfo.tierAmount or 0, ilevel = unitGearInfo.ilevel or 0, } end end end elseif (IsInGroup()) then local unitIdCache = Details222.UnitIdCache.Party for i = 1, 4 do local unitId = unitIdCache[i] local guid = UnitGUID(unitId) if (guid) then local unitGearInfo = openRaidLib.GetUnitGear(unitId) if (unitGearInfo) then gearCache[guid] = { tierAmount = unitGearInfo.tierAmount or 0, ilevel = unitGearInfo.ilevel or 0, } end end end end if (bNeedPlayerGear) then local playerGearInfo = openRaidLib.GetUnitGear("player") if (playerGearInfo) then gearCache[UnitGUID("player")] = { tierAmount = playerGearInfo.tierAmount or 0, ilevel = playerGearInfo.ilevel or 0, } end end end end --in case the player left the raid during the encounter --this function clear the encounter_id from the cache local checkIfEncounterIsDone = function() if (not _current_encounter_id) then return end if (IsInRaid()) then --raid local inCombat = false for i = 1, GetNumGroupMembers() do if (UnitAffectingCombat("raid" .. i)) then inCombat = true break end end if (not inCombat) then _current_encounter_id = nil end elseif (IsInGroup()) then --party (dungeon) local inCombat = false for i = 1, GetNumGroupMembers() -1 do if (UnitAffectingCombat("party" .. i)) then inCombat = true break end end if (not inCombat) then _current_encounter_id = nil end else _current_encounter_id = nil end end --this function is guaranteed to run after a combat is done --can also run when the player leaves combat state (regen enabled) function Details:RunScheduledEventsAfterCombat(OnRegenEnabled) if (Details.debug) then --Details:Msg("(debug) running scheduled events after combat end.") end --when the user requested data from the storage but is in combat lockdown if (Details.schedule_storage_load) then Details.schedule_storage_load = nil Details.ScheduleLoadStorage() end --store a boss encounter when out of combat since it might need to load the storage if (Details.schedule_store_boss_encounter) then if (not Details.logoff_saving_data) then local successful, errortext = pcall(Details.Database.StoreEncounter) if (not successful) then Details:Msg("error occurred on Details.Database.StoreEncounter():", errortext) end end Details.schedule_store_boss_encounter = nil end if (Details.schedule_store_boss_encounter_wipe) then if (not Details.logoff_saving_data) then local successful, errortext = pcall(Details.Database.StoreWipe) if (not successful) then Details:Msg("error occurred on Details.Database.StoreWipe():", errortext) end end Details.schedule_store_boss_encounter_wipe = nil end --when a large amount of data has been removed and the player is in combat, schedule to run the hard garbage collector (the blizzard one, not the details! internal) if (Details.schedule_hard_garbage_collect) then if (Details.debug) then Details:Msg("(debug) found schedule collectgarbage().") end Details.schedule_hard_garbage_collect = false collectgarbage() end for index, instancia in ipairs(Details.tabela_instancias) do if (instancia.ativa) then --1 = none, we doesn't need to call instancia:AdjustAlphaByContext(true) end end if (not OnRegenEnabled) then Details:Destroy(bitfield_swap_cache) Details:Destroy(empower_cache) Details222.AutoRunCode.DispatchAutoRunCode("on_leavecombat") end if (Details.solo and Details.PluginCount.SOLO > 0) then --code too old and I don't have documentation for it if (Details.SoloTables.Plugins [Details.SoloTables.Mode].Stop) then Details.SoloTables.Plugins [Details.SoloTables.Mode].Stop() end end end function Details.parser_functions:CHALLENGE_MODE_END(...) --doesn't exists Details:Msg("CHALLENGE_MODE_END", GetTime()) end --WORLD_STATE_TIMER_START are a timer only used on scenarios function Details.parser_functions:WORLD_STATE_TIMER_START(...) local zoneName, instanceType, difficultyID, difficultyName, maxPlayers, dynamicDifficulty, isDynamic, instanceMapID, instanceGroupSize = GetInstanceInfo() if (difficultyID == 8) then if (Details222.MythicPlus.CHALLENGE_MODE_START_AT) then --would be nil if a world timer starts before the challenge mode start event --todo: should also check if the mythic+ is active if (Details222.MythicPlus.CHALLENGE_MODE_START_AT + 10 > GetTime()) then if (not Details222.MythicPlus.WorldStateTimerStartAt) then local payload1, payload2, payload3 = ... payload1 = payload1 or "" payload2 = payload2 or "" payload3 = payload3 or "" Details222.MythicPlus.LogStep("Event: WORLD_STATE_TIMER_START | payload1: " .. payload1 .. " | payload2: " .. payload2 .. " | payload3: " .. payload3) Details:SendEvent("COMBAT_MYTHICDUNGEON_START") Details222.MythicPlus.WorldStateTimerStartAt = time() end end end end end function Details.parser_functions:CHALLENGE_MODE_START(...) --~challenge ~mythic+ ~m+ --send mythic dungeon start event if (Details.debug) then end if (DetailsMythicPlusFrame.ZoneLeftTimer and not DetailsMythicPlusFrame.ZoneLeftTimer:IsCancelled()) then DetailsMythicPlusFrame.ZoneLeftTimer:Cancel() end local zoneName, instanceType, difficultyID, difficultyName, maxPlayers, dynamicDifficulty, isDynamic, instanceMapID, instanceGroupSize = GetInstanceInfo() if (difficultyID == 8) then Details222.MythicPlus.CHALLENGE_MODE_START_AT = GetTime() Details222.MythicPlus.WorldStateTimerStartAt = nil Details222.MythicPlus.WorldStateTimerEndAt = nil Details222.MythicPlus.LogStep("Event: CHALLENGE_MODE_START") end end local keystoneLevels = {} Details.KeystoneLevels = keystoneLevels --save the keystone level for each of the 5 party members local saveGroupMembersKeystoneLevel = function() wipe(keystoneLevels) local libOpenRaid = LibStub("LibOpenRaid-1.0", true) if (libOpenRaid) then for i = 1, GetNumGroupMembers()-1 do local unitId = "party" .. i if (UnitExists(unitId)) then local unitKeystoneInfo = libOpenRaid.GetKeystoneInfo(unitId) if (unitKeystoneInfo) then local unitName = Details:GetFullName(unitId) keystoneLevels[unitName] = unitKeystoneInfo.level end end end local unitId = "player" if (UnitExists(unitId)) then local unitKeystoneInfo = libOpenRaid.GetKeystoneInfo(unitId) if (unitKeystoneInfo) then local unitName = Details:GetFullName(unitId) keystoneLevels[unitName] = unitKeystoneInfo.level end end end end function Details222.CacheKeystoneForAllGroupMembers() local _, instanceType, difficultyID = GetInstanceInfo() if (instanceType == "party") then saveGroupMembersKeystoneLevel() end end function Details.parser_functions:CHALLENGE_MODE_COMPLETED(...) --~complete ~finish ~mythic ~m+ Details222.MythicPlus.WorldStateTimerEndAt = time() --wait until the keystone is updated and send it to the party saveGroupMembersKeystoneLevel() ---@type number mapID ---@type number level ---@type number time ---@type boolean onTime ---@type number keystoneUpgradeLevels ---@type boolean practiceRun ---@type number oldDungeonScore ---@type number newDungeonScore ---@type boolean isMapRecord ---@type boolean isAffixRecord ---@type number primaryAffix ---@type boolean isEligibleForScore ---@type table upgradeMembers local mapID, level, time, onTime, keystoneUpgradeLevels, practiceRun, oldDungeonScore, newDungeonScore, isAffixRecord, isMapRecord, primaryAffix, isEligibleForScore, upgradeMembers = C_ChallengeMode.GetCompletionInfo() Details222.MythicPlus.MapID = mapID Details222.MythicPlus.Level = level --level of the key just finished Details222.MythicPlus.OnTime = onTime Details222.MythicPlus.KeystoneUpgradeLevels = keystoneUpgradeLevels Details222.MythicPlus.PracticeRun = practiceRun Details222.MythicPlus.OldDungeonScore = oldDungeonScore Details222.MythicPlus.NewDungeonScore = newDungeonScore Details222.MythicPlus.IsAffixRecord = isAffixRecord Details222.MythicPlus.IsMapRecord = isMapRecord Details222.MythicPlus.PrimaryAffix = primaryAffix Details222.MythicPlus.IsEligibleForScore = isEligibleForScore Details222.MythicPlus.UpgradeMembers = upgradeMembers local dungeonName, id, timeLimit, texture, backgroundTexture = C_ChallengeMode.GetMapUIInfo(mapID) Details222.MythicPlus.DungeonName = dungeonName Details222.MythicPlus.DungeonID = id Details222.MythicPlus.TimeLimit = timeLimit Details222.MythicPlus.Texture = texture Details222.MythicPlus.BackgroundTexture = backgroundTexture if (time) then Details222.MythicPlus.time = math.floor(time / 1000) Details:Msg("run elapsed time:", DetailsFramework:IntegerToTimer(time / 1000)) else Details222.MythicPlus.time = 0.1 end if (Details.mythic_plus.show_damage_graphic) then C_Timer.After(0, function() if (ChallengeModeCompleteBanner) then ChallengeModeCompleteBanner.timeToHold = 0.01 end end) end --send mythic dungeon end event local zoneName, instanceType, difficultyID, difficultyName, maxPlayers, dynamicDifficulty, isDynamic, instanceMapID, instanceGroupSize = GetInstanceInfo() if (difficultyID == 8) then Details:SendEvent("COMBAT_MYTHICDUNGEON_END") end Details222.MythicPlus.LogStep("===== Mythic+ Finished =====") end function Details.parser_functions:PLAYER_REGEN_ENABLED(...) --check if the player is a rogue and has the aura Vanish if (not IsInGroup() and not IsInRaid()) then if (Details.playerclass == "ROGUE") then --if the player has vanish aura, skip this check ---@type aurainfo local auraInfo = C_UnitAuras.GetPlayerAuraBySpellID(11327) if (auraInfo) then return true end end end if (Details.auto_swap_to_dynamic_overall) then Details:InstanceCall(autoSwapDynamicOverallData, false) end --elapsed combat time Details.LatestCombatDone = GetTime() local currentCombat = Details:GetCurrentCombat() currentCombat.CombatEndedAt = GetTime() currentCombat.TotalElapsedCombatTime = currentCombat.CombatEndedAt - (currentCombat.CombatStartedAt or 0) C_Timer.After(10, checkIfEncounterIsDone) --playing alone, just finish the combat right now if (not IsInGroup() and not IsInRaid()) then currentCombat.playing_solo = true Details:SairDoCombate() else --is in a raid or party group C_Timer.After(1, function() if (IsInRaid()) then local raidUnitIdCache = Details222.UnitIdCache.Raid local bInCombat = false for i = 1, GetNumGroupMembers() do if (UnitAffectingCombat(raidUnitIdCache[i])) then bInCombat = true break end end if (not bInCombat) then Details:RunScheduledEventsAfterCombat(true) end elseif (IsInGroup()) then local bInCombat = false local partyUnitIds = Details222.UnitIdCache.Party for i = 1, #partyUnitIds do if (UnitExists(partyUnitIds[i]) and UnitAffectingCombat(partyUnitIds[i])) then bInCombat = true break end end if (not bInCombat) then Details:RunScheduledEventsAfterCombat(true) end end end) end end function Details.parser_functions:PLAYER_TALENT_UPDATE() if (IsInGroup() or IsInRaid()) then if (Details.SendTalentTimer and not Details.SendTalentTimer:IsCancelled()) then Details.SendTalentTimer:Cancel() end Details.SendTalentTimer = C_Timer.NewTimer(11, function() Details:SendCharacterData() end) end end function Details:RefreshPlayerSpecialization() local specIndex = detailsFramework.GetSpecialization() if (specIndex) then local specID = detailsFramework.GetSpecializationInfo(specIndex) if (specID and specID ~= 0) then local guid = UnitGUID("player") if (guid) then Details.cached_specs[guid] = specID Details.playerspecid = specID end end end end function Details.parser_functions:PLAYER_SPECIALIZATION_CHANGED() --some parts of details! does call this function, check first for past expansions if (detailsFramework.IsTimewalkWoW()) then return end Details:RefreshPlayerSpecialization() if (IsInGroup() or IsInRaid()) then if (Details.SendTalentTimer and not Details.SendTalentTimer:IsCancelled()) then Details.SendTalentTimer:Cancel() end Details.SendTalentTimer = C_Timer.NewTimer(11, function() Details:SendCharacterData() end) end end --[=[ --this is mostly triggered when the player enters in a dual against another player function Details.parser_functions:UNIT_FACTION(unit) if (true) then --disable until figure out how to make this work properlly --at the moment this event is firing at bgs, arenas, etc making horde icons to show at random return end --check if outdoors --unit was nil, nameplate might bug here, it should track after the event if (Details.zone_type == "none" and unit) then local serial = UnitGUID(unit) --the serial is valid and isn't THE player and the serial is from a player? if (serial and serial ~= UnitGUID("player") and serial:find("Player")) then Details.duel_candidates[serial] = GetTime() local playerName = Details:GetFullName(unit) --check if the player is inside the current combat and flag the objects if (playerName and _current_combat) then local enemyPlayer1 = _current_combat:GetActor(1, playerName) local enemyPlayer2 = _current_combat:GetActor(2, playerName) local enemyPlayer3 = _current_combat:GetActor(3, playerName) local enemyPlayer4 = _current_combat:GetActor(4, playerName) if (enemyPlayer1) then --set to show when the player is solo play enemyPlayer1.grupo = true enemyPlayer1.enemy = true if (IsInGroup()) then --broadcast the enemy to group members so they can "watch" the damage end end if (enemyPlayer2) then enemyPlayer2.grupo = true enemyPlayer2.enemy = true end if (enemyPlayer3) then enemyPlayer3.grupo = true enemyPlayer3.enemy = true end if (enemyPlayer4) then enemyPlayer4.grupo = true enemyPlayer4.enemy = true end end end end end --]=] function Details.parser_functions:ROLE_CHANGED_INFORM(...) if (Details.last_assigned_role ~= _UnitGroupRolesAssigned("player")) then Details:CheckSwitchOnLogon (true) Details.last_assigned_role = _UnitGroupRolesAssigned("player") end end function Details.parser_functions:PLAYER_ROLES_ASSIGNED(...) if (Details.last_assigned_role ~= _UnitGroupRolesAssigned("player")) then Details:CheckSwitchOnLogon (true) Details.last_assigned_role = _UnitGroupRolesAssigned("player") end end function Details:InGroup() return Details.in_group end function Details.parser_functions:GROUP_ROSTER_UPDATE(...) if (not Details.in_group) then Details.in_group = IsInGroup() or IsInRaid() if (Details.in_group) then --player entered in a group, cleanup and set the new enviromnent Details222.GarbageCollector.RestartInternalGarbageCollector(true) petContainer.Reset() Details:SchedulePetUpdate(1) Details:InstanceCall(Details.AdjustAlphaByContext) Details:CheckSwitchOnLogon() Details:CheckVersion() Details:SendEvent("GROUP_ONENTER") Details222.AutoRunCode.DispatchAutoRunCode("on_groupchange") Details:Destroy(Details.trusted_characters) C_Timer.After(5, Details.ScheduleSyncPlayerActorData) end else Details.in_group = IsInGroup() or IsInRaid() if (not Details.in_group) then --player left the group, run routines to cleanup the environment Details222.GarbageCollector.RestartInternalGarbageCollector(true) petContainer.Reset() Details:SchedulePetUpdate(1) Details:Destroy(Details.details_users) Details:InstanceCall(Details.AdjustAlphaByContext) Details:CheckSwitchOnLogon() Details:SendEvent("GROUP_ONLEAVE") Details222.AutoRunCode.DispatchAutoRunCode("on_groupchange") Details:Destroy(Details.trusted_characters) else --player is still in a group Details:SchedulePetUpdate(2) --send char data if (Details.SendCharDataOnGroupChange and not Details.SendCharDataOnGroupChange:IsCancelled()) then return end Details.SendCharDataOnGroupChange = C_Timer.NewTimer(11, function() Details:SendCharacterData() Details.SendCharDataOnGroupChange = nil end) end end Details:SchedulePetUpdate(6) end function Details.parser_functions:START_TIMER(...) --~timer if (Details.debug) then Details:Msg("(debug) found a timer.") end local _, zoneType = GetInstanceInfo() --check if the player is inside an arena if (zoneType == "arena") then if (Details.debug) then Details:Msg("(debug) timer is an arena countdown.") end Details:StartArenaSegment(...) --check if the player is inside a battleground elseif (zoneType == "battleground") then if (Details.debug) then Details:Msg("(debug) timer is a battleground countdown.") end local _, timeSeconds = select(1, ...) if (Details.start_battleground) then Details.Schedules.Cancel(Details.start_battleground) end --create new schedule Details.start_battleground = Details.Schedules.NewTimer(timeSeconds, Details.CreateBattlegroundSegment) Details.Schedules.SetName(Details.start_battleground, "Battleground Start Timer") end end function Details:CreateBattlegroundSegment() if (_in_combat) then Details222.discardSegment = true Details:EndCombat() end Details.lastBattlegroundStartTime = GetTime() Details222.StartCombat() if (Details.debug) then Details:Msg("(debug) a battleground has started.") end end --~load local TurnTheSpeakersOn = function() if (not Details.gump) then --failed to load the framework if (not Details.instance_load_failed) then Details:CreatePanicWarning() end Details.instance_load_failed.text:SetText("Framework for Details! isn't loaded.\nIf you just updated the addon, please reboot the game client.\nWe apologize for the inconvenience and thank you for your comprehension.") return end Details222.AutoRunCode.Code = {} Details.popup = _G.GameCooltip Details.in_group = IsInGroup() or IsInRaid() Details.temp_table1 = {} Details.encounter = {} Details.in_combat = false Details.combat_id = 0 Details.opened_windows = 0 local _, _, _, toc = GetBuildInfo() if (toc >= 100200) then Details.playername = UnitName("player") .. "-" .. (GetRealmName():gsub("[%s-]", '')) else Details.playername = UnitName("player") end Details.playerclass = select(2, UnitClass("player")) Details.playername = Details:Ambiguate(Details.playername) --player faction and enemy faction Details.faction = UnitFactionGroup("player") if (Details.faction == PLAYER_FACTION_GROUP[0]) then --player is horde Details.faction_against = PLAYER_FACTION_GROUP[1] --ally Details.faction_id = 0 elseif (Details.faction == PLAYER_FACTION_GROUP[1]) then --player is alliance Details.faction_against = PLAYER_FACTION_GROUP[0] --horde Details.faction_id = 1 end --this function applies the Details.default_profile to Details object, this isn't yet the player profile which will load later Details222.LoadSavedVariables.DefaultProfile() --load up data from savedvariables for the character Details222.LoadSavedVariables.CharacterData() --load up data from saved variables for the account (shared among all the players' characters; this is not the Blizzard account, lol). Details222.LoadSavedVariables.SharedData() --load data of the segments saved from latest game session Details222.LoadSavedVariables.CombatSegments() --load the profiles Details:LoadConfig() Details:UpdateParserGears() --load auto run code Details222.AutoRunCode.StartAutoRun() Details.isLoaded = true end function Details.IsLoaded() return Details.isLoaded end function Details.parser_functions:ADDON_LOADED(...) local addonName = select(1, ...) if (addonName == "Details") then TurnTheSpeakersOn() end end local playerLogin = CreateFrame("frame") playerLogin:RegisterEvent("PLAYER_LOGIN") playerLogin:SetScript("OnEvent", function() Details222.StartUp.StartMeUp() end) function Details.parser_functions:PET_BATTLE_OPENING_START(...) Details.pet_battle = true for index, instance in ipairs(Details.tabela_instancias) do if (instance.ativa) then if (Details.debug) then Details:Msg("(debug) hidding windows for Pet Battle.") end instance:SetWindowAlphaForCombat(true, true, 0) end end end function Details.parser_functions:PET_BATTLE_CLOSE(...) Details.pet_battle = false for index, instance in ipairs(Details.tabela_instancias) do if (instance.ativa) then if (Details.debug) then Details:Msg("(debug) Pet Battle finished, calling AdjustAlphaByContext().") end instance:AdjustAlphaByContext(true) end end end function Details.parser_functions:UNIT_NAME_UPDATE(...) Details:SchedulePetUpdate(5) end function Details.parser_functions:PLAYER_TARGET_CHANGED(...) Details:SendEvent("PLAYER_TARGET") end local parser_functions = Details.parser_functions function Details:OnEvent(event, ...) local func = parser_functions[event] if (func) then return func(nil, ...) end end Details.listener:SetScript("OnEvent", Details.OnEvent) ---return the backup table with regular logs, error and backups /dumpt __details_backup._general_logs function Details222.SaveVariables.GetBackupLogs() ---@type {_general_logs: table, _exit_error: table, _instance_backup: table} local backupTable = __details_backup if (not backupTable) then __details_backup = { --[[GLOBAL]] _general_logs = {}, _exit_error = {}, _instance_backup = {}, } return __details_backup end backupTable._general_logs = backupTable._general_logs or {} backupTable._exit_error = backupTable._exit_error or {} backupTable._instance_backup = backupTable._instance_backup or {} return backupTable end function Details222.SaveVariables.LogEvent(...) local args = {...} local newArgs = {} for index, value in ipairs(args) do if (type(value) == "string" or type(value) == "number" or type(value) == "boolean") then newArgs[index] = tostring(value) end end local currentDate = Details222.Date.GetDateForLogs() local text = currentDate .. " | " .. table.concat(newArgs, ", ") local backupLogs = Details222.SaveVariables.GetBackupLogs() table.insert(backupLogs._general_logs, 1, text) table.remove(backupLogs._general_logs, 30) end --logout function ~save ~logout ~savedata ---@type frame local databaseSaver = CreateFrame("frame") databaseSaver:RegisterEvent("PLAYER_LOGOUT") databaseSaver:SetScript("OnEvent", function(...) --maximum amount of exit errors to be logged, new error are always added to the top of the list (index 1) local exitErrorsMaxSize = 10 --safe guard logs and user settings local backupLogs = Details222.SaveVariables.GetBackupLogs() ---@type table local exitErrors = backupLogs._exit_error ---@param text string the error to be logged local addToExitErrors = function(text) table.insert(exitErrors, 1, Details222.Date.GetDateForLogs() .. " | " .. text) table.remove(exitErrors, 11) end ---@type string current step of the logout process, used to log which is the current step when an error happens local currentStep = "" --save the time played on this class, run protected local savePlayTimeClass, savePlayTimeErrorText = pcall(function() Details.SavePlayTimeOnClass() end) if (not savePlayTimeClass) then addToExitErrors("Saving Play Time: " .. savePlayTimeErrorText) end ---@type table record a log of events that happened during the logout process _detalhes_global.exit_log = {} ---@type table record errors that happened during the logout process _detalhes_global.exit_errors = _detalhes_global.exit_errors or {} currentStep = "Checking the framework integrity" if (not Details.gump) then --failed to load the framework tinsert(_detalhes_global.exit_log, "The framework wasn't in Details member 'gump'.") tinsert(_detalhes_global.exit_errors, 1, currentStep .. " | " .. Details222.Date.GetDateForLogs() .. " | " .. Details.GetVersionString() .. " | Framework wasn't loaded |") return end local logSaverError = function(errortext) local writeLog = function() _detalhes_global = _detalhes_global or {} tinsert(_detalhes_global.exit_errors, 1, currentStep .. " | " .. Details222.Date.GetDateForLogs() .. " | " .. Details.GetVersionString() .. " | " .. errortext .. " | " .. debugstack()) tremove(_detalhes_global.exit_errors, exitErrorsMaxSize) addToExitErrors(currentStep .. " | " .. Details222.Date.GetDateForLogs() .. " | " .. Details.GetVersionString() .. " | " .. errortext .. " | " .. debugstack()) end xpcall(writeLog, addToExitErrors) end Details.saver_error_func = logSaverError Details.logoff_saving_data = true --close breakdown window if (Details.CloseBreakdownWindow) then tinsert(_detalhes_global.exit_log, "1 - Closing Breakdown Window.") currentStep = "Closing Breakdown Window" xpcall(Details.CloseBreakdownWindow, logSaverError) end --do not save window pos if (Details.tabela_instancias) then local clearInstances = function() currentStep = "Dealing With Instances" tinsert(_detalhes_global.exit_log, "2 - Clearing user placed position from instance windows.") for id, instance in Details:ListInstances() do if (id) then tinsert(_detalhes_global.exit_log, " - " .. id .. " has baseFrame: " .. (instance.baseframe and "yes" or "no") .. ".") if (instance.baseframe) then instance.baseframe:SetUserPlaced(false) instance.baseframe:SetDontSavePosition(true) end end end end xpcall(clearInstances, logSaverError) else tinsert(_detalhes_global.exit_errors, 1, "not _detalhes.tabela_instancias") tremove(_detalhes_global.exit_errors, exitErrorsMaxSize) addToExitErrors("not _detalhes.tabela_instancias | " .. Details.GetVersionString()) end --if is in combat during the logout, stop the combat if (Details.in_combat and Details.tabela_vigente) then tinsert(_detalhes_global.exit_log, "3 - Leaving current combat.") currentStep = "Leaving Current Combat" xpcall(Details.SairDoCombate, logSaverError) Details.can_panic_mode = true end --switch back to default, settings changed by automation if (Details.CheckSwitchOnLogon and Details.tabela_instancias and Details.tabela_instancias[1] and getmetatable(Details.tabela_instancias[1])) then tinsert(_detalhes_global.exit_log, "4 - Reversing switches.") currentStep = "Check Switch on Logon" xpcall(Details.CheckSwitchOnLogon, logSaverError) end --user requested a wipe of the full configuration if (Details.wipe_full_config) then tinsert(_detalhes_global.exit_log, "5 - Is a full config wipe.") addToExitErrors("true: _detalhes.wipe_full_config | " .. Details.GetVersionString()) _detalhes_global = nil _detalhes_database = nil return end --save the config tinsert(_detalhes_global.exit_log, "6 - Saving Config.") currentStep = "Saving Config" xpcall(Details.SaveConfig, logSaverError) tinsert(_detalhes_global.exit_log, "7 - Saving Profiles.") currentStep = "Saving Profile" xpcall(Details.SaveProfile, logSaverError) --save the nicktag cache tinsert(_detalhes_global.exit_log, "8 - Saving nicktag cache.") local saveNicktabCache = function() _detalhes_database.nick_tag_cache = Details.CopyTable(_detalhes_database.nick_tag_cache) end xpcall(saveNicktabCache, logSaverError) --save auto run code data tinsert(_detalhes_global.exit_log, "9 - Saving Auto Run Code.") local saveAutoRunCode = function() Details222.AutoRunCode.OnLogout() end xpcall(saveAutoRunCode, logSaverError) end) --end of saving data local eraNamedSpellsToID = {} -- ~parserstart ~startparser ~cleu ~parser function Details222.Parser.OnParserEvent() local time, token, hidding, who_serial, who_name, who_flags, who_flags2, target_serial, target_name, target_flags, target_flags2, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12 = CombatLogGetCurrentEventInfo() local func = token_list[token] if (func) then return func(nil, token, time, who_serial, who_name, who_flags, target_serial, target_name, target_flags, target_flags2, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12) end end local out_of_combat_interresting_events = { ["SPELL_SUMMON"] = parser.summon, } function Details222.Parser.OnParserEventOutOfCombat() local time, token, hidding, who_serial, who_name, who_flags, who_flags2, target_serial, target_name, target_flags, target_flags2, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12 = CombatLogGetCurrentEventInfo() local func = out_of_combat_interresting_events[token] if (func) then return func(nil, token, time, who_serial, who_name, who_flags, target_serial, target_name, target_flags, target_flags2, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12) end end local parserDebug = {} function Details.OnParserEventDebug() --buffs: spellschool, auraType, amount, arg1, arg2, arg3 local time, token, hidding, sourceSerial, sourceName, sourceFlags, who_flags2, targetSerial, targetName, targetFlags, target_flags2, spellId, spellName, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, unknown1, unknown2, unknown3, unknown4, unknown5 = CombatLogGetCurrentEventInfo() if (not parserDebug[token]) then parserDebug[token] = true end end Details222.parser_frame:SetScript("OnEvent", Details222.Parser.OnParserEvent) Details222.PFrame = Details222.parser_frame function Details:UpdateParser() _tempo = Details._tempo end function Details:GetActorFromCache(value) return damage_cache[value] or damage_cache_pets[value] or damage_cache_petsOwners[value] end ---return tables containing the cache of actors ---@return table damageCache, table damageCachePets, table damageCachePetOwners, table healingCache function Details222.Cache.GetParserCacheTables() return damage_cache, damage_cache_pets, damage_cache_petsOwners, healing_cache end function Details:PrintParserCacheIndexes() local amount = 0 for n, nn in pairs(damage_cache) do amount = amount + 1 end Details:Msg("parser damage_cache", amount) amount = 0 for n, nn in pairs(damage_cache_pets) do amount = amount + 1 end Details:Msg("parser damage_cache_pets", amount) amount = 0 for n, nn in pairs(damage_cache_petsOwners) do amount = amount + 1 end Details:Msg("parser damage_cache_petsOwners", amount) amount = 0 for n, nn in pairs(healing_cache) do amount = amount + 1 end Details:Msg("parser healing_cache", amount) amount = 0 for n, nn in pairs(energy_cache) do amount = amount + 1 end Details:Msg("parser energy_cache", amount) amount = 0 for n, nn in pairs(misc_cache) do amount = amount + 1 end Details:Msg("parser misc_cache", amount) Details:Msg("group damage", #Details.cache_damage_group) Details:Msg("group damage", #Details.cache_healing_group) end function Details:GetActorsOnDamageCache() return Details.cache_damage_group end function Details:GetActorsOnHealingCache() return Details.cache_healing_group end --called when the zone type changes and ENCOUNTER_END event function Details222.Cache.ClearAugmentationCache() Details:Destroy(augmentation_cache.ebon_might) --~roskash Details:Destroy(augmentation_cache.prescience) Details:Destroy(augmentation_cache.shield) Details:Destroy(augmentation_cache.infernobless) Details:Destroy(augmentation_cache.breath_targets) Details:Destroy(augmentation_cache.prescience_stacks) end --called when restaring the garbage collector, on some options change, at the end of a combat (before COMBAT_PLAYER_LEAVE and after COMBAT_PLAYER_LEAVING) function Details:ClearParserCache(bIsFromCombatStart) --~wipe Details:Destroy(damage_cache) Details:Destroy(damage_cache_pets) Details:Destroy(damage_cache_petsOwners) Details:Destroy(healing_cache) Details:Destroy(energy_cache) Details:Destroy(misc_cache) Details:Destroy(misc_cache_pets) Details:Destroy(misc_cache_petsOwners) Details:Destroy(npcid_cache) Details:Destroy(enemy_cast_cache) Details:Destroy(empower_cache) Details:Destroy(ignore_death_cache) Details:Destroy(reflection_damage) Details:Destroy(reflection_debuffs) Details:Destroy(reflection_events) Details:Destroy(reflection_auras) Details:Destroy(reflection_dispels) Details:Destroy(dk_pets_cache.army) Details:Destroy(dk_pets_cache.apoc) Details:Destroy(cacheAnything.paladin_vivaldi_blessings) Details:Destroy(cacheAnything.rampage_cast_amount) if (not bIsFromCombatStart) then --check if the player is in a mythic dungeon run, if so, don't clear the cache if (Details.zone_type ~= "party") then Details:Destroy(augmentation_cache.ebon_might) --~roskash Details:Destroy(augmentation_cache.prescience) Details:Destroy(augmentation_cache.prescience_stacks) Details:Destroy(augmentation_cache.shield) Details:Destroy(augmentation_cache.infernobless) end end Details:Destroy(augmentation_cache.breath_targets) cacheAnything.track_hunter_frenzy = Details.combat_log.track_hunter_frenzy if (Details.combat_log.merge_gemstones_1007) then --ring powers merged, https://gist.github.com/ljosberinn/65abe150133ff3a08cd70f840f7dd019 (by Gerrit Alex - WCL) override_spellId[403225] = 404884 --Flame Licked Stone override_spellId[404974] = 404884 --Shining Obsidian Stone override_spellId[405220] = 404884 --Pestilent Plague Stone override_spellId[405221] = 404884 --Pestilent Plague Stone override_spellId[405209] = 404884 --Humming Arcane Stone override_spellId[403391] = 404884 --Freezing Ice Stone override_spellId[404911] = 404884 --Desirous Blood Stone override_spellId[404941] = 404884 --Shining Obsidian Stone override_spellId[403087] = 404884 --Storm Infused Stone override_spellId[403273] = 404884 --Fel Flame via Entropic Fel Stone override_spellId[403171] = 404884 --Uncontainable Charge via Echoing Thunder Stone override_spellId[405235] = 404884 --Wild Spirit Stone override_spellId[403381] = 404884 --Deluging Water Stone override_spellId[405118] = 404884 --Exuding Steam Stone override_spellId[403408] = 404884 --Exuding Steam Stone override_spellId[403336] = 404884 --Indomitable Earth Stone override_spellId[403392] = 404884 --Cold Frost Stone override_spellId[403376] = 404884 --Gleaming Iron Stone override_spellId[403253] = 404884 --Raging Magma Stone override_spellId[403257] = 404884 --Searing Smokey Stone else override_spellId[403225] = nil --Flame Licked Stone override_spellId[404974] = nil --Shining Obsidian Stone override_spellId[405220] = nil --Pestilent Plague Stone override_spellId[405221] = nil --Pestilent Plague Stone override_spellId[405209] = nil --Humming Arcane Stone override_spellId[403391] = nil --Freezing Ice Stone override_spellId[404911] = nil --Desirous Blood Stone override_spellId[404941] = nil --Shining Obsidian Stone override_spellId[403087] = nil --Storm Infused Stone override_spellId[403273] = nil --Fel Flame via Entropic Fel Stone override_spellId[403171] = nil --Uncontainable Charge via Echoing Thunder Stone override_spellId[405235] = nil --Wild Spirit Stone override_spellId[403381] = nil --Deluging Water Stone override_spellId[405118] = nil --Exuding Steam Stone override_spellId[403408] = nil --Exuding Steam Stone override_spellId[403336] = nil --Indomitable Earth Stone override_spellId[403392] = nil --Cold Frost Stone override_spellId[403376] = nil --Gleaming Iron Stone override_spellId[403253] = nil --Raging Magma Stone override_spellId[403257] = nil --Searing Smokey Stone end if (Details.combat_log.merge_critical_heals) then override_spellId[94472] = 81751 --disc priest attonement and crit. Crits use separate id. override_spellId[281469] = 270501 --disc priest contrition attonement and crit. Crits use separate id. override_spellId[388025] = 388024 --MW monk Ancient Teachings, heals from damage, crit and normal are separate. override_spellId[389325] = 389328 --MW monk Awakened Faeline, ^ else override_spellId[94472] = nil --disc priest attonement and crit. Crits use separate id. override_spellId[281469] = nil --disc priest contrition attonement and crit. Crits use separate id. override_spellId[388025] = nil --MW monk Ancient Teachings, heals from damage, crit and normal are separate. override_spellId[389325] = nil --MW monk Awakened Faeline, ^ end damage_cache = setmetatable({}, Details.weaktable) damage_cache_pets = setmetatable({}, Details.weaktable) damage_cache_petsOwners = setmetatable({}, Details.weaktable) healing_cache = setmetatable({}, Details.weaktable) energy_cache = setmetatable({}, Details.weaktable) misc_cache = setmetatable({}, Details.weaktable) misc_cache_pets = setmetatable({}, Details.weaktable) misc_cache_petsOwners = setmetatable({}, Details.weaktable) end function parser:RevomeActorFromCache(actor_serial, actor_name) if (actor_name) then damage_cache[actor_name] = nil damage_cache_pets[actor_name] = nil damage_cache_petsOwners[actor_name] = nil healing_cache[actor_serial] = nil energy_cache[actor_name] = nil misc_cache[actor_name] = nil misc_cache_pets[actor_name] = nil misc_cache_petsOwners[actor_name] = nil end if (actor_serial) then damage_cache[actor_serial] = nil damage_cache_pets[actor_serial] = nil damage_cache_petsOwners[actor_serial] = nil healing_cache[actor_serial] = nil energy_cache[actor_serial] = nil misc_cache[actor_serial] = nil misc_cache_pets[actor_serial] = nil misc_cache_petsOwners[actor_serial] = nil end end function Details:UptadeRaidMembersCache() Details:Destroy(raid_members_cache) Details:Destroy(tanks_members_cache) Details:Destroy(auto_regen_cache) Details:Destroy(bitfield_swap_cache) Details:Destroy(empower_cache) local currentCombat = Details:GetCurrentCombat() local groupRoster = currentCombat.raid_roster if (IsInRaid()) then local unitIdCache = Details222.UnitIdCache.Raid for i = 1, GetNumGroupMembers() do local unitId = unitIdCache[i] local unitName = GetUnitName(unitId, true) local unitGUID = UnitGUID(unitId) local _, unitClass = UnitClass(unitId) Details222.ClassCache.ByName[unitName] = unitClass Details222.ClassCache.ByGUID[unitGUID] = unitClass raid_members_cache[unitGUID] = unitName groupRoster[unitName] = unitGUID local role = _UnitGroupRolesAssigned(unitName) if (role == "TANK") then tanks_members_cache[unitGUID] = true end if (auto_regen_power_specs[Details.cached_specs[unitGUID]]) then auto_regen_cache[unitName] = auto_regen_power_specs[Details.cached_specs[unitGUID]] end end elseif (IsInGroup()) then local unitIdCache = Details222.UnitIdCache.Party for i = 1, GetNumGroupMembers()-1 do local unitId = unitIdCache[i] local unitName = GetUnitName(unitId, true) local unitGUID = UnitGUID(unitId) raid_members_cache[unitGUID] = unitName groupRoster[unitName] = unitGUID local role = _UnitGroupRolesAssigned(unitName) if (role == "TANK") then tanks_members_cache[unitGUID] = true end if (auto_regen_power_specs[Details.cached_specs[unitGUID]]) then auto_regen_cache[unitName] = auto_regen_power_specs[Details.cached_specs[unitGUID]] end end --player local playerName = Details.playername local playerGUID = UnitGUID("player") raid_members_cache[playerGUID] = playerName groupRoster[playerName] = playerGUID local role = _UnitGroupRolesAssigned(playerName) if (role == "TANK") then tanks_members_cache[playerGUID] = true end if (auto_regen_power_specs[Details.cached_specs[playerGUID]]) then auto_regen_cache[playerName] = auto_regen_power_specs[Details.cached_specs[playerGUID]] end else local playerName = Details.playername local playerGUID = UnitGUID("player") raid_members_cache[playerGUID] = playerName groupRoster[playerName] = playerGUID local role = _UnitGroupRolesAssigned(playerName) if (role == "TANK") then tanks_members_cache[playerGUID] = true else local spec = detailsFramework.GetSpecialization() if (spec and spec ~= 0) then if (detailsFramework.GetSpecializationRole (spec) == "TANK") then tanks_members_cache[playerGUID] = true end end end if (auto_regen_power_specs[Details.cached_specs[playerGUID]]) then auto_regen_cache[playerName] = auto_regen_power_specs[Details.cached_specs[playerGUID]] end end if (Details.iam_a_tank) then tanks_members_cache[UnitGUID("player")] = true end end ---return true or false ---@param unitGUID string ---@return boolean function Details:IsATank(unitGUID) return tanks_members_cache[unitGUID] or false end ---returns the unit name ---@param unitGUID string ---@return string function Details:IsInCache(unitGUID) return raid_members_cache[unitGUID] end ---return the internal raid members cache, containing the unitGUID as key and the unitName as value ---@return table function Details:GetParserPlayerCache() return raid_members_cache end --serach key: ~cache function Details:UpdateParserGears(bIsFromCombatStart) --refresh combat tables _current_combat = Details.tabela_vigente --last events pointer last_events_cache = _current_combat.player_last_events _amount_of_last_events = Details.deadlog_events _use_shield_overheal = Details.parser_options.shield_overheal shield_spellid_cache = Details.shield_spellid_cache --refresh total containers _current_total = _current_combat.totals _current_gtotal = _current_combat.totals_grupo --refresh actors containers _current_damage_container = _current_combat[1] _current_heal_container = _current_combat[2] _current_energy_container = _current_combat[3] _current_misc_container = _current_combat[4] --refresh data capture options --_recording_self_buffs = _detalhes.RecordPlayerSelfBuffs --can be deprecated --_recording_healing = _detalhes.RecordHealingDone --can be deprecated --_recording_took_damage = _detalhes.RecordRealTimeTookDamage --_recording_ability_with_buffs = _detalhes.RecordPlayerAbilityWithBuffs --can be deprecated _in_combat = Details.in_combat Details:Destroy(ignored_npcids) --fill it with the default npcs ignored for npcId in pairs(Details.default_ignored_npcs) do ignored_npcids[npcId] = true end --fill it with the npcs the user ignored for npcId in pairs(Details.npcid_ignored) do ignored_npcids[npcId] = true end ignored_npcids[0] = nil if (_in_combat) then if (Details.parser_options.energy_overflow) then if (not Details.AutoRegenThread or Details.AutoRegenThread:IsCancelled()) then Details.AutoRegenThread = C_Timer.NewTicker(AUTO_REGEN_PRECISION / 10, regen_power_overflow_check) --at the moment, runs 5 times per second end end else if (Details.AutoRegenThread and not Details.AutoRegenThread:IsCancelled()) then Details.AutoRegenThread:Cancel() Details.AutoRegenThread = nil end end if (Details.hooks["HOOK_COOLDOWN"].enabled) then _hook_cooldowns = true else _hook_cooldowns = false end if (Details.hooks["HOOK_DEATH"].enabled) then _hook_deaths = true else _hook_deaths = false end if (Details.hooks["HOOK_BATTLERESS"].enabled) then _hook_battleress = true else _hook_battleress = false end if (Details.hooks["HOOK_INTERRUPT"].enabled) then _hook_interrupt = true else _hook_interrupt = false end is_using_spellId_override = Details.override_spellids Details:ClearParserCache(bIsFromCombatStart) end function Details.DumpIgnoredNpcs() return ignored_npcids end --serach key: ~api ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --details api functions --number of combat function Details:GetCombatId() return Details.combat_id end ---return true if in combat ---@return boolean bIsInCombat function Details:IsInCombat() return _in_combat end function Details:IsInEncounter() return Details.encounter_table.id and true or false end function Details:GetAllActors(_combat, _actorname) return Details:GetActor(_combat, 1, _actorname), Details:GetActor(_combat, 2, _actorname), Details:GetActor(_combat, 3, _actorname), Details:GetActor(_combat, 4, _actorname) end --get player function Details:GetPlayer(_actorname, _combat, _attribute) return Details:GetActor(_combat, _attribute, _actorname) end --get an actor function Details:GetActor(combatId, attribute, actorName) if (not combatId) then combatId = "current" --current combat end if (not attribute) then attribute = 1 --damage end if (not actorName) then actorName = Details.playername end if (combatId == 0 or combatId == "current") then local actor = Details.tabela_vigente(attribute, actorName) if (actor) then return actor else return nil end elseif (combatId == -1 or combatId == "overall") then local actor = Details.tabela_overall(attribute, actorName) if (actor) then return actor else return nil end elseif (type(combatId) == "number") then local segmentsTable = Details:GetCombatSegments() ---@type combat local combatObject = segmentsTable[combatId] if (combatObject) then ---@type actor local actorObject = combatObject(attribute, actorName) if (actorObject) then return actorObject else return nil end else return nil end else return nil end end function Details:GetUnitId(unitName) unitName = unitName or self.nome unitName = Details:Ambiguate(unitName) local openRaidLib = LibStub:GetLibrary("LibOpenRaid-1.0", true) if (openRaidLib) then local unitId = openRaidLib.GetUnitID(unitName) if (unitId) then return unitId end end if (IsInRaid()) then for i = 1, GetNumGroupMembers() do local unitId = "raid" .. i if (GetUnitName(unitId, true) == unitName) then return unitId end end elseif (IsInGroup()) then for i = 1, GetNumGroupMembers() -1 do local unitId = "party" .. i if (GetUnitName(unitId, true) == unitName) then return unitId end end if (Details.playername == unitName) then return "player" end end end ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --battleground parser Details.pvp_parser_frame:SetScript("OnEvent", function(self, event) self:ReadPvPData() end) function Details:BgScoreUpdate() RequestBattlefieldScoreData() end --start the virtual parser function Details.pvp_parser_frame:StartBgUpdater() Details.pvp_parser_frame:RegisterEvent("UPDATE_BATTLEFIELD_SCORE") if (Details.pvp_parser_frame.ticker) then Details.Schedules.Cancel(Details.pvp_parser_frame.ticker) end Details.pvp_parser_frame.ticker = Details.Schedules.NewTicker(10, Details.BgScoreUpdate) Details.Schedules.SetName(Details.pvp_parser_frame.ticker, "Battleground Updater") end --stop the virtual parser function Details.pvp_parser_frame:StopBgUpdater() Details.pvp_parser_frame:UnregisterEvent("UPDATE_BATTLEFIELD_SCORE") Details.Schedules.Cancel(Details.pvp_parser_frame.ticker) Details.pvp_parser_frame.ticker = nil end function Details.pvp_parser_frame:ReadPvPData() local players = GetNumBattlefieldScores() local _player, realmName = UnitFullName("player") if (not realmName) then realmName = GetRealmName() realmName = realmName:gsub("[%s-]", "") end local currentCombat = Details:GetCurrentCombat() for i = 1, players do local name, killingBlows, honorableKills, deaths, honorGained, faction, race, rank, class, classToken, damageDone, healingDone, bgRating, ratingChange, preMatchMMR, mmrChange, talentSpec if (isCATA or isWOTLK or isERA) then name, killingBlows, honorableKills, deaths, honorGained, faction, rank, race, class, classToken, damageDone, healingDone, bgRating, ratingChange, preMatchMMR, mmrChange, talentSpec = GetBattlefieldScore(i) else name, killingBlows, honorableKills, deaths, honorGained, faction, race, class, classToken, damageDone, healingDone, bgRating, ratingChange, preMatchMMR, mmrChange, talentSpec = GetBattlefieldScore(i) end if (not isWOTLK and not isERA and not isCATA) then --Must be dragonflight if (not name:match("%-")) then name = name .. "-" .. realmName end end name = Details:Ambiguate(name) --damage done local actor = currentCombat:GetActor(DETAILS_ATTRIBUTE_DAMAGE, name) if (actor) then if (damageDone == 0) then damageDone = damageDone + Details:GetOrderNumber() end actor.total = damageDone actor.classe = classToken or "UNKNOW" elseif (name ~= "Unknown" and type(name) == "string" and string.len(name) > 1) then local guid = UnitGUID(Details:Ambiguate(name)) if (guid) then local flag if (Details.faction_id == faction) then --is from the same faction flag = 0x514 else flag = 0x548 end actor = _current_damage_container:GetOrCreateActor (guid, name, flag, true) actor.total = Details:GetOrderNumber() actor.classe = classToken or "UNKNOW" if (flag == 0x548) then --oponent actor.enemy = true end end end --healing done local actor = currentCombat:GetActor(DETAILS_ATTRIBUTE_HEAL, name) if (actor) then if (healingDone == 0) then healingDone = healingDone + Details:GetOrderNumber() end actor.total = healingDone actor.classe = classToken or "UNKNOW" elseif (name ~= "Unknown" and type(name) == "string" and string.len(name) > 1) then local guid = UnitGUID(name) if (guid) then local flag if (Details.faction_id == faction) then --is from the same faction flag = 0x514 else flag = 0x548 end actor = _current_heal_container:GetOrCreateActor (guid, name, flag, true) actor.total = Details:GetOrderNumber() actor.classe = classToken or "UNKNOW" if (flag == 0x548) then --oponent actor.enemy = true end end end end end --[= local detailsParserDebugFrame = CreateFrame("frame", "DetailsParserDebugFrame", UIParent) detailsParserDebugFrame:SetSize(100, 200) DetailsFramework:ApplyStandardBackdrop(detailsParserDebugFrame) detailsParserDebugFrame:SetPoint("left", UIParent, "left", 2, 350) detailsParserDebugFrame.AllIcons = {} detailsParserDebugFrame.AllTexts = {} local iconSize = 40 detailsParserDebugFrame:Hide() local spellIcon1 = detailsParserDebugFrame:CreateTexture(nil, "overlay") spellIcon1:SetSize(iconSize, iconSize) spellIcon1:SetPoint("topleft", detailsParserDebugFrame, "topleft", 2, -5) local text1 = detailsParserDebugFrame:CreateFontString(nil, "overlay", "GameFontNormal") text1:SetPoint("left", spellIcon1, "right", 2, 0) local spellIcon2 = detailsParserDebugFrame:CreateTexture(nil, "overlay") spellIcon2:SetSize(iconSize, iconSize) spellIcon2:SetPoint("topleft", spellIcon1, "bottomleft", 0, -2) local text2 = detailsParserDebugFrame:CreateFontString(nil, "overlay", "GameFontNormal") text2:SetPoint("left", spellIcon2, "right", 2, 0) local spellIcon3 = detailsParserDebugFrame:CreateTexture(nil, "overlay") spellIcon3:SetSize(iconSize, iconSize) spellIcon3:SetPoint("topleft", spellIcon2, "bottomleft", 0, -2) local text3 = detailsParserDebugFrame:CreateFontString(nil, "overlay", "GameFontNormal") text3:SetPoint("left", spellIcon3, "right", 2, 0) local spellIcon4 = detailsParserDebugFrame:CreateTexture(nil, "overlay") spellIcon4:SetSize(iconSize, iconSize) spellIcon4:SetPoint("topleft", spellIcon3, "bottomleft", 0, -2) local text4 = detailsParserDebugFrame:CreateFontString(nil, "overlay", "GameFontNormal") text4:SetPoint("left", spellIcon4, "right", 2, 0) local spellIcon5 = detailsParserDebugFrame:CreateTexture(nil, "overlay") spellIcon5:SetSize(iconSize, iconSize) spellIcon5:SetPoint("topleft", spellIcon4, "bottomleft", 0, -2) local text5 = detailsParserDebugFrame:CreateFontString(nil, "overlay", "GameFontNormal") text5:SetPoint("left", spellIcon5, "right", 2, 0) tinsert(detailsParserDebugFrame.AllIcons, spellIcon1) tinsert(detailsParserDebugFrame.AllIcons, spellIcon2) tinsert(detailsParserDebugFrame.AllIcons, spellIcon3) tinsert(detailsParserDebugFrame.AllIcons, spellIcon4) tinsert(detailsParserDebugFrame.AllIcons, spellIcon5) tinsert(detailsParserDebugFrame.AllTexts, text1) tinsert(detailsParserDebugFrame.AllTexts, text2) tinsert(detailsParserDebugFrame.AllTexts, text3) tinsert(detailsParserDebugFrame.AllTexts, text4) tinsert(detailsParserDebugFrame.AllTexts, text5) function detailsParserDebugFrame:BlinkIcon(spellId, iconId) local spellName, _, spellIcon = GetSpellInfo(spellId) local icon = self.AllIcons[iconId] if (spellIcon) then icon:SetTexture(spellIcon) icon:Show() C_Timer.After(1, function() icon:Hide() end) end end --]=] --end