isaacscript-common 7.5.0 → 7.5.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.
Files changed (45) hide show
  1. package/dist/classes/DefaultMap.d.ts +3 -2
  2. package/dist/classes/DefaultMap.d.ts.map +1 -1
  3. package/dist/classes/DefaultMap.lua +2 -1
  4. package/dist/features/customStage/customStageUtils.d.ts +2 -1
  5. package/dist/features/customStage/customStageUtils.d.ts.map +1 -1
  6. package/dist/features/customStage/customStageUtils.lua +40 -1
  7. package/dist/features/customStage/exports.d.ts +1 -25
  8. package/dist/features/customStage/exports.d.ts.map +1 -1
  9. package/dist/features/customStage/exports.lua +28 -29
  10. package/dist/features/customStage/v.d.ts +0 -2
  11. package/dist/features/customStage/v.d.ts.map +1 -1
  12. package/dist/features/customStage/v.lua +0 -2
  13. package/dist/features/customStage/versusScreen.d.ts.map +1 -1
  14. package/dist/features/customStage/versusScreen.lua +74 -60
  15. package/dist/functions/doors.d.ts +6 -5
  16. package/dist/functions/doors.d.ts.map +1 -1
  17. package/dist/functions/doors.lua +25 -12
  18. package/dist/functions/enums.d.ts +3 -3
  19. package/dist/functions/enums.lua +3 -3
  20. package/dist/functions/players.d.ts.map +1 -1
  21. package/dist/functions/players.lua +3 -2
  22. package/dist/index.d.ts +105 -102
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/interfaces/{CustomStageLua.d.ts → CustomStageTSConfig.d.ts} +86 -60
  25. package/dist/interfaces/CustomStageTSConfig.d.ts.map +1 -0
  26. package/dist/interfaces/{CustomStageLua.lua → CustomStageTSConfig.lua} +0 -0
  27. package/dist/interfaces/JSONRoomsFile.d.ts +6 -5
  28. package/dist/interfaces/JSONRoomsFile.d.ts.map +1 -1
  29. package/dist/interfaces/private/CustomStage.d.ts +1 -1
  30. package/dist/interfaces/private/CustomStage.d.ts.map +1 -1
  31. package/package.json +1 -1
  32. package/src/classes/DefaultMap.ts +3 -2
  33. package/src/features/customStage/customStageUtils.ts +52 -1
  34. package/src/features/customStage/exports.ts +33 -45
  35. package/src/features/customStage/init.ts +1 -1
  36. package/src/features/customStage/v.ts +0 -6
  37. package/src/features/customStage/versusScreen.ts +70 -55
  38. package/src/functions/doors.ts +37 -21
  39. package/src/functions/enums.ts +3 -3
  40. package/src/functions/players.ts +7 -3
  41. package/src/index.ts +1 -1
  42. package/src/interfaces/{CustomStageLua.ts → CustomStageTSConfig.ts} +107 -41
  43. package/src/interfaces/JSONRoomsFile.ts +6 -5
  44. package/src/interfaces/private/CustomStage.ts +4 -1
  45. package/dist/interfaces/CustomStageLua.d.ts.map +0 -1
@@ -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 { getEntityID } from "../../functions/entities";
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";
@@ -26,7 +26,7 @@ import {
26
26
  DEFAULT_BASE_STAGE_TYPE,
27
27
  INVALID_STAGE_VALUE,
28
28
  } from "./exports";
29
- import v, { customBossPNGPaths } from "./v";
29
+ import v from "./v";
30
30
 
31
31
  const DEFAULT_CHARACTER = PlayerType.ISAAC;
32
32
  const DEFAULT_STAGE_ID = StageID.BASEMENT;
@@ -137,22 +137,25 @@ export function playVersusScreenAnimation(customStage: CustomStage): void {
137
137
  pause();
138
138
  hud.SetVisible(false);
139
139
 
140
- const [playerNamePNGPath, playerPortraitPNGPath] = getPlayerPNGPaths();
141
- versusScreenSprite.ReplaceSpritesheet(
142
- PLAYER_NAME_ANM2_LAYER,
143
- playerNamePNGPath,
144
- );
145
- versusScreenSprite.ReplaceSpritesheet(
146
- PLAYER_PORTRAIT_ANM2_LAYER,
147
- playerPortraitPNGPath,
148
- );
140
+ // Player
141
+ {
142
+ const { namePNGPath, portraitPNGPath } = getPlayerPNGPaths();
143
+ versusScreenSprite.ReplaceSpritesheet(PLAYER_NAME_ANM2_LAYER, namePNGPath);
144
+ versusScreenSprite.ReplaceSpritesheet(
145
+ PLAYER_PORTRAIT_ANM2_LAYER,
146
+ portraitPNGPath,
147
+ );
148
+ }
149
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
- );
150
+ // Boss
151
+ {
152
+ const { namePNGPath, portraitPNGPath } = getBossPNGPaths(customStage);
153
+ versusScreenSprite.ReplaceSpritesheet(BOSS_NAME_ANM2_LAYER, namePNGPath);
154
+ versusScreenSprite.ReplaceSpritesheet(
155
+ BOSS_PORTRAIT_ANM2_LAYER,
156
+ portraitPNGPath,
157
+ );
158
+ }
156
159
 
157
160
  versusScreenSprite.LoadGraphics();
158
161
 
@@ -186,67 +189,81 @@ function willVanillaVersusScreenPlay() {
186
189
  }
187
190
 
188
191
  /** Use the character of the 0th player. */
189
- function getPlayerPNGPaths(): [
190
- playerNamePNGPath: string,
191
- playerPortraitPNGPath: string,
192
- ] {
192
+ function getPlayerPNGPaths(): {
193
+ namePNGPath: string;
194
+ portraitPNGPath: string;
195
+ } {
193
196
  const player = Isaac.GetPlayer();
194
197
  const character = player.GetPlayerType();
195
198
 
196
- let playerNamePNGFileName = PLAYER_NAME_PNG_FILE_NAMES[character];
197
- if (playerNamePNGFileName === undefined) {
198
- playerNamePNGFileName = PLAYER_NAME_PNG_FILE_NAMES[DEFAULT_CHARACTER];
199
+ let namePNGFileName = PLAYER_NAME_PNG_FILE_NAMES[character];
200
+ if (namePNGFileName === undefined) {
201
+ namePNGFileName = PLAYER_NAME_PNG_FILE_NAMES[DEFAULT_CHARACTER];
199
202
  }
200
203
 
201
- const playerNamePNGPath = `${PNG_PATH_PREFIX}/${playerNamePNGFileName}`;
204
+ const namePNGPath = `${PNG_PATH_PREFIX}/${namePNGFileName}`;
202
205
 
203
- let playerPortraitFileName = PLAYER_PORTRAIT_PNG_FILE_NAMES[character];
204
- if (playerNamePNGFileName === undefined) {
205
- playerPortraitFileName = PLAYER_PORTRAIT_PNG_FILE_NAMES[DEFAULT_CHARACTER];
206
+ let portraitFileName = PLAYER_PORTRAIT_PNG_FILE_NAMES[character];
207
+ if (namePNGFileName === undefined) {
208
+ portraitFileName = PLAYER_PORTRAIT_PNG_FILE_NAMES[DEFAULT_CHARACTER];
206
209
  }
207
210
 
208
- const playerPortraitPNGPath = `${PLAYER_PORTRAIT_PNG_PATH_PREFIX}/${playerPortraitFileName}`;
211
+ const portraitPNGPath = `${PLAYER_PORTRAIT_PNG_PATH_PREFIX}/${portraitFileName}`;
209
212
 
210
- return [playerNamePNGPath, playerPortraitPNGPath];
213
+ return { namePNGPath, portraitPNGPath };
211
214
  }
212
215
 
213
216
  /** Use the boss of the first boss found. */
214
- function getBossPNGPaths(): [
215
- bossNamePNGPath: string,
216
- bossPortraitPNGPath: string,
217
- ] {
218
- const bosses = getBosses();
219
- const firstBoss = bosses[0];
220
-
217
+ function getBossPNGPaths(customStage: CustomStage): {
218
+ namePNGPath: string;
219
+ portraitPNGPath: string;
220
+ } {
221
221
  // Prefer the PNG paths specified by the end-user, if any.
222
- if (firstBoss !== undefined) {
223
- const entityID = getEntityID(firstBoss);
224
- const pngPaths = customBossPNGPaths.get(entityID);
225
- if (pngPaths !== undefined) {
226
- return pngPaths;
227
- }
222
+ const paths = getBossPNGPathsCustom(customStage);
223
+ if (paths !== undefined) {
224
+ return paths;
228
225
  }
229
226
 
230
227
  // If this is not a vanilla boss, default to showing question marks.
228
+ const bosses = getBosses();
229
+ const firstBoss = bosses[0];
231
230
  const bossID = firstBoss === undefined ? 0 : firstBoss.GetBossID();
232
231
  if (bossID === 0) {
233
232
  const questionMarkSprite = `${PNG_PATH_PREFIX}/${
234
233
  BOSS_NAME_PNG_FILE_NAMES[BossID.BLUE_BABY]
235
234
  }`;
236
- const bossNamePNGPath = questionMarkSprite;
237
- const bossPortraitPNGPath = questionMarkSprite;
238
- return [bossNamePNGPath, bossPortraitPNGPath];
235
+ const namePNGPath = questionMarkSprite;
236
+ const portraitPNGPath = questionMarkSprite;
237
+ return { namePNGPath, portraitPNGPath };
239
238
  }
240
239
 
241
240
  // If this is a vanilla boss, it will have a boss ID, and we can use the corresponding vanilla
242
241
  // files.
243
- const bossNamePNGFileName = BOSS_NAME_PNG_FILE_NAMES[bossID];
244
- const bossNamePNGPath = `${PNG_PATH_PREFIX}/${bossNamePNGFileName}`;
242
+ const namePNGFileName = BOSS_NAME_PNG_FILE_NAMES[bossID];
243
+ const namePNGPath = `${PNG_PATH_PREFIX}/${namePNGFileName}`;
245
244
 
246
- const bossPortraitPNGFileName = BOSS_PORTRAIT_PNG_FILE_NAMES[bossID];
247
- const bossPortraitPNGPath = `${PNG_PATH_PREFIX}/${bossPortraitPNGFileName}`;
245
+ const portraitPNGFileName = BOSS_PORTRAIT_PNG_FILE_NAMES[bossID];
246
+ const portraitPNGPath = `${PNG_PATH_PREFIX}/${portraitPNGFileName}`;
248
247
 
249
- return [bossNamePNGPath, bossPortraitPNGPath];
248
+ return { namePNGPath, portraitPNGPath };
249
+ }
250
+
251
+ function getBossPNGPathsCustom(
252
+ customStage: CustomStage,
253
+ ): { namePNGPath: string; portraitPNGPath: string } | undefined {
254
+ if (customStage.bossPool === undefined) {
255
+ return undefined;
256
+ }
257
+
258
+ const roomSubType = getRoomSubType();
259
+ const matchingBossEntry = customStage.bossPool.find(
260
+ (bossEntry) => bossEntry.subType === roomSubType,
261
+ );
262
+ if (matchingBossEntry === undefined) {
263
+ return undefined;
264
+ }
265
+
266
+ return matchingBossEntry.versusScreen;
250
267
  }
251
268
 
252
269
  function finishVersusScreenAnimation() {
@@ -267,10 +284,8 @@ export function versusScreenPostRender(): void {
267
284
  return;
268
285
  }
269
286
 
270
- const isPaused = game.IsPaused();
271
- if (isPaused) {
272
- return;
273
- }
287
+ // We do not want to early return when the game is paused because we need to start displaying the
288
+ // black screen as soon as the slide animation starts.
274
289
 
275
290
  if (versusScreenSprite.IsFinished(VERSUS_SCREEN_ANIMATION_NAME)) {
276
291
  finishVersusScreenAnimation();
@@ -21,6 +21,7 @@ import { ROOM_SHAPE_TO_DOOR_SLOTS } from "../objects/roomShapeToDoorSlots";
21
21
  import { arrayToBitFlags } from "./bitwise";
22
22
  import { directionToVector } from "./direction";
23
23
  import { getEnumValues } from "./enums";
24
+ import { hasFlag } from "./flag";
24
25
  import { isTSTLSet } from "./tstlClass";
25
26
  import { asNumber } from "./types";
26
27
 
@@ -53,6 +54,21 @@ export function doorSlotFlagToDoorSlot(doorSlotFlag: DoorSlotFlag): DoorSlot {
53
54
  return doorSlot === undefined ? DEFAULT_DOOR_SLOT : doorSlot;
54
55
  }
55
56
 
57
+ export function doorSlotFlagsToDoorSlots(
58
+ doorSlotFlags: BitFlags<DoorSlotFlag>,
59
+ ): DoorSlot[] {
60
+ const doorSlots: DoorSlot[] = [];
61
+
62
+ for (const doorSlotFlag of getEnumValues(DoorSlotFlag)) {
63
+ if (hasFlag(doorSlotFlags, doorSlotFlag)) {
64
+ const doorSlot = doorSlotFlagToDoorSlot(doorSlotFlag);
65
+ doorSlots.push(doorSlot);
66
+ }
67
+ }
68
+
69
+ return doorSlots;
70
+ }
71
+
56
72
  export function doorSlotToDirection(doorSlot: DoorSlot): Direction {
57
73
  return DOOR_SLOT_TO_DIRECTION[doorSlot];
58
74
  }
@@ -61,6 +77,27 @@ export function doorSlotToDoorSlotFlag(doorSlot: DoorSlot): DoorSlotFlag {
61
77
  return DOOR_SLOT_TO_DOOR_SLOT_FLAG[doorSlot];
62
78
  }
63
79
 
80
+ /**
81
+ * Helper function to convert an array of door slots or a set of door slots to the resulting bit
82
+ * flag number.
83
+ */
84
+ export function doorSlotsToDoorSlotFlags(
85
+ doorSlots:
86
+ | DoorSlot[]
87
+ | readonly DoorSlot[]
88
+ | Set<DoorSlot>
89
+ | ReadonlySet<DoorSlot>,
90
+ ): BitFlags<DoorSlotFlag> {
91
+ const doorSlotArray = isTSTLSet(doorSlots)
92
+ ? [...doorSlots.values()]
93
+ : (doorSlots as DoorSlot[]);
94
+ const doorSlotFlagArray = doorSlotArray.map((doorSlot) =>
95
+ doorSlotToDoorSlotFlag(doorSlot),
96
+ );
97
+
98
+ return arrayToBitFlags(doorSlotFlagArray);
99
+ }
100
+
64
101
  export function getAngelRoomDoor(): GridEntityDoor | undefined {
65
102
  const angelRoomDoors = getDoors(RoomType.ANGEL);
66
103
  return angelRoomDoors.length === 0 ? undefined : angelRoomDoors[0];
@@ -113,27 +150,6 @@ export function getDoorSlotEnterPositionOffset(
113
150
  return oppositeVector.mul(ROOM_ENTRY_OFFSET_FROM_DOOR);
114
151
  }
115
152
 
116
- /**
117
- * Helper function to convert an array of door slots or a set of door slots to the resulting bit
118
- * flag number.
119
- */
120
- export function getDoorSlotFlags(
121
- doorSlots:
122
- | DoorSlot[]
123
- | readonly DoorSlot[]
124
- | Set<DoorSlot>
125
- | ReadonlySet<DoorSlot>,
126
- ): BitFlags<DoorSlotFlag> {
127
- const doorSlotArray = isTSTLSet(doorSlots)
128
- ? [...doorSlots.values()]
129
- : (doorSlots as DoorSlot[]);
130
- const doorSlotFlagArray = doorSlotArray.map((doorSlot) =>
131
- doorSlotToDoorSlotFlag(doorSlot),
132
- );
133
-
134
- return arrayToBitFlags(doorSlotFlagArray);
135
- }
136
-
137
153
  /** Helper function to get the possible door slots that can exist for a given room shape. */
138
154
  export function getDoorSlotsForRoomShape(
139
155
  roomShape: RoomShape,
@@ -18,7 +18,7 @@ import { irange } from "./utils";
18
18
  * Also see the `getEnumKeys` and `getEnumValues` helper functions.
19
19
  *
20
20
  * For a more in depth explanation, see:
21
- * https://isaacscript.github.io/gotchas#iterating-over-enums
21
+ * https://isaacscript.github.io/main/gotchas#iterating-over-enums
22
22
  */
23
23
  export function getEnumEntries<T>(
24
24
  transpiledEnum: T,
@@ -57,7 +57,7 @@ export function getEnumEntries<T>(
57
57
  * Also see the `getEnumEntries` and `getEnumValues` helper functions.
58
58
  *
59
59
  * For a more in depth explanation, see:
60
- * https://isaacscript.github.io/gotchas#iterating-over-enums
60
+ * https://isaacscript.github.io/main/gotchas#iterating-over-enums
61
61
  */
62
62
  export function getEnumKeys<T>(transpiledEnum: T): string[] {
63
63
  const enumEntries = getEnumEntries(transpiledEnum);
@@ -84,7 +84,7 @@ export function getEnumLength<T>(transpiledEnum: T): int {
84
84
  * Also see the `getEnumEntries` and `getEnumKeys` helper functions.
85
85
  *
86
86
  * For a more in depth explanation, see:
87
- * https://isaacscript.github.io/gotchas#iterating-over-enums
87
+ * https://isaacscript.github.io/main/gotchas#iterating-over-enums
88
88
  */
89
89
  export function getEnumValues<T>(transpiledEnum: T): Array<T[keyof T]> {
90
90
  const enumEntries = getEnumEntries(transpiledEnum);
@@ -21,7 +21,11 @@ import {
21
21
  import { getCollectibleMaxCharges } from "./collectibles";
22
22
  import { getCollectibleArray } from "./collectibleSet";
23
23
  import { getEnumValues } from "./enums";
24
- import { getPlayerIndexVanilla, getPlayers } from "./playerIndex";
24
+ import {
25
+ getAllPlayers,
26
+ getPlayerIndexVanilla,
27
+ getPlayers,
28
+ } from "./playerIndex";
25
29
  import { addTearsStat } from "./tears";
26
30
  import { isNumber } from "./types";
27
31
  import { repeat } from "./utils";
@@ -127,12 +131,12 @@ export function addTrinketCostume(
127
131
  export function anyPlayerHasCollectible(
128
132
  collectibleType: CollectibleType,
129
133
  ): boolean {
130
- const players = getPlayers();
134
+ const players = getAllPlayers();
131
135
  return players.some((player) => player.HasCollectible(collectibleType));
132
136
  }
133
137
 
134
138
  export function anyPlayerHasTrinket(trinketType: TrinketType): boolean {
135
- const players = getPlayers();
139
+ const players = getAllPlayers();
136
140
  return players.some((player) => player.HasTrinket(trinketType));
137
141
  }
138
142
 
package/src/index.ts CHANGED
@@ -181,7 +181,7 @@ export * from "./functions/utils";
181
181
  export * from "./functions/vector";
182
182
  export * from "./interfaces/ChargeBarSprites";
183
183
  export * from "./interfaces/Corner";
184
- export * from "./interfaces/CustomStageLua";
184
+ export * from "./interfaces/CustomStageTSConfig";
185
185
  export * from "./interfaces/GridEntityCustomData";
186
186
  export * from "./interfaces/JSONRoomsFile";
187
187
  export * from "./interfaces/PlayerHealth";
@@ -9,8 +9,11 @@
9
9
  *
10
10
  * The `CustomStageLua` interface extends this, adding room metadata.
11
11
  */
12
+
13
+ import { Immutable } from "../types/Immutable";
14
+
12
15
  // ts-prune-ignore-next
13
- export type CustomStageTSConfig = Readonly<{
16
+ export interface CustomStageTSConfig {
14
17
  /** Mandatory. The name of the custom stage. */
15
18
  name: string;
16
19
 
@@ -38,8 +41,14 @@ export type CustomStageTSConfig = Readonly<{
38
41
  * number of the stage that will be warped to and used as a basis for the stage by the level
39
42
  * generation algorithm.
40
43
  *
41
- * (It is not possible to use Basement 1 as a base stage due to conflicts with the `Game.SetStage`
42
- * method.)
44
+ * For example, if you wanted to have a custom stage with a small amount of rooms per floor, then
45
+ * you should choose 2 as a base. (This would copy the number of rooms that would appear in
46
+ * Basement 2.) And if you wanted to have a custom stage with the maximum amount of rooms, then
47
+ * you should choose 13 as a base. (This would copy the number of rooms that would appear on The
48
+ * Void.)
49
+ *
50
+ * It is not possible to use Basement 1 as a base stage due to conflicts with the `Game.SetStage`
51
+ * method.
43
52
  *
44
53
  * If not specified, `LevelStage.BASEMENT_2` (2) will be used.
45
54
  *
@@ -65,7 +74,7 @@ export type CustomStageTSConfig = Readonly<{
65
74
  * the graphics for the walls and floor.) If not specified, the graphics for Basement will be
66
75
  * used.
67
76
  */
68
- backdropPNGPaths?: Readonly<{
77
+ backdropPNGPaths?: {
69
78
  /**
70
79
  * An array that contains the full paths to the graphic files that are used for the floor in
71
80
  * narrow rooms. (The "n" stands for "narrow").
@@ -75,7 +84,7 @@ export type CustomStageTSConfig = Readonly<{
75
84
  *
76
85
  * For an example of this, see the vanilla file "resources/gfx/backdrop/01_basement_nfloor.png".
77
86
  */
78
- nFloors: readonly string[];
87
+ nFloors: string[];
79
88
 
80
89
  /**
81
90
  * An array that contains the full paths to the graphic files that are used for the floor in L
@@ -86,7 +95,7 @@ export type CustomStageTSConfig = Readonly<{
86
95
  *
87
96
  * For an example of this, see the vanilla file "resources/gfx/backdrop/01_lbasementfloor.png".
88
97
  */
89
- lFloors: readonly string[];
98
+ lFloors: string[];
90
99
 
91
100
  /**
92
101
  * An array that contains the full paths to the graphic files that are used for the walls of the
@@ -99,7 +108,7 @@ export type CustomStageTSConfig = Readonly<{
99
108
  * the vanilla file, they concatenate all four variations together into one PNG file. However,
100
109
  * for the custom stages feature, you must separate each wall variation into a separate file.)
101
110
  */
102
- walls: readonly string[];
111
+ walls: string[];
103
112
 
104
113
  /**
105
114
  * An array that contains the full paths to the graphic files for the stage's corners.
@@ -114,8 +123,8 @@ export type CustomStageTSConfig = Readonly<{
114
123
  * you must separate each corner variation into a separate file (and put it in a different file
115
124
  * from the walls).
116
125
  */
117
- corners: readonly string[];
118
- }>;
126
+ corners: string[];
127
+ };
119
128
 
120
129
  /**
121
130
  * Optional. The full path to the spritesheet that contains the graphics of the decorations for
@@ -153,7 +162,7 @@ export type CustomStageTSConfig = Readonly<{
153
162
  * Optional. A collection of paths that contain graphics for the doors of the floor. If not
154
163
  * specified, the doors for Basement will be used.
155
164
  */
156
- doorPNGPaths?: Readonly<{
165
+ doorPNGPaths?: {
157
166
  /**
158
167
  * Optional. The full path to the spritesheet that contains the graphics of the normal doors for
159
168
  * the floor.
@@ -261,7 +270,7 @@ export type CustomStageTSConfig = Readonly<{
261
270
  * located at: `resources/gfx/grid/door_02b_chestroomdoor.png`
262
271
  */
263
272
  chestRoom?: string; // RoomType.CHEST (20)
264
- }>;
273
+ };
265
274
 
266
275
  /**
267
276
  * Optional. An array of shadow objects that describe the graphics for the custom shadows of the
@@ -269,7 +278,7 @@ export type CustomStageTSConfig = Readonly<{
269
278
  * Basement, a shadow of a sideways V is used, among others.) If not specified, no extra shadows
270
279
  * will be drawn.
271
280
  */
272
- shadows?: Readonly<{
281
+ shadows?: {
273
282
  /**
274
283
  * Optional. An array containing the shadows that will be used in rooms of shape
275
284
  * `RoomShape.SHAPE_1x1` (1), `RoomShape.IH` (2), and `RoomShape.IV` (3).
@@ -278,7 +287,7 @@ export type CustomStageTSConfig = Readonly<{
278
287
  *
279
288
  * If not specified, no extra shadows will be drawn in these room shapes.
280
289
  */
281
- "1x1"?: readonly CustomStageShadow[];
290
+ "1x1"?: CustomStageShadow[];
282
291
 
283
292
  /**
284
293
  * Optional. An array containing the shadows that will be used in rooms of shape
@@ -288,7 +297,7 @@ export type CustomStageTSConfig = Readonly<{
288
297
  *
289
298
  * If not specified, no extra shadows will be drawn in these room shapes.
290
299
  */
291
- "1x2"?: readonly CustomStageShadow[];
300
+ "1x2"?: CustomStageShadow[];
292
301
 
293
302
  /**
294
303
  * Optional. An array containing the shadows that will be used in rooms of shape
@@ -298,7 +307,7 @@ export type CustomStageTSConfig = Readonly<{
298
307
  *
299
308
  * If not specified, no extra shadows will be drawn in these room shapes.
300
309
  */
301
- "2x1"?: readonly CustomStageShadow[];
310
+ "2x1"?: CustomStageShadow[];
302
311
 
303
312
  /**
304
313
  * Optional. An array containing the shadows that will be used in rooms of shape
@@ -309,14 +318,22 @@ export type CustomStageTSConfig = Readonly<{
309
318
  *
310
319
  * If not specified, no extra shadows will be drawn in these room shapes.
311
320
  */
312
- "2x2"?: readonly CustomStageShadow[];
313
- }>;
321
+ "2x2"?: CustomStageShadow[];
322
+ };
314
323
 
315
- /** Optional. An array containing the bosses that should be used for the stage. */
316
- bossPool?: readonly CustomStageBossPoolEntry[];
324
+ /**
325
+ * Optional. An array containing the bosses that should be used for the stage. This can include
326
+ * both vanilla bosses and modded bosses.
327
+ */
328
+ bossPool?: CustomStageBossPoolEntry[];
317
329
 
318
- /** Optional. A collection of colors used in the boss "versus" screen. */
319
- versusScreen?: Readonly<{
330
+ /**
331
+ * Optional. A collection of colors used for in the boss "versus" screen for all of the bosses.
332
+ *
333
+ * Note that these graphics will only be applied if one or more bosses are specified in the
334
+ * `bossPool` field.
335
+ */
336
+ versusScreen?: {
320
337
  /**
321
338
  * Optional. An object representing the color to use for the background of the boss "versus"
322
339
  * screen. If not specified, the color for Basement 1 will be used.
@@ -324,7 +341,7 @@ export type CustomStageTSConfig = Readonly<{
324
341
  * For a list of the colors that correspond to the vanilla stages, see
325
342
  * `versusScreenBackgroundColors.ts`.
326
343
  */
327
- backgroundColor?: Readonly<{
344
+ backgroundColor?: {
328
345
  /**
329
346
  * @minimum 0
330
347
  * @maximum 1
@@ -348,7 +365,7 @@ export type CustomStageTSConfig = Readonly<{
348
365
  * @maximum 1
349
366
  */
350
367
  a: number;
351
- }>;
368
+ };
352
369
 
353
370
  /**
354
371
  * Optional. An object representing the color to use for the dirt spots in the boss "versus"
@@ -358,7 +375,7 @@ export type CustomStageTSConfig = Readonly<{
358
375
  * For a list of the colors that correspond to the vanilla stages, see
359
376
  * `versusScreenDirtSpotColors.ts`.
360
377
  */
361
- dirtSpotColor?: Readonly<{
378
+ dirtSpotColor?: {
362
379
  /**
363
380
  * @minimum 0
364
381
  * @maximum 1
@@ -382,16 +399,16 @@ export type CustomStageTSConfig = Readonly<{
382
399
  * @maximum 1
383
400
  */
384
401
  a: number;
385
- }>;
386
- }>;
387
- }>;
402
+ };
403
+ };
404
+ }
388
405
 
389
406
  /**
390
407
  * A description of a custom stage shadow. (In this context, "shadows" are the outlines from things
391
408
  * on the roof. For example, in Basement, a shadow of a sideways V is used, among others.)
392
409
  */
393
410
  // ts-prune-ignore-next
394
- export type CustomStageShadow = Readonly<{
411
+ export interface CustomStageShadow {
395
412
  /**
396
413
  * The full path to the shadow overlay PNG file.
397
414
  *
@@ -406,7 +423,7 @@ export type CustomStageShadow = Readonly<{
406
423
  * If not specified, an object of `{ r: 0, g: 0, b: 0, a: 0.25 }` will be used (which corresponds
407
424
  * to 75% faded black).
408
425
  */
409
- color?: Readonly<{
426
+ color?: {
410
427
  /**
411
428
  * @minimum 0
412
429
  * @maximum 1
@@ -430,27 +447,66 @@ export type CustomStageShadow = Readonly<{
430
447
  * @maximum 1
431
448
  */
432
449
  a: number;
433
- }>;
434
- }>;
450
+ };
451
+ }
435
452
 
436
- /** An object that represents a possible boss for a custom stage. */
453
+ /**
454
+ * An object that represents a possible boss for a custom stage. This can be for a vanilla boss or a
455
+ * custom boss.
456
+ */
437
457
  // ts-prune-ignore-next
438
458
  export interface CustomStageBossPoolEntry {
439
459
  /**
440
- * The name of the boss. This must correspond to the entry in "entities2.xml".
441
- *
442
- * Note that since there is no way to determine the corresponding `EntityType` of the boss during
443
- * compile-time, you must specify the `EntityType` at run-time when your mod first loads using the
444
- * `registerCustomBoss` helper function.
460
+ * The name of the boss. This should correspond to the entry for the boss in the "entities2.xml"
461
+ * file.
445
462
  */
446
463
  name: string;
447
464
 
465
+ /**
466
+ * The arbitrary sub-type chosen for this boss, ranging between 1 and 999. You must set the boss
467
+ * rooms for this boss to this sub-type in Basement Renovator by right-clicking on the room on the
468
+ * right-hand-side.
469
+ *
470
+ * It does not matter if the arbitrary sub-type overlaps with any of the vanilla `BossID` values
471
+ * (e.g. vanilla Boss Room sub-types in "00.special_rooms.stb"). It also does not matter if this
472
+ * value overlaps with the values from other mods.
473
+ *
474
+ * If you are creating an entry for a vanilla boss, it is recommended that you match the sub-type
475
+ * with the corresponding vanilla `BossID` value. This will make things a bit easier to understand
476
+ * for people working on your mod, but is not a hard requirement.
477
+ *
478
+ * @minimum 1
479
+ * @maximum 999
480
+ */
481
+ subType: number;
482
+
448
483
  /**
449
484
  * The weight of the boss. This is used when randomly selecting which boss to use for the floor.
450
485
  * For example, use a value of 1 if you want this boss to be equally likely as any other boss, 0.5
451
486
  * if you want it to be half as likely, 2 if you want it to be twice as likely, and so on.
452
487
  */
453
488
  weight: number;
489
+
490
+ /** Optional. A collection of sprites used for the boss on the "versus" screen. */
491
+ versusScreen?: {
492
+ // eslint-disable-next-line isaacscript/complete-sentences-jsdoc
493
+ /**
494
+ * Mandatory. The full path to the spritesheet that contains the graphics of the name of the
495
+ * boss that will be displayed on the top of the boss "versus" screen.
496
+ *
497
+ * If not specified, a sprite showing "???" will be used.
498
+ */
499
+ namePNGPath: string;
500
+
501
+ // eslint-disable-next-line isaacscript/complete-sentences-jsdoc
502
+ /**
503
+ * Mandatory. The full path to the spritesheet that contains the portrait of the boss that will
504
+ * be displayed on the right side of the boss "versus" screen.
505
+ *
506
+ * If not specified, a sprite showing "???" will be used.
507
+ */
508
+ portraitPNGPath: string;
509
+ };
454
510
  }
455
511
 
456
512
  /**
@@ -460,19 +516,29 @@ export interface CustomStageBossPoolEntry {
460
516
  *
461
517
  * The `CustomStage` interface extends this, adding more data.
462
518
  */
463
- export interface CustomStageLua extends CustomStageTSConfig {
464
- readonly roomsMetadata: readonly CustomStageRoomMetadata[];
519
+ interface CustomStageLuaUnsafe extends CustomStageTSConfig {
520
+ /**
521
+ * This contains metadata about each room in a custom stage, which is used at run-time.
522
+ * (Internally, the IsaacScript standard library uses this as a basis for determining which rooms
523
+ * should randomly appear on the floor.)
524
+ */
525
+ roomsMetadata: CustomStageRoomMetadata[];
465
526
  }
466
527
 
528
+ /** @internal */
529
+ export type CustomStageLua = Immutable<CustomStageLuaUnsafe>;
530
+
467
531
  /**
468
532
  * Metadata about a custom stage room. Each custom stage object contains an array with metadata for
469
533
  * each room.
534
+ *
535
+ * @internal
470
536
  */
471
- export type CustomStageRoomMetadata = Readonly<{
537
+ export interface CustomStageRoomMetadata {
472
538
  type: number;
473
539
  variant: number;
474
540
  subType: number;
475
541
  shape: number;
476
542
  doorSlotFlags: number;
477
543
  weight: number;
478
- }>;
544
+ }