isaacscript-common 6.19.0 → 6.20.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 (43) hide show
  1. package/dist/features/customStage/exports.d.ts.map +1 -1
  2. package/dist/features/customStage/exports.lua +2 -7
  3. package/dist/features/customTrapdoor/init.d.ts.map +1 -1
  4. package/dist/features/customTrapdoor/init.lua +10 -2
  5. package/dist/features/extraConsoleCommands/commandsSubroutines.d.ts.map +1 -1
  6. package/dist/features/extraConsoleCommands/commandsSubroutines.lua +2 -1
  7. package/dist/features/extraConsoleCommands/listCommands.d.ts.map +1 -1
  8. package/dist/features/extraConsoleCommands/listCommands.lua +2 -1
  9. package/dist/functions/curses.d.ts +3 -0
  10. package/dist/functions/curses.d.ts.map +1 -0
  11. package/dist/functions/curses.lua +11 -0
  12. package/dist/functions/dimensions.d.ts +12 -0
  13. package/dist/functions/dimensions.d.ts.map +1 -0
  14. package/dist/functions/dimensions.lua +35 -0
  15. package/dist/functions/level.d.ts.map +1 -1
  16. package/dist/functions/level.lua +8 -7
  17. package/dist/functions/levelGrid.d.ts +152 -0
  18. package/dist/functions/levelGrid.d.ts.map +1 -0
  19. package/dist/functions/levelGrid.lua +326 -0
  20. package/dist/functions/roomData.d.ts +5 -0
  21. package/dist/functions/roomData.d.ts.map +1 -1
  22. package/dist/functions/roomData.lua +6 -0
  23. package/dist/functions/roomGrid.d.ts +8 -0
  24. package/dist/functions/roomGrid.d.ts.map +1 -1
  25. package/dist/functions/rooms.d.ts +39 -61
  26. package/dist/functions/rooms.d.ts.map +1 -1
  27. package/dist/functions/rooms.lua +126 -200
  28. package/dist/index.d.ts +3 -0
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.lua +24 -0
  31. package/package.json +2 -2
  32. package/src/features/customStage/exports.ts +10 -11
  33. package/src/features/customTrapdoor/init.ts +7 -3
  34. package/src/features/extraConsoleCommands/commandsSubroutines.ts +2 -1
  35. package/src/features/extraConsoleCommands/listCommands.ts +2 -1
  36. package/src/functions/curses.ts +9 -0
  37. package/src/functions/dimensions.ts +41 -0
  38. package/src/functions/level.ts +7 -10
  39. package/src/functions/levelGrid.ts +424 -0
  40. package/src/functions/roomData.ts +12 -0
  41. package/src/functions/roomGrid.ts +9 -0
  42. package/src/functions/rooms.ts +90 -206
  43. package/src/index.ts +3 -0
@@ -8,6 +8,7 @@ import {
8
8
  GridRoom,
9
9
  HomeRoomSubType,
10
10
  ItemPoolType,
11
+ LevelCurse,
11
12
  MinibossID,
12
13
  RoomDescriptorFlag,
13
14
  RoomShape,
@@ -17,14 +18,11 @@ import {
17
18
  StageID,
18
19
  } from "isaac-typescript-definitions";
19
20
  import { game, sfxManager } from "../cachedClasses";
20
- import {
21
- LEVEL_GRID_ROW_WIDTH,
22
- MAX_LEVEL_GRID_INDEX,
23
- NUM_DIMENSIONS,
24
- } from "../constants";
25
- import { ROOM_SHAPE_TO_DOOR_SLOTS_TO_GRID_INDEX_DELTA } from "../objects/roomShapeToDoorSlotsToGridIndexDelta";
21
+ import { MAX_LEVEL_GRID_INDEX } from "../constants";
26
22
  import { ROOM_TYPE_NAMES } from "../objects/roomTypeNames";
27
23
  import { MINE_SHAFT_ROOM_SUB_TYPE_SET } from "../sets/mineShaftRoomSubTypesSet";
24
+ import { hasCurse } from "./curses";
25
+ import { inDimension } from "./dimensions";
28
26
  import {
29
27
  closeAllDoors,
30
28
  getDoors,
@@ -40,18 +38,15 @@ import {
40
38
  setEntityVelocities,
41
39
  } from "./positionVelocity";
42
40
  import {
43
- getRoomAllowedDoors,
44
41
  getRoomData,
45
- getRoomDescriptor,
46
42
  getRoomDescriptorReadOnly,
47
43
  getRoomGridIndex,
48
44
  getRoomName,
49
- getRoomShape,
50
45
  getRoomStageID,
51
46
  getRoomSubType,
52
47
  } from "./roomData";
53
- import { getGridIndexDelta } from "./roomShape";
54
- import { erange, irange } from "./utils";
48
+ import { getGotoCommand } from "./stage";
49
+ import { irange } from "./utils";
55
50
 
56
51
  /**
57
52
  * Helper function for quickly switching to a new room without playing a particular animation. Use
@@ -75,73 +70,53 @@ export function changeRoom(roomGridIndex: int): void {
75
70
  game.ChangeRoom(roomGridIndex);
76
71
  }
77
72
 
78
- /**
79
- * Helper function to get an array with every valid `Dimension` (not including `Dimension.CURRENT`).
80
- */
81
- export function getAllDimensions(): Dimension[] {
82
- return erange(NUM_DIMENSIONS) as Dimension[];
83
- }
84
-
85
- /** Helper function to get the grid index for every room on the entire floor. */
86
- export function getAllRoomGridIndexes(): int[] {
87
- const rooms = getRooms();
88
- return rooms.map((roomDescriptor) => roomDescriptor.SafeGridIndex);
89
- }
90
-
91
- /**
92
- * Helper function to get the current dimension. Most of the time, this will be `Dimension.MAIN`,
93
- * but it can change if e.g. the player is in the mirror world of Downpour/Dross.
94
- */
95
- export function getDimension(): Dimension {
96
- const level = game.GetLevel();
97
- const roomGridIndex = getRoomGridIndex();
98
- const roomDescription = level.GetRoomByIdx(roomGridIndex, Dimension.CURRENT);
99
- const currentRoomHash = GetPtrHash(roomDescription);
100
-
101
- for (const dimension of getAllDimensions()) {
102
- const dimensionRoomDescription = level.GetRoomByIdx(
103
- roomGridIndex,
104
- dimension,
105
- );
106
- const dimensionRoomHash = GetPtrHash(dimensionRoomDescription);
107
-
108
- if (dimensionRoomHash === currentRoomHash) {
109
- return dimension;
110
- }
111
- }
112
-
113
- error("Failed to get the current dimension.");
114
- }
115
-
116
73
  /**
117
74
  * Helper function to get the number of rooms that are currently on the floor layout. This does not
118
75
  * include off-grid rooms, like the Devil Room.
119
76
  */
120
77
  export function getNumRooms(): int {
121
- const rooms = getRooms();
78
+ const rooms = getRoomsInGrid();
122
79
  return rooms.length;
123
80
  }
124
81
 
125
82
  /**
126
- * Helper function to get an array of all of the safe grid indexes for rooms that match the
127
- * specified room type.
83
+ * Helper function to get the room data for a specific room type and variant combination. This is
84
+ * accomplished by using the "goto" console command to load the specified room into the
85
+ * `GridRoom.DEBUG` slot.
128
86
  *
129
- * This function only searches through rooms in the current dimension.
87
+ * Note that the side effect of using the "goto" console command is that it will trigger a room
88
+ * transition after a short delay. By default, this function cancels the incoming room transition by
89
+ * using the `Game.StartRoomTransition` method to travel to the same room.
130
90
  *
131
- * This function is variadic, meaning that you can specify N arguments to get the combined grid
132
- * indexes for N room types.
91
+ * @param roomType The type of room to retrieve.
92
+ * @param roomVariant The room variant to retrieve. (The room variant is the "ID" of the room in
93
+ * Basement Renovator.)
94
+ * @param cancelRoomTransition Optional. Whether to cancel the room transition by using the
95
+ * `Game.StartRoomTransition` method to travel to the same room. Default
96
+ * is true. Set this to false if you are getting the data for many rooms
97
+ * at the same time, and then use the `teleport` helper function when
98
+ * you are finished.
133
99
  */
134
- export function getRoomGridIndexesForType(...roomTypes: RoomType[]): int[] {
135
- const roomTypesSet = new Set<RoomType>([...roomTypes]);
136
-
137
- const rooms = getRooms();
138
- const matchingRooms = rooms.filter(
139
- (roomDescriptor) =>
140
- roomDescriptor.Data !== undefined &&
141
- roomTypesSet.has(roomDescriptor.Data.Type),
142
- );
100
+ export function getRoomDataForTypeVariant(
101
+ roomType: RoomType,
102
+ roomVariant: int,
103
+ cancelRoomTransition = true,
104
+ ): Readonly<RoomConfig> | undefined {
105
+ const command = getGotoCommand(roomType, roomVariant);
106
+ Isaac.ExecuteCommand(command);
107
+ const newRoomData = getRoomData(GridRoom.DEBUG);
108
+
109
+ if (cancelRoomTransition) {
110
+ const roomGridIndex = getRoomGridIndex();
111
+ teleport(
112
+ roomGridIndex,
113
+ Direction.NO_DIRECTION,
114
+ RoomTransitionAnim.FADE,
115
+ true,
116
+ );
117
+ }
143
118
 
144
- return matchingRooms.map((roomDescriptor) => roomDescriptor.SafeGridIndex);
119
+ return newRoomData;
145
120
  }
146
121
 
147
122
  /**
@@ -157,40 +132,6 @@ export function getRoomItemPoolType(): ItemPoolType {
157
132
  return itemPool.GetPoolForRoom(roomType, roomSeed);
158
133
  }
159
134
 
160
- /**
161
- * Helper function to get the grid indexes of all the rooms connected to the given room index.
162
- *
163
- * @param roomGridIndex Optional. Default is the current room index.
164
- */
165
- export function getRoomNeighbors(roomGridIndex?: int): int[] {
166
- const roomDescriptor = getRoomDescriptor(roomGridIndex);
167
-
168
- if (
169
- roomDescriptor.SafeGridIndex < 0 ||
170
- roomDescriptor.SafeGridIndex > MAX_LEVEL_GRID_INDEX
171
- ) {
172
- return [];
173
- }
174
-
175
- const roomData = roomDescriptor.Data;
176
- if (roomData === undefined) {
177
- return [];
178
- }
179
-
180
- const roomShape = roomData.Shape;
181
- const gridIndexDeltas = getRoomShapeNeighborGridIndexDeltas(roomShape);
182
- const gridIndexes = gridIndexDeltas.map(
183
- (gridIndexDelta) => roomDescriptor.SafeGridIndex + gridIndexDelta,
184
- );
185
- return gridIndexes.filter((gridIndex) => roomExists(gridIndex));
186
- }
187
-
188
- export function getRoomShapeNeighborGridIndexDeltas(
189
- roomShape: RoomShape,
190
- ): int[] {
191
- return [...ROOM_SHAPE_TO_DOOR_SLOTS_TO_GRID_INDEX_DELTA[roomShape].values()];
192
- }
193
-
194
135
  /**
195
136
  * Helper function to get the proper name of a room type.
196
137
  *
@@ -201,9 +142,12 @@ export function getRoomTypeName(roomType: RoomType): string {
201
142
  }
202
143
 
203
144
  /**
204
- * Helper function to get the room descriptor for every room on the level, including off-grid rooms.
205
- * Uses the `Level.GetRooms` method to accomplish this. Rooms without data are assumed to be
206
- * non-existent and are not added to the list.
145
+ * Helper function to get the room descriptor for every room on the level. This includes off-grid
146
+ * rooms, such as the Devil Room. (Off-grid rooms will only be included if they the data exists,
147
+ * which only usually happens once they have been visited at least once.)
148
+ *
149
+ * Under the hood, this function uses the `Level.GetRooms` method to accomplish this. Rooms without
150
+ * data are assumed to be non-existent and are not added to the list.
207
151
  *
208
152
  * @param includeExtraDimensionalRooms Optional. On some floors (e.g. Downpour 2, Mines 2),
209
153
  * extra-dimensional rooms are automatically generated and can be
@@ -215,21 +159,23 @@ export function getRooms(
215
159
  const level = game.GetLevel();
216
160
  const roomList = level.GetRooms();
217
161
 
162
+ /** Indexed by room safe grid index. We use a map to avoid adding extra dimensional rooms. */
218
163
  const roomsMap = new Map<int, RoomDescriptor>();
219
- if (includeExtraDimensionalRooms) {
220
- for (let i = 0; i < roomList.Size; i++) {
221
- const roomDescriptor = roomList.Get(i);
222
- if (roomDescriptor !== undefined && roomDescriptor.Data !== undefined) {
223
- roomsMap.set(roomDescriptor.ListIndex, roomDescriptor);
224
- }
164
+
165
+ for (let i = 0; i < roomList.Size; i++) {
166
+ const roomDescriptor = roomList.Get(i);
167
+ if (roomDescriptor === undefined || roomDescriptor.Data === undefined) {
168
+ continue;
225
169
  }
226
- } else {
227
- for (const roomGridIndex of irange(MAX_LEVEL_GRID_INDEX)) {
228
- const roomDescriptor = level.GetRoomByIdx(roomGridIndex);
229
- if (roomDescriptor.Data !== undefined) {
230
- roomsMap.set(roomDescriptor.ListIndex, roomDescriptor);
231
- }
170
+
171
+ if (
172
+ !includeExtraDimensionalRooms &&
173
+ roomsMap.has(roomDescriptor.SafeGridIndex)
174
+ ) {
175
+ continue;
232
176
  }
177
+
178
+ roomsMap.set(roomDescriptor.SafeGridIndex, roomDescriptor);
233
179
  }
234
180
 
235
181
  return [...roomsMap.values()];
@@ -237,8 +183,10 @@ export function getRooms(
237
183
 
238
184
  /**
239
185
  * Helper function to get the room descriptor for every room on the level except for rooms that are
240
- * not on the grid. Uses the `Level.GetRooms` method to accomplish this. Rooms without data are
241
- * assumed to be non-existent and are not added to the list.
186
+ * not on the grid.
187
+ *
188
+ * Under the hood, this function uses the `Level.GetRooms` method to accomplish this. Rooms without
189
+ * data are assumed to be non-existent and are not added to the list.
242
190
  *
243
191
  * @param includeExtraDimensionalRooms Optional. On some floors (e.g. Downpour 2, Mines 2),
244
192
  * extra-dimensional rooms are automatically be generated and can be
@@ -253,15 +201,19 @@ export function getRoomsInGrid(
253
201
 
254
202
  /**
255
203
  * Helper function to get the room descriptor for every room on the level in a specific dimension.
256
- * Uses the `Level.GetRooms` method to accomplish this. Rooms without data are assumed to be
257
- * non-existent and are not added to the list.
204
+ * This will not include any off-grid rooms, such as the Devil Room.
205
+ *
206
+ * Under the hood, this function uses the `Level.GetRooms` method to accomplish this. Rooms without
207
+ * data are assumed to be non-existent and are not added to the list.
258
208
  *
259
209
  * @returns A map of room ListIndex to RoomDescriptor.
260
210
  */
261
211
  export function getRoomsOfDimension(dimension: Dimension): RoomDescriptor[] {
262
212
  const level = game.GetLevel();
263
213
 
214
+ /** We use a map instead of an array because room shapes occupy more than one room grid index. */
264
215
  const roomsMap = new Map<int, RoomDescriptor>();
216
+
265
217
  for (const roomGridIndex of irange(MAX_LEVEL_GRID_INDEX)) {
266
218
  const roomDescriptor = level.GetRoomByIdx(roomGridIndex, dimension);
267
219
  if (roomDescriptor.Data !== undefined) {
@@ -361,10 +313,6 @@ export function inDevilsCrownTreasureRoom(): boolean {
361
313
  return hasFlag(roomDescriptor.Flags, RoomDescriptorFlag.DEVIL_TREASURE);
362
314
  }
363
315
 
364
- export function inDimension(dimension: Dimension): boolean {
365
- return dimension === getDimension();
366
- }
367
-
368
316
  export function inDoubleTrouble(): boolean {
369
317
  const room = game.GetRoom();
370
318
  const roomType = room.GetType();
@@ -484,87 +432,6 @@ export function isAllRoomsClear(onlyCheckRoomTypes?: RoomType[]): boolean {
484
432
  return matchingRooms.every((roomDescriptor) => roomDescriptor.Clear);
485
433
  }
486
434
 
487
- export function isDoorSlotValidAtGridIndex(
488
- doorSlot: DoorSlot,
489
- roomGridIndex: int,
490
- ): boolean {
491
- const allowedDoors = getRoomAllowedDoors(roomGridIndex);
492
- return allowedDoors.has(doorSlot);
493
- }
494
-
495
- export function isDoorSlotValidAtGridIndexForRedRoom(
496
- doorSlot: DoorSlot,
497
- roomGridIndex: int,
498
- ): boolean {
499
- const doorSlotValidAtGridIndex = isDoorSlotValidAtGridIndex(
500
- doorSlot,
501
- roomGridIndex,
502
- );
503
- if (!doorSlotValidAtGridIndex) {
504
- return false;
505
- }
506
-
507
- const roomShape = getRoomShape(roomGridIndex);
508
- if (roomShape === undefined) {
509
- return false;
510
- }
511
-
512
- const delta = getGridIndexDelta(roomShape, doorSlot);
513
- if (delta === undefined) {
514
- return false;
515
- }
516
-
517
- const redRoomGridIndex = roomGridIndex + delta;
518
- return (
519
- !roomExists(redRoomGridIndex) &&
520
- redRoomGridIndex >= 0 &&
521
- redRoomGridIndex <= MAX_LEVEL_GRID_INDEX
522
- );
523
- }
524
-
525
- /**
526
- * Helper function to detect if the provided room was created by the Red Key item. Under the hood,
527
- * this checks for the `RoomDescriptorFlag.FLAG_RED_ROOM` flag.
528
- *
529
- * @param roomGridIndex Optional. Default is the current room index.
530
- */
531
- export function isRedKeyRoom(roomGridIndex?: int): boolean {
532
- const roomDescriptor = getRoomDescriptor(roomGridIndex);
533
- return hasFlag(roomDescriptor.Flags, RoomDescriptorFlag.RED_ROOM);
534
- }
535
-
536
- /**
537
- * Helper function to determine if the provided room is part of the floor layout. For example, Devil
538
- * Rooms and the Mega Satan room are not considered to be inside the map.
539
- *
540
- * @param roomGridIndex Optional. Default is the current room index.
541
- */
542
- export function isRoomInsideMap(roomGridIndex?: int): boolean {
543
- if (roomGridIndex === undefined) {
544
- roomGridIndex = getRoomGridIndex();
545
- }
546
-
547
- return roomGridIndex >= 0;
548
- }
549
-
550
- /** Helper function to check if a room exists at the given room grid index. */
551
- export function roomExists(roomGridIndex: int): boolean {
552
- const roomData = getRoomData(roomGridIndex);
553
- return roomData !== undefined;
554
- }
555
-
556
- /**
557
- * Helper function to get the coordinates of a given grid index. The floor is represented by a 13x13
558
- * grid. For example, since the starting room is in the center, the starting room grid index of 84
559
- * be equal to coordinates of (?, ?).
560
- */
561
- export function roomGridIndexToXY(roomGridIndex: int): [x: int, y: int] {
562
- const x = roomGridIndex % LEVEL_GRID_ROW_WIDTH;
563
- const y = Math.floor(roomGridIndex / LEVEL_GRID_ROW_WIDTH);
564
-
565
- return [x, y];
566
- }
567
-
568
435
  /**
569
436
  * If the `Room.Update` method is called in a `POST_NEW_ROOM` callback, then some entities will
570
437
  * slide around (such as the player). Since those entity velocities are already at zero, setting
@@ -635,21 +502,34 @@ export function setRoomUncleared(): void {
635
502
  /**
636
503
  * Helper function to change the current room. It can be used for both teleportation and "normal"
637
504
  * room transitions, depending on what is passed for the `direction` and `roomTransitionAnim`
638
- * arguments. Use this function instead of invoking the `Game.StartRoomTransition` method directly
639
- * so that you do not forget to set `Level.LeaveDoor` property and to prevent crashing on invalid
640
- * room grid indexes.
505
+ * arguments.
506
+ *
507
+ * Use this function instead of invoking the `Game.StartRoomTransition` method directly so that:
508
+ * - you do not forget to set `Level.LeaveDoor` property
509
+ * - to prevent crashing on invalid room grid indexes
510
+ * - to automatically handle Curse of the Maze
641
511
  *
642
512
  * @param roomGridIndex The room grid index of the destination room.
643
513
  * @param direction Optional. Default is `Direction.NO_DIRECTION`.
644
514
  * @param roomTransitionAnim Optional. Default is `RoomTransitionAnim.TELEPORT`.
515
+ * @param force Optional. Whether to temporarily disable Curse of the Maze. Default is false. If set
516
+ * to false, then this function may not go to the provided room grid index.
645
517
  */
646
518
  export function teleport(
647
519
  roomGridIndex: int,
648
520
  direction = Direction.NO_DIRECTION,
649
521
  roomTransitionAnim = RoomTransitionAnim.TELEPORT,
522
+ force = false,
650
523
  ): void {
651
524
  const level = game.GetLevel();
652
525
 
526
+ // Before starting a room transition, we must ensure that Curse of the Maze is not in effect, or
527
+ // else the room transition might send us to the wrong room.
528
+ const shouldTempDisableCurse = force && hasCurse(LevelCurse.MAZE);
529
+ if (shouldTempDisableCurse) {
530
+ level.RemoveCurses(LevelCurse.MAZE);
531
+ }
532
+
653
533
  const roomData = getRoomData(roomGridIndex);
654
534
  if (roomData === undefined) {
655
535
  error(
@@ -662,4 +542,8 @@ export function teleport(
662
542
  level.LeaveDoor = DoorSlot.NO_DOOR_SLOT;
663
543
 
664
544
  game.StartRoomTransition(roomGridIndex, direction, roomTransitionAnim);
545
+
546
+ if (shouldTempDisableCurse) {
547
+ level.AddCurse(LevelCurse.MAZE, false);
548
+ }
665
549
  }
package/src/index.ts CHANGED
@@ -97,9 +97,11 @@ export * from "./functions/collectibles";
97
97
  export * from "./functions/collectibleSet";
98
98
  export * from "./functions/collectibleTag";
99
99
  export * from "./functions/color";
100
+ export * from "./functions/curses";
100
101
  export * from "./functions/debug";
101
102
  export * from "./functions/deepCopy";
102
103
  export * from "./functions/deepCopyTests";
104
+ export * from "./functions/dimensions";
103
105
  export * from "./functions/direction";
104
106
  export * from "./functions/doors";
105
107
  export * from "./functions/easing";
@@ -123,6 +125,7 @@ export * from "./functions/jsonRoom";
123
125
  export * from "./functions/kColor";
124
126
  export * from "./functions/language";
125
127
  export * from "./functions/level";
128
+ export * from "./functions/levelGrid";
126
129
  export * from "./functions/log";
127
130
  export * from "./functions/map";
128
131
  export * from "./functions/math";