isaacscript-common
Version:
Helper functions and features for IsaacScript mods.
527 lines (474 loc) • 15.4 kB
text/typescript
import { BossID, LevelStage, StageID } from "isaac-typescript-definitions";
import { BOSS_ID_VALUES } from "../cachedEnumValues";
import { isStoryBossID } from "../functions/storyBosses";
import { ReadonlyMap } from "../types/ReadonlyMap";
import { ReadonlySet } from "../types/ReadonlySet";
// The "bosspools.xml" file does not actually correspond to the real boss pools, so these data
// structures were determined through experimentation on v1.7.8a.
// We use sets of strings instead of tuples for these data structures because TypeScript/Lua does
// not have real tuples. If we store bosses as tuples, then we cannot do a set lookup in O(1).
/** For `StageID.BASEMENT` (1). */
const BASEMENT_BOSSES = [
BossID.MONSTRO, // 1
BossID.LARRY_JR, // 2
BossID.FAMINE, // 9
BossID.DUKE_OF_FLIES, // 13
BossID.GEMINI, // 17
BossID.STEVEN, // 20
BossID.HEADLESS_HORSEMAN, // 22
BossID.FALLEN, // 23
BossID.DINGLE, // 44
BossID.GURGLING, // 56
BossID.LITTLE_HORN, // 60
// - `BossID.RAG_MAN` (61) was removed in Repentance.
BossID.DANGLE, // 64
BossID.TURDLING, // 65
BossID.BABY_PLUM, // 84 (added in Repentance)
] as const;
/** For `StageID.CELLAR` (2). */
const CELLAR_BOSSES = [
BossID.FAMINE, // 9
BossID.DUKE_OF_FLIES, // 13
// - `BossID.FISTULA` (18) was removed in Repentance.
BossID.HEADLESS_HORSEMAN, // 22
BossID.FALLEN, // 23
BossID.BLIGHTED_OVUM, // 32
BossID.WIDOW, // 34
BossID.PIN, // 37
BossID.HAUNT, // 43
BossID.LITTLE_HORN, // 60
BossID.RAG_MAN, // 61
BossID.BABY_PLUM, // 84 (added in Repentance)
] as const;
/** For `StageID.BURNING_BASEMENT` (3). */
const BURNING_BASEMENT_BOSSES = [
BossID.MONSTRO, // 1
BossID.LARRY_JR, // 2
BossID.FAMINE, // 9
BossID.DUKE_OF_FLIES, // 13
BossID.GEMINI, // 17 (added in Repentance)
BossID.STEVEN, // 20 (added in Repentance)
BossID.HEADLESS_HORSEMAN, // 22
BossID.FALLEN, // 23
// - `BossID.HAUNT` (43) was removed in Repentance.
BossID.DINGLE, // 44 (added in Repentance)
BossID.GURGLING, // 56 (added in Repentance)
BossID.LITTLE_HORN, // 60
BossID.RAG_MAN, // 61
BossID.DANGLE, // 64 (added in Repentance)
BossID.TURDLING, // 65 (added in Repentance)
BossID.BABY_PLUM, // 84 (added in Repentance)
] as const;
/** For `StageID.DOWNPOUR` (27). */
const DOWNPOUR_BOSSES = [
BossID.LIL_BLUB, // 75
BossID.WORMWOOD, // 76
BossID.RAINMAKER, // 77
BossID.MIN_MIN, // 91
] as const;
/** For `StageID.DROSS` (28). */
const DROSS_BOSSES = [
BossID.LIL_BLUB, // 75
BossID.WORMWOOD, // 76
BossID.CLOG, // 92
BossID.COLOSTOMIA, // 95
BossID.TURDLET, // 97
] as const;
/** The set of unique bosses for Basement, Cellar, Burning Basement, Downpour, and Dross. */
const ALL_BASEMENT_BOSSES_SET = new ReadonlySet<BossID>([
...BASEMENT_BOSSES,
...CELLAR_BOSSES,
...BURNING_BASEMENT_BOSSES,
...DOWNPOUR_BOSSES,
...DROSS_BOSSES,
]);
/** For `StageID.CAVES` (4). */
const CAVES_BOSSES = [
BossID.CHUB, // 3
BossID.GURDY, // 4
BossID.PESTILENCE, // 10
BossID.PEEP, // 14
BossID.FISTULA, // 18 (added in Repentance)
BossID.CHAD, // 21
BossID.HEADLESS_HORSEMAN, // 22
BossID.GURDY_JR, // 28
BossID.MEGA_FATTY, // 47
BossID.MEGA_MAW, // 45
// - `BossID.DARK_ONE` (50) was removed in Repentance.
BossID.FALLEN, // 23
BossID.STAIN, // 57
// - `BossID.FORSAKEN` (59) was removed in Repentance.
// - `BossID.FRAIL` (66) was removed in Repentance.
BossID.RAG_MEGA, // 67
BossID.BIG_HORN, // 69
BossID.BUMBINO, // 94 (added in Repentance)
] as const;
/** For `StageID.CATACOMBS` (5). */
const CATACOMBS_BOSSES = [
BossID.PESTILENCE, // 10
BossID.PEEP, // 14
BossID.HEADLESS_HORSEMAN, // 22
BossID.FALLEN, // 23
BossID.HOLLOW, // 26
BossID.CARRION_QUEEN, // 27
BossID.GURDY_JR, // 28
BossID.HUSK, // 29
BossID.WRETCHED, // 36
BossID.DARK_ONE, // 50
BossID.POLYCEPHALUS, // 52
// - `BossID.STAIN` (57) was removed in Repentance.
BossID.FORSAKEN, // 59
BossID.FRAIL, // 66
BossID.RAG_MEGA, // 67
BossID.BIG_HORN, // 69
BossID.BUMBINO, // 94 (added in Repentance)
] as const;
/** For `StageID.FLOODED_CAVES` (6). */
const FLOODED_CAVES_BOSSES = [
BossID.CHUB, // 3
BossID.GURDY, // 4
BossID.PESTILENCE, // 10
BossID.PEEP, // 14
BossID.FISTULA, // 18 (added in Repentance)
BossID.CHAD, // 21
BossID.HEADLESS_HORSEMAN, // 22
BossID.FALLEN, // 23
BossID.GURDY_JR, // 28
BossID.MEGA_MAW, // 45 (added in Repentance)
BossID.MEGA_FATTY, // 47 (added in Repentance)
// - `BossID.DARK_ONE` (50) was removed in Repentance.
// - `BossID.POLYCEPHALUS` (52) was removed in Repentance.
BossID.STAIN, // 57
BossID.FORSAKEN, // 59
BossID.FRAIL, // 66
BossID.RAG_MEGA, // 67
BossID.BIG_HORN, // 69
BossID.BUMBINO, // 94 (added in Repentance)
] as const;
/** For `StageID.MINES` (29). */
const MINES_BOSSES = [
BossID.REAP_CREEP, // 74
BossID.TUFF_TWINS, // 80
BossID.HORNFEL, // 82
BossID.GREAT_GIDEON, // 83
] as const;
/** For `StageID.ASHPIT` (30). */
const ASHPIT_BOSSES = [
BossID.PILE, // 73
BossID.GREAT_GIDEON, // 83
BossID.SINGE, // 93
BossID.SHELL, // 96
BossID.CLUTCH, // 102
] as const;
/** The set of unique bosses for Caves, Catacombs, Flooded Caves, Mines, and Ashpit. */
const ALL_CAVES_BOSSES_SET = new ReadonlySet<BossID>([
...CAVES_BOSSES,
...CATACOMBS_BOSSES,
...FLOODED_CAVES_BOSSES,
...MINES_BOSSES,
...ASHPIT_BOSSES,
]);
/**
* For `StageID.DEPTHS` (7).
*
* Note that this set includes Mom, even though they are not technically in the boss pool.
*/
const DEPTHS_BOSSES = [
BossID.MONSTRO_2, // 5
BossID.MOM, // 6
BossID.WAR, // 11
BossID.LOKI, // 15
BossID.GISH, // 19
BossID.HEADLESS_HORSEMAN, // 22
BossID.FALLEN, // 23
BossID.GATE, // 46
BossID.CAGE, // 48
// - `BossID.ADVERSARY` (51) was removed in Repentance.
BossID.BROWNIE, // 58
BossID.SISTERS_VIS, // 68
BossID.REAP_CREEP, // 74 (added in Repentance)
] as const;
/**
* For `StageID.NECROPOLIS` (8).
*
* Note that this set includes Mom, even though they are not technically in the boss pool.
*/
const NECROPOLIS_BOSSES = [
BossID.MOM, // 6
BossID.WAR, // 11
BossID.LOKI, // 15
BossID.HEADLESS_HORSEMAN, // 22
BossID.FALLEN, // 23
BossID.BLOAT, // 30
BossID.MASK_OF_INFAMY, // 35
// - `BossID.GATE` (46) was removed in Repentance.
BossID.ADVERSARY, // 51
BossID.BROWNIE, // 58
BossID.SISTERS_VIS, // 68
BossID.PILE, // 73 (added in Repentance)
] as const;
/**
* For `StageID.DANK_DEPTHS` (9).
*
* Note that this set includes Mom, even though they are not technically in the boss pool.
*/
const DANK_DEPTHS_BOSSES = [
BossID.MONSTRO_2, // 5
BossID.MOM, // 6
BossID.WAR, // 11
BossID.LOKI, // 15
BossID.GISH, // 19
BossID.HEADLESS_HORSEMAN, // 22
BossID.FALLEN, // 23
BossID.GATE, // 46 (added in Repentance)
BossID.CAGE, // 48 (added in Repentance)
// - `BossID.ADVERSARY` (51) was removed in Repentance.
BossID.BROWNIE, // 58
BossID.SISTERS_VIS, // 68
BossID.REAP_CREEP, // 74 (added in Repentance)
] as const;
/**
* For `StageID.MAUSOLEUM` (31).
*
* Note that this set includes Mausoleum Mom, even though they are not technically in the boss pool.
*/
const MAUSOLEUM_BOSSES = [
BossID.SIREN, // 79
BossID.HERETIC, // 81
BossID.MAUSOLEUM_MOM, // 89
] as const;
/**
* For `StageID.GEHENNA` (32).
*
* Note that this set includes Mausoleum Mom, even though they are not technically in the boss pool.
*/
const GEHENNA_BOSSES = [
BossID.VISAGE, // 78
BossID.MAUSOLEUM_MOM, // 89
BossID.HORNY_BOYS, // 101
] as const;
/** The set of unique bosses for Depths, Necropolis, Dank Depths, Mausoleum, and Gehenna. */
const ALL_DEPTHS_BOSSES_SET = new ReadonlySet<BossID>([
...DEPTHS_BOSSES,
...NECROPOLIS_BOSSES,
...DANK_DEPTHS_BOSSES,
...MAUSOLEUM_BOSSES,
...GEHENNA_BOSSES,
]);
/**
* For `StageID.WOMB` (10).
*
* Note that this set includes Mom's Heart & It Lives, even though they are not technically in the
* boss pool.
*/
const WOMB_BOSSES = [
BossID.SCOLEX, // 7
BossID.MOMS_HEART, // 8
BossID.DEATH, // 12
BossID.BLASTOCYST, // 16
BossID.HEADLESS_HORSEMAN, // 22
BossID.FALLEN, // 23
BossID.IT_LIVES, // 25
// - `BossID.BLOAT` (30) was removed in Repentance.
BossID.LOKII, // 31
// - `BossID.TERATOMA` (33) was removed in Repentance.
BossID.CONQUEST, // 38
// - `BossID.DADDY_LONG_LEGS` (41) was removed in Repentance.
// - `BossID.TRIACHNID` (42) was removed in Repentance.
BossID.MAMA_GURDY, // 49
BossID.MR_FRED, // 53
// - `BossID.SISTERS_VIS` (68) was removed in Repentance.
BossID.MATRIARCH, // 72
] as const;
/**
* For `StageID.UTERO` (11).
*
* Note that this set includes Mom's Heart & It Lives, even though they are not technically in the
* boss pool.
*/
const UTERO_BOSSES = [
BossID.MOMS_HEART, // 8
BossID.DEATH, // 12
// - `BossID.BLASTOCYST` (16) was removed in Repentance.
BossID.HEADLESS_HORSEMAN, // 22
BossID.FALLEN, // 23
BossID.IT_LIVES, // 25
BossID.BLOAT, // 30
BossID.LOKII, // 31
BossID.TERATOMA, // 33
BossID.CONQUEST, // 38
BossID.DADDY_LONG_LEGS, // 41
BossID.TRIACHNID, // 42
// - `BossID.MAMA_GURDY` (49) was removed in Repentance.
// - `BossID.MR_FRED` (52) was removed in Repentance.
// - `BossID.SISTERS_VIS` (68) was removed in Repentance.
// - `BossID.MATRIARCH` (72) was removed in Repentance.
] as const;
/**
* For `StageID.SCARRED_WOMB` (12).
*
* Note that this set includes Mom's Heart & It Lives, even though they are not technically in the
* boss pool.
*/
const SCARRED_WOMB_BOSSES = [
BossID.SCOLEX, // 7
BossID.MOMS_HEART, // 8
BossID.DEATH, // 12
BossID.BLASTOCYST, // 16
BossID.HEADLESS_HORSEMAN, // 22
BossID.FALLEN, // 23
BossID.IT_LIVES, // 25
// - `BossID.BLOAT` (30) was removed in Repentance.
BossID.LOKII, // 31 (added in Repentance)
// - `BossID.TERATOMA` (33) was removed in Repentance.
BossID.CONQUEST, // 38
// - `BossID.DADDY_LONG_LEGS` (41) was removed in Repentance.
BossID.TRIACHNID, // 42
BossID.MAMA_GURDY, // 49
BossID.MR_FRED, // 53 (added in Repentance)
// - `BossID.SISTERS_VIS` (68) was removed in Repentance.
BossID.MATRIARCH, // 72
] as const;
/**
* For `StageID.CORPSE` (33).
*
* Note that this set includes Mother, even though she is not technically in the boss pool.
*/
const CORPSE_BOSSES = [
BossID.SCOURGE, // 85
BossID.CHIMERA, // 86
BossID.ROTGUT, // 87
BossID.MOTHER, // 88
] as const;
/** The set of unique bosses for Womb, Utero, Scarred Womb, and Corpse. */
const ALL_WOMB_BOSSES_SET = new ReadonlySet([
...WOMB_BOSSES,
...UTERO_BOSSES,
...SCARRED_WOMB_BOSSES,
...CORPSE_BOSSES,
]);
const BLUE_WOMB_BOSSES = [
BossID.HUSH, // 63
] as const;
const ALL_BLUE_WOMB_BOSSES_SET = new ReadonlySet<BossID>([...BLUE_WOMB_BOSSES]);
const SHEOL_BOSSES = [
BossID.SATAN, // 24
] as const;
const CATHEDRAL_BOSSES = [
BossID.ISAAC, // 39
] as const;
const ALL_STAGE_10_BOSSES_SET = new ReadonlySet<BossID>([
...SHEOL_BOSSES,
...CATHEDRAL_BOSSES,
]);
/**
* Note that this set includes Mega Satan, even though they are not technically in the boss pool.
*/
const DARK_ROOM_BOSSES = [
BossID.LAMB, // 54
BossID.MEGA_SATAN, // 55
] as const;
/**
* Note that this set includes Mega Satan, even though they are not technically in the boss pool.
*/
const CHEST_BOSSES = [
BossID.BLUE_BABY, // 40
BossID.MEGA_SATAN, // 55
] as const;
const ALL_STAGE_11_BOSSES_SET = new ReadonlySet<BossID>([
...DARK_ROOM_BOSSES,
...CHEST_BOSSES,
]);
const VOID_BOSSES = [
BossID.DELIRIUM, // 70
] as const;
const ALL_VOID_BOSSES_SET = new ReadonlySet<BossID>([...VOID_BOSSES]);
/**
* Includes Dogma and The Beast. Does not include Ultra Famine, Ultra Pestilence, Ultra War, and
* Ultra Death (since they do not have boss IDs).
*/
const HOME_BOSSES = [
BossID.DOGMA, // 99
BossID.BEAST, // 100
] as const;
const ALL_HOME_BOSSES_SET = new ReadonlySet<BossID>([...HOME_BOSSES]);
export const STAGE_ID_TO_BOSS_IDS = new ReadonlyMap<StageID, readonly BossID[]>(
[
[StageID.BASEMENT, BASEMENT_BOSSES], // 1
[StageID.CELLAR, CELLAR_BOSSES], // 2
[StageID.BURNING_BASEMENT, BURNING_BASEMENT_BOSSES], // 3
[StageID.DOWNPOUR, DOWNPOUR_BOSSES], // 27
[StageID.DROSS, DROSS_BOSSES], // 28
[StageID.CAVES, CAVES_BOSSES], // 4
[StageID.CATACOMBS, CATACOMBS_BOSSES], // 5
[StageID.FLOODED_CAVES, FLOODED_CAVES_BOSSES], // 6
[StageID.MINES, MINES_BOSSES], // 29
[StageID.ASHPIT, ASHPIT_BOSSES], // 30
[StageID.DEPTHS, DEPTHS_BOSSES], // 7
[StageID.NECROPOLIS, NECROPOLIS_BOSSES], // 8
[StageID.DANK_DEPTHS, DANK_DEPTHS_BOSSES], // 9
[StageID.MAUSOLEUM, MAUSOLEUM_BOSSES], // 31
[StageID.GEHENNA, GEHENNA_BOSSES], // 32
[StageID.WOMB, WOMB_BOSSES], // 10
[StageID.UTERO, UTERO_BOSSES], // 11
[StageID.SCARRED_WOMB, SCARRED_WOMB_BOSSES], // 12
[StageID.CORPSE, CORPSE_BOSSES], // 33
[StageID.BLUE_WOMB, BLUE_WOMB_BOSSES], // 13
[StageID.SHEOL, SHEOL_BOSSES], // 14
[StageID.CATHEDRAL, CATHEDRAL_BOSSES], // 15
[StageID.DARK_ROOM, DARK_ROOM_BOSSES], // 16
[StageID.CHEST, CHEST_BOSSES], // 17
[StageID.VOID, VOID_BOSSES], // 26
[StageID.HOME, HOME_BOSSES], // 35
],
);
export const STAGE_TO_COMBINED_BOSS_SET_MAP = new ReadonlyMap<
LevelStage,
ReadonlySet<BossID>
>([
[LevelStage.BASEMENT_1, ALL_BASEMENT_BOSSES_SET], // 1
[LevelStage.BASEMENT_2, ALL_BASEMENT_BOSSES_SET], // 2
[LevelStage.CAVES_1, ALL_CAVES_BOSSES_SET], // 3
[LevelStage.CAVES_2, ALL_CAVES_BOSSES_SET], // 4
[LevelStage.DEPTHS_1, ALL_DEPTHS_BOSSES_SET], // 5
[LevelStage.DEPTHS_2, ALL_DEPTHS_BOSSES_SET], // 6
[LevelStage.WOMB_1, ALL_WOMB_BOSSES_SET], // 7
[LevelStage.WOMB_2, ALL_WOMB_BOSSES_SET], // 8
[LevelStage.BLUE_WOMB, ALL_BLUE_WOMB_BOSSES_SET], // 9
[LevelStage.SHEOL_CATHEDRAL, ALL_STAGE_10_BOSSES_SET], // 10
[LevelStage.DARK_ROOM_CHEST, ALL_STAGE_11_BOSSES_SET], // 11
[LevelStage.VOID, ALL_VOID_BOSSES_SET], // 12
[LevelStage.HOME, ALL_HOME_BOSSES_SET], // 13
]);
export const ALL_BOSSES: readonly BossID[] = BOSS_ID_VALUES.filter(
(bossID) => bossID !== BossID.RAGLICH,
);
export const NON_STORY_BOSSES: readonly BossID[] = ALL_BOSSES.filter(
(bossID) => !isStoryBossID(bossID),
);
export const BOSS_ID_TO_STAGE_IDS = (() => {
const partialBossIDsToStageIDs: Partial<Record<BossID, Set<StageID>>> = {};
for (const bossID of BOSS_ID_VALUES) {
const stageIDs = new Set<StageID>();
for (const [stageID, bossIDs] of STAGE_ID_TO_BOSS_IDS) {
if (bossIDs.includes(bossID)) {
stageIDs.add(stageID);
}
}
partialBossIDsToStageIDs[bossID] = stageIDs;
}
const bossIDsToStageIDs = partialBossIDsToStageIDs as Record<
BossID,
Set<StageID>
>;
// In Repentance, the following bosses will have empty sets:
// - BossID.ULTRA_GREED (62)
// - BossID.ULTRA_GREEDIER (71)
// - BossID.MAUSOLEUM_MOMS_HEART (90)
// - BossID.RAGLICH (98)
bossIDsToStageIDs[BossID.ULTRA_GREED].add(StageID.ULTRA_GREED); // 62
bossIDsToStageIDs[BossID.ULTRA_GREEDIER].add(StageID.ULTRA_GREED); // 71
bossIDsToStageIDs[BossID.MAUSOLEUM_MOMS_HEART].add(StageID.MAUSOLEUM); // 90
bossIDsToStageIDs[BossID.MAUSOLEUM_MOMS_HEART].add(StageID.GEHENNA); // 90
return bossIDsToStageIDs as Readonly<Record<BossID, ReadonlySet<StageID>>>;
})();