-
Notifications
You must be signed in to change notification settings - Fork 18
/
generate-catalyst-data.ts
177 lines (150 loc) · 6.86 KB
/
generate-catalyst-data.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
import { getAllDefs, getDef } from '@d2api/manifest-node';
import { TierType } from 'bungie-api-ts/destiny2/interfaces.js';
import { ItemCategoryHashes } from '../data/generated-enums.js';
import { annotate, uniqAndSortArray, writeFile } from './helpers.js';
import { infoLog } from './log.js';
const TAG = 'CATALYST-DATA';
const exoticWeaponHashesWithCatalyst: number[] = [];
const exoticWeaponHashToCatalystRecord: Record<string, number> = {};
const catalystRecordNames: string[] = [];
const catalystPresentationNodeHash = getCatalystPresentationNodeHash();
// These catalysts are not available in-game.
const IGNORED_CATALYSTS = [
4273298922, // Bastion
2732252706, // Devil's Ruin
];
const IGNORED_CATALYSTS_NAMES: string[] = []; // Filled in below with names via hashes from above
IGNORED_CATALYSTS.forEach((hash) =>
IGNORED_CATALYSTS_NAMES.push(getDef('InventoryItem', hash)?.displayProperties.name ?? ''),
);
const allsockets = getAllDefs('SocketType');
const inventoryItemsWithDummies = getAllDefs('InventoryItem').filter(
(i) => !i.crafting && !IGNORED_CATALYSTS.includes(i.hash),
);
const inventoryItems = inventoryItemsWithDummies.filter(
(i) => !i.itemCategoryHashes?.includes(ItemCategoryHashes.Dummies),
);
const craftableExotics = getAllDefs('InventoryItem')
.filter((i) => i.crafting)
.map((i) => getDef('InventoryItem', i.crafting.outputItemHash))
.filter((i) => i?.inventory?.tierType === TierType.Exotic);
// this is keyed with record hashes, and the values are catalyst inventoryItem icons
// (more interesting than the all-identical icons on catalyst triumphs)
const triumphData: any = { icon: String, source: String };
getDef('PresentationNode', catalystPresentationNodeHash)?.children.presentationNodes.forEach((p) =>
getDef('PresentationNode', p.presentationNodeHash)?.children.records.forEach((r) => {
const recordName = getDef('Record', r.recordHash)?.displayProperties.name;
catalystRecordNames.push(recordName ?? '');
}),
);
// loop the catalyst section of triumphs
getDef('PresentationNode', catalystPresentationNodeHash)?.children.presentationNodes.forEach((p) =>
getDef('PresentationNode', p.presentationNodeHash)?.children.records.forEach((r) => {
const record = getDef('Record', r.recordHash);
const recordName = record?.displayProperties.name;
if (!record || !recordName) {
return;
}
// look for an inventoryItem with the same name, and plugStyle 1 (should find the catalyst for that gun)
let itemWithSameName = inventoryItems.find(
(i) => i.displayProperties.name === recordName && i.plug?.plugStyle === 1,
);
// Work around for weirdly named catalysts
if (recordName === 'Two-Tailed Fox Catalyst') {
itemWithSameName = inventoryItems.find(
(i) => i.displayProperties.name === 'Third Tail' && i.plug?.plugStyle === 1,
);
}
// Work around for exotic quest craftables
// still no good icon for osteo striga catalyst
switch (recordName) {
case 'Revision Zero Catalyst':
itemWithSameName = findDummyItemWithSpecificName('4-Timer Refit');
break;
case 'Immovable Refit':
itemWithSameName = findDummyItemWithSpecificName('Immovable Refit');
break;
case 'Wish-Keeper Catalyst':
itemWithSameName = findDummyItemWithSpecificName('Hatchling Refit');
break;
}
const matchingExotic =
(itemWithSameName &&
(findWeaponViaCatalystPlug(itemWithSameName.hash) ??
findWeaponViaCatalystPCH(itemWithSameName.plug?.plugCategoryHash))) ??
craftableExotics.find((i) =>
record.displayProperties.description.includes(i!.displayProperties.name),
);
if (matchingExotic) {
exoticWeaponHashToCatalystRecord[matchingExotic.hash] = r.recordHash;
exoticWeaponHashesWithCatalyst.push(matchingExotic.hash);
}
// and get its icon image
const icon = itemWithSameName?.displayProperties?.icon;
// this "if" check is because of classified data situations
if (icon) {
triumphData[r.recordHash] = icon;
} else {
if (!IGNORED_CATALYSTS_NAMES.some((term) => recordName?.includes(term))) {
infoLog(TAG, `no catalyst image found for ${r.recordHash} ${recordName}`);
}
}
}),
);
// Generate List of Sorted Exotic Weapons Hashes with Catalysts
const pretty = `const exoticWeaponHashesWithCatalyst = new Set<number>([\n${uniqAndSortArray(
exoticWeaponHashesWithCatalyst,
)
.map((h) => `${h},\n`)
.join('')}]);\n\nexport default exoticWeaponHashesWithCatalyst;`;
const annotatedExoticHashes = annotate(pretty);
// Get all Dummy Catalyst items, figure out their PCHs, and map them to the reinitializationPossiblePlugHashes items if available
// This will create a mapping of dummy catalyst -> actual catalyst for all guns that 'auto-apply' catalyst when pulled from collections
const dummyCatalystMapping = Object.fromEntries(
getAllDefs('InventoryItem')
.filter((i) => i.itemType === 20 && i.plug?.uiPlugLabel === 'masterwork_interactable')
.filter((i) => i.plug?.plugCategoryHash && i.hash)
.map((i) => [i.hash, findAutoAppliedCatalystForCatalystPCH(i.plug!.plugCategoryHash)])
.filter(([hash, catalyst]) => hash && catalyst)
.map(([hash, catalyst]) => [hash, catalyst!]),
);
writeFile('./output/dummy-catalyst-mapping.json', dummyCatalystMapping);
writeFile('./output/catalyst-triumph-icons.json', triumphData);
writeFile('./output/exotics-with-catalysts.ts', annotatedExoticHashes);
writeFile('./output/exotic-to-catalyst-record.json', exoticWeaponHashToCatalystRecord);
function getCatalystPresentationNodeHash(): number | undefined {
const presentationNodes = getAllDefs('PresentationNode');
const catNodeHash = presentationNodes.find(
(p) =>
p.displayProperties.name === 'Exotic Catalysts' && p.children.presentationNodes.length > 1,
)?.hash;
return catNodeHash;
}
function findWeaponViaCatalystPlug(catalystPlugHash: number) {
return inventoryItems.find((item) =>
item.sockets?.socketEntries.find(
(socket) => socket.reusablePlugItems[0]?.plugItemHash === catalystPlugHash,
),
);
}
function findWeaponViaCatalystPCH(catalystPCH: number | undefined) {
const socketTypeHash = allsockets.find((sockets) =>
sockets.plugWhitelist?.find((plug) => plug.categoryHash === catalystPCH),
)?.hash;
return inventoryItems.find((item) =>
item.sockets?.socketEntries.find((socket) => socket.socketTypeHash === socketTypeHash),
);
}
function findDummyItemWithSpecificName(DummyItemName: string) {
return inventoryItemsWithDummies.find(
(i) =>
i.displayProperties.name === DummyItemName &&
i.itemCategoryHashes?.includes(ItemCategoryHashes.Dummies),
);
}
function findAutoAppliedCatalystForCatalystPCH(catalystPCH: number) {
const plug = allsockets
.flatMap((socket) => socket.plugWhitelist || [])
.find((plug) => plug.categoryHash === catalystPCH);
return plug?.reinitializationPossiblePlugHashes?.[0];
}