isaacscript-common 30.12.11 → 31.0.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/index.rollup.d.ts +141 -20
- package/dist/isaacscript-common.lua +157 -111
- package/dist/src/classes/features/other/ItemPoolDetection.lua +4 -2
- package/dist/src/classes/features/other/customStages/backdrop.lua +3 -3
- package/dist/src/functions/hex.lua +13 -7
- package/dist/src/functions/levelGrid.d.ts +22 -13
- package/dist/src/functions/levelGrid.d.ts.map +1 -1
- package/dist/src/functions/levelGrid.lua +46 -23
- package/dist/src/functions/logMisc.d.ts.map +1 -1
- package/dist/src/functions/logMisc.lua +3 -7
- package/dist/src/functions/roomData.d.ts +1 -5
- package/dist/src/functions/roomData.d.ts.map +1 -1
- package/dist/src/functions/roomGrid.lua +6 -6
- package/dist/src/functions/roomShape.d.ts +1 -1
- package/dist/src/functions/roomShape.d.ts.map +1 -1
- package/dist/src/functions/roomShape.lua +1 -1
- package/dist/src/functions/roomShapeWalls.lua +2 -2
- package/dist/src/functions/rooms.d.ts +98 -1
- package/dist/src/functions/rooms.d.ts.map +1 -1
- package/dist/src/functions/rooms.lua +145 -68
- package/package.json +2 -2
- package/src/classes/features/other/ItemPoolDetection.ts +6 -6
- package/src/classes/features/other/customStages/backdrop.ts +3 -3
- package/src/functions/hex.ts +8 -8
- package/src/functions/levelGrid.ts +51 -24
- package/src/functions/logMisc.ts +5 -9
- package/src/functions/roomData.ts +4 -0
- package/src/functions/roomGrid.ts +2 -2
- package/src/functions/roomShape.ts +1 -1
- package/src/functions/roomShapeWalls.ts +2 -2
- package/src/functions/rooms.ts +237 -118
|
@@ -7,9 +7,9 @@ local Map = ____lualib.Map
|
|
|
7
7
|
local __TS__Spread = ____lualib.__TS__Spread
|
|
8
8
|
local __TS__ArrayFilter = ____lualib.__TS__ArrayFilter
|
|
9
9
|
local __TS__ArrayMap = ____lualib.__TS__ArrayMap
|
|
10
|
+
local __TS__ArrayEvery = ____lualib.__TS__ArrayEvery
|
|
10
11
|
local __TS__StringIncludes = ____lualib.__TS__StringIncludes
|
|
11
12
|
local __TS__ArrayIncludes = ____lualib.__TS__ArrayIncludes
|
|
12
|
-
local __TS__ArrayEvery = ____lualib.__TS__ArrayEvery
|
|
13
13
|
local ____exports = {}
|
|
14
14
|
local ____isaac_2Dtypescript_2Ddefinitions = require("isaac-typescript-definitions")
|
|
15
15
|
local AngelRoomSubType = ____isaac_2Dtypescript_2Ddefinitions.AngelRoomSubType
|
|
@@ -19,7 +19,6 @@ local DownpourRoomSubType = ____isaac_2Dtypescript_2Ddefinitions.DownpourRoomSub
|
|
|
19
19
|
local DungeonSubType = ____isaac_2Dtypescript_2Ddefinitions.DungeonSubType
|
|
20
20
|
local GridRoom = ____isaac_2Dtypescript_2Ddefinitions.GridRoom
|
|
21
21
|
local HomeRoomSubType = ____isaac_2Dtypescript_2Ddefinitions.HomeRoomSubType
|
|
22
|
-
local LevelStage = ____isaac_2Dtypescript_2Ddefinitions.LevelStage
|
|
23
22
|
local RoomDescriptorFlag = ____isaac_2Dtypescript_2Ddefinitions.RoomDescriptorFlag
|
|
24
23
|
local RoomShape = ____isaac_2Dtypescript_2Ddefinitions.RoomShape
|
|
25
24
|
local RoomType = ____isaac_2Dtypescript_2Ddefinitions.RoomType
|
|
@@ -58,9 +57,8 @@ local getRoomData = ____roomData.getRoomData
|
|
|
58
57
|
local getRoomDescriptor = ____roomData.getRoomDescriptor
|
|
59
58
|
local getRoomDescriptorReadOnly = ____roomData.getRoomDescriptorReadOnly
|
|
60
59
|
local getRoomGridIndex = ____roomData.getRoomGridIndex
|
|
61
|
-
local
|
|
62
|
-
local
|
|
63
|
-
local getRoomSubType = ____roomData.getRoomSubType
|
|
60
|
+
local ____roomShape = require("src.functions.roomShape")
|
|
61
|
+
local isLRoomShape = ____roomShape.isLRoomShape
|
|
64
62
|
local ____roomTransition = require("src.functions.roomTransition")
|
|
65
63
|
local reloadRoom = ____roomTransition.reloadRoom
|
|
66
64
|
local ____stage = require("src.functions.stage")
|
|
@@ -136,6 +134,105 @@ function ____exports.getRoomsOutsideGrid(self)
|
|
|
136
134
|
function(____, readOnlyRoomDescriptor) return getRoomDescriptor(nil, readOnlyRoomDescriptor.SafeGridIndex) end
|
|
137
135
|
)
|
|
138
136
|
end
|
|
137
|
+
--- Helper function to determine if the provided room is equal to `RoomShape.1x2` or `RoomShape.2x1`.
|
|
138
|
+
function ____exports.is2x1Room(self, roomData)
|
|
139
|
+
return roomData.Shape == RoomShape.SHAPE_1x2 or roomData.Shape == RoomShape.SHAPE_2x1
|
|
140
|
+
end
|
|
141
|
+
function ____exports.isAngelShop(self, roomData)
|
|
142
|
+
return roomData.Type == RoomType.ANGEL and roomData.Subtype == asNumber(nil, AngelRoomSubType.SHOP)
|
|
143
|
+
end
|
|
144
|
+
function ____exports.isBeastRoom(self, roomData)
|
|
145
|
+
return roomData.Type == RoomType.DUNGEON and roomData.Subtype == asNumber(nil, DungeonSubType.BEAST_ROOM)
|
|
146
|
+
end
|
|
147
|
+
--- Helper function to check if the provided room is a boss room for a particular boss. This will
|
|
148
|
+
-- only work for bosses that have dedicated boss rooms in the "00.special rooms.stb" file.
|
|
149
|
+
function ____exports.isBossRoomOf(self, roomData, bossID)
|
|
150
|
+
return roomData.Type == RoomType.BOSS and roomData.StageID == StageID.SPECIAL_ROOMS and roomData.Subtype == asNumber(nil, bossID)
|
|
151
|
+
end
|
|
152
|
+
--- Helper function for determining whether the provided room is a crawl space. Use this function
|
|
153
|
+
-- over comparing to `RoomType.DUNGEON` or `GridRoom.DUNGEON_IDX` since there is a special case of
|
|
154
|
+
-- the player being in a boss fight that takes place in a dungeon.
|
|
155
|
+
function ____exports.isCrawlSpace(self, roomData)
|
|
156
|
+
return roomData.Type == RoomType.DUNGEON and roomData.Subtype == asNumber(nil, DungeonSubType.NORMAL)
|
|
157
|
+
end
|
|
158
|
+
--- Helper function to detect if the provided room is one of the rooms in the Death Certificate area.
|
|
159
|
+
function ____exports.isDeathCertificateArea(self, roomData)
|
|
160
|
+
return roomData.StageID == StageID.HOME and (roomData.Subtype == asNumber(nil, HomeRoomSubType.DEATH_CERTIFICATE_ENTRANCE) or roomData.Subtype == asNumber(nil, HomeRoomSubType.DEATH_CERTIFICATE_ITEMS))
|
|
161
|
+
end
|
|
162
|
+
--- Helper function to detect if the provided room is a Treasure Room created when entering with a
|
|
163
|
+
-- Devil's Crown trinket.
|
|
164
|
+
--
|
|
165
|
+
-- Under the hood, this checks for `RoomDescriptorFlag.DEVIL_TREASURE`.
|
|
166
|
+
function ____exports.isDevilsCrownTreasureRoom(self, roomDescriptor)
|
|
167
|
+
return hasFlag(nil, roomDescriptor.Flags, RoomDescriptorFlag.DEVIL_TREASURE)
|
|
168
|
+
end
|
|
169
|
+
--- Helper function to detect if the provided room is a Double Trouble Boss Room.
|
|
170
|
+
--
|
|
171
|
+
-- This is performed by checking for the string "Double Trouble" inside of the room name. The
|
|
172
|
+
-- vanilla game uses this convention for every Double Trouble Boss Room. Note that this method might
|
|
173
|
+
-- fail for mods that add extra Double Trouble rooms but do not follow the convention.
|
|
174
|
+
--
|
|
175
|
+
-- Internally, the game is coded to detect Double Trouble Boss Rooms by checking for the variant
|
|
176
|
+
-- range of 3700 through 3850. We intentionally do not use this method since it may not work as well
|
|
177
|
+
-- with modded rooms.
|
|
178
|
+
function ____exports.isDoubleTrouble(self, roomData)
|
|
179
|
+
return roomData.Type == RoomType.BOSS and __TS__StringIncludes(roomData.Name, "Double Trouble")
|
|
180
|
+
end
|
|
181
|
+
--- Helper function to determine if the index of the provided room is equal to `GridRoom.GENESIS`.
|
|
182
|
+
function ____exports.isGenesisRoom(self, roomDescriptor)
|
|
183
|
+
return roomDescriptor.GridIndex == asNumber(nil, GridRoom.GENESIS)
|
|
184
|
+
end
|
|
185
|
+
--- Helper function to check if the provided room is either the left Home closet (behind the red
|
|
186
|
+
-- door) or the right Home closet (with one random pickup).
|
|
187
|
+
--
|
|
188
|
+
-- Home closets have a unique shape that is different from any other room in the game.
|
|
189
|
+
function ____exports.isHomeCloset(self, roomData)
|
|
190
|
+
return roomData.StageID == StageID.HOME and (roomData.Subtype == asNumber(nil, HomeRoomSubType.CLOSET_LEFT) or roomData.Subtype == asNumber(nil, HomeRoomSubType.CLOSET_RIGHT))
|
|
191
|
+
end
|
|
192
|
+
--- Helper function to determine if the provided room is one of the four L room shapes.
|
|
193
|
+
function ____exports.isLRoom(self, roomData)
|
|
194
|
+
return isLRoomShape(nil, roomData.Shape)
|
|
195
|
+
end
|
|
196
|
+
--- Helper function to determine if the index of the provided room is equal to `GridRoom.MEGA_SATAN`.
|
|
197
|
+
function ____exports.isMegaSatanRoom(self, roomDescriptor)
|
|
198
|
+
return roomDescriptor.GridIndex == asNumber(nil, GridRoom.MEGA_SATAN)
|
|
199
|
+
end
|
|
200
|
+
--- Helper function to determine if the provided room is part of the Repentance "escape sequence" in
|
|
201
|
+
-- the Mines/Ashpit.
|
|
202
|
+
function ____exports.isMineShaft(self, roomData)
|
|
203
|
+
return (roomData.StageID == StageID.MINES or roomData.StageID == StageID.ASHPIT) and MINE_SHAFT_ROOM_SUB_TYPE_SET:has(roomData.Subtype)
|
|
204
|
+
end
|
|
205
|
+
--- Helper function to check if the provided room is a miniboss room for a particular miniboss. This
|
|
206
|
+
-- will only work for mini-bosses that have dedicated boss rooms in the "00.special rooms.stb" file.
|
|
207
|
+
function ____exports.isMinibossRoomOf(self, roomData, minibossID)
|
|
208
|
+
return roomData.Type == RoomType.MINI_BOSS and roomData.StageID == StageID.SPECIAL_ROOMS and roomData.Subtype == asNumber(nil, minibossID)
|
|
209
|
+
end
|
|
210
|
+
--- Helper function to check if the provided room is a "mirror room" in Downpour or Dross. (These
|
|
211
|
+
-- rooms are marked with a specific sub-type.)
|
|
212
|
+
function ____exports.isMirrorRoom(self, roomData)
|
|
213
|
+
return roomData.Type == RoomType.DEFAULT and (roomData.StageID == StageID.DOWNPOUR or roomData.StageID == StageID.DROSS) and roomData.Subtype == asNumber(nil, DownpourRoomSubType.MIRROR)
|
|
214
|
+
end
|
|
215
|
+
--- Helper function to check if the provided room matches one of the given room types.
|
|
216
|
+
--
|
|
217
|
+
-- This function is variadic, which means you can pass as many room types as you want to match for.
|
|
218
|
+
function ____exports.isRoomType(self, roomData, ...)
|
|
219
|
+
local roomTypes = {...}
|
|
220
|
+
return __TS__ArrayIncludes(roomTypes, roomData.Type)
|
|
221
|
+
end
|
|
222
|
+
--- Helper function for checking if the provided room is a secret exit that leads to a Repentance
|
|
223
|
+
-- floor.
|
|
224
|
+
function ____exports.isSecretExit(self, roomDescriptor)
|
|
225
|
+
return roomDescriptor.GridIndex == asNumber(nil, GridRoom.SECRET_EXIT)
|
|
226
|
+
end
|
|
227
|
+
--- Helper function for checking if the provided room is a secret shop (from the Member Card
|
|
228
|
+
-- collectible).
|
|
229
|
+
--
|
|
230
|
+
-- Secret shops are simply copies of normal shops, but with the backdrop of a secret room. In other
|
|
231
|
+
-- words, they will have the same room type, room variant, and room sub-type of a normal shop. Thus,
|
|
232
|
+
-- the only way to detect them is by using the grid index.
|
|
233
|
+
function ____exports.isSecretShop(self, roomDescriptor)
|
|
234
|
+
return roomDescriptor.GridIndex == asNumber(nil, GridRoom.SECRET_SHOP)
|
|
235
|
+
end
|
|
139
236
|
local SECRET_ROOM_TYPES = __TS__New(ReadonlySet, {RoomType.SECRET, RoomType.SUPER_SECRET, RoomType.ULTRA_SECRET})
|
|
140
237
|
--- Helper function for quickly switching to a new room without playing a particular animation. Use
|
|
141
238
|
-- this helper function over invoking the `Game.ChangeRoom` method directly to ensure that you do
|
|
@@ -251,45 +348,34 @@ end
|
|
|
251
348
|
--- Helper function to determine if the current room shape is equal to `RoomShape.1x2` or
|
|
252
349
|
-- `RoomShape.2x1`.
|
|
253
350
|
function ____exports.in2x1Room(self)
|
|
254
|
-
local
|
|
255
|
-
|
|
256
|
-
return roomShape == RoomShape.SHAPE_1x2 or roomShape == RoomShape.SHAPE_2x1
|
|
351
|
+
local roomData = getRoomData(nil)
|
|
352
|
+
return ____exports.is2x1Room(nil, roomData)
|
|
257
353
|
end
|
|
258
354
|
function ____exports.inAngelShop(self)
|
|
259
|
-
local
|
|
260
|
-
|
|
261
|
-
local roomSubType = getRoomSubType(nil)
|
|
262
|
-
return roomType == RoomType.ANGEL and roomSubType == asNumber(nil, AngelRoomSubType.SHOP)
|
|
355
|
+
local roomData = getRoomData(nil)
|
|
356
|
+
return ____exports.isAngelShop(nil, roomData)
|
|
263
357
|
end
|
|
264
358
|
function ____exports.inBeastRoom(self)
|
|
265
|
-
local
|
|
266
|
-
|
|
267
|
-
local roomSubType = getRoomSubType(nil)
|
|
268
|
-
return roomType == RoomType.DUNGEON and roomSubType == asNumber(nil, DungeonSubType.BEAST_ROOM)
|
|
359
|
+
local roomData = getRoomData(nil)
|
|
360
|
+
return ____exports.isBeastRoom(nil, roomData)
|
|
269
361
|
end
|
|
270
362
|
--- Helper function to check if the current room is a boss room for a particular boss. This will only
|
|
271
363
|
-- work for bosses that have dedicated boss rooms in the "00.special rooms.stb" file.
|
|
272
364
|
function ____exports.inBossRoomOf(self, bossID)
|
|
273
|
-
local
|
|
274
|
-
|
|
275
|
-
local roomStageID = getRoomStageID(nil)
|
|
276
|
-
local roomSubType = getRoomSubType(nil)
|
|
277
|
-
return roomType == RoomType.BOSS and roomStageID == StageID.SPECIAL_ROOMS and roomSubType == asNumber(nil, bossID)
|
|
365
|
+
local roomData = getRoomData(nil)
|
|
366
|
+
return ____exports.isBossRoomOf(nil, roomData, bossID)
|
|
278
367
|
end
|
|
279
368
|
--- Helper function for determining whether the current room is a crawl space. Use this function over
|
|
280
369
|
-- comparing to `RoomType.DUNGEON` or `GridRoom.DUNGEON_IDX` since there is a special case of the
|
|
281
|
-
-- player being in a boss fight that
|
|
370
|
+
-- player being in a boss fight that takes place in a dungeon.
|
|
282
371
|
function ____exports.inCrawlSpace(self)
|
|
283
|
-
local
|
|
284
|
-
|
|
285
|
-
local roomSubType = getRoomSubType(nil)
|
|
286
|
-
return roomType == RoomType.DUNGEON and roomSubType == asNumber(nil, DungeonSubType.NORMAL)
|
|
372
|
+
local roomData = getRoomData(nil)
|
|
373
|
+
return ____exports.isCrawlSpace(nil, roomData)
|
|
287
374
|
end
|
|
288
375
|
--- Helper function to detect if the current room is one of the rooms in the Death Certificate area.
|
|
289
376
|
function ____exports.inDeathCertificateArea(self)
|
|
290
|
-
local
|
|
291
|
-
|
|
292
|
-
return roomStageID == StageID.HOME and (roomSubType == asNumber(nil, HomeRoomSubType.DEATH_CERTIFICATE_ENTRANCE) or roomSubType == asNumber(nil, HomeRoomSubType.DEATH_CERTIFICATE_ITEMS))
|
|
377
|
+
local roomData = getRoomData(nil)
|
|
378
|
+
return ____exports.isDeathCertificateArea(nil, roomData)
|
|
293
379
|
end
|
|
294
380
|
--- Helper function to detect if the current room is a Treasure Room created when entering with a
|
|
295
381
|
-- Devil's Crown trinket.
|
|
@@ -297,83 +383,74 @@ end
|
|
|
297
383
|
-- Under the hood, this checks for `RoomDescriptorFlag.DEVIL_TREASURE`.
|
|
298
384
|
function ____exports.inDevilsCrownTreasureRoom(self)
|
|
299
385
|
local roomDescriptor = getRoomDescriptorReadOnly(nil)
|
|
300
|
-
return
|
|
386
|
+
return ____exports.isDevilsCrownTreasureRoom(nil, roomDescriptor)
|
|
301
387
|
end
|
|
302
388
|
--- Helper function to detect if the current room is a Double Trouble Boss Room.
|
|
303
389
|
--
|
|
304
390
|
-- This is performed by checking for the string "Double Trouble" inside of the room name. The
|
|
305
391
|
-- vanilla game uses this convention for every Double Trouble Boss Room. Note that this method might
|
|
306
392
|
-- fail for mods that add extra Double Trouble rooms but do not follow the convention.
|
|
393
|
+
--
|
|
394
|
+
-- Internally, the game is coded to detect Double Trouble Boss Rooms by checking for the variant
|
|
395
|
+
-- range of 3700 through 3850. We intentionally do not use this method since it may not work as well
|
|
396
|
+
-- with modded rooms.
|
|
307
397
|
function ____exports.inDoubleTrouble(self)
|
|
308
|
-
local
|
|
309
|
-
|
|
310
|
-
local roomName = getRoomName(nil)
|
|
311
|
-
return roomType == RoomType.BOSS and __TS__StringIncludes(roomName, "Double Trouble")
|
|
398
|
+
local roomData = getRoomData(nil)
|
|
399
|
+
return ____exports.isDoubleTrouble(nil, roomData)
|
|
312
400
|
end
|
|
401
|
+
--- Helper function to determine if the current room index is equal to `GridRoom.GENESIS`.
|
|
313
402
|
function ____exports.inGenesisRoom(self)
|
|
314
|
-
local
|
|
315
|
-
return
|
|
403
|
+
local roomDescriptor = getRoomDescriptorReadOnly(nil)
|
|
404
|
+
return ____exports.isGenesisRoom(nil, roomDescriptor)
|
|
316
405
|
end
|
|
317
406
|
--- Helper function to check if the current room is either the left Home closet (behind the red door)
|
|
318
407
|
-- or the right Home closet (with one random pickup).
|
|
319
408
|
--
|
|
320
409
|
-- Home closets have a unique shape that is different from any other room in the game.
|
|
321
410
|
function ____exports.inHomeCloset(self)
|
|
322
|
-
local
|
|
323
|
-
|
|
324
|
-
local roomSubType = getRoomSubType(nil)
|
|
325
|
-
return stage == LevelStage.HOME and (roomSubType == asNumber(nil, HomeRoomSubType.CLOSET_LEFT) or roomSubType == asNumber(nil, HomeRoomSubType.CLOSET_RIGHT))
|
|
411
|
+
local roomData = getRoomData(nil)
|
|
412
|
+
return ____exports.isHomeCloset(nil, roomData)
|
|
326
413
|
end
|
|
327
414
|
--- Helper function to determine if the current room shape is one of the four L room shapes.
|
|
328
415
|
function ____exports.inLRoom(self)
|
|
329
|
-
local
|
|
330
|
-
|
|
331
|
-
return roomShape == RoomShape.LTL or roomShape == RoomShape.LTR or roomShape == RoomShape.LBL or roomShape == RoomShape.LBR
|
|
416
|
+
local roomData = getRoomData(nil)
|
|
417
|
+
return ____exports.isLRoom(nil, roomData)
|
|
332
418
|
end
|
|
333
419
|
--- Helper function to determine if the current room index is equal to `GridRoom.MEGA_SATAN`.
|
|
334
420
|
function ____exports.inMegaSatanRoom(self)
|
|
335
|
-
local
|
|
336
|
-
return
|
|
421
|
+
local roomDescriptor = getRoomDescriptorReadOnly(nil)
|
|
422
|
+
return ____exports.isMegaSatanRoom(nil, roomDescriptor)
|
|
337
423
|
end
|
|
338
424
|
--- Helper function to determine if the current room is part of the Repentance "escape sequence" in
|
|
339
425
|
-- the Mines/Ashpit.
|
|
340
426
|
function ____exports.inMineShaft(self)
|
|
341
|
-
local
|
|
342
|
-
|
|
343
|
-
return (roomStageID == StageID.MINES or roomStageID == StageID.ASHPIT) and MINE_SHAFT_ROOM_SUB_TYPE_SET:has(roomSubType)
|
|
427
|
+
local roomData = getRoomData(nil)
|
|
428
|
+
return ____exports.isMineShaft(nil, roomData)
|
|
344
429
|
end
|
|
345
430
|
--- Helper function to check if the current room is a miniboss room for a particular miniboss. This
|
|
346
431
|
-- will only work for mini-bosses that have dedicated boss rooms in the "00.special rooms.stb" file.
|
|
347
432
|
function ____exports.inMinibossRoomOf(self, minibossID)
|
|
348
|
-
local
|
|
349
|
-
|
|
350
|
-
local roomStageID = getRoomStageID(nil)
|
|
351
|
-
local roomSubType = getRoomSubType(nil)
|
|
352
|
-
return roomType == RoomType.MINI_BOSS and roomStageID == StageID.SPECIAL_ROOMS and roomSubType == asNumber(nil, minibossID)
|
|
433
|
+
local roomData = getRoomData(nil)
|
|
434
|
+
return ____exports.isMinibossRoomOf(nil, roomData, minibossID)
|
|
353
435
|
end
|
|
354
436
|
--- Helper function to check if the current room is a "mirror room" in Downpour or Dross. (These
|
|
355
437
|
-- rooms are marked with a specific sub-type.)
|
|
356
438
|
function ____exports.inMirrorRoom(self)
|
|
357
|
-
local
|
|
358
|
-
|
|
359
|
-
local roomStageID = getRoomStageID(nil)
|
|
360
|
-
local roomSubType = getRoomSubType(nil)
|
|
361
|
-
return roomType == RoomType.DEFAULT and (roomStageID == StageID.DOWNPOUR or roomStageID == StageID.DROSS) and roomSubType == asNumber(nil, DownpourRoomSubType.MIRROR)
|
|
439
|
+
local roomData = getRoomData(nil)
|
|
440
|
+
return ____exports.isMirrorRoom(nil, roomData)
|
|
362
441
|
end
|
|
363
442
|
--- Helper function to check if the current room matches one of the given room types.
|
|
364
443
|
--
|
|
365
444
|
-- This function is variadic, which means you can pass as many room types as you want to match for.
|
|
366
445
|
function ____exports.inRoomType(self, ...)
|
|
367
|
-
local
|
|
368
|
-
|
|
369
|
-
local thisRoomType = room:GetType()
|
|
370
|
-
return __TS__ArrayIncludes(roomTypes, thisRoomType)
|
|
446
|
+
local roomData = getRoomData(nil)
|
|
447
|
+
return ____exports.isRoomType(nil, roomData, ...)
|
|
371
448
|
end
|
|
372
449
|
--- Helper function for checking if the current room is a secret exit that leads to a Repentance
|
|
373
450
|
-- floor.
|
|
374
451
|
function ____exports.inSecretExit(self)
|
|
375
|
-
local
|
|
376
|
-
return
|
|
452
|
+
local roomDescriptor = getRoomDescriptorReadOnly(nil)
|
|
453
|
+
return ____exports.isSecretExit(nil, roomDescriptor)
|
|
377
454
|
end
|
|
378
455
|
--- Helper function for checking if the current room is a secret shop (from the Member Card
|
|
379
456
|
-- collectible).
|
|
@@ -382,8 +459,8 @@ end
|
|
|
382
459
|
-- words, they will have the same room type, room variant, and room sub-type of a normal shop. Thus,
|
|
383
460
|
-- the only way to detect them is by using the grid index.
|
|
384
461
|
function ____exports.inSecretShop(self)
|
|
385
|
-
local
|
|
386
|
-
return
|
|
462
|
+
local roomDescriptor = getRoomDescriptorReadOnly(nil)
|
|
463
|
+
return ____exports.isSecretShop(nil, roomDescriptor)
|
|
387
464
|
end
|
|
388
465
|
--- Helper function to determine whether or not the current room is the starting room of a floor. It
|
|
389
466
|
-- only returns true for the starting room of the primary dimension (meaning that being in the
|
|
@@ -477,12 +554,12 @@ function ____exports.setRoomCleared(self)
|
|
|
477
554
|
for ____, door in ipairs(getDoors(nil)) do
|
|
478
555
|
do
|
|
479
556
|
if isHiddenSecretRoomDoor(nil, door) then
|
|
480
|
-
goto
|
|
557
|
+
goto __continue77
|
|
481
558
|
end
|
|
482
559
|
openDoorFast(nil, door)
|
|
483
560
|
door.ExtraVisible = false
|
|
484
561
|
end
|
|
485
|
-
::
|
|
562
|
+
::__continue77::
|
|
486
563
|
end
|
|
487
564
|
sfxManager:Stop(SoundEffect.DOOR_HEAVY_OPEN)
|
|
488
565
|
game:ShakeScreen(0)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "isaacscript-common",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "31.0.1",
|
|
4
4
|
"description": "Helper functions and features for IsaacScript mods.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"isaac",
|
|
@@ -25,6 +25,6 @@
|
|
|
25
25
|
"main": "dist/src/index",
|
|
26
26
|
"types": "dist/index.rollup.d.ts",
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"isaac-typescript-definitions": "^13.0.
|
|
28
|
+
"isaac-typescript-definitions": "^13.0.24"
|
|
29
29
|
}
|
|
30
30
|
}
|
|
@@ -99,7 +99,7 @@ export class ItemPoolDetection extends Feature {
|
|
|
99
99
|
}
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
-
const
|
|
102
|
+
const { removedItemsMap, removedTrinketsMap } =
|
|
103
103
|
removeItemsAndTrinketsThatAffectItemPools();
|
|
104
104
|
|
|
105
105
|
// Blacklist every collectible in the game except for the provided collectible.
|
|
@@ -173,10 +173,10 @@ export class ItemPoolDetection extends Feature {
|
|
|
173
173
|
* Before checking the item pools, remove any collectibles or trinkets that would affect the
|
|
174
174
|
* retrieved collectible types.
|
|
175
175
|
*/
|
|
176
|
-
function removeItemsAndTrinketsThatAffectItemPools():
|
|
177
|
-
removedItemsMap: Map<PlayerIndex, CollectibleType[]
|
|
178
|
-
removedTrinketsMap: Map<PlayerIndex, TrinketType[]
|
|
179
|
-
|
|
176
|
+
function removeItemsAndTrinketsThatAffectItemPools(): {
|
|
177
|
+
removedItemsMap: Map<PlayerIndex, CollectibleType[]>;
|
|
178
|
+
removedTrinketsMap: Map<PlayerIndex, TrinketType[]>;
|
|
179
|
+
} {
|
|
180
180
|
const removedItemsMap = new Map<PlayerIndex, CollectibleType[]>();
|
|
181
181
|
const removedTrinketsMap = new Map<PlayerIndex, TrinketType[]>();
|
|
182
182
|
for (const player of getAllPlayers()) {
|
|
@@ -207,7 +207,7 @@ function removeItemsAndTrinketsThatAffectItemPools(): [
|
|
|
207
207
|
mapSetPlayer(removedTrinketsMap, player, removedTrinkets);
|
|
208
208
|
}
|
|
209
209
|
|
|
210
|
-
return
|
|
210
|
+
return { removedItemsMap, removedTrinketsMap };
|
|
211
211
|
}
|
|
212
212
|
|
|
213
213
|
function restoreItemsAndTrinketsThatAffectItemPools(
|
|
@@ -14,7 +14,7 @@ import { LadderSubTypeCustom } from "../../../../enums/LadderSubTypeCustom";
|
|
|
14
14
|
import { getRandomArrayElement } from "../../../../functions/array";
|
|
15
15
|
import { spawnEffectWithSeed } from "../../../../functions/entitiesSpecific";
|
|
16
16
|
import { newRNG } from "../../../../functions/rng";
|
|
17
|
-
import {
|
|
17
|
+
import { isLRoomShape, isNarrowRoom } from "../../../../functions/roomShape";
|
|
18
18
|
import {
|
|
19
19
|
removeCharactersBefore,
|
|
20
20
|
trimPrefix,
|
|
@@ -155,7 +155,7 @@ function spawnWallEntity(
|
|
|
155
155
|
);
|
|
156
156
|
}
|
|
157
157
|
|
|
158
|
-
if (
|
|
158
|
+
if (isLRoomShape(roomShape)) {
|
|
159
159
|
const cornerPNGPath = getBackdropPNGPath(
|
|
160
160
|
customStage,
|
|
161
161
|
BackdropKind.CORNER,
|
|
@@ -223,7 +223,7 @@ function spawnFloorEntity(customStage: CustomStage, rng: RNG) {
|
|
|
223
223
|
);
|
|
224
224
|
sprite.ReplaceSpritesheet(layerID, wallPNGPath);
|
|
225
225
|
}
|
|
226
|
-
} else if (
|
|
226
|
+
} else if (isLRoomShape(roomShape)) {
|
|
227
227
|
for (const layerID of L_FLOOR_ANM2_LAYERS) {
|
|
228
228
|
const LFloorPNGPath = getBackdropPNGPath(
|
|
229
229
|
customStage,
|
package/src/functions/hex.ts
CHANGED
|
@@ -9,7 +9,7 @@ const HEX_STRING_LENGTH = 6;
|
|
|
9
9
|
* @param alpha Optional. Range is from 0 to 1. Default is 1. (The same as the `Color` constructor.)
|
|
10
10
|
*/
|
|
11
11
|
export function hexToColor(hexString: string, alpha = 1): Readonly<Color> {
|
|
12
|
-
const
|
|
12
|
+
const { r, g, b } = hexToRGB(hexString);
|
|
13
13
|
|
|
14
14
|
// Color values should be between 0 and 1.
|
|
15
15
|
const base = 255;
|
|
@@ -23,40 +23,40 @@ export function hexToColor(hexString: string, alpha = 1): Readonly<Color> {
|
|
|
23
23
|
* @param alpha Range is from 0 to 1. Default is 1.
|
|
24
24
|
*/
|
|
25
25
|
export function hexToKColor(hexString: string, alpha = 1): Readonly<KColor> {
|
|
26
|
-
const
|
|
26
|
+
const { r, g, b } = hexToRGB(hexString);
|
|
27
27
|
|
|
28
28
|
// KColor values should be between 0 and 1.
|
|
29
29
|
const base = 255;
|
|
30
30
|
return KColor(r / base, g / base, b / base, alpha);
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
function hexToRGB(hexString: string):
|
|
33
|
+
function hexToRGB(hexString: string): { r: float; g: float; b: float } {
|
|
34
34
|
hexString = hexString.replace("#", "");
|
|
35
35
|
if (hexString.length !== HEX_STRING_LENGTH) {
|
|
36
36
|
logError(`Hex strings must be of length: ${HEX_STRING_LENGTH}`);
|
|
37
|
-
return
|
|
37
|
+
return { r: 0, g: 0, b: 0 };
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
const rString = hexString.slice(0, 2);
|
|
41
41
|
const r = tonumber(`0x${rString}`);
|
|
42
42
|
if (r === undefined) {
|
|
43
43
|
logError(`Failed to convert \`0x${rString}\` to a number.`);
|
|
44
|
-
return
|
|
44
|
+
return { r: 0, g: 0, b: 0 };
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
const gString = hexString.slice(2, 4);
|
|
48
48
|
const g = tonumber(`0x${gString}`);
|
|
49
49
|
if (g === undefined) {
|
|
50
50
|
logError(`Failed to convert \`0x${gString}\` to a number.`);
|
|
51
|
-
return
|
|
51
|
+
return { r: 0, g: 0, b: 0 };
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
const bString = hexString.slice(4, 6);
|
|
55
55
|
const b = tonumber(`0x${bString}`);
|
|
56
56
|
if (b === undefined) {
|
|
57
57
|
logError(`Failed to convert \`0x${bString}\` to a number.`);
|
|
58
|
-
return
|
|
58
|
+
return { r: 0, g: 0, b: 0 };
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
return
|
|
61
|
+
return { r, g, b };
|
|
62
62
|
}
|
|
@@ -10,9 +10,7 @@
|
|
|
10
10
|
import type { DoorSlot, RoomShape } from "isaac-typescript-definitions";
|
|
11
11
|
import {
|
|
12
12
|
DisplayFlag,
|
|
13
|
-
DownpourRoomSubType,
|
|
14
13
|
LevelStateFlag,
|
|
15
|
-
MinesRoomSubType,
|
|
16
14
|
RoomDescriptorFlag,
|
|
17
15
|
RoomType,
|
|
18
16
|
} from "isaac-typescript-definitions";
|
|
@@ -36,8 +34,13 @@ import {
|
|
|
36
34
|
getRoomShape,
|
|
37
35
|
} from "./roomData";
|
|
38
36
|
import { getGridIndexDelta } from "./roomShape";
|
|
39
|
-
import {
|
|
40
|
-
|
|
37
|
+
import {
|
|
38
|
+
getRooms,
|
|
39
|
+
getRoomsInsideGrid,
|
|
40
|
+
isMineShaft,
|
|
41
|
+
isMirrorRoom,
|
|
42
|
+
isSecretRoomType,
|
|
43
|
+
} from "./rooms";
|
|
41
44
|
|
|
42
45
|
const LEFT = -1;
|
|
43
46
|
const UP = -LEVEL_GRID_ROW_WIDTH;
|
|
@@ -123,17 +126,23 @@ export function getAllRoomGridIndexes(): readonly int[] {
|
|
|
123
126
|
*
|
|
124
127
|
* @param seedOrRNG Optional. The `Seed` or `RNG` object to use. If an `RNG` object is provided, the
|
|
125
128
|
* `RNG.Next` method will be called. Default is `getRandomSeed()`.
|
|
129
|
+
* @param ensureDeadEnd Optional. Whether to pick a valid dead end attached to a normal room. If
|
|
130
|
+
* false, the function will randomly pick from any valid location that would
|
|
131
|
+
* have a red door.
|
|
126
132
|
* @returns Either a tuple of adjacent room grid index, `DoorSlot`, and new room grid index, or
|
|
127
133
|
* undefined.
|
|
128
134
|
*/
|
|
129
|
-
export function getNewRoomCandidate(
|
|
135
|
+
export function getNewRoomCandidate(
|
|
136
|
+
seedOrRNG: Seed | RNG = getRandomSeed(),
|
|
137
|
+
ensureDeadEnd = true,
|
|
138
|
+
):
|
|
130
139
|
| {
|
|
131
140
|
readonly adjacentRoomGridIndex: int;
|
|
132
141
|
readonly doorSlot: DoorSlot;
|
|
133
142
|
readonly newRoomGridIndex: int;
|
|
134
143
|
}
|
|
135
144
|
| undefined {
|
|
136
|
-
const newRoomCandidatesForLevel = getNewRoomCandidatesForLevel();
|
|
145
|
+
const newRoomCandidatesForLevel = getNewRoomCandidatesForLevel(ensureDeadEnd);
|
|
137
146
|
if (newRoomCandidatesForLevel.length === 0) {
|
|
138
147
|
return undefined;
|
|
139
148
|
}
|
|
@@ -143,14 +152,17 @@ export function getNewRoomCandidate(seedOrRNG: Seed | RNG = getRandomSeed()):
|
|
|
143
152
|
|
|
144
153
|
/**
|
|
145
154
|
* Helper function to iterate through the possible doors for a room and see if any of them would be
|
|
146
|
-
* a valid spot to insert a brand new room on the floor.
|
|
147
|
-
* connected to any other existing rooms on the floor.)
|
|
155
|
+
* a valid spot to insert a brand new room on the floor.
|
|
148
156
|
*
|
|
149
157
|
* @param roomGridIndex Optional. Default is the current room index.
|
|
158
|
+
* @param ensureDeadEnd Optional. Whether to only include doors that lead to a valid dead end
|
|
159
|
+
* attached to a normal room. If false, the function will include all doors
|
|
160
|
+
* that would have a red door.
|
|
150
161
|
* @returns A array of tuples of `DoorSlot` and room grid index.
|
|
151
162
|
*/
|
|
152
163
|
export function getNewRoomCandidatesBesideRoom(
|
|
153
164
|
roomGridIndex?: int,
|
|
165
|
+
ensureDeadEnd = true,
|
|
154
166
|
): ReadonlyArray<{ readonly doorSlot: DoorSlot; readonly roomGridIndex: int }> {
|
|
155
167
|
const roomDescriptor = getRoomDescriptor(roomGridIndex);
|
|
156
168
|
|
|
@@ -189,7 +201,7 @@ export function getNewRoomCandidatesBesideRoom(
|
|
|
189
201
|
// Check to see if hypothetically creating a room at the given room grid index would be a dead
|
|
190
202
|
// end. In other words, if we created the room, we would only want it to connect to one other
|
|
191
203
|
// room (this one).
|
|
192
|
-
if (!isDeadEnd(adjacentRoomGridIndex)) {
|
|
204
|
+
if (ensureDeadEnd && !isDeadEnd(adjacentRoomGridIndex)) {
|
|
193
205
|
continue;
|
|
194
206
|
}
|
|
195
207
|
|
|
@@ -203,13 +215,17 @@ export function getNewRoomCandidatesBesideRoom(
|
|
|
203
215
|
}
|
|
204
216
|
|
|
205
217
|
/**
|
|
206
|
-
* Helper function to
|
|
207
|
-
* room.
|
|
218
|
+
* Helper function to get all of the spots on the floor to insert a brand new room.
|
|
208
219
|
*
|
|
220
|
+
* @param ensureDeadEnd Optional. Whether to only include spots that are a valid dead end attached
|
|
221
|
+
* to a normal room. If false, the function will include all valid spots that
|
|
222
|
+
* have a red door.
|
|
209
223
|
* @returns A array of tuples containing the adjacent room grid index, the `DoorSlot`, and the new
|
|
210
224
|
* room grid index.
|
|
211
225
|
*/
|
|
212
|
-
export function getNewRoomCandidatesForLevel(
|
|
226
|
+
export function getNewRoomCandidatesForLevel(
|
|
227
|
+
ensureDeadEnd = true,
|
|
228
|
+
): ReadonlyArray<{
|
|
213
229
|
readonly adjacentRoomGridIndex: int;
|
|
214
230
|
readonly doorSlot: DoorSlot;
|
|
215
231
|
readonly newRoomGridIndex: int;
|
|
@@ -222,21 +238,22 @@ export function getNewRoomCandidatesForLevel(): ReadonlyArray<{
|
|
|
222
238
|
(room) =>
|
|
223
239
|
room.Data !== undefined &&
|
|
224
240
|
room.Data.Type === RoomType.DEFAULT &&
|
|
225
|
-
|
|
226
|
-
//
|
|
227
|
-
room.Data.Subtype !== asNumber(DownpourRoomSubType.MIRROR) &&
|
|
228
|
-
room.Data.Subtype !== asNumber(MinesRoomSubType.MINESHAFT_ENTRANCE),
|
|
241
|
+
!isMirrorRoom(room.Data) && // Mirror rooms do not count as special rooms.
|
|
242
|
+
!isMineShaft(room.Data), // Mineshaft rooms do not count as special rooms.
|
|
229
243
|
);
|
|
230
244
|
|
|
245
|
+
const roomsToLookThrough = ensureDeadEnd ? normalRooms : rooms;
|
|
246
|
+
|
|
231
247
|
const newRoomCandidates: Array<{
|
|
232
248
|
readonly adjacentRoomGridIndex: int;
|
|
233
249
|
readonly doorSlot: DoorSlot;
|
|
234
250
|
readonly newRoomGridIndex: int;
|
|
235
251
|
}> = [];
|
|
236
252
|
|
|
237
|
-
for (const room of
|
|
253
|
+
for (const room of roomsToLookThrough) {
|
|
238
254
|
const newRoomCandidatesBesideRoom = getNewRoomCandidatesBesideRoom(
|
|
239
255
|
room.SafeGridIndex,
|
|
256
|
+
ensureDeadEnd,
|
|
240
257
|
);
|
|
241
258
|
for (const { doorSlot, roomGridIndex } of newRoomCandidatesBesideRoom) {
|
|
242
259
|
newRoomCandidates.push({
|
|
@@ -489,23 +506,29 @@ export function isRoomInsideGrid(roomGridIndex?: int): boolean {
|
|
|
489
506
|
}
|
|
490
507
|
|
|
491
508
|
/**
|
|
492
|
-
* Helper function to generate a new room on the floor
|
|
493
|
-
* room.
|
|
509
|
+
* Helper function to generate a new room on the floor.
|
|
494
510
|
*
|
|
495
511
|
* Under the hood, this function uses the `Level.MakeRedRoomDoor` method to create the room.
|
|
496
512
|
*
|
|
497
|
-
* The newly created room will have data corresponding to the game's randomly generated red room. If
|
|
498
|
-
* you want to modify this, use the `setRoomData` helper function.
|
|
499
|
-
*
|
|
500
513
|
* @param seedOrRNG Optional. The `Seed` or `RNG` object to use. If an `RNG` object is provided, the
|
|
501
514
|
* `RNG.Next` method will be called. Default is `Level.GetDungeonPlacementSeed`.
|
|
502
515
|
* Note that the RNG is only used to select the random location to put the room on
|
|
503
516
|
* the floor; it does not influence the randomly chosen room contents. (That is
|
|
504
517
|
* performed by the game and can not be manipulated prior to its generation.)
|
|
518
|
+
* @param ensureDeadEnd Optional. Whether to place the room at a valid dead end attached to a normal
|
|
519
|
+
* room. If false, it will randomly appear at any valid location that would
|
|
520
|
+
* have a red door.
|
|
521
|
+
* @param customRoomData Optional. By default, the newly created room will have data corresponding
|
|
522
|
+
* to the game's randomly generated red room. If you provide this function
|
|
523
|
+
* with room data, it will be used to override the vanilla data.
|
|
505
524
|
* @returns The room grid index of the new room or undefined if the floor had no valid dead ends to
|
|
506
525
|
* place a room.
|
|
507
526
|
*/
|
|
508
|
-
export function newRoom(
|
|
527
|
+
export function newRoom(
|
|
528
|
+
seedOrRNG?: Seed | RNG,
|
|
529
|
+
ensureDeadEnd = true,
|
|
530
|
+
customRoomData?: RoomConfig,
|
|
531
|
+
): int | undefined {
|
|
509
532
|
const level = game.GetLevel();
|
|
510
533
|
|
|
511
534
|
if (seedOrRNG === undefined) {
|
|
@@ -513,7 +536,7 @@ export function newRoom(seedOrRNG?: Seed | RNG): int | undefined {
|
|
|
513
536
|
}
|
|
514
537
|
const rng = isRNG(seedOrRNG) ? seedOrRNG : newRNG(seedOrRNG);
|
|
515
538
|
|
|
516
|
-
const newRoomCandidate = getNewRoomCandidate(rng);
|
|
539
|
+
const newRoomCandidate = getNewRoomCandidate(rng, ensureDeadEnd);
|
|
517
540
|
if (newRoomCandidate === undefined) {
|
|
518
541
|
return undefined;
|
|
519
542
|
}
|
|
@@ -530,6 +553,10 @@ export function newRoom(seedOrRNG?: Seed | RNG): int | undefined {
|
|
|
530
553
|
RoomDescriptorFlag.RED_ROOM,
|
|
531
554
|
);
|
|
532
555
|
|
|
556
|
+
if (customRoomData !== undefined) {
|
|
557
|
+
roomDescriptor.Data = customRoomData;
|
|
558
|
+
}
|
|
559
|
+
|
|
533
560
|
// By default, the new room will not appear on the map, even if the player has The Mind. Thus, we
|
|
534
561
|
// must manually alter the `DisplayFlags` of the room descriptor.
|
|
535
562
|
const roomData = roomDescriptor.Data;
|