Framework, update
This commit is contained in:
+161
-1
@@ -17,6 +17,17 @@ local _
|
||||
---@field Texture texture
|
||||
---@field Label fontstring
|
||||
|
||||
---@class chart_backdropindicator : frame a frame which is used to indicate a specific time frame in the chart with a colored texture
|
||||
---@field fieldTexture texture the texture which indicates the amount of time the effect was active, it is painted over the background texture
|
||||
---@field fieldLabel fontstring the label showing the name of the indicator, example: bloodlust, heroism, etc
|
||||
---@field indicatorTexture texture a small squere texture located in the top right of the chart frame, it is used to indicate the color of the indicator
|
||||
---@field indicatorLabel fontstring the label showing the name of the indicator within the indicatorTexture
|
||||
---@field bInUse boolean if the indicator is in use or not
|
||||
---@field startTime number
|
||||
---@field endTime number
|
||||
---@field labelText string
|
||||
---@field color color
|
||||
|
||||
---@alias x_axisdatatype
|
||||
---| "time" when setting the text into the labels, it will be converted into a time format
|
||||
---| "number" same as timer, but the number is not comverted to time
|
||||
@@ -36,6 +47,11 @@ local _
|
||||
---@field chartBottomOffset number the offset of the bottom side of the chart frame to the plot frame
|
||||
---@field xAxisLabelsYOffset number default: -6, the offset of the horizontal axis labels to the horizontal axis line (y coordinate)
|
||||
---@field smoothnessLevel number default: 0, the smoothness level of the chart lines, 0 is no smoothness
|
||||
---@field backdropIndicators chart_backdropindicator[]
|
||||
---@field nextBackdropIndicator number tell which is the next backdrop indicator to be used
|
||||
---@field CreateBackdropIndicator fun(self: df_chartmulti|df_chart, index: number) : chart_backdropindicator create a new backdrop indicator
|
||||
---@field GetBackdropIndicator fun(self: df_chartmulti|df_chart) : chart_backdropindicator get a backdrop indicator by index
|
||||
---@field ResetBackdropIndicators fun(self: df_chartmulti|df_chart) reset all backdrop indicators
|
||||
---@field SetAxesColor fun(self: df_chartmulti, red: number|string|table|nil, green: number|nil, blue: number|nil, alpha: number|nil) : boolean set the color of both axis lines
|
||||
---@field SetAxesThickness fun(self: df_chartmulti, thickness: number) : boolean set the thickness of both axis lines
|
||||
---@field CreateAxesLines fun(self: df_chartmulti|df_chart, xOffset: number, yOffset: number, whichSide: "left"|"right", thickness: number, amountYLabels: number, amountXLabels: number, red: any, green: number|nil, blue: number|nil, alpha: number|nil)
|
||||
@@ -56,6 +72,8 @@ local chartFrameSharedConstructor = function(self)
|
||||
self.chartBottomOffset = 0
|
||||
self.xAxisLabelsYOffset = -6
|
||||
self.smoothnessLevel = 0
|
||||
self.backdropIndicators = {}
|
||||
self.nextBackdropIndicator = 1
|
||||
end
|
||||
|
||||
---@class df_chart: frame, df_data, df_value, df_chartshared
|
||||
@@ -389,6 +407,138 @@ detailsFramework.ChartFrameSharedMixin = {
|
||||
SetXAxisDataType = function(self, dataType)
|
||||
setXAxisDataType(self, dataType)
|
||||
end,
|
||||
|
||||
---create a new backdrop indicator, this is called from the function GetBackdropIndicator
|
||||
---@param self df_chartmulti|df_chart
|
||||
---@return chart_backdropindicator
|
||||
CreateBackdropIndicator = function(self, nextIndicatorIndex)
|
||||
---@type chart_backdropindicator
|
||||
local newBackdropIndicator = CreateFrame("frame", "$parentBackdropIndicator" .. nextIndicatorIndex, self.plotFrame)
|
||||
--make the backdrop indicators bebelow the plot frame
|
||||
newBackdropIndicator:SetFrameLevel(self.plotFrame:GetFrameLevel() - 1)
|
||||
|
||||
newBackdropIndicator.fieldTexture = newBackdropIndicator:CreateTexture(nil, "overlay")
|
||||
newBackdropIndicator.fieldTexture:SetAllPoints()
|
||||
|
||||
newBackdropIndicator.fieldLabel = newBackdropIndicator:CreateFontString(nil, "overlay", "GameFontNormal")
|
||||
newBackdropIndicator.fieldLabel:SetTextColor(1, 1, 1, 0.3)
|
||||
newBackdropIndicator.fieldLabel:SetJustifyH("left")
|
||||
newBackdropIndicator.fieldLabel:SetJustifyV("top")
|
||||
detailsFramework:SetFontSize(newBackdropIndicator.fieldLabel, 10)
|
||||
newBackdropIndicator.fieldLabel:SetPoint("topleft", newBackdropIndicator.fieldTexture, "topleft", 2, -2)
|
||||
|
||||
newBackdropIndicator.indicatorTexture = newBackdropIndicator:CreateTexture(nil, "overlay")
|
||||
newBackdropIndicator.indicatorTexture:SetSize(10, 10)
|
||||
|
||||
newBackdropIndicator.indicatorLabel = newBackdropIndicator:CreateFontString(nil, "overlay", "GameFontNormal")
|
||||
newBackdropIndicator.indicatorLabel:SetTextColor(1, 1, 1, 0.837)
|
||||
newBackdropIndicator.indicatorLabel:SetJustifyH("left")
|
||||
newBackdropIndicator.indicatorLabel:SetPoint("left", newBackdropIndicator.indicatorTexture, "right", 2, 0)
|
||||
|
||||
return newBackdropIndicator
|
||||
end,
|
||||
|
||||
---reset the backdrop indicators by hidding all of them
|
||||
---@param self df_chartmulti|df_chart
|
||||
ResetBackdropIndicators = function(self)
|
||||
for i = 1, #self.backdropIndicators do
|
||||
local thisBackdropIndicator = self.backdropIndicators[i]
|
||||
thisBackdropIndicator:Hide()
|
||||
thisBackdropIndicator.bInUse = false
|
||||
end
|
||||
self.nextBackdropIndicator = 1
|
||||
end,
|
||||
|
||||
---get a backdrop indicator, if it doesn't exist, create a new one
|
||||
---@param self df_chartmulti|df_chart
|
||||
---@return chart_backdropindicator
|
||||
GetBackdropIndicator = function(self)
|
||||
local nextIndicator = self.nextBackdropIndicator
|
||||
|
||||
if (not self.backdropIndicators[nextIndicator]) then
|
||||
self.backdropIndicators[nextIndicator] = self:CreateBackdropIndicator(nextIndicator)
|
||||
end
|
||||
|
||||
self.nextBackdropIndicator = nextIndicator + 1
|
||||
return self.backdropIndicators[nextIndicator]
|
||||
end,
|
||||
|
||||
---add a backdrop indicator to the chart
|
||||
---@param self df_chartmulti|df_chart
|
||||
---@param label string this is a text to be displayed on the left side of the indicator and on the top right corner of the chart panel
|
||||
---@param timeStart number the start time of the indicator
|
||||
---@param timeEnd number the end time of the indicator
|
||||
---@param red number|nil
|
||||
---@param green number|nil
|
||||
---@param blue number|nil
|
||||
---@param alpha number|nil
|
||||
AddBackdropIndicator = function(self, label, timeStart, timeEnd, red, green, blue, alpha)
|
||||
assert(type(label) == "string", "AddBackdropIndicator: label must be a string.")
|
||||
assert(type(timeStart) == "number", "AddBackdropIndicator: timeStart must be a number.")
|
||||
assert(type(timeEnd) == "number", "AddBackdropIndicator: timeEnd must be a number.")
|
||||
|
||||
red, green, blue, alpha = detailsFramework:ParseColors(red, green, blue, alpha)
|
||||
|
||||
local backdropIndicator = self:GetBackdropIndicator()
|
||||
backdropIndicator.bInUse = true
|
||||
backdropIndicator.startTime = timeStart
|
||||
backdropIndicator.endTime = timeEnd
|
||||
backdropIndicator.labelText = label
|
||||
backdropIndicator.color = {red, green, blue, alpha}
|
||||
|
||||
return true
|
||||
end,
|
||||
|
||||
---when Plot() is called, this function will be called to show the backdrop indicators
|
||||
---it gets the x_axisdatatype or if not existant defaults to "time", calculate the area in pixels using the plot area width and the plot area 'time'
|
||||
---then set the texture color, label texts and show the small squere indicators in the top right of the plot area
|
||||
---@param self df_chartmulti|df_chart
|
||||
ShowBackdropIndicators = function(self)
|
||||
--get the x axis data type
|
||||
local xDataType = self.xAxisDataType or "time"
|
||||
--get the max value of the data type
|
||||
local dataSize = self.xAxisDataNumber or self.GetDataSize and self:GetDataSize() or 0
|
||||
--frame width in pixels
|
||||
local frameWidth = self.plotFrame:GetWidth()
|
||||
|
||||
for i = 1, self.nextBackdropIndicator-1 do
|
||||
local thisIndicator = self.backdropIndicators[i]
|
||||
if (not thisIndicator.bInUse) then
|
||||
break
|
||||
end
|
||||
|
||||
local startTime = thisIndicator.startTime
|
||||
local endTime = thisIndicator.endTime
|
||||
local labelText = thisIndicator.labelText
|
||||
local color = thisIndicator.color
|
||||
|
||||
--set the point where the indicator will be placed
|
||||
local startX = startTime / dataSize * frameWidth
|
||||
local endX = endTime / dataSize * frameWidth
|
||||
|
||||
thisIndicator:SetPoint("topleft", self.plotFrame, "topleft", startX, 0)
|
||||
thisIndicator:SetPoint("bottomright", self.plotFrame, "bottomleft", endX, 0)
|
||||
|
||||
thisIndicator.fieldLabel:SetText(labelText)
|
||||
thisIndicator.fieldTexture:SetColorTexture(unpack(color))
|
||||
|
||||
thisIndicator.indicatorLabel:SetText(labelText)
|
||||
thisIndicator.indicatorTexture:SetColorTexture(unpack(color))
|
||||
|
||||
local stringWidth = thisIndicator.indicatorLabel:GetStringWidth()
|
||||
local squareWidth = thisIndicator.indicatorTexture:GetWidth()
|
||||
|
||||
if (i == 1) then
|
||||
local space = stringWidth + squareWidth
|
||||
thisIndicator.indicatorTexture:SetPoint("topright", self.plotFrame, "topright", -space + 2, -30)
|
||||
else
|
||||
local space = stringWidth + squareWidth + 10
|
||||
thisIndicator.indicatorTexture:SetPoint("left", self.backdropIndicators[i-1].indicatorTexture, "left", -space, 0)
|
||||
end
|
||||
|
||||
thisIndicator:Show()
|
||||
end
|
||||
end,
|
||||
}
|
||||
|
||||
detailsFramework.ChartFrameMixin = {
|
||||
@@ -481,7 +631,10 @@ detailsFramework.ChartFrameMixin = {
|
||||
---@param self df_chart
|
||||
Reset = function(self)
|
||||
self:HideLines()
|
||||
self:ResetMinMaxValues()
|
||||
self.nextLine = 1
|
||||
self.xAxisDataNumber = 0
|
||||
self:ResetBackdropIndicators()
|
||||
end,
|
||||
|
||||
---@param self df_chart
|
||||
@@ -578,6 +731,8 @@ detailsFramework.ChartFrameMixin = {
|
||||
line:SetEndPoint("bottomleft", currentXPoint, currentYPoint)
|
||||
end
|
||||
|
||||
self:ShowBackdropIndicators()
|
||||
|
||||
if (bUpdateLabels or bUpdateLabels == nil) then
|
||||
updateLabelValues(self)
|
||||
end
|
||||
@@ -735,7 +890,11 @@ detailsFramework.MultiChartFrameMixin = {
|
||||
---@param self df_chartmulti
|
||||
Reset = function(self)
|
||||
self:HideCharts()
|
||||
self:ResetMinMaxValues()
|
||||
self:ResetBackdropIndicators()
|
||||
self.nextChartFrame = 1
|
||||
self.biggestDataValue = 0
|
||||
self.xAxisDataNumber = 0
|
||||
end,
|
||||
|
||||
---set the min and max values of all charts
|
||||
@@ -835,7 +994,7 @@ detailsFramework.MultiChartFrameMixin = {
|
||||
Plot = function(multiChartFrame)
|
||||
local minValue, multiChartMaxValue = multiChartFrame:GetMinMaxValues()
|
||||
local plotAreaWidth = multiChartFrame.plotFrame:GetWidth() --if there's no axis, the plotFrame has no width
|
||||
local maxDataSize = multiChartFrame:GetMaxDataSize()
|
||||
local maxDataSize = multiChartFrame:GetMaxDataSize() --it's not clearing when a new boss is selected
|
||||
local eachLineWidth = plotAreaWidth / maxDataSize
|
||||
local allCharts = multiChartFrame:GetCharts()
|
||||
|
||||
@@ -857,6 +1016,7 @@ detailsFramework.MultiChartFrameMixin = {
|
||||
chartFrame:Plot(yPointScale, bUpdateLabels)
|
||||
end
|
||||
|
||||
multiChartFrame:ShowBackdropIndicators()
|
||||
updateLabelValues(multiChartFrame)
|
||||
multiChartFrame:UpdateChartNamesIndicator()
|
||||
end,
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
|
||||
|
||||
local dversion = 446
|
||||
local dversion = 447
|
||||
local major, minor = "DetailsFramework-1.0", dversion
|
||||
local DF, oldminor = LibStub:NewLibrary(major, minor)
|
||||
|
||||
|
||||
+9
-2
@@ -891,6 +891,7 @@ detailsFramework.DataMixin = {
|
||||
---@field ValueConstructor fun(self: df_value)
|
||||
---@field SetMinMaxValues fun(self: df_value, minValue: number, maxValue: number)
|
||||
---@field GetMinMaxValues fun(self: df_value) : number, number
|
||||
---@field ResetMinMaxValues fun(self: df_value)
|
||||
---@field GetMinValue fun(self: df_value) : number
|
||||
---@field GetMaxValue fun(self: df_value) : number
|
||||
---@field SetMinValue fun(self: df_value, minValue: number)
|
||||
@@ -905,8 +906,7 @@ detailsFramework.ValueMixin = {
|
||||
---initialize the value table
|
||||
---@param self table
|
||||
ValueConstructor = function(self)
|
||||
self.minValue = 0
|
||||
self.maxValue = 1
|
||||
self:ResetMinMaxValues()
|
||||
end,
|
||||
|
||||
---set the min and max values
|
||||
@@ -925,6 +925,13 @@ detailsFramework.ValueMixin = {
|
||||
return self.minValue, self.maxValue
|
||||
end,
|
||||
|
||||
---reset the min and max values
|
||||
---@param self table
|
||||
ResetMinMaxValues = function(self)
|
||||
self.minValue = 0
|
||||
self.maxValue = 1
|
||||
end,
|
||||
|
||||
---get the min value
|
||||
---@param self table
|
||||
---@return number
|
||||
|
||||
@@ -149,6 +149,10 @@
|
||||
---@alias valueamount number used to represent a value, such as a damage amount, a healing amount, or a resource amount.
|
||||
---@alias timestring string refers to a string showing a time value, such as "1:23" or "1:23:45".
|
||||
---@alias combattime number elapsed time of a combat or time in seconds that a unit has been in combat.
|
||||
---@alias coordleft number
|
||||
---@alias coordright number
|
||||
---@alias coordtop number
|
||||
---@alias coordbottom number
|
||||
|
||||
---@class _G
|
||||
---@field RegisterAttributeDriver fun(statedriver: frame, attribute: string, conditional: string)
|
||||
|
||||
+94
-58
@@ -3745,7 +3745,7 @@ function damageClass:ToolTip_Enemies (instancia, numero, barra, keydown)
|
||||
end
|
||||
|
||||
---------DAMAGE TAKEN
|
||||
function damageClass:ToolTip_DamageTaken (instance, numero, barra, keydown)
|
||||
function damageClass:ToolTip_DamageTaken(instance, numero, barra, keydown)
|
||||
--if the object has a owner, it's a pet
|
||||
local owner = self.owner
|
||||
if (owner and owner.classe) then
|
||||
@@ -3761,13 +3761,19 @@ function damageClass:ToolTip_DamageTaken (instance, numero, barra, keydown)
|
||||
local combatObject = instance:GetShowingCombat()
|
||||
local damageContainer = combatObject:GetContainer(DETAILS_ATTRIBUTE_DAMAGE)
|
||||
|
||||
local damageTakenSorted = {}
|
||||
---@type {key1:actorname, key2:valueamount, key3:class, key4:actor}
|
||||
local damageTakenDataSorted = {}
|
||||
local mainAttribute, subAttribute = instance:GetDisplay()
|
||||
|
||||
if (subAttribute == DETAILS_SUBATTRIBUTE_ENEMIES) then
|
||||
for _, actorObject in damageContainer:ListActors() do
|
||||
if (actorObject:IsGroupPlayer() and actorObject.targets[actorName]) then
|
||||
damageTakenSorted [#damageTakenSorted+1] = {actorName, actorObject.targets[actorName], actorObject.classe, actorObject}
|
||||
damageTakenDataSorted [#damageTakenDataSorted+1] = {
|
||||
actorName,
|
||||
actorObject.targets[actorName],
|
||||
actorObject:Class(),
|
||||
actorObject
|
||||
}
|
||||
end
|
||||
end
|
||||
else
|
||||
@@ -3775,67 +3781,66 @@ function damageClass:ToolTip_DamageTaken (instance, numero, barra, keydown)
|
||||
--get the aggressor
|
||||
local enemyActorObject = damageContainer:GetActor(enemyName)
|
||||
if (enemyActorObject) then
|
||||
--local name = enemyName
|
||||
---@type {key1:actorname, key2:valueamount, key3:class, key4:actor}
|
||||
local damageTakenTable
|
||||
local damageInflictedByThisEnemy = enemyActorObject.targets[actorName]
|
||||
|
||||
if (damageInflictedByThisEnemy) then
|
||||
if (enemyActorObject:IsPlayer() or enemyActorObject:IsNeutralOrEnemy()) then
|
||||
damageTakenTable = {enemyName, damageInflictedByThisEnemy, enemyActorObject.classe, enemyActorObject}
|
||||
damageTakenSorted [#damageTakenSorted+1] = damageTakenTable
|
||||
damageTakenTable = {enemyName, damageInflictedByThisEnemy, enemyActorObject:Class(), enemyActorObject}
|
||||
damageTakenDataSorted[#damageTakenDataSorted+1] = damageTakenTable
|
||||
end
|
||||
end
|
||||
|
||||
--special cases - monk stagger
|
||||
if (enemyName == actorName and self.classe == "MONK") then
|
||||
local ff = enemyActorObject.friendlyfire [enemyName]
|
||||
if (ff and ff.total > 0) then
|
||||
local staggerDamage = ff.spells [124255] or 0
|
||||
if (enemyName == actorName and self:Class() == "MONK") then
|
||||
local friendlyFire = enemyActorObject.friendlyfire[enemyName]
|
||||
if (friendlyFire and friendlyFire.total > 0) then
|
||||
local staggerDamage = friendlyFire.spells[124255] or 0
|
||||
if (staggerDamage > 0) then
|
||||
if (damageTakenTable) then
|
||||
damageTakenTable [2] = damageTakenTable [2] + staggerDamage
|
||||
damageTakenTable[2] = damageTakenTable[2] + staggerDamage
|
||||
else
|
||||
damageTakenSorted [#damageTakenSorted+1] = {enemyName, staggerDamage, "MONK", enemyActorObject}
|
||||
damageTakenDataSorted[#damageTakenDataSorted+1] = {enemyName, staggerDamage, "MONK", enemyActorObject}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
local max = #damageTakenSorted
|
||||
if (max > 10) then
|
||||
max = 10
|
||||
local maxDataAllowed = #damageTakenDataSorted
|
||||
if (maxDataAllowed > 10) then
|
||||
maxDataAllowed = 10
|
||||
end
|
||||
|
||||
local bIsMaximized = false
|
||||
if (keydown == "shift" or TooltipMaximizedMethod == 2 or TooltipMaximizedMethod == 3 or instance.sub_atributo == 6 or Details.damage_taken_everything) then
|
||||
max = #damageTakenSorted
|
||||
maxDataAllowed = #damageTakenDataSorted
|
||||
bIsMaximized = true
|
||||
end
|
||||
|
||||
if (subAttribute == DETAILS_SUBATTRIBUTE_ENEMIES) then
|
||||
Details:AddTooltipSpellHeaderText (Loc ["STRING_DAMAGE_TAKEN_FROM"], headerColor, #damageTakenSorted, [[Interface\Buttons\UI-MicroStream-Red]], 0.1875, 0.8125, 0.15625, 0.78125)
|
||||
Details:AddTooltipSpellHeaderText(Loc ["STRING_DAMAGE_TAKEN_FROM"], headerColor, #damageTakenDataSorted, [[Interface\Buttons\UI-MicroStream-Red]], 0.1875, 0.8125, 0.15625, 0.78125)
|
||||
else
|
||||
Details:AddTooltipSpellHeaderText (Loc ["STRING_FROM"], headerColor, #damageTakenSorted, [[Interface\Addons\Details\images\icons]], 0.126953125, 0.1796875, 0, 0.0546875)
|
||||
Details:AddTooltipSpellHeaderText(Loc ["STRING_FROM"], headerColor, #damageTakenDataSorted, [[Interface\Addons\Details\images\icons]], 0.126953125, 0.1796875, 0, 0.0546875)
|
||||
end
|
||||
|
||||
if (bIsMaximized) then
|
||||
--highlight
|
||||
GameCooltip:AddIcon ([[Interface\AddOns\Details\images\key_shift]], 1, 2, Details.tooltip_key_size_width, Details.tooltip_key_size_height, 0, 1, 0, 0.640625, Details.tooltip_key_overlay2)
|
||||
GameCooltip:AddIcon([[Interface\AddOns\Details\images\key_shift]], 1, 2, Details.tooltip_key_size_width, Details.tooltip_key_size_height, 0, 1, 0, 0.640625, Details.tooltip_key_overlay2)
|
||||
if (subAttribute == DETAILS_SUBATTRIBUTE_ENEMIES) then
|
||||
GameCooltip:AddStatusBar (100, 1, 0.7, g, b, 1)
|
||||
GameCooltip:AddStatusBar(100, 1, 0.7, g, b, 1)
|
||||
else
|
||||
Details:AddTooltipHeaderStatusbar (r, g, b, 1)
|
||||
Details:AddTooltipHeaderStatusbar(r, g, b, 1)
|
||||
end
|
||||
else
|
||||
GameCooltip:AddIcon ([[Interface\AddOns\Details\images\key_shift]], 1, 2, Details.tooltip_key_size_width, Details.tooltip_key_size_height, 0, 1, 0, 0.640625, Details.tooltip_key_overlay1)
|
||||
GameCooltip:AddIcon([[Interface\AddOns\Details\images\key_shift]], 1, 2, Details.tooltip_key_size_width, Details.tooltip_key_size_height, 0, 1, 0, 0.640625, Details.tooltip_key_overlay1)
|
||||
if (subAttribute == DETAILS_SUBATTRIBUTE_ENEMIES) then
|
||||
GameCooltip:AddStatusBar (100, 1, 0.7, 0, 0, barAlha)
|
||||
GameCooltip:AddStatusBar(100, 1, 0.7, 0, 0, barAlha)
|
||||
else
|
||||
Details:AddTooltipHeaderStatusbar (r, g, b, barAlha)
|
||||
Details:AddTooltipHeaderStatusbar(r, g, b, barAlha)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -3843,65 +3848,96 @@ function damageClass:ToolTip_DamageTaken (instance, numero, barra, keydown)
|
||||
local iconBorderTexCoord = Details.tooltip.icon_border_texcoord
|
||||
|
||||
-- create a full list of incoming damage, before adding any lines to the tooltip, so we can sort them appropriately
|
||||
|
||||
---@class cooltip_icon
|
||||
---@field key1 textureid
|
||||
---@field key2 number 1 for main tooltip frame, 2 for the secondary frame
|
||||
---@field key3 number 1 for the left side, 2 for the right size
|
||||
---@field key4 width
|
||||
---@field key5 height
|
||||
---@field key6 coordleft
|
||||
---@field key7 coordright
|
||||
---@field key8 coordtop
|
||||
---@field key9 coordbottom
|
||||
|
||||
---@type {key1:valueamount, key2:table<string, string>, key3:cooltip_icon}
|
||||
local lines_to_add = {}
|
||||
for i = 1, max do
|
||||
local enemyActorObject = damageTakenSorted[i][4]
|
||||
|
||||
for i = 1, maxDataAllowed do
|
||||
local enemyActorObject = damageTakenDataSorted[i][4]
|
||||
|
||||
--only shows damage from enemies or from the player it self
|
||||
--the player it self can only be placed on the list by the iteration above
|
||||
--the iteration doesnt check friendly fire for all actors, only a few cases like Monk Stagger
|
||||
|
||||
if (enemyActorObject:IsNeutralOrEnemy() or enemyActorObject.nome == self.nome) then
|
||||
local all_spells = {}
|
||||
if (enemyActorObject:IsNeutralOrEnemy() or enemyActorObject:Name() == self:Name()) then
|
||||
---@type {key1:spellid, key2:valueamount, key:actorname}
|
||||
local spellTargetDamageList = {}
|
||||
|
||||
for spellid, spell in pairs(enemyActorObject.spells._ActorTable) do
|
||||
local on_target = spell.targets [self.nome]
|
||||
if (on_target) then
|
||||
tinsert(all_spells, {spellid, on_target, enemyActorObject.nome})
|
||||
for spellId, spellTable in pairs(enemyActorObject.spells._ActorTable) do
|
||||
local damageOnTarget = spellTable.targets[self:Name()]
|
||||
if (damageOnTarget) then
|
||||
tinsert(spellTargetDamageList, {spellId, damageOnTarget, enemyActorObject:Name()})
|
||||
end
|
||||
end
|
||||
|
||||
--friendly fire
|
||||
local friendlyFire = enemyActorObject.friendlyfire [self.nome]
|
||||
local friendlyFire = enemyActorObject.friendlyfire[self:Name()]
|
||||
if (friendlyFire) then
|
||||
for spellid, amount in pairs(friendlyFire.spells) do
|
||||
tinsert(all_spells, {spellid, amount, enemyActorObject.nome})
|
||||
for spellId, valueAmount in pairs(friendlyFire.spells) do
|
||||
table.insert(spellTargetDamageList, {spellId, valueAmount, enemyActorObject:Name()})
|
||||
end
|
||||
end
|
||||
|
||||
for _, spell in ipairs(all_spells) do
|
||||
local spellname, _, spellicon = _GetSpellInfo(spell [1])
|
||||
for _, spell in ipairs(spellTargetDamageList) do
|
||||
local spellId, valueAmount, thisActorName = unpack(spell)
|
||||
|
||||
local spellName, _, spellIcon = _GetSpellInfo(spellId)
|
||||
local addTextArgs = {spellName .. " (|cFFFFFF00" .. thisActorName .. "|r)", Details:Format(valueAmount) .. " (" .. string.format("%.1f", (valueAmount / totalDamageTaken) * 100) .. "%)"}
|
||||
---@type cooltip_icon
|
||||
local addIconArgs = {spellIcon, 1, 1, iconSize.W, iconSize.H, iconBorderTexCoord.L, iconBorderTexCoord.R, iconBorderTexCoord.T, iconBorderTexCoord.B}
|
||||
|
||||
tinsert(lines_to_add, {
|
||||
spell [2],
|
||||
{spellname .. " (|cFFFFFF00" .. spell [3] .. "|r)", FormatTooltipNumber (_, spell [2]).." (" .. format("%.1f", (spell [2] / totalDamageTaken) * 100).."%)"},
|
||||
{spellicon, 1, 1, iconSize.W, iconSize.H, iconBorderTexCoord.L, iconBorderTexCoord.R, iconBorderTexCoord.T, iconBorderTexCoord.B}
|
||||
valueAmount,
|
||||
addTextArgs,
|
||||
addIconArgs
|
||||
})
|
||||
end
|
||||
|
||||
else
|
||||
local amount, addLineArgs, addIconArgs = damageTakenSorted[i][2]
|
||||
local aggressorName = Details:GetOnlyName(damageTakenSorted[i][1])
|
||||
if (bIsMaximized and damageTakenSorted[i][1]:find(Details.playername)) then
|
||||
addLineArgs = { aggressorName, FormatTooltipNumber (_, damageTakenSorted[i][2]).." ("..format("%.1f", (damageTakenSorted[i][2]/totalDamageTaken) * 100).."%)", nil, "yellow" }
|
||||
else
|
||||
addLineArgs = { aggressorName, FormatTooltipNumber (_, damageTakenSorted[i][2]).." ("..format("%.1f", (damageTakenSorted[i][2]/totalDamageTaken) * 100).."%)" }
|
||||
end
|
||||
local classe = damageTakenSorted[i][3]
|
||||
---@type actorname, valueamount, class, actor
|
||||
local thisAggrossorTable = damageTakenDataSorted[i]
|
||||
local actorName = thisAggrossorTable[1]
|
||||
local amount = thisAggrossorTable[2]
|
||||
local class = thisAggrossorTable[3]
|
||||
local actorObject = thisAggrossorTable[4]
|
||||
|
||||
if (not classe) then
|
||||
classe = "UNKNOW"
|
||||
---@type {key1:actorname, key2:string, key3:nil, key4:color}
|
||||
local addLineArgs
|
||||
---@type cooltip_icon
|
||||
local addIconArgs
|
||||
|
||||
local aggressorName = Details:GetOnlyName(actorName)
|
||||
if (bIsMaximized and actorName:find(Details.playername)) then
|
||||
addLineArgs = {aggressorName, Details:Format(amount) .. " ("..string.format("%.1f", (amount / totalDamageTaken) * 100) .. "%)", nil, "yellow"}
|
||||
else
|
||||
addLineArgs = {aggressorName, Details:Format(amount) .. " ("..string.format("%.1f", (amount / totalDamageTaken) * 100) .. "%)"}
|
||||
end
|
||||
|
||||
if (classe == "UNKNOW") then
|
||||
addIconArgs = { "Interface\\LFGFRAME\\LFGROLE_BW", nil, nil, iconSize.W, iconSize.H, .25, .5, 0, 1 }
|
||||
else
|
||||
addIconArgs= { instance.row_info.icon_file, nil, nil, iconSize.W, iconSize.H, unpack(Details.class_coords [classe]) }
|
||||
if (not class) then
|
||||
class = "UNKNOW"
|
||||
end
|
||||
tinsert(lines_to_add, { amount, addLineArgs, addIconArgs })
|
||||
|
||||
if (class == "UNKNOW") then
|
||||
addIconArgs = {"Interface\\LFGFRAME\\LFGROLE_BW", nil, nil, iconSize.W, iconSize.H, .25, .5, 0, 1}
|
||||
else
|
||||
addIconArgs= {instance.row_info.icon_file, nil, nil, iconSize.W, iconSize.H, unpack(Details.class_coords [class])}
|
||||
end
|
||||
tinsert(lines_to_add, {amount, addLineArgs, addIconArgs})
|
||||
end
|
||||
end
|
||||
|
||||
table.sort(lines_to_add, function(a, b) return a[1] > b[1] end)
|
||||
table.sort(lines_to_add, Details.Sort1)
|
||||
|
||||
for _, line in ipairs(lines_to_add) do
|
||||
GameCooltip:AddLine(unpack(line[2]))
|
||||
GameCooltip:AddIcon(unpack(line[3]))
|
||||
|
||||
+79
-6
@@ -5935,17 +5935,90 @@ local SPELL_POWER_PAIN = SPELL_POWER_PAIN or (PowerEnum and PowerEnum.Pain) or 1
|
||||
|
||||
local parserDebug = {}
|
||||
function Details.OnParserEventDebug()
|
||||
local time, token, hidding, who_serial, who_name, who_flags, who_flags2, target_serial, target_name, target_flags, target_flags2, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12 = CombatLogGetCurrentEventInfo()
|
||||
local time, token, hidding, who_serial, who_name, who_flags, who_flags2, target_serial, target_name, target_flags, target_flags2, spellId, spellName, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, unknown1, unknown2, unknown3, unknown4, unknown5 = CombatLogGetCurrentEventInfo()
|
||||
|
||||
if (not parserDebug[token]) then
|
||||
parserDebug[token] = true
|
||||
print(token)
|
||||
end
|
||||
|
||||
if (token == "SPELL_DAMAGE") then
|
||||
if (A13 ~= nil or unknown1 ~= nil or unknown2 ~= nil or unknown3 ~= nil or unknown4 ~= nil or unknown5) then
|
||||
--print(time, token, hidding, who_serial, who_name, who_flags, who_flags2, target_serial, target_name, target_flags, target_flags2, spellId, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18)
|
||||
end
|
||||
--print(time, token, hidding, who_serial, who_name, who_flags, who_flags2, target_serial, target_name, target_flags, target_flags2, spellId, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18)
|
||||
|
||||
if (spellName == "Fate Mirror") then
|
||||
--print(time, token, hidding, who_serial, who_name, who_flags, who_flags2, target_serial, target_name, target_flags, target_flags2, spellId, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18)
|
||||
end
|
||||
end
|
||||
|
||||
if (token == "SPELL_AURA_APPLIED") then
|
||||
--print(spellName)
|
||||
end
|
||||
|
||||
--local func = token_list[token]
|
||||
--if (func) then
|
||||
-- return func(nil, token, time, who_serial, who_name, who_flags, target_serial, target_name, target_flags, target_flags2, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12)
|
||||
-- return func(nil, token, time, who_serial, who_name, who_flags, target_serial, target_name, target_flags, target_flags2, spellId, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12)
|
||||
--end
|
||||
|
||||
--[=[ getspellinfo
|
||||
["1"] = "Spatial Paradox", buff
|
||||
["3"] = 5199645,
|
||||
["4"] = 0,
|
||||
["5"] = 0,
|
||||
["6"] = 100,
|
||||
["7"] = 406789,
|
||||
["8"] = 5199645,
|
||||
|
||||
["1"] = "Spatial Paradox", buff
|
||||
["3"] = 5199645,
|
||||
["4"] = 0,
|
||||
["5"] = 0,
|
||||
["6"] = 60,
|
||||
["7"] = 406732,
|
||||
["8"] = 5199645,
|
||||
|
||||
["1"] = "Ebon Might", --spell cast start
|
||||
["3"] = 5061347,
|
||||
["4"] = 1473,
|
||||
["5"] = 0,
|
||||
["6"] = 0,
|
||||
["7"] = 395152,
|
||||
["8"] = 5061347,
|
||||
--]=]
|
||||
|
||||
if (who_serial == UnitGUID("player")) then
|
||||
GLOB = GLOB or {}
|
||||
--table.insert(GLOB, {time, token, hidding, who_serial, who_name, who_flags, who_flags2, target_serial, target_name, target_flags, target_flags2, spellId, spellName, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18})
|
||||
--print(time, token, hidding, who_serial, who_name, who_flags, who_flags2, target_serial, target_name, target_flags, target_flags2, spellId, spellName, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18)
|
||||
end
|
||||
|
||||
--two spells triggering _support
|
||||
--404908,"Fate Mirror"
|
||||
--395152,"Ebon Might"
|
||||
|
||||
--SPELL_DAMAGE_SUPPORT on spellId 395152 spellname "Ebon Might", only seens to exists in the offline version of the combat log
|
||||
|
||||
if (spellId == 395152) then --Ebon Might "cast start" and "buff applyed"
|
||||
--print(time, token, hidding, who_serial, who_name, who_flags, who_flags2, target_serial, target_name, target_flags, target_flags2, spellId, spellName, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, unknown1, unknown2, unknown3, unknown4, unknown5)
|
||||
end
|
||||
|
||||
if (spellName== "Ebon Might") then
|
||||
--print(token, spellName, spellId)
|
||||
end
|
||||
|
||||
if (token == "SPELL_CAST_START") then
|
||||
if (who_serial == UnitGUID("player")) then
|
||||
--print(token, spellName, spellId)
|
||||
end
|
||||
end
|
||||
|
||||
--Prescience, Fate Mirror, Ebon Might, Breath of Eons, Shifting Sands
|
||||
|
||||
--offline cleu:
|
||||
--6/30 14:25:28.988 SPELL_DAMAGE,Player-5764-0001609B,"Mikito-Fyrakk",0x518,0x0,Creature-0-5770-2444-8-198594-00009DF6EF,"Cleave Training Dummy",0x30a28,0x0,44425,"Arcane Barrage",0x40,0000000000000000,0000000000000000,0,0,0,0,0,0,-1,0,0,0,0.00,0.00,2112,0.0000,0,18252,18251,-1,64,0,0,0,nil,nil,nil
|
||||
--6/30 14:25:28.988 SPELL_DAMAGE_SUPPORT,Player-5764-0001609B,"Mikito-Fyrakk",0x518,0x0,Creature-0-5770-2444-8-198594-00009DF6EF,"Cleave Training Dummy",0x30a28,0x0,395152,"Ebon Might",0xc,0000000000000000,0000000000000000,0,0,0,0,0,0,-1,0,0,0,0.00,0.00,2112,0.0000,0,2572,2571,-1,64,0,0,0,nil,nil,nil,Player-5764-0001FACE
|
||||
end
|
||||
|
||||
function Details.OnParserEventClassicEra()
|
||||
@@ -5994,11 +6067,11 @@ local SPELL_POWER_PAIN = SPELL_POWER_PAIN or (PowerEnum and PowerEnum.Pain) or 1
|
||||
}
|
||||
Details.parser_frame:SetScript("OnEvent", Details.OnParserEventClassicEra)
|
||||
else
|
||||
--if ("I'm debugging something") then
|
||||
-- Details.parser_frame:SetScript("OnEvent", Details.OnParserEventDebug)
|
||||
--else
|
||||
if (false and "I'm debugging something") then
|
||||
Details.parser_frame:SetScript("OnEvent", Details.OnParserEventDebug)
|
||||
else
|
||||
Details.parser_frame:SetScript("OnEvent", Details.OnParserEvent)
|
||||
--end
|
||||
end
|
||||
end
|
||||
|
||||
function Details:UpdateParser()
|
||||
|
||||
@@ -2523,6 +2523,192 @@ local addonName, Details222 = ...
|
||||
|
||||
})
|
||||
|
||||
|
||||
_detalhes:InstallSkin ("10 Years Anniversary", {
|
||||
file = [[Interface\AddOns\Details\images\skins\ten_years_skin.tga]],
|
||||
author = "Details!",
|
||||
version = "1.0",
|
||||
site = "unknown",
|
||||
desc = "based on AddonSkins for ElvUI, this skin has opaque title bar and background.",
|
||||
no_cache = true,
|
||||
|
||||
--general
|
||||
can_change_alpha_head = true,
|
||||
|
||||
--icon anchors
|
||||
icon_anchor_main = {-4, -5},
|
||||
icon_anchor_plugins = {-7, -13},
|
||||
icon_plugins_size = {19, 18},
|
||||
|
||||
--micro frames
|
||||
micro_frames = {
|
||||
color = {0.525490, 0.525490, 0.525490, 1},
|
||||
font = "FORCED SQUARE",
|
||||
size = 11,
|
||||
textymod = 1,
|
||||
},
|
||||
|
||||
-- the four anchors (for when the toolbar is on the top side)
|
||||
icon_point_anchor = {-35, -0.5},
|
||||
left_corner_anchor = {-107, 0},
|
||||
right_corner_anchor = {96, 0},
|
||||
|
||||
-- the four anchors (for when the toolbar is on the bottom side)
|
||||
icon_point_anchor_bottom = {-37, 12},
|
||||
left_corner_anchor_bottom = {-107, 0},
|
||||
right_corner_anchor_bottom = {96, 0},
|
||||
|
||||
--[[ callback function execute after all changes on the window, first argument is this skin table, second is the instance where the skin was applied --]]
|
||||
callback = function(self, instance) end,
|
||||
--[[ control_script is a OnUpdate script, it start right after all changes on the window and also after the callback --]]
|
||||
--[[ control_script_on_start run before the control_script, use it to reset values if needed --]]
|
||||
control_script_on_start = nil,
|
||||
control_script = nil,
|
||||
|
||||
--instance overwrites
|
||||
--[[ when a skin is selected, all customized properties of the window is reseted and then the overwrites are applied]]
|
||||
--[[ for the complete cprop list see the file classe_instancia_include.lua]]
|
||||
|
||||
icon_on_top = true,
|
||||
icon_ignore_alpha = true,
|
||||
icon_titletext_position = {2, 5},
|
||||
|
||||
instance_cprops = {
|
||||
menu_icons_alpha = 0.92,
|
||||
["show_statusbar"] = false,
|
||||
["color"] = {1,1,1,1},
|
||||
["menu_anchor"] = {17, 2, ["side"] = 2},
|
||||
["bg_r"] = 0.517647058823529,
|
||||
["color_buttons"] = {1,1,1,1},
|
||||
["bars_sort_direction"] = 1,
|
||||
["instance_button_anchor"] = {-27,1},
|
||||
["row_info"] = {
|
||||
["textR_outline"] = false,
|
||||
["textL_outline"] = false,
|
||||
["use_spec_icons"] = true,
|
||||
["textL_enable_custom_text"] = false,
|
||||
["icon_file"] = "Interface\\AddOns\\Details\\images\\spec_icons_normal",
|
||||
["texture_background_file"] = "Interface\\AddOns\\Details\\images\\BantoBar",
|
||||
["start_after_icon"] = true,
|
||||
["texture_highlight"] = "Interface\\FriendsFrame\\UI-FriendsList-Highlight",
|
||||
["textR_enable_custom_text"] = false,
|
||||
["textR_custom_text"] = "{data1} ({data2}, {data3}%)",
|
||||
["percent_type"] = 1,
|
||||
["fixed_text_color"] = {0.905882352941177,0.905882352941177,0.905882352941177,1},
|
||||
["space"] = {
|
||||
["right"] = -3,
|
||||
["right_noborder"] = -3,
|
||||
["left"] = 1,
|
||||
["left_noborder"] = 1,
|
||||
["between"] = 1,
|
||||
},
|
||||
["texture"] = "DGround",
|
||||
["texture_background_class_color"] = false,
|
||||
["fixed_texture_background_color"] = {0,0,0,0.295484036207199},
|
||||
["font_face_file"] = "Fonts\\ARIALN.TTF",
|
||||
["alpha"] = 1,
|
||||
["textR_class_colors"] = false,
|
||||
["models"] = {
|
||||
["upper_model"] = "Spells\\AcidBreath_SuperGreen.M2",
|
||||
["lower_model"] = "World\\EXPANSION02\\DOODADS\\Coldarra\\COLDARRALOCUS.m2",
|
||||
["upper_alpha"] = 0.5,
|
||||
["lower_enabled"] = false,
|
||||
["lower_alpha"] = 0.1,
|
||||
["upper_enabled"] = false,
|
||||
},
|
||||
["backdrop"] = {
|
||||
["enabled"] = false,
|
||||
["size"] = 5,
|
||||
["color"] = {0, 0, 0, 1},
|
||||
["texture"] = "Details BarBorder 1",
|
||||
},
|
||||
["texture_background"] = "BantoBar",
|
||||
["textL_custom_text"] = "{data1}. {data3}{data2}",
|
||||
["no_icon"] = false,
|
||||
["font_size"] = 16,
|
||||
["height"] = 21,
|
||||
["textL_class_colors"] = false,
|
||||
["font_face"] = "FORCED SQUARE",
|
||||
["texture_class_colors"] = true,
|
||||
["texture_file"] = "Interface\\AddOns\\Details\\images\\bar_background",
|
||||
["textL_show_number"] = true,
|
||||
["fixed_texture_color"] = {0.862745098039216,0.862745098039216,0.862745098039216,1},
|
||||
},
|
||||
["bars_grow_direction"] = 1,
|
||||
["menu_alpha"] = {
|
||||
["enabled"] = false,
|
||||
["onleave"] = 1,
|
||||
["ignorebars"] = false,
|
||||
["iconstoo"] = true,
|
||||
["onenter"] = 1,
|
||||
},
|
||||
["total_bar"] = {
|
||||
["enabled"] = false,
|
||||
["only_in_group"] = true,
|
||||
["icon"] = "Interface\\ICONS\\INV_Sigil_Thorim",
|
||||
["color"] = {1,1,1},
|
||||
},
|
||||
["plugins_grow_direction"] = 1,
|
||||
["strata"] = "LOW",
|
||||
["show_sidebars"] = true,
|
||||
["show_sidebars_need_resize_by"] = 1,
|
||||
["hide_in_combat_alpha"] = 0,
|
||||
["menu_icons"] = {true, true, true, true, true, false, ["space"] = -1, ["shadow"] = true},
|
||||
["desaturated_menu"] = false,
|
||||
["auto_hide_menu"] = {
|
||||
["left"] = false,
|
||||
["right"] = false,
|
||||
},
|
||||
["window_scale"] = 1.0,
|
||||
["grab_on_top"] = false,
|
||||
["menu_anchor_down"] = {16, -2},
|
||||
["statusbar_info"] = {
|
||||
["alpha"] = 1,
|
||||
["overlay"] = {1,1,1},
|
||||
},
|
||||
["hide_icon"] = true,
|
||||
["micro_displays_side"] = 2,
|
||||
["bg_alpha"] = 1,
|
||||
["auto_current"] = true,
|
||||
["toolbar_side"] = 1,
|
||||
["bg_g"] = 0.517647058823529,
|
||||
["backdrop_texture"] = "Details Ground",
|
||||
["hide_in_combat"] = false,
|
||||
["skin"] = "ElvUI Style II",
|
||||
["menu_icons_size"] = 0.850000023841858,
|
||||
["wallpaper"] = {
|
||||
["enabled"] = true,
|
||||
["width"] = 265.999979475717,
|
||||
["texcoord"] = {0.0480000019073486,0.298000011444092,0.630999984741211,0.755999984741211},
|
||||
["overlay"] = {0.999997794628143,0.999997794628143,0.999997794628143,0.799998223781586},
|
||||
["anchor"] = "all",
|
||||
["height"] = 226.000007591173,
|
||||
["alpha"] = 0.800000071525574,
|
||||
["texture"] = "Interface\\AddOns\\Details\\images\\skins\\elvui",
|
||||
},
|
||||
["stretch_button_side"] = 1,
|
||||
["attribute_text"] = {
|
||||
["enabled"] = true,
|
||||
["shadow"] = true,
|
||||
["side"] = 1,
|
||||
["enable_custom_text"] = false,
|
||||
["custom_text"] = "{name}",
|
||||
["text_face"] = "FORCED SQUARE",
|
||||
["anchor"] = {-18, 5},
|
||||
["text_color"] = {1,1,1,0.7},
|
||||
["text_size"] = 12,
|
||||
},
|
||||
["bg_b"] = 0.517647058823529,
|
||||
},
|
||||
|
||||
skin_options = {
|
||||
{spacement = true, type = "button", name = Loc ["STRING_OPTIONS_SKIN_ELVUI_BUTTON1"], func = align_right_chat, desc = Loc ["STRING_OPTIONS_SKIN_ELVUI_BUTTON1_DESC"]},
|
||||
{type = "button", name = Loc ["STRING_OPTIONS_SKIN_ELVUI_BUTTON2"], func = set_tooltip_elvui1, desc = Loc ["STRING_OPTIONS_SKIN_ELVUI_BUTTON2_DESC"]},
|
||||
{type = "button", name = Loc ["STRING_OPTIONS_SKIN_ELVUI_BUTTON3"], func = set_tooltip_elvui2, desc = Loc ["STRING_OPTIONS_SKIN_ELVUI_BUTTON3_DESC"]},
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
_detalhes:InstallSkin ("Safe Skin Legion Beta", {
|
||||
file = [[Interface\AddOns\Details\images\skins\classic_skin_v1.blp]],
|
||||
author = "Details!",
|
||||
@@ -2806,3 +2992,5 @@ local addonName, Details222 = ...
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 3.0 MiB |
Reference in New Issue
Block a user