Back to Home

ESO Lua File v100019

ingame/crafting/smithingcreation_shared.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
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
ZO_SharedSmithingCreation = ZO_Object:Subclass()
function ZO_SharedSmithingCreation:New(...)
    local smithingCreation = ZO_Object.New(self)
    smithingCreation:Initialize(...)
    return smithingCreation
end
local function GetCurrentCraftingLevel()
    local craftingType = GetCraftingInteractionType()
    if craftingType == CRAFTING_TYPE_BLACKSMITHING then
        return GetNonCombatBonus(NON_COMBAT_BONUS_BLACKSMITHING_LEVEL)
    elseif craftingType == CRAFTING_TYPE_CLOTHIER then
        return GetNonCombatBonus(NON_COMBAT_BONUS_CLOTHIER_LEVEL)
    elseif craftingType == CRAFTING_TYPE_WOODWORKING then
        return GetNonCombatBonus(NON_COMBAT_BONUS_WOODWORKING_LEVEL)
    end
end
function ZO_SharedSmithingCreation:Initialize(control, owner)
    self.control = control
    self.owner = owner
    local function HandleDirtyEvent()
        self:HandleDirtyEvent()
    end
    self.control:RegisterForEvent(EVENT_FINISHED_SMITHING_TRAIT_RESEARCH, HandleDirtyEvent)
    self.dirty = true
    control:SetHandler("OnUpdate", function() self:OnUpdate() end)
end
function ZO_SharedSmithingCreation:OnUpdate()
    if self.tooltipDirty then
        self:UpdateTooltipInternal()
        self.tooltipDirty = false
    end
end
function ZO_SharedSmithingCreation:SetCraftingType(craftingType, oldCraftingType, isCraftingTypeDifferent)
    if isCraftingTypeDifferent or not self.typeFilter then
        self.selectedMaterialCountCache = {}
        self:RefreshAvailableFilters()
    end
end
function ZO_SharedSmithingCreation:HandleDirtyEvent()
    if not self.performingFullRefresh then
        if self.control:IsHidden() then
            self.dirty = true
        else
            self:RefreshAllLists()
        end
    end
end
local CRAFTING_TYPE_TO_TOOLTIP_SOUND =
{
    [CRAFTING_TYPE_BLACKSMITHING] = SOUNDS.BLACKSMITH_CREATE_TOOLTIP_GLOW,
    [CRAFTING_TYPE_CLOTHIER] = SOUNDS.CLOTHIER_CREATE_TOOLTIP_GLOW,
    [CRAFTING_TYPE_WOODWORKING] = SOUNDS.WOODWORKER_CREATE_TOOLTIP_GLOW
}
function ZO_SharedSmithingCreation:GetCreateTooltipSound()
    local craftingType = GetCraftingInteractionType()
    return CRAFTING_TYPE_TO_TOOLTIP_SOUND[craftingType]
end
function ZO_SharedSmithingCreation:RefreshVisiblePatterns()
    if not self.performingFullRefresh then
        self.patternList:RefreshVisible()
    end
end
function ZO_SharedSmithingCreation:RefreshAllLists()
    if self.typeFilter then
        self.dirty = false
        self.performingFullRefresh = true
        self:RefreshStyleList()
        self:RefreshPatternList()
        self:RefreshTraitList()
        self.performingFullRefresh = false
        self:OnSelectedPatternChanged(self.patternList:GetSelectedData())
    end
    self:OnRefreshAllLists()
    -- Special case on full refreshes, the style list depends on the pattern list, but the pattern list is also dependent on knowing if there's any valid styles.
    -- If there are no valid styles then none of the patterns can be selected, so clear it out.
    if not self.styleList:GetSelectedData() then
        self.patternList:Clear()
        self.patternList:Commit()
    end
end
function ZO_SharedSmithingCreation:OnRefreshAllLists()
    --No base implementation
end
local USABILITY_TYPE_INVALID = nil
local USABILITY_TYPE_USABLE = true
local USABILITY_TYPE_VALID_BUT_MISSING_REQUIREMENT = false
function ZO_SharedSmithingCreation:GetSelectedPatternIndex()
    return self.patternList:GetSelectedData() and self.patternList:GetSelectedData().patternIndex
end
function ZO_SharedSmithingCreation:GetSelectedMaterialIndex()
    return self.materialList:GetSelectedData() and self.materialList:GetSelectedData().materialIndex
end
function ZO_SharedSmithingCreation:GetSelectedMaterialQuantity()
    local selectedData = self.materialList:GetSelectedData()
    if selectedData then
        return self:GetMaterialQuantity(selectedData.patternIndex, selectedData.materialIndex) or 0
    end
    return 0
end
function ZO_SharedSmithingCreation:GetSelectedStyleIndex()
    return self.styleList:GetSelectedData() and self.styleList:GetSelectedData().styleIndex
end
function ZO_SharedSmithingCreation:GetSelectedTraitIndex()
    return self.traitList:GetSelectedData() and self.traitList:GetSelectedData().traitIndex
end
function ZO_SharedSmithingCreation:GetIsUsingUniversalStyleItem()
    return self.savedVars.useUniversalStyleItemChecked
end
function ZO_SharedSmithingCreation:GetAllCraftingParameters()
    return self:GetSelectedPatternIndex(), self:GetSelectedMaterialIndex(), 
end
function ZO_SharedSmithingCreation:GetAllNonTraitCraftingParameters()
    return self:GetSelectedPatternIndex(), self:GetSelectedMaterialIndex(), 
           self:GetSelectedMaterialQuantity(), self:GetSelectedStyleIndex()
end
function ZO_SharedSmithingCreation:OnSelectedPatternChanged(patternData, selectedDuringRebuild)
    if self:IsInvalidMode() then return end
    if not self.performingFullRefresh then
        self.performingFullRefresh = true
        
        if not selectedDuringRebuild then
               local oldStyle = self:GetSelectedStyleIndex()
               self:RefreshStyleList()
               local newStyle = self:GetSelectedStyleIndex()
               if newStyle ~= oldStyle then
                    self.styleList:RefreshVisible()
                    self.patternList:RefreshVisible()
               end
            self:RefreshMaterialList(patternData)
            self:RefreshTraitList()
        end
        self.materialList:RefreshVisible()
        if self.keybindStripDescriptor then
            KEYBIND_STRIP:UpdateKeybindButtonGroup(self.keybindStripDescriptor)
        else
            self.owner:OnSelectedPatternChanged()
        end
        self:UpdateTooltip()
        self.performingFullRefresh = false
    end
end
function ZO_SharedSmithingCreation:SelectValidKnowledgeIndices()
     local patternIndex = self:GetSelectedPatternIndex()
     local styleIndex = self:GetSelectedStyleIndex()
     if styleIndex and patternIndex then
          if not IsSmithingStyleKnown(styleIndex, patternIndex) then 
               styleIndex = GetFirstKnownStyleIndex(patternIndex)
               self.styleList:SetSelectedDataIndex(styleIndex)
               self.styleList:RefreshVisible()
               return
          end
     end
     for patternIndex = 1, GetNumSmithingPatterns() do
          styleIndex = GetFirstKnownStyleIndex(patternIndex)
          if styleIndex then
               self.patternList:SetSelectedDataIndex(patternIndex)
               self.styleList:SetSelectedDataIndex(styleIndex)
               self.styleList:RefreshVisible()
               return
          end
     end
end
function ZO_SharedSmithingCreation:OnFilterChanged(haveMaterialsChecked, haveKnowledgeChecked, useUniversalStyleItemChecked)
    self.savedVars.haveMaterialChecked = haveMaterialsChecked
     local hadKnowledgeChecked = self.savedVars.haveKnowledgeChecked
    self.savedVars.haveKnowledgeChecked = haveKnowledgeChecked
     if not hadKnowledgeChecked and self.savedVars.haveKnowledgeChecked then
          self:SelectValidKnowledgeIndices()
     end
    self.savedVars.useUniversalStyleItemChecked = useUniversalStyleItemChecked
    self:HandleDirtyEvent()
    if useUniversalStyleItemChecked then
        TriggerTutorial(TUTORIAL_TRIGGER_UNIVERSAL_STYLE_ITEM)
    end
end
ZO_SMITHING_CREATION_FILTER_TYPE_WEAPONS = 1
ZO_SMITHING_CREATION_FILTER_TYPE_ARMOR = 2
ZO_SMITHING_CREATION_FILTER_TYPE_SET_WEAPONS = 3
ZO_SMITHING_CREATION_FILTER_TYPE_SET_ARMOR = 4
function ZO_SharedSmithingCreation:ChangeTypeFilter(filterData)
    self.typeFilter = filterData.descriptor
    self:HandleDirtyEvent()
end
local MIN_SCALE = .6
local MAX_SCALE = 1.0
local BASE_NUM_ITEMS_IN_LIST = 5
local function CustomTooltipAnchor(tooltip, button)
    local centerX, centerY = button:GetCenter()
    local parentCenterX, parentCenterY = button:GetParent():GetCenter()
    tooltip:SetOwner(button:GetParent(), BOTTOM, centerX - parentCenterX, centerY - parentCenterY)
end
local function SetupSharedSlot(control, slotType, listContainer, list)
    ZO_InventorySlot_SetType(control, slotType)
    control.isMoving = list:IsMoving()
    if not control.isMoving then
    end
end
local function SetHighlightColor(highlightTexture, usable)
    if highlightTexture then
        if usable then
            highlightTexture:SetColor(1, 1, 1, highlightTexture:GetAlpha())
        else
            highlightTexture:SetColor(1, 0, 0, highlightTexture:GetAlpha())
        end
    end
end
local function OnHorizonalScrollListShown(list)
    local listContainer = list:GetControl():GetParent() 
    listContainer.selectedLabel:SetHidden(false)
end
function ZO_SharedSmithingCreation:OnHorizonalScrollListCleared(list)
    local listContainer = list:GetControl():GetParent() 
    listContainer.selectedLabel:SetHidden(true)
    self:SetLabelHidden(listContainer.extraInfoLabel, true)
end
function ZO_SharedSmithingCreation:IsInvalidMode()
    local type = GetCraftingInteractionType()
    return (type == CRAFTING_TYPE_INVALID) or (self.owner.mode ~= SMITHING_MODE_CREATION)
end
function ZO_SharedSmithingCreation:InitializePatternList(scrollListControl, listSlotTemplate)
    local listContainer = self.control:GetNamedChild("PatternList")
    listContainer.titleLabel:SetText(GetString(SI_SMITHING_HEADER_ITEM))
    local function SetupFunction(control, data, selected, selectedDuringRebuild, enabled)
        if self:IsInvalidMode() then return end
        local patternIndex = data.patternIndex
        local materialOverride = self:GetSelectedMaterialIndex()
        local materialQuantityOverride = select(3, GetSmithingPatternMaterialItemInfo(patternIndex, materialOverride))
        local styleOverride = self.styleList:GetSelectedData() and self.styleList:GetSelectedData().itemStyle
        local traitOverride = self.traitList:GetSelectedData() and self.traitList:GetSelectedData().traitType
        local _, _, icon, _, _, _, _ = GetSmithingPatternInfo(patternIndex, materialOverride, materialQuantityOverride, styleOverride, traitOverride)
          local styleIndex = self:GetSelectedStyleIndex()
          if not styleIndex then styleIndex = 2 end -- this is actually the default for this, not 0 or 1
        local isStyleKnown = IsSmithingStyleKnown(styleIndex, patternIndex)
        local meetsTraitRequirement = data.numTraitsRequired <= data.numTraitsKnown 
        local usable = meetsTraitRequirement and isStyleKnown
        ZO_ItemSlot_SetupSlot(control, 1, icon, usable, not enabled)
        if selected then
            if data.numTraitsRequired > 0 then
                listContainer.selectedLabel:SetText(self:GetPlatformFormattedTextString(SI_SMITHING_SELECTED_PATTERN, data.patternName, data.numTraitsRequired))
            else
                listContainer.selectedLabel:SetText(zo_strformat(SI_SMITHING_SELECTED_PATTERN_NO_TRAITS, data.patternName))
            end
            SetHighlightColor(highlightTexture, usable)
            self.isPatternUsable = usable and USABILITY_TYPE_USABLE or USABILITY_TYPE_VALID_BUT_MISSING_REQUIREMENT
        end
    end
    local function EqualityFunction(leftData, rightData)
        return leftData.craftingType == rightData.craftingType and leftData.patternIndex == rightData.patternIndex
    end
    local function OnHorizonalScrollListCleared(...)
        self:OnHorizonalScrollListCleared(...)
    end
    self.patternList = scrollListControl:New(listContainer.listControl, listSlotTemplate, BASE_NUM_ITEMS_IN_LIST, SetupFunction, EqualityFunction, OnHorizonalScrollListShown, OnHorizonalScrollListCleared)
     self.patternList:SetOnSelectedDataChangedCallback(function(selectedData, oldData, selectedDuringRebuild)
        self:OnSelectedPatternChanged(selectedData, selectedDuringRebuild)
    end)
    local highlightTexture = listContainer.highlightTexture
    self.patternList:SetSelectionHighlightInfo(highlightTexture, highlightTexture and highlightTexture.pulseAnimation)
    self.patternList:SetScaleExtents(MIN_SCALE, MAX_SCALE)
end
local function GetRankTooLowString()
    local craftingType = GetCraftingInteractionType()
    if craftingType == CRAFTING_TYPE_BLACKSMITHING then
        return SI_SMITHING_RANK_TOO_LOW_BLACKSMITHING
    elseif craftingType == CRAFTING_TYPE_CLOTHIER then
        return SI_SMITHING_RANK_TOO_LOW_CLOTHIER
    elseif craftingType == CRAFTING_TYPE_WOODWORKING then
        return SI_SMITHING_RANK_TOO_LOW_WOODWORKING
    end
end
function ZO_SharedSmithingCreation:GetMaterialInformation(data)
     local stackCount = GetCurrentSmithingMaterialItemCount(data.patternIndex, data.materialIndex)
     local currentSelectedQuantity = self:GetMaterialQuantity(data.patternIndex, data.materialIndex)
     local currentRank = GetCurrentCraftingLevel()
     local meetsRankRequirement = currentRank >= data.rankRequirement
     local hasAboveMin = stackCount >= data.min
     local hasEnoughInInventory = currentSelectedQuantity <= stackCount and self.materialQuantitySpinner:GetValue() <= stackCount
     local usable = meetsRankRequirement and hasAboveMin and hasEnoughInInventory
     return stackCount, currentSelectedQuantity, currentRank, meetsRankRequirement, hasAboveMin, hasEnoughInInventory, usable
end
function ZO_SharedSmithingCreation:InitializeMaterialList(scrollListControl, spinnerControl, hideSpinnerWhenRankRequirementNotMet, listSlotTemplate)
    local listContainer = self.control:GetNamedChild("MaterialList")
    local highlightTexture = listContainer.highlightTexture
    listContainer.titleLabel:SetText(GetString(SI_SMITHING_HEADER_MATERIAL))
    self.materialQuantitySpinner = spinnerControl:New(listContainer:GetNamedChild("Spinner"))
    self.materialQuantitySpinner:RegisterCallback("OnValueChanged", function(value)
        if not self.performingFullRefresh then
            self:AdjustCurrentMaterialQuantityForAllPatterns(value)
        end
          local data = self.materialList:GetSelectedData()
          local stackCount, currentSelectedQuality, currentRank, meetsRankRequirement, hasAboveMin, hasEnoughInInventory, usable = self:GetMaterialInformation(data)
          self.isMaterialUsable = usable and USABILITY_TYPE_USABLE or USABILITY_TYPE_VALID_BUT_MISSING_REQUIREMENT
          ZO_ItemSlot_SetupSlot(self.selectedMaterialControl, stackCount, data.icon, usable)
          self:UpdateTooltip()
          self.owner:OnSelectedPatternChanged()
          KEYBIND_STRIP:UpdateKeybindButtonGroup(self.keybindStripDescriptor)
    end)
    ZO_CraftingUtils_ConnectSpinnerToCraftingProcess(self.materialQuantitySpinner)
    local function SetupFunction(control, data, selected, selectedDuringRebuild, enabled)
        if self:IsInvalidMode() then return end
        SetupSharedSlot(control, SLOT_TYPE_SMITHING_MATERIAL, listContainer, self.materialList)
        control.patternIndex = data.patternIndex
        control.materialIndex = data.materialIndex
          local stackCount, currentSelectedQuantity, currentRank, meetsRankRequirement, hasAboveMin, hasEnoughInInventory, usable = self:GetMaterialInformation(data)
        ZO_ItemSlot_SetupSlot(control, stackCount, data.icon, usable, not enabled)
        if selected then
               self.selectedMaterialControl = control
            SetHighlightColor(highlightTexture, usable)
            local function ShowHideSpinnerIfNeeded(hidden)
                if hideSpinnerWhenRankRequirementNotMet then
                    self.materialQuantitySpinner:GetControl():SetHidden(hidden)
                end
            end
            if usable then
                self:SetLabelHidden(listContainer.extraInfoLabel, true)
                ShowHideSpinnerIfNeeded(false)
            else
                if not meetsRankRequirement then
                    self:SetLabelHidden(listContainer.extraInfoLabel, false)
                    listContainer.extraInfoLabel:SetText(zo_strformat(GetRankTooLowString(), data.rankRequirement))
                    ShowHideSpinnerIfNeeded(true)
                else
                    self:SetLabelHidden(listContainer.extraInfoLabel, true)
                    ShowHideSpinnerIfNeeded(false)
                end
            end
            self.isMaterialUsable = usable and USABILITY_TYPE_USABLE or USABILITY_TYPE_VALID_BUT_MISSING_REQUIREMENT
            
            self.materialQuantitySpinner:SetValidValuesFunction(function(startingPoint, step)
                return GetSmithingPatternNextMaterialQuantity(data.patternIndex, data.materialIndex, startingPoint, step)
            end)
            self.materialQuantitySpinner:SetMinMax(data.min, data.max)
            self.materialQuantitySpinner:SetSoftMax(stackCount)
            self.materialQuantitySpinner:SetValue(currentSelectedQuantity)
            listContainer.selectedLabel:SetText(self:GetPlatformFormattedTextString(SI_SMITHING_MATERIAL_QUANTITY, data.name, data.min, data.max))
            if not selectedDuringRebuild then
                self:RefreshVisiblePatterns()
                self:RefreshStyleList()
            end
        end
    end
    local function EqualityFunction(leftData, rightData)
        return leftData.craftingType == rightData.craftingType and leftData.name == rightData.name
    end
    local function OnMaterialHorizonalScrollListShown(list)
        self.materialQuantitySpinner:GetControl():SetHidden(false)
    end
    local function OnMaterialHorizonalScrollListCleared(list)
        self:OnHorizonalScrollListCleared(list)
        self.materialQuantitySpinner:GetControl():SetHidden(true)
    end
    self.materialList = scrollListControl:New(listContainer.listControl, listSlotTemplate, BASE_NUM_ITEMS_IN_LIST, SetupFunction, EqualityFunction, OnMaterialHorizonalScrollListShown, OnMaterialHorizonalScrollListCleared)
    self.materialList:SetNoItemText(GetString(SI_SMITHING_NO_MATERIALS_FOUND))
    self.materialList:SetSelectionHighlightInfo(highlightTexture, highlightTexture and highlightTexture.pulseAnimation)
    self.materialList:SetScaleExtents(MIN_SCALE, MAX_SCALE)
end
function ZO_SharedSmithingCreation:InitializeStyleList(scrollListControl, styleUnknownFont, notEnoughInInventoryFont, listSlotTemplate)
    local listContainer = self.control:GetNamedChild("StyleList")
    local highlightTexture = listContainer.highlightTexture
    listContainer.titleLabel:SetText(GetString(SI_SMITHING_HEADER_STYLE))
    local function SetupFunction(control, data, selected, selectedDuringRebuild, enabled)
        if self:IsInvalidMode() then return end
        SetupSharedSlot(control, SLOT_TYPE_SMITHING_STYLE, listContainer, self.styleList)
        control.styleIndex = data.styleIndex
        local usesUniversalStyleItem = self:GetIsUsingUniversalStyleItem()
        local stackCount = GetCurrentSmithingStyleItemCount(data.styleIndex)
        local hasEnoughInInventory = stackCount > 0
        local universalStyleItemCount = GetCurrentSmithingStyleItemCount(ZO_ADJUSTED_UNIVERSAL_STYLE_ITEM_INDEX)
        local isStyleKnown = IsSmithingStyleKnown(data.styleIndex, self:GetSelectedPatternIndex())
        local usable = ((stackCount > 0 and not usesUniversalStyleItem) or (usesUniversalStyleItem and universalStyleItemCount > 0)) and isStyleKnown
        ZO_ItemSlot_SetupSlot(control, stackCount, data.icon, usable, not enabled)
        local stackCountLabel = GetControl(control, "StackCount")
        stackCountLabel:SetHidden(usesUniversalStyleItem)
        if selected then
            SetHighlightColor(highlightTexture, usable)
            self:SetLabelHidden(listContainer.extraInfoLabel, true)
            if not usable then
                if not isStyleKnown then
                    self:SetLabelHidden(listContainer.extraInfoLabel, false)
                    listContainer.extraInfoLabel:SetText(GetString(SI_SMITHING_UNKNOWN_STYLE))
                elseif not hasEnoughInInventory then
                    -- do nothing, already hidden above
                end
            end
            local universalStyleItemCount = GetCurrentSmithingStyleItemCount(ZO_ADJUSTED_UNIVERSAL_STYLE_ITEM_INDEX)
            self.isStyleUsable = usable and USABILITY_TYPE_USABLE or USABILITY_TYPE_VALID_BUT_MISSING_REQUIREMENT
            if not data.localizedName then
                if data.itemStyle == ITEMSTYLE_NONE then
                    data.localizedName = GetString("SI_ITEMSTYLE", data.itemStyle)
                else
                    if usesUniversalStyleItem then
                        data.localizedName = self:GetPlatformFormattedTextString(SI_CRAFTING_UNIVERSAL_STYLE_DESCRIPTION, GetString("SI_ITEMSTYLE", data.itemStyle))
                    else
                        data.localizedName = self:GetPlatformFormattedTextString(SI_SMITHING_STYLE_DESCRIPTION, data.name, GetString("SI_ITEMSTYLE", data.itemStyle))
                    end
                end
            end
            
            listContainer.selectedLabel:SetText(data.localizedName)
            if not selectedDuringRebuild then
                self:RefreshVisiblePatterns()
            end
        end
    end
    local function EqualityFunction(leftData, rightData)
        return leftData.craftingType == rightData.craftingType and leftData.name == rightData.name
    end
    local function OnHorizonalScrollListCleared(...)
        self:OnHorizonalScrollListCleared(...)
    end
    self.styleList = scrollListControl:New(listContainer.listControl, listSlotTemplate, BASE_NUM_ITEMS_IN_LIST, SetupFunction, EqualityFunction, OnHorizonalScrollListShown, OnHorizonalScrollListCleared)
    self.styleList:SetNoItemText(GetString(SI_SMITHING_NO_STYLE_FOUND))
    self.styleList:SetSelectionHighlightInfo(highlightTexture, highlightTexture and highlightTexture.pulseAnimation)
    self.styleList:SetScaleExtents(MIN_SCALE, MAX_SCALE)
    self.styleList:SetOnSelectedDataChangedCallback(function(selectedData, oldData, selectedDuringRebuild)
        self:UpdateTooltip()
        self.owner:OnSelectedStyleChanged()
        self:OnStyleChanged(selectedData)
    end)
end
function ZO_SharedSmithingCreation:OnStyleChanged(selectedData)
    -- no additional functionality needed at the shared level
end
function ZO_SharedSmithingCreation:InitializeTraitList(scrollListControl, traitUnknownFont, notEnoughInInventoryFont, listSlotTemplate)
    local listContainer = self.control:GetNamedChild("TraitList")
    local highlightTexture = listContainer.highlightTexture
    listContainer.titleLabel:SetText(GetString(SI_SMITHING_HEADER_TRAIT))
    local function SetupFunction(control, data, selected, selectedDuringRebuild, enabled)
        if self:IsInvalidMode() then return end
        SetupSharedSlot(control, SLOT_TYPE_SMITHING_TRAIT, listContainer, self.traitList)
        ZO_ItemSlot_SetAlwaysShowStackCount(control, data.traitType ~= ITEM_TRAIT_TYPE_NONE)
        control.traitIndex = data.traitIndex
        control.traitType = data.traitType
        local stackCount = GetCurrentSmithingTraitItemCount(data.traitIndex)
        local hasEnoughInInventory = stackCount > 0
        local isTraitKnown = false
        if self:IsCraftableWithoutTrait() then
            local patternIndex, materialIndex, materialQty, styleIndex = self:GetAllNonTraitCraftingParameters()
            isTraitKnown = IsSmithingTraitKnownForResult(patternIndex, materialIndex, materialQty, styleIndex, data.traitIndex)
        end
        local usable = data.traitType == ITEM_TRAIT_TYPE_NONE or (hasEnoughInInventory and isTraitKnown)
        ZO_ItemSlot_SetupSlot(control, stackCount, data.icon, usable, not enabled)
        if selected then
            SetHighlightColor(highlightTexture, usable)
            
            self:SetLabelHidden(listContainer.extraInfoLabel, usable or data.traitType == ITEM_TRAIT_TYPE_NONE)
            if usable then
                self.isTraitUsable = USABILITY_TYPE_USABLE
            else
                self.isTraitUsable = USABILITY_TYPE_VALID_BUT_MISSING_REQUIREMENT
                if not isTraitKnown then
                    listContainer.extraInfoLabel:SetText(GetString(SI_SMITHING_TRAIT_MUST_BE_RESEARCHED))
                elseif not hasEnoughInInventory then
                    self:SetLabelHidden(listContainer.extraInfoLabel, true)
                end
            end
            if not data.localizedName then
                if data.traitType == ITEM_TRAIT_TYPE_NONE then
                    data.localizedName = GetString("SI_ITEMTRAITTYPE", data.traitType)
                else
                    data.localizedName = self:GetPlatformFormattedTextString(SI_SMITHING_TRAIT_DESCRIPTION, data.name, GetString("SI_ITEMTRAITTYPE", data.traitType))
                end
            end
            
            listContainer.selectedLabel:SetText(data.localizedName)
            if not selectedDuringRebuild then
                self:RefreshVisiblePatterns()
            end
        end
    end
    local function EqualityFunction(leftData, rightData)
        return leftData.craftingType == rightData.craftingType and leftData.name == rightData.name
    end
    local function OnHorizonalScrollListCleared(...)
        self:OnHorizonalScrollListCleared(...)
    end
    self.traitList = scrollListControl:New(listContainer.listControl, listSlotTemplate, BASE_NUM_ITEMS_IN_LIST, SetupFunction, EqualityFunction, OnHorizonalScrollListShown, OnHorizonalScrollListCleared)
    self.traitList:SetSelectionHighlightInfo(highlightTexture, highlightTexture and highlightTexture.pulseAnimation)
    self.traitList:SetScaleExtents(MIN_SCALE, MAX_SCALE)
    self.traitList:SetOnSelectedDataChangedCallback(function(selectedData, oldData, selectedDuringRebuild)
        self:UpdateTooltip()
        self.owner:OnSelectedTraitChanged()
    end)
end
function ZO_SharedSmithingCreation:DoesPatternPassFilter(patternData)
    if patternData.resultingItemFilterType == ITEMFILTERTYPE_WEAPONS then
        if self.typeFilter == ZO_SMITHING_CREATION_FILTER_TYPE_WEAPONS then
            if patternData.numTraitsRequired ~= 0 then
                return false
            end
        elseif self.typeFilter == ZO_SMITHING_CREATION_FILTER_TYPE_SET_WEAPONS then
            if patternData.numTraitsRequired == 0 then
                return false
            end
        else
            return false
        end
    elseif patternData.resultingItemFilterType == ITEMFILTERTYPE_ARMOR then
        if self.typeFilter == ZO_SMITHING_CREATION_FILTER_TYPE_ARMOR  then
            if patternData.numTraitsRequired ~= 0 then
                return false
            end
        elseif self.typeFilter == ZO_SMITHING_CREATION_FILTER_TYPE_SET_ARMOR then
            if patternData.numTraitsRequired == 0 then
                return false
            end
        else
            return false
        end
    end
    
    if self.savedVars.haveKnowledgeChecked then
        if patternData.numTraitsKnown < patternData.numTraitsRequired then
            return false
        end
        patternData.materialData = patternData.materialData or self:GenerateMaterialDataForPattern(patternData.patternIndex)
        if #patternData.materialData == 0 then
            return false
        end
    end
    if self.savedVars.haveMaterialChecked then
        patternData.materialData = patternData.materialData or self:GenerateMaterialDataForPattern(patternData.patternIndex)
        if #patternData.materialData == 0 then
            return false
        end
    end
    return true
end
function ZO_SharedSmithingCreation:CreatePatternList()
     self.patternList:Clear()
    for patternIndex = 1, GetNumSmithingPatterns() do
        local patternName, baseName, _, numMaterials, numTraitsRequired, numTraitsKnown, resultingItemFilterType = GetSmithingPatternInfo(patternIndex)
          local styleKnown = IsSmithingStyleKnown(self:GetSelectedStyleIndex(), patternIndex)
        if numMaterials > 0 then
            local data = { craftingType = GetCraftingInteractionType(), patternIndex = patternIndex, patternName = patternName, baseName = baseName, numTraitsRequired = numTraitsRequired, numTraitsKnown = numTraitsKnown, resultingItemFilterType = resultingItemFilterType, styleKnown = styleKnown }
            if self:DoesPatternPassFilter(data) then
                self.patternList:AddEntry(data)
            end
        end
    end
     self.patternList:Commit()
end
function ZO_SharedSmithingCreation:RefreshPatternList()
    self:CreatePatternList()
    if self.typeFilter == ZO_SMITHING_CREATION_FILTER_TYPE_WEAPONS or self.typeFilter == ZO_SMITHING_CREATION_FILTER_TYPE_SET_WEAPONS then
        self.patternList:SetNoItemText(GetString(SI_SMITHING_NO_WEAPONS_FOUND))
    elseif self.typeFilter == ZO_SMITHING_CREATION_FILTER_TYPE_ARMOR or self.typeFilter == ZO_SMITHING_CREATION_FILTER_TYPE_SET_ARMOR then
        self.patternList:SetNoItemText(GetString(SI_SMITHING_NO_ARMOR_FOUND))
    end
end
function ZO_SharedSmithingCreation:DoesMaterialPassFilter(data)
    if self.savedVars.haveKnowledgeChecked then
        if GetCurrentCraftingLevel() < data.rankRequirement then
            return false
        end
    end
    if self.savedVars.haveMaterialChecked then
        if GetCurrentSmithingMaterialItemCount(data.patternIndex, data.materialIndex) < data.min then
            return false
        end
    end
    return true
end
function ZO_SharedSmithingCreation:GenerateMaterialDataForPattern(patternIndex)
    local instanceFilter = {}
    local _, _, _, numMaterials = GetSmithingPatternInfo(patternIndex)
    for materialIndex = 1, numMaterials do
        local name, icon, stack, sellPrice, meetsUsageRequirement, equipType, itemStyle, quality, itemInstanceId, rankRequirement = GetSmithingPatternMaterialItemInfo(patternIndex, materialIndex)
        if instanceFilter[itemInstanceId] then
            local existingData = instanceFilter[itemInstanceId]
            existingData.min = zo_min(existingData.min, stack)
            if not self:GetMaterialQuantity(patternIndex, materialIndex) then
                self:SetMaterialQuantity(patternIndex, materialIndex, existingData.min)
            end
            existingData.max = zo_max(existingData.max, stack)
        else
            local data = { craftingType = GetCraftingInteractionType(), patternIndex = patternIndex, materialIndex = materialIndex, name = name, icon = icon, quality = quality, rankRequirement = rankRequirement, min = stack, max = stack }
            if not self:GetMaterialQuantity(patternIndex, materialIndex) then
                self:SetMaterialQuantity(patternIndex, materialIndex, stack)
            end
            instanceFilter[itemInstanceId] = data
            instanceFilter[#instanceFilter + 1] = data
        end
    end
    local materialData = {}
    for i, data in ipairs(instanceFilter) do
        if self:DoesMaterialPassFilter(data) then
            materialData[#materialData + 1] = data
        end
    end
    return materialData
end
function ZO_SharedSmithingCreation:RefreshMaterialList(patternData)
    local oldSelectedData = self.materialList:GetSelectedData()
    local oldArmorType = nil
    local newArmorType = nil
    if (oldSelectedData) then
        oldArmorType = GetSmithingPatternArmorType(oldSelectedData.patternIndex)
    end
    self.materialList:Clear()
    if patternData then
        newArmorType = GetSmithingPatternArmorType(patternData.patternIndex)
        patternData.materialData = patternData.materialData or self:GenerateMaterialDataForPattern(patternData.patternIndex)
        for itemInstanceId, data in pairs(patternData.materialData) do
            self.materialList:AddEntry(data)
        end
    end
    self.materialList:Commit()
    if (oldArmorType and newArmorType) and (oldArmorType ~= newArmorType) then
        local index = self.materialList:FindIndexFromData(oldSelectedData, function(oldData, newData) return (oldData.rankRequirement == newData.rankRequirement) end)
        if (index) then
            self.materialList:SetSelectedIndex(index)
        end
    end
end
function ZO_SharedSmithingCreation:DoesStylePassFilter(styleIndex, alwaysHideIfLocked)
    if self.savedVars.haveKnowledgeChecked or alwaysHideIfLocked then
        if not IsSmithingStyleKnown(styleIndex, self:GetSelectedPatternIndex()) then
            return false
        end
    end
    if self.savedVars.haveMaterialChecked then
        if GetCurrentSmithingStyleItemCount(styleIndex) == 0 and not self:GetIsUsingUniversalStyleItem() then
            return false
        end
    end
    if styleIndex == ZO_ADJUSTED_UNIVERSAL_STYLE_ITEM_INDEX then
        return false
    end
    local patternData = self.patternList:GetSelectedData()
    if patternData then
        patternData.materialData = patternData.materialData or self:GenerateMaterialDataForPattern(patternData.patternIndex)
        if #patternData.materialData == 0 then
            return false
        end
        if not CanSmithingStyleBeUsedOnPattern(styleIndex, self:GetSelectedPatternIndex(), patternData.materialData[1].materialIndex, patternData.materialData[1].min) then
            return false
        end
    end
    return true
end
local STYLE_LIST_USI_BG_STANDARD_ALPHA = 0.35
local STYLE_LIST_USI_BG_LOW_ALPHA = 0.21
function ZO_SharedSmithingCreation:RefreshStyleList()
    self.styleList:Clear()
    for styleIndex = 1, GetNumSmithingStyleItems() do
        local name, icon, sellPrice, meetsUsageRequirement, itemStyle, quality, alwaysHideIfLocked = GetSmithingStyleItemInfo(styleIndex)
        if meetsUsageRequirement and self:DoesStylePassFilter(styleIndex, alwaysHideIfLocked) then
            self.styleList:AddEntry({ craftingType = GetCraftingInteractionType(), styleIndex = styleIndex, name = name, itemStyle = itemStyle, icon = icon, quality = quality })
        end
    end
    self.styleList:Commit()
    local styleListControl = self.control:GetNamedChild("StyleList")
    if self:GetIsUsingUniversalStyleItem() then
        local universalItemBg = styleListControl.universalItemBg
        universalItemBg:SetHidden(false)
        if GetCurrentSmithingStyleItemCount(ZO_ADJUSTED_UNIVERSAL_STYLE_ITEM_INDEX) == 0 then
            universalItemBg:SetColor(ZO_ERROR_COLOR:UnpackRGBA())
            universalItemBg:SetAlpha(STYLE_LIST_USI_BG_LOW_ALPHA)
        else
            universalItemBg:SetColor(ZO_COLOR_UNIVERSAL_ITEM:UnpackRGBA())
            universalItemBg:SetAlpha(STYLE_LIST_USI_BG_STANDARD_ALPHA)
        end
    else
        styleListControl.universalItemBg:SetHidden(true)
    end
end
function ZO_SharedSmithingCreation:DoesTraitPassFilter(traitIndex, traitType)
    assert(self.typeFilter)
    if ZO_CraftingUtils_IsTraitAppliedToWeapons(traitType) then
        if self.typeFilter == ZO_SMITHING_CREATION_FILTER_TYPE_ARMOR or self.typeFilter == ZO_SMITHING_CREATION_FILTER_TYPE_SET_ARMOR then
            return false
        end
    elseif ZO_CraftingUtils_IsTraitAppliedToArmor(traitType) then
        if self.typeFilter == ZO_SMITHING_CREATION_FILTER_TYPE_WEAPONS or self.typeFilter == ZO_SMITHING_CREATION_FILTER_TYPE_SET_WEAPONS then
            return false
        end
    end
    if self.savedVars.haveKnowledgeChecked then
        if not self:IsCraftableWithoutTrait() then
            return false
        end
        local patternIndex, materialIndex, materialQty, styleIndex = self:GetAllNonTraitCraftingParameters()
        if not IsSmithingTraitKnownForResult(patternIndex, materialIndex, materialQty, styleIndex, traitIndex) then
            return false
        end
    end
    if self.savedVars.haveMaterialChecked then
        if GetCurrentSmithingTraitItemCount(traitIndex) == 0 then
            return false
        end
    end
    return true
end
function ZO_SharedSmithingCreation:RefreshTraitList()
    self.traitList:Clear()
    for traitIndex = 1, GetNumSmithingTraitItems() do
        local traitType, name, icon, sellPrice, meetsUsageRequirement, itemStyle, quality = GetSmithingTraitItemInfo(traitIndex)
        if traitType then
            if traitType == ITEM_TRAIT_TYPE_NONE then
                self.traitList:AddEntry({ craftingType = GetCraftingInteractionType(), traitIndex = traitIndex, traitType = traitType, icon = "EsoUI/Art/Crafting/crafting_smithing_noTrait.dds" })
            elseif self:DoesTraitPassFilter(traitIndex, traitType) then
                self.traitList:AddEntry({ craftingType = GetCraftingInteractionType(), traitIndex = traitIndex, name = name, traitType = traitType, icon = icon, quality = quality })
            end
        end
    end
    self.traitList:Commit()
end
function ZO_SharedSmithingCreation:UpdateTooltip()
    self.tooltipDirty = true
end
function ZO_SharedSmithingCreation:UpdateTooltipInternal()
    if self:AreSelectionsValid() then
        self.resultTooltip:SetHidden(false)
        self.resultTooltip:ClearLines()
        self:SetupResultTooltip(self:GetAllCraftingParameters())
    else
        self.resultTooltip:SetHidden(true)
    end
end
function ZO_SharedSmithingCreation:AdjustCurrentMaterialQuantityForAllPatterns(updatedQuantity)
    local selectedData = self.materialList:GetSelectedData()
    if selectedData then
        local selectedDataMaterialIndex = selectedData.materialIndex -- keep track of the material to update
        local patternCount = #self.patternList.list
        for patternListIndex = 1, patternCount do
            local minQuantity = updatedQuantity
            local maxQuantity = updatedQuantity
            -- before updating the material quantity, do a safety check against the min and max for the material
            local patternData = self.patternList.list[patternListIndex]
            local patternIndex = patternData.patternIndex
            local currentPatternMaterialData = patternData.materialData or self:GenerateMaterialDataForPattern(patternIndex)
            if currentPatternMaterialData then
                local materialCount = #currentPatternMaterialData
                for materialListIndex = 1, materialCount do
                    local currentMaterialData = currentPatternMaterialData[materialListIndex]
                    if currentMaterialData.materialIndex == selectedDataMaterialIndex then
                        minQuantity = currentMaterialData.min
                        maxQuantity = currentMaterialData.max
                        break
                    end
                end
            end
            -- safe update based on material min and max
            if self:GetMaterialQuantity(patternIndex, selectedDataMaterialIndex) and updatedQuantity >= minQuantity and updatedQuantity <= maxQuantity then
                --then get a valid quantity for this pattern and material closest to the updatedQuantity
                local validQuantity = GetSmithingPatternNextMaterialQuantity(patternIndex, selectedDataMaterialIndex, updatedQuantity, 0)
                self:SetMaterialQuantity(patternIndex, selectedDataMaterialIndex, validQuantity)
            end
        end
    end
end
function ZO_SharedSmithingCreation:SetMaterialQuantity(patternIndex, materialIndex, quantity)
    if not self.selectedMaterialCountCache then 
          self.selectedMaterialCountCache = {}
     end
     
     if not self.selectedMaterialCountCache[patternIndex] then
        self.selectedMaterialCountCache[patternIndex] = {}
    end
    self.selectedMaterialCountCache[patternIndex][materialIndex] = quantity
end
function ZO_SharedSmithingCreation:GetMaterialQuantity(patternIndex, materialIndex)
    if self.selectedMaterialCountCache and self.selectedMaterialCountCache[patternIndex] then
        return self.selectedMaterialCountCache[patternIndex][materialIndex]
    end
     return nil
end
function ZO_SharedSmithingCreation:AreSelectionsValid()
    if self:GetSelectedPatternIndex() and self:GetSelectedMaterialIndex() and self:GetSelectedMaterialQuantity() > 0 and self:GetSelectedStyleIndex() and self:GetSelectedTraitIndex() then
        return self.isPatternUsable ~= USABILITY_TYPE_INVALID and self.isMaterialUsable ~= USABILITY_TYPE_INVALID and self.isStyleUsable ~= USABILITY_TYPE_INVALID and self.isTraitUsable ~= USABILITY_TYPE_INVALID
    end
    return false
end
function ZO_SharedSmithingCreation:IsCraftable()
    if self:GetSelectedPatternIndex() and self:GetSelectedMaterialIndex() and self:GetSelectedMaterialQuantity() > 0 and self:GetSelectedStyleIndex() and self:GetSelectedTraitIndex() then
        return self.isPatternUsable and self.isMaterialUsable and self.isStyleUsable and self.isTraitUsable
    end
    return false
end
function ZO_SharedSmithingCreation:IsCraftableWithoutTrait()
    if self:GetSelectedPatternIndex() and self:GetSelectedMaterialIndex() and self:GetSelectedMaterialQuantity() > 0 and self:GetSelectedStyleIndex() then
        return self.isStyleUsable ~= USABILITY_TYPE_INVALID
    end
    return false
end
function ZO_SharedSmithingCreation:Create()
end
function ZO_SharedSmithingCreation:GetUniversalStyleItemLink()
    return GetSmithingStyleItemLink(ZO_ADJUSTED_UNIVERSAL_STYLE_ITEM_INDEX)
end
function ZO_SharedSmithingCreation:TriggerUSITutorial()
    local universalStyleItemCount = GetCurrentSmithingStyleItemCount(ZO_ADJUSTED_UNIVERSAL_STYLE_ITEM_INDEX)
    if universalStyleItemCount > 0 then
        TriggerTutorial(TUTORIAL_TRIGGER_UNIVERSAL_STYLE_ITEM)
    end
end