@stackedapp/utils 2.61.1 → 2.62.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.
@@ -296,18 +296,22 @@ function evaluateAchievement(cond, ctx) {
296
296
  const playerAchData = ctx.snap.achievements?.[cond.id];
297
297
  const details = [];
298
298
  let isMet = true;
299
- if (!playerAchData) {
300
- isMet = false;
299
+ // Legacy "must have it" detail; minCount: 0 is treated as undefined for back-compat.
300
+ const hasPositiveMin = cond.minCount != null && cond.minCount > 0;
301
+ if (!hasPositiveMin && cond.maxCount == null) {
302
+ const met = !!playerAchData;
303
+ if (!met)
304
+ isMet = false;
301
305
  details.push({
302
- isMet: false,
306
+ isMet: met,
303
307
  kind: 'achievement',
304
- trackerAmount: 0,
308
+ trackerAmount: met ? 1 : 0,
305
309
  trackerGoal: 1,
306
- percentCompleted: 0,
307
- text: templateText(cond, { current: 0, goal: 1, name: cond.name }, `Have the achievement ${cond.name}`),
310
+ percentCompleted: met ? 100 : 0,
311
+ text: templateText(cond, { current: met ? 1 : 0, goal: 1, name: cond.name }, `Have the achievement ${cond.name}`),
308
312
  });
309
313
  }
310
- if (cond.minCount) {
314
+ if (hasPositiveMin) {
311
315
  const trackerAmount = playerAchData?.count || 0;
312
316
  const trackerGoal = cond.minCount;
313
317
  const met = trackerAmount >= trackerGoal;
@@ -322,13 +326,29 @@ function evaluateAchievement(cond, ctx) {
322
326
  text: templateText(cond, { current: trackerAmount, goal: trackerGoal, name: cond.name }, `Have the achievement ${cond.name} more than ${trackerGoal} times`),
323
327
  });
324
328
  }
329
+ if (cond.maxCount != null) {
330
+ const trackerAmount = playerAchData?.count || 0;
331
+ const met = trackerAmount <= cond.maxCount;
332
+ if (!met)
333
+ isMet = false;
334
+ details.push({
335
+ isMet: met,
336
+ kind: 'achievement',
337
+ trackerAmount,
338
+ trackerGoal: cond.maxCount,
339
+ percentCompleted: met ? 100 : 0,
340
+ text: templateText(cond, { current: trackerAmount, goal: cond.maxCount, name: cond.name }, cond.maxCount === 0
341
+ ? `Do not have the achievement ${cond.name}`
342
+ : `Have the achievement ${cond.name} at most ${cond.maxCount} times`),
343
+ });
344
+ }
325
345
  return { isMet, details };
326
346
  }
327
347
  function evaluateMembership(cond, ctx) {
328
348
  const playerData = ctx.snap.memberships?.[cond.id];
329
349
  const details = [];
330
350
  let isMet = true;
331
- if (cond.minCount) {
351
+ if (cond.minCount != null) {
332
352
  const trackerAmount = playerData?.count || 0;
333
353
  const trackerGoal = cond.minCount;
334
354
  const met = trackerAmount >= trackerGoal;
@@ -343,7 +363,7 @@ function evaluateMembership(cond, ctx) {
343
363
  text: templateText(cond, { current: trackerAmount, goal: trackerGoal, name: cond.name }, trackerGoal > 1 ? `Have at least ${trackerGoal} ${cond.name} memberships` : `Have a ${cond.name} membership`),
344
364
  });
345
365
  }
346
- if (cond.maxCount) {
366
+ if (cond.maxCount != null) {
347
367
  const trackerAmount = playerData?.count || 0;
348
368
  const met = trackerAmount <= cond.maxCount;
349
369
  if (!met)
@@ -354,10 +374,13 @@ function evaluateMembership(cond, ctx) {
354
374
  trackerAmount,
355
375
  trackerGoal: cond.maxCount,
356
376
  percentCompleted: met ? 100 : 0,
357
- text: templateText(cond, { current: trackerAmount, goal: cond.maxCount, name: cond.name }, `Have less than ${cond.maxCount} ${cond.name} memberships`),
377
+ text: templateText(cond, { current: trackerAmount, goal: cond.maxCount, name: cond.name }, cond.maxCount === 0
378
+ ? `Do not have a ${cond.name} membership`
379
+ : `Have at most ${cond.maxCount} ${cond.name} memberships`),
358
380
  });
359
381
  }
360
382
  const timeOwned = (playerData?.expiresAt || 0) - Date.now();
383
+ // Truthy check: timeOwned is hugely negative when no membership exists, so `!= null` would make minMs: 0 misfire.
361
384
  if (cond.minMs) {
362
385
  const trackerAmount = Number((timeOwned / (1000 * 60 * 60 * 24)).toFixed(1));
363
386
  const trackerGoal = Number((cond.minMs / (1000 * 60 * 60 * 24)).toFixed(1));
@@ -373,7 +396,7 @@ function evaluateMembership(cond, ctx) {
373
396
  text: templateText(cond, { current: trackerAmount, goal: trackerGoal, name: cond.name }, `Own ${cond.name} membership for at least ${trackerGoal} days`),
374
397
  });
375
398
  }
376
- if (cond.maxMs) {
399
+ if (cond.maxMs != null) {
377
400
  const trackerAmount = Number((timeOwned / (1000 * 60 * 60 * 24)).toFixed(1));
378
401
  const met = timeOwned <= cond.maxMs;
379
402
  if (!met)
@@ -392,25 +415,55 @@ function evaluateMembership(cond, ctx) {
392
415
  function evaluateQuest(cond, ctx) {
393
416
  const playerQuestData = ctx.snap.quests?.[cond.id];
394
417
  const trackerAmount = playerQuestData?.completions || 0;
395
- const trackerGoal = cond.completions || 1;
396
- const isMet = playerQuestData ? trackerAmount >= (cond.completions || 0) : false;
397
- return {
398
- isMet,
399
- details: [
400
- {
401
- isMet,
402
- kind: 'quest',
403
- trackerAmount,
404
- trackerGoal,
405
- percentCompleted: (0, helpers_1.calculatePercent)(trackerAmount, trackerGoal, isMet),
406
- text: templateText(cond, { current: trackerAmount, goal: trackerGoal, name: cond.name }, cond.completions === 1
407
- ? `Complete the quest ${cond.name}`
408
- : (cond.completions || 0) < 1
409
- ? `Start the quest ${cond.name}`
410
- : `Complete the quest ${cond.name} ${cond.completions} times`),
411
- },
412
- ],
413
- };
418
+ const details = [];
419
+ let isMet = true;
420
+ // Legacy "must have started" detail; completions: 0 is treated as undefined for back-compat.
421
+ const hasPositiveMin = cond.completions != null && cond.completions > 0;
422
+ if (!hasPositiveMin && cond.maxCompletions == null) {
423
+ const met = !!playerQuestData;
424
+ if (!met)
425
+ isMet = false;
426
+ details.push({
427
+ isMet: met,
428
+ kind: 'quest',
429
+ trackerAmount,
430
+ trackerGoal: 1,
431
+ percentCompleted: met ? 100 : 0,
432
+ text: templateText(cond, { current: trackerAmount, goal: 1, name: cond.name }, `Start the quest ${cond.name}`),
433
+ });
434
+ }
435
+ if (hasPositiveMin) {
436
+ const trackerGoal = cond.completions;
437
+ const met = trackerAmount >= trackerGoal;
438
+ if (!met)
439
+ isMet = false;
440
+ details.push({
441
+ isMet: met,
442
+ kind: 'quest',
443
+ trackerAmount,
444
+ trackerGoal,
445
+ percentCompleted: (0, helpers_1.calculatePercent)(trackerAmount, trackerGoal, met),
446
+ text: templateText(cond, { current: trackerAmount, goal: trackerGoal, name: cond.name }, trackerGoal === 1
447
+ ? `Complete the quest ${cond.name}`
448
+ : `Complete the quest ${cond.name} ${trackerGoal} times`),
449
+ });
450
+ }
451
+ if (cond.maxCompletions != null) {
452
+ const met = trackerAmount <= cond.maxCompletions;
453
+ if (!met)
454
+ isMet = false;
455
+ details.push({
456
+ isMet: met,
457
+ kind: 'quest',
458
+ trackerAmount,
459
+ trackerGoal: cond.maxCompletions,
460
+ percentCompleted: met ? 100 : 0,
461
+ text: templateText(cond, { current: trackerAmount, goal: cond.maxCompletions, name: cond.name }, cond.maxCompletions === 0
462
+ ? `Do not complete the quest ${cond.name}`
463
+ : `Complete the quest ${cond.name} at most ${cond.maxCompletions} times`),
464
+ });
465
+ }
466
+ return { isMet, details };
414
467
  }
415
468
  function evaluateEntityLink(cond, ctx) {
416
469
  const linkCount = ctx.snap.entityLinks?.filter((link) => (link.kind || types_1.DEFAULT_ENTITY_KIND) === cond.linkKind).length || 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stackedapp/utils",
3
- "version": "2.61.1",
3
+ "version": "2.62.1",
4
4
  "description": "Public utilities for Stacked platform SDK",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",