isaacscript-common 6.21.0 → 6.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/dist/constants.d.ts +6 -0
  2. package/dist/constants.d.ts.map +1 -1
  3. package/dist/constants.lua +4 -0
  4. package/dist/enums/private/StageTravelState.d.ts +4 -3
  5. package/dist/enums/private/StageTravelState.d.ts.map +1 -1
  6. package/dist/enums/private/StageTravelState.lua +6 -4
  7. package/dist/features/customStage/customStageGridEntities.d.ts.map +1 -1
  8. package/dist/features/customStage/customStageGridEntities.lua +8 -1
  9. package/dist/features/customStage/exports.d.ts +14 -4
  10. package/dist/features/customStage/exports.d.ts.map +1 -1
  11. package/dist/features/customStage/exports.lua +32 -19
  12. package/dist/features/customStage/streakText.lua +1 -1
  13. package/dist/features/customStage/versusScreen.d.ts.map +1 -1
  14. package/dist/features/customStage/versusScreen.lua +32 -1
  15. package/dist/features/customTrapdoor/blackSprite.d.ts.map +1 -1
  16. package/dist/features/customTrapdoor/blackSprite.lua +1 -1
  17. package/dist/features/customTrapdoor/customTrapdoorConstants.d.ts +4 -0
  18. package/dist/features/customTrapdoor/customTrapdoorConstants.d.ts.map +1 -1
  19. package/dist/features/customTrapdoor/exports.d.ts +11 -8
  20. package/dist/features/customTrapdoor/exports.d.ts.map +1 -1
  21. package/dist/features/customTrapdoor/exports.lua +6 -6
  22. package/dist/features/customTrapdoor/init.lua +24 -14
  23. package/dist/features/customTrapdoor/touched.lua +1 -0
  24. package/dist/features/customTrapdoor/v.d.ts +2 -2
  25. package/dist/features/customTrapdoor/v.d.ts.map +1 -1
  26. package/dist/features/playerInventory.d.ts +7 -0
  27. package/dist/features/playerInventory.d.ts.map +1 -1
  28. package/dist/features/playerInventory.lua +7 -0
  29. package/dist/interfaces/private/CustomTrapdoorDescription.d.ts +2 -2
  30. package/dist/interfaces/private/CustomTrapdoorDescription.d.ts.map +1 -1
  31. package/dist/types/TrapdoorDestination.d.ts +3 -0
  32. package/dist/types/TrapdoorDestination.d.ts.map +1 -0
  33. package/dist/types/TrapdoorDestination.lua +2 -0
  34. package/package.json +2 -2
  35. package/src/constants.ts +6 -0
  36. package/src/enums/private/StageTravelState.ts +2 -1
  37. package/src/features/customStage/customStageGridEntities.ts +16 -2
  38. package/src/features/customStage/exports.ts +37 -14
  39. package/src/features/customStage/streakText.ts +1 -1
  40. package/src/features/customStage/versusScreen.ts +29 -0
  41. package/src/features/customTrapdoor/blackSprite.ts +5 -1
  42. package/src/features/customTrapdoor/customTrapdoorConstants.ts +4 -0
  43. package/src/features/customTrapdoor/exports.ts +8 -9
  44. package/src/features/customTrapdoor/init.ts +39 -18
  45. package/src/features/customTrapdoor/touched.ts +5 -0
  46. package/src/features/customTrapdoor/v.ts +2 -5
  47. package/src/features/playerInventory.ts +7 -0
  48. package/src/interfaces/private/CustomTrapdoorDescription.ts +2 -4
  49. package/src/types/TrapdoorDestination.ts +5 -0
@@ -1,4 +1,10 @@
1
1
  import { CollectibleType, ItemPoolType } from "isaac-typescript-definitions";
2
+ /**
3
+ * The combination of the following flags:
4
+ * - `DisplayFlag.VISIBLE` (1 << 0)
5
+ * - `DisplayFlag.SHADOW` (1 << 1)
6
+ * - `DisplayFlag.SHOW_ICON` (1 << 2)
7
+ */
2
8
  export declare const ALL_DISPLAY_FLAGS: BitFlags<number & {
3
9
  readonly __bitFlagBrand: symbol;
4
10
  } & {
@@ -1 +1 @@
1
- {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EAGf,YAAY,EAEb,MAAM,8BAA8B,CAAC;AAKtC,eAAO,MAAM,iBAAiB;;;;EAI7B,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,iCAAiC,SAAS,CAAC;AAExD;;;GAGG;AACH,eAAO,MAAM,mBAAmB,4CAA4C,CAAC;AAE7E,mEAAmE;AACnE,eAAO,MAAM,kBAAkB,KAAK,CAAC;AAErC,oGAAoG;AACpG,eAAO,MAAM,wBAAwB,KAAK,CAAC;AAE3C,eAAO,MAAM,sBAAsB,wBAAwB,CAAC;AAE5D,gGAAgG;AAChG,eAAO,MAAM,qBAAqB,KAAK,CAAC;AAExC,eAAO,MAAM,kBAAkB,KAAK,CAAC;AAErC;;;;GAIG;AACH,eAAO,MAAM,gCAAgC,KAAK,CAAC;AAEnD;;;GAGG;AACH,eAAO,MAAM,cAAc,iBAAiB,CAAC;AAE7C;;;;GAIG;AACH,eAAO,MAAM,+BAA+B,iBACrB,CAAC;AAExB,2EAA2E;AAC3E,eAAO,MAAM,sBAAsB,KAAK,CAAC;AAEzC,8EAA8E;AAC9E,eAAO,MAAM,wBAAwB,KAAK,CAAC;AAE3C,eAAO,MAAM,6BAA6B,KAAK,CAAC;AAEhD;;;GAGG;AACH,eAAO,MAAM,wBAAwB,KAAK,CAAC;AAE3C;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,KAAK,CAAC;AAEvC;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,MAAM,CAAC;AAExC;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,KAAK,CAAC;AAEpC,6DAA6D;AAC7D,eAAO,MAAM,cAAc,IAAI,CAAC;AAEhC,iFAAiF;AACjF,eAAO,MAAM,2BAA2B,KAAK,CAAC;AAE9C;;;;;GAKG;AACH,eAAO,MAAM,yBAAyB,MAAM,CAAC;AAE7C,eAAO,MAAM,wBAAwB,QAA6B,CAAC;AAEnE,yFAAyF;AACzF,eAAO,MAAM,0BAA0B,MAAM,CAAC;AAE9C,qFAAqF;AACrF,eAAO,MAAM,qBAAqB,MAAM,CAAC;AAEzC;;;GAGG;AACH,eAAO,MAAM,cAAc,IAAM,CAAC;AAElC,yCAAyC;AACzC,eAAO,MAAM,uCAAuC,QAAmB,CAAC;AAExE,iCAAiC;AACjC,eAAO,MAAM,sCAAsC,QAAmB,CAAC;AAEvE;;;GAGG;AACH,eAAO,MAAM,gCAAgC,QAAmB,CAAC;AAEjE,+EAA+E;AAC/E,eAAO,MAAM,iCAAiC,SAAS,CAAC;AAExD,eAAO,MAAM,cAAc,QAA+B,CAAC;AAE3D;;;GAGG;AACH,eAAO,MAAM,iBAAiB,QAAyB,CAAC;AAExD,eAAO,MAAM,sBAAsB,OAAO,CAAC;AAC3C,eAAO,MAAM,sBAAsB,QAA8B,CAAC;AAElE,eAAO,MAAM,yBAAyB,MAAM,CAAC;AAE7C,6FAA6F;AAC7F,eAAO,MAAM,gDAAgD,QAAQ,CAAC;AAEtE;;;GAGG;AACH,eAAO,MAAM,cAAc,KAAK,CAAC;AAEjC;;;GAGG;AACH,eAAO,MAAM,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAgB,CAAC;AAExD;;;GAGG;AACH,eAAO,MAAM,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAgB,CAAC;AAEzD;;;;;GAKG;AACH,eAAO,MAAM,YAAY,EAAE,QAAQ,CAAC,KAAK,CAAkB,CAAC;AAE5D;;;;GAIG;AACH,eAAO,MAAM,aAAa,EAAE,QAAQ,CAAC,MAAM,CAAsB,CAAC"}
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EAGf,YAAY,EAEb,MAAM,8BAA8B,CAAC;AAKtC;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB;;;;EAI7B,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,iCAAiC,SAAS,CAAC;AAExD;;;GAGG;AACH,eAAO,MAAM,mBAAmB,4CAA4C,CAAC;AAE7E,mEAAmE;AACnE,eAAO,MAAM,kBAAkB,KAAK,CAAC;AAErC,oGAAoG;AACpG,eAAO,MAAM,wBAAwB,KAAK,CAAC;AAE3C,eAAO,MAAM,sBAAsB,wBAAwB,CAAC;AAE5D,gGAAgG;AAChG,eAAO,MAAM,qBAAqB,KAAK,CAAC;AAExC,eAAO,MAAM,kBAAkB,KAAK,CAAC;AAErC;;;;GAIG;AACH,eAAO,MAAM,gCAAgC,KAAK,CAAC;AAEnD;;;GAGG;AACH,eAAO,MAAM,cAAc,iBAAiB,CAAC;AAE7C;;;;GAIG;AACH,eAAO,MAAM,+BAA+B,iBACrB,CAAC;AAExB,2EAA2E;AAC3E,eAAO,MAAM,sBAAsB,KAAK,CAAC;AAEzC,8EAA8E;AAC9E,eAAO,MAAM,wBAAwB,KAAK,CAAC;AAE3C,eAAO,MAAM,6BAA6B,KAAK,CAAC;AAEhD;;;GAGG;AACH,eAAO,MAAM,wBAAwB,KAAK,CAAC;AAE3C;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,KAAK,CAAC;AAEvC;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,MAAM,CAAC;AAExC;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,KAAK,CAAC;AAEpC,6DAA6D;AAC7D,eAAO,MAAM,cAAc,IAAI,CAAC;AAEhC,iFAAiF;AACjF,eAAO,MAAM,2BAA2B,KAAK,CAAC;AAE9C;;;;;GAKG;AACH,eAAO,MAAM,yBAAyB,MAAM,CAAC;AAE7C,eAAO,MAAM,wBAAwB,QAA6B,CAAC;AAEnE,yFAAyF;AACzF,eAAO,MAAM,0BAA0B,MAAM,CAAC;AAE9C,qFAAqF;AACrF,eAAO,MAAM,qBAAqB,MAAM,CAAC;AAEzC;;;GAGG;AACH,eAAO,MAAM,cAAc,IAAM,CAAC;AAElC,yCAAyC;AACzC,eAAO,MAAM,uCAAuC,QAAmB,CAAC;AAExE,iCAAiC;AACjC,eAAO,MAAM,sCAAsC,QAAmB,CAAC;AAEvE;;;GAGG;AACH,eAAO,MAAM,gCAAgC,QAAmB,CAAC;AAEjE,+EAA+E;AAC/E,eAAO,MAAM,iCAAiC,SAAS,CAAC;AAExD,eAAO,MAAM,cAAc,QAA+B,CAAC;AAE3D;;;GAGG;AACH,eAAO,MAAM,iBAAiB,QAAyB,CAAC;AAExD,eAAO,MAAM,sBAAsB,OAAO,CAAC;AAC3C,eAAO,MAAM,sBAAsB,QAA8B,CAAC;AAElE,eAAO,MAAM,yBAAyB,MAAM,CAAC;AAE7C,6FAA6F;AAC7F,eAAO,MAAM,gDAAgD,QAAQ,CAAC;AAEtE;;;GAGG;AACH,eAAO,MAAM,cAAc,KAAK,CAAC;AAEjC;;;GAGG;AACH,eAAO,MAAM,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAgB,CAAC;AAExD;;;GAGG;AACH,eAAO,MAAM,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAgB,CAAC;AAEzD;;;;;GAKG;AACH,eAAO,MAAM,YAAY,EAAE,QAAQ,CAAC,KAAK,CAAkB,CAAC;AAE5D;;;;GAIG;AACH,eAAO,MAAM,aAAa,EAAE,QAAQ,CAAC,MAAM,CAAsB,CAAC"}
@@ -10,6 +10,10 @@ local ____enums = require("functions.enums")
10
10
  local getEnumLength = ____enums.getEnumLength
11
11
  local ____flag = require("functions.flag")
12
12
  local addFlag = ____flag.addFlag
13
+ --- The combination of the following flags:
14
+ -- - `DisplayFlag.VISIBLE` (1 << 0)
15
+ -- - `DisplayFlag.SHADOW` (1 << 1)
16
+ -- - `DisplayFlag.SHOW_ICON` (1 << 2)
13
17
  ____exports.ALL_DISPLAY_FLAGS = addFlag(nil, DisplayFlag.VISIBLE, DisplayFlag.SHADOW, DisplayFlag.SHOW_ICON)
14
18
  --- The distance of the laser when Azazel does not have any range up items yet. For more info, see
15
19
  -- the documentation for the `getAzazelBrimstoneDistance` function.
@@ -2,8 +2,9 @@ export declare enum StageTravelState {
2
2
  NONE = 0,
3
3
  PLAYERS_JUMPING_DOWN = 1,
4
4
  PIXELATION_TO_BLACK = 2,
5
- PAUSING_ON_BLACK = 3,
6
- PIXELATION_TO_ROOM = 4,
7
- PLAYERS_LAYING_DOWN = 5
5
+ WAITING_FOR_FIRST_PIXELATION_TO_END = 3,
6
+ WAITING_FOR_SECOND_PIXELATION_TO_GET_HALF_WAY = 4,
7
+ PIXELATION_TO_ROOM = 5,
8
+ PLAYERS_LAYING_DOWN = 6
8
9
  }
9
10
  //# sourceMappingURL=StageTravelState.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"StageTravelState.d.ts","sourceRoot":"","sources":["../../../src/enums/private/StageTravelState.ts"],"names":[],"mappings":"AAAA,oBAAY,gBAAgB;IAC1B,IAAI,IAAA;IACJ,oBAAoB,IAAA;IACpB,mBAAmB,IAAA;IACnB,gBAAgB,IAAA;IAChB,kBAAkB,IAAA;IAClB,mBAAmB,IAAA;CACpB"}
1
+ {"version":3,"file":"StageTravelState.d.ts","sourceRoot":"","sources":["../../../src/enums/private/StageTravelState.ts"],"names":[],"mappings":"AAAA,oBAAY,gBAAgB;IAC1B,IAAI,IAAA;IACJ,oBAAoB,IAAA;IACpB,mBAAmB,IAAA;IACnB,mCAAmC,IAAA;IACnC,6CAA6C,IAAA;IAC7C,kBAAkB,IAAA;IAClB,mBAAmB,IAAA;CACpB"}
@@ -6,10 +6,12 @@ ____exports.StageTravelState.PLAYERS_JUMPING_DOWN = 1
6
6
  ____exports.StageTravelState[____exports.StageTravelState.PLAYERS_JUMPING_DOWN] = "PLAYERS_JUMPING_DOWN"
7
7
  ____exports.StageTravelState.PIXELATION_TO_BLACK = 2
8
8
  ____exports.StageTravelState[____exports.StageTravelState.PIXELATION_TO_BLACK] = "PIXELATION_TO_BLACK"
9
- ____exports.StageTravelState.PAUSING_ON_BLACK = 3
10
- ____exports.StageTravelState[____exports.StageTravelState.PAUSING_ON_BLACK] = "PAUSING_ON_BLACK"
11
- ____exports.StageTravelState.PIXELATION_TO_ROOM = 4
9
+ ____exports.StageTravelState.WAITING_FOR_FIRST_PIXELATION_TO_END = 3
10
+ ____exports.StageTravelState[____exports.StageTravelState.WAITING_FOR_FIRST_PIXELATION_TO_END] = "WAITING_FOR_FIRST_PIXELATION_TO_END"
11
+ ____exports.StageTravelState.WAITING_FOR_SECOND_PIXELATION_TO_GET_HALF_WAY = 4
12
+ ____exports.StageTravelState[____exports.StageTravelState.WAITING_FOR_SECOND_PIXELATION_TO_GET_HALF_WAY] = "WAITING_FOR_SECOND_PIXELATION_TO_GET_HALF_WAY"
13
+ ____exports.StageTravelState.PIXELATION_TO_ROOM = 5
12
14
  ____exports.StageTravelState[____exports.StageTravelState.PIXELATION_TO_ROOM] = "PIXELATION_TO_ROOM"
13
- ____exports.StageTravelState.PLAYERS_LAYING_DOWN = 5
15
+ ____exports.StageTravelState.PLAYERS_LAYING_DOWN = 6
14
16
  ____exports.StageTravelState[____exports.StageTravelState.PLAYERS_LAYING_DOWN] = "PLAYERS_LAYING_DOWN"
15
17
  return ____exports
@@ -1 +1 @@
1
- {"version":3,"file":"customStageGridEntities.d.ts","sourceRoot":"","sources":["../../../src/features/customStage/customStageGridEntities.ts"],"names":[],"mappings":";AAgBA,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAI3D,0CAA0C;AAC1C,wBAAgB,2BAA2B,CACzC,WAAW,EAAE,WAAW,EACxB,UAAU,EAAE,UAAU,GACrB,IAAI,CAyBN;AAED,oCAAoC;AACpC,wBAAgB,qBAAqB,CACnC,WAAW,EAAE,WAAW,EACxB,UAAU,EAAE,UAAU,GACrB,IAAI,CAqBN;AAED,mCAAmC;AACnC,wBAAgB,oBAAoB,CAClC,WAAW,EAAE,WAAW,EACxB,UAAU,EAAE,UAAU,GACrB,IAAI,CAkBN;AAED,mCAAmC;AACnC,wBAAgB,qBAAqB,CACnC,WAAW,EAAE,WAAW,EACxB,UAAU,EAAE,UAAU,GACrB,IAAI,CAmBN;AAuDD,wBAAgB,uBAAuB,CACrC,WAAW,EAAE,WAAW,EACxB,UAAU,EAAE,UAAU,GACrB,IAAI,CAYN;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,WAAW,EAAE,WAAW,EACxB,UAAU,EAAE,UAAU,GACrB,IAAI,CAsBN"}
1
+ {"version":3,"file":"customStageGridEntities.d.ts","sourceRoot":"","sources":["../../../src/features/customStage/customStageGridEntities.ts"],"names":[],"mappings":";AAkBA,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAM3D,0CAA0C;AAC1C,wBAAgB,2BAA2B,CACzC,WAAW,EAAE,WAAW,EACxB,UAAU,EAAE,UAAU,GACrB,IAAI,CAyBN;AAED,oCAAoC;AACpC,wBAAgB,qBAAqB,CACnC,WAAW,EAAE,WAAW,EACxB,UAAU,EAAE,UAAU,GACrB,IAAI,CAqBN;AAED,mCAAmC;AACnC,wBAAgB,oBAAoB,CAClC,WAAW,EAAE,WAAW,EACxB,UAAU,EAAE,UAAU,GACrB,IAAI,CAkBN;AAED,mCAAmC;AACnC,wBAAgB,qBAAqB,CACnC,WAAW,EAAE,WAAW,EACxB,UAAU,EAAE,UAAU,GACrB,IAAI,CAmBN;AAuDD,wBAAgB,uBAAuB,CACrC,WAAW,EAAE,WAAW,EACxB,UAAU,EAAE,UAAU,GACrB,IAAI,CAsBN;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,WAAW,EAAE,WAAW,EACxB,UAAU,EAAE,UAAU,GACrB,IAAI,CAsBN"}
@@ -19,10 +19,14 @@ local ____pickupsSpecific = require("functions.pickupsSpecific")
19
19
  local getCoins = ____pickupsSpecific.getCoins
20
20
  local getCollectibles = ____pickupsSpecific.getCollectibles
21
21
  local getTrinkets = ____pickupsSpecific.getTrinkets
22
+ local ____stage = require("functions.stage")
23
+ local calculateStageType = ____stage.calculateStageType
22
24
  local ____vector = require("functions.vector")
23
25
  local vectorEquals = ____vector.vectorEquals
24
26
  local ____exports = require("features.customTrapdoor.exports")
25
27
  local spawnCustomTrapdoor = ____exports.spawnCustomTrapdoor
28
+ local ____exports = require("features.customStage.exports")
29
+ local DEFAULT_BASE_STAGE = ____exports.DEFAULT_BASE_STAGE
26
30
  local ____v = require("features.customStage.v")
27
31
  local v = ____v.default
28
32
  function getNewDoorPNGPath(self, customStage, fileName)
@@ -226,7 +230,10 @@ function ____exports.convertVanillaTrapdoors(self, customStage, gridEntity)
226
230
  return
227
231
  end
228
232
  removeGridEntity(nil, gridEntity, true)
229
- local destination = v.run.firstFloor and ({customStage.name, 2}) or nil
233
+ local baseStage = customStage.baseStage == nil and DEFAULT_BASE_STAGE or customStage.baseStage
234
+ local vanillaNextStage = baseStage + 2
235
+ local vanillaNextStageType = calculateStageType(nil, vanillaNextStage)
236
+ local destination = v.run.firstFloor and ({customStage.name, 2}) or ({vanillaNextStage, vanillaNextStageType})
230
237
  spawnCustomTrapdoor(nil, gridEntity.Position, destination)
231
238
  end
232
239
  --- The rewards are based on the ones from the wiki:
@@ -1,4 +1,7 @@
1
- import { EntityType } from "isaac-typescript-definitions";
1
+ import { EntityType, LevelStage, StageType } from "isaac-typescript-definitions";
2
+ export declare const DEFAULT_BASE_STAGE = LevelStage.BASEMENT_2;
3
+ export declare const DEFAULT_BASE_STAGE_TYPE = StageType.ORIGINAL;
4
+ export declare const INVALID_STAGE_VALUE: LevelStage;
2
5
  /**
3
6
  * Helper function to warp to a custom stage/level.
4
7
  *
@@ -6,9 +9,11 @@ import { EntityType } from "isaac-typescript-definitions";
6
9
  * more details: https://isaacscript.github.io/main/custom-stages/
7
10
  *
8
11
  * @param name The name of the custom stage, corresponding to what is in the "tsconfig.json" file.
9
- * @param firstFloor Whether to go to the first floor or the second floor. For example, if you have
10
- * a custom stage emulating Caves, then the first floor would be Caves 1, and the
11
- * second floor would be Caves 2.
12
+ * @param firstFloor Optional. Whether to go to the first floor or the second floor. For example, if
13
+ * you have a custom stage emulating Caves, then the first floor would be Caves 1,
14
+ * and the second floor would be Caves 2. Default is true.
15
+ * @param verbose Optional. Whether to log additional information about the rooms that are chosen.
16
+ * Default is false.
12
17
  */
13
18
  export declare function setCustomStage(name: string, firstFloor?: boolean, verbose?: boolean): void;
14
19
  /**
@@ -37,4 +42,9 @@ export declare function setCustomStage(name: string, firstFloor?: boolean, verbo
37
42
  * will be displayed on the right side of the boss "versus" screen.
38
43
  */
39
44
  export declare function registerCustomBoss(entityType: EntityType, variant: int, subType: int, namePNGPath: string, portraitPNGPath: string): void;
45
+ /**
46
+ * Helper function to disable the custom stage. This is typically called before taking the player to
47
+ * a vanilla floor.
48
+ */
49
+ export declare function disableCustomStage(): void;
40
50
  //# sourceMappingURL=exports.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"exports.d.ts","sourceRoot":"","sources":["../../../src/features/customStage/exports.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EAKX,MAAM,8BAA8B,CAAC;AAsBtC;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,EACZ,UAAU,UAAO,EACjB,OAAO,UAAQ,GACd,IAAI,CA6CN;AA4ED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,UAAU,EACtB,OAAO,EAAE,GAAG,EACZ,OAAO,EAAE,GAAG,EACZ,WAAW,EAAE,MAAM,EACnB,eAAe,EAAE,MAAM,GACtB,IAAI,CAGN"}
1
+ {"version":3,"file":"exports.d.ts","sourceRoot":"","sources":["../../../src/features/customStage/exports.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EACV,UAAU,EAGV,SAAS,EACV,MAAM,8BAA8B,CAAC;AAmBtC,eAAO,MAAM,kBAAkB,wBAAwB,CAAC;AACxD,eAAO,MAAM,uBAAuB,qBAAqB,CAAC;AAE1D,eAAO,MAAM,mBAAmB,YAAmB,CAAC;AAEpD;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,EACZ,UAAU,UAAO,EACjB,OAAO,UAAQ,GACd,IAAI,CAwDN;AA4ED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,UAAU,EACtB,OAAO,EAAE,GAAG,EACZ,OAAO,EAAE,GAAG,EACZ,WAAW,EAAE,MAAM,EACnB,eAAe,EAAE,MAAM,GACtB,IAAI,CAGN;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,IAAI,IAAI,CAEzC"}
@@ -35,10 +35,10 @@ function setStageRoomsData(self, customStage, rng, verbose)
35
35
  for ____, room in ipairs(getRoomsInGrid(nil)) do
36
36
  do
37
37
  if room.SafeGridIndex == startingRoomGridIndex then
38
- goto __continue6
38
+ goto __continue7
39
39
  end
40
40
  if room.Data == nil then
41
- goto __continue6
41
+ goto __continue7
42
42
  end
43
43
  local roomType = room.Data.Type
44
44
  local roomShapeMap = customStage.roomTypeMap:get(roomType)
@@ -46,19 +46,19 @@ function setStageRoomsData(self, customStage, rng, verbose)
46
46
  if roomType == RoomType.DEFAULT then
47
47
  logError((((("Failed to find any custom rooms for RoomType." .. tostring(RoomType[roomType])) .. " (") .. tostring(roomType)) .. ") for custom stage: ") .. customStage.name)
48
48
  end
49
- goto __continue6
49
+ goto __continue7
50
50
  end
51
51
  local roomShape = room.Data.Shape
52
52
  local roomDoorSlotFlagMap = roomShapeMap:get(roomShape)
53
53
  if roomDoorSlotFlagMap == nil then
54
54
  logError((((((((("Failed to find any custom rooms for RoomType." .. tostring(RoomType[roomType])) .. " (") .. tostring(roomType)) .. ") + RoomShape.") .. tostring(RoomShape[roomShape])) .. " (") .. tostring(roomShape)) .. ") for custom stage: ") .. customStage.name)
55
- goto __continue6
55
+ goto __continue7
56
56
  end
57
57
  local doorSlotFlags = room.Data.Doors
58
58
  local roomsMetadata = roomDoorSlotFlagMap:get(doorSlotFlags)
59
59
  if roomsMetadata == nil then
60
60
  logError((((((((((("Failed to find any custom rooms for RoomType." .. tostring(RoomType[roomType])) .. " (") .. tostring(roomType)) .. ") + RoomShape.") .. tostring(RoomShape[roomShape])) .. " (") .. tostring(roomShape)) .. ") + DoorSlotFlags ") .. tostring(doorSlotFlags)) .. " for custom stage: ") .. customStage.name)
61
- goto __continue6
61
+ goto __continue7
62
62
  end
63
63
  local randomRoom = getRandomCustomStageRoom(nil, roomsMetadata, rng, verbose)
64
64
  local newRoomData = customStageCachedRoomData:get(randomRoom.variant)
@@ -66,26 +66,29 @@ function setStageRoomsData(self, customStage, rng, verbose)
66
66
  newRoomData = getRoomDataForTypeVariant(nil, roomType, randomRoom.variant, false)
67
67
  if newRoomData == nil then
68
68
  logError((("Failed to get the room data for room variant " .. tostring(randomRoom.variant)) .. " for custom stage: ") .. customStage.name)
69
- goto __continue6
69
+ goto __continue7
70
70
  end
71
71
  customStageCachedRoomData:set(randomRoom.variant, newRoomData)
72
72
  end
73
73
  room.Data = newRoomData
74
74
  end
75
- ::__continue6::
75
+ ::__continue7::
76
76
  end
77
77
  end
78
- local DEFAULT_BASE_STAGE = LevelStage.BASEMENT_2
79
- local DEFAULT_BASE_STAGE_TYPE = StageType.ORIGINAL
78
+ ____exports.DEFAULT_BASE_STAGE = LevelStage.BASEMENT_2
79
+ ____exports.DEFAULT_BASE_STAGE_TYPE = StageType.ORIGINAL
80
+ ____exports.INVALID_STAGE_VALUE = -1
80
81
  --- Helper function to warp to a custom stage/level.
81
82
  --
82
83
  -- Custom stages/levels must first be defined in the "tsconfig.json" file. See the documentation for
83
84
  -- more details: https://isaacscript.github.io/main/custom-stages/
84
85
  --
85
86
  -- @param name The name of the custom stage, corresponding to what is in the "tsconfig.json" file.
86
- -- @param firstFloor Whether to go to the first floor or the second floor. For example, if you have
87
- -- a custom stage emulating Caves, then the first floor would be Caves 1, and the
88
- -- second floor would be Caves 2.
87
+ -- @param firstFloor Optional. Whether to go to the first floor or the second floor. For example, if
88
+ -- you have a custom stage emulating Caves, then the first floor would be Caves 1,
89
+ -- and the second floor would be Caves 2. Default is true.
90
+ -- @param verbose Optional. Whether to log additional information about the rooms that are chosen.
91
+ -- Default is false.
89
92
  function ____exports.setCustomStage(self, name, firstFloor, verbose)
90
93
  if firstFloor == nil then
91
94
  firstFloor = true
@@ -98,22 +101,27 @@ function ____exports.setCustomStage(self, name, firstFloor, verbose)
98
101
  error(("Failed to set the custom stage of \"" .. name) .. "\" because it was not found in the custom stages map. (Try restarting IsaacScript / recompiling the mod / restarting the game, and try again. If that does not work, you probably forgot to define it in your \"tsconfig.json\" file.) See the website for more details on how to set up custom stages.")
99
102
  end
100
103
  local level = game:GetLevel()
104
+ local stage = level:GetStage()
101
105
  local seeds = game:GetSeeds()
102
106
  local startSeed = seeds:GetStartSeed()
103
107
  local rng = newRNG(nil, startSeed)
104
108
  v.run.currentCustomStage = customStage
105
109
  v.run.firstFloor = firstFloor
106
- local baseStage = customStage.baseStage == nil and DEFAULT_BASE_STAGE or customStage.baseStage
110
+ if stage == ____exports.INVALID_STAGE_VALUE then
111
+ level:SetStage(LevelStage.BASEMENT_1, StageType.ORIGINAL)
112
+ end
113
+ local baseStage = customStage.baseStage == nil and ____exports.DEFAULT_BASE_STAGE or customStage.baseStage
107
114
  if not firstFloor then
108
115
  baseStage = baseStage + 1
109
116
  end
110
- local baseStageType = customStage.baseStageType == nil and DEFAULT_BASE_STAGE_TYPE or customStage.baseStageType
111
- setStage(nil, baseStage, baseStageType)
117
+ local baseStageType = customStage.baseStageType == nil and ____exports.DEFAULT_BASE_STAGE_TYPE or customStage.baseStageType
118
+ local reseed = stage >= baseStage
119
+ setStage(nil, baseStage, baseStageType, reseed)
112
120
  setStageRoomsData(nil, customStage, rng, verbose)
113
- local stage = -1
114
- local stageType = StageType.WRATH_OF_THE_LAMB
115
- level:SetStage(stage, stageType)
116
- reorderedCallbacksSetStage(nil, stage, stageType)
121
+ local targetStage = ____exports.INVALID_STAGE_VALUE
122
+ local targetStageType = StageType.WRATH_OF_THE_LAMB
123
+ level:SetStage(targetStage, targetStageType)
124
+ reorderedCallbacksSetStage(nil, targetStage, targetStageType)
117
125
  end
118
126
  --- By default, unknown bosses will be drawn on the boss "versus" screen as "???". If your custom
119
127
  -- stage has custom bosses, you can use this function to register the corresponding graphic file
@@ -142,4 +150,9 @@ function ____exports.registerCustomBoss(self, entityType, variant, subType, name
142
150
  local entityID = getEntityIDFromConstituents(nil, entityType, variant, subType)
143
151
  customBossPNGPaths:set(entityID, {namePNGPath, portraitPNGPath})
144
152
  end
153
+ --- Helper function to disable the custom stage. This is typically called before taking the player to
154
+ -- a vanilla floor.
155
+ function ____exports.disableCustomStage(self)
156
+ v.run.currentCustomStage = nil
157
+ end
145
158
  return ____exports
@@ -133,7 +133,7 @@ local EMPTY_SHADER_NAME = "IsaacScript-RenderAboveHUD"
133
133
  TEXT_STAY_FRAME = 8
134
134
  TEXT_OUT_FRAME = 60
135
135
  --- This matches the offset that the vanilla game uses; determined via trial and error.
136
- local STREAK_SPRITE_TOP_OFFSET = Vector(0, 48.25)
136
+ local STREAK_SPRITE_TOP_OFFSET = Vector(0, 47)
137
137
  --- This matches the offset that the vanilla game uses; determined via trial and error.
138
138
  local STREAK_SPRITE_BOTTOM_OFFSET = Vector(0, -48.25)
139
139
  STREAK_TEXT_BOTTOM_Y_OFFSET = -9
@@ -1 +1 @@
1
- {"version":3,"file":"versusScreen.d.ts","sourceRoot":"","sources":["../../../src/features/customStage/versusScreen.ts"],"names":[],"mappings":"AAYA,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAqE3D;;;;GAIG;AACH,wBAAgB,gBAAgB,IAAI,IAAI,CAavC;AAED,wBAAgB,yBAAyB,CAAC,WAAW,EAAE,WAAW,GAAG,IAAI,CAuDxE;AA+ED,wBAAgB,sBAAsB,IAAI,IAAI,CAoC7C"}
1
+ {"version":3,"file":"versusScreen.d.ts","sourceRoot":"","sources":["../../../src/features/customStage/versusScreen.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AA2E3D;;;;GAIG;AACH,wBAAgB,gBAAgB,IAAI,IAAI,CAavC;AAED,wBAAgB,yBAAyB,CAAC,WAAW,EAAE,WAAW,GAAG,IAAI,CAwExE;AAoFD,wBAAgB,sBAAsB,IAAI,IAAI,CAoC7C"}
@@ -1,13 +1,15 @@
1
1
  local ____lualib = require("lualib_bundle")
2
2
  local Map = ____lualib.Map
3
+ local __TS__ArraySome = ____lualib.__TS__ArraySome
3
4
  local ____exports = {}
4
- local getPlayerPNGPaths, getBossPNGPaths, DEFAULT_CHARACTER, PNG_PATH_PREFIX, PLAYER_PORTRAIT_PNG_PATH_PREFIX
5
+ local willVanillaVersusScreenPlay, getPlayerPNGPaths, getBossPNGPaths, DEFAULT_CHARACTER, PNG_PATH_PREFIX, PLAYER_PORTRAIT_PNG_PATH_PREFIX
5
6
  local ____isaac_2Dtypescript_2Ddefinitions = require("isaac-typescript-definitions")
6
7
  local BossID = ____isaac_2Dtypescript_2Ddefinitions.BossID
7
8
  local PlayerType = ____isaac_2Dtypescript_2Ddefinitions.PlayerType
8
9
  local RoomType = ____isaac_2Dtypescript_2Ddefinitions.RoomType
9
10
  local SoundEffect = ____isaac_2Dtypescript_2Ddefinitions.SoundEffect
10
11
  local StageID = ____isaac_2Dtypescript_2Ddefinitions.StageID
12
+ local StageType = ____isaac_2Dtypescript_2Ddefinitions.StageType
11
13
  local ____cachedClasses = require("cachedClasses")
12
14
  local game = ____cachedClasses.game
13
15
  local sfxManager = ____cachedClasses.sfxManager
@@ -34,11 +36,24 @@ local VERSUS_SCREEN_DIRT_SPOT_COLORS = ____versusScreenDirtSpotColors.VERSUS_SCR
34
36
  local ____pause = require("features.pause")
35
37
  local pause = ____pause.pause
36
38
  local unpause = ____pause.unpause
39
+ local ____runInNFrames = require("features.runInNFrames")
40
+ local runNextGameFrame = ____runInNFrames.runNextGameFrame
37
41
  local ____customStageConstants = require("features.customStage.customStageConstants")
38
42
  local ISAACSCRIPT_CUSTOM_STAGE_GFX_PATH = ____customStageConstants.ISAACSCRIPT_CUSTOM_STAGE_GFX_PATH
43
+ local ____exports = require("features.customStage.exports")
44
+ local DEFAULT_BASE_STAGE = ____exports.DEFAULT_BASE_STAGE
45
+ local DEFAULT_BASE_STAGE_TYPE = ____exports.DEFAULT_BASE_STAGE_TYPE
46
+ local INVALID_STAGE_VALUE = ____exports.INVALID_STAGE_VALUE
39
47
  local ____v = require("features.customStage.v")
40
48
  local v = ____v.default
41
49
  local customBossPNGPaths = ____v.customBossPNGPaths
50
+ function willVanillaVersusScreenPlay(self)
51
+ local bosses = getBosses(nil)
52
+ return __TS__ArraySome(
53
+ bosses,
54
+ function(____, boss) return boss:GetBossID() ~= 0 end
55
+ )
56
+ end
42
57
  function getPlayerPNGPaths(self)
43
58
  local player = Isaac.GetPlayer()
44
59
  local character = player:GetPlayerType()
@@ -126,10 +141,26 @@ end
126
141
  function ____exports.playVersusScreenAnimation(self, customStage)
127
142
  local room = game:GetRoom()
128
143
  local roomType = room:GetType()
144
+ local roomCleared = room:IsClear()
129
145
  local hud = game:GetHUD()
130
146
  if roomType ~= RoomType.BOSS then
131
147
  return
132
148
  end
149
+ if roomCleared then
150
+ return
151
+ end
152
+ if willVanillaVersusScreenPlay(nil) then
153
+ local level = game:GetLevel()
154
+ level:SetStage(DEFAULT_BASE_STAGE, DEFAULT_BASE_STAGE_TYPE)
155
+ runNextGameFrame(
156
+ nil,
157
+ function()
158
+ local futureLevel = game:GetLevel()
159
+ futureLevel:SetStage(INVALID_STAGE_VALUE, StageType.ORIGINAL)
160
+ end
161
+ )
162
+ return
163
+ end
133
164
  v.run.showingBossVersusScreen = true
134
165
  pause(nil)
135
166
  hud:SetVisible(false)
@@ -1 +1 @@
1
- {"version":3,"file":"blackSprite.d.ts","sourceRoot":"","sources":["../../../src/features/customTrapdoor/blackSprite.ts"],"names":[],"mappings":"AAUA,wBAAgB,eAAe,IAAI,IAAI,CAYtC"}
1
+ {"version":3,"file":"blackSprite.d.ts","sourceRoot":"","sources":["../../../src/features/customTrapdoor/blackSprite.ts"],"names":[],"mappings":"AAUA,wBAAgB,eAAe,IAAI,IAAI,CAgBtC"}
@@ -7,7 +7,7 @@ local ____v = require("features.customTrapdoor.v")
7
7
  local v = ____v.default
8
8
  local blackSprite = Sprite()
9
9
  function ____exports.drawBlackSprite(self)
10
- if v.run.state ~= StageTravelState.PAUSING_ON_BLACK then
10
+ if v.run.state ~= StageTravelState.WAITING_FOR_FIRST_PIXELATION_TO_END and v.run.state ~= StageTravelState.WAITING_FOR_SECOND_PIXELATION_TO_GET_HALF_WAY then
11
11
  return
12
12
  end
13
13
  if not blackSprite:IsLoaded() then
@@ -1,6 +1,10 @@
1
1
  import { GridEntityType } from "isaac-typescript-definitions";
2
2
  export declare const CUSTOM_TRAPDOOR_FEATURE_NAME = "customTrapdoor";
3
3
  export declare const GridEntityTypeCustom: {
4
+ /**
5
+ * We arbitrarily choose 1000 as to not conflict with end-user mods. (The expectation is that
6
+ * end-user mods will begin their enums with values of 1 and increment upwards.)
7
+ */
4
8
  readonly TRAPDOOR_CUSTOM: GridEntityType;
5
9
  };
6
10
  /** This also applies to crawl spaces. The value was determined through trial and error. */
@@ -1 +1 @@
1
- {"version":3,"file":"customTrapdoorConstants.d.ts","sourceRoot":"","sources":["../../../src/features/customTrapdoor/customTrapdoorConstants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAE9D,eAAO,MAAM,4BAA4B,mBAAmB,CAAC;AAE7D,eAAO,MAAM,oBAAoB;;CAEvB,CAAC;AAEX,2FAA2F;AAC3F,eAAO,MAAM,sBAAsB,KAAK,CAAC;AAEzC,eAAO,MAAM,iCAAiC,QAA+B,CAAC;AAC9E,eAAO,MAAM,6BAA6B,KAAK,CAAC;AAEhD,eAAO,MAAM,uBAAuB,OAAO,CAAC;AAE5C,eAAO,MAAM,oCAAoC,EAAE,WAAW,CAAC,MAAM,CAClC,CAAC;AAEpC,eAAO,MAAM,0BAA0B,KAAK,CAAC;AAE7C,eAAO,MAAM,4CAA4C,IAAI,CAAC;AAC9D,eAAO,MAAM,+CAA+C,IAAI,CAAC"}
1
+ {"version":3,"file":"customTrapdoorConstants.d.ts","sourceRoot":"","sources":["../../../src/features/customTrapdoor/customTrapdoorConstants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAE9D,eAAO,MAAM,4BAA4B,mBAAmB,CAAC;AAE7D,eAAO,MAAM,oBAAoB;IAC/B;;;OAGG;;CAEK,CAAC;AAEX,2FAA2F;AAC3F,eAAO,MAAM,sBAAsB,KAAK,CAAC;AAEzC,eAAO,MAAM,iCAAiC,QAA+B,CAAC;AAC9E,eAAO,MAAM,6BAA6B,KAAK,CAAC;AAEhD,eAAO,MAAM,uBAAuB,OAAO,CAAC;AAE5C,eAAO,MAAM,oCAAoC,EAAE,WAAW,CAAC,MAAM,CAClC,CAAC;AAEpC,eAAO,MAAM,0BAA0B,KAAK,CAAC;AAE7C,eAAO,MAAM,4CAA4C,IAAI,CAAC;AAC9D,eAAO,MAAM,+CAA+C,IAAI,CAAC"}
@@ -1,4 +1,7 @@
1
- import { LevelStage, StageType } from "isaac-typescript-definitions";
1
+ /// <reference types="isaac-typescript-definitions" />
2
+ /// <reference types="isaac-typescript-definitions" />
3
+ /// <reference types="isaac-typescript-definitions" />
4
+ import { TrapdoorDestination } from "../../types/TrapdoorDestination";
2
5
  /**
3
6
  * Helper function to spawn a trapdoor grid entity that will have one or more of the following
4
7
  * attributes:
@@ -14,17 +17,17 @@ import { LevelStage, StageType } from "isaac-typescript-definitions";
14
17
  *
15
18
  * @param gridIndexOrPosition The location in the room to spawn the trapdoor.
16
19
  * @param destination Optional. Used to specify where the player will go after jumping into the
17
- * trapdoor. Can either be a tuple or undefined. For example, a destination of
18
- * `[LevelStage.CAVES_1, StageType.ORIGINAL]` corresponds to Caves 1, and a
19
- * destination of `["Slaughterhouse", 1]` corresponds to a custom stage of
20
- * Slaughterhouse 1. If the destination is set to undefined, then the "normal"
21
- * destination corresponding to the current stage and room will be used (e.g. the
22
- * next floor).
20
+ * trapdoor. Can either be a vanilla stage tuple, a custom stage tuple, or
21
+ * undefined. For example, a destination of `[LevelStage.CAVES_1,
22
+ * StageType.ORIGINAL]` corresponds to Caves 1, and a destination of
23
+ * `["Slaughterhouse", 1]` corresponds to a custom stage of Slaughterhouse 1. If
24
+ * the destination is undefined, then the "normal" destination corresponding to
25
+ * the current stage and room will be used (e.g. the next floor, in most cases).
23
26
  * @param anm2Path Optional. The path to the anm2 file to use. By default, the vanilla trapdoor anm2
24
27
  * of "gfx/grid/door_11_trapdoor.anm2" will be used. The specified anm2 file must
25
28
  * have animations called "Opened", "Closed", and "Open Animation".
26
29
  * @param spawnOpen Optional. Whether or not to spawn the trapdoor in an open state. By default,
27
30
  * behavior will be used that emulates a vanilla trapdoor.
28
31
  */
29
- export declare function spawnCustomTrapdoor(gridIndexOrPosition: int | Vector, destination?: [stage: LevelStage, stageType: StageType] | [customStageName: string, floorNum: int], anm2Path?: string, spawnOpen?: boolean): GridEntity;
32
+ export declare function spawnCustomTrapdoor(gridIndexOrPosition: int | Vector, destination?: TrapdoorDestination, anm2Path?: string, spawnOpen?: boolean): GridEntity;
30
33
  //# sourceMappingURL=exports.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"exports.d.ts","sourceRoot":"","sources":["../../../src/features/customTrapdoor/exports.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,UAAU,EACV,SAAS,EACV,MAAM,8BAA8B,CAAC;AAgBtC;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,mBAAmB,CACjC,mBAAmB,EAAE,GAAG,GAAG,MAAM,EACjC,WAAW,CAAC,EACR,CAAC,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,CAAC,GACzC,CAAC,eAAe,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,CAAC,EAC5C,QAAQ,SAAmC,EAC3C,SAAS,CAAC,EAAE,OAAO,GAClB,UAAU,CAwCZ"}
1
+ {"version":3,"file":"exports.d.ts","sourceRoot":"","sources":["../../../src/features/customTrapdoor/exports.ts"],"names":[],"mappings":";;;AAYA,OAAO,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AAStE;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,mBAAmB,CACjC,mBAAmB,EAAE,GAAG,GAAG,MAAM,EACjC,WAAW,CAAC,EAAE,mBAAmB,EACjC,QAAQ,SAAmC,EAC3C,SAAS,CAAC,EAAE,OAAO,GAClB,UAAU,CAwCZ"}
@@ -45,12 +45,12 @@ end
45
45
  --
46
46
  -- @param gridIndexOrPosition The location in the room to spawn the trapdoor.
47
47
  -- @param destination Optional. Used to specify where the player will go after jumping into the
48
- -- trapdoor. Can either be a tuple or undefined. For example, a destination of
49
- -- `[LevelStage.CAVES_1, StageType.ORIGINAL]` corresponds to Caves 1, and a
50
- -- destination of `["Slaughterhouse", 1]` corresponds to a custom stage of
51
- -- Slaughterhouse 1. If the destination is set to undefined, then the "normal"
52
- -- destination corresponding to the current stage and room will be used (e.g. the
53
- -- next floor).
48
+ -- trapdoor. Can either be a vanilla stage tuple, a custom stage tuple, or
49
+ -- undefined. For example, a destination of `[LevelStage.CAVES_1,
50
+ -- StageType.ORIGINAL]` corresponds to Caves 1, and a destination of
51
+ -- `["Slaughterhouse", 1]` corresponds to a custom stage of Slaughterhouse 1. If
52
+ -- the destination is undefined, then the "normal" destination corresponding to
53
+ -- the current stage and room will be used (e.g. the next floor, in most cases).
54
54
  -- @param anm2Path Optional. The path to the anm2 file to use. By default, the vanilla trapdoor anm2
55
55
  -- of "gfx/grid/door_11_trapdoor.anm2" will be used. The specified anm2 file must
56
56
  -- have animations called "Opened", "Closed", and "Open Animation".
@@ -2,7 +2,7 @@ local ____lualib = require("lualib_bundle")
2
2
  local __TS__ArraySome = ____lualib.__TS__ArraySome
3
3
  local Map = ____lualib.Map
4
4
  local ____exports = {}
5
- local postRender, checkAllPlayersJumpComplete, checkPixelationToBlackComplete, checkPausingOnBlackComplete, checkAllPlayersLayingDownComplete, goToCustomDestination, anyPlayerPlayingExtraAnimation, postPEffectUpdate, checkJumpComplete, postGridEntityCustomUpdateTrapdoor
5
+ local postRender, checkAllPlayersJumpComplete, checkPixelationToBlackComplete, checkSecondPixelationHalfWay, checkAllPlayersLayingDownComplete, goToCustomDestination, anyPlayerPlayingExtraAnimation, postPEffectUpdate, checkJumpComplete, postGridEntityCustomUpdateTrapdoor
6
6
  local ____isaac_2Dtypescript_2Ddefinitions = require("isaac-typescript-definitions")
7
7
  local Direction = ____isaac_2Dtypescript_2Ddefinitions.Direction
8
8
  local EntityCollisionClass = ____isaac_2Dtypescript_2Ddefinitions.EntityCollisionClass
@@ -29,6 +29,7 @@ local setStage = ____stage.setStage
29
29
  local ____types = require("functions.types")
30
30
  local isString = ____types.isString
31
31
  local ____exports = require("features.customStage.exports")
32
+ local disableCustomStage = ____exports.disableCustomStage
32
33
  local setCustomStage = ____exports.setCustomStage
33
34
  local ____streakText = require("features.customStage.streakText")
34
35
  local topStreakTextStart = ____streakText.topStreakTextStart
@@ -55,7 +56,7 @@ local v = ____v.default
55
56
  function postRender(self)
56
57
  checkAllPlayersJumpComplete(nil)
57
58
  checkPixelationToBlackComplete(nil)
58
- checkPausingOnBlackComplete(nil)
59
+ checkSecondPixelationHalfWay(nil)
59
60
  checkAllPlayersLayingDownComplete(nil)
60
61
  drawBlackSprite(nil)
61
62
  end
@@ -78,25 +79,33 @@ function checkPixelationToBlackComplete(self)
78
79
  end
79
80
  local hud = game:GetHUD()
80
81
  local renderFrameCount = Isaac.GetFrameCount()
81
- local roomGridIndex = getRoomGridIndex(nil)
82
82
  local renderFrameScreenBlack = v.run.stateRenderFrame + PIXELATION_TO_BLACK_FRAMES
83
83
  if renderFrameCount < renderFrameScreenBlack then
84
84
  return
85
85
  end
86
- v.run.state = StageTravelState.PAUSING_ON_BLACK
87
- v.run.stateRenderFrame = renderFrameCount
86
+ v.run.state = StageTravelState.WAITING_FOR_FIRST_PIXELATION_TO_END
88
87
  hud:SetVisible(false)
89
- goToCustomDestination(nil)
90
- teleport(
88
+ runNextGameFrame(
91
89
  nil,
92
- roomGridIndex,
93
- Direction.NO_DIRECTION,
94
- RoomTransitionAnim.PIXELATION,
95
- true
90
+ function()
91
+ local level = game:GetLevel()
92
+ local startingRoomIndex = level:GetStartingRoomIndex()
93
+ local futureRenderFrameCount = Isaac.GetFrameCount()
94
+ v.run.state = StageTravelState.WAITING_FOR_SECOND_PIXELATION_TO_GET_HALF_WAY
95
+ v.run.stateRenderFrame = futureRenderFrameCount
96
+ goToCustomDestination(nil)
97
+ teleport(
98
+ nil,
99
+ startingRoomIndex,
100
+ Direction.NO_DIRECTION,
101
+ RoomTransitionAnim.PIXELATION,
102
+ true
103
+ )
104
+ end
96
105
  )
97
106
  end
98
- function checkPausingOnBlackComplete(self)
99
- if v.run.state ~= StageTravelState.PAUSING_ON_BLACK or v.run.stateRenderFrame == nil then
107
+ function checkSecondPixelationHalfWay(self)
108
+ if v.run.state ~= StageTravelState.WAITING_FOR_SECOND_PIXELATION_TO_GET_HALF_WAY or v.run.stateRenderFrame == nil then
100
109
  return
101
110
  end
102
111
  local hud = game:GetHUD()
@@ -110,8 +119,8 @@ function checkPausingOnBlackComplete(self)
110
119
  runNextRoom(
111
120
  nil,
112
121
  function()
113
- movePlayersToCenter(nil)
114
122
  v.run.state = StageTravelState.PLAYERS_LAYING_DOWN
123
+ movePlayersToCenter(nil)
115
124
  for ____, player in ipairs(getAllPlayers(nil)) do
116
125
  player:AnimateAppear()
117
126
  player.EntityCollisionClass = EntityCollisionClass.ALL
@@ -145,6 +154,7 @@ function goToCustomDestination(self)
145
154
  local firstFloor = arg2 == 1
146
155
  setCustomStage(nil, "Slaughterhouse", firstFloor)
147
156
  else
157
+ disableCustomStage(nil)
148
158
  setStage(nil, arg1, arg2)
149
159
  end
150
160
  end
@@ -73,6 +73,7 @@ function setPlayerAttributes(self, trapdoorPlayer, position)
73
73
  player.Velocity = VectorZero
74
74
  player.EntityCollisionClass = EntityCollisionClass.NONE
75
75
  player.GridCollisionClass = EntityGridCollisionClass.NONE
76
+ player:StopExtraAnimation()
76
77
  end
77
78
  end
78
79
  function dropTaintedForgotten(self, player)
@@ -1,13 +1,13 @@
1
- import { LevelStage, StageType } from "isaac-typescript-definitions";
2
1
  import { DefaultMap } from "../../classes/DefaultMap";
3
2
  import { StageTravelState } from "../../enums/private/StageTravelState";
4
3
  import { CustomTrapdoorDescription } from "../../interfaces/private/CustomTrapdoorDescription";
4
+ import { TrapdoorDestination } from "../../types/TrapdoorDestination";
5
5
  declare const v: {
6
6
  run: {
7
7
  state: StageTravelState;
8
8
  /** The render frame that this state was reached. */
9
9
  stateRenderFrame: number | null;
10
- destination: [stage: LevelStage, stageType: StageType] | [customStageName: string, floorNum: number] | null;
10
+ destination: TrapdoorDestination | null;
11
11
  };
12
12
  level: {
13
13
  /** Indexed by room list index and grid index. */
@@ -1 +1 @@
1
- {"version":3,"file":"v.d.ts","sourceRoot":"","sources":["../../../src/features/customTrapdoor/v.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AACrE,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,sCAAsC,CAAC;AACxE,OAAO,EAAE,yBAAyB,EAAE,MAAM,oDAAoD,CAAC;AAE/F,QAAA,MAAM,CAAC;;;QAIH,oDAAoD;;;;;QAUpD,iDAAiD;;;CAKpD,CAAC;AACF,eAAe,CAAC,CAAC"}
1
+ {"version":3,"file":"v.d.ts","sourceRoot":"","sources":["../../../src/features/customTrapdoor/v.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,sCAAsC,CAAC;AACxE,OAAO,EAAE,yBAAyB,EAAE,MAAM,oDAAoD,CAAC;AAC/F,OAAO,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AAEtE,QAAA,MAAM,CAAC;;;QAIH,oDAAoD;;;;;QAOpD,iDAAiD;;;CAKpD,CAAC;AACF,eAAe,CAAC,CAAC"}
@@ -10,6 +10,13 @@ import { CollectibleType } from "isaac-typescript-definitions";
10
10
  * middle of the run (e.g. with D4), the order of the inventory will not correspond to the order
11
11
  * that the items were actually given to the player. In this case, the inventory will be in the
12
12
  * order of the lowest `CollectibleType` to the highest.
13
+ *
14
+ * Under the hood, the inventory tracking works by tracking the number of collectibles that a player
15
+ * has on every frame. Thus, in a situation where a collectible was both added and removed to the
16
+ * player on the same frame, the amount of total collectibles would stay the same, and the inventory
17
+ * would not be updated. In vanilla, this situation would never happen, but another mod might do
18
+ * this for some reason. (With that said, the next time that a collectible is normally added or
19
+ * removed, it would trigger a re-scan, and the previous changes would be picked up.)
13
20
  */
14
21
  export declare function getPlayerInventory(player: EntityPlayer, includeActiveCollectibles?: boolean): CollectibleType[];
15
22
  //# sourceMappingURL=playerInventory.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"playerInventory.d.ts","sourceRoot":"","sources":["../../src/features/playerInventory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAe,MAAM,8BAA8B,CAAC;AAyG5E;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,YAAY,EACpB,yBAAyB,UAAO,GAC/B,eAAe,EAAE,CAanB"}
1
+ {"version":3,"file":"playerInventory.d.ts","sourceRoot":"","sources":["../../src/features/playerInventory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAe,MAAM,8BAA8B,CAAC;AAyG5E;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,YAAY,EACpB,yBAAyB,UAAO,GAC/B,eAAe,EAAE,CAanB"}
@@ -91,6 +91,13 @@ end
91
91
  -- middle of the run (e.g. with D4), the order of the inventory will not correspond to the order
92
92
  -- that the items were actually given to the player. In this case, the inventory will be in the
93
93
  -- order of the lowest `CollectibleType` to the highest.
94
+ --
95
+ -- Under the hood, the inventory tracking works by tracking the number of collectibles that a player
96
+ -- has on every frame. Thus, in a situation where a collectible was both added and removed to the
97
+ -- player on the same frame, the amount of total collectibles would stay the same, and the inventory
98
+ -- would not be updated. In vanilla, this situation would never happen, but another mod might do
99
+ -- this for some reason. (With that said, the next time that a collectible is normally added or
100
+ -- removed, it would trigger a re-scan, and the previous changes would be picked up.)
94
101
  function ____exports.getPlayerInventory(self, player, includeActiveCollectibles)
95
102
  if includeActiveCollectibles == nil then
96
103
  includeActiveCollectibles = true
@@ -1,7 +1,7 @@
1
- import { LevelStage, StageType } from "isaac-typescript-definitions";
1
+ import { TrapdoorDestination } from "../../types/TrapdoorDestination";
2
2
  export interface CustomTrapdoorDescription {
3
3
  open: boolean;
4
- destination: [stage: LevelStage, stageType: StageType] | [customStageName: string, floorNum: int];
4
+ destination: TrapdoorDestination;
5
5
  firstSpawn: boolean;
6
6
  }
7
7
  //# sourceMappingURL=CustomTrapdoorDescription.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"CustomTrapdoorDescription.d.ts","sourceRoot":"","sources":["../../../src/interfaces/private/CustomTrapdoorDescription.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAErE,MAAM,WAAW,yBAAyB;IACxC,IAAI,EAAE,OAAO,CAAC;IACd,WAAW,EACP,CAAC,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,CAAC,GACzC,CAAC,eAAe,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,CAAC,CAAC;IAC7C,UAAU,EAAE,OAAO,CAAC;CACrB"}
1
+ {"version":3,"file":"CustomTrapdoorDescription.d.ts","sourceRoot":"","sources":["../../../src/interfaces/private/CustomTrapdoorDescription.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AAEtE,MAAM,WAAW,yBAAyB;IACxC,IAAI,EAAE,OAAO,CAAC;IACd,WAAW,EAAE,mBAAmB,CAAC;IACjC,UAAU,EAAE,OAAO,CAAC;CACrB"}
@@ -0,0 +1,3 @@
1
+ import { LevelStage, StageType } from "isaac-typescript-definitions";
2
+ export declare type TrapdoorDestination = [stage: LevelStage, stageType: StageType] | [customStageName: string, floorNum: int];
3
+ //# sourceMappingURL=TrapdoorDestination.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TrapdoorDestination.d.ts","sourceRoot":"","sources":["../../src/types/TrapdoorDestination.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAErE,oBAAY,mBAAmB,GAC3B,CAAC,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,CAAC,GACzC,CAAC,eAAe,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ local ____exports = {}
2
+ return ____exports
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "isaacscript-common",
3
- "version": "6.21.0",
3
+ "version": "6.22.0",
4
4
  "description": "Helper functions and features for IsaacScript mods.",
5
5
  "keywords": [
6
6
  "isaac",
@@ -22,6 +22,6 @@
22
22
  "main": "dist/index",
23
23
  "types": "dist/index.d.ts",
24
24
  "dependencies": {
25
- "isaac-typescript-definitions": "^3.1.3"
25
+ "isaac-typescript-definitions": "^3.1.4"
26
26
  }
27
27
  }
package/src/constants.ts CHANGED
@@ -9,6 +9,12 @@ import { NUM_NORMAL_PILL_COLORS } from "./constantsFirstLast";
9
9
  import { getEnumLength } from "./functions/enums";
10
10
  import { addFlag } from "./functions/flag";
11
11
 
12
+ /**
13
+ * The combination of the following flags:
14
+ * - `DisplayFlag.VISIBLE` (1 << 0)
15
+ * - `DisplayFlag.SHADOW` (1 << 1)
16
+ * - `DisplayFlag.SHOW_ICON` (1 << 2)
17
+ */
12
18
  export const ALL_DISPLAY_FLAGS = addFlag(
13
19
  DisplayFlag.VISIBLE,
14
20
  DisplayFlag.SHADOW,
@@ -2,7 +2,8 @@ export enum StageTravelState {
2
2
  NONE,
3
3
  PLAYERS_JUMPING_DOWN,
4
4
  PIXELATION_TO_BLACK,
5
- PAUSING_ON_BLACK,
5
+ WAITING_FOR_FIRST_PIXELATION_TO_END,
6
+ WAITING_FOR_SECOND_PIXELATION_TO_GET_HALF_WAY,
6
7
  PIXELATION_TO_ROOM,
7
8
  PLAYERS_LAYING_DOWN,
8
9
  }
@@ -2,6 +2,7 @@ import {
2
2
  CollectibleType,
3
3
  EntityType,
4
4
  GridEntityType,
5
+ LevelStage,
5
6
  TrinketType,
6
7
  } from "isaac-typescript-definitions";
7
8
  import { DecorationVariant } from "../../enums/DecorationVariant";
@@ -13,9 +14,12 @@ import {
13
14
  getCollectibles,
14
15
  getTrinkets,
15
16
  } from "../../functions/pickupsSpecific";
17
+ import { calculateStageType } from "../../functions/stage";
16
18
  import { vectorEquals } from "../../functions/vector";
17
19
  import { CustomStage } from "../../interfaces/CustomStage";
20
+ import { TrapdoorDestination } from "../../types/TrapdoorDestination";
18
21
  import { spawnCustomTrapdoor } from "../customTrapdoor/exports";
22
+ import { DEFAULT_BASE_STAGE } from "./exports";
19
23
  import v from "./v";
20
24
 
21
25
  /** For `GridEntityType.DECORATION` (1) */
@@ -189,9 +193,19 @@ export function convertVanillaTrapdoors(
189
193
 
190
194
  removeGridEntity(gridEntity, true);
191
195
 
192
- const destination: [string, int] | undefined = v.run.firstFloor
196
+ // - If we are on the first floor of a custom stage, then the destination will be the second floor
197
+ // of the custom stage. (e.g. Caves 1 to Caves 2)
198
+ // - If we are on the second floor of a custom stage, then the destination will be the vanilla
199
+ // floor equivalent to 2 floors after the floor used as a basis for the custom stage.
200
+ const baseStage =
201
+ customStage.baseStage === undefined
202
+ ? DEFAULT_BASE_STAGE
203
+ : customStage.baseStage;
204
+ const vanillaNextStage = (baseStage + 2) as LevelStage;
205
+ const vanillaNextStageType = calculateStageType(vanillaNextStage);
206
+ const destination: TrapdoorDestination = v.run.firstFloor
193
207
  ? [customStage.name, 2]
194
- : undefined;
208
+ : [vanillaNextStage, vanillaNextStageType];
195
209
  spawnCustomTrapdoor(gridEntity.Position, destination);
196
210
  }
197
211
 
@@ -23,8 +23,10 @@ import v, {
23
23
  customStagesMap,
24
24
  } from "./v";
25
25
 
26
- const DEFAULT_BASE_STAGE = LevelStage.BASEMENT_2;
27
- const DEFAULT_BASE_STAGE_TYPE = StageType.ORIGINAL;
26
+ export const DEFAULT_BASE_STAGE = LevelStage.BASEMENT_2;
27
+ export const DEFAULT_BASE_STAGE_TYPE = StageType.ORIGINAL;
28
+
29
+ export const INVALID_STAGE_VALUE = -1 as LevelStage;
28
30
 
29
31
  /**
30
32
  * Helper function to warp to a custom stage/level.
@@ -33,9 +35,11 @@ const DEFAULT_BASE_STAGE_TYPE = StageType.ORIGINAL;
33
35
  * more details: https://isaacscript.github.io/main/custom-stages/
34
36
  *
35
37
  * @param name The name of the custom stage, corresponding to what is in the "tsconfig.json" file.
36
- * @param firstFloor Whether to go to the first floor or the second floor. For example, if you have
37
- * a custom stage emulating Caves, then the first floor would be Caves 1, and the
38
- * second floor would be Caves 2.
38
+ * @param firstFloor Optional. Whether to go to the first floor or the second floor. For example, if
39
+ * you have a custom stage emulating Caves, then the first floor would be Caves 1,
40
+ * and the second floor would be Caves 2. Default is true.
41
+ * @param verbose Optional. Whether to log additional information about the rooms that are chosen.
42
+ * Default is false.
39
43
  */
40
44
  export function setCustomStage(
41
45
  name: string,
@@ -50,6 +54,7 @@ export function setCustomStage(
50
54
  }
51
55
 
52
56
  const level = game.GetLevel();
57
+ const stage = level.GetStage();
53
58
  const seeds = game.GetSeeds();
54
59
  const startSeed = seeds.GetStartSeed();
55
60
  const rng = newRNG(startSeed);
@@ -57,6 +62,12 @@ export function setCustomStage(
57
62
  v.run.currentCustomStage = customStage;
58
63
  v.run.firstFloor = firstFloor;
59
64
 
65
+ // Before changing the stage, we have to revert the bugged stage, if necessary. This prevents the
66
+ // bug where the backdrop will not spawn.
67
+ if (stage === INVALID_STAGE_VALUE) {
68
+ level.SetStage(LevelStage.BASEMENT_1, StageType.ORIGINAL);
69
+ }
70
+
60
71
  let baseStage: int =
61
72
  customStage.baseStage === undefined
62
73
  ? DEFAULT_BASE_STAGE
@@ -64,22 +75,26 @@ export function setCustomStage(
64
75
  if (!firstFloor) {
65
76
  baseStage++;
66
77
  }
78
+
67
79
  const baseStageType =
68
80
  customStage.baseStageType === undefined
69
81
  ? DEFAULT_BASE_STAGE_TYPE
70
82
  : (customStage.baseStageType as StageType);
71
- setStage(baseStage as LevelStage, baseStageType);
83
+
84
+ const reseed = (stage as int) >= baseStage;
85
+
86
+ setStage(baseStage as LevelStage, baseStageType, reseed);
72
87
 
73
88
  setStageRoomsData(customStage, rng, verbose);
74
89
 
75
- // Set the stage to an invalid value, which will prevent the walls and floors from loading. We
76
- // must use `StageType.WRATH_OF_THE_LAMB` instead of `StageType.ORIGINAL` or else the walls will
77
- // not render properly. DeadInfinity suspects that this might be because it is trying to use the
78
- // Dark Room's backdrop (instead of The Chest).
79
- const stage = -1 as LevelStage;
80
- const stageType = StageType.WRATH_OF_THE_LAMB;
81
- level.SetStage(stage, stageType);
82
- reorderedCallbacksSetStage(stage, stageType);
90
+ // Set the stage to an invalid value, which will prevent the walls and floors from loading.
91
+ // Furthermore, we must use `StageType.WRATH_OF_THE_LAMB` instead of `StageType.ORIGINAL` or else
92
+ // the walls will not render properly. DeadInfinity suspects that this might be because it is
93
+ // trying to use the Dark Room's backdrop (instead of The Chest).
94
+ const targetStage = INVALID_STAGE_VALUE;
95
+ const targetStageType = StageType.WRATH_OF_THE_LAMB;
96
+ level.SetStage(targetStage, targetStageType);
97
+ reorderedCallbacksSetStage(targetStage, targetStageType);
83
98
 
84
99
  // We must reload the current room in order for the `Level.SetStage` method to take effect.
85
100
  // Furthermore, we need to cancel the queued warp to the `GridRoom.DEBUG` room. We can accomplish
@@ -197,3 +212,11 @@ export function registerCustomBoss(
197
212
  const entityID = getEntityIDFromConstituents(entityType, variant, subType);
198
213
  customBossPNGPaths.set(entityID, [namePNGPath, portraitPNGPath]);
199
214
  }
215
+
216
+ /**
217
+ * Helper function to disable the custom stage. This is typically called before taking the player to
218
+ * a vanilla floor.
219
+ */
220
+ export function disableCustomStage(): void {
221
+ v.run.currentCustomStage = null;
222
+ }
@@ -26,7 +26,7 @@ const TEXT_STAY_FRAME = 8;
26
26
  const TEXT_OUT_FRAME = 60;
27
27
 
28
28
  /** This matches the offset that the vanilla game uses; determined via trial and error. */
29
- const STREAK_SPRITE_TOP_OFFSET = Vector(0, 48.25);
29
+ const STREAK_SPRITE_TOP_OFFSET = Vector(0, 47);
30
30
 
31
31
  /** This matches the offset that the vanilla game uses; determined via trial and error. */
32
32
  const STREAK_SPRITE_BOTTOM_OFFSET = Vector(0, -48.25);
@@ -4,6 +4,7 @@ import {
4
4
  RoomType,
5
5
  SoundEffect,
6
6
  StageID,
7
+ StageType,
7
8
  } from "isaac-typescript-definitions";
8
9
  import { game, sfxManager } from "../../cachedClasses";
9
10
  import { arrayRemove } from "../../functions/array";
@@ -18,7 +19,13 @@ import { PLAYER_PORTRAIT_PNG_FILE_NAMES } from "../../objects/playerPortraitPNGF
18
19
  import { VERSUS_SCREEN_BACKGROUND_COLORS } from "../../objects/versusScreenBackgroundColors";
19
20
  import { VERSUS_SCREEN_DIRT_SPOT_COLORS } from "../../objects/versusScreenDirtSpotColors";
20
21
  import { pause, unpause } from "../pause";
22
+ import { runNextGameFrame } from "../runInNFrames";
21
23
  import { ISAACSCRIPT_CUSTOM_STAGE_GFX_PATH } from "./customStageConstants";
24
+ import {
25
+ DEFAULT_BASE_STAGE,
26
+ DEFAULT_BASE_STAGE_TYPE,
27
+ INVALID_STAGE_VALUE,
28
+ } from "./exports";
22
29
  import v, { customBossPNGPaths } from "./v";
23
30
 
24
31
  const DEFAULT_CHARACTER = PlayerType.ISAAC;
@@ -102,12 +109,29 @@ export function versusScreenInit(): void {
102
109
  export function playVersusScreenAnimation(customStage: CustomStage): void {
103
110
  const room = game.GetRoom();
104
111
  const roomType = room.GetType();
112
+ const roomCleared = room.IsClear();
105
113
  const hud = game.GetHUD();
106
114
 
107
115
  if (roomType !== RoomType.BOSS) {
108
116
  return;
109
117
  }
110
118
 
119
+ if (roomCleared) {
120
+ return;
121
+ }
122
+
123
+ if (willVanillaVersusScreenPlay()) {
124
+ // Since we are on an invalid stage, the versus screen will have a completely black background.
125
+ // Revert to using the background from the default stage.
126
+ const level = game.GetLevel();
127
+ level.SetStage(DEFAULT_BASE_STAGE, DEFAULT_BASE_STAGE_TYPE);
128
+ runNextGameFrame(() => {
129
+ const futureLevel = game.GetLevel();
130
+ futureLevel.SetStage(INVALID_STAGE_VALUE, StageType.ORIGINAL);
131
+ });
132
+ return;
133
+ }
134
+
111
135
  v.run.showingBossVersusScreen = true;
112
136
 
113
137
  pause();
@@ -156,6 +180,11 @@ export function playVersusScreenAnimation(customStage: CustomStage): void {
156
180
  }
157
181
  }
158
182
 
183
+ function willVanillaVersusScreenPlay() {
184
+ const bosses = getBosses();
185
+ return bosses.some((boss) => boss.GetBossID() !== 0);
186
+ }
187
+
159
188
  /** Use the character of the 0th player. */
160
189
  function getPlayerPNGPaths(): [
161
190
  playerNamePNGPath: string,
@@ -9,7 +9,11 @@ import v from "./v";
9
9
  const blackSprite = Sprite();
10
10
 
11
11
  export function drawBlackSprite(): void {
12
- if (v.run.state !== StageTravelState.PAUSING_ON_BLACK) {
12
+ if (
13
+ v.run.state !== StageTravelState.WAITING_FOR_FIRST_PIXELATION_TO_END &&
14
+ v.run.state !==
15
+ StageTravelState.WAITING_FOR_SECOND_PIXELATION_TO_GET_HALF_WAY
16
+ ) {
13
17
  return;
14
18
  }
15
19
 
@@ -3,6 +3,10 @@ import { GridEntityType } from "isaac-typescript-definitions";
3
3
  export const CUSTOM_TRAPDOOR_FEATURE_NAME = "customTrapdoor";
4
4
 
5
5
  export const GridEntityTypeCustom = {
6
+ /**
7
+ * We arbitrarily choose 1000 as to not conflict with end-user mods. (The expectation is that
8
+ * end-user mods will begin their enums with values of 1 and increment upwards.)
9
+ */
6
10
  TRAPDOOR_CUSTOM: 1000 as GridEntityType,
7
11
  } as const;
8
12
 
@@ -10,6 +10,7 @@ import { getNextStage, getNextStageType } from "../../functions/nextStage";
10
10
  import { getRoomListIndex } from "../../functions/roomData";
11
11
  import { isVector } from "../../functions/vector";
12
12
  import { CustomTrapdoorDescription } from "../../interfaces/private/CustomTrapdoorDescription";
13
+ import { TrapdoorDestination } from "../../types/TrapdoorDestination";
13
14
  import { spawnCustomGridEntity } from "../customGridEntity";
14
15
  import {
15
16
  CUSTOM_TRAPDOOR_FEATURE_NAME,
@@ -33,12 +34,12 @@ import v from "./v";
33
34
  *
34
35
  * @param gridIndexOrPosition The location in the room to spawn the trapdoor.
35
36
  * @param destination Optional. Used to specify where the player will go after jumping into the
36
- * trapdoor. Can either be a tuple or undefined. For example, a destination of
37
- * `[LevelStage.CAVES_1, StageType.ORIGINAL]` corresponds to Caves 1, and a
38
- * destination of `["Slaughterhouse", 1]` corresponds to a custom stage of
39
- * Slaughterhouse 1. If the destination is set to undefined, then the "normal"
40
- * destination corresponding to the current stage and room will be used (e.g. the
41
- * next floor).
37
+ * trapdoor. Can either be a vanilla stage tuple, a custom stage tuple, or
38
+ * undefined. For example, a destination of `[LevelStage.CAVES_1,
39
+ * StageType.ORIGINAL]` corresponds to Caves 1, and a destination of
40
+ * `["Slaughterhouse", 1]` corresponds to a custom stage of Slaughterhouse 1. If
41
+ * the destination is undefined, then the "normal" destination corresponding to
42
+ * the current stage and room will be used (e.g. the next floor, in most cases).
42
43
  * @param anm2Path Optional. The path to the anm2 file to use. By default, the vanilla trapdoor anm2
43
44
  * of "gfx/grid/door_11_trapdoor.anm2" will be used. The specified anm2 file must
44
45
  * have animations called "Opened", "Closed", and "Open Animation".
@@ -47,9 +48,7 @@ import v from "./v";
47
48
  */
48
49
  export function spawnCustomTrapdoor(
49
50
  gridIndexOrPosition: int | Vector,
50
- destination?:
51
- | [stage: LevelStage, stageType: StageType]
52
- | [customStageName: string, floorNum: int],
51
+ destination?: TrapdoorDestination,
53
52
  anm2Path = "gfx/grid/door_11_trapdoor.anm2",
54
53
  spawnOpen?: boolean,
55
54
  ): GridEntity {
@@ -16,7 +16,7 @@ import { getRoomGridIndex, getRoomListIndex } from "../../functions/roomData";
16
16
  import { teleport } from "../../functions/roomTransition";
17
17
  import { setStage } from "../../functions/stage";
18
18
  import { isString } from "../../functions/types";
19
- import { setCustomStage } from "../customStage/exports";
19
+ import { disableCustomStage, setCustomStage } from "../customStage/exports";
20
20
  import { topStreakTextStart } from "../customStage/streakText";
21
21
  import { enableAllInputs } from "../disableInputs";
22
22
  import { runNextGameFrame } from "../runInNFrames";
@@ -48,7 +48,7 @@ export function customTrapdoorInit(mod: ModUpgraded): void {
48
48
  function postRender() {
49
49
  checkAllPlayersJumpComplete();
50
50
  checkPixelationToBlackComplete();
51
- checkPausingOnBlackComplete();
51
+ checkSecondPixelationHalfWay();
52
52
  checkAllPlayersLayingDownComplete();
53
53
  drawBlackSprite();
54
54
  }
@@ -77,6 +77,8 @@ function checkAllPlayersJumpComplete() {
77
77
  Direction.NO_DIRECTION,
78
78
  RoomTransitionAnim.PIXELATION,
79
79
  );
80
+
81
+ // Next, we wait a certain amount of render frames for the pixelation to fade the screen to black.
80
82
  }
81
83
 
82
84
  function checkPixelationToBlackComplete() {
@@ -89,7 +91,6 @@ function checkPixelationToBlackComplete() {
89
91
 
90
92
  const hud = game.GetHUD();
91
93
  const renderFrameCount = Isaac.GetFrameCount();
92
- const roomGridIndex = getRoomGridIndex();
93
94
 
94
95
  const renderFrameScreenBlack =
95
96
  v.run.stateRenderFrame + PIXELATION_TO_BLACK_FRAMES;
@@ -97,25 +98,44 @@ function checkPixelationToBlackComplete() {
97
98
  return;
98
99
  }
99
100
 
100
- v.run.state = StageTravelState.PAUSING_ON_BLACK;
101
- v.run.stateRenderFrame = renderFrameCount;
101
+ v.run.state = StageTravelState.WAITING_FOR_FIRST_PIXELATION_TO_END;
102
102
 
103
+ // Now, we display a black sprite on top of the pixelation effect, to prevent showing the rest of
104
+ // the animation.
103
105
  hud.SetVisible(false);
104
- goToCustomDestination();
105
106
 
106
- // Start another pixelation effect. This time, we will keep the screen black with the sprite, and
107
- // then remove the black sprite once the pixelation effect is halfway complete.
108
- teleport(
109
- roomGridIndex,
110
- Direction.NO_DIRECTION,
111
- RoomTransitionAnim.PIXELATION,
112
- true,
113
- );
107
+ // If the pixelation effect is not fully allowed to complete, the game's internal buffer will not
108
+ // be flushed. The consequence of this is that after 11 custom stage transitions, the "log.txt"
109
+ // starts to become become spammed with: [ASSERT] - PushRenderTarget: stack overflow!
110
+
111
+ // In order to work around this, we fully let the animation complete by only continuing the stage
112
+ // transition on the next game frame.
113
+ runNextGameFrame(() => {
114
+ const level = game.GetLevel();
115
+ const startingRoomIndex = level.GetStartingRoomIndex();
116
+ const futureRenderFrameCount = Isaac.GetFrameCount();
117
+
118
+ v.run.state =
119
+ StageTravelState.WAITING_FOR_SECOND_PIXELATION_TO_GET_HALF_WAY;
120
+ v.run.stateRenderFrame = futureRenderFrameCount;
121
+
122
+ goToCustomDestination();
123
+
124
+ // Start another pixelation effect. This time, we will keep the screen black with the sprite,
125
+ // and then remove the black sprite once the pixelation effect is halfway complete.
126
+ teleport(
127
+ startingRoomIndex,
128
+ Direction.NO_DIRECTION,
129
+ RoomTransitionAnim.PIXELATION,
130
+ true,
131
+ );
132
+ });
114
133
  }
115
134
 
116
- function checkPausingOnBlackComplete() {
135
+ function checkSecondPixelationHalfWay() {
117
136
  if (
118
- v.run.state !== StageTravelState.PAUSING_ON_BLACK ||
137
+ v.run.state !==
138
+ StageTravelState.WAITING_FOR_SECOND_PIXELATION_TO_GET_HALF_WAY ||
119
139
  v.run.stateRenderFrame === null
120
140
  ) {
121
141
  return;
@@ -135,12 +155,12 @@ function checkPausingOnBlackComplete() {
135
155
  hud.SetVisible(true);
136
156
 
137
157
  runNextRoom(() => {
158
+ v.run.state = StageTravelState.PLAYERS_LAYING_DOWN;
159
+
138
160
  // After the room transition, the players will be placed next to a door, but they should be in
139
161
  // the center of the room to emulate what happens on a vanilla stage.
140
162
  movePlayersToCenter();
141
163
 
142
- v.run.state = StageTravelState.PLAYERS_LAYING_DOWN;
143
-
144
164
  for (const player of getAllPlayers()) {
145
165
  player.AnimateAppear();
146
166
 
@@ -186,6 +206,7 @@ function goToCustomDestination() {
186
206
  setCustomStage("Slaughterhouse", firstFloor);
187
207
  } else {
188
208
  // A number represents a vanilla `LevelStage`.
209
+ disableCustomStage();
189
210
  setStage(arg1, arg2 as StageType);
190
211
  }
191
212
  }
@@ -81,6 +81,9 @@ function playerTouchedCustomTrapdoor(
81
81
  v.run.state = StageTravelState.PLAYERS_JUMPING_DOWN;
82
82
  v.run.destination = trapdoorDescription.destination;
83
83
 
84
+ // We don't want to allow pausing, since that will allow render frames to pass without advancing
85
+ // the stage traveling logic. (We track how many render frames have passed to know when to move to
86
+ // the next step.)
84
87
  const whitelist = new Set([ButtonAction.CONSOLE]);
85
88
  disableAllInputsExceptFor(CUSTOM_TRAPDOOR_FEATURE_NAME, whitelist);
86
89
  setPlayerAttributes(player, gridEntity.Position);
@@ -114,6 +117,8 @@ function setPlayerAttributes(trapdoorPlayer: EntityPlayer, position: Vector) {
114
117
  // We don't want enemy attacks to move the players.
115
118
  player.EntityCollisionClass = EntityCollisionClass.NONE;
116
119
  player.GridCollisionClass = EntityGridCollisionClass.NONE;
120
+
121
+ player.StopExtraAnimation();
117
122
  }
118
123
  }
119
124
 
@@ -1,7 +1,7 @@
1
- import { LevelStage, StageType } from "isaac-typescript-definitions";
2
1
  import { DefaultMap } from "../../classes/DefaultMap";
3
2
  import { StageTravelState } from "../../enums/private/StageTravelState";
4
3
  import { CustomTrapdoorDescription } from "../../interfaces/private/CustomTrapdoorDescription";
4
+ import { TrapdoorDestination } from "../../types/TrapdoorDestination";
5
5
 
6
6
  const v = {
7
7
  run: {
@@ -10,10 +10,7 @@ const v = {
10
10
  /** The render frame that this state was reached. */
11
11
  stateRenderFrame: null as int | null,
12
12
 
13
- destination: null as
14
- | [stage: LevelStage, stageType: StageType]
15
- | [customStageName: string, floorNum: int]
16
- | null,
13
+ destination: null as TrapdoorDestination | null,
17
14
  },
18
15
 
19
16
  level: {
@@ -114,6 +114,13 @@ function postCollectibleRemoved(
114
114
  * middle of the run (e.g. with D4), the order of the inventory will not correspond to the order
115
115
  * that the items were actually given to the player. In this case, the inventory will be in the
116
116
  * order of the lowest `CollectibleType` to the highest.
117
+ *
118
+ * Under the hood, the inventory tracking works by tracking the number of collectibles that a player
119
+ * has on every frame. Thus, in a situation where a collectible was both added and removed to the
120
+ * player on the same frame, the amount of total collectibles would stay the same, and the inventory
121
+ * would not be updated. In vanilla, this situation would never happen, but another mod might do
122
+ * this for some reason. (With that said, the next time that a collectible is normally added or
123
+ * removed, it would trigger a re-scan, and the previous changes would be picked up.)
117
124
  */
118
125
  export function getPlayerInventory(
119
126
  player: EntityPlayer,
@@ -1,9 +1,7 @@
1
- import { LevelStage, StageType } from "isaac-typescript-definitions";
1
+ import { TrapdoorDestination } from "../../types/TrapdoorDestination";
2
2
 
3
3
  export interface CustomTrapdoorDescription {
4
4
  open: boolean;
5
- destination:
6
- | [stage: LevelStage, stageType: StageType]
7
- | [customStageName: string, floorNum: int];
5
+ destination: TrapdoorDestination;
8
6
  firstSpawn: boolean;
9
7
  }
@@ -0,0 +1,5 @@
1
+ import { LevelStage, StageType } from "isaac-typescript-definitions";
2
+
3
+ export type TrapdoorDestination =
4
+ | [stage: LevelStage, stageType: StageType]
5
+ | [customStageName: string, floorNum: int];