isaacscript-common
Version:
Helper functions and features for IsaacScript mods.
1,074 lines • 57.4 kB
JavaScript
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ModdedElementSets = void 0;
const isaac_typescript_definitions_1 = require("isaac-typescript-definitions");
const cachedEnumValues_1 = require("../../../cachedEnumValues");
const cachedClasses_1 = require("../../../core/cachedClasses");
const constants_1 = require("../../../core/constants");
const constantsVanilla_1 = require("../../../core/constantsVanilla");
const decorators_1 = require("../../../decorators");
const ISCFeature_1 = require("../../../enums/ISCFeature");
const array_1 = require("../../../functions/array");
const cards_1 = require("../../../functions/cards");
const collectibleTag_1 = require("../../../functions/collectibleTag");
const collectibles_1 = require("../../../functions/collectibles");
const flag_1 = require("../../../functions/flag");
const set_1 = require("../../../functions/set");
const trinkets_1 = require("../../../functions/trinkets");
const types_1 = require("../../../functions/types");
const utils_1 = require("../../../functions/utils");
const itemConfigCardTypesForCards_1 = require("../../../sets/itemConfigCardTypesForCards");
const ReadonlyMap_1 = require("../../../types/ReadonlyMap");
const Feature_1 = require("../../private/Feature");
const CONDITIONAL_FLYING_COLLECTIBLE_TYPES = [
isaac_typescript_definitions_1.CollectibleType.BIBLE,
isaac_typescript_definitions_1.CollectibleType.EMPTY_VESSEL,
isaac_typescript_definitions_1.CollectibleType.ASTRAL_PROJECTION,
isaac_typescript_definitions_1.CollectibleType.RECALL,
];
const TRANSFORMATION_TO_TAG_MAP = new ReadonlyMap_1.ReadonlyMap([
[isaac_typescript_definitions_1.PlayerForm.GUPPY, isaac_typescript_definitions_1.ItemConfigTag.GUPPY], // 0
[isaac_typescript_definitions_1.PlayerForm.BEELZEBUB, isaac_typescript_definitions_1.ItemConfigTag.FLY], // 1
[isaac_typescript_definitions_1.PlayerForm.FUN_GUY, isaac_typescript_definitions_1.ItemConfigTag.MUSHROOM], // 2
[isaac_typescript_definitions_1.PlayerForm.SERAPHIM, isaac_typescript_definitions_1.ItemConfigTag.ANGEL], // 3
[isaac_typescript_definitions_1.PlayerForm.BOB, isaac_typescript_definitions_1.ItemConfigTag.BOB], // 4
[isaac_typescript_definitions_1.PlayerForm.SPUN, isaac_typescript_definitions_1.ItemConfigTag.SYRINGE], // 5
[isaac_typescript_definitions_1.PlayerForm.YES_MOTHER, isaac_typescript_definitions_1.ItemConfigTag.MOM], // 6
[isaac_typescript_definitions_1.PlayerForm.CONJOINED, isaac_typescript_definitions_1.ItemConfigTag.BABY], // 7
[isaac_typescript_definitions_1.PlayerForm.LEVIATHAN, isaac_typescript_definitions_1.ItemConfigTag.DEVIL], // 8
[isaac_typescript_definitions_1.PlayerForm.OH_CRAP, isaac_typescript_definitions_1.ItemConfigTag.POOP], // 9
[isaac_typescript_definitions_1.PlayerForm.BOOKWORM, isaac_typescript_definitions_1.ItemConfigTag.BOOK], // 10
// PlayerForm.ADULTHOOD (11) is based on pill usage.
[isaac_typescript_definitions_1.PlayerForm.SPIDER_BABY, isaac_typescript_definitions_1.ItemConfigTag.SPIDER], // 12
// PlayerForm.STOMPY (13) is based on size.
]);
/**
* A feature that lazy-inits and caches various arrays and sets that include both vanilla and modded
* elements. This is useful for performance purposes (so that we do not have to reconstruct the
* arrays/sets multiple times).
*
* The modded arrays/sets are created using the functions from
* `ISCFeature.MODDED_ELEMENT_DETECTION`.
*/
class ModdedElementSets extends Feature_1.Feature {
arraysInitialized = false;
// ----------------
// Main collections
// ----------------
allCollectibleTypesArray = [];
allCollectibleTypesSet = new Set();
moddedCollectibleTypesArray = [];
moddedCollectibleTypesSet = new Set();
allTrinketTypesArray = [];
allTrinketTypesSet = new Set();
moddedTrinketTypesArray = [];
moddedTrinketTypesSet = new Set();
allCardTypesArray = [];
allCardTypesSet = new Set();
moddedCardTypesArray = [];
moddedCardTypesSet = new Set();
allPillEffectsArray = [];
allPillEffectsSet = new Set();
moddedPillEffectsArray = [];
moddedPillEffectsSet = new Set();
// -----------------
// Other collections
// -----------------
cacheFlagToCollectibleTypesMap = new Map();
cacheFlagToTrinketTypesMap = new Map();
flyingCollectibleTypes = [];
permanentFlyingCollectibleTypes = [];
flyingTrinketTypes = [];
tagToCollectibleTypesMap = new Map();
edenActiveCollectibleTypesSet = new Set();
edenPassiveCollectibleTypesSet = new Set();
qualityToCollectibleTypesMap = new Map();
itemConfigCardTypeToCardTypeMap = new Map();
/**
* The array of card types that are not:
*
* - ItemConfigCardType.RUNE
* - ItemConfigCardType.SPECIAL_OBJECT
*/
cardTypeCardArray = [];
moddedElementDetection;
/** @internal */
constructor(moddedElementDetection) {
super();
this.featuresUsed = [ISCFeature_1.ISCFeature.MODDED_ELEMENT_DETECTION];
this.moddedElementDetection = moddedElementDetection;
}
lazyInit() {
if (this.arraysInitialized) {
return;
}
this.arraysInitialized = true;
this.lazyInitModdedCollectibleTypes();
this.lazyInitModdedTrinketTypes();
this.lazyInitModdedCardTypes();
this.lazyInitModdedPillEffects();
this.lazyInitTagToCollectibleTypesMap();
this.lazyInitCacheFlagToCollectibleTypesMap();
this.lazyInitCacheFlagToTrinketTypesMap();
this.lazyInitFlyingCollectibleTypesSet();
this.lazyInitFlyingTrinketTypesSet();
this.lazyInitEdenCollectibleTypesSet();
this.lazyInitQualityToCollectibleTypesMap();
this.lazyInitCardTypes();
}
lazyInitModdedCollectibleTypes() {
for (const collectibleType of constantsVanilla_1.VANILLA_COLLECTIBLE_TYPES) {
this.allCollectibleTypesArray.push(collectibleType);
this.allCollectibleTypesSet.add(collectibleType);
}
const firstModdedCollectibleType = this.moddedElementDetection.getFirstModdedCollectibleType();
if (firstModdedCollectibleType === undefined) {
return;
}
const lastCollectibleType = this.moddedElementDetection.getLastCollectibleType();
const moddedCollectibleTypes = (0, utils_1.iRange)(firstModdedCollectibleType, lastCollectibleType);
for (const collectibleTypeInt of moddedCollectibleTypes) {
// Modded collectible types are contiguous, but we check every value just in case.
const collectibleType = (0, types_1.asCollectibleType)(collectibleTypeInt);
const itemConfigItem = cachedClasses_1.itemConfig.GetCollectible(collectibleType);
if (itemConfigItem !== undefined) {
this.moddedCollectibleTypesArray.push(collectibleType);
this.moddedCollectibleTypesSet.add(collectibleType);
this.allCollectibleTypesArray.push(collectibleType);
this.allCollectibleTypesSet.add(collectibleType);
}
}
}
lazyInitModdedTrinketTypes() {
for (const trinketType of constantsVanilla_1.VANILLA_TRINKET_TYPES) {
this.allTrinketTypesArray.push(trinketType);
this.allTrinketTypesSet.add(trinketType);
}
const firstModdedTrinketType = this.moddedElementDetection.getFirstModdedTrinketType();
if (firstModdedTrinketType === undefined) {
return;
}
const lastTrinketType = this.moddedElementDetection.getLastTrinketType();
const moddedTrinketTypes = (0, utils_1.iRange)(firstModdedTrinketType, lastTrinketType);
for (const trinketTypeInt of moddedTrinketTypes) {
// Modded trinket types are contiguous, but we check every value just in case.
const trinketType = (0, types_1.asTrinketType)(trinketTypeInt);
const itemConfigItem = cachedClasses_1.itemConfig.GetTrinket(trinketType);
if (itemConfigItem !== undefined) {
this.moddedTrinketTypesArray.push(trinketType);
this.moddedTrinketTypesSet.add(trinketType);
this.allTrinketTypesArray.push(trinketType);
this.allTrinketTypesSet.add(trinketType);
}
}
}
lazyInitModdedCardTypes() {
for (const cardType of constantsVanilla_1.VANILLA_CARD_TYPES) {
this.allCardTypesArray.push(cardType);
this.allCardTypesSet.add(cardType);
}
const firstModdedCardType = this.moddedElementDetection.getFirstModdedCardType();
if (firstModdedCardType === undefined) {
return;
}
const lastCardType = this.moddedElementDetection.getLastCardType();
const moddedCardTypes = (0, utils_1.iRange)(firstModdedCardType, lastCardType);
for (const cardTypeInt of moddedCardTypes) {
// Modded card types are contiguous, but we check every value just in case.
const cardType = (0, types_1.asCardType)(cardTypeInt);
const itemConfigCard = cachedClasses_1.itemConfig.GetCard(cardType);
if (itemConfigCard !== undefined) {
this.moddedCardTypesArray.push(cardType);
this.moddedCardTypesSet.add(cardType);
this.allCardTypesArray.push(cardType);
this.allCardTypesSet.add(cardType);
}
}
}
lazyInitModdedPillEffects() {
for (const pillEffect of constantsVanilla_1.VANILLA_PILL_EFFECTS) {
this.allPillEffectsArray.push(pillEffect);
this.allPillEffectsSet.add(pillEffect);
}
const firstModdedPillEffect = this.moddedElementDetection.getFirstModdedPillEffect();
if (firstModdedPillEffect === undefined) {
return;
}
const lastPillEffect = this.moddedElementDetection.getLastPillEffect();
const moddedPillEffects = (0, utils_1.iRange)(firstModdedPillEffect, lastPillEffect);
for (const pillEffectInt of moddedPillEffects) {
// Modded pill effects are contiguous, but we check every value just in case.
const pillEffect = (0, types_1.asPillEffect)(pillEffectInt);
const itemConfigPillEffect = cachedClasses_1.itemConfig.GetPillEffect(pillEffect);
if (itemConfigPillEffect !== undefined) {
this.moddedPillEffectsArray.push(pillEffect);
this.moddedPillEffectsSet.add(pillEffect);
this.allPillEffectsArray.push(pillEffect);
this.allPillEffectsSet.add(pillEffect);
}
}
}
lazyInitTagToCollectibleTypesMap() {
// The tag to collectible types map should be valid for every tag, so we initialize it with
// empty arrays.
for (const itemConfigTag of cachedEnumValues_1.ITEM_CONFIG_TAG_VALUES) {
this.tagToCollectibleTypesMap.set(itemConfigTag, []);
}
for (const collectibleType of this.getCollectibleTypes()) {
for (const itemConfigTag of cachedEnumValues_1.ITEM_CONFIG_TAG_VALUES) {
if (!(0, collectibleTag_1.collectibleHasTag)(collectibleType, itemConfigTag)) {
continue;
}
const collectibleTypes = this.tagToCollectibleTypesMap.get(itemConfigTag);
if (collectibleTypes === undefined) {
const flagName = (0, flag_1.getFlagName)(itemConfigTag, isaac_typescript_definitions_1.ItemConfigTag);
error(`Failed to get the collectible types for item tag: ${flagName}`);
}
collectibleTypes.push(collectibleType);
}
}
}
lazyInitCacheFlagToCollectibleTypesMap() {
for (const cacheFlag of cachedEnumValues_1.CACHE_FLAG_VALUES) {
const collectibleTypes = [];
for (const collectibleType of this.getCollectibleTypes()) {
if ((0, collectibles_1.collectibleHasCacheFlag)(collectibleType, cacheFlag)) {
collectibleTypes.push(collectibleType);
}
}
this.cacheFlagToCollectibleTypesMap.set(cacheFlag, collectibleTypes);
}
}
lazyInitCacheFlagToTrinketTypesMap() {
for (const cacheFlag of cachedEnumValues_1.CACHE_FLAG_VALUES) {
const trinketTypes = [];
for (const trinketType of this.getTrinketTypes()) {
if ((0, trinkets_1.trinketHasCacheFlag)(trinketType, cacheFlag)) {
trinketTypes.push(trinketType);
}
}
this.cacheFlagToTrinketTypesMap.set(cacheFlag, trinketTypes);
}
}
lazyInitFlyingCollectibleTypesSet() {
// Instead of manually compiling a list of collectibles that grant flying, we can instead
// dynamically look for collectibles that have `CacheFlag.FLYING`. But none of the collectibles
// with a cache of "all" grant flying (including all of the 3 Dollar Bill collectibles and all
// of the Birthright effects), so we can safely remove them from the list.
const collectibleTypesWithFlyingCacheFlag = this.getCollectibleTypesWithCacheFlag(isaac_typescript_definitions_1.CacheFlag.FLYING);
const collectibleTypesWithAllCacheFlag = this.getCollectibleTypesWithCacheFlag(isaac_typescript_definitions_1.CacheFlag.ALL);
this.flyingCollectibleTypes = (0, array_1.arrayRemove)(collectibleTypesWithFlyingCacheFlag, ...collectibleTypesWithAllCacheFlag);
// Additionally, create a second set that represents the collectible types that grant flying
// non-conditionally.
this.permanentFlyingCollectibleTypes = (0, array_1.arrayRemove)(this.flyingCollectibleTypes, ...CONDITIONAL_FLYING_COLLECTIBLE_TYPES);
}
lazyInitFlyingTrinketTypesSet() {
// Instead of manually compiling a list of trinkets that grant flying, we can instead
// dynamically look for collectibles that have `CacheFlag.FLYING`. But none of the trinkets with
// `CacheFlag.ALL` grant flying except for Azazel's Stump, so we can safely remove them from the
// list.
const trinketTypesWithFlyingCacheFlag = this.getTrinketsTypesWithCacheFlag(isaac_typescript_definitions_1.CacheFlag.FLYING);
const trinketTypesWithAllCacheFlag = this.getTrinketsTypesWithCacheFlag(isaac_typescript_definitions_1.CacheFlag.ALL);
const trinketTypesWithAllCacheFlagThatDontGrantFlying = (0, array_1.arrayRemove)(trinketTypesWithAllCacheFlag, isaac_typescript_definitions_1.TrinketType.AZAZELS_STUMP);
this.flyingTrinketTypes = (0, array_1.arrayRemove)(trinketTypesWithFlyingCacheFlag, ...trinketTypesWithAllCacheFlagThatDontGrantFlying);
}
lazyInitEdenCollectibleTypesSet() {
for (const collectibleType of this.getCollectibleTypes()) {
if ((0, collectibles_1.isHiddenCollectible)(collectibleType)
|| (0, collectibleTag_1.collectibleHasTag)(collectibleType, isaac_typescript_definitions_1.ItemConfigTag.NO_EDEN)) {
continue;
}
if ((0, collectibles_1.isActiveCollectible)(collectibleType)) {
this.edenActiveCollectibleTypesSet.add(collectibleType);
}
if ((0, collectibles_1.isPassiveOrFamiliarCollectible)(collectibleType)) {
this.edenPassiveCollectibleTypesSet.add(collectibleType);
}
}
}
lazyInitQualityToCollectibleTypesMap() {
for (const quality of constants_1.QUALITIES) {
const collectibleTypes = [];
for (const collectibleType of this.getCollectibleTypes()) {
const collectibleTypeQuality = (0, collectibles_1.getCollectibleQuality)(collectibleType);
if (collectibleTypeQuality === quality) {
collectibleTypes.push(collectibleType);
}
}
this.qualityToCollectibleTypesMap.set(quality, collectibleTypes);
}
}
lazyInitCardTypes() {
// The card type to cards map should be valid for every card type, so we initialize it with
// empty arrays.
for (const itemConfigCardType of cachedEnumValues_1.ITEM_CONFIG_CARD_TYPE_VALUES) {
this.itemConfigCardTypeToCardTypeMap.set(itemConfigCardType, []);
}
for (const cardType of this.getCardTypes()) {
const itemConfigCardType = (0, cards_1.getItemConfigCardType)(cardType);
if (itemConfigCardType !== undefined) {
const cardTypes = this.itemConfigCardTypeToCardTypeMap.get(itemConfigCardType);
(0, utils_1.assertDefined)(cardTypes, `Failed to get the card types for item config card type: ${itemConfigCardType}`);
cardTypes.push(cardType);
if (itemConfigCardTypesForCards_1.ITEM_CONFIG_CARD_TYPES_FOR_CARDS.has(itemConfigCardType)) {
this.cardTypeCardArray.push(cardType);
}
}
}
}
// ------------
// Collectibles
// ------------
/**
* Returns an array containing every valid collectible type in the game, including modded
* collectibles.
*
* Use this if you need to iterate over the collectibles in order. If you need to do O(1) lookups,
* then use the `getCollectibleTypesSet` helper function instead.
*
* This function can only be called if at least one callback has been executed. This is because
* not all collectible types will necessarily be present when a mod first loads (due to mod load
* order).
*
* In order to use this function, you must upgrade your mod with `ISCFeature.MODDED_ELEMENT_SETS`.
*
* @public
*/
getCollectibleTypes() {
this.lazyInit();
return this.allCollectibleTypesArray;
}
/**
* Returns a set containing every valid collectible type in the game, including modded
* collectibles.
*
* Use this if you need to do O(1) lookups. If you need to iterate over the collectibles in order,
* then use the `getCollectibleTypes` helper function instead.
*
* This function can only be called if at least one callback has been executed. This is because
* not all collectible types will necessarily be present when a mod first loads (due to mod load
* order).
*
* In order to use this function, you must upgrade your mod with `ISCFeature.MODDED_ELEMENT_SETS`.
*
* @public
*/
getCollectibleTypeSet() {
this.lazyInit();
return this.allCollectibleTypesSet;
}
/**
* Returns an array containing every modded collectible type in the game.
*
* Use this if you need to iterate over the collectibles in order. If you need to do O(1) lookups,
* then use the `getModdedCollectibleTypesSet` helper function instead.
*
* This function can only be called if at least one callback has been executed. This is because
* not all collectible types will necessarily be present when a mod first loads (due to mod load
* order).
*
* In order to use this function, you must upgrade your mod with `ISCFeature.MODDED_ELEMENT_SETS`.
*
* @public
*/
getModdedCollectibleTypes() {
this.lazyInit();
return this.moddedCollectibleTypesArray;
}
/**
* Returns a set containing every modded collectible type in the game.
*
* Use this if you need to do O(1) lookups. If you need to iterate over the collectibles in order,
* then use the `getModdedCollectibleTypes` helper function instead.
*
* This function can only be called if at least one callback has been executed. This is because
* not all collectible types will necessarily be present when a mod first loads (due to mod load
* order).
*
* In order to use this function, you must upgrade your mod with `ISCFeature.MODDED_ELEMENT_SETS`.
*
* @public
*/
getModdedCollectibleTypesSet() {
this.lazyInit();
return this.moddedCollectibleTypesSet;
}
/**
* Iterates over every collectible in the game and returns a map containing the number of each
* item that the player has.
*
* Note that this will filter out non-real collectibles like Lilith's Incubus.
*
* This function can only be called if at least one callback has been executed. This is because
* not all collectible types will necessarily be present when a mod first loads (due to mod load
* order).
*
* In order to use this function, you must upgrade your mod with `ISCFeature.MODDED_ELEMENT_SETS`.
*
* @public
*/
getPlayerCollectibleMap(player) {
const collectibleArray = this.getCollectibleTypes();
const collectibleMap = new Map();
for (const collectibleType of collectibleArray) {
// We specify "true" as the second argument to filter out things like Lilith's Incubus.
const numCollectibles = player.GetCollectibleNum(collectibleType, true);
if (numCollectibles > 0) {
collectibleMap.set(collectibleType, numCollectibles);
}
}
// If the player has TMTRAINER, they might also have glitched items.
if (player.HasCollectible(isaac_typescript_definitions_1.CollectibleType.TMTRAINER)) {
let collectibleType = constants_1.FIRST_GLITCHED_COLLECTIBLE_TYPE;
let itemConfigItem;
do {
itemConfigItem = cachedClasses_1.itemConfig.GetCollectible(collectibleType);
if (itemConfigItem !== undefined) {
// The `EntityPlayer.GetCollectibleNum` method is bugged with TMTrainer items and will
// always return 0. To work around this, we simply assume that if the player has the
// collectible, then they have one copy of the item.
const hasCollectibles = player.HasCollectible(collectibleType, true);
if (hasCollectibles) {
collectibleMap.set(collectibleType, 1);
}
}
collectibleType--; // eslint-disable-line complete/strict-enums
} while (itemConfigItem !== undefined);
}
return collectibleMap;
}
// --------
// Trinkets
// --------
/**
* Returns an array containing every modded trinket type in the game.
*
* Use this if you need to iterate over the trinkets in order. If you need to do O(1) lookups,
* then use the `getModdedTrinketTypesSet` helper function instead.
*
* This function can only be called if at least one callback has been executed. This is because
* not all trinket types will necessarily be present when a mod first loads (due to mod load
* order).
*
* In order to use this function, you must upgrade your mod with `ISCFeature.MODDED_ELEMENT_SETS`.
*
* @public
*/
getTrinketTypes() {
this.lazyInit();
return this.allTrinketTypesArray;
}
/**
* Returns a set containing every modded trinket type in the game.
*
* Use this if you need to do O(1) lookups. If you need to iterate over the trinkets in order,
* then use the `getModdedTrinketTypes` helper function instead.
*
* This function can only be called if at least one callback has been executed. This is because
* not all trinket types will necessarily be present when a mod first loads (due to mod load
* order).
*
* In order to use this function, you must upgrade your mod with `ISCFeature.MODDED_ELEMENT_SETS`.
*
* @public
*/
getTrinketTypesSet() {
this.lazyInit();
return this.allTrinketTypesSet;
}
/**
* Returns an array containing every modded trinket type in the game.
*
* Use this if you need to iterate over the trinkets in order. If you need to do O(1) lookups,
* then use the `getModdedTrinketTypesSet` helper function instead.
*
* This function can only be called if at least one callback has been executed. This is because
* not all trinket types will necessarily be present when a mod first loads (due to mod load
* order).
*
* In order to use this function, you must upgrade your mod with `ISCFeature.MODDED_ELEMENT_SETS`.
*
* @public
*/
getModdedTrinketTypes() {
this.lazyInit();
return this.moddedTrinketTypesArray;
}
/**
* Returns a set containing every modded trinket type in the game.
*
* Use this if you need to do O(1) lookups. If you need to iterate over the trinkets in order,
* then use the `getModdedTrinketTypes` helper function instead.
*
* This function can only be called if at least one callback has been executed. This is because
* not all trinket types will necessarily be present when a mod first loads (due to mod load
* order).
*
* In order to use this function, you must upgrade your mod with `ISCFeature.MODDED_ELEMENT_SETS`.
*
* @public
*/
getModdedTrinketTypesSet() {
this.lazyInit();
return this.moddedTrinketTypesSet;
}
// -----
// Cards
// -----
/**
* Returns an array containing every valid card type in the game, including modded cards.
*
* Use this if you need to iterate over the cards in order. If you need to do O(1) lookups, then
* use the `getCardTypesSet` helper function instead.
*
* This function can only be called if at least one callback has been executed. This is because
* not all card types will necessarily be present when a mod first loads (due to mod load order).
*
* In order to use this function, you must upgrade your mod with `ISCFeature.MODDED_ELEMENT_SETS`.
*
* @public
*/
getCardTypes() {
this.lazyInit();
return this.allCardTypesArray;
}
/**
* Returns a set containing every valid card type in the game, including modded cards.
*
* Use this if you need to do O(1) lookups. If you need to iterate over the cards in order, then
* use the `getCardTypes` helper function instead.
*
* This function can only be called if at least one callback has been executed. This is because
* not all card types will necessarily be present when a mod first loads (due to mod load order).
*
* In order to use this function, you must upgrade your mod with `ISCFeature.MODDED_ELEMENT_SETS`.
*
* @public
*/
getCardTypesSet() {
this.lazyInit();
return this.allCardTypesSet;
}
/**
* Returns an array containing every modded card type in the game.
*
* Use this if you need to iterate over the cards in order. If you need to do O(1) lookups, then
* use the `getModdedCardTypesSet` helper function instead.
*
* This function can only be called if at least one callback has been executed. This is because
* not all card types will necessarily be present when a mod first loads (due to mod load order).
*
* In order to use this function, you must upgrade your mod with `ISCFeature.MODDED_ELEMENT_SETS`.
*
* @public
*/
getModdedCardTypes() {
this.lazyInit();
return this.moddedCardTypesArray;
}
/**
* Returns a set containing every modded card type in the game.
*
* Use this if you need to do O(1) lookups. If you need to iterate over the cards in order, then
* use the `getModdedCardTypes` helper function instead.
*
* This function can only be called if at least one callback has been executed. This is because
* not all card types will necessarily be present when a mod first loads (due to mod load order).
*
* In order to use this function, you must upgrade your mod with `ISCFeature.MODDED_ELEMENT_SETS`.
*
* @public
*/
getModdedCardTypesSet() {
this.lazyInit();
return this.moddedCardTypesSet;
}
// ------------
// Pill Effects
// ------------
/**
* Returns an array containing every valid pill effect in the game, including modded pill effects.
*
* Use this if you need to iterate over the pill effects in order. If you need to do O(1) lookups,
* then use the `getPillEffectSet` helper function instead.
*
* This function can only be called if at least one callback has been executed. This is because
* not all pill effects will necessarily be present when a mod first loads (due to mod load
* order).
*
* In order to use this function, you must upgrade your mod with `ISCFeature.MODDED_ELEMENT_SETS`.
*
* @public
*/
getPillEffects() {
this.lazyInit();
return this.allPillEffectsArray;
}
/**
* Returns a set containing every valid pill effect in the game, including modded pill effects.
*
* Use this if you need to do O(1) lookups. If you need to iterate over the pill effects in order,
* then use the `getPillEffects` helper function instead.
*
* This function can only be called if at least one callback has been executed. This is because
* not all pill effects will necessarily be present when a mod first loads (due to mod load
* order).
*
* In order to use this function, you must upgrade your mod with `ISCFeature.MODDED_ELEMENT_SETS`.
*
* @public
*/
getPillEffectsSet() {
this.lazyInit();
return this.allPillEffectsSet;
}
/**
* Returns an array containing every modded pill effect in the game.
*
* Use this if you need to iterate over the pill effects in order. If you need to do O(1) lookups,
* then use the `getModdedPillEffectsSet` helper function instead.
*
* This function can only be called if at least one callback has been executed. This is because
* not all pill effects will necessarily be present when a mod first loads (due to mod load
* order).
*
* In order to use this function, you must upgrade your mod with `ISCFeature.MODDED_ELEMENT_SETS`.
*
* @public
*/
getModdedPillEffects() {
this.lazyInit();
return this.moddedPillEffectsArray;
}
/**
* Returns a set containing every modded pill effect in the game.
*
* Use this if you need to do O(1) lookups. If you need to iterate over the pill effects in order,
* then use the `getModdedPillEffects` helper function instead.
*
* This function can only be called if at least one callback has been executed. This is because
* not all pill effects will necessarily be present when a mod first loads (due to mod load
* order).
*
* In order to use this function, you must upgrade your mod with `ISCFeature.MODDED_ELEMENT_SETS`.
*
* @public
*/
getModdedPillEffectsSet() {
this.lazyInit();
return this.moddedPillEffectsSet;
}
// -----------
// Cache Flags
// -----------
/**
* Returns a set containing every collectible type with the given cache flag, including modded
* collectibles.
*
* This function can only be called if at least one callback has been executed. This is because
* not all collectible types will necessarily be present when a mod first loads (due to mod load
* order).
*
* In order to use this function, you must upgrade your mod with `ISCFeature.MODDED_ELEMENT_SETS`.
*
* @public
*/
getCollectibleTypesWithCacheFlag(cacheFlag) {
this.lazyInit();
const collectiblesSet = this.cacheFlagToCollectibleTypesMap.get(cacheFlag);
if (collectiblesSet === undefined) {
return [];
}
return collectiblesSet;
}
/**
* Returns a set containing every trinket type with the given cache flag, including modded
* trinkets.
*
* This function can only be called if at least one callback has been executed. This is because
* not all trinket types will necessarily be present when a mod first loads (due to mod load
* order).
*
* In order to use this function, you must upgrade your mod with `ISCFeature.MODDED_ELEMENT_SETS`.
*
* @public
*/
getTrinketsTypesWithCacheFlag(cacheFlag) {
this.lazyInit();
const trinketTypes = this.cacheFlagToTrinketTypesMap.get(cacheFlag);
if (trinketTypes === undefined) {
return [];
}
return trinketTypes;
}
/**
* Returns an array containing every collectible type that the player has that matches the
* provided `CacheFlag`.
*
* For example, if the cache flag is `CacheFlag.FLYING`, and the player has one Lord of the Pit
* and two Dead Doves, then this function would return:
*
* ```ts
* [
* CollectibleType.LORD_OF_THE_PIT,
* CollectibleType.DEAD_DOVE,
* CollectibleType.DEAD_DOVE,
* ]
* ```
*
* Note that this array will not include collectibles that the player does not really have, like
* Lilith's Incubus.
*
* This function can only be called if at least one callback has been executed. This is because
* not all collectible types will necessarily be present when a mod first loads (due to mod load
* order).
*
* In order to use this function, you must upgrade your mod with `ISCFeature.MODDED_ELEMENT_SETS`.
*
* @public
*/
getPlayerCollectiblesWithCacheFlag(player, cacheFlag) {
const collectiblesWithCacheFlag = this.getCollectibleTypesWithCacheFlag(cacheFlag);
const playerCollectibles = [];
for (const collectibleType of collectiblesWithCacheFlag) {
// We specify "true" as the second argument to filter out things like Lilith's Incubus.
const numCollectibles = player.GetCollectibleNum(collectibleType, true);
(0, utils_1.repeat)(numCollectibles, () => {
playerCollectibles.push(collectibleType);
});
}
return playerCollectibles;
}
/**
* Returns a map containing every trinket type that the player has that matches the provided
* `CacheFlag`. The values of the map correspond to the multiplier for that trinket.
*
* This function can only be called if at least one callback has been executed. This is because
* not all trinket types will necessarily be present when a mod first loads (due to mod load
* order).
*
* In order to use this function, you must upgrade your mod with `ISCFeature.MODDED_ELEMENT_SETS`.
*
* @public
*/
getPlayerTrinketsWithCacheFlag(player, cacheFlag) {
const trinketTypesWithCacheFlag = this.getTrinketsTypesWithCacheFlag(cacheFlag);
const playerTrinkets = new Map();
for (const trinketType of trinketTypesWithCacheFlag) {
const trinketMultiplier = player.GetTrinketMultiplier(trinketType);
if (trinketMultiplier > 0) {
playerTrinkets.set(trinketType, trinketMultiplier);
}
}
return playerTrinkets;
}
/**
* Returns a set of all of the collectibles that grant flight. This is derived from collectibles
* that have `CacheFlag.FLYING` set in the "items.xml" file.
*
* Vanilla collectibles that only grant flight conditionally are manually pruned. Collectibles
* such as Empty Vessel should be checked for via the `hasFlyingTemporaryEffect` function.
*
* Under the hood, this is determined by looking at the collectibles that have `CacheFlag.FLYING`
* and excluding the ones that have `CacheFlag.ALL`. (None of the collectibles with
* `CacheFlag.ALL` grant flying, including all of the 3 Dollar Bill collectibles and all of the
* Birthright effects.)
*
* This function can only be called if at least one callback has been executed. This is because
* not all collectible types will necessarily be present when a mod first loads (due to mod load
* order).
*
* In order to use this function, you must upgrade your mod with `ISCFeature.MODDED_ELEMENT_SETS`.
*
* @param includeConditionalItems Whether collectibles that only grant flight conditionally should
* be included in the set (like Empty Vessel).
* @public
*/
getFlyingCollectibleTypes(includeConditionalItems) {
this.lazyInit();
return includeConditionalItems
? this.flyingCollectibleTypes
: this.permanentFlyingCollectibleTypes;
}
/**
* Returns a set of all of the trinkets that grant flight. (All vanilla trinkets that grant flight
* do so conditionally, like Bat Wing and Azazel's Stump.)
*
* Under the hood, this is determined by looking at the trinkets that have `CacheFlag.FLYING` and
* excluding the ones that have `CacheFlag.ALL`. (None of the trinket with `CacheFlag.ALL` grant
* flying except for Azazel's Stump.)
*
* This function can only be called if at least one callback has been executed. This is because
* not all trinket types will necessarily be present when a mod first loads (due to mod load
* order).
*
* In order to use this function, you must upgrade your mod with `ISCFeature.MODDED_ELEMENT_SETS`.
*
* @public
*/
getFlyingTrinketTypes() {
this.lazyInit();
return this.flyingTrinketTypes;
}
// ----------------
// Collectible Tags
// ----------------
/**
* Returns a set containing every collectible type with the given tag.
*
* For example, to get all of the collectible types that count as offensive for the purposes of
* Tainted Lost:
*
* ```ts
* const offensiveCollectibleTypes = getCollectibleTypesWithTag(ItemConfigTag.OFFENSIVE);
* ```
*
* This function can only be called if at least one callback has been executed. This is because
* not all collectible types will necessarily be present when a mod first loads (due to mod load
* order).
*
* In order to use this function, you must upgrade your mod with `ISCFeature.MODDED_ELEMENT_SETS`.
*
* @public
*/
getCollectibleTypesWithTag(itemConfigTag) {
this.lazyInit();
const collectibleTypes = this.tagToCollectibleTypesMap.get(itemConfigTag);
(0, utils_1.assertDefined)(collectibleTypes, `The item config tag of ${itemConfigTag} is not a valid value of the "ItemConfigTag" enum.`);
return collectibleTypes;
}
/**
* Returns an array of collectible types that a player has with a particular tag.
*
* This function can only be called if at least one callback has been executed. This is because
* not all collectible types will necessarily be present when a mod first loads (due to mod load
* order).
*
* In order to use this function, you must upgrade your mod with `ISCFeature.MODDED_ELEMENT_SETS`.
*
* @public
*/
getPlayerCollectiblesWithTag(player, itemConfigTag) {
const collectibleTypesWithTag = this.getCollectibleTypesWithTag(itemConfigTag);
const playerCollectibles = [];
for (const collectibleType of collectibleTypesWithTag) {
// We specify "true" as the second argument to filter out things like Lilith's Incubus.
const numCollectibles = player.GetCollectibleNum(collectibleType, true);
(0, utils_1.repeat)(numCollectibles, () => {
playerCollectibles.push(collectibleType);
});
}
return playerCollectibles;
}
/**
* Helper function to get all of the collectible types in the game that count towards a particular
* transformation.
*
* For example, to get all of the collectible types that count towards Guppy:
*
* ```ts
* const guppyCollectibleTypes = getCollectiblesForTransformation(PlayerForm.GUPPY);
* ```
*
* This function can only be called if at least one callback has been executed. This is because
* not all collectible types will necessarily be present when a mod first loads (due to mod load
* order).
*
* In order to use this function, you must upgrade your mod with `ISCFeature.MODDED_ELEMENT_SETS`.
*
* @public
*/
getCollectibleTypesForTransformation(playerForm) {
const itemConfigTag = TRANSFORMATION_TO_TAG_MAP.get(playerForm);
(0, utils_1.assertDefined)(itemConfigTag, `Failed to get the collectible types for the transformation of ${playerForm} because that transformation is not based on collectibles.`);
return this.getCollectibleTypesWithTag(itemConfigTag);
}
/**
* Returns an array of collectible types that a player has that count towards a particular
* transformation.
*
* This function can only be called if at least one callback has been executed. This is because
* not all collectible types will necessarily be present when a mod first loads (due to mod load
* order).
*
* In order to use this function, you must upgrade your mod with `ISCFeature.MODDED_ELEMENT_SETS`.
*
* @public
*/
getPlayerCollectiblesForTransformation(player, playerForm) {
const collectibleForTransformation = this.getCollectibleTypesForTransformation(playerForm);
const playerCollectibles = [];
for (const collectibleType of collectibleForTransformation) {
// We specify "true" as the second argument to filter out things like Lilith's Incubus.
const numCollectibles = player.GetCollectibleNum(collectibleType, true);
(0, utils_1.repeat)(numCollectibles, () => {
playerCollectibles.push(collectibleType);
});
}
return playerCollectibles;
}
/**
* Returns a set containing every valid passive item that can be randomly granted to Eden as a
* starting item.
*
* Under the hood, this is determined by looking at the "noeden" tag in "items_metadata.xml".
*
* This function can only be called if at least one callback has been executed. This is because
* not all collectible types will necessarily be present when a mod first loads (due to mod load
* order).
*
* In order to use this function, you must upgrade your mod with `ISCFeature.MODDED_ELEMENT_SETS`.
*
* @public
*/
getEdenActiveCollectibleTypes() {
this.lazyInit();
return this.edenActiveCollectibleTypesSet;
}
/**
* Returns a set containing every valid passive item that can be randomly granted to Eden as a
* starting item.
*
* Under the hood, this is determined by looking at the "noeden" tag in "items_metadata.xml".
*
* This function can only be called if at least one callback has been executed. This is because
* not all collectible types will necessarily be present when a mod first loads (due to mod load
* order).
*
* In order to use this function, you must upgrade your mod with `ISCFeature.MODDED_ELEMENT_SETS`.
*
* @public
*/
getEdenPassiveCollectibleTypes() {
this.lazyInit();
return this.edenPassiveCollectibleTypesSet;
}
/**
* Returns a random active collectible type that that is a valid starting item for Eden.
*
* This function can only be called if at least one callback has been executed. This is because
* not all collectible types will necessarily be present when a mod first loads (due to mod load
* order).
*
* If you want to get an unseeded collectible type, you must explicitly pass `undefined` to the
* `seedOrRNG` parameter.
*
* In order to use this function, you must upgrade your mod with `ISCFeature.MODDED_ELEMENT_SETS`.
*
* @param seedOrRNG The `Seed` or `RNG` object to use. If an `RNG` object is provided, the
* `RNG.Next` method will be called. If `undefined` is provided, it will default
* to a random seed.
* @param exceptions Optional. An array of runes to not select.
* @public
*/
getRandomEdenActiveCollectibleType(seedOrRNG, exceptions = []) {
this.lazyInit();
return (0, set_1.getRandomSetElement)(this.edenPassiveCollectibleTypesSet, seedOrRNG, exceptions);
}
/**
* Returns a random passive collectible type that that is a valid starting item for Eden
* (including familiars).
*
* This function can only be called if at least one callback has been executed. This is because
* not all collectible types will necessarily be present when a mod first loads (due to mod load
* order).
*
* If you want to get an unseeded collectible type, you must explicitly pass `undefined` to the
* `seedOrRNG` parameter.
*
* In order to use this function, you must upgrade your mod with `ISCFeature.MODDED_ELEMENT_SETS`.
*
* @param seedOrRNG The `Seed` or `RNG` object to use. If an `RNG` object is provided, the
* `RNG.Next` method will be called. If `undefined` is provided, it will default
* to a random seed.
* @param exceptions Optional. An array of runes to not select.
* @public
*/
getRandomEdenPassiveCollectibleType(seedOrRNG, exceptions = []) {
this.lazyInit();
return (0, set_1.getRandomSetElement)(this.edenPassiveCollectibleTypesSet, seedOrRNG, exceptions);
}
// -------------------
// Collectible Quality
// -------------------
/**
* Returns an array containing every collectible type with the given quality.
*
* This function can only be called if at least one callback has been executed. This is because
* not all collectible types will necessarily be present when a mod first loads (due to mod load
* order).
*
* In order to use this function, you must upgrade your mod with `ISCFeature.MODDED_ELEMENT_SETS`.
*
* @public
*/
getCollectibleTypesOfQuality(quality) {
this.lazyInit();
const collectibleTypes = this.qualityToCollectibleTypesMap.get(quality);
(0, utils_1.assertDefined)(collectibleTypes, `The quality of ${quality} is not a valid quality.`);
return collectibleTypes;
}
/**
* Returns an array of collectible types that a player has that are of a particular quality.
*
* This function can only be called if at least one callback has been executed. This is because
* not all collectible types will necessarily be present when a mod first loads (due to mod load
* order).
*
* In order to use this function, you must upgrade your mod with `ISCFeature.MODDED_ELEMENT_SETS`.
*
* @public
*/
getPlayerCollectiblesOfQuality(player, quality) {
const collectibleTypesOfQuality = this.getCollectibleTypesOfQuality(quality);
const playerCollectibleTypes = [];
for (const collectibleType of collectibleTypesOfQuality) {
// We specify "true" as the second argument to filter out things like Lilith's Incubus.
const numCollectibles = player.GetCollectibleNum(collectibleType, true);
(0, utils_1.repeat)(numCollectibles, () => {
playerCollectibleTypes.push(collectibleType);
});
}
return playerCollectibleTypes;
}
// ----------------------
// Item Config Card Types
// ----------------------
/**
* Helper function to get an array of card types matching the `ItemConfigCardType`.
*
* This function is variadic, meaning that you can you can specify N card types to get an array
* containing cards that match any of the specified types.
*
* This function can only be called if at least one callback has been executed. This is because
* not all card types will necessarily be present when a mod first loads (due to mod load order).
*
* In order to use this function, you must upgrade your mod with `ISCFeature.MODDED_ELEMENT_SETS`.
*
* @public
*/
getCardTypesOfType(...itemConfigCardTypes) {
this.lazyInit();
const matchingCardTypes = [];
for (const itemConfigCardType of itemConfigCardTypes) {
const cardTypes = this.itemConfigCardTypeToCardTypeMap.get(itemConfigCardType);
(0, utils_1.assertDefined)(cardTypes, `Failed to get the card types for item config type: ${itemConfigCardType}`);
for (const cardType of cardTypes) {
matchingCardTypes.push(cardType);
}
}
return matchingCardTypes;
}
/**
* Helper function to get a random card type that matches the provided `ItemConfigCardType`.
*
* This function can only be called if at least one callback has been executed. This is because
* not all card types will necessarily be present when a mod first loads (due to mod load order).
*
* If you want to get an