You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

768 lines
23 KiB

-- combat class object
local _detalhes = _G._detalhes
local Loc = LibStub ("AceLocale-3.0"):GetLocale ( "Details" )
local _
--[[global]] DETAILS_TOTALS_ONLYGROUP = true
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--> local pointers
local _setmetatable = setmetatable -- lua local
local _ipairs = ipairs -- lua local
local _pairs = pairs -- lua local
local _bit_band = bit.band -- lua local
local _date = date -- lua local
local _table_remove = table.remove -- lua local
local _rawget = rawget
local _math_max = math.max
local _math_floor = math.floor
local _GetTime = GetTime
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--> constants
local combate = _detalhes.combate
local container_combatentes = _detalhes.container_combatentes
local class_type_dano = _detalhes.atributos.dano
local class_type_cura = _detalhes.atributos.cura
local class_type_e_energy = _detalhes.atributos.e_energy
local class_type_misc = _detalhes.atributos.misc
local REACTION_HOSTILE = 0x00000040
local CONTROL_PLAYER = 0x00000100
--local _tempo = time()
local _tempo = _GetTime()
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--> api functions
--combat (container type, actor name)
_detalhes.call_combate = function (self, class_type, name)
local container = self[class_type]
local index_mapa = container._NameIndexTable [name]
local actor = container._ActorTable [index_mapa]
return actor
end
combate.__call = _detalhes.call_combate
--get the start date and end date
function combate:GetDate()
return self.data_inicio, self.data_fim
end
--set the combat date
function combate:SetDate (started, ended)
if (started and type (started) == "string") then
self.data_inicio = started
end
if (ended and type (ended) == "string") then
self.data_fim = ended
end
end
--return data for charts
function combate:GetTimeData (name)
return self.TimeData [name]
end
function combate:GetContainer (attribute)
return self [attribute]
end
function combate:GetRoster()
return self.raid_roster
end
function combate:InstanceType()
return _rawget (self, "instance_type")
end
function combate:IsTrash()
return _rawget (self, "is_trash")
end
function combate:GetDifficulty()
return self.is_boss and self.is_boss.diff
end
function combate:GetBossInfo()
return self.is_boss
end
function combate:GetPhases()
return self.PhaseData
end
function combate:GetPvPInfo()
return self.is_pvp
end
function combate:GetMythicDungeonInfo()
return self.is_mythic_dungeon
end
function combate:GetMythicDungeonTrashInfo()
return self.is_mythic_dungeon_trash
end
function combate:IsMythicDungeon()
local is_segment = self.is_mythic_dungeon_segment
local run_id = self.is_mythic_dungeon_run_id
return is_segment, run_id
end
function combate:IsMythicDungeonOverall()
return self.is_mythic_dungeon and self.is_mythic_dungeon.OverallSegment
end
function combate:GetArenaInfo()
return self.is_arena
end
function combate:GetDeaths()
return self.last_events_tables
end
function combate:GetCombatId()
return self.combat_id
end
function combate:GetCombatNumber()
return self.combat_counter
end
function combate:GetAlteranatePower()
return self.alternate_power
end
--return the name of the encounter or enemy
function combate:GetCombatName (try_find)
if (self.is_pvp) then
return self.is_pvp.name
elseif (self.is_boss) then
return self.is_boss.encounter
elseif (self.is_mythic_dungeon_trash) then
return self.is_mythic_dungeon_trash.ZoneName .. " (" .. Loc ["STRING_SEGMENTS_LIST_TRASH"] .. ")"
elseif (_rawget (self, "is_trash")) then
return Loc ["STRING_SEGMENT_TRASH"]
else
if (self.enemy) then
return self.enemy
end
if (try_find) then
return _detalhes:FindEnemy()
end
end
return Loc ["STRING_UNKNOW"]
end
--[[global]] DETAILS_SEGMENTID_OVERALL = -1
--[[global]] DETAILS_SEGMENTID_CURRENT = 0
--enum segments type
--[[global]] DETAILS_SEGMENTTYPE_GENERIC = 0
--[[global]] DETAILS_SEGMENTTYPE_OVERALL = 1
--[[global]] DETAILS_SEGMENTTYPE_DUNGEON_TRASH = 5
--[[global]] DETAILS_SEGMENTTYPE_DUNGEON_BOSS = 6
--[[global]] DETAILS_SEGMENTTYPE_RAID_TRASH = 7
--[[global]] DETAILS_SEGMENTTYPE_RAID_BOSS = 8
--[[global]] DETAILS_SEGMENTTYPE_MYTHICDUNGEON_GENERIC = 10
--[[global]] DETAILS_SEGMENTTYPE_MYTHICDUNGEON_TRASH = 11
--[[global]] DETAILS_SEGMENTTYPE_MYTHICDUNGEON_OVERALL = 12
--[[global]] DETAILS_SEGMENTTYPE_MYTHICDUNGEON_TRASHOVERALL = 13
--[[global]] DETAILS_SEGMENTTYPE_MYTHICDUNGEON_BOSS = 14
--[[global]] DETAILS_SEGMENTTYPE_PVP_ARENA = 20
--[[global]] DETAILS_SEGMENTTYPE_PVP_BATTLEGROUND = 21
function combate:GetCombatType()
--> mythic dungeon
local isMythicDungeon = is_mythic_dungeon_segment
if (isMythicDungeon) then
local isMythicDungeonTrash = self.is_mythic_dungeon_trash
if (isMythicDungeonTrash) then
return DETAILS_SEGMENTTYPE_MYTHICDUNGEON_TRASH
else
local isMythicDungeonOverall = self.is_mythic_dungeon and self.is_mythic_dungeon.OverallSegment
local isMythicDungeonTrashOverall = self.is_mythic_dungeon and self.is_mythic_dungeon.TrashOverallSegment
if (isMythicDungeonOverall) then
return DETAILS_SEGMENTTYPE_MYTHICDUNGEON_OVERALL
elseif (isMythicDungeonTrashOverall) then
return DETAILS_SEGMENTTYPE_MYTHICDUNGEON_TRASHOVERALL
end
local bossEncounter = self.is_boss
if (bossEncounter) then
return DETAILS_SEGMENTTYPE_MYTHICDUNGEON_BOSS
end
return DETAILS_SEGMENTTYPE_MYTHICDUNGEON_GENERIC
end
end
--> arena
local arenaInfo = self.is_arena
if (arenaInfo) then
return DETAILS_SEGMENTTYPE_PVP_ARENA
end
--> battleground
local battlegroundInfo = self.is_pvp
if (battlegroundInfo) then
return DETAILS_SEGMENTTYPE_PVP_BATTLEGROUND
end
--> dungeon or raid
local instanceType = self.instance_type
if (instanceType == "party") then
local bossEncounter = self.is_boss
if (bossEncounter) then
return DETAILS_SEGMENTTYPE_DUNGEON_BOSS
else
return DETAILS_SEGMENTTYPE_DUNGEON_TRASH
end
elseif (instanceType == "raid") then
local bossEncounter = self.is_boss
if (bossEncounter) then
return DETAILS_SEGMENTTYPE_RAID_BOSS
else
return DETAILS_SEGMENTTYPE_RAID_TRASH
end
end
--> overall data
if (self == _detalhes.tabela_overall) then
return DETAILS_SEGMENTTYPE_OVERALL
end
return DETAILS_SEGMENTTYPE_GENERIC
end
--return a numeric table with all actors on the specific containter
function combate:GetActorList (container)
return self [container]._ActorTable
end
function combate:GetActor (container, name)
local index = self [container] and self [container]._NameIndexTable [name]
if (index) then
return self [container]._ActorTable [index]
end
return nil
end
--return the combat time in seconds
function combate:GetFormatedCombatTime()
local time = self:GetCombatTime()
local m, s = _math_floor (time/60), _math_floor (time%60)
return m, s
end
function combate:GetCombatTime()
if (self.end_time) then
return _math_max (self.end_time - self.start_time, 0.1)
elseif (self.start_time and _detalhes.in_combat and self ~= _detalhes.tabela_overall) then
return _math_max (_GetTime() - self.start_time, 0.1)
else
return 0.1
end
end
function combate:GetStartTime()
return self.start_time
end
function combate:SetStartTime (t)
self.start_time = t
end
function combate:GetEndTime()
return self.end_time
end
function combate:SetEndTime (t)
self.end_time = t
end
--return the total of a specific attribute
local power_table = {0, 1, 3, 6, 0, "alternatepower"}
function combate:GetTotal (attribute, subAttribute, onlyGroup)
if (attribute == 1 or attribute == 2) then
if (onlyGroup) then
return self.totals_grupo [attribute]
else
return self.totals [attribute]
end
elseif (attribute == 3) then
if (subAttribute == 5) then --> resources
return self.totals.resources or 0
end
if (onlyGroup) then
return self.totals_grupo [attribute] [power_table [subAttribute]]
else
return self.totals [attribute] [power_table [subAttribute]]
end
elseif (attribute == 4) then
local subName = _detalhes:GetInternalSubAttributeName (attribute, subAttribute)
if (onlyGroup) then
return self.totals_grupo [attribute] [subName]
else
return self.totals [attribute] [subName]
end
end
return 0
end
function combate:CreateAlternatePowerTable (actorName)
local t = {last = 0, total = 0}
self.alternate_power [actorName] = t
return t
end
local tremove = _G.tremove
--delete an actor from the combat ~delete ~erase ~remove
function combate:DeleteActor(attribute, actorName, removeDamageTaken)
local container = self[attribute]
if (container) then
local actorTable = container._ActorTable
--store the index it was found
local indexToDelete
--get the object for the deleted actor
local deletedActor = self(attribute, actorName)
if (not deletedActor) then
return
else
for i = 1, #actorTable do
local actor = actorTable[i]
if (actor.nome == actorName) then
--print ("Details: found the actor: ", actorName, actor.nome, i)
indexToDelete = i
break
end
end
end
for i = 1, #actorTable do
--is this not the actor we want to remove?
if (i ~= indexToDelete) then
local actor = actorTable[i]
if (not actor.isTank) then
--get the damage dealt and remove
local damageDoneToRemovedActor = (actor.targets[actorName]) or 0
actor.targets[actorName] = nil
actor.total = actor.total - damageDoneToRemovedActor
actor.total_without_pet = actor.total_without_pet - damageDoneToRemovedActor
--damage taken
if (removeDamageTaken) then
local hadDamageTaken = actor.damage_from[actorName]
if (hadDamageTaken) then
--query the deleted actor to know how much damage it applied to this actor
local damageDoneToActor = (deletedActor.targets[actor.nome]) or 0
actor.damage_taken = actor.damage_taken - damageDoneToActor
end
end
--spells
local spellsTable = actor.spells._ActorTable
for spellId, spellTable in pairs(spellsTable) do
local damageDoneToRemovedActor = (spellTable.targets[actorName]) or 0
spellTable.targets[actorName] = nil
spellTable.total = spellTable.total - damageDoneToRemovedActor
end
end
end
end
if (indexToDelete) then
local actorToDelete = self(attribute, actorName)
local actorToDelete2 = container._ActorTable[indexToDelete]
if (actorToDelete ~= actorToDelete2) then
Details:Msg("error 0xDE8745")
end
local index = container._NameIndexTable[actorName]
if (indexToDelete ~= index) then
Details:Msg("error 0xDE8751")
end
--remove actor
tremove(container._ActorTable, index)
--remap
container:Remap()
end
end
end
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--> internals
function combate:CreateNewCombatTable()
return combate:NovaTabela()
end
--class constructor
function combate:NovaTabela (iniciada, _tabela_overall, combatId, ...)
local esta_tabela = {true, true, true, true, true}
esta_tabela [1] = container_combatentes:NovoContainer (_detalhes.container_type.CONTAINER_DAMAGE_CLASS, esta_tabela, combatId) --> Damage
esta_tabela [2] = container_combatentes:NovoContainer (_detalhes.container_type.CONTAINER_HEAL_CLASS, esta_tabela, combatId) --> Healing
esta_tabela [3] = container_combatentes:NovoContainer (_detalhes.container_type.CONTAINER_ENERGY_CLASS, esta_tabela, combatId) --> Energies
esta_tabela [4] = container_combatentes:NovoContainer (_detalhes.container_type.CONTAINER_MISC_CLASS, esta_tabela, combatId) --> Misc
esta_tabela [5] = container_combatentes:NovoContainer (_detalhes.container_type.CONTAINER_DAMAGE_CLASS, esta_tabela, combatId) --> place holder for customs
_setmetatable (esta_tabela, combate)
_detalhes.combat_counter = _detalhes.combat_counter + 1
esta_tabela.combat_counter = _detalhes.combat_counter
--> try discover if is a pvp combat
local who_serial, who_name, who_flags, alvo_serial, alvo_name, alvo_flags = ...
if (who_serial) then --> aqui ir� identificar o boss ou o oponente
if (alvo_name and _bit_band (alvo_flags, REACTION_HOSTILE) ~= 0) then --> tentando pegar o inimigo pelo alvo
esta_tabela.contra = alvo_name
if (_bit_band (alvo_flags, CONTROL_PLAYER) ~= 0) then
esta_tabela.pvp = true --> o alvo � da fac��o oposta ou foi dado mind control
end
elseif (who_name and _bit_band (who_flags, REACTION_HOSTILE) ~= 0) then --> tentando pegar o inimigo pelo who caso o mob � quem deu o primeiro hit
esta_tabela.contra = who_name
if (_bit_band (who_flags, CONTROL_PLAYER) ~= 0) then
esta_tabela.pvp = true --> o who � da fac��o oposta ou foi dado mind control
end
else
esta_tabela.pvp = true --> se ambos s�o friendly, seria isso um PVP entre jogadores da mesma fac��o?
end
end
--> start/end time (duration)
esta_tabela.data_fim = 0
esta_tabela.data_inicio = 0
esta_tabela.tempo_start = _tempo
--> record deaths
esta_tabela.last_events_tables = {}
--> last events from players
esta_tabela.player_last_events = {}
--> players in the raid
esta_tabela.raid_roster = {}
esta_tabela.raid_roster_indexed = {}
--> frags
esta_tabela.frags = {}
esta_tabela.frags_need_refresh = false
--> alternate power
esta_tabela.alternate_power = {}
--> time data container
esta_tabela.TimeData = _detalhes:TimeDataCreateCombatTables()
esta_tabela.PhaseData = {{1, 1}, damage = {}, heal = {}, damage_section = {}, heal_section = {}} --[1] phase number [2] phase started
--> for external plugin usage, these tables are guaranteed to be saved with the combat
esta_tabela.spells_cast_timeline = {}
esta_tabela.aura_timeline = {}
esta_tabela.cleu_timeline = {}
--> cleu events
esta_tabela.cleu_events = {
n = 1 --event counter
}
--> Skill cache (not used)
esta_tabela.CombatSkillCache = {}
-- a tabela sem o tempo de inicio � a tabela descartavel do inicio do addon
if (iniciada) then
--esta_tabela.start_time = _tempo
esta_tabela.start_time = _GetTime()
esta_tabela.end_time = nil
else
esta_tabela.start_time = 0
esta_tabela.end_time = nil
end
-- o container ir� armazenar as classes de dano -- cria um novo container de indexes de seriais de jogadores --par�metro 1 classe armazenada no container, par�metro 2 = flag da classe
esta_tabela[1].need_refresh = true
esta_tabela[2].need_refresh = true
esta_tabela[3].need_refresh = true
esta_tabela[4].need_refresh = true
esta_tabela[5].need_refresh = true
if (_tabela_overall) then --> link � a tabela de combate do overall
esta_tabela[1].shadow = _tabela_overall[1]
esta_tabela[2].shadow = _tabela_overall[2]
esta_tabela[3].shadow = _tabela_overall[3]
esta_tabela[4].shadow = _tabela_overall[4]
end
esta_tabela.totals = {
0, --> dano
0, --> cura
{--> e_energy
[0] = 0, --> mana
[1] = 0, --> rage
[3] = 0, --> energy (rogues cat)
[6] = 0, --> runepower (dk)
alternatepower = 0,
},
{--> misc
cc_break = 0, --> armazena quantas quebras de CC
ress = 0, --> armazena quantos pessoas ele reviveu
interrupt = 0, --> armazena quantos interrupt a pessoa deu
dispell = 0, --> armazena quantos dispell esta pessoa recebeu
dead = 0, --> armazena quantas vezes essa pessia morreu
cooldowns_defensive = 0, --> armazena quantos cooldowns a raid usou
buff_uptime = 0, --> armazena quantos cooldowns a raid usou
debuff_uptime = 0 --> armazena quantos cooldowns a raid usou
},
--> avoid using this values bellow, they aren't updated by the parser, only on demand by a user interaction.
voidzone_damage = 0,
frags_total = 0,
--> end
}
esta_tabela.totals_grupo = {
0, --> dano
0, --> cura
{--> e_energy
[0] = 0, --> mana
[1] = 0, --> rage
[3] = 0, --> energy (rogues cat)
[6] = 0, --> runepower (dk)
alternatepower = 0,
},
{--> misc
cc_break = 0, --> armazena quantas quebras de CC
ress = 0, --> armazena quantos pessoas ele reviveu
interrupt = 0, --> armazena quantos interrupt a pessoa deu
dispell = 0, --> armazena quantos dispell esta pessoa recebeu
dead = 0, --> armazena quantas vezes essa oessia morreu
cooldowns_defensive = 0, --> armazena quantos cooldowns a raid usou
buff_uptime = 0,
debuff_uptime = 0
}
}
return esta_tabela
end
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--> core
function combate:CreateLastEventsTable (player_name)
local t = {}
for i = 1, _detalhes.deadlog_events do
t [i] = {}
end
t.n = 1
self.player_last_events [player_name] = t
return t
end
--trava o tempo dos jogadores ap�s o t�rmino do combate.
function combate:TravarTempos()
if (self [1]) then
for _, jogador in _ipairs (self [1]._ActorTable) do --> damage
if (jogador:Iniciar()) then -- retorna se ele esta com o dps ativo
jogador:TerminarTempo()
jogador:Iniciar (false) --trava o dps do jogador
else
if (jogador.start_time == 0) then
jogador.start_time = _tempo
end
if (not jogador.end_time) then
jogador.end_time = _tempo
end
end
end
end
if (self [2]) then
for _, jogador in _ipairs (self [2]._ActorTable) do --> healing
if (jogador:Iniciar()) then -- retorna se ele esta com o dps ativo
jogador:TerminarTempo()
jogador:Iniciar (false) --trava o dps do jogador
else
if (jogador.start_time == 0) then
jogador.start_time = _tempo
end
if (not jogador.end_time) then
jogador.end_time = _tempo
end
end
end
end
end
function combate:seta_data (tipo)
if (tipo == _detalhes._detalhes_props.DATA_TYPE_START) then
self.data_inicio = _date ("%H:%M:%S")
elseif (tipo == _detalhes._detalhes_props.DATA_TYPE_END) then
self.data_fim = _date ("%H:%M:%S")
end
end
function combate:seta_tempo_decorrido()
--self.end_time = _tempo
self.end_time = _GetTime()
end
function _detalhes.refresh:r_combate (tabela_combate, shadow)
_setmetatable (tabela_combate, _detalhes.combate)
tabela_combate.__index = _detalhes.combate
tabela_combate.shadow = shadow
end
function _detalhes.clear:c_combate (tabela_combate)
--tabela_combate.__index = {}
tabela_combate.__index = nil
tabela_combate.__call = {}
tabela_combate._combat_table = nil
tabela_combate.shadow = nil
end
combate.__sub = function (combate1, combate2)
if (combate1 ~= _detalhes.tabela_overall) then
return
end
--> sub dano
for index, actor_T2 in _ipairs (combate2[1]._ActorTable) do
local actor_T1 = combate1[1]:PegarCombatente (actor_T2.serial, actor_T2.nome, actor_T2.flag_original, true)
actor_T1 = actor_T1 - actor_T2
actor_T2:subtract_total (combate1)
end
combate1 [1].need_refresh = true
--> sub heal
for index, actor_T2 in _ipairs (combate2[2]._ActorTable) do
local actor_T1 = combate1[2]:PegarCombatente (actor_T2.serial, actor_T2.nome, actor_T2.flag_original, true)
actor_T1 = actor_T1 - actor_T2
actor_T2:subtract_total (combate1)
end
combate1 [2].need_refresh = true
--> sub energy
for index, actor_T2 in _ipairs (combate2[3]._ActorTable) do
local actor_T1 = combate1[3]:PegarCombatente (actor_T2.serial, actor_T2.nome, actor_T2.flag_original, true)
actor_T1 = actor_T1 - actor_T2
actor_T2:subtract_total (combate1)
end
combate1 [3].need_refresh = true
--> sub misc
for index, actor_T2 in _ipairs (combate2[4]._ActorTable) do
local actor_T1 = combate1[4]:PegarCombatente (actor_T2.serial, actor_T2.nome, actor_T2.flag_original, true)
actor_T1 = actor_T1 - actor_T2
actor_T2:subtract_total (combate1)
end
combate1 [4].need_refresh = true
--> reduz o tempo
combate1.start_time = combate1.start_time + combate2:GetCombatTime()
--> apaga as mortes da luta diminuida
local amt_mortes = #combate2.last_events_tables --> quantas mortes teve nessa luta
if (amt_mortes > 0) then
for i = #combate1.last_events_tables, #combate1.last_events_tables-amt_mortes, -1 do
_table_remove (combate1.last_events_tables, #combate1.last_events_tables)
end
end
--> frags
for fragName, fragAmount in pairs (combate2.frags) do
if (fragAmount) then
if (combate1.frags [fragName]) then
combate1.frags [fragName] = combate1.frags [fragName] - fragAmount
else
combate1.frags [fragName] = fragAmount
end
end
end
combate1.frags_need_refresh = true
--> alternate power
local overallPowerTable = combate1.alternate_power
for actorName, powerTable in pairs (combate2.alternate_power) do
local power = overallPowerTable [actorName]
if (power) then
power.total = power.total - powerTable.total
end
combate2.alternate_power [actorName].last = 0
end
return combate1
end
combate.__add = function (combate1, combate2)
local all_containers = {combate2 [class_type_dano]._ActorTable, combate2 [class_type_cura]._ActorTable, combate2 [class_type_e_energy]._ActorTable, combate2 [class_type_misc]._ActorTable}
local custom_combat
if (combate1 ~= _detalhes.tabela_overall) then
custom_combat = combate1
end
for class_type, actor_container in ipairs (all_containers) do
for _, actor in ipairs (actor_container) do
local shadow
if (class_type == class_type_dano) then
shadow = _detalhes.atributo_damage:r_connect_shadow (actor, true, custom_combat)
elseif (class_type == class_type_cura) then
shadow = _detalhes.atributo_heal:r_connect_shadow (actor, true, custom_combat)
elseif (class_type == class_type_e_energy) then
shadow = _detalhes.atributo_energy:r_connect_shadow (actor, true, custom_combat)
elseif (class_type == class_type_misc) then
shadow = _detalhes.atributo_misc:r_connect_shadow (actor, true, custom_combat)
end
shadow.boss_fight_component = actor.boss_fight_component or shadow.boss_fight_component
shadow.fight_component = actor.fight_component or shadow.fight_component
shadow.grupo = actor.grupo or shadow.grupo
end
end
--> alternate power
local overallPowerTable = combate1.alternate_power
for actorName, powerTable in pairs (combate2.alternate_power) do
local power = overallPowerTable [actorName]
if (not power) then
power = combate1:CreateAlternatePowerTable (actorName)
end
power.total = power.total + powerTable.total
combate2.alternate_power [actorName].last = 0
end
return combate1
end
function _detalhes:UpdateCombat()
_tempo = _detalhes._tempo
end