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.

1773 lines
61 KiB

local Loc = LibStub ("AceLocale-3.0"):GetLocale ("Details_TimeLine")
local SharedMedia = LibStub:GetLibrary("LibSharedMedia-3.0")
--> create the plugin object
local TimeLine = _detalhes:NewPluginObject ("Details_TimeLine", DETAILSPLUGIN_ALWAYSENABLED)
tinsert (UISpecialFrames, "Details_TimeLine")
--> main frame (shortcut)
local TimeLineFrame = TimeLine.Frame
local debugmode = false
local _
local COMBATLOG_OBJECT_REACTION_FRIENDLY = COMBATLOG_OBJECT_REACTION_FRIENDLY
local COMBATLOG_OBJECT_TYPE_PLAYER = COMBATLOG_OBJECT_TYPE_PLAYER
local _bit_band = bit.band
local tinsert = tinsert
local type_cooldown = "cooldowns_timeline"
local type_debuff = "debuff_timeline"
local type_enemy_spells = "spellcast_boss"
local type_boss_spells = "boss_spells"
local red = {1, 0, 0, .25}
local green = {0, 1, 0, .25}
local _combat_object
local parser_cache = {}
local _GetSpellInfo = _detalhes.getspellinfo
--> shortcut for current combat
local _is_in_combat = false
TimeLine:SetPluginDescription (Loc ["STRING_PLUGIN_DESC"])
TimeLine.version_string = "v3.3"
local menu_wallpaper_tex = {.6, 0.1, 0, 0.64453125}
local menu_wallpaper_color = {1, 1, 1, 0.15}
local function CreatePluginFrames()
TimeLine.combat_data = {} --temp avoid errors on initialization
--> catch Details! main object
local _detalhes = _G._detalhes
if (not _detalhes) then
return
end
local DetailsFrameWork = _detalhes.gump
local framework = _G._detalhes:GetFramework()
local options_text_template = framework:GetTemplate ("font", "OPTIONS_FONT_TEMPLATE")
local options_dropdown_template = framework:GetTemplate ("dropdown", "OPTIONS_DROPDOWN_TEMPLATE")
local options_switch_template = framework:GetTemplate ("switch", "OPTIONS_CHECKBOX_TEMPLATE")
local options_slider_template = framework:GetTemplate ("slider", "OPTIONS_SLIDER_TEMPLATE")
local options_button_template = framework:GetTemplate ("button", "OPTIONS_BUTTON_TEMPLATE")
framework.button_templates ["ADL_BUTTON_TEMPLATE"] = {
backdrop = {edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1, bgFile = [[Interface\Tooltips\UI-Tooltip-Background]], tileSize = 64, tile = true},
backdropcolor = {.3, .3, .3, .9},
onentercolor = {.6, .6, .6, .9},
backdropbordercolor = {0, 0, 0, 1},
onenterbordercolor = {0, 0, 0, 1},
}
options_button_template = framework:GetTemplate ("button", "ADL_BUTTON_TEMPLATE")
local all_types = {type_cooldown, type_debuff, type_enemy_spells, type_boss_spells}
local all_types_names = {Loc ["STRING_TYPE_COOLDOWN"], Loc ["STRING_TYPE_DEBUFF"], "Enemy Spell Cast", "Spell Timeline"}
local current_type = all_types [2]
local current_segment = 1
local class_icons_with_alpha = [[Interface\AddOns\Details\images\classes_small_alpha]]
local BUTTON_BACKGROUND_COLOR = {.5, .5, .5, .3}
local BUTTON_BACKGROUND_COLORHIGHLIGHT = {.5, .5, .5, .8}
local BUTTON_BACKGROUND_COLOR2 = {.4, .4, .4, .3}
local BUTTON_BACKGROUND_COLORHIGHLIGHT2 = {.4, .4, .4, .8}
local c = CreateFrame ("Button", nil, TimeLineFrame, "UIPanelCloseButton")
c:SetWidth (20)
c:SetHeight (20)
c:SetPoint ("TOPRIGHT", TimeLineFrame, "TOPRIGHT", -2, -3)
c:SetFrameLevel (TimeLineFrame:GetFrameLevel()+1)
c:GetNormalTexture():SetDesaturated (true)
c:SetAlpha (1)
--title bar
local titlebar = CreateFrame ("frame", nil, TimeLineFrame, "BackdropTemplate")
titlebar:SetPoint ("topleft", TimeLineFrame, "topleft", 2, -3)
titlebar:SetPoint ("topright", TimeLineFrame, "topright", -2, -3)
titlebar:SetHeight (20)
titlebar:SetBackdrop ({edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1, bgFile = [[Interface\AddOns\Details\images\background]], tileSize = 64, tile = true})
titlebar:SetBackdropColor (.5, .5, .5, 1)
titlebar:SetBackdropBorderColor (0, 0, 0, 1)
local name_bg_texture = TimeLineFrame:CreateTexture (nil, "background")
name_bg_texture:SetTexture ([[Interface\PetBattles\_PetBattleHorizTile]], true)
name_bg_texture:SetHorizTile (true)
name_bg_texture:SetTexCoord (0, 1, 126/256, 19/256)
name_bg_texture:SetPoint ("topleft", TimeLineFrame, "topleft", 2, -22)
name_bg_texture:SetPoint ("bottomright", TimeLineFrame, "bottomright")
name_bg_texture:SetHeight (54)
name_bg_texture:SetVertexColor (0, 0, 0, 0.2)
--window title
local titleLabel = _detalhes.gump:NewLabel (titlebar, titlebar, nil, "titulo", "Details! Time Line", "GameFontHighlightLeft", 12, {227/255, 186/255, 4/255})
titleLabel:SetPoint ("center", TimeLineFrame, "center")
titleLabel:SetPoint ("top", TimeLineFrame, "top", 0, -7)
local search
local myname = UnitName ("player")
local GameCooltip = GameCooltip
TimeLine.debuff_temp_table = {}
TimeLine.current_enemy_spells = {}
TimeLine.current_spells_individual = {}
--> record if button is shown
TimeLine.showing = false
TimeLine.open = false
--> record if boss window is open or not
TimeLine.window_open = false
--> rows
TimeLine.rows = {}
--> new combat
TimeLine.current_battle_cooldowns_timeline = {}
function TimeLine:NewCombat()
TimeLine.current_battle_cooldowns_timeline = {}
TimeLine.debuff_temp_table = {}
TimeLine.current_enemy_spells = {}
TimeLine.current_spells_individual = {}
end
function TimeLine:CanShowIcon()
if (#TimeLine.combat_data > 0) then
TimeLine:ShowIcon()
else
TimeLine:HideIcon()
end
end
function TimeLine:FinishCombat()
--> is in debug mode?
if (debugmode and _combat_object and not _combat_object.is_boss) then
_combat_object.is_boss = {
index = 1,
name = _detalhes:GetBossName (1098, 1),
zone = "Throne of Thunder",
mapid = 1098,
encounter = "Jin'Rohk the Breaker"
}
end
--> is a boss encounter?
if (_combat_object and _combat_object.is_boss) then
--> combat information
tinsert (TimeLine.combat_data, 1, {})
--save the encounter elapsed time
TimeLine.combat_data [1].total_time = _combat_object:GetCombatTime()
--save the encounter name
local boss = _combat_object.is_boss
if (boss) then
TimeLine.combat_data [1].name = boss.name
else
TimeLine.combat_data [1].name = _combat_object.enemy
end
--save the date
local _start, _end = _combat_object:GetDate()
TimeLine.combat_data [1].date_start = _start or date ("%H:%M:%S")
TimeLine.combat_data [1].date_end = _end or date ("%H:%M:%S")
--> cooldowns
table.insert (TimeLine.cooldowns_timeline, 1, TimeLine.current_battle_cooldowns_timeline)
--> debuffs - close opened debuffs
for player_name, player_table in pairs (TimeLine.debuff_temp_table) do
for spellid, spell_table in pairs (player_table) do
if (spell_table.active) then
spell_table.active = false
tinsert (spell_table, _combat_object:GetCombatTime())
end
end
end
--> debufs - store debuff data
tinsert (TimeLine.debuff_timeline, 1, TimeLine.debuff_temp_table)
--> boss spells
tinsert (TimeLine.spellcast_boss, 1, TimeLine.current_enemy_spells)
tinsert (TimeLine.boss_spells, 1, TimeLine.current_spells_individual)
--> deaths
local deaths = {}
local death_list = _combat_object:GetDeaths()
for i, t in ipairs (death_list) do
local this_death = {}
local time_of_death = t[2]
local player_name = t[3]
local last_events = {}
for i = #t[1], 1, -1 do
local damage_event = t[1][i]
if (type (damage_event [1]) == "boolean" and damage_event [1]) then
local time = damage_event [4]
if (time+8 > time_of_death) then
tinsert (last_events, 1, damage_event)
if (#last_events >= 3) then
break
end
end
end
end
this_death.time = t.dead_at
this_death.events = last_events
if (not deaths [player_name]) then
deaths [player_name] = {}
end
tinsert (deaths [player_name], this_death)
end
tinsert (TimeLine.deaths_data, 1, deaths)
--> limit segments
if (#TimeLine.cooldowns_timeline > TimeLine.db.max_segments) then
table.remove (TimeLine.cooldowns_timeline, TimeLine.db.max_segments+1)
table.remove (TimeLine.debuff_timeline, TimeLine.db.max_segments+1)
table.remove (TimeLine.combat_data, TimeLine.db.max_segments+1)
table.remove (TimeLine.deaths_data, TimeLine.db.max_segments+1)
table.remove (TimeLine.spellcast_boss, TimeLine.db.max_segments+1)
end
--> show icon
if (TimeLine.open) then
TimeLine:Refresh()
end
TimeLine:ShowIcon()
else
--> discart cooldown table
table.wipe (TimeLine.current_battle_cooldowns_timeline or {})
table.wipe (TimeLine.debuff_temp_table or {})
table.wipe (TimeLine.current_enemy_spells or {})
table.wipe (TimeLine.current_spells_individual or {})
end
end
function TimeLine:OnDetailsEvent (event, ...)
if (event == "HIDE") then --> plugin hidded, disabled
self.open = false
elseif (event == "SHOW") then --> plugin hidded, disabled
self.open = true
TimeLine:RefreshScale()
elseif (event == "COMBAT_PLAYER_ENTER") then --> combat started
parser_cache = TimeLine:GetParserPlayerCache()
_combat_object = select (1, ...)
if (not _combat_object and Details) then
_combat_object = Details:GetCurrentCombat()
if (not _combat_object) then
return
end
end
--> create temp tables
TimeLine:NewCombat()
_is_in_combat = true
elseif (event == "COMBAT_PLAYER_LEAVE") then
TimeLine:FinishCombat()
_is_in_combat = false
elseif (event == "DETAILS_DATA_RESET") then
table.wipe (TimeLine.cooldowns_timeline)
table.wipe (TimeLine.combat_data)
table.wipe (TimeLine.debuff_timeline)
table.wipe (TimeLine.deaths_data)
TimeLine:Refresh()
TimeLine:CloseWindow()
TimeLine:HideIcon()
elseif (event == "DETAILS_STARTED") then
TimeLine:CanShowIcon()
elseif (event == "PLUGIN_DISABLED") then
TimeLine:HideIcon()
TimeLine:CloseWindow()
TimeLineFrame:UnregisterEvent ("COMBAT_LOG_EVENT_UNFILTERED")
elseif (event == "PLUGIN_ENABLED") then
TimeLineFrame:RegisterEvent ("COMBAT_LOG_EVENT_UNFILTERED")
TimeLine:CanShowIcon()
end
end
--> show icon on toolbar
function TimeLine:ShowIcon()
TimeLine.showing = true
--> [1] button to show [2] button animation: "star", "blink" or true (blink)
TimeLine:ShowToolbarIcon (TimeLine.ToolbarButton, "star")
end
--> hide icon on toolbar
function TimeLine:HideIcon()
TimeLine.showing = false
TimeLine:HideToolbarIcon (TimeLine.ToolbarButton)
end
function TimeLine:DelaySegmentRefresh()
for index, combat in ipairs (TimeLine.combat_data) do
if (combat.name) then
return Details_TimeLineSegmentDropdown.MyObject:Select (current_segment, true)
end
end
end
function TimeLine.RefreshWindow()
--> refresh it
TimeLine:Refresh()
--> refresh segments dropdown
TimeLine:ScheduleTimer ("DelaySegmentRefresh", 2)
return true
end
--> user clicked on button, need open or close window
function TimeLine:OpenWindow()
if (TimeLine.Frame:IsShown()) then
return TimeLine:CloseWindow()
else
TimeLine.open = true
end
--> build all window data
TimeLine:Refresh()
TimeLine:ScheduleTimer ("DelaySegmentRefresh", 1)
--> show
--TimeLineFrame:Show()
DetailsPluginContainerWindow.OpenPlugin (TimeLine)
--> hide cooltip
GameCooltip2:Hide()
return true
end
function TimeLine:CloseWindow()
--TimeLineFrame:Hide()
DetailsPluginContainerWindow.ClosePlugin()
TimeLine.open = false
return true
end
local cooltip_menu = function()
local CoolTip = GameCooltip2
CoolTip:Reset()
CoolTip:SetType ("menu")
CoolTip:SetOption ("TextSize", Details.font_sizes.menus)
CoolTip:SetOption ("TextFont", Details.font_faces.menus)
CoolTip:SetOption ("LineHeightSizeOffset", 3)
CoolTip:SetOption ("VerticalOffset", 2)
CoolTip:SetOption ("VerticalPadding", -4)
CoolTip:SetOption ("FrameHeightSizeOffset", -3)
Details:SetTooltipMinWidth()
--build the menu options
--> debuffs
CoolTip:AddLine ("Enemy Debuff Timeline")
CoolTip:AddMenu (1, function()
DetailsPluginContainerWindow.OpenPlugin (TimeLine)
current_type = type_debuff
TimeLine:Refresh()
TimeLine:RefreshButtons()
TimeLine:ScheduleTimer ("DelaySegmentRefresh", 1)
CoolTip:Hide()
end, "main")
CoolTip:AddIcon ([[Interface\ICONS\Spell_Shadow_ShadowWordPain]], 1, 1, 16, 16, 5/64, 59/64, 5/64, 59/64)
--> cooldowns
CoolTip:AddLine ("Raid Cooldown Timeline")
CoolTip:AddMenu (1, function()
DetailsPluginContainerWindow.OpenPlugin (TimeLine)
current_type = type_cooldown
TimeLine:Refresh()
TimeLine:RefreshButtons()
TimeLine:ScheduleTimer ("DelaySegmentRefresh", 1)
CoolTip:Hide()
end, "graph")
CoolTip:AddIcon ([[Interface\ICONS\Spell_Holy_GuardianSpirit]], 1, 1, 16, 16, 5/64, 59/64, 5/64, 59/64)
--> enemy spells
CoolTip:AddLine ("Enemy Cast Timeline")
CoolTip:AddMenu (1, function()
DetailsPluginContainerWindow.OpenPlugin (TimeLine)
current_type = type_enemy_spells
TimeLine:Refresh()
TimeLine:RefreshButtons()
TimeLine:ScheduleTimer ("DelaySegmentRefresh", 1)
CoolTip:Hide()
end, "graph")
CoolTip:AddIcon ([[Interface\ICONS\Spell_Shadow_SummonVoidWalker]], 1, 1, 16, 16, 5/64, 59/64, 5/64, 59/64)
--> spell individual
CoolTip:AddLine ("Enemy Spells Timeline")
CoolTip:AddMenu (1, function()
DetailsPluginContainerWindow.OpenPlugin (TimeLine)
current_type = type_boss_spells
TimeLine:Refresh()
TimeLine:RefreshButtons()
TimeLine:ScheduleTimer ("DelaySegmentRefresh", 1)
CoolTip:Hide()
end, "graph")
CoolTip:AddIcon ([[Interface\ICONS\Spell_Shadow_Requiem]], 1, 1, 16, 16, 5/64, 59/64, 5/64, 59/64)
--apply the backdrop settings to the menu
Details:FormatCooltipBackdrop()
CoolTip:SetOwner (TIMELINE_BUTTON, "bottom", "top", 0, 0)
CoolTip:ShowCooltip()
end
--> create the button to show on toolbar [1] function OnClick [2] texture [3] tooltip [4] width or 14 [5] height or 14 [6] frame name or nil
TimeLine.ToolbarButton = _detalhes.ToolBar:NewPluginToolbarButton (TimeLine.OpenWindow, [[Interface\Addons\Details_TimeLine\icon]], Loc ["STRING_PLUGIN_NAME"], Loc ["STRING_TOOLTIP"], 12, 12, "TIMELINE_BUTTON", cooltip_menu)
TimeLine.ToolbarButton.shadow = true
--> setpoint anchors mod if needed
TimeLine.ToolbarButton.y = 0
TimeLine.ToolbarButton.x = 0
--> build main frame
TimeLineFrame:SetFrameStrata ("HIGH")
TimeLineFrame:SetToplevel (true)
TimeLineFrame:SetPoint ("center", UIParent, "center", 0, 0)
TimeLineFrame.Width = 925
TimeLineFrame.Height = 575
local CONST_TOTAL_TIMELINES = 21 --timers shown in the top of the window
local CONST_ROW_HEIGHT = 16
local CONST_VALID_WIDHT = 784
local CONST_PLAYERFIELD_SIZE = 96
local CONST_VALID_HEIGHT = 528
local CONST_MENU_Y_POS = 6
local mode_buttons_width = 120
local mode_buttons_height = 20
TimeLineFrame:SetSize (TimeLineFrame.Width, TimeLineFrame.Height)
--[=
TimeLineFrame:EnableMouse (true)
TimeLineFrame:SetResizable (false)
TimeLineFrame:SetMovable (true)
TimeLineFrame:SetScript ("OnMouseDown",
function (self, botao)
if (botao == "LeftButton") then
if (self.isMoving) then
return
end
self:StartMoving()
self.isMoving = true
elseif (botao == "RightButton") then
if (self.isMoving) then
return
end
TimeLine:CloseWindow()
end
end)
TimeLineFrame:SetScript ("OnMouseUp",
function (self)
if (self.isMoving) then
self:StopMovingOrSizing()
self.isMoving = false
end
end)
--
--]=]
TimeLineFrame:SetBackdrop (_detalhes.PluginDefaults and _detalhes.PluginDefaults.Backdrop or {bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", tile = true, tileSize = 16,
edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1,
insets = {left = 1, right = 1, top = 1, bottom = 1}})
TimeLineFrame:SetBackdropColor (unpack (_detalhes.PluginDefaults and _detalhes.PluginDefaults.BackdropColor or {0, 0, 0, .6}))
TimeLineFrame:SetBackdropBorderColor (unpack (_detalhes.PluginDefaults and _detalhes.PluginDefaults.BackdropBorderColor or {0, 0, 0, 1}))
TimeLineFrame.bg1 = TimeLineFrame:CreateTexture (nil, "background")
TimeLineFrame.bg1:SetTexture ([[Interface\AddOns\Details\images\background]], true)
TimeLineFrame.bg1:SetAlpha (0.7)
TimeLineFrame.bg1:SetVertexColor (0.27, 0.27, 0.27)
TimeLineFrame.bg1:SetVertTile (true)
TimeLineFrame.bg1:SetHorizTile (true)
TimeLineFrame.bg1:SetAllPoints()
-- statusbar below the timeline chart
local bottom_texture = DetailsFrameWork:NewImage (TimeLineFrame, nil, TimeLineFrame.Width-4, 50, "background", nil, nil, "$parentBottomTexture")
bottom_texture:SetColorTexture (0, 0, 0, .6)
bottom_texture:SetPoint ("bottomleft", TimeLineFrame, "bottomleft", 2, 4)
TimeLine.Times = {}
for i = 1, CONST_TOTAL_TIMELINES do
local time = DetailsFrameWork:NewLabel (TimeLineFrame, nil, "$parentTime"..i, nil, "00:00")
time:SetPoint ("topleft", TimeLineFrame, "topleft", (CONST_PLAYERFIELD_SIZE-29) + (i*39), -28)
TimeLine.Times [i] = time
local line = DetailsFrameWork:NewImage (TimeLineFrame, nil, 1, CONST_VALID_HEIGHT, "border", nil, nil, "$parentTime"..i.."Bar")
line:SetColorTexture (1, 1, 1, .05)
line:SetPoint ("topleft", time, "topleft", 0, -10)
end
function TimeLine:UpdateTimeLine (total_time)
local linha = TimeLine.Times [CONST_TOTAL_TIMELINES]
local minutos, segundos = math.floor (total_time / 60), math.floor (total_time % 60)
if (segundos < 10) then
segundos = "0" .. segundos
end
if (minutos > 0) then
if (minutos < 10) then
minutos = "0" .. minutos
end
linha:SetText (minutos .. ":" .. segundos)
else
linha:SetText ("00:" .. segundos)
end
local time_div = total_time / (CONST_TOTAL_TIMELINES-1) --786 -- 49.125
for i = 2, CONST_TOTAL_TIMELINES -1 do
local linha = TimeLine.Times [i]
local this_time = time_div * (i-1)
local minutos, segundos = math.floor (this_time / 60), math.floor (this_time % 60)
if (segundos < 10) then
segundos = "0" .. segundos
end
if (minutos > 0) then
if (minutos < 10) then
minutos = "0" .. minutos
end
linha:SetText (minutos .. ":" .. segundos)
else
linha:SetText ("00:" .. segundos)
end
end
end
--> dropdown select type (label)
local select_type_label = DetailsFrameWork:NewLabel (TimeLineFrame, nil, "$parentTypeLabel", nil, Loc ["STRING_TYPE"])
--> dropdown select type (dropdown)
local selectTypeOption = function (_, _, selected)
current_type = selected
TimeLine:Refresh()
end
local type_menu = {
{value = type_cooldown, label = Loc ["STRING_TYPE_COOLDOWN"], onclick = selectTypeOption, icon = [[Interface\ICONS\Spell_Holy_GuardianSpirit]]},
{value = type_debuff, label = Loc ["STRING_TYPE_DEBUFF"], onclick = selectTypeOption, icon = [[Interface\ICONS\Spell_Shadow_ShadowWordPain]]}
}
local buildTypeMenu = function()
return type_menu
end
local select_type_dropdown = DetailsFrameWork:NewDropDown (TimeLineFrame, nil, "$parentTypeDropdown", nil, 120, 20, buildTypeMenu, 1, options_dropdown_template)
--> dropdown select combat (label)
local select_segment_label = DetailsFrameWork:NewLabel (TimeLineFrame, nil, "$parentSegmentLabel", nil, Loc ["STRING_SELECT_SEGMENT"], "GameFontNormal", nil, "orange")
select_segment_label.textsize = 9
--> dropdown select combat (dropdown)
local selectCombatOption = function (_, _, segment)
current_segment = segment
TimeLine:Refresh()
end
local buildCombatMenu = function()
local t = {}
for index, combat in ipairs (TimeLine.combat_data) do
if (combat.name) then
local minutos, segundos = math.floor (combat.total_time/60), math.floor (combat.total_time%60)
t [#t+1] = {value = index, label = combat.name .. " (" .. index .. ")", onclick = selectCombatOption, icon = [[Interface\GROUPFRAME\UI-GROUP-MAINASSISTICON]], desc = minutos .. "m " .. segundos .. "s " .. " (" .. (combat.date_start or "") .. " - " .. (combat.date_end or "") .. ")"}
end
end
return t
end
local select_segment_dropdown = DetailsFrameWork:NewDropDown (TimeLineFrame, nil, "$parentSegmentDropdown", nil, 120, 20, buildCombatMenu, nil, options_dropdown_template)
--> change mode end
local change_mode = function (_, _, mode)
current_type = mode
TimeLine:Refresh()
TimeLine:RefreshButtons()
end
--> select Cooldowns button
local show_cooldowns_button = framework:NewButton (TimeLineFrame, _, "$parentModeCooldownsButton", "ModeCooldownsButton", mode_buttons_width, mode_buttons_height, change_mode, type_cooldown, nil, nil, "Cooldowns", 1)
show_cooldowns_button:SetPoint ("bottomleft", TimeLineFrame, "bottomleft", 10, CONST_MENU_Y_POS)
show_cooldowns_button:SetTemplate (TimeLine:GetFramework():GetTemplate ("button", "DETAILS_PLUGIN_BUTTON_TEMPLATE"))
show_cooldowns_button:SetIcon ([[Interface\ICONS\Spell_Holy_GuardianSpirit]], nil, nil, nil, {5/64, 59/64, 5/64, 59/64}, nil, nil, 2)
local show_debuffs_button = framework:NewButton (TimeLineFrame, _, "$parentModeDebuffsButton", "ModeDebuffsButton", mode_buttons_width, mode_buttons_height, change_mode, type_debuff, nil, nil, "Debuffs", 1, options_button_template)
show_debuffs_button:SetPoint ("bottomleft", show_cooldowns_button, "bottomright", 5, 0)
show_debuffs_button:SetTemplate (TimeLine:GetFramework():GetTemplate ("button", "DETAILS_PLUGIN_BUTTON_TEMPLATE"))
show_debuffs_button:SetIcon ([[Interface\ICONS\Spell_Shadow_ShadowWordPain]], nil, nil, nil, {5/64, 59/64, 5/64, 59/64}, nil, nil, 2)
local show_enemyspells_button = framework:NewButton (TimeLineFrame, _, "$parentModeEnemyCastButton", "ModeEnemyCastButton", mode_buttons_width, mode_buttons_height, change_mode, type_enemy_spells, nil, nil, "Enemy Cast", 1, options_button_template)
show_enemyspells_button:SetPoint ("bottomleft", show_debuffs_button, "bottomright", 5, 0)
show_enemyspells_button:SetTemplate (TimeLine:GetFramework():GetTemplate ("button", "DETAILS_PLUGIN_BUTTON_TEMPLATE"))
show_enemyspells_button:SetIcon ([[Interface\ICONS\Spell_Shadow_SummonVoidWalker]], nil, nil, nil, {5/64, 59/64, 5/64, 59/64}, nil, nil, 2)
local show_spellsindividual_button = framework:NewButton (TimeLineFrame, _, "$parentModeEnemySpellsButton", "ModeEnemySpellsButton", mode_buttons_width, mode_buttons_height, change_mode, type_boss_spells, nil, nil, "Enemy Spells", 1, options_button_template)
show_spellsindividual_button:SetPoint ("bottomleft", show_enemyspells_button, "bottomright", 5, 0)
show_spellsindividual_button:SetTemplate (TimeLine:GetFramework():GetTemplate ("button", "DETAILS_PLUGIN_BUTTON_TEMPLATE"))
show_spellsindividual_button:SetIcon ([[Interface\ICONS\Spell_Shadow_Requiem]], nil, nil, nil, {5/64, 59/64, 5/64, 59/64}, nil, nil, 2)
local all_buttons = {show_cooldowns_button, show_debuffs_button, show_enemyspells_button, show_spellsindividual_button}
local set_button_as_pressed = function (button)
button:SetTemplate (TimeLine:GetFramework():GetTemplate ("button", "DETAILS_PLUGIN_BUTTONSELECTED_TEMPLATE"))
end
function TimeLine:RefreshButtons()
for _, button in ipairs (all_buttons) do
button:SetTemplate (TimeLine:GetFramework():GetTemplate ("button", "DETAILS_PLUGIN_BUTTON_TEMPLATE"))
end
if (current_type == type_cooldown) then
set_button_as_pressed (show_cooldowns_button)
elseif (current_type == type_debuff) then
set_button_as_pressed (show_debuffs_button)
elseif (current_type == type_enemy_spells) then
set_button_as_pressed (show_enemyspells_button)
elseif (current_type == type_boss_spells) then
set_button_as_pressed (show_spellsindividual_button)
end
end
TimeLine:RefreshButtons()
--> erase data button
local delete_button_func = function()
table.wipe (TimeLine.cooldowns_timeline)
table.wipe (TimeLine.combat_data)
table.wipe (TimeLine.debuff_timeline)
table.wipe (TimeLine.deaths_data)
TimeLine:Refresh()
if (TimeLine.open) then
TimeLine:HideIcon()
TimeLine:CloseWindow()
end
end
local delete_button = framework:NewButton (TimeLineFrame, _, "$parentDeleteButton", "DeleteButton", 100, 20, delete_button_func, nil, nil, nil, Loc ["STRING_RESET"], 1, TimeLine:GetFramework():GetTemplate ("button", "DETAILS_PLUGIN_BUTTON_TEMPLATE"))
delete_button:SetPoint ("bottomright", TimeLineFrame, "bottomright", -10, CONST_MENU_Y_POS)
delete_button:SetIcon ([[Interface\Buttons\UI-StopButton]], nil, nil, nil, {0, 1, 0, 1}, nil, nil, 2)
local options_button = framework:NewButton (TimeLineFrame, _, "$parentOptionsPanelButton", "OptionsPanelButton", 100, 20, TimeLine.OpenOptionsPanel, nil, nil, nil, Loc ["STRING_OPTIONS"], 1, TimeLine:GetFramework():GetTemplate ("button", "DETAILS_PLUGIN_BUTTON_TEMPLATE"))
options_button:SetPoint ("right", delete_button, "left", 2, 0)
options_button:SetIcon ([[Interface\Buttons\UI-OptionsButton]], nil, nil, nil, {0, 1, 0, 1}, nil, nil, 2)
--
local useIconsFunc = function()
TimeLine.db.useicons = not TimeLine.db.useicons
TimeLine:Refresh()
end
local useIconsText = framework:CreateLabel (TimeLineFrame, Loc ["STRING_SPELLICONS"], 9, "orange", "GameFontNormal", "UseIconsLabel", nil, "overlay")
useIconsText:SetPoint ("right", options_button, "left", -4, 0)
local useIconsCheckbox = framework:CreateSwitch (TimeLineFrame, useIconsFunc, false)
useIconsCheckbox:SetTemplate (framework:GetTemplate ("switch", "OPTIONS_CHECKBOX_TEMPLATE"))
useIconsCheckbox:SetAsCheckBox()
useIconsCheckbox:SetSize (16, 16)
useIconsCheckbox:SetPoint ("right", useIconsText, "left", -2, 0)
function TimeLine:UpdateShowSpellIconState()
useIconsCheckbox:SetValue (TimeLine.db.useicons)
end
--
--> search field ~search
local onPressEnter = function (_, _, text)
if (type (text) == "string") then
search = string.lower (text)
TimeLine:Refresh()
end
end
local search_label = DetailsFrameWork:NewLabel (TimeLineFrame, nil, "$parentSearchLabel", nil, Loc ["STRING_SEARCH"], "GameFontNormal", nil, "orange")
search_label.textsize = 9
local CONST_MENU_Y_POS_SECOND_ROW = 36
search_label:SetPoint ("bottomleft", TimeLineFrame, "bottomleft", 10, CONST_MENU_Y_POS_SECOND_ROW)
local searchbox = DetailsFrameWork:NewTextEntry (TimeLineFrame, _, "$parentSearch", "searchbox", 120, 20, onPressEnter, nil, nil, nil, nil, options_button_template)
searchbox:SetPoint ("left", search_label, "right", 2, 0)
searchbox:SetHook ("OnEscapePressed", function() search = nil; searchbox:SetText(""); searchbox:ClearFocus(); TimeLine:Refresh() end)
searchbox:SetHook ("OnEditFocusLost", function()
if (searchbox:GetText() == "") then
search = nil
TimeLine:Refresh()
end
end)
searchbox:SetHook ("OnTextChanged", function()
if (searchbox:GetText() ~= "") then
search = string.lower (searchbox:GetText())
TimeLine:Refresh()
else
search = nil
TimeLine:Refresh()
end
end)
--> Clear search field
local clearsearch = CreateFrame ("button", nil, TimeLineFrame, "UIPanelCloseButton")
clearsearch:SetPoint ("left", searchbox.widget, "right", -4, 0)
clearsearch:SetSize (24, 24)
clearsearch:SetScript ("OnClick", function (self)
searchbox:SetText ("")
searchbox:ClearFocus()
search = nil
TimeLine:Refresh()
end)
--> set the point on the segment box
select_segment_dropdown:SetPoint ("bottomright", delete_button, "topright", 0, 2)
select_segment_label:SetPoint ("right", select_segment_dropdown, "left", -2, 0)
local backdrop_row = {bgFile = [[Interface\Tooltips\UI-Tooltip-Background]], tile = true, tileSize = 16, insets = {left = 0, right = 0, top = 0, bottom = 0}}
function TimeLine:HideRows()
for i = 1, #TimeLine.rows do
local row = TimeLine.rows [i]
row:Hide()
end
end
local top_bar = DetailsFrameWork:NewImage (TimeLineFrame, nil, CONST_VALID_WIDHT + CONST_PLAYERFIELD_SIZE, 1, "overlay", nil, nil, "$parentRowTopLine")
top_bar:SetColorTexture (1, 1, 1, .4)
local bottom_bar = DetailsFrameWork:NewImage (TimeLineFrame, nil, CONST_VALID_WIDHT + CONST_PLAYERFIELD_SIZE, 1, "overlay", nil, nil, "$parentRowBottomLine")
bottom_bar:SetColorTexture (1, 1, 1, .4)
local row_on_enter = function (self)
top_bar:Show()
bottom_bar:Show()
top_bar:SetPoint ("bottomleft", self, "topleft")
top_bar:SetPoint ("bottomright", self, "topright")
bottom_bar:SetPoint ("topleft", self, "bottomleft")
bottom_bar:SetPoint ("topright", self, "bottomright")
self:SetBackdropColor (0.8, 0.8, 0.8, 0.4)
end
local row_on_leave = function (self)
top_bar:Hide()
bottom_bar:Hide()
self:SetBackdropColor (unpack (self.backdropColor))
end
local block_backdrop_onenter = {bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", tile = true, tileSize = 16,
edgeFile = [[Interface\AddOns\Details\images\border_2]], edgeSize = 8,
insets = {left = 0, right = 0, top = 0, bottom = 0}}
local block_backdrop_cooltip_white = {1, 1, 1, 1}
local block_on_enter = function (self)
self:SetBackdrop (block_backdrop_onenter)
self:SetBackdropBorderColor (0, 0, 0, .9)
self.texture:SetBlendMode ("ADD")
local parent = self:GetParent()
top_bar:SetPoint ("bottomleft", parent, "topleft")
top_bar:SetPoint ("bottomright", parent, "topright")
bottom_bar:SetPoint ("topleft", parent, "bottomleft")
bottom_bar:SetPoint ("topright", parent, "bottomright")
self:GetParent():SetBackdropColor (0.8, 0.8, 0.8, 0.4)
top_bar:Show()
bottom_bar:Show()
local spell = self.spell
local spell_name, _, spell_icon = GetSpellInfo (spell [1])
GameCooltip:Reset()
Details:CooltipPreset (2)
GameCooltip:SetOption ("TextSize", 10)
-- pegar o jogador
-- dar foreach nos cooldowns
-- ver se algum come�a antes e termina depois de come�ar este
local player_name = spell [4]
local playertable = TimeLine [current_type] [current_segment] [player_name]
local time = spell [2]
local duration = spell [5]
--playertable � uma array com os cooldowns usados
if (current_type == type_cooldown) then
GameCooltip:AddLine (spell_name, nil, 1, "white")
GameCooltip:AddIcon (spell_icon, 1, 1, 14, 14, .1, .9, .1, .9)
local minutos, segundos = math.floor (spell [2]/60), math.floor (spell [2]%60)
GameCooltip:AddLine (Loc ["STRING_TIME"] .. ": |cFFFFFFFF" .. minutos .. "m " .. segundos .. "s |r")
local class = TimeLine:GetClass (spell [3] or "")
local formatedTargetName = DetailsFrameWork:AddClassColorToText (spell [3] or "", class)
GameCooltip:AddLine (Loc ["STRING_TARGET"] .. ": |cFFFFFFFF" .. formatedTargetName .. "|r")
for index, spellused in ipairs (playertable) do
if (spellused [3] ~= spell[1]) then --spellids diferentes
--tempo de luta que o cooldown foi usado
-- se ele foi usado antes e se foi usado a 8 seg de diferen�a
if ( (spellused [1] <= time and spellused [1] + 8 >= time) or (spellused [1] >= time and spellused [1] - 8 <= time) ) then
local spellname, _, spellicon = GetSpellInfo (spellused [3])
GameCooltip:AddLine (spellname, nil, 1, "silver")
GameCooltip:AddIcon (spellicon, 1, 1, 14, 14, .1, .9, .1, .9)
local minutos, segundos = math.floor (spellused [1]/60), math.floor (spellused [1]%60)
GameCooltip:AddLine (Loc ["STRING_TIME"] .. ": " .. minutos .. "m " .. segundos .. "s ", nil, 1, "silver")
local class = TimeLine:GetClass (spellused [2] or "")
local formatedTargetName = DetailsFrameWork:AddClassColorToText (spellused [2] or "", class)
GameCooltip:AddLine (Loc ["STRING_TARGET"] .. ": |cFFFFFFFF" .. formatedTargetName .. "|r")
GameCooltip:AddLine (" ")
end
end
end
elseif (current_type == type_debuff) then
GameCooltip:AddLine (spell_name, nil, 1, "white")
GameCooltip:AddIcon (spell_icon, 1, 1, 14, 14, .1, .9, .1, .9)
local minutos, segundos = math.floor (spell [2]/60), math.floor (spell [2]%60)
GameCooltip:AddLine (Loc ["STRING_TIME"] .. ": |cFFFFFFFF" .. minutos .. "m " .. segundos .. "s |r")
GameCooltip:AddLine (Loc ["STRING_SOURCE"] .. ": |cFFFFFFFF" .. (spell [6].source or Loc ["STRING_UNKNOWN"]) .. "|r")
GameCooltip:AddLine (Loc ["STRING_ELAPSED"] .. ": |cFFFFFFFF" .. string.format ("%.1f", duration) .. " " .. Loc ["STRING_SECONDS"] .. "|r")
local blocks = self:GetParent().blocks
for _, block in ipairs (blocks) do
if (block.debuff_start and block:IsShown() and block ~= self and ((block.debuff_start <= time and block.debuff_start + 8 >= time) or (block.debuff_start >= time and block.debuff_start - 8 <= time))) then
GameCooltip:AddLine ("")
local spell_name, _, spell_icon = GetSpellInfo (block.spell [1])
GameCooltip:AddLine (spell_name)
GameCooltip:AddIcon (spell_icon, 1, 1, 14, 14, .1, .9, .1, .9)
local minutos, segundos = math.floor (block.debuff_start / 60), math.floor (block.debuff_start % 60)
GameCooltip:AddLine (Loc ["STRING_TIME"] .. ": |cFFFFFFFF" .. minutos .. "m " .. segundos .. "s ")
GameCooltip:AddLine (Loc ["STRING_SOURCE"] .. ": |cFFFFFFFF" .. (block.spell [6].source or Loc ["STRING_UNKNOWN"]))
GameCooltip:AddLine (Loc ["STRING_ELAPSED"] .. ": |cFFFFFFFF" .. string.format ("%.1f", block.spell [5]) .. " " .. Loc ["STRING_SECONDS"])
end
end
elseif (current_type == type_enemy_spells) then
GameCooltip:AddLine (spell_name, nil, 1, "white")
GameCooltip:AddIcon (spell_icon, 1, 1, 14, 14, .1, .9, .1, .9)
local minutos, segundos = math.floor (spell [2]/60), math.floor (spell [2]%60)
GameCooltip:AddLine (Loc ["STRING_TIME"] .. ": |cFFFFFFFF" .. minutos .. "m " .. segundos .. "s |r")
GameCooltip:AddLine (Loc ["STRING_SOURCE"] .. ": |cFFFFFFFF" .. (spell [6].source or Loc ["STRING_UNKNOWN"]) .. "|r")
local class = TimeLine:GetClass (self.TargetName or "")
local formatedTargetName = DetailsFrameWork:AddClassColorToText (self.TargetName or "", class)
GameCooltip:AddLine (Loc ["STRING_TARGET"] .. ": |cFFFFFFFF" .. formatedTargetName .. "|r")
local blocks = self:GetParent().blocks
for _, block in ipairs (blocks) do
if (block.cast_start and block:IsShown() and block ~= self and ((block.cast_start <= time and block.cast_start + 8 >= time) or (block.cast_start >= time and block.cast_start - 8 <= time))) then
GameCooltip:AddLine ("")
local spell_name, _, spell_icon = GetSpellInfo (block.spell [1])
GameCooltip:AddLine (spell_name)
GameCooltip:AddIcon (spell_icon, 1, 1, 14, 14, .1, .9, .1, .9)
local minutos, segundos = math.floor (block.cast_start / 60), math.floor (block.cast_start % 60)
GameCooltip:AddLine (Loc ["STRING_TIME"] .. ": |cFFFFFFFF" .. minutos .. "m " .. segundos .. "s ")
GameCooltip:AddLine (Loc ["STRING_SOURCE"] .. ": |cFFFFFFFF" .. (block.SourceName or Loc ["STRING_UNKNOWN"]))
local targetName = block.TargetName or ""
local class = TimeLine:GetClass (targetName)
local formatedTargetName = DetailsFrameWork:AddClassColorToText (targetName, class)
GameCooltip:AddLine (Loc ["STRING_TARGET"] .. ": |cFFFFFFFF" .. formatedTargetName .. "|r")
end
end
elseif (current_type == type_boss_spells) then
GameCooltip:AddLine (spell_name, nil, 1, "white")
GameCooltip:AddIcon (spell_icon, 1, 1, 14, 14, .1, .9, .1, .9)
local minutos, segundos = math.floor (spell [2]/60), math.floor (spell [2]%60)
GameCooltip:AddLine (Loc ["STRING_TIME"] .. ": |cFFFFFFFF" .. minutos .. "m " .. segundos .. "s |r")
GameCooltip:AddLine (Loc ["STRING_SOURCE"] .. ": |cFFFFFFFF" .. (spell [6].source or Loc ["STRING_UNKNOWN"]) .. "|r")
local class = TimeLine:GetClass (self.TargetName or "")
local formatedTargetName = DetailsFrameWork:AddClassColorToText (self.TargetName or "", class)
GameCooltip:AddLine (Loc ["STRING_TARGET"] .. ": |cFFFFFFFF" .. formatedTargetName .. "|r")
local blocks = self:GetParent().blocks
for _, block in ipairs (blocks) do
if (block.cast_start and block:IsShown() and block ~= self and ((block.cast_start <= time and block.cast_start + 8 >= time) or (block.cast_start >= time and block.cast_start - 8 <= time))) then
GameCooltip:AddLine ("")
local spell_name, _, spell_icon = GetSpellInfo (block.spell [1])
GameCooltip:AddLine (spell_name)
GameCooltip:AddIcon (spell_icon, 1, 1, 14, 14, .1, .9, .1, .9)
local minutos, segundos = math.floor (block.cast_start / 60), math.floor (block.cast_start % 60)
GameCooltip:AddLine (Loc ["STRING_TIME"] .. ": |cFFFFFFFF" .. minutos .. "m " .. segundos .. "s ")
GameCooltip:AddLine (Loc ["STRING_SOURCE"] .. ": |cFFFFFFFF" .. (block.SourceName or Loc ["STRING_UNKNOWN"]))
local targetName = block.TargetName or ""
local class = TimeLine:GetClass (targetName)
local formatedTargetName = DetailsFrameWork:AddClassColorToText (targetName, class)
GameCooltip:AddLine (Loc ["STRING_TARGET"] .. ": |cFFFFFFFF" .. formatedTargetName .. "|r")
end
end
end
GameCooltip:ShowCooltip (self, "tooltip")
end
local block_on_leave = function (self)
self:SetBackdrop (nil)
self:SetBackdropBorderColor (0, 0, 0, 0)
self.texture:SetBlendMode ("BLEND")
top_bar:Hide()
bottom_bar:Hide()
self:GetParent():SetBackdropColor (unpack (self:GetParent().backdropColor))
GameCooltip:Hide()
end
function TimeLine:CreateSpellBlock (row)
local block = CreateFrame ("frame", nil, row, "BackdropTemplate")
row.block_frame_level = row.block_frame_level + 1
if (row.block_frame_level > 9) then
row.block_frame_level = 3
end
block.spell = {0, 0, "", "", 0} -- [1] spellid [2] used at [3] target name [4] playername [5] effect time
block:SetHeight (CONST_ROW_HEIGHT-2)
block:SetScript ("OnEnter", block_on_enter)
block:SetScript ("OnLeave", block_on_leave)
local texture = block:CreateTexture (nil, "background")
texture:SetAllPoints (block)
texture:SetColorTexture (0, 1, 0, .2)
block.texture = texture
local icon = block:CreateTexture (nil, "border")
icon:SetPoint ("topleft", texture, "topleft", 0, 0)
icon:SetPoint ("bottomleft", texture, "bottomleft", 0, 0)
icon:SetWidth (16)
block.spellicon = icon
return block
end
local SetSpellBlock = function (row, block, time_used, spellid, effect_time, target, total_time, pixel_per_sec, player_name, player_table, color, block_index)
block.spell [1] = spellid
block.spell [2] = time_used
block.spell [3] = target
block.spell [4] = player_name
block.spell [5] = effect_time
block.spell [6] = player_table
local where = pixel_per_sec * time_used
block:ClearAllPoints()
block:SetPoint ("left", row, "left", where + CONST_PLAYERFIELD_SIZE, 0)
if (effect_time < 5) then
effect_time = 5
elseif (effect_time > 20) then
effect_time = 20
end
block:SetWidth (effect_time * pixel_per_sec)
block.texture:SetColorTexture (unpack (color))
if (TimeLine.db.useicons) then
local _, _, icon = GetSpellInfo (spellid)
block.spellicon:SetTexture (icon)
block.spellicon:SetTexCoord (.1, .9, .1, .9)
block.spellicon:SetWidth (block:GetHeight())
block.spellicon:SetAlpha (0.834)
--remove the background texture is using the icon of the spell
block.texture:SetColorTexture (0, 0, 0, 0)
else
block.spellicon:SetTexture (nil)
end
if (search) then
local spellname = GetSpellInfo (spellid)
spellname = string.lower (spellname)
if (spellname:find (search)) then
block:Show()
else
block:Hide()
end
else
block:Show()
end
end
local pin_on_enter = function (self)
self:SetBackdrop (block_backdrop_onenter)
self:SetBackdropBorderColor (0, 0, 0, .9)
self.texture:SetBlendMode ("ADD")
top_bar:SetPoint ("bottom", self:GetParent(), "top")
bottom_bar:SetPoint ("top", self:GetParent(), "bottom")
self:GetParent():SetBackdropColor (0.8, 0.8, 0.8, 0.4)
top_bar:Show()
bottom_bar:Show()
GameCooltip:Reset()
Details:CooltipPreset (2)
GameCooltip:SetOption ("TextSize", 10)
for i = 1, #self.table do
local spellname, _, spellicon = _GetSpellInfo (self.table[i][2])
GameCooltip:AddLine (spellname, TimeLine:ToK2 (self.table[i][3]), 1, "silver", "orange")
GameCooltip:AddIcon (spellicon, 1, 1, 14, 14)
end
GameCooltip:AddLine ("")
local minutos, segundos = math.floor (self.time/60), math.floor (self.time%60)
GameCooltip:AddLine (Loc ["STRING_TIME"] .. ": " .. minutos .. "m " .. segundos .. "s ")
GameCooltip:ShowCooltip (self, "tooltip")
end
local pin_on_leave = function (self)
GameCooltip:Hide()
end
local PlaceDeathPins = function (row, i, death, total_time, pixel_per_sec)
local pin = row.pins [i]
if (not pin) then
pin = CreateFrame ("frame", nil, row, "BackdropTemplate")
pin:SetFrameLevel (12)
pin:SetFrameStrata ("DIALOG")
pin:SetScript ("OnEnter", pin_on_enter)
pin:SetScript ("OnLeave", pin_on_leave)
local texture = pin:CreateTexture (nil, "overlay")
texture:SetAllPoints()
texture:SetColorTexture (1, 1, 1, 0.4)
pin.texture = texture
pin:SetSize (4, 14)
pin.table = {}
row.pins [i] = pin
end
local where = pixel_per_sec * death.time
pin:ClearAllPoints()
pin:SetPoint ("left", row, "left", where + CONST_PLAYERFIELD_SIZE, 0)
pin.time = death.time
for event = 1, #death.events do
local ev = death.events[event]
pin.table [event] = {ev[4], ev[2], ev[3]} --time spellid amount
end
pin:Show()
end
--> player_table: [1] time [2] target [3] spellid
local SetPlayer = function (row, player_name, player_table, total_time, pixel_per_sec, deaths)
--> set name and class color
if (type (player_name) == "string" and player_name:find ("-")) then
row.name.text = player_name:gsub (("-.*"), "")
else
if (type (player_name) == "number") then
row.name.text = GetSpellInfo (player_name)
else
row.name.text = player_name
end
end
local class = TimeLine:GetClass (player_name)
local spec = TimeLine:GetSpec (player_name)
if (spec) then
row.name.color = TimeLine.class_colors [class or "PRIEST"]
row.icon.texture = [[Interface\AddOns\Details\images\spec_icons_normal_alpha]]
row.icon.texcoord = _detalhes.class_specs_coords [spec]
elseif (class) then
if (class == "UNKNOW") then
if (current_type == type_boss_spells) then
--use the icon spell icon as the class icon
local spellName, _, spellIcon = GetSpellInfo (player_name)
row.icon.texture = spellIcon
row.icon.texcoord = {.1, .9, .1, .9}
row.name.color = "white"
elseif (current_type == type_enemy_spells) then
row.name.color = "white"
row.icon.texture = nil
else
row.name.color = "white"
row.icon.texture = nil
end
else
row.name.color = TimeLine.class_colors [class]
row.icon.texture = class_icons_with_alpha
row.icon.texcoord = TimeLine.class_coords [class]
end
else
if (current_type == type_boss_spells) then
--use the icon spell icon as the class icon
local _, _, spellIcon = GetSpellInfo (player_table [3])
row.icon.texture = spellIcon
row.icon.texcoord = {.1, .9, .1, .9}
else
row.name.color = "white"
row.icon.texture = nil
end
end
--> clear all blocks
for _, block in ipairs (row.blocks) do
block:Hide()
end
--> clear all pins
for index, pin in ipairs (row.pins) do
pin:Hide()
wipe (pin.table)
end
--> place death pins
if (deaths) then
for index, death in ipairs (deaths) do
PlaceDeathPins (row, index, death, total_time, pixel_per_sec)
end
end
--> if showing cooldowns:
if (current_type == type_cooldown and player_table) then
for spell_index, spell_table in ipairs (player_table) do
local time_used = spell_table [1]
local target = spell_table [2]
local spellid = spell_table [3]
local spellInfo = DetailsFrameWork.CooldownsInfo [spellid]
local cooldown, effectTime
if (spellInfo) then
cooldown, effectTime = spellInfo.cooldown, spellInfo.duration
else
cooldown, effectTime = 8, 8
end
effectTime = effectTime or 6
local block = row.blocks [spell_index]
if (not block) then
row.blocks [spell_index] = TimeLine:CreateSpellBlock (row)
block = row.blocks [spell_index]
end
SetSpellBlock (row, block, time_used, spellid, effectTime, target, total_time, pixel_per_sec, player_name, player_table, green)
end
--> showing debuffs
elseif (current_type == type_debuff and player_table) then
local o = 1
for spell_id, debuff_timers in pairs (player_table) do
local start = true
local i = 1
for index, time in ipairs (debuff_timers) do
if (start) then
local start_time = time
local end_time = debuff_timers [i+1]
local block = row.blocks [o]
if (not block) then
row.blocks [o] = TimeLine:CreateSpellBlock (row)
block = row.blocks [o]
end
local debuff_elapsed = (end_time or 0) - (start_time or 0)
block.debuff_start = start_time
block.debuff_end = end_time
SetSpellBlock (row, block, start_time, spell_id, debuff_elapsed, myname, total_time, pixel_per_sec, player_name, debuff_timers, red, o)
o = o + 1
end
start = not start
i = i + 1
end
end
elseif (current_type == type_enemy_spells and player_table) then
local o = 1
for index, spellTable in ipairs (player_table) do
local combatTime, sourceName, spellID, token, targetName = unpack (spellTable)
local block = row.blocks [o]
if (not block) then
row.blocks [o] = TimeLine:CreateSpellBlock (row)
block = row.blocks [o]
end
block.cast_start = combatTime
block.cast_end = combatTime + 8
local spellName, _, spellIcon = GetSpellInfo (spellID)
player_table.source = sourceName or ""
player_table.target = targetName or ""
block.SourceName = sourceName or ""
block.TargetName = targetName or ""
SetSpellBlock (row, block, combatTime, spellID, 8, spellName, total_time, pixel_per_sec, player_name, player_table, red, o)
o = o + 1
end
elseif (current_type == type_boss_spells and player_table) then
local o = 1
for index, spellTable in ipairs (player_table) do
local combatTime, sourceName, spellID, token, targetName = unpack (spellTable)
local block = row.blocks [o]
if (not block) then
row.blocks [o] = TimeLine:CreateSpellBlock (row)
block = row.blocks [o]
end
block.cast_start = combatTime
block.cast_end = combatTime + 8
local spellName, _, spellIcon = GetSpellInfo (spellID)
player_table.source = sourceName or ""
player_table.target = targetName or ""
block.SourceName = sourceName or ""
block.TargetName = targetName or ""
SetSpellBlock (row, block, combatTime, spellID, 8, spellName, total_time, pixel_per_sec, player_name, player_table, red, o)
o = o + 1
end
end
end
local on_row_mousedown = function (self, button)
if (button == "LeftButton") then
self:GetParent():StartMoving()
self:GetParent().isMoving = true
end
end
local on_row_mouseup = function (self)
if (self:GetParent().isMoving) then
self:GetParent():StopMovingOrSizing()
self:GetParent().isMoving = false
end
end
function TimeLine:CreateRow()
local index = #TimeLine.rows+1
-- cria as labels e mouse overs e da o set point
local f = CreateFrame ("frame", "DetailsTimeTimeRow"..index, TimeLineFrame, "BackdropTemplate")
f:SetSize (TimeLineFrame.Width - 15, CONST_ROW_HEIGHT)
f:SetScript ("OnEnter", row_on_enter)
f:SetScript ("OnLeave", row_on_leave)
f.block_frame_level = 3
f.icon = DetailsFrameWork:NewImage (f, nil, CONST_ROW_HEIGHT, CONST_ROW_HEIGHT, "overlay", nil, nil, "$parentIcon")
f.icon:SetPoint ("left", f, "left", 2, 0)
f.name = DetailsFrameWork:NewLabel (f, nil, "$parentName", nil)
f.name:SetPoint ("left", f.icon, "right", 2, 0)
f:SetBackdrop (backdrop_row)
if (index%2 == 0) then
f.backdropColor = BUTTON_BACKGROUND_COLOR
f:SetBackdropColor (unpack (BUTTON_BACKGROUND_COLOR))
else
f.backdropColor = BUTTON_BACKGROUND_COLOR2
f:SetBackdropColor (unpack (BUTTON_BACKGROUND_COLOR2))
end
local height = (index * CONST_ROW_HEIGHT) + 32
f:SetPoint ("topleft", TimeLineFrame, "topleft", 8, -height)
f.blocks = {}
f.pins = {}
TimeLine.rows [index] = f
return f
end
local sort = function (a, b)
return a[2] < b[2]
end
function TimeLine:Refresh()
-- quando abrir sempre ir pro ultimo combate
TimeLine:HideRows()
if (not TimeLine.combat_data [1]) then
return
end
local _table_to_use = TimeLine [current_type] [current_segment]
local total_time = TimeLine.combat_data [current_segment].total_time
local pixel_per_sec = CONST_VALID_WIDHT / total_time
local i = 0
if (not _table_to_use) then
TimeLine:Msg (Loc ["STRING_DATAINVALID"])
return
end
local sorted = {}
for player_name, player_table in pairs (_table_to_use) do
sorted [#sorted+1] = {player_table, player_name}
end
table.sort (sorted, sort)
local deaths = TimeLine.deaths_data
local segment_deaths = deaths [current_segment]
local has_something = {}
for index, t in ipairs (sorted) do
local player_table, player_name = t [1], t [2]
i = i + 1
local row = TimeLine.rows [i]
if (not row) then
row = TimeLine:CreateRow()
end
local deaths = segment_deaths [player_name]
SetPlayer (row, player_name, player_table, total_time, pixel_per_sec, deaths)
has_something [player_name] = true
row:Show()
end
if (current_type ~= type_boss_spells and current_type ~= type_enemy_spells) then
local sorted = {}
for player_name, t in pairs (segment_deaths) do
if (not has_something [player_name]) then
sorted [#sorted+1] = {t, player_name}
end
end
table.sort (sorted, sort)
for index, t in ipairs (sorted) do
i = i + 1
local row = TimeLine.rows [i]
if (not row) then
row = TimeLine:CreateRow()
end
local death, player_name = t [1], t [2]
SetPlayer (row, player_name, nil, total_time, pixel_per_sec, death)
row:Show()
end
end
TimeLine:UpdateTimeLine (total_time)
end
end
--<
function TimeLine:ArrangeCooldownsInOrder (auraType, auraList)
if (auraType == "BUFF") then
local newTable = {}
for _, spellid in ipairs (auraList) do
tinsert (newTable, {spellid, GetSpellInfo (spellid)})
end
table.sort (newTable, function(a, b) return a[2] < b[2] end)
return newTable
elseif (auraType == "DEBUFF") then
local newTable = {}
for _, spellid in ipairs (auraList) do
tinsert (newTable, {spellid, GetSpellInfo (spellid)})
end
table.sort (newTable, function(a, b) return a[2] < b[2] end)
return newTable
end
end
function TimeLine:OnCooldown (token, time, who_serial, who_name, who_flags, target_serial, target_name, target_flags, spellid, spellname)
--> hooks run inside parser and do not check if the plugin is enabled or not.
--> we need to check this here before continue.
if (not TimeLine.__enabled) then
return
elseif (not _is_in_combat) then
return
end
local data = {_combat_object:GetCombatTime(), target_name, spellid}
local player_table = TimeLine.current_battle_cooldowns_timeline [who_name]
if (not player_table) then
TimeLine.current_battle_cooldowns_timeline [who_name] = {}
player_table = TimeLine.current_battle_cooldowns_timeline [who_name]
end
tinsert (player_table, data)
end
function TimeLine:EnemySpellCast (time, token, hidding, sourceGUID, sourceName, sourceFlag, sourceFlag2, targetGUID, targetName, targetFlag, targetFlag2, spellID, spellName, spellType, amount, overKill, school, resisted, blocked, absorbed, isCritical)
if (sourceFlag) then
if (_bit_band (sourceFlag, 0x00000060) ~= 0) then --is enemy
if (_bit_band (sourceFlag, 0x00000400) == 0) then --is not player
local petInfo = Details.tabela_pets.pets [sourceGUID]
if (not petInfo or _bit_band (petInfo[3], 0x00000400) == 0) then --isnt a pet or owner isn't a player
sourceName = sourceName or select (1, GetSpellInfo (spellID or 0)) or "--x--x--"
--spells cast by enemy
local data = {_combat_object:GetCombatTime(), sourceName, spellID, token, targetName}
local enemyTable = TimeLine.current_enemy_spells [sourceName]
if (not enemyTable) then
enemyTable = {}
TimeLine.current_enemy_spells [sourceName] = enemyTable
end
tinsert (enemyTable, data)
--individual spells cast
local data = {_combat_object:GetCombatTime(), sourceName, spellID, token, targetName}
local spellsTable = TimeLine.current_spells_individual [spellID]
if (not spellsTable) then
spellsTable = {}
TimeLine.current_spells_individual [spellID] = spellsTable
end
tinsert (spellsTable, data)
end
end
end
end
end
function TimeLine:AuraOn (time, _, _, who_serial, who_name, who_flags, _, target_serial, target_name, target_flags, _, spellid, spellname, spelltype, auratype, amount)
if (auratype == "DEBUFF") then
--> is the target a player?
if (_bit_band (target_flags, 0x00000400) ~= 0) then
--> is the source an enemy?
if (_bit_band (who_flags, 0x00000060) ~= 0 or (not who_serial or not who_name)) then
local player_table = TimeLine.debuff_temp_table [target_name]
if (not player_table) then
player_table = {}
TimeLine.debuff_temp_table [target_name] = player_table
end
local spell_table = player_table [spellid]
if (not spell_table) then
spell_table = {}
spell_table.source = who_name or "[*] " .. spellname
spell_table.stacks = {}
player_table [spellid] = spell_table
end
--was a refresh
if (spell_table.active) then
return
end
--array
tinsert (spell_table, _combat_object:GetCombatTime())
--hash
spell_table.active = true
end
end
end
end
function TimeLine:AuraOff (time, _, _, who_serial, who_name, who_flags, _, target_serial, target_name, target_flags, _, spellid, spellname, spelltype, auratype, amount)
if (auratype == "DEBUFF") then
--> is the target a player?
if (_bit_band (target_flags, 0x00000400) ~= 0) then
--> is the source an enemy?
if (_bit_band (who_flags, 0x00000060) ~= 0 or (not who_serial or not who_name)) then
local player_table = TimeLine.debuff_temp_table [target_name]
if (not player_table) then
return
end
local spell_table = player_table [spellid]
if (not spell_table) then
return
end
if (not spell_table.active) then
return
end
--array
tinsert (spell_table, _combat_object:GetCombatTime())
--hash
spell_table.active = false
end
end
end
end
local build_options_panel = function()
local options_frame = TimeLine:CreatePluginOptionsFrame ("TimeLineOptionsWindow", Loc ["STRING_OPTIONS_TITLE"], 1)
local menu = {
--o icone pode ser mostrado sempre que tiver algum segmento nele.
--ao resetar ele esconde o icone, ao resetar o details ele apaga os dados tbm e esconde o �cone.
{
type = "range",
get = function() return TimeLine.db.max_segments end,
set = function (self, fixedparam, value) TimeLine.db.max_segments = value end,
min = 3,
max = 25,
step = 1,
desc = Loc ["STRING_OPTIONS_MAXSEGMENTS_DESC"],
name = Loc ["STRING_OPTIONS_MAXSEGMENTS"]
},
{
type = "color",
get = function() return TimeLine.db.backdrop_color end,
set = function (self, r, g, b, a)
local current = TimeLine.db.backdrop_color
current[1], current[2], current[3], current[4] = r, g, b, a
TimeLine:RefreshBackgroundColor()
end,
desc = Loc ["STRING_OPTIONS_BGCOLOR_DESC"],
name = Loc ["STRING_OPTIONS_BGCOLOR"]
},
{
type = "range",
get = function() return TimeLine.db.window_scale end,
set = function (self, fixedparam, value) TimeLine.db.window_scale = value; TimeLine:RefreshScale() end,
min = 0.65,
max = 1.50,
step = 0.1,
desc = Loc ["STRING_OPTIONS_WINDOWCOLOR_DESC"],
name = Loc ["STRING_OPTIONS_WINDOWCOLOR"],
usedecimals = true,
},
}
local options_text_template = TimeLine:GetFramework():GetTemplate ("font", "OPTIONS_FONT_TEMPLATE")
local options_dropdown_template = TimeLine:GetFramework():GetTemplate ("dropdown", "OPTIONS_DROPDOWN_TEMPLATE")
local options_switch_template = TimeLine:GetFramework():GetTemplate ("switch", "OPTIONS_CHECKBOX_TEMPLATE")
local options_slider_template = TimeLine:GetFramework():GetTemplate ("slider", "OPTIONS_SLIDER_TEMPLATE")
local options_button_template = TimeLine:GetFramework():GetTemplate ("button", "OPTIONS_BUTTON_TEMPLATE")
_detalhes.gump:BuildMenu (options_frame, menu, 15, -65, 260, true, options_text_template, options_dropdown_template, options_switch_template, true, options_slider_template, options_button_template)
options_frame:SetBackdropColor (0, 0, 0, .9)
end
TimeLine.OpenOptionsPanel = function()
if (not TimeLineOptionsWindow) then
build_options_panel()
end
TimeLineOptionsWindow:Show()
end
function TimeLine:RefreshBackgroundColor()
TimeLineFrame:SetBackdropColor (unpack (TimeLine.db.backdrop_color))
end
function TimeLine:RefreshScale()
local scale = TimeLine.db.window_scale
if (TimeLineFrame) then
TimeLineFrame:SetScale (scale)
end
end
local CombatLogGetCurrentEventInfo = CombatLogGetCurrentEventInfo
function TimeLine:OnEvent (_, event, ...)
if (event == "COMBAT_LOG_EVENT_UNFILTERED" and _combat_object) then
local time, token, hidding, sourceGUID, sourceName, sourceFlag, sourceFlag2, targetGUID, targetName, targetFlag, targetFlag2, spellID, spellName, spellType, amount, overKill, school, resisted, blocked, absorbed, isCritical = CombatLogGetCurrentEventInfo()
local ev = token
if (ev == "SPELL_AURA_APPLIED" or ev == "SPELL_AURA_REFRESH") then
TimeLine:AuraOn (time, token, hidding, sourceGUID, sourceName, sourceFlag, sourceFlag2, targetGUID, targetName, targetFlag, targetFlag2, spellID, spellName, spellType, amount, overKill, school, resisted, blocked, absorbed, isCritical)
elseif (ev == "SPELL_AURA_REMOVED") then
TimeLine:AuraOff (time, token, hidding, sourceGUID, sourceName, sourceFlag, sourceFlag2, targetGUID, targetName, targetFlag, targetFlag2, spellID, spellName, spellType, amount, overKill, school, resisted, blocked, absorbed, isCritical)
elseif (ev == "SPELL_CAST_SUCCESS") then
TimeLine:EnemySpellCast (time, token, hidding, sourceGUID, sourceName, sourceFlag, sourceFlag2, targetGUID, targetName, targetFlag, targetFlag2, spellID, spellName, spellType, amount, overKill, school, resisted, blocked, absorbed, isCritical)
end
elseif (event == "ADDON_LOADED") then
local AddonName = select (1, ...)
if (AddonName == "Details_TimeLine") then
if (_G._detalhes) then
--> create widgets
CreatePluginFrames()
local MINIMAL_DETAILS_VERSION_REQUIRED = 128
local db = DetailsTimeLineDB
--> Install
local install, saveddata, is_enabled = _G._detalhes:InstallPlugin ("TOOLBAR", Loc ["STRING_PLUGIN_NAME"], [[Interface\CHATFRAME\ChatFrameExpandArrow]], TimeLine, "DETAILS_PLUGIN_TIME_LINE", MINIMAL_DETAILS_VERSION_REQUIRED, "Details! Team", TimeLine.version_string)
if (type (install) == "table" and install.error) then
print (install.error)
return
end
if (not db) then
db = {}
DetailsTimeLineDB = db
db.hide_on_combat = false
db.useicons = true
db.max_segments = 4
db.backdrop_color = {0, 0, 0, .4}
db.window_scale = 1
db.cooldowns_timeline = {}
db.debuff_timeline = {}
db.combat_data = {}
db.deaths_data = {}
if (saveddata) then
saveddata.cooldowns_timeline = nil
saveddata.debuff_timeline = nil
saveddata.combat_data = nil
saveddata.deaths_data = nil
saveddata.hide_on_combat = nil
saveddata.max_segments = nil
saveddata.backdrop_color = nil
saveddata.window_scale = nil
end
end
TimeLine.db = db
TimeLine.saveddata = db
TimeLine.cooldowns_timeline = db.cooldowns_timeline
TimeLine.debuff_timeline = db.debuff_timeline
TimeLine.combat_data = db.combat_data
TimeLine.deaths_data = db.deaths_data
--store which spells are active per boss
db.BossSpellCast = db.BossSpellCast or {}
TimeLine.spellcast_boss = db.BossSpellCast
--store individual spells
db.IndividualSpells = db.IndividualSpells or {}
TimeLine.boss_spells = db.IndividualSpells
--> Register needed events
_G._detalhes:RegisterEvent (TimeLine, "COMBAT_PLAYER_ENTER")
_G._detalhes:RegisterEvent (TimeLine, "COMBAT_PLAYER_LEAVE")
_G._detalhes:RegisterEvent (TimeLine, "DETAILS_DATA_RESET")
_G._detalhes:InstallHook (DETAILS_HOOK_COOLDOWN, TimeLine.OnCooldown)
TimeLineFrame:RegisterEvent ("COMBAT_LOG_EVENT_UNFILTERED")
--> Register slash commands
SLASH_DETAILS_TIMELINE1 = "/timeline"
function SlashCmdList.DETAILS_TIMELINE (msg, editbox)
TimeLine:OpenWindow()
end
TimeLine:RefreshBackgroundColor()
TimeLine:UpdateShowSpellIconState()
--> embed the plugin into the plugin window
if (DetailsPluginContainerWindow) then
DetailsPluginContainerWindow.EmbedPlugin (TimeLine, TimeLine.Frame)
end
end
end
end
end