local detailsFramework = DetailsFramework if (not detailsFramework or not DetailsFrameworkCanLoad) then return end ---@class df_icongeneric : table, df_icon ---@field expirationTime number ---@field CooldownBrightnessTexture texture ---@field CooldownTexture texture ---@field CountdownText fontstring ---@field CooldownEdge texture ---@field options table ---@field NextIcon number ---@field IconPool table table which store the icons created for this iconrow ---@class df_iconrow_generic_options : table ---@field icon_width number? @The width of the icon. ---@field icon_height number? @The height of the icon. ---@field texcoord table? @The texture coordinates of the icon. ---@field show_text boolean? @Whether to show text on the icon. ---@field text_color table? @The color of the text. ---@field text_size number? @The size of the text. ---@field text_font string? @The font of the text. ---@field text_outline string? @The outline style of the text. ---@field text_anchor df_anchor? @The anchor point of the text. ---@field text_alpha_by_percent boolean? @Whether to change the alpha of the text by the percentage of the cooldown. ---@field desc_text boolean? @Whether to show description text. ---@field desc_text_color table? @The color of the description text. ---@field desc_text_size number? @The size of the description text. ---@field desc_text_font string? @The font of the description text. ---@field desc_text_outline string? @The outline style of the description text. ---@field desc_text_anchor string? @The anchor point of the description text. ---@field desc_text_rel_anchor string? @The relative anchor point of the description text. ---@field desc_text_x_offset number? @The x offset of the description text. ---@field desc_text_y_offset number? @The y offset of the description text. ---@field stack_text boolean? @Whether to show stack text. ---@field stack_text_color table? @The color of the stack text. ---@field stack_text_size number? @The size of the stack text. ---@field stack_text_font string? @The font of the stack text. ---@field stack_text_outline string? @The outline style of the stack text. ---@field stack_text_anchor string? @The anchor point of the stack text. ---@field stack_text_rel_anchor string? @The relative anchor point of the stack text. ---@field stack_text_x_offset number? @The x offset of the stack text. ---@field stack_text_y_offset number? @The y offset of the stack text. ---@field left_padding number? @The distance between the right and left sides. ---@field top_padding number? @The distance between the top and bottom sides. ---@field icon_padding number? @The distance between each icon. ---@field backdrop table? @The backdrop options. ---@field backdrop_color table? @The color of the backdrop. ---@field backdrop_border_color table? @The color of the backdrop border. ---@field anchor table? @The anchor options. ---@field grow_direction number? @The direction in which the icons grow. ---@field center_alignment boolean? @Whether to align the icons in the center. ---@field surpress_blizzard_cd_timer boolean? @Whether to suppress the Blizzard cooldown timer. ---@field surpress_tulla_omni_cc boolean? @Whether to suppress the Tulla OmniCC cooldown count. ---@field on_tick_cooldown_update boolean? @Whether to update cooldowns on every tick. ---@field cooldown_max_brightness number? @The maximum brightness of the cooldown, it is adjusted by the percent. ---@field decimal_timer boolean? @Whether to display the timer in decimal format. ---@field show_cooldown boolean? @Whether to show blizzard cooldown animation. ---@field cooldown_reverse boolean? @Whether to reverse the cooldown animation. ---@field cooldown_swipe_enabled boolean? @Whether to enable the cooldown swipe animation. ---@field cooldown_edge_texture string? @The texture for the cooldown edge. ---@field show_horizontal_swipe boolean? @Whether to show the horizontal swipe animation. ---@field swipe_alpha number? @The alpha value for the swipe animation. ---@field swipe_brightness number? @The brightness value for the swipe animation. ---@field swipe_progressive_color boolean? @Whether to use progressive color for the swipe animation. Start on Green and goes to Red, follows percent amount. ---@field swipe_color table? @When the color isn't progressive, this is the color of the swipe ---@field swipe_color_start number[]? @Whether to use yellow color for the swipe animation. ---@field swipe_color_end number[]? @Whether to use red color for the swipe animation. ---@field remove_on_finish boolean? @Whether to remove the icon when the cooldown finishes. Only usable if the icon has a identifier (from setting specific). ---@field first_icon_use_anchor boolean? @The anchor point for the first ico will use the anchor set in options.anchor. ---@class df_iconrow_generic : df_iconrow ---@field SetCooldown fun(self:df_iconrow_generic, iconFrame:df_icongeneric) ---@field OnIconTick fun(iconFrame:df_icongeneric) ---@field FormatCooldownTime fun(thisTime:number) ---@field FormatCooldownTimeDecimal fun(formattedTime:number) ---@field GetIconGrowDirection fun(self:df_iconrow_generic):number ---@field OnOptionChanged fun(self:df_iconrow_generic, optionName:string) ---@field CreateIcon fun(self:df_iconrow_generic, iconName:string) ---@field GetIcon fun(self:df_iconrow_generic) ---@field SetStacks fun(self:df_iconrow_generic, iconFrame:df_icongeneric, bIsShown:boolean, stacksAmount:number?) ---@field AddSpecificIcon fun(self:df_iconrow_generic, identifierKey:any, spellId:number, borderColor:table, startTime:number, duration:number, forceTexture:string, descText:string, count:number, debuffType:string, caster:string, canStealOrPurge:boolean, spellName:string, isBuff:boolean, modRate:number, iconSettings:table) ---@field AddSpecificIconWithTemplate fun(self:df_iconrow_generic, iconTemplateTable:table) ---@field IsIconShown fun(self:df_iconrow_generic, identifierKey:any) ---@field SetIcon fun(self:df_iconrow_generic, spellId:number, borderColor:table, startTime:number, duration:number, iconTexture:string, descText:string, count:number, debuffType:string, caster:string, canStealOrPurge:boolean, spellName:string, isBuff:boolean, modRate:number, iconSettings:table, expirationTime:number?) ---@field RemoveSpecificIcon fun(self:df_iconrow_generic, identifierKey:any) ---@field ClearIcons fun(self:df_iconrow_generic, resetBuffs:boolean?, resetDebuffs:boolean?) ---@field AlignAuraIcons fun(self:df_iconrow_generic) ---@field SetAuraWithIconTemplate fun(self:df_iconrow_generic, auraInfo:aurainfo, iconTemplateTable:table) ---@field IconPool table ---@field NextIcon number ---@field AuraCache table ---@field shownAmount number ---@field options table ---@field SetSpecificAuraWithIconTemplate fun(self:df_iconrow_generic, identifierKey:any, auraInfo:aurainfo, iconTemplateTable:table) local unpack = unpack local CreateFrame = CreateFrame local PixelUtil = PixelUtil local GetTime = GetTime local Clamp = detailsFramework.Math.Clamp local GetSpellInfo = GetSpellInfo or function(spellID) if not spellID then return nil end local si = C_Spell.GetSpellInfo(spellID) if si then return si.name, nil, si.iconID, si.castTime, si.minRange, si.maxRange, si.spellID, si.originalIconID end end local spellIconCache = {} local spellNameCache = {} local emptyTable = {} local white = {1, 1, 1, 1} local sortIconByShownState = function(i1, i2) return i1:IsShown() and not i2:IsShown() end local iconFrameOnHideScript = function(self) if (self.cooldownLooper) then self.cooldownLooper:Cancel() end end local checkPointCallback = function(iconFrame) if (iconFrame.timeRemaining < 3) then end return true end detailsFramework.IconGenericMixin = { ---create a new icon frame ---@param self df_iconrow_generic the parent frame ---@param iconName string the name of the icon frame ---@return df_icongeneric CreateIcon = function(self, iconName) ---@type df_icongeneric local newIcon = CreateFrame("frame", iconName, self) ---@type texture newIcon.Texture = newIcon:CreateTexture(nil, "artwork", nil, 1) PixelUtil.SetPoint(newIcon.Texture, "topleft", newIcon, "topleft", 1, -1) PixelUtil.SetPoint(newIcon.Texture, "bottomright", newIcon, "bottomright", -1, 1) ---@type texture newIcon.CooldownBrightnessTexture = newIcon:CreateTexture(nil, "artwork", nil, 2) newIcon.CooldownBrightnessTexture:SetBlendMode("ADD") PixelUtil.SetPoint(newIcon.CooldownBrightnessTexture, "topleft", newIcon.Texture, "topleft", 0, 0) PixelUtil.SetPoint(newIcon.CooldownBrightnessTexture, "bottomright", newIcon.Texture, "bottomright", 0, 0) ---@type texture newIcon.Border = newIcon:CreateTexture(nil, "background") newIcon.Border:SetAllPoints() newIcon.Border:SetColorTexture(0, 0, 0) ---@type fontstring newIcon.StackText = newIcon:CreateFontString(nil, "overlay", "GameFontNormal") newIcon.StackText:SetPoint("bottomright", newIcon, "bottomright", 0, 0) newIcon.StackText:Hide() newIcon.StackTextShadow = newIcon:CreateFontString(nil, "artwork", "GameFontNormal") newIcon.StackTextShadow:SetPoint("center", newIcon.StackText, "center", 0, 0) newIcon.StackTextShadow:SetTextColor(0, 0, 0) newIcon.StackTextShadow:Hide() ---@type fontstring newIcon.Desc = newIcon:CreateFontString(nil, "overlay", "GameFontNormal") newIcon.Desc:SetPoint("bottom", newIcon, "top", 0, 2) newIcon.Desc:Hide() --create a overlay texture which will indicate the cooldown time newIcon.CooldownTexture = newIcon:CreateTexture(self:GetName() .. "CooldownTexture", "overlay", nil, 6) newIcon.CooldownTexture:SetTexture("Interface\\BUTTONS\\GreyscaleRamp64", "CLAMP", "CLAMP", "TRILINEAR") newIcon.CooldownTexture:SetHeight(2) newIcon.CooldownTexture:SetPoint("bottomleft", newIcon.Texture, "bottomleft", 0, 0) newIcon.CooldownTexture:SetPoint("bottomright", newIcon.Texture, "bottomright", 0, 0) newIcon.CooldownTexture:Hide() newIcon.CooldownEdge = newIcon:CreateTexture(self:GetName() .. "CooldownEdge", "overlay", nil, 7) newIcon.CountdownText = newIcon:CreateFontString(self:GetName() .. "CooldownText", "overlay", "GameFontNormal") newIcon.CountdownText:SetPoint("center", newIcon, "center", 0, 0) newIcon.CountdownText:Hide() newIcon.stacks = 0 newIcon:SetScript("OnHide", iconFrameOnHideScript) local cooldownFrame = CreateFrame("cooldown", "$parentCooldownFrame", newIcon, "CooldownFrameTemplate, BackdropTemplate") cooldownFrame:SetAllPoints() cooldownFrame:EnableMouse(false) cooldownFrame:SetFrameLevel(newIcon:GetFrameLevel()+1) cooldownFrame.CountdownText = ({cooldownFrame:GetRegions()})[1] newIcon.Cooldown = cooldownFrame return newIcon end, ---get an icon frame from the pool ---@param self df_iconrow_generic the parent frame ---@return df_icongeneric GetIcon = function(self) ---@type df_icongeneric local iconFrame = self.IconPool[self.NextIcon] if (not iconFrame) then ---@type df_icongeneric iconFrame = self:CreateIcon("$parentIcon" .. self.NextIcon) iconFrame.parentIconRow = self iconFrame.Desc:Hide() iconFrame.Texture:SetTexCoord(0.1, 0.9, 0.1, 0.9) iconFrame.Border:ClearAllPoints() iconFrame.Border:SetPoint("topleft", iconFrame, "topleft", -1, 1) iconFrame.Border:SetPoint("bottomright", iconFrame, "bottomright", 1, -1) iconFrame.Border:SetTexture(130759) iconFrame.Border:SetTexCoord(0.296875, 0.5703125, 0, 0.515625) iconFrame.Border:SetDrawLayer("overlay", 7) iconFrame.StackText:SetTextColor(detailsFramework:ParseColors(self.options.stack_text_color)) detailsFramework:SetFontSize(iconFrame.StackText, self.options.stack_text_size) detailsFramework:SetFontFace(iconFrame.StackText, self.options.stack_text_font) detailsFramework:SetFontOutline(iconFrame.StackText, self.options.stack_text_outline) detailsFramework:SetFontFace(iconFrame.StackTextShadow, self.options.stack_text_font) detailsFramework:SetFontSize(iconFrame.StackTextShadow, self.options.stack_text_size+1) iconFrame.StackText:ClearAllPoints() iconFrame.StackText:SetPoint(self.options.stack_text_anchor or "center", iconFrame, self.options.stack_text_rel_anchor or "center", self.options.stack_text_x_offset or 0, self.options.stack_text_y_offset or 0) self.IconPool[self.NextIcon] = iconFrame iconFrame = iconFrame end self.NextIcon = self.NextIcon + 1 return iconFrame end, SetStacks = function(self, iconFrame, bIsShown, stacksAmount) if (bIsShown) then iconFrame.StackText:Show() iconFrame.StackTextShadow:Show() iconFrame.StackText:SetText(stacksAmount) iconFrame.StackTextShadow:SetText(stacksAmount) iconFrame.stacks = stacksAmount else iconFrame.StackText:Hide() iconFrame.StackTextShadow:Hide() iconFrame.stacks = 0 end end, ---adds only if not existing already in the cache ---@param self df_iconrow_generic the parent frame AddSpecificIcon = function(self, identifierKey, spellId, borderColor, startTime, duration, forceTexture, descText, count, debuffType, caster, canStealOrPurge, spellName, isBuff, modRate, iconSettings) if (not identifierKey or identifierKey == "") then return end if (not self.AuraCache[identifierKey]) then ---@type df_icongeneric local icon = self:SetIcon(spellId, borderColor, startTime, duration, forceTexture, descText, count, debuffType, caster, canStealOrPurge, spellName, isBuff or false, modRate, iconSettings) icon.identifierKey = identifierKey self.AuraCache[identifierKey] = true end end, AddSpecificIconWithTemplate = function(self, iconTemplateTable) self:AddSpecificIcon(iconTemplateTable.id, iconTemplateTable.id, nil, iconTemplateTable.startTime, iconTemplateTable.duration, nil, nil, iconTemplateTable.count, nil, nil, nil, nil, nil, nil, iconTemplateTable) end, IsIconShown = function(self, identifierKey) if (not identifierKey or identifierKey == "") then return end if (self.AuraCache[identifierKey]) then return true end end, SetSpecificAuraWithIconTemplate = function(self, identifierKey, auraInfo, iconTemplateTable) if (not identifierKey or identifierKey == "") then return end if (not self.AuraCache[identifierKey]) then ---@type df_icongeneric local iconFrame = self:SetAuraWithIconTemplate(auraInfo, iconTemplateTable) iconFrame.identifierKey = identifierKey self.AuraCache[identifierKey] = true end end, ---set an icon frame with a template ---@param self df_iconrow_generic the parent frame ---@param auraInfo aurainfo ---@param iconTemplateTable df_icontemplate SetAuraWithIconTemplate = function(self, auraInfo, iconTemplateTable) local startTime = auraInfo.expirationTime - auraInfo.duration ---@type df_icongeneric return self:SetIcon(auraInfo.spellId, nil, startTime, auraInfo.duration, auraInfo.icon, nil, auraInfo.applications, auraInfo.dispelName, auraInfo.sourceUnit, auraInfo.isStealable, auraInfo.name, auraInfo.isHelpful, auraInfo.timeMod, iconTemplateTable, auraInfo.expirationTime) end, ---set an icon frame with a template ---@param self df_iconrow_generic the parent frame ---@return df_icongeneric? SetIcon = function(self, spellId, borderColor, startTime, duration, iconTexture, descText, count, debuffType, caster, canStealOrPurge, spellName, isBuff, modRate, iconSettings, expirationTime) local actualSpellName, spellIcon = spellNameCache[spellId], spellIconCache[spellId] iconSettings = iconSettings or emptyTable if (not actualSpellName) then actualSpellName, _, spellIcon = GetSpellInfo(spellId) spellIconCache[spellId] = spellIcon spellNameCache[spellId] = actualSpellName end if iconTexture then spellIcon = iconTexture end if (not spellIcon) then if (iconSettings.texture) then spellIcon = iconSettings.texture end end if (iconSettings.overrideTexture) then spellIcon = iconSettings.overrideTexture end if (spellIcon) then spellName = spellName or actualSpellName or "unknown_aura" modRate = modRate or 1 ---@type df_icongeneric local iconFrame = self:GetIcon() self.shownAmount = self.NextIcon - 1 iconFrame.expirationTime = expirationTime local width = iconSettings.width or self.options.icon_width local height = iconSettings.height or self.options.icon_height or width --adjust the width and height by scale local scale = iconSettings.scale or 1 width = width * scale height = height * scale PixelUtil.SetSize(iconFrame, width, height) --set the texture points to be all points minus one iconFrame.Texture:ClearAllPoints() PixelUtil.SetPoint(iconFrame.Texture, "topleft", iconFrame, "topleft", 1, -1) PixelUtil.SetPoint(iconFrame.Texture, "bottomright", iconFrame, "bottomright", -1, 1) iconFrame.textureWidth = math.max(iconFrame.Texture:GetWidth(), width) iconFrame.textureHeight = math.max(iconFrame.Texture:GetHeight(), height) --for some reason, GetHeight() was returning 0 on the first call --cache size iconFrame.width = width iconFrame.height = height iconFrame:Show() if (iconFrame.Texture.texture ~= spellIcon or (iconSettings.coords and iconSettings.coords ~= iconFrame.currentCoords)) then iconFrame.Texture:SetTexture(spellIcon, "CLAMP", "CLAMP", iconSettings.textureFilter or "LINEAR") --"TRILINEAR" if (iconSettings.coords) then iconFrame.Texture:SetTexCoord(unpack(iconSettings.coords)) iconFrame.currentCoords = iconSettings.coords elseif (self.options.texcoord ~= iconFrame.currentCoords) then iconFrame.Texture:SetTexCoord(unpack(self.options.texcoord)) iconFrame.currentCoords = self.options.texcoord else iconFrame.Texture:SetTexCoord(0, 1, 0, 1) end if (iconSettings.points) then iconFrame.Texture:ClearAllPoints() for i = 1, #iconSettings.points do local point = iconSettings.points[i] iconFrame.Texture:SetPoint(point[1], iconFrame, point[2], point[3], point[4]) end iconFrame.Texture:SetSize(width, height) end iconFrame.Texture.texture = spellIcon elseif (not iconSettings.coords) then if (self.options.texcoord ~= iconFrame.currentCoords) then iconFrame.Texture:SetTexCoord(unpack(self.options.texcoord)) iconFrame.currentCoords = self.options.texcoord else iconFrame.Texture:SetTexCoord(0, 1, 0, 1) end end iconFrame:SetIgnoreParentAlpha(false) if (iconSettings.color) then local r, g, b, a = detailsFramework:ParseColors(iconSettings.color) iconFrame.Texture:SetVertexColor(r, g, b, a) --ignore the param alpha has the settings might have an alpha for it iconFrame:SetIgnoreParentAlpha(true) else iconFrame.Texture:SetVertexColor(1, 1, 1, 1) end if (borderColor) then iconFrame.Border:Show() iconFrame.Border:SetVertexColor(unpack(borderColor)) else if (iconSettings.borderColor) then iconFrame.Border:Show() iconFrame.Border:SetTexture(iconSettings.borderTexture or 130759) iconFrame.Border:SetVertexColor(unpack(iconSettings.borderColor or white)) else iconFrame.Border:Hide() end end if (count and count > 1 and self.options.stack_text) then self:SetStacks(iconFrame, true, count) else self:SetStacks(iconFrame, false) end iconFrame.stacks = count or 0 if (iconSettings.alpha) then iconFrame.Texture:SetAlpha(iconSettings.alpha) else iconFrame.Texture:SetAlpha(1) end --iconFrame.Texture:SetBlendMode("ADD") iconFrame.CooldownBrightnessTexture:SetTexture(iconFrame.Texture:GetTexture()) do local left, top, c, bottom, right = iconFrame.Texture:GetTexCoord() iconFrame.CooldownBrightnessTexture:SetTexCoord(left, right, top, bottom) local coords = iconFrame.CooldownBrightnessTexture.cords if (coords) then coords[1] = left coords[2] = right coords[3] = top coords[4] = bottom else iconFrame.CooldownBrightnessTexture.cords = {left, right, top, bottom} end iconFrame.CooldownBrightnessTexture.top = top iconFrame.CooldownBrightnessTexture.bottom = bottom PixelUtil.SetPoint(iconFrame.CooldownBrightnessTexture, "bottomright", iconFrame.Texture, "bottomright", 0, 0) end --make information available iconFrame.spellId = spellId iconFrame.startTime = startTime iconFrame.duration = duration iconFrame.endTime = (startTime and duration and startTime + duration) or 0 iconFrame.count = count iconFrame.debuffType = debuffType iconFrame.caster = caster iconFrame.canStealOrPurge = canStealOrPurge iconFrame.isBuff = isBuff iconFrame.spellName = spellName iconFrame.modRate = modRate iconFrame.options = self.options if (startTime and duration and duration > 0) then local endTime = startTime + duration local now = GetTime() if (endTime > now) then iconFrame.CooldownTexture:Show() iconFrame.CooldownBrightnessTexture:Show() iconFrame.CooldownTexture:SetHeight(1) self:SetCooldown(iconFrame) end else iconFrame.CooldownBrightnessTexture:Hide() iconFrame.CooldownTexture:Hide() end iconFrame.identifierKey = nil -- only used for "specific" add/remove --add the spell into the cache self.AuraCache[spellId or -1] = true self.AuraCache[spellName] = true self.AuraCache.canStealOrPurge = self.AuraCache.canStealOrPurge or canStealOrPurge self.AuraCache.hasEnrage = self.AuraCache.hasEnrage or debuffType == "" --yes, enrages are empty-string... --show the frame self:Show() return iconFrame end end, ---@param self df_iconrow_generic the parent frame ---@param iconFrame df_icongeneric SetCooldown = function(self, iconFrame) if (iconFrame.cooldownLooper) then iconFrame.cooldownLooper:Cancel() end local options = iconFrame.options --iconFrame:SetScale(3) --debug if (options.show_horizontal_swipe) then iconFrame.CooldownEdge:Show() iconFrame.CooldownTexture:Show() iconFrame.CooldownBrightnessTexture:Show() iconFrame.CooldownEdge.texture = nil iconFrame.CooldownEdge:SetAlpha(0.834) PixelUtil.SetSize(iconFrame.CooldownEdge, iconFrame.CooldownTexture:GetWidth(), 2) PixelUtil.SetPoint(iconFrame.CooldownEdge, "topleft", iconFrame.CooldownTexture, "topleft", 0, 0) PixelUtil.SetPoint(iconFrame.CooldownEdge, "topright", iconFrame.CooldownTexture, "topright", 0, 0) PixelUtil.SetHeight(iconFrame.CooldownEdge, 8) iconFrame.CooldownEdge:SetTexture(options.swipe_white, "CLAMP", "CLAMP", "TRILINEAR") iconFrame.CooldownEdge.texture = options.swipe_white local swipe_brightness = options.swipe_brightness iconFrame.CooldownBrightnessTexture:SetAlpha(swipe_brightness) local swipe_darkness = options.swipe_alpha iconFrame.CooldownTexture:SetAlpha(swipe_darkness) iconFrame.CooldownTexture:SetVertexColor(unpack(options.swipe_color)) else iconFrame.CooldownEdge:Hide() iconFrame.CooldownBrightnessTexture:Hide() iconFrame.CooldownTexture:Hide() end if (options.show_text) then detailsFramework:SetFontColor(iconFrame.CountdownText, self.options.text_color) detailsFramework:SetFontSize(iconFrame.CountdownText, self.options.text_size) detailsFramework:SetFontFace(iconFrame.CountdownText, self.options.text_font) detailsFramework:SetFontOutline(iconFrame.CountdownText, self.options.text_outline) detailsFramework:SetAnchor(iconFrame.CountdownText, self.options.text_anchor, iconFrame) iconFrame.CountdownText:Show() iconFrame.CountdownText:SetAlpha(1) else iconFrame.CountdownText:Hide() end if (options.show_cooldown) then iconFrame.Cooldown:Show() iconFrame.Cooldown:SetReverse(options.cooldown_reverse) iconFrame.Cooldown:SetDrawSwipe(options.cooldown_swipe_enabled) iconFrame.Cooldown:SetEdgeTexture(options.cooldown_edge_texture) --the yellow edge that follows the cooldown animation iconFrame.Cooldown:SetHideCountdownNumbers(options.surpress_blizzard_cd_timer) iconFrame.Cooldown:SetSwipeTexture([[Interface\Masks\SquareMask]], 0, 0, 0, 0.3) --iconFrame.Cooldown:SetSwipeColor(1, 1, 1, 1) --iconFrame.Cooldown:SetSwipeColor(0, 0, 0, 0.1) --iconFrame.Cooldown:SetDrawEdge(true) --the same shit as above --iconFrame.Cooldown:SetDrawSwipe(true) --iconFrame.Cooldown:SetDrawBling(true) --edge of the animation, a thin horizontal texture --iconFrame.Cooldown:SetEdgeScale(4) --edge of the animation, a thin horizontal texture if (not options.surpress_blizzard_cd_timer) then detailsFramework:SetFontColor(iconFrame.Cooldown.CountdownText, self.options.text_color) detailsFramework:SetFontSize(iconFrame.Cooldown.CountdownText, self.options.text_size) detailsFramework:SetFontFace(iconFrame.Cooldown.CountdownText, self.options.text_font) detailsFramework:SetFontOutline(iconFrame.Cooldown.CountdownText, self.options.text_outline) end iconFrame.Cooldown.noCooldownCount = options.surpress_tulla_omni_cc CooldownFrame_Set(iconFrame.Cooldown, iconFrame.startTime, iconFrame.duration, true, true, iconFrame.modRate) iconFrame.CooldownBrightnessTexture:Show() else iconFrame.Cooldown:Hide() end self.OnIconTick(iconFrame) local amountOfLoops = math.floor(iconFrame.duration / 0.25) local loopEndCallback = nil if (iconFrame.options.remove_on_finish) then --increase the amount of loops in one, so the last loop will remove the icon --otherwise it might finish amountOfLoops = amountOfLoops + 1 local newLooper = detailsFramework.Schedules.NewLooper(0.25, self.OnIconTick, amountOfLoops, loopEndCallback, checkPointCallback, iconFrame) iconFrame.cooldownLooper = newLooper else local newLooper = detailsFramework.Schedules.NewLooper(0.25, self.OnIconTick, amountOfLoops, loopEndCallback, checkPointCallback, iconFrame) iconFrame.cooldownLooper = newLooper end end, ---@param iconFrame df_icongeneric OnIconTick = function(iconFrame) local now = GetTime() --local percent = (now - iconFrame.startTime) / iconFrame.duration --no mod rate local percent = (((now - iconFrame.startTime) / (iconFrame.modRate or 1)) / (iconFrame.duration / (iconFrame.modRate or 1))) or 0 local options = iconFrame.options percent = Saturate(percent) --percent = abs(percent - 1) iconFrame.timeRemaining = iconFrame.duration - (now - iconFrame.startTime) if (percent >= 1) then --time expired if (options.remove_on_finish) then iconFrame:GetParent():RemoveSpecificIcon(iconFrame.identifierKey) return else percent = 1 end end if (options.show_horizontal_swipe) then local newHeight = math.min(iconFrame.textureHeight * percent, iconFrame.textureHeight) iconFrame.CooldownTexture:SetHeight(newHeight) PixelUtil.SetPoint(iconFrame.CooldownBrightnessTexture, "bottomright", iconFrame.Texture, "bottomright", 0, newHeight) --iconFrame.textureHeight - local left, right, top, bottom = unpack(iconFrame.CooldownBrightnessTexture.cords) local newBottomCord = Lerp(iconFrame.CooldownBrightnessTexture.top, iconFrame.CooldownBrightnessTexture.bottom, abs(percent - 1)) iconFrame.CooldownBrightnessTexture:SetTexCoord(left, right, top, newBottomCord) --local newBrightness = Lerp(Saturate(options.cooldown_max_brightness-0.6), options.cooldown_max_brightness, percent) --iconFrame.CooldownBrightnessTexture:SetAlpha(newBrightness) if (options.swipe_progressive_color) then --interpolate from green to red --percent goes from 0 to 1, where zero is the start of the cooldown and 1 is the end if (options.swipe_color_start and options.swipe_color_end) then --use the first and second color local r1, g1, b1 = unpack(options.swipe_color_start) local r2, g2, b2 = unpack(options.swipe_color_end) local r, g, b = detailsFramework.Math.LerpLinearColor(percent, 1, r1, g1, b1, r2, g2, b2) iconFrame.CooldownEdge:SetVertexColor(r, g, b, 0.834) else --use a solid color iconFrame.CooldownEdge:SetVertexColor(unpack(options.swipe_color)) end --iconFrame.CooldownEdge:SetVertexColor(percent, math.abs(percent-1), 0, 0.834) local alpha = Saturate(0.2 + percent) iconFrame.CooldownEdge:SetAlpha(alpha) else --use a solid color iconFrame.CooldownEdge:SetVertexColor(unpack(options.swipe_color)) end end if (options.show_cooldown) then if (options.cooldown_max_brightness) then iconFrame.CooldownBrightnessTexture:SetAlpha(Lerp(0, options.cooldown_max_brightness, percent)) local swipeAlpha = Saturate(Lerp(0, 1, percent)) local exponentialCurve = 0.1 * math.exp(3.5 * swipeAlpha) exponentialCurve = Saturate(exponentialCurve) iconFrame.Cooldown:SetSwipeColor(0, 0, 0, exponentialCurve) end end --show the countdown text if (options.show_text) then iconFrame.CountdownText:SetText(iconFrame.parentIconRow.FormatCooldownTime((iconFrame.duration - (now - iconFrame.startTime)) / (iconFrame.modRate or 1))) if (options.text_alpha_by_percent) then iconFrame.CountdownText:SetAlpha(percent) end end end, FormatCooldownTime = function(thisTime) if (thisTime >= 3600) then thisTime = math.floor(thisTime / 3600) .. "h" elseif (thisTime >= 60) then thisTime = math.floor(thisTime / 60) .. "m" else thisTime = math.floor(thisTime) end return thisTime end, FormatCooldownTimeDecimal = function(formattedTime) if formattedTime < 10 then return ("%.1f"):format(formattedTime) elseif formattedTime < 60 then return ("%d"):format(formattedTime) elseif formattedTime < 3600 then return ("%d:%02d"):format(formattedTime/60%60, formattedTime%60) elseif formattedTime < 86400 then return ("%dh %02dm"):format(formattedTime/(3600), formattedTime/60%60) else return ("%dd %02dh"):format(formattedTime/86400, (formattedTime/3600) - (math.floor(formattedTime/86400) * 24)) end end, ---@param self df_iconrow_generic the parent frame ---@param identifierKey any RemoveSpecificIcon = function(self, identifierKey) if (not identifierKey or identifierKey == "") then return end if (not self.AuraCache[identifierKey]) then return end self.AuraCache[identifierKey] = nil local iconPool = self.IconPool --find and hide the icon frame for i = 1, self.NextIcon -1 do local iconFrame = iconPool[i] if (iconFrame.identifierKey and iconFrame.identifierKey == identifierKey) then iconFrame:Hide() iconFrame:ClearAllPoints() iconFrame.identifierKey = nil else self.AuraCache[iconFrame.spellId] = true self.AuraCache[iconFrame.spellName] = true self.AuraCache.canStealOrPurge = self.AuraCache.canStealOrPurge or iconFrame.canStealOrPurge self.AuraCache.hasEnrage = self.AuraCache.hasEnrage or iconFrame.debuffType == "" --yes, enrages are empty-string... end end self:AlignAuraIcons() end, ---@param self df_iconrow_generic the parent frame ClearIcons = function(self, resetBuffs, resetDebuffs) resetBuffs = resetBuffs ~= false resetDebuffs = resetDebuffs ~= false table.wipe(self.AuraCache) local iconPool = self.IconPool for i = 1, self.NextIcon -1 do local iconFrame = iconPool[i] if (iconFrame.isBuff == nil) then iconFrame:Hide() iconFrame:ClearAllPoints() elseif (resetBuffs and iconFrame.isBuff) then iconFrame:Hide() iconFrame:ClearAllPoints() elseif (resetDebuffs and not iconFrame.isBuff) then iconFrame:Hide() iconFrame:ClearAllPoints() else self.AuraCache[iconFrame.spellId] = true self.AuraCache[iconFrame.spellName] = true self.AuraCache.canStealOrPurge = self.AuraCache.canStealOrPurge or iconFrame.canStealOrPurge self.AuraCache.hasEnrage = self.AuraCache.hasEnrage or iconFrame.debuffType == "" --yes, enrages are empty-string... end end self:AlignAuraIcons() end, ---@param self df_iconrow_generic the parent frame AlignAuraIcons = function(self) local iconPool = self.IconPool local iconAmount = #iconPool local countStillShown = 0 if iconAmount == 0 then self:Hide() else table.sort(iconPool, sortIconByShownState) local shownAmount = 0 for i = 1, iconAmount do if iconPool[i]:IsShown() then shownAmount = shownAmount + 1 end end local width = 0 local growDirection = self:GetIconGrowDirection() local nWhichSide = self.options.anchor.side local bIsCenterAligned = detailsFramework.ShouldCenterAlign[nWhichSide] --re-anchor not hidden for i = 1, shownAmount do local bIsFirstIcon = i == 1 local iconFrame = iconPool[i] local anchorTo = bIsFirstIcon and self or self.IconPool[i - 1] local xPadding if (bIsFirstIcon) then xPadding = self.options.left_padding else xPadding = self.options.icon_padding or 1 xPadding = xPadding * (growDirection == 2 and -1 or 1) end countStillShown = countStillShown + (iconFrame:IsShown() and 1 or 0) iconFrame:ClearAllPoints() if (growDirection == 1) then --grow to right if (bIsFirstIcon) then if (self.options.first_icon_use_anchor) then detailsFramework:SetAnchor(iconFrame, self.options.anchor, self) else local attachSide = (bIsCenterAligned and "center") or (nWhichSide and not detailsFramework.SideIsCorner[nWhichSide] and "left") or "bottomleft" PixelUtil.SetPoint(iconFrame, attachSide, anchorTo, attachSide, 0, 0) end else PixelUtil.SetPoint(iconFrame, "left", anchorTo, "right", xPadding, 0) end elseif (growDirection == 2) then --grow to left if (bIsFirstIcon) then if (self.options.first_icon_use_anchor) then detailsFramework:SetAnchor(iconFrame, self.options.anchor, self) else local attachSide = (bIsCenterAligned and "center") or (nWhichSide and not detailsFramework.SideIsCorner[nWhichSide] and "right") or "bottomright" PixelUtil.SetPoint(iconFrame, attachSide, anchorTo, attachSide, 0, 0) end else PixelUtil.SetPoint(iconFrame, "right", anchorTo, "left", xPadding, 0) end end width = width + ((iconFrame.width or iconFrame:GetWidth())) + 1 --* iconFrame:GetScale() removed the getscale as now the scale are applied to the width and height end if (bIsCenterAligned) then self:SetWidth(width) end self.shownAmount = shownAmount end self.NextIcon = countStillShown + 1 if countStillShown > 0 then self:Show() end end, ---@param self df_iconrow_generic the parent frame GetIconGrowDirection = function(self) local side = self.options.anchor.side return detailsFramework.GrowDirectionBySide[side] end, ---@param self df_iconrow_generic the parent frame OnOptionChanged = function(self, optionName) if (self.SetBackdropColor) then self:SetBackdropColor(unpack(self.options.backdrop_color)) self:SetBackdropBorderColor(unpack(self.options.backdrop_border_color)) end end, } ---@type df_iconrow_generic_options local default_iconrow_generic_options = { icon_width = 20, icon_height = 20, texcoord = {.1, .9, .1, .9}, show_text = false, text_color = {1, 1, 1, 1}, text_size = 12, text_font = "Arial Narrow", text_outline = "NONE", text_anchor = {side = 9, x = 0, y = 0}, text_alpha_by_percent = false, desc_text = true, desc_text_color = {1, 1, 1, 1}, desc_text_size = 7, desc_text_font = "Arial Narrow", desc_text_outline = "NONE", desc_text_anchor = "bottom", desc_text_rel_anchor = "top", desc_text_x_offset = 0, desc_text_y_offset = 2, stack_text = true, stack_text_color = {1, 1, 1, 1}, stack_text_size = 10, stack_text_font = "Arial Narrow", stack_text_outline = "NONE", stack_text_anchor = "center", stack_text_rel_anchor = "bottomright", stack_text_x_offset = 0, stack_text_y_offset = 0, left_padding = 1, --distance between right and left top_padding = 1, --distance between top and bottom icon_padding = 1, --distance between each icon backdrop = {}, backdrop_color = {0, 0, 0, 0.5}, backdrop_border_color = {0, 0, 0, 1}, anchor = {side = 6, x = 2, y = 0}, grow_direction = 1, --1 = to right 2 = to left center_alignment = false, --if true if will align the icons with grow_direction and then set the iconRow width to match the length used by all icons show_cooldown = false, surpress_tulla_omni_cc = false, decimal_timer = false, --nop, not in use cooldown_reverse = false, cooldown_swipe_enabled = true, cooldown_edge_texture = "Interface\\Cooldown\\edge", cooldown_max_brightness = 0.7, surpress_blizzard_cd_timer = false, on_tick_cooldown_update = true, --nop, not in use show_horizontal_swipe = true, swipe_progressive_color = true, swipe_alpha = 0.5, swipe_brightness = 0.5, swipe_color = {0, 0, 0}, --this variable is having conflicts because it's in use by other things swipe_color_start = {0, 1, 0}, swipe_color_end = {1, 0, 0}, remove_on_finish = false, first_icon_use_anchor = false, } ---@param parent frame ---@param name string? ---@param options table? ---@return df_iconrow_generic function detailsFramework:CreateIconRowGeneric(parent, name, options) local newIconRowFrame = CreateFrame("frame", name, parent, "BackdropTemplate") newIconRowFrame.IconPool = {} newIconRowFrame.NextIcon = 1 newIconRowFrame.AuraCache = {} newIconRowFrame.shownAmount = 0 detailsFramework:Mixin(newIconRowFrame, detailsFramework.IconGenericMixin) detailsFramework:Mixin(newIconRowFrame, detailsFramework.OptionsFunctions) newIconRowFrame:BuildOptionsTable(default_iconrow_generic_options, options) newIconRowFrame:SetSize(1, 1) newIconRowFrame:SetBackdrop(newIconRowFrame.options.backdrop) newIconRowFrame:SetBackdropColor(unpack(newIconRowFrame.options.backdrop_color)) newIconRowFrame:SetBackdropBorderColor(unpack(newIconRowFrame.options.backdrop_border_color)) return newIconRowFrame end