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.
- 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/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/level.d.ts.map +1 -1
- package/dist/functions/level.lua +8 -7
- package/dist/functions/levelGrid.d.ts +152 -0
- package/dist/functions/levelGrid.d.ts.map +1 -0
- package/dist/functions/levelGrid.lua +326 -0
- 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 +39 -61
- package/dist/functions/rooms.d.ts.map +1 -1
- package/dist/functions/rooms.lua +126 -200
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.lua +24 -0
- package/package.json +2 -2
- 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/functions/curses.ts +9 -0
- package/src/functions/dimensions.ts +41 -0
- package/src/functions/level.ts +7 -10
- package/src/functions/levelGrid.ts +424 -0
- package/src/functions/roomData.ts +12 -0
- package/src/functions/roomGrid.ts +9 -0
- package/src/functions/rooms.ts +90 -206
- package/src/index.ts +3 -0
|
@@ -63,6 +63,7 @@ import { getNPCs } from "../../functions/entitiesSpecific";
|
|
|
63
63
|
import { getEnumValues } from "../../functions/enums";
|
|
64
64
|
import { addFlag } from "../../functions/flag";
|
|
65
65
|
import { spawnGridEntity } from "../../functions/gridEntities";
|
|
66
|
+
import { getRoomGridIndexesForType } from "../../functions/levelGrid";
|
|
66
67
|
import {
|
|
67
68
|
logPlayerEffects,
|
|
68
69
|
logRoom,
|
|
@@ -85,7 +86,7 @@ import {
|
|
|
85
86
|
useActiveItemTemp,
|
|
86
87
|
} from "../../functions/players";
|
|
87
88
|
import { gridCoordinatesToWorldPosition } from "../../functions/roomGrid";
|
|
88
|
-
import { changeRoom
|
|
89
|
+
import { changeRoom } from "../../functions/rooms";
|
|
89
90
|
import { onSetSeed, restart, setUnseeded } from "../../functions/run";
|
|
90
91
|
import { getGoldenTrinketType } from "../../functions/trinkets";
|
|
91
92
|
import { irange, printConsole, printEnabled } from "../../functions/utils";
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { LevelCurse } from "isaac-typescript-definitions";
|
|
2
|
+
import { game } from "../cachedClasses";
|
|
3
|
+
import { hasFlag } from "./flag";
|
|
4
|
+
|
|
5
|
+
export function hasCurse(curse: LevelCurse): boolean {
|
|
6
|
+
const level = game.GetLevel();
|
|
7
|
+
const curses = level.GetCurses();
|
|
8
|
+
return hasFlag(curses, curse);
|
|
9
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { Dimension } from "isaac-typescript-definitions";
|
|
2
|
+
import { game } from "../cachedClasses";
|
|
3
|
+
import { NUM_DIMENSIONS } from "../constants";
|
|
4
|
+
import { getRoomGridIndex } from "./roomData";
|
|
5
|
+
import { erange } from "./utils";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Helper function to get an array with every valid `Dimension` (not including `Dimension.CURRENT`).
|
|
9
|
+
*/
|
|
10
|
+
export function getAllDimensions(): Dimension[] {
|
|
11
|
+
return erange(NUM_DIMENSIONS) as Dimension[];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Helper function to get the current dimension. Most of the time, this will be `Dimension.MAIN`,
|
|
16
|
+
* but it can change if e.g. the player is in the mirror world of Downpour/Dross.
|
|
17
|
+
*/
|
|
18
|
+
export function getDimension(): Dimension {
|
|
19
|
+
const level = game.GetLevel();
|
|
20
|
+
const roomGridIndex = getRoomGridIndex();
|
|
21
|
+
const roomDescription = level.GetRoomByIdx(roomGridIndex, Dimension.CURRENT);
|
|
22
|
+
const currentRoomHash = GetPtrHash(roomDescription);
|
|
23
|
+
|
|
24
|
+
for (const dimension of getAllDimensions()) {
|
|
25
|
+
const dimensionRoomDescription = level.GetRoomByIdx(
|
|
26
|
+
roomGridIndex,
|
|
27
|
+
dimension,
|
|
28
|
+
);
|
|
29
|
+
const dimensionRoomHash = GetPtrHash(dimensionRoomDescription);
|
|
30
|
+
|
|
31
|
+
if (dimensionRoomHash === currentRoomHash) {
|
|
32
|
+
return dimension;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
error("Failed to get the current dimension.");
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function inDimension(dimension: Dimension): boolean {
|
|
40
|
+
return dimension === getDimension();
|
|
41
|
+
}
|
package/src/functions/level.ts
CHANGED
|
@@ -1,21 +1,18 @@
|
|
|
1
1
|
import { DoorSlot } from "isaac-typescript-definitions";
|
|
2
2
|
import { game } from "../cachedClasses";
|
|
3
3
|
import { getEnumValues } from "./enums";
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
getRooms,
|
|
7
|
-
isDoorSlotValidAtGridIndexForRedRoom,
|
|
8
|
-
} from "./rooms";
|
|
4
|
+
import { isDoorSlotValidAtGridIndexForRedRoom } from "./levelGrid";
|
|
5
|
+
import { getNumRooms, getRoomsInGrid } from "./rooms";
|
|
9
6
|
|
|
10
7
|
export function fillLevelWithRedRooms(): void {
|
|
11
8
|
const level = game.GetLevel();
|
|
12
9
|
|
|
13
|
-
let
|
|
10
|
+
let numRoomsInGrid: int;
|
|
14
11
|
do {
|
|
15
|
-
const
|
|
16
|
-
|
|
12
|
+
const roomsInGrid = getRoomsInGrid();
|
|
13
|
+
numRoomsInGrid = roomsInGrid.length;
|
|
17
14
|
|
|
18
|
-
for (const roomDescriptor of
|
|
15
|
+
for (const roomDescriptor of roomsInGrid) {
|
|
19
16
|
for (const doorSlot of getEnumValues(DoorSlot)) {
|
|
20
17
|
if (
|
|
21
18
|
isDoorSlotValidAtGridIndexForRedRoom(
|
|
@@ -27,5 +24,5 @@ export function fillLevelWithRedRooms(): void {
|
|
|
27
24
|
}
|
|
28
25
|
}
|
|
29
26
|
}
|
|
30
|
-
} while (
|
|
27
|
+
} while (numRoomsInGrid !== getNumRooms());
|
|
31
28
|
}
|
|
@@ -0,0 +1,424 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* These functions have to do with the room grid index for the level (i.e. the position that the
|
|
3
|
+
* room is on the grid that represents the map for the level).
|
|
4
|
+
*
|
|
5
|
+
* For functions having to do with the grid index inside of the room, see the "Room Grid" functions.
|
|
6
|
+
*
|
|
7
|
+
* @module
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
DoorSlot,
|
|
12
|
+
DownpourRoomSubType,
|
|
13
|
+
MinesRoomSubType,
|
|
14
|
+
RoomDescriptorFlag,
|
|
15
|
+
RoomShape,
|
|
16
|
+
RoomType,
|
|
17
|
+
} from "isaac-typescript-definitions";
|
|
18
|
+
import { game } from "../cachedClasses";
|
|
19
|
+
import { LEVEL_GRID_ROW_WIDTH, MAX_LEVEL_GRID_INDEX } from "../constants";
|
|
20
|
+
import { ROOM_SHAPE_TO_DOOR_SLOTS_TO_GRID_INDEX_DELTA } from "../objects/roomShapeToDoorSlotsToGridIndexDelta";
|
|
21
|
+
import { getRandomArrayElement } from "./array";
|
|
22
|
+
import { doorSlotToDoorSlotFlag } from "./doors";
|
|
23
|
+
import { hasFlag } from "./flag";
|
|
24
|
+
import { getRandomSeed } from "./rng";
|
|
25
|
+
import {
|
|
26
|
+
getRoomAllowedDoors,
|
|
27
|
+
getRoomData,
|
|
28
|
+
getRoomDescriptor,
|
|
29
|
+
getRoomGridIndex,
|
|
30
|
+
getRoomShape,
|
|
31
|
+
} from "./roomData";
|
|
32
|
+
import { getRooms, getRoomsInGrid } from "./rooms";
|
|
33
|
+
import { getGridIndexDelta } from "./roomShape";
|
|
34
|
+
|
|
35
|
+
const LEFT = -1;
|
|
36
|
+
const UP = -LEVEL_GRID_ROW_WIDTH;
|
|
37
|
+
const RIGHT = 1;
|
|
38
|
+
const DOWN = LEVEL_GRID_ROW_WIDTH;
|
|
39
|
+
|
|
40
|
+
const ADJACENT_ROOM_GRID_INDEX_DELTAS: readonly int[] = [LEFT, UP, RIGHT, DOWN];
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Helper function to get the room grid indexes that are adjacent to a given room grid index.
|
|
44
|
+
*
|
|
45
|
+
* Adjacent room grid indexes that are outside of the grid will not be included in the returned
|
|
46
|
+
* array.
|
|
47
|
+
*
|
|
48
|
+
* If a room grid index is provided that is outside of the grid, then an empty array will be
|
|
49
|
+
* returned.
|
|
50
|
+
*
|
|
51
|
+
* Note that this function does not take the shape of the room into account; it only looks at a
|
|
52
|
+
* single room grid index.
|
|
53
|
+
*
|
|
54
|
+
* @param roomGridIndex Optional. Default is the current room index.
|
|
55
|
+
*/
|
|
56
|
+
export function getAdjacentRoomGridIndexes(roomGridIndex?: int): int[] {
|
|
57
|
+
const roomGridIndexToUse =
|
|
58
|
+
roomGridIndex === undefined ? getRoomGridIndex() : roomGridIndex;
|
|
59
|
+
|
|
60
|
+
if (!isRoomGridIndexInBounds(roomGridIndexToUse)) {
|
|
61
|
+
return [];
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const adjacentRoomGridIndexes = ADJACENT_ROOM_GRID_INDEX_DELTAS.map(
|
|
65
|
+
(delta) => roomGridIndexToUse + delta,
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
return adjacentRoomGridIndexes.filter((adjacentRoomGridIndex) =>
|
|
69
|
+
isRoomGridIndexInBounds(adjacentRoomGridIndex),
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Helper function to get the room safe grid index for every room on the entire floor. */
|
|
74
|
+
export function getAllRoomGridIndexes(): int[] {
|
|
75
|
+
const rooms = getRooms();
|
|
76
|
+
return rooms.map((roomDescriptor) => roomDescriptor.SafeGridIndex);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Helper function to pick a random valid spot on the floor to insert a brand new room. Note that
|
|
81
|
+
* some floors will not have any valid spots. If this is the case, this function will return
|
|
82
|
+
* undefined.
|
|
83
|
+
*
|
|
84
|
+
* @param seedOrRNG Optional. The `Seed` or `RNG` object to use. If an `RNG` object is provided, the
|
|
85
|
+
* `RNG.Next` method will be called. Default is `getRandomSeed()`.
|
|
86
|
+
* @returns Either a tuple of adjacent room grid index, `DoorSlot`, and new room grid index, or
|
|
87
|
+
* undefined.
|
|
88
|
+
*/
|
|
89
|
+
export function getNewRoomCandidate(
|
|
90
|
+
seedOrRNG: Seed | RNG = getRandomSeed(),
|
|
91
|
+
):
|
|
92
|
+
| [adjacentRoomGridIndex: int, doorSlot: DoorSlot, newRoomGridIndex: int]
|
|
93
|
+
| undefined {
|
|
94
|
+
const newRoomCandidatesForFloor = getNewRoomCandidatesForFloor();
|
|
95
|
+
if (newRoomCandidatesForFloor.length === 0) {
|
|
96
|
+
return undefined;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return getRandomArrayElement(newRoomCandidatesForFloor, seedOrRNG);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Helper function to iterate through the possible doors for a room and see if any of them would be
|
|
104
|
+
* a valid spot to insert a brand new room on the floor.
|
|
105
|
+
*
|
|
106
|
+
* @param roomGridIndex Optional. Default is the current room index.
|
|
107
|
+
* @returns A array of tuples of `DoorSlot` and room grid index.
|
|
108
|
+
*/
|
|
109
|
+
export function getNewRoomCandidatesBesideRoom(
|
|
110
|
+
roomGridIndex?: int,
|
|
111
|
+
): Array<[doorSlot: DoorSlot, roomGridIndex: int]> {
|
|
112
|
+
const roomDescriptor = getRoomDescriptor(roomGridIndex);
|
|
113
|
+
|
|
114
|
+
if (!isRoomGridIndexInBounds(roomDescriptor.SafeGridIndex)) {
|
|
115
|
+
return [];
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const roomData = roomDescriptor.Data;
|
|
119
|
+
if (roomData === undefined) {
|
|
120
|
+
return [];
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const doorSlotToRoomGridIndexes = getRoomShapeNeighborGridIndexes(
|
|
124
|
+
roomDescriptor.SafeGridIndex,
|
|
125
|
+
roomData.Shape,
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
const roomCandidates: Array<[DoorSlot, int]> = [];
|
|
129
|
+
|
|
130
|
+
for (const [
|
|
131
|
+
doorSlot,
|
|
132
|
+
neighborRoomGridIndex,
|
|
133
|
+
] of doorSlotToRoomGridIndexes.entries()) {
|
|
134
|
+
// The "getRoomShapeNeighborGridIndexes" returns grid indexes for every possible door, but the
|
|
135
|
+
// real room we are examining will only have a subset of these doors.
|
|
136
|
+
const doorSlotFlag = doorSlotToDoorSlotFlag(doorSlot);
|
|
137
|
+
if (!hasFlag(roomData.Doors, doorSlotFlag)) {
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// If the room already exists, then it is not a possible candidate for a new room.
|
|
142
|
+
if (roomExists(neighborRoomGridIndex)) {
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Check to see if hypothetically creating a room at the given room grid index would be a dead
|
|
147
|
+
// end. In other words, if we created the room, we would only want it to connect to one other
|
|
148
|
+
// room (this one).
|
|
149
|
+
if (!isDeadEnd(neighborRoomGridIndex)) {
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
roomCandidates.push([doorSlot, neighborRoomGridIndex]);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return roomCandidates;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Helper function to search through all of the rooms on the floor for a spot to insert a brand new
|
|
161
|
+
* room.
|
|
162
|
+
*
|
|
163
|
+
* @returns A array of tuples of adjacent room grid index, `DoorSlot`, and new room grid index.
|
|
164
|
+
*/
|
|
165
|
+
export function getNewRoomCandidatesForFloor(): Array<
|
|
166
|
+
[adjacentRoomGridIndex: int, doorSlot: DoorSlot, newRoomGridIndex: int]
|
|
167
|
+
> {
|
|
168
|
+
const rooms = getRoomsInGrid();
|
|
169
|
+
const normalRooms = rooms.filter(
|
|
170
|
+
(room) =>
|
|
171
|
+
room.Data !== undefined &&
|
|
172
|
+
room.Data.Type === RoomType.DEFAULT &&
|
|
173
|
+
room.Data.Subtype !== (DownpourRoomSubType.MIRROR as int) &&
|
|
174
|
+
room.Data.Subtype !== (MinesRoomSubType.MINESHAFT_ENTRANCE as int),
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
const newRoomCandidates: Array<[int, DoorSlot, int]> = [];
|
|
178
|
+
|
|
179
|
+
for (const room of normalRooms) {
|
|
180
|
+
const newRoomCandidatesBesideRoom = getNewRoomCandidatesBesideRoom(
|
|
181
|
+
room.SafeGridIndex,
|
|
182
|
+
);
|
|
183
|
+
for (const [doorSlot, newRoomGridIndex] of newRoomCandidatesBesideRoom) {
|
|
184
|
+
newRoomCandidates.push([room.SafeGridIndex, doorSlot, newRoomGridIndex]);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return newRoomCandidates;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Helper function to get an array of all of the safe grid indexes for rooms that match the
|
|
193
|
+
* specified room type.
|
|
194
|
+
*
|
|
195
|
+
* This function only searches through rooms in the current dimension.
|
|
196
|
+
*
|
|
197
|
+
* This function is variadic, meaning that you can specify N arguments to get the combined grid
|
|
198
|
+
* indexes for N room types.
|
|
199
|
+
*/
|
|
200
|
+
export function getRoomGridIndexesForType(...roomTypes: RoomType[]): int[] {
|
|
201
|
+
const roomTypesSet = new Set<RoomType>([...roomTypes]);
|
|
202
|
+
|
|
203
|
+
const rooms = getRooms();
|
|
204
|
+
const matchingRooms = rooms.filter(
|
|
205
|
+
(roomDescriptor) =>
|
|
206
|
+
roomDescriptor.Data !== undefined &&
|
|
207
|
+
roomTypesSet.has(roomDescriptor.Data.Type),
|
|
208
|
+
);
|
|
209
|
+
|
|
210
|
+
return matchingRooms.map((roomDescriptor) => roomDescriptor.SafeGridIndex);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Helper function to get the grid indexes of all the rooms connected to the given room index,
|
|
215
|
+
* taking the shape of the room into account. (This will only include rooms with valid data.)
|
|
216
|
+
*
|
|
217
|
+
* Returns an empty map if the provided room grid index is out of bounds or has no associated room
|
|
218
|
+
* data.
|
|
219
|
+
*
|
|
220
|
+
* @param roomGridIndex Optional. Default is the current room index.
|
|
221
|
+
* @returns A map of `DoorSlot` to the corresponding room grid index.
|
|
222
|
+
*/
|
|
223
|
+
export function getRoomNeighbors(roomGridIndex?: int): Map<DoorSlot, int> {
|
|
224
|
+
const roomDescriptor = getRoomDescriptor(roomGridIndex);
|
|
225
|
+
|
|
226
|
+
if (!isRoomGridIndexInBounds(roomDescriptor.SafeGridIndex)) {
|
|
227
|
+
return new Map();
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const roomData = roomDescriptor.Data;
|
|
231
|
+
if (roomData === undefined) {
|
|
232
|
+
return new Map();
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const doorSlotToRoomGridIndexes = getRoomShapeNeighborGridIndexes(
|
|
236
|
+
roomDescriptor.SafeGridIndex,
|
|
237
|
+
roomData.Shape,
|
|
238
|
+
);
|
|
239
|
+
|
|
240
|
+
// The "getRoomShapeNeighborGridIndexes" returns grid indexes for every possible door, but the
|
|
241
|
+
// real room we are examining will only have a subset of these doors. However, we do not have to
|
|
242
|
+
// worry about filtering the map, since we perform a room existence check below.
|
|
243
|
+
const roomNeighbors = new Map<DoorSlot, int>();
|
|
244
|
+
for (const [
|
|
245
|
+
doorSlot,
|
|
246
|
+
neighborRoomGridIndex,
|
|
247
|
+
] of doorSlotToRoomGridIndexes.entries()) {
|
|
248
|
+
if (roomExists(neighborRoomGridIndex)) {
|
|
249
|
+
roomNeighbors.set(doorSlot, neighborRoomGridIndex);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
return roomNeighbors;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Helper function to get the room grid index delta that each hypothetical door in a given room
|
|
258
|
+
* shape would go to.
|
|
259
|
+
*
|
|
260
|
+
* This is used by the `getRoomShapeNeighborGridIndexes` function.
|
|
261
|
+
*
|
|
262
|
+
* @returns A map of `DoorSlot` to the corresponding room grid index delta.
|
|
263
|
+
*/
|
|
264
|
+
export function getRoomShapeNeighborGridIndexDeltas(
|
|
265
|
+
roomShape: RoomShape,
|
|
266
|
+
): Map<DoorSlot, int> {
|
|
267
|
+
return ROOM_SHAPE_TO_DOOR_SLOTS_TO_GRID_INDEX_DELTA[roomShape];
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Helper function to get the room grid index that each hypothetical door in a given room shape
|
|
272
|
+
* would go to. (This will not include room grid indexes that are outside of the grid.)
|
|
273
|
+
*
|
|
274
|
+
* @param safeRoomGridIndex This must be the room safe grid index (i.e. the top-left room grid index
|
|
275
|
+
* for the respective room).
|
|
276
|
+
* @param roomShape The shape of the room.
|
|
277
|
+
* @returns A map of `DoorSlot` to the corresponding room grid index.
|
|
278
|
+
*/
|
|
279
|
+
export function getRoomShapeNeighborGridIndexes(
|
|
280
|
+
safeRoomGridIndex: int,
|
|
281
|
+
roomShape: RoomShape,
|
|
282
|
+
): Map<DoorSlot, int> {
|
|
283
|
+
const roomShapeNeighborGridIndexDeltas =
|
|
284
|
+
getRoomShapeNeighborGridIndexDeltas(roomShape);
|
|
285
|
+
|
|
286
|
+
const neighborGridIndexes = new Map<DoorSlot, int>();
|
|
287
|
+
for (const [doorSlot, delta] of roomShapeNeighborGridIndexDeltas.entries()) {
|
|
288
|
+
const roomGridIndex = safeRoomGridIndex + delta;
|
|
289
|
+
if (isRoomGridIndexInBounds(roomGridIndex)) {
|
|
290
|
+
neighborGridIndexes.set(doorSlot, roomGridIndex);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
return neighborGridIndexes;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Helper function to check if the given room grid index is a dead end. Specifically, this is
|
|
299
|
+
* defined as having only one adjacent room that exists.
|
|
300
|
+
*
|
|
301
|
+
* Note that this function does not take the shape of the room into account; it only looks at a
|
|
302
|
+
* single room grid index.
|
|
303
|
+
*
|
|
304
|
+
* This function does not care if the given room grid index actually exists, so you can use it to
|
|
305
|
+
* check if a hypothetical room would be a dead end.
|
|
306
|
+
*
|
|
307
|
+
* @param roomGridIndex Optional. Default is the current room index.
|
|
308
|
+
*/
|
|
309
|
+
export function isDeadEnd(roomGridIndex?: int): boolean {
|
|
310
|
+
const adjacentRoomGridIndexes = getAdjacentRoomGridIndexes(roomGridIndex);
|
|
311
|
+
const adjacentRoomData = adjacentRoomGridIndexes.map(
|
|
312
|
+
(adjacentRoomGridIndex) => getRoomData(adjacentRoomGridIndex),
|
|
313
|
+
);
|
|
314
|
+
const existingRoomData = adjacentRoomData.filter(
|
|
315
|
+
(data): data is RoomConfig => data !== undefined,
|
|
316
|
+
);
|
|
317
|
+
|
|
318
|
+
return existingRoomData.length === 1;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
export function isDoorSlotValidAtGridIndex(
|
|
322
|
+
doorSlot: DoorSlot,
|
|
323
|
+
roomGridIndex: int,
|
|
324
|
+
): boolean {
|
|
325
|
+
const allowedDoors = getRoomAllowedDoors(roomGridIndex);
|
|
326
|
+
return allowedDoors.has(doorSlot);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
export function isDoorSlotValidAtGridIndexForRedRoom(
|
|
330
|
+
doorSlot: DoorSlot,
|
|
331
|
+
roomGridIndex: int,
|
|
332
|
+
): boolean {
|
|
333
|
+
const doorSlotValidAtGridIndex = isDoorSlotValidAtGridIndex(
|
|
334
|
+
doorSlot,
|
|
335
|
+
roomGridIndex,
|
|
336
|
+
);
|
|
337
|
+
if (!doorSlotValidAtGridIndex) {
|
|
338
|
+
return false;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
const roomShape = getRoomShape(roomGridIndex);
|
|
342
|
+
if (roomShape === undefined) {
|
|
343
|
+
return false;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
const delta = getGridIndexDelta(roomShape, doorSlot);
|
|
347
|
+
if (delta === undefined) {
|
|
348
|
+
return false;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
const redRoomGridIndex = roomGridIndex + delta;
|
|
352
|
+
return (
|
|
353
|
+
!roomExists(redRoomGridIndex) && isRoomGridIndexInBounds(redRoomGridIndex)
|
|
354
|
+
);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Helper function to detect if the provided room was created by the Red Key item. Under the hood,
|
|
359
|
+
* this checks for the `RoomDescriptorFlag.FLAG_RED_ROOM` flag.
|
|
360
|
+
*
|
|
361
|
+
* @param roomGridIndex Optional. Default is the current room index.
|
|
362
|
+
*/
|
|
363
|
+
export function isRedKeyRoom(roomGridIndex?: int): boolean {
|
|
364
|
+
const roomDescriptor = getRoomDescriptor(roomGridIndex);
|
|
365
|
+
return hasFlag(roomDescriptor.Flags, RoomDescriptorFlag.RED_ROOM);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Helper function to determine if a given room grid index is inside of the normal 13x13 level grid.
|
|
370
|
+
*
|
|
371
|
+
* For example, Devil Rooms and the Mega Satan room are not considered to be inside the grid.
|
|
372
|
+
*/
|
|
373
|
+
export function isRoomGridIndexInBounds(roomGridIndex: int): boolean {
|
|
374
|
+
return roomGridIndex >= 0 && roomGridIndex <= MAX_LEVEL_GRID_INDEX;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* Helper function to generate a new room on the floor at a valid dead end attached to a normal
|
|
379
|
+
* room.
|
|
380
|
+
*
|
|
381
|
+
* Under the hood, this function uses the `Level.MakeRedRoomDoor` method to create the room.
|
|
382
|
+
*
|
|
383
|
+
* The newly created room will have data corresponding to the game's randomly generated red room. If
|
|
384
|
+
* you want to modify this, use the `setRoomData` helper function.
|
|
385
|
+
*
|
|
386
|
+
* @returns The room grid index of the new room or undefined if the floor had no valid dead ends to
|
|
387
|
+
* place a room.
|
|
388
|
+
*/
|
|
389
|
+
export function newRoom(): int | undefined {
|
|
390
|
+
const newRoomCandidate = getNewRoomCandidate();
|
|
391
|
+
if (newRoomCandidate === undefined) {
|
|
392
|
+
return undefined;
|
|
393
|
+
}
|
|
394
|
+
const [adjacentRoomGridIndex, doorSlot, newRoomGridIndex] = newRoomCandidate;
|
|
395
|
+
|
|
396
|
+
const level = game.GetLevel();
|
|
397
|
+
level.MakeRedRoomDoor(adjacentRoomGridIndex, doorSlot);
|
|
398
|
+
|
|
399
|
+
return newRoomGridIndex;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/** Helper function to check if a room exists at the given room grid index. */
|
|
403
|
+
export function roomExists(roomGridIndex: int): boolean {
|
|
404
|
+
const roomData = getRoomData(roomGridIndex);
|
|
405
|
+
return roomData !== undefined;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
/**
|
|
409
|
+
* Helper function to get the coordinates of a given grid index. The floor is represented by a 13x13
|
|
410
|
+
* grid.
|
|
411
|
+
*
|
|
412
|
+
* - Since the starting room is in the center, the starting room grid index of 84 is equal to
|
|
413
|
+
* coordinates of (6, 6).
|
|
414
|
+
* - The top-left grid index of 0 is equal to coordinates of: (12, 0)
|
|
415
|
+
* - The top-right grid index of 12 is equal to coordinates of: (0, 0)
|
|
416
|
+
* - The bottom-left grid index of 156 is equal to coordinates of: (0, 12)
|
|
417
|
+
* - The bottom-right grid index of 168 is equal to coordinates of: (12, 12)
|
|
418
|
+
*/
|
|
419
|
+
export function roomGridIndexToXY(roomGridIndex: int): [x: int, y: int] {
|
|
420
|
+
const x = roomGridIndex % LEVEL_GRID_ROW_WIDTH;
|
|
421
|
+
const y = Math.floor(roomGridIndex / LEVEL_GRID_ROW_WIDTH);
|
|
422
|
+
|
|
423
|
+
return [x, y];
|
|
424
|
+
}
|
|
@@ -198,3 +198,15 @@ export function getRoomVisitedCount(roomGridIndex?: int): int {
|
|
|
198
198
|
const roomDescriptor = getRoomDescriptor(roomGridIndex);
|
|
199
199
|
return roomDescriptor.VisitedCount;
|
|
200
200
|
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Helper function to set the data for a given room. This will change the room type, contents, and
|
|
204
|
+
* so on.
|
|
205
|
+
*/
|
|
206
|
+
export function setRoomData(
|
|
207
|
+
roomGridIndex: int,
|
|
208
|
+
data: Readonly<RoomConfig>,
|
|
209
|
+
): void {
|
|
210
|
+
const roomDescriptor = getRoomDescriptor(roomGridIndex);
|
|
211
|
+
roomDescriptor.Data = data;
|
|
212
|
+
}
|
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* These functions have to do with the grid index inside of a room (i.e. the grid index that grid
|
|
3
|
+
* entities use).
|
|
4
|
+
*
|
|
5
|
+
* For functions having to do with the room grid index of the level, see the "Level Grid" functions.
|
|
6
|
+
*
|
|
7
|
+
* @module
|
|
8
|
+
*/
|
|
9
|
+
|
|
1
10
|
import { RoomShape } from "isaac-typescript-definitions";
|
|
2
11
|
import { L_ROOM_SHAPE_TO_RECTANGLES } from "../objects/LRoomShapeToRectangles";
|
|
3
12
|
import { inRectangle } from "./math";
|