diff --git a/config/i18n.json b/config/i18n.json index f4b3bbce5c..6367d37710 100644 --- a/config/i18n.json +++ b/config/i18n.json @@ -1050,6 +1050,7 @@ "Name": "Name", "Power": "Power", "Damage": "Damage", + "Foundry": "Foundry", "Intrinsics": "Intrinsic", "OriginTraits": "Origin Trait", "Shaders": "Cosmetics", diff --git a/src/app/inventory/__snapshots__/d2-stores.test.ts.snap b/src/app/inventory/__snapshots__/d2-stores.test.ts.snap index d174eda73a..26b24928d1 100644 --- a/src/app/inventory/__snapshots__/d2-stores.test.ts.snap +++ b/src/app/inventory/__snapshots__/d2-stores.test.ts.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`process stores generates a correct Armor CSV export 1`] = ` +exports[`process stores generates a correct armor CSV export 1`] = ` [ { "Discipline": 32, @@ -8663,10 +8663,12 @@ exports[`process stores generates a correct Armor CSV export 1`] = ` ] `; -exports[`process stores generates a correct Ghost CSV export 1`] = ` +exports[`process stores generates a correct ghost CSV export 1`] = ` [ { + "Energy Capacity": 1, "Equipped": false, + "Event": "", "Hash": 573576346, "Id": ""6917529085740071304"", "Loadouts": "", @@ -8678,12 +8680,16 @@ exports[`process stores generates a correct Ghost CSV export 1`] = ` "Perks 1": undefined, "Perks 2": undefined, "Perks 3": undefined, + "Season": 2, "Source": "mercury", "Tag": undefined, "Tier": "Exotic", + "Year": 1, }, { + "Energy Capacity": 6, "Equipped": true, + "Event": "", "Hash": 1283654212, "Id": ""6917529127946162805"", "Loadouts": "", @@ -8695,12 +8701,16 @@ exports[`process stores generates a correct Ghost CSV export 1`] = ` "Perks 1": undefined, "Perks 2": undefined, "Perks 3": undefined, + "Season": 8, "Source": "deluxe", "Tag": undefined, "Tier": "Exotic", + "Year": 3, }, { + "Energy Capacity": 1, "Equipped": false, + "Event": "", "Hash": 227918504, "Id": ""6917529156697381898"", "Loadouts": "", @@ -8712,12 +8722,16 @@ exports[`process stores generates a correct Ghost CSV export 1`] = ` "Perks 1": undefined, "Perks 2": undefined, "Perks 3": undefined, + "Season": 5, "Source": "eververse", "Tag": undefined, "Tier": "Exotic", + "Year": 2, }, { + "Energy Capacity": 1, "Equipped": false, + "Event": "", "Hash": 4238874520, "Id": ""6917529193444242909"", "Loadouts": "", @@ -8729,12 +8743,16 @@ exports[`process stores generates a correct Ghost CSV export 1`] = ` "Perks 1": undefined, "Perks 2": undefined, "Perks 3": undefined, + "Season": 10, "Source": "seasonpass", "Tag": undefined, "Tier": "Exotic", + "Year": 3, }, { + "Energy Capacity": 10, "Equipped": true, + "Event": "", "Hash": 3398078482, "Id": ""6917529198421309490"", "Loadouts": "", @@ -8746,12 +8764,16 @@ exports[`process stores generates a correct Ghost CSV export 1`] = ` "Perks 1": "Blinding Light*", "Perks 2": "Prize Pursuant*", "Perks 3": "Standard Glimmer Booster*", + "Season": 11, "Source": "eververse", "Tag": undefined, "Tier": "Exotic", + "Year": 3, }, { + "Energy Capacity": 6, "Equipped": false, + "Event": "", "Hash": 1558857471, "Id": ""6917529261791074579"", "Loadouts": "", @@ -8763,12 +8785,16 @@ exports[`process stores generates a correct Ghost CSV export 1`] = ` "Perks 1": undefined, "Perks 2": undefined, "Perks 3": undefined, + "Season": 2, "Source": "eververse", "Tag": undefined, "Tier": "Exotic", + "Year": 1, }, { + "Energy Capacity": 1, "Equipped": false, + "Event": "", "Hash": 1090082587, "Id": ""6917529441305449079"", "Loadouts": "", @@ -8780,12 +8806,16 @@ exports[`process stores generates a correct Ghost CSV export 1`] = ` "Perks 1": undefined, "Perks 2": undefined, "Perks 3": undefined, + "Season": 10, "Source": "eververse", "Tag": undefined, "Tier": "Exotic", + "Year": 3, }, { + "Energy Capacity": 1, "Equipped": false, + "Event": "", "Hash": 527607310, "Id": ""6917529613981176946"", "Loadouts": "", @@ -8797,12 +8827,16 @@ exports[`process stores generates a correct Ghost CSV export 1`] = ` "Perks 1": undefined, "Perks 2": undefined, "Perks 3": undefined, + "Season": 6, "Source": "eververse", "Tag": undefined, "Tier": "Exotic", + "Year": 2, }, { + "Energy Capacity": 1, "Equipped": false, + "Event": "", "Hash": 634549439, "Id": ""6917529805257494505"", "Loadouts": "", @@ -8814,12 +8848,16 @@ exports[`process stores generates a correct Ghost CSV export 1`] = ` "Perks 1": undefined, "Perks 2": undefined, "Perks 3": undefined, + "Season": 17, "Source": "eververse", "Tag": undefined, "Tier": "Exotic", + "Year": 5, }, { + "Energy Capacity": 1, "Equipped": false, + "Event": "", "Hash": 2313814566, "Id": ""6917529812262344063"", "Loadouts": "", @@ -8831,12 +8869,16 @@ exports[`process stores generates a correct Ghost CSV export 1`] = ` "Perks 1": undefined, "Perks 2": undefined, "Perks 3": undefined, + "Season": 18, "Source": "deluxe", "Tag": undefined, "Tier": "Exotic", + "Year": 5, }, { + "Energy Capacity": 1, "Equipped": false, + "Event": "", "Hash": 227918505, "Id": ""6917529812274003175"", "Loadouts": "", @@ -8848,12 +8890,16 @@ exports[`process stores generates a correct Ghost CSV export 1`] = ` "Perks 1": undefined, "Perks 2": undefined, "Perks 3": undefined, + "Season": 5, "Source": "eververse", "Tag": undefined, "Tier": "Exotic", + "Year": 2, }, { + "Energy Capacity": 1, "Equipped": false, + "Event": "", "Hash": 1302968378, "Id": ""6917529842269845415"", "Loadouts": "", @@ -8865,12 +8911,16 @@ exports[`process stores generates a correct Ghost CSV export 1`] = ` "Perks 1": undefined, "Perks 2": undefined, "Perks 3": undefined, + "Season": 18, "Source": "eververse", "Tag": undefined, "Tier": "Exotic", + "Year": 5, }, { + "Energy Capacity": 1, "Equipped": false, + "Event": "", "Hash": 3809059822, "Id": ""6917529859037522184"", "Loadouts": "", @@ -8882,12 +8932,16 @@ exports[`process stores generates a correct Ghost CSV export 1`] = ` "Perks 1": undefined, "Perks 2": undefined, "Perks 3": undefined, + "Season": 19, "Source": "rasputin", "Tag": undefined, "Tier": "Exotic", + "Year": 5, }, { + "Energy Capacity": 1, "Equipped": false, + "Event": "", "Hash": 1490914249, "Id": ""6917529891097015415"", "Loadouts": "", @@ -8899,12 +8953,16 @@ exports[`process stores generates a correct Ghost CSV export 1`] = ` "Perks 1": undefined, "Perks 2": undefined, "Perks 3": undefined, + "Season": 20, "Source": "seasonpass", "Tag": undefined, "Tier": "Exotic", + "Year": 6, }, { + "Energy Capacity": 1, "Equipped": false, + "Event": "", "Hash": 3705925878, "Id": ""6917529932856531597"", "Loadouts": "", @@ -8916,12 +8974,16 @@ exports[`process stores generates a correct Ghost CSV export 1`] = ` "Perks 1": undefined, "Perks 2": undefined, "Perks 3": undefined, + "Season": 14, "Source": "eververse", "Tag": undefined, "Tier": "Exotic", + "Year": 4, }, { + "Energy Capacity": 1, "Equipped": true, + "Event": "", "Hash": 210874516, "Id": ""6917529932874117020"", "Loadouts": "", @@ -8933,12 +8995,16 @@ exports[`process stores generates a correct Ghost CSV export 1`] = ` "Perks 1": undefined, "Perks 2": undefined, "Perks 3": undefined, + "Season": 22, "Source": "deluxe", "Tag": undefined, "Tier": "Exotic", + "Year": 6, }, { + "Energy Capacity": 1, "Equipped": false, + "Event": "", "Hash": 1013853356, "Id": ""6917529950744002122"", "Loadouts": "", @@ -8950,12 +9016,16 @@ exports[`process stores generates a correct Ghost CSV export 1`] = ` "Perks 1": undefined, "Perks 2": undefined, "Perks 3": undefined, + "Season": 12, "Source": "eververse", "Tag": undefined, "Tier": "Exotic", + "Year": 4, }, { + "Energy Capacity": 1, "Equipped": false, + "Event": "", "Hash": 3775649487, "Id": ""6917529951182029807"", "Loadouts": "", @@ -8967,12 +9037,16 @@ exports[`process stores generates a correct Ghost CSV export 1`] = ` "Perks 1": undefined, "Perks 2": undefined, "Perks 3": undefined, + "Season": 22, "Source": "seasonpass", "Tag": undefined, "Tier": "Exotic", + "Year": 6, }, { + "Energy Capacity": 1, "Equipped": false, + "Event": "The Dawning", "Hash": 2549404869, "Id": ""6917529966167567978"", "Loadouts": "", @@ -8984,12 +9058,16 @@ exports[`process stores generates a correct Ghost CSV export 1`] = ` "Perks 1": undefined, "Perks 2": undefined, "Perks 3": undefined, + "Season": 2, "Source": "eververse", "Tag": undefined, "Tier": "Exotic", + "Year": 1, }, { + "Energy Capacity": 1, "Equipped": false, + "Event": "", "Hash": 3733702440, "Id": ""6917529993263920330"", "Loadouts": "", @@ -9001,14 +9079,16 @@ exports[`process stores generates a correct Ghost CSV export 1`] = ` "Perks 1": undefined, "Perks 2": undefined, "Perks 3": undefined, + "Season": 23, "Source": "eververse", "Tag": undefined, "Tier": "Exotic", + "Year": 6, }, ] `; -exports[`process stores generates a correct Weapons CSV export 1`] = ` +exports[`process stores generates a correct weapon CSV export 1`] = ` [ { "AA": 40, @@ -9025,7 +9105,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 33, @@ -9087,7 +9166,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "omolon", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 77, @@ -9149,7 +9227,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 38, @@ -9211,7 +9288,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 79, @@ -9273,7 +9349,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 20, @@ -9335,7 +9410,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 41, @@ -9397,7 +9471,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 30, @@ -9459,7 +9532,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 26, @@ -9521,7 +9593,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": true, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 63, @@ -9583,7 +9654,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 45, @@ -9645,7 +9715,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 69, @@ -9707,7 +9776,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 80, @@ -9769,7 +9837,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 100, @@ -9831,7 +9898,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 69, @@ -9893,7 +9959,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 36, @@ -9955,7 +10020,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 74, @@ -10017,7 +10081,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 60, "Guard Resistance": 70, "Handling": 0, @@ -10079,7 +10142,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 80, "Handling": 0, @@ -10141,7 +10203,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 23, @@ -10203,7 +10264,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "hakke", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 56, @@ -10265,7 +10325,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "fotc", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 45, @@ -10327,7 +10386,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 63, @@ -10389,7 +10447,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "omolon", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 53, @@ -10451,7 +10508,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 27, @@ -10513,7 +10569,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "field-forged", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 72, @@ -10575,7 +10630,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 37, @@ -10637,7 +10691,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 23, @@ -10699,7 +10752,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 13, @@ -10761,7 +10813,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "The Dawning", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 48, @@ -10823,7 +10874,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 38, @@ -10885,7 +10935,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 90, "Guard Resistance": 50, "Handling": 0, @@ -10947,7 +10996,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "The Dawning", "Foundry": "hakke", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 68, @@ -11009,7 +11057,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 29, @@ -11071,7 +11118,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 75, @@ -11133,7 +11179,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 49, @@ -11195,7 +11240,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 87, @@ -11257,7 +11301,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 79, @@ -11319,7 +11362,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 65, @@ -11381,7 +11423,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 43, @@ -11443,7 +11484,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 46, @@ -11505,7 +11545,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 73, @@ -11567,7 +11606,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 54, @@ -11629,7 +11667,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "omolon", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 67, @@ -11691,7 +11728,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 36, @@ -11753,7 +11789,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 55, @@ -11815,7 +11850,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 46, @@ -11877,7 +11911,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 95, @@ -11939,7 +11972,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 52, @@ -12001,7 +12033,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 66, @@ -12063,7 +12094,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 25, @@ -12125,7 +12155,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 35, @@ -12187,7 +12216,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 71, @@ -12249,7 +12277,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 38, @@ -12311,7 +12338,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "suros", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 70, @@ -12373,7 +12399,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 69, @@ -12435,7 +12460,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "omolon", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 62, @@ -12497,7 +12521,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 69, @@ -12559,7 +12582,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 58, @@ -12621,7 +12643,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "omolon", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 67, @@ -12683,7 +12704,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 40, @@ -12745,7 +12765,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 59, @@ -12807,7 +12826,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 52, @@ -12869,7 +12887,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "suros", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 45, @@ -12931,7 +12948,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 37, @@ -12993,7 +13009,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 70, @@ -13055,7 +13070,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 82, @@ -13117,7 +13131,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 35, @@ -13179,7 +13192,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 52, @@ -13241,7 +13253,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "veist", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 70, @@ -13303,7 +13314,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 70, @@ -13365,7 +13375,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 50, @@ -13427,7 +13436,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 40, @@ -13489,7 +13497,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 60, @@ -13551,7 +13558,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 41, @@ -13613,7 +13619,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 66, @@ -13675,7 +13680,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "omolon", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 57, @@ -13737,7 +13741,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 46, @@ -13799,7 +13802,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "veist", - "Guard Efficiency": 0, "Guard Endurance": 40, "Guard Resistance": 10, "Handling": 0, @@ -13861,7 +13863,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 45, @@ -13923,7 +13924,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 77, @@ -13985,7 +13985,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 65, @@ -14047,7 +14046,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 21, @@ -14109,7 +14107,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 30, @@ -14171,7 +14168,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 76, @@ -14233,7 +14229,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 50, @@ -14295,7 +14290,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 60, @@ -14357,7 +14351,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 60, @@ -14419,7 +14412,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 38, @@ -14481,7 +14473,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "hakke", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 31, @@ -14543,7 +14534,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 24, @@ -14605,7 +14595,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 71, @@ -14667,7 +14656,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 48, @@ -14729,7 +14717,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 53, @@ -14791,7 +14778,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 55, @@ -14853,7 +14839,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "Guardian Games", "Foundry": "hakke", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 48, @@ -14915,7 +14900,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 47, @@ -14977,7 +14961,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 75, @@ -15039,7 +15022,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 36, @@ -15101,7 +15083,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 36, @@ -15163,7 +15144,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 73, @@ -15225,7 +15205,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 60, @@ -15287,7 +15266,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "Guardian Games", "Foundry": "hakke", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 43, @@ -15349,7 +15327,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 88, @@ -15411,7 +15388,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "hakke", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 28, @@ -15473,7 +15449,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 31, @@ -15535,7 +15510,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 49, @@ -15597,7 +15571,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 33, @@ -15659,7 +15632,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 26, @@ -15721,7 +15693,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 37, @@ -15783,7 +15754,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "suros", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 57, @@ -15845,7 +15815,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 66, @@ -15907,7 +15876,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "omolon", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 80, @@ -15969,7 +15937,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 43, @@ -16031,7 +15998,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "suros", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 36, @@ -16093,7 +16059,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "hakke", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 30, @@ -16155,7 +16120,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 60, @@ -16217,7 +16181,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 53, @@ -16279,7 +16242,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 40, @@ -16341,7 +16303,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 17, @@ -16403,7 +16364,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 56, @@ -16465,7 +16425,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "veist", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 72, @@ -16527,7 +16486,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "Festival of the Lost", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 60, @@ -16589,7 +16547,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 47, @@ -16651,7 +16608,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 86, @@ -16713,7 +16669,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "fotc", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 41, @@ -16775,7 +16730,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": true, "Event": "", "Foundry": "omolon", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 36, @@ -16837,7 +16791,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 53, @@ -16899,7 +16852,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 21, @@ -16961,7 +16913,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 59, @@ -17023,7 +16974,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "veist", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 72, @@ -17085,7 +17035,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "Festival of the Lost", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 26, @@ -17147,7 +17096,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 42, @@ -17209,7 +17157,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 33, @@ -17271,7 +17218,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 20, @@ -17333,7 +17279,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 70, @@ -17395,7 +17340,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "veist", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 49, @@ -17457,7 +17401,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "fotc", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 40, @@ -17519,7 +17462,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "omolon", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 42, @@ -17581,7 +17523,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 55, @@ -17643,7 +17584,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 78, @@ -17705,7 +17645,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 70, @@ -17767,7 +17706,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "omolon", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 24, @@ -17829,7 +17767,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 50, @@ -17891,7 +17828,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 40, @@ -17953,7 +17889,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "suros", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 37, @@ -18015,7 +17950,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "hakke", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 33, @@ -18077,7 +18011,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 72, @@ -18139,7 +18072,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "veist", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 73, @@ -18201,7 +18133,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "The Dawning", "Foundry": "hakke", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 69, @@ -18263,7 +18194,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "The Dawning", "Foundry": "omolon", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 33, @@ -18325,7 +18255,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "The Dawning", "Foundry": "omolon", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 77, @@ -18387,7 +18316,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "The Dawning", "Foundry": "omolon", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 68, @@ -18449,7 +18377,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "hakke", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 25, @@ -18511,7 +18438,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 68, @@ -18573,7 +18499,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 38, @@ -18635,7 +18560,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 32, @@ -18697,7 +18621,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 37, @@ -18759,7 +18682,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "tex-mechanica", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 45, @@ -18821,7 +18743,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "veist", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 86, @@ -18883,7 +18804,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "hakke", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 23, @@ -18945,7 +18865,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 80, "Handling": 0, @@ -19007,7 +18926,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 63, @@ -19069,7 +18987,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 66, @@ -19131,7 +19048,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "omolon", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 13, @@ -19193,7 +19109,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "veist", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 24, @@ -19255,7 +19170,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "suros", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 76, @@ -19317,7 +19231,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "hakke", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 28, @@ -19379,7 +19292,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "suros", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 46, @@ -19441,7 +19353,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 33, @@ -19503,7 +19414,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "hakke", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 30, @@ -19565,7 +19475,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": true, "Event": "", "Foundry": "hakke", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 40, @@ -19627,7 +19536,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 51, @@ -19689,7 +19597,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 14, @@ -19751,7 +19658,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 76, @@ -19813,7 +19719,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 23, @@ -19875,7 +19780,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 20, @@ -19937,7 +19841,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 47, @@ -19999,7 +19902,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "suros", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 57, @@ -20061,7 +19963,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 81, @@ -20123,7 +20024,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 45, @@ -20185,7 +20085,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 49, @@ -20247,7 +20146,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 68, @@ -20309,7 +20207,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 72, @@ -20371,7 +20268,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 45, @@ -20433,7 +20329,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 82, @@ -20495,7 +20390,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 39, @@ -20557,7 +20451,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "Guardian Games", "Foundry": "omolon", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 81, @@ -20619,7 +20512,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 29, @@ -20681,7 +20573,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 38, @@ -20743,7 +20634,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 42, @@ -20805,7 +20695,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 54, @@ -20867,7 +20756,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "suros", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 63, @@ -20929,7 +20817,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "hakke", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 31, @@ -20991,7 +20878,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 16, @@ -21053,7 +20939,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 53, @@ -21115,7 +21000,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "suros", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 43, @@ -21177,7 +21061,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 53, @@ -21239,7 +21122,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 26, @@ -21301,7 +21183,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 60, @@ -21363,7 +21244,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 24, @@ -21425,7 +21305,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": true, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 17, @@ -21487,7 +21366,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 43, @@ -21549,7 +21427,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 25, @@ -21611,7 +21488,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 90, "Guard Resistance": 0, "Handling": 0, @@ -21673,7 +21549,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 52, @@ -21735,7 +21610,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "veist", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 17, @@ -21797,7 +21671,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 47, @@ -21859,7 +21732,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 49, @@ -21921,7 +21793,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 82, @@ -21983,7 +21854,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 54, @@ -22045,7 +21915,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "omolon", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 65, @@ -22107,7 +21976,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 36, @@ -22169,7 +22037,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "Solstice", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 35, @@ -22231,7 +22098,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": true, "Event": "Solstice", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 40, @@ -22293,7 +22159,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 19, @@ -22355,7 +22220,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 30, @@ -22417,7 +22281,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 15, @@ -22479,7 +22342,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 67, @@ -22541,7 +22403,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "veist", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 33, @@ -22603,7 +22464,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 33, @@ -22665,7 +22525,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "omolon", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 38, @@ -22727,7 +22586,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 47, @@ -22789,7 +22647,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 53, @@ -22851,7 +22708,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "veist", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 48, @@ -22913,7 +22769,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 77, @@ -22975,7 +22830,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "omolon", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 58, @@ -23037,7 +22891,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "omolon", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 56, @@ -23099,7 +22952,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "Solstice", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 35, @@ -23161,7 +23013,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "omolon", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 25, @@ -23223,7 +23074,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 44, @@ -23285,7 +23135,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "omolon", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 60, @@ -23347,7 +23196,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "omolon", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 45, @@ -23409,7 +23257,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "suros", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 66, @@ -23471,7 +23318,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "omolon", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 40, @@ -23533,7 +23379,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "veist", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 63, @@ -23595,7 +23440,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 71, @@ -23657,7 +23501,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 18, @@ -23719,7 +23562,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 36, @@ -23781,7 +23623,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 59, @@ -23843,7 +23684,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 69, @@ -23905,7 +23745,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 33, @@ -23967,7 +23806,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 58, @@ -24029,7 +23867,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 56, @@ -24091,7 +23928,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": true, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 47, @@ -24153,7 +23989,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 47, @@ -24215,7 +24050,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 60, @@ -24277,7 +24111,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "Festival of the Lost", "Foundry": "nadir", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 25, @@ -24339,7 +24172,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "cassoid", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 41, @@ -24401,7 +24233,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "omolon", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 59, @@ -24463,7 +24294,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 40, @@ -24525,7 +24355,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 33, @@ -24587,7 +24416,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "nadir", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 65, @@ -24649,7 +24477,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "Festival of the Lost", "Foundry": "nadir", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 26, @@ -24711,7 +24538,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 45, @@ -24773,7 +24599,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": true, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 51, @@ -24835,7 +24660,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 0, @@ -24897,7 +24721,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 46, @@ -24959,7 +24782,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "Festival of the Lost", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 17, @@ -25021,7 +24843,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 49, @@ -25083,7 +24904,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 59, @@ -25145,7 +24965,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 26, @@ -25207,7 +25026,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 43, @@ -25269,7 +25087,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 63, @@ -25331,7 +25148,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": true, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 55, @@ -25393,7 +25209,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 55, @@ -25455,7 +25270,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 84, @@ -25517,7 +25331,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "nadir", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 64, @@ -25579,7 +25392,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "hakke", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 38, @@ -25641,7 +25453,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 49, @@ -25703,7 +25514,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "cassoid", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 29, @@ -25765,7 +25575,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "veist", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 92, @@ -25827,7 +25636,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 92, @@ -25889,7 +25697,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 35, @@ -25951,7 +25758,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 60, @@ -26013,7 +25819,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "cassoid", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 67, @@ -26075,7 +25880,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 68, @@ -26137,7 +25941,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 71, @@ -26199,7 +26002,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "suros", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 52, @@ -26261,7 +26063,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "veist", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 83, @@ -26323,7 +26124,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 29, @@ -26385,7 +26185,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 50, @@ -26447,7 +26246,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 32, @@ -26509,7 +26307,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 28, @@ -26571,7 +26368,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 36, @@ -26633,7 +26429,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 37, @@ -26695,7 +26490,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": true, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 53, @@ -26757,7 +26551,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 61, @@ -26819,7 +26612,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 67, @@ -26881,7 +26673,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "cassoid", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 8, @@ -26943,7 +26734,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 51, @@ -27005,7 +26795,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 75, @@ -27067,7 +26856,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "The Dawning", "Foundry": "suros", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 46, @@ -27129,7 +26917,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "The Dawning", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 80, "Handling": 0, @@ -27191,7 +26978,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "nadir", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 40, @@ -27253,7 +27039,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "cassoid", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 13, @@ -27315,7 +27100,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "The Dawning", "Foundry": "omolon", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 33, @@ -27377,7 +27161,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 80, @@ -27439,7 +27222,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "The Dawning", "Foundry": "omolon", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 48, @@ -27501,7 +27283,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "cassoid", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 17, @@ -27563,7 +27344,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "field-forged", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 56, @@ -27625,7 +27405,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 32, @@ -27687,7 +27466,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 59, @@ -27749,7 +27527,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "Guardian Games", "Foundry": "omolon", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 84, @@ -27811,7 +27588,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "Guardian Games", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 51, @@ -27873,7 +27649,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "Guardian Games", "Foundry": "hakke", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 53, @@ -27935,7 +27710,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 0, @@ -27997,7 +27771,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "field-forged", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 33, @@ -28059,7 +27832,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "nadir", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 80, "Handling": 0, @@ -28121,7 +27893,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 30, @@ -28183,7 +27954,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "field-forged", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 37, @@ -28245,7 +28015,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 60, @@ -28307,7 +28076,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 61, @@ -28369,7 +28137,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "field-forged", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 62, @@ -28431,7 +28198,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 16, @@ -28493,7 +28259,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 30, @@ -28555,7 +28320,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 61, @@ -28617,7 +28381,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 78, @@ -28679,7 +28442,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 43, @@ -28741,7 +28503,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 61, @@ -28803,7 +28564,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 90, "Guard Resistance": 0, "Handling": 0, @@ -28865,7 +28625,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 16, @@ -28927,7 +28686,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 21, @@ -28989,7 +28747,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 42, @@ -29051,7 +28808,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 25, @@ -29113,7 +28869,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "omolon", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 52, @@ -29175,7 +28930,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 59, @@ -29237,7 +28991,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "suros", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 46, @@ -29299,7 +29052,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "suros", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 66, @@ -29361,7 +29113,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "omolon", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 44, @@ -29423,7 +29174,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 59, @@ -29485,7 +29235,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "omolon", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 64, @@ -29547,7 +29296,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "cassoid", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 50, @@ -29609,7 +29357,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 63, @@ -29671,7 +29418,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 42, @@ -29733,7 +29479,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 37, @@ -29795,7 +29540,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "veist", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 73, @@ -29857,7 +29601,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 40, "Guard Resistance": 40, "Handling": 0, @@ -29919,7 +29662,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "omolon", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 60, @@ -29981,7 +29723,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "omolon", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 40, @@ -30043,7 +29784,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "veist", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 58, @@ -30105,7 +29845,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 27, @@ -30167,7 +29906,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 61, @@ -30229,7 +29967,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "suros", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 51, @@ -30291,7 +30028,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "cassoid", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 62, @@ -30353,7 +30089,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 76, @@ -30415,7 +30150,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "omolon", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 44, @@ -30477,7 +30211,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 21, @@ -30539,7 +30272,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 27, @@ -30601,7 +30333,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 17, @@ -30663,7 +30394,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 45, @@ -30725,7 +30455,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 45, @@ -30787,7 +30516,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 32, @@ -30849,7 +30577,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 80, "Handling": 0, @@ -30911,7 +30638,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "suros", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 57, @@ -30973,7 +30699,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 47, @@ -31035,7 +30760,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 34, @@ -31097,7 +30821,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "veist", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 53, @@ -31159,7 +30882,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 57, @@ -31221,7 +30943,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 47, @@ -31283,7 +31004,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 53, @@ -31345,7 +31065,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "omolon", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 43, @@ -31407,7 +31126,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 55, @@ -31469,7 +31187,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "cassoid", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 27, @@ -31531,7 +31248,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "field-forged", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 77, @@ -31593,7 +31309,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "field-forged", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 46, @@ -31655,7 +31370,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "fotc", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 16, @@ -31717,7 +31431,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 29, @@ -31779,7 +31492,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "omolon", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 19, @@ -31841,7 +31553,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "cassoid", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 23, @@ -31903,7 +31614,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": undefined, - "Guard Efficiency": 0, "Guard Endurance": 90, "Guard Resistance": 0, "Handling": 0, @@ -31965,7 +31675,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "field-forged", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 69, @@ -32027,7 +31736,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "suros", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 66, @@ -32089,7 +31797,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "omolon", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 46, @@ -32151,7 +31858,6 @@ exports[`process stores generates a correct Weapons CSV export 1`] = ` "Equipped": false, "Event": "", "Foundry": "fotc", - "Guard Efficiency": 0, "Guard Endurance": 0, "Guard Resistance": 0, "Handling": 16, diff --git a/src/app/inventory/d2-stores.test.ts b/src/app/inventory/d2-stores.test.ts index f661495ebc..038fd7d3cd 100644 --- a/src/app/inventory/d2-stores.test.ts +++ b/src/app/inventory/d2-stores.test.ts @@ -1,6 +1,6 @@ import { getWeaponArchetypeSocket } from 'app/utils/socket-utils'; import { BucketHashes } from 'data/d2/generated-enums'; -import { getTestStores } from 'testing/test-utils'; +import { getTestStores, setupi18n } from 'testing/test-utils'; import { generateCSVExportData } from './spreadsheets'; import { DimStore } from './store-types'; @@ -96,14 +96,12 @@ describe('process stores', () => { throw new Error('Expected at least one item with a perk that cannot roll'); }); - test.each(['Weapons', 'Armor', 'Ghost'] as const)( - 'generates a correct %s CSV export', - (type) => { - const getTag = () => undefined; - const getNotes = () => undefined; - const loadoutsByItem = {}; - const csvExport = generateCSVExportData(type, stores, getTag, getNotes, loadoutsByItem); - expect(csvExport).toMatchSnapshot(); - }, - ); + test.each(['weapon', 'armor', 'ghost'] as const)('generates a correct %s CSV export', (type) => { + setupi18n(); + const getTag = () => undefined; + const getNotes = () => undefined; + const loadoutsByItem = {}; + const csvExport = generateCSVExportData(type, stores, getTag, getNotes, loadoutsByItem, []); + expect(csvExport).toMatchSnapshot(); + }); }); diff --git a/src/app/inventory/spreadsheets.ts b/src/app/inventory/spreadsheets.ts index a213b26fc8..6a20ee40fc 100644 --- a/src/app/inventory/spreadsheets.ts +++ b/src/app/inventory/spreadsheets.ts @@ -1,25 +1,21 @@ +import { CustomStatDef, DestinyVersion } from '@destinyitemmanager/dim-api-types'; import { currentAccountSelector } from 'app/accounts/selectors'; +import { customStatsSelector, languageSelector } from 'app/dim-api/selectors'; import { gaEvent } from 'app/google'; import { LoadoutsByItem, loadoutsByItemSelector } from 'app/loadout/selectors'; +import { buildStatInfo, getColumns } from 'app/organizer/Columns'; +import { CSVColumn, SpreadsheetContext } from 'app/organizer/table-types'; import { D1_StatHashes } from 'app/search/d1-known-values'; -import D2Sources from 'app/search/items/search-filters/d2-sources'; -import { dimArmorStatHashByName } from 'app/search/search-filter-values'; +import { TOTAL_STAT_HASH } from 'app/search/d2-known-values'; import { ThunkResult } from 'app/store/types'; import { filterMap } from 'app/utils/collections'; import { compareBy } from 'app/utils/comparators'; import { DimError } from 'app/utils/dim-error'; import { download } from 'app/utils/download'; -import { - getItemKillTrackerInfo, - getItemYear, - getMasterworkStatNames, - getSpecialtySocketMetadatas, - isD1Item, - isKillTrackerSocket, -} from 'app/utils/item-utils'; +import { localizedSorter } from 'app/utils/intl'; +import { isD1Item, isKillTrackerSocket } from 'app/utils/item-utils'; import { getDisplayedItemSockets, getSocketsByIndexes } from 'app/utils/socket-utils'; import { DestinyClass } from 'bungie-api-ts/destiny2'; -import { D2EventInfo } from 'data/d2/d2-event-info-v2'; import { BucketHashes, StatHashes } from 'data/d2/generated-enums'; import _ from 'lodash'; import Papa from 'papaparse'; @@ -28,20 +24,19 @@ import { TagValue, tagConfig } from './dim-item-info'; import { D1GridNode, DimItem } from './item-types'; import { getNotesSelector, getTagSelector, storesSelector } from './selectors'; import { DimStore } from './store-types'; -import { getEvent, getSeason } from './store/season'; function getClass(type: DestinyClass) { switch (type) { case DestinyClass.Titan: - return 'titan'; + return 'Titan'; case DestinyClass.Hunter: - return 'hunter'; + return 'Hunter'; case DestinyClass.Warlock: - return 'warlock'; + return 'Warlock'; case DestinyClass.Unknown: - return 'unknown'; + return 'Unknown'; case DestinyClass.Classified: - return 'classified'; + return 'Classified'; } } @@ -62,71 +57,202 @@ const D1_FILTERED_NODE_HASHES = [ 2086308543, // Upgrade Defense 4044819214, // The Life Exotic ]; -// ignore raid & calus sources in favor of more detailed sources -const sourceKeys = Object.keys(D2Sources).filter((k) => !['raid', 'calus'].includes(k)); + +/** Stat names are not localized in CSV. We use a map to preserve order. */ +export const csvStatNamesForDestinyVersion = (destinyVersion: DestinyVersion) => + new Map([ + [StatHashes.RecoilDirection, 'Recoil'], + [StatHashes.AimAssistance, 'AA'], + [StatHashes.Impact, 'Impact'], + [StatHashes.Range, 'Range'], + [StatHashes.Zoom, 'Zoom'], + [StatHashes.BlastRadius, 'Blast Radius'], + [StatHashes.Velocity, 'Velocity'], + [StatHashes.Stability, 'Stability'], + [StatHashes.RoundsPerMinute, 'ROF'], + [StatHashes.ReloadSpeed, 'Reload'], + [StatHashes.AmmoCapacity, 'Mag'], + [StatHashes.Magazine, 'Mag'], + [StatHashes.Handling, destinyVersion === 2 ? 'Handling' : 'Equip'], + [StatHashes.ChargeTime, 'Charge Time'], + [StatHashes.DrawTime, 'Draw Time'], + [StatHashes.Accuracy, 'Accuracy'], + [StatHashes.ChargeRate, 'Charge Rate'], + [StatHashes.GuardResistance, 'Guard Resistance'], + [StatHashes.GuardEndurance, 'Guard Endurance'], + [StatHashes.SwingSpeed, 'Swing Speed'], + [StatHashes.ShieldDuration, 'Shield Duration'], + [StatHashes.AirborneEffectiveness, 'Airborne Effectiveness'], + [StatHashes.Mobility, 'Mobility'], + [StatHashes.Resilience, 'Resilience'], + [StatHashes.Recovery, 'Recovery'], + [StatHashes.Discipline, destinyVersion === 2 ? 'Discipline' : 'Disc'], + [StatHashes.Intellect, destinyVersion === 2 ? 'Intellect' : 'Int'], + [StatHashes.Strength, destinyVersion === 2 ? 'Strength' : 'Str'], + [TOTAL_STAT_HASH, 'Total'], + ]); export function generateCSVExportData( - type: 'Weapons' | 'Armor' | 'Ghost', + type: 'weapon' | 'armor' | 'ghost', stores: DimStore[], getTag: (item: DimItem) => TagValue | undefined, getNotes: (item: DimItem) => string | undefined, loadoutsByItem: LoadoutsByItem, + customStats: CustomStatDef[], ) { - const nameMap: { [storeId: string]: string } = {}; + const storeNamesById: { [storeId: string]: string } = {}; let allItems: DimItem[] = []; for (const store of stores) { allItems = allItems.concat(store.items); - nameMap[store.id] = - store.id === 'vault' - ? 'Vault' - : `${capitalizeFirstLetter(getClass(store.classType))}(${store.powerLevel})`; + storeNamesById[store.id] = + store.id === 'vault' ? 'Vault' : `${getClass(store.classType)}(${store.powerLevel})`; } allItems.sort(compareBy((item) => item.index)); - const items: DimItem[] = []; - for (const item of allItems) { - if (!item.primaryStat && type !== 'Ghost') { - continue; - } + let items: DimItem[] = []; + if (type === 'weapon') { + items = allItems.filter( + (item) => + // Checking the primary stat filters out some quest items + item.primaryStat && + (item.primaryStat?.statHash === D1_StatHashes.Attack || + item.primaryStat?.statHash === StatHashes.Attack), + ); + } else if (type === 'armor') { + items = allItems.filter( + // Checking the primary stat filters out festival masks + (item) => item.primaryStat && item.primaryStat?.statHash === StatHashes.Defense, + ); + } else if (type === 'ghost') { + items = allItems.filter((item) => item.bucket.hash === BucketHashes.Ghost); + } - if (type === 'Weapons') { - if ( - item.primaryStat?.statHash === D1_StatHashes.Attack || - item.primaryStat?.statHash === StatHashes.Attack - ) { - items.push(item); + // We need to always emit enough columns for all perks + const maxPerks = getMaxPerks(items); + const statHashes = buildStatInfo(items); + const destinyVersion = items[0]?.destinyVersion ?? 2; + + // Use the column definitions from Organizer to drive the CSV output + const columns = getColumns( + 'spreadsheet', + type, + statHashes, + getTag, + getNotes, + () => undefined /* wishList */, + false /* hasWishList */, + customStats, + loadoutsByItem, + new Set() /* newItems */, + destinyVersion /* destinyVersion */, + ); + + // The order of the spreadsheet columns differs from the Organizer order. + // PapaParse determines column order from the insertion order of data into the + // first object that's returned, so we need to iterate the columns in this + // order. + const statNames = csvStatNamesForDestinyVersion(destinyVersion); + const order = [ + 'name', + 'hash', + 'id', + 'tag', + 'tier', + 'Type', + 'source', + 'Equippable', + 'Category', + 'dmg', + 'power', + 'energy', + 'masterworkStat', + 'masterworkTier', + 'location', + 'locked', + 'Equipped', + 'year', + 'season', + 'event', + ...[...statNames.keys()].map((statHash) => `stat${statHash}`), + ...[...statNames.keys()].map((statHash) => `base${statHash}`), + 'crafted', + 'level', + 'killTracker', + 'foundry', + 'modslot', + 'loadouts', + 'notes', + // unknown columns end up here + // then perks + ]; + columns.sort( + compareBy((c) => { + if (c.id === 'perks') { + // perks are always last + return 2000; + } + const index = order.indexOf(c.id); + if (index < 0) { + // A new column was added and we need to add it to the order above + throw new Error(`missing-column-order ${c.id}`); } - } else if (type === 'Armor') { - if (item.primaryStat?.statHash === StatHashes.Defense) { - items.push(item); + return index; + }), + ); + + const context: SpreadsheetContext = { storeNamesById, maxPerks }; + const data = items.map((item) => { + const row: Record = {}; + for (const column of columns) { + const value = column.value(item); + if (typeof column.csv === 'string') { + row[column.csv] ||= value; + } else if (column.csv) { + const values = column.csv(value, item, context); + if (!values || values.length === 0) { + continue; + } + if (Array.isArray(values[0])) { + for (const [key, value] of values as CSVColumn[]) { + row[key] ||= value; + } + } else { + const [key, value] = values; + row[key] ||= value; + } + } else { + // Column has no CSV representation - either remove the column in spreadsheet mode or add a 'csv' or 'csvVal' property + throw new Error(`missing-csv: ${column.id}`); } - } else if (type === 'Ghost' && item.bucket.hash === BucketHashes.Ghost) { - items.push(item); } - } + return row; + }); - switch (type) { - case 'Weapons': - return downloadWeapons(items, nameMap, getTag, getNotes, loadoutsByItem); - case 'Armor': - return downloadArmor(items, nameMap, getTag, getNotes, loadoutsByItem); - case 'Ghost': - return downloadGhost(items, nameMap, getTag, getNotes, loadoutsByItem); - } + return data; } -export function downloadCsvFiles(type: 'Weapons' | 'Armor' | 'Ghost'): ThunkResult { +export function downloadCsvFiles(type: 'weapon' | 'armor' | 'ghost'): ThunkResult { return async (_dispatch, getState) => { const stores = storesSelector(getState()); const getTag = getTagSelector(getState()); const getNotes = getNotesSelector(getState()); const loadoutsForItem = loadoutsByItemSelector(getState()); + const customStats = customStatsSelector(getState()); + const language = languageSelector(getState()); // perhaps we're loading if (stores.length === 0) { return; } - const data = generateCSVExportData(type, stores, getTag, getNotes, loadoutsForItem); - downloadCsv(`destiny${type}`, Papa.unparse(data)); + const data = generateCSVExportData( + type, + stores, + getTag, + getNotes, + loadoutsForItem, + customStats, + ); + data.sort(localizedSorter(language, (r) => (r as { Name: string }).Name)); + downloadCsv(`destiny-${type}`, Papa.unparse(data)); }; } @@ -207,13 +333,6 @@ export function importTagsNotesFromCsv(files: File[]): ThunkResult { if (D1_FILTERED_NODE_HASHES.includes(node.hash)) { return; @@ -282,364 +401,3 @@ function getMaxPerks(items: DimItem[]) { ) || 0 ); } - -function addPerks(row: Record, item: DimItem, maxPerks: number) { - const perks = - isD1Item(item) && item.talentGrid - ? buildNodeNames(item.talentGrid.nodes) - : item.sockets - ? buildSocketNames(item) - : []; - - _.times(maxPerks, (index) => { - row[`Perks ${index}`] = perks[index]; - }); -} - -function formatLoadouts(item: DimItem, loadouts: LoadoutsByItem) { - return loadouts[item.id]?.map(({ loadout }) => loadout.name).join(', ') ?? ''; -} - -function downloadGhost( - items: DimItem[], - nameMap: { [key: string]: string }, - getTag: (item: DimItem) => TagValue | undefined, - getNotes: (item: DimItem) => string | undefined, - loadouts: LoadoutsByItem, -) { - // We need to always emit enough columns for all perks - const maxPerks = getMaxPerks(items); - - const data = items.map((item) => { - const row = { - Name: item.name, - Hash: item.hash, - Id: `"${item.id}"`, - Tag: getTag(item), - Tier: item.tier, - Source: source(item), - Owner: nameMap[item.owner], - Locked: item.locked, - Equipped: item.equipped, - Loadouts: formatLoadouts(item, loadouts), - Notes: getNotes(item), - }; - - addPerks(row, item, maxPerks); - - return row; - }); - - return data; -} - -function equippable(item: DimItem) { - return item.classType === DestinyClass.Unknown ? 'Any' : item.classTypeNameLocalized; -} - -export function source(item: DimItem) { - if (item.destinyVersion === 2) { - return ( - sourceKeys.find( - (src) => - (item.source && D2Sources[src].sourceHashes?.includes(item.source)) || - D2Sources[src].itemHashes?.includes(item.hash), - ) || '' - ); - } -} - -function downloadArmor( - items: DimItem[], - nameMap: { [key: string]: string }, - getTag: (item: DimItem) => TagValue | undefined, - getNotes: (item: DimItem) => string | undefined, - loadouts: LoadoutsByItem, -) { - // We need to always emit enough columns for all perks - const maxPerks = getMaxPerks(items); - - // In PapaParse, the keys of the first objects are used as columns. So if a - // key is omitted from the first object, it won't show up. - // TODO: Replace PapaParse with a simpler/smaller CSV generator - const data = items.map((item) => { - const row: Record = { - Name: item.name, - Hash: item.hash, - Id: `"${item.id}"`, - Tag: getTag(item), - Tier: item.tier, - Type: item.typeName, - Source: source(item), - Equippable: equippable(item), - [item.destinyVersion === 1 ? 'Light' : 'Power']: item.power, - }; - if (item.destinyVersion === 2) { - row['Energy Capacity'] = item.energy?.energyCapacity || undefined; - } - row.Owner = nameMap[item.owner]; - if (item.destinyVersion === 1) { - row['% Leveled'] = (item.percentComplete * 100).toFixed(0); - } - row.Locked = item.locked; - row.Equipped = item.equipped; - row.Year = getItemYear(item); - if (item.destinyVersion === 2) { - row.Season = getSeason(item); - const event = getEvent(item); - row.Event = event ? D2EventInfo[event].name : ''; - } - - if (isD1Item(item)) { - row['% Quality'] = item.quality?.min ?? 0; - } - const stats: { [name: string]: { value: number; pct: number; base: number } } = {}; - if (item.stats) { - if (isD1Item(item)) { - for (const stat of item.stats) { - let pct = 0; - if (stat.scaled?.min) { - pct = Math.round((100 * stat.scaled.min) / (stat.split || 1)); - } - stats[stat.statHash] = { - value: stat.value, - pct, - base: 0, - }; - } - } else { - for (const stat of item.stats) { - stats[stat.statHash] = { - value: stat.value, - base: stat.base, - pct: 0, - }; - } - } - } - if (item.destinyVersion === 1) { - row['% IntQ'] = stats.Intellect?.pct ?? 0; - row['% DiscQ'] = stats.Discipline?.pct ?? 0; - row['% StrQ'] = stats.Strength?.pct ?? 0; - row.Int = stats.Intellect?.value ?? 0; - row.Disc = stats.Discipline?.value ?? 0; - row.Str = stats.Strength?.value ?? 0; - } else { - const armorStats = Object.keys(dimArmorStatHashByName).map((statName) => ({ - name: statName, - stat: stats[dimArmorStatHashByName[statName]!], - })); - for (const stat of armorStats) { - row[capitalizeFirstLetter(stat.name)] = stat.stat?.value ?? 0; - } - for (const stat of armorStats) { - row[`${capitalizeFirstLetter(stat.name)} (Base)`] = stat.stat?.base ?? 0; - } - - if (item.sockets) { - row['Seasonal Mod'] = getSpecialtySocketMetadatas(item)?.map((m) => m.slotTag) ?? ''; - } - } - - row.Loadouts = formatLoadouts(item, loadouts); - row.Notes = getNotes(item); - - addPerks(row, item, maxPerks); - - return row; - }); - return data; -} - -function downloadWeapons( - items: DimItem[], - nameMap: { [key: string]: string }, - getTag: (item: DimItem) => TagValue | undefined, - getNotes: (item: DimItem) => string | undefined, - loadouts: LoadoutsByItem, -) { - // We need to always emit enough columns for all perks - const maxPerks = getMaxPerks(items); - - // In PapaParse, the keys of the first objects are used as columns. So if a - // key is omitted from the first object, it won't show up. - // TODO: Replace PapaParse with a simpler/smaller CSV generator - const data = items.map((item) => { - const row: Record = { - Name: item.name, - Hash: item.hash, - Id: `"${item.id}"`, - Tag: getTag(item), - Tier: item.tier, - Type: item.typeName, - Source: source(item), - Category: item.type, - Element: item.element?.displayProperties.name, - [item.destinyVersion === 1 ? 'Light' : 'Power']: item.power, - }; - if (item.destinyVersion === 2) { - row['Masterwork Type'] = getMasterworkStatNames(item.masterworkInfo) || undefined; - row['Masterwork Tier'] = item.masterworkInfo?.tier || undefined; - } - row.Owner = nameMap[item.owner]; - if (item.destinyVersion === 1) { - row['% Leveled'] = (item.percentComplete * 100).toFixed(0); - } - row.Locked = item.locked; - row.Equipped = item.equipped; - row.Year = getItemYear(item); - if (item.destinyVersion === 2) { - row.Season = getSeason(item); - const event = getEvent(item); - row.Event = event ? D2EventInfo[event].name : ''; - } - - const stats = { - aa: 0, - impact: 0, - range: 0, - zoom: 0, - stability: 0, - rof: 0, - reload: 0, - magazine: 0, - equipSpeed: 0, - drawtime: 0, - chargetime: 0, - accuracy: 0, - recoil: 0, - blastRadius: 0, - velocity: 0, - airborne: 0, - shieldduration: 0, - chargerate: 0, - guardresist: 0, - guardefficiency: 0, - guardendurance: 0, - swingspeed: 0, - }; - - if (item.stats) { - for (const stat of item.stats) { - if (stat.value) { - switch (stat.statHash) { - case StatHashes.RecoilDirection: - stats.recoil = stat.value; - break; - case StatHashes.AimAssistance: - stats.aa = stat.value; - break; - case StatHashes.Impact: - stats.impact = stat.value; - break; - case StatHashes.Range: - stats.range = stat.value; - break; - case StatHashes.Zoom: - stats.zoom = stat.value; - break; - case StatHashes.Stability: - stats.stability = stat.value; - break; - case StatHashes.RoundsPerMinute: - stats.rof = stat.value; - break; - case StatHashes.ReloadSpeed: - stats.reload = stat.value; - break; - case StatHashes.Magazine: - case StatHashes.AmmoCapacity: - stats.magazine = stat.value; - break; - case StatHashes.Handling: - stats.equipSpeed = stat.value; - break; - case StatHashes.DrawTime: - stats.drawtime = stat.value; - break; - case StatHashes.ChargeTime: - stats.chargetime = stat.value; - break; - case StatHashes.Accuracy: - stats.accuracy = stat.value; - break; - case StatHashes.BlastRadius: - stats.blastRadius = stat.value; - break; - case StatHashes.Velocity: - stats.velocity = stat.value; - break; - case StatHashes.AirborneEffectiveness: - stats.airborne = stat.value; - break; - case StatHashes.ShieldDuration: - stats.shieldduration = stat.value; - break; - case StatHashes.ChargeRate: - stats.chargerate = stat.value; - break; - case StatHashes.GuardResistance: - stats.guardresist = stat.value; - break; - case StatHashes.GuardEfficiency: - stats.guardefficiency = stat.value; - break; - case StatHashes.GuardEndurance: - stats.guardendurance = stat.value; - break; - case StatHashes.SwingSpeed: - stats.swingspeed = stat.value; - break; - } - } - } - } - - row.Recoil = stats.recoil; - row.AA = stats.aa; - row.Impact = stats.impact; - row.Range = stats.range; - row.Zoom = stats.zoom; - row['Blast Radius'] = stats.blastRadius; - row.Velocity = stats.velocity; - row.Stability = stats.stability; - row.ROF = stats.rof; - row.Reload = stats.reload; - row.Mag = stats.magazine; - if (item.destinyVersion === 2) { - row.Handling = stats.equipSpeed; - } else { - row.Equip = stats.equipSpeed; - } - row['Charge Time'] = stats.chargetime; - if (item.destinyVersion === 2) { - row['Draw Time'] = stats.drawtime; - row.Accuracy = stats.accuracy; - - // Sword Stats - row['Charge Rate'] = stats.chargerate; - row['Guard Resistance'] = stats.guardresist; - row['Guard Efficiency'] = stats.guardefficiency; - row['Guard Endurance'] = stats.guardendurance; - row['Swing Speed'] = stats.swingspeed; - - row['Shield Duration'] = stats.shieldduration; // Glaive - row['Airborne Effectiveness'] = stats.airborne; - - row.Crafted = item.crafted; - row['Crafted Level'] = item.craftedInfo?.level ?? 0; - - row['Kill Tracker'] = getItemKillTrackerInfo(item)?.count ?? 0; - row.Foundry = item.foundry; - } - - row.Loadouts = formatLoadouts(item, loadouts); - row.Notes = getNotes(item); - - addPerks(row, item, maxPerks); - - return row; - }); - - return data; -} diff --git a/src/app/organizer/Columns.tsx b/src/app/organizer/Columns.tsx index cf8edf849c..9248801762 100644 --- a/src/app/organizer/Columns.tsx +++ b/src/app/organizer/Columns.tsx @@ -13,7 +13,6 @@ import TagIcon from 'app/inventory/TagIcon'; import { TagValue, tagConfig } from 'app/inventory/dim-item-info'; import { D1Item, DimItem, DimSocket } from 'app/inventory/item-types'; import { storesSelector } from 'app/inventory/selectors'; -import { source } from 'app/inventory/spreadsheets'; import { isHarmonizable } from 'app/inventory/store/deepsight'; import { getEvent, getSeason } from 'app/inventory/store/season'; import { getStatSortOrder } from 'app/inventory/store/stats'; @@ -28,6 +27,7 @@ import InGameLoadoutIcon from 'app/loadout/ingame/InGameLoadoutIcon'; import { InGameLoadout, Loadout, isInGameLoadout } from 'app/loadout/loadout-types'; import { LoadoutsByItem } from 'app/loadout/selectors'; import { breakerTypeNames, weaponMasterworkY2SocketTypeHash } from 'app/search/d2-known-values'; +import D2Sources from 'app/search/items/search-filters/d2-sources'; import { quoteFilterString } from 'app/search/query-parser'; import { statHashByName } from 'app/search/search-filter-values'; import { getColor, percent } from 'app/shell/formatters'; @@ -77,7 +77,13 @@ import React from 'react'; import { useSelector } from 'react-redux'; import { createCustomStatColumns } from './CustomStatColumns'; +import { + buildNodeNames, + buildSocketNames, + csvStatNamesForDestinyVersion, +} from 'app/inventory/spreadsheets'; import { DeepsightHarmonizerIcon } from 'app/item-popup/DeepsightHarmonizerIcon'; +import { DestinyClass } from 'bungie-api-ts/destiny2'; import styles from './ItemTable.m.scss'; // eslint-disable-line css-modules/no-unused-class import { ColumnDefinition, ColumnGroup, SortDirection, Value } from './table-types'; @@ -121,6 +127,7 @@ const perkStringSort: Comparator = (a, b) => { * This function generates the columns. */ export function getColumns( + useCase: 'organizer' | 'spreadsheet', itemsType: 'weapon' | 'armor' | 'ghost', statHashes: { [statHash: number]: StatInfo; @@ -133,7 +140,7 @@ export function getColumns( loadoutsByItem: LoadoutsByItem, newItems: Set, destinyVersion: DestinyVersion, - onPlugClicked: (value: { item: DimItem; socket: DimSocket; plugHash: number }) => void, + onPlugClicked?: (value: { item: DimItem; socket: DimSocket; plugHash: number }) => void, ): ColumnDefinition[] { const customStatHashes = customStatDefs.map((c) => c.statHash); const statsGroup: ColumnGroup = { @@ -149,7 +156,9 @@ export function getColumns( header: t('Organizer.Columns.StatQuality'), }; - type ColumnWithStat = ColumnDefinition & { statHash: number }; + const csvStatNames = csvStatNamesForDestinyVersion(destinyVersion); + + type ColumnWithStat = ColumnDefinition & { statHash: StatHashes }; const statColumns: ColumnWithStat[] = _.sortBy( filterMap(Object.entries(statHashes), ([statHashStr, statInfo]): ColumnWithStat | undefined => { const statHash = parseInt(statHashStr, 10) as StatHashes; @@ -177,7 +186,7 @@ export function getColumns( if (stat?.statHash === StatHashes.RecoilDirection) { return recoilValue(stat.value); } - return stat?.value || 0; + return stat?.value; }, cell: (_val, item: DimItem) => { const stat = item.stats?.find((s) => s.statHash === statHash); @@ -191,6 +200,12 @@ export function getColumns( const statName = _.invert(statHashByName)[statHash]; return `stat:${statName}:${statName === 'rof' ? '=' : '>='}${value}`; }, + csv: (_value, item) => { + // Re-find the stat instead of using the value passed in, because the + // value passed in can be different if it's Recoil. + const stat = item.stats?.find((s) => s.statHash === statHash); + return [csvStatNames.get(statHash) ?? `UnknownStat ${statHash}`, stat?.value ?? 0]; + }, }; }), (s) => getStatSortOrder(s.statHash), @@ -199,19 +214,20 @@ export function getColumns( const isGhost = itemsType === 'ghost'; const isArmor = itemsType === 'armor'; const isWeapon = itemsType === 'weapon'; + const isSpreadsheet = useCase === 'spreadsheet'; const baseStatColumns: ColumnWithStat[] = - destinyVersion === 2 + destinyVersion === 2 && (isArmor || !isSpreadsheet) ? statColumns.map((column) => ({ ...column, id: `base${column.statHash}`, columnGroup: baseStatsGroup, - value: (item: DimItem): number => { + value: (item: DimItem): number | undefined => { const stat = item.stats?.find((s) => s.statHash === column.statHash); if (stat?.statHash === StatHashes.RecoilDirection) { return recoilValue(stat.base); } - return stat?.base || 0; + return stat?.base; }, cell: (_val, item: DimItem) => { const stat = item.stats?.find((s) => s.statHash === column.statHash); @@ -221,6 +237,15 @@ export function getColumns( return ; }, filter: (value) => `basestat:${_.invert(statHashByName)[column.statHash]}:>=${value}`, + csv: (_value, item) => { + // Re-find the stat instead of using the value passed in, because the + // value passed in can be different if it's Recoil. + const stat = item.stats?.find((s) => s.statHash === column.statHash); + return [ + `${csvStatNames.get(column.statHash) ?? `UnknownStatBase ${column.statHash}`} (Base)`, + stat?.base ?? 0, + ]; + }, })) : []; @@ -228,7 +253,7 @@ export function getColumns( destinyVersion === 1 && isArmor ? _.sortBy( Object.entries(statHashes).map(([statHashStr, statInfo]): ColumnWithStat => { - const statHash = parseInt(statHashStr, 10); + const statHash = parseInt(statHashStr, 10) as StatHashes; return { statHash, id: `quality_${statHash}`, @@ -250,6 +275,16 @@ export function getColumns( {value}% ); }, + csv: (_value, item) => { + if (!isD1Item(item)) { + throw new Error('Expected D1 item'); + } + const stat = item.stats?.find((s) => s.statHash === statHash); + return [ + `% ${csvStatNames.get(statHash) ?? `UnknownStat ${statHash}`}Q`, + stat?.scaled?.min ? Math.round((100 * stat.scaled.min) / (stat.split || 1)) : 0, + ]; + }, }; }), (s) => getStatSortOrder(s.statHash), @@ -267,35 +302,52 @@ export function getColumns( return columnDef; } - const customStats = createCustomStatColumns(customStatDefs); + const customStats = isSpreadsheet ? [] : createCustomStatColumns(customStatDefs); const columns: ColumnDefinition[] = _.compact([ - c({ - id: 'icon', - header: t('Organizer.Columns.Icon'), - value: (i) => i.icon, - cell: (_val, item) => ( - - {(ref, onClick) => ( -
- - {item.crafted && } -
- )} -
- ), - noSort: true, - noHide: true, - }), + !isSpreadsheet && + c({ + id: 'icon', + header: t('Organizer.Columns.Icon'), + value: (i) => i.icon, + cell: (_val, item) => ( + + {(ref, onClick) => ( +
+ + {item.crafted && } +
+ )} +
+ ), + noSort: true, + noHide: true, + }), c({ id: 'name', header: t('Organizer.Columns.Name'), + csv: 'Name', value: (i) => i.name, filter: (name) => `name:${quoteFilterString(name)}`, }), + isSpreadsheet && + c({ + id: 'hash', + header: 'Hash', + csv: 'Hash', + value: (i) => i.hash, + }), + isSpreadsheet && + c({ + id: 'id', + header: 'Id', + csv: 'Id', + value: (i) => `"${i.id}"`, + }), !isGhost && c({ id: 'power', + csv: destinyVersion === 2 ? 'Power' : 'Light', header: , dropdownLabel: t('Organizer.Columns.Power'), value: (item) => item.power, @@ -306,15 +358,26 @@ export function getColumns( c({ id: 'dmg', header: t('Organizer.Columns.Damage'), + csv: 'Element', value: (item) => item.element?.displayProperties.name, cell: (_val, item) => , filter: (_val, item) => `is:${getItemDamageShortName(item)}`, }), + isArmor && + isSpreadsheet && + c({ + id: 'Equippable', + header: 'Equippable', + csv: 'Equippable', + value: (item) => + item.classType === DestinyClass.Unknown ? 'Any' : item.classTypeNameLocalized, + }), (isArmor || isGhost) && destinyVersion === 2 && c({ id: 'energy', header: t('Organizer.Columns.Energy'), + csv: 'Energy Capacity', value: (item) => item.energy?.energyCapacity, defaultSort: SortDirection.DESC, filter: (value) => `energycapacity:>=${value}`, @@ -322,6 +385,7 @@ export function getColumns( c({ id: 'locked', header: , + csv: 'Locked', dropdownLabel: t('Organizer.Columns.Locked'), value: (i) => i.locked, cell: (value) => (value ? : undefined), @@ -331,19 +395,21 @@ export function getColumns( c({ id: 'tag', header: t('Organizer.Columns.Tag'), - value: (item) => getTag(item) ?? '', + csv: 'Tag', + value: (item) => getTag(item), cell: (value) => value && , sort: compareBy((tag) => (tag && tag in tagConfig ? tagConfig[tag].sortOrder : 1000)), filter: (value) => `tag:${value || 'none'}`, }), - c({ - id: 'new', - header: t('Organizer.Columns.New'), - value: (item) => newItems.has(item.id), - cell: (value) => (value ? : undefined), - defaultSort: SortDirection.DESC, - filter: (value) => `${value ? '' : '-'}is:new`, - }), + !isSpreadsheet && + c({ + id: 'new', + header: t('Organizer.Columns.New'), + value: (item) => newItems.has(item.id), + cell: (value) => (value ? : undefined), + defaultSort: SortDirection.DESC, + filter: (value) => `${value ? '' : '-'}is:new`, + }), destinyVersion === 2 && isWeapon && c({ @@ -354,15 +420,19 @@ export function getColumns( craftedDate ? <>{new Date(craftedDate * 1000).toLocaleString()} : undefined, defaultSort: SortDirection.DESC, filter: (value) => `${value ? '' : '-'}is:crafted`, + // TODO: nicer to put the date in the CSV + csv: (value) => ['Crafted', value ? 'crafted' : false], + }), + !isSpreadsheet && + c({ + id: 'recency', + header: t('Organizer.Columns.Recency'), + value: (item) => item.id, + cell: () => '', }), - c({ - id: 'recency', - header: t('Organizer.Columns.Recency'), - value: (item) => item.id, - cell: () => '', - }), destinyVersion === 2 && isWeapon && + !isSpreadsheet && c({ id: 'wishList', header: t('Organizer.Columns.WishList'), @@ -384,9 +454,33 @@ export function getColumns( c({ id: 'tier', header: t('Organizer.Columns.Tier'), + csv: 'Tier', value: (i) => i.tier, filter: (value) => `is:${value}`, }), + isSpreadsheet && + !isGhost && + c({ + id: 'Type', + header: 'Type', + csv: 'Type', + value: (i) => i.typeName, + }), + isSpreadsheet && + isWeapon && + c({ + id: 'Category', + header: 'Category', + csv: 'Category', + value: (i) => i.type, + }), + isSpreadsheet && + c({ + id: 'Equipped', + header: 'Equipped', + csv: 'Equipped', + value: (i) => i.equipped, + }), destinyVersion === 2 && isArmor && c({ @@ -412,17 +506,24 @@ export function getColumns( .map((m) => `modslot:${m}`) .join(' ') : ``, + csv: (value) => [ + 'Seasonal Mod', + // Yes, this is an array most of the time, or an empty string + value?.split(',') ?? '', + ], }), destinyVersion === 1 && c({ id: 'percentComplete', header: t('Organizer.Columns.PercentComplete'), + csv: '% Leveled', value: (item) => item.percentComplete, cell: (value) => percent(value), filter: (value) => `percentage:>=${value}`, }), destinyVersion === 2 && isWeapon && + !isSpreadsheet && c({ id: 'archetype', header: t('Organizer.Columns.Archetype'), @@ -449,6 +550,7 @@ export function getColumns( }), destinyVersion === 2 && isWeapon && + !isSpreadsheet && c({ id: 'breaker', header: t('Organizer.Columns.Breaker'), @@ -467,6 +569,7 @@ export function getColumns( }), destinyVersion === 2 && isArmor && + !isSpreadsheet && c({ id: 'intrinsics', header: t('Organizer.Columns.Intrinsics'), @@ -500,9 +603,26 @@ export function getColumns( sort: perkStringSort, filter: (value) => typeof value === 'string' ? `exactperk:${quoteFilterString(value)}` : undefined, + csv: (_value, item, { maxPerks }) => { + // This could go on any of the perks columns, since it computes a very + // different view of perks, but I just picked one. + const perks = + isD1Item(item) && item.talentGrid + ? buildNodeNames(item.talentGrid.nodes) + : item.sockets + ? buildSocketNames(item) + : []; + + // Return multiple columns + return _.times( + maxPerks, + (index) => [`Perks ${index}`, perks[index]] as [name: string, value: string | undefined], + ); + }, }), destinyVersion === 2 && isWeapon && + !isSpreadsheet && c({ id: 'traits', header: t('Organizer.Columns.Traits'), @@ -521,6 +641,7 @@ export function getColumns( destinyVersion === 2 && isWeapon && + !isSpreadsheet && c({ id: 'originTrait', header: t('Organizer.Columns.OriginTraits'), @@ -537,6 +658,7 @@ export function getColumns( typeof value === 'string' ? `exactperk:${quoteFilterString(value)}` : undefined, }), destinyVersion === 2 && + !isSpreadsheet && c({ id: 'shaders', header: t('Organizer.Columns.Shaders'), @@ -560,6 +682,7 @@ export function getColumns( c({ id: 'quality', header: t('Organizer.Columns.Quality'), + csv: '% Quality', value: (item) => (isD1Item(item) && item.quality ? item.quality.min : 0), cell: (value) => {value}%, filter: (value) => `quality:>=${value}`, @@ -573,6 +696,7 @@ export function getColumns( value: (item) => item.masterworkInfo?.tier, defaultSort: SortDirection.DESC, filter: (value) => `masterwork:>=${value}`, + csv: 'Masterwork Tier', }), destinyVersion === 2 && isWeapon && @@ -580,6 +704,7 @@ export function getColumns( id: 'masterworkStat', header: t('Organizer.Columns.MasterworkStat'), value: (item) => getMasterworkStatNames(item.masterworkInfo), + csv: 'Masterwork Type', }), destinyVersion === 2 && isWeapon && @@ -588,9 +713,11 @@ export function getColumns( header: t('Organizer.Columns.Level'), value: (item) => item.craftedInfo?.level, defaultSort: SortDirection.DESC, + csv: (value) => ['Crafted Level', value ?? 0], }), destinyVersion === 2 && isWeapon && + !isSpreadsheet && c({ id: 'harmonizable', header: t('Organizer.Columns.Harmonizable'), @@ -615,16 +742,28 @@ export function getColumns( ); }, defaultSort: SortDirection.DESC, + csv: (value) => ['Kill Tracker', value ?? 0], + }), + destinyVersion === 2 && + isWeapon && + c({ + id: 'foundry', + header: t('Organizer.Columns.Foundry'), + csv: 'Foundry', + value: (item) => item.foundry, + filter: (value) => `foundry:${value}`, }), destinyVersion === 2 && c({ id: 'source', + csv: 'Source', header: t('Organizer.Columns.Source'), value: source, filter: (value) => `source:${value}`, }), c({ id: 'year', + csv: 'Year', header: t('Organizer.Columns.Year'), value: (item) => getItemYear(item), filter: (value) => `year:${value}`, @@ -632,6 +771,7 @@ export function getColumns( destinyVersion === 2 && c({ id: 'season', + csv: 'Season', header: t('Organizer.Columns.Season'), value: (i) => getSeason(i), filter: (value) => `season:${value}`, @@ -645,12 +785,14 @@ export function getColumns( return event ? D2EventInfo[event].name : undefined; }, filter: (value) => `event:${value}`, + csv: (value) => ['Event', value ?? ''], }), c({ id: 'location', header: t('Organizer.Columns.Location'), value: (item) => item.owner, cell: (_val, item) => , + csv: (value, _item, { storeNamesById }) => ['Owner', storeNamesById[value]], }), c({ id: 'loadouts', @@ -690,23 +832,25 @@ export function getColumns( return loadout && `inloadout:${quoteFilterString(loadout.loadout.name)}`; } }, + csv: (value) => ['Loadouts', value ?? ''], }), c({ id: 'notes', header: t('Organizer.Columns.Notes'), - value: (item) => getNotes(item) ?? '', + csv: 'Notes', + value: (item) => getNotes(item), cell: (_val, item) => , gridWidth: 'minmax(200px, 1fr)', - filter: (value) => `notes:${quoteFilterString(value)}`, + filter: (value) => `notes:${quoteFilterString(value ?? '')}`, }), isWeapon && hasWishList && c({ id: 'wishListNote', header: t('Organizer.Columns.WishListNotes'), - value: (item) => wishList(item)?.notes?.trim() ?? '', + value: (item) => wishList(item)?.notes?.trim(), gridWidth: 'minmax(200px, 1fr)', - filter: (value) => `wishlistnotes:${quoteFilterString(value)}`, + filter: (value) => `wishlistnotes:${quoteFilterString(value ?? '')}`, }), ]); @@ -960,3 +1104,55 @@ function getIntrinsicSockets(item: DimItem) { ? [intrinsicSocket, ...extraIntrinsicSockets] : extraIntrinsicSockets; } + +/** + * This builds stat infos for all the stats that are relevant to a particular category of items. + * It will return the same result for the same category, since all items in a category share stats. + */ +export function buildStatInfo(items: DimItem[]): { + [statHash: number]: StatInfo; +} { + const statHashes: { + [statHash: number]: StatInfo; + } = {}; + for (const item of items) { + if (item.stats) { + for (const stat of item.stats) { + if (statHashes[stat.statHash]) { + // TODO: we don't yet use the min and max values + statHashes[stat.statHash].max = Math.max(statHashes[stat.statHash].max, stat.value); + statHashes[stat.statHash].min = Math.min(statHashes[stat.statHash].min, stat.value); + } else { + statHashes[stat.statHash] = { + id: stat.statHash, + displayProperties: stat.displayProperties, + min: stat.value, + max: stat.value, + enabled: true, + lowerBetter: stat.smallerIsBetter, + statMaximumValue: stat.maximumValue, + bar: stat.bar, + getStat(item) { + return item.stats ? item.stats.find((s) => s.statHash === stat.statHash) : undefined; + }, + }; + } + } + } + } + return statHashes; +} + +// ignore raid & calus sources in favor of more detailed sources +const sourceKeys = Object.keys(D2Sources).filter((k) => !['raid', 'calus'].includes(k)); +function source(item: DimItem) { + if (item.destinyVersion === 2) { + return ( + sourceKeys.find( + (src) => + (item.source && D2Sources[src].sourceHashes?.includes(item.source)) || + D2Sources[src].itemHashes?.includes(item.hash), + ) || '' + ); + } +} diff --git a/src/app/organizer/ItemTable.tsx b/src/app/organizer/ItemTable.tsx index 9a016a478d..5df5b1e7cb 100644 --- a/src/app/organizer/ItemTable.tsx +++ b/src/app/organizer/ItemTable.tsx @@ -44,7 +44,7 @@ import _ from 'lodash'; import React, { ReactNode, memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'; import Dropzone, { DropzoneOptions } from 'react-dropzone'; import { useSelector } from 'react-redux'; -import { getColumnSelectionId, getColumns } from './Columns'; +import { buildStatInfo, getColumnSelectionId, getColumns } from './Columns'; import EnabledColumnsSelector from './EnabledColumnsSelector'; import ItemActions, { TagCommandInfo } from './ItemActions'; import { itemIncludesCategories } from './filtering-utils'; @@ -72,13 +72,13 @@ const categoryToClass: LookupTable = { }; const downloadButtonSettings = [ - { categoryId: ['weapons'], csvType: 'Weapons' as const, label: tl('Bucket.Weapons') }, + { categoryId: ['weapons'], csvType: 'weapon' as const, label: tl('Bucket.Weapons') }, { categoryId: ['hunter', 'titan', 'warlock'], - csvType: 'Armor' as const, + csvType: 'armor' as const, label: tl('Bucket.Armor'), }, - { categoryId: ['ghosts'], csvType: 'Ghost' as const, label: tl('Bucket.Ghost') }, + { categoryId: ['ghosts'], csvType: 'ghost' as const, label: tl('Bucket.Ghost') }, ]; const MemoRow = memo(TableRow); @@ -189,6 +189,7 @@ export default function ItemTable({ categories }: { categories: ItemCategoryTree const columns: ColumnDefinition[] = useMemo( () => getColumns( + 'organizer', itemType, statHashes, getTag, @@ -400,7 +401,6 @@ export default function ItemTable({ categories }: { categories: ItemCategoryTree lastSelectedId.current = item.id; }; - // TODO: drive the CSV export off the same column definitions as this table! let downloadAction: ReactNode | null = null; const downloadButtonSetting = downloadButtonSettings.find((setting) => setting.categoryId.includes(categories[1]?.id), @@ -606,44 +606,6 @@ function sortRows( return Array.from(unsortedRows).sort(comparator); } -/** - * This builds stat infos for all the stats that are relevant to a particular category of items. - * It will return the same result for the same category, since all items in a category share stats. - */ -function buildStatInfo(items: DimItem[]): { - [statHash: number]: StatInfo; -} { - const statHashes: { - [statHash: number]: StatInfo; - } = {}; - for (const item of items) { - if (item.stats) { - for (const stat of item.stats) { - if (statHashes[stat.statHash]) { - // TODO: we don't yet use the min and max values - statHashes[stat.statHash].max = Math.max(statHashes[stat.statHash].max, stat.value); - statHashes[stat.statHash].min = Math.min(statHashes[stat.statHash].min, stat.value); - } else { - statHashes[stat.statHash] = { - id: stat.statHash, - displayProperties: stat.displayProperties, - min: stat.value, - max: stat.value, - enabled: true, - lowerBetter: stat.smallerIsBetter, - statMaximumValue: stat.maximumValue, - bar: stat.bar, - getStat(item) { - return item.stats ? item.stats.find((s) => s.statHash === stat.statHash) : undefined; - }, - }; - } - } - } - } - return statHashes; -} - function TableRow({ row, filteredColumns, diff --git a/src/app/organizer/table-types.ts b/src/app/organizer/table-types.ts index d9df7e3abe..fcc7e334e5 100644 --- a/src/app/organizer/table-types.ts +++ b/src/app/organizer/table-types.ts @@ -16,6 +16,8 @@ export interface ColumnGroup { dropdownLabel?: React.ReactNode; } +export type CSVColumn = [name: string, value: string | string[] | number | boolean | undefined]; + // TODO: column groupings? // TODO: custom configs like the total column? // prop methods make this invariant over V, so disable the rule here @@ -52,6 +54,32 @@ export interface ColumnDefinition { * but sometimes a custom stat should be limited to only displaying for a certain class */ limitToClass?: DestinyClass; + + /** + * A name for this column when it is output as CSV. This will reuse the value + * function as-is. We could reuse the header, but that's localized, while + * historically our CSV column names haven't been. + * + * Alternately, provide a function to override both the column name and the + * value, or emit multiple columns at once. This is mostly to achieve + * compatibility with the existing CSV format, but sometimes it's used to + * output complex data for CSV. For example, perks are output as multiple + * columns. + */ + csv?: + | string + | { + bivarianceHack( + value: V, + item: DimItem, + spreadsheetContext: SpreadsheetContext, + ): CSVColumn | CSVColumn[]; + }['bivarianceHack']; +} + +export interface SpreadsheetContext { + storeNamesById: { [key: string]: string }; + maxPerks: number; } export interface Row { diff --git a/src/app/settings/Spreadsheets.tsx b/src/app/settings/Spreadsheets.tsx index 152419bfd2..305b6b8d6e 100644 --- a/src/app/settings/Spreadsheets.tsx +++ b/src/app/settings/Spreadsheets.tsx @@ -34,7 +34,7 @@ export default function Spreadsheets() { } }; - const downloadCsv = (type: 'Armor' | 'Weapons' | 'Ghost') => dispatch(downloadCsvFiles(type)); + const downloadCsv = (type: 'armor' | 'weapon' | 'ghost') => dispatch(downloadCsvFiles(type)); return (
@@ -48,7 +48,7 @@ export default function Spreadsheets() {