isaacscript-common 8.4.6 → 8.5.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 (63) hide show
  1. package/dist/features/characterStats.d.ts.map +1 -1
  2. package/dist/features/characterStats.lua +1 -2
  3. package/dist/features/firstLast.d.ts +6 -3
  4. package/dist/features/firstLast.d.ts.map +1 -1
  5. package/dist/features/firstLast.lua +6 -3
  6. package/dist/functions/bosses.d.ts +5 -2
  7. package/dist/functions/bosses.d.ts.map +1 -1
  8. package/dist/functions/bosses.lua +11 -6
  9. package/dist/functions/collectibleCacheFlag.d.ts +1 -1
  10. package/dist/functions/collectibleCacheFlag.d.ts.map +1 -1
  11. package/dist/functions/collectibleCacheFlag.lua +1 -2
  12. package/dist/functions/collectibleSet.lua +32 -32
  13. package/dist/functions/collectibleTag.d.ts +1 -1
  14. package/dist/functions/collectibleTag.d.ts.map +1 -1
  15. package/dist/functions/collectibleTag.lua +1 -3
  16. package/dist/functions/eden.d.ts +1 -1
  17. package/dist/functions/eden.d.ts.map +1 -1
  18. package/dist/functions/eden.lua +1 -2
  19. package/dist/functions/flying.d.ts +1 -1
  20. package/dist/functions/flying.d.ts.map +1 -1
  21. package/dist/functions/flying.lua +5 -2
  22. package/dist/functions/input.d.ts +2 -2
  23. package/dist/functions/input.d.ts.map +1 -1
  24. package/dist/functions/input.lua +2 -4
  25. package/dist/functions/playerHealth.d.ts +93 -0
  26. package/dist/functions/playerHealth.d.ts.map +1 -1
  27. package/dist/functions/playerHealth.lua +216 -33
  28. package/dist/functions/players.d.ts +1 -111
  29. package/dist/functions/players.d.ts.map +1 -1
  30. package/dist/functions/players.lua +20 -279
  31. package/dist/functions/revive.d.ts.map +1 -1
  32. package/dist/functions/revive.lua +2 -1
  33. package/dist/functions/stats.d.ts +16 -0
  34. package/dist/functions/stats.d.ts.map +1 -1
  35. package/dist/functions/stats.lua +78 -0
  36. package/dist/functions/transformations.d.ts +1 -1
  37. package/dist/functions/transformations.d.ts.map +1 -1
  38. package/dist/functions/trinketCacheFlag.d.ts +1 -1
  39. package/dist/functions/trinketCacheFlag.d.ts.map +1 -1
  40. package/dist/functions/trinketCacheFlag.lua +1 -3
  41. package/dist/functions/trinketSet.lua +32 -32
  42. package/dist/index.d.ts +19 -30
  43. package/dist/sets/bossSets.d.ts +1 -0
  44. package/dist/sets/bossSets.d.ts.map +1 -1
  45. package/dist/sets/bossSets.lua +19 -0
  46. package/package.json +1 -1
  47. package/src/features/characterStats.ts +1 -2
  48. package/src/features/firstLast.ts +6 -3
  49. package/src/functions/bosses.ts +15 -6
  50. package/src/functions/collectibleCacheFlag.ts +3 -3
  51. package/src/functions/collectibleSet.ts +32 -32
  52. package/src/functions/collectibleTag.ts +2 -3
  53. package/src/functions/eden.ts +3 -3
  54. package/src/functions/flying.ts +4 -4
  55. package/src/functions/input.ts +4 -5
  56. package/src/functions/playerHealth.ts +269 -7
  57. package/src/functions/players.ts +1 -348
  58. package/src/functions/revive.ts +2 -6
  59. package/src/functions/stats.ts +75 -0
  60. package/src/functions/transformations.ts +1 -1
  61. package/src/functions/trinketCacheFlag.ts +2 -3
  62. package/src/functions/trinketSet.ts +32 -32
  63. package/src/sets/bossSets.ts +24 -0
@@ -6,7 +6,6 @@ import {
6
6
  } from "isaac-typescript-definitions";
7
7
  import { KEYBOARD_TO_STRING } from "../maps/keyboardToString";
8
8
  import { getEnumValues } from "./enums";
9
- import { copySet } from "./set";
10
9
  import { trimPrefix } from "./string";
11
10
 
12
11
  const MODIFIER_KEYS: readonly Keyboard[] = [
@@ -52,12 +51,12 @@ export function controllerToString(controller: Controller): string | undefined {
52
51
  return trimPrefix(key, "BUTTON_");
53
52
  }
54
53
 
55
- export function getMoveActions(): Set<ButtonAction> {
56
- return copySet(MOVEMENT_ACTIONS_SET);
54
+ export function getMoveActions(): ReadonlySet<ButtonAction> {
55
+ return MOVEMENT_ACTIONS_SET;
57
56
  }
58
57
 
59
- export function getShootActions(): Set<ButtonAction> {
60
- return copySet(SHOOTING_ACTIONS_SET);
58
+ export function getShootActions(): ReadonlySet<ButtonAction> {
59
+ return SHOOTING_ACTIONS_SET;
61
60
  }
62
61
 
63
62
  /** Iterates over all inputs to determine if a particular button is pressed (i.e. held down). */
@@ -3,19 +3,16 @@ import {
3
3
  CollectibleType,
4
4
  HeartSubType,
5
5
  PlayerType,
6
+ TrinketType,
6
7
  } from "isaac-typescript-definitions";
7
8
  import { MAX_PLAYER_HEART_CONTAINERS } from "../core/constants";
8
9
  import { HealthType } from "../enums/HealthType";
9
10
  import { PlayerHealth, SoulHeartType } from "../interfaces/PlayerHealth";
11
+ import { countSetBits, getKBitOfN, getNumBitsOfN } from "./bitwise";
12
+ import { getCharacterMaxHeartContainers } from "./characters";
10
13
  import { getTotalCharge } from "./charge";
11
14
  import { getEnumValues } from "./enums";
12
- import {
13
- getPlayerBlackHearts,
14
- getPlayerHearts,
15
- getPlayerSoulHearts,
16
- isCharacter,
17
- setActiveItem,
18
- } from "./players";
15
+ import { isCharacter, isKeeper, setActiveItem } from "./players";
19
16
  import { repeat } from "./utils";
20
17
 
21
18
  export function addPlayerHealthType(
@@ -71,6 +68,78 @@ export function addPlayerHealthType(
71
68
  }
72
69
  }
73
70
 
71
+ /**
72
+ * Returns whether or not all of the player's soul-heart-type hearts are black hearts.
73
+ *
74
+ * Note that this function does not consider red heart containers.
75
+ *
76
+ * For example:
77
+ *
78
+ * - If the player has one black heart, this function would return true.
79
+ * - If the player has one soul heart and two black hearts, this function would return false.
80
+ * - If the player has no black hearts, this function will return false.
81
+ * - If the player has one red heart container and three black hearts, this function would return
82
+ * true.
83
+ */
84
+ export function doesPlayerHaveAllBlackHearts(player: EntityPlayer): boolean {
85
+ const soulHearts = getPlayerSoulHearts(player);
86
+ const blackHearts = getPlayerBlackHearts(player);
87
+
88
+ return blackHearts > 0 && soulHearts === 0;
89
+ }
90
+
91
+ /**
92
+ * Returns whether or not all of the player's soul-heart-type hearts are soul hearts.
93
+ *
94
+ * Note that this function does not consider red heart containers.
95
+ *
96
+ * For example:
97
+ *
98
+ * - If the player has two soul hearts and one black heart, this function would return false.
99
+ * - If the player has no soul hearts, this function will return false.
100
+ * - If the player has one red heart container and three soul hearts, this function would return
101
+ * true.
102
+ */
103
+ export function doesPlayerHaveAllSoulHearts(player: EntityPlayer): boolean {
104
+ const soulHearts = getPlayerSoulHearts(player);
105
+ const blackHearts = getPlayerBlackHearts(player);
106
+
107
+ return soulHearts > 0 && blackHearts === 0;
108
+ }
109
+
110
+ /**
111
+ * Returns the number of slots that the player has remaining for new heart containers, accounting
112
+ * for broken hearts. For example, if the player is Judas and has 1 red heart containers and 2 full
113
+ * soul hearts and 3 broken hearts, then this function would return 6 (i.e. 12 - 1 - 2 - 3).
114
+ */
115
+ export function getPlayerAvailableHeartSlots(player: EntityPlayer): int {
116
+ const maxHeartContainers = getPlayerMaxHeartContainers(player);
117
+ const effectiveMaxHearts = player.GetEffectiveMaxHearts();
118
+ const normalAndBoneHeartContainers = effectiveMaxHearts / 2;
119
+ const soulHearts = player.GetSoulHearts();
120
+ const soulHeartContainers = math.ceil(soulHearts / 2);
121
+ const totalHeartContainers =
122
+ normalAndBoneHeartContainers + soulHeartContainers;
123
+ const brokenHearts = player.GetBrokenHearts();
124
+ const totalOccupiedHeartSlots = totalHeartContainers + brokenHearts;
125
+
126
+ return maxHeartContainers - totalOccupiedHeartSlots;
127
+ }
128
+
129
+ /**
130
+ * Returns the number of black hearts that the player has, excluding any soul hearts. For example,
131
+ * if the player has one full black heart, one full soul heart, and one half black heart, this
132
+ * function returns 3.
133
+ *
134
+ * This is different from the `EntityPlayer.GetBlackHearts` method, since that returns a bitmask.
135
+ */
136
+ export function getPlayerBlackHearts(player: EntityPlayer): int {
137
+ const blackHeartsBitmask = player.GetBlackHearts();
138
+ const blackHeartBits = countSetBits(blackHeartsBitmask);
139
+
140
+ return blackHeartBits * 2;
141
+ }
142
+
74
143
  /**
75
144
  * Helper function to get an object representing the player's health. You can use this in
76
145
  * combination with the `setPlayerHealth` function to restore the player's health back to a certain
@@ -211,6 +280,159 @@ export function getPlayerHealthType(
211
280
  }
212
281
  }
213
282
 
283
+ /**
284
+ * Returns the number of red hearts that the player has, excluding any rotten hearts. For example,
285
+ * if the player has one full black heart, one full soul heart, and one half black heart, this
286
+ * function returns 3.
287
+ *
288
+ * This is different from the `EntityPlayer.GetHearts` method, since that returns a value that
289
+ * includes rotten hearts.
290
+ */
291
+ export function getPlayerHearts(player: EntityPlayer): int {
292
+ const rottenHearts = player.GetRottenHearts();
293
+ const hearts = player.GetHearts();
294
+
295
+ return hearts - rottenHearts * 2;
296
+ }
297
+
298
+ /**
299
+ * Helper function that returns the type of the rightmost heart. This does not including golden
300
+ * hearts or broken hearts, since they cannot be damaged directly.
301
+ */
302
+ export function getPlayerLastHeart(player: EntityPlayer): HealthType {
303
+ const hearts = player.GetHearts();
304
+ const effectiveMaxHearts = player.GetEffectiveMaxHearts();
305
+ const soulHearts = player.GetSoulHearts();
306
+ const blackHearts = player.GetBlackHearts();
307
+ const eternalHearts = player.GetEternalHearts();
308
+ const boneHearts = player.GetBoneHearts();
309
+ const rottenHearts = player.GetRottenHearts();
310
+
311
+ const soulHeartSlots = soulHearts / 2;
312
+ const lastHeartIndex = boneHearts + soulHeartSlots - 1;
313
+ const isLastHeartBone = player.IsBoneHeart(lastHeartIndex);
314
+
315
+ if (isLastHeartBone) {
316
+ const isLastContainerEmpty = hearts <= effectiveMaxHearts - 2;
317
+ if (isLastContainerEmpty) {
318
+ return HealthType.BONE;
319
+ }
320
+
321
+ if (rottenHearts > 0) {
322
+ return HealthType.ROTTEN;
323
+ }
324
+
325
+ if (eternalHearts > 0) {
326
+ return HealthType.ETERNAL;
327
+ }
328
+
329
+ return HealthType.RED;
330
+ }
331
+
332
+ if (soulHearts > 0) {
333
+ const numBits = getNumBitsOfN(blackHearts);
334
+ const finalBit = getKBitOfN(numBits - 1, blackHearts);
335
+ const isBlack = finalBit === 1;
336
+
337
+ if (isBlack) {
338
+ return HealthType.BLACK;
339
+ }
340
+
341
+ // If it is not a black heart, it must be a soul heart.
342
+ return HealthType.SOUL;
343
+ }
344
+
345
+ if (eternalHearts > 0) {
346
+ return HealthType.ETERNAL;
347
+ }
348
+
349
+ if (rottenHearts > 0) {
350
+ return HealthType.ROTTEN;
351
+ }
352
+
353
+ return HealthType.RED;
354
+ }
355
+
356
+ /**
357
+ * Returns the maximum heart containers that the provided player can have. Normally, this is 12, but
358
+ * it can change depending on the character (e.g. Keeper) and other things (e.g. Mother's Kiss).
359
+ * This function does not account for Broken Hearts; use the `getPlayerAvailableHeartSlots` helper
360
+ * function for that.
361
+ */
362
+ export function getPlayerMaxHeartContainers(player: EntityPlayer): int {
363
+ const character = player.GetPlayerType();
364
+ const characterMaxHeartContainers = getCharacterMaxHeartContainers(character);
365
+
366
+ // 1
367
+ // Magdalene can increase her maximum heart containers with Birthright.
368
+ if (
369
+ character === PlayerType.MAGDALENE &&
370
+ player.HasCollectible(CollectibleType.BIRTHRIGHT)
371
+ ) {
372
+ const extraMaxHeartContainersFromBirthright = 6;
373
+ return characterMaxHeartContainers + extraMaxHeartContainersFromBirthright;
374
+ }
375
+
376
+ // 14, 33
377
+ // Keeper and Tainted Keeper can increase their coin containers with Mother's Kiss and Greed's
378
+ // Gullet.
379
+ if (isKeeper(player)) {
380
+ const numMothersKisses = player.GetTrinketMultiplier(
381
+ TrinketType.MOTHERS_KISS,
382
+ );
383
+ const hasGreedsGullet = player.HasCollectible(
384
+ CollectibleType.GREEDS_GULLET,
385
+ );
386
+ const coins = player.GetNumCoins();
387
+ const greedsGulletCoinContainers = hasGreedsGullet
388
+ ? Math.floor(coins / 25)
389
+ : 0;
390
+
391
+ return (
392
+ characterMaxHeartContainers +
393
+ numMothersKisses +
394
+ greedsGulletCoinContainers
395
+ );
396
+ }
397
+
398
+ return characterMaxHeartContainers;
399
+ }
400
+
401
+ /**
402
+ * Returns the number of soul hearts that the player has, excluding any black hearts. For example,
403
+ * if the player has one full black heart, one full soul heart, and one half black heart, this
404
+ * function returns 2.
405
+ *
406
+ * This is different from the `EntityPlayer.GetSoulHearts` method, since that returns the combined
407
+ * number of soul hearts and black hearts.
408
+ */
409
+ export function getPlayerSoulHearts(player: EntityPlayer): int {
410
+ const soulHearts = player.GetSoulHearts();
411
+ const blackHearts = getPlayerBlackHearts(player);
412
+
413
+ return soulHearts - blackHearts;
414
+ }
415
+
416
+ /**
417
+ * Helper function to determine how many heart containers that Tainted Magdalene has that will not
418
+ * be automatically depleted over time. By default, this is 2, but this function will return 4 so
419
+ * that it is consistent with the `player.GetHearts` and `player.GetMaxHearts` methods.
420
+ *
421
+ * If Tainted Magdalene has Birthright, she will gained an additional non-temporary heart container.
422
+ *
423
+ * This function does not validate whether or not the provided player is Tainted Magdalene; that
424
+ * should be accomplished before invoking this function.
425
+ */
426
+ export function getTaintedMagdaleneNonTemporaryMaxHearts(
427
+ player: EntityPlayer,
428
+ ): int {
429
+ const maxHearts = player.GetMaxHearts();
430
+ const hasBirthright = player.HasCollectible(CollectibleType.BIRTHRIGHT);
431
+ const maxNonTemporaryMaxHearts = hasBirthright ? 6 : 4;
432
+
433
+ return Math.min(maxHearts, maxNonTemporaryMaxHearts);
434
+ }
435
+
214
436
  /** Returns a `PlayerHealth` object with all zeros. */
215
437
  export function newPlayerHealth(): PlayerHealth {
216
438
  return {
@@ -421,3 +643,43 @@ export function setPlayerHealth(
421
643
  );
422
644
  }
423
645
  }
646
+
647
+ /**
648
+ * Helper function to see if a certain damage amount would deal "permanent" damage to Tainted
649
+ * Magdalene.
650
+ *
651
+ * Tainted Magdalene has "permanent" health and "temporary" health. When standing still and doing
652
+ * nothing, all of Tainted Magdalene's temporary health will eventually go away.
653
+ *
654
+ * Before using this function, it is expected that you check to see if the player is Tainted
655
+ * Magdalene first, or else it will give a nonsensical result.
656
+ */
657
+ export function wouldDamageTaintedMagdaleneNonTemporaryHeartContainers(
658
+ player: EntityPlayer,
659
+ damageAmount: float,
660
+ ): boolean {
661
+ // Regardless of the damage amount, damage to a player cannot remove a soul heart and a red heart
662
+ // at the same time.
663
+ const soulHearts = player.GetSoulHearts();
664
+ if (soulHearts > 0) {
665
+ return false;
666
+ }
667
+
668
+ // Regardless of the damage amount, damage to a player cannot remove a bone heart and a red heart
669
+ // at the same time.
670
+ const boneHearts = player.GetBoneHearts();
671
+ if (boneHearts > 0) {
672
+ return false;
673
+ }
674
+
675
+ // Account for rotten hearts eating away at more red hearts than usual.
676
+ const hearts = player.GetHearts();
677
+ const rottenHearts = player.GetRottenHearts();
678
+ const effectiveDamageAmount =
679
+ damageAmount + Math.min(rottenHearts, damageAmount);
680
+
681
+ const heartsAfterDamage = hearts - effectiveDamageAmount;
682
+ const nonTemporaryMaxHearts =
683
+ getTaintedMagdaleneNonTemporaryMaxHearts(player);
684
+ return heartsAfterDamage < nonTemporaryMaxHearts;
685
+ }