isaacscript-common 6.18.0 → 6.20.1
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.
- package/dist/constants.d.ts +5 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.lua +4 -0
- package/dist/features/customStage/exports.d.ts.map +1 -1
- package/dist/features/customStage/exports.lua +2 -7
- package/dist/features/customTrapdoor/init.d.ts.map +1 -1
- package/dist/features/customTrapdoor/init.lua +10 -2
- package/dist/features/extraConsoleCommands/commandsSubroutines.d.ts.map +1 -1
- package/dist/features/extraConsoleCommands/commandsSubroutines.lua +2 -1
- package/dist/features/extraConsoleCommands/listCommands.d.ts.map +1 -1
- package/dist/features/extraConsoleCommands/listCommands.lua +2 -1
- package/dist/features/playerInventory.d.ts.map +1 -1
- package/dist/features/playerInventory.lua +2 -4
- package/dist/functions/curses.d.ts +3 -0
- package/dist/functions/curses.d.ts.map +1 -0
- package/dist/functions/curses.lua +11 -0
- package/dist/functions/dimensions.d.ts +12 -0
- package/dist/functions/dimensions.d.ts.map +1 -0
- package/dist/functions/dimensions.lua +35 -0
- package/dist/functions/eden.d.ts.map +1 -1
- package/dist/functions/eden.lua +2 -4
- package/dist/functions/itemPool.d.ts +18 -0
- package/dist/functions/itemPool.d.ts.map +1 -0
- package/dist/functions/itemPool.lua +133 -0
- package/dist/functions/level.d.ts.map +1 -1
- package/dist/functions/level.lua +8 -7
- package/dist/functions/levelGrid.d.ts +155 -0
- package/dist/functions/levelGrid.d.ts.map +1 -0
- package/dist/functions/levelGrid.lua +349 -0
- package/dist/functions/rockAlt.d.ts +4 -4
- package/dist/functions/rockAlt.d.ts.map +1 -1
- package/dist/functions/rockAlt.lua +69 -20
- package/dist/functions/roomData.d.ts +5 -0
- package/dist/functions/roomData.d.ts.map +1 -1
- package/dist/functions/roomData.lua +6 -0
- package/dist/functions/roomGrid.d.ts +8 -0
- package/dist/functions/roomGrid.d.ts.map +1 -1
- package/dist/functions/rooms.d.ts +42 -61
- package/dist/functions/rooms.d.ts.map +1 -1
- package/dist/functions/rooms.lua +129 -200
- package/dist/functions/saveFile.d.ts +1 -6
- package/dist/functions/saveFile.d.ts.map +1 -1
- package/dist/functions/saveFile.lua +4 -113
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.lua +32 -0
- package/package.json +2 -2
- package/src/constants.ts +8 -0
- package/src/features/customStage/exports.ts +10 -11
- package/src/features/customTrapdoor/init.ts +7 -3
- package/src/features/extraConsoleCommands/commandsSubroutines.ts +2 -1
- package/src/features/extraConsoleCommands/listCommands.ts +2 -1
- package/src/features/playerInventory.ts +2 -3
- package/src/functions/curses.ts +9 -0
- package/src/functions/dimensions.ts +41 -0
- package/src/functions/eden.ts +2 -4
- package/src/functions/itemPool.ts +178 -0
- package/src/functions/level.ts +7 -10
- package/src/functions/levelGrid.ts +468 -0
- package/src/functions/rockAlt.ts +111 -29
- package/src/functions/roomData.ts +12 -0
- package/src/functions/roomGrid.ts +9 -0
- package/src/functions/rooms.ts +93 -206
- package/src/functions/saveFile.ts +5 -147
- package/src/index.ts +4 -0
package/src/functions/rooms.ts
CHANGED
|
@@ -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 {
|
|
54
|
-
import {
|
|
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,56 @@ 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 =
|
|
78
|
+
const rooms = getRoomsInGrid();
|
|
122
79
|
return rooms.length;
|
|
123
80
|
}
|
|
124
81
|
|
|
125
82
|
/**
|
|
126
|
-
* Helper function to get
|
|
127
|
-
* specified room
|
|
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.
|
|
86
|
+
*
|
|
87
|
+
* Returns undefined if the provided room type and variant combination were not found. (A warning
|
|
88
|
+
* message will also appear on the console, since the "goto" command will fail.)
|
|
128
89
|
*
|
|
129
|
-
*
|
|
90
|
+
* Note that the side effect of using the "goto" console command is that it will trigger a room
|
|
91
|
+
* transition after a short delay. By default, this function cancels the incoming room transition by
|
|
92
|
+
* using the `Game.StartRoomTransition` method to travel to the same room.
|
|
130
93
|
*
|
|
131
|
-
*
|
|
132
|
-
*
|
|
94
|
+
* @param roomType The type of room to retrieve.
|
|
95
|
+
* @param roomVariant The room variant to retrieve. (The room variant is the "ID" of the room in
|
|
96
|
+
* Basement Renovator.)
|
|
97
|
+
* @param cancelRoomTransition Optional. Whether to cancel the room transition by using the
|
|
98
|
+
* `Game.StartRoomTransition` method to travel to the same room. Default
|
|
99
|
+
* is true. Set this to false if you are getting the data for many rooms
|
|
100
|
+
* at the same time, and then use the `teleport` helper function when
|
|
101
|
+
* you are finished.
|
|
133
102
|
*/
|
|
134
|
-
export function
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
103
|
+
export function getRoomDataForTypeVariant(
|
|
104
|
+
roomType: RoomType,
|
|
105
|
+
roomVariant: int,
|
|
106
|
+
cancelRoomTransition = true,
|
|
107
|
+
): Readonly<RoomConfig> | undefined {
|
|
108
|
+
const command = getGotoCommand(roomType, roomVariant);
|
|
109
|
+
Isaac.ExecuteCommand(command);
|
|
110
|
+
const newRoomData = getRoomData(GridRoom.DEBUG);
|
|
111
|
+
|
|
112
|
+
if (cancelRoomTransition) {
|
|
113
|
+
const roomGridIndex = getRoomGridIndex();
|
|
114
|
+
teleport(
|
|
115
|
+
roomGridIndex,
|
|
116
|
+
Direction.NO_DIRECTION,
|
|
117
|
+
RoomTransitionAnim.FADE,
|
|
118
|
+
true,
|
|
119
|
+
);
|
|
120
|
+
}
|
|
143
121
|
|
|
144
|
-
return
|
|
122
|
+
return newRoomData;
|
|
145
123
|
}
|
|
146
124
|
|
|
147
125
|
/**
|
|
@@ -157,40 +135,6 @@ export function getRoomItemPoolType(): ItemPoolType {
|
|
|
157
135
|
return itemPool.GetPoolForRoom(roomType, roomSeed);
|
|
158
136
|
}
|
|
159
137
|
|
|
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
138
|
/**
|
|
195
139
|
* Helper function to get the proper name of a room type.
|
|
196
140
|
*
|
|
@@ -201,9 +145,12 @@ export function getRoomTypeName(roomType: RoomType): string {
|
|
|
201
145
|
}
|
|
202
146
|
|
|
203
147
|
/**
|
|
204
|
-
* Helper function to get the room descriptor for every room on the level
|
|
205
|
-
*
|
|
206
|
-
*
|
|
148
|
+
* Helper function to get the room descriptor for every room on the level. This includes off-grid
|
|
149
|
+
* rooms, such as the Devil Room. (Off-grid rooms will only be included if they the data exists,
|
|
150
|
+
* which only usually happens once they have been visited at least once.)
|
|
151
|
+
*
|
|
152
|
+
* Under the hood, this function uses the `Level.GetRooms` method to accomplish this. Rooms without
|
|
153
|
+
* data are assumed to be non-existent and are not added to the list.
|
|
207
154
|
*
|
|
208
155
|
* @param includeExtraDimensionalRooms Optional. On some floors (e.g. Downpour 2, Mines 2),
|
|
209
156
|
* extra-dimensional rooms are automatically generated and can be
|
|
@@ -215,21 +162,23 @@ export function getRooms(
|
|
|
215
162
|
const level = game.GetLevel();
|
|
216
163
|
const roomList = level.GetRooms();
|
|
217
164
|
|
|
165
|
+
/** Indexed by room safe grid index. We use a map to avoid adding extra dimensional rooms. */
|
|
218
166
|
const roomsMap = new Map<int, RoomDescriptor>();
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
}
|
|
167
|
+
|
|
168
|
+
for (let i = 0; i < roomList.Size; i++) {
|
|
169
|
+
const roomDescriptor = roomList.Get(i);
|
|
170
|
+
if (roomDescriptor === undefined || roomDescriptor.Data === undefined) {
|
|
171
|
+
continue;
|
|
225
172
|
}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
173
|
+
|
|
174
|
+
if (
|
|
175
|
+
!includeExtraDimensionalRooms &&
|
|
176
|
+
roomsMap.has(roomDescriptor.SafeGridIndex)
|
|
177
|
+
) {
|
|
178
|
+
continue;
|
|
232
179
|
}
|
|
180
|
+
|
|
181
|
+
roomsMap.set(roomDescriptor.SafeGridIndex, roomDescriptor);
|
|
233
182
|
}
|
|
234
183
|
|
|
235
184
|
return [...roomsMap.values()];
|
|
@@ -237,8 +186,10 @@ export function getRooms(
|
|
|
237
186
|
|
|
238
187
|
/**
|
|
239
188
|
* Helper function to get the room descriptor for every room on the level except for rooms that are
|
|
240
|
-
* not on the grid.
|
|
241
|
-
*
|
|
189
|
+
* not on the grid.
|
|
190
|
+
*
|
|
191
|
+
* Under the hood, this function uses the `Level.GetRooms` method to accomplish this. Rooms without
|
|
192
|
+
* data are assumed to be non-existent and are not added to the list.
|
|
242
193
|
*
|
|
243
194
|
* @param includeExtraDimensionalRooms Optional. On some floors (e.g. Downpour 2, Mines 2),
|
|
244
195
|
* extra-dimensional rooms are automatically be generated and can be
|
|
@@ -253,15 +204,19 @@ export function getRoomsInGrid(
|
|
|
253
204
|
|
|
254
205
|
/**
|
|
255
206
|
* Helper function to get the room descriptor for every room on the level in a specific dimension.
|
|
256
|
-
*
|
|
257
|
-
*
|
|
207
|
+
* This will not include any off-grid rooms, such as the Devil Room.
|
|
208
|
+
*
|
|
209
|
+
* Under the hood, this function uses the `Level.GetRooms` method to accomplish this. Rooms without
|
|
210
|
+
* data are assumed to be non-existent and are not added to the list.
|
|
258
211
|
*
|
|
259
212
|
* @returns A map of room ListIndex to RoomDescriptor.
|
|
260
213
|
*/
|
|
261
214
|
export function getRoomsOfDimension(dimension: Dimension): RoomDescriptor[] {
|
|
262
215
|
const level = game.GetLevel();
|
|
263
216
|
|
|
217
|
+
/** We use a map instead of an array because room shapes occupy more than one room grid index. */
|
|
264
218
|
const roomsMap = new Map<int, RoomDescriptor>();
|
|
219
|
+
|
|
265
220
|
for (const roomGridIndex of irange(MAX_LEVEL_GRID_INDEX)) {
|
|
266
221
|
const roomDescriptor = level.GetRoomByIdx(roomGridIndex, dimension);
|
|
267
222
|
if (roomDescriptor.Data !== undefined) {
|
|
@@ -361,10 +316,6 @@ export function inDevilsCrownTreasureRoom(): boolean {
|
|
|
361
316
|
return hasFlag(roomDescriptor.Flags, RoomDescriptorFlag.DEVIL_TREASURE);
|
|
362
317
|
}
|
|
363
318
|
|
|
364
|
-
export function inDimension(dimension: Dimension): boolean {
|
|
365
|
-
return dimension === getDimension();
|
|
366
|
-
}
|
|
367
|
-
|
|
368
319
|
export function inDoubleTrouble(): boolean {
|
|
369
320
|
const room = game.GetRoom();
|
|
370
321
|
const roomType = room.GetType();
|
|
@@ -484,87 +435,6 @@ export function isAllRoomsClear(onlyCheckRoomTypes?: RoomType[]): boolean {
|
|
|
484
435
|
return matchingRooms.every((roomDescriptor) => roomDescriptor.Clear);
|
|
485
436
|
}
|
|
486
437
|
|
|
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
438
|
/**
|
|
569
439
|
* If the `Room.Update` method is called in a `POST_NEW_ROOM` callback, then some entities will
|
|
570
440
|
* slide around (such as the player). Since those entity velocities are already at zero, setting
|
|
@@ -635,21 +505,34 @@ export function setRoomUncleared(): void {
|
|
|
635
505
|
/**
|
|
636
506
|
* Helper function to change the current room. It can be used for both teleportation and "normal"
|
|
637
507
|
* room transitions, depending on what is passed for the `direction` and `roomTransitionAnim`
|
|
638
|
-
* arguments.
|
|
639
|
-
*
|
|
640
|
-
*
|
|
508
|
+
* arguments.
|
|
509
|
+
*
|
|
510
|
+
* Use this function instead of invoking the `Game.StartRoomTransition` method directly so that:
|
|
511
|
+
* - you do not forget to set `Level.LeaveDoor` property
|
|
512
|
+
* - to prevent crashing on invalid room grid indexes
|
|
513
|
+
* - to automatically handle Curse of the Maze
|
|
641
514
|
*
|
|
642
515
|
* @param roomGridIndex The room grid index of the destination room.
|
|
643
516
|
* @param direction Optional. Default is `Direction.NO_DIRECTION`.
|
|
644
517
|
* @param roomTransitionAnim Optional. Default is `RoomTransitionAnim.TELEPORT`.
|
|
518
|
+
* @param force Optional. Whether to temporarily disable Curse of the Maze. Default is false. If set
|
|
519
|
+
* to false, then this function may not go to the provided room grid index.
|
|
645
520
|
*/
|
|
646
521
|
export function teleport(
|
|
647
522
|
roomGridIndex: int,
|
|
648
523
|
direction = Direction.NO_DIRECTION,
|
|
649
524
|
roomTransitionAnim = RoomTransitionAnim.TELEPORT,
|
|
525
|
+
force = false,
|
|
650
526
|
): void {
|
|
651
527
|
const level = game.GetLevel();
|
|
652
528
|
|
|
529
|
+
// Before starting a room transition, we must ensure that Curse of the Maze is not in effect, or
|
|
530
|
+
// else the room transition might send us to the wrong room.
|
|
531
|
+
const shouldTempDisableCurse = force && hasCurse(LevelCurse.MAZE);
|
|
532
|
+
if (shouldTempDisableCurse) {
|
|
533
|
+
level.RemoveCurses(LevelCurse.MAZE);
|
|
534
|
+
}
|
|
535
|
+
|
|
653
536
|
const roomData = getRoomData(roomGridIndex);
|
|
654
537
|
if (roomData === undefined) {
|
|
655
538
|
error(
|
|
@@ -662,4 +545,8 @@ export function teleport(
|
|
|
662
545
|
level.LeaveDoor = DoorSlot.NO_DOOR_SLOT;
|
|
663
546
|
|
|
664
547
|
game.StartRoomTransition(roomGridIndex, direction, roomTransitionAnim);
|
|
548
|
+
|
|
549
|
+
if (shouldTempDisableCurse) {
|
|
550
|
+
level.AddCurse(LevelCurse.MAZE, false);
|
|
551
|
+
}
|
|
665
552
|
}
|
|
@@ -1,32 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
ItemPoolType,
|
|
5
|
-
PlayerType,
|
|
6
|
-
TrinketType,
|
|
7
|
-
} from "isaac-typescript-definitions";
|
|
8
|
-
import { game } from "../cachedClasses";
|
|
9
|
-
import { PlayerIndex } from "../types/PlayerIndex";
|
|
10
|
-
import { getCollectibleSet } from "./collectibleSet";
|
|
11
|
-
import { collectibleHasTag } from "./collectibleTag";
|
|
12
|
-
import { mapGetPlayer, mapSetPlayer } from "./playerDataStructures";
|
|
13
|
-
import { getPlayers } from "./playerIndex";
|
|
14
|
-
import { anyPlayerHasCollectible, getPlayersOfType } from "./players";
|
|
15
|
-
import { repeat } from "./utils";
|
|
16
|
-
|
|
17
|
-
const COLLECTIBLES_THAT_AFFECT_ITEM_POOLS: readonly CollectibleType[] = [
|
|
18
|
-
CollectibleType.CHAOS, // 402
|
|
19
|
-
CollectibleType.SACRED_ORB, // 691
|
|
20
|
-
CollectibleType.TMTRAINER, // 721
|
|
21
|
-
];
|
|
22
|
-
|
|
23
|
-
const TRINKETS_THAT_AFFECT_ITEM_POOLS: readonly TrinketType[] = [
|
|
24
|
-
TrinketType.NO,
|
|
25
|
-
];
|
|
1
|
+
import { CollectibleType, ItemPoolType } from "isaac-typescript-definitions";
|
|
2
|
+
import { isCollectibleInItemPool } from "./itemPool";
|
|
3
|
+
import { anyPlayerHasCollectible } from "./players";
|
|
26
4
|
|
|
27
5
|
/**
|
|
28
6
|
* Helper function to see if the given collectible is unlocked on the current save file. This
|
|
29
|
-
* requires providing the corresponding item pool that the collectible is located in.
|
|
7
|
+
* requires providing the corresponding item pool that the collectible is normally located in.
|
|
30
8
|
*
|
|
31
9
|
* - If any player currently has the collectible, then it is assumed to be unlocked. (This is
|
|
32
10
|
* because in almost all cases, when a collectible is added to a player's inventory, it is
|
|
@@ -37,11 +15,6 @@ const TRINKETS_THAT_AFFECT_ITEM_POOLS: readonly TrinketType[] = [
|
|
|
37
15
|
* - If the collectible is non-offensive, any Tainted Losts will be temporarily changed to Isaac and
|
|
38
16
|
* then changed back. (This is because Tainted Lost is not able to retrieve non-offensive
|
|
39
17
|
* collectibles from item pools).
|
|
40
|
-
*
|
|
41
|
-
* Under the hood, this function works by using the `ItemPool.AddRoomBlacklist` method to blacklist
|
|
42
|
-
* every collectible except for the one provided. Unfortunately, this is not a general-purpose
|
|
43
|
-
* "isCollectibleInItemPool" algorithm, because when a pool is depleted, it will automatically pull
|
|
44
|
-
* items from the Treasure Room pool, and there is no way to distinguish when this happens.
|
|
45
18
|
*/
|
|
46
19
|
export function isCollectibleUnlocked(
|
|
47
20
|
collectibleType: CollectibleType,
|
|
@@ -51,120 +24,5 @@ export function isCollectibleUnlocked(
|
|
|
51
24
|
return true;
|
|
52
25
|
}
|
|
53
26
|
|
|
54
|
-
|
|
55
|
-
// temporarily change the character to Isaac.
|
|
56
|
-
const taintedLosts = getPlayersOfType(PlayerType.THE_LOST_B);
|
|
57
|
-
const isOffensive = collectibleHasTag(
|
|
58
|
-
collectibleType,
|
|
59
|
-
ItemConfigTag.OFFENSIVE,
|
|
60
|
-
);
|
|
61
|
-
let changedPlayerTypes = false;
|
|
62
|
-
if (!isOffensive) {
|
|
63
|
-
changedPlayerTypes = true;
|
|
64
|
-
for (const player of taintedLosts) {
|
|
65
|
-
player.ChangePlayerType(PlayerType.ISAAC);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const [removedItemsMap, removedTrinketsMap] =
|
|
70
|
-
removeItemsAndTrinketsThatAffectItemPools();
|
|
71
|
-
|
|
72
|
-
// Blacklist every collectible in the game except for the provided collectible.
|
|
73
|
-
const itemPool = game.GetItemPool();
|
|
74
|
-
const collectibleSet = getCollectibleSet();
|
|
75
|
-
for (const collectibleTypeInSet of collectibleSet.values()) {
|
|
76
|
-
if (collectibleTypeInSet !== collectibleType) {
|
|
77
|
-
itemPool.AddRoomBlacklist(collectibleTypeInSet);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// Get a collectible from the pool and see if it is the intended collectible. (We can use any
|
|
82
|
-
// arbitrary value as the seed since it should not influence the result.)
|
|
83
|
-
const seed = 1 as Seed;
|
|
84
|
-
const retrievedCollectibleType = itemPool.GetCollectible(
|
|
85
|
-
itemPoolType,
|
|
86
|
-
false,
|
|
87
|
-
seed,
|
|
88
|
-
);
|
|
89
|
-
|
|
90
|
-
const collectibleUnlocked = retrievedCollectibleType === collectibleType;
|
|
91
|
-
|
|
92
|
-
// Reset the blacklist
|
|
93
|
-
itemPool.ResetRoomBlacklist();
|
|
94
|
-
|
|
95
|
-
restoreItemsAndTrinketsThatAffectItemPools(
|
|
96
|
-
removedItemsMap,
|
|
97
|
-
removedTrinketsMap,
|
|
98
|
-
);
|
|
99
|
-
|
|
100
|
-
// Change any players back to Tainted Lost, if necessary.
|
|
101
|
-
if (changedPlayerTypes) {
|
|
102
|
-
for (const player of taintedLosts) {
|
|
103
|
-
player.ChangePlayerType(PlayerType.THE_LOST_B);
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
return collectibleUnlocked;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Before checking the item pools, remove any collectibles or trinkets that would affect the
|
|
112
|
-
* retrieved collectible types.
|
|
113
|
-
*/
|
|
114
|
-
function removeItemsAndTrinketsThatAffectItemPools(): [
|
|
115
|
-
removedItemsMap: Map<PlayerIndex, CollectibleType[]>,
|
|
116
|
-
removedTrinketsMap: Map<PlayerIndex, TrinketType[]>,
|
|
117
|
-
] {
|
|
118
|
-
const removedItemsMap = new Map<PlayerIndex, CollectibleType[]>();
|
|
119
|
-
const removedTrinketsMap = new Map<PlayerIndex, TrinketType[]>();
|
|
120
|
-
for (const player of getPlayers()) {
|
|
121
|
-
const removedItems: CollectibleType[] = [];
|
|
122
|
-
for (const itemToRemove of COLLECTIBLES_THAT_AFFECT_ITEM_POOLS) {
|
|
123
|
-
if (player.HasCollectible(itemToRemove)) {
|
|
124
|
-
const numCollectibles = player.GetCollectibleNum(itemToRemove);
|
|
125
|
-
repeat(numCollectibles, () => {
|
|
126
|
-
player.RemoveCollectible(itemToRemove);
|
|
127
|
-
removedItems.push(itemToRemove);
|
|
128
|
-
});
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
mapSetPlayer(removedItemsMap, player, removedItems);
|
|
133
|
-
|
|
134
|
-
const removedTrinkets: TrinketType[] = [];
|
|
135
|
-
for (const trinketToRemove of TRINKETS_THAT_AFFECT_ITEM_POOLS) {
|
|
136
|
-
if (player.HasTrinket(trinketToRemove)) {
|
|
137
|
-
const numTrinkets = player.GetTrinketMultiplier(trinketToRemove);
|
|
138
|
-
repeat(numTrinkets, () => {
|
|
139
|
-
player.TryRemoveTrinket(trinketToRemove);
|
|
140
|
-
removedTrinkets.push(trinketToRemove);
|
|
141
|
-
});
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
mapSetPlayer(removedTrinketsMap, player, removedTrinkets);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
return [removedItemsMap, removedTrinketsMap];
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
function restoreItemsAndTrinketsThatAffectItemPools(
|
|
152
|
-
removedItemsMap: Map<PlayerIndex, CollectibleType[]>,
|
|
153
|
-
removedTrinketsMap: Map<PlayerIndex, TrinketType[]>,
|
|
154
|
-
) {
|
|
155
|
-
for (const player of getPlayers()) {
|
|
156
|
-
const removedItems = mapGetPlayer(removedItemsMap, player);
|
|
157
|
-
if (removedItems !== undefined) {
|
|
158
|
-
for (const collectibleType of removedItems) {
|
|
159
|
-
player.AddCollectible(collectibleType, 0, false); // Prevent Chaos from spawning pickups
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
const removedTrinkets = mapGetPlayer(removedTrinketsMap, player);
|
|
164
|
-
if (removedTrinkets !== undefined) {
|
|
165
|
-
for (const trinketType of removedTrinkets) {
|
|
166
|
-
player.AddTrinket(trinketType, false);
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
}
|
|
27
|
+
return isCollectibleInItemPool(collectibleType, itemPoolType);
|
|
170
28
|
}
|
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";
|
|
@@ -117,11 +119,13 @@ export * from "./functions/gridEntities";
|
|
|
117
119
|
export * from "./functions/gridEntitiesSpecific";
|
|
118
120
|
export * from "./functions/input";
|
|
119
121
|
export * from "./functions/isaacAPIClass";
|
|
122
|
+
export * from "./functions/itemPool";
|
|
120
123
|
export * from "./functions/jsonHelpers";
|
|
121
124
|
export * from "./functions/jsonRoom";
|
|
122
125
|
export * from "./functions/kColor";
|
|
123
126
|
export * from "./functions/language";
|
|
124
127
|
export * from "./functions/level";
|
|
128
|
+
export * from "./functions/levelGrid";
|
|
125
129
|
export * from "./functions/log";
|
|
126
130
|
export * from "./functions/map";
|
|
127
131
|
export * from "./functions/math";
|