isaacscript-common 6.22.4 → 7.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 (207) hide show
  1. package/dist/callbacks/postPlayerChangeHealth.lua +8 -1
  2. package/dist/callbacks/postPlayerChangeStat.d.ts +3 -0
  3. package/dist/callbacks/postPlayerChangeStat.d.ts.map +1 -0
  4. package/dist/callbacks/postPlayerChangeStat.lua +59 -0
  5. package/dist/callbacks/subscriptions/postPlayerChangeHealth.d.ts +2 -2
  6. package/dist/callbacks/subscriptions/postPlayerChangeHealth.d.ts.map +1 -1
  7. package/dist/callbacks/subscriptions/postPlayerChangeHealth.lua +9 -2
  8. package/dist/callbacks/subscriptions/postPlayerChangeStat.d.ts +12 -0
  9. package/dist/callbacks/subscriptions/postPlayerChangeStat.d.ts.map +1 -0
  10. package/dist/callbacks/subscriptions/postPlayerChangeStat.lua +35 -0
  11. package/dist/classes/DefaultMap.d.ts +3 -3
  12. package/dist/classes/DefaultMap.d.ts.map +1 -1
  13. package/dist/classes/DefaultMap.lua +37 -0
  14. package/dist/core/constants.d.ts +1 -1
  15. package/dist/core/constants.d.ts.map +1 -1
  16. package/dist/core/constants.lua +1 -1
  17. package/dist/enums/ModCallbackCustom.d.ts +70 -39
  18. package/dist/enums/ModCallbackCustom.d.ts.map +1 -1
  19. package/dist/enums/ModCallbackCustom.lua +37 -35
  20. package/dist/enums/StatType.d.ts +30 -0
  21. package/dist/enums/StatType.d.ts.map +1 -0
  22. package/dist/enums/StatType.lua +30 -0
  23. package/dist/enums/index.d.ts +1 -0
  24. package/dist/enums/index.d.ts.map +1 -1
  25. package/dist/enums/index.lua +8 -0
  26. package/dist/enums/private/CopyableIsaacAPIClassType.d.ts +5 -1
  27. package/dist/enums/private/CopyableIsaacAPIClassType.d.ts.map +1 -1
  28. package/dist/enums/private/CopyableIsaacAPIClassType.lua +3 -1
  29. package/dist/enums/private/SerializationBrand.d.ts +1 -0
  30. package/dist/enums/private/SerializationBrand.d.ts.map +1 -1
  31. package/dist/enums/private/SerializationBrand.lua +1 -0
  32. package/dist/features/customStage/versusScreen.lua +2 -2
  33. package/dist/features/disableInputs.d.ts +1 -1
  34. package/dist/features/disableInputs.d.ts.map +1 -1
  35. package/dist/features/runInNFrames.lua +1 -6
  36. package/dist/features/runNextRoom.lua +1 -6
  37. package/dist/features/saveDataManager/exports.d.ts +13 -8
  38. package/dist/features/saveDataManager/exports.d.ts.map +1 -1
  39. package/dist/features/saveDataManager/exports.lua +12 -7
  40. package/dist/features/saveDataManager/maps.d.ts +2 -2
  41. package/dist/features/saveDataManager/maps.d.ts.map +1 -1
  42. package/dist/features/saveDataManager/merge.d.ts +1 -0
  43. package/dist/features/saveDataManager/merge.d.ts.map +1 -1
  44. package/dist/features/saveDataManager/merge.lua +1 -0
  45. package/dist/features/taintedLazarusPlayers.lua +1 -6
  46. package/dist/functions/bitSet128.d.ts +25 -0
  47. package/dist/functions/bitSet128.d.ts.map +1 -0
  48. package/dist/functions/bitSet128.lua +71 -0
  49. package/dist/functions/collectibles.d.ts +2 -2
  50. package/dist/functions/collectibles.lua +2 -2
  51. package/dist/functions/color.d.ts +1 -1
  52. package/dist/functions/color.d.ts.map +1 -1
  53. package/dist/functions/color.lua +2 -2
  54. package/dist/functions/deepCopy.d.ts +1 -0
  55. package/dist/functions/deepCopy.d.ts.map +1 -1
  56. package/dist/functions/deepCopy.lua +2 -1
  57. package/dist/functions/familiars.d.ts +4 -4
  58. package/dist/functions/familiars.lua +4 -4
  59. package/dist/functions/flag.d.ts +1 -1
  60. package/dist/functions/flag.lua +1 -1
  61. package/dist/functions/index.d.ts +2 -0
  62. package/dist/functions/index.d.ts.map +1 -1
  63. package/dist/functions/index.lua +16 -0
  64. package/dist/functions/isaacAPIClass.d.ts +1 -1
  65. package/dist/functions/isaacAPIClass.d.ts.map +1 -1
  66. package/dist/functions/jsonRoom.lua +4 -4
  67. package/dist/functions/kColor.d.ts +1 -1
  68. package/dist/functions/kColor.d.ts.map +1 -1
  69. package/dist/functions/kColor.lua +2 -2
  70. package/dist/functions/log.lua +1 -1
  71. package/dist/functions/npcs.d.ts +2 -2
  72. package/dist/functions/npcs.lua +2 -2
  73. package/dist/functions/playerIndex.d.ts +1 -1
  74. package/dist/functions/playerIndex.lua +1 -1
  75. package/dist/functions/playerStats.d.ts +6 -0
  76. package/dist/functions/playerStats.d.ts.map +1 -0
  77. package/dist/functions/playerStats.lua +22 -0
  78. package/dist/functions/players.d.ts +1 -1
  79. package/dist/functions/players.lua +1 -1
  80. package/dist/functions/rng.d.ts +1 -1
  81. package/dist/functions/rng.d.ts.map +1 -1
  82. package/dist/functions/rng.lua +2 -2
  83. package/dist/functions/roomTransition.d.ts +1 -1
  84. package/dist/functions/roomTransition.lua +1 -1
  85. package/dist/functions/rooms.d.ts +1 -1
  86. package/dist/functions/rooms.lua +1 -1
  87. package/dist/functions/sprites.d.ts +3 -3
  88. package/dist/functions/sprites.lua +3 -3
  89. package/dist/functions/tears.d.ts +5 -4
  90. package/dist/functions/tears.d.ts.map +1 -1
  91. package/dist/functions/tears.lua +5 -4
  92. package/dist/functions/utils.d.ts +4 -1
  93. package/dist/functions/utils.d.ts.map +1 -1
  94. package/dist/functions/utils.lua +4 -1
  95. package/dist/functions/vector.d.ts +1 -1
  96. package/dist/functions/vector.d.ts.map +1 -1
  97. package/dist/functions/vector.lua +2 -2
  98. package/dist/initCustomCallbacks.d.ts.map +1 -1
  99. package/dist/initCustomCallbacks.lua +3 -0
  100. package/dist/interfaces/SaveData.d.ts +44 -8
  101. package/dist/interfaces/SaveData.d.ts.map +1 -1
  102. package/dist/interfaces/SaveData.lua +66 -0
  103. package/dist/interfaces/StatTypeType.d.ts +18 -0
  104. package/dist/interfaces/StatTypeType.d.ts.map +1 -0
  105. package/dist/interfaces/StatTypeType.lua +4 -0
  106. package/dist/interfaces/index.d.ts +1 -0
  107. package/dist/interfaces/index.d.ts.map +1 -1
  108. package/dist/interfaces/private/AddCallbackParameterCustom.d.ts +2 -0
  109. package/dist/interfaces/private/AddCallbackParameterCustom.d.ts.map +1 -1
  110. package/dist/objects/callbackRegisterFunctions.d.ts.map +1 -1
  111. package/dist/objects/callbackRegisterFunctions.lua +3 -0
  112. package/dist/objects/isaacAPIClassTypeToBrand.d.ts.map +1 -1
  113. package/dist/objects/isaacAPIClassTypeToBrand.lua +7 -1
  114. package/dist/objects/isaacAPIClassTypeToFunctions.d.ts.map +1 -1
  115. package/dist/objects/isaacAPIClassTypeToFunctions.lua +12 -1
  116. package/dist/types/index.d.ts +0 -1
  117. package/dist/types/index.d.ts.map +1 -1
  118. package/package.json +2 -2
  119. package/src/callbacks/postPlayerChangeHealth.ts +7 -1
  120. package/src/callbacks/postPlayerChangeStat.ts +68 -0
  121. package/src/callbacks/subscriptions/postPlayerChangeHealth.ts +5 -1
  122. package/src/callbacks/subscriptions/postPlayerChangeStat.ts +55 -0
  123. package/src/classes/DefaultMap.ts +36 -7
  124. package/src/core/constants.ts +1 -1
  125. package/src/enums/ModCallbackCustom.ts +36 -4
  126. package/src/enums/StatType.ts +47 -0
  127. package/src/enums/index.ts +1 -0
  128. package/src/enums/indexTypeDoc.ts +1 -0
  129. package/src/enums/private/CopyableIsaacAPIClassType.ts +5 -1
  130. package/src/enums/private/SerializationBrand.ts +1 -0
  131. package/src/features/customStage/versusScreen.ts +2 -2
  132. package/src/features/disableInputs.ts +3 -9
  133. package/src/features/runInNFrames.ts +1 -1
  134. package/src/features/runNextRoom.ts +1 -1
  135. package/src/features/saveDataManager/exports.ts +28 -10
  136. package/src/features/saveDataManager/merge.ts +1 -0
  137. package/src/features/saveDataManager/save.ts +2 -1
  138. package/src/features/taintedLazarusPlayers.ts +2 -2
  139. package/src/functions/bitSet128.ts +96 -0
  140. package/src/functions/collectibles.ts +2 -2
  141. package/src/functions/color.ts +3 -2
  142. package/src/functions/deepCopy.ts +2 -1
  143. package/src/functions/entities.ts +2 -2
  144. package/src/functions/familiars.ts +4 -4
  145. package/src/functions/flag.ts +1 -1
  146. package/src/functions/index.ts +2 -0
  147. package/src/functions/indexTypeDoc.ts +2 -0
  148. package/src/functions/isaacAPIClass.ts +0 -1
  149. package/src/functions/jsonRoom.ts +4 -4
  150. package/src/functions/kColor.ts +2 -2
  151. package/src/functions/log.ts +1 -1
  152. package/src/functions/npcs.ts +2 -2
  153. package/src/functions/playerIndex.ts +1 -1
  154. package/src/functions/playerStats.ts +26 -0
  155. package/src/functions/players.ts +1 -1
  156. package/src/functions/rng.ts +2 -2
  157. package/src/functions/roomTransition.ts +1 -1
  158. package/src/functions/rooms.ts +1 -1
  159. package/src/functions/sprites.ts +3 -3
  160. package/src/functions/tears.ts +5 -4
  161. package/src/functions/utils.ts +5 -1
  162. package/src/functions/vector.ts +2 -2
  163. package/src/initCustomCallbacks.ts +2 -0
  164. package/src/interfaces/SaveData.ts +256 -8
  165. package/src/interfaces/StatTypeType.ts +18 -0
  166. package/src/interfaces/index.ts +1 -0
  167. package/src/interfaces/indexTypeDoc.ts +1 -0
  168. package/src/interfaces/private/AddCallbackParameterCustom.ts +2 -0
  169. package/src/objects/callbackRegisterFunctions.ts +2 -0
  170. package/src/objects/isaacAPIClassTypeToBrand.ts +1 -0
  171. package/src/objects/isaacAPIClassTypeToFunctions.ts +12 -0
  172. package/src/types/index.ts +0 -1
  173. package/src/types/indexTypeDoc.ts +0 -1
  174. package/dist/classes/indexTypeDoc.d.ts +0 -3
  175. package/dist/classes/indexTypeDoc.d.ts.map +0 -1
  176. package/dist/classes/indexTypeDoc.lua +0 -4
  177. package/dist/core/indexTypeDoc.d.ts +0 -5
  178. package/dist/core/indexTypeDoc.d.ts.map +0 -1
  179. package/dist/core/indexTypeDoc.lua +0 -6
  180. package/dist/enums/indexTypeDoc.d.ts +0 -9
  181. package/dist/enums/indexTypeDoc.d.ts.map +0 -1
  182. package/dist/enums/indexTypeDoc.lua +0 -10
  183. package/dist/features/indexTypeDoc.d.ts +0 -30
  184. package/dist/features/indexTypeDoc.d.ts.map +0 -1
  185. package/dist/features/indexTypeDoc.lua +0 -31
  186. package/dist/functions/indexTypeDoc.d.ts +0 -99
  187. package/dist/functions/indexTypeDoc.d.ts.map +0 -1
  188. package/dist/functions/indexTypeDoc.lua +0 -100
  189. package/dist/indexTypeDoc.d.ts +0 -10
  190. package/dist/indexTypeDoc.d.ts.map +0 -1
  191. package/dist/indexTypeDoc.lua +0 -11
  192. package/dist/interfaces/indexTypeDoc.d.ts +0 -11
  193. package/dist/interfaces/indexTypeDoc.d.ts.map +0 -1
  194. package/dist/interfaces/indexTypeDoc.lua +0 -2
  195. package/dist/maps/indexTypeDoc.d.ts +0 -5
  196. package/dist/maps/indexTypeDoc.d.ts.map +0 -1
  197. package/dist/maps/indexTypeDoc.lua +0 -6
  198. package/dist/objects/indexTypeDoc.d.ts +0 -2
  199. package/dist/objects/indexTypeDoc.d.ts.map +0 -1
  200. package/dist/objects/indexTypeDoc.lua +0 -3
  201. package/dist/types/IsaacAPIClass.d.ts +0 -5
  202. package/dist/types/IsaacAPIClass.d.ts.map +0 -1
  203. package/dist/types/IsaacAPIClass.lua +0 -2
  204. package/dist/types/indexTypeDoc.d.ts +0 -12
  205. package/dist/types/indexTypeDoc.d.ts.map +0 -1
  206. package/dist/types/indexTypeDoc.lua +0 -3
  207. package/src/types/IsaacAPIClass.ts +0 -3
@@ -22,7 +22,7 @@ export function getJSONRoomDoorSlotFlags(
22
22
  const roomShapeNumber = tonumber(roomShapeString);
23
23
  if (roomShapeNumber === undefined) {
24
24
  error(
25
- `Failed to parse the "shape" property of a JSON room: ${roomShapeString}`,
25
+ `Failed to parse the "shape" field of a JSON room: ${roomShapeString}`,
26
26
  );
27
27
  }
28
28
  const roomShape = roomShapeNumber as RoomShape;
@@ -33,7 +33,7 @@ export function getJSONRoomDoorSlotFlags(
33
33
  const existsString = door.$.exists;
34
34
  if (existsString !== "True" && existsString !== "False") {
35
35
  error(
36
- `Failed to parse the "exists" property of a JSON room door: ${existsString}`,
36
+ `Failed to parse the "exists" field of a JSON room door: ${existsString}`,
37
37
  );
38
38
  }
39
39
 
@@ -44,13 +44,13 @@ export function getJSONRoomDoorSlotFlags(
44
44
  const xString = door.$.x;
45
45
  const x = tonumber(xString);
46
46
  if (x === undefined) {
47
- error(`Failed to parse the "x" property of a JSON room door: ${xString}`);
47
+ error(`Failed to parse the "x" field of a JSON room door: ${xString}`);
48
48
  }
49
49
 
50
50
  const yString = door.$.y;
51
51
  const y = tonumber(yString);
52
52
  if (y === undefined) {
53
- error(`Failed to parse the "y" property of a JSON room door: ${yString}`);
53
+ error(`Failed to parse the "y" field of a JSON room door: ${yString}`);
54
54
  }
55
55
 
56
56
  const doorSlot = getRoomShapeDoorSlot(roomShape, x, y);
@@ -9,8 +9,8 @@ export type SerializedKColor = LuaMap<string, unknown> & {
9
9
  readonly __serializedKColorBrand: symbol;
10
10
  };
11
11
 
12
- const KEYS = ["Red", "Green", "Blue", "Alpha"];
13
12
  const OBJECT_NAME = "KColor";
13
+ const KEYS = ["Red", "Green", "Blue", "Alpha"];
14
14
 
15
15
  /** Helper function to copy a `KColor` Isaac API class. */
16
16
  export function copyKColor(kColor: KColor): KColor {
@@ -84,7 +84,7 @@ export function getRandomKColor(
84
84
  return KColor(r, g, b, alpha);
85
85
  }
86
86
 
87
- /** Helper function to check if something is an instantiated KColor object. */
87
+ /** Helper function to check if something is an instantiated `KColor` object. */
88
88
  export function isKColor(object: unknown): object is KColor {
89
89
  return isIsaacAPIClassOfType(object, OBJECT_NAME);
90
90
  }
@@ -455,7 +455,7 @@ export function logUseFlags(
455
455
  * the Isaac API).
456
456
  */
457
457
  export function logUserdata(this: void, userdata: unknown): void {
458
- if (isUserdata(userdata)) {
458
+ if (!isUserdata(userdata)) {
459
459
  log("Userdata: [not userdata]");
460
460
  return;
461
461
  }
@@ -108,8 +108,8 @@ export function isAliveExceptionNPC(npc: EntityNPC): boolean {
108
108
  * that is spawned when the boss does the multi-stomp attack.
109
109
  *
110
110
  * When this attack occurs, four extra copies of Daddy Long Legs will be spawned with the same
111
- * entity type, variant, and sub-type. The `Entity.Parent` property will be undefined in this case,
112
- * so the way to tell them apart is to check for a non-undefined `Entity.SpawnerEntity` property.
111
+ * entity type, variant, and sub-type. The `Entity.Parent` field will be undefined in this case, so
112
+ * the way to tell them apart is to check for a non-undefined `Entity.SpawnerEntity` field.
113
113
  */
114
114
  export function isDaddyLongLegsChildStompEntity(npc: EntityNPC): boolean {
115
115
  return (
@@ -198,7 +198,7 @@ export function getSubPlayerParent(
198
198
 
199
199
  /**
200
200
  * Helper function to detect if a particular player is a "child" player, meaning that they have a
201
- * non-undefined `EntityPlayer.Parent` property. (For example, the Strawman Keeper.)
201
+ * non-undefined `EntityPlayer.Parent` field. (For example, the Strawman Keeper.)
202
202
  */
203
203
  export function isChildPlayer(player: EntityPlayer): boolean {
204
204
  return player.Parent !== undefined;
@@ -0,0 +1,26 @@
1
+ import { StatType } from "../enums/StatType";
2
+ import { StatTypeType } from "../interfaces/StatTypeType";
3
+
4
+ /** Helper function to get the stat for a player corresponding to the `StatType`. */
5
+ export function getPlayerStat<T extends StatType>(
6
+ player: EntityPlayer,
7
+ statType: T,
8
+ ): StatTypeType[T] {
9
+ // We can't use a switch statement here because control-flow based type analysis does not apply to
10
+ // generic type parameters.
11
+ return {
12
+ [StatType.DAMAGE]: player.Damage, // 1 << 0
13
+ [StatType.FIRE_DELAY]: player.MaxFireDelay, // 1 << 1
14
+ [StatType.SHOT_SPEED]: player.ShotSpeed, // 1 << 2
15
+ [StatType.TEAR_HEIGHT]: player.TearHeight, // 1 << 3
16
+ [StatType.TEAR_RANGE]: player.TearRange, // 1 << 3
17
+ [StatType.TEAR_FALLING_ACCELERATION]: player.TearFallingAcceleration, // 1 << 3
18
+ [StatType.TEAR_FALLING_SPEED]: player.TearFallingSpeed, // 1 << 3
19
+ [StatType.MOVE_SPEED]: player.MoveSpeed, // 1 << 4
20
+ [StatType.TEAR_FLAG]: player.TearFlags, // 1 << 5
21
+ [StatType.TEAR_COLOR]: player.TearColor, // 1 << 6
22
+ [StatType.FLYING]: player.CanFly, // 1 << 7
23
+ [StatType.LUCK]: player.Luck, // 1 << 10
24
+ [StatType.SIZE]: player.SizeMulti, // 1 << 11
25
+ }[statType];
26
+ }
@@ -48,7 +48,7 @@ export function addCollectibleCostume(
48
48
 
49
49
  /**
50
50
  * Helper function to add a stat to a player based on the `CacheFlag` provided. Call this function
51
- * from the EvaluateCache callback.
51
+ * from the `EVALUATE_CACHE` callback.
52
52
  *
53
53
  * Note that for `CacheFlag.FIRE_DELAY`, the "amount" argument will be interpreted as the tear stat
54
54
  * to add (and not the amount to mutate `EntityPlayer.MaxFireDelay` by).
@@ -15,8 +15,8 @@ export type SerializedRNG = LuaMap<string, unknown> & {
15
15
  */
16
16
  const RECOMMENDED_SHIFT_IDX = 35;
17
17
 
18
- const KEYS = ["seed"];
19
18
  const OBJECT_NAME = "RNG";
19
+ const KEYS = ["seed"];
20
20
 
21
21
  /** Helper function to copy an `RNG` Isaac API class. */
22
22
  export function copyRNG(rng: RNG): RNG {
@@ -60,7 +60,7 @@ export function getRandomSeed(): Seed {
60
60
  return safeRandomNumber as Seed;
61
61
  }
62
62
 
63
- /** Helper function to check if something is an instantiated RNG object. */
63
+ /** Helper function to check if something is an instantiated `RNG` object. */
64
64
  export function isRNG(object: unknown): object is RNG {
65
65
  return isIsaacAPIClassOfType(object, OBJECT_NAME);
66
66
  }
@@ -31,7 +31,7 @@ export function reloadRoom(): void {
31
31
  * arguments.
32
32
  *
33
33
  * Use this function instead of invoking the `Game.StartRoomTransition` method directly so that:
34
- * - you do not forget to set `Level.LeaveDoor` property
34
+ * - you do not forget to set the `Level.LeaveDoor` field
35
35
  * - to prevent crashing on invalid room grid indexes
36
36
  * - to automatically handle Curse of the Maze
37
37
  *
@@ -50,7 +50,7 @@ import { erange, irange } from "./utils";
50
50
  /**
51
51
  * Helper function for quickly switching to a new room without playing a particular animation. Use
52
52
  * this helper function over invoking the `Game.ChangeRoom` method directly to ensure that you do
53
- * not forget to set the `LeaveDoor` property and to prevent crashing on invalid room grid indexes.
53
+ * not forget to set the `LeaveDoor` field and to prevent crashing on invalid room grid indexes.
54
54
  */
55
55
  export function changeRoom(roomGridIndex: int): void {
56
56
  const level = game.GetLevel();
@@ -34,9 +34,9 @@ export function clearSprite(sprite: Sprite, ...layerIDs: int[]): void {
34
34
  *
35
35
  * Note that this function is bugged with the Stop Watch or the Broken Watch, since using the
36
36
  * `Sprite.SetFrame` method will reset the internal accumulator used to slow down the playback speed
37
- * of the animation. (The `PlaybackSpeed` property of the sprite is not used.) Thus, it is only safe
38
- * to use this function on animations that are not slowed down by Stop Watch or Broken Watch, such
39
- * as player animations.
37
+ * of the animation. (The `PlaybackSpeed` field of the sprite is not used.) Thus, it is only safe to
38
+ * use this function on animations that are not slowed down by Stop Watch or Broken Watch, such as
39
+ * player animations.
40
40
  */
41
41
  export function getLastFrameOfAnimation(
42
42
  sprite: Sprite,
@@ -1,6 +1,7 @@
1
1
  /**
2
- * - Converts the specified amount of tears stat into MaxFireDelay and adds it to the player.
3
- * - This function should only be used inside the EvaluateCache callback.
2
+ * - Converts the specified amount of tears stat into the format of `EntityPlayer.MaxFireDelay` and
3
+ * adds it to the player.
4
+ * - This function should only be used inside the `EVALUATE_CACHE` callback.
4
5
  * - In this context, the "tears stat" represents what is shown on the in-game stat UI.
5
6
  */
6
7
  export function addTearsStat(player: EntityPlayer, tearsStat: float): void {
@@ -11,7 +12,7 @@ export function addTearsStat(player: EntityPlayer, tearsStat: float): void {
11
12
  }
12
13
 
13
14
  /**
14
- * - The `EntityPlayer` object stores a player's tear rate in the `MaxFireDelay` attribute. This is
15
+ * - The `EntityPlayer` object stores a player's tear rate in the `MaxFireDelay` field. This is
15
16
  * equivalent to how many tears the player can shoot per frame.
16
17
  * - If you already have a "tears" stat and you want to convert it back to MaxFireDelay, then use
17
18
  * this function.
@@ -22,7 +23,7 @@ export function getFireDelay(tearsStat: float): float {
22
23
  }
23
24
 
24
25
  /**
25
- * - The `EntityPlayer` object stores a player's tear rate in the `MaxFireDelay` attribute. This is
26
+ * - The `EntityPlayer` object stores a player's tear rate in the `MaxFireDelay` field. This is
26
27
  * equivalent to how many tears the player can shoot per frame.
27
28
  * - If you want to convert this to the "tears" stat that is shown on the in-game stat UI, then use
28
29
  * this function.
@@ -187,9 +187,13 @@ export function repeat(n: int, func: (i: int) => void): void {
187
187
  * When you see this function, it simply means that the programmer intends to add in more code to
188
188
  * this spot later.
189
189
  *
190
+ * This function is variadic, meaning that you can pass as many arguments as you want. (This is
191
+ * useful as a means to prevent lint warnings.)
192
+ *
190
193
  * This function does not actually do anything. (It is an "empty" function.)
191
194
  */
192
- export function todo(): void {}
195
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
196
+ export function todo(...args: unknown[]): void {}
193
197
 
194
198
  /**
195
199
  * Helper function to sort a two-dimensional array by the first element.
@@ -11,8 +11,8 @@ export type SerializedVector = LuaMap<string, unknown> & {
11
11
  readonly __serializedVectorBrand: symbol;
12
12
  };
13
13
 
14
- const KEYS = ["X", "Y"];
15
14
  const OBJECT_NAME = "Vector";
15
+ const KEYS = ["X", "Y"];
16
16
 
17
17
  /** Helper function to copy a `Vector` Isaac API class. */
18
18
  export function copyVector(vector: Vector): Vector {
@@ -91,7 +91,7 @@ export function isSerializedVector(
91
91
  return tableHasKeys(object, ...KEYS) && object.has(SerializationBrand.VECTOR);
92
92
  }
93
93
 
94
- /** Helper function to check if something is an instantiated Vector object. */
94
+ /** Helper function to check if something is an instantiated `Vector` object. */
95
95
  export function isVector(object: unknown): object is Vector {
96
96
  return isIsaacAPIClassOfType(object, OBJECT_NAME);
97
97
  }
@@ -37,6 +37,7 @@ import { postPickupStateChangedInit } from "./callbacks/postPickupStateChanged";
37
37
  import { postPitRenderInit } from "./callbacks/postPitRender";
38
38
  import { postPitUpdateInit } from "./callbacks/postPitUpdate";
39
39
  import { postPlayerChangeHealthInit } from "./callbacks/postPlayerChangeHealth";
40
+ import { postPlayerChangeStatInit } from "./callbacks/postPlayerChangeStat";
40
41
  import { postPlayerChangeTypeInit } from "./callbacks/postPlayerChangeType";
41
42
  import { postPlayerCollectibleCallbacksInit } from "./callbacks/postPlayerCollectible";
42
43
  import { postPlayerFatalDamageInit } from "./callbacks/postPlayerFatalDamage";
@@ -109,6 +110,7 @@ export function initCustomCallbacks(mod: ModUpgraded): void {
109
110
  postPitRenderInit(mod);
110
111
  postPitUpdateInit(mod);
111
112
  postPlayerChangeHealthInit(mod);
113
+ postPlayerChangeStatInit(mod);
112
114
  postPlayerChangeTypeInit(mod);
113
115
  postPlayerCollectibleCallbacksInit(mod);
114
116
  postPlayerFatalDamageInit(mod);
@@ -1,3 +1,5 @@
1
+ /* eslint-disable max-classes-per-file */
2
+
1
3
  /**
2
4
  * This is the format of the object that you give to the save data manager. It will contains all of
3
5
  * the variables for the particular mod feature.
@@ -11,19 +13,265 @@
11
13
  * - `boolean`
12
14
  * - `number`
13
15
  * - `string`
16
+ * - `undefined` (will be skipped over when saving)
17
+ * - `null` (will be skipped over when saving)
14
18
  * - `Map` / `DefaultMap`
15
19
  * - `Set`
16
20
  * - serializable Isaac API classes (such as `Color`)
17
21
  * - TSTL classes (i.e. classes that you made yourself)
18
22
  * - sub-objects or a `LuaMap` that contains the above values
23
+ */
24
+ export interface SaveData<
25
+ Persistent = unknown,
26
+ Run = unknown,
27
+ Level = unknown,
28
+ > {
29
+ persistent?: Serializable<Persistent>;
30
+ run?: Serializable<Run>;
31
+ level?: Serializable<Level>;
32
+ room?: Record<string, unknown>; // Room data is never saved to disk.
33
+ }
34
+
35
+ /**
36
+ * A type that represents valid serializable data fed to the save data manager.
37
+ *
38
+ * Custom errors are thrown when an Isaac API class or a nested custom class is detected.
19
39
  *
20
- * (Unfortunately, it is not possible to create a recursive type definition that matches these
21
- * properties. This means that the TypeScript compiler will not be able to validate that you are
22
- * passing in serializable data.)
40
+ * The recursive nature of this type is based on the `Immutable` type:
41
+ * https://stackoverflow.com/questions/41879327/deepreadonly-object-typescript
23
42
  */
24
- export interface SaveData {
25
- persistent?: Record<string, unknown>;
26
- run?: Record<string, unknown>;
27
- level?: Record<string, unknown>;
28
- room?: Record<string, unknown>;
43
+ type Serializable<T> =
44
+ // Allow the trivial case of primitives.
45
+ T extends SerializablePrimitive
46
+ ? T
47
+ : // Allow a specific subset of Isaac API classes that are copyable / serializable.
48
+ T extends SerializableIsaacAPIClass
49
+ ? T
50
+ : // Disallow all other Isaac API classes.
51
+ T extends IsaacAPIClass
52
+ ? ErrorIsaacAPIClassIsNotSerializable
53
+ : // Allow some specific "container" objects.
54
+ // These container objects are explicitly handled by the save data manager, but there are
55
+ // restrictions on the things inside of them.
56
+ T extends Array<infer U>
57
+ ? SerializableArray<U>
58
+ : T extends ReadonlyArray<infer U>
59
+ ? SerializableReadonlyArray<U>
60
+ : T extends Map<infer K, infer V>
61
+ ? SerializableMap<K, V>
62
+ : T extends ReadonlyMap<infer K, infer V>
63
+ ? SerializableReadonlyMap<K, V>
64
+ : T extends Set<infer V>
65
+ ? SerializableSet<V>
66
+ : T extends ReadonlySet<infer V>
67
+ ? SerializableReadonlySet<V>
68
+ : // Allow any other object, as long as the values are themselves serializable.
69
+ SerializableObject<T>;
70
+
71
+ /**
72
+ * This is mostly copied from the `Serializable` type. The difference is that we want to disallow
73
+ * classes with methods.
74
+ */
75
+ type SerializableInsideArrayOrMap<T> =
76
+ // Allow the trivial case of primitives.
77
+ T extends SerializablePrimitive
78
+ ? T
79
+ : // Allow a specific subset of Isaac API classes that are copyable / serializable.
80
+ T extends SerializableIsaacAPIClass
81
+ ? T
82
+ : // Disallow all other Isaac API classes.
83
+ T extends IsaacAPIClass
84
+ ? ErrorIsaacAPIClassIsNotSerializable
85
+ : // Allow some specific "container" objects.
86
+ // These container objects are explicitly handled by the save data manager, but there are
87
+ // restrictions on the things inside of them.
88
+ T extends Array<infer U>
89
+ ? SerializableArray<U>
90
+ : T extends ReadonlyArray<infer U>
91
+ ? SerializableReadonlyArray<U>
92
+ : T extends Map<infer K, infer V>
93
+ ? SerializableMap<K, V>
94
+ : T extends ReadonlyMap<infer K, infer V>
95
+ ? SerializableReadonlyMap<K, V>
96
+ : T extends Set<infer V>
97
+ ? SerializableSet<V>
98
+ : T extends ReadonlySet<infer V>
99
+ ? SerializableReadonlySet<V>
100
+ : // Disallow objects with functions on them (i.e. classes with methods).
101
+ // (This has to be after allowing maps and sets, because those have functions inside of them.)
102
+ T extends HasMethods<T>
103
+ ? ErrorCustomClassNotSerializable
104
+ : // Disallow functions.
105
+ // (We can only disallow functions when inside of containers, because we want to allow classes
106
+ // with methods attached to normal objects.)
107
+ T extends Function // eslint-disable-line @typescript-eslint/ban-types
108
+ ? FunctionIsNotSerializable
109
+ : // Finally, allow any other object, as long as the values are themselves serializable.
110
+ SerializableObject<T>;
111
+
112
+ type SerializablePrimitive = boolean | string | number | undefined | null;
113
+ type SerializableArray<T> = Array<SerializableInsideArrayOrMap<T>>;
114
+ type SerializableReadonlyArray<T> = ReadonlyArray<
115
+ SerializableInsideArrayOrMap<T>
116
+ >;
117
+ type SerializableMap<K, V> = Map<
118
+ SerializableInsideArrayOrMap<K>,
119
+ SerializableInsideArrayOrMap<V>
120
+ >;
121
+ type SerializableReadonlyMap<K, V> = ReadonlyMap<
122
+ SerializableInsideArrayOrMap<K>,
123
+ SerializableInsideArrayOrMap<V>
124
+ >;
125
+ type SerializableSet<T> = Set<SerializableInsideArrayOrMap<T>>;
126
+ type SerializableReadonlySet<T> = ReadonlySet<SerializableInsideArrayOrMap<T>>;
127
+ type SerializableObject<T> = { [K in keyof T]: Serializable<T[K]> };
128
+ type SerializableIsaacAPIClass = BitSet128 | Color | KColor | RNG | Vector;
129
+
130
+ // eslint-disable-next-line @typescript-eslint/ban-types
131
+ type HasMethods<T> = {} extends {
132
+ // eslint-disable-next-line @typescript-eslint/ban-types
133
+ [K in keyof T as T[K] extends Function ? K : never]-?: 1;
134
+ }
135
+ ? never
136
+ : T;
137
+
138
+ type FunctionIsNotSerializable =
139
+ "Error: Functions are not serializable. For more information, see: https://isaacscript.github.io/main/gotchas#functions-are-not-serializable";
140
+
141
+ type ErrorIsaacAPIClassIsNotSerializable =
142
+ "Error: Isaac API classes (such as e.g. `Entity` or `RoomConfig`) are not serializable. For more information, see: https://isaacscript.github.io/main/gotchas#isaac-api-classes-are-not-serializable";
143
+
144
+ type ErrorCustomClassNotSerializable =
145
+ "Error: Custom classes with one or more methods are not serializable when inside of an array, map, or set. For more information, see: https://isaacscript.github.io/main/gotchas#custom-classes-are-not-serializable";
146
+
147
+ // -----
148
+ // Tests
149
+ // -----
150
+
151
+ function test<Persistent, Run, Level>(
152
+ _saveData: SaveData<Persistent, Run, Level>,
153
+ ) {}
154
+
155
+ {
156
+ const saveDataWithPrimitives = {
157
+ run: {
158
+ foo: 123,
159
+ bar: "bar",
160
+ baz: true,
161
+ nested: {
162
+ foo: 123,
163
+ bar: "bar",
164
+ baz: true,
165
+ },
166
+ },
167
+ };
168
+
169
+ // Primitives and nested primitives are allowed.
170
+ test(saveDataWithPrimitives);
171
+ }
172
+
173
+ {
174
+ const saveDataWithEntity = {
175
+ run: {
176
+ foo: {} as Entity,
177
+ },
178
+ };
179
+
180
+ // @ts-expect-error Isaac API classes are not serializable.
181
+ test(saveDataWithEntity);
182
+ }
183
+
184
+ {
185
+ const saveDataWithMap = {
186
+ run: {
187
+ foo: new Map<string, string>(),
188
+ },
189
+ };
190
+
191
+ // Maps with primitive values are allowed.
192
+ test(saveDataWithMap);
193
+ }
194
+
195
+ {
196
+ const saveDataWithMap = {
197
+ run: {
198
+ foo: new Map<string, () => void>(),
199
+ },
200
+ };
201
+
202
+ // @ts-expect-error Maps with function values are not serializable.
203
+ test(saveDataWithMap);
204
+ }
205
+
206
+ {
207
+ const saveDataWithMap = {
208
+ run: {
209
+ foo: new Map<string, Map<string, string>>(),
210
+ },
211
+ };
212
+
213
+ // Nested maps are allowed.
214
+ test(saveDataWithMap);
215
+ }
216
+
217
+ {
218
+ class Foo {
219
+ someField = 123;
220
+ }
221
+
222
+ const saveDataWithClass = {
223
+ run: {
224
+ foo: new Foo(),
225
+ },
226
+ };
227
+
228
+ // Custom classes without methods are allowed.
229
+ test(saveDataWithClass);
230
+ }
231
+
232
+ {
233
+ class Foo {
234
+ someField = 123;
235
+ someMethod() {} // eslint-disable-line class-methods-use-this
236
+ }
237
+
238
+ const saveDataWithClassWithMethod = {
239
+ run: {
240
+ foo: new Foo(),
241
+ },
242
+ };
243
+
244
+ // Custom classes with methods are allowed.
245
+ test(saveDataWithClassWithMethod);
246
+ }
247
+
248
+ {
249
+ class Foo {
250
+ someField = 123;
251
+ }
252
+
253
+ const saveDataWithNestedClass = {
254
+ run: {
255
+ fooMap: new Map<string, Foo>(),
256
+ },
257
+ };
258
+
259
+ // Nested custom classes without methods are allowed.
260
+ test(saveDataWithNestedClass);
261
+ }
262
+
263
+ {
264
+ class Foo {
265
+ someField = 123;
266
+ someMethod() {} // eslint-disable-line class-methods-use-this
267
+ }
268
+
269
+ const saveDataWithNestedClass = {
270
+ run: {
271
+ fooMap: new Map<string, Foo>(),
272
+ },
273
+ };
274
+
275
+ // @ts-expect-error Nested custom classes with methods are not serializable.
276
+ test(saveDataWithNestedClass);
29
277
  }
@@ -0,0 +1,18 @@
1
+ import { TearFlag } from "isaac-typescript-definitions";
2
+ import { StatType } from "../enums/StatType";
3
+
4
+ export interface StatTypeType {
5
+ [StatType.DAMAGE]: float;
6
+ [StatType.FIRE_DELAY]: float;
7
+ [StatType.SHOT_SPEED]: float;
8
+ [StatType.TEAR_HEIGHT]: float;
9
+ [StatType.TEAR_RANGE]: float;
10
+ [StatType.TEAR_FALLING_ACCELERATION]: float;
11
+ [StatType.TEAR_FALLING_SPEED]: float;
12
+ [StatType.MOVE_SPEED]: float;
13
+ [StatType.TEAR_FLAG]: BitFlags<TearFlag>;
14
+ [StatType.TEAR_COLOR]: Color;
15
+ [StatType.FLYING]: boolean;
16
+ [StatType.LUCK]: float;
17
+ [StatType.SIZE]: Vector;
18
+ }
@@ -7,4 +7,5 @@ export * from "./PlayerHealth";
7
7
  export * from "./PocketItemDescription";
8
8
  export * from "./RoomDescription";
9
9
  export * from "./SaveData";
10
+ export * from "./StatTypeType";
10
11
  export * from "./TrinketSituation";
@@ -7,4 +7,5 @@ export * as PlayerHealth from "./PlayerHealth";
7
7
  export * as PocketItemDescription from "./PocketItemDescription";
8
8
  export * as RoomDescription from "./RoomDescription";
9
9
  export * as SaveData from "./SaveData";
10
+ export * as StatTypeType from "./StatTypeType";
10
11
  export * as TrinketSituation from "./TrinketSituation";
@@ -50,6 +50,7 @@ import { PostPickupStateChangedRegisterParameters } from "../../callbacks/subscr
50
50
  import { PostPitRenderRegisterParameters } from "../../callbacks/subscriptions/postPitRender";
51
51
  import { PostPitUpdateRegisterParameters } from "../../callbacks/subscriptions/postPitUpdate";
52
52
  import { PostPlayerChangeHealthRegisterParameters } from "../../callbacks/subscriptions/postPlayerChangeHealth";
53
+ import { PostPlayerChangeStatRegisterParameters } from "../../callbacks/subscriptions/postPlayerChangeStat";
53
54
  import { PostPlayerChangeTypeRegisterParameters } from "../../callbacks/subscriptions/postPlayerChangeType";
54
55
  import { PostPlayerCollectibleAddedRegisterParameters } from "../../callbacks/subscriptions/postPlayerCollectibleAdded";
55
56
  import { PostPlayerCollectibleRemovedRegisterParameters } from "../../callbacks/subscriptions/postPlayerCollectibleRemoved";
@@ -140,6 +141,7 @@ export interface AddCallbackParameterCustom {
140
141
  [ModCallbackCustom.POST_PIT_RENDER]: PostPitRenderRegisterParameters;
141
142
  [ModCallbackCustom.POST_PIT_UPDATE]: PostPitUpdateRegisterParameters;
142
143
  [ModCallbackCustom.POST_PLAYER_CHANGE_HEALTH]: PostPlayerChangeHealthRegisterParameters;
144
+ [ModCallbackCustom.POST_PLAYER_CHANGE_STAT]: PostPlayerChangeStatRegisterParameters;
143
145
  [ModCallbackCustom.POST_PLAYER_CHANGE_TYPE]: PostPlayerChangeTypeRegisterParameters;
144
146
  [ModCallbackCustom.POST_PLAYER_COLLECTIBLE_ADDED]: PostPlayerCollectibleAddedRegisterParameters;
145
147
  [ModCallbackCustom.POST_PLAYER_COLLECTIBLE_REMOVED]: PostPlayerCollectibleRemovedRegisterParameters;
@@ -50,6 +50,7 @@ import { postPickupStateChangedRegister } from "../callbacks/subscriptions/postP
50
50
  import { postPitRenderRegister } from "../callbacks/subscriptions/postPitRender";
51
51
  import { postPitUpdateRegister } from "../callbacks/subscriptions/postPitUpdate";
52
52
  import { postPlayerChangeHealthRegister } from "../callbacks/subscriptions/postPlayerChangeHealth";
53
+ import { postPlayerChangeStatRegister } from "../callbacks/subscriptions/postPlayerChangeStat";
53
54
  import { postPlayerChangeTypeRegister } from "../callbacks/subscriptions/postPlayerChangeType";
54
55
  import { postPlayerCollectibleAddedRegister } from "../callbacks/subscriptions/postPlayerCollectibleAdded";
55
56
  import { postPlayerCollectibleRemovedRegister } from "../callbacks/subscriptions/postPlayerCollectibleRemoved";
@@ -155,6 +156,7 @@ export const CALLBACK_REGISTER_FUNCTIONS: {
155
156
  [ModCallbackCustom.POST_PIT_RENDER]: postPitRenderRegister,
156
157
  [ModCallbackCustom.POST_PIT_UPDATE]: postPitUpdateRegister,
157
158
  [ModCallbackCustom.POST_PLAYER_CHANGE_HEALTH]: postPlayerChangeHealthRegister,
159
+ [ModCallbackCustom.POST_PLAYER_CHANGE_STAT]: postPlayerChangeStatRegister,
158
160
  [ModCallbackCustom.POST_PLAYER_CHANGE_TYPE]: postPlayerChangeTypeRegister,
159
161
  [ModCallbackCustom.POST_PLAYER_COLLECTIBLE_ADDED]:
160
162
  postPlayerCollectibleAddedRegister,
@@ -4,6 +4,7 @@ import { SerializationBrand } from "../enums/private/SerializationBrand";
4
4
  export const ISAAC_API_CLASS_TYPE_TO_BRAND: {
5
5
  readonly [key in CopyableIsaacAPIClassType]: SerializationBrand;
6
6
  } = {
7
+ [CopyableIsaacAPIClassType.BIT_SET_128]: SerializationBrand.BIT_SET_128,
7
8
  [CopyableIsaacAPIClassType.COLOR]: SerializationBrand.COLOR,
8
9
  [CopyableIsaacAPIClassType.K_COLOR]: SerializationBrand.K_COLOR,
9
10
  [CopyableIsaacAPIClassType.RNG]: SerializationBrand.RNG,
@@ -1,6 +1,12 @@
1
1
  /* eslint-disable @typescript-eslint/no-explicit-any */
2
2
 
3
3
  import { CopyableIsaacAPIClassType } from "../enums/private/CopyableIsaacAPIClassType";
4
+ import {
5
+ copyBitSet128,
6
+ deserializeBitSet128,
7
+ isSerializedBitSet128,
8
+ serializeBitSet128,
9
+ } from "../functions/bitSet128";
4
10
  import {
5
11
  copyColor,
6
12
  deserializeColor,
@@ -36,6 +42,12 @@ interface IsaacAPIClassTypeFunctions {
36
42
  export const ISAAC_API_CLASS_TYPE_TO_FUNCTIONS: {
37
43
  readonly [key in CopyableIsaacAPIClassType]: IsaacAPIClassTypeFunctions;
38
44
  } = {
45
+ [CopyableIsaacAPIClassType.BIT_SET_128]: {
46
+ isSerialized: isSerializedBitSet128,
47
+ copy: copyBitSet128,
48
+ serialize: serializeBitSet128,
49
+ deserialize: deserializeBitSet128,
50
+ },
39
51
  [CopyableIsaacAPIClassType.COLOR]: {
40
52
  isSerialized: isSerializedColor,
41
53
  copy: copyColor,
@@ -2,7 +2,6 @@ export * from "./AnyEntity";
2
2
  export * from "./AnyGridEntity";
3
3
  export * from "./CollectibleIndex";
4
4
  export * from "./Immutable";
5
- export * from "./IsaacAPIClass";
6
5
  export * from "./PickingUpItem";
7
6
  export * from "./PickupIndex";
8
7
  export * from "./PlayerIndex";
@@ -2,7 +2,6 @@ export * as AnyEntity from "./AnyEntity";
2
2
  export * as AnyGridEntity from "./AnyGridEntity";
3
3
  export * as CollectibleIndex from "./CollectibleIndex";
4
4
  export * as Immutable from "./Immutable";
5
- export * as IsaacAPIClass from "./IsaacAPIClass";
6
5
  export * as PickingUpItem from "./PickingUpItem";
7
6
  export * as PickupIndex from "./PickupIndex";
8
7
  export * as PlayerIndex from "./PlayerIndex";