Back to Home

ESO Lua File v101041

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
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 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)
    barControl:RegisterForEvent(EVENT_GAMEPAD_PREFERRED_MODE_CHANGED, function() self:OnValueChanged(barControl, barInfo, visualType) end)
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)
    barControl:UnregisterForEvent(EVENT_GAMEPAD_PREFERRED_MODE_CHANGED)
end
local function ApplyPlatformStyleToShield(left, right, leftOverlay, rightOverlay)
    ApplyTemplateToControl(left, ZO_GetPlatformTemplate(leftOverlay))
    ApplyTemplateToControl(right, ZO_GetPlatformTemplate(rightOverlay))
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_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 = CreateControlFromVirtual("$(parent)PowerShieldRightOverlay", attributeBar, self.layoutData.barRightOverlayTemplate)
        info.overlayControls = { shieldLeftOverlay, shieldRightOverlay }
        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, ZO_POWER_BAR_GRADIENT_COLORS[COMBAT_MECHANIC_FLAGS_HEALTH])
            ZO_StatusBar_SetGradientColor(overlay.noHealingInner, NO_HEALING_FILL_COLOR_GRADIENT)
            ZO_StatusBar_SetGradientColor(overlay.noHealingOuter, NO_HEALING_BORDER_COLOR_GRADIENT)
            ZO_StatusBar_SetGradientColor(overlay.fakeNoHealingInner, NO_HEALING_FILL_COLOR_GRADIENT)
            ZO_StatusBar_SetGradientColor(overlay.fakeNoHealingOuter, NO_HEALING_BORDER_COLOR_GRADIENT)
            overlay:SetValue(1)
        end
        ZO_PreHookHandler(leftStatusBar, "OnMinMaxValueChanged", function(_, min, max)
            info.attributeMax = max
            self:OnStatusBarValueChanged(attributeBar, info)
        end)
        ZO_PreHookHandler(leftStatusBar, "OnValueChanged", function(_, value)
            info.attributeValue = value
            self:OnStatusBarValueChanged(attributeBar, info)
        end)
        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)
        rightControl:SetHidden(true)
        return
    else
        leftControl:SetHidden(false)
        rightControl:SetHidden(false)
    end
    local leftAttributeBar, rightAttributeBar = unpack(attributeBar.barControls)
    local halfWidth = leftAttributeBar:GetWidth()
    local leftOffsetX = halfWidth * (1 - percentOfBarRequested)
    -- Add an extra 0.5 to resolve pixel rounding issues in gamepad UI.
    local rightOffsetX = leftOffsetX + halfWidth * percentOfBarRequested + 0.5
    leftControl:ClearAnchors()
    leftControl:SetAnchor(LEFT, leftAttributeBar, LEFT, leftOffsetX, 0)
    leftControl:SetAnchor(RIGHT, leftAttributeBar, LEFT, rightOffsetX, 0)
    rightControl:ClearAnchors()
    rightControl:SetAnchor(RIGHT, rightAttributeBar, RIGHT, -leftOffsetX, 0)
    rightControl:SetAnchor(LEFT, rightAttributeBar, RIGHT, -rightOffsetX, 0)
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 * .5
        local trauma = traumaInfo.value * .5
        local noHealing = noHealingInfo.value
        -- 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.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.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.noHealingInner, noHealingSize)
        self:ApplyValueToBar(attributeBar, barInfo, leftOverlay.noHealingOuter, rightOverlay.noHealingOuter, noHealingSize)
        local fakeNoHealingSize = noHealing > 0 and fakeHealthSize or 0
        self:ApplyValueToBar(attributeBar, barInfo, leftOverlay.fakeNoHealingInner, rightOverlay.fakeNoHealingInner, fakeNoHealingSize)
        self:ApplyValueToBar(attributeBar, barInfo, leftOverlay.fakeNoHealingOuter, rightOverlay.fakeNoHealingOuter, fakeNoHealingSize)
    else
        leftOverlay:SetHidden(true)
        rightOverlay:SetHidden(true)
    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 self.attributeInfo then
        for _, info in pairs(self.attributeInfo) do
            if info.overlayControls then
                ApplyPlatformStyleToShield(info.overlayControls[LEFT_BAR], info.overlayControls[RIGHT_BAR], self.layoutData.barLeftOverlayTemplate, self.layoutData.barRightOverlayTemplate)
            end
        end
    end
end