isaacscript-common 6.11.1 → 6.11.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/callbacks/postFlip.lua +2 -2
- package/dist/callbacks/subscriptions/postFirstFlip.d.ts +1 -1
- package/dist/callbacks/subscriptions/postFirstFlip.d.ts.map +1 -1
- package/dist/callbacks/subscriptions/postFirstFlip.lua +2 -2
- package/dist/callbacks/subscriptions/postFlip.d.ts +1 -1
- package/dist/callbacks/subscriptions/postFlip.d.ts.map +1 -1
- package/dist/callbacks/subscriptions/postFlip.lua +2 -2
- package/dist/enums/ModCallbackCustom.d.ts +2 -2
- package/dist/enums/private/StageTravelState.d.ts +6 -1
- package/dist/enums/private/StageTravelState.d.ts.map +1 -1
- package/dist/enums/private/StageTravelState.lua +10 -0
- package/dist/enums/private/TrapdoorAnimation.d.ts +6 -0
- package/dist/enums/private/TrapdoorAnimation.d.ts.map +1 -0
- package/dist/enums/private/TrapdoorAnimation.lua +6 -0
- package/dist/features/customGridEntity.d.ts +8 -5
- package/dist/features/customGridEntity.d.ts.map +1 -1
- package/dist/features/customGridEntity.lua +58 -15
- package/dist/features/customStage/exports.d.ts.map +1 -1
- package/dist/features/customStage/exports.lua +0 -13
- package/dist/features/customStage/init.d.ts.map +1 -1
- package/dist/features/customStage/init.lua +16 -1
- package/dist/features/customStage/streakText.d.ts.map +1 -1
- package/dist/features/customStage/streakText.lua +0 -1
- package/dist/features/customTrapdoor/blackSprite.d.ts +2 -0
- package/dist/features/customTrapdoor/blackSprite.d.ts.map +1 -0
- package/dist/features/customTrapdoor/blackSprite.lua +19 -0
- package/dist/features/customTrapdoor/customTrapdoorConstants.d.ts +8 -3
- package/dist/features/customTrapdoor/customTrapdoorConstants.d.ts.map +1 -1
- package/dist/features/customTrapdoor/customTrapdoorConstants.lua +9 -1
- package/dist/features/customTrapdoor/exports.d.ts +11 -19
- package/dist/features/customTrapdoor/exports.d.ts.map +1 -1
- package/dist/features/customTrapdoor/exports.lua +48 -82
- package/dist/features/customTrapdoor/init.d.ts +3 -0
- package/dist/features/customTrapdoor/init.d.ts.map +1 -0
- package/dist/features/customTrapdoor/init.lua +173 -0
- package/dist/features/customTrapdoor/openClose.d.ts +5 -0
- package/dist/features/customTrapdoor/openClose.d.ts.map +1 -0
- package/dist/features/customTrapdoor/openClose.lua +60 -0
- package/dist/features/customTrapdoor/touched.d.ts +4 -0
- package/dist/features/customTrapdoor/touched.d.ts.map +1 -0
- package/dist/features/customTrapdoor/touched.lua +141 -0
- package/dist/features/customTrapdoor/v.d.ts +16 -2
- package/dist/features/customTrapdoor/v.d.ts.map +1 -1
- package/dist/features/customTrapdoor/v.lua +8 -6
- package/dist/features/extraConsoleCommands/init.d.ts.map +1 -1
- package/dist/features/extraConsoleCommands/init.lua +3 -1
- package/dist/features/taintedLazarusPlayers.d.ts.map +1 -1
- package/dist/features/taintedLazarusPlayers.lua +13 -21
- package/dist/functions/log.lua +3 -3
- package/dist/functions/playerIndex.d.ts +5 -0
- package/dist/functions/playerIndex.d.ts.map +1 -1
- package/dist/functions/playerIndex.lua +16 -6
- package/dist/functions/table.d.ts +1 -1
- package/dist/functions/table.d.ts.map +1 -1
- package/dist/initFeatures.d.ts.map +1 -1
- package/dist/initFeatures.lua +3 -0
- package/dist/interfaces/CustomGridEntityData.d.ts +5 -1
- package/dist/interfaces/CustomGridEntityData.d.ts.map +1 -1
- package/dist/interfaces/private/CustomTrapdoorDescription.d.ts +3 -0
- package/dist/interfaces/private/CustomTrapdoorDescription.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/callbacks/postFlip.ts +3 -2
- package/src/callbacks/subscriptions/postFirstFlip.ts +6 -3
- package/src/callbacks/subscriptions/postFlip.ts +6 -3
- package/src/enums/ModCallbackCustom.ts +2 -2
- package/src/enums/private/StageTravelState.ts +5 -1
- package/src/enums/private/TrapdoorAnimation.ts +5 -0
- package/src/features/customGridEntity.ts +68 -10
- package/src/features/customStage/exports.ts +3 -22
- package/src/features/customStage/init.ts +20 -0
- package/src/features/customStage/streakText.ts +0 -1
- package/src/features/customTrapdoor/blackSprite.ts +16 -0
- package/src/features/customTrapdoor/customTrapdoorConstants.ts +13 -3
- package/src/features/customTrapdoor/exports.ts +52 -121
- package/src/features/customTrapdoor/init.ts +215 -0
- package/src/features/customTrapdoor/openClose.ts +103 -0
- package/src/features/customTrapdoor/touched.ts +175 -0
- package/src/features/customTrapdoor/v.ts +16 -10
- package/src/features/deployJSONRoom.ts +1 -1
- package/src/features/extraConsoleCommands/init.ts +5 -2
- package/src/features/saveDataManager/main.ts +1 -1
- package/src/features/taintedLazarusPlayers.ts +32 -31
- package/src/functions/deepCopy.ts +2 -2
- package/src/functions/entities.ts +1 -1
- package/src/functions/playerIndex.ts +12 -0
- package/src/functions/rng.ts +1 -1
- package/src/functions/table.ts +2 -2
- package/src/initFeatures.ts +2 -0
- package/src/interfaces/CustomGridEntityData.ts +6 -1
- package/src/interfaces/private/CustomTrapdoorDescription.ts +4 -0
|
@@ -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 {
|
|
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 {
|
|
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
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
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
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
79
|
-
|
|
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
|
-
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
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
|
|
91
|
+
return gridEntity;
|
|
131
92
|
}
|
|
132
93
|
|
|
133
|
-
function
|
|
134
|
-
|
|
135
|
-
|
|
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
|
-
|
|
166
|
-
// standing close to it, and open otherwise.
|
|
167
|
-
return defaultShouldOpenFunc(gridEntity);
|
|
98
|
+
return [nextStage, nextStageType];
|
|
168
99
|
}
|
|
@@ -0,0 +1,215 @@
|
|
|
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
|
+
PIXELATION_TO_BLACK_FRAMES,
|
|
25
|
+
} from "./customTrapdoorConstants";
|
|
26
|
+
import { checkCustomTrapdoorOpenClose } from "./openClose";
|
|
27
|
+
import { checkCustomTrapdoorPlayerTouched } from "./touched";
|
|
28
|
+
import v from "./v";
|
|
29
|
+
|
|
30
|
+
export function customTrapdoorInit(mod: ModUpgraded): void {
|
|
31
|
+
saveDataManager(CUSTOM_TRAPDOOR_FEATURE_NAME, v);
|
|
32
|
+
|
|
33
|
+
mod.AddCallback(ModCallback.POST_RENDER, postRender); // 2
|
|
34
|
+
mod.AddCallback(ModCallback.POST_PEFFECT_UPDATE, postPEffectUpdate); // 4
|
|
35
|
+
mod.AddCallbackCustom(
|
|
36
|
+
ModCallbackCustom.POST_GRID_ENTITY_UPDATE,
|
|
37
|
+
postGridEntityUpdateTrapdoor,
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// ModCallback.POST_RENDER (2)
|
|
42
|
+
function postRender() {
|
|
43
|
+
checkAllPlayersJumpComplete();
|
|
44
|
+
checkPixelationToBlackComplete();
|
|
45
|
+
checkPausingOnBlackComplete();
|
|
46
|
+
checkAllPlayersLayingDownComplete();
|
|
47
|
+
drawBlackSprite();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function checkAllPlayersJumpComplete() {
|
|
51
|
+
if (v.run.state !== StageTravelState.PLAYERS_JUMPING_DOWN) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (anyPlayerPlayingExtraAnimation()) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const renderFrameCount = Isaac.GetFrameCount();
|
|
60
|
+
const roomGridIndex = getRoomGridIndex();
|
|
61
|
+
|
|
62
|
+
v.run.state = StageTravelState.PIXELATION_TO_BLACK;
|
|
63
|
+
v.run.stateRenderFrame = renderFrameCount;
|
|
64
|
+
|
|
65
|
+
// In order to display the pixelation effect that should happen when we go to a new floor, we need
|
|
66
|
+
// to start a room transition. We arbitrarily pick the current room for this purpose.
|
|
67
|
+
game.StartRoomTransition(
|
|
68
|
+
roomGridIndex,
|
|
69
|
+
Direction.NO_DIRECTION,
|
|
70
|
+
RoomTransitionAnim.PIXELATION,
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function checkPixelationToBlackComplete() {
|
|
75
|
+
if (
|
|
76
|
+
v.run.state !== StageTravelState.PIXELATION_TO_BLACK ||
|
|
77
|
+
v.run.stateRenderFrame === null
|
|
78
|
+
) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const hud = game.GetHUD();
|
|
83
|
+
const renderFrameCount = Isaac.GetFrameCount();
|
|
84
|
+
const roomGridIndex = getRoomGridIndex();
|
|
85
|
+
|
|
86
|
+
const renderFrameScreenBlack =
|
|
87
|
+
v.run.stateRenderFrame + PIXELATION_TO_BLACK_FRAMES;
|
|
88
|
+
if (renderFrameCount < renderFrameScreenBlack) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
v.run.state = StageTravelState.PAUSING_ON_BLACK;
|
|
93
|
+
v.run.stateRenderFrame = renderFrameCount;
|
|
94
|
+
|
|
95
|
+
hud.SetVisible(false);
|
|
96
|
+
goToCustomDestination();
|
|
97
|
+
|
|
98
|
+
// Start another pixelation effect. This time, we will keep the screen black with the sprite, and
|
|
99
|
+
// then remove the black sprite once the pixelation effect is halfway complete.
|
|
100
|
+
game.StartRoomTransition(
|
|
101
|
+
roomGridIndex,
|
|
102
|
+
Direction.NO_DIRECTION,
|
|
103
|
+
RoomTransitionAnim.PIXELATION,
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function checkPausingOnBlackComplete() {
|
|
108
|
+
if (
|
|
109
|
+
v.run.state !== StageTravelState.PAUSING_ON_BLACK ||
|
|
110
|
+
v.run.stateRenderFrame === null
|
|
111
|
+
) {
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const hud = game.GetHUD();
|
|
116
|
+
const renderFrameCount = Isaac.GetFrameCount();
|
|
117
|
+
|
|
118
|
+
const renderFrameScreenBlack =
|
|
119
|
+
v.run.stateRenderFrame + PIXELATION_TO_BLACK_FRAMES;
|
|
120
|
+
if (renderFrameCount < renderFrameScreenBlack) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
v.run.state = StageTravelState.PIXELATION_TO_ROOM;
|
|
125
|
+
|
|
126
|
+
hud.SetVisible(true);
|
|
127
|
+
|
|
128
|
+
runNextRoom(() => {
|
|
129
|
+
// After the room transition, the players will be placed next to a door, but they should be in
|
|
130
|
+
// the center of the room to emulate what happens on a vanilla stage.
|
|
131
|
+
movePlayersToCenter();
|
|
132
|
+
|
|
133
|
+
v.run.state = StageTravelState.PLAYERS_LAYING_DOWN;
|
|
134
|
+
|
|
135
|
+
for (const player of getAllPlayers()) {
|
|
136
|
+
player.AnimateAppear();
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
// In vanilla, the streak text appears about when the pixelation has faded and while Isaac is
|
|
141
|
+
// still laying on the ground. Unfortunately, we cannot exactly replicate the vanilla timing,
|
|
142
|
+
// because the level text will bug out and smear the background if we play it from a `POST_RENDER`
|
|
143
|
+
// callback. Thus, we run it on the next game frame as a workaround.
|
|
144
|
+
runNextGameFrame(() => {
|
|
145
|
+
topStreakTextStart();
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function checkAllPlayersLayingDownComplete() {
|
|
150
|
+
if (v.run.state !== StageTravelState.PLAYERS_LAYING_DOWN) {
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (anyPlayerPlayingExtraAnimation()) {
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
v.run.state = StageTravelState.NONE;
|
|
159
|
+
|
|
160
|
+
enableAllInputs(CUSTOM_TRAPDOOR_FEATURE_NAME);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function goToCustomDestination() {
|
|
164
|
+
if (v.run.destination === null) {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (isString(v.run.destination)) {
|
|
169
|
+
setCustomStage("Slaughterhouse");
|
|
170
|
+
} else {
|
|
171
|
+
const [stage, stageType] = v.run.destination;
|
|
172
|
+
setStage(stage, stageType);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function anyPlayerPlayingExtraAnimation() {
|
|
177
|
+
const players = getAllPlayers();
|
|
178
|
+
return players.some((player) => !player.IsExtraAnimationFinished());
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// ModCallback.POST_PEFFECT_UPDATE (4)
|
|
182
|
+
function postPEffectUpdate(player: EntityPlayer) {
|
|
183
|
+
checkJumpComplete(player);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function checkJumpComplete(player: EntityPlayer) {
|
|
187
|
+
if (v.run.state !== StageTravelState.PLAYERS_JUMPING_DOWN) {
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// In this state, the players are jumping down the hole (i.e. playing the "Trapdoor" animation).
|
|
192
|
+
// When it completes, they will return to normal (i.e. just standing on top of the trapdoor).
|
|
193
|
+
// Thus, make them invisible at the end of the animation. (They will automatically be set to
|
|
194
|
+
// visible again once we travel to the next floor.)
|
|
195
|
+
const sprite = player.GetSprite();
|
|
196
|
+
if (sprite.IsFinished("Trapdoor")) {
|
|
197
|
+
player.Visible = false;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// ModCallbackCustom.POST_GRID_ENTITY_UPDATE
|
|
202
|
+
// GridEntityType.TRAPDOOR (17)
|
|
203
|
+
function postGridEntityUpdateTrapdoor(gridEntity: GridEntity) {
|
|
204
|
+
const roomListIndex = getRoomListIndex();
|
|
205
|
+
const gridIndex = gridEntity.GetGridIndex();
|
|
206
|
+
|
|
207
|
+
const roomTrapdoorMap = v.level.trapdoors.getAndSetDefault(roomListIndex);
|
|
208
|
+
const trapdoorDescription = roomTrapdoorMap.get(gridIndex);
|
|
209
|
+
if (trapdoorDescription === undefined) {
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
checkCustomTrapdoorOpenClose(gridEntity, trapdoorDescription);
|
|
214
|
+
checkCustomTrapdoorPlayerTouched(gridEntity, trapdoorDescription);
|
|
215
|
+
}
|
|
@@ -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
|
+
}
|