Major upgrade for alpha testers
This commit is contained in:
@@ -112,6 +112,7 @@
|
||||
---@field __destroyedBy string
|
||||
---@field amountCasts {[string]: table<string, number>}
|
||||
---@field instance_type instancetype "raid" or "party" or "pvp" or "arena" or "none" or "scenario"
|
||||
---@field run_time number
|
||||
---@field end_time number
|
||||
---@field start_time number
|
||||
---@field combat_counter number
|
||||
@@ -130,10 +131,12 @@
|
||||
---@field is_boss table
|
||||
---@field is_world_trash_combat boolean when true this combat is a regular combat done in the world, not in a dungeon, raid, battleground, arena, ...
|
||||
---@field player_last_events table<string, table[]> record the latest events of each player, latter used to build the death log
|
||||
---@field GetCombatType fun(combat: combat) : number
|
||||
---@field GetCombatUID fun(combat: combat) : uniquecombatid
|
||||
---@field GetTimeData fun(combat: combat, dataName: string) : table
|
||||
---@field GetPhases fun(combat: combat) : table
|
||||
---@field GetCombatTime fun(combat) : number
|
||||
---@field GetRunTime fun(combat) : number
|
||||
---@field GetDeaths fun(combat) : table --get the table which contains the deaths of the combat
|
||||
---@field GetStartTime fun(combat: combat) : number
|
||||
---@field SetStartTime fun(combat: combat, time: number)
|
||||
|
||||
@@ -855,6 +855,11 @@ end
|
||||
debuffTrackedAuraScrollBox:SetPoint("topleft", auraPanel_Auto, "topleft", 16 +(scrollWidth * 2) + xLocation, y)
|
||||
buffTrackedAuraScrollBox:SetPoint("topleft", auraPanel_Auto, "topleft", 24 +(scrollWidth * 3) + xLocation, y)
|
||||
|
||||
buffTrackedAuraScrollBox:GetTitleFontString():SetText(newAuraPanel.LocTexts.BUFFS_TRACKED)
|
||||
debuffTrackedAuraScrollBox:GetTitleFontString():SetText(newAuraPanel.LocTexts.DEBUFFS_TRACKED)
|
||||
buffIgnoredAuraScrollBox:GetTitleFontString():SetText(newAuraPanel.LocTexts.BUFFS_IGNORED)
|
||||
debuffIgnoredAuraScrollBox:GetTitleFontString():SetText(newAuraPanel.LocTexts.DEBUFFS_IGNORED)
|
||||
|
||||
newAuraPanel.buff_ignored = buffIgnoredAuraScrollBox
|
||||
newAuraPanel.debuff_ignored = debuffIgnoredAuraScrollBox
|
||||
newAuraPanel.buff_tracked = buffTrackedAuraScrollBox
|
||||
|
||||
+963
-1031
File diff suppressed because it is too large
Load Diff
@@ -45,6 +45,7 @@
|
||||
|
||||
---@class detailsframework
|
||||
---@field dversion number
|
||||
---@field internalFunctions table
|
||||
---@field OptionsFunctions df_optionsmixin
|
||||
---@field GlobalWidgetControlNames table
|
||||
---@field RoundedCornerPanelMixin df_roundedcornermixin
|
||||
@@ -56,7 +57,7 @@
|
||||
---@field EditorMixin df_editormixin
|
||||
---@field ClassCache {ID:number, Name:string, FileString:string, Texture:string, TexCoord:number[]}[] only available after calling GetClassList()
|
||||
---@field Math df_math
|
||||
---@field FontOutlineFlags {key1:outline, key2:string}[]
|
||||
---@field FontOutlineFlags table<outline, boolean>
|
||||
---@field table df_table_functions
|
||||
---@field AnchorPoints string[]
|
||||
---@field ClassFileNameToIndex table<string, number> engClass -> classIndex
|
||||
@@ -102,7 +103,7 @@
|
||||
---@field CommaValue fun(self:table, value:number) : string convert a number to a string with commas, e.g. 1000000 -> 1,000,000
|
||||
---@field SplitTextInLines fun(self:table, text:string) : string[] split a text into lines
|
||||
---@field UnitGroupRolesAssigned fun(unitId: unit, bUseSupport:boolean, specId: specializationid) : string there's no self here
|
||||
---@field SetAnchor fun(self:table, widget:uiobject, anchorTable:df_anchor, anchorTo:uiobject)
|
||||
---@field SetAnchor fun(self:table, widget:uiobject, anchorTable:df_anchor, anchorTo:uiobject?) only adjust the anchors of a widget, does not save values
|
||||
---@field AddTextureToText fun(text:string, textureInfo:table, bAddSpace:boolean?, bAddAfterText:boolean) : string textureInfo is a table with .texture .width .height .coords{left, right, top, bottom}
|
||||
---@field CreateTextureInfo fun(texture:atlasname|texturepath|textureid, width:number?, height:number?, left:number?, right:number?, top:number?, bottom:number?, imageWidthnumber?, imageHeightnumber?) : table
|
||||
---@field ApplyStandardBackdrop fun(self:table, frame:frame, bUseSolidColor:boolean?, alphaScale:number?)
|
||||
@@ -132,8 +133,10 @@
|
||||
---@field CreateAuraScrollBox fun(self:table, parent:frame, name:string?, data:table?, onRemoveCallback:function?, options:table?) : df_aurascrollbox
|
||||
---@field CreateGridScrollBox fun(self:table, parent:frame, name:string?, refreshFunc:function, data:table?, createColumnFrameFunc:function, options:table?) : df_gridscrollbox
|
||||
---@field CreateCanvasScrollBox fun(self:table, parent:frame, child:frame?, name:string?, options:table?) : df_canvasscrollbox
|
||||
---@field CreateTabContainer fun(self:table, parent:frame, title:string, frameName:string, tabList:df_tabinfotable[], optionsTable:table?, hookList:table?, languageInfo:table?) : df_tabcontainer
|
||||
---@field GetSizeFromPercent fun(self:table, uiObject:uiobject, percent:number) : number get the min size of a uiObject and multiply it by the percent passed
|
||||
---@field BuildMenu fun(self:table, parent:frame, menuOptions:df_menu_table[], xOffset:number?, yOffset:number?, height:number?, useColon:boolean?, textTemplate:table?, dropdownTemplate:table?, switchTemplate:table?, switchIsCheckbox:boolean?, sliderTemplate:table?, buttonTemplate:table?, valueChangeHook:function?)
|
||||
---@field BuildMenuVolatile fun(self:table, parent:frame, menuOptions:df_menu_table[], xOffset:number?, yOffset:number?, height:number?, useColon:boolean?, textTemplate:table?, dropdownTemplate:table?, switchTemplate:table?, switchIsCheckbox:boolean?, sliderTemplate:table?, buttonTemplate:table?, valueChangeHook:function?)
|
||||
---@field
|
||||
---@field
|
||||
|
||||
|
||||
@@ -986,16 +986,13 @@ function DetailsFrameworkDropDownOnHide(self)
|
||||
object:Close()
|
||||
end
|
||||
|
||||
local iconSizeTable = {16, 16}
|
||||
function DF:BuildDropDownFontList(onClick, icon, iconTexcoord, iconSize)
|
||||
local fontTable = {}
|
||||
|
||||
if (type(iconSize) ~= "table") then
|
||||
iconSize = {iconSize or 16, iconSize or 16}
|
||||
end
|
||||
|
||||
local SharedMedia = LibStub:GetLibrary("LibSharedMedia-3.0")
|
||||
for name, fontPath in pairs(SharedMedia:HashTable("font")) do
|
||||
fontTable[#fontTable+1] = {value = name, label = name, onclick = onClick, icon = icon, iconsize = iconSize, texcoord = iconTexcoord, font = fontPath, descfont = "abcdefg ABCDEFG"}
|
||||
fontTable[#fontTable+1] = {value = name, label = name, onclick = onClick, icon = icon, iconsize = iconSizeTable, texcoord = iconTexcoord, font = fontPath, descfont = "abcdefg ABCDEFG"}
|
||||
end
|
||||
|
||||
table.sort(fontTable, function(t1, t2) return t1.label < t2.label end)
|
||||
@@ -1141,11 +1138,12 @@ function DF:CreateOutlineListGenerator(callback)
|
||||
local newGenerator = function()
|
||||
local dropdownOptions = {}
|
||||
|
||||
for i, outlineInfo in ipairs(DF.FontOutlineFlags) do
|
||||
local outlineName, outlineLoc = unpack(outlineInfo)
|
||||
for index, outlineInfo in pairs(DF.FontOutlineFlags) do
|
||||
local outlineValue = outlineInfo[1]
|
||||
local outlineName = outlineInfo[2]
|
||||
table.insert(dropdownOptions, {
|
||||
label = outlineLoc,
|
||||
value = outlineName,
|
||||
label = outlineName,
|
||||
value = outlineValue,
|
||||
onclick = callback
|
||||
})
|
||||
end
|
||||
|
||||
+657
-100
@@ -14,7 +14,7 @@ local _
|
||||
--[=[
|
||||
file description: this file has the code for the object editor
|
||||
the object editor itself is a frame and has a scrollframe as canvas showing another frame where there's the options for the editing object
|
||||
|
||||
|
||||
--]=]
|
||||
|
||||
|
||||
@@ -22,10 +22,10 @@ local _
|
||||
--the mapTable is a table with the attribute name as a key, and the value is the profile key. For example, {["size"] = "text_size"} means profileTable["text_size"] = 10.
|
||||
|
||||
---@class df_editor_attribute
|
||||
---@field name string
|
||||
---@field label string
|
||||
---@field name string?
|
||||
---@field label string?
|
||||
---@field widget string
|
||||
---@field default any
|
||||
---@field default any?
|
||||
---@field minvalue number?
|
||||
---@field maxvalue number?
|
||||
---@field step number?
|
||||
@@ -55,12 +55,15 @@ local attributes = {
|
||||
name = "font",
|
||||
label = "Font",
|
||||
widget = "fontdropdown",
|
||||
setter = function(widget, value) widget:SetFont(value, select(2, widget:GetFont())) end
|
||||
setter = function(widget, value)
|
||||
local font = LibStub:GetLibrary("LibSharedMedia-3.0"):Fetch("font", value)
|
||||
widget:SetFont(font, select(2, widget:GetFont()))
|
||||
end
|
||||
},
|
||||
{
|
||||
name = "color",
|
||||
label = "Color",
|
||||
widget = "colordropdown",
|
||||
widget = "color",
|
||||
setter = function(widget, value) widget:SetTextColor(unpack(value)) end
|
||||
},
|
||||
{
|
||||
@@ -69,6 +72,7 @@ local attributes = {
|
||||
widget = "range",
|
||||
setter = function(widget, value) widget:SetAlpha(value) end
|
||||
},
|
||||
{widget = "blank"},
|
||||
{
|
||||
name = "shadow",
|
||||
label = "Draw Shadow",
|
||||
@@ -78,7 +82,7 @@ local attributes = {
|
||||
{
|
||||
name = "shadowcolor",
|
||||
label = "Shadow Color",
|
||||
widget = "colordropdown",
|
||||
widget = "color",
|
||||
setter = function(widget, value) widget:SetShadowColor(unpack(value)) end
|
||||
},
|
||||
{
|
||||
@@ -103,6 +107,7 @@ local attributes = {
|
||||
widget = "outlinedropdown",
|
||||
setter = function(widget, value) widget:SetFont(widget:GetFont(), select(2, widget:GetFont()), value) end
|
||||
},
|
||||
{widget = "blank"},
|
||||
{
|
||||
name = "anchor",
|
||||
label = "Anchor",
|
||||
@@ -113,16 +118,16 @@ local attributes = {
|
||||
name = "anchoroffsetx",
|
||||
label = "Anchor X Offset",
|
||||
widget = "range",
|
||||
minvalue = -20,
|
||||
maxvalue = 20,
|
||||
minvalue = -100,
|
||||
maxvalue = 100,
|
||||
setter = function(widget, value) detailsFramework:SetAnchor(widget, value, widget:GetParent()) end
|
||||
},
|
||||
{
|
||||
name = "anchoroffsety",
|
||||
label = "Anchor Y Offset",
|
||||
widget = "range",
|
||||
minvalue = -20,
|
||||
maxvalue = 20,
|
||||
minvalue = -100,
|
||||
maxvalue = 100,
|
||||
setter = function(widget, value) detailsFramework:SetAnchor(widget, value, widget:GetParent()) end
|
||||
},
|
||||
{
|
||||
@@ -134,56 +139,59 @@ local attributes = {
|
||||
maxvalue = math.pi*2,
|
||||
setter = function(widget, value) widget:SetRotation(value) end
|
||||
},
|
||||
{
|
||||
name = "scale",
|
||||
label = "Scale",
|
||||
widget = "range",
|
||||
usedecimals = true,
|
||||
minvalue = 0.65,
|
||||
maxvalue = 2.5,
|
||||
setter = function(widget, value) widget:SetScale(value) end
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
local profileTable = {
|
||||
spellname_text_size = 10,
|
||||
spellname_text_font = "Arial Narrow",
|
||||
spellname_text_color = {1, 1, 1, 1},
|
||||
spellname_text_outline = "NONE",
|
||||
spellname_text_shadow_color = {0, 0, 0, 1},
|
||||
spellname_text_shadow_color_offset = {1, -1},
|
||||
spellname_text_anchor = {side = 9, x = 0, y = 0},
|
||||
}
|
||||
|
||||
--create a map table for the profile table
|
||||
local mapTable = {
|
||||
text = "text test",
|
||||
size = "spellname_text_size",
|
||||
font = "spellname_text_font",
|
||||
color = "spellname_text_color",
|
||||
outline = "spellname_text_outline",
|
||||
shadowcolor = "spellname_text_shadow_color",
|
||||
shadowoffsetx = "spellname_text_shadow_color_offset[1]",
|
||||
shadowoffsety = "spellname_text_shadow_color_offset[2]",
|
||||
anchor = "spellname_text_anchor.side",
|
||||
anchoroffsetx = "spellname_text_anchor.x",
|
||||
anchoroffsety = "spellname_text_anchor.y",
|
||||
}
|
||||
|
||||
local table_path = {
|
||||
shadowWidth = "text_settings[1].width",
|
||||
shadowHeight = "text_settings[1].height",
|
||||
shadowEnabled = "text_settings.settings.enabled",
|
||||
text = "text_settings.settings.text.current_text",
|
||||
}
|
||||
|
||||
local text_settings = {
|
||||
shadowWidth = {{width = 100}},
|
||||
shadowHeight = {{height = 100}},
|
||||
shadowEnabled = {settings = {enabled = true}},
|
||||
text = {settings = {text = {current_text = "hellow world"}}},
|
||||
}
|
||||
|
||||
---@class df_editormixin : table
|
||||
---@field GetAllRegisteredObjects fun(self:df_editor):df_editor_objectinfo[]
|
||||
---@field GetEditingObject fun(self:df_editor):uiobject
|
||||
---@field GetEditingOptions fun(self:df_editor):df_editobjectoptions
|
||||
---@field GetExtraOptions fun(self:df_editor):table
|
||||
---@field GetEditingProfile fun(self:df_editor):table, table
|
||||
---@field GetOnEditCallback fun(self:df_editor):function
|
||||
---@field GetOptionsFrame fun(self:df_editor):frame
|
||||
---@field GetCanvasScrollBox fun(self:df_editor):df_canvasscrollbox
|
||||
---@field EditObject fun(self:df_editor, object:uiobject, profileTable:table, profileKeyMap:table, callback:function)
|
||||
---@field GetObjectSelector fun(self:df_editor):df_scrollbox
|
||||
---@field EditObject fun(self:df_editor, object:uiobject, profileTable:table, profileKeyMap:table, extraOptions:table?, callback:function?, options:df_editobjectoptions?)
|
||||
---@field PrepareObjectForEditing fun(self:df_editor)
|
||||
---@field CreateMoverGuideLines fun(self:df_editor)
|
||||
---@field GetOverTheTopFrame fun(self:df_editor):frame
|
||||
---@field GetMoverFrame fun(self:df_editor):frame
|
||||
---@field StartObjectMovement fun(self:df_editor, anchorSettings:df_anchor)
|
||||
---@field StopObjectMovement fun(self:df_editor)
|
||||
|
||||
---@class df_editobjectoptions : table
|
||||
---@field use_colon boolean if true a colon is shown after the option name
|
||||
---@field can_move boolean if true the object can be moved
|
||||
---@field use_guide_lines boolean if true guide lines are shown when the object is being moved
|
||||
|
||||
---@type df_editobjectoptions
|
||||
local editObjectDefaultOptions = {
|
||||
use_colon = true,
|
||||
can_move = true,
|
||||
use_guide_lines = true,
|
||||
}
|
||||
|
||||
local getParentTable = function(profileTable, profileKey)
|
||||
local parentPath
|
||||
if (profileKey:match("%]$")) then
|
||||
parentPath = profileKey:gsub("%s*%[.*%]%s*$", "")
|
||||
else
|
||||
parentPath = profileKey:gsub("%.[^.]*$", "")
|
||||
end
|
||||
|
||||
local parentTable = detailsFramework.table.getfrompath(profileTable, parentPath)
|
||||
return parentTable
|
||||
end
|
||||
|
||||
detailsFramework.EditorMixin = {
|
||||
---@param self df_editor
|
||||
@@ -191,6 +199,18 @@ detailsFramework.EditorMixin = {
|
||||
return self.editingObject
|
||||
end,
|
||||
|
||||
---@param self df_editor
|
||||
---@return df_editobjectoptions
|
||||
GetEditingOptions = function(self)
|
||||
return self.editingOptions
|
||||
end,
|
||||
|
||||
---@param self df_editor
|
||||
---@return table
|
||||
GetExtraOptions = function(self)
|
||||
return self.editingExtraOptions
|
||||
end,
|
||||
|
||||
---@param self df_editor
|
||||
---@return table, table
|
||||
GetEditingProfile = function(self)
|
||||
@@ -207,28 +227,117 @@ detailsFramework.EditorMixin = {
|
||||
return self.optionsFrame
|
||||
end,
|
||||
|
||||
GetOverTheTopFrame = function(self)
|
||||
return self.overTheTopFrame
|
||||
end,
|
||||
|
||||
GetMoverFrame = function(self)
|
||||
return self.moverFrame
|
||||
end,
|
||||
|
||||
GetCanvasScrollBox = function(self)
|
||||
return self.canvasScrollBox
|
||||
end,
|
||||
|
||||
---@param self df_editor
|
||||
---@param object uiobject
|
||||
---@param profileTable table
|
||||
---@param profileKeyMap table
|
||||
---@param callback function calls when an attribute is changed with the payload: editingObject, optionName, newValue, profileTable, profileKey
|
||||
EditObject = function(self, object, profileTable, profileKeyMap, callback)
|
||||
assert(type(object) == "table", "EditObject(object) expects an UIObject on first parameter.")
|
||||
assert(type(profileTable) == "table", "EditObject(object) expects a table on second parameter.")
|
||||
assert(object.GetObjectType, "EditObject(object) expects an UIObject on first parameter.")
|
||||
GetObjectSelector = function(self)
|
||||
return self.objectSelector
|
||||
end,
|
||||
|
||||
EditObjectById = function(self, id)
|
||||
---@type df_editor_objectinfo
|
||||
local objectRegistered = self:GetObjectById(id)
|
||||
assert(type(objectRegistered) == "table", "EditObjectById() object not found.")
|
||||
self:EditObject(objectRegistered)
|
||||
end,
|
||||
|
||||
EditObjectByIndex = function(self, index)
|
||||
---@type df_editor_objectinfo
|
||||
local objectRegistered = self:GetObjectByIndex(index)
|
||||
assert(type(objectRegistered) == "table", "EditObjectById() object not found.")
|
||||
self:EditObject(objectRegistered)
|
||||
end,
|
||||
|
||||
---@param self df_editor
|
||||
---@param registeredObject df_editor_objectinfo
|
||||
EditObject = function(self, registeredObject)
|
||||
--clear previous values
|
||||
self.editingObject = nil
|
||||
self.editingProfileMap = nil
|
||||
self.editingProfileTable = nil
|
||||
self.editingOptions = nil
|
||||
self.editingExtraOptions = nil
|
||||
self.onEditCallback = nil
|
||||
|
||||
local object = registeredObject.object
|
||||
local profileTable = registeredObject.profiletable
|
||||
local profileKeyMap = registeredObject.profilekeymap
|
||||
local extraOptions = registeredObject.extraoptions
|
||||
local callback = registeredObject.callback
|
||||
local options = registeredObject.options
|
||||
|
||||
--as there's no other place which this members are set, there is no need to create setter functions
|
||||
self.editingObject = object
|
||||
self.editingProfileMap = profileKeyMap
|
||||
self.editingProfileTable = profileTable
|
||||
self.onEditCallback = callback
|
||||
self.editingOptions = options
|
||||
self.editingExtraOptions = extraOptions
|
||||
|
||||
if (type(callback) == "function") then
|
||||
self.onEditCallback = callback
|
||||
end
|
||||
|
||||
self:PrepareObjectForEditing()
|
||||
end,
|
||||
|
||||
---@param self df_editor
|
||||
CreateMoverGuideLines = function(self)
|
||||
local overTheTopFrame = self:GetOverTheTopFrame()
|
||||
local moverFrame = self:GetMoverFrame()
|
||||
|
||||
self.moverGuideLines = {
|
||||
left = overTheTopFrame:CreateTexture(nil, "overlay"),
|
||||
right = overTheTopFrame:CreateTexture(nil, "overlay"),
|
||||
top = overTheTopFrame:CreateTexture(nil, "overlay"),
|
||||
bottom = overTheTopFrame:CreateTexture(nil, "overlay"),
|
||||
}
|
||||
|
||||
for side, texture in pairs(self.moverGuideLines) do
|
||||
texture:SetColorTexture(.8, .8, .8, 0.1)
|
||||
texture:SetSize(1, 1)
|
||||
texture:SetDrawLayer("overlay", 7)
|
||||
texture:Hide()
|
||||
|
||||
if (side == "left" or side == "right") then
|
||||
texture:SetHeight(1)
|
||||
texture:SetWidth(GetScreenWidth())
|
||||
else
|
||||
texture:SetWidth(1)
|
||||
texture:SetHeight(GetScreenHeight())
|
||||
end
|
||||
end
|
||||
end,
|
||||
|
||||
UpdateGuideLinesAnchors = function(self)
|
||||
local object = self:GetEditingObject()
|
||||
|
||||
for side, texture in pairs(self.moverGuideLines) do
|
||||
texture:ClearAllPoints()
|
||||
if (side == "left" or side == "right") then
|
||||
if (side == "left") then
|
||||
texture:SetPoint("right", object, "left", -2, 0)
|
||||
else
|
||||
texture:SetPoint("left", object, "right", 2, 0)
|
||||
end
|
||||
else
|
||||
if (side == "top") then
|
||||
texture:SetPoint("bottom", object, "top", 0, 2)
|
||||
else
|
||||
texture:SetPoint("top", object, "bottom", 0, -2)
|
||||
end
|
||||
end
|
||||
end
|
||||
end,
|
||||
|
||||
PrepareObjectForEditing = function(self)
|
||||
--get the object and its profile table with the current values
|
||||
local object = self:GetEditingObject()
|
||||
@@ -243,56 +352,135 @@ detailsFramework.EditorMixin = {
|
||||
local objectType = object:GetObjectType()
|
||||
local attributeList
|
||||
|
||||
--get options and extra options
|
||||
local editingOptions = self:GetEditingOptions()
|
||||
local extraOptions = self:GetExtraOptions()
|
||||
|
||||
--get the attribute list for the object type
|
||||
if (objectType == "FontString") then
|
||||
---@cast object fontstring
|
||||
attributeList = attributes[objectType]
|
||||
end
|
||||
|
||||
--if there's extra options, add the attributeList to a new table and right after the extra options
|
||||
if (extraOptions and #extraOptions > 0) then
|
||||
local attributeListWithExtraOptions = {}
|
||||
|
||||
for i = 1, #attributeList do
|
||||
attributeListWithExtraOptions[#attributeListWithExtraOptions+1] = attributeList[i]
|
||||
end
|
||||
|
||||
attributeListWithExtraOptions[#attributeListWithExtraOptions+1] = {widget = "blank", default = true}
|
||||
|
||||
for i = 1, #extraOptions do
|
||||
attributeListWithExtraOptions[#attributeListWithExtraOptions+1] = extraOptions[i]
|
||||
end
|
||||
|
||||
attributeList = attributeListWithExtraOptions
|
||||
end
|
||||
|
||||
local anchorSettings
|
||||
|
||||
--table to use on DF:BuildMenu()
|
||||
local menuOptions = {}
|
||||
for i = 1, #attributeList do
|
||||
local option = attributeList[i]
|
||||
|
||||
--get the key to be used on profile table
|
||||
local profileKey = profileMap[option.name]
|
||||
local value
|
||||
|
||||
--if the key contains a dot or a bracket, it means it's a table path, example: "text_settings[1].width"
|
||||
if (profileKey and (profileKey:match("%.") or profileKey:match("%["))) then
|
||||
value = detailsFramework.table.getfrompath(profileTable, profileKey)
|
||||
if (option.widget == "blank") then
|
||||
menuOptions[#menuOptions+1] = {type = "blank"}
|
||||
else
|
||||
value = profileTable[profileKey]
|
||||
end
|
||||
--get the key to be used on profile table
|
||||
local profileKey = profileMap[option.name]
|
||||
local value
|
||||
|
||||
--if no value is found, attempt to get a default
|
||||
value = value or option.default
|
||||
--if the key contains a dot or a bracket, it means it's a table path, example: "text_settings[1].width"
|
||||
if (profileKey and (profileKey:match("%.") or profileKey:match("%["))) then
|
||||
value = detailsFramework.table.getfrompath(profileTable, profileKey)
|
||||
else
|
||||
value = profileTable[profileKey]
|
||||
end
|
||||
|
||||
if (value) then
|
||||
menuOptions[#menuOptions+1] = {
|
||||
type = option.widget,
|
||||
name = option.label,
|
||||
get = function() return value end,
|
||||
set = function(widget, fixedValue, newValue)
|
||||
if (profileKey and (profileKey:match("%.") or profileKey:match("%["))) then
|
||||
detailsFramework.table.setfrompath(profileTable, profileKey, value)
|
||||
else
|
||||
profileTable[profileKey] = value
|
||||
end
|
||||
--if no value is found, attempt to get a default
|
||||
if (type(value) == "nil") then
|
||||
value = option.default
|
||||
end
|
||||
|
||||
if (self:GetOnEditCallback()) then
|
||||
self:GetOnEditCallback()(object, option.name, newValue, profileTable, profileKey)
|
||||
end
|
||||
local bHasValue = type(value) ~= "nil"
|
||||
|
||||
if (option.name == "anchor" or option.name == "anchoroffsetx" or option.name == "anchoroffsety") then
|
||||
local anchorTable = detailsFramework.table.getfrompath(profileTable, profileKey:gsub("%.[^.]*$", ""))
|
||||
option.setter(object, anchorTable)
|
||||
else
|
||||
option.setter(object, newValue)
|
||||
end
|
||||
end,
|
||||
min = option.minvalue,
|
||||
max = option.maxvalue,
|
||||
step = option.step,
|
||||
usedecimals = option.usedecimals,
|
||||
}
|
||||
local minValue = option.minvalue
|
||||
local maxValue = option.maxvalue
|
||||
|
||||
if (option.name == "anchoroffsetx") then
|
||||
minValue = -object:GetParent():GetWidth()/2
|
||||
maxValue = object:GetParent():GetWidth()/2
|
||||
elseif (option.name == "anchoroffsety") then
|
||||
minValue = -object:GetParent():GetHeight()/2
|
||||
maxValue = object:GetParent():GetHeight()/2
|
||||
end
|
||||
|
||||
if (option.name == "classcolor") then print("", value) end
|
||||
|
||||
if (bHasValue) then
|
||||
if (option.name == "classcolor") then print("HERE", value) end
|
||||
|
||||
local parentTable = getParentTable(profileTable, profileKey)
|
||||
|
||||
if (option.name == "anchor" or option.name == "anchoroffsetx" or option.name == "anchoroffsety") then
|
||||
anchorSettings = parentTable
|
||||
end
|
||||
|
||||
menuOptions[#menuOptions+1] = {
|
||||
type = option.widget,
|
||||
name = option.label,
|
||||
get = function() return value end,
|
||||
set = function(widget, fixedValue, newValue, ...)
|
||||
--color is a table with 4 indexes for each color plus alpha
|
||||
if (option.widget == "color") then
|
||||
--calor callback sends the red color in the fixedParameter slot
|
||||
local r, g, b, alpha = fixedValue, newValue, ...
|
||||
--need to use the same table from the profile table
|
||||
parentTable[1] = r
|
||||
parentTable[2] = g
|
||||
parentTable[3] = b
|
||||
parentTable[4] = alpha
|
||||
|
||||
newValue = parentTable
|
||||
else
|
||||
detailsFramework.table.setfrompath(profileTable, profileKey, newValue)
|
||||
end
|
||||
|
||||
if (self:GetOnEditCallback()) then
|
||||
self:GetOnEditCallback()(object, option.name, newValue, profileTable, profileKey)
|
||||
end
|
||||
|
||||
--update the widget visual
|
||||
--anchoring uses SetAnchor() which require the anchorTable to be passed
|
||||
if (option.name == "anchor" or option.name == "anchoroffsetx" or option.name == "anchoroffsety") then
|
||||
anchorSettings = parentTable
|
||||
|
||||
if (option.name == "anchor") then
|
||||
anchorSettings.x = 0
|
||||
anchorSettings.y = 0
|
||||
end
|
||||
|
||||
self:StopObjectMovement()
|
||||
|
||||
option.setter(object, parentTable)
|
||||
|
||||
if (editingOptions.can_move) then
|
||||
self:StartObjectMovement(anchorSettings)
|
||||
end
|
||||
else
|
||||
option.setter(object, newValue)
|
||||
end
|
||||
end,
|
||||
min = minValue,
|
||||
max = maxValue,
|
||||
step = option.step,
|
||||
usedecimals = option.usedecimals,
|
||||
id = option.name,
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -303,7 +491,9 @@ detailsFramework.EditorMixin = {
|
||||
|
||||
local optionsFrame = self:GetOptionsFrame()
|
||||
local canvasScrollBox = self:GetCanvasScrollBox()
|
||||
local bUseColon = true
|
||||
|
||||
local bUseColon = editingOptions.use_colon
|
||||
|
||||
local bSwitchIsCheckbox = true
|
||||
local maxHeight = 5000
|
||||
|
||||
@@ -318,25 +508,361 @@ detailsFramework.EditorMixin = {
|
||||
local options_slider_template = detailsFramework:GetTemplate("slider", "OPTIONS_SLIDER_TEMPLATE")
|
||||
local options_button_template = detailsFramework:GetTemplate("button", "OPTIONS_BUTTON_TEMPLATE")
|
||||
|
||||
detailsFramework:BuildMenu(optionsFrame, menuOptions, 0, -2, maxHeight, bUseColon, options_text_template, options_dropdown_template, options_switch_template, bSwitchIsCheckbox, options_slider_template, options_button_template)
|
||||
detailsFramework:BuildMenuVolatile(optionsFrame, menuOptions, 0, -2, maxHeight, bUseColon, options_text_template, options_dropdown_template, options_switch_template, bSwitchIsCheckbox, options_slider_template, options_button_template)
|
||||
|
||||
if (editingOptions.can_move) then
|
||||
self:StartObjectMovement(anchorSettings)
|
||||
end
|
||||
end,
|
||||
|
||||
---@param self df_editor
|
||||
---@param anchorSettings df_anchor
|
||||
StartObjectMovement = function(self, anchorSettings)
|
||||
local object = self:GetEditingObject()
|
||||
local moverFrame = self:GetMoverFrame()
|
||||
|
||||
--self:UpdateGuideLinesAnchors()
|
||||
|
||||
--update guidelines
|
||||
if (self:GetEditingOptions().use_guide_lines) then
|
||||
--show all four guidelines
|
||||
for side, texture in pairs(self.moverGuideLines) do
|
||||
texture:Show()
|
||||
end
|
||||
end
|
||||
|
||||
local optionsFrame = self:GetOptionsFrame()
|
||||
|
||||
moverFrame:EnableMouse(true)
|
||||
moverFrame:SetMovable(true)
|
||||
moverFrame:ClearAllPoints()
|
||||
moverFrame:Show()
|
||||
|
||||
--update the mover frame size to match the object size
|
||||
if (object:GetObjectType() == "FontString") then
|
||||
---@cast object fontstring
|
||||
local width = object:GetStringWidth()
|
||||
local height = object:GetStringHeight()
|
||||
moverFrame:SetSize(width, height)
|
||||
else
|
||||
local width, height = object:GetSize()
|
||||
moverFrame:SetSize(width, height)
|
||||
end
|
||||
|
||||
for i = 1, object:GetNumPoints() do
|
||||
local point, relativeTo, relativePoint, x, y = object:GetPoint(i)
|
||||
moverFrame:SetPoint(point, relativeTo, relativePoint, x, y)
|
||||
end
|
||||
|
||||
local currentPosX, currentPosY
|
||||
|
||||
moverFrame:SetScript("OnMouseDown", function()
|
||||
object:ClearAllPoints()
|
||||
object:SetPoint("topleft", moverFrame, "topleft", 0, 0)
|
||||
currentPosX, currentPosY = moverFrame:GetCenter()
|
||||
moverFrame:SetBackdropBorderColor(1, 1, 0, 0)
|
||||
|
||||
for i = 1, object:GetNumPoints() do
|
||||
local point, relativeTo, relativePoint, x, y = object:GetPoint(i)
|
||||
moverFrame:SetPoint(point, relativeTo, relativePoint, x, y)
|
||||
end
|
||||
|
||||
moverFrame:StartMoving()
|
||||
moverFrame.bIsMoving = true
|
||||
end)
|
||||
|
||||
moverFrame:SetScript("OnMouseUp", function()
|
||||
moverFrame:StopMovingOrSizing()
|
||||
moverFrame.bIsMoving = false
|
||||
|
||||
object:ClearAllPoints()
|
||||
detailsFramework:SetAnchor(object, anchorSettings, object:GetParent())
|
||||
|
||||
moverFrame:ClearAllPoints()
|
||||
for i = 1, object:GetNumPoints() do
|
||||
local point, relativeTo, relativePoint, x, y = object:GetPoint(i)
|
||||
moverFrame:SetPoint(point, relativeTo, relativePoint, x, y)
|
||||
end
|
||||
end)
|
||||
|
||||
--detailsFramework:SetAnchor(moverFrame, anchorSettings)
|
||||
--detailsFramework:SetAnchor(object, anchorSettings, moverFrame)
|
||||
|
||||
moverFrame:SetScript("OnUpdate", function()
|
||||
--if the object isn't moving, make the mover follow the object position
|
||||
if (moverFrame.bIsMoving) then
|
||||
object:ClearAllPoints()
|
||||
object:SetPoint("topleft", moverFrame, "topleft", 0, 0)
|
||||
|
||||
--if the object is moving, check if the moverFrame has moved
|
||||
local newPosX, newPosY = moverFrame:GetCenter()
|
||||
|
||||
--did the frame moved?
|
||||
if (newPosX ~= currentPosX) then
|
||||
local xOffset = newPosX - currentPosX
|
||||
anchorSettings.x = anchorSettings.x + xOffset
|
||||
local anchorXSlider = optionsFrame:GetWidgetById("anchoroffsetx")
|
||||
anchorXSlider:SetValueNoCallback(anchorSettings.x)
|
||||
end
|
||||
|
||||
if (newPosY ~= currentPosY) then
|
||||
local yOffset = newPosY - currentPosY
|
||||
anchorSettings.y = anchorSettings.y + yOffset
|
||||
local anchorYSlider = optionsFrame:GetWidgetById("anchoroffsety")
|
||||
anchorYSlider:SetValueNoCallback(anchorSettings.y)
|
||||
end
|
||||
|
||||
currentPosX, currentPosY = newPosX, newPosY
|
||||
end
|
||||
|
||||
--[=[
|
||||
--update the mover frame size to match the object size
|
||||
if (object:GetObjectType() == "FontString") then
|
||||
---@cast object fontstring
|
||||
local width = object:GetStringWidth()
|
||||
local height = object:GetStringHeight()
|
||||
moverFrame:SetSize(width, height)
|
||||
else
|
||||
local width, height = object:GetSize()
|
||||
moverFrame:SetSize(width, height)
|
||||
end
|
||||
--]=]
|
||||
end)
|
||||
end,
|
||||
|
||||
---@param self df_editor
|
||||
StopObjectMovement = function(self)
|
||||
local moverFrame = self:GetMoverFrame()
|
||||
|
||||
moverFrame:EnableMouse(false)
|
||||
moverFrame:SetScript("OnUpdate", nil)
|
||||
|
||||
--hide all four guidelines
|
||||
for side, texture in pairs(self.moverGuideLines) do
|
||||
texture:Hide()
|
||||
end
|
||||
|
||||
moverFrame:Hide()
|
||||
end,
|
||||
|
||||
RegisterObject = function(self, object, localizedLabel, id, profileTable, profileKeyMap, extraOptions, callback, options)
|
||||
assert(type(object) == "table", "RegisterObjectToEdit() expects an UIObject on #1 parameter.")
|
||||
assert(object.GetObjectType, "RegisterObjectToEdit() expects an UIObject on #1 parameter.")
|
||||
assert(type(profileTable) == "table", "RegisterObjectToEdit() expects a table on #4 parameter.")
|
||||
assert(type(id) ~= "nil" and type(id) ~= "boolean", "RegisterObjectToEdit() expects an ID on parameter #3.")
|
||||
assert(type(callback) == "function" or callback == nil, "RegisterObjectToEdit() expects a function or nil as the #7 parameter.")
|
||||
|
||||
local registeredObjects = self:GetAllRegisteredObjects()
|
||||
|
||||
--is object already registered?
|
||||
for i = 1, #registeredObjects do
|
||||
local objectRegistered = registeredObjects[i]
|
||||
if (objectRegistered.object == object) then
|
||||
error("RegisterObjectToEdit() object already registered.")
|
||||
end
|
||||
end
|
||||
|
||||
--deploy the options table
|
||||
options = type(options) == "table" and options or {}
|
||||
detailsFramework.table.deploy(options, editObjectDefaultOptions)
|
||||
|
||||
localizedLabel = type(localizedLabel) == "string" and localizedLabel or "invalid label"
|
||||
|
||||
---@type df_editor_objectinfo
|
||||
local objectRegistered = {
|
||||
object = object,
|
||||
label = localizedLabel,
|
||||
id = id,
|
||||
profiletable = profileTable,
|
||||
profilekeymap = profileKeyMap,
|
||||
extraoptions = extraOptions or {},
|
||||
callback = callback,
|
||||
options = options,
|
||||
}
|
||||
|
||||
registeredObjects[#registeredObjects+1] = objectRegistered
|
||||
self.registeredObjectsByID[id] = objectRegistered
|
||||
|
||||
local objectSelector = self:GetObjectSelector()
|
||||
objectSelector:RefreshMe()
|
||||
|
||||
--what to do after an object is registered?
|
||||
return objectRegistered
|
||||
end,
|
||||
|
||||
UnregisterObject = function(self, object)
|
||||
local registeredObjects = self:GetAllRegisteredObjects()
|
||||
|
||||
for i = 1, #registeredObjects do
|
||||
local objectRegistered = registeredObjects[i]
|
||||
if (objectRegistered.object == object) then
|
||||
self.registeredObjectsByID[objectRegistered.id] = nil
|
||||
table.remove(registeredObjects, i)
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
local objectSelector = self:GetObjectSelector()
|
||||
objectSelector:RefreshMe()
|
||||
|
||||
--stop editing the object
|
||||
end,
|
||||
|
||||
---@param self df_editor
|
||||
---@return df_editor_objectinfo[]
|
||||
GetAllRegisteredObjects = function(self)
|
||||
return self.registeredObjects
|
||||
end,
|
||||
|
||||
---@param self df_editor
|
||||
---@return df_editor_objectinfo?
|
||||
GetObjectByRef = function(self, object)
|
||||
local registeredObjects = self:GetAllRegisteredObjects()
|
||||
for i = 1, #registeredObjects do
|
||||
local objectRegistered = registeredObjects[i]
|
||||
if (objectRegistered.object == object) then
|
||||
return objectRegistered
|
||||
end
|
||||
end
|
||||
end,
|
||||
|
||||
GetObjectByIndex = function(self, index)
|
||||
local registeredObjects = self:GetAllRegisteredObjects()
|
||||
return registeredObjects[index]
|
||||
end,
|
||||
|
||||
GetObjectById = function(self, id)
|
||||
return self.registeredObjectsByID[id]
|
||||
end,
|
||||
|
||||
CreateObjectSelectionList = function(self, scroll_width, scroll_height, scroll_lines, scroll_line_height)
|
||||
local editorFrame = self
|
||||
|
||||
local refreshFunc = function(self, data, offset, totalLines) --~refresh
|
||||
for i = 1, totalLines do
|
||||
local index = i + offset
|
||||
---@type df_editor_objectinfo
|
||||
local objectRegistered = data[index]
|
||||
|
||||
if (objectRegistered) then
|
||||
local line = self:GetLine(i)
|
||||
line.index = index
|
||||
if (objectRegistered.object:GetObjectType() == "Texture") then
|
||||
line.Icon:SetTexture([[Interface\AnimCreate\AnimCreateIcons]])
|
||||
line.Icon:SetTexCoord(1/4, 2/4, 1/4, 2/4)
|
||||
|
||||
elseif (objectRegistered.object:GetObjectType() == "Texture") then
|
||||
line.Icon:SetTexture([[Interface\AnimCreate\AnimCreateIcons]])
|
||||
line.Icon:SetTexCoord(2/4, 3/4, 0, 1/4)
|
||||
end
|
||||
|
||||
line.Label:SetText(objectRegistered.label)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local createLineFunc = function(self, index) -- ~createline --~line
|
||||
local line = CreateFrame("button", "$parentLine" .. index, self, "BackdropTemplate")
|
||||
line:SetPoint("topleft", self, "topleft", 1, -((index-1)*(scroll_line_height+1)) - 1)
|
||||
line:SetSize(scroll_width - 2, scroll_line_height)
|
||||
|
||||
line:SetBackdrop({bgFile = [[Interface\Tooltips\UI-Tooltip-Background]], tileSize = 64, tile = true})
|
||||
if (index % 2 == 0) then
|
||||
line:SetBackdropColor(.1, .1, .1, .1)
|
||||
else
|
||||
line:SetBackdropColor(.1, .1, .1, .4)
|
||||
end
|
||||
|
||||
detailsFramework:Mixin(line, detailsFramework.HeaderFunctions)
|
||||
|
||||
--line:SetScript("OnEnter", lineOnEnter)
|
||||
--line:SetScript("OnLeave", lineOnLeave)
|
||||
|
||||
line:SetScript("OnClick", function(self)
|
||||
local objectRegistered = editorFrame:GetObjectByIndex(self.index)
|
||||
editorFrame:EditObject(objectRegistered)
|
||||
end)
|
||||
|
||||
--icon
|
||||
local objectIcon = line:CreateTexture("$parentIcon", "overlay")
|
||||
objectIcon:SetSize(scroll_line_height - 2, scroll_line_height - 2)
|
||||
|
||||
--object label
|
||||
local objectLabel = line:CreateFontString("$parentLabel", "overlay", "GameFontNormal")
|
||||
|
||||
objectIcon:SetPoint("left", line, "left", 2, 0)
|
||||
objectLabel:SetPoint("left", objectIcon, "right", 2, 0)
|
||||
|
||||
line.Icon = objectIcon
|
||||
line.Label = objectLabel
|
||||
|
||||
return line
|
||||
end
|
||||
|
||||
local selectObjectScrollBox = detailsFramework:CreateScrollBox(self:GetParent(), "$parentSelectObjectScrollBox", refreshFunc, editorFrame:GetAllRegisteredObjects(), scroll_width, scroll_height, scroll_lines, scroll_line_height)
|
||||
detailsFramework:ReskinSlider(selectObjectScrollBox)
|
||||
|
||||
function selectObjectScrollBox:RefreshMe()
|
||||
selectObjectScrollBox:SetData(editorFrame:GetAllRegisteredObjects())
|
||||
selectObjectScrollBox:Refresh()
|
||||
end
|
||||
|
||||
--create lines
|
||||
for i = 1, scroll_lines do
|
||||
selectObjectScrollBox:CreateLine(createLineFunc)
|
||||
end
|
||||
|
||||
return selectObjectScrollBox
|
||||
end,
|
||||
}
|
||||
|
||||
---@class df_editor_defaultoptions : table
|
||||
---@field width number
|
||||
---@field height number
|
||||
---@field create_object_list boolean
|
||||
---@field object_list_width number
|
||||
---@field object_list_height number
|
||||
---@field object_list_lines number
|
||||
---@field object_list_line_height number
|
||||
|
||||
---@class df_editor_defaultoptions
|
||||
local editorDefaultOptions = {
|
||||
width = 400,
|
||||
height = 600,
|
||||
create_object_list = true,
|
||||
object_list_width = 200,
|
||||
object_list_height = 420,
|
||||
object_list_lines = 20,
|
||||
object_list_line_height = 20,
|
||||
}
|
||||
|
||||
---@class df_editor : frame, df_optionsmixin, df_editormixin
|
||||
---@field options table
|
||||
---@field registeredObjects df_editor_objectinfo[]
|
||||
---@field registeredObjectsByID table<any, df_editor_objectinfo>
|
||||
---@field editingObject uiobject
|
||||
---@field editingProfileTable table
|
||||
---@field editingProfileMap table
|
||||
---@field editingOptions df_editobjectoptions
|
||||
---@field editingExtraOptions table
|
||||
---@field moverGuideLines table<string, texture>
|
||||
---@field onEditCallback function
|
||||
---@field optionsFrame frame
|
||||
---@field overTheTopFrame frame
|
||||
---@field objectSelector df_scrollbox
|
||||
---@field moverFrame frame
|
||||
---@field canvasScrollBox df_canvasscrollbox
|
||||
|
||||
---@class df_editor_objectinfo : table
|
||||
---@field object uiobject
|
||||
---@field label string
|
||||
---@field id any
|
||||
---@field profiletable table
|
||||
---@field profilekeymap table
|
||||
---@field extraoptions table
|
||||
---@field callback function
|
||||
---@field options df_editobjectoptions
|
||||
|
||||
function detailsFramework:CreateEditor(parent, name, options)
|
||||
name = name or ("DetailsFrameworkEditor" .. math.random(100000, 10000000))
|
||||
local editorFrame = CreateFrame("frame", name, parent, "BackdropTemplate")
|
||||
@@ -344,10 +870,25 @@ function detailsFramework:CreateEditor(parent, name, options)
|
||||
detailsFramework:Mixin(editorFrame, detailsFramework.EditorMixin)
|
||||
detailsFramework:Mixin(editorFrame, detailsFramework.OptionsFunctions)
|
||||
|
||||
editorFrame.registeredObjects = {}
|
||||
editorFrame.registeredObjectsByID = {}
|
||||
|
||||
editorFrame:BuildOptionsTable(editorDefaultOptions, options)
|
||||
|
||||
editorFrame:SetSize(editorFrame.options.width, editorFrame.options.height)
|
||||
|
||||
if (editorFrame.options.create_object_list) then
|
||||
local scrollWidth = editorFrame.options.object_list_width
|
||||
local scrollHeight = editorFrame.options.object_list_height
|
||||
local scrollLinesAmount = editorFrame.options.object_list_lines
|
||||
local scrollLineHeight = editorFrame.options.object_list_line_height
|
||||
|
||||
local objectSelector = editorFrame:CreateObjectSelectionList(scrollWidth, scrollHeight, scrollLinesAmount, scrollLineHeight)
|
||||
objectSelector:SetPoint("topleft", editorFrame, "topright", 2, 0)
|
||||
editorFrame.objectSelector = objectSelector
|
||||
objectSelector:RefreshMe()
|
||||
end
|
||||
|
||||
--options frame is the frame that holds the options for the editing object, it is used as the parent frame for BuildMenuVolatile()
|
||||
local optionsFrame = CreateFrame("frame", name .. "OptionsFrame", editorFrame, "BackdropTemplate")
|
||||
optionsFrame:SetSize(editorFrame.options.width, 5000)
|
||||
@@ -355,6 +896,22 @@ function detailsFramework:CreateEditor(parent, name, options)
|
||||
local canvasFrame = detailsFramework:CreateCanvasScrollBox(editorFrame, optionsFrame, name .. "CanvasScrollBox")
|
||||
canvasFrame:SetAllPoints()
|
||||
|
||||
--over the top frame is a frame that is always on top of everything else
|
||||
local OTTFrame = CreateFrame("frame", "$parentOTTFrame", UIParent)
|
||||
OTTFrame:SetFrameStrata("TOOLTIP")
|
||||
editorFrame.overTheTopFrame = OTTFrame
|
||||
|
||||
--frame that is used to move the object
|
||||
local moverFrame = CreateFrame("frame", "$parentMoverFrame", OTTFrame, "BackdropTemplate")
|
||||
moverFrame:SetClampedToScreen(true)
|
||||
moverFrame:SetBackdrop({
|
||||
edgeFile = "Interface\\Buttons\\WHITE8x8",
|
||||
edgeSize = 1,
|
||||
})
|
||||
editorFrame.moverFrame = moverFrame
|
||||
|
||||
editorFrame:CreateMoverGuideLines()
|
||||
|
||||
editorFrame.optionsFrame = optionsFrame
|
||||
editorFrame.canvasScrollBox = canvasFrame
|
||||
|
||||
|
||||
@@ -0,0 +1,663 @@
|
||||
|
||||
---@class detailsframework
|
||||
local detailsFramework = _G.DetailsFramework
|
||||
if (not detailsFramework or not DetailsFrameworkCanLoad) then
|
||||
return
|
||||
end
|
||||
|
||||
local CreateFrame = CreateFrame
|
||||
local defaultRed, defaultGreen, defaultBlue = detailsFramework:GetDefaultBackdropColor()
|
||||
--local defaultColorTable = {defaultRed, defaultGreen, defaultBlue, 1}
|
||||
local defaultColorTable = {0.98, 0.98, 0.98, 1}
|
||||
local defaultBorderColorTable = {0.1, 0.1, 0.1, 1}
|
||||
|
||||
---@type edgenames[]
|
||||
local cornerNames = {"TopLeft", "TopRight", "BottomLeft", "BottomRight"}
|
||||
|
||||
---@class blz_backdrop : table
|
||||
---@field TopLeftCorner texture
|
||||
---@field TopRightCorner texture
|
||||
---@field BottomLeftCorner texture
|
||||
---@field BottomRightCorner texture
|
||||
---@field TopEdge texture
|
||||
---@field BottomEdge texture
|
||||
---@field LeftEdge texture
|
||||
---@field RightEdge texture
|
||||
---@field Center texture
|
||||
|
||||
---@class cornertextures : table
|
||||
---@field TopLeft texture
|
||||
---@field TopRight texture
|
||||
---@field BottomLeft texture
|
||||
---@field BottomRight texture
|
||||
|
||||
---@class edgetextures : table
|
||||
---@field Top texture
|
||||
---@field Bottom texture
|
||||
---@field Left texture
|
||||
---@field Right texture
|
||||
|
||||
---@class df_roundedpanel_options : table
|
||||
---@field width number
|
||||
---@field height number
|
||||
---@field use_titlebar boolean
|
||||
---@field use_scalebar boolean
|
||||
---@field title string
|
||||
---@field scale number
|
||||
---@field roundness number
|
||||
---@field color any
|
||||
---@field border_color any
|
||||
---@field corner_texture texturepath|textureid
|
||||
---@field horizontal_border_size_offset number?
|
||||
|
||||
---@class df_roundedpanel_preset : table, df_roundedpanel_options
|
||||
---@field border_color any
|
||||
---@field color any
|
||||
---@field roundness number
|
||||
|
||||
---@class df_roundedcornermixin : table
|
||||
---@field RoundedCornerConstructor fun(self:df_roundedpanel) --called from CreateRoundedPanel
|
||||
---@field SetColor fun(self:df_roundedpanel, red: any, green: number|nil, blue: number|nil, alpha: number|nil)
|
||||
---@field SetBorderCornerColor fun(self:df_roundedpanel, red: any, green: number|nil, blue: number|nil, alpha: number|nil)
|
||||
---@field SetRoundness fun(self:df_roundedpanel, slope: number)
|
||||
---@field GetCornerSize fun(self:df_roundedpanel) : width, height
|
||||
---@field OnSizeChanged fun(self:df_roundedpanel) --called when the frame size changes
|
||||
---@field CreateBorder fun(self:df_roundedpanel) --called from SetBorderCornerColor if the border is not created yet
|
||||
---@field CalculateBorderEdgeSize fun(self:df_roundedpanel, alignment: "vertical"|"horizontal"): number --calculate the size of the border edge texture
|
||||
---@field SetTitleBarColor fun(self:df_roundedpanel, red: any, green: number|nil, blue: number|nil, alpha: number|nil)
|
||||
---@field GetMaxFrameLevel fun(self:df_roundedpanel) : number --return the max frame level of the frame and its children
|
||||
|
||||
---@class df_roundedpanel : frame, df_roundedcornermixin, df_optionsmixin, df_titlebar
|
||||
---@field bHasBorder boolean
|
||||
---@field bHasTitleBar boolean
|
||||
---@field options df_roundedpanel_options
|
||||
---@field cornerRoundness number
|
||||
---@field CornerTextures cornertextures
|
||||
---@field CenterTextures texture[]
|
||||
---@field BorderCornerTextures cornertextures
|
||||
---@field BorderEdgeTextures edgetextures
|
||||
---@field TitleBar df_roundedpanel
|
||||
---@field bIsTitleBar boolean
|
||||
---@field TopLeft texture corner texture
|
||||
---@field TopRight texture corner texture
|
||||
---@field BottomLeft texture corner texture
|
||||
---@field BottomRight texture corner texture
|
||||
---@field TopEdgeBorder texture border edge
|
||||
---@field BottomEdgeBorder texture border edge
|
||||
---@field LeftEdgeBorder texture border edge
|
||||
---@field RightEdgeBorder texture border edge
|
||||
---@field TopLeftBorder texture border corner
|
||||
---@field TopRightBorder texture border corner
|
||||
---@field BottomLeftBorder texture border corner
|
||||
---@field BottomRightBorder texture border corner
|
||||
---@field TopHorizontalEdge texture texture connecting the top corners
|
||||
---@field BottomHorizontalEdge texture texture connecting the bottom corners
|
||||
---@field CenterBlock texture texture connecting the bottom left of the topleft corner with the top right of the bottom right corner
|
||||
|
||||
---@param self df_roundedpanel
|
||||
---@param textures cornertextures
|
||||
---@param width number|nil
|
||||
---@param height number|nil
|
||||
---@param xOffset number|nil
|
||||
---@param yOffset number|nil
|
||||
---@param bIsBorder boolean|nil
|
||||
local setCornerPoints = function(self, textures, width, height, xOffset, yOffset, bIsBorder)
|
||||
for cornerName, thisTexture in pairs(textures) do
|
||||
thisTexture:SetSize(width or 16, height or 16)
|
||||
thisTexture:SetTexture(self.options.corner_texture)
|
||||
|
||||
--set the mask
|
||||
if (not thisTexture.MaskTexture and bIsBorder) then
|
||||
thisTexture.MaskTexture = self:CreateMaskTexture(nil, "background")
|
||||
thisTexture.MaskTexture:SetSize(74, 64)
|
||||
thisTexture:AddMaskTexture(thisTexture.MaskTexture)
|
||||
thisTexture.MaskTexture:SetTexture([[Interface\Azerite\AzeriteGoldRingRank2]]) --1940690
|
||||
--thisTexture.MaskTexture:Hide()
|
||||
end
|
||||
|
||||
xOffset = xOffset or 0
|
||||
yOffset = yOffset or 0
|
||||
|
||||
--todo: adjust the other corners setpoint offset
|
||||
--todo (done): use mask when the alpha is below 0.98, disable the mask when the alpha is above 0.98
|
||||
|
||||
if (cornerName == "TopLeft") then
|
||||
thisTexture:SetTexCoord(0, 0.5, 0, 0.5)
|
||||
thisTexture:SetPoint(cornerName, self, cornerName, -xOffset, yOffset)
|
||||
if (thisTexture.MaskTexture) then
|
||||
thisTexture.MaskTexture:SetPoint(cornerName, self, cornerName, -18-xOffset, 16+yOffset)
|
||||
end
|
||||
|
||||
elseif (cornerName == "TopRight") then
|
||||
thisTexture:SetTexCoord(0.5, 1, 0, 0.5)
|
||||
thisTexture:SetPoint(cornerName, self, cornerName, xOffset, yOffset)
|
||||
if (thisTexture.MaskTexture) then
|
||||
thisTexture.MaskTexture:SetPoint(cornerName, self, cornerName, -18+xOffset, 16+yOffset)
|
||||
end
|
||||
|
||||
elseif (cornerName == "BottomLeft") then
|
||||
thisTexture:SetTexCoord(0, 0.5, 0.5, 1)
|
||||
thisTexture:SetPoint(cornerName, self, cornerName, -xOffset, -yOffset)
|
||||
if (thisTexture.MaskTexture) then
|
||||
thisTexture.MaskTexture:SetPoint(cornerName, self, cornerName, -18-xOffset, 16-yOffset)
|
||||
end
|
||||
|
||||
elseif (cornerName == "BottomRight") then
|
||||
thisTexture:SetTexCoord(0.5, 1, 0.5, 1)
|
||||
thisTexture:SetPoint(cornerName, self, cornerName, xOffset, -yOffset)
|
||||
if (thisTexture.MaskTexture) then
|
||||
thisTexture.MaskTexture:SetPoint(cornerName, self, cornerName, -18+xOffset, 16-yOffset)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
detailsFramework.RoundedCornerPanelMixin = {
|
||||
RoundedCornerConstructor = function(self)
|
||||
self.CornerTextures = {}
|
||||
self.CenterTextures = {}
|
||||
self.BorderCornerTextures = {}
|
||||
self.BorderEdgeTextures = {}
|
||||
|
||||
self.cornerRoundness = 0
|
||||
|
||||
for i = 1, #cornerNames do
|
||||
---@type texture
|
||||
local newCornerTexture = self:CreateTexture(nil, "border", nil, 0)
|
||||
self.CornerTextures[cornerNames[i]] = newCornerTexture
|
||||
self[cornerNames[i]] = newCornerTexture
|
||||
end
|
||||
|
||||
--create the top texture which connects the top corners with a horizontal line
|
||||
---@type texture
|
||||
local topHorizontalEdge = self:CreateTexture(nil, "border", nil, 0)
|
||||
topHorizontalEdge:SetPoint("topleft", self.CornerTextures["TopLeft"], "topright", 0, 0)
|
||||
topHorizontalEdge:SetPoint("bottomleft", self.CornerTextures["TopLeft"], "bottomright", 0, 0)
|
||||
topHorizontalEdge:SetPoint("topright", self.CornerTextures["TopRight"], "topleft", 0, 0)
|
||||
topHorizontalEdge:SetPoint("bottomright", self.CornerTextures["TopRight"], "bottomleft", 0, 0)
|
||||
topHorizontalEdge:SetColorTexture(unpack(defaultColorTable))
|
||||
|
||||
--create the bottom texture which connects the bottom corners with a horizontal line
|
||||
---@type texture
|
||||
local bottomHorizontalEdge = self:CreateTexture(nil, "border", nil, 0)
|
||||
bottomHorizontalEdge:SetPoint("topleft", self.CornerTextures["BottomLeft"], "topright", 0, 0)
|
||||
bottomHorizontalEdge:SetPoint("bottomleft", self.CornerTextures["BottomLeft"], "bottomright", 0, 0)
|
||||
bottomHorizontalEdge:SetPoint("topright", self.CornerTextures["BottomRight"], "topleft", 0, 0)
|
||||
bottomHorizontalEdge:SetPoint("bottomright", self.CornerTextures["BottomRight"], "bottomleft", 0, 0)
|
||||
bottomHorizontalEdge:SetColorTexture(unpack(defaultColorTable))
|
||||
|
||||
--create the center block which connects the bottom left of the topleft corner with the top right of the bottom right corner
|
||||
---@type texture
|
||||
local centerBlock = self:CreateTexture(nil, "border", nil, 0)
|
||||
centerBlock:SetPoint("topleft", self.CornerTextures["TopLeft"], "bottomleft", 0, 0)
|
||||
centerBlock:SetPoint("bottomleft", self.CornerTextures["BottomLeft"], "topleft", 0, 0)
|
||||
centerBlock:SetPoint("topright", self.CornerTextures["BottomRight"], "topright", 0, 0)
|
||||
centerBlock:SetPoint("bottomright", self.CornerTextures["BottomRight"], "topright", 0, 0)
|
||||
centerBlock:SetColorTexture(unpack(defaultColorTable))
|
||||
|
||||
self.CenterTextures[#self.CenterTextures+1] = topHorizontalEdge
|
||||
self.CenterTextures[#self.CenterTextures+1] = bottomHorizontalEdge
|
||||
self.CenterTextures[#self.CenterTextures+1] = centerBlock
|
||||
|
||||
self.TopHorizontalEdge = topHorizontalEdge
|
||||
self.BottomHorizontalEdge = bottomHorizontalEdge
|
||||
self.CenterBlock = centerBlock
|
||||
|
||||
---@type width
|
||||
local width = self.options.width
|
||||
---@type height
|
||||
local height = self.options.height
|
||||
|
||||
self:SetSize(width, height)
|
||||
|
||||
--fill the corner and edge textures table
|
||||
setCornerPoints(self, self.CornerTextures)
|
||||
end,
|
||||
|
||||
---get the highest frame level of the rounded panel and its children
|
||||
---@param self df_roundedpanel
|
||||
---@return framelevel
|
||||
GetMaxFrameLevel = function(self)
|
||||
---@type framelevel
|
||||
local maxFrameLevel = 0
|
||||
local children = {self:GetChildren()}
|
||||
|
||||
for i = 1, #children do
|
||||
local thisChild = children[i]
|
||||
---@cast thisChild frame
|
||||
if (thisChild:GetFrameLevel() > maxFrameLevel) then
|
||||
maxFrameLevel = thisChild:GetFrameLevel()
|
||||
end
|
||||
end
|
||||
|
||||
return maxFrameLevel
|
||||
end,
|
||||
|
||||
---create a frame placed at the top side of the rounded panel, this frame has a member called 'Text' which is a fontstring for the title
|
||||
---@param self df_roundedpanel
|
||||
---@return df_roundedpanel
|
||||
CreateTitleBar = function(self)
|
||||
---@type df_roundedpanel
|
||||
local titleBar = detailsFramework:CreateRoundedPanel(self, "$parentTitleBar", {width = self.options.width - 6, height = 16})
|
||||
titleBar:SetPoint("top", self, "top", 0, -4)
|
||||
titleBar:SetRoundness(5)
|
||||
titleBar:SetFrameLevel(9500)
|
||||
titleBar.bIsTitleBar = true
|
||||
self.TitleBar = titleBar
|
||||
self.bHasTitleBar = true
|
||||
|
||||
local textFontString = titleBar:CreateFontString("$parentText", "overlay", "GameFontNormal")
|
||||
textFontString:SetPoint("center", titleBar, "center", 0, 0)
|
||||
titleBar.Text = textFontString
|
||||
|
||||
local closeButton = detailsFramework:CreateCloseButton(titleBar, "$parentCloseButton")
|
||||
closeButton:SetPoint("right", titleBar, "right", -3, 0)
|
||||
closeButton:SetSize(10, 10)
|
||||
closeButton:SetAlpha(0.3)
|
||||
closeButton:SetScript("OnClick", function(self)
|
||||
self:GetParent():GetParent():Hide()
|
||||
end)
|
||||
detailsFramework:SetButtonTexture(closeButton, "common-search-clearbutton")
|
||||
|
||||
return titleBar
|
||||
end,
|
||||
|
||||
---return the width and height of the corner textures
|
||||
---@param self df_roundedpanel
|
||||
---@return number, number
|
||||
GetCornerSize = function(self)
|
||||
return self.CornerTextures["TopLeft"]:GetSize()
|
||||
end,
|
||||
|
||||
---set how rounded the corners should be
|
||||
---@param self df_roundedpanel
|
||||
---@param roundness number
|
||||
SetRoundness = function(self, roundness)
|
||||
self.cornerRoundness = roundness
|
||||
self:OnSizeChanged()
|
||||
end,
|
||||
|
||||
---adjust the size of the corner textures and the border edge textures
|
||||
---@param self df_roundedpanel
|
||||
OnSizeChanged = function(self)
|
||||
--if the frame has a titlebar, need to adjust the size of the titlebar
|
||||
if (self.bHasTitleBar) then
|
||||
self.TitleBar:SetWidth(self:GetWidth() - 14)
|
||||
end
|
||||
|
||||
--if the frame height is below 32, need to recalculate the size of the corners
|
||||
---@type height
|
||||
local frameHeight = self:GetHeight()
|
||||
|
||||
if (frameHeight < 32) then
|
||||
local newCornerSize = frameHeight / 2
|
||||
|
||||
--set the new size of the corners on all corner textures
|
||||
for _, thisTexture in pairs(self.CornerTextures) do
|
||||
thisTexture:SetSize(newCornerSize - (self.cornerRoundness - 2), newCornerSize)
|
||||
end
|
||||
|
||||
--check if the frame has border and set the size of the border corners as well
|
||||
if (self.bHasBorder) then
|
||||
for _, thisTexture in pairs(self.BorderCornerTextures) do
|
||||
thisTexture:SetSize(newCornerSize-2, newCornerSize+2)
|
||||
end
|
||||
|
||||
--hide the left and right edges as the corner textures already is enough to fill the frame
|
||||
self.BorderEdgeTextures["Left"]:Hide()
|
||||
self.BorderEdgeTextures["Right"]:Hide()
|
||||
|
||||
local horizontalEdgesNewSize = self:CalculateBorderEdgeSize("horizontal")
|
||||
self.BorderEdgeTextures["Top"]:SetSize(horizontalEdgesNewSize + (self.options.horizontal_border_size_offset or 0), 1)
|
||||
self.BorderEdgeTextures["Bottom"]:SetSize(horizontalEdgesNewSize + (self.options.horizontal_border_size_offset or 0), 1)
|
||||
end
|
||||
|
||||
self.CenterBlock:Hide()
|
||||
else
|
||||
if (self.bHasBorder) then
|
||||
self.BorderEdgeTextures["Left"]:Show()
|
||||
self.BorderEdgeTextures["Right"]:Show()
|
||||
end
|
||||
|
||||
---@type width, height
|
||||
local cornerWidth, cornerHeight = 16, 16
|
||||
|
||||
self.CenterBlock:Show()
|
||||
|
||||
for _, thisTexture in pairs(self.CornerTextures) do
|
||||
thisTexture:SetSize(cornerWidth-self.cornerRoundness, cornerHeight-self.cornerRoundness)
|
||||
end
|
||||
|
||||
if (self.bHasBorder) then
|
||||
for _, thisTexture in pairs(self.BorderCornerTextures) do
|
||||
thisTexture:SetSize(cornerWidth-self.cornerRoundness, cornerHeight-self.cornerRoundness)
|
||||
thisTexture.MaskTexture:SetSize(74-(self.cornerRoundness*0.75), 64-self.cornerRoundness)
|
||||
end
|
||||
|
||||
local horizontalEdgesNewSize = self:CalculateBorderEdgeSize("horizontal")
|
||||
self.BorderEdgeTextures["Top"]:SetSize(horizontalEdgesNewSize, 1)
|
||||
self.BorderEdgeTextures["Bottom"]:SetSize(horizontalEdgesNewSize, 1)
|
||||
|
||||
local verticalEdgesNewSize = self:CalculateBorderEdgeSize("vertical")
|
||||
self.BorderEdgeTextures["Left"]:SetSize(1, verticalEdgesNewSize)
|
||||
self.BorderEdgeTextures["Right"]:SetSize(1, verticalEdgesNewSize)
|
||||
end
|
||||
end
|
||||
end,
|
||||
|
||||
---get the size of the edge texture
|
||||
---@param self df_roundedpanel
|
||||
---@param alignment "vertical"|"horizontal"
|
||||
---@return number edgeSize
|
||||
CalculateBorderEdgeSize = function(self, alignment)
|
||||
---@type string
|
||||
local borderCornerName = next(self.BorderCornerTextures)
|
||||
if (not borderCornerName) then
|
||||
return 0
|
||||
end
|
||||
|
||||
---@type texture
|
||||
local borderTexture = self.BorderCornerTextures[borderCornerName]
|
||||
|
||||
alignment = alignment:lower()
|
||||
|
||||
if (alignment == "vertical") then
|
||||
return self:GetHeight() - (borderTexture:GetHeight() * 2) + 2
|
||||
|
||||
elseif (alignment == "horizontal") then
|
||||
return self:GetWidth() - (borderTexture:GetHeight() * 2) + 2
|
||||
end
|
||||
|
||||
error("df_roundedpanel:CalculateBorderEdgeSize(self, alignment) alignment must be 'vertical' or 'horizontal'")
|
||||
end,
|
||||
|
||||
---@param self df_roundedpanel
|
||||
CreateBorder = function(self)
|
||||
local r, g, b, a = 0, 0, 0, 0.8
|
||||
|
||||
--create the corner edges
|
||||
for i = 1, #cornerNames do
|
||||
---@type texture
|
||||
local newBorderTexture = self:CreateTexture(nil, "background", nil, 0)
|
||||
self.BorderCornerTextures[cornerNames[i]] = newBorderTexture
|
||||
newBorderTexture:SetColorTexture(unpack(defaultColorTable))
|
||||
newBorderTexture:SetVertexColor(r, g, b, a)
|
||||
self[cornerNames[i] .. "Border"] = newBorderTexture
|
||||
end
|
||||
|
||||
setCornerPoints(self, self.BorderCornerTextures, 16, 16, 1, 1, true)
|
||||
|
||||
--create the top, left, bottom and right edges, the edge has 1pixel width and connects the corners
|
||||
---@type texture
|
||||
local topEdge = self:CreateTexture(nil, "background", nil, 0)
|
||||
topEdge:SetPoint("bottom", self, "top", 0, 0)
|
||||
self.BorderEdgeTextures["Top"] = topEdge
|
||||
|
||||
---@type texture
|
||||
local leftEdge = self:CreateTexture(nil, "background", nil, 0)
|
||||
leftEdge:SetPoint("right", self, "left", 0, 0)
|
||||
self.BorderEdgeTextures["Left"] = leftEdge
|
||||
|
||||
---@type texture
|
||||
local bottomEdge = self:CreateTexture(nil, "background", nil, 0)
|
||||
bottomEdge:SetPoint("top", self, "bottom", 0, 0)
|
||||
self.BorderEdgeTextures["Bottom"] = bottomEdge
|
||||
|
||||
---@type texture
|
||||
local rightEdge = self:CreateTexture(nil, "background", nil, 0)
|
||||
rightEdge:SetPoint("left", self, "right", 0, 0)
|
||||
self.BorderEdgeTextures["Right"] = rightEdge
|
||||
|
||||
---@type width
|
||||
local horizontalEdgeSize = self:CalculateBorderEdgeSize("horizontal")
|
||||
---@type height
|
||||
local verticalEdgeSize = self:CalculateBorderEdgeSize("vertical")
|
||||
|
||||
--set the edges size
|
||||
topEdge:SetSize(horizontalEdgeSize, 1)
|
||||
leftEdge:SetSize(1, verticalEdgeSize)
|
||||
bottomEdge:SetSize(horizontalEdgeSize, 1)
|
||||
rightEdge:SetSize(1, verticalEdgeSize)
|
||||
|
||||
for edgeName, thisTexture in pairs(self.BorderEdgeTextures) do
|
||||
---@cast thisTexture texture
|
||||
thisTexture:SetColorTexture(unpack(defaultColorTable))
|
||||
thisTexture:SetVertexColor(r, g, b, a)
|
||||
end
|
||||
|
||||
self.TopEdgeBorder = topEdge
|
||||
self.BottomEdgeBorder = bottomEdge
|
||||
self.LeftEdgeBorder = leftEdge
|
||||
self.RightEdgeBorder = rightEdge
|
||||
|
||||
self.bHasBorder = true
|
||||
end,
|
||||
|
||||
---@param self df_roundedpanel
|
||||
---@param red any
|
||||
---@param green number|nil
|
||||
---@param blue number|nil
|
||||
---@param alpha number|nil
|
||||
SetTitleBarColor = function(self, red, green, blue, alpha)
|
||||
if (self.bHasTitleBar) then
|
||||
red, green, blue, alpha = detailsFramework:ParseColors(red, green, blue, alpha)
|
||||
self.TitleBar:SetColor(red, green, blue, alpha)
|
||||
end
|
||||
end,
|
||||
|
||||
---@param self df_roundedpanel
|
||||
---@param red any
|
||||
---@param green number|nil
|
||||
---@param blue number|nil
|
||||
---@param alpha number|nil
|
||||
SetBorderCornerColor = function(self, red, green, blue, alpha)
|
||||
if (not self.bHasBorder) then
|
||||
self:CreateBorder()
|
||||
end
|
||||
|
||||
red, green, blue, alpha = detailsFramework:ParseColors(red, green, blue, alpha)
|
||||
|
||||
for _, thisTexture in pairs(self.BorderCornerTextures) do
|
||||
thisTexture:SetVertexColor(red, green, blue, alpha)
|
||||
end
|
||||
|
||||
for _, thisTexture in pairs(self.BorderEdgeTextures) do
|
||||
thisTexture:SetVertexColor(red, green, blue, alpha)
|
||||
end
|
||||
end,
|
||||
|
||||
---@param self df_roundedpanel
|
||||
---@param red any
|
||||
---@param green number|nil
|
||||
---@param blue number|nil
|
||||
---@param alpha number|nil
|
||||
SetColor = function(self, red, green, blue, alpha)
|
||||
red, green, blue, alpha = detailsFramework:ParseColors(red, green, blue, alpha)
|
||||
|
||||
for _, thisTexture in pairs(self.CornerTextures) do
|
||||
thisTexture:SetVertexColor(red, green, blue, alpha)
|
||||
end
|
||||
|
||||
for _, thisTexture in pairs(self.CenterTextures) do
|
||||
thisTexture:SetVertexColor(red, green, blue, alpha)
|
||||
end
|
||||
|
||||
if (self.bHasBorder) then
|
||||
if (alpha < 0.98) then
|
||||
--if using borders, the two border textures overlaps making the alpha be darker than it should
|
||||
for _, thisTexture in pairs(self.BorderCornerTextures) do
|
||||
thisTexture.MaskTexture:Show()
|
||||
end
|
||||
else
|
||||
for _, thisTexture in pairs(self.BorderCornerTextures) do
|
||||
thisTexture.MaskTexture:Hide()
|
||||
end
|
||||
end
|
||||
end
|
||||
end,
|
||||
}
|
||||
|
||||
local defaultOptions = {
|
||||
width = 200,
|
||||
height = 200,
|
||||
use_titlebar = false,
|
||||
use_scalebar = false,
|
||||
title = "",
|
||||
scale = 1,
|
||||
roundness = 0,
|
||||
color = defaultColorTable,
|
||||
border_color = defaultColorTable,
|
||||
corner_texture = [[Interface\CHARACTERFRAME\TempPortraitAlphaMaskSmall]],
|
||||
}
|
||||
|
||||
local defaultPreset = {
|
||||
border_color = {.1, .1, .1, 0.834},
|
||||
color = {defaultRed, defaultGreen, defaultBlue},
|
||||
roundness = 3,
|
||||
}
|
||||
|
||||
---create a regular panel with rounded corner
|
||||
---@param parent frame
|
||||
---@param name string|nil
|
||||
---@param optionsTable table|nil
|
||||
---@return df_roundedpanel
|
||||
function detailsFramework:CreateRoundedPanel(parent, name, optionsTable)
|
||||
---@type df_roundedpanel
|
||||
local newRoundedPanel = CreateFrame("frame", name, parent, "BackdropTemplate")
|
||||
newRoundedPanel:EnableMouse(true)
|
||||
newRoundedPanel.__dftype = "df_roundedpanel"
|
||||
newRoundedPanel.__rcorners = true
|
||||
|
||||
detailsFramework:Mixin(newRoundedPanel, detailsFramework.RoundedCornerPanelMixin)
|
||||
detailsFramework:Mixin(newRoundedPanel, detailsFramework.OptionsFunctions)
|
||||
newRoundedPanel:BuildOptionsTable(defaultOptions, optionsTable or {})
|
||||
newRoundedPanel:RoundedCornerConstructor()
|
||||
newRoundedPanel:SetScript("OnSizeChanged", newRoundedPanel.OnSizeChanged)
|
||||
|
||||
if (newRoundedPanel.options.use_titlebar) then
|
||||
---@type df_roundedpanel
|
||||
local titleBar = detailsFramework:CreateRoundedPanel(newRoundedPanel, "$parentTitleBar", {height = 26})
|
||||
titleBar:SetPoint("top", newRoundedPanel, "top", 0, -7)
|
||||
newRoundedPanel.TitleBar = titleBar
|
||||
titleBar:SetRoundness(5)
|
||||
newRoundedPanel.bHasTitleBar = true
|
||||
end
|
||||
|
||||
if (newRoundedPanel.options.use_scalebar) then
|
||||
detailsFramework:CreateScaleBar(newRoundedPanel.TitleBar or newRoundedPanel, newRoundedPanel.options)
|
||||
newRoundedPanel:SetScale(newRoundedPanel.options.scale)
|
||||
end
|
||||
|
||||
newRoundedPanel:SetRoundness(newRoundedPanel.options.roundness)
|
||||
newRoundedPanel:SetColor(newRoundedPanel.options.color)
|
||||
newRoundedPanel:SetBorderCornerColor(newRoundedPanel.options.border_color)
|
||||
|
||||
return newRoundedPanel
|
||||
end
|
||||
|
||||
local applyPreset = function(frame, preset)
|
||||
if (preset.border_color) then
|
||||
frame:SetBorderCornerColor(preset.border_color)
|
||||
end
|
||||
|
||||
if (preset.color) then
|
||||
frame:SetColor(preset.color)
|
||||
end
|
||||
|
||||
if (preset.roundness) then
|
||||
frame:SetRoundness(preset.roundness)
|
||||
else
|
||||
frame:SetRoundness(1)
|
||||
end
|
||||
|
||||
if (preset.use_titlebar) then
|
||||
frame:CreateTitleBar()
|
||||
end
|
||||
end
|
||||
|
||||
---set a frame to have rounded corners following the settings passed by the preset table
|
||||
---@param frame frame
|
||||
---@param preset df_roundedpanel_preset?
|
||||
function detailsFramework:AddRoundedCornersToFrame(frame, preset)
|
||||
frame = frame and frame.widget or frame
|
||||
assert(frame and frame.GetObjectType and frame.SetPoint, "AddRoundedCornersToFrame(frame): frame must be a frame object.")
|
||||
|
||||
if (frame.__rcorners) then
|
||||
return
|
||||
end
|
||||
|
||||
if (frame.GetBackdropBorderColor) then
|
||||
local red, green, blue, alpha = frame:GetBackdropBorderColor()
|
||||
if (alpha and alpha > 0) then
|
||||
detailsFramework:MsgWarning("AddRoundedCornersToFrame() applyed to a frame with a backdrop border.")
|
||||
detailsFramework:Msg(debugstack(2, 1, 0))
|
||||
end
|
||||
end
|
||||
|
||||
---@cast frame +df_roundedcornermixin
|
||||
detailsFramework:Mixin(frame, detailsFramework.RoundedCornerPanelMixin)
|
||||
|
||||
if (not frame["BuildOptionsTable"]) then
|
||||
---@cast frame +df_optionsmixin
|
||||
detailsFramework:Mixin(frame, detailsFramework.OptionsFunctions)
|
||||
end
|
||||
|
||||
frame:BuildOptionsTable(defaultOptions, {})
|
||||
|
||||
frame.options.width = frame:GetWidth()
|
||||
frame.options.height = frame:GetHeight()
|
||||
|
||||
frame:RoundedCornerConstructor()
|
||||
frame:HookScript("OnSizeChanged", frame.OnSizeChanged)
|
||||
|
||||
frame.__rcorners = true
|
||||
|
||||
--handle preset
|
||||
if (preset and type(preset) == "table") then
|
||||
frame.options.horizontal_border_size_offset = preset.horizontal_border_size_offset
|
||||
applyPreset(frame, preset)
|
||||
else
|
||||
applyPreset(frame, defaultPreset)
|
||||
end
|
||||
end
|
||||
|
||||
---test case:
|
||||
C_Timer.After(1, function()
|
||||
|
||||
if true then return end
|
||||
|
||||
local DF = DetailsFramework
|
||||
|
||||
local parent = UIParent
|
||||
local name = "NewRoundedCornerFrame"
|
||||
local optionsTable = {
|
||||
use_titlebar = true,
|
||||
use_scalebar = true,
|
||||
title = "Test",
|
||||
scale = 1.0,
|
||||
}
|
||||
|
||||
---@type df_roundedpanel
|
||||
local frame = _G[name] or DF:CreateRoundedPanel(parent, name, optionsTable)
|
||||
frame:SetSize(800, 600)
|
||||
frame:SetPoint("center", parent, "center", 0, 0)
|
||||
|
||||
frame:SetColor(.1, .1, .1, 1)
|
||||
frame:SetTitleBarColor(.2, .2, .2, .5)
|
||||
frame:SetBorderCornerColor(.2, .2, .2, .5)
|
||||
frame:SetRoundness(0)
|
||||
|
||||
local radiusSlider = DF:CreateSlider(frame, 120, 14, 0, 15, 1, frame.cornerRoundness, false, "RadiusBar", nil, nil, DF:GetTemplate("slider", "OPTIONS_SLIDER_TEMPLATE"))
|
||||
radiusSlider:SetHook("OnValueChange", function(self, fixedValue, value)
|
||||
value = floor(value)
|
||||
if (frame.cornerRoundness == value) then
|
||||
return
|
||||
end
|
||||
frame:SetRoundness(value)
|
||||
end)
|
||||
|
||||
local radiusText = frame:CreateFontString(nil, "overlay", "GameFontNormal")
|
||||
radiusText:SetText("Radius:")
|
||||
radiusText:SetPoint("bottomleft", radiusSlider.widget, "topleft", 0, 0)
|
||||
radiusSlider:SetPoint(10, -100)
|
||||
end)
|
||||
+14
-65
@@ -1,6 +1,6 @@
|
||||
|
||||
|
||||
local dversion = 482
|
||||
local dversion = 484
|
||||
local major, minor = "DetailsFramework-1.0", dversion
|
||||
local DF, oldminor = LibStub:NewLibrary(major, minor)
|
||||
|
||||
@@ -49,6 +49,8 @@ function DF:MsgWarning(msg, ...)
|
||||
print("|cFFFFFFAA" .. (self.__name or "Details!Framework") .. "|r |cFFFFAA00[Warning]|r", msg, ...)
|
||||
end
|
||||
|
||||
DF.internalFunctions = DF.internalFunctions or {}
|
||||
|
||||
local PixelUtil = PixelUtil or DFPixelUtil
|
||||
if (not PixelUtil) then
|
||||
--check if is in classic, TBC, or WotLK wow, if it is, build a replacement for PixelUtil
|
||||
@@ -514,6 +516,8 @@ function DF.table.getfrompath(t, path)
|
||||
end
|
||||
|
||||
return value
|
||||
else
|
||||
return t[path] or t[tonumber(path)]
|
||||
end
|
||||
end
|
||||
|
||||
@@ -539,7 +543,12 @@ function DF.table.setfrompath(t, path, value)
|
||||
lastTable[lastKey] = value
|
||||
return true
|
||||
end
|
||||
else
|
||||
t[path] = value
|
||||
return true
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
---find the value inside the table, and it it's not found, add it
|
||||
@@ -1888,7 +1897,7 @@ local anchoringFunctions = {
|
||||
---set the anchor point using a df_anchor table
|
||||
---@param widget uiobject
|
||||
---@param anchorTable df_anchor
|
||||
---@param anchorTo uiobject
|
||||
---@param anchorTo uiobject?
|
||||
function DF:SetAnchor(widget, anchorTable, anchorTo)
|
||||
anchorTo = anchorTo or widget:GetParent()
|
||||
anchoringFunctions[anchorTable.side](widget, anchorTo, anchorTable.x, anchorTable.y)
|
||||
@@ -2093,67 +2102,6 @@ end
|
||||
TutorialAlertFrame:Show()
|
||||
end
|
||||
|
||||
local refresh_options = function(self)
|
||||
for _, widget in ipairs(self.widget_list) do
|
||||
if (widget._get) then
|
||||
if (widget.widget_type == "label") then
|
||||
if (widget._get() and not widget.languageAddonId) then
|
||||
widget:SetText(widget._get())
|
||||
end
|
||||
|
||||
elseif (widget.widget_type == "select") then
|
||||
widget:Select(widget._get())
|
||||
|
||||
elseif (widget.widget_type == "toggle" or widget.widget_type == "range") then
|
||||
widget:SetValue(widget._get())
|
||||
|
||||
elseif (widget.widget_type == "textentry") then
|
||||
widget:SetText(widget._get())
|
||||
|
||||
elseif (widget.widget_type == "color") then
|
||||
local default_value, g, b, a = widget._get()
|
||||
if (type(default_value) == "table") then
|
||||
widget:SetColor (unpack(default_value))
|
||||
|
||||
else
|
||||
widget:SetColor (default_value, g, b, a)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local get_frame_by_id = function(self, id)
|
||||
return self.widgetids [id]
|
||||
end
|
||||
|
||||
function DF:ClearOptionsPanel(frame)
|
||||
for i = 1, #frame.widget_list do
|
||||
frame.widget_list[i]:Hide()
|
||||
if (frame.widget_list[i].hasLabel) then
|
||||
frame.widget_list[i].hasLabel:SetText("")
|
||||
end
|
||||
end
|
||||
|
||||
table.wipe(frame.widgetids)
|
||||
end
|
||||
|
||||
function DF:SetAsOptionsPanel(frame)
|
||||
frame.RefreshOptions = refresh_options
|
||||
frame.widget_list = {}
|
||||
frame.widget_list_by_type = {
|
||||
["dropdown"] = {}, -- "select"
|
||||
["switch"] = {}, -- "toggle"
|
||||
["slider"] = {}, -- "range"
|
||||
["color"] = {}, --
|
||||
["button"] = {}, -- "execute"
|
||||
["textentry"] = {}, --
|
||||
["label"] = {}, --"text"
|
||||
}
|
||||
frame.widgetids = {}
|
||||
frame.GetWidgetById = get_frame_by_id
|
||||
end
|
||||
|
||||
function DF:CreateOptionsFrame(name, title, template)
|
||||
template = template or 1
|
||||
|
||||
@@ -2162,7 +2110,7 @@ end
|
||||
tinsert(UISpecialFrames, name)
|
||||
|
||||
newOptionsFrame:SetSize(500, 200)
|
||||
newOptionsFrame.RefreshOptions = refresh_options
|
||||
newOptionsFrame.RefreshOptions = DF.internalFunctions.RefreshOptionsPanel
|
||||
newOptionsFrame.widget_list = {}
|
||||
|
||||
newOptionsFrame:SetScript("OnMouseDown", function(self, button)
|
||||
@@ -2200,7 +2148,7 @@ end
|
||||
tinsert(UISpecialFrames, name)
|
||||
|
||||
newOptionsFrame:SetSize(500, 200)
|
||||
newOptionsFrame.RefreshOptions = refresh_options
|
||||
newOptionsFrame.RefreshOptions = DF.internalFunctions.RefreshOptionsPanel
|
||||
newOptionsFrame.widget_list = {}
|
||||
|
||||
newOptionsFrame:SetScript("OnMouseDown", function(self, button)
|
||||
@@ -2703,6 +2651,7 @@ function DF:CreateAnimation(animation, animationType, order, duration, arg1, arg
|
||||
|
||||
elseif (animationType == "ROTATION") then
|
||||
anim:SetDegrees(arg1) --degree
|
||||
--print("SetOrigin", arg2, arg3, arg4)
|
||||
anim:SetOrigin(arg2 or "center", arg3 or 0, arg4 or 0) --point, x, y
|
||||
|
||||
elseif (animationType == "TRANSLATION") then
|
||||
|
||||
@@ -706,6 +706,17 @@ local canvasScrollBoxDefaultOptions = {
|
||||
function detailsFramework:CreateCanvasScrollBox(parent, child, name, options)
|
||||
---@type df_canvasscrollbox
|
||||
local canvasScrollBox = CreateFrame("scrollframe", name or ("DetailsFrameworkCanvasScroll" .. math.random(50000, 10000000)), parent, "BackdropTemplate, UIPanelScrollFrameTemplate")
|
||||
canvasScrollBox.scrollStep = 20
|
||||
|
||||
canvasScrollBox:SetScript("OnMouseWheel", function(self, value)
|
||||
local scrollBar = self
|
||||
local scrollStep = scrollBar.scrollStep or scrollBar:GetHeight() / 2
|
||||
if ( value > 0 ) then
|
||||
scrollBar:SetVerticalScroll(scrollBar:GetVerticalScroll() - scrollStep)
|
||||
else
|
||||
scrollBar:SetVerticalScroll(scrollBar:GetVerticalScroll() + scrollStep)
|
||||
end
|
||||
end)
|
||||
|
||||
detailsFramework:Mixin(canvasScrollBox, detailsFramework.CanvasScrollBoxMixin)
|
||||
detailsFramework:Mixin(canvasScrollBox, detailsFramework.OptionsFunctions)
|
||||
|
||||
+17
-11
@@ -232,6 +232,11 @@ DF:Mixin(DFSliderMetaFunctions, DF.ScriptHookMixin)
|
||||
return self(value)
|
||||
end
|
||||
|
||||
function DFSliderMetaFunctions:SetValueNoCallback(value)
|
||||
self.NoCallback = true
|
||||
self.slider:SetValue(value)
|
||||
end
|
||||
|
||||
-- thumb size
|
||||
function DFSliderMetaFunctions:SetThumbSize(width, height)
|
||||
if (not width) then
|
||||
@@ -697,6 +702,18 @@ DF:Mixin(DFSliderMetaFunctions, DF.ScriptHookMixin)
|
||||
table.insert(object.previous_value, 1, amt)
|
||||
table.remove(object.previous_value, 4)
|
||||
|
||||
if (object.useDecimals) then
|
||||
slider.amt:SetText(string.format("%.2f", amt))
|
||||
else
|
||||
slider.amt:SetText(math.floor(amt))
|
||||
end
|
||||
object.ivalue = amt
|
||||
|
||||
if (object.NoCallback) then
|
||||
object.NoCallback = false
|
||||
return
|
||||
end
|
||||
|
||||
--some plugins registered OnValueChanged and others with OnValueChange
|
||||
local kill = object:RunHooksForWidget("OnValueChanged", slider, object.FixedValue, amt, object)
|
||||
if (kill) then
|
||||
@@ -711,17 +728,6 @@ DF:Mixin(DFSliderMetaFunctions, DF.ScriptHookMixin)
|
||||
if (object.OnValueChanged) then
|
||||
object.OnValueChanged(slider, object.FixedValue, amt)
|
||||
end
|
||||
|
||||
if (amt < 10 and amt >= 1) then
|
||||
amt = "0" .. amt
|
||||
end
|
||||
|
||||
if (object.useDecimals) then
|
||||
slider.amt:SetText(string.format("%.2f", amt))
|
||||
else
|
||||
slider.amt:SetText(math.floor(amt))
|
||||
end
|
||||
object.ivalue = amt
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -271,6 +271,8 @@ function detailsFramework:CreateTabContainer(parent, title, frameName, tabList,
|
||||
local buttonTextSize = optionsTable.button_text_size or 10
|
||||
local containerWidthOffset = optionsTable.container_width_offset or 0
|
||||
|
||||
local bFirstTabIsCreateOnDemand = false
|
||||
|
||||
--create the base frame
|
||||
---@type df_tabcontainer
|
||||
local tabContainer = CreateFrame("frame", frameName, parent["widget"] or parent, "BackdropTemplate")
|
||||
@@ -318,10 +320,14 @@ function detailsFramework:CreateTabContainer(parent, title, frameName, tabList,
|
||||
if (tabInfo.createOnDemandFunc) then
|
||||
tabFrame:SetScript("OnShow", function()
|
||||
if (tabInfo.createOnDemandFunc) then
|
||||
detailsFramework:Dispatch(tabInfo.createOnDemandFunc, tabFrame, parent)
|
||||
detailsFramework:Dispatch(tabInfo.createOnDemandFunc, tabFrame, tabContainer, parent)
|
||||
tabInfo.createOnDemandFunc = nil
|
||||
end
|
||||
end)
|
||||
|
||||
if (tabIndex == 1) then
|
||||
bFirstTabIsCreateOnDemand = true
|
||||
end
|
||||
end
|
||||
|
||||
--attempt to get the localized text from the language system using the addonId and the frameInfo.text
|
||||
@@ -405,7 +411,12 @@ function detailsFramework:CreateTabContainer(parent, title, frameName, tabList,
|
||||
tabContainer:SetScript("OnShow", tabContainer.OnShow)
|
||||
--select the first frame
|
||||
local defaultTab = 1
|
||||
tabContainer:SelectTabByIndex(defaultTab)
|
||||
|
||||
if (bFirstTabIsCreateOnDemand) then
|
||||
C_Timer.After(0, function() tabContainer:SelectTabByIndex(defaultTab) end)
|
||||
else
|
||||
tabContainer:SelectTabByIndex(defaultTab)
|
||||
end
|
||||
|
||||
return tabContainer
|
||||
end
|
||||
|
||||
@@ -1937,6 +1937,7 @@ function detailsFramework:CreateCastBar(parent, name, settingsOverride)
|
||||
detailsFramework:Mixin(castBar, detailsFramework.CastFrameFunctions)
|
||||
detailsFramework:Mixin(castBar, detailsFramework.StatusBarFunctions)
|
||||
|
||||
|
||||
castBar:CreateTextureMask()
|
||||
castBar:AddMaskTexture(castBar.flashTexture)
|
||||
castBar:AddMaskTexture(castBar.background)
|
||||
|
||||
@@ -39,7 +39,7 @@ if (WOW_PROJECT_ID ~= WOW_PROJECT_MAINLINE and not isExpansion_Dragonflight()) t
|
||||
end
|
||||
|
||||
local major = "LibOpenRaid-1.0"
|
||||
local CONST_LIB_VERSION = 113
|
||||
local CONST_LIB_VERSION = 114
|
||||
|
||||
if (LIB_OPEN_RAID_MAX_VERSION) then
|
||||
if (CONST_LIB_VERSION <= LIB_OPEN_RAID_MAX_VERSION) then
|
||||
|
||||
@@ -547,7 +547,10 @@ do
|
||||
[5384] = {cooldown = 30, duration = 0, specs = {}, talent = false, charges = 1, class = "HUNTER", type = 5}, --Feign Death
|
||||
[186387] = {cooldown = 30, duration = 6, specs = {}, talent = false, charges = 1, class = "HUNTER", type = 8}, --Bursting Shot
|
||||
[236776] = {cooldown = 40, duration = 0, specs = {253, 254, 255}, talent = false, charges = 1, class = "HUNTER", type = 8}, --High Explosive Trap
|
||||
|
||||
[272682] = {cooldown = 45, duration = 4, specs = {253, 254, 255}, talent = false, charges = 1, class = "HUNTER", type = 7}, --Master's Call
|
||||
--Boar nil 62305 Master's Call
|
||||
--Boar Tiranaa 54216 Master's Call
|
||||
--Tiranaa Tiranaa 272682 Master's Call
|
||||
|
||||
--~druid
|
||||
-- 102 - Balance
|
||||
|
||||
@@ -416,6 +416,10 @@
|
||||
end
|
||||
end
|
||||
|
||||
function classCombat:GetRunTime()
|
||||
return self.run_time or self:GetCombatTime()
|
||||
end
|
||||
|
||||
function classCombat:GetStartTime()
|
||||
return self.start_time
|
||||
end
|
||||
|
||||
+82
-98
@@ -377,7 +377,8 @@
|
||||
|
||||
--create a new combat object and preplace the current one
|
||||
local newCombatObject = Details.combate:NovaTabela(true, Details.tabela_overall, combatCounter, ...)
|
||||
Details.tabela_vigente = newCombatObject
|
||||
Details:SetCurrentCombat(newCombatObject)
|
||||
|
||||
--flag this combat as being created
|
||||
newCombatObject.IsBeingCreated = true
|
||||
|
||||
@@ -514,14 +515,17 @@
|
||||
Details:Msg("(debug) |cFFFFFF00ended a combat|r|cFFFF7700", Details.encounter_table and Details.encounter_table.name or "")
|
||||
end
|
||||
|
||||
if (Details.tabela_vigente.bIsClosed) then
|
||||
---@type combat
|
||||
local currentCombat = Details:GetCurrentCombat()
|
||||
|
||||
if (currentCombat.bIsClosed) then
|
||||
return
|
||||
end
|
||||
Details.tabela_vigente.bIsClosed = true
|
||||
currentCombat.bIsClosed = true
|
||||
|
||||
if (Details.tabela_vigente.__destroyed) then
|
||||
if (currentCombat.__destroyed) then
|
||||
Details:Msg("a deleted combat was found during combat end, please report this bug on discord:")
|
||||
Details:Msg("combat destroyed by:", Details.tabela_vigente.__destroyedBy)
|
||||
Details:Msg("combat destroyed by:", currentCombat.__destroyedBy)
|
||||
end
|
||||
|
||||
--flag the addon as 'leaving combat'
|
||||
@@ -538,13 +542,12 @@
|
||||
--Details222.TimeCapture.StopCombat() --it did not start
|
||||
|
||||
--check if this isn't a boss and try to find a boss in the segment
|
||||
if (not Details.tabela_vigente.is_boss) then
|
||||
|
||||
if (not currentCombat.is_boss) then
|
||||
--if this is a mythic+ dungeon, do not scan for encounter journal boss names in the actor list
|
||||
Details:FindBoss()
|
||||
|
||||
--still didn't find the boss
|
||||
if (not Details.tabela_vigente.is_boss) then
|
||||
if (not currentCombat.is_boss) then
|
||||
local ZoneName, _, DifficultyID, _, _, _, _, ZoneMapID = GetInstanceInfo()
|
||||
local findboss = Details:GetRaidBossFindFunction (ZoneMapID)
|
||||
if (findboss) then
|
||||
@@ -558,34 +561,34 @@
|
||||
|
||||
Details:OnCombatPhaseChanged() --.PhaseData is nil here on alpha-32
|
||||
|
||||
if (Details.tabela_vigente.bossFunction) then
|
||||
Details:CancelTimer(Details.tabela_vigente.bossFunction)
|
||||
Details.tabela_vigente.bossFunction = nil
|
||||
if (currentCombat.bossFunction) then
|
||||
Details:CancelTimer(currentCombat.bossFunction)
|
||||
currentCombat.bossFunction = nil
|
||||
end
|
||||
|
||||
--stop combat ticker
|
||||
Details:StopCombatTicker()
|
||||
|
||||
--lock timers
|
||||
Details.tabela_vigente:LockActivityTime()
|
||||
currentCombat:LockActivityTime()
|
||||
|
||||
--get waste shields
|
||||
if (Details.close_shields) then
|
||||
Details:CloseShields (Details.tabela_vigente)
|
||||
Details:CloseShields (currentCombat)
|
||||
end
|
||||
|
||||
--salva hora, minuto, segundo do fim da luta
|
||||
Details.tabela_vigente:seta_data (Details._detalhes_props.DATA_TYPE_END)
|
||||
Details.tabela_vigente:seta_tempo_decorrido()
|
||||
currentCombat:seta_data (Details._detalhes_props.DATA_TYPE_END)
|
||||
currentCombat:seta_tempo_decorrido()
|
||||
|
||||
--drop last events table to garbage collector
|
||||
Details.tabela_vigente.player_last_events = {}
|
||||
currentCombat.player_last_events = {}
|
||||
|
||||
--flag instance type
|
||||
local _, InstanceType = GetInstanceInfo()
|
||||
Details.tabela_vigente.instance_type = InstanceType
|
||||
currentCombat.instance_type = InstanceType
|
||||
|
||||
if (not Details.tabela_vigente.is_boss and bIsFromEncounterEnd and type(bIsFromEncounterEnd) == "table") then
|
||||
if (not currentCombat.is_boss and bIsFromEncounterEnd and type(bIsFromEncounterEnd) == "table") then
|
||||
local encounterID, encounterName, difficultyID, raidSize, endStatus = unpack(bIsFromEncounterEnd)
|
||||
if (encounterID) then
|
||||
local ZoneName, InstanceType, DifficultyID, DifficultyName, _, _, _, ZoneMapID = GetInstanceInfo()
|
||||
@@ -603,7 +606,7 @@
|
||||
end
|
||||
local _, boss_index = Details:GetBossEncounterDetailsFromEncounterId (ZoneMapID, encounterID)
|
||||
|
||||
Details.tabela_vigente.is_boss = {
|
||||
currentCombat.is_boss = {
|
||||
index = boss_index or 0,
|
||||
name = encounterName,
|
||||
encounter = encounterName,
|
||||
@@ -621,83 +624,68 @@
|
||||
--tag as a mythic dungeon segment, can be any type of segment, this tag also avoid the segment to be tagged as trash
|
||||
local mythicLevel = C_ChallengeMode and C_ChallengeMode.GetActiveKeystoneInfo()
|
||||
if (mythicLevel and mythicLevel >= 2) then
|
||||
Details.tabela_vigente.is_mythic_dungeon_segment = true
|
||||
Details.tabela_vigente.is_mythic_dungeon_run_id = Details.mythic_dungeon_id
|
||||
currentCombat.is_mythic_dungeon_segment = true
|
||||
currentCombat.is_mythic_dungeon_run_id = Details.mythic_dungeon_id
|
||||
end
|
||||
|
||||
--send item level after a combat if is in raid or party group
|
||||
C_Timer.After(1, Details.ScheduleSyncPlayerActorData)
|
||||
|
||||
--if this segment isn't a boss fight
|
||||
if (not Details.tabela_vigente.is_boss) then
|
||||
|
||||
if (Details.tabela_vigente.is_pvp or Details.tabela_vigente.is_arena) then
|
||||
if (not currentCombat.is_boss) then
|
||||
if (currentCombat.is_pvp or currentCombat.is_arena) then
|
||||
Details:FlagActorsOnPvPCombat()
|
||||
end
|
||||
|
||||
if (Details.tabela_vigente.is_arena) then
|
||||
Details.tabela_vigente.enemy = "[" .. ARENA .. "] " .. Details.tabela_vigente.is_arena.name
|
||||
if (currentCombat.is_arena) then
|
||||
currentCombat.enemy = "[" .. ARENA .. "] " .. currentCombat.is_arena.name
|
||||
end
|
||||
|
||||
local in_instance = IsInInstance() --garrison returns party as instance type.
|
||||
if ((InstanceType == "party" or InstanceType == "raid") and in_instance) then
|
||||
if (InstanceType == "party") then
|
||||
if (Details.tabela_vigente.is_mythic_dungeon_segment) then --setted just above
|
||||
if (currentCombat.is_mythic_dungeon_segment) then --setted just above
|
||||
--is inside a mythic+ dungeon and this is not a boss segment, so tag it as a dungeon mythic+ trash segment
|
||||
local zoneName, instanceType, difficultyID, difficultyName, maxPlayers, dynamicDifficulty, isDynamic, instanceMapID, instanceGroupSize = GetInstanceInfo()
|
||||
Details.tabela_vigente.is_mythic_dungeon_trash = {
|
||||
currentCombat.is_mythic_dungeon_trash = {
|
||||
ZoneName = zoneName,
|
||||
MapID = instanceMapID,
|
||||
Level = Details.MythicPlus.Level,
|
||||
EJID = Details.MythicPlus.ejID,
|
||||
}
|
||||
Details:Msg("segment tagged as mythic+ trash.")
|
||||
else
|
||||
--tag the combat as trash clean up
|
||||
Details.tabela_vigente.is_trash = true
|
||||
currentCombat.is_trash = true
|
||||
end
|
||||
else
|
||||
Details.tabela_vigente.is_trash = true
|
||||
currentCombat.is_trash = true
|
||||
end
|
||||
else
|
||||
if (not in_instance) then
|
||||
if (Details.world_combat_is_trash) then
|
||||
Details.tabela_vigente.is_world_trash_combat = true
|
||||
currentCombat.is_world_trash_combat = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if (not Details.tabela_vigente.enemy) then
|
||||
if (not currentCombat.enemy) then
|
||||
local enemy = Details:FindEnemy()
|
||||
|
||||
if (enemy and Details.debug) then
|
||||
Details:Msg("(debug) enemy found", enemy)
|
||||
end
|
||||
|
||||
Details.tabela_vigente.enemy = enemy
|
||||
end
|
||||
|
||||
if (Details.debug) then
|
||||
-- Details:Msg("(debug) forcing equalize actors behavior.")
|
||||
-- Details:EqualizeActorsSchedule (Details.host_of)
|
||||
currentCombat.enemy = enemy
|
||||
end
|
||||
|
||||
Details:FlagActorsOnCommonFight() --fight_component
|
||||
else
|
||||
|
||||
--this segment is a boss fight
|
||||
if (not InCombatLockdown() and not UnitAffectingCombat("player")) then
|
||||
|
||||
else
|
||||
--Details.schedule_flag_boss_components = true
|
||||
end
|
||||
|
||||
--calling here without checking for combat since the does not ran too long for scripts
|
||||
Details:FlagActorsOnBossFight()
|
||||
|
||||
local boss_id = Details.encounter_table.id
|
||||
|
||||
if (bossKilled) then
|
||||
Details.tabela_vigente.is_boss.killed = true
|
||||
currentCombat.is_boss.killed = true
|
||||
|
||||
--add to storage
|
||||
if (not InCombatLockdown() and not UnitAffectingCombat("player") and not Details.logoff_saving_data) then
|
||||
@@ -709,11 +697,11 @@
|
||||
Details.schedule_store_boss_encounter = true
|
||||
end
|
||||
|
||||
Details:SendEvent("COMBAT_BOSS_DEFEATED", nil, Details.tabela_vigente)
|
||||
Details:SendEvent("COMBAT_BOSS_DEFEATED", nil, currentCombat)
|
||||
|
||||
Details:CheckFor_TrashSuppressionOnEncounterEnd()
|
||||
else
|
||||
Details:SendEvent("COMBAT_BOSS_WIPE", nil, Details.tabela_vigente)
|
||||
Details:SendEvent("COMBAT_BOSS_WIPE", nil, currentCombat)
|
||||
|
||||
--add to storage
|
||||
if (not InCombatLockdown() and not UnitAffectingCombat("player") and not Details.logoff_saving_data) then
|
||||
@@ -724,17 +712,15 @@
|
||||
else
|
||||
Details.schedule_store_boss_encounter_wipe = true
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
Details.tabela_vigente.is_boss.index = Details.tabela_vigente.is_boss.index or 1
|
||||
currentCombat.is_boss.index = currentCombat.is_boss.index or 1
|
||||
|
||||
Details.tabela_vigente.enemy = Details.tabela_vigente.is_boss.encounter
|
||||
|
||||
if (Details.tabela_vigente.instance_type == "raid") then
|
||||
currentCombat.enemy = currentCombat.is_boss.encounter
|
||||
|
||||
if (currentCombat.instance_type == "raid") then
|
||||
Details.last_encounter2 = Details.last_encounter
|
||||
Details.last_encounter = Details.tabela_vigente.is_boss.name
|
||||
Details.last_encounter = currentCombat.is_boss.name
|
||||
|
||||
if (Details.pre_pot_used) then
|
||||
Details.last_combat_pre_pot_used = Details.CopyTable(Details.pre_pot_used)
|
||||
@@ -748,17 +734,17 @@
|
||||
|
||||
if (bIsFromEncounterEnd) then
|
||||
if (Details.encounter_table.start) then
|
||||
Details.tabela_vigente:SetStartTime (Details.encounter_table.start)
|
||||
currentCombat:SetStartTime(Details.encounter_table.start)
|
||||
end
|
||||
Details.tabela_vigente:SetEndTime (Details.encounter_table ["end"] or GetTime())
|
||||
currentCombat:SetEndTime(Details.encounter_table["end"] or GetTime())
|
||||
end
|
||||
|
||||
--encounter boss function
|
||||
local bossFunction, bossFunctionType = Details:GetBossFunction (Details.tabela_vigente.is_boss.mapid or 0, Details.tabela_vigente.is_boss.index or 0)
|
||||
local bossFunction, bossFunctionType = Details:GetBossFunction(currentCombat.is_boss.mapid or 0, currentCombat.is_boss.index or 0)
|
||||
if (bossFunction) then
|
||||
if (bitBand(bossFunctionType, 0x2) ~= 0) then --end of combat
|
||||
if (not Details.logoff_saving_data) then
|
||||
local successful, errortext = pcall(bossFunction, Details.tabela_vigente)
|
||||
local successful, errortext = pcall(bossFunction, currentCombat)
|
||||
if (not successful) then
|
||||
Details:Msg("error occurred on Encounter Boss Function:", errortext)
|
||||
end
|
||||
@@ -766,14 +752,12 @@
|
||||
end
|
||||
end
|
||||
|
||||
if (Details.tabela_vigente.instance_type == "raid") then
|
||||
--schedule captures off
|
||||
|
||||
Details:CaptureSet (false, "damage", false, 15)
|
||||
Details:CaptureSet (false, "energy", false, 15)
|
||||
Details:CaptureSet (false, "aura", false, 15)
|
||||
Details:CaptureSet (false, "energy", false, 15)
|
||||
Details:CaptureSet (false, "spellcast", false, 15)
|
||||
if (currentCombat.instance_type == "raid") then
|
||||
Details:CaptureSet(false, "damage", false, 15)
|
||||
Details:CaptureSet(false, "energy", false, 15)
|
||||
Details:CaptureSet(false, "aura", false, 15)
|
||||
Details:CaptureSet(false, "energy", false, 15)
|
||||
Details:CaptureSet(false, "spellcast", false, 15)
|
||||
|
||||
if (Details.debug) then
|
||||
Details:Msg("(debug) freezing parser for 15 seconds.")
|
||||
@@ -781,8 +765,8 @@
|
||||
end
|
||||
|
||||
--schedule sync
|
||||
Details:EqualizeActorsSchedule (Details.host_of)
|
||||
if (Details:GetEncounterEqualize (Details.tabela_vigente.is_boss.mapid, Details.tabela_vigente.is_boss.index)) then
|
||||
Details:EqualizeActorsSchedule(Details.host_of)
|
||||
if (Details:GetEncounterEqualize(currentCombat.is_boss.mapid, currentCombat.is_boss.index)) then
|
||||
Details:ScheduleTimer("DelayedSyncAlert", 3)
|
||||
end
|
||||
end
|
||||
@@ -792,7 +776,7 @@
|
||||
Details.CloseSoloDebuffs()
|
||||
end
|
||||
|
||||
local tempo_do_combate = Details.tabela_vigente:GetCombatTime()
|
||||
local tempo_do_combate = currentCombat:GetCombatTime()
|
||||
|
||||
---@type combat
|
||||
local invalidCombat
|
||||
@@ -805,11 +789,11 @@
|
||||
local zoneName, zoneType = GetInstanceInfo()
|
||||
if (not bShouldForceDiscard and (zoneType == "none" or tempo_do_combate >= Details.minimum_combat_time or not segmentsTable[1])) then
|
||||
--combat accepted
|
||||
Details.tabela_historico:AddCombat(Details.tabela_vigente) --move a tabela atual para dentro do hist�rico
|
||||
if (Details.tabela_vigente.is_boss) then
|
||||
Details.tabela_historico:AddCombat(currentCombat) --move a tabela atual para dentro do hist�rico
|
||||
if (currentCombat.is_boss) then
|
||||
if (IsInRaid()) then
|
||||
local cleuID = Details.tabela_vigente.is_boss.id
|
||||
local diff = Details.tabela_vigente.is_boss.diff
|
||||
local cleuID = currentCombat.is_boss.id
|
||||
local diff = currentCombat.is_boss.diff
|
||||
if (cleuID and diff == 16) then -- 16 mythic
|
||||
local raidData = Details.raid_data
|
||||
|
||||
@@ -821,60 +805,60 @@
|
||||
end
|
||||
|
||||
--get or build a table for this cleuID
|
||||
mythicRaidData [cleuID] = mythicRaidData [cleuID] or {wipes = 0, kills = 0, best_try = 1, longest = 0, try_history = {}}
|
||||
local cleuIDData = mythicRaidData [cleuID]
|
||||
mythicRaidData[cleuID] = mythicRaidData[cleuID] or {wipes = 0, kills = 0, best_try = 1, longest = 0, try_history = {}}
|
||||
local cleuIDData = mythicRaidData[cleuID]
|
||||
|
||||
--store encounter data for plugins and weakauras
|
||||
if (Details.tabela_vigente:GetCombatTime() > cleuIDData.longest) then
|
||||
cleuIDData.longest = Details.tabela_vigente:GetCombatTime()
|
||||
if (currentCombat:GetCombatTime() > cleuIDData.longest) then
|
||||
cleuIDData.longest = currentCombat:GetCombatTime()
|
||||
end
|
||||
|
||||
if (Details.tabela_vigente.is_boss.killed) then
|
||||
if (currentCombat.is_boss.killed) then
|
||||
cleuIDData.kills = cleuIDData.kills + 1
|
||||
cleuIDData.best_try = 0
|
||||
table.insert(cleuIDData.try_history, {0, Details.tabela_vigente:GetCombatTime()})
|
||||
table.insert(cleuIDData.try_history, {0, currentCombat:GetCombatTime()})
|
||||
--print("KILL", "best try", cleuIDData.best_try, "amt kills", cleuIDData.kills, "wipes", cleuIDData.wipes, "longest", cleuIDData.longest)
|
||||
else
|
||||
cleuIDData.wipes = cleuIDData.wipes + 1
|
||||
if (Details.boss1_health_percent and Details.boss1_health_percent < cleuIDData.best_try) then
|
||||
cleuIDData.best_try = Details.boss1_health_percent
|
||||
table.insert(cleuIDData.try_history, {Details.boss1_health_percent, Details.tabela_vigente:GetCombatTime()})
|
||||
table.insert(cleuIDData.try_history, {Details.boss1_health_percent, currentCombat:GetCombatTime()})
|
||||
end
|
||||
--print("WIPE", "best try", cleuIDData.best_try, "amt kills", cleuIDData.kills, "wipes", cleuIDData.wipes, "longest", cleuIDData.longest)
|
||||
end
|
||||
end
|
||||
end
|
||||
--
|
||||
end
|
||||
|
||||
--the combat is valid, see if the user is sharing data with somebody
|
||||
if (Details.shareData) then
|
||||
local zipData = Details:CompressData (Details.tabela_vigente, "comm")
|
||||
local zipData = Details:CompressData(currentCombat, "comm")
|
||||
if (zipData) then
|
||||
print("has zip data")
|
||||
end
|
||||
end
|
||||
|
||||
else
|
||||
--combat denied: combat did not pass the filter and cannot be added into the segment history
|
||||
--rewind the data set to the first slot in the segments table
|
||||
showTutorialForDiscardedSegment()
|
||||
|
||||
--change the current combat to the latest combat available in the segment table
|
||||
invalidCombat = Details.tabela_vigente
|
||||
Details.tabela_vigente = segmentsTable[1]
|
||||
invalidCombat = currentCombat
|
||||
Details:SetCurrentCombat(segmentsTable[1])
|
||||
currentCombat = Details:GetCurrentCombat()
|
||||
|
||||
--if it rewinds to an already erased combat, then create a new combat
|
||||
if (Details.tabela_vigente.__destroyed) then
|
||||
Details.tabela_vigente = Details.combate:NovaTabela(nil, Details.tabela_overall)
|
||||
if (currentCombat.__destroyed) then
|
||||
Details:SetCurrentCombat(Details.combate:NovaTabela(nil, Details.tabela_overall))
|
||||
currentCombat = Details:GetCurrentCombat()
|
||||
end
|
||||
|
||||
if (Details.tabela_vigente:GetStartTime() == 0) then
|
||||
Details.tabela_vigente:SetStartTime(GetTime())
|
||||
Details.tabela_vigente:SetEndTime(GetTime())
|
||||
if (currentCombat:GetStartTime() == 0) then
|
||||
currentCombat:SetStartTime(GetTime())
|
||||
currentCombat:SetEndTime(GetTime())
|
||||
end
|
||||
|
||||
Details.tabela_vigente.resincked = true
|
||||
currentCombat.resincked = true
|
||||
Details:InstanceCallDetailsFunc(Details.AtualizarJanela)
|
||||
|
||||
if (Details.solo) then --code to update "solo" plugins, there's no solo plugins for details! at the moment
|
||||
@@ -905,8 +889,8 @@
|
||||
Details.in_combat = false
|
||||
Details.leaving_combat = false
|
||||
|
||||
Details:Destroy(Details.tabela_vigente.PhaseData.damage_section)
|
||||
Details:Destroy(Details.tabela_vigente.PhaseData.heal_section)
|
||||
Details:Destroy(currentCombat.PhaseData.damage_section)
|
||||
Details:Destroy(currentCombat.PhaseData.heal_section)
|
||||
Details:Destroy(Details.cache_damage_group)
|
||||
Details:Destroy(Details.cache_healing_group)
|
||||
|
||||
@@ -938,7 +922,7 @@
|
||||
Details:SendEvent("COMBAT_INVALID")
|
||||
Details:SendEvent("COMBAT_PLAYER_LEAVE", nil, invalidCombat)
|
||||
else
|
||||
Details:SendEvent("COMBAT_PLAYER_LEAVE", nil, Details.tabela_vigente)
|
||||
Details:SendEvent("COMBAT_PLAYER_LEAVE", nil, currentCombat)
|
||||
end
|
||||
|
||||
Details:CheckForTextTimeCounter()
|
||||
@@ -948,7 +932,7 @@
|
||||
--issue: invalidCombat will be just floating around in memory if not destroyed
|
||||
end --end of leaving combat function
|
||||
|
||||
function Details:GetPlayersInArena()
|
||||
function Details:GetPlayersInArena() --ARENA_OPPONENT_UPDATE
|
||||
local aliados = GetNumGroupMembers() -- LE_PARTY_CATEGORY_HOME
|
||||
for i = 1, aliados-1 do
|
||||
local role = UnitGroupRolesAssigned and UnitGroupRolesAssigned("party" .. i) or "DAMAGER"
|
||||
|
||||
@@ -603,6 +603,23 @@ local classTypeUtility = Details.atributos.misc
|
||||
actorContainer:Cleanup()
|
||||
end
|
||||
end
|
||||
else
|
||||
if (combatObject.is_mythic_dungeon_segment) then
|
||||
for i = 1, DETAILS_COMBAT_AMOUNT_CONTAINERS do
|
||||
---@type actorcontainer
|
||||
local actorContainer = combatObject:GetContainer(i)
|
||||
if (actorContainer) then
|
||||
local actorTable = actorContainer:GetActorTable()
|
||||
for o = #actorTable, 1, -1 do
|
||||
---@type actor
|
||||
local actorObject = actorTable[o]
|
||||
for funcName in pairs(Details222.Mixins.ActorMixin) do
|
||||
actorObject[funcName] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
+54
-15
@@ -57,22 +57,22 @@
|
||||
local _spell_energy_func = Details.habilidade_e_energy.Add
|
||||
local _spell_utility_func = Details.habilidade_misc.Add
|
||||
|
||||
--current combat and overall pointers
|
||||
local _current_combat = Details.tabela_vigente or {} --placeholder table
|
||||
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
--cache
|
||||
--cache current combat
|
||||
local _current_combat = Details.tabela_vigente or {} --placeholder table
|
||||
|
||||
--total container pointers
|
||||
local _current_total = _current_combat.totals
|
||||
local _current_gtotal = _current_combat.totals_grupo
|
||||
--cache total table
|
||||
local _current_total = _current_combat.totals
|
||||
local _current_gtotal = _current_combat.totals_grupo
|
||||
|
||||
--actors container pointers
|
||||
local _current_damage_container = _current_combat [1]
|
||||
local _current_heal_container = _current_combat [2]
|
||||
local _current_energy_container = _current_combat [3]
|
||||
local _current_misc_container = _current_combat [4]
|
||||
--cache actors containers
|
||||
local _current_damage_container = _current_combat [1]
|
||||
local _current_heal_container = _current_combat [2]
|
||||
local _current_energy_container = _current_combat [3]
|
||||
local _current_misc_container = _current_combat [4]
|
||||
|
||||
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
--cache
|
||||
local names_cache = {}
|
||||
local names_cache = {}
|
||||
--damage
|
||||
local damage_cache = setmetatable({}, Details.weaktable)
|
||||
local damage_cache_pets = setmetatable({}, Details.weaktable)
|
||||
@@ -467,6 +467,9 @@
|
||||
|
||||
--Volatile Spark on razga'reth
|
||||
[194999] = true,
|
||||
|
||||
--Ozumat - Throne of Tides
|
||||
[44566] = true,
|
||||
}
|
||||
|
||||
local ignored_npcids = {}
|
||||
@@ -5758,19 +5761,53 @@ local SPELL_POWER_PAIN = SPELL_POWER_PAIN or (PowerEnum and PowerEnum.Pain) or 1
|
||||
end
|
||||
end
|
||||
|
||||
function Details.parser_functions:CHALLENGE_MODE_END(...) --doesn't exists
|
||||
Details:Msg("CHALLENGE_MODE_END", GetTime())
|
||||
end
|
||||
|
||||
--WORLD_STATE_TIMER_START are a timer only used on scenarios
|
||||
function Details.parser_functions:WORLD_STATE_TIMER_START(...)
|
||||
local zoneName, instanceType, difficultyID, difficultyName, maxPlayers, dynamicDifficulty, isDynamic, instanceMapID, instanceGroupSize = GetInstanceInfo()
|
||||
if (difficultyID == 8) then
|
||||
if (Details222.MythicPlus.CHALLENGE_MODE_START_AT + 10 > GetTime()) then
|
||||
if (not Details222.MythicPlus.WorldStateTimerStartAt) then
|
||||
local payload1, payload2, payload3 = ...
|
||||
payload1 = payload1 or ""
|
||||
payload2 = payload2 or ""
|
||||
payload3 = payload3 or ""
|
||||
Details222.MythicPlus.LogStep("Event: WORLD_STATE_TIMER_START | payload1: " .. payload1 .. " | payload2: " .. payload2 .. " | payload3: " .. payload3)
|
||||
Details:SendEvent("COMBAT_MYTHICDUNGEON_START")
|
||||
Details222.MythicPlus.WorldStateTimerStartAt = time()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Details.parser_functions:CHALLENGE_MODE_START(...)
|
||||
--send mythic dungeon start event
|
||||
if (Details.debug) then
|
||||
print("parser event", "CHALLENGE_MODE_START", ...)
|
||||
end
|
||||
|
||||
local zoneName, instanceType, difficultyID, difficultyName, maxPlayers, dynamicDifficulty, isDynamic, instanceMapID, instanceGroupSize = GetInstanceInfo()
|
||||
if (difficultyID == 8) then
|
||||
Details:SendEvent("COMBAT_MYTHICDUNGEON_START")
|
||||
Details222.MythicPlus.CHALLENGE_MODE_START_AT = GetTime()
|
||||
Details222.MythicPlus.WorldStateTimerStartAt = nil
|
||||
Details222.MythicPlus.WorldStateTimerEndAt = nil
|
||||
Details222.MythicPlus.LogStep("Event: CHALLENGE_MODE_START")
|
||||
end
|
||||
end
|
||||
|
||||
function Details.parser_functions:CHALLENGE_MODE_COMPLETED(...)
|
||||
Details222.MythicPlus.WorldStateTimerEndAt = time()
|
||||
|
||||
local mapChallengeModeID, level, time, onTime, keystoneUpgradeLevels, practiceRun,
|
||||
oldOverallDungeonScore, newOverallDungeonScore, IsMapRecord, IsAffixRecord,
|
||||
PrimaryAffix, isEligibleForScore, members
|
||||
= C_ChallengeMode.GetCompletionInfo()
|
||||
|
||||
Details222.MythicPlus.time = math.floor(time / 1000)
|
||||
Details222.MythicPlus.bOnTime = onTime
|
||||
|
||||
--send mythic dungeon end event
|
||||
local zoneName, instanceType, difficultyID, difficultyName, maxPlayers, dynamicDifficulty, isDynamic, instanceMapID, instanceGroupSize = GetInstanceInfo()
|
||||
if (difficultyID == 8) then
|
||||
@@ -5806,6 +5843,8 @@ local SPELL_POWER_PAIN = SPELL_POWER_PAIN or (PowerEnum and PowerEnum.Pain) or 1
|
||||
if (not okay) then
|
||||
Details:Msg("something went wrong (0x7878):", errorText)
|
||||
end
|
||||
|
||||
Details222.MythicPlus.LogStep("===== Mythic+ Finished =====")
|
||||
end
|
||||
|
||||
function Details.parser_functions:PLAYER_REGEN_ENABLED(...)
|
||||
|
||||
@@ -9006,11 +9006,11 @@ end
|
||||
|
||||
gameCooltip:AddLine("Remove Common Segments", nil, 1, "white", nil, Details.font_sizes.menus, Details.font_faces.menus)
|
||||
gameCooltip:AddIcon([[Interface\Buttons\UI-StopButton]], 1, 1, 14, 14, 0, 1, 0, 1, "orange")
|
||||
gameCooltip:AddMenu(1, function() return Details.tabela_historico:ResetDataByCombatType("generic") end)
|
||||
gameCooltip:AddMenu(1, function() Details.tabela_historico:ResetDataByCombatType("generic"); GameCooltip:Hide() end)
|
||||
|
||||
gameCooltip:AddLine("Reset, but keep Mythic+ Overall Segments", nil, 1, "white", nil, Details.font_sizes.menus, Details.font_faces.menus)
|
||||
gameCooltip:AddIcon([[Interface\Buttons\UI-StopButton]], 1, 1, 14, 14, 0, 1, 0, 1, "orange")
|
||||
gameCooltip:AddMenu(1, function() return Details.tabela_historico:ResetDataByCombatType("m+overall") end)
|
||||
gameCooltip:AddMenu(1, function() Details.tabela_historico:ResetDataByCombatType("m+overall"); GameCooltip:Hide() end)
|
||||
|
||||
gameCooltip:AddLine("$div", nil, 1, nil, -5, -11)
|
||||
|
||||
|
||||
@@ -6401,7 +6401,8 @@ do
|
||||
return Details:Msg(Loc ["STRING_OPTIONS_SPELL_IDERROR"])
|
||||
end
|
||||
|
||||
Details:UserCustomSpellAdd (id, name, icon)
|
||||
local bAddedByUser = true
|
||||
Details:UserCustomSpellAdd (id, name, icon, bAddedByUser)
|
||||
|
||||
panel:Refresh()
|
||||
|
||||
@@ -6910,16 +6911,6 @@ do
|
||||
desc = "When the run is done, make an overall segment.",
|
||||
},
|
||||
|
||||
{--overall only with bosses
|
||||
type = "toggle",
|
||||
get = function() return Details.mythic_plus.make_overall_boss_only end,
|
||||
set = function(self, fixedparam, value)
|
||||
Details.mythic_plus.make_overall_boss_only = value
|
||||
end,
|
||||
name = "Overall Segment Boss Only",
|
||||
desc = "Only add boss segments on the overall.",
|
||||
},
|
||||
|
||||
{--merge trash
|
||||
type = "toggle",
|
||||
get = function() return Details.mythic_plus.merge_boss_trash end,
|
||||
|
||||
+60
-46
@@ -49,10 +49,8 @@ end
|
||||
|
||||
--]]
|
||||
|
||||
--precisa converter um wipe em um trash segment? provavel que sim
|
||||
|
||||
-- at the end of a mythic run, if enable on settings, merge all the segments from the mythic run into only one
|
||||
function DetailsMythicPlusFrame.MergeSegmentsOnEnd() --~merge
|
||||
--at the end of a mythic run, if enable on settings, merge all the segments from the mythic run into only one
|
||||
if (DetailsMythicPlusFrame.DevelopmentDebug) then
|
||||
print("Details!", "MergeSegmentsOnEnd() > starting to merge mythic segments.", "InCombatLockdown():", InCombatLockdown())
|
||||
end
|
||||
@@ -66,16 +64,18 @@ function DetailsMythicPlusFrame.MergeSegmentsOnEnd() --~merge
|
||||
local newCombat = Details:GetCurrentCombat()
|
||||
local segmentsTable = Details:GetCombatSegments()
|
||||
|
||||
local totalTime = 0
|
||||
local timeInCombat = 0
|
||||
local startDate, endDate = "", ""
|
||||
local lastSegment
|
||||
local totalSegments = 0
|
||||
|
||||
--copy deaths occured on all segments to the new segment, also sum the activity combat time
|
||||
if (Details.mythic_plus.reverse_death_log) then
|
||||
for i = 1, 40 do --copy the deaths from the first segment to the last one
|
||||
local thisCombat = segmentsTable[i]
|
||||
if (thisCombat and thisCombat.is_mythic_dungeon_run_id == Details.mythic_dungeon_id) then
|
||||
newCombat:CopyDeathsFrom(thisCombat, true)
|
||||
timeInCombat = timeInCombat + thisCombat:GetCombatTime()
|
||||
end
|
||||
end
|
||||
else
|
||||
@@ -84,11 +84,30 @@ function DetailsMythicPlusFrame.MergeSegmentsOnEnd() --~merge
|
||||
if (thisCombat) then
|
||||
if (thisCombat.is_mythic_dungeon_run_id == Details.mythic_dungeon_id) then
|
||||
newCombat:CopyDeathsFrom(thisCombat, true)
|
||||
timeInCombat = timeInCombat + thisCombat:GetCombatTime()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local zoneName, instanceType, difficultyID, difficultyName, maxPlayers, dynamicDifficulty, isDynamic, instanceMapID, instanceGroupSize = GetInstanceInfo()
|
||||
|
||||
--tag the segment as mythic overall segment
|
||||
newCombat.is_mythic_dungeon = {
|
||||
StartedAt = Details.MythicPlus.StartedAt, --the start of the run
|
||||
EndedAt = Details.MythicPlus.EndedAt, --the end of the run
|
||||
WorldStateTimerStart = Details222.MythicPlus.WorldStateTimerStartAt,
|
||||
WorldStateTimerEnd = Details222.MythicPlus.WorldStateTimerEndAt,
|
||||
TimeInCombat = timeInCombat,
|
||||
SegmentID = "overall", --segment number within the dungeon
|
||||
RunID = Details.mythic_dungeon_id,
|
||||
OverallSegment = true,
|
||||
ZoneName = Details.MythicPlus.DungeonName,
|
||||
MapID = instanceMapID,
|
||||
Level = Details.MythicPlus.Level,
|
||||
EJID = Details.MythicPlus.ejID,
|
||||
}
|
||||
|
||||
--add all boss segments from this run to this new segment
|
||||
for i = 1, 40 do --from the newer combat to the oldest
|
||||
local thisCombat = segmentsTable[i]
|
||||
@@ -96,15 +115,12 @@ function DetailsMythicPlusFrame.MergeSegmentsOnEnd() --~merge
|
||||
local canAddThisSegment = true
|
||||
if (Details.mythic_plus.make_overall_boss_only) then
|
||||
if (not thisCombat.is_boss) then
|
||||
canAddThisSegment = false
|
||||
--canAddThisSegment = false --disabled
|
||||
end
|
||||
end
|
||||
|
||||
if (canAddThisSegment) then
|
||||
newCombat = newCombat + thisCombat
|
||||
--newCombat:CopyDeathsFrom(thisCombat, true)
|
||||
|
||||
totalTime = totalTime + thisCombat:GetCombatTime()
|
||||
totalSegments = totalSegments + 1
|
||||
|
||||
if (DetailsMythicPlusFrame.DevelopmentDebug) then
|
||||
@@ -113,7 +129,7 @@ function DetailsMythicPlusFrame.MergeSegmentsOnEnd() --~merge
|
||||
|
||||
if (endDate == "") then
|
||||
local _, whenEnded = thisCombat:GetDate()
|
||||
endDate =whenEnded
|
||||
endDate = whenEnded
|
||||
end
|
||||
lastSegment = thisCombat
|
||||
end
|
||||
@@ -126,33 +142,33 @@ function DetailsMythicPlusFrame.MergeSegmentsOnEnd() --~merge
|
||||
end
|
||||
|
||||
if (DetailsMythicPlusFrame.DevelopmentDebug) then
|
||||
print("Details!", "MergeSegmentsOnEnd() > totalTime:", totalTime, "startDate:", startDate)
|
||||
print("Details!", "MergeSegmentsOnEnd() > totalTime:", timeInCombat, "startDate:", startDate)
|
||||
end
|
||||
|
||||
local zoneName, instanceType, difficultyID, difficultyName, maxPlayers, dynamicDifficulty, isDynamic, instanceMapID, instanceGroupSize = GetInstanceInfo()
|
||||
|
||||
--tag the segment as mythic overall segment
|
||||
newCombat.is_mythic_dungeon = {
|
||||
StartedAt = Details.MythicPlus.StartedAt, --the start of the run
|
||||
EndedAt = Details.MythicPlus.EndedAt, --the end of the run
|
||||
SegmentID = "overall", --segment number within the dungeon
|
||||
RunID = Details.mythic_dungeon_id,
|
||||
OverallSegment = true,
|
||||
ZoneName = Details.MythicPlus.DungeonName,
|
||||
MapID = instanceMapID,
|
||||
Level = Details.MythicPlus.Level,
|
||||
EJID = Details.MythicPlus.ejID,
|
||||
}
|
||||
|
||||
newCombat.total_segments_added = totalSegments
|
||||
|
||||
newCombat.is_mythic_dungeon_segment = true
|
||||
newCombat.is_mythic_dungeon_run_id = Details.mythic_dungeon_id
|
||||
|
||||
--check if both values are valid, this can get invalid if the player leaves the dungeon before the timer ends or the game crashes
|
||||
if (type(Details222.MythicPlus.time) == "number") then
|
||||
newCombat.run_time = Details222.MythicPlus.time
|
||||
Details222.MythicPlus.LogStep("GetCompletionInfo() Found, Time: " .. Details222.MythicPlus.time)
|
||||
|
||||
elseif (newCombat.is_mythic_dungeon.WorldStateTimerEnd and newCombat.is_mythic_dungeon.WorldStateTimerStart) then
|
||||
local runTime = newCombat.is_mythic_dungeon.WorldStateTimerEnd - newCombat.is_mythic_dungeon.WorldStateTimerStart
|
||||
newCombat.run_time = Details222.MythicPlus.time
|
||||
Details222.MythicPlus.LogStep("World State Timers is Available, Run Time: " .. runTime .. "| start:" .. newCombat.is_mythic_dungeon.WorldStateTimerStart .. "| end:" .. newCombat.is_mythic_dungeon.WorldStateTimerEnd)
|
||||
else
|
||||
newCombat.run_time = timeInCombat
|
||||
Details222.MythicPlus.LogStep("GetCompletionInfo() and World State Timers not Found, Activity Time: " .. timeInCombat)
|
||||
end
|
||||
|
||||
newCombat:SetStartTime(GetTime() - timeInCombat)
|
||||
newCombat:SetEndTime(GetTime())
|
||||
Details222.MythicPlus.LogStep("Activity Time: " .. timeInCombat)
|
||||
|
||||
--set the segment time and date
|
||||
newCombat:SetStartTime (GetTime() - totalTime)
|
||||
newCombat:SetEndTime (GetTime())
|
||||
newCombat:SetDate (startDate, endDate)
|
||||
newCombat:SetDate(startDate, endDate)
|
||||
|
||||
--immediatly finishes the segment just started
|
||||
Details:SairDoCombate()
|
||||
@@ -391,9 +407,9 @@ function DetailsMythicPlusFrame.MergeRemainingTrashAfterAllBossesDone()
|
||||
end
|
||||
end
|
||||
|
||||
--this function is called right after defeat a boss inside a mythic dungeon
|
||||
--it comes from details! control leave combat
|
||||
function DetailsMythicPlusFrame.BossDefeated(this_is_end_end, encounterID, encounterName, difficultyID, raidSize, endStatus) --hold your breath and count to ten
|
||||
--this function is called right after defeat a boss inside a mythic dungeon
|
||||
--it comes from details! control leave combat
|
||||
if (DetailsMythicPlusFrame.DevelopmentDebug) then
|
||||
print("Details!", "BossDefeated() > boss defeated | SegmentID:", Details.MythicPlus.SegmentID, " | mapID:", Details.MythicPlus.DungeonID)
|
||||
end
|
||||
@@ -650,13 +666,13 @@ function DetailsMythicPlusFrame.MythicDungeonStarted()
|
||||
local mythicLevel = C_ChallengeMode.GetActiveKeystoneInfo()
|
||||
local zoneName, _, _, _, _, _, _, currentZoneID = GetInstanceInfo()
|
||||
|
||||
local mapID = C_Map.GetBestMapForUnit ("player")
|
||||
local mapID = C_Map.GetBestMapForUnit("player")
|
||||
|
||||
if (not mapID) then
|
||||
return
|
||||
end
|
||||
|
||||
local ejID = DF.EncounterJournal.EJ_GetInstanceForMap (mapID)
|
||||
local ejID = DF.EncounterJournal.EJ_GetInstanceForMap(mapID)
|
||||
|
||||
--setup the mythic run info
|
||||
Details.MythicPlus.Started = true
|
||||
@@ -669,12 +685,12 @@ function DetailsMythicPlusFrame.MythicDungeonStarted()
|
||||
Details.MythicPlus.ejID = ejID
|
||||
Details.MythicPlus.PreviousBossKilledAt = time()
|
||||
|
||||
Details:SaveState_CurrentMythicDungeonRun (Details.mythic_dungeon_id, zoneName, currentZoneID, time()+9.7, 1, mythicLevel, ejID, time())
|
||||
Details:SaveState_CurrentMythicDungeonRun(Details.mythic_dungeon_id, zoneName, currentZoneID, time()+9.7, 1, mythicLevel, ejID, time())
|
||||
|
||||
local name, groupType, difficultyID, difficult = GetInstanceInfo()
|
||||
if (groupType == "party" and Details.overall_clear_newchallenge) then
|
||||
Details.historico:ResetOverallData()
|
||||
Details:Msg("overall data are now reset.")
|
||||
Details:Msg("the overall data has been reset.") --localize-me
|
||||
|
||||
if (Details.debug) then
|
||||
Details:Msg("(debug) timer is for a mythic+ dungeon, overall has been reseted.")
|
||||
@@ -684,19 +700,17 @@ function DetailsMythicPlusFrame.MythicDungeonStarted()
|
||||
if (DetailsMythicPlusFrame.DevelopmentDebug) then
|
||||
print("Details!", "MythicDungeonStarted() > State set to Mythic Dungeon, new combat starting in 10 seconds.")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function DetailsMythicPlusFrame.OnChallengeModeStart()
|
||||
--is this a mythic dungeon?
|
||||
local _, _, difficulty, _, _, _, _, currentZoneID = GetInstanceInfo()
|
||||
local _, _, difficultyID, _, _, _, _, currentZoneID = GetInstanceInfo()
|
||||
|
||||
if (difficulty == 8 and DetailsMythicPlusFrame.LastTimer and DetailsMythicPlusFrame.LastTimer+2 > GetTime()) then
|
||||
if (difficultyID == 8) then
|
||||
--start the dungeon on Details!
|
||||
DetailsMythicPlusFrame.MythicDungeonStarted()
|
||||
--print("D! mythic dungeon started!")
|
||||
Details222.MythicPlus.LogStep("OnChallengeModeStart()")
|
||||
else
|
||||
|
||||
--print("D! mythic dungeon was already started!")
|
||||
--from zone changed
|
||||
local mythicLevel = C_ChallengeMode.GetActiveKeystoneInfo()
|
||||
@@ -794,10 +808,10 @@ function DetailsMythicPlusFrame.EventListener.OnDetailsEvent(contextObject, even
|
||||
lowerInstance = Details:GetInstance(lowerInstance)
|
||||
if (lowerInstance) then
|
||||
C_Timer.After(3, function()
|
||||
if (lowerInstance:IsEnabled()) then
|
||||
--if (lowerInstance:IsEnabled()) then
|
||||
--todo, need localization
|
||||
lowerInstance:InstanceAlert("Details!" .. " " .. "Damage" .. " " .. "Meter", {[[Interface\AddOns\Details\images\minimap]], 16, 16, false}, 3, {function() end}, false, true)
|
||||
end
|
||||
--lowerInstance:InstanceAlert("Details!" .. " " .. "Damage" .. " " .. "Meter", {[[Interface\AddOns\Details\images\minimap]], 16, 16, false}, 3, {function() end}, false, true)
|
||||
--end
|
||||
end)
|
||||
end
|
||||
end
|
||||
@@ -812,7 +826,7 @@ function DetailsMythicPlusFrame.EventListener.OnDetailsEvent(contextObject, even
|
||||
Details:Destroy(Details.cached_specs)
|
||||
end
|
||||
|
||||
C_Timer.After(0.5, DetailsMythicPlusFrame.OnChallengeModeStart)
|
||||
C_Timer.After(0.25, DetailsMythicPlusFrame.OnChallengeModeStart)
|
||||
|
||||
--debugging
|
||||
local mPlusSettings = Details.mythic_plus
|
||||
@@ -825,7 +839,7 @@ function DetailsMythicPlusFrame.EventListener.OnDetailsEvent(contextObject, even
|
||||
|
||||
local mythicLevel = C_ChallengeMode.GetActiveKeystoneInfo()
|
||||
local zoneName, _, _, _, _, _, _, currentZoneID = GetInstanceInfo()
|
||||
Details222.MythicPlus.LogStep("CHALLENGE_MODE_START | settings: " .. result .. " | level: " .. mythicLevel .. " | zone: " .. zoneName .. " | zoneId: " .. currentZoneID)
|
||||
Details222.MythicPlus.LogStep("COMBAT_MYTHICDUNGEON_START | settings: " .. result .. " | level: " .. mythicLevel .. " | zone: " .. zoneName .. " | zoneId: " .. currentZoneID)
|
||||
|
||||
elseif (event == "COMBAT_MYTHICDUNGEON_END") then
|
||||
--ignore the event if ignoring mythic dungeon special treatment
|
||||
@@ -842,7 +856,7 @@ end
|
||||
|
||||
DetailsMythicPlusFrame:SetScript("OnEvent", function(_, event, ...)
|
||||
if (event == "START_TIMER") then
|
||||
DetailsMythicPlusFrame.LastTimer = GetTime()
|
||||
--DetailsMythicPlusFrame.LastTimer = GetTime()
|
||||
|
||||
elseif (event == "ZONE_CHANGED_NEW_AREA") then
|
||||
if (DetailsMythicPlusFrame.IsDoingMythicDungeon) then
|
||||
|
||||
+11
-1
@@ -495,6 +495,13 @@ function Details:ApplyProfile(profileName, bNoSave, bIsCopy)
|
||||
Details.time_type = 2
|
||||
end
|
||||
|
||||
Details.capture_real["damage"] = true
|
||||
Details.capture_real["heal"] = true
|
||||
Details.capture_real["energy"] = true
|
||||
Details.capture_real["miscdata"] = true
|
||||
Details.capture_real["aura"] = true
|
||||
Details.capture_real["spellcast"] = true
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
@@ -1346,6 +1353,7 @@ local default_global_data = {
|
||||
custom = {},
|
||||
savedStyles = {},
|
||||
savedCustomSpells = {},
|
||||
userCustomSpells = {}, --spells modified by the user
|
||||
savedTimeCaptures = {},
|
||||
lastUpdateWarning = 0,
|
||||
update_warning_timeout = 10,
|
||||
@@ -1594,7 +1602,9 @@ local default_global_data = {
|
||||
mythicrun_chart_frame = {},
|
||||
mythicrun_chart_frame_minimized = {},
|
||||
mythicrun_chart_frame_ready = {},
|
||||
},
|
||||
|
||||
mythicrun_time_type = 1, --1: combat time (the amount of time the player is in combat) 2: run time (the amount of time it took to finish the mythic+ run)
|
||||
}, --implementar esse time_type quando estiver dando refresh na janela
|
||||
|
||||
--plugin window positions
|
||||
plugin_window_pos = {},
|
||||
|
||||
@@ -336,12 +336,15 @@ do
|
||||
return customItemList
|
||||
end
|
||||
|
||||
function Details:UserCustomSpellUpdate(index, spellName, spellIcon)
|
||||
function Details:UserCustomSpellUpdate(index, spellName, spellIcon) --called from the options panel > rename spells
|
||||
---@type savedspelldata
|
||||
local savedSpellData = Details.savedCustomSpells[index]
|
||||
if (savedSpellData) then
|
||||
local spellId = savedSpellData[1]
|
||||
savedSpellData[2], savedSpellData[3] = spellName or savedSpellData[2], spellIcon or savedSpellData[3]
|
||||
return rawset(Details.spellcache, savedSpellData[1], {savedSpellData[2], 1, savedSpellData[3]})
|
||||
rawset(Details.spellcache, spellId, {savedSpellData[2], 1, savedSpellData[3]})
|
||||
Details.userCustomSpells[spellId] = true
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
@@ -416,7 +419,13 @@ do
|
||||
end
|
||||
end
|
||||
|
||||
function Details:UserCustomSpellAdd(spellId, spellName, spellIcon)
|
||||
function Details:UserCustomSpellAdd(spellId, spellName, spellIcon, bAddedByUser)
|
||||
if (Details.userCustomSpells[spellId]) then
|
||||
if (not bAddedByUser) then
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
local isOverwrite = false
|
||||
for index, savedSpellData in ipairs(Details.savedCustomSpells) do
|
||||
if (savedSpellData[1] == spellId) then
|
||||
@@ -431,7 +440,11 @@ do
|
||||
tinsert(Details.savedCustomSpells, {spellId, spellName, spellIcon})
|
||||
end
|
||||
|
||||
return rawset(Details.spellcache, spellId, {spellName, 1, spellIcon})
|
||||
rawset(Details.spellcache, spellId, {spellName, 1, spellIcon})
|
||||
|
||||
if (bAddedByUser) then
|
||||
Details.userCustomSpells[spellId] = true
|
||||
end
|
||||
end
|
||||
|
||||
function Details:UserCustomSpellRemove(index)
|
||||
|
||||
@@ -276,7 +276,10 @@ function Details:StartMeUp()
|
||||
Details.listener:RegisterEvent("PLAYER_SPECIALIZATION_CHANGED")
|
||||
Details.listener:RegisterEvent("PLAYER_TALENT_UPDATE")
|
||||
Details.listener:RegisterEvent("CHALLENGE_MODE_START")
|
||||
--Details.listener:RegisterEvent("CHALLENGE_MODE_END") --doesn't exists ingame (only at cleu)
|
||||
Details.listener:RegisterEvent("CHALLENGE_MODE_COMPLETED")
|
||||
Details.listener:RegisterEvent("WORLD_STATE_TIMER_START")
|
||||
|
||||
end
|
||||
|
||||
Details.parser_frame:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
|
||||
|
||||
Reference in New Issue
Block a user