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,217 @@
1
+ import {
2
+ Direction,
3
+ ModCallback,
4
+ RoomTransitionAnim,
5
+ } from "isaac-typescript-definitions";
6
+ import { game } from "../../cachedClasses";
7
+ import { ModUpgraded } from "../../classes/ModUpgraded";
8
+ import { ModCallbackCustom } from "../../enums/ModCallbackCustom";
9
+ import { StageTravelState } from "../../enums/private/StageTravelState";
10
+ import { movePlayersToCenter } from "../../functions/playerCenter";
11
+ import { getAllPlayers } from "../../functions/playerIndex";
12
+ import { getRoomGridIndex, getRoomListIndex } from "../../functions/roomData";
13
+ import { setStage } from "../../functions/stage";
14
+ import { isString } from "../../functions/types";
15
+ import { setCustomStage } from "../customStage/exports";
16
+ import { topStreakTextStart } from "../customStage/streakText";
17
+ import { enableAllInputs } from "../disableInputs";
18
+ import { runNextGameFrame } from "../runInNFrames";
19
+ import { runNextRoom } from "../runNextRoom";
20
+ import { saveDataManager } from "../saveDataManager/exports";
21
+ import { drawBlackSprite } from "./blackSprite";
22
+ import {
23
+ CUSTOM_TRAPDOOR_FEATURE_NAME,
24
+ GridEntityTypeCustom,
25
+ PIXELATION_TO_BLACK_FRAMES,
26
+ } from "./customTrapdoorConstants";
27
+ import { checkCustomTrapdoorOpenClose } from "./openClose";
28
+ import { checkCustomTrapdoorPlayerTouched } from "./touched";
29
+ import v from "./v";
30
+
31
+ export function customTrapdoorInit(mod: ModUpgraded): void {
32
+ saveDataManager(CUSTOM_TRAPDOOR_FEATURE_NAME, v);
33
+
34
+ mod.AddCallback(ModCallback.POST_RENDER, postRender); // 2
35
+ mod.AddCallback(ModCallback.POST_PEFFECT_UPDATE, postPEffectUpdate); // 4
36
+ mod.AddCallbackCustom(
37
+ ModCallbackCustom.POST_GRID_ENTITY_CUSTOM_UPDATE,
38
+ postGridEntityCustomUpdateTrapdoor,
39
+ GridEntityTypeCustom.TRAPDOOR_CUSTOM,
40
+ );
41
+ }
42
+
43
+ // ModCallback.POST_RENDER (2)
44
+ function postRender() {
45
+ checkAllPlayersJumpComplete();
46
+ checkPixelationToBlackComplete();
47
+ checkPausingOnBlackComplete();
48
+ checkAllPlayersLayingDownComplete();
49
+ drawBlackSprite();
50
+ }
51
+
52
+ function checkAllPlayersJumpComplete() {
53
+ if (v.run.state !== StageTravelState.PLAYERS_JUMPING_DOWN) {
54
+ return;
55
+ }
56
+
57
+ if (anyPlayerPlayingExtraAnimation()) {
58
+ return;
59
+ }
60
+
61
+ const renderFrameCount = Isaac.GetFrameCount();
62
+ const roomGridIndex = getRoomGridIndex();
63
+
64
+ v.run.state = StageTravelState.PIXELATION_TO_BLACK;
65
+ v.run.stateRenderFrame = renderFrameCount;
66
+
67
+ // In order to display the pixelation effect that should happen when we go to a new floor, we need
68
+ // to start a room transition. We arbitrarily pick the current room for this purpose.
69
+ game.StartRoomTransition(
70
+ roomGridIndex,
71
+ Direction.NO_DIRECTION,
72
+ RoomTransitionAnim.PIXELATION,
73
+ );
74
+ }
75
+
76
+ function checkPixelationToBlackComplete() {
77
+ if (
78
+ v.run.state !== StageTravelState.PIXELATION_TO_BLACK ||
79
+ v.run.stateRenderFrame === null
80
+ ) {
81
+ return;
82
+ }
83
+
84
+ const hud = game.GetHUD();
85
+ const renderFrameCount = Isaac.GetFrameCount();
86
+ const roomGridIndex = getRoomGridIndex();
87
+
88
+ const renderFrameScreenBlack =
89
+ v.run.stateRenderFrame + PIXELATION_TO_BLACK_FRAMES;
90
+ if (renderFrameCount < renderFrameScreenBlack) {
91
+ return;
92
+ }
93
+
94
+ v.run.state = StageTravelState.PAUSING_ON_BLACK;
95
+ v.run.stateRenderFrame = renderFrameCount;
96
+
97
+ hud.SetVisible(false);
98
+ goToCustomDestination();
99
+
100
+ // Start another pixelation effect. This time, we will keep the screen black with the sprite, and
101
+ // then remove the black sprite once the pixelation effect is halfway complete.
102
+ game.StartRoomTransition(
103
+ roomGridIndex,
104
+ Direction.NO_DIRECTION,
105
+ RoomTransitionAnim.PIXELATION,
106
+ );
107
+ }
108
+
109
+ function checkPausingOnBlackComplete() {
110
+ if (
111
+ v.run.state !== StageTravelState.PAUSING_ON_BLACK ||
112
+ v.run.stateRenderFrame === null
113
+ ) {
114
+ return;
115
+ }
116
+
117
+ const hud = game.GetHUD();
118
+ const renderFrameCount = Isaac.GetFrameCount();
119
+
120
+ const renderFrameScreenBlack =
121
+ v.run.stateRenderFrame + PIXELATION_TO_BLACK_FRAMES;
122
+ if (renderFrameCount < renderFrameScreenBlack) {
123
+ return;
124
+ }
125
+
126
+ v.run.state = StageTravelState.PIXELATION_TO_ROOM;
127
+
128
+ hud.SetVisible(true);
129
+
130
+ runNextRoom(() => {
131
+ // After the room transition, the players will be placed next to a door, but they should be in
132
+ // the center of the room to emulate what happens on a vanilla stage.
133
+ movePlayersToCenter();
134
+
135
+ v.run.state = StageTravelState.PLAYERS_LAYING_DOWN;
136
+
137
+ for (const player of getAllPlayers()) {
138
+ player.AnimateAppear();
139
+ }
140
+ });
141
+
142
+ // In vanilla, the streak text appears about when the pixelation has faded and while Isaac is
143
+ // still laying on the ground. Unfortunately, we cannot exactly replicate the vanilla timing,
144
+ // because the level text will bug out and smear the background if we play it from a `POST_RENDER`
145
+ // callback. Thus, we run it on the next game frame as a workaround.
146
+ runNextGameFrame(() => {
147
+ topStreakTextStart();
148
+ });
149
+ }
150
+
151
+ function checkAllPlayersLayingDownComplete() {
152
+ if (v.run.state !== StageTravelState.PLAYERS_LAYING_DOWN) {
153
+ return;
154
+ }
155
+
156
+ if (anyPlayerPlayingExtraAnimation()) {
157
+ return;
158
+ }
159
+
160
+ v.run.state = StageTravelState.NONE;
161
+
162
+ enableAllInputs(CUSTOM_TRAPDOOR_FEATURE_NAME);
163
+ }
164
+
165
+ function goToCustomDestination() {
166
+ if (v.run.destination === null) {
167
+ return;
168
+ }
169
+
170
+ if (isString(v.run.destination)) {
171
+ setCustomStage("Slaughterhouse");
172
+ } else {
173
+ const [stage, stageType] = v.run.destination;
174
+ setStage(stage, stageType);
175
+ }
176
+ }
177
+
178
+ function anyPlayerPlayingExtraAnimation() {
179
+ const players = getAllPlayers();
180
+ return players.some((player) => !player.IsExtraAnimationFinished());
181
+ }
182
+
183
+ // ModCallback.POST_PEFFECT_UPDATE (4)
184
+ function postPEffectUpdate(player: EntityPlayer) {
185
+ checkJumpComplete(player);
186
+ }
187
+
188
+ function checkJumpComplete(player: EntityPlayer) {
189
+ if (v.run.state !== StageTravelState.PLAYERS_JUMPING_DOWN) {
190
+ return;
191
+ }
192
+
193
+ // In this state, the players are jumping down the hole (i.e. playing the "Trapdoor" animation).
194
+ // When it completes, they will return to normal (i.e. just standing on top of the trapdoor).
195
+ // Thus, make them invisible at the end of the animation. (They will automatically be set to
196
+ // visible again once we travel to the next floor.)
197
+ const sprite = player.GetSprite();
198
+ if (sprite.IsFinished("Trapdoor")) {
199
+ player.Visible = false;
200
+ }
201
+ }
202
+
203
+ // ModCallbackCustom.POST_GRID_ENTITY_CUSTOM_UPDATE
204
+ // GridEntityTypeCustom.TRAPDOOR_CUSTOM
205
+ function postGridEntityCustomUpdateTrapdoor(gridEntity: GridEntity) {
206
+ const roomListIndex = getRoomListIndex();
207
+ const gridIndex = gridEntity.GetGridIndex();
208
+
209
+ const roomTrapdoorMap = v.level.trapdoors.getAndSetDefault(roomListIndex);
210
+ const trapdoorDescription = roomTrapdoorMap.get(gridIndex);
211
+ if (trapdoorDescription === undefined) {
212
+ return;
213
+ }
214
+
215
+ checkCustomTrapdoorOpenClose(gridEntity, trapdoorDescription);
216
+ checkCustomTrapdoorPlayerTouched(gridEntity, trapdoorDescription);
217
+ }
@@ -0,0 +1,103 @@
1
+ import { RoomType } from "isaac-typescript-definitions";
2
+ import { game } from "../../cachedClasses";
3
+ import { TrapdoorAnimation } from "../../enums/private/TrapdoorAnimation";
4
+ import { anyPlayerCloserThan } from "../../functions/positionVelocity";
5
+ import { CustomTrapdoorDescription } from "../../interfaces/private/CustomTrapdoorDescription";
6
+ import { getRoomClearGameFrame } from "../roomClearFrame";
7
+ import {
8
+ TRAPDOOR_BOSS_REACTION_FRAMES,
9
+ TRAPDOOR_OPEN_DISTANCE,
10
+ TRAPDOOR_OPEN_DISTANCE_AFTER_BOSS,
11
+ } from "./customTrapdoorConstants";
12
+
13
+ export function checkCustomTrapdoorOpenClose(
14
+ gridEntity: GridEntity,
15
+ trapdoorDescription: CustomTrapdoorDescription,
16
+ ): void {
17
+ /** By default, trapdoors will never close if they are already open. */
18
+ if (trapdoorDescription.open) {
19
+ return;
20
+ }
21
+
22
+ if (shouldTrapdoorOpen(gridEntity, trapdoorDescription.firstSpawn)) {
23
+ open(gridEntity, trapdoorDescription);
24
+ }
25
+ }
26
+
27
+ function shouldTrapdoorOpen(
28
+ gridEntity: GridEntity,
29
+ firstSpawn: boolean,
30
+ ): boolean {
31
+ const room = game.GetRoom();
32
+ const roomClear = room.IsClear();
33
+
34
+ return (
35
+ !anyPlayerCloserThan(gridEntity.Position, TRAPDOOR_OPEN_DISTANCE) &&
36
+ !isPlayerCloseAfterBoss(gridEntity.Position) &&
37
+ !shouldBeClosedFromStartingInRoomWithEnemies(firstSpawn, roomClear)
38
+ );
39
+ }
40
+
41
+ function isPlayerCloseAfterBoss(position: Vector) {
42
+ const gameFrameCount = game.GetFrameCount();
43
+ const room = game.GetRoom();
44
+ const roomType = room.GetType();
45
+ const roomClearGameFrame = getRoomClearGameFrame();
46
+
47
+ // In order to prevent a player from accidentally entering a freshly-spawned trapdoor after
48
+ // killing the boss of the floor, we use a wider open distance for a short amount of frames.
49
+ if (
50
+ roomType !== RoomType.BOSS ||
51
+ roomClearGameFrame === undefined ||
52
+ gameFrameCount >= roomClearGameFrame + TRAPDOOR_BOSS_REACTION_FRAMES
53
+ ) {
54
+ return false;
55
+ }
56
+
57
+ return anyPlayerCloserThan(position, TRAPDOOR_OPEN_DISTANCE_AFTER_BOSS);
58
+ }
59
+
60
+ function shouldBeClosedFromStartingInRoomWithEnemies(
61
+ firstSpawn: boolean,
62
+ roomClear: boolean,
63
+ ) {
64
+ return firstSpawn && !roomClear;
65
+ }
66
+
67
+ function open(
68
+ gridEntity: GridEntity,
69
+ trapdoorDescription: CustomTrapdoorDescription,
70
+ ) {
71
+ trapdoorDescription.open = true;
72
+
73
+ const sprite = gridEntity.GetSprite();
74
+ sprite.Play(TrapdoorAnimation.OPEN_ANIMATION, true);
75
+ }
76
+
77
+ export function shouldTrapdoorSpawnOpen(
78
+ gridEntity: GridEntity,
79
+ firstSpawn: boolean,
80
+ ): boolean {
81
+ const room = game.GetRoom();
82
+ const roomFrameCount = room.GetFrameCount();
83
+ const roomClear = room.IsClear();
84
+
85
+ // Trapdoors created after a room has already initialized should spawn closed by default:
86
+ // - Trapdoors created after bosses should spawn closed so that players do not accidentally jump
87
+ // into them.
88
+ // - Trapdoors created by We Need to Go Deeper should spawn closed because the player will be
89
+ // standing on top of them.
90
+ if (roomFrameCount > 0) {
91
+ return false;
92
+ }
93
+
94
+ // If we just entered a new room with enemies in it, spawn the trapdoor closed so that the player
95
+ // has to defeat the enemies first before using the trapdoor.
96
+ if (!roomClear) {
97
+ return false;
98
+ }
99
+
100
+ // If we just entered a new room that is already cleared, spawn the trapdoor closed if we are
101
+ // standing close to it, and open otherwise.
102
+ return shouldTrapdoorOpen(gridEntity, firstSpawn);
103
+ }
@@ -0,0 +1,195 @@
1
+ import {
2
+ ButtonAction,
3
+ EntityCollisionClass,
4
+ EntityGridCollisionClass,
5
+ EntityPartition,
6
+ PlayerType,
7
+ } from "isaac-typescript-definitions";
8
+ import { VectorZero } from "../../constants";
9
+ import { StageTravelState } from "../../enums/private/StageTravelState";
10
+ import { easeOutSine } from "../../functions/easing";
11
+ import {
12
+ getAllPlayers,
13
+ getOtherPlayers,
14
+ isChildPlayer,
15
+ } from "../../functions/playerIndex";
16
+ import { isCharacter } from "../../functions/players";
17
+ import { CustomTrapdoorDescription } from "../../interfaces/private/CustomTrapdoorDescription";
18
+ import { disableAllInputsExceptFor } from "../disableInputs";
19
+ import { isPlayerUsingPony } from "../ponyDetection";
20
+ import { runInNGameFrames, runNextRenderFrame } from "../runInNFrames";
21
+ import {
22
+ ANIMATIONS_THAT_PREVENT_STAGE_TRAVEL,
23
+ CUSTOM_TRAPDOOR_FEATURE_NAME,
24
+ OTHER_PLAYER_TRAPDOOR_JUMP_DELAY_GAME_FRAMES,
25
+ OTHER_PLAYER_TRAPDOOR_JUMP_DURATION_GAME_FRAMES,
26
+ TRAPDOOR_TOUCH_DISTANCE,
27
+ } from "./customTrapdoorConstants";
28
+ import v from "./v";
29
+
30
+ export function checkCustomTrapdoorPlayerTouched(
31
+ gridEntity: GridEntity,
32
+ trapdoorDescription: CustomTrapdoorDescription,
33
+ ): void {
34
+ if (v.run.state !== StageTravelState.NONE) {
35
+ return;
36
+ }
37
+
38
+ if (!trapdoorDescription.open) {
39
+ return;
40
+ }
41
+
42
+ const playersTouching = Isaac.FindInRadius(
43
+ gridEntity.Position,
44
+ TRAPDOOR_TOUCH_DISTANCE,
45
+ EntityPartition.PLAYER,
46
+ );
47
+ for (const playerEntity of playersTouching) {
48
+ const player = playerEntity.ToPlayer();
49
+ if (player === undefined) {
50
+ continue;
51
+ }
52
+
53
+ if (
54
+ // We don't want a Pony dash to transition to a new floor or a crawl space.
55
+ !isPlayerUsingPony(player) &&
56
+ !isChildPlayer(player) &&
57
+ canPlayerInteractWithTrapdoor(player)
58
+ ) {
59
+ playerTouchedCustomTrapdoor(gridEntity, trapdoorDescription, player);
60
+ return; // Prevent two players from touching the same entity.
61
+ }
62
+ }
63
+ }
64
+
65
+ function canPlayerInteractWithTrapdoor(player: EntityPlayer) {
66
+ // Players cannot interact with stage travel entities when items are queued or while playing
67
+ // certain animations.
68
+ const sprite = player.GetSprite();
69
+ const animation = sprite.GetAnimation();
70
+ return (
71
+ !player.IsHoldingItem() &&
72
+ !ANIMATIONS_THAT_PREVENT_STAGE_TRAVEL.has(animation)
73
+ );
74
+ }
75
+
76
+ function playerTouchedCustomTrapdoor(
77
+ gridEntity: GridEntity,
78
+ trapdoorDescription: CustomTrapdoorDescription,
79
+ player: EntityPlayer,
80
+ ) {
81
+ v.run.state = StageTravelState.PLAYERS_JUMPING_DOWN;
82
+ v.run.destination = trapdoorDescription.destination;
83
+
84
+ const whitelist = new Set([ButtonAction.CONSOLE]);
85
+ disableAllInputsExceptFor(CUSTOM_TRAPDOOR_FEATURE_NAME, whitelist);
86
+ setPlayerAttributes(player, gridEntity.Position);
87
+ dropTaintedForgotten(player);
88
+
89
+ player.PlayExtraAnimation("Trapdoor");
90
+
91
+ const otherPlayers = getOtherPlayers(player);
92
+ otherPlayers.forEach((otherPlayer, i) => {
93
+ const gameFramesToWaitBeforeJumping =
94
+ OTHER_PLAYER_TRAPDOOR_JUMP_DELAY_GAME_FRAMES * (i + 1);
95
+ const otherPlayerPtr = EntityPtr(otherPlayer);
96
+ runInNGameFrames(() => {
97
+ startDelayedJump(otherPlayerPtr, gridEntity.Position);
98
+ }, gameFramesToWaitBeforeJumping);
99
+ });
100
+ }
101
+
102
+ function setPlayerAttributes(trapdoorPlayer: EntityPlayer, position: Vector) {
103
+ // Snap the player to the exact position of the trapdoor so that they cleanly jump down the hole.
104
+ trapdoorPlayer.Position = position;
105
+
106
+ for (const player of getAllPlayers()) {
107
+ // Disable the controls to prevent the player from moving, shooting, and so on. (We also disable
108
+ // the inputs in the `INPUT_ACTION` callback, but that does not prevent mouse inputs.)
109
+ player.ControlsEnabled = false;
110
+
111
+ // Freeze all players.
112
+ player.Velocity = VectorZero;
113
+
114
+ // We don't want enemy attacks to move the players.
115
+ player.EntityCollisionClass = EntityCollisionClass.NONE;
116
+ player.GridCollisionClass = EntityGridCollisionClass.NONE;
117
+
118
+ player.SubType = -1;
119
+ }
120
+ }
121
+
122
+ function dropTaintedForgotten(player: EntityPlayer) {
123
+ if (isCharacter(player, PlayerType.THE_FORGOTTEN_B)) {
124
+ const taintedSoul = player.GetOtherTwin();
125
+ if (taintedSoul !== undefined) {
126
+ taintedSoul.ThrowHeldEntity(VectorZero);
127
+ }
128
+ }
129
+ }
130
+
131
+ function startDelayedJump(entityPtr: EntityPtr, trapdoorPosition: Vector) {
132
+ const entity = entityPtr.Ref;
133
+ if (entity === undefined) {
134
+ return;
135
+ }
136
+
137
+ const player = entity.ToPlayer();
138
+ if (player === undefined) {
139
+ return;
140
+ }
141
+
142
+ player.PlayExtraAnimation("Trapdoor");
143
+
144
+ adjustPlayerPositionToTrapdoor(entityPtr, player.Position, trapdoorPosition);
145
+ }
146
+
147
+ function adjustPlayerPositionToTrapdoor(
148
+ entityPtr: EntityPtr,
149
+ startPos: Vector,
150
+ endPos: Vector,
151
+ ) {
152
+ if (v.run.state !== StageTravelState.PLAYERS_JUMPING_DOWN) {
153
+ return;
154
+ }
155
+
156
+ const entity = entityPtr.Ref;
157
+ if (entity === undefined) {
158
+ return;
159
+ }
160
+
161
+ const player = entity.ToPlayer();
162
+ if (player === undefined) {
163
+ return;
164
+ }
165
+
166
+ runNextRenderFrame(() => {
167
+ adjustPlayerPositionToTrapdoor(entityPtr, startPos, endPos);
168
+ });
169
+
170
+ const sprite = player.GetSprite();
171
+ if (sprite.IsFinished("Trapdoor")) {
172
+ player.Position = endPos;
173
+ player.Velocity = VectorZero;
174
+ return;
175
+ }
176
+
177
+ const frame = sprite.GetFrame();
178
+ if (frame >= OTHER_PLAYER_TRAPDOOR_JUMP_DURATION_GAME_FRAMES) {
179
+ // We have already arrived at the trapdoor.
180
+ player.Position = endPos;
181
+ player.Velocity = VectorZero;
182
+ return;
183
+ }
184
+
185
+ // Make the player jump towards the trapdoor. We use an easing function so that the distance
186
+ // traveled is not linear, emulating what the game does.
187
+ const totalDifference = endPos.sub(startPos);
188
+ const progress = frame / OTHER_PLAYER_TRAPDOOR_JUMP_DURATION_GAME_FRAMES;
189
+ const easeProgress = easeOutSine(progress);
190
+ const differenceForThisFrame = totalDifference.mul(easeProgress);
191
+ const targetPosition = startPos.add(differenceForThisFrame);
192
+
193
+ player.Position = targetPosition;
194
+ player.Velocity = VectorZero;
195
+ }
@@ -1,20 +1,26 @@
1
+ import { LevelStage, StageType } from "isaac-typescript-definitions";
2
+ import { DefaultMap } from "../../classes/DefaultMap";
1
3
  import { StageTravelState } from "../../enums/private/StageTravelState";
2
4
  import { CustomTrapdoorDescription } from "../../interfaces/private/CustomTrapdoorDescription";
3
5
 
4
6
  const v = {
5
7
  run: {
6
8
  state: StageTravelState.NONE,
9
+
10
+ /** The render frame that this state was reached. */
11
+ stateRenderFrame: null as int | null,
12
+
13
+ destination: null as
14
+ | [stage: LevelStage, stageType: StageType]
15
+ | string
16
+ | null,
7
17
  },
8
18
 
9
- room: {
10
- /** Indexed by grid index. */
11
- trapdoors: new Map<int, CustomTrapdoorDescription>(),
19
+ level: {
20
+ /** Indexed by room list index and grid index. */
21
+ trapdoors: new DefaultMap<int, Map<int, CustomTrapdoorDescription>>(
22
+ () => new Map(),
23
+ ),
12
24
  },
13
25
  };
14
-
15
- export function getCustomTrapdoorDescription(
16
- gridEntity: GridEntity,
17
- ): CustomTrapdoorDescription | undefined {
18
- const gridIndex = gridEntity.GetGridIndex();
19
- return v.room.trapdoors.get(gridIndex);
20
- }
26
+ export default v;
@@ -161,7 +161,7 @@ function preUseItemWeNeedToGoDeeper(
161
161
  fillRoomWithDecorations();
162
162
  });
163
163
 
164
- // Cancel the effect.
164
+ // Cancel the original effect.
165
165
  return true;
166
166
  }
167
167
 
@@ -225,7 +225,7 @@ function respawnPersistentEntities() {
225
225
  * Specifically, this will clear the current room of all entities and grid entities, and then spawn
226
226
  * all of the entries and grid entities in the provided JSON room.
227
227
  *
228
- * This function is meant to be used in the PostNewRoom callback.
228
+ * This function is meant to be used in the `POST_NEW_ROOM` callback.
229
229
  *
230
230
  * For example:
231
231
  *
@@ -277,7 +277,7 @@ export function deployJSONRoom(
277
277
  * Specifically, this will clear the current room of all entities and grid entities, and then spawn
278
278
  * all of the entries and grid entities in one of the provided JSON rooms.
279
279
  *
280
- * This function is meant to be used in the PostNewRoom callback.
280
+ * This function is meant to be used in the `POST_NEW_ROOM` callback.
281
281
  *
282
282
  * Note that this function does not simply choose a random element in the provided array; it will
283
283
  * properly account for each room weight using the algorithm from:
@@ -355,8 +355,8 @@ export function emptyRoom(fillWithDecorations: boolean): void {
355
355
  }
356
356
 
357
357
  /**
358
- * We remove entities in the PostNewRoom callback instead of in the PreRoomEntitySpawn callback so
359
- * that they will not re-appear when we re-enter the room.
358
+ * We remove entities in the `POST_NEW_ROOM` callback instead of in the PreRoomEntitySpawn callback
359
+ * so that they will not re-appear when we re-enter the room.
360
360
  */
361
361
  function removeSpecificNPCs() {
362
362
  const room = game.GetRoom();