steamworks-ffi-node 0.1.1 → 0.2.1

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.
@@ -0,0 +1,673 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.SteamAchievementManager = void 0;
37
+ const koffi = __importStar(require("koffi"));
38
+ /**
39
+ * Manages all Steam achievement operations
40
+ */
41
+ class SteamAchievementManager {
42
+ constructor(libraryLoader, apiCore) {
43
+ this.libraryLoader = libraryLoader;
44
+ this.apiCore = apiCore;
45
+ }
46
+ /**
47
+ * Get all achievements from Steam
48
+ */
49
+ async getAllAchievements() {
50
+ if (!this.apiCore.isInitialized()) {
51
+ console.warn('[Steamworks] WARNING: Steam API not initialized');
52
+ return [];
53
+ }
54
+ const userStatsInterface = this.apiCore.getUserStatsInterface();
55
+ if (!userStatsInterface) {
56
+ console.warn('[Steamworks] WARNING: UserStats interface not available');
57
+ return [];
58
+ }
59
+ try {
60
+ // Run callbacks to ensure we have latest data
61
+ this.apiCore.runCallbacks();
62
+ const achievements = [];
63
+ const numAchievements = this.libraryLoader.SteamAPI_ISteamUserStats_GetNumAchievements(userStatsInterface);
64
+ console.log(`[Steamworks] Found ${numAchievements} achievements in Steam`);
65
+ for (let i = 0; i < numAchievements; i++) {
66
+ try {
67
+ // Get achievement API name
68
+ const apiName = this.libraryLoader.SteamAPI_ISteamUserStats_GetAchievementName(userStatsInterface, i);
69
+ if (!apiName)
70
+ continue;
71
+ // Get display name
72
+ const displayName = this.libraryLoader.SteamAPI_ISteamUserStats_GetAchievementDisplayAttribute(userStatsInterface, apiName, 'name') || apiName;
73
+ // Get description
74
+ const description = this.libraryLoader.SteamAPI_ISteamUserStats_GetAchievementDisplayAttribute(userStatsInterface, apiName, 'desc') || '';
75
+ // Check if unlocked and get unlock time
76
+ const unlockedPtr = koffi.alloc('bool', 1);
77
+ const unlockTimePtr = koffi.alloc('uint32', 1);
78
+ const hasAchievement = this.libraryLoader.SteamAPI_ISteamUserStats_GetAchievementAndUnlockTime(userStatsInterface, apiName, unlockedPtr, unlockTimePtr);
79
+ const unlocked = hasAchievement ? koffi.decode(unlockedPtr, 'bool') : false;
80
+ const unlockTime = hasAchievement && unlocked ? koffi.decode(unlockTimePtr, 'uint32') : 0;
81
+ achievements.push({
82
+ apiName,
83
+ displayName,
84
+ description,
85
+ unlocked,
86
+ unlockTime // Now returns actual Steam unlock time (Unix timestamp)
87
+ });
88
+ }
89
+ catch (error) {
90
+ console.warn(`Failed to get achievement ${i}:`, error.message);
91
+ }
92
+ }
93
+ return achievements;
94
+ }
95
+ catch (error) {
96
+ console.error('[Steamworks] ERROR: Failed to get achievements:', error.message);
97
+ return [];
98
+ }
99
+ }
100
+ /**
101
+ * Unlock achievement in Steam
102
+ */
103
+ async unlockAchievement(achievementName) {
104
+ if (!this.apiCore.isInitialized()) {
105
+ console.warn('[Steamworks] WARNING: Steam API not initialized');
106
+ return false;
107
+ }
108
+ const userStatsInterface = this.apiCore.getUserStatsInterface();
109
+ if (!userStatsInterface) {
110
+ console.warn('[Steamworks] WARNING: UserStats interface not available');
111
+ return false;
112
+ }
113
+ try {
114
+ console.log(`[Steamworks] Attempting to unlock achievement: ${achievementName}`);
115
+ // Set the achievement
116
+ const result = this.libraryLoader.SteamAPI_ISteamUserStats_SetAchievement(userStatsInterface, achievementName);
117
+ if (result) {
118
+ // Store stats to commit the achievement to Steam servers
119
+ const storeResult = this.libraryLoader.SteamAPI_ISteamUserStats_StoreStats(userStatsInterface);
120
+ if (storeResult) {
121
+ // Run callbacks to process the achievement unlock
122
+ this.apiCore.runCallbacks();
123
+ console.log(`[Steamworks] Achievement unlocked successfully: ${achievementName}`);
124
+ return true;
125
+ }
126
+ else {
127
+ console.error(`[Steamworks] ERROR: Failed to store stats for achievement: ${achievementName}`);
128
+ return false;
129
+ }
130
+ }
131
+ else {
132
+ console.error(`[Steamworks] ERROR: Failed to set achievement: ${achievementName}`);
133
+ return false;
134
+ }
135
+ }
136
+ catch (error) {
137
+ console.error(`[Steamworks] ERROR: Error unlocking achievement ${achievementName}:`, error.message);
138
+ return false;
139
+ }
140
+ }
141
+ /**
142
+ * Clear achievement in Steam (for testing)
143
+ */
144
+ async clearAchievement(achievementName) {
145
+ if (!this.apiCore.isInitialized()) {
146
+ console.warn('[Steamworks] WARNING: Steam API not initialized');
147
+ return false;
148
+ }
149
+ const userStatsInterface = this.apiCore.getUserStatsInterface();
150
+ if (!userStatsInterface) {
151
+ console.warn('[Steamworks] WARNING: UserStats interface not available');
152
+ return false;
153
+ }
154
+ try {
155
+ console.log(`[Steamworks] Attempting to clear achievement: ${achievementName}`);
156
+ // Clear the achievement
157
+ const result = this.libraryLoader.SteamAPI_ISteamUserStats_ClearAchievement(userStatsInterface, achievementName);
158
+ if (result) {
159
+ // Store stats to commit the change to Steam servers
160
+ const storeResult = this.libraryLoader.SteamAPI_ISteamUserStats_StoreStats(userStatsInterface);
161
+ if (storeResult) {
162
+ // Run callbacks to process the change
163
+ this.apiCore.runCallbacks();
164
+ console.log(`[Steamworks] Achievement cleared successfully: ${achievementName}`);
165
+ return true;
166
+ }
167
+ else {
168
+ console.error(`[Steamworks] ERROR: Failed to store stats for clearing achievement: ${achievementName}`);
169
+ return false;
170
+ }
171
+ }
172
+ else {
173
+ console.error(`[Steamworks] ERROR: Failed to clear achievement: ${achievementName}`);
174
+ return false;
175
+ }
176
+ }
177
+ catch (error) {
178
+ console.error(`[Steamworks] ERROR: Error clearing achievement ${achievementName}:`, error.message);
179
+ return false;
180
+ }
181
+ }
182
+ /**
183
+ * Check if achievement is unlocked
184
+ */
185
+ async isAchievementUnlocked(achievementName) {
186
+ if (!this.apiCore.isInitialized()) {
187
+ return false;
188
+ }
189
+ const userStatsInterface = this.apiCore.getUserStatsInterface();
190
+ if (!userStatsInterface) {
191
+ return false;
192
+ }
193
+ try {
194
+ const unlockedPtr = koffi.alloc('bool', 1);
195
+ const unlockTimePtr = koffi.alloc('uint32', 1);
196
+ const hasAchievement = this.libraryLoader.SteamAPI_ISteamUserStats_GetAchievementAndUnlockTime(userStatsInterface, achievementName, unlockedPtr, unlockTimePtr);
197
+ return hasAchievement ? koffi.decode(unlockedPtr, 'bool') : false;
198
+ }
199
+ catch (error) {
200
+ console.error(`[Steamworks] ERROR: Error checking achievement ${achievementName}:`, error.message);
201
+ return false;
202
+ }
203
+ }
204
+ /**
205
+ * Get achievement by API name
206
+ */
207
+ async getAchievementByName(achievementName) {
208
+ const achievements = await this.getAllAchievements();
209
+ return achievements.find(a => a.apiName === achievementName) || null;
210
+ }
211
+ /**
212
+ * Get total number of achievements
213
+ */
214
+ async getTotalAchievementCount() {
215
+ if (!this.apiCore.isInitialized()) {
216
+ return 0;
217
+ }
218
+ const userStatsInterface = this.apiCore.getUserStatsInterface();
219
+ if (!userStatsInterface) {
220
+ return 0;
221
+ }
222
+ try {
223
+ return this.libraryLoader.SteamAPI_ISteamUserStats_GetNumAchievements(userStatsInterface);
224
+ }
225
+ catch (error) {
226
+ console.error('[Steamworks] ERROR: Error getting achievement count:', error.message);
227
+ return 0;
228
+ }
229
+ }
230
+ /**
231
+ * Get number of unlocked achievements
232
+ */
233
+ async getUnlockedAchievementCount() {
234
+ const achievements = await this.getAllAchievements();
235
+ return achievements.filter(a => a.unlocked).length;
236
+ }
237
+ /**
238
+ * Get achievement icon handle
239
+ * Returns icon handle for use with ISteamUtils::GetImageRGBA()
240
+ * Returns 0 if no icon set or still loading
241
+ */
242
+ async getAchievementIcon(achievementName) {
243
+ if (!this.apiCore.isInitialized()) {
244
+ console.warn('[Steamworks] WARNING: Steam API not initialized');
245
+ return 0;
246
+ }
247
+ const userStatsInterface = this.apiCore.getUserStatsInterface();
248
+ if (!userStatsInterface) {
249
+ console.warn('[Steamworks] WARNING: UserStats interface not available');
250
+ return 0;
251
+ }
252
+ try {
253
+ const iconHandle = this.libraryLoader.SteamAPI_ISteamUserStats_GetAchievementIcon(userStatsInterface, achievementName);
254
+ console.log(`[Steamworks] Achievement icon handle for ${achievementName}: ${iconHandle}`);
255
+ return iconHandle;
256
+ }
257
+ catch (error) {
258
+ console.error(`[Steamworks] ERROR: Error getting achievement icon:`, error.message);
259
+ return 0;
260
+ }
261
+ }
262
+ /**
263
+ * Indicate achievement progress to user
264
+ * Shows a progress notification in Steam overlay
265
+ */
266
+ async indicateAchievementProgress(achievementName, currentProgress, maxProgress) {
267
+ if (!this.apiCore.isInitialized()) {
268
+ console.warn('[Steamworks] WARNING: Steam API not initialized');
269
+ return false;
270
+ }
271
+ const userStatsInterface = this.apiCore.getUserStatsInterface();
272
+ if (!userStatsInterface) {
273
+ console.warn('[Steamworks] WARNING: UserStats interface not available');
274
+ return false;
275
+ }
276
+ try {
277
+ console.log(`[Steamworks] Indicating achievement progress: ${achievementName} (${currentProgress}/${maxProgress})`);
278
+ const result = this.libraryLoader.SteamAPI_ISteamUserStats_IndicateAchievementProgress(userStatsInterface, achievementName, currentProgress, maxProgress);
279
+ if (result) {
280
+ this.apiCore.runCallbacks();
281
+ console.log(`[Steamworks] Achievement progress indicated successfully`);
282
+ return true;
283
+ }
284
+ else {
285
+ console.error(`[Steamworks] ERROR: Failed to indicate achievement progress`);
286
+ return false;
287
+ }
288
+ }
289
+ catch (error) {
290
+ console.error(`[Steamworks] ERROR: Error indicating achievement progress:`, error.message);
291
+ return false;
292
+ }
293
+ }
294
+ /**
295
+ * Get achievement progress limits (for integer-based progress)
296
+ */
297
+ async getAchievementProgressLimitsInt(achievementName) {
298
+ if (!this.apiCore.isInitialized()) {
299
+ console.warn('[Steamworks] WARNING: Steam API not initialized');
300
+ return null;
301
+ }
302
+ const userStatsInterface = this.apiCore.getUserStatsInterface();
303
+ if (!userStatsInterface) {
304
+ console.warn('[Steamworks] WARNING: UserStats interface not available');
305
+ return null;
306
+ }
307
+ try {
308
+ const minPtr = koffi.alloc('int32', 1);
309
+ const maxPtr = koffi.alloc('int32', 1);
310
+ const result = this.libraryLoader.SteamAPI_ISteamUserStats_GetAchievementProgressLimitsInt32(userStatsInterface, achievementName, minPtr, maxPtr);
311
+ if (result) {
312
+ return {
313
+ minProgress: koffi.decode(minPtr, 'int32'),
314
+ maxProgress: koffi.decode(maxPtr, 'int32')
315
+ };
316
+ }
317
+ return null;
318
+ }
319
+ catch (error) {
320
+ console.error(`[Steamworks] ERROR: Error getting achievement progress limits:`, error.message);
321
+ return null;
322
+ }
323
+ }
324
+ /**
325
+ * Get achievement progress limits (for float-based progress)
326
+ */
327
+ async getAchievementProgressLimitsFloat(achievementName) {
328
+ if (!this.apiCore.isInitialized()) {
329
+ console.warn('[Steamworks] WARNING: Steam API not initialized');
330
+ return null;
331
+ }
332
+ const userStatsInterface = this.apiCore.getUserStatsInterface();
333
+ if (!userStatsInterface) {
334
+ console.warn('[Steamworks] WARNING: UserStats interface not available');
335
+ return null;
336
+ }
337
+ try {
338
+ const minPtr = koffi.alloc('float', 1);
339
+ const maxPtr = koffi.alloc('float', 1);
340
+ const result = this.libraryLoader.SteamAPI_ISteamUserStats_GetAchievementProgressLimitsFloat(userStatsInterface, achievementName, minPtr, maxPtr);
341
+ if (result) {
342
+ return {
343
+ minProgress: koffi.decode(minPtr, 'float'),
344
+ maxProgress: koffi.decode(maxPtr, 'float')
345
+ };
346
+ }
347
+ return null;
348
+ }
349
+ catch (error) {
350
+ console.error(`[Steamworks] ERROR: Error getting achievement progress limits:`, error.message);
351
+ return null;
352
+ }
353
+ }
354
+ /**
355
+ * Request stats for another user (friend)
356
+ * This is async - you need to wait for the callback before calling getUserAchievement
357
+ */
358
+ async requestUserStats(steamId) {
359
+ if (!this.apiCore.isInitialized()) {
360
+ console.warn('[Steamworks] WARNING: Steam API not initialized');
361
+ return false;
362
+ }
363
+ const userStatsInterface = this.apiCore.getUserStatsInterface();
364
+ if (!userStatsInterface) {
365
+ console.warn('[Steamworks] WARNING: UserStats interface not available');
366
+ return false;
367
+ }
368
+ try {
369
+ console.log(`[Steamworks] Requesting user stats for Steam ID: ${steamId}`);
370
+ const steamIdNum = BigInt(steamId);
371
+ const callHandle = this.libraryLoader.SteamAPI_ISteamUserStats_RequestUserStats(userStatsInterface, steamIdNum);
372
+ if (callHandle !== BigInt(0)) {
373
+ console.log(`[Steamworks] User stats request sent (call handle: ${callHandle})`);
374
+ return true;
375
+ }
376
+ else {
377
+ console.error(`[Steamworks] ERROR: Failed to request user stats`);
378
+ return false;
379
+ }
380
+ }
381
+ catch (error) {
382
+ console.error(`[Steamworks] ERROR: Error requesting user stats:`, error.message);
383
+ return false;
384
+ }
385
+ }
386
+ /**
387
+ * Get achievement status for another user (friend)
388
+ * Must call requestUserStats() first and wait for callback
389
+ */
390
+ async getUserAchievement(steamId, achievementName) {
391
+ if (!this.apiCore.isInitialized()) {
392
+ console.warn('[Steamworks] WARNING: Steam API not initialized');
393
+ return null;
394
+ }
395
+ const userStatsInterface = this.apiCore.getUserStatsInterface();
396
+ if (!userStatsInterface) {
397
+ console.warn('[Steamworks] WARNING: UserStats interface not available');
398
+ return null;
399
+ }
400
+ try {
401
+ const steamIdNum = BigInt(steamId);
402
+ const unlockedPtr = koffi.alloc('bool', 1);
403
+ const unlockTimePtr = koffi.alloc('uint32', 1);
404
+ const result = this.libraryLoader.SteamAPI_ISteamUserStats_GetUserAchievementAndUnlockTime(userStatsInterface, steamIdNum, achievementName, unlockedPtr, unlockTimePtr);
405
+ if (result) {
406
+ // Get display info for achievement
407
+ const displayName = this.libraryLoader.SteamAPI_ISteamUserStats_GetAchievementDisplayAttribute(userStatsInterface, achievementName, 'name') || achievementName;
408
+ const description = this.libraryLoader.SteamAPI_ISteamUserStats_GetAchievementDisplayAttribute(userStatsInterface, achievementName, 'desc') || '';
409
+ const unlocked = koffi.decode(unlockedPtr, 'bool');
410
+ const unlockTime = unlocked ? koffi.decode(unlockTimePtr, 'uint32') : 0;
411
+ return {
412
+ steamId,
413
+ apiName: achievementName,
414
+ displayName,
415
+ description,
416
+ unlocked,
417
+ unlockTime
418
+ };
419
+ }
420
+ return null;
421
+ }
422
+ catch (error) {
423
+ console.error(`[Steamworks] ERROR: Error getting user achievement:`, error.message);
424
+ return null;
425
+ }
426
+ }
427
+ /**
428
+ * Request global achievement percentages
429
+ * This is async - wait for callback before calling getAchievementAchievedPercent
430
+ */
431
+ async requestGlobalAchievementPercentages() {
432
+ if (!this.apiCore.isInitialized()) {
433
+ console.warn('[Steamworks] WARNING: Steam API not initialized');
434
+ return false;
435
+ }
436
+ const userStatsInterface = this.apiCore.getUserStatsInterface();
437
+ if (!userStatsInterface) {
438
+ console.warn('[Steamworks] WARNING: UserStats interface not available');
439
+ return false;
440
+ }
441
+ try {
442
+ console.log(`[Steamworks] Requesting global achievement percentages...`);
443
+ const callHandle = this.libraryLoader.SteamAPI_ISteamUserStats_RequestGlobalAchievementPercentages(userStatsInterface);
444
+ if (callHandle !== BigInt(0)) {
445
+ console.log(`[Steamworks] Global achievement percentages request sent (call handle: ${callHandle})`);
446
+ return true;
447
+ }
448
+ else {
449
+ console.error(`[Steamworks] ERROR: Failed to request global achievement percentages`);
450
+ return false;
451
+ }
452
+ }
453
+ catch (error) {
454
+ console.error(`[Steamworks] ERROR: Error requesting global achievement percentages:`, error.message);
455
+ return false;
456
+ }
457
+ }
458
+ /**
459
+ * Get percentage of users who unlocked a specific achievement
460
+ * Must call requestGlobalAchievementPercentages() first
461
+ */
462
+ async getAchievementAchievedPercent(achievementName) {
463
+ if (!this.apiCore.isInitialized()) {
464
+ console.warn('[Steamworks] WARNING: Steam API not initialized');
465
+ return null;
466
+ }
467
+ const userStatsInterface = this.apiCore.getUserStatsInterface();
468
+ if (!userStatsInterface) {
469
+ console.warn('[Steamworks] WARNING: UserStats interface not available');
470
+ return null;
471
+ }
472
+ try {
473
+ const percentPtr = koffi.alloc('float', 1);
474
+ const result = this.libraryLoader.SteamAPI_ISteamUserStats_GetAchievementAchievedPercent(userStatsInterface, achievementName, percentPtr);
475
+ if (result) {
476
+ const percent = koffi.decode(percentPtr, 'float');
477
+ console.log(`[Steamworks] Achievement ${achievementName} global unlock: ${percent.toFixed(2)}%`);
478
+ return percent;
479
+ }
480
+ return null;
481
+ }
482
+ catch (error) {
483
+ console.error(`[Steamworks] ERROR: Error getting achievement percentage:`, error.message);
484
+ return null;
485
+ }
486
+ }
487
+ /**
488
+ * Get all achievements with global unlock percentages
489
+ * Must call requestGlobalAchievementPercentages() first and wait for callback
490
+ */
491
+ async getAllAchievementsWithGlobalStats() {
492
+ const achievements = await this.getAllAchievements();
493
+ const result = [];
494
+ for (const ach of achievements) {
495
+ const percent = await this.getAchievementAchievedPercent(ach.apiName);
496
+ result.push({
497
+ apiName: ach.apiName,
498
+ displayName: ach.displayName,
499
+ description: ach.description,
500
+ unlocked: ach.unlocked,
501
+ globalUnlockPercentage: percent ?? 0
502
+ });
503
+ }
504
+ return result;
505
+ }
506
+ /**
507
+ * Get most achieved achievement info
508
+ * Returns iterator for use with getNextMostAchievedAchievementInfo
509
+ */
510
+ async getMostAchievedAchievementInfo() {
511
+ if (!this.apiCore.isInitialized()) {
512
+ console.warn('[Steamworks] WARNING: Steam API not initialized');
513
+ return null;
514
+ }
515
+ const userStatsInterface = this.apiCore.getUserStatsInterface();
516
+ if (!userStatsInterface) {
517
+ console.warn('[Steamworks] WARNING: UserStats interface not available');
518
+ return null;
519
+ }
520
+ try {
521
+ const nameBuffer = Buffer.alloc(256);
522
+ const percentPtr = koffi.alloc('float', 1);
523
+ const unlockedPtr = koffi.alloc('bool', 1);
524
+ const iterator = this.libraryLoader.SteamAPI_ISteamUserStats_GetMostAchievedAchievementInfo(userStatsInterface, nameBuffer, 256, percentPtr, unlockedPtr);
525
+ if (iterator !== -1) {
526
+ const apiName = nameBuffer.toString('utf8').split('\0')[0];
527
+ const percent = koffi.decode(percentPtr, 'float');
528
+ const unlocked = koffi.decode(unlockedPtr, 'bool');
529
+ return { apiName, percent, unlocked, iterator };
530
+ }
531
+ return null;
532
+ }
533
+ catch (error) {
534
+ console.error(`[Steamworks] ERROR: Error getting most achieved achievement:`, error.message);
535
+ return null;
536
+ }
537
+ }
538
+ /**
539
+ * Get next most achieved achievement info (iterate by popularity)
540
+ */
541
+ async getNextMostAchievedAchievementInfo(previousIterator) {
542
+ if (!this.apiCore.isInitialized()) {
543
+ console.warn('[Steamworks] WARNING: Steam API not initialized');
544
+ return null;
545
+ }
546
+ const userStatsInterface = this.apiCore.getUserStatsInterface();
547
+ if (!userStatsInterface) {
548
+ console.warn('[Steamworks] WARNING: UserStats interface not available');
549
+ return null;
550
+ }
551
+ try {
552
+ const nameBuffer = Buffer.alloc(256);
553
+ const percentPtr = koffi.alloc('float', 1);
554
+ const unlockedPtr = koffi.alloc('bool', 1);
555
+ const iterator = this.libraryLoader.SteamAPI_ISteamUserStats_GetNextMostAchievedAchievementInfo(userStatsInterface, previousIterator, nameBuffer, 256, percentPtr, unlockedPtr);
556
+ if (iterator !== -1) {
557
+ const apiName = nameBuffer.toString('utf8').split('\0')[0];
558
+ const percent = koffi.decode(percentPtr, 'float');
559
+ const unlocked = koffi.decode(unlockedPtr, 'bool');
560
+ return { apiName, percent, unlocked, iterator };
561
+ }
562
+ return null;
563
+ }
564
+ catch (error) {
565
+ console.error(`[Steamworks] ERROR: Error getting next most achieved achievement:`, error.message);
566
+ return null;
567
+ }
568
+ }
569
+ /**
570
+ * Get all achievements sorted by global unlock percentage (most achieved first)
571
+ * Must call requestGlobalAchievementPercentages() first
572
+ */
573
+ async getAllAchievementsSortedByPopularity() {
574
+ const results = [];
575
+ // Get first achievement
576
+ const first = await this.getMostAchievedAchievementInfo();
577
+ if (!first) {
578
+ console.warn('[Steamworks] WARNING: No global achievement data available. Call requestGlobalAchievementPercentages() first.');
579
+ return results;
580
+ }
581
+ // Get display info
582
+ const userStatsInterface = this.apiCore.getUserStatsInterface();
583
+ if (userStatsInterface) {
584
+ const displayName = this.libraryLoader.SteamAPI_ISteamUserStats_GetAchievementDisplayAttribute(userStatsInterface, first.apiName, 'name') || first.apiName;
585
+ const description = this.libraryLoader.SteamAPI_ISteamUserStats_GetAchievementDisplayAttribute(userStatsInterface, first.apiName, 'desc') || '';
586
+ results.push({
587
+ apiName: first.apiName,
588
+ displayName,
589
+ description,
590
+ unlocked: first.unlocked,
591
+ globalUnlockPercentage: first.percent
592
+ });
593
+ }
594
+ // Iterate through remaining achievements
595
+ let iterator = first.iterator;
596
+ while (iterator !== -1) {
597
+ const next = await this.getNextMostAchievedAchievementInfo(iterator);
598
+ if (!next)
599
+ break;
600
+ if (userStatsInterface) {
601
+ const displayName = this.libraryLoader.SteamAPI_ISteamUserStats_GetAchievementDisplayAttribute(userStatsInterface, next.apiName, 'name') || next.apiName;
602
+ const description = this.libraryLoader.SteamAPI_ISteamUserStats_GetAchievementDisplayAttribute(userStatsInterface, next.apiName, 'desc') || '';
603
+ results.push({
604
+ apiName: next.apiName,
605
+ displayName,
606
+ description,
607
+ unlocked: next.unlocked,
608
+ globalUnlockPercentage: next.percent
609
+ });
610
+ }
611
+ iterator = next.iterator;
612
+ }
613
+ return results;
614
+ }
615
+ /**
616
+ * Reset all stats and optionally achievements
617
+ * WARNING: This clears ALL user stats and achievements!
618
+ */
619
+ async resetAllStats(includeAchievements = false) {
620
+ if (!this.apiCore.isInitialized()) {
621
+ console.warn('[Steamworks] WARNING: Steam API not initialized');
622
+ return false;
623
+ }
624
+ const userStatsInterface = this.apiCore.getUserStatsInterface();
625
+ if (!userStatsInterface) {
626
+ console.warn('[Steamworks] WARNING: UserStats interface not available');
627
+ return false;
628
+ }
629
+ try {
630
+ console.log(`[Steamworks] Resetting all stats (achievements: ${includeAchievements ? 'YES' : 'NO'})`);
631
+ console.warn('[Steamworks] WARNING: This will reset ALL user statistics!');
632
+ const result = this.libraryLoader.SteamAPI_ISteamUserStats_ResetAllStats(userStatsInterface, includeAchievements);
633
+ if (result) {
634
+ // Store the reset
635
+ const storeResult = this.libraryLoader.SteamAPI_ISteamUserStats_StoreStats(userStatsInterface);
636
+ if (storeResult) {
637
+ this.apiCore.runCallbacks();
638
+ console.log(`[Steamworks] All stats reset successfully`);
639
+ return true;
640
+ }
641
+ else {
642
+ console.error(`[Steamworks] ERROR: Failed to store stats after reset`);
643
+ return false;
644
+ }
645
+ }
646
+ else {
647
+ console.error(`[Steamworks] ERROR: Failed to reset stats`);
648
+ return false;
649
+ }
650
+ }
651
+ catch (error) {
652
+ console.error(`[Steamworks] ERROR: Error resetting stats:`, error.message);
653
+ return false;
654
+ }
655
+ }
656
+ /**
657
+ * Get all achievements with icon handles
658
+ */
659
+ async getAllAchievementsWithIcons() {
660
+ const achievements = await this.getAllAchievements();
661
+ const result = [];
662
+ for (const ach of achievements) {
663
+ const iconHandle = await this.getAchievementIcon(ach.apiName);
664
+ result.push({
665
+ ...ach,
666
+ iconHandle
667
+ });
668
+ }
669
+ return result;
670
+ }
671
+ }
672
+ exports.SteamAchievementManager = SteamAchievementManager;
673
+ //# sourceMappingURL=SteamAchievementManager.js.map