isaacscript-common 80.2.1 → 80.2.3

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 (65) hide show
  1. package/dist/classes/ModUpgraded.d.ts.map +1 -1
  2. package/dist/classes/ModUpgraded.lua +18 -8
  3. package/dist/classes/features/other/DeployJSONRoom.lua +11 -10
  4. package/dist/classes/features/other/extraConsoleCommands/commands.d.ts +1 -1
  5. package/dist/classes/features/other/extraConsoleCommands/commands.d.ts.map +1 -1
  6. package/dist/classes/features/other/extraConsoleCommands/commands.lua +104 -102
  7. package/dist/classes/features/other/extraConsoleCommands/subroutines.d.ts.map +1 -1
  8. package/dist/classes/features/other/extraConsoleCommands/subroutines.lua +6 -4
  9. package/dist/core/constantsFirstLast.d.ts +4 -26
  10. package/dist/core/constantsFirstLast.d.ts.map +1 -1
  11. package/dist/core/constantsFirstLast.lua +0 -18
  12. package/dist/functions/cards.d.ts +1 -0
  13. package/dist/functions/cards.d.ts.map +1 -1
  14. package/dist/functions/cards.lua +7 -0
  15. package/dist/functions/collectibles.d.ts +1 -1
  16. package/dist/functions/collectibles.d.ts.map +1 -1
  17. package/dist/functions/collectibles.lua +3 -1
  18. package/dist/functions/doors.d.ts.map +1 -1
  19. package/dist/functions/doors.lua +14 -4
  20. package/dist/functions/entities.d.ts.map +1 -1
  21. package/dist/functions/entities.lua +19 -30
  22. package/dist/functions/gridEntities.d.ts.map +1 -1
  23. package/dist/functions/gridEntities.lua +8 -13
  24. package/dist/functions/jsonRoom.d.ts.map +1 -1
  25. package/dist/functions/jsonRoom.lua +13 -5
  26. package/dist/functions/pills.d.ts +1 -0
  27. package/dist/functions/pills.d.ts.map +1 -1
  28. package/dist/functions/pills.lua +6 -0
  29. package/dist/functions/run.d.ts +1 -2
  30. package/dist/functions/run.d.ts.map +1 -1
  31. package/dist/functions/run.lua +2 -3
  32. package/dist/functions/string.d.ts.map +1 -1
  33. package/dist/functions/string.lua +5 -3
  34. package/dist/functions/table.d.ts +3 -2
  35. package/dist/functions/table.d.ts.map +1 -1
  36. package/dist/functions/table.lua +3 -2
  37. package/dist/functions/trinkets.d.ts +1 -0
  38. package/dist/functions/trinkets.d.ts.map +1 -1
  39. package/dist/functions/trinkets.lua +6 -0
  40. package/dist/functions/types.d.ts +9 -0
  41. package/dist/functions/types.d.ts.map +1 -1
  42. package/dist/functions/types.lua +17 -0
  43. package/dist/index.rollup.d.ts +23 -32
  44. package/dist/isaacscript-common.lua +254 -207
  45. package/dist/lualib_bundle.lua +5 -0
  46. package/dist/objects/itemPoolTypeToCollectibleTypesSet.lua +4 -3
  47. package/package.json +2 -2
  48. package/src/classes/ModUpgraded.ts +18 -4
  49. package/src/classes/features/other/DeployJSONRoom.ts +11 -11
  50. package/src/classes/features/other/extraConsoleCommands/commands.ts +124 -118
  51. package/src/classes/features/other/extraConsoleCommands/subroutines.ts +5 -4
  52. package/src/core/constantsFirstLast.ts +3 -37
  53. package/src/functions/cards.ts +7 -0
  54. package/src/functions/collectibles.ts +5 -4
  55. package/src/functions/doors.ts +8 -3
  56. package/src/functions/entities.ts +40 -19
  57. package/src/functions/gridEntities.ts +15 -5
  58. package/src/functions/jsonRoom.ts +18 -5
  59. package/src/functions/pills.ts +7 -1
  60. package/src/functions/run.ts +6 -4
  61. package/src/functions/string.ts +4 -3
  62. package/src/functions/table.ts +3 -2
  63. package/src/functions/trinkets.ts +9 -1
  64. package/src/functions/types.ts +25 -0
  65. package/src/objects/itemPoolTypeToCollectibleTypesSet.ts +4 -4
@@ -8,11 +8,9 @@ import {
8
8
  CardType,
9
9
  Challenge,
10
10
  CollectibleType,
11
- LevelStage,
12
11
  PillColor,
13
12
  PillEffect,
14
13
  PlayerType,
15
- RoomType,
16
14
  TrinketType,
17
15
  } from "isaac-typescript-definitions";
18
16
  import { getEnumLength, getHighestEnumValue } from "../functions/enums";
@@ -62,13 +60,7 @@ export const NUM_VANILLA_TRINKET_TYPES = getEnumLength(TrinketType) - 1;
62
60
  /** Equal to `Card.FOOL`. */
63
61
  export const FIRST_CARD_TYPE = CardType.FOOL;
64
62
 
65
- /**
66
- * Calculated from the `CardType` enum.
67
- *
68
- * Note that this could be calculated from the length of the enum, because card types are
69
- * contiguous. However, we instead get the highest enum value to be safer and to make the code more
70
- * consistent with collectibles and trinkets.
71
- */
63
+ /** Calculated from the `CardType` enum. */
72
64
  export const LAST_VANILLA_CARD_TYPE = getHighestEnumValue(CardType);
73
65
 
74
66
  /** Calculated from the `Card` enum. `Card.NULL` is not included. */
@@ -81,13 +73,7 @@ export const NUM_VANILLA_CARD_TYPES = getEnumLength(CardType) - 1;
81
73
  /** Equal to `PillEffect.BAD_GAS`. */
82
74
  export const FIRST_PILL_EFFECT = PillEffect.BAD_GAS;
83
75
 
84
- /**
85
- * Calculated from the `PillEffect` enum.
86
- *
87
- * Note that this could be calculated from the length of the enum, because pill effects are
88
- * contiguous. However, we instead get the highest enum value to be safer and to make the code more
89
- * consistent with collectibles and trinkets.
90
- */
76
+ /** Calculated from the `PillEffect` enum. */
91
77
  export const LAST_VANILLA_PILL_EFFECT = getHighestEnumValue(PillEffect);
92
78
 
93
79
  /**
@@ -136,29 +122,9 @@ export const FIRST_CHARACTER = PlayerType.ISAAC;
136
122
 
137
123
  // It is not possible to determine "LAST_PLAYER_TYPE", since there is no associated config.
138
124
 
139
- /**
140
- * Calculated from the `PlayerType` enum.
141
- *
142
- * Note that this could be calculated from the length of the enum, because characters are
143
- * contiguous. However, we instead get the highest enum value to be safer and to make the code more
144
- * consistent with collectibles and trinkets.
145
- */
125
+ /** Calculated from the `PlayerType` enum. */
146
126
  export const LAST_VANILLA_CHARACTER = getHighestEnumValue(PlayerType);
147
127
 
148
- // ----------
149
- // Room Types
150
- // ----------
151
-
152
- export const FIRST_ROOM_TYPE = RoomType.DEFAULT;
153
- export const LAST_ROOM_TYPE = getHighestEnumValue(RoomType);
154
-
155
- // ------
156
- // Stages
157
- // ------
158
-
159
- export const FIRST_STAGE = LevelStage.BASEMENT_1;
160
- export const LAST_STAGE = getHighestEnumValue(LevelStage);
161
-
162
128
  // ----------
163
129
  // Challenges
164
130
  // ----------
@@ -10,6 +10,7 @@ import {
10
10
  import { CARD_NAMES, DEFAULT_CARD_NAME } from "../objects/cardNames";
11
11
  import { ITEM_CONFIG_CARD_TYPES_FOR_CARDS } from "../sets/itemConfigCardTypesForCards";
12
12
  import { addFlag } from "./flag";
13
+ import { asCardType } from "./types";
13
14
 
14
15
  /**
15
16
  * Helper function to get a card description from a `CardType` value. Returns "Unknown" if the
@@ -158,6 +159,12 @@ export function isTarotCard(cardType: CardType): boolean {
158
159
  return isCardType(cardType, ItemConfigCardType.TAROT);
159
160
  }
160
161
 
162
+ export function isValidCardType(cardType: int): cardType is CardType {
163
+ const potentialCardType = asCardType(cardType);
164
+ const itemConfigCard = itemConfig.GetCard(potentialCardType);
165
+ return itemConfigCard !== undefined;
166
+ }
167
+
161
168
  export function isVanillaCardType(cardType: CardType): boolean {
162
169
  return cardType <= LAST_VANILLA_CARD_TYPE;
163
170
  }
@@ -35,7 +35,7 @@ import { getEntityID } from "./entities";
35
35
  import { hasFlag } from "./flag";
36
36
  import { isCollectible } from "./pickupVariants";
37
37
  import { clearSprite, spriteEquals } from "./sprites";
38
- import { isInteger } from "./types";
38
+ import { asCollectibleType, isInteger } from "./types";
39
39
  import { assertDefined } from "./utils";
40
40
 
41
41
  const COLLECTIBLE_ANM2_PATH = "gfx/005.100_collectible.anm2";
@@ -569,9 +569,10 @@ export function isSingleUseCollectible(
569
569
  }
570
570
 
571
571
  export function isValidCollectibleType(
572
- collectibleType: CollectibleType,
573
- ): boolean {
574
- const itemConfigItem = itemConfig.GetCollectible(collectibleType);
572
+ collectibleType: int,
573
+ ): collectibleType is CollectibleType {
574
+ const potentialCollectibleType = asCollectibleType(collectibleType);
575
+ const itemConfigItem = itemConfig.GetCollectible(potentialCollectibleType);
575
576
  return itemConfigItem !== undefined;
576
577
  }
577
578
 
@@ -29,8 +29,10 @@ import { ROOM_SHAPE_TO_DOOR_SLOTS } from "../objects/roomShapeToDoorSlots";
29
29
  import { ReadonlySet } from "../types/ReadonlySet";
30
30
  import { arrayToBitFlags } from "./bitwise";
31
31
  import { directionToVector } from "./direction";
32
+ import { isEnumValue } from "./enums";
32
33
  import { hasFlag } from "./flag";
33
34
  import { isTSTLSet } from "./tstlClass";
35
+ import { parseIntSafe } from "./types";
34
36
 
35
37
  export function closeAllDoors(): void {
36
38
  for (const door of getDoors()) {
@@ -278,7 +280,6 @@ export function getRoomShapeDoorSlot(
278
280
  x: int,
279
281
  y: int,
280
282
  ): DoorSlot | undefined {
281
- // The type assertion is necessary for some reason.
282
283
  const doorSlotCoordinates = ROOM_SHAPE_TO_DOOR_SLOT_COORDINATES[
283
284
  roomShape
284
285
  ] as Record<DoorSlot, readonly [x: int, y: int]>;
@@ -286,7 +287,11 @@ export function getRoomShapeDoorSlot(
286
287
  for (const [doorSlotString, coordinates] of Object.entries(
287
288
  doorSlotCoordinates,
288
289
  )) {
289
- const doorSlot = tonumber(doorSlotString) as DoorSlot;
290
+ const doorSlot = parseIntSafe(doorSlotString);
291
+ if (doorSlot === undefined || !isEnumValue(doorSlot, DoorSlot)) {
292
+ continue;
293
+ }
294
+
290
295
  const [doorX, doorY] = coordinates;
291
296
  if (x === doorX && y === doorY) {
292
297
  return doorSlot;
@@ -304,10 +309,10 @@ export function getRoomShapeDoorSlotCoordinates(
304
309
  roomShape: RoomShape,
305
310
  doorSlot: DoorSlot,
306
311
  ): readonly [x: int, y: int] | undefined {
307
- // The type assertion is necessary for some reason.
308
312
  const doorSlotCoordinates = ROOM_SHAPE_TO_DOOR_SLOT_COORDINATES[
309
313
  roomShape
310
314
  ] as Record<DoorSlot, readonly [x: int, y: int]>;
315
+
311
316
  return doorSlotCoordinates[doorSlot];
312
317
  }
313
318
 
@@ -18,7 +18,7 @@ import { newReadonlyColor } from "./readOnly";
18
18
  import { getRandomSeed, isRNG, newRNG } from "./rng";
19
19
  import { setSpriteOpacity } from "./sprites";
20
20
  import { isTSTLSet } from "./tstlClass";
21
- import { asNPCState, isPrimitive } from "./types";
21
+ import { asNPCState, isPrimitive, parseIntSafe } from "./types";
22
22
  import { assertDefined } from "./utils";
23
23
  import { doesVectorHaveLength, isVector, vectorToString } from "./vector";
24
24
 
@@ -173,22 +173,37 @@ export function getConstituentsFromEntityID(
173
173
 
174
174
  const [entityTypeString, variantString, subTypeString] = parts;
175
175
 
176
- const entityType = tonumber(entityTypeString);
176
+ assertDefined(
177
+ entityTypeString,
178
+ `Failed to get the first constituent from an entity ID: ${entityID}`,
179
+ );
180
+
181
+ assertDefined(
182
+ variantString,
183
+ `Failed to get the second constituent from an entity ID: ${entityID}`,
184
+ );
185
+
186
+ assertDefined(
187
+ subTypeString,
188
+ `Failed to get the third constituent from an entity ID: ${entityID}`,
189
+ );
190
+
191
+ const entityType = parseIntSafe(entityTypeString);
177
192
  assertDefined(
178
193
  entityType,
179
- `Failed to convert the entity type to a number: ${entityTypeString}`,
194
+ `Failed to convert the entity type to an integer: ${entityTypeString}`,
180
195
  );
181
196
 
182
- const variant = tonumber(variantString);
197
+ const variant = parseIntSafe(variantString);
183
198
  assertDefined(
184
199
  variant,
185
- `Failed to convert the entity variant to a number: ${variantString}`,
200
+ `Failed to convert the entity variant to an integer: ${variantString}`,
186
201
  );
187
202
 
188
- const subType = tonumber(subTypeString);
203
+ const subType = parseIntSafe(subTypeString);
189
204
  assertDefined(
190
205
  subType,
191
- `Failed to convert the entity sub-type to a number: ${subTypeString}`,
206
+ `Failed to convert the entity sub-type to an integer: ${subTypeString}`,
192
207
  );
193
208
 
194
209
  return [entityType, variant, subType];
@@ -464,18 +479,23 @@ export function parseEntityID(
464
479
 
465
480
  const [entityTypeString, variantString, subTypeString] = entityIDArray;
466
481
 
467
- const entityType = tonumber(entityTypeString);
468
- if (entityType === undefined) {
482
+ if (
483
+ entityTypeString === undefined ||
484
+ variantString === undefined ||
485
+ subTypeString === undefined
486
+ ) {
469
487
  return undefined;
470
488
  }
471
489
 
472
- const variant = tonumber(variantString);
473
- if (variant === undefined) {
474
- return undefined;
475
- }
490
+ const entityType = parseIntSafe(entityTypeString);
491
+ const variant = parseIntSafe(variantString);
492
+ const subType = parseIntSafe(subTypeString);
476
493
 
477
- const subType = tonumber(subTypeString);
478
- if (subType === undefined) {
494
+ if (
495
+ entityType === undefined ||
496
+ variant === undefined ||
497
+ subType === undefined
498
+ ) {
479
499
  return undefined;
480
500
  }
481
501
 
@@ -500,13 +520,14 @@ export function parseEntityTypeVariantString(
500
520
 
501
521
  const [entityTypeString, variantString] = entityTypeVariantArray;
502
522
 
503
- const entityType = tonumber(entityTypeString);
504
- if (entityType === undefined) {
523
+ if (entityTypeString === undefined || variantString === undefined) {
505
524
  return undefined;
506
525
  }
507
526
 
508
- const variant = tonumber(variantString);
509
- if (variant === undefined) {
527
+ const entityType = parseIntSafe(entityTypeString);
528
+ const variant = parseIntSafe(variantString);
529
+
530
+ if (entityType === undefined || variant === undefined) {
510
531
  return undefined;
511
532
  }
512
533
 
@@ -26,7 +26,7 @@ import { removeEntities } from "./entities";
26
26
  import { getEffects } from "./entitiesSpecific";
27
27
  import { isCircleIntersectingRectangle } from "./math";
28
28
  import { roomUpdateSafe } from "./rooms";
29
- import { isInteger } from "./types";
29
+ import { isInteger, parseIntSafe } from "./types";
30
30
  import { assertDefined, eRange, iRange } from "./utils";
31
31
  import { isVector, vectorEquals } from "./vector";
32
32
 
@@ -169,22 +169,32 @@ export function getConstituentsFromGridEntityID(
169
169
  const parts = gridEntityID.split(".");
170
170
  if (parts.length !== 2) {
171
171
  error(
172
- `Failed to get the constituents from grid entity ID: ${gridEntityID}`,
172
+ `Failed to get the constituents from a grid entity ID: ${gridEntityID}`,
173
173
  );
174
174
  }
175
175
 
176
176
  const [gridEntityTypeString, variantString] = parts;
177
177
 
178
- const gridEntityType = tonumber(gridEntityTypeString);
178
+ assertDefined(
179
+ gridEntityTypeString,
180
+ `Failed to get the first constituent from a grid entity ID: ${gridEntityID}`,
181
+ );
182
+
183
+ assertDefined(
184
+ variantString,
185
+ `Failed to get the second constituent from a grid entity ID: ${gridEntityID}`,
186
+ );
187
+
188
+ const gridEntityType = parseIntSafe(gridEntityTypeString);
179
189
  assertDefined(
180
190
  gridEntityType,
181
191
  `Failed to convert the grid entity type to a number: ${gridEntityTypeString}`,
182
192
  );
183
193
 
184
- const variant = tonumber(variantString);
194
+ const variant = parseIntSafe(variantString);
185
195
  assertDefined(
186
196
  variant,
187
- `Failed to convert the grid entity variant to a number: ${variantString}`,
197
+ `Failed to convert the grid entity variant to an integer: ${variantString}`,
188
198
  );
189
199
 
190
200
  return [gridEntityType, variant];
@@ -7,6 +7,7 @@ import { isEnumValue } from "./enums";
7
7
  import { addFlag } from "./flag";
8
8
  import { log } from "./log";
9
9
  import { getRandomFloat } from "./random";
10
+ import { parseIntSafe } from "./types";
10
11
  import { assertDefined } from "./utils";
11
12
 
12
13
  /** This represents either a `JSONRoom` or a `JSONEntity`. */
@@ -24,7 +25,7 @@ export function getJSONRoomDoorSlotFlags(
24
25
  jsonRoom: JSONRoom,
25
26
  ): BitFlags<DoorSlotFlag> {
26
27
  const roomShapeString = jsonRoom.$.shape;
27
- const roomShape = tonumber(roomShapeString);
28
+ const roomShape = parseIntSafe(roomShapeString);
28
29
  assertDefined(
29
30
  roomShape,
30
31
  `Failed to parse the "shape" field of a JSON room: ${roomShapeString}`,
@@ -51,14 +52,14 @@ export function getJSONRoomDoorSlotFlags(
51
52
  }
52
53
 
53
54
  const xString = door.$.x;
54
- const x = tonumber(xString);
55
+ const x = parseIntSafe(xString);
55
56
  assertDefined(
56
57
  x,
57
58
  `Failed to parse the "x" field of a JSON room door: ${xString}`,
58
59
  );
59
60
 
60
61
  const yString = door.$.y;
61
- const y = tonumber(yString);
62
+ const y = parseIntSafe(yString);
62
63
  assertDefined(
63
64
  y,
64
65
  `Failed to parse the "y" field of a JSON room door: ${yString}`,
@@ -92,7 +93,13 @@ export function getJSONRoomOfVariant(
92
93
  ): JSONRoom | undefined {
93
94
  const jsonRoomsOfVariant = jsonRooms.filter((jsonRoom) => {
94
95
  const roomVariantString = jsonRoom.$.variant;
95
- const roomVariant = tonumber(roomVariantString);
96
+ const roomVariant = parseIntSafe(roomVariantString);
97
+ if (roomVariant === undefined) {
98
+ error(
99
+ `Failed to convert a JSON room variant to an integer: ${roomVariantString}`,
100
+ );
101
+ }
102
+
96
103
  return roomVariant === variant;
97
104
  });
98
105
 
@@ -125,7 +132,13 @@ export function getJSONRoomsOfSubType(
125
132
  ): JSONRoom[] {
126
133
  return jsonRooms.filter((jsonRoom) => {
127
134
  const roomSubTypeString = jsonRoom.$.subtype;
128
- const roomSubType = tonumber(roomSubTypeString);
135
+ const roomSubType = parseIntSafe(roomSubTypeString);
136
+ if (roomSubType === undefined) {
137
+ error(
138
+ `Failed to convert a JSON room sub-type to an integer: ${roomSubTypeString}`,
139
+ );
140
+ }
141
+
129
142
  return roomSubType === subType;
130
143
  });
131
144
  }
@@ -28,7 +28,7 @@ import {
28
28
  DEFAULT_PILL_EFFECT_TYPE,
29
29
  PILL_EFFECT_TYPES,
30
30
  } from "../objects/pillEffectTypes";
31
- import { asNumber, asPillColor } from "./types";
31
+ import { asNumber, asPillColor, asPillEffect } from "./types";
32
32
  import { iRange } from "./utils";
33
33
 
34
34
  /**
@@ -214,6 +214,12 @@ export function isModdedPillEffect(pillEffect: PillEffect): boolean {
214
214
  return !isVanillaPillEffect(pillEffect);
215
215
  }
216
216
 
217
+ export function isValidPillEffect(pillEffect: int): pillEffect is PillEffect {
218
+ const potentialPillEffect = asPillEffect(pillEffect);
219
+ const itemConfigPillEffect = itemConfig.GetPillEffect(potentialPillEffect);
220
+ return itemConfigPillEffect !== undefined;
221
+ }
222
+
217
223
  export function isVanillaPillEffect(pillEffect: PillEffect): boolean {
218
224
  return pillEffect <= LAST_VANILLA_PILL_EFFECT;
219
225
  }
@@ -1,8 +1,10 @@
1
- import type { PlayerType } from "isaac-typescript-definitions";
2
- import { Challenge, SeedEffect } from "isaac-typescript-definitions";
1
+ import {
2
+ Challenge,
3
+ PlayerType,
4
+ SeedEffect,
5
+ } from "isaac-typescript-definitions";
3
6
  import { SEED_EFFECTS } from "../arrays/cachedEnumValues";
4
7
  import { game } from "../core/cachedClasses";
5
- import { FIRST_CHARACTER } from "../core/constantsFirstLast";
6
8
  import { getCharacterName } from "./characters";
7
9
  import { log } from "./log";
8
10
  import { isString } from "./types";
@@ -84,7 +86,7 @@ export function restart(character?: PlayerType): void {
84
86
  return;
85
87
  }
86
88
 
87
- if (character < FIRST_CHARACTER) {
89
+ if (character < PlayerType.ISAAC) {
88
90
  error(`Restarting as a character of ${character} would crash the game.`);
89
91
  }
90
92
 
@@ -1,3 +1,4 @@
1
+ import { parseIntSafe } from "./types";
1
2
  import { assertDefined } from "./utils";
2
3
 
3
4
  export function capitalizeFirstLetter(string: string): string {
@@ -158,9 +159,9 @@ export function parseSemanticVersion(versionString: string):
158
159
  return undefined;
159
160
  }
160
161
 
161
- const majorVersion = tonumber(majorVersionString);
162
- const minorVersion = tonumber(minorVersionString);
163
- const patchVersion = tonumber(patchVersionString);
162
+ const majorVersion = parseIntSafe(majorVersionString);
163
+ const minorVersion = parseIntSafe(minorVersionString);
164
+ const patchVersion = parseIntSafe(patchVersionString);
164
165
 
165
166
  if (
166
167
  majorVersion === undefined ||
@@ -66,8 +66,9 @@ export function getBooleansFromTable(
66
66
  }
67
67
 
68
68
  /**
69
- * Helper function to safely get number values from specific keys on a Lua table. Will throw an
70
- * error if the specific value does not exist on the table or if it cannot be converted to a number.
69
+ * Helper function to safely get number values from specific keys on a Lua table. If the values are
70
+ * strings, they will be converted to numbers. Will throw an error if the specific value does not
71
+ * exist on the table or if it cannot be converted to a number.
71
72
  *
72
73
  * This function is variadic, meaning that you can specify N arguments to get N values.
73
74
  */
@@ -14,7 +14,7 @@ import { getEnumLength } from "./enums";
14
14
  import { hasFlag } from "./flag";
15
15
  import { isTrinket } from "./pickupVariants";
16
16
  import { clearSprite } from "./sprites";
17
- import { asNumber } from "./types";
17
+ import { asNumber, asTrinketType } from "./types";
18
18
 
19
19
  /**
20
20
  * Add this to a `TrinketType` to get the corresponding golden trinket type.
@@ -165,6 +165,14 @@ export function isModdedTrinketType(trinketType: TrinketType): boolean {
165
165
  return !isVanillaTrinketType(trinketType);
166
166
  }
167
167
 
168
+ export function isValidTrinketType(
169
+ trinketType: int,
170
+ ): trinketType is TrinketType {
171
+ const potentialTrinketType = asTrinketType(trinketType);
172
+ const itemConfigItem = itemConfig.GetTrinket(potentialTrinketType);
173
+ return itemConfigItem !== undefined;
174
+ }
175
+
168
176
  export function isVanillaTrinketType(trinketType: TrinketType): boolean {
169
177
  return trinketType <= LAST_VANILLA_TRINKET_TYPE;
170
178
  }
@@ -216,3 +216,28 @@ export function isTable(
216
216
  export function isUserdata(variable: unknown): variable is LuaUserdata {
217
217
  return type(variable) === "userdata";
218
218
  }
219
+
220
+ /**
221
+ * Helper function to convert a string to an integer. Returns undefined if the string is not an
222
+ * integer.
223
+ *
224
+ * Under the hood, this uses the built-in `tonumber` and `math.floor` functions.
225
+ *
226
+ * This is named `parseIntSafe` in order to match the helper function in `isaacscript-common-ts`.
227
+ */
228
+ export function parseIntSafe(string: string): int | undefined {
229
+ if (!isString(string)) {
230
+ return undefined;
231
+ }
232
+
233
+ // - The `tonumber` function correctly deals with leading and trailing whitespace.
234
+ // - The `tonumber` function correctly deals with a mix of numbers and letters. (e.g. `1a` returns
235
+ // undefined.)
236
+ const number = tonumber(string);
237
+ if (number === undefined) {
238
+ return undefined;
239
+ }
240
+
241
+ const flooredNumber = math.floor(number);
242
+ return number === flooredNumber ? flooredNumber : undefined;
243
+ }
@@ -4,7 +4,7 @@ import type {
4
4
  } from "isaac-typescript-definitions";
5
5
  import { ITEM_POOL_TYPE_VALUES } from "../arrays/cachedEnumValues";
6
6
  import * as itemPoolsJSON from "../data/itempools.json";
7
- import { asCollectibleType } from "../functions/types";
7
+ import { asCollectibleType, parseIntSafe } from "../functions/types";
8
8
  import { ITEM_POOL_TYPE_TO_ITEM_POOL_NAME } from "../maps/itemPoolTypeToItemPoolName";
9
9
 
10
10
  export const ITEM_POOL_TYPE_TO_COLLECTIBLE_TYPES_SET: Readonly<
@@ -22,14 +22,14 @@ export const ITEM_POOL_TYPE_TO_COLLECTIBLE_TYPES_SET: Readonly<
22
22
  const collectibleTypesSet = new Set<CollectibleType>();
23
23
 
24
24
  for (const itemPoolJSONElement of itemPoolJSON.Item) {
25
- const collectibleTypeNumber = tonumber(itemPoolJSONElement.$.Id);
26
- if (collectibleTypeNumber === undefined) {
25
+ const collectibleTypeInt = parseIntSafe(itemPoolJSONElement.$.Id);
26
+ if (collectibleTypeInt === undefined) {
27
27
  error(
28
28
  `Failed to parse a collectible type in the "itempools.json" file: ${itemPoolJSONElement.$.Id}`,
29
29
  );
30
30
  }
31
31
 
32
- const collectibleType = asCollectibleType(collectibleTypeNumber);
32
+ const collectibleType = asCollectibleType(collectibleTypeInt);
33
33
  collectibleTypesSet.add(collectibleType);
34
34
  }
35
35