"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.ModdedElementDetection = void 0;
const isaac_typescript_definitions_1 = require("isaac-typescript-definitions");
const cachedClasses_1 = require("../../../core/cachedClasses");
const constantsFirstLast_1 = require("../../../core/constantsFirstLast");
const decorators_1 = require("../../../decorators");
const Feature_1 = require("../../private/Feature");
// eslint-disable-next-line isaacscript/strict-enums
const FIRST_MODDED_COLLECTIBLE_TYPE = constantsFirstLast_1.LAST_VANILLA_COLLECTIBLE_TYPE + 1;
// eslint-disable-next-line isaacscript/strict-enums
const FIRST_MODDED_TRINKET_TYPE = constantsFirstLast_1.LAST_VANILLA_TRINKET_TYPE + 1;
// eslint-disable-next-line isaacscript/strict-enums
const FIRST_MODDED_CARD_TYPE = constantsFirstLast_1.LAST_VANILLA_CARD_TYPE + 1;
// eslint-disable-next-line isaacscript/strict-enums
const FIRST_MODDED_PILL_EFFECT = constantsFirstLast_1.LAST_VANILLA_PILL_EFFECT + 1;
/**
 * Mods can add extra things to the game (e.g. collectibles, trinkets, and so on). Since mods load
 * in alphabetical order, the total number of things can't be properly be known until at least one
 * callback fires (which indicates that all mods have been loaded).
 *
 * This feature gates all such functions behind a callback check. Subsequently, these functions will
 * throw a runtime error if they are called in the menu, before any callbacks have occurred. This
 * ensures that the proper values are always returned and allows you to get immediate feedback if
 * you accidentally access them from the menu.
 */
class ModdedElementDetection extends Feature_1.Feature {
    atLeastOneCallbackFired = false;
    /** @internal */
    constructor() {
        super();
        this.callbacksUsed = [
            // 9
            [isaac_typescript_definitions_1.ModCallback.POST_PLAYER_INIT, this.postPlayerInit],
        ];
    }
    // ModCallback.POST_PLAYER_INIT (9)
    postPlayerInit = () => {
        this.atLeastOneCallbackFired = true;
    };
    errorIfNoCallbacksFired(constantType) {
        if (!this.atLeastOneCallbackFired) {
            error(`Failed to retrieve a ${constantType} constant. Since not all mods have been loaded yet, any constants of this type will be inaccurate. Thus, you must wait until at least one callback fires before retrieving these types of constants.`);
        }
    }
    // ------------
    // Collectibles
    // ------------
    /**
     * Returns the first modded collectible type, or undefined if there are no modded collectibles.
     *
     * This function can only be called if at least one callback has been executed. This is because
     * not all collectibles 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_DETECTION`.
     *
     * @public
     */
    getFirstModdedCollectibleType() {
        this.errorIfNoCallbacksFired("collectible");
        const itemConfigItem = cachedClasses_1.itemConfig.GetCollectible(FIRST_MODDED_COLLECTIBLE_TYPE);
        return itemConfigItem === undefined
            ? undefined
            : FIRST_MODDED_COLLECTIBLE_TYPE;
    }
    /**
     * Will change depending on how many modded collectibles there are.
     *
     * Equal to `itemConfig.GetCollectibles().Size - 1`. (`Size` includes invalid collectibles, like
     * 666. We subtract one to account for `CollectibleType.NULL`.)
     *
     * If there are no mods present that add any custom collectibles, this function will return
     * `CollectibleType.MOMS_RING` (732).
     *
     * This function can only be called if at least one callback has been executed. This is because
     * not all collectibles 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_DETECTION`.
     *
     * @public
     */
    getLastCollectibleType() {
        this.errorIfNoCallbacksFired("collectible");
        return cachedClasses_1.itemConfig.GetCollectibles().Size - 1;
    }
    /**
     * Returns the total number of collectibles in the item config, including both vanilla and modded
     * collectibles. If you just need the number of vanilla collectible types, use the
     * `NUM_VANILLA_COLLECTIBLE_TYPES` constant.
     *
     * This function can only be called if at least one callback has been executed. This is because
     * not all collectibles 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_DETECTION`.
     *
     * @public
     */
    getNumCollectibleTypes() {
        this.errorIfNoCallbacksFired("collectible");
        return constantsFirstLast_1.NUM_VANILLA_COLLECTIBLE_TYPES + this.getNumModdedCollectibleTypes();
    }
    /**
     * Unlike vanilla collectible types, modded collectible types are always contiguous.
     *
     * This function can only be called if at least one callback has been executed. This is because
     * not all collectibles 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_DETECTION`.
     *
     * @public
     */
    getNumModdedCollectibleTypes() {
        this.errorIfNoCallbacksFired("collectible");
        return this.getLastCollectibleType() - constantsFirstLast_1.LAST_VANILLA_COLLECTIBLE_TYPE;
    }
    // --------
    // Trinkets
    // --------
    /**
     * Returns the first modded trinket type, or undefined if there are no modded trinkets.
     *
     * This function can only be called if at least one callback has been executed. This is because
     * not all trinkets 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_DETECTION`.
     *
     * @public
     */
    getFirstModdedTrinketType() {
        this.errorIfNoCallbacksFired("trinket");
        const itemConfigItem = cachedClasses_1.itemConfig.GetTrinket(FIRST_MODDED_TRINKET_TYPE);
        return itemConfigItem === undefined ? undefined : FIRST_MODDED_TRINKET_TYPE;
    }
    /**
     * Will change depending on how many modded trinkets there are.
     *
     * Equal to `itemConfig.GetTrinkets().Size - 1`. (`Size` includes invalid trinkets, like 47. We
     * subtract one to account for `TrinketType.NULL`.)
     *
     * If there are no mods present that add any custom trinkets, this function will return
     * `TrinketType.SIGIL_OF_BAPHOMET` (189).
     *
     * This function can only be called if at least one callback has been executed. This is because
     * not all trinkets 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_DETECTION`.
     *
     * @public
     */
    getLastTrinketType() {
        this.errorIfNoCallbacksFired("trinket");
        return cachedClasses_1.itemConfig.GetTrinkets().Size - 1;
    }
    /**
     * Returns the total number of trinkets in the item config, including both vanilla and modded
     * trinkets. If you just need the number of vanilla trinket types, use the
     * `NUM_VANILLA_TRINKET_TYPES` constant.
     *
     * This function can only be called if at least one callback has been executed. This is because
     * not all trinkets 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_DETECTION`.
     *
     * @public
     */
    getNumTrinketTypes() {
        this.errorIfNoCallbacksFired("trinket");
        return constantsFirstLast_1.NUM_VANILLA_TRINKET_TYPES + this.getNumModdedTrinketTypes();
    }
    /**
     * Unlike vanilla trinket types, modded trinket types are always contiguous.
     *
     * This function can only be called if at least one callback has been executed. This is because
     * not all trinkets 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_DETECTION`.
     *
     * @public
     */
    getNumModdedTrinketTypes() {
        this.errorIfNoCallbacksFired("trinket");
        return this.getLastTrinketType() - constantsFirstLast_1.LAST_VANILLA_TRINKET_TYPE;
    }
    // -----
    // Cards
    // -----
    /**
     * Returns the first modded card sub-type, or undefined if there are no modded cards.
     *
     * This function can only be called if at least one callback has been executed. This is because
     * not all cards 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_DETECTION`.
     *
     * @public
     */
    getFirstModdedCardType() {
        this.errorIfNoCallbacksFired("card");
        const itemConfigCard = cachedClasses_1.itemConfig.GetCard(FIRST_MODDED_CARD_TYPE);
        return itemConfigCard === undefined ? undefined : FIRST_MODDED_CARD_TYPE;
    }
    /**
     * Will change depending on how many modded cards there are.
     *
     * Equal to `itemConfig.GetCards().Size - 1`. (`Size` includes invalid cards, but since cards are
     * contiguous, there are no invalid cards. We subtract one to account for `CardType.NULL`.)
     *
     * If there are no mods present that add any custom cards, this function will return
     * `CardType.SOUL_OF_JACOB_AND_ESAU` (97).
     *
     * This function can only be called if at least one callback has been executed. This is because
     * not all trinkets 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_DETECTION`.
     *
     * @public
     */
    getLastCardType() {
        this.errorIfNoCallbacksFired("card");
        return cachedClasses_1.itemConfig.GetCards().Size - 1;
    }
    /**
     * Returns the total number of cards in the item config, including both vanilla and modded cards.
     * If you just need the number of vanilla card types, use the `NUM_VANILLA_CARD_TYPES` constant.
     *
     * This function can only be called if at least one callback has been executed. This is because
     * not all cards 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_DETECTION`.
     *
     * @public
     */
    getNumCardTypes() {
        this.errorIfNoCallbacksFired("card");
        return constantsFirstLast_1.NUM_VANILLA_CARD_TYPES + this.getNumModdedCardTypes();
    }
    /**
     * Like vanilla card types, modded card types are always contiguous.
     *
     * This function can only be called if at least one callback has been executed. This is because
     * not all cards 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_DETECTION`.
     *
     * @public
     */
    getNumModdedCardTypes() {
        this.errorIfNoCallbacksFired("card");
        return this.getLastCardType() - constantsFirstLast_1.LAST_VANILLA_CARD_TYPE;
    }
    // ------------
    // Pill Effects
    // ------------
    /**
     * Returns the first modded pill effect, or undefined if there are no modded pill effects.
     *
     * 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_DETECTION`.
     *
     * @public
     */
    getFirstModdedPillEffect() {
        this.errorIfNoCallbacksFired("pill");
        const itemConfigPillEffect = cachedClasses_1.itemConfig.GetPillEffect(FIRST_MODDED_PILL_EFFECT);
        return itemConfigPillEffect === undefined
            ? undefined
            : FIRST_MODDED_PILL_EFFECT;
    }
    /**
     * Will change depending on how many modded pill effects there are.
     *
     * Equal to `itemConfig.GetPillEffects().Size - 1`. (`Size` includes invalid pill effects, but
     * since pill effects are contiguous, there are no invalid pill effects. We subtract one to
     * account for the enum starting at 0 instead of 1.)
     *
     * If there are no mods present that add any custom pill effects, this function will return
     * `PillEffect.EXPERIMENTAL` (49).
     *
     * 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_DETECTION`.
     *
     * @public
     */
    getLastPillEffect() {
        this.errorIfNoCallbacksFired("pill");
        return cachedClasses_1.itemConfig.GetPillEffects().Size - 1;
    }
    /**
     * Returns the total number of pill effects in the item config, including both vanilla and modded
     * pill effects. If you just need the number of vanilla pill effects, use the
     * `NUM_VANILLA_PILL_EFFECTS` constant.
     *
     * This function can only be called if at least one callback has been executed. This is because
     * not all cards 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_DETECTION`.
     *
     * @public
     */
    getNumPillEffects() {
        this.errorIfNoCallbacksFired("pill");
        return constantsFirstLast_1.NUM_VANILLA_PILL_EFFECTS + this.getNumModdedPillEffects();
    }
    /**
     * Like vanilla pill effects, modded pill effects are always contiguous.
     *
     * This function can only be called if at least one callback has been executed. This is because
     * not all cards 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_DETECTION`.
     *
     * @public
     */
    getNumModdedPillEffects() {
        this.errorIfNoCallbacksFired("card");
        return this.getLastPillEffect() - constantsFirstLast_1.LAST_VANILLA_PILL_EFFECT;
    }
}
exports.ModdedElementDetection = ModdedElementDetection;
__decorate([
    decorators_1.Exported
], ModdedElementDetection.prototype, "getFirstModdedCollectibleType", null);
__decorate([
    decorators_1.Exported
], ModdedElementDetection.prototype, "getLastCollectibleType", null);
__decorate([
    decorators_1.Exported
], ModdedElementDetection.prototype, "getNumCollectibleTypes", null);
__decorate([
    decorators_1.Exported
], ModdedElementDetection.prototype, "getNumModdedCollectibleTypes", null);
__decorate([
    decorators_1.Exported
], ModdedElementDetection.prototype, "getFirstModdedTrinketType", null);
__decorate([
    decorators_1.Exported
], ModdedElementDetection.prototype, "getLastTrinketType", null);
__decorate([
    decorators_1.Exported
], ModdedElementDetection.prototype, "getNumTrinketTypes", null);
__decorate([
    decorators_1.Exported
], ModdedElementDetection.prototype, "getNumModdedTrinketTypes", null);
__decorate([
    decorators_1.Exported
], ModdedElementDetection.prototype, "getFirstModdedCardType", null);
__decorate([
    decorators_1.Exported
], ModdedElementDetection.prototype, "getLastCardType", null);
__decorate([
    decorators_1.Exported
], ModdedElementDetection.prototype, "getNumCardTypes", null);
__decorate([
    decorators_1.Exported
], ModdedElementDetection.prototype, "getNumModdedCardTypes", null);
__decorate([
    decorators_1.Exported
], ModdedElementDetection.prototype, "getFirstModdedPillEffect", null);
__decorate([
    decorators_1.Exported
], ModdedElementDetection.prototype, "getLastPillEffect", null);
__decorate([
    decorators_1.Exported
], ModdedElementDetection.prototype, "getNumPillEffects", null);
__decorate([
    decorators_1.Exported
], ModdedElementDetection.prototype, "getNumModdedPillEffects", null);
