Back to Home

ESO Lua File v101041

ingame/skillsadvisor/skillsadvisor_manager.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
--
--[[ SkillsAdvisor Singleton ]]--
--
local SkillsAdvisor_Manager = ZO_CallbackObject:Subclass()
function SkillsAdvisor_Manager:New(...)
    ZO_SKILLS_ADVISOR_SINGLETON = ZO_CallbackObject.New(self)
    ZO_SKILLS_ADVISOR_SINGLETON:Initialize(...) -- ZO_CallbackObject does not have an initialize function
    return ZO_SKILLS_ADVISOR_SINGLETON
end
function SkillsAdvisor_Manager:Initialize()
    self.skillBuilds = {}
    self.availableAbilityList = {}
    self.purchasedAbilityList = {}
    self.numSkillBuildIndicies = 0
    self.numAvailableSkillBuilds = 0
    self.advancedSkillBuildData =
    {
        name = GetString(SI_SKILLS_ADVISOR_ADVANCED_PLAYER_NAME),
        description = GetString(SI_SKILLS_ADVISOR_ADVANCED_PLAYER_DESCRIPTION),
        isTank = false,
        isHealer = false,
        isDPS = false,
        skillAbilities = {},
    }
    local function UpdateSkillBuildData()
        self:UpdateSkillBuildData()
    end
    local function RefreshVisibleAbilityLists()
        local BROADCAST = true
        self:RefreshVisibleAbilityLists(BROADCAST)
    end
    SKILLS_DATA_MANAGER:RegisterCallback("FullSystemUpdated", UpdateSkillBuildData)
    SKILLS_DATA_MANAGER:RegisterCallback("SkillLineRankUpdated", RefreshVisibleAbilityLists)
    SKILL_POINT_ALLOCATION_MANAGER:RegisterCallback("OnSkillsCleared", RefreshVisibleAbilityLists)
    SKILL_POINT_ALLOCATION_MANAGER:RegisterCallback("PurchasedChanged", RefreshVisibleAbilityLists)
    SKILL_POINT_ALLOCATION_MANAGER:RegisterCallback("SkillProgressionKeyChanged", RefreshVisibleAbilityLists)
    SKILLS_AND_ACTION_BAR_MANAGER:RegisterCallback("SkillPointAllocationModeChanged", RefreshVisibleAbilityLists)
    SKILLS_AND_ACTION_BAR_MANAGER:RegisterCallback("RespecStateReset", RefreshVisibleAbilityLists)
    EVENT_MANAGER:RegisterForEvent("SkillsAdvisor_Manager", EVENT_SKILL_BUILD_SELECTION_UPDATED, function(eventId, ...) self:OnBuildSelectionUpdated(...) end)
    --TODO: Support def changes
end
function SkillsAdvisor_Manager:UpdateSkillBuildData()
    if not SKILLS_DATA_MANAGER:IsDataReady() then
        --If we don't have skills data, the rest of this is pretty useless, so wait until the data becomes ready
        return
    end
    self.numAvailableSkillBuilds = GetNumAvailableSkillBuilds()
    self.isAdvancedMode = IsSkillBuildAdvancedMode()
    local selectedSkillBuildId = GetSkillBuildId()
    if self.isAdvancedMode or selectedSkillBuildId <= 0 then
        selectedSkillBuildId = nil
    end
    self.selectedSkillBuildId = selectedSkillBuildId
    self.selectedSkillBuildIndex = nil
    for skillBuildIndex = 1, self.numAvailableSkillBuilds do
        local skillBuildId = GetAvailableSkillBuildIdByIndex(skillBuildIndex)
        local oldSkillBuild = self.skillBuilds[skillBuildIndex]
        -- Builds don't change, so we only need to load when IDs are different
        if oldSkillBuild == nil or oldSkillBuild.id ~= skillBuildId then
            local name, description, isTank, isHealer, isDPS = GetSkillBuildInfo(skillBuildId)
            self.skillBuilds[skillBuildIndex] = 
            {
                id = skillBuildId,
                index = skillBuildIndex,
                name = zo_strformat(SI_SKILLS_ADVISOR_SKILL_BUILD_NAME, name),
                description = zo_strformat(SI_SKILLS_ADVISOR_SKILL_BUILD_DESCRIPTION, description),
                isTank = isTank,
                isHealer = isHealer,
                isDPS = isDPS,
                skillAbilities = {},
            }
        end
        if skillBuildId == selectedSkillBuildId then
            self.selectedSkillBuildIndex = skillBuildIndex
        end
    end
    self.numSkillBuildIndicies = self.numAvailableSkillBuilds + 1
    self.skillBuilds[self.numSkillBuildIndicies] = self.advancedSkillBuildData
    self.advancedSkillBuildData.index = self.numSkillBuildIndicies
    -- Remove remaining stale entries, if any
    for i = self.numSkillBuildIndicies + 1, #self.skillBuilds do
        self.skillBuilds[i] = nil
    end
    
    -- Only get SkillBuild Data for currently selected SkillBuild
    if self.selectedSkillBuildIndex ~= nil then
        local skillAbilities = self.skillBuilds[self.selectedSkillBuildIndex].skillAbilities
        ZO_ClearNumericallyIndexedTable(skillAbilities)
        for skillBuildAbilityIndex = 1, GetNumSkillBuildAbilities(selectedSkillBuildId) do
            local skillProgressionData = self:GetSkillProgressionData(selectedSkillBuildId, skillBuildAbilityIndex)
            if skillProgressionData then
                table.insert(skillAbilities, skillProgressionData)
            end
        end
    end
    local DONT_BROADCAST = false
    self:RefreshVisibleAbilityLists(DONT_BROADCAST)
    self:FireCallbacks("OnSkillsAdvisorDataUpdated")
end
function SkillsAdvisor_Manager:GetSkillProgressionData(skillBuildId, skillBuildAbilityIndex)
    local skillType, skillLineIndex, skillIndex, _, skillBuildMorphChoice, skillBuildRank = GetSkillBuildEntryInfo(skillBuildId, skillBuildAbilityIndex)
    local skillData = SKILLS_DATA_MANAGER:GetSkillDataByIndices(skillType, skillLineIndex, skillIndex)
    if not skillData then
        -- ESO-566272 / ESO-581396: We added logging but it didn't really help us narrow down why this can happen.
        -- Best guess is a timing issue between getting the skills info and the actual C++ progression data populated.
        -- Removed the logging that was here because it was flooding our reports for no real benefit.
        -- TODO: A refactor to separate progression from static data would probably solve this, but that's not going to be something we'll do anytime soon
        return nil
    end
    local skillProgressionData = skillData:IsPassive() and skillData:GetRankData(skillBuildRank) or skillData:GetMorphData(skillBuildMorphChoice)
    return skillProgressionData
end
do 
    local ADVANCED_MODE_SELECTED = true
    local SKILL_BUILD_SELECTED = false
    function SkillsAdvisor_Manager:OnSkillBuildSelected(skillBuildIndex)
        if skillBuildIndex == self.numSkillBuildIndicies then
            SelectSkillBuild(0, ADVANCED_MODE_SELECTED)
        else
            SelectSkillBuild(self.skillBuilds[skillBuildIndex].id, SKILL_BUILD_SELECTED)
        end
    end
end
function SkillsAdvisor_Manager:OnRequestSelectSkillLine()
    self:FireCallbacks("OnRequestSelectSkillLine")
end
function SkillsAdvisor_Manager:OnBuildSelectionUpdated()
    self:FireCallbacks("OnSelectedSkillBuildUpdated")
end
function SkillsAdvisor_Manager:IsAdvancedModeSelected()
    return self.isAdvancedMode
end
function SkillsAdvisor_Manager:GetNumSkillBuildOptions()
    return self.numSkillBuildIndicies
end
function SkillsAdvisor_Manager:GetAvailableSkillBuildByIndex(index) 
    return self.skillBuilds[index]
end
function SkillsAdvisor_Manager:GetAvailableSkillBuildById(skillBuildId, getAdvancedIfNoId)
    if skillBuildId or getAdvancedIfNoId then
        for _, data in ipairs(self.skillBuilds) do
            if data.id == skillBuildId then
                return data
            end
        end
    end
    return nil
end
function SkillsAdvisor_Manager:GetSelectedSkillBuildId()
    return self.selectedSkillBuildId
end
function SkillsAdvisor_Manager:GetSelectedSkillBuildIndex()
    local selectedSkillBuild = self:GetAvailableSkillBuildById(self.selectedSkillBuildId, self:IsAdvancedModeSelected())
    if selectedSkillBuild then
        return selectedSkillBuild.index
    else
        return nil
    end
end
function SkillsAdvisor_Manager:GetSkillBuildRoleLinesById(skillBuildId)
    local skillBuild = self:GetAvailableSkillBuildById(skillBuildId)  
    local results = {}
    if skillBuild then   
        local selectedRoles = {}     
        if skillBuild.isDPS then
            table.insert(selectedRoles, LFG_ROLE_DPS)
        end
        if skillBuild.isHealer then
            table.insert(selectedRoles, LFG_ROLE_HEAL)
        end
        if skillBuild.isTank then
            table.insert(selectedRoles, LFG_ROLE_TANK)
        end
        if #selectedRoles == 1 then
            local text = zo_strformat(SI_TOOLTIP_ITEM_ROLE, GetString("SI_LFGROLE", selectedRoles[1]), zo_iconFormat(ZO_GetRoleIcon(selectedRoles[1]), "100%", "100%"))
            table.insert(results, zo_strformat(SI_TOOLTIP_ITEM_ROLE_FORMAT, text))
        elseif #selectedRoles > 1 then
            table.insert(results, GetString(SI_TOOLTIP_ITEM_ROLES_FORMAT))
            for i, role in ipairs(selectedRoles) do
                local text = zo_strformat(SI_TOOLTIP_ITEM_ROLE, GetString("SI_LFGROLE", role), zo_iconFormat(ZO_GetRoleIcon(role), "100%", "100%"))
                table.insert(results, text)
            end
        end
    end
    return results
end
function SkillsAdvisor_Manager:GetAvailableAbilityList()
    return self.availableAbilityList
end
function SkillsAdvisor_Manager:GetPurchasedAbilityList()
    return self.purchasedAbilityList
end
function SkillsAdvisor_Manager:RefreshVisibleAbilityLists(broadcast)
    ZO_ClearNumericallyIndexedTable(self.availableAbilityList)
    ZO_ClearNumericallyIndexedTable(self.purchasedAbilityList)
    if self.selectedSkillBuildId ~= nil then
        local skillBuild = self:GetAvailableSkillBuildById(self.selectedSkillBuildId)
        if skillBuild ~= nil then
            local suggestionLimit = GetSkillsAdvisorSuggestionLimit()
            local firstLockedAbilityAtLimit
            for _, skillProgressionData in ipairs(skillBuild.skillAbilities) do
                local skillData = skillProgressionData:GetSkillData()
                local skillPointAllocator = skillData:GetPointAllocator()
                local isUnlocked = skillProgressionData:IsUnlocked()
                if skillPointAllocator:IsPurchased() then
                    if skillData:IsPassive() then
                        local entryRank = skillProgressionData:GetRank()
                        local allocatedRank = skillPointAllocator:GetRank()
                        if allocatedRank >= entryRank then
                            table.insert(self.purchasedAbilityList, skillProgressionData)
                        elseif allocatedRank + 1 == entryRank then
                            -- This data is the next rank to purchased
                            if #self.availableAbilityList < suggestionLimit then
                                if #self.availableAbilityList < suggestionLimit - 1 or isUnlocked then
                                    table.insert(self.availableAbilityList, skillProgressionData)
                                elseif firstLockedAbilityAtLimit == nil then
                                    firstLockedAbilityAtLimit = skillProgressionData
                                end
                            end
                        end  
                    else
                        local entryMorphSlot = skillProgressionData:GetMorphSlot()
                        local allocatedMorphSlot = skillPointAllocator:GetMorphSlot()
                        if skillProgressionData:IsBase() or entryMorphSlot == allocatedMorphSlot then
                            table.insert(self.purchasedAbilityList, skillProgressionData)
                        elseif #self.availableAbilityList < suggestionLimit then
                            if allocatedMorphSlot == MORPH_SLOT_BASE then
                                -- If we're currently at base, any advised morph is acceptable to display
                                if #self.availableAbilityList < suggestionLimit - 1 or isUnlocked then
                                    table.insert(self.availableAbilityList, skillProgressionData)
                                elseif firstLockedAbilityAtLimit == nil then
                                    firstLockedAbilityAtLimit = skillProgressionData
                                end
                            else
                                -- If we've already morphed, only continue to show this morph as advised if the other morph is NOT advised
                                local siblingMorphData = skillProgressionData:GetSiblingMorphData()
                                if not siblingMorphData:IsAdvised() then 
                                    if #self.availableAbilityList < suggestionLimit - 1 or isUnlocked then
                                        table.insert(self.availableAbilityList, skillProgressionData)
                                    elseif firstLockedAbilityAtLimit == nil then
                                        firstLockedAbilityAtLimit = skillProgressionData
                                    end
                                end
                            end
                        end
                    end
                elseif #self.availableAbilityList < suggestionLimit then
                    if #self.availableAbilityList < suggestionLimit - 1 or isUnlocked then
                        if skillData:IsPassive() then
                            if skillProgressionData:GetRank() == 1 then
                                table.insert(self.availableAbilityList, skillProgressionData)
                            end
                        else
                            if skillProgressionData:IsBase() then
                                table.insert(self.availableAbilityList, skillProgressionData)
                            end
                        end
                    elseif firstLockedAbilityAtLimit == nil then
                        firstLockedAbilityAtLimit = skillProgressionData
                    end
                end
            end
            if #self.availableAbilityList <= suggestionLimit - 1 and firstLockedAbilityAtLimit then
                table.insert(self.availableAbilityList, firstLockedAbilityAtLimit)
            end
        end
    end
    if broadcast then
        self:FireCallbacks("RefreshVisibleAbilityLists")
    end
end
function SkillsAdvisor_Manager:IsSkillDataInSelectedBuild(skillData)
    local skillType, skillLineIndex, skillIndex = skillData:GetIndices()
    local skillProgressionData
    if skillData:IsPassive() then
        skillProgressionData = skillData:GetRankData(1)
    else
        skillProgressionData = skillData:GetMorphData(MORPH_SLOT_BASE)
    end
    return self:IsSkillProgressionDataInSelectedBuild(skillProgressionData)
end
function SkillsAdvisor_Manager:IsSkillProgressionDataInSelectedBuild(skillProgressionData)
    if self.selectedSkillBuildIndex ~= nil then
        local skillAbilities = self.skillBuilds[self.selectedSkillBuildIndex].skillAbilities
        for _, skillBuildProgressionData in ipairs(skillAbilities) do
            if skillBuildProgressionData == skillProgressionData then
                return true
            end
        end
    end
    return false
end
function SkillsAdvisor_Manager:GetValidatedRankIndex(rankIndex)
    if not rankIndex or rankIndex == 0 then 
        return 1
    else
        return rankIndex
    end    
end
function SkillsAdvisor_Manager:SetupKeyboardSkillBuildTooltip(data)
    if data then
        local buildTypeTable = ZO_SKILLS_ADVISOR_SINGLETON:GetSkillBuildRoleLinesById(data.id)
        GameTooltip:AddLine(data.name, "ZoFontHeader", ZO_SELECTED_TEXT:UnpackRGBA())
        ZO_Tooltip_AddDivider(GameTooltip)
        for i, type in ipairs(buildTypeTable) do
            GameTooltip:AddLine(type, "", ZO_SELECTED_TEXT:UnpackRGBA())  
        end
        GameTooltip:AddLine(data.description, "", ZO_NORMAL_TEXT:UnpackRGBA())
        if GetDefaultSkillBuildId() == data.id then
            GameTooltip:AddLine(GetString(SI_SKILLS_ADVISOR_SKILL_BUILD_NEW_PLAYER), "", ZO_SUCCEEDED_TEXT:UnpackRGBA())
        end
    end
end
SkillsAdvisor_Manager:New()