isaacscript-common 31.5.0 → 31.6.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 (126) hide show
  1. package/dist/index.rollup.d.ts +26 -4
  2. package/dist/isaacscript-common.lua +626 -645
  3. package/dist/src/classes/ModFeature.d.ts.map +1 -1
  4. package/dist/src/classes/ModFeature.lua +5 -9
  5. package/dist/src/classes/ModUpgraded.d.ts.map +1 -1
  6. package/dist/src/classes/ModUpgraded.lua +8 -14
  7. package/dist/src/classes/callbacks/PostNewRoomEarly.lua +2 -2
  8. package/dist/src/classes/features/callbackLogic/CustomGridEntities.d.ts.map +1 -1
  9. package/dist/src/classes/features/callbackLogic/CustomGridEntities.lua +3 -3
  10. package/dist/src/classes/features/other/CharacterHealthConversion.lua +1 -1
  11. package/dist/src/classes/features/other/CustomItemPools.d.ts.map +1 -1
  12. package/dist/src/classes/features/other/CustomItemPools.lua +12 -6
  13. package/dist/src/classes/features/other/CustomStages.d.ts.map +1 -1
  14. package/dist/src/classes/features/other/CustomStages.lua +12 -14
  15. package/dist/src/classes/features/other/CustomTrapdoors.d.ts.map +1 -1
  16. package/dist/src/classes/features/other/CustomTrapdoors.lua +6 -8
  17. package/dist/src/classes/features/other/DeployJSONRoom.d.ts.map +1 -1
  18. package/dist/src/classes/features/other/DeployJSONRoom.lua +15 -15
  19. package/dist/src/classes/features/other/ModdedElementSets.d.ts.map +1 -1
  20. package/dist/src/classes/features/other/ModdedElementSets.lua +21 -12
  21. package/dist/src/classes/features/other/Pause.d.ts.map +1 -1
  22. package/dist/src/classes/features/other/Pause.lua +4 -6
  23. package/dist/src/classes/features/other/SaveDataManager.d.ts.map +1 -1
  24. package/dist/src/classes/features/other/SaveDataManager.lua +5 -9
  25. package/dist/src/classes/features/other/customStages/backdrop.lua +9 -10
  26. package/dist/src/classes/features/other/saveDataManager/restoreDefaults.lua +1 -1
  27. package/dist/src/functions/array.d.ts +3 -0
  28. package/dist/src/functions/array.d.ts.map +1 -1
  29. package/dist/src/functions/array.lua +9 -3
  30. package/dist/src/functions/bitSet128.d.ts.map +1 -1
  31. package/dist/src/functions/bitSet128.lua +4 -6
  32. package/dist/src/functions/bitwise.d.ts.map +1 -1
  33. package/dist/src/functions/bitwise.lua +7 -3
  34. package/dist/src/functions/color.d.ts.map +1 -1
  35. package/dist/src/functions/color.lua +5 -9
  36. package/dist/src/functions/deepCopy.lua +23 -31
  37. package/dist/src/functions/entities.d.ts.map +1 -1
  38. package/dist/src/functions/entities.lua +20 -18
  39. package/dist/src/functions/entitiesSpecific.d.ts.map +1 -1
  40. package/dist/src/functions/entitiesSpecific.lua +11 -27
  41. package/dist/src/functions/enums.d.ts +6 -4
  42. package/dist/src/functions/enums.d.ts.map +1 -1
  43. package/dist/src/functions/enums.lua +13 -9
  44. package/dist/src/functions/gridEntities.d.ts.map +1 -1
  45. package/dist/src/functions/gridEntities.lua +18 -11
  46. package/dist/src/functions/gridEntitiesSpecific.d.ts.map +1 -1
  47. package/dist/src/functions/gridEntitiesSpecific.lua +16 -28
  48. package/dist/src/functions/input.d.ts +3 -0
  49. package/dist/src/functions/input.d.ts.map +1 -1
  50. package/dist/src/functions/input.lua +14 -14
  51. package/dist/src/functions/jsonRoom.d.ts.map +1 -1
  52. package/dist/src/functions/jsonRoom.lua +35 -23
  53. package/dist/src/functions/kColor.d.ts.map +1 -1
  54. package/dist/src/functions/kColor.lua +6 -12
  55. package/dist/src/functions/map.d.ts.map +1 -1
  56. package/dist/src/functions/map.lua +3 -3
  57. package/dist/src/functions/minimap.d.ts.map +1 -1
  58. package/dist/src/functions/minimap.lua +17 -9
  59. package/dist/src/functions/players.d.ts.map +1 -1
  60. package/dist/src/functions/players.lua +17 -22
  61. package/dist/src/functions/rng.d.ts.map +1 -1
  62. package/dist/src/functions/rng.lua +3 -3
  63. package/dist/src/functions/roomShapeWalls.d.ts.map +1 -1
  64. package/dist/src/functions/roomShapeWalls.lua +7 -3
  65. package/dist/src/functions/roomTransition.d.ts.map +1 -1
  66. package/dist/src/functions/roomTransition.lua +7 -3
  67. package/dist/src/functions/rooms.d.ts.map +1 -1
  68. package/dist/src/functions/rooms.lua +8 -5
  69. package/dist/src/functions/serialization.d.ts.map +1 -1
  70. package/dist/src/functions/serialization.lua +8 -18
  71. package/dist/src/functions/table.d.ts.map +1 -1
  72. package/dist/src/functions/table.lua +6 -12
  73. package/dist/src/functions/tstlClass.d.ts.map +1 -1
  74. package/dist/src/functions/tstlClass.lua +3 -3
  75. package/dist/src/functions/utils.d.ts +9 -0
  76. package/dist/src/functions/utils.d.ts.map +1 -1
  77. package/dist/src/functions/utils.lua +14 -6
  78. package/dist/src/functions/vector.d.ts.map +1 -1
  79. package/dist/src/functions/vector.lua +4 -6
  80. package/dist/src/functions/weighted.d.ts.map +1 -1
  81. package/dist/src/functions/weighted.lua +7 -3
  82. package/dist/src/sets/bossSets.d.ts.map +1 -1
  83. package/dist/src/sets/bossSets.lua +3 -3
  84. package/package.json +2 -2
  85. package/src/classes/ModFeature.ts +16 -12
  86. package/src/classes/ModUpgraded.ts +18 -16
  87. package/src/classes/callbacks/PostNewRoomEarly.ts +2 -2
  88. package/src/classes/features/callbackLogic/CustomGridEntities.ts +2 -3
  89. package/src/classes/features/other/CharacterHealthConversion.ts +1 -1
  90. package/src/classes/features/other/CustomItemPools.ts +9 -8
  91. package/src/classes/features/other/CustomStages.ts +9 -10
  92. package/src/classes/features/other/CustomTrapdoors.ts +9 -10
  93. package/src/classes/features/other/DeployJSONRoom.ts +21 -21
  94. package/src/classes/features/other/ModdedElementSets.ts +18 -21
  95. package/src/classes/features/other/Pause.ts +9 -6
  96. package/src/classes/features/other/SaveDataManager.ts +14 -16
  97. package/src/classes/features/other/customStages/backdrop.ts +5 -6
  98. package/src/classes/features/other/saveDataManager/restoreDefaults.ts +1 -1
  99. package/src/functions/array.ts +8 -6
  100. package/src/functions/bitSet128.ts +9 -10
  101. package/src/functions/bitwise.ts +6 -3
  102. package/src/functions/color.ts +13 -15
  103. package/src/functions/deepCopy.ts +18 -24
  104. package/src/functions/deepCopyTests.ts +5 -6
  105. package/src/functions/entities.ts +22 -20
  106. package/src/functions/entitiesSpecific.ts +10 -27
  107. package/src/functions/enums.ts +29 -17
  108. package/src/functions/gridEntities.ts +14 -16
  109. package/src/functions/gridEntitiesSpecific.ts +15 -28
  110. package/src/functions/input.ts +3 -3
  111. package/src/functions/jsonRoom.ts +39 -27
  112. package/src/functions/kColor.ts +17 -20
  113. package/src/functions/map.ts +5 -5
  114. package/src/functions/minimap.ts +16 -15
  115. package/src/functions/players.ts +7 -10
  116. package/src/functions/rng.ts +5 -5
  117. package/src/functions/roomShapeWalls.ts +3 -4
  118. package/src/functions/roomTransition.ts +5 -5
  119. package/src/functions/rooms.ts +5 -6
  120. package/src/functions/serialization.ts +25 -30
  121. package/src/functions/table.ts +18 -20
  122. package/src/functions/tstlClass.ts +5 -5
  123. package/src/functions/utils.ts +27 -6
  124. package/src/functions/vector.ts +9 -10
  125. package/src/functions/weighted.ts +5 -5
  126. package/src/sets/bossSets.ts +5 -5
@@ -2,7 +2,7 @@ import { ReadonlySet } from "../types/ReadonlySet";
2
2
  import { getRandomInt } from "./random";
3
3
  import { getRandomSeed, isRNG, newRNG } from "./rng";
4
4
  import { isNumber, isTable } from "./types";
5
- import { eRange } from "./utils";
5
+ import { assertDefined, eRange } from "./utils";
6
6
 
7
7
  /**
8
8
  * Helper function for determining if two arrays contain the exact same elements. Note that this
@@ -344,6 +344,9 @@ export function getArrayIndexes<T>(array: T[] | readonly T[]): int[] {
344
344
  * Helper function to return the last element of an array.
345
345
  *
346
346
  * If the array is empty, this will return undefined.
347
+ *
348
+ * (Note that TSTL does not support `Array.at(-1)`, which would make this helper function largely
349
+ * unnecessary.)
347
350
  */
348
351
  export function getLastElement<T>(array: T[]): T | undefined {
349
352
  return array[array.length - 1];
@@ -372,11 +375,10 @@ export function getRandomArrayElement<T>(
372
375
  exceptions.length > 0 ? arrayRemove(array, ...exceptions) : array;
373
376
  const randomIndex = getRandomArrayIndex(arrayToUse, seedOrRNG);
374
377
  const randomElement = arrayToUse[randomIndex];
375
- if (randomElement === undefined) {
376
- error(
377
- `Failed to get a random array element since the random index of ${randomIndex} was not valid.`,
378
- );
379
- }
378
+ assertDefined(
379
+ randomElement,
380
+ `Failed to get a random array element since the random index of ${randomIndex} was not valid.`,
381
+ );
380
382
 
381
383
  return randomElement;
382
384
  }
@@ -7,6 +7,7 @@ import {
7
7
  tableHasKeys,
8
8
  } from "./table";
9
9
  import { isTable } from "./types";
10
+ import { assertDefined } from "./utils";
10
11
 
11
12
  export type SerializedBitSet128 = LuaMap<string, unknown> & {
12
13
  readonly __serializedBitSet128Brand: symbol;
@@ -45,16 +46,14 @@ export function deserializeBitSet128(
45
46
 
46
47
  const [l, h] = getNumbersFromTable(bitSet128, OBJECT_NAME, ...KEYS);
47
48
 
48
- if (l === undefined) {
49
- error(
50
- `Failed to deserialize a ${OBJECT_NAME} object since the provided object did not have a value for: l`,
51
- );
52
- }
53
- if (h === undefined) {
54
- error(
55
- `Failed to deserialize a ${OBJECT_NAME} object since the provided object did not have a value for: h`,
56
- );
57
- }
49
+ assertDefined(
50
+ l,
51
+ `Failed to deserialize a ${OBJECT_NAME} object since the provided object did not have a value for: l`,
52
+ );
53
+ assertDefined(
54
+ h,
55
+ `Failed to deserialize a ${OBJECT_NAME} object since the provided object did not have a value for: h`,
56
+ );
58
57
 
59
58
  return BitSet128(l, h);
60
59
  }
@@ -1,4 +1,5 @@
1
1
  import { addFlag } from "./flag";
2
+ import { assertDefined } from "./utils";
2
3
 
3
4
  /** Helper function to convert a set of flags to a single `BitFlags` object. */
4
5
  export function arrayToBitFlags<T extends BitFlag | BitFlag128>(
@@ -33,9 +34,11 @@ export function convertDecimalToBinary(number: number, minLength?: int): int[] {
33
34
  const bitsString = number.toString(2);
34
35
  for (const bitString of bitsString) {
35
36
  const bit = tonumber(bitString);
36
- if (bit === undefined) {
37
- error(`Failed to convert the following number to binary: ${number}`);
38
- }
37
+ assertDefined(
38
+ bit,
39
+ `Failed to convert the following number to binary: ${number}`,
40
+ );
41
+
39
42
  bits.push(bit);
40
43
  }
41
44
 
@@ -9,6 +9,7 @@ import {
9
9
  tableHasKeys,
10
10
  } from "./table";
11
11
  import { isTable } from "./types";
12
+ import { assertDefined } from "./utils";
12
13
 
13
14
  export type SerializedColor = LuaMap<string, unknown> & {
14
15
  readonly __serializedColorBrand: symbol;
@@ -58,21 +59,18 @@ export function deserializeColor(color: SerializedColor): Color {
58
59
  ...KEYS,
59
60
  );
60
61
 
61
- if (r === undefined) {
62
- error(
63
- `Failed to deserialize a ${OBJECT_NAME} object since the provided object did not have a value for: R`,
64
- );
65
- }
66
- if (g === undefined) {
67
- error(
68
- `Failed to deserialize a ${OBJECT_NAME} object since the provided object did not have a value for: G`,
69
- );
70
- }
71
- if (b === undefined) {
72
- error(
73
- `Failed to deserialize a ${OBJECT_NAME} object since the provided object did not have a value for: B`,
74
- );
75
- }
62
+ assertDefined(
63
+ r,
64
+ `Failed to deserialize a ${OBJECT_NAME} object since the provided object did not have a value for: R`,
65
+ );
66
+ assertDefined(
67
+ g,
68
+ `Failed to deserialize a ${OBJECT_NAME} object since the provided object did not have a value for: G`,
69
+ );
70
+ assertDefined(
71
+ b,
72
+ `Failed to deserialize a ${OBJECT_NAME} object since the provided object did not have a value for: B`,
73
+ );
76
74
 
77
75
  return Color(r, g, b, a, ro, go, bo);
78
76
  }
@@ -24,7 +24,7 @@ import {
24
24
  newTSTLClass,
25
25
  } from "./tstlClass";
26
26
  import { asString, isNumber, isPrimitive } from "./types";
27
- import { getTraversalDescription } from "./utils";
27
+ import { assertDefined, getTraversalDescription } from "./utils";
28
28
 
29
29
  /**
30
30
  * `deepCopy` is a semi-generic deep cloner. It will recursively copy all of the values so that none
@@ -361,11 +361,10 @@ function getNewDefaultMap(
361
361
  const defaultMapValue = defaultMap.get(
362
362
  SerializationBrand.DEFAULT_MAP_VALUE,
363
363
  );
364
- if (defaultMapValue === undefined) {
365
- error(
366
- `Failed to deserialize a default map of "${traversalDescription}", since there was no serialization brand of: ${SerializationBrand.DEFAULT_MAP_VALUE}`,
367
- );
368
- }
364
+ assertDefined(
365
+ defaultMapValue,
366
+ `Failed to deserialize a default map of "${traversalDescription}", since there was no serialization brand of: ${SerializationBrand.DEFAULT_MAP_VALUE}`,
367
+ );
369
368
 
370
369
  // eslint-disable-next-line isaacscript/no-invalid-default-map
371
370
  return new DefaultMap(defaultMapValue);
@@ -518,21 +517,21 @@ function deepCopyTSTLClass(
518
517
  const tstlClassName = tstlClass.get(SerializationBrand.TSTL_CLASS) as
519
518
  | string
520
519
  | undefined;
521
- if (tstlClassName === undefined) {
522
- error(
523
- "Failed to deserialize a TSTL class since the brand did not contain the class name.",
524
- );
525
- }
520
+ assertDefined(
521
+ tstlClassName,
522
+ "Failed to deserialize a TSTL class since the brand did not contain the class name.",
523
+ );
526
524
 
527
525
  const classConstructor = classConstructors.get(tstlClassName);
528
- if (classConstructor === undefined) {
529
- error(
530
- `Failed to deserialize a TSTL class since there was no constructor registered for a class name of "${tstlClassName}". If this mod is using the save data manager, it must register the class constructor with the "saveDataManagerRegisterClass" method.`,
531
- );
532
- }
526
+ assertDefined(
527
+ classConstructor,
528
+ `Failed to deserialize a TSTL class since there was no constructor registered for a class name of "${tstlClassName}". If this mod is using the save data manager, it must register the class constructor with the "saveDataManagerRegisterClass" method.`,
529
+ );
533
530
 
534
531
  // eslint-disable-next-line new-cap
535
532
  newClass = new classConstructor() as TSTLClass;
533
+
534
+ break;
536
535
  }
537
536
  }
538
537
 
@@ -725,16 +724,10 @@ function deepCopyUserdata(
725
724
  serializationType: SerializationType,
726
725
  traversalDescription: string,
727
726
  ) {
728
- const classType = getIsaacAPIClassName(value);
729
- if (classType === undefined) {
730
- error(
731
- `The deep copy function was not able to derive the Isaac API class type for: ${traversalDescription}`,
732
- );
733
- }
734
-
735
727
  if (!isCopyableIsaacAPIClass(value)) {
728
+ const className = getIsaacAPIClassName(value) ?? "Unknown";
736
729
  error(
737
- `The deep copy function does not support serializing "${traversalDescription}", since it is an Isaac API class of type: ${classType}`,
730
+ `The deep copy function does not support serializing "${traversalDescription}", since it is an Isaac API class of type: ${className}`,
738
731
  );
739
732
  }
740
733
 
@@ -747,6 +740,7 @@ function deepCopyUserdata(
747
740
  return serializeIsaacAPIClass(value);
748
741
  }
749
742
 
743
+ // eslint-disable-next-line isaacscript/require-break
750
744
  case SerializationType.DESERIALIZE: {
751
745
  error(
752
746
  `The deep copy function can not deserialize "${traversalDescription}", since it is userdata.`,
@@ -69,8 +69,7 @@ function copiedObjectHasKeyAndValueString() {
69
69
  "copiedObjectHasKeyAndValueString",
70
70
  );
71
71
 
72
- const value = newObject[keyToLookFor];
73
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
72
+ const value = newObject[keyToLookFor] as string | undefined;
74
73
  if (value === undefined) {
75
74
  error(`The copied object did not have a key of: ${keyToLookFor}`);
76
75
  }
@@ -236,8 +235,9 @@ function copiedObjectHasChildObject() {
236
235
  "copiedObjectHasChildObject",
237
236
  );
238
237
 
239
- const childObject = newObject[childObjectIndex];
240
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
238
+ const childObject = newObject[childObjectIndex] as
239
+ | (typeof oldObject)["abc"]
240
+ | undefined;
241
241
  if (childObject === undefined) {
242
242
  error(`Failed to find the child object at index: ${childObjectIndex}`);
243
243
  }
@@ -246,8 +246,7 @@ function copiedObjectHasChildObject() {
246
246
  error(`The copied child object had a type of: ${typeof childObject}`);
247
247
  }
248
248
 
249
- const value = childObject[keyToLookFor];
250
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
249
+ const value = childObject[keyToLookFor] as string | undefined;
251
250
  if (value === undefined) {
252
251
  error(`The child object did not have a key of: ${keyToLookFor}`);
253
252
  }
@@ -13,6 +13,7 @@ import { isRNG, newRNG } from "./rng";
13
13
  import { setSpriteOpacity } from "./sprites";
14
14
  import { isTSTLSet } from "./tstlClass";
15
15
  import { isPrimitive } from "./types";
16
+ import { assertDefined } from "./utils";
16
17
  import { doesVectorHaveLength, isVector, vectorToString } from "./vector";
17
18
 
18
19
  /** From DeadInfinity. */
@@ -167,21 +168,22 @@ export function getConstituentsFromEntityID(
167
168
  const [entityTypeString, variantString, subTypeString] = parts;
168
169
 
169
170
  const entityType = tonumber(entityTypeString);
170
- if (entityType === undefined) {
171
- error(`Failed to convert the entity type to a number: ${entityTypeString}`);
172
- }
171
+ assertDefined(
172
+ entityType,
173
+ `Failed to convert the entity type to a number: ${entityTypeString}`,
174
+ );
173
175
 
174
176
  const variant = tonumber(variantString);
175
- if (variant === undefined) {
176
- error(`Failed to convert the entity variant to a number: ${variantString}`);
177
- }
177
+ assertDefined(
178
+ variant,
179
+ `Failed to convert the entity variant to a number: ${variantString}`,
180
+ );
178
181
 
179
182
  const subType = tonumber(subTypeString);
180
- if (subType === undefined) {
181
- error(
182
- `Failed to convert the entity sub-type to a number: ${subTypeString}`,
183
- );
184
- }
183
+ assertDefined(
184
+ subType,
185
+ `Failed to convert the entity sub-type to a number: ${subTypeString}`,
186
+ );
185
187
 
186
188
  return [entityType, variant, subType];
187
189
  }
@@ -241,9 +243,7 @@ export function getEntityFields(
241
243
  const metatable = getmetatable(entity) as
242
244
  | LuaMap<AnyNotNil, unknown>
243
245
  | undefined;
244
- if (metatable === undefined) {
245
- error("Failed to get the metatable for an entity.");
246
- }
246
+ assertDefined(metatable, "Failed to get the metatable for an entity.");
247
247
 
248
248
  setPrimitiveEntityFields(entity, metatable, entityFields);
249
249
 
@@ -258,9 +258,10 @@ export function getEntityFields(
258
258
  const parentTable = metatable.get("__parent") as
259
259
  | LuaMap<AnyNotNil, unknown>
260
260
  | undefined;
261
- if (parentTable === undefined) {
262
- error('Failed to get the "__parent" table for an entity.');
263
- }
261
+ assertDefined(
262
+ parentTable,
263
+ 'Failed to get the "__parent" table for an entity.',
264
+ );
264
265
 
265
266
  setPrimitiveEntityFields(entity, parentTable, entityFields);
266
267
 
@@ -275,9 +276,10 @@ function setPrimitiveEntityFields(
275
276
  const propGetTable = metatable.get("__propget") as
276
277
  | LuaMap<AnyNotNil, unknown>
277
278
  | undefined;
278
- if (propGetTable === undefined) {
279
- error('Failed to get the "__propget" table for an entity.');
280
- }
279
+ assertDefined(
280
+ propGetTable,
281
+ 'Failed to get the "__propget" table for an entity.',
282
+ );
281
283
 
282
284
  for (const [key] of propGetTable) {
283
285
  // The values of this table are functions. Thus, we use the key to index the original entity.
@@ -12,6 +12,7 @@ import type {
12
12
  import { EntityType } from "isaac-typescript-definitions";
13
13
  import { VectorZero } from "../core/constants";
14
14
  import { getEntities, removeEntities, spawn } from "./entities";
15
+ import { assertDefined } from "./utils";
15
16
 
16
17
  /**
17
18
  * Helper function to get all of the bombs in the room. (Specifically, this refers to the
@@ -562,9 +563,7 @@ export function spawnBomb(
562
563
  );
563
564
 
564
565
  const bomb = entity.ToBomb();
565
- if (bomb === undefined) {
566
- error("Failed to spawn a bomb.");
567
- }
566
+ assertDefined(bomb, "Failed to spawn a bomb.");
568
567
 
569
568
  return bomb;
570
569
  }
@@ -608,9 +607,7 @@ export function spawnEffect(
608
607
  );
609
608
 
610
609
  const effect = entity.ToEffect();
611
- if (effect === undefined) {
612
- error("Failed to spawn an effect.");
613
- }
610
+ assertDefined(effect, "Failed to spawn an effect.");
614
611
 
615
612
  return effect;
616
613
  }
@@ -659,9 +656,7 @@ export function spawnFamiliar(
659
656
  );
660
657
 
661
658
  const familiar = entity.ToFamiliar();
662
- if (familiar === undefined) {
663
- error("Failed to spawn a familiar.");
664
- }
659
+ assertDefined(familiar, "Failed to spawn a familiar.");
665
660
 
666
661
  return familiar;
667
662
  }
@@ -705,9 +700,7 @@ export function spawnKnife(
705
700
  );
706
701
 
707
702
  const knife = entity.ToKnife();
708
- if (knife === undefined) {
709
- error("Failed to spawn a knife.");
710
- }
703
+ assertDefined(knife, "Failed to spawn a knife.");
711
704
 
712
705
  return knife;
713
706
  }
@@ -751,9 +744,7 @@ export function spawnLaser(
751
744
  );
752
745
 
753
746
  const laser = entity.ToLaser();
754
- if (laser === undefined) {
755
- error("Failed to spawn a laser.");
756
- }
747
+ assertDefined(laser, "Failed to spawn a laser.");
757
748
 
758
749
  return laser;
759
750
  }
@@ -803,9 +794,7 @@ export function spawnNPC(
803
794
  );
804
795
 
805
796
  const npc = entity.ToNPC();
806
- if (npc === undefined) {
807
- error("Failed to spawn an NPC.");
808
- }
797
+ assertDefined(npc, "Failed to spawn an NPC.");
809
798
 
810
799
  return npc;
811
800
  }
@@ -856,9 +845,7 @@ export function spawnPickup(
856
845
  );
857
846
 
858
847
  const pickup = entity.ToPickup();
859
- if (pickup === undefined) {
860
- error("Failed to spawn a pickup.");
861
- }
848
+ assertDefined(pickup, "Failed to spawn a pickup.");
862
849
 
863
850
  return pickup;
864
851
  }
@@ -902,9 +889,7 @@ export function spawnProjectile(
902
889
  );
903
890
 
904
891
  const projectile = entity.ToProjectile();
905
- if (projectile === undefined) {
906
- error("Failed to spawn a projectile.");
907
- }
892
+ assertDefined(projectile, "Failed to spawn a projectile.");
908
893
 
909
894
  return projectile;
910
895
  }
@@ -987,9 +972,7 @@ export function spawnTear(
987
972
  );
988
973
 
989
974
  const tear = entity.ToTear();
990
- if (tear === undefined) {
991
- error("Failed to spawn a tear.");
992
- }
975
+ assertDefined(tear, "Failed to spawn a tear.");
993
976
 
994
977
  return tear;
995
978
  }
@@ -1,8 +1,8 @@
1
1
  import { ReadonlySet } from "../types/ReadonlySet";
2
2
  import { getRandomArrayElement } from "./array";
3
3
  import { getRandomSeed } from "./rng";
4
- import { isString } from "./types";
5
- import { iRange } from "./utils";
4
+ import { isNumber, isString } from "./types";
5
+ import { assertDefined, iRange } from "./utils";
6
6
 
7
7
  /**
8
8
  * TypeScriptToLua will transpile TypeScript enums to Lua tables that have a double mapping. Thus,
@@ -60,13 +60,17 @@ export function getEnumEntries<T>(
60
60
  * For a more in depth explanation, see:
61
61
  * https://isaacscript.github.io/main/gotchas#iterating-over-enums
62
62
  */
63
- export function getEnumKeys<T>(transpiledEnum: T): string[] {
63
+ export function getEnumKeys(
64
+ transpiledEnum: Record<string | number, string | number>,
65
+ ): string[] {
64
66
  const enumEntries = getEnumEntries(transpiledEnum);
65
67
  return enumEntries.map(([key, _value]) => key);
66
68
  }
67
69
 
68
70
  /** Helper function to get the amount of entries inside of an enum. */
69
- export function getEnumLength<T>(transpiledEnum: T): int {
71
+ export function getEnumLength(
72
+ transpiledEnum: Record<string | number, string | number>,
73
+ ): int {
70
74
  const enumEntries = getEnumEntries(transpiledEnum);
71
75
  return enumEntries.length;
72
76
  }
@@ -95,18 +99,17 @@ export function getEnumValues<T>(transpiledEnum: T): Array<T[keyof T]> {
95
99
  /**
96
100
  * Helper function to get the enum value with the highest value.
97
101
  *
98
- * Note that this is not necessarily the enum value that is declared last, since there is no way to
99
- * infer that at run-time.
102
+ * Note that this is not necessarily the enum value that is declared last in the code, since there
103
+ * is no way to infer that at run-time.
100
104
  */
101
105
  export function getHighestEnumValue<T>(transpiledEnum: T): T[keyof T] {
102
106
  const enumValues = getEnumValues(transpiledEnum);
103
107
 
104
108
  const lastElement = enumValues[enumValues.length - 1];
105
- if (lastElement === undefined) {
106
- error(
107
- "Failed to get the last value from an enum since the enum was empty.",
108
- );
109
- }
109
+ assertDefined(
110
+ lastElement,
111
+ "Failed to get the last value from an enum since the enum was empty.",
112
+ );
110
113
 
111
114
  return lastElement;
112
115
  }
@@ -128,6 +131,15 @@ export function getRandomEnumValue<T>(
128
131
  return getRandomArrayElement(enumValues, seedOrRNG, exceptions);
129
132
  }
130
133
 
134
+ /** Helper function to validate that a particular value exists inside of an enum. */
135
+ export function isEnumValue(
136
+ value: number | string,
137
+ transpiledEnum: Record<string | number, string | number>,
138
+ ): boolean {
139
+ const enumValues = getEnumValues(transpiledEnum);
140
+ return enumValues.includes(value);
141
+ }
142
+
131
143
  /**
132
144
  * Helper function to check every value of a custom enum for -1. Will throw an run-time error if any
133
145
  * -1 values are found. This is helpful because many methods of the Isaac class return -1 if they
@@ -167,12 +179,12 @@ export function validateEnumContiguous<T>(
167
179
  ): void {
168
180
  const values = getEnumValues(transpiledEnum);
169
181
  const lastValue = values[values.length - 1];
170
- if (lastValue === undefined) {
171
- error(
172
- "Failed to validate that an enum was contiguous, since the last value was undefined.",
173
- );
174
- }
175
- if (typeof lastValue !== "number") {
182
+ assertDefined(
183
+ lastValue,
184
+ "Failed to validate that an enum was contiguous, since the last value was undefined.",
185
+ );
186
+
187
+ if (!isNumber(lastValue)) {
176
188
  error(
177
189
  "Failed to validate that an enum was contiguous, since the last value was not a number.",
178
190
  );
@@ -23,7 +23,7 @@ import { getEffects } from "./entitiesSpecific";
23
23
  import { isCircleIntersectingRectangle } from "./math";
24
24
  import { roomUpdateSafe } from "./rooms";
25
25
  import { asNumber, isNumber } from "./types";
26
- import { eRange, iRange } from "./utils";
26
+ import { assertDefined, eRange, iRange } from "./utils";
27
27
  import { isVector, vectorEquals } from "./vector";
28
28
 
29
29
  const BREAKABLE_GRID_ENTITY_TYPES_BY_EXPLOSIONS =
@@ -58,11 +58,11 @@ export function convertXMLGridEntityType(
58
58
  gridEntityXMLVariant: int,
59
59
  ): [GridEntityType, int] | undefined {
60
60
  const gridEntityArray = GRID_ENTITY_XML_MAP.get(gridEntityXMLType);
61
- if (gridEntityArray === undefined) {
62
- error(
63
- `Failed to find an entry in the grid entity map for XML entity type: ${gridEntityXMLType}`,
64
- );
65
- }
61
+ assertDefined(
62
+ gridEntityArray,
63
+ `Failed to find an entry in the grid entity map for XML entity type: ${gridEntityXMLType}`,
64
+ );
65
+
66
66
  const gridEntityType = gridEntityArray[0];
67
67
  let variant = gridEntityArray[1];
68
68
 
@@ -165,18 +165,16 @@ export function getConstituentsFromGridEntityID(
165
165
  const [gridEntityTypeString, variantString] = parts;
166
166
 
167
167
  const gridEntityType = tonumber(gridEntityTypeString);
168
- if (gridEntityType === undefined) {
169
- error(
170
- `Failed to convert the grid entity type to a number: ${gridEntityTypeString}`,
171
- );
172
- }
168
+ assertDefined(
169
+ gridEntityType,
170
+ `Failed to convert the grid entity type to a number: ${gridEntityTypeString}`,
171
+ );
173
172
 
174
173
  const variant = tonumber(variantString);
175
- if (variant === undefined) {
176
- error(
177
- `Failed to convert the grid entity variant to a number: ${variantString}`,
178
- );
179
- }
174
+ assertDefined(
175
+ variant,
176
+ `Failed to convert the grid entity variant to a number: ${variantString}`,
177
+ );
180
178
 
181
179
  return [gridEntityType, variant];
182
180
  }