@rine-kit/pubg-domain 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (127) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +38 -2
  3. package/dist/config/default-team.json +27 -0
  4. package/dist/src/config/chicken-index.d.ts +9 -0
  5. package/dist/src/config/chicken-index.js +37 -0
  6. package/dist/src/config/chicken-index.js.map +1 -0
  7. package/dist/src/config/team.d.ts +22 -0
  8. package/dist/src/config/team.js +69 -0
  9. package/dist/src/config/team.js.map +1 -0
  10. package/dist/src/context/context-store.d.ts +40 -0
  11. package/dist/src/context/context-store.js +119 -0
  12. package/dist/src/context/context-store.js.map +1 -0
  13. package/dist/src/context/team-context.d.ts +13 -0
  14. package/dist/src/context/team-context.js +16 -0
  15. package/dist/src/context/team-context.js.map +1 -0
  16. package/dist/src/core.d.ts +23 -0
  17. package/dist/src/core.js +24 -0
  18. package/dist/src/core.js.map +1 -0
  19. package/dist/src/data/model.d.ts +123 -0
  20. package/dist/src/data/model.js +100 -0
  21. package/dist/src/data/model.js.map +1 -0
  22. package/dist/src/data/pubg-api-client.d.ts +59 -0
  23. package/dist/src/data/pubg-api-client.js +441 -0
  24. package/dist/src/data/pubg-api-client.js.map +1 -0
  25. package/dist/src/engine/query-engine.d.ts +20 -0
  26. package/dist/src/engine/query-engine.js +765 -0
  27. package/dist/src/engine/query-engine.js.map +1 -0
  28. package/dist/src/index.d.ts +27 -0
  29. package/dist/src/index.js +28 -0
  30. package/dist/src/index.js.map +1 -0
  31. package/dist/src/migration/importer.d.ts +35 -0
  32. package/dist/src/migration/importer.js +229 -0
  33. package/dist/src/migration/importer.js.map +1 -0
  34. package/dist/src/review/combat-scope.d.ts +11 -0
  35. package/dist/src/review/combat-scope.js +101 -0
  36. package/dist/src/review/combat-scope.js.map +1 -0
  37. package/dist/src/review/event-combination-engine.d.ts +17 -0
  38. package/dist/src/review/event-combination-engine.js +534 -0
  39. package/dist/src/review/event-combination-engine.js.map +1 -0
  40. package/dist/src/review/fight-detector.d.ts +4 -0
  41. package/dist/src/review/fight-detector.js +138 -0
  42. package/dist/src/review/fight-detector.js.map +1 -0
  43. package/dist/src/review/fight-integrity-validator.d.ts +13 -0
  44. package/dist/src/review/fight-integrity-validator.js +134 -0
  45. package/dist/src/review/fight-integrity-validator.js.map +1 -0
  46. package/dist/src/review/fun-candidate-generator.d.ts +9 -0
  47. package/dist/src/review/fun-candidate-generator.js +267 -0
  48. package/dist/src/review/fun-candidate-generator.js.map +1 -0
  49. package/dist/src/review/fun-event-generator.d.ts +8 -0
  50. package/dist/src/review/fun-event-generator.js +740 -0
  51. package/dist/src/review/fun-event-generator.js.map +1 -0
  52. package/dist/src/review/fun-intelligence.d.ts +13 -0
  53. package/dist/src/review/fun-intelligence.js +20 -0
  54. package/dist/src/review/fun-intelligence.js.map +1 -0
  55. package/dist/src/review/fun-ranker.d.ts +13 -0
  56. package/dist/src/review/fun-ranker.js +68 -0
  57. package/dist/src/review/fun-ranker.js.map +1 -0
  58. package/dist/src/review/heavy-weapon-intelligence.d.ts +5 -0
  59. package/dist/src/review/heavy-weapon-intelligence.js +215 -0
  60. package/dist/src/review/heavy-weapon-intelligence.js.map +1 -0
  61. package/dist/src/review/key-operation-detector.d.ts +3 -0
  62. package/dist/src/review/key-operation-detector.js +126 -0
  63. package/dist/src/review/key-operation-detector.js.map +1 -0
  64. package/dist/src/review/match-selector.d.ts +18 -0
  65. package/dist/src/review/match-selector.js +202 -0
  66. package/dist/src/review/match-selector.js.map +1 -0
  67. package/dist/src/review/review-analyzer.d.ts +2 -0
  68. package/dist/src/review/review-analyzer.js +652 -0
  69. package/dist/src/review/review-analyzer.js.map +1 -0
  70. package/dist/src/review/review-facts.d.ts +17 -0
  71. package/dist/src/review/review-facts.js +560 -0
  72. package/dist/src/review/review-facts.js.map +1 -0
  73. package/dist/src/review/review-share.d.ts +48 -0
  74. package/dist/src/review/review-share.js +37 -0
  75. package/dist/src/review/review-share.js.map +1 -0
  76. package/dist/src/review/selection-store.d.ts +25 -0
  77. package/dist/src/review/selection-store.js +80 -0
  78. package/dist/src/review/selection-store.js.map +1 -0
  79. package/dist/src/review/session-review.d.ts +72 -0
  80. package/dist/src/review/session-review.js +492 -0
  81. package/dist/src/review/session-review.js.map +1 -0
  82. package/dist/src/review/special-events.d.ts +3 -0
  83. package/dist/src/review/special-events.js +258 -0
  84. package/dist/src/review/special-events.js.map +1 -0
  85. package/dist/src/review/subgraph.d.ts +26 -0
  86. package/dist/src/review/subgraph.js +146 -0
  87. package/dist/src/review/subgraph.js.map +1 -0
  88. package/dist/src/review/supplemental-intelligence.d.ts +15 -0
  89. package/dist/src/review/supplemental-intelligence.js +431 -0
  90. package/dist/src/review/supplemental-intelligence.js.map +1 -0
  91. package/dist/src/review/team-damage.d.ts +8 -0
  92. package/dist/src/review/team-damage.js +180 -0
  93. package/dist/src/review/team-damage.js.map +1 -0
  94. package/dist/src/review/telemetry-events.d.ts +72 -0
  95. package/dist/src/review/telemetry-events.js +573 -0
  96. package/dist/src/review/telemetry-events.js.map +1 -0
  97. package/dist/src/review/telemetry.d.ts +86 -0
  98. package/dist/src/review/telemetry.js +266 -0
  99. package/dist/src/review/telemetry.js.map +1 -0
  100. package/dist/src/review/types.d.ts +501 -0
  101. package/dist/src/review/types.js +2 -0
  102. package/dist/src/review/types.js.map +1 -0
  103. package/dist/src/review/vehicle-intelligence.d.ts +3 -0
  104. package/dist/src/review/vehicle-intelligence.js +148 -0
  105. package/dist/src/review/vehicle-intelligence.js.map +1 -0
  106. package/dist/src/schema/query.d.ts +876 -0
  107. package/dist/src/schema/query.js +185 -0
  108. package/dist/src/schema/query.js.map +1 -0
  109. package/dist/src/schema/status.d.ts +66 -0
  110. package/dist/src/schema/status.js +49 -0
  111. package/dist/src/schema/status.js.map +1 -0
  112. package/dist/src/service/pubg-domain-service.d.ts +192 -0
  113. package/dist/src/service/pubg-domain-service.js +1598 -0
  114. package/dist/src/service/pubg-domain-service.js.map +1 -0
  115. package/dist/src/storage/repository-port.d.ts +84 -0
  116. package/dist/src/storage/repository-port.js +2 -0
  117. package/dist/src/storage/repository-port.js.map +1 -0
  118. package/dist/src/storage/sqlite-repository-adapter.d.ts +33 -0
  119. package/dist/src/storage/sqlite-repository-adapter.js +66 -0
  120. package/dist/src/storage/sqlite-repository-adapter.js.map +1 -0
  121. package/dist/src/storage/sqlite-repository.d.ts +73 -0
  122. package/dist/src/storage/sqlite-repository.js +652 -0
  123. package/dist/src/storage/sqlite-repository.js.map +1 -0
  124. package/dist/src/time/selector-resolver.d.ts +34 -0
  125. package/dist/src/time/selector-resolver.js +364 -0
  126. package/dist/src/time/selector-resolver.js.map +1 -0
  127. package/package.json +42 -3
@@ -0,0 +1,431 @@
1
+ import { isVehicleWeapon } from './telemetry-events.js';
2
+ function inMatch(event) {
3
+ return event.phase !== 'pre_match';
4
+ }
5
+ function item(value) {
6
+ return String(value ?? '')
7
+ .replace(/^Item_/iu, '')
8
+ .replace(/^Weapon_/iu, '')
9
+ .replace(/_C$/iu, '');
10
+ }
11
+ function weapon(value) {
12
+ if (!value)
13
+ return null;
14
+ return value
15
+ .replace(/^Item_Weapon_/iu, '')
16
+ .replace(/^Weap/iu, '')
17
+ .replace(/_C$/iu, '');
18
+ }
19
+ function playerEvents(events, teamIds, playerId) {
20
+ return events.filter((event) => event.actorId === playerId && teamIds.has(playerId) && inMatch(event));
21
+ }
22
+ function evidence(events) {
23
+ return [...new Set(events.map((event) => event.id))];
24
+ }
25
+ function timeOf(event) {
26
+ return event.timeSeconds ?? Number.POSITIVE_INFINITY;
27
+ }
28
+ function sortedEvidence(events) {
29
+ return evidence([...events].sort((left, right) => timeOf(left) - timeOf(right) || left.id.localeCompare(right.id)));
30
+ }
31
+ export function extractStunGunStats(events, teamIds) {
32
+ const result = [];
33
+ for (const playerId of teamIds) {
34
+ const owned = playerEvents(events, teamIds, playerId).filter((event) => /stungun/iu.test(`${event.itemId ?? ''} ${event.weaponId ?? ''}`));
35
+ const pickups = owned.filter((event) => event.type === 'ITEM_ACQUIRE');
36
+ const shots = owned.filter((event) => event.type === 'ATTACK');
37
+ const hits = owned.filter((event) => event.type === 'DAMAGE' && event.hit);
38
+ const teammateHits = hits.filter((event) => Boolean(event.victimId && teamIds.has(event.victimId))).length;
39
+ const enemyHits = hits.filter((event) => Boolean(event.victimId && !teamIds.has(event.victimId))).length;
40
+ if (!owned.length)
41
+ continue;
42
+ result.push({
43
+ id: `stun-gun-${playerId}`,
44
+ playerId,
45
+ pickups: pickups.length,
46
+ shots: shots.length,
47
+ confirmedHits: hits.length,
48
+ teammateHits,
49
+ enemyHits,
50
+ unknownOutcomes: Math.max(0, shots.length - hits.length),
51
+ evidenceIds: sortedEvidence(owned),
52
+ });
53
+ }
54
+ return result.sort((left, right) => left.playerId.localeCompare(right.playerId));
55
+ }
56
+ function recoveryKind(itemId) {
57
+ const value = itemId.toLowerCase();
58
+ if (value.includes('bandage'))
59
+ return 'bandages';
60
+ if (value.includes('firstaid'))
61
+ return 'firstAids';
62
+ if (value.includes('medkit'))
63
+ return 'medKits';
64
+ if (value.includes('adrenaline'))
65
+ return 'adrenaline';
66
+ if (value.includes('energydrink'))
67
+ return 'energyDrinks';
68
+ if (value.includes('painkiller'))
69
+ return 'painkillers';
70
+ if (/item_(?:heal|boost)_/iu.test(value))
71
+ return 'otherUses';
72
+ return null;
73
+ }
74
+ export function extractRecoveryStats(events, teamIds) {
75
+ const result = [];
76
+ for (const playerId of teamIds) {
77
+ const owned = playerEvents(events, teamIds, playerId);
78
+ const uses = owned.filter((event) => event.type === 'ITEM_USE' && recoveryKind(event.itemId ?? '') !== null);
79
+ const stats = {
80
+ id: `recovery-${playerId}`,
81
+ playerId,
82
+ bandages: 0,
83
+ firstAids: 0,
84
+ medKits: 0,
85
+ adrenaline: 0,
86
+ energyDrinks: 0,
87
+ painkillers: 0,
88
+ otherUses: 0,
89
+ healingAmount: owned
90
+ .filter((event) => event.type === 'HEAL')
91
+ .reduce((sum, event) => sum + Math.max(0, event.healAmount ?? 0), 0),
92
+ evidenceIds: sortedEvidence([...uses, ...owned.filter((event) => event.type === 'HEAL')]),
93
+ };
94
+ for (const event of uses) {
95
+ const kind = recoveryKind(event.itemId ?? '');
96
+ if (kind && kind in stats)
97
+ stats[kind] += 1;
98
+ }
99
+ if (uses.length || stats.healingAmount > 0)
100
+ result.push(stats);
101
+ }
102
+ return result.sort((left, right) => left.playerId.localeCompare(right.playerId));
103
+ }
104
+ function itemCategory(event) {
105
+ const category = `${event.itemCategory ?? ''}/${event.itemSubCategory ?? ''}`.toLowerCase();
106
+ if (category.includes('weapon'))
107
+ return 'weapons';
108
+ if (category.includes('throwable'))
109
+ return 'throwables';
110
+ if (category.includes('ammunition') || category.includes('ammo'))
111
+ return 'ammunition';
112
+ if (category.includes('heal'))
113
+ return 'healing';
114
+ if (category.includes('boost'))
115
+ return 'boosts';
116
+ if (category.includes('armor') || category.includes('headgear') || category.includes('vest'))
117
+ return 'armor';
118
+ if (category.includes('attachment'))
119
+ return 'attachments';
120
+ return null;
121
+ }
122
+ function notableItem(itemId) {
123
+ const value = item(itemId);
124
+ if (/panzer|rocket/iu.test(value))
125
+ return 'Panzerfaust';
126
+ if (/m24/iu.test(value))
127
+ return 'M24';
128
+ if (/beryl/iu.test(value))
129
+ return 'Beryl M762';
130
+ if (/bluezonegrenade/iu.test(value))
131
+ return '蓝区手雷';
132
+ if (/flashbang/iu.test(value))
133
+ return '闪光弹';
134
+ if (/firstaid/iu.test(value))
135
+ return '急救包';
136
+ if (/bandage/iu.test(value))
137
+ return '绷带';
138
+ return null;
139
+ }
140
+ export function extractLootStats(events, teamIds) {
141
+ const result = [];
142
+ for (const playerId of teamIds) {
143
+ const loot = playerEvents(events, teamIds, playerId).filter((event) => event.type === 'ITEM_LOOT');
144
+ if (!loot.length)
145
+ continue;
146
+ const stats = {
147
+ id: `loot-${playerId}`,
148
+ playerId,
149
+ lootBoxPickups: loot.length,
150
+ weapons: 0,
151
+ throwables: 0,
152
+ ammunition: 0,
153
+ healing: 0,
154
+ boosts: 0,
155
+ armor: 0,
156
+ attachments: 0,
157
+ notableItems: [],
158
+ evidenceIds: sortedEvidence(loot),
159
+ };
160
+ for (const event of loot) {
161
+ const category = itemCategory(event);
162
+ if (category)
163
+ stats[category] += 1;
164
+ const notable = notableItem(event.itemId ?? '');
165
+ if (notable && !stats.notableItems.includes(notable))
166
+ stats.notableItems.push(notable);
167
+ }
168
+ result.push(stats);
169
+ }
170
+ return result.sort((left, right) => left.playerId.localeCompare(right.playerId));
171
+ }
172
+ const ACTIVITY_FIELDS = {
173
+ pickup: {
174
+ weapons: 'pickupWeapons',
175
+ throwables: 'pickupThrowables',
176
+ ammunition: 'pickupAmmunition',
177
+ healing: 'pickupHealing',
178
+ boosts: 'pickupBoosts',
179
+ armor: 'pickupArmor',
180
+ attachments: 'pickupAttachments',
181
+ },
182
+ drop: {
183
+ weapons: 'dropWeapons',
184
+ throwables: 'dropThrowables',
185
+ ammunition: 'dropAmmunition',
186
+ healing: 'dropHealing',
187
+ boosts: 'dropBoosts',
188
+ armor: 'dropArmor',
189
+ attachments: 'dropAttachments',
190
+ },
191
+ };
192
+ function incrementLootCategory(stats, direction, category) {
193
+ if (!category)
194
+ return;
195
+ const field = ACTIVITY_FIELDS[direction][category];
196
+ const numericStats = stats;
197
+ numericStats[field] = (numericStats[field] ?? 0) + 1;
198
+ }
199
+ function cosmeticKind(event) {
200
+ const value = `${event.itemId ?? ''} ${event.itemCategory ?? ''} ${event.itemSubCategory ?? ''}`.toLowerCase();
201
+ if (!/skin|cosmetic|costume|outfit|clothing|apparel|fashion/u.test(value))
202
+ return null;
203
+ return /costume|outfit|clothing|apparel|fashion/u.test(value) ? 'clothing' : 'skin';
204
+ }
205
+ /**
206
+ * Count item movement without pretending that every pickup is rare loot.
207
+ * Cosmetic fields use only explicit item-event metadata; kill/settlement
208
+ * payloads are deliberately not interpreted as pickup records.
209
+ */
210
+ export function extractLootActivityStats(events, teamIds) {
211
+ const result = [];
212
+ for (const playerId of teamIds) {
213
+ const owned = playerEvents(events, teamIds, playerId);
214
+ const pickups = owned.filter((event) => event.type === 'ITEM_ACQUIRE');
215
+ const drops = owned.filter((event) => event.type === 'ITEM_DROP');
216
+ const lootBoxes = owned.filter((event) => event.type === 'ITEM_LOOT');
217
+ if (!pickups.length && !drops.length && !lootBoxes.length)
218
+ continue;
219
+ const stats = {
220
+ id: `loot-activity-${playerId}`,
221
+ playerId,
222
+ pickupEvents: pickups.length,
223
+ dropEvents: drops.length,
224
+ lootBoxPickups: lootBoxes.length,
225
+ pickupWeapons: 0,
226
+ pickupThrowables: 0,
227
+ pickupAmmunition: 0,
228
+ pickupHealing: 0,
229
+ pickupBoosts: 0,
230
+ pickupArmor: 0,
231
+ pickupAttachments: 0,
232
+ dropWeapons: 0,
233
+ dropThrowables: 0,
234
+ dropAmmunition: 0,
235
+ dropHealing: 0,
236
+ dropBoosts: 0,
237
+ dropArmor: 0,
238
+ dropAttachments: 0,
239
+ cosmeticPickups: 0,
240
+ clothingPickups: 0,
241
+ notableItems: [],
242
+ evidenceIds: sortedEvidence([...pickups, ...drops, ...lootBoxes]),
243
+ };
244
+ for (const event of pickups) {
245
+ incrementLootCategory(stats, 'pickup', itemCategory(event));
246
+ const cosmetic = cosmeticKind(event);
247
+ if (cosmetic) {
248
+ stats.cosmeticPickups += 1;
249
+ if (cosmetic === 'clothing')
250
+ stats.clothingPickups += 1;
251
+ }
252
+ const notable = notableItem(event.itemId ?? '');
253
+ if (notable && !stats.notableItems.includes(notable))
254
+ stats.notableItems.push(notable);
255
+ }
256
+ for (const event of drops)
257
+ incrementLootCategory(stats, 'drop', itemCategory(event));
258
+ for (const event of lootBoxes) {
259
+ const notable = notableItem(event.itemId ?? '');
260
+ if (notable && !stats.notableItems.includes(notable))
261
+ stats.notableItems.push(notable);
262
+ }
263
+ result.push(stats);
264
+ }
265
+ return result.sort((left, right) => left.playerId.localeCompare(right.playerId));
266
+ }
267
+ export function extractVehicleTrunkTransfers(events, teamIds) {
268
+ return events
269
+ .filter((event) => inMatch(event) &&
270
+ (event.type === 'ITEM_TRUNK_PUT' || event.type === 'ITEM_TRUNK_PICKUP') &&
271
+ event.actorId !== null &&
272
+ teamIds.has(event.actorId) &&
273
+ Boolean(event.itemId))
274
+ .sort((left, right) => timeOf(left) - timeOf(right) || left.id.localeCompare(right.id))
275
+ .map((event, index) => ({
276
+ id: `trunk-transfer-${index + 1}`,
277
+ direction: event.type === 'ITEM_TRUNK_PUT' ? 'PUT' : 'PICKUP',
278
+ playerId: event.actorId,
279
+ item: item(event.itemId),
280
+ ...(event.itemCategory ? { itemCategory: event.itemCategory } : {}),
281
+ ...(event.itemSubCategory ? { itemSubCategory: event.itemSubCategory } : {}),
282
+ vehicleId: event.vehicleId,
283
+ vehicleType: event.vehicleType,
284
+ stackCount: event.stackCount ?? null,
285
+ time: event.timeSeconds,
286
+ evidenceIds: [event.id],
287
+ }));
288
+ }
289
+ export function extractEnvironmentStats(events, teamIds) {
290
+ const result = [];
291
+ for (const playerId of teamIds) {
292
+ const owned = playerEvents(events, teamIds, playerId);
293
+ const interaction = owned.filter((event) => event.type === 'OBJECT_INTERACTION');
294
+ const destroyed = owned.filter((event) => event.type === 'OBJECT_DESTROY');
295
+ const vaults = owned.filter((event) => event.type === 'VAULT');
296
+ if (!interaction.length && !destroyed.length && !vaults.length)
297
+ continue;
298
+ const destroyedObjects = new Map();
299
+ for (const event of destroyed) {
300
+ const objectType = event.objectType?.trim() || '未命名物体';
301
+ destroyedObjects.set(objectType, (destroyedObjects.get(objectType) ?? 0) + 1);
302
+ }
303
+ const terrainActions = destroyed.filter((event) => /dig|terrain|ground|hole|crater|deform/u.test(`${event.objectType ?? ''} ${event.rawType}`)).length;
304
+ result.push({
305
+ id: `environment-${playerId}`,
306
+ playerId,
307
+ doorOpens: interaction.filter((event) => /opening/iu.test(event.objectStatus ?? '')).length,
308
+ doorCloses: interaction.filter((event) => /closing/iu.test(event.objectStatus ?? '')).length,
309
+ windowsDestroyed: destroyed.filter((event) => /window/iu.test(event.objectType ?? '')).length,
310
+ fencesDestroyed: destroyed.filter((event) => /fence/iu.test(event.objectType ?? '')).length,
311
+ vaults: vaults.length,
312
+ ledgeGrabs: vaults.filter((event) => event.isLedgeGrab === true).length,
313
+ vaultsOnVehicle: vaults.filter((event) => event.isVaultOnVehicle === true).length,
314
+ destroyedObjects: [...destroyedObjects.entries()]
315
+ .map(([objectType, count]) => ({ objectType, count }))
316
+ .sort((left, right) => right.count - left.count || left.objectType.localeCompare(right.objectType)),
317
+ terrainActions,
318
+ eventTimes: [
319
+ ...new Set([...interaction, ...destroyed, ...vaults]
320
+ .map((event) => event.timeSeconds)
321
+ .filter((time) => time !== null)),
322
+ ].sort((left, right) => left - right),
323
+ evidenceIds: sortedEvidence([...interaction, ...destroyed, ...vaults]),
324
+ });
325
+ }
326
+ return result.sort((left, right) => left.playerId.localeCompare(right.playerId));
327
+ }
328
+ export function extractArmorBreakFacts(events, teamIds) {
329
+ const armorEvents = events.filter((event) => event.type === 'ARMOR_DESTROY' &&
330
+ inMatch(event) &&
331
+ event.actorId &&
332
+ teamIds.has(event.actorId));
333
+ return armorEvents
334
+ .sort((left, right) => timeOf(left) - timeOf(right) || left.id.localeCompare(right.id))
335
+ .map((event) => {
336
+ const followUp = event.attackId
337
+ ? events.find((candidate) => candidate.attackId === event.attackId &&
338
+ (candidate.type === 'KNOCK' || candidate.type === 'KILL'))
339
+ : undefined;
340
+ return {
341
+ id: `armor-break-${event.id}`,
342
+ actorPlayerId: event.actorId,
343
+ victimPlayerId: event.victimId,
344
+ armorItem: event.itemId,
345
+ armorSlot: event.itemSubCategory ?? null,
346
+ weapon: weapon(event.weaponId),
347
+ damageReason: event.damageReason ?? null,
348
+ distanceMeters: event.distanceMeters,
349
+ attackId: event.attackId,
350
+ time: event.timeSeconds,
351
+ followUp: followUp?.type === 'KNOCK' ? 'KNOCK' : followUp?.type === 'KILL' ? 'KILL' : null,
352
+ evidenceIds: [...new Set([event.id, ...(followUp ? [followUp.id] : [])])],
353
+ };
354
+ });
355
+ }
356
+ function vehicleEvent(event) {
357
+ return (event.type === 'WHEEL_DESTROY' ||
358
+ event.type === 'VEHICLE_DAMAGE' ||
359
+ event.type === 'VEHICLE_DESTROY' ||
360
+ isVehicleWeapon(event));
361
+ }
362
+ export function extractVehicleImpactFacts(events, teamIds) {
363
+ const grouped = new Map();
364
+ for (const event of events) {
365
+ if (!event.actorId || !teamIds.has(event.actorId) || !inMatch(event) || !event.attackId)
366
+ continue;
367
+ const key = `${event.actorId}:${event.attackId}`;
368
+ const bucket = grouped.get(key) ?? [];
369
+ bucket.push(event);
370
+ grouped.set(key, bucket);
371
+ }
372
+ const result = [];
373
+ for (const [key, groupedEvents] of grouped) {
374
+ if (!groupedEvents.some(vehicleEvent))
375
+ continue;
376
+ const wheels = groupedEvents.filter((event) => event.type === 'WHEEL_DESTROY');
377
+ const vehicleDamage = groupedEvents
378
+ .filter((event) => event.type === 'VEHICLE_DAMAGE')
379
+ .reduce((sum, event) => sum + event.vehicleDamage, 0);
380
+ const playerDamage = groupedEvents
381
+ .filter((event) => event.type === 'DAMAGE' && event.victimId)
382
+ .reduce((sum, event) => sum + event.damage, 0);
383
+ const knocks = groupedEvents.filter((event) => event.type === 'KNOCK').length;
384
+ const kills = groupedEvents.filter((event) => event.type === 'KILL').length;
385
+ const vehicleId = groupedEvents.find((event) => event.vehicleId)?.vehicleId ?? null;
386
+ const vehicleType = groupedEvents.find((event) => event.vehicleType)?.vehicleType ?? null;
387
+ const destroyedDirectly = groupedEvents.filter((event) => event.type === 'VEHICLE_DESTROY').length;
388
+ const lastTime = Math.max(...groupedEvents.map((event) => event.timeSeconds ?? 0));
389
+ const linkedDestroy = destroyedDirectly > 0
390
+ ? 0
391
+ : events.filter((event) => event.type === 'VEHICLE_DESTROY' &&
392
+ event.actorId === groupedEvents[0]?.actorId &&
393
+ vehicleId !== null &&
394
+ event.vehicleId === vehicleId &&
395
+ event.timeSeconds !== null &&
396
+ event.timeSeconds >= (groupedEvents[0]?.timeSeconds ?? 0) &&
397
+ event.timeSeconds <= lastTime + 10).length;
398
+ // Vehicle damage is already aggregated by VehicleStats. Keep attack-level
399
+ // facts only when they form a useful chain (wheel destruction, a vehicle
400
+ // destruction, or a player knock/kill), rather than persisting hundreds of
401
+ // ordinary bullet-to-vehicle ticks.
402
+ if (!wheels.length && knocks <= 0 && kills <= 0 && destroyedDirectly <= 0 && linkedDestroy <= 0)
403
+ continue;
404
+ const [playerId, attackId] = key.split(':');
405
+ if (!playerId || !attackId)
406
+ continue;
407
+ result.push({
408
+ id: `vehicle-impact-${playerId}-${attackId}`,
409
+ playerId,
410
+ attackId,
411
+ vehicleId,
412
+ vehicleType,
413
+ wheelsDestroyed: wheels.length,
414
+ vehicleDamage: Math.round(vehicleDamage * 100) / 100,
415
+ playerDamage: Math.round(playerDamage * 100) / 100,
416
+ knocks,
417
+ kills,
418
+ vehicleDestroyed: destroyedDirectly + linkedDestroy,
419
+ time: groupedEvents.reduce((earliest, event) => earliest === null || (event.timeSeconds !== null && event.timeSeconds < earliest)
420
+ ? event.timeSeconds
421
+ : earliest, null),
422
+ evidenceIds: sortedEvidence([
423
+ ...groupedEvents,
424
+ ...events.filter((event) => event.type === 'VEHICLE_DESTROY' && linkedDestroy > 0 && event.vehicleId === vehicleId),
425
+ ]),
426
+ });
427
+ }
428
+ return result.sort((left, right) => (left.time ?? Number.POSITIVE_INFINITY) - (right.time ?? Number.POSITIVE_INFINITY) ||
429
+ left.id.localeCompare(right.id));
430
+ }
431
+ //# sourceMappingURL=supplemental-intelligence.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"supplemental-intelligence.js","sourceRoot":"","sources":["../../../src/review/supplemental-intelligence.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import type { FlashStats, TeamDamageFact, TeamDamageSource, TeamVehicleEvent } from './types.js';
2
+ import type { NormalizedTelemetryEvent } from './telemetry-events.js';
3
+ export declare function teamDamageSource(event: NormalizedTelemetryEvent): TeamDamageSource | null;
4
+ /** Aggregate only real friendly-fire hit records. Attack/miss records are excluded. */
5
+ export declare function extractTeamDamageFacts(events: NormalizedTelemetryEvent[], teamIds: Set<string>): TeamDamageFact[];
6
+ /** Keep collision hit/knock/kill facts separate from distance and ride facts. */
7
+ export declare function extractTeamVehicleEvents(events: NormalizedTelemetryEvent[], teamIds: Set<string>): TeamVehicleEvent[];
8
+ export declare function extractFlashStats(events: NormalizedTelemetryEvent[], teamIds: Set<string>): FlashStats[];
@@ -0,0 +1,180 @@
1
+ import { isExplosiveWeapon, isFlashWeapon, isMeleeWeapon, isVehicleWeapon, meleeKindOf, } from './telemetry-events.js';
2
+ function weaponOf(event) {
3
+ return event.weaponId ?? event.itemId;
4
+ }
5
+ function trackedPair(event, teamIds) {
6
+ return Boolean(event.actorId &&
7
+ event.victimId &&
8
+ event.actorId !== event.victimId &&
9
+ teamIds.has(event.actorId) &&
10
+ teamIds.has(event.victimId));
11
+ }
12
+ function damageValue(event) {
13
+ return event.type === 'VEHICLE_DAMAGE' && event.vehicleDamage > 0
14
+ ? event.vehicleDamage
15
+ : event.damage;
16
+ }
17
+ function hitConfirmed(event) {
18
+ return event.type === 'VEHICLE_DAMAGE'
19
+ ? event.vehicleDamage > 0 || event.damage > 0 || event.hit
20
+ : event.type === 'DAMAGE' && (event.damage > 0 || event.hit);
21
+ }
22
+ export function teamDamageSource(event) {
23
+ if (!['DAMAGE', 'VEHICLE_DAMAGE'].includes(event.type))
24
+ return null;
25
+ if (isVehicleWeapon(event))
26
+ return 'VEHICLE';
27
+ if (isMeleeWeapon(weaponOf(event), event.damageTypeCategory))
28
+ return 'MELEE';
29
+ if (isExplosiveWeapon(weaponOf(event)))
30
+ return 'EXPLOSIVE';
31
+ return 'GUN';
32
+ }
33
+ /** Aggregate only real friendly-fire hit records. Attack/miss records are excluded. */
34
+ export function extractTeamDamageFacts(events, teamIds) {
35
+ const grouped = new Map();
36
+ for (const event of events) {
37
+ if (!trackedPair(event, teamIds) || !hitConfirmed(event))
38
+ continue;
39
+ const source = teamDamageSource(event);
40
+ if (!source || !event.actorId || !event.victimId)
41
+ continue;
42
+ // Keep explicit punches, kicks and other melee weapons separate. A single
43
+ // player can hit a teammate with both feet and fists in the same match;
44
+ // merging the buckets would make the ledger lose the action detail.
45
+ const meleeKind = source === 'MELEE' ? meleeKindOf(weaponOf(event), event.damageTypeCategory) : null;
46
+ const key = `${event.actorId}:${event.victimId}:${source}:${meleeKind}:${event.phase ?? 'unknown'}`;
47
+ const existing = grouped.get(key);
48
+ if (existing) {
49
+ existing.hitCount += 1;
50
+ existing.damage += damageValue(event);
51
+ if (event.timeSeconds !== null)
52
+ existing.timestamps.push(event.timeSeconds);
53
+ if (!existing.weapon && weaponOf(event))
54
+ existing.weapon = weaponOf(event);
55
+ if (!existing.damageTypeCategory && event.damageTypeCategory)
56
+ existing.damageTypeCategory = event.damageTypeCategory;
57
+ if (!existing.vehicleId && event.vehicleId)
58
+ existing.vehicleId = event.vehicleId;
59
+ existing.evidenceIds.push(event.id);
60
+ continue;
61
+ }
62
+ const timestamps = event.timeSeconds === null ? [] : [event.timeSeconds];
63
+ grouped.set(key, {
64
+ id: `team-damage-${grouped.size + 1}`,
65
+ actorPlayerId: event.actorId,
66
+ victimPlayerId: event.victimId,
67
+ hitCount: 1,
68
+ damage: damageValue(event),
69
+ source,
70
+ timestamp: event.timeSeconds,
71
+ timestamps,
72
+ ...(weaponOf(event) ? { weapon: weaponOf(event) } : {}),
73
+ ...(event.damageTypeCategory ? { damageTypeCategory: event.damageTypeCategory } : {}),
74
+ ...(meleeKind ? { meleeKind } : {}),
75
+ ...(event.vehicleId ? { vehicleId: event.vehicleId } : {}),
76
+ ...(event.phase ? { phase: event.phase } : {}),
77
+ evidenceIds: [event.id],
78
+ });
79
+ }
80
+ return [...grouped.values()].map((fact) => ({
81
+ ...fact,
82
+ damage: Math.round(fact.damage * 100) / 100,
83
+ timestamps: [...new Set(fact.timestamps)].sort((left, right) => left - right),
84
+ evidenceIds: [...new Set(fact.evidenceIds)],
85
+ }));
86
+ }
87
+ function vehicleEventKey(event) {
88
+ if (event.type === 'KILL') {
89
+ return `KILL:${event.victimId ?? event.dbnoId ?? event.attackId ?? event.id}`;
90
+ }
91
+ if (event.type === 'KNOCK') {
92
+ return `KNOCK:${event.dbnoId ?? `${event.victimId ?? ''}:${event.attackId ?? event.timeSeconds ?? event.id}`}`;
93
+ }
94
+ return `HIT:${event.id}`;
95
+ }
96
+ /** Keep collision hit/knock/kill facts separate from distance and ride facts. */
97
+ export function extractTeamVehicleEvents(events, teamIds) {
98
+ const confirmedDriverVehicles = new Set();
99
+ for (const event of events) {
100
+ if (event.actorId && event.vehicleId && event.driverConfirmed === true) {
101
+ confirmedDriverVehicles.add(`${event.actorId}:${event.vehicleId}`);
102
+ }
103
+ }
104
+ const seen = new Set();
105
+ const result = [];
106
+ for (const event of events) {
107
+ if (!trackedPair(event, teamIds) || !event.actorId || !event.victimId)
108
+ continue;
109
+ const type = event.type === 'KNOCK' ? 'KNOCK' : event.type === 'KILL' ? 'KILL' : 'HIT';
110
+ if (type === 'HIT' && !['DAMAGE', 'VEHICLE_DAMAGE'].includes(event.type))
111
+ continue;
112
+ if (type !== 'HIT' && !['KNOCK', 'KILL'].includes(event.type))
113
+ continue;
114
+ if (!isVehicleWeapon(event))
115
+ continue;
116
+ if (type === 'HIT' && !hitConfirmed(event))
117
+ continue;
118
+ const key = `${type}:${event.actorId}:${event.victimId}:${vehicleEventKey(event)}:${event.phase ?? 'unknown'}`;
119
+ if (seen.has(key))
120
+ continue;
121
+ seen.add(key);
122
+ result.push({
123
+ id: `team-vehicle-${result.length + 1}`,
124
+ type,
125
+ actorPlayerId: event.actorId,
126
+ victimPlayerId: event.victimId,
127
+ damage: damageValue(event),
128
+ timestamp: event.timeSeconds,
129
+ vehicleId: event.vehicleId,
130
+ driverConfirmed: event.driverConfirmed === true ||
131
+ Boolean(event.vehicleId && confirmedDriverVehicles.has(`${event.actorId}:${event.vehicleId}`)),
132
+ ...(event.phase ? { phase: event.phase } : {}),
133
+ evidenceIds: [event.id],
134
+ });
135
+ }
136
+ return result;
137
+ }
138
+ export function extractFlashStats(events, teamIds) {
139
+ const grouped = new Map();
140
+ const counted = new Set();
141
+ const lastByWeapon = new Map();
142
+ for (const event of events) {
143
+ // Depending on the game mode, a flash is represented by either the
144
+ // weapon attack record or LogPlayerUseThrowable. Both may be emitted for
145
+ // the same use, so attackId is used only as a de-duplication key.
146
+ if (!['ATTACK', 'THROWABLE_USE'].includes(event.type) ||
147
+ !event.actorId ||
148
+ !teamIds.has(event.actorId) ||
149
+ !isFlashWeapon(weaponOf(event)))
150
+ continue;
151
+ const signature = `${event.actorId}:${weaponOf(event) ?? 'flash'}`;
152
+ const attackKey = event.attackId ? `${event.actorId}:${event.attackId}` : null;
153
+ const previous = lastByWeapon.get(signature);
154
+ const isDuplicateRecord = !attackKey &&
155
+ previous &&
156
+ previous.type !== event.type &&
157
+ previous.time !== null &&
158
+ event.timeSeconds !== null &&
159
+ Math.abs(previous.time - event.timeSeconds) <= 0.5;
160
+ const key = attackKey ?? (isDuplicateRecord ? previous.key : event.id);
161
+ if (counted.has(key))
162
+ continue;
163
+ counted.add(key);
164
+ const current = grouped.get(event.actorId) ?? {
165
+ id: `flash-${event.actorId}`,
166
+ playerId: event.actorId,
167
+ uses: 0,
168
+ evidenceIds: [],
169
+ };
170
+ current.uses += 1;
171
+ current.evidenceIds.push(event.id);
172
+ grouped.set(event.actorId, current);
173
+ lastByWeapon.set(signature, { type: event.type, time: event.timeSeconds, key });
174
+ }
175
+ return [...grouped.values()].map((item) => ({
176
+ ...item,
177
+ evidenceIds: [...new Set(item.evidenceIds)],
178
+ }));
179
+ }
180
+ //# sourceMappingURL=team-damage.js.map