Back to Home

ESO Lua File v101044

publicallingames/tooltip/abilitytooltips.lua

[◄ back to folders ]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
--Section Generators
function ZO_Tooltip:AddAbilityProgressBar(currentXP, lastRankXP, nextRankXP)
    local bar = self:AcquireStatusBar(self:GetStyle("abilityProgressBar"))
    local maxValue = 1
    local currentValue = 1
    if nextRankXP == lastRankXP or currentXP >= nextRankXP then
        bar:SetMinMax(0, maxValue)
        bar:SetValue(currentValue)
    else
        maxValue = nextRankXP - lastRankXP
        currentValue = currentXP - lastRankXP
        bar:SetMinMax(0, maxValue)
        bar:SetValue(currentValue)
    end
    local narrationText = function()
        local range = maxValue
        local percentage = currentValue / range
        percentage = string.format("%.2f", percentage * 100)
        return zo_strformat(SI_SCREEN_NARRATION_PROGRESS_BAR_PERCENT_FORMATTER, percentage)
    end
end
do
    local TANK_ROLE_ICON = zo_iconFormat("EsoUI/Art/LFG/LFG_tank_down_no_glow_64.dds", 40, 40)
    local HEALER_ROLE_ICON = zo_iconFormat("EsoUI/Art/LFG/LFG_healer_down_no_glow_64.dds", 40, 40)
    local DAMAGE_ROLE_ICON = zo_iconFormat("EsoUI/Art/LFG/LFG_dps_down_no_glow_64.dds", 40, 40)
    local g_roleIconTable = {}
    local UNKNOWN_STAT_VALUE_TEXT = GetString(SI_ABILITY_TOOLTIP_UNKNOWN_VALUE)
    local UNKNOWN_STAT_VALUE_TYPE_TEXT = GetString(SI_ABILITY_TOOLTIP_UNKNOWN_VALUE_TYPE)
    local UNKNOWN_SECONDS_STAT_VALUE_TEXT = GetString(SI_ABILITY_TOOLTIP_UNKNOWN_SECONDS)
    local function GetNextAbilityMechanicFlagIter(abilityId)
        return function(_, lastFlag)
            return GetNextAbilityMechanicFlag(abilityId, lastFlag)
        end
    end
    function ZO_Tooltip:AddAbilityStats(abilityId, overrideActiveRank, overrideCasterUnitTag)
        overrideCasterUnitTag = overrideCasterUnitTag or "player"
        local statsSection = self:AcquireSection(self:GetStyle("abilityStatsSection"))
        --Channel/Cast Time
        local channeled, durationValue = GetAbilityCastInfo(abilityId, overrideActiveRank, overrideCasterUnitTag)
        if channeled ~= nil then
            -- Only include a line if we can determine if it's going to be definitely either channeled or cast
            local castTimePair = statsSection:AcquireStatValuePair(self:GetStyle("statValuePair"))
            if channeled then
                castTimePair:SetStat(GetString(SI_ABILITY_TOOLTIP_CHANNEL_TIME_LABEL), self:GetStyle("statValuePairStat"))
                if durationValue then
                    castTimePair:SetValue(ZO_FormatTimeMilliseconds(durationValue, TIME_FORMAT_STYLE_CHANNEL_TIME, TIME_FORMAT_PRECISION_TENTHS_RELEVANT, TIME_FORMAT_DIRECTION_NONE), self:GetStyle("abilityStatValuePairValue"))
                else
                    castTimePair:SetValue(UNKNOWN_SECONDS_STAT_VALUE_TEXT)
                end
            else
                castTimePair:SetStat(GetString(SI_ABILITY_TOOLTIP_CAST_TIME_LABEL), self:GetStyle("statValuePairStat"))
                if durationValue then
                    castTimePair:SetValue(ZO_FormatTimeMilliseconds(durationValue, TIME_FORMAT_STYLE_CAST_TIME, TIME_FORMAT_PRECISION_TENTHS_RELEVANT, TIME_FORMAT_DIRECTION_NONE), self:GetStyle("abilityStatValuePairValue"))
                else
                    castTimePair:SetValue(UNKNOWN_SECONDS_STAT_VALUE_TEXT)
                end
            end
            statsSection:AddStatValuePair(castTimePair)
        end
        --Target
        local targetDescription = GetAbilityTargetDescription(abilityId, overrideActiveRank, overrideCasterUnitTag)
        if targetDescription and targetDescription ~= "" then
            local targetPair = statsSection:AcquireStatValuePair(self:GetStyle("statValuePair"))
            targetPair:SetStat(GetString(SI_ABILITY_TOOLTIP_TARGET_TYPE_LABEL), self:GetStyle("statValuePairStat"))
            targetPair:SetValue(targetDescription, self:GetStyle("abilityStatValuePairValue"))
            statsSection:AddStatValuePair(targetPair)
        end
        --Range
        local minRangeCM, maxRangeCM = GetAbilityRange(abilityId, overrideActiveRank, overrideCasterUnitTag)
        local maxRangeM = maxRangeCM and FormatFloatRelevantFraction(maxRangeCM / 100) or UNKNOWN_STAT_VALUE_TEXT
        if maxRangeM == UNKNOWN_STAT_VALUE_TEXT or maxRangeCM > 0 then
            local minRangeM = minRangeCM and FormatFloatRelevantFraction(minRangeCM / 100) or UNKNOWN_STAT_VALUE_TEXT
            local rangePair = statsSection:AcquireStatValuePair(self:GetStyle("statValuePair"))
            rangePair:SetStat(GetString(SI_ABILITY_TOOLTIP_RANGE_LABEL), self:GetStyle("statValuePairStat"))
            if minRangeM == UNKNOWN_STAT_VALUE_TEXT or minRangeCM > 0 then
                rangePair:SetValue(zo_strformat(SI_ABILITY_TOOLTIP_MIN_TO_MAX_RANGE, minRangeM, maxRangeM), self:GetStyle("abilityStatValuePairValue"))
            elseif maxRangeM == UNKNOWN_STAT_VALUE_TEXT then
                rangePair:SetValue(GetString(SI_ABILITY_TOOLTIP_UNKNOWN_METERS), self:GetStyle("abilityStatValuePairValue")) 
            else
                rangePair:SetValue(zo_strformat(SI_ABILITY_TOOLTIP_RANGE, maxRangeM), self:GetStyle("abilityStatValuePairValue"))
            end
            statsSection:AddStatValuePair(rangePair)
        end
        --Radius/Distance
        local radiusCM = GetAbilityRadius(abilityId, overrideActiveRank, overrideCasterUnitTag)
        local radiusM = radiusCM and FormatFloatRelevantFraction(radiusCM / 100) or UNKNOWN_STAT_VALUE_TEXT
        if radiusM == UNKNOWN_STAT_VALUE_TEXT or radiusCM > 0 then
            local angleDistanceCM = GetAbilityAngleDistance(abilityId)
            -- Angle distance is the distance to the left and right of the caster, not total distance. So we're multiplying by 2 to accurately reflect that in the UI.
            local areaWidthM = angleDistanceCM and FormatFloatRelevantFraction(angleDistanceCM * 2 / 100) or UNKNOWN_STAT_VALUE_TEXT
            local showArea = areaWidthM == UNKNOWN_STAT_VALUE_TEXT or angleDistanceCM > 0
            local radiusDistancePair = statsSection:AcquireStatValuePair(self:GetStyle("statValuePair"))
            local statLabel = showArea and GetString(SI_ABILITY_TOOLTIP_AREA_LABEL) or GetString(SI_ABILITY_TOOLTIP_RADIUS_LABEL)
            radiusDistancePair:SetStat(statLabel, self:GetStyle("statValuePairStat"))
            if showArea then
                radiusDistancePair:SetValue(zo_strformat(SI_ABILITY_TOOLTIP_AOE_DIMENSIONS, radiusM, areaWidthM), self:GetStyle("abilityStatValuePairValue"))
            elseif radiusM == UNKNOWN_STAT_VALUE_TEXT then
                radiusDistancePair:SetValue(GetString(SI_ABILITY_TOOLTIP_UNKNOWN_METERS), self:GetStyle("abilityStatValuePairValue")) 
            else
                radiusDistancePair:SetValue(zo_strformat(SI_ABILITY_TOOLTIP_RADIUS, radiusM), self:GetStyle("abilityStatValuePairValue")) 
            end
            statsSection:AddStatValuePair(radiusDistancePair)
        end
        --Duration
        local durationValue = nil
        local isAbilityDurationToggled = IsAbilityDurationToggled(abilityId, overrideCasterUnitTag)
        if isAbilityDurationToggled == nil then
            durationValue = UNKNOWN_STAT_VALUE_TYPE_TEXT
        elseif isAbilityDurationToggled then
            durationValue = GetString(SI_ABILITY_TOOLTIP_TOGGLE_DURATION)
        else
            local durationMS = GetAbilityDuration(abilityId, overrideActiveRank, overrideCasterUnitTag)
            if durationMS == nil then
                durationValue = UNKNOWN_SECONDS_STAT_VALUE_TEXT
            elseif durationMS > 0 then
                durationValue = ZO_FormatTimeMilliseconds(durationMS, TIME_FORMAT_STYLE_DURATION, TIME_FORMAT_PRECISION_TENTHS_RELEVANT, TIME_FORMAT_DIRECTION_NONE)
            end
        end
        if durationValue then
            local durationPair = statsSection:AcquireStatValuePair(self:GetStyle("statValuePair"))
            durationPair:SetStat(GetString(SI_ABILITY_TOOLTIP_DURATION_LABEL), self:GetStyle("statValuePairStat"))
            durationPair:SetValue(durationValue, self:GetStyle("abilityStatValuePairValue"))
            statsSection:AddStatValuePair(durationPair)
        end
        --Cooldown
        local cooldownMS = GetAbilityCooldown(abilityId, overrideCasterUnitTag)
        local unknownCooldown = cooldownMS == nil
        if unknownCooldown or cooldownMS > 0 then
            local cooldownPair = statsSection:AcquireStatValuePair(self:GetStyle("statValuePair"))
            cooldownPair:SetStat(GetString(SI_ABILITY_TOOLTIP_COOLDOWN), self:GetStyle("statValuePairStat"))
            local cooldownValue = unknownCooldown and UNKNOWN_SECONDS_STAT_VALUE_TEXT or ZO_FormatTimeMilliseconds(cooldownMS, TIME_FORMAT_STYLE_DURATION, TIME_FORMAT_PRECISION_TENTHS_RELEVANT, TIME_FORMAT_DIRECTION_NONE)
            cooldownPair:SetValue(cooldownValue, self:GetStyle("abilityStatValuePairValue"))
            statsSection:AddStatValuePair(cooldownPair)
        end
        -- Costs
        self:AddAbilityCosts(abilityId, overrideActiveRank, statsSection)
        self:AddAbilityCostsOverTime(abilityId, overrideActiveRank, statsSection)
        --Roles
        local isTankRole, isHealerRole, isDamageRole = GetAbilityRoles(abilityId)
        local roleNarrationText = {}
        if isTankRole then
            table.insert(g_roleIconTable, TANK_ROLE_ICON)
            table.insert(roleNarrationText, GetString("SI_LFGROLE", LFG_ROLE_TANK))
        end
        if isHealerRole then
            table.insert(g_roleIconTable, HEALER_ROLE_ICON)
            table.insert(roleNarrationText, GetString("SI_LFGROLE", LFG_ROLE_HEAL))
        end
        if isDamageRole then
            table.insert(g_roleIconTable, DAMAGE_ROLE_ICON)
            table.insert(roleNarrationText, GetString("SI_LFGROLE", LFG_ROLE_DPS))
        end
        if #g_roleIconTable > 0 then
            local rolesPair = statsSection:AcquireStatValuePair(self:GetStyle("statValuePair"))
            rolesPair:SetStat(GetString(SI_ABILITY_TOOLTIP_ROLE_LABEL), self:GetStyle("statValuePairStat"))
            local finalIconText = table.concat(g_roleIconTable, "")
            rolesPair:SetValueWithCustomNarration(finalIconText, roleNarrationText, self:GetStyle("abilityStatValuePairValue"))
            statsSection:AddStatValuePair(rolesPair)
            ZO_ClearNumericallyIndexedTable(g_roleIconTable)
        end
        self:AddSection(statsSection)
    end
    function ZO_Tooltip:GetMechanicFlagStyle(mechanicFlag)
        if mechanicFlag == COMBAT_MECHANIC_FLAGS_MAGICKA then
            return self:GetStyle("abilityStatValuePairMagickaValue")
        elseif mechanicFlag == COMBAT_MECHANIC_FLAGS_STAMINA then
            return self:GetStyle("abilityStatValuePairStaminaValue")
        elseif mechanicFlag == COMBAT_MECHANIC_FLAGS_HEALTH then
            return self:GetStyle("abilityStatValuePairHealthValue")
        end
        return self:GetStyle("abilityStatValuePairValue")
    end
    function ZO_Tooltip:AddAbilityCosts(abilityId, overrideActiveRank, statsSection)
        local costAbility = GetCurrentChainedAbility(abilityId)
        local baseCost, mechanicFlags, isCostChargedPerTick = GetAbilityBaseCostInfo(costAbility, overrideActiveRank, "player")
        if baseCost == 0 or isCostChargedPerTick == true then
            -- If cost is definitely 0 or isCostChargedPerTick is true, we don't want to show
            -- anything for cost no matter what
            return
        end
        local isUnknownCost = baseCost == nil
        local isUnknownMechanics = mechanicFlags == nil
        local isUnknownIfCostChargedPerTick = isCostChargedPerTick == nil
        if isUnknownIfCostChargedPerTick or (isUnknownCost and isUnknownMechanics) then
            -- Super generic "we know nothing about the cost" variant
            local costPair = statsSection:AcquireStatValuePair(self:GetStyle("statValuePair"))
            costPair:SetStat(GetString(SI_ABILITY_TOOLTIP_RESOURCE_COST_LABEL), self:GetStyle("statValuePairStat"))
            costPair:SetValue(UNKNOWN_STAT_VALUE_TYPE_TEXT, self:GetStyle("abilityStatValuePairValue"))
            statsSection:AddStatValuePair(costPair)
            return
        end
        if mechanicFlags then
            -- Mechanics known, add a specific line for each
            for flag in ZO_FlagHelpers.MaskHasFlagsIterator(mechanicFlags) do
                local costPair = statsSection:AcquireStatValuePair(self:GetStyle("statValuePair"))
                costPair:SetStat(GetString(SI_ABILITY_TOOLTIP_RESOURCE_COST_LABEL), self:GetStyle("statValuePairStat"))
                -- Cost may or may not be known
                local cost = isUnknownCost and UNKNOWN_STAT_VALUE_TEXT or GetAbilityCost(costAbility, flag, overrideActiveRank, "player")
                local mechanicName = GetString("SI_COMBATMECHANICFLAGS", flag)
                local costString = zo_strformat(SI_ABILITY_TOOLTIP_RESOURCE_COST, cost, mechanicName)
                local style = self:GetMechanicFlagStyle(flag)
                costPair:SetValue(costString, style)
                statsSection:AddStatValuePair(costPair)
            end
        else
            -- Don't know the mechanics but must know the cost (see super generic case above), just add a single generic cost line
            local costPair = statsSection:AcquireStatValuePair(self:GetStyle("statValuePair"))
            costPair:SetStat(GetString(SI_ABILITY_TOOLTIP_RESOURCE_COST_LABEL), self:GetStyle("statValuePairStat"))
            local costString = zo_strformat(SI_ABILITY_TOOLTIP_RESOURCE_COST, baseCost, UNKNOWN_STAT_VALUE_TYPE_TEXT)
            costPair:SetValue(costString, self:GetStyle("abilityStatValuePairValue"))
            statsSection:AddStatValuePair(costPair)
        end
    end
    function ZO_Tooltip:AddAbilityCostsOverTime(abilityId, overrideActiveRank, statsSection)
        local costAbility = GetCurrentChainedAbility(abilityId)
        local baseCost, mechanicFlags, isCostChargedPerTick = GetAbilityBaseCostInfo(costAbility, overrideActiveRank, "player")
        if isCostChargedPerTick == nil then
            -- Do nothing, AddAbilityCosts would have already added a line for this state
            return
        end
        if baseCost == 0 or not isCostChargedPerTick then
            -- If cost is definitely 0 or isCostChargedPerTick is false, we don't want to show
            -- anything for cost over time no matter what
            return
        end
        -- If we got this far, we definitely know we're cost over time
        local frequencyMS = GetAbilityFrequencyMS(costAbility, "player")
        if frequencyMS == 0 then
            -- If frequencyMS is definitely 0 we don't want to show anything no matter what
            return
        end
        local isUnknownCost = baseCost == nil
        local isUnknownMechanics = mechanicFlags == nil
        local isUnknownFrequency = frequencyMS == nil
        local formattedFrequency = frequencyMS and ZO_FormatTimeMilliseconds(frequencyMS, TIME_FORMAT_STYLE_SHOW_LARGEST_UNIT, TIME_FORMAT_PRECISION_TENTHS_RELEVANT, TIME_FORMAT_DIRECTION_NONE) or GetString(SI_ABILITY_TOOLTIP_UNKNOWN_SECONDS_SHORT)
        if mechanicFlags then
            -- Mechanics known, add a specific line for each
            for flag in ZO_FlagHelpers.MaskHasFlagsIterator(mechanicFlags) do
                local costPair = statsSection:AcquireStatValuePair(self:GetStyle("statValuePair"))
                costPair:SetStat(GetString(SI_ABILITY_TOOLTIP_RESOURCE_COST_LABEL), self:GetStyle("statValuePairStat"))
                -- Cost may or may not be known
                local cost = isUnknownCost and UNKNOWN_STAT_VALUE_TEXT or GetAbilityCostPerTick(costAbility, flag, overrideActiveRank)
                local mechanicName = GetString("SI_COMBATMECHANICFLAGS", flag)
                local costOverTimeString = zo_strformat(SI_ABILITY_TOOLTIP_RESOURCE_COST_OVER_TIME, cost, mechanicName, formattedFrequency)
                local style = self:GetMechanicFlagStyle(flag)
                costPair:SetValue(costOverTimeString, style)
                statsSection:AddStatValuePair(costPair)
            end
        else
            -- Don't know the mechanics, just add a single generic cost line
            local costPair = statsSection:AcquireStatValuePair(self:GetStyle("statValuePair"))
            costPair:SetStat(GetString(SI_ABILITY_TOOLTIP_RESOURCE_COST_LABEL), self:GetStyle("statValuePairStat"))
            if isUnknownCost then
                if isUnknownFrequency then
                    costPair:SetValue(GetString(SI_ABILITY_TOOLTIP_UNKNOWN_COST_OVER_UNKNOWN_TIME), self:GetStyle("abilityStatValuePairValue"))
                else
                    local costOverTimeString = zo_strformat(SI_ABILITY_TOOLTIP_UNKNOWN_COST_OVER_TIME, formattedFrequency)
                    costPair:SetValue(costOverTimeString, self:GetStyle("abilityStatValuePairValue"))
                end
            else
                local costOverTimeString = zo_strformat(SI_ABILITY_TOOLTIP_RESOURCE_COST_OVER_TIME, baseCost, UNKNOWN_STAT_VALUE_TYPE_TEXT, formattedFrequency)
                costPair:SetValue(costOverTimeString, self:GetStyle("abilityStatValuePairValue"))
            end
            statsSection:AddStatValuePair(costPair)
        end
    end
end
function ZO_Tooltip:AddAbilityDescription(abilityId, overrideDescription, overrideCasterUnitTag)
    local descriptionHeader = GetAbilityDescriptionHeader(abilityId, overrideCasterUnitTag)
    local NO_OVERRIDE_RANK = nil
    local description = overrideDescription or GetAbilityDescription(abilityId, NO_OVERRIDE_RANK, overrideCasterUnitTag)
    local bodyDescriptionStyle = self:GetStyle("bodyDescription")
    if descriptionHeader ~= "" or description ~= "" then
        local descriptionSection = self:AcquireSection(self:GetStyle("bodySection"))
        if descriptionHeader ~= "" then
            --descriptionHeader has already been run through grammar
            descriptionSection:AddLine(descriptionHeader, self:GetStyle("bodyHeader"))
        end
        if description ~= "" then
            --description has already been run through grammar
            descriptionSection:AddLine(description, bodyDescriptionStyle)
        end
        self:AddSection(descriptionSection)
    end
    local craftedAbilityId = GetAbilityCraftedAbilityId(abilityId)
    if craftedAbilityId ~= 0 then
        for slotType = SCRIBING_SLOT_ITERATION_BEGIN, SCRIBING_SLOT_ITERATION_END do
            local scriptDescription = GenerateCraftedAbilityScriptSlotDescriptionForAbilityDescription(abilityId, slotType, overrideCasterUnitTag)
            if scriptDescription ~= "" then
                local descriptionSection = self:AcquireSection(self:GetStyle("bodySection"))
                descriptionSection:AddLine(scriptDescription, bodyDescriptionStyle)
                self:AddSection(descriptionSection)
            end
        end
    end
end
function ZO_Tooltip:AddAbilityNewEffects(...)
    local numNewEffectReturns = select("#", ...)
    if(numNewEffectReturns > 0) then
        for i = 1, numNewEffectReturns do
            local newEffect = select(i, ...)
            local newEffectSection = self:AcquireSection(self:GetStyle("bodySection"))
            newEffectSection:AddLine(GetString(SI_ABILITY_TOOLTIP_NEW_EFFECT), self:GetStyle("newEffectTitle"), self:GetStyle("bodyHeader"))
            newEffectSection:AddLine(newEffect, self:GetStyle("newEffectBody"), self:GetStyle("bodyDescription"))
            self:AddSection(newEffectSection)
        end
    end
end
--Layout Functions
function ZO_Tooltip:LayoutSimpleAbility(abilityId, options)
    local omitHeader = options and options.omitHeader
    if not omitHeader then
        local headerSection = self:AcquireSection(self:GetStyle("abilityHeaderSection"))
        self:AddSectionEvenIfEmpty(headerSection)
    end
    local formattedAbilityName = ZO_CachedStrFormat(SI_ABILITY_NAME, GetAbilityName(abilityId))
    self:AddLine(formattedAbilityName, self:GetStyle("title"))
    if not IsAbilityPassive(abilityId) then
        self:AddAbilityStats(abilityId)
    end
    self:AddAbilityDescription(abilityId)
end
function ZO_Tooltip:LayoutEndlessDungeonBuffAbility(buffAbilityId, includeLifetimeStacks, stackCount)
    local buffType, isAvatarVision = GetAbilityEndlessDungeonBuffType(buffAbilityId)
    local buffBucketType = GetAbilityEndlessDungeonBuffBucketType(buffAbilityId)
    local showStacks = includeLifetimeStacks or ShouldAbilityShowStacks(buffAbilityId)
    if not stackCount then
        stackCount = showStacks and GetNumStacksForEndlessDungeonBuff(buffAbilityId, includeLifetimeStacks)
    end
    local headerSection = self:AcquireSection(self:GetStyle("abilityHeaderSection"))
    local buffBucketTypeText = ZO_CachedStrFormat(SI_ENDLESS_DUNGEON_BUFF_TYPE_FORMATTER, GetString("SI_ENDLESSDUNGEONBUFFBUCKETTYPE", buffBucketType))
    headerSection:AddLine(buffBucketTypeText, self:GetStyle("abilityHeader"))
    local buffTypeText = isAvatarVision and GetString("SI_ENDLESSDUNGEONBUFFTYPE_AVATAR", buffType) or GetString("SI_ENDLESSDUNGEONBUFFTYPE", buffType)
    buffTypeText = ZO_CachedStrFormat(SI_ENDLESS_DUNGEON_BUFF_TYPE_FORMATTER, buffTypeText)
    headerSection:AddLine(buffTypeText, self:GetStyle("abilityHeader"))
    if showStacks then
        headerSection:AddLine(zo_iconTextFormat("EsoUI/Art/Tooltips/icon_moras_gift.dds", 20, 20, stackCount), self:GetStyle("abilityStack"))
    end
    self:AddSection(headerSection)
    local abilityName = GetAbilityName(buffAbilityId)
    local formattedAbilityName
    if showStacks then
        formattedAbilityName = zo_strformat(SI_ABILITY_NAME_WITH_QUANTITY, abilityName, stackCount)
    else
        formattedAbilityName = ZO_CachedStrFormat(SI_ABILITY_NAME, abilityName)
    end
    self:AddLine(formattedAbilityName, self:GetStyle("title"))
    self:AddAbilityDescription(buffAbilityId)
end
function ZO_Tooltip:LayoutAbilityLink(link, options)
    local abilityId = GetAbilityIdFromLink(link)
    self:LayoutSimpleAbility(abilityId, options)
end