"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.setCollectiblesRerolledForItemTracker = exports.setCollectibleSubType = exports.setCollectibleSprite = exports.setCollectiblePedestalType = exports.setCollectibleGlitched = exports.setCollectibleEmpty = exports.setCollectibleBlind = exports.removeCollectiblePickupDelay = exports.preventCollectibleRotation = exports.newCollectibleSprite = exports.isVanillaCollectibleType = exports.isValidCollectibleType = exports.isSingleUseCollectible = exports.isQuality = exports.isPassiveOrFamiliarCollectible = exports.isModdedCollectibleType = exports.isHiddenCollectible = exports.isGlitchedCollectible = exports.isFamiliarCollectible = exports.isBlindCollectible = exports.isActiveCollectible = exports.getVanillaCollectibleTypesOfQuality = exports.getCollectibleTags = exports.getCollectibleQuality = exports.getCollectiblePedestalType = exports.getCollectibleName = exports.getCollectibleMaxCharges = exports.getCollectibleItemType = exports.getCollectibleInitCharge = exports.getCollectibleGfxFilename = exports.getCollectibleDevilHeartPrice = exports.getCollectibleDevilCoinPrice = exports.getCollectibleDescription = exports.getCollectibleChargeType = exports.collectibleSpriteEquals = exports.collectibleHasCacheFlag = exports.clearCollectibleSprite = void 0;
const isaac_typescript_definitions_1 = require("isaac-typescript-definitions");
const cachedClasses_1 = require("../core/cachedClasses");
const constants_1 = require("../core/constants");
const constantsFirstLast_1 = require("../core/constantsFirstLast");
const constantsVanilla_1 = require("../core/constantsVanilla");
const collectibleDescriptions_1 = require("../objects/collectibleDescriptions");
const collectibleNames_1 = require("../objects/collectibleNames");
const singleUseActiveCollectibleTypesSet_1 = require("../sets/singleUseActiveCollectibleTypesSet");
const entities_1 = require("./entities");
const flag_1 = require("./flag");
const pickupVariants_1 = require("./pickupVariants");
const sprites_1 = require("./sprites");
const types_1 = require("./types");
const utils_1 = require("./utils");
const COLLECTIBLE_ANM2_PATH = "gfx/005.100_collectible.anm2";
const DEFAULT_COLLECTIBLE_PRICE = 15;
/** Glitched items start at id 4294967295 (the final 32-bit integer) and increment backwards. */
const GLITCHED_ITEM_THRESHOLD = 4_000_000_000;
const QUALITY_TO_VANILLA_COLLECTIBLE_TYPES_MAP = (() => {
    const qualityToCollectibleTypesMap = new Map();
    for (const quality of constants_1.QUALITIES) {
        const collectibleTypes = [];
        for (const collectibleType of constantsVanilla_1.VANILLA_COLLECTIBLE_TYPES) {
            const collectibleTypeQuality = getCollectibleQuality(collectibleType);
            if (collectibleTypeQuality === quality) {
                collectibleTypes.push(collectibleType);
            }
        }
        qualityToCollectibleTypesMap.set(quality, collectibleTypes);
    }
    return qualityToCollectibleTypesMap;
})();
/** The `isBlindCollectible` function needs a reference sprite to work properly. */
const questionMarkSprite = (() => {
    const sprite = Sprite();
    sprite.Load("gfx/005.100_collectible.anm2", false);
    sprite.ReplaceSpritesheet(1, "gfx/items/collectibles/questionmark.png");
    sprite.LoadGraphics();
    return sprite;
})();
function clearCollectibleSprite(collectible) {
    if (!(0, pickupVariants_1.isCollectible)(collectible)) {
        const entityID = (0, entities_1.getEntityID)(collectible);
        error(`The "clearCollectibleSprite" function was given a non-collectible: ${entityID}`);
    }
    setCollectibleSprite(collectible, undefined);
}
exports.clearCollectibleSprite = clearCollectibleSprite;
/** Helper function to check in the item config if a given collectible has a given cache flag. */
function collectibleHasCacheFlag(collectibleOrCollectibleType, cacheFlag) {
    const collectibleType = getCollectibleTypeFromArg(collectibleOrCollectibleType, "collectibleHasCacheFlag");
    const itemConfigItem = cachedClasses_1.itemConfig.GetCollectible(collectibleType);
    if (itemConfigItem === undefined) {
        return false;
    }
    return (0, flag_1.hasFlag)(itemConfigItem.CacheFlags, cacheFlag);
}
exports.collectibleHasCacheFlag = collectibleHasCacheFlag;
/** Helper function to check if two collectible sprites have the same sprite sheet loaded. */
function collectibleSpriteEquals(sprite1, sprite2) {
    // We start at negative 40 texels upwards, as by default we assume a collectible that is sitting
    // on a pedestal. We finish at positive 10 texels downwards, in order to make comparing shop items
    // more accurate.
    const xStart = -1;
    const xFinish = 1;
    const xIncrement = 1;
    const yStart = -40;
    const yFinish = 10;
    const yIncrement = 3;
    return (0, sprites_1.spriteEquals)(sprite1, sprite2, isaac_typescript_definitions_1.CollectibleSpriteLayer.HEAD, xStart, xFinish, xIncrement, yStart, yFinish, yIncrement);
}
exports.collectibleSpriteEquals = collectibleSpriteEquals;
/**
 * Helper function to get the charge type that a collectible has. Returns
 * `ItemConfigChargeType.NORMAL` if the provided collectible type was not valid.
 */
function getCollectibleChargeType(collectibleOrCollectibleType) {
    const collectibleType = getCollectibleTypeFromArg(collectibleOrCollectibleType, "getCollectibleChargeType");
    const itemConfigItem = cachedClasses_1.itemConfig.GetCollectible(collectibleType);
    if (itemConfigItem === undefined) {
        return isaac_typescript_definitions_1.ItemConfigChargeType.NORMAL;
    }
    return itemConfigItem.ChargeType;
}
exports.getCollectibleChargeType = getCollectibleChargeType;
/**
 * Helper function to get the in-game description for a collectible. Returns "Unknown" if the
 * provided collectible type was not valid.
 *
 * This function works for both vanilla and modded collectibles.
 */
function getCollectibleDescription(collectibleOrCollectibleType) {
    const collectibleType = getCollectibleTypeFromArg(collectibleOrCollectibleType, "getCollectibleDescription");
    // "ItemConfigItem.Description" is bugged with vanilla items on patch v1.7.6, so we use a
    // hard-coded map as a workaround.
    const collectibleDescription = collectibleDescriptions_1.COLLECTIBLE_DESCRIPTIONS[collectibleType];
    if (collectibleDescription !== undefined) {
        return collectibleDescription;
    }
    const itemConfigItem = cachedClasses_1.itemConfig.GetCollectible(collectibleType);
    if (itemConfigItem !== undefined) {
        return itemConfigItem.Description;
    }
    return collectibleDescriptions_1.DEFAULT_COLLECTIBLE_DESCRIPTION;
}
exports.getCollectibleDescription = getCollectibleDescription;
/**
 * Helper function to get the coin cost that a collectible item would be if it were being offered in
 * a Devil Room deal. Returns 0 if passed `CollectibleType.NULL`.
 */
function getCollectibleDevilCoinPrice(collectibleOrCollectibleType) {
    const collectibleType = getCollectibleTypeFromArg(collectibleOrCollectibleType, "getCollectibleDescription");
    if (collectibleType === isaac_typescript_definitions_1.CollectibleType.NULL) {
        return 0;
    }
    const itemConfigItem = cachedClasses_1.itemConfig.GetCollectible(collectibleType);
    if (itemConfigItem === undefined) {
        return DEFAULT_COLLECTIBLE_PRICE;
    }
    return itemConfigItem.DevilPrice * DEFAULT_COLLECTIBLE_PRICE;
}
exports.getCollectibleDevilCoinPrice = getCollectibleDevilCoinPrice;
/**
 * Helper function to get the heart cost that a collectible item would be if it were being offered
 * in a Devil Room deal. Returns 0 if passed `CollectibleType.NULL`.
 */
function getCollectibleDevilHeartPrice(collectibleOrCollectibleType, player) {
    const collectibleType = getCollectibleTypeFromArg(collectibleOrCollectibleType, "getCollectibleDevilHeartPrice");
    const maxHearts = player.GetMaxHearts();
    if (collectibleType === isaac_typescript_definitions_1.CollectibleType.NULL) {
        return 0;
    }
    if (maxHearts === 0) {
        return isaac_typescript_definitions_1.PickupPrice.THREE_SOUL_HEARTS;
    }
    const defaultCollectiblePrice = isaac_typescript_definitions_1.PickupPrice.ONE_HEART;
    const itemConfigItem = cachedClasses_1.itemConfig.GetCollectible(collectibleType);
    if (itemConfigItem === undefined) {
        return defaultCollectiblePrice;
    }
    const twoHeartPrice = maxHearts === 2
        ? isaac_typescript_definitions_1.PickupPrice.ONE_HEART_AND_TWO_SOUL_HEARTS
        : isaac_typescript_definitions_1.PickupPrice.TWO_HEARTS;
    return itemConfigItem.DevilPrice === 2
        ? twoHeartPrice
        : isaac_typescript_definitions_1.PickupPrice.ONE_HEART;
}
exports.getCollectibleDevilHeartPrice = getCollectibleDevilHeartPrice;
/**
 * Helper function to get the path to a collectible PNG file. Returns the path to the question mark
 * sprite (i.e. from Curse of the Blind) if the provided collectible type was not valid.
 *
 * If you intentionally want the path to the question mark sprite, pass -1 as the collectible type.
 *
 * Note that this does not return the file name, but the full path to the collectible's PNG file.
 * The function is named "GfxFilename" to correspond to the associated `ItemConfigItem.GfxFileName`
 * field.
 */
function getCollectibleGfxFilename(collectibleOrCollectibleType) {
    if (collectibleOrCollectibleType === -1) {
        return constants_1.BLIND_ITEM_PNG_PATH;
    }
    const collectibleType = getCollectibleTypeFromArg(collectibleOrCollectibleType, "getCollectibleGfxFilename");
    const itemConfigItem = cachedClasses_1.itemConfig.GetCollectible(collectibleType);
    if (itemConfigItem === undefined) {
        return constants_1.BLIND_ITEM_PNG_PATH;
    }
    return itemConfigItem.GfxFileName;
}
exports.getCollectibleGfxFilename = getCollectibleGfxFilename;
/**
 * Helper function to get the initial amount of charges that a collectible has. In most cases, when
 * picking up an active collectible for the first time, it will be fully charged, which corresponds
 * to an `InitCharge` value of -1. However, in some cases, this may be different. For example,
 * Eden's Soul starts without any charges, so it has an `InitCharge` value of 0.
 *
 * This function returns 0 if the provided collectible type was not valid. This function returns -1
 * if the provided collectible type was not an active collectible.
 */
function getCollectibleInitCharge(collectibleOrCollectibleType) {
    const collectibleType = getCollectibleTypeFromArg(collectibleOrCollectibleType, "getCollectibleInitCharge");
    const itemConfigItem = cachedClasses_1.itemConfig.GetCollectible(collectibleType);
    if (itemConfigItem === undefined) {
        return 0;
    }
    return itemConfigItem.InitCharge;
}
exports.getCollectibleInitCharge = getCollectibleInitCharge;
/**
 * Helper function to get the `ItemType` of a collectible. Returns `ItemType.ITEM_NULL` if the
 * provided collectible type was not valid.
 */
function getCollectibleItemType(collectibleOrCollectibleType) {
    const collectibleType = getCollectibleTypeFromArg(collectibleOrCollectibleType, "getCollectibleItemType");
    const itemConfigItem = cachedClasses_1.itemConfig.GetCollectible(collectibleType);
    if (itemConfigItem === undefined) {
        return isaac_typescript_definitions_1.ItemType.NULL;
    }
    return itemConfigItem.Type;
}
exports.getCollectibleItemType = getCollectibleItemType;
/**
 * Helper function to get the maximum amount of charges that a collectible has. Returns 0 if the
 * provided collectible type was not valid.
 */
function getCollectibleMaxCharges(collectibleOrCollectibleType) {
    const collectibleType = getCollectibleTypeFromArg(collectibleOrCollectibleType, "getCollectibleMaxCharges");
    const itemConfigItem = cachedClasses_1.itemConfig.GetCollectible(collectibleType);
    if (itemConfigItem === undefined) {
        return 0;
    }
    return itemConfigItem.MaxCharges;
}
exports.getCollectibleMaxCharges = getCollectibleMaxCharges;
/**
 * Helper function to get the name of a collectible. Returns "Unknown" if the provided collectible
 * type is not valid.
 *
 * This function works for both vanilla and modded collectibles.
 *
 * For example, `getCollectibleName(CollectibleType.SAD_ONION)` would return "Sad Onion".
 */
function getCollectibleName(collectibleOrCollectibleType) {
    const collectibleType = getCollectibleTypeFromArg(collectibleOrCollectibleType, "getCollectibleName");
    // "ItemConfigItem.Name" is bugged with vanilla items on patch v1.7.6, so we use a hard-coded map
    // as a workaround.
    const collectibleName = collectibleNames_1.COLLECTIBLE_NAMES[collectibleType];
    if (collectibleName !== undefined) {
        return collectibleName;
    }
    const itemConfigItem = cachedClasses_1.itemConfig.GetCollectible(collectibleType);
    if (itemConfigItem !== undefined) {
        return itemConfigItem.Name;
    }
    return collectibleNames_1.DEFAULT_COLLECTIBLE_NAME;
}
exports.getCollectibleName = getCollectibleName;
/**
 * Helper function to get the "pedestal type" of a collectible. For example, it might be sitting on
 * top of a broken Blood Donation Machine, or it might be sitting on top of an opened Spiked Chest.
 */
function getCollectiblePedestalType(collectible) {
    if (!(0, pickupVariants_1.isCollectible)(collectible)) {
        const entityID = (0, entities_1.getEntityID)(collectible);
        error(`The "getCollectiblePedestalType" function was given a non-collectible: ${entityID}`);
    }
    const sprite = collectible.GetSprite();
    return sprite.GetOverlayFrame();
}
exports.getCollectiblePedestalType = getCollectiblePedestalType;
/**
 * Helper function to get a collectible's quality, which ranges from 0 to 4 (inclusive). For
 * example, Mom's Knife has a quality of 4. Returns 0 if the provided collectible type was not
 * valid.
 */
function getCollectibleQuality(collectibleOrCollectibleType) {
    const collectibleType = getCollectibleTypeFromArg(collectibleOrCollectibleType, "getCollectibleQuality");
    const itemConfigItem = cachedClasses_1.itemConfig.GetCollectible(collectibleType);
    if (itemConfigItem === undefined) {
        return 0;
    }
    return itemConfigItem.Quality;
}
exports.getCollectibleQuality = getCollectibleQuality;
/**
 * Helper function to get the tags of a collectible (which is the composition of zero or more
 * `ItemConfigTag`). Returns 0 if the provided collectible type is not valid.
 *
 * For example:
 *
 * ```ts
 * const collectibleType = CollectibleType.SAD_ONION;
 * const itemConfigTags = getCollectibleTags(collectibleType); // itemConfigTags is "18350080"
 * ```
 */
function getCollectibleTags(collectibleOrCollectibleType) {
    const collectibleType = getCollectibleTypeFromArg(collectibleOrCollectibleType, "getCollectibleTags");
    const itemConfigItem = cachedClasses_1.itemConfig.GetCollectible(collectibleType);
    return itemConfigItem === undefined ? isaac_typescript_definitions_1.ItemConfigTagZero : itemConfigItem.Tags;
}
exports.getCollectibleTags = getCollectibleTags;
/**
 * Returns an array containing every vanilla collectible type with the given quality.
 *
 * Note that this function will only return vanilla collectible types. To handle modded collectible
 * types, use the `getCollectibleTypesOfQuality` helper function instead.
 */
function getVanillaCollectibleTypesOfQuality(quality) {
    const collectibleTypes = QUALITY_TO_VANILLA_COLLECTIBLE_TYPES_MAP.get(quality);
    (0, utils_1.assertDefined)(collectibleTypes, `Failed to find the vanilla collectible types corresponding to quality: ${quality}`);
    return collectibleTypes;
}
exports.getVanillaCollectibleTypesOfQuality = getVanillaCollectibleTypesOfQuality;
/** Returns true if the item type in the item config is equal to `ItemType.ACTIVE`. */
function isActiveCollectible(collectibleType) {
    const itemType = getCollectibleItemType(collectibleType);
    return itemType === isaac_typescript_definitions_1.ItemType.ACTIVE;
}
exports.isActiveCollectible = isActiveCollectible;
/**
 * Returns true if the collectible has a red question mark sprite.
 *
 * Note that this function will not work properly in a render callback with the `RenderMode` set to
 * `RenderMode.WATER_REFLECT`. If this is detected, this function will throw a run-time error.
 */
function isBlindCollectible(collectible) {
    if (!(0, pickupVariants_1.isCollectible)(collectible)) {
        const entityID = (0, entities_1.getEntityID)(collectible);
        error(`The "isBlindCollectible" function was given a non-collectible: ${entityID}`);
    }
    const room = cachedClasses_1.game.GetRoom();
    const renderMode = room.GetRenderMode();
    if (renderMode === isaac_typescript_definitions_1.RenderMode.WATER_REFLECT) {
        error('The "isBlindCollectible" function will not work properly in a render callback with the render mode equal to "RenderMode.WATER_REFLECT". Make sure that you properly account for this case if you are calling this function in a render callback.');
    }
    const sprite = collectible.GetSprite();
    const animation = sprite.GetAnimation();
    const frame = sprite.GetFrame();
    questionMarkSprite.SetFrame(animation, frame);
    return collectibleSpriteEquals(sprite, questionMarkSprite);
}
exports.isBlindCollectible = isBlindCollectible;
/** Returns true if the item type in the item config is equal to `ItemType.FAMILIAR`. */
function isFamiliarCollectible(collectibleType) {
    const itemType = getCollectibleItemType(collectibleType);
    return itemType === isaac_typescript_definitions_1.ItemType.FAMILIAR;
}
exports.isFamiliarCollectible = isFamiliarCollectible;
/**
 * Returns whether the given collectible is a "glitched" item. All items are replaced by glitched
 * items once a player has TMTRAINER. However, glitched items can also "naturally" appear in secret
 * rooms and I AM ERROR rooms if the "Corrupted Data" achievement is unlocked.
 *
 * Under the hood, this checks if the sub-type of the collectible is greater than 4,000,000,000.
 */
function isGlitchedCollectible(collectible) {
    return (collectible.Variant === isaac_typescript_definitions_1.PickupVariant.COLLECTIBLE &&
        collectible.SubType > GLITCHED_ITEM_THRESHOLD);
}
exports.isGlitchedCollectible = isGlitchedCollectible;
/**
 * Returns true if the collectible has the "Hidden" attribute in the item config.
 *
 * Hidden collectibles will not show up in any pools and Eden will not start with them.
 */
function isHiddenCollectible(collectibleOrCollectibleType) {
    const collectibleType = getCollectibleTypeFromArg(collectibleOrCollectibleType, "isHiddenCollectible");
    const itemConfigItem = cachedClasses_1.itemConfig.GetCollectible(collectibleType);
    return itemConfigItem !== undefined && itemConfigItem.Hidden;
}
exports.isHiddenCollectible = isHiddenCollectible;
function isModdedCollectibleType(collectibleType) {
    return !isVanillaCollectibleType(collectibleType);
}
exports.isModdedCollectibleType = isModdedCollectibleType;
/**
 * Returns true if the item type in the item config is equal to `ItemType.ITEM_PASSIVE` or
 * `ItemType.ITEM_FAMILIAR`.
 */
function isPassiveOrFamiliarCollectible(collectibleOrCollectibleType) {
    const collectibleType = getCollectibleTypeFromArg(collectibleOrCollectibleType, "isPassiveCollectible");
    const itemType = getCollectibleItemType(collectibleType);
    return itemType === isaac_typescript_definitions_1.ItemType.PASSIVE || itemType === isaac_typescript_definitions_1.ItemType.FAMILIAR;
}
exports.isPassiveOrFamiliarCollectible = isPassiveOrFamiliarCollectible;
/** Helper function to check if a collectible type is a particular quality. */
function isQuality(collectibleOrCollectibleType, quality) {
    const actualQuality = getCollectibleQuality(collectibleOrCollectibleType);
    return quality === actualQuality;
}
exports.isQuality = isQuality;
/**
 * Helper function to determine if a particular collectible will disappear from the player's
 * inventory upon use. Note that this will not work will modded collectibles, as there is no way to
 * dynamically know if a modded collectible will disappear.
 */
function isSingleUseCollectible(collectibleType) {
    return singleUseActiveCollectibleTypesSet_1.SINGLE_USE_ACTIVE_COLLECTIBLE_TYPES_SET.has(collectibleType);
}
exports.isSingleUseCollectible = isSingleUseCollectible;
function isValidCollectibleType(collectibleType) {
    const potentialCollectibleType = (0, types_1.asCollectibleType)(collectibleType);
    const itemConfigItem = cachedClasses_1.itemConfig.GetCollectible(potentialCollectibleType);
    return itemConfigItem !== undefined;
}
exports.isValidCollectibleType = isValidCollectibleType;
function isVanillaCollectibleType(collectibleType) {
    return collectibleType <= constantsFirstLast_1.LAST_VANILLA_COLLECTIBLE_TYPE;
}
exports.isVanillaCollectibleType = isVanillaCollectibleType;
/**
 * Helper function to generate a new sprite based on a collectible. If the provided collectible type
 * is invalid, a sprite with a Curse of the Blind question mark will be returned.
 *
 * If you intentionally want a question mark sprite, pass -1 as the collectible type.
 */
function newCollectibleSprite(collectibleType) {
    const sprite = Sprite();
    sprite.Load(COLLECTIBLE_ANM2_PATH, false);
    // We want to clear the pedestal layers so that the returned sprite only has the collectible
    // image. We can't use the `Sprite.Reset` method for this purpose because that would unload the
    // anm2 file.
    (0, sprites_1.clearSprite)(sprite);
    const gfxFileName = getCollectibleGfxFilename(collectibleType);
    sprite.ReplaceSpritesheet(isaac_typescript_definitions_1.CollectibleSpriteLayer.HEAD, gfxFileName);
    sprite.LoadGraphics();
    const defaultAnimation = sprite.GetDefaultAnimation();
    sprite.Play(defaultAnimation, true);
    return sprite;
}
exports.newCollectibleSprite = newCollectibleSprite;
/**
 * Helper function to remove the rotation behavior from a collectible. This will happen by default
 * when collectibles are spawned when playing as Tainted Isaac or when having Binge Eater.
 *
 * Under the hood, this is accomplished by morphing the collectible with the `ignoreModifiers`
 * argument set to true.
 */
function preventCollectibleRotation(collectible) {
    if (!(0, pickupVariants_1.isCollectible)(collectible)) {
        const entityID = (0, entities_1.getEntityID)(collectible);
        error(`The "preventCollectibleRotation" function was given a non-collectible: ${entityID}`);
    }
    collectible.Morph(collectible.Type, collectible.Variant, collectible.SubType, true, true, true);
}
exports.preventCollectibleRotation = preventCollectibleRotation;
/**
 * Helper function to remove all pickup delay on a collectible. By default, collectibles have a 20
 * frame delay before they can be picked up by a player.
 */
function removeCollectiblePickupDelay(collectible) {
    if (!(0, pickupVariants_1.isCollectible)(collectible)) {
        const entityID = (0, entities_1.getEntityID)(collectible);
        error(`The "removeCollectiblePickupDelay" function was given a non-collectible: ${entityID}`);
    }
    collectible.Wait = 0;
}
exports.removeCollectiblePickupDelay = removeCollectiblePickupDelay;
/**
 * Helper function to set a collectible sprite to a question mark (i.e. how collectibles look when
 * the player has Curse of the Blind).
 */
function setCollectibleBlind(collectible) {
    if (!(0, pickupVariants_1.isCollectible)(collectible)) {
        const entityID = (0, entities_1.getEntityID)(collectible);
        error(`The "setCollectibleBlind" function was given a non-collectible: ${entityID}`);
    }
    setCollectibleSprite(collectible, constants_1.BLIND_ITEM_PNG_PATH);
}
exports.setCollectibleBlind = setCollectibleBlind;
/**
 * Helper function to remove the collectible from a collectible pedestal and make it appear as if a
 * player has already taken the item. This is accomplished by changing the sub-type to
 * `CollectibleType.NULL` and then setting the sprite to an empty/missing PNG file.
 *
 * For more information, see the documentation for the "clearSprite" helper function.
 */
function setCollectibleEmpty(collectible) {
    if (!(0, pickupVariants_1.isCollectible)(collectible)) {
        const entityID = (0, entities_1.getEntityID)(collectible);
        error(`The "setCollectibleEmpty" function was given a non-collectible: ${entityID}`);
    }
    collectible.SubType = isaac_typescript_definitions_1.CollectibleType.NULL;
    clearCollectibleSprite(collectible);
}
exports.setCollectibleEmpty = setCollectibleEmpty;
/**
 * Helper function to change a collectible into a "glitched" item (like the ones that appear when
 * the player has TMTRAINER).
 */
function setCollectibleGlitched(collectible) {
    if (!(0, pickupVariants_1.isCollectible)(collectible)) {
        const entityID = (0, entities_1.getEntityID)(collectible);
        error(`The "setCollectibleGlitched" function was given a non-collectible: ${entityID}`);
    }
    // We need to generate a new glitched item. Thus, we temporarily give the player TMTRAINER, if
    // necessary.
    const player = Isaac.GetPlayer();
    const hasTMTRAINER = player.HasCollectible(isaac_typescript_definitions_1.CollectibleType.TMTRAINER);
    if (!hasTMTRAINER) {
        player.AddCollectible(isaac_typescript_definitions_1.CollectibleType.TMTRAINER, 0, false);
    }
    const itemPool = cachedClasses_1.game.GetItemPool();
    const collectibleType = itemPool.GetCollectible(constants_1.DEFAULT_ITEM_POOL_TYPE);
    setCollectibleSubType(collectible, collectibleType);
    if (!hasTMTRAINER) {
        player.RemoveCollectible(isaac_typescript_definitions_1.CollectibleType.TMTRAINER);
    }
}
exports.setCollectibleGlitched = setCollectibleGlitched;
/**
 * Helper function to set the "pedestal type" of a collectible. For example, it might be sitting on
 * top of a broken Blood Donation Machine and you want to change it to be sitting on top of an
 * opened Spiked Chest.
 */
function setCollectiblePedestalType(collectible, collectiblePedestalType) {
    if (!(0, pickupVariants_1.isCollectible)(collectible)) {
        const entityID = (0, entities_1.getEntityID)(collectible);
        error(`The "setCollectiblePedestalType" function was given a non-collectible: ${entityID}`);
    }
    const sprite = collectible.GetSprite();
    const overlayAnimation = sprite.GetOverlayAnimation();
    sprite.SetOverlayFrame(overlayAnimation, collectiblePedestalType);
}
exports.setCollectiblePedestalType = setCollectiblePedestalType;
/**
 * Helper function to change the sprite of a collectible pedestal entity.
 *
 * For more information about removing the collectible sprite, see the documentation for the
 * "clearSprite" helper function.
 *
 * @param collectible The collectible whose sprite you want to modify.
 * @param pngPath Equal to either the spritesheet path to load (e.g.
 *                "gfx/items/collectibles/collectibles_001_thesadonion.png") or undefined. If
 *                undefined, the sprite will be removed, making it appear like the collectible has
 *                already been taken by the player.
 */
function setCollectibleSprite(collectible, pngPath) {
    if (!(0, pickupVariants_1.isCollectible)(collectible)) {
        const entityID = (0, entities_1.getEntityID)(collectible);
        error(`The "setCollectibleSprite" function was given a non-collectible: ${entityID}`);
    }
    const sprite = collectible.GetSprite();
    if (pngPath === undefined) {
        // We want to remove the little circle that appears on top of the pedestal, which is why we also
        // clear `CollectibleSpriteLayer.ITEM_SHADOW`.
        (0, sprites_1.clearSprite)(sprite, isaac_typescript_definitions_1.CollectibleSpriteLayer.HEAD, isaac_typescript_definitions_1.CollectibleSpriteLayer.ITEM_SHADOW);
    }
    else {
        sprite.ReplaceSpritesheet(isaac_typescript_definitions_1.CollectibleSpriteLayer.HEAD, pngPath);
        sprite.LoadGraphics();
    }
}
exports.setCollectibleSprite = setCollectibleSprite;
/**
 * Helper function to change the collectible on a pedestal. Simply updating the `SubType` field is
 * not sufficient because the sprite will not change.
 */
function setCollectibleSubType(collectible, newCollectibleType) {
    if (!(0, pickupVariants_1.isCollectible)(collectible)) {
        const entityID = (0, entities_1.getEntityID)(collectible);
        error(`The "setCollectibleSubType" function was given a non-collectible: ${entityID}`);
    }
    // You cannot morph a pedestal to "CollectibleType.NULL"; it would instead create a new random
    // collectible item.
    if (newCollectibleType === isaac_typescript_definitions_1.CollectibleType.NULL) {
        setCollectibleEmpty(collectible);
        return;
    }
    // The naive way to change a collectible's sub-type is to set it directly. However, doing this
    // will not update the sprite. Manually updating the sprite works in most situations, but does not
    // work when the pedestal is empty. Instead, we can simply morph the collectible, which seems to
    // work in all situations.
    collectible.Morph(isaac_typescript_definitions_1.EntityType.PICKUP, isaac_typescript_definitions_1.PickupVariant.COLLECTIBLE, newCollectibleType, true, true, true);
}
exports.setCollectibleSubType = setCollectibleSubType;
/**
 * Helper function to put a message in the log.txt file to let the Rebirth Item Tracker know that
 * the build has been rerolled.
 */
function setCollectiblesRerolledForItemTracker() {
    // This cannot use the "log" function since the prefix will prevent the Rebirth Item Tracker from
    // recognizing the message. The number here does not matter since the tracker does not check for a
    // specific number.
    Isaac.DebugString("Added 3 Collectibles");
}
exports.setCollectiblesRerolledForItemTracker = setCollectiblesRerolledForItemTracker;
function getCollectibleTypeFromArg(collectibleOrCollectibleType, functionName) {
    if ((0, types_1.isInteger)(collectibleOrCollectibleType)) {
        return collectibleOrCollectibleType;
    }
    const collectible = collectibleOrCollectibleType;
    if (!(0, pickupVariants_1.isCollectible)(collectible)) {
        const entityID = (0, entities_1.getEntityID)(collectible);
        error(`The "${functionName}" function was given a non-collectible: ${entityID}`);
    }
    return collectible.SubType;
}
