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 _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 = _detalhes if not _detalhes then return end local DetailsFrameWork = _detalhes.gump local framework = _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) 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\AddOns\Details_TimeLine\textures\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 _combat_object = ... 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:SetTexture(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:SetTexture(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:SetTexture(1, 1, 1, .4) local bottom_bar = DetailsFrameWork:NewImage(TimeLineFrame, nil, CONST_VALID_WIDHT + CONST_PLAYERFIELD_SIZE, 1, "overlay", nil, nil, "$parentRowBottomLine") bottom_bar:SetTexture(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("Button", nil, row) 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:SetTexture(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:SetTexture(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:SetTexture(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("Button", nil, row) 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:SetTexture(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 effectTime = spellInfo and spellInfo.duration or 8 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("Button", "DetailsTimeTimeRow"..index, TimeLineFrame) 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, sourceGUID, sourceName, sourceFlag, targetGUID, targetName, targetFlag, 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 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 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 not _bit_band(who_flags, 0x00000010) == 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 not _bit_band(who_flags, 0x00000010) == 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 function TimeLine:OnEvent(_, event, ...) if event == "COMBAT_LOG_EVENT_UNFILTERED" and _combat_object then local time, token, sourceGUID, sourceName, sourceFlag, targetGUID, targetName, targetFlag, spellID, spellName, spellType, amount, overKill, school, resisted, blocked, absorbed, isCritical = ... if token == "SPELL_AURA_APPLIED" or token == "SPELL_AURA_REFRESH" then TimeLine:AuraOn(time, token, sourceGUID, sourceName, sourceFlag, targetGUID, targetName, targetFlag, spellID, spellName, spellType, amount, overKill, school, resisted, blocked, absorbed, isCritical) elseif token == "SPELL_AURA_REMOVED" then TimeLine:AuraOff(time, token, sourceGUID, sourceName, sourceFlag, targetGUID, targetName, targetFlag, spellID, spellName, spellType, amount, overKill, school, resisted, blocked, absorbed, isCritical) elseif token == "SPELL_CAST_SUCCESS" then TimeLine:EnemySpellCast(time, token, sourceGUID, sourceName, sourceFlag, targetGUID, targetName, targetFlag, spellID, spellName, spellType, amount, overKill, school, resisted, blocked, absorbed, isCritical) end elseif event == "ADDON_LOADED" then local AddonName = ... if AddonName == "Details_TimeLine" then if _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 _detalhes:RegisterEvent(TimeLine, "COMBAT_PLAYER_ENTER") _detalhes:RegisterEvent(TimeLine, "COMBAT_PLAYER_LEAVE") _detalhes:RegisterEvent(TimeLine, "DETAILS_DATA_RESET") _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