isaacscript-common 30.12.11 → 31.0.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.
@@ -12,7 +12,6 @@ import {
12
12
  DungeonSubType,
13
13
  GridRoom,
14
14
  HomeRoomSubType,
15
- LevelStage,
16
15
  RoomDescriptorFlag,
17
16
  RoomShape,
18
17
  RoomType,
@@ -44,10 +43,8 @@ import {
44
43
  getRoomDescriptor,
45
44
  getRoomDescriptorReadOnly,
46
45
  getRoomGridIndex,
47
- getRoomName,
48
- getRoomStageID,
49
- getRoomSubType,
50
46
  } from "./roomData";
47
+ import { isLRoomShape } from "./roomShape";
51
48
  import { reloadRoom } from "./roomTransition";
52
49
  import { getGotoCommand } from "./stage";
53
50
  import { asNumber } from "./types";
@@ -293,32 +290,18 @@ export function getRoomsOutsideGrid(): RoomDescriptor[] {
293
290
  * `RoomShape.2x1`.
294
291
  */
295
292
  export function in2x1Room(): boolean {
296
- const room = game.GetRoom();
297
- const roomShape = room.GetRoomShape();
298
-
299
- return roomShape === RoomShape.SHAPE_1x2 || roomShape === RoomShape.SHAPE_2x1;
293
+ const roomData = getRoomData();
294
+ return is2x1Room(roomData);
300
295
  }
301
296
 
302
297
  export function inAngelShop(): boolean {
303
- const room = game.GetRoom();
304
- const roomType = room.GetType();
305
- const roomSubType = getRoomSubType();
306
-
307
- return (
308
- roomType === RoomType.ANGEL &&
309
- roomSubType === asNumber(AngelRoomSubType.SHOP)
310
- );
298
+ const roomData = getRoomData();
299
+ return isAngelShop(roomData);
311
300
  }
312
301
 
313
302
  export function inBeastRoom(): boolean {
314
- const room = game.GetRoom();
315
- const roomType = room.GetType();
316
- const roomSubType = getRoomSubType();
317
-
318
- return (
319
- roomType === RoomType.DUNGEON &&
320
- roomSubType === asNumber(DungeonSubType.BEAST_ROOM)
321
- );
303
+ const roomData = getRoomData();
304
+ return isBeastRoom(roomData);
322
305
  }
323
306
 
324
307
  /**
@@ -326,46 +309,26 @@ export function inBeastRoom(): boolean {
326
309
  * work for bosses that have dedicated boss rooms in the "00.special rooms.stb" file.
327
310
  */
328
311
  export function inBossRoomOf(bossID: BossID): boolean {
329
- const room = game.GetRoom();
330
- const roomType = room.GetType();
331
- const roomStageID = getRoomStageID();
332
- const roomSubType = getRoomSubType();
333
-
334
- return (
335
- roomType === RoomType.BOSS &&
336
- roomStageID === StageID.SPECIAL_ROOMS &&
337
- roomSubType === asNumber(bossID)
338
- );
312
+ const roomData = getRoomData();
313
+ return isBossRoomOf(roomData, bossID);
339
314
  }
340
315
 
341
316
  /**
342
317
  * Helper function for determining whether the current room is a crawl space. Use this function over
343
318
  * comparing to `RoomType.DUNGEON` or `GridRoom.DUNGEON_IDX` since there is a special case of the
344
- * player being in a boss fight that take place in a dungeon.
319
+ * player being in a boss fight that takes place in a dungeon.
345
320
  */
346
321
  export function inCrawlSpace(): boolean {
347
- const room = game.GetRoom();
348
- const roomType = room.GetType();
349
- const roomSubType = getRoomSubType();
350
-
351
- return (
352
- roomType === RoomType.DUNGEON &&
353
- roomSubType === asNumber(DungeonSubType.NORMAL)
354
- );
322
+ const roomData = getRoomData();
323
+ return isCrawlSpace(roomData);
355
324
  }
356
325
 
357
326
  /**
358
327
  * Helper function to detect if the current room is one of the rooms in the Death Certificate area.
359
328
  */
360
329
  export function inDeathCertificateArea(): boolean {
361
- const roomStageID = getRoomStageID();
362
- const roomSubType = getRoomSubType();
363
-
364
- return (
365
- roomStageID === StageID.HOME &&
366
- (roomSubType === asNumber(HomeRoomSubType.DEATH_CERTIFICATE_ENTRANCE) ||
367
- roomSubType === asNumber(HomeRoomSubType.DEATH_CERTIFICATE_ITEMS))
368
- );
330
+ const roomData = getRoomData();
331
+ return isDeathCertificateArea(roomData);
369
332
  }
370
333
 
371
334
  /**
@@ -376,7 +339,7 @@ export function inDeathCertificateArea(): boolean {
376
339
  */
377
340
  export function inDevilsCrownTreasureRoom(): boolean {
378
341
  const roomDescriptor = getRoomDescriptorReadOnly();
379
- return hasFlag(roomDescriptor.Flags, RoomDescriptorFlag.DEVIL_TREASURE);
342
+ return isDevilsCrownTreasureRoom(roomDescriptor);
380
343
  }
381
344
 
382
345
  /**
@@ -385,19 +348,20 @@ export function inDevilsCrownTreasureRoom(): boolean {
385
348
  * This is performed by checking for the string "Double Trouble" inside of the room name. The
386
349
  * vanilla game uses this convention for every Double Trouble Boss Room. Note that this method might
387
350
  * fail for mods that add extra Double Trouble rooms but do not follow the convention.
351
+ *
352
+ * Internally, the game is coded to detect Double Trouble Boss Rooms by checking for the variant
353
+ * range of 3700 through 3850. We intentionally do not use this method since it may not work as well
354
+ * with modded rooms.
388
355
  */
389
356
  export function inDoubleTrouble(): boolean {
390
- const room = game.GetRoom();
391
- const roomType = room.GetType();
392
- const roomName = getRoomName();
393
-
394
- return roomType === RoomType.BOSS && roomName.includes("Double Trouble");
357
+ const roomData = getRoomData();
358
+ return isDoubleTrouble(roomData);
395
359
  }
396
360
 
361
+ /** Helper function to determine if the current room index is equal to `GridRoom.GENESIS`. */
397
362
  export function inGenesisRoom(): boolean {
398
- const roomGridIndex = getRoomGridIndex();
399
-
400
- return roomGridIndex === asNumber(GridRoom.GENESIS);
363
+ const roomDescriptor = getRoomDescriptorReadOnly();
364
+ return isGenesisRoom(roomDescriptor);
401
365
  }
402
366
 
403
367
  /**
@@ -407,35 +371,20 @@ export function inGenesisRoom(): boolean {
407
371
  * Home closets have a unique shape that is different from any other room in the game.
408
372
  */
409
373
  export function inHomeCloset(): boolean {
410
- const level = game.GetLevel();
411
- const stage = level.GetStage();
412
- const roomSubType = getRoomSubType();
413
-
414
- return (
415
- stage === LevelStage.HOME &&
416
- (roomSubType === asNumber(HomeRoomSubType.CLOSET_LEFT) ||
417
- roomSubType === asNumber(HomeRoomSubType.CLOSET_RIGHT))
418
- );
374
+ const roomData = getRoomData();
375
+ return isHomeCloset(roomData);
419
376
  }
420
377
 
421
378
  /** Helper function to determine if the current room shape is one of the four L room shapes. */
422
379
  export function inLRoom(): boolean {
423
- const room = game.GetRoom();
424
- const roomShape = room.GetRoomShape();
425
-
426
- return (
427
- roomShape === RoomShape.LTL ||
428
- roomShape === RoomShape.LTR ||
429
- roomShape === RoomShape.LBL ||
430
- roomShape === RoomShape.LBR
431
- );
380
+ const roomData = getRoomData();
381
+ return isLRoom(roomData);
432
382
  }
433
383
 
434
384
  /** Helper function to determine if the current room index is equal to `GridRoom.MEGA_SATAN`. */
435
385
  export function inMegaSatanRoom(): boolean {
436
- const roomGridIndex = getRoomGridIndex();
437
-
438
- return roomGridIndex === asNumber(GridRoom.MEGA_SATAN);
386
+ const roomDescriptor = getRoomDescriptorReadOnly();
387
+ return isMegaSatanRoom(roomDescriptor);
439
388
  }
440
389
 
441
390
  /**
@@ -443,14 +392,8 @@ export function inMegaSatanRoom(): boolean {
443
392
  * the Mines/Ashpit.
444
393
  */
445
394
  export function inMineShaft(): boolean {
446
- const roomStageID = getRoomStageID();
447
- const roomSubType = getRoomSubType();
448
-
449
- return (
450
- (roomStageID === StageID.MINES || roomStageID === StageID.ASHPIT) &&
451
- // eslint-disable-next-line isaacscript/strict-enums
452
- MINE_SHAFT_ROOM_SUB_TYPE_SET.has(roomSubType)
453
- );
395
+ const roomData = getRoomData();
396
+ return isMineShaft(roomData);
454
397
  }
455
398
 
456
399
  /**
@@ -458,16 +401,8 @@ export function inMineShaft(): boolean {
458
401
  * will only work for mini-bosses that have dedicated boss rooms in the "00.special rooms.stb" file.
459
402
  */
460
403
  export function inMinibossRoomOf(minibossID: MinibossID): boolean {
461
- const room = game.GetRoom();
462
- const roomType = room.GetType();
463
- const roomStageID = getRoomStageID();
464
- const roomSubType = getRoomSubType();
465
-
466
- return (
467
- roomType === RoomType.MINI_BOSS &&
468
- roomStageID === StageID.SPECIAL_ROOMS &&
469
- roomSubType === asNumber(minibossID)
470
- );
404
+ const roomData = getRoomData();
405
+ return isMinibossRoomOf(roomData, minibossID);
471
406
  }
472
407
 
473
408
  /**
@@ -475,16 +410,8 @@ export function inMinibossRoomOf(minibossID: MinibossID): boolean {
475
410
  * rooms are marked with a specific sub-type.)
476
411
  */
477
412
  export function inMirrorRoom(): boolean {
478
- const room = game.GetRoom();
479
- const roomType = room.GetType();
480
- const roomStageID = getRoomStageID();
481
- const roomSubType = getRoomSubType();
482
-
483
- return (
484
- roomType === RoomType.DEFAULT &&
485
- (roomStageID === StageID.DOWNPOUR || roomStageID === StageID.DROSS) &&
486
- roomSubType === asNumber(DownpourRoomSubType.MIRROR)
487
- );
413
+ const roomData = getRoomData();
414
+ return isMirrorRoom(roomData);
488
415
  }
489
416
 
490
417
  /**
@@ -493,9 +420,8 @@ export function inMirrorRoom(): boolean {
493
420
  * This function is variadic, which means you can pass as many room types as you want to match for.
494
421
  */
495
422
  export function inRoomType(...roomTypes: RoomType[]): boolean {
496
- const room = game.GetRoom();
497
- const thisRoomType = room.GetType();
498
- return roomTypes.includes(thisRoomType);
423
+ const roomData = getRoomData();
424
+ return isRoomType(roomData, ...roomTypes);
499
425
  }
500
426
 
501
427
  /**
@@ -503,9 +429,8 @@ export function inRoomType(...roomTypes: RoomType[]): boolean {
503
429
  * floor.
504
430
  */
505
431
  export function inSecretExit(): boolean {
506
- const roomGridIndex = getRoomGridIndex();
507
-
508
- return roomGridIndex === asNumber(GridRoom.SECRET_EXIT);
432
+ const roomDescriptor = getRoomDescriptorReadOnly();
433
+ return isSecretExit(roomDescriptor);
509
434
  }
510
435
 
511
436
  /**
@@ -517,9 +442,8 @@ export function inSecretExit(): boolean {
517
442
  * the only way to detect them is by using the grid index.
518
443
  */
519
444
  export function inSecretShop(): boolean {
520
- const roomGridIndex = getRoomGridIndex();
521
-
522
- return roomGridIndex === asNumber(GridRoom.SECRET_SHOP);
445
+ const roomDescriptor = getRoomDescriptorReadOnly();
446
+ return isSecretShop(roomDescriptor);
523
447
  }
524
448
 
525
449
  /**
@@ -535,6 +459,16 @@ export function inStartingRoom(): boolean {
535
459
  return roomGridIndex === startingRoomGridIndex && inDimension(Dimension.MAIN);
536
460
  }
537
461
 
462
+ /**
463
+ * Helper function to determine if the provided room is equal to `RoomShape.1x2` or `RoomShape.2x1`.
464
+ */
465
+ export function is2x1Room(roomData: RoomConfig): boolean {
466
+ return (
467
+ roomData.Shape === RoomShape.SHAPE_1x2 ||
468
+ roomData.Shape === RoomShape.SHAPE_2x1
469
+ );
470
+ }
471
+
538
472
  /**
539
473
  * Helper function to loop through every room on the floor and see if it has been cleared.
540
474
  *
@@ -588,6 +522,179 @@ export function isAllRoomsClear(
588
522
  return matchingRooms.every((roomDescriptor) => roomDescriptor.Clear);
589
523
  }
590
524
 
525
+ export function isAngelShop(roomData: RoomConfig): boolean {
526
+ return (
527
+ roomData.Type === RoomType.ANGEL &&
528
+ roomData.Subtype === asNumber(AngelRoomSubType.SHOP)
529
+ );
530
+ }
531
+
532
+ export function isBeastRoom(roomData: RoomConfig): boolean {
533
+ return (
534
+ roomData.Type === RoomType.DUNGEON &&
535
+ roomData.Subtype === asNumber(DungeonSubType.BEAST_ROOM)
536
+ );
537
+ }
538
+
539
+ /**
540
+ * Helper function to check if the provided room is a boss room for a particular boss. This will
541
+ * only work for bosses that have dedicated boss rooms in the "00.special rooms.stb" file.
542
+ */
543
+ export function isBossRoomOf(roomData: RoomConfig, bossID: BossID): boolean {
544
+ return (
545
+ roomData.Type === RoomType.BOSS &&
546
+ roomData.StageID === StageID.SPECIAL_ROOMS &&
547
+ roomData.Subtype === asNumber(bossID)
548
+ );
549
+ }
550
+
551
+ /**
552
+ * Helper function for determining whether the provided room is a crawl space. Use this function
553
+ * over comparing to `RoomType.DUNGEON` or `GridRoom.DUNGEON_IDX` since there is a special case of
554
+ * the player being in a boss fight that takes place in a dungeon.
555
+ */
556
+ export function isCrawlSpace(roomData: RoomConfig): boolean {
557
+ return (
558
+ roomData.Type === RoomType.DUNGEON &&
559
+ roomData.Subtype === asNumber(DungeonSubType.NORMAL)
560
+ );
561
+ }
562
+
563
+ /**
564
+ * Helper function to detect if the provided room is one of the rooms in the Death Certificate area.
565
+ */
566
+ export function isDeathCertificateArea(roomData: RoomConfig): boolean {
567
+ return (
568
+ roomData.StageID === StageID.HOME &&
569
+ (roomData.Subtype ===
570
+ asNumber(HomeRoomSubType.DEATH_CERTIFICATE_ENTRANCE) ||
571
+ roomData.Subtype === asNumber(HomeRoomSubType.DEATH_CERTIFICATE_ITEMS))
572
+ );
573
+ }
574
+
575
+ /**
576
+ * Helper function to detect if the provided room is a Treasure Room created when entering with a
577
+ * Devil's Crown trinket.
578
+ *
579
+ * Under the hood, this checks for `RoomDescriptorFlag.DEVIL_TREASURE`.
580
+ */
581
+ export function isDevilsCrownTreasureRoom(
582
+ roomDescriptor: RoomDescriptor,
583
+ ): boolean {
584
+ return hasFlag(roomDescriptor.Flags, RoomDescriptorFlag.DEVIL_TREASURE);
585
+ }
586
+
587
+ /**
588
+ * Helper function to detect if the provided room is a Double Trouble Boss Room.
589
+ *
590
+ * This is performed by checking for the string "Double Trouble" inside of the room name. The
591
+ * vanilla game uses this convention for every Double Trouble Boss Room. Note that this method might
592
+ * fail for mods that add extra Double Trouble rooms but do not follow the convention.
593
+ *
594
+ * Internally, the game is coded to detect Double Trouble Boss Rooms by checking for the variant
595
+ * range of 3700 through 3850. We intentionally do not use this method since it may not work as well
596
+ * with modded rooms.
597
+ */
598
+ export function isDoubleTrouble(roomData: RoomConfig): boolean {
599
+ return (
600
+ roomData.Type === RoomType.BOSS && roomData.Name.includes("Double Trouble")
601
+ );
602
+ }
603
+
604
+ /**
605
+ * Helper function to determine if the index of the provided room is equal to `GridRoom.GENESIS`.
606
+ */
607
+ export function isGenesisRoom(roomDescriptor: RoomDescriptor): boolean {
608
+ return roomDescriptor.GridIndex === asNumber(GridRoom.GENESIS);
609
+ }
610
+
611
+ /**
612
+ * Helper function to check if the provided room is either the left Home closet (behind the red
613
+ * door) or the right Home closet (with one random pickup).
614
+ *
615
+ * Home closets have a unique shape that is different from any other room in the game.
616
+ */
617
+ export function isHomeCloset(roomData: RoomConfig): boolean {
618
+ return (
619
+ roomData.StageID === StageID.HOME &&
620
+ (roomData.Subtype === asNumber(HomeRoomSubType.CLOSET_LEFT) ||
621
+ roomData.Subtype === asNumber(HomeRoomSubType.CLOSET_RIGHT))
622
+ );
623
+ }
624
+
625
+ /** Helper function to determine if the provided room is one of the four L room shapes. */
626
+ export function isLRoom(roomData: RoomConfig): boolean {
627
+ return isLRoomShape(roomData.Shape);
628
+ }
629
+
630
+ /**
631
+ * Helper function to determine if the index of the provided room is equal to `GridRoom.MEGA_SATAN`.
632
+ */
633
+ export function isMegaSatanRoom(roomDescriptor: RoomDescriptor): boolean {
634
+ return roomDescriptor.GridIndex === asNumber(GridRoom.MEGA_SATAN);
635
+ }
636
+
637
+ /**
638
+ * Helper function to determine if the provided room is part of the Repentance "escape sequence" in
639
+ * the Mines/Ashpit.
640
+ */
641
+ export function isMineShaft(roomData: RoomConfig): boolean {
642
+ return (
643
+ (roomData.StageID === StageID.MINES ||
644
+ roomData.StageID === StageID.ASHPIT) &&
645
+ // eslint-disable-next-line isaacscript/strict-enums
646
+ MINE_SHAFT_ROOM_SUB_TYPE_SET.has(roomData.Subtype)
647
+ );
648
+ }
649
+
650
+ /**
651
+ * Helper function to check if the provided room is a miniboss room for a particular miniboss. This
652
+ * will only work for mini-bosses that have dedicated boss rooms in the "00.special rooms.stb" file.
653
+ */
654
+ export function isMinibossRoomOf(
655
+ roomData: RoomConfig,
656
+ minibossID: MinibossID,
657
+ ): boolean {
658
+ return (
659
+ roomData.Type === RoomType.MINI_BOSS &&
660
+ roomData.StageID === StageID.SPECIAL_ROOMS &&
661
+ roomData.Subtype === asNumber(minibossID)
662
+ );
663
+ }
664
+
665
+ /**
666
+ * Helper function to check if the provided room is a "mirror room" in Downpour or Dross. (These
667
+ * rooms are marked with a specific sub-type.)
668
+ */
669
+ export function isMirrorRoom(roomData: RoomConfig): boolean {
670
+ return (
671
+ roomData.Type === RoomType.DEFAULT &&
672
+ (roomData.StageID === StageID.DOWNPOUR ||
673
+ roomData.StageID === StageID.DROSS) &&
674
+ roomData.Subtype === asNumber(DownpourRoomSubType.MIRROR)
675
+ );
676
+ }
677
+
678
+ /**
679
+ * Helper function to check if the provided room matches one of the given room types.
680
+ *
681
+ * This function is variadic, which means you can pass as many room types as you want to match for.
682
+ */
683
+ export function isRoomType(
684
+ roomData: RoomConfig,
685
+ ...roomTypes: RoomType[]
686
+ ): boolean {
687
+ return roomTypes.includes(roomData.Type);
688
+ }
689
+
690
+ /**
691
+ * Helper function for checking if the provided room is a secret exit that leads to a Repentance
692
+ * floor.
693
+ */
694
+ export function isSecretExit(roomDescriptor: RoomDescriptor): boolean {
695
+ return roomDescriptor.GridIndex === asNumber(GridRoom.SECRET_EXIT);
696
+ }
697
+
591
698
  /**
592
699
  * Helper function to detect if a room type is a Secret Room, a Super Secret Room, or an Ultra
593
700
  * Secret Room.
@@ -596,6 +703,18 @@ export function isSecretRoomType(roomType: RoomType): boolean {
596
703
  return SECRET_ROOM_TYPES.has(roomType);
597
704
  }
598
705
 
706
+ /**
707
+ * Helper function for checking if the provided room is a secret shop (from the Member Card
708
+ * collectible).
709
+ *
710
+ * Secret shops are simply copies of normal shops, but with the backdrop of a secret room. In other
711
+ * words, they will have the same room type, room variant, and room sub-type of a normal shop. Thus,
712
+ * the only way to detect them is by using the grid index.
713
+ */
714
+ export function isSecretShop(roomDescriptor: RoomDescriptor): boolean {
715
+ return roomDescriptor.GridIndex === asNumber(GridRoom.SECRET_SHOP);
716
+ }
717
+
599
718
  /**
600
719
  * If the `Room.Update` method is called in a `POST_NEW_ROOM` callback, then some entities will
601
720
  * slide around (such as the player). Since those entity velocities are already at zero, setting