isaacscript-common 6.11.1 → 6.13.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 (216) hide show
  1. package/dist/callbacks/postFlip.lua +2 -2
  2. package/dist/callbacks/postGridEntityCustomRender.d.ts +2 -0
  3. package/dist/callbacks/postGridEntityCustomRender.d.ts.map +1 -0
  4. package/dist/callbacks/postGridEntityCustomRender.lua +36 -0
  5. package/dist/callbacks/postGridEntityCustomUpdate.d.ts +2 -0
  6. package/dist/callbacks/postGridEntityCustomUpdate.d.ts.map +1 -0
  7. package/dist/callbacks/postGridEntityCustomUpdate.lua +36 -0
  8. package/dist/callbacks/postNewRoomEarly.lua +2 -2
  9. package/dist/callbacks/postPickupInitFirst.lua +1 -20
  10. package/dist/callbacks/reorderedCallbacks.d.ts +5 -5
  11. package/dist/callbacks/reorderedCallbacks.d.ts.map +1 -1
  12. package/dist/callbacks/reorderedCallbacks.lua +5 -5
  13. package/dist/callbacks/subscriptions/postFirstFlip.d.ts +1 -1
  14. package/dist/callbacks/subscriptions/postFirstFlip.d.ts.map +1 -1
  15. package/dist/callbacks/subscriptions/postFirstFlip.lua +2 -2
  16. package/dist/callbacks/subscriptions/postFlip.d.ts +1 -1
  17. package/dist/callbacks/subscriptions/postFlip.d.ts.map +1 -1
  18. package/dist/callbacks/subscriptions/postFlip.lua +2 -2
  19. package/dist/callbacks/subscriptions/postGridEntityCustomRender.d.ts +6 -0
  20. package/dist/callbacks/subscriptions/postGridEntityCustomRender.d.ts.map +1 -0
  21. package/dist/callbacks/subscriptions/postGridEntityCustomRender.lua +29 -0
  22. package/dist/callbacks/subscriptions/postGridEntityCustomUpdate.d.ts +6 -0
  23. package/dist/callbacks/subscriptions/postGridEntityCustomUpdate.d.ts.map +1 -0
  24. package/dist/callbacks/subscriptions/postGridEntityCustomUpdate.lua +29 -0
  25. package/dist/constants.d.ts +1 -5
  26. package/dist/constants.d.ts.map +1 -1
  27. package/dist/constants.lua +0 -7
  28. package/dist/constantsFirstLast.d.ts +5 -1
  29. package/dist/constantsFirstLast.d.ts.map +1 -1
  30. package/dist/constantsFirstLast.lua +6 -0
  31. package/dist/enums/ModCallbackCustom.d.ts +91 -68
  32. package/dist/enums/ModCallbackCustom.d.ts.map +1 -1
  33. package/dist/enums/ModCallbackCustom.lua +62 -58
  34. package/dist/enums/private/StageTravelState.d.ts +6 -1
  35. package/dist/enums/private/StageTravelState.d.ts.map +1 -1
  36. package/dist/enums/private/StageTravelState.lua +10 -0
  37. package/dist/enums/private/TrapdoorAnimation.d.ts +6 -0
  38. package/dist/enums/private/TrapdoorAnimation.d.ts.map +1 -0
  39. package/dist/enums/private/TrapdoorAnimation.lua +6 -0
  40. package/dist/features/collectibleItemPoolType.d.ts +2 -2
  41. package/dist/features/collectibleItemPoolType.lua +2 -2
  42. package/dist/features/customGridEntity.d.ts +17 -5
  43. package/dist/features/customGridEntity.d.ts.map +1 -1
  44. package/dist/features/customGridEntity.lua +78 -15
  45. package/dist/features/customStage/exports.d.ts.map +1 -1
  46. package/dist/features/customStage/exports.lua +0 -13
  47. package/dist/features/customStage/init.d.ts.map +1 -1
  48. package/dist/features/customStage/init.lua +16 -1
  49. package/dist/features/customStage/streakText.d.ts.map +1 -1
  50. package/dist/features/customStage/streakText.lua +0 -1
  51. package/dist/features/customTrapdoor/blackSprite.d.ts +2 -0
  52. package/dist/features/customTrapdoor/blackSprite.d.ts.map +1 -0
  53. package/dist/features/customTrapdoor/blackSprite.lua +19 -0
  54. package/dist/features/customTrapdoor/customTrapdoorConstants.d.ts +8 -3
  55. package/dist/features/customTrapdoor/customTrapdoorConstants.d.ts.map +1 -1
  56. package/dist/features/customTrapdoor/customTrapdoorConstants.lua +9 -1
  57. package/dist/features/customTrapdoor/exports.d.ts +11 -19
  58. package/dist/features/customTrapdoor/exports.d.ts.map +1 -1
  59. package/dist/features/customTrapdoor/exports.lua +48 -82
  60. package/dist/features/customTrapdoor/init.d.ts +3 -0
  61. package/dist/features/customTrapdoor/init.d.ts.map +1 -0
  62. package/dist/features/customTrapdoor/init.lua +174 -0
  63. package/dist/features/customTrapdoor/openClose.d.ts +5 -0
  64. package/dist/features/customTrapdoor/openClose.d.ts.map +1 -0
  65. package/dist/features/customTrapdoor/openClose.lua +60 -0
  66. package/dist/features/customTrapdoor/touched.d.ts +4 -0
  67. package/dist/features/customTrapdoor/touched.d.ts.map +1 -0
  68. package/dist/features/customTrapdoor/touched.lua +159 -0
  69. package/dist/features/customTrapdoor/v.d.ts +16 -2
  70. package/dist/features/customTrapdoor/v.d.ts.map +1 -1
  71. package/dist/features/customTrapdoor/v.lua +8 -6
  72. package/dist/features/deployJSONRoom.d.ts +2 -2
  73. package/dist/features/deployJSONRoom.lua +2 -2
  74. package/dist/features/extraConsoleCommands/init.d.ts.map +1 -1
  75. package/dist/features/extraConsoleCommands/init.lua +14 -15
  76. package/dist/features/extraConsoleCommands/listCommands.d.ts +15 -19
  77. package/dist/features/extraConsoleCommands/listCommands.d.ts.map +1 -1
  78. package/dist/features/extraConsoleCommands/listCommands.lua +34 -42
  79. package/dist/features/pause.d.ts +1 -1
  80. package/dist/features/pause.d.ts.map +1 -1
  81. package/dist/features/pause.lua +87 -8
  82. package/dist/features/persistentEntities.d.ts.map +1 -1
  83. package/dist/features/persistentEntities.lua +7 -7
  84. package/dist/features/pickupIndex.d.ts +19 -0
  85. package/dist/features/pickupIndex.d.ts.map +1 -0
  86. package/dist/features/pickupIndex.lua +197 -0
  87. package/dist/features/roomHistory.d.ts +24 -0
  88. package/dist/features/roomHistory.d.ts.map +1 -0
  89. package/dist/features/roomHistory.lua +89 -0
  90. package/dist/features/taintedLazarusPlayers.d.ts.map +1 -1
  91. package/dist/features/taintedLazarusPlayers.lua +13 -21
  92. package/dist/functions/collectibles.d.ts +26 -13
  93. package/dist/functions/collectibles.d.ts.map +1 -1
  94. package/dist/functions/collectibles.lua +26 -13
  95. package/dist/functions/entities.d.ts +3 -3
  96. package/dist/functions/entities.d.ts.map +1 -1
  97. package/dist/functions/entities.lua +8 -3
  98. package/dist/functions/gridEntities.d.ts +2 -2
  99. package/dist/functions/gridEntities.lua +2 -2
  100. package/dist/functions/isaacAPIClass.d.ts +64 -0
  101. package/dist/functions/isaacAPIClass.d.ts.map +1 -1
  102. package/dist/functions/isaacAPIClass.lua +84 -1
  103. package/dist/functions/log.lua +3 -3
  104. package/dist/functions/map.d.ts +2 -0
  105. package/dist/functions/map.d.ts.map +1 -1
  106. package/dist/functions/map.lua +7 -0
  107. package/dist/functions/pickupVariants.d.ts +2 -2
  108. package/dist/functions/pickupVariants.d.ts.map +1 -1
  109. package/dist/functions/pickupVariants.lua +2 -2
  110. package/dist/functions/playerCenter.lua +2 -2
  111. package/dist/functions/playerIndex.d.ts +5 -3
  112. package/dist/functions/playerIndex.d.ts.map +1 -1
  113. package/dist/functions/playerIndex.lua +15 -24
  114. package/dist/functions/roomData.d.ts +3 -2
  115. package/dist/functions/roomData.d.ts.map +1 -1
  116. package/dist/functions/roomData.lua +3 -2
  117. package/dist/functions/rooms.d.ts +6 -6
  118. package/dist/functions/rooms.lua +6 -6
  119. package/dist/functions/set.d.ts +2 -0
  120. package/dist/functions/set.d.ts.map +1 -1
  121. package/dist/functions/set.lua +6 -0
  122. package/dist/functions/stage.d.ts +1 -0
  123. package/dist/functions/stage.d.ts.map +1 -1
  124. package/dist/functions/stage.lua +4 -0
  125. package/dist/functions/table.d.ts +1 -1
  126. package/dist/functions/table.d.ts.map +1 -1
  127. package/dist/index.d.ts +3 -0
  128. package/dist/index.d.ts.map +1 -1
  129. package/dist/index.lua +23 -0
  130. package/dist/initCustomCallbacks.d.ts.map +1 -1
  131. package/dist/initCustomCallbacks.lua +6 -0
  132. package/dist/initFeatures.d.ts.map +1 -1
  133. package/dist/initFeatures.lua +9 -0
  134. package/dist/interfaces/AddCallbackParameterCustom.d.ts +4 -0
  135. package/dist/interfaces/AddCallbackParameterCustom.d.ts.map +1 -1
  136. package/dist/interfaces/CustomGridEntityData.d.ts +5 -1
  137. package/dist/interfaces/CustomGridEntityData.d.ts.map +1 -1
  138. package/dist/interfaces/RoomDescription.d.ts +14 -0
  139. package/dist/interfaces/RoomDescription.d.ts.map +1 -0
  140. package/dist/interfaces/RoomDescription.lua +2 -0
  141. package/dist/interfaces/private/CustomTrapdoorDescription.d.ts +3 -0
  142. package/dist/interfaces/private/CustomTrapdoorDescription.d.ts.map +1 -1
  143. package/dist/objects/callbackRegisterFunctions.d.ts.map +1 -1
  144. package/dist/objects/callbackRegisterFunctions.lua +6 -0
  145. package/dist/types/CollectibleIndex.d.ts +1 -1
  146. package/dist/types/PickupIndex.d.ts +17 -0
  147. package/dist/types/PickupIndex.d.ts.map +1 -0
  148. package/dist/types/PickupIndex.lua +2 -0
  149. package/dist/types/PlayerIndex.d.ts +1 -1
  150. package/dist/upgradeMod.d.ts.map +1 -1
  151. package/package.json +2 -2
  152. package/src/callbacks/postFlip.ts +3 -2
  153. package/src/callbacks/postGridEntityCustomRender.ts +44 -0
  154. package/src/callbacks/postGridEntityCustomUpdate.ts +44 -0
  155. package/src/callbacks/postNewRoomEarly.ts +2 -2
  156. package/src/callbacks/postPickupInitFirst.ts +3 -32
  157. package/src/callbacks/postPlayerReorderedCallbacks.ts +3 -3
  158. package/src/callbacks/reorderedCallbacks.ts +9 -8
  159. package/src/callbacks/subscriptions/postFirstFlip.ts +6 -3
  160. package/src/callbacks/subscriptions/postFlip.ts +6 -3
  161. package/src/callbacks/subscriptions/postGridEntityCustomRender.ts +41 -0
  162. package/src/callbacks/subscriptions/postGridEntityCustomUpdate.ts +41 -0
  163. package/src/constants.ts +1 -9
  164. package/src/constantsFirstLast.ts +16 -0
  165. package/src/enums/ModCallbackCustom.ts +35 -10
  166. package/src/enums/private/StageTravelState.ts +5 -1
  167. package/src/enums/private/TrapdoorAnimation.ts +5 -0
  168. package/src/features/collectibleItemPoolType.ts +3 -3
  169. package/src/features/customGridEntity.ts +93 -10
  170. package/src/features/customStage/exports.ts +3 -22
  171. package/src/features/customStage/init.ts +20 -0
  172. package/src/features/customStage/streakText.ts +0 -1
  173. package/src/features/customTrapdoor/blackSprite.ts +22 -0
  174. package/src/features/customTrapdoor/customTrapdoorConstants.ts +13 -3
  175. package/src/features/customTrapdoor/exports.ts +52 -121
  176. package/src/features/customTrapdoor/init.ts +217 -0
  177. package/src/features/customTrapdoor/openClose.ts +103 -0
  178. package/src/features/customTrapdoor/touched.ts +195 -0
  179. package/src/features/customTrapdoor/v.ts +16 -10
  180. package/src/features/deployJSONRoom.ts +5 -5
  181. package/src/features/extraConsoleCommands/init.ts +22 -16
  182. package/src/features/extraConsoleCommands/listCommands.ts +38 -43
  183. package/src/features/pause.ts +97 -7
  184. package/src/features/persistentEntities.ts +9 -8
  185. package/src/features/pickupIndex.ts +257 -0
  186. package/src/features/playerInventory.ts +2 -2
  187. package/src/features/roomHistory.ts +113 -0
  188. package/src/features/saveDataManager/main.ts +3 -3
  189. package/src/features/taintedLazarusPlayers.ts +37 -36
  190. package/src/functions/collectibles.ts +26 -13
  191. package/src/functions/deepCopy.ts +2 -2
  192. package/src/functions/entities.ts +7 -4
  193. package/src/functions/gridEntities.ts +2 -2
  194. package/src/functions/isaacAPIClass.ts +106 -1
  195. package/src/functions/map.ts +10 -0
  196. package/src/functions/pickupVariants.ts +2 -2
  197. package/src/functions/playerCenter.ts +2 -2
  198. package/src/functions/playerIndex.ts +20 -21
  199. package/src/functions/rng.ts +1 -1
  200. package/src/functions/roomData.ts +3 -2
  201. package/src/functions/rooms.ts +6 -6
  202. package/src/functions/set.ts +7 -1
  203. package/src/functions/stage.ts +10 -1
  204. package/src/functions/table.ts +2 -2
  205. package/src/index.ts +3 -0
  206. package/src/initCustomCallbacks.ts +4 -0
  207. package/src/initFeatures.ts +6 -0
  208. package/src/interfaces/AddCallbackParameterCustom.ts +4 -0
  209. package/src/interfaces/CustomGridEntityData.ts +6 -1
  210. package/src/interfaces/RoomDescription.ts +19 -0
  211. package/src/interfaces/private/CustomTrapdoorDescription.ts +4 -0
  212. package/src/objects/callbackRegisterFunctions.ts +6 -0
  213. package/src/types/CollectibleIndex.ts +1 -1
  214. package/src/types/PickupIndex.ts +15 -0
  215. package/src/types/PlayerIndex.ts +1 -1
  216. package/src/upgradeMod.ts +2 -1
@@ -0,0 +1,5 @@
1
+ export enum TrapdoorAnimation {
2
+ OPENED = "Opened",
3
+ CLOSED = "Closed",
4
+ OPEN_ANIMATION = "Open Animation",
5
+ }
@@ -1,5 +1,5 @@
1
1
  // The item pool type of a collectible is not stored on the collectible. Thus, we scan for incoming
2
- // item pool types in the PreGetCollectible callback, and then assume that the next spawned
2
+ // item pool types in the `PRE_GET_COLLECTIBLE` callback, and then assume that the next spawned
3
3
  // collectible has this item pool type.
4
4
 
5
5
  import {
@@ -45,8 +45,8 @@ function postPickupInitCollectible(pickup: EntityPickup) {
45
45
 
46
46
  /**
47
47
  * Helper function to get the item pool type that a given collectible came from. Since there is no
48
- * native method in the API to get this, we listen in the PreGetCollectible callback for item pool
49
- * types, and then assume that the next spawned collectible will match.
48
+ * native method in the API to get this, we listen in the `PRE_GET_COLLECTIBLE` callback for item
49
+ * pool types, and then assume that the next spawned collectible will match.
50
50
  */
51
51
  export function getCollectibleItemPoolType(
52
52
  collectible: EntityPickup,
@@ -19,6 +19,7 @@ import {
19
19
  import { getRoomListIndex } from "../functions/roomData";
20
20
  import { isVector } from "../functions/vector";
21
21
  import { CustomGridEntityData } from "../interfaces/CustomGridEntityData";
22
+ import { runNextGameFrame } from "./runInNFrames";
22
23
  import { saveDataManager } from "./saveDataManager/exports";
23
24
 
24
25
  const FEATURE_NAME = "customGridEntity";
@@ -30,6 +31,10 @@ const v = {
30
31
  () => new Map(),
31
32
  ),
32
33
  },
34
+
35
+ room: {
36
+ manuallyUsingShovel: false,
37
+ },
33
38
  };
34
39
 
35
40
  /** @internal */
@@ -53,12 +58,54 @@ export function customGridEntityInit(mod: ModUpgraded): void {
53
58
  function preUseItemWeNeedToGoDeeper(
54
59
  _collectibleType: CollectibleType,
55
60
  _rng: RNG,
56
- _player: EntityPlayer,
61
+ player: EntityPlayer,
57
62
  _useFlags: BitFlags<UseFlag>,
58
63
  _activeSlot: ActiveSlot,
59
64
  _customVarData: int,
60
65
  ): boolean | undefined {
61
- return undefined;
66
+ // If a player uses We Need to Go Deeper on top of a custom grid entity, then they will always get
67
+ // a crawlspace, due to how custom grids are implemented with decorations. Thus, remove the custom
68
+ // grid entity to prevent this from happening if needed.
69
+ const room = game.GetRoom();
70
+ const roomListIndex = getRoomListIndex();
71
+ const roomCustomGridEntities = v.level.customGridEntities.get(roomListIndex);
72
+ if (roomCustomGridEntities === undefined) {
73
+ return undefined;
74
+ }
75
+
76
+ const gridIndex = room.GetGridIndex(player.Position);
77
+ const customGridEntity = roomCustomGridEntities.get(gridIndex);
78
+ if (customGridEntity === undefined) {
79
+ return undefined;
80
+ }
81
+
82
+ // If the custom grid entity has collision, then the player should not be able to be standing on
83
+ // top of it.
84
+ if (customGridEntity.gridCollisionClass !== GridCollisionClass.NONE) {
85
+ return undefined;
86
+ }
87
+
88
+ removeGridEntity(customGridEntity.gridIndex, false);
89
+
90
+ const playerPtr = EntityPtr(player);
91
+ runNextGameFrame(() => {
92
+ const futureEntity = playerPtr.Ref;
93
+ if (futureEntity === undefined) {
94
+ return;
95
+ }
96
+
97
+ const futurePlayer = futureEntity.ToPlayer();
98
+ if (futurePlayer === undefined) {
99
+ return;
100
+ }
101
+
102
+ v.room.manuallyUsingShovel = true;
103
+ futurePlayer.UseActiveItem(CollectibleType.WE_NEED_TO_GO_DEEPER);
104
+ v.room.manuallyUsingShovel = false;
105
+ });
106
+
107
+ // Cancel the original effect.
108
+ return true;
62
109
  }
63
110
 
64
111
  // ModCallbackCustom.POST_NEW_ROOM_REORDERED
@@ -81,12 +128,18 @@ function postNewRoomReordered() {
81
128
 
82
129
  const sprite = decoration.GetSprite();
83
130
  sprite.Load(data.anm2Path, true);
84
- sprite.Play(data.defaultAnimation, true);
131
+ const animationToPlay =
132
+ data.defaultAnimation === undefined
133
+ ? sprite.GetDefaultAnimation()
134
+ : data.defaultAnimation;
135
+ sprite.Play(animationToPlay, true);
85
136
  }
86
137
  }
87
138
 
88
139
  /**
89
- * Helper function to spawn a custom grid entity.
140
+ * Helper function to spawn a custom grid entity. Custom grid entities are persistent in that they
141
+ * will reappear if the player leaves and re-enters the room. (It will be manually respawned in the
142
+ * `POST_NEW_ROOM` callback.)
90
143
  *
91
144
  * This is an IsaacScript feature because the vanilla game does not support any custom grid
92
145
  * entities. Under the hood, IsaacScript accomplishes this by using decorations with an arbitrary
@@ -103,17 +156,18 @@ function postNewRoomReordered() {
103
156
  * @param gridIndexOrPosition The grid index or position in the room that you want to spawn the grid
104
157
  * entity at. If a position is specified, the closest grid index will be
105
158
  * used.
106
- * @param anm2Path The path to the ANM2 file to use for the sprite.
107
- * @param defaultAnimation The name of the animation to play after the sprite is initialized and
108
- * after the player re-enters a room with this grid entity in it.
109
159
  * @param gridCollisionClass The collision class that you want the custom grid entity to have.
160
+ * @param anm2Path The path to the ANM2 file to use for the sprite.
161
+ * @param defaultAnimation Optional. The name of the animation to play after the sprite is
162
+ * initialized and after the player re-enters a room with this grid entity
163
+ * in it. If not specified, the default animation in the anm2 will be used.
110
164
  */
111
165
  export function spawnCustomGridEntity(
112
166
  gridEntityTypeCustom: GridEntityType,
113
167
  gridIndexOrPosition: int | Vector,
114
- anm2Path: string,
115
- defaultAnimation: string,
116
168
  gridCollisionClass: GridCollisionClass,
169
+ anm2Path: string,
170
+ defaultAnimation?: string,
117
171
  ): GridEntity {
118
172
  errorIfFeaturesNotInitialized(FEATURE_NAME);
119
173
 
@@ -142,7 +196,11 @@ export function spawnCustomGridEntity(
142
196
 
143
197
  const sprite = decoration.GetSprite();
144
198
  sprite.Load(anm2Path, true);
145
- sprite.Play(defaultAnimation, true);
199
+ const animationToPlay =
200
+ defaultAnimation === undefined
201
+ ? sprite.GetDefaultAnimation()
202
+ : defaultAnimation;
203
+ sprite.Play(animationToPlay, true);
146
204
 
147
205
  const customGridEntityData: CustomGridEntityData = {
148
206
  gridEntityTypeCustom,
@@ -216,3 +274,28 @@ export function removeCustomGrid(
216
274
 
217
275
  return decoration;
218
276
  }
277
+
278
+ /**
279
+ * Helper function to get the custom grid entities in the current room. Returns an array of tuples
280
+ * containing the raw decoration grid entity and the associated entity data.
281
+ */
282
+ export function getCustomGridEntities(): Array<
283
+ [gridEntity: GridEntity, data: CustomGridEntityData]
284
+ > {
285
+ const roomListIndex = getRoomListIndex();
286
+ const roomCustomGridEntities = v.level.customGridEntities.get(roomListIndex);
287
+ if (roomCustomGridEntities === undefined) {
288
+ return [];
289
+ }
290
+
291
+ const room = game.GetRoom();
292
+ const customGridEntities: Array<[GridEntity, CustomGridEntityData]> = [];
293
+ for (const [gridIndex, data] of roomCustomGridEntities.entries()) {
294
+ const gridEntity = room.GetGridEntity(gridIndex);
295
+ if (gridEntity !== undefined) {
296
+ customGridEntities.push([gridEntity, data]);
297
+ }
298
+ }
299
+
300
+ return customGridEntities;
301
+ }
@@ -1,10 +1,8 @@
1
1
  import {
2
- Direction,
3
2
  EntityType,
4
3
  GridRoom,
5
4
  LevelStage,
6
5
  RoomShape,
7
- RoomTransitionAnim,
8
6
  RoomType,
9
7
  StageType,
10
8
  } from "isaac-typescript-definitions";
@@ -12,12 +10,10 @@ import { game } from "../../cachedClasses";
12
10
  import { reorderedCallbacksSetStage } from "../../callbacks/reorderedCallbacks";
13
11
  import { getEntityIDFromConstituents } from "../../functions/entities";
14
12
  import { log, logError } from "../../functions/log";
15
- import { movePlayersToCenter } from "../../functions/playerCenter";
16
13
  import { newRNG } from "../../functions/rng";
17
14
  import { getRoomData } from "../../functions/roomData";
18
15
  import { getRooms } from "../../functions/rooms";
19
16
  import { getGotoCommand, setStage } from "../../functions/stage";
20
- import { runNextRoom } from "../runNextRoom";
21
17
  import { getRandomCustomStageRoom } from "./customStageUtils";
22
18
  import { topStreakTextStart } from "./streakText";
23
19
  import v, {
@@ -136,24 +132,9 @@ export function setCustomStage(name: string, verbose = false): void {
136
132
 
137
133
  // We must reload the current room in order for the `Level.SetStage` method to take effect.
138
134
  // Furthermore, we need to cancel the queued warp to the `GridRoom.DEBUG` room. We can accomplish
139
- // both of these things by initiating a room transition to the starting room of the floor. (We
140
- // assume that since we just warped to a new floor, we are already in the starting room.)
141
- game.StartRoomTransition(
142
- startingRoomGridIndex,
143
- Direction.NO_DIRECTION,
144
- RoomTransitionAnim.FADE,
145
- );
146
-
147
- // We do more setup once the room is reloaded from the transition.
148
- runNextRoom(postRoomTransition);
149
- }
150
-
151
- function postRoomTransition() {
152
- // After the room transition, the players will be placed next to a door, but they should be in the
153
- // center of the room to emulate what happens on a vanilla stage.
154
- movePlayersToCenter();
155
-
156
- topStreakTextStart();
135
+ // both of these things by initiating a room transition to an arbitrary room. However, we rely on
136
+ // the parent function to do this, since for normal purposes, we need to initiate a room
137
+ // transition for pixelation purposes.
157
138
  }
158
139
 
159
140
  export function setCustomStageDebug(): void {
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  DoorSlotFlag,
3
3
  GridEntityType,
4
+ LevelCurse,
4
5
  ModCallback,
5
6
  RoomShape,
6
7
  RoomType,
@@ -8,6 +9,7 @@ import {
8
9
  import { ModUpgraded } from "../../classes/ModUpgraded";
9
10
  import { ModCallbackCustom } from "../../enums/ModCallbackCustom";
10
11
  import { isArray } from "../../functions/array";
12
+ import { hasFlag, removeFlag } from "../../functions/flag";
11
13
  import { CustomStage, RoomTypeMap } from "../../interfaces/CustomStage";
12
14
  import {
13
15
  CustomStageLua,
@@ -49,6 +51,7 @@ export function customStageInit(mod: ModUpgraded): void {
49
51
  versusScreenInit();
50
52
 
51
53
  mod.AddCallback(ModCallback.POST_RENDER, postRender); // 2
54
+ mod.AddCallback(ModCallback.POST_CURSE_EVAL, postCurseEval); // 12
52
55
  mod.AddCallback(ModCallback.POST_GAME_STARTED, postGameStarted); // 15
53
56
  mod.AddCallback(ModCallback.GET_SHADER_PARAMS, getShaderParams); // 21
54
57
  mod.AddCallbackCustom(
@@ -137,6 +140,23 @@ function postRender() {
137
140
  versusScreenPostRender();
138
141
  }
139
142
 
143
+ // ModCallback.POST_CURSE_EVAL (12)
144
+ function postCurseEval(
145
+ curses: BitFlags<LevelCurse>,
146
+ ): BitFlags<LevelCurse> | undefined {
147
+ const customStage = v.run.currentCustomStage;
148
+ if (customStage === null) {
149
+ return undefined;
150
+ }
151
+
152
+ // Prevent XL floors on custom stages, since the streak text will not work properly.
153
+ if (hasFlag(curses, LevelCurse.MAZE)) {
154
+ return removeFlag(curses, LevelCurse.MAZE);
155
+ }
156
+
157
+ return undefined;
158
+ }
159
+
140
160
  // ModCallback.POST_GAME_STARTED (15)
141
161
  function postGameStarted() {
142
162
  // We don't early return here because we need to unconditionally reset the sprites.
@@ -287,7 +287,6 @@ function renderSprite(
287
287
  const adjustedX = centeredX + adjustment;
288
288
  const adjustedY = position.Y + STREAK_TEXT_BOTTOM_Y_OFFSET;
289
289
 
290
- sprite.RenderLayer(0, position);
291
290
  font.DrawStringScaled(
292
291
  name,
293
292
  adjustedX,
@@ -0,0 +1,22 @@
1
+ import { VectorZero } from "../../constants";
2
+ import { StageTravelState } from "../../enums/private/StageTravelState";
3
+ import v from "./v";
4
+
5
+ // In order to represent a black sprite, we just use the first frame of the boss versus screen
6
+ // animation. However, we must lazy load the sprite in order to prevent issues with mods that
7
+ // replace the vanilla files. (For some reason, loading the sprites will cause the overwrite to no
8
+ // longer apply on the second and subsequent runs.)
9
+ const blackSprite = Sprite();
10
+
11
+ export function drawBlackSprite(): void {
12
+ if (v.run.state !== StageTravelState.PAUSING_ON_BLACK) {
13
+ return;
14
+ }
15
+
16
+ if (!blackSprite.IsLoaded()) {
17
+ blackSprite.Load("gfx/ui/boss/versusscreen.anm2", true);
18
+ blackSprite.SetFrame("Scene", 0);
19
+ }
20
+
21
+ blackSprite.RenderLayer(0, VectorZero);
22
+ }
@@ -2,12 +2,22 @@ import { GridEntityType } from "isaac-typescript-definitions";
2
2
 
3
3
  export const CUSTOM_TRAPDOOR_FEATURE_NAME = "customTrapdoor";
4
4
 
5
+ export const GridEntityTypeCustom = {
6
+ TRAPDOOR_CUSTOM: 1000 as GridEntityType,
7
+ } as const;
8
+
5
9
  /** This also applies to crawl spaces. The value was determined through trial and error. */
6
10
  export const TRAPDOOR_OPEN_DISTANCE = 60;
7
11
 
8
12
  export const TRAPDOOR_OPEN_DISTANCE_AFTER_BOSS = TRAPDOOR_OPEN_DISTANCE * 2.5;
9
13
  export const TRAPDOOR_BOSS_REACTION_FRAMES = 30;
10
14
 
11
- export const GridEntityTypeCustom = {
12
- TRAPDOOR_CUSTOM: 1000 as GridEntityType,
13
- } as const;
15
+ export const TRAPDOOR_TOUCH_DISTANCE = 16.5;
16
+
17
+ export const ANIMATIONS_THAT_PREVENT_STAGE_TRAVEL: ReadonlySet<string> =
18
+ new Set(["Happy", "Sad", "Jump"]);
19
+
20
+ export const PIXELATION_TO_BLACK_FRAMES = 52;
21
+
22
+ export const OTHER_PLAYER_TRAPDOOR_JUMP_DELAY_GAME_FRAMES = 6;
23
+ export const OTHER_PLAYER_TRAPDOOR_JUMP_DURATION_GAME_FRAMES = 5;
@@ -1,23 +1,22 @@
1
1
  import {
2
2
  GridCollisionClass,
3
3
  LevelStage,
4
- RoomType,
5
4
  StageType,
6
5
  } from "isaac-typescript-definitions";
7
6
  import { game } from "../../cachedClasses";
7
+ import { TrapdoorAnimation } from "../../enums/private/TrapdoorAnimation";
8
8
  import { errorIfFeaturesNotInitialized } from "../../featuresInitialized";
9
9
  import { getNextStage, getNextStageType } from "../../functions/nextStage";
10
- import { anyPlayerCloserThan } from "../../functions/positionVelocity";
10
+ import { getRoomListIndex } from "../../functions/roomData";
11
+ import { isVector } from "../../functions/vector";
12
+ import { CustomTrapdoorDescription } from "../../interfaces/private/CustomTrapdoorDescription";
11
13
  import { spawnCustomGridEntity } from "../customGridEntity";
12
- import { getRoomClearGameFrame } from "../roomClearFrame";
13
14
  import {
14
15
  CUSTOM_TRAPDOOR_FEATURE_NAME,
15
16
  GridEntityTypeCustom,
16
- TRAPDOOR_BOSS_REACTION_FRAMES,
17
- TRAPDOOR_OPEN_DISTANCE,
18
- TRAPDOOR_OPEN_DISTANCE_AFTER_BOSS,
19
17
  } from "./customTrapdoorConstants";
20
- import { getCustomTrapdoorDescription } from "./v";
18
+ import { shouldTrapdoorSpawnOpen } from "./openClose";
19
+ import v from "./v";
21
20
 
22
21
  /**
23
22
  * Helper function to spawn a trapdoor grid entity that will have one or more of the following
@@ -26,7 +25,7 @@ import { getCustomTrapdoorDescription } from "./v";
26
25
  * - custom destination (or custom logic for after the player enters)
27
26
  * - custom graphics
28
27
  * - custom logic for opening/closing
29
- * - TODO: animation
28
+ * - TODO: player jumping animation?
30
29
  *
31
30
  * You can use this function to take the player to your custom stage.
32
31
  *
@@ -34,135 +33,67 @@ import { getCustomTrapdoorDescription } from "./v";
34
33
  * respawned every time the player re-enters the room.
35
34
  *
36
35
  * @param gridIndexOrPosition The location in the room to spawn the trapdoor.
37
- * @param _destination Used to specify where the player will go after jumping into the trapdoor. Can
38
- * either be a tuple containing the stage and stage type, a string containing
39
- * the name of a custom stage, or undefined. If undefined, nothing will happen
40
- * after the player jumps in the trapdoor. (Use undefined to perform some custom
41
- * behavior and/or handle the traveling part yourself.) You can also specify a
42
- * function that returns one of these things. By default, the destination will
43
- * be set to the next floor like that of a vanilla trapdoor.
36
+ * @param destination Optional. Used to specify where the player will go after jumping into the
37
+ * trapdoor. Can either be a tuple containing the stage and stage type, or a
38
+ * string containing the name of a custom stage. If not specified at all, then
39
+ * the "normal" destination corresponding to the current stage and room will be
40
+ * used (e.g. the next floor).
44
41
  * @param anm2Path Optional. The path to the anm2 file to use. By default, the vanilla trapdoor anm2
45
- * of "gfx/grid/door_11_trapdoor.anm2" will be used.
46
- * @param _shouldOpenFunc Optional. If the trapdoor is currently closed, this function will run on
47
- * every frame to determine if it should open. By default, a function that
48
- * emulates a vanilla trapdoor will be used.
49
- * @param _shouldCloseFunc Optional. If the trapdoor is currently open, this function will run on
50
- * every frame to determine if it should close. By default, a function that
51
- * emulates a vanilla trapdoor will be used.
52
- * @param _spawnOpen Optional. Whether or not to spawn the trapdoor in an open state. Can either be
53
- * a boolean or a function returning a boolean. By default, a function that
54
- * emulates a vanilla trapdoor will be used.
42
+ * of "gfx/grid/door_11_trapdoor.anm2" will be used. The specified anm2 file must
43
+ * have animations called "Opened", "Closed", and "Open Animation".
44
+ * @param spawnOpen Optional. Whether or not to spawn the trapdoor in an open state. By default,
45
+ * behavior will be used that emulates a vanilla trapdoor.
55
46
  */
56
47
  export function spawnCustomTrapdoor(
57
48
  gridIndexOrPosition: int | Vector,
58
- _destination:
59
- | [stage: LevelStage, stageType: StageType]
60
- | string
61
- | ((
62
- gridEntity: GridEntity,
63
- ) =>
64
- | [stage: LevelStage, stageType: StageType]
65
- | string
66
- | undefined) = defaultDestinationFunc,
49
+ destination?: [stage: LevelStage, stageType: StageType] | string,
67
50
  anm2Path = "gfx/grid/door_11_trapdoor.anm2",
68
- _shouldOpenFunc: (gridEntity: GridEntity) => boolean = defaultShouldOpenFunc,
69
- _shouldCloseFunc: (
70
- gridEntity: GridEntity,
71
- ) => boolean = defaultShouldCloseFunc,
72
- _spawnOpen:
73
- | boolean
74
- | ((gridEntity: GridEntity) => boolean) = defaultShouldSpawnOpenFunc,
51
+ spawnOpen?: boolean,
75
52
  ): GridEntity {
76
53
  errorIfFeaturesNotInitialized(CUSTOM_TRAPDOOR_FEATURE_NAME);
77
54
 
78
- // TODO
79
- return spawnCustomGridEntity(
55
+ const room = game.GetRoom();
56
+ const roomFrameCount = room.GetFrameCount();
57
+ const roomListIndex = getRoomListIndex();
58
+ const gridIndex = isVector(gridIndexOrPosition)
59
+ ? room.GetGridIndex(gridIndexOrPosition)
60
+ : gridIndexOrPosition;
61
+
62
+ const gridEntity = spawnCustomGridEntity(
80
63
  GridEntityTypeCustom.TRAPDOOR_CUSTOM,
81
64
  gridIndexOrPosition,
82
- anm2Path,
83
- "Closed",
84
65
  GridCollisionClass.NONE,
66
+ anm2Path,
67
+ TrapdoorAnimation.OPENED,
85
68
  );
86
- }
87
-
88
- function defaultDestinationFunc(): [stage: LevelStage, stageType: StageType] {
89
- const nextStage = getNextStage();
90
- const nextStageType = getNextStageType();
91
-
92
- return [nextStage, nextStageType];
93
- }
94
-
95
- function defaultShouldOpenFunc(gridEntity: GridEntity): boolean {
96
- const trapdoorDescription = getCustomTrapdoorDescription(gridEntity);
97
- if (trapdoorDescription === undefined) {
98
- return false;
99
- }
100
-
101
- const room = game.GetRoom();
102
- const roomClear = room.IsClear();
103
-
104
- return (
105
- !anyPlayerCloserThan(gridEntity.Position, TRAPDOOR_OPEN_DISTANCE) &&
106
- !isPlayerCloseAfterBoss(gridEntity.Position) &&
107
- !shouldBeClosedFromStartingInRoomWithEnemies(
108
- trapdoorDescription.firstSpawn,
109
- roomClear,
110
- )
111
- );
112
- }
113
69
 
114
- function isPlayerCloseAfterBoss(position: Vector) {
115
- const gameFrameCount = game.GetFrameCount();
116
- const room = game.GetRoom();
117
- const roomType = room.GetType();
118
- const roomClearGameFrame = getRoomClearGameFrame();
119
-
120
- // In order to prevent a player from accidentally entering a freshly-spawned trapdoor after
121
- // killing the boss of the floor, we use a wider open distance for a short amount of frames.
122
- if (
123
- roomType !== RoomType.BOSS ||
124
- roomClearGameFrame === undefined ||
125
- gameFrameCount >= roomClearGameFrame + TRAPDOOR_BOSS_REACTION_FRAMES
126
- ) {
127
- return false;
70
+ const firstSpawn = roomFrameCount !== 0;
71
+ const open =
72
+ spawnOpen === undefined
73
+ ? shouldTrapdoorSpawnOpen(gridEntity, firstSpawn)
74
+ : spawnOpen;
75
+ const destinationToUse =
76
+ destination === undefined ? getDefaultDestination() : destination;
77
+
78
+ const roomTrapdoorMap = v.level.trapdoors.getAndSetDefault(roomListIndex);
79
+ const customTrapdoorDescription: CustomTrapdoorDescription = {
80
+ open,
81
+ destination: destinationToUse,
82
+ firstSpawn,
83
+ };
84
+ roomTrapdoorMap.set(gridIndex, customTrapdoorDescription);
85
+
86
+ if (!open) {
87
+ const sprite = gridEntity.GetSprite();
88
+ sprite.Play(TrapdoorAnimation.CLOSED, true);
128
89
  }
129
90
 
130
- return anyPlayerCloserThan(position, TRAPDOOR_OPEN_DISTANCE_AFTER_BOSS);
91
+ return gridEntity;
131
92
  }
132
93
 
133
- function shouldBeClosedFromStartingInRoomWithEnemies(
134
- firstSpawn: boolean,
135
- roomClear: boolean,
136
- ) {
137
- return firstSpawn && !roomClear;
138
- }
139
-
140
- /** By default, trapdoors will never close if they are already open. */
141
- function defaultShouldCloseFunc(): boolean {
142
- return false;
143
- }
144
-
145
- function defaultShouldSpawnOpenFunc(gridEntity: GridEntity): boolean {
146
- const room = game.GetRoom();
147
- const roomFrameCount = room.GetFrameCount();
148
- const roomClear = room.IsClear();
149
-
150
- // Trapdoors created after a room has already initialized should spawn closed by default:
151
- // - Trapdoors created after bosses should spawn closed so that players do not accidentally jump
152
- // into them.
153
- // - Trapdoors created by We Need to Go Deeper should spawn closed because the player will be
154
- // standing on top of them.
155
- if (roomFrameCount > 0) {
156
- return false;
157
- }
158
-
159
- // If we just entered a new room with enemies in it, spawn the trapdoor closed so that the player
160
- // has to defeat the enemies first before using the trapdoor.
161
- if (!roomClear) {
162
- return false;
163
- }
94
+ function getDefaultDestination(): [stage: LevelStage, stageType: StageType] {
95
+ const nextStage = getNextStage();
96
+ const nextStageType = getNextStageType();
164
97
 
165
- // If we just entered a new room that is already cleared, spawn the trapdoor closed if we are
166
- // standing close to it, and open otherwise.
167
- return defaultShouldOpenFunc(gridEntity);
98
+ return [nextStage, nextStageType];
168
99
  }