isaacscript-common 7.5.0 → 7.6.1
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/classes/DefaultMap.d.ts +3 -2
- package/dist/classes/DefaultMap.d.ts.map +1 -1
- package/dist/classes/DefaultMap.lua +2 -1
- package/dist/features/customGridEntity.d.ts +6 -3
- package/dist/features/customGridEntity.d.ts.map +1 -1
- package/dist/features/customGridEntity.lua +20 -11
- package/dist/features/customStage/customStageConstants.d.ts +1 -0
- package/dist/features/customStage/customStageConstants.d.ts.map +1 -1
- package/dist/features/customStage/customStageConstants.lua +1 -0
- package/dist/features/customStage/customStageGridEntities.d.ts.map +1 -1
- package/dist/features/customStage/customStageGridEntities.lua +9 -5
- package/dist/features/customStage/customStageUtils.d.ts +2 -1
- package/dist/features/customStage/customStageUtils.d.ts.map +1 -1
- package/dist/features/customStage/customStageUtils.lua +40 -1
- package/dist/features/customStage/exports.d.ts +1 -25
- package/dist/features/customStage/exports.d.ts.map +1 -1
- package/dist/features/customStage/exports.lua +28 -29
- package/dist/features/customStage/init.d.ts.map +1 -1
- package/dist/features/customStage/init.lua +3 -1
- package/dist/features/customStage/v.d.ts +0 -2
- package/dist/features/customStage/v.d.ts.map +1 -1
- package/dist/features/customStage/v.lua +0 -2
- package/dist/features/customStage/versusScreen.d.ts.map +1 -1
- package/dist/features/customStage/versusScreen.lua +80 -60
- package/dist/features/customTrapdoor/exports.d.ts +28 -16
- package/dist/features/customTrapdoor/exports.d.ts.map +1 -1
- package/dist/features/customTrapdoor/exports.lua +45 -61
- package/dist/features/customTrapdoor/init.d.ts.map +1 -1
- package/dist/features/customTrapdoor/init.lua +12 -10
- package/dist/features/customTrapdoor/spawn.d.ts +6 -0
- package/dist/features/customTrapdoor/spawn.d.ts.map +1 -0
- package/dist/features/customTrapdoor/spawn.lua +51 -0
- package/dist/features/customTrapdoor/v.d.ts +2 -2
- package/dist/features/customTrapdoor/v.d.ts.map +1 -1
- package/dist/functions/doors.d.ts +6 -5
- package/dist/functions/doors.d.ts.map +1 -1
- package/dist/functions/doors.lua +25 -12
- package/dist/functions/enums.d.ts +3 -3
- package/dist/functions/enums.lua +3 -3
- package/dist/functions/players.d.ts.map +1 -1
- package/dist/functions/players.lua +3 -2
- package/dist/index.d.ts +172 -11145
- package/dist/index.d.ts.map +1 -1
- package/dist/index.lua +4 -4
- package/dist/interfaces/{CustomStageLua.d.ts → CustomStageTSConfig.d.ts} +87 -61
- package/dist/interfaces/CustomStageTSConfig.d.ts.map +1 -0
- package/dist/interfaces/{CustomStageLua.lua → CustomStageTSConfig.lua} +0 -0
- package/dist/interfaces/GridEntityCustomData.d.ts +2 -2
- package/dist/interfaces/GridEntityCustomData.d.ts.map +1 -1
- package/dist/interfaces/JSONRoomsFile.d.ts +6 -5
- package/dist/interfaces/JSONRoomsFile.d.ts.map +1 -1
- package/dist/interfaces/private/CustomStage.d.ts +1 -1
- package/dist/interfaces/private/CustomStage.d.ts.map +1 -1
- package/dist/interfaces/private/CustomTrapdoorDescription.d.ts +2 -2
- package/dist/interfaces/private/CustomTrapdoorDescription.d.ts.map +1 -1
- package/dist/interfaces/private/CustomTrapdoorDestination.d.ts +14 -0
- package/dist/interfaces/private/CustomTrapdoorDestination.d.ts.map +1 -0
- package/dist/{types/TrapdoorDestination.lua → interfaces/private/CustomTrapdoorDestination.lua} +0 -0
- package/package.json +1 -1
- package/src/classes/DefaultMap.ts +3 -2
- package/src/features/customGridEntity.ts +30 -20
- package/src/features/customStage/customStageConstants.ts +2 -0
- package/src/features/customStage/customStageGridEntities.ts +20 -16
- package/src/features/customStage/customStageUtils.ts +52 -1
- package/src/features/customStage/exports.ts +33 -45
- package/src/features/customStage/init.ts +3 -2
- package/src/features/customStage/v.ts +0 -6
- package/src/features/customStage/versusScreen.ts +78 -57
- package/src/features/customTrapdoor/exports.ts +60 -66
- package/src/features/customTrapdoor/init.ts +12 -11
- package/src/features/customTrapdoor/spawn.ts +53 -0
- package/src/features/customTrapdoor/v.ts +2 -2
- package/src/functions/doors.ts +37 -21
- package/src/functions/enums.ts +3 -3
- package/src/functions/players.ts +7 -3
- package/src/index.ts +3 -4
- package/src/interfaces/{CustomStageLua.ts → CustomStageTSConfig.ts} +108 -42
- package/src/interfaces/GridEntityCustomData.ts +2 -2
- package/src/interfaces/JSONRoomsFile.ts +6 -5
- package/src/interfaces/private/CustomStage.ts +4 -1
- package/src/interfaces/private/CustomTrapdoorDescription.ts +2 -2
- package/src/interfaces/private/CustomTrapdoorDestination.ts +14 -0
- package/dist/interfaces/CustomStageLua.d.ts.map +0 -1
- package/dist/types/TrapdoorDestination.d.ts +0 -6
- package/dist/types/TrapdoorDestination.d.ts.map +0 -1
- package/src/types/TrapdoorDestination.ts +0 -8
|
@@ -16,9 +16,11 @@ import {
|
|
|
16
16
|
import { calculateStageType } from "../../functions/stage";
|
|
17
17
|
import { vectorEquals } from "../../functions/vector";
|
|
18
18
|
import { CustomStage } from "../../interfaces/private/CustomStage";
|
|
19
|
-
import { TrapdoorDestination } from "../../types/TrapdoorDestination";
|
|
20
19
|
import { isCustomGridEntity } from "../customGridEntity";
|
|
21
|
-
import {
|
|
20
|
+
import {
|
|
21
|
+
spawnCustomTrapdoor,
|
|
22
|
+
spawnCustomTrapdoorToVanilla,
|
|
23
|
+
} from "../customTrapdoor/exports";
|
|
22
24
|
import { DEFAULT_BASE_STAGE } from "./exports";
|
|
23
25
|
import v from "./v";
|
|
24
26
|
|
|
@@ -203,20 +205,22 @@ export function convertVanillaTrapdoors(
|
|
|
203
205
|
|
|
204
206
|
removeGridEntity(gridEntity, true);
|
|
205
207
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
208
|
+
if (v.run.firstFloor) {
|
|
209
|
+
// If we are on the first floor of a custom stage, then the destination will be the second floor
|
|
210
|
+
// of the custom stage. (e.g. Caves 1 to Caves 2)
|
|
211
|
+
spawnCustomTrapdoor(gridEntity.Position, customStage.name, 2);
|
|
212
|
+
} else {
|
|
213
|
+
// If we are on the second floor of a custom stage, then the destination will be the vanilla
|
|
214
|
+
// floor equivalent to 2 floors after the floor used as a basis for the custom stage.
|
|
215
|
+
const baseStage =
|
|
216
|
+
customStage.baseStage === undefined
|
|
217
|
+
? DEFAULT_BASE_STAGE
|
|
218
|
+
: customStage.baseStage;
|
|
219
|
+
const stage = (baseStage + 2) as LevelStage;
|
|
220
|
+
const stageType = calculateStageType(stage);
|
|
221
|
+
|
|
222
|
+
spawnCustomTrapdoorToVanilla(gridEntity.Position, stage, stageType);
|
|
223
|
+
}
|
|
220
224
|
}
|
|
221
225
|
|
|
222
226
|
/**
|
|
@@ -2,7 +2,10 @@ import { sumArray } from "../../functions/array";
|
|
|
2
2
|
import { log } from "../../functions/log";
|
|
3
3
|
import { getRandomFloat } from "../../functions/random";
|
|
4
4
|
import { getRandomSeed } from "../../functions/rng";
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
CustomStageBossPoolEntry,
|
|
7
|
+
CustomStageRoomMetadata,
|
|
8
|
+
} from "../../interfaces/CustomStageTSConfig";
|
|
6
9
|
|
|
7
10
|
/**
|
|
8
11
|
* Helper function to get a random custom stage room from an array of custom stage rooms.
|
|
@@ -52,3 +55,51 @@ function getCustomStageRoomWithChosenWeight(
|
|
|
52
55
|
`Failed to get a custom stage room with chosen weight: ${chosenWeight}`,
|
|
53
56
|
);
|
|
54
57
|
}
|
|
58
|
+
|
|
59
|
+
export function getRandomBossRoomFromPool(
|
|
60
|
+
roomsMetadata: readonly CustomStageRoomMetadata[],
|
|
61
|
+
bossPool: readonly CustomStageBossPoolEntry[],
|
|
62
|
+
seedOrRNG: Seed | RNG = getRandomSeed(),
|
|
63
|
+
verbose = false,
|
|
64
|
+
): CustomStageRoomMetadata {
|
|
65
|
+
const totalWeight = getTotalWeightOfBossPool(bossPool);
|
|
66
|
+
if (verbose) {
|
|
67
|
+
log(`Total weight of the custom stage boss pool provided: ${totalWeight}`);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const chosenWeight = getRandomFloat(0, totalWeight, seedOrRNG);
|
|
71
|
+
if (verbose) {
|
|
72
|
+
log(`Randomly chose weight for custom stage boss pool: ${chosenWeight}`);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const bossEntry = getBossEntryWithChosenWeight(bossPool, chosenWeight);
|
|
76
|
+
|
|
77
|
+
const roomsMetadataForBoss = roomsMetadata.filter(
|
|
78
|
+
(roomMetadata) => roomMetadata.subType === bossEntry.subType,
|
|
79
|
+
);
|
|
80
|
+
return getRandomCustomStageRoom(roomsMetadataForBoss, seedOrRNG, verbose);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function getTotalWeightOfBossPool(
|
|
84
|
+
bossPool: readonly CustomStageBossPoolEntry[],
|
|
85
|
+
): float {
|
|
86
|
+
const weights = bossPool.map((bossEntry) => bossEntry.weight);
|
|
87
|
+
return sumArray(weights);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function getBossEntryWithChosenWeight(
|
|
91
|
+
bossPool: readonly CustomStageBossPoolEntry[],
|
|
92
|
+
chosenWeight: float,
|
|
93
|
+
): CustomStageBossPoolEntry {
|
|
94
|
+
for (const bossEntry of bossPool) {
|
|
95
|
+
if (chosenWeight < bossEntry.weight) {
|
|
96
|
+
return bossEntry;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
chosenWeight -= bossEntry.weight;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
error(
|
|
103
|
+
`Failed to get a custom stage boss entry with chosen weight: ${chosenWeight}`,
|
|
104
|
+
);
|
|
105
|
+
}
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import {
|
|
11
|
-
|
|
11
|
+
DoorSlot,
|
|
12
12
|
LevelStage,
|
|
13
13
|
RoomShape,
|
|
14
14
|
RoomType,
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
} from "isaac-typescript-definitions";
|
|
17
17
|
import { reorderedCallbacksSetStageInternal } from "../../callbacks/reorderedCallbacks";
|
|
18
18
|
import { game } from "../../core/cachedClasses";
|
|
19
|
-
import {
|
|
19
|
+
import { doorSlotFlagsToDoorSlots } from "../../functions/doors";
|
|
20
20
|
import { logError } from "../../functions/log";
|
|
21
21
|
import { newRNG } from "../../functions/rng";
|
|
22
22
|
import {
|
|
@@ -25,13 +25,13 @@ import {
|
|
|
25
25
|
} from "../../functions/rooms";
|
|
26
26
|
import { setStage } from "../../functions/stage";
|
|
27
27
|
import { asNumber } from "../../functions/types";
|
|
28
|
+
import { CustomStageRoomMetadata } from "../../interfaces/CustomStageTSConfig";
|
|
28
29
|
import { CustomStage } from "../../interfaces/private/CustomStage";
|
|
29
|
-
import {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
} from "./v";
|
|
30
|
+
import {
|
|
31
|
+
getRandomBossRoomFromPool,
|
|
32
|
+
getRandomCustomStageRoom,
|
|
33
|
+
} from "./customStageUtils";
|
|
34
|
+
import v, { customStageCachedRoomData, customStagesMap } from "./v";
|
|
35
35
|
|
|
36
36
|
export const DEFAULT_BASE_STAGE = LevelStage.BASEMENT_2;
|
|
37
37
|
export const DEFAULT_BASE_STAGE_TYPE = StageType.ORIGINAL;
|
|
@@ -135,8 +135,8 @@ function setStageRoomsData(
|
|
|
135
135
|
const roomType = room.Data.Type;
|
|
136
136
|
const roomShapeMap = customStage.roomTypeMap.get(roomType);
|
|
137
137
|
if (roomShapeMap === undefined) {
|
|
138
|
-
// Only show errors for non-default room types. (
|
|
139
|
-
// special rooms.)
|
|
138
|
+
// Only show errors for non-default room types. (We do not require that end-users provide
|
|
139
|
+
// custom rooms for shops and other special rooms inside of their XML file.)
|
|
140
140
|
if (roomType === RoomType.DEFAULT) {
|
|
141
141
|
logError(
|
|
142
142
|
`Failed to find any custom rooms for RoomType.${RoomType[roomType]} (${roomType}) for custom stage: ${customStage.name}`,
|
|
@@ -160,10 +160,32 @@ function setStageRoomsData(
|
|
|
160
160
|
logError(
|
|
161
161
|
`Failed to find any custom rooms for RoomType.${RoomType[roomType]} (${roomType}) + RoomShape.${RoomShape[roomShape]} (${roomShape}) + DoorSlotFlags ${doorSlotFlags} for custom stage: ${customStage.name}`,
|
|
162
162
|
);
|
|
163
|
+
|
|
164
|
+
const header = `For reference, a DoorSlotFlags of ${doorSlotFlags} is equal to the following doors being enabled:\n`;
|
|
165
|
+
const doorSlots = doorSlotFlagsToDoorSlots(doorSlotFlags);
|
|
166
|
+
const doorSlotLines = doorSlots.map(
|
|
167
|
+
(doorSlot) => `- DoorSlot.${DoorSlot[doorSlot]} (${doorSlot})`,
|
|
168
|
+
);
|
|
169
|
+
const explanation = header + doorSlotLines.join("\n");
|
|
170
|
+
logError(explanation);
|
|
163
171
|
continue;
|
|
164
172
|
}
|
|
165
173
|
|
|
166
|
-
|
|
174
|
+
let randomRoom: CustomStageRoomMetadata;
|
|
175
|
+
if (roomType === RoomType.BOSS) {
|
|
176
|
+
if (customStage.bossPool === undefined) {
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
randomRoom = getRandomBossRoomFromPool(
|
|
181
|
+
roomsMetadata,
|
|
182
|
+
customStage.bossPool,
|
|
183
|
+
rng,
|
|
184
|
+
verbose,
|
|
185
|
+
);
|
|
186
|
+
} else {
|
|
187
|
+
randomRoom = getRandomCustomStageRoom(roomsMetadata, rng, verbose);
|
|
188
|
+
}
|
|
167
189
|
|
|
168
190
|
let newRoomData = customStageCachedRoomData.get(randomRoom.variant);
|
|
169
191
|
if (newRoomData === undefined) {
|
|
@@ -187,40 +209,6 @@ function setStageRoomsData(
|
|
|
187
209
|
}
|
|
188
210
|
}
|
|
189
211
|
|
|
190
|
-
/**
|
|
191
|
-
* By default, unknown bosses will be drawn on the boss "versus" screen as "???". If your custom
|
|
192
|
-
* stage has custom bosses, you can use this function to register the corresponding graphic file
|
|
193
|
-
* files for them.
|
|
194
|
-
*
|
|
195
|
-
* For reference:
|
|
196
|
-
* - The vanilla name sprite for Monstro is located at:
|
|
197
|
-
* `resources/gfx/ui/boss/bossname_20.0_monstro.png`
|
|
198
|
-
* - The vanilla portrait sprite for Monstro is located at:
|
|
199
|
-
* `resources/gfx/ui/boss/portrait_20.0_monstro.png`
|
|
200
|
-
*
|
|
201
|
-
* (Note that boss metadata like this cannot be specified with the rest of the custom stage metadata
|
|
202
|
-
* in the "tsconfig.json" file because there is not a way to retrieve the name of an entity at
|
|
203
|
-
* run-time.)
|
|
204
|
-
*
|
|
205
|
-
* @param entityType The entity type of the custom boss.
|
|
206
|
-
* @param variant The variant of the custom boss.
|
|
207
|
-
* @param subType The sub-type of the custom boss.
|
|
208
|
-
* @param namePNGPath The full path to the PNG file that contains the name of the boss that will be
|
|
209
|
-
* displayed on the top of the boss "versus" screen.
|
|
210
|
-
* @param portraitPNGPath The full path to the PNG file that contains the portrait of the boss that
|
|
211
|
-
* will be displayed on the right side of the boss "versus" screen.
|
|
212
|
-
*/
|
|
213
|
-
export function registerCustomBoss(
|
|
214
|
-
entityType: EntityType,
|
|
215
|
-
variant: int,
|
|
216
|
-
subType: int,
|
|
217
|
-
namePNGPath: string,
|
|
218
|
-
portraitPNGPath: string,
|
|
219
|
-
): void {
|
|
220
|
-
const entityID = getEntityIDFromConstituents(entityType, variant, subType);
|
|
221
|
-
customBossPNGPaths.set(entityID, [namePNGPath, portraitPNGPath]);
|
|
222
|
-
}
|
|
223
|
-
|
|
224
212
|
/**
|
|
225
213
|
* Helper function to disable the custom stage. This is typically called before taking the player to
|
|
226
214
|
* a vanilla floor.
|
|
@@ -13,10 +13,11 @@ import { hasFlag, removeFlag } from "../../functions/flag";
|
|
|
13
13
|
import {
|
|
14
14
|
CustomStageLua,
|
|
15
15
|
CustomStageRoomMetadata,
|
|
16
|
-
} from "../../interfaces/
|
|
16
|
+
} from "../../interfaces/CustomStageTSConfig";
|
|
17
17
|
import { CustomStage, RoomTypeMap } from "../../interfaces/private/CustomStage";
|
|
18
18
|
import { saveDataManager } from "../saveDataManager/exports";
|
|
19
19
|
import { setCustomStageBackdrop } from "./backdrop";
|
|
20
|
+
import { CUSTOM_STAGE_FEATURE_NAME } from "./customStageConstants";
|
|
20
21
|
import {
|
|
21
22
|
convertVanillaTrapdoors,
|
|
22
23
|
removeUrnRewards,
|
|
@@ -42,7 +43,7 @@ export function customStageInit(mod: ModUpgraded): void {
|
|
|
42
43
|
return;
|
|
43
44
|
}
|
|
44
45
|
|
|
45
|
-
saveDataManager(
|
|
46
|
+
saveDataManager(CUSTOM_STAGE_FEATURE_NAME, v);
|
|
46
47
|
versusScreenInit();
|
|
47
48
|
|
|
48
49
|
mod.AddCallback(ModCallback.POST_RENDER, postRender); // 2
|
|
@@ -40,9 +40,3 @@ export const customStagesMap = new Map<string, CustomStage>();
|
|
|
40
40
|
|
|
41
41
|
/** Indexed by room variant. */
|
|
42
42
|
export const customStageCachedRoomData = new Map<int, Readonly<RoomConfig>>();
|
|
43
|
-
|
|
44
|
-
/** Indexed by entity ID. */
|
|
45
|
-
export const customBossPNGPaths = new Map<
|
|
46
|
-
string,
|
|
47
|
-
[namePNGPath: string, portraitPNGPath: string]
|
|
48
|
-
>();
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
import { game, sfxManager } from "../../core/cachedClasses";
|
|
10
10
|
import { arrayRemove } from "../../functions/array";
|
|
11
11
|
import { getBosses } from "../../functions/bosses";
|
|
12
|
-
import {
|
|
12
|
+
import { getRoomSubType } from "../../functions/roomData";
|
|
13
13
|
import { erange } from "../../functions/utils";
|
|
14
14
|
import { CustomStage } from "../../interfaces/private/CustomStage";
|
|
15
15
|
import { BOSS_NAME_PNG_FILE_NAMES } from "../../objects/bossNamePNGFileNames";
|
|
@@ -18,15 +18,19 @@ import { PLAYER_NAME_PNG_FILE_NAMES } from "../../objects/playerNamePNGFileNames
|
|
|
18
18
|
import { PLAYER_PORTRAIT_PNG_FILE_NAMES } from "../../objects/playerPortraitPNGFileNames";
|
|
19
19
|
import { VERSUS_SCREEN_BACKGROUND_COLORS } from "../../objects/versusScreenBackgroundColors";
|
|
20
20
|
import { VERSUS_SCREEN_DIRT_SPOT_COLORS } from "../../objects/versusScreenDirtSpotColors";
|
|
21
|
+
import { disableAllSound, enableAllSound } from "../disableAllSound";
|
|
21
22
|
import { pause, unpause } from "../pause";
|
|
22
23
|
import { runNextGameFrame } from "../runInNFrames";
|
|
23
|
-
import {
|
|
24
|
+
import {
|
|
25
|
+
CUSTOM_STAGE_FEATURE_NAME,
|
|
26
|
+
ISAACSCRIPT_CUSTOM_STAGE_GFX_PATH,
|
|
27
|
+
} from "./customStageConstants";
|
|
24
28
|
import {
|
|
25
29
|
DEFAULT_BASE_STAGE,
|
|
26
30
|
DEFAULT_BASE_STAGE_TYPE,
|
|
27
31
|
INVALID_STAGE_VALUE,
|
|
28
32
|
} from "./exports";
|
|
29
|
-
import v
|
|
33
|
+
import v from "./v";
|
|
30
34
|
|
|
31
35
|
const DEFAULT_CHARACTER = PlayerType.ISAAC;
|
|
32
36
|
const DEFAULT_STAGE_ID = StageID.BASEMENT;
|
|
@@ -136,23 +140,27 @@ export function playVersusScreenAnimation(customStage: CustomStage): void {
|
|
|
136
140
|
|
|
137
141
|
pause();
|
|
138
142
|
hud.SetVisible(false);
|
|
143
|
+
disableAllSound(CUSTOM_STAGE_FEATURE_NAME);
|
|
144
|
+
|
|
145
|
+
// Player
|
|
146
|
+
{
|
|
147
|
+
const { namePNGPath, portraitPNGPath } = getPlayerPNGPaths();
|
|
148
|
+
versusScreenSprite.ReplaceSpritesheet(PLAYER_NAME_ANM2_LAYER, namePNGPath);
|
|
149
|
+
versusScreenSprite.ReplaceSpritesheet(
|
|
150
|
+
PLAYER_PORTRAIT_ANM2_LAYER,
|
|
151
|
+
portraitPNGPath,
|
|
152
|
+
);
|
|
153
|
+
}
|
|
139
154
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
const [bossNamePNGPath, bossPortraitPNGPath] = getBossPNGPaths();
|
|
151
|
-
versusScreenSprite.ReplaceSpritesheet(BOSS_NAME_ANM2_LAYER, bossNamePNGPath);
|
|
152
|
-
versusScreenSprite.ReplaceSpritesheet(
|
|
153
|
-
BOSS_PORTRAIT_ANM2_LAYER,
|
|
154
|
-
bossPortraitPNGPath,
|
|
155
|
-
);
|
|
155
|
+
// Boss
|
|
156
|
+
{
|
|
157
|
+
const { namePNGPath, portraitPNGPath } = getBossPNGPaths(customStage);
|
|
158
|
+
versusScreenSprite.ReplaceSpritesheet(BOSS_NAME_ANM2_LAYER, namePNGPath);
|
|
159
|
+
versusScreenSprite.ReplaceSpritesheet(
|
|
160
|
+
BOSS_PORTRAIT_ANM2_LAYER,
|
|
161
|
+
portraitPNGPath,
|
|
162
|
+
);
|
|
163
|
+
}
|
|
156
164
|
|
|
157
165
|
versusScreenSprite.LoadGraphics();
|
|
158
166
|
|
|
@@ -186,67 +194,81 @@ function willVanillaVersusScreenPlay() {
|
|
|
186
194
|
}
|
|
187
195
|
|
|
188
196
|
/** Use the character of the 0th player. */
|
|
189
|
-
function getPlayerPNGPaths():
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
197
|
+
function getPlayerPNGPaths(): {
|
|
198
|
+
namePNGPath: string;
|
|
199
|
+
portraitPNGPath: string;
|
|
200
|
+
} {
|
|
193
201
|
const player = Isaac.GetPlayer();
|
|
194
202
|
const character = player.GetPlayerType();
|
|
195
203
|
|
|
196
|
-
let
|
|
197
|
-
if (
|
|
198
|
-
|
|
204
|
+
let namePNGFileName = PLAYER_NAME_PNG_FILE_NAMES[character];
|
|
205
|
+
if (namePNGFileName === undefined) {
|
|
206
|
+
namePNGFileName = PLAYER_NAME_PNG_FILE_NAMES[DEFAULT_CHARACTER];
|
|
199
207
|
}
|
|
200
208
|
|
|
201
|
-
const
|
|
209
|
+
const namePNGPath = `${PNG_PATH_PREFIX}/${namePNGFileName}`;
|
|
202
210
|
|
|
203
|
-
let
|
|
204
|
-
if (
|
|
205
|
-
|
|
211
|
+
let portraitFileName = PLAYER_PORTRAIT_PNG_FILE_NAMES[character];
|
|
212
|
+
if (namePNGFileName === undefined) {
|
|
213
|
+
portraitFileName = PLAYER_PORTRAIT_PNG_FILE_NAMES[DEFAULT_CHARACTER];
|
|
206
214
|
}
|
|
207
215
|
|
|
208
|
-
const
|
|
216
|
+
const portraitPNGPath = `${PLAYER_PORTRAIT_PNG_PATH_PREFIX}/${portraitFileName}`;
|
|
209
217
|
|
|
210
|
-
return
|
|
218
|
+
return { namePNGPath, portraitPNGPath };
|
|
211
219
|
}
|
|
212
220
|
|
|
213
221
|
/** Use the boss of the first boss found. */
|
|
214
|
-
function getBossPNGPaths():
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
const bosses = getBosses();
|
|
219
|
-
const firstBoss = bosses[0];
|
|
220
|
-
|
|
222
|
+
function getBossPNGPaths(customStage: CustomStage): {
|
|
223
|
+
namePNGPath: string;
|
|
224
|
+
portraitPNGPath: string;
|
|
225
|
+
} {
|
|
221
226
|
// Prefer the PNG paths specified by the end-user, if any.
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
if (pngPaths !== undefined) {
|
|
226
|
-
return pngPaths;
|
|
227
|
-
}
|
|
227
|
+
const paths = getBossPNGPathsCustom(customStage);
|
|
228
|
+
if (paths !== undefined) {
|
|
229
|
+
return paths;
|
|
228
230
|
}
|
|
229
231
|
|
|
230
232
|
// If this is not a vanilla boss, default to showing question marks.
|
|
233
|
+
const bosses = getBosses();
|
|
234
|
+
const firstBoss = bosses[0];
|
|
231
235
|
const bossID = firstBoss === undefined ? 0 : firstBoss.GetBossID();
|
|
232
236
|
if (bossID === 0) {
|
|
233
237
|
const questionMarkSprite = `${PNG_PATH_PREFIX}/${
|
|
234
238
|
BOSS_NAME_PNG_FILE_NAMES[BossID.BLUE_BABY]
|
|
235
239
|
}`;
|
|
236
|
-
const
|
|
237
|
-
const
|
|
238
|
-
return
|
|
240
|
+
const namePNGPath = questionMarkSprite;
|
|
241
|
+
const portraitPNGPath = questionMarkSprite;
|
|
242
|
+
return { namePNGPath, portraitPNGPath };
|
|
239
243
|
}
|
|
240
244
|
|
|
241
245
|
// If this is a vanilla boss, it will have a boss ID, and we can use the corresponding vanilla
|
|
242
246
|
// files.
|
|
243
|
-
const
|
|
244
|
-
const
|
|
247
|
+
const namePNGFileName = BOSS_NAME_PNG_FILE_NAMES[bossID];
|
|
248
|
+
const namePNGPath = `${PNG_PATH_PREFIX}/${namePNGFileName}`;
|
|
249
|
+
|
|
250
|
+
const portraitPNGFileName = BOSS_PORTRAIT_PNG_FILE_NAMES[bossID];
|
|
251
|
+
const portraitPNGPath = `${PNG_PATH_PREFIX}/${portraitPNGFileName}`;
|
|
252
|
+
|
|
253
|
+
return { namePNGPath, portraitPNGPath };
|
|
254
|
+
}
|
|
245
255
|
|
|
246
|
-
|
|
247
|
-
|
|
256
|
+
function getBossPNGPathsCustom(
|
|
257
|
+
customStage: CustomStage,
|
|
258
|
+
): { namePNGPath: string; portraitPNGPath: string } | undefined {
|
|
259
|
+
if (customStage.bossPool === undefined) {
|
|
260
|
+
return undefined;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
const roomSubType = getRoomSubType();
|
|
264
|
+
const matchingBossEntry = customStage.bossPool.find(
|
|
265
|
+
(bossEntry) => bossEntry.subType === roomSubType,
|
|
266
|
+
);
|
|
267
|
+
if (matchingBossEntry === undefined) {
|
|
268
|
+
return undefined;
|
|
269
|
+
}
|
|
248
270
|
|
|
249
|
-
return
|
|
271
|
+
return matchingBossEntry.versusScreen;
|
|
250
272
|
}
|
|
251
273
|
|
|
252
274
|
function finishVersusScreenAnimation() {
|
|
@@ -256,6 +278,7 @@ function finishVersusScreenAnimation() {
|
|
|
256
278
|
|
|
257
279
|
unpause();
|
|
258
280
|
hud.SetVisible(true);
|
|
281
|
+
enableAllSound(CUSTOM_STAGE_FEATURE_NAME);
|
|
259
282
|
|
|
260
283
|
// The sound effect only plays once the versus cutscene is over.
|
|
261
284
|
sfxManager.Play(SoundEffect.CASTLE_PORTCULLIS);
|
|
@@ -267,10 +290,8 @@ export function versusScreenPostRender(): void {
|
|
|
267
290
|
return;
|
|
268
291
|
}
|
|
269
292
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
return;
|
|
273
|
-
}
|
|
293
|
+
// We do not want to early return when the game is paused because we need to start displaying the
|
|
294
|
+
// black screen as soon as the slide animation starts.
|
|
274
295
|
|
|
275
296
|
if (versusScreenSprite.IsFinished(VERSUS_SCREEN_ANIMATION_NAME)) {
|
|
276
297
|
finishVersusScreenAnimation();
|
|
@@ -1,45 +1,28 @@
|
|
|
1
|
-
import {
|
|
2
|
-
GridCollisionClass,
|
|
3
|
-
LevelStage,
|
|
4
|
-
StageType,
|
|
5
|
-
} from "isaac-typescript-definitions";
|
|
6
|
-
import { game } from "../../core/cachedClasses";
|
|
7
|
-
import { TrapdoorAnimation } from "../../enums/private/TrapdoorAnimation";
|
|
1
|
+
import { LevelStage, StageType } from "isaac-typescript-definitions";
|
|
8
2
|
import { errorIfFeaturesNotInitialized } from "../../featuresInitialized";
|
|
9
3
|
import { getNextStage, getNextStageType } from "../../functions/nextStage";
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import { TrapdoorDestination } from "../../types/TrapdoorDestination";
|
|
14
|
-
import { spawnCustomGridEntity } from "../customGridEntity";
|
|
15
|
-
import {
|
|
16
|
-
CUSTOM_TRAPDOOR_FEATURE_NAME,
|
|
17
|
-
GridEntityTypeCustom,
|
|
18
|
-
} from "./customTrapdoorConstants";
|
|
19
|
-
import { shouldTrapdoorSpawnOpen } from "./openClose";
|
|
20
|
-
import v from "./v";
|
|
4
|
+
import { CustomTrapdoorDestination } from "../../interfaces/private/CustomTrapdoorDestination";
|
|
5
|
+
import { CUSTOM_TRAPDOOR_FEATURE_NAME } from "./customTrapdoorConstants";
|
|
6
|
+
import { spawnCustomTrapdoorToDestination } from "./spawn";
|
|
21
7
|
|
|
22
8
|
/**
|
|
23
|
-
* Helper function to spawn a trapdoor grid entity that will
|
|
24
|
-
*
|
|
9
|
+
* Helper function to spawn a trapdoor grid entity that will take a player to a custom stage. If you
|
|
10
|
+
* want to create a custom trapdoor that goes to a vanilla stage instead, use the
|
|
11
|
+
* `spawnCustomTrapdoorToVanilla` helper function.
|
|
12
|
+
*
|
|
13
|
+
* Custom trapdoors can have one or more of the following attributes:
|
|
25
14
|
*
|
|
26
15
|
* - custom destination (or custom logic for after the player enters)
|
|
27
16
|
* - custom graphics
|
|
28
17
|
* - custom logic for opening/closing
|
|
29
18
|
*
|
|
30
|
-
* You can use this function to take the player to your custom stage.
|
|
31
|
-
*
|
|
32
19
|
* Under the hood, the custom trapdoor is represented by a decoration grid entity and is manually
|
|
33
20
|
* respawned every time the player re-enters the room.
|
|
34
21
|
*
|
|
35
22
|
* @param gridIndexOrPosition The location in the room to spawn the trapdoor.
|
|
36
|
-
* @param
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
* StageType.ORIGINAL]` corresponds to Caves 1, and a destination of
|
|
40
|
-
* `["Slaughterhouse", 1]` corresponds to a custom stage of Slaughterhouse 1. If
|
|
41
|
-
* the destination is undefined, then the "normal" destination corresponding to
|
|
42
|
-
* the current stage and room will be used (e.g. the next floor, in most cases).
|
|
23
|
+
* @param customStageName The name of the custom stage.
|
|
24
|
+
* @param customStageFloorNum The floor of the custom stage. For most purposes, you should use 1 or
|
|
25
|
+
* 2.
|
|
43
26
|
* @param anm2Path Optional. The path to the anm2 file to use. By default, the vanilla trapdoor anm2
|
|
44
27
|
* of "gfx/grid/door_11_trapdoor.anm2" will be used. The specified anm2 file must
|
|
45
28
|
* have animations called "Opened", "Closed", and "Open Animation".
|
|
@@ -48,54 +31,65 @@ import v from "./v";
|
|
|
48
31
|
*/
|
|
49
32
|
export function spawnCustomTrapdoor(
|
|
50
33
|
gridIndexOrPosition: int | Vector,
|
|
51
|
-
|
|
34
|
+
customStageName: string,
|
|
35
|
+
customStageFloorNum: int,
|
|
52
36
|
anm2Path = "gfx/grid/door_11_trapdoor.anm2",
|
|
53
37
|
spawnOpen?: boolean,
|
|
54
38
|
): GridEntity {
|
|
55
39
|
errorIfFeaturesNotInitialized(CUSTOM_TRAPDOOR_FEATURE_NAME);
|
|
56
40
|
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
? room.GetGridIndex(gridIndexOrPosition)
|
|
62
|
-
: gridIndexOrPosition;
|
|
41
|
+
const destination: CustomTrapdoorDestination = {
|
|
42
|
+
customStageName,
|
|
43
|
+
customStageFloorNum,
|
|
44
|
+
};
|
|
63
45
|
|
|
64
|
-
|
|
65
|
-
GridEntityTypeCustom.TRAPDOOR_CUSTOM,
|
|
46
|
+
return spawnCustomTrapdoorToDestination(
|
|
66
47
|
gridIndexOrPosition,
|
|
67
|
-
|
|
48
|
+
destination,
|
|
68
49
|
anm2Path,
|
|
69
|
-
|
|
50
|
+
spawnOpen,
|
|
70
51
|
);
|
|
52
|
+
}
|
|
71
53
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
54
|
+
/**
|
|
55
|
+
* This is the same thing as the `spawnCustomTrapdoor` function, but instead of having a destination
|
|
56
|
+
* of a custom stage, it has a destination of a vanilla stage.
|
|
57
|
+
*
|
|
58
|
+
* For more information, see the `spawnCustomTrapdoor` function.
|
|
59
|
+
*
|
|
60
|
+
* @param gridIndexOrPosition The location in the room to spawn the trapdoor.
|
|
61
|
+
* @param stage Optional. The number of the vanilla stage to go to. If not provided, the "normal"
|
|
62
|
+
* next stage will be selected.
|
|
63
|
+
* @param stageType The stage type of the vanilla stage to go to. If not provided, the "normal" next
|
|
64
|
+
* stage type will be selected.
|
|
65
|
+
* @param anm2Path Optional. The path to the anm2 file to use. By default, the vanilla trapdoor anm2
|
|
66
|
+
* of "gfx/grid/door_11_trapdoor.anm2" will be used. The specified anm2 file must
|
|
67
|
+
* have animations called "Opened", "Closed", and "Open Animation".
|
|
68
|
+
* @param spawnOpen Optional. Whether or not to spawn the trapdoor in an open state. By default,
|
|
69
|
+
* behavior will be used that emulates a vanilla trapdoor.
|
|
70
|
+
*/
|
|
71
|
+
export function spawnCustomTrapdoorToVanilla(
|
|
72
|
+
gridIndexOrPosition: int | Vector,
|
|
73
|
+
stage?: LevelStage,
|
|
74
|
+
stageType?: StageType,
|
|
75
|
+
anm2Path = "gfx/grid/door_11_trapdoor.anm2",
|
|
76
|
+
spawnOpen?: boolean,
|
|
77
|
+
): GridEntity {
|
|
78
|
+
errorIfFeaturesNotInitialized(CUSTOM_TRAPDOOR_FEATURE_NAME);
|
|
92
79
|
|
|
93
|
-
|
|
94
|
-
|
|
80
|
+
const vanillaStage = stage === undefined ? getNextStage() : stage;
|
|
81
|
+
const vanillaStageType =
|
|
82
|
+
stageType === undefined ? getNextStageType() : stageType;
|
|
95
83
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
84
|
+
const destination: CustomTrapdoorDestination = {
|
|
85
|
+
vanillaStage,
|
|
86
|
+
vanillaStageType,
|
|
87
|
+
};
|
|
99
88
|
|
|
100
|
-
return
|
|
89
|
+
return spawnCustomTrapdoorToDestination(
|
|
90
|
+
gridIndexOrPosition,
|
|
91
|
+
destination,
|
|
92
|
+
anm2Path,
|
|
93
|
+
spawnOpen,
|
|
94
|
+
);
|
|
101
95
|
}
|