Back to Home

ESO Lua File v101044

ingame/unitattributevisualizer/modules/powershield.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
ZO_ATTRIBUTE_BAR_POWER_SHIELD_NO_HEALING_LEVEL = 1000
ZO_ATTRIBUTE_BAR_POWER_SHIELD_LEVEL = 2000
ZO_ATTRIBUTE_BAR_POWER_SHIELD_TRAUMA_LEVEL = 3000
ZO_ATTRIBUTE_BAR_POWER_SHIELD_TRAUMA_GLOSS_LEVEL = 3001
ZO_ATTRIBUTE_BAR_POWER_SHIELD_FAKE_HEALTH_LEVEL = 4000
ZO_ATTRIBUTE_BAR_POWER_SHIELD_FAKE_HEALTH_GLOSS_LEVEL = 4001
ZO_ATTRIBUTE_BAR_POWER_SHIELD_FAKE_NO_HEALING_OUTER_LEVEL = 5000
ZO_ATTRIBUTE_BAR_POWER_SHIELD_FAKE_NO_HEALING_INNER_LEVEL = 5001
local FULL_ALPHA_VALUE = 1
local FADED_ALPHA_VALUE = 0.3
local RELEVANT_VISUAL_TYPES =
{
    ATTRIBUTE_VISUAL_POWER_SHIELDING,
    ATTRIBUTE_VISUAL_TRAUMA,
    ATTRIBUTE_VISUAL_NO_HEALING,
}
ZO_UnitVisualizer_PowerShieldModule = ZO_UnitAttributeVisualizerModuleBase:Subclass()
function ZO_UnitVisualizer_PowerShieldModule:New(...)
    return ZO_UnitAttributeVisualizerModuleBase.New(self, ...)
end
function ZO_UnitVisualizer_PowerShieldModule:Initialize(layoutData)
    self.layoutData = layoutData
end
function ZO_UnitVisualizer_PowerShieldModule:CreateInfoTable(control, oldInfo, stat, attribute, power)
    if control then
        local info = oldInfo or { visualInfo = {} }
        for _, visualType in ipairs(RELEVANT_VISUAL_TYPES) do
            if not info.visualInfo[visualType] then
                info.visualInfo[visualType] = {}
            end
            local visualInfo = info.visualInfo[visualType]
            visualInfo.value, visualInfo.maxValue = self:GetInitialValueAndMarkMostRecent(visualType, stat, attribute, power)
            if visualInfo.lastValue == nil then
                visualInfo.lastValue = 0
            end
        end
        return info
    end
    return nil
end
function ZO_UnitVisualizer_PowerShieldModule:OnAdded(healthBarControl, magickaBarControl, staminaBarControl)
    self.attributeBarControls =
    {
        [ATTRIBUTE_HEALTH] = healthBarControl,
    }
    if IsPlayerActivated() then
        self:InitializeBarValues()
    end
    local function OnSizeChanged(resizing, bar, size)
        if bar == healthBarControl then
            local info = self.attributeInfo and self.attributeInfo[ATTRIBUTE_HEALTH]
            if info then
                info.isResizing = resizing
            end
        end
    end
    local STARTING_RESIZE = true
    local STOPPING_RESIZE = false
    self:GetOwner():RegisterCallback("AttributeBarSizeChangingStart", function(...) OnSizeChanged(STARTING_RESIZE, ...) end)
    self:GetOwner():RegisterCallback("AttributeBarSizeChangingStopped", function(...) OnSizeChanged(STOPPING_RESIZE, ...) end)
    EVENT_MANAGER:RegisterForEvent("ZO_UnitVisualizer_PowerShieldModule" .. self:GetModuleId(), EVENT_PLAYER_ACTIVATED, function() self:InitializeBarValues() end)
    EVENT_MANAGER:RegisterForUpdate("ZO_UnitVisualizer_PowerShieldModule" .. self:GetModuleId(), 0, function() self:OnUpdate() end)
end
function ZO_UnitVisualizer_PowerShieldModule:InitializeBarValues()
    local healthBarControl = self.attributeBarControls[ATTRIBUTE_HEALTH]
    local oldBarInfo = self.attributeInfo
    self.attributeInfo =
    {
        [ATTRIBUTE_HEALTH] = self:CreateInfoTable(healthBarControl, oldBarInfo and oldBarInfo[ATTRIBUTE_HEALTH], STAT_MITIGATION, ATTRIBUTE_HEALTH, COMBAT_MECHANIC_FLAGS_HEALTH),
    }
    for attribute, bar in pairs(self.attributeBarControls) do
        local barInfo = self.attributeInfo[attribute]
        for visualType, _ in pairs(barInfo.visualInfo) do
            self:OnValueChanged(bar, barInfo, visualType)
        end
    end
end
function ZO_UnitVisualizer_PowerShieldModule:OnUnitChanged()
end
function ZO_UnitVisualizer_PowerShieldModule:OnUpdate()
    if self.attributeInfo then
        for attribute, info in pairs(self.attributeInfo) do
            if info.isResizing then
                self:UpdateValue(self.attributeBarControls[attribute], info)
            end
        end
    end
end
function ZO_UnitVisualizer_PowerShieldModule:IsUnitVisualRelevant(visualType, stat, attribute, powerType)
    if self.attributeInfo == nil or self.attributeInfo[attribute] == nil then
        return false
    end
    for _, currentVisualType in ipairs(RELEVANT_VISUAL_TYPES) do
        if visualType == currentVisualType then
            return true
        end
    end
    return false
end
function ZO_UnitVisualizer_PowerShieldModule:OnUnitAttributeVisualAdded(visualType, stat, attribute, powerType, value, maxValue)
    local barInfo = self.attributeInfo[attribute]
    local info = barInfo.visualInfo[visualType]
    local barControl = self.attributeBarControls[attribute]
    info.value = info.value + value
    info.maxValue = info.maxValue + maxValue
    self:OnValueChanged(barControl, barInfo, visualType)
end
function ZO_UnitVisualizer_PowerShieldModule:OnUnitAttributeVisualUpdated(visualType, stat, attribute, powerType, oldValue, newValue, oldMaxValue, newMaxValue)
    local barInfo = self.attributeInfo[attribute]
    local info = barInfo.visualInfo[visualType]
    info.value = info.value + (newValue - oldValue)
    info.maxValue = info.maxValue + (newMaxValue - oldMaxValue)
    self:OnValueChanged(self.attributeBarControls[attribute], barInfo, visualType)
end
function ZO_UnitVisualizer_PowerShieldModule:OnUnitAttributeVisualRemoved(visualType, stat, attribute, powerType, value, maxValue)
    local barInfo = self.attributeInfo[attribute]
    local info = barInfo.visualInfo[visualType]
    local barControl = self.attributeBarControls[attribute]
    info.value = info.value - value
    info.maxValue = info.maxValue - maxValue
    self:OnValueChanged(barControl, barInfo, visualType)
end
local function ApplyPlatformStyleToShield(left, right, leftOverlay, rightOverlay)
    ApplyTemplateToControl(left, ZO_GetPlatformTemplate(leftOverlay))
    if rightOverlay then
        ApplyTemplateToControl(right, ZO_GetPlatformTemplate(rightOverlay))
    end
end
local LEFT_BAR, RIGHT_BAR = 1, 2
local SHIELD_COLOR_GRADIENT = { ZO_ColorDef:New(.5, .5, 1, .3), ZO_ColorDef:New(.25, .25, .5, .5) }
local TRAUMA_COLOR_GRADIENT = { ZO_ColorDef:New("ab1c6473"), ZO_ColorDef:New("ab76bcc3") }
local NO_HEALING_FILL_COLOR_GRADIENT = { ZO_ColorDef:New("1a0909"), ZO_ColorDef:New("1a0909") }
local NO_HEALING_FILL_GROUP_FRAME_COLOR_GRADIENT = { ZO_ColorDef:New("501212"), ZO_ColorDef:New("501212") }
local NO_HEALING_BORDER_COLOR_GRADIENT = { ZO_ColorDef:New("da3030"), ZO_ColorDef:New("722323") }
function ZO_UnitVisualizer_PowerShieldModule:ShowOverlay(attributeBar, info)
    if not info.overlayControls then
        local leftStatusBar, rightStatusBar = unpack(attributeBar.barControls)
        local shieldLeftOverlay = CreateControlFromVirtual("$(parent)PowerShieldLeftOverlay", attributeBar, self.layoutData.barLeftOverlayTemplate)
        local shieldRightOverlay = (rightStatusBar and self.layoutData.barRightOverlayTemplate) and CreateControlFromVirtual("$(parent)PowerShieldRightOverlay", attributeBar, self.layoutData.barRightOverlayTemplate)
        info.overlayControls = { shieldLeftOverlay, shieldRightOverlay }
        local noHealingFillGradient
        if self.layoutData.noHealingGradientOverride then
            noHealingFillGradient = self.layoutData.noHealingGradientOverride
        elseif rightStatusBar then
            noHealingFillGradient = NO_HEALING_FILL_COLOR_GRADIENT
        else
            noHealingFillGradient = NO_HEALING_FILL_GROUP_FRAME_COLOR_GRADIENT
        end
        local fakeHealthGradient = self.layoutData.fakeHealthGradientOverride or ZO_POWER_BAR_GRADIENT_COLORS[COMBAT_MECHANIC_FLAGS_HEALTH]
        for _, overlay in ipairs(info.overlayControls) do
            ZO_StatusBar_SetGradientColor(overlay, SHIELD_COLOR_GRADIENT)
            ZO_StatusBar_SetGradientColor(overlay.traumaBar, TRAUMA_COLOR_GRADIENT)
            ZO_StatusBar_SetGradientColor(overlay.fakeHealthBar, fakeHealthGradient)
            ZO_StatusBar_SetGradientColor(overlay.noHealingInner, noHealingFillGradient)
            ZO_StatusBar_SetGradientColor(overlay.fakeNoHealingInner, noHealingFillGradient)
            if overlay.noHealingOuter and overlay.fakeNoHealingOuter then
                ZO_StatusBar_SetGradientColor(overlay.noHealingOuter, NO_HEALING_BORDER_COLOR_GRADIENT)
                ZO_StatusBar_SetGradientColor(overlay.fakeNoHealingOuter, NO_HEALING_BORDER_COLOR_GRADIENT)
            end
            overlay:SetValue(1)
        end
        leftStatusBar:SetHandler("OnMinMaxValueChanged", function(_, min, max)
            info.attributeMax = max
            self:OnStatusBarValueChanged(attributeBar, info)
        end, "PowerShield")
        leftStatusBar:SetHandler("OnValueChanged", function(_, value)
            info.attributeValue = value
            self:OnStatusBarValueChanged(attributeBar, info)
        end, "PowerShield")
        info.attributeMax = select(2, leftStatusBar:GetMinMax())
        info.attributeValue = leftStatusBar:GetValue()
    end
    ApplyPlatformStyleToShield(info.overlayControls[LEFT_BAR], info.overlayControls[RIGHT_BAR], self.layoutData.barLeftOverlayTemplate, self.layoutData.barRightOverlayTemplate)
    self:GetOwner():NotifyTakingControlOf(attributeBar)
    self:GetOwner():NotifyEndingControlOf(attributeBar)
end
function ZO_UnitVisualizer_PowerShieldModule:ShouldHideBar(barInfo)
    for _, visualInfo in pairs(barInfo.visualInfo) do
        if visualInfo.value > 0 then
            return false
        end
    end
    return true
end
function ZO_UnitVisualizer_PowerShieldModule:ApplyValueToBar(attributeBar, barInfo, leftControl, rightControl, value)
    local percentOfBarRequested = zo_clamp(value / barInfo.attributeMax, 0, 1.0)
    -- arbitrary hardcoded threshold to avoid "too-small" values
    if percentOfBarRequested <= .01 then
        leftControl:SetHidden(true)
        if rightControl then 
            rightControl:SetHidden(true)
        end
        return
    else
        leftControl:SetHidden(false)
        if rightControl then
            rightControl:SetHidden(false)
        end
    end
    local leftAttributeBar, rightAttributeBar = unpack(attributeBar.barControls)
    local halfWidth = leftAttributeBar:GetWidth()
    local leftOffsetX = halfWidth * (1 - percentOfBarRequested)
    if rightControl and rightAttributeBar then
        leftControl:ClearAnchors()
        leftControl:SetAnchor(LEFT, leftAttributeBar, LEFT, leftOffsetX, 0)
        leftControl:SetAnchor(RIGHT, leftAttributeBar, RIGHT)
        rightControl:ClearAnchors()
        rightControl:SetAnchor(RIGHT, rightAttributeBar, RIGHT, -leftOffsetX, 0)
        rightControl:SetAnchor(LEFT, rightAttributeBar, LEFT)
    else
        -- In the case that we only have a single bar, that bar grows left-to-right.
        leftControl:ClearAnchors()
        leftControl:SetAnchor(RIGHT, leftAttributeBar, RIGHT, -leftOffsetX, 0)
        leftControl:SetAnchor(LEFT, leftAttributeBar, RIGHT, -halfWidth, 0)
    end
end
function ZO_UnitVisualizer_PowerShieldModule:OnStatusBarValueChanged(attributeBar, barInfo)
    local shieldInfo, traumaInfo, noHealingInfo = barInfo.visualInfo[ATTRIBUTE_VISUAL_POWER_SHIELDING], barInfo.visualInfo[ATTRIBUTE_VISUAL_TRAUMA], barInfo.visualInfo[ATTRIBUTE_VISUAL_NO_HEALING]
    local leftOverlay, rightOverlay = unpack(barInfo.overlayControls)
    if not self:ShouldHideBar(barInfo) then
        -- This math just establishes the relationships between each bar: the clamping and scaling to turn these into actual control positions happens in ApplyValueToBar().
        -- Each bar is drawn on top of the last one in the sequence, so the actual amount of each bar the player will see will always be distance between the last bar and the next.
        -- These are the source values: we work a half-scale because we apply one half of the value's magnitude on each side of the total bar.
        -- We don't do this for health because the parent attribute bar provides us with half-values.
        -- The anti-healing status is binary; if its value is positive, the overlay is on, otherwise it's off.
        local health = barInfo.attributeValue
        local shield = shieldInfo.value
        local trauma = traumaInfo.value
        local noHealing = noHealingInfo.value
        -- In the case where we're a brand new visualizer on a unit with an already extant visualized effect, it's possible for us to not have a max health value. In that case, we'll try to grab it from the bar.
        if attributeBar.barControls[ATTRIBUTE_HEALTH].max and barInfo.attributeMax ~= attributeBar.barControls[ATTRIBUTE_HEALTH].max then
            barInfo.attributeMax = attributeBar.barControls[ATTRIBUTE_HEALTH].max
        end
        if rightOverlay then
            shield = shield * .5
            trauma = trauma * .5
        end
        -- Shields add to your original health bar, so they grow out of that value.
        -- When that amount extends beyond your max health we need shrink your fakehealth to compensate, which we carry over as shieldOverflow
        local shieldBarSize = health + shield
        self:ApplyValueToBar(attributeBar, barInfo, leftOverlay, rightOverlay, shieldBarSize)
        local shieldOverflow = zo_max(0, shieldBarSize - barInfo.attributeMax)
        -- Trauma starts at your current health value, minus any shield overflow.
        -- This means that you should perceive the size of this bar as being your "health", it just needs to be overhealed before you can benefit from extra heal.
        local traumaBarSize = health - shieldOverflow
        self:ApplyValueToBar(attributeBar, barInfo, leftOverlay.traumaBar, rightOverlay and rightOverlay.traumaBar, traumaBarSize)
        -- Then the fakehealth starts at the step 2 interpretation of health minus any trauma experienced.
        -- Sometimes trauma and shield overflow will be 0, in which case this value is the same as your actual health, otherwise it shrinks to fit each effect.
        local fakeHealthSize = traumaBarSize - trauma
        self:ApplyValueToBar(attributeBar, barInfo, leftOverlay.fakeHealthBar, rightOverlay and rightOverlay.fakeHealthBar, fakeHealthSize)
        -- The anti-healing overlay always matches the current health value if it's on.
        local noHealingSize = noHealing > 0 and health or 0
        self:ApplyValueToBar(attributeBar, barInfo, leftOverlay.noHealingInner, rightOverlay and rightOverlay.noHealingInner, noHealingSize)
        if leftOverlay.noHealingOuter then
            self:ApplyValueToBar(attributeBar, barInfo, leftOverlay.noHealingOuter, rightOverlay and rightOverlay.noHealingOuter, noHealingSize)
        end
        local fakeNoHealingSize = noHealing > 0 and fakeHealthSize or 0
        self:ApplyValueToBar(attributeBar, barInfo, leftOverlay.fakeNoHealingInner, rightOverlay and rightOverlay.fakeNoHealingInner, fakeNoHealingSize)
        if leftOverlay.fakeNoHealingOuter then
            self:ApplyValueToBar(attributeBar, barInfo, leftOverlay.fakeNoHealingOuter, rightOverlay and rightOverlay.fakeNoHealingOuter, fakeNoHealingSize)
        end
    else
        leftOverlay:SetHidden(true)
        if rightOverlay then
            rightOverlay:SetHidden(true)
        end
    end
end
function ZO_UnitVisualizer_PowerShieldModule:UpdateValue(attributeBar, info)
    if info.overlayControls then
        self:OnStatusBarValueChanged(attributeBar, info)
    end
end
local STATE_GAINED_SOUND_FOR_VISUAL_TYPE =
{
    [ATTRIBUTE_VISUAL_POWER_SHIELDING] = STAT_STATE_SHIELD_GAINED,
    [ATTRIBUTE_VISUAL_TRAUMA] = STAT_STATE_TRAUMA_GAINED,
    --TODO AntiHealing: Add sound for anti-healing?
}
local STATE_LOST_SOUND_FOR_VISUAL_TYPE =
{
    [ATTRIBUTE_VISUAL_POWER_SHIELDING] = STAT_STATE_SHIELD_LOST,
    [ATTRIBUTE_VISUAL_TRAUMA] = STAT_STATE_TRAUMA_LOST,
    --TODO AntiHealing: Add sound for anti-healing?
}
function ZO_UnitVisualizer_PowerShieldModule:OnValueChanged(attributeBar, barInfo, visualType)
    local visualInfo = barInfo.visualInfo[visualType]
    local value = visualInfo.value
    local lastValue = visualInfo.lastValue
    visualInfo.lastValue = value
    if value > 0 and lastValue <= 0 then
        self:ShowOverlay(attributeBar, barInfo)
        self.owner:PlaySoundFromStat(STAT_MITIGATION, STATE_GAINED_SOUND_FOR_VISUAL_TYPE[visualType])
        TriggerTutorial(TUTORIAL_TRIGGER_COMBAT_STATUS_EFFECT)
    elseif value <= 0 and lastValue > 0 then
        self.owner:PlaySoundFromStat(STAT_MITIGATION, STATE_LOST_SOUND_FOR_VISUAL_TYPE[visualType])
    end
    self:UpdateValue(attributeBar, barInfo)
end
function ZO_UnitVisualizer_PowerShieldModule:ApplyPlatformStyle()
    if IsPlayerActivated() then
        for attribute, bar in pairs(self.attributeBarControls) do
            local barInfo = self.attributeInfo and self.attributeInfo[attribute]
            if barInfo and barInfo.overlayControls then
                ApplyPlatformStyleToShield(barInfo.overlayControls[LEFT_BAR], barInfo.overlayControls[RIGHT_BAR], self.layoutData.barLeftOverlayTemplate, self.layoutData.barRightOverlayTemplate)
            end
            for visualType in pairs(barInfo.visualInfo) do
                self:OnValueChanged(bar, barInfo, visualType)
            end
        end
    end
end
function ZO_UnitVisualizer_PowerShieldModule:DoAlphaUpdate(isNearby)
    if IsPlayerActivated() then
        for attribute, bar in ipairs(self.attributeBarControls) do
            local barInfo = self.attributeInfo and self.attributeInfo[attribute]
            if barInfo and barInfo.overlayControls then
                for _, overlay in pairs(barInfo.overlayControls) do
                    local alpha = isNearby and FULL_ALPHA_VALUE or FADED_ALPHA_VALUE
                    overlay:SetAlpha(alpha)
                end
            end
        end
    end
end