"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.isFamiliarThatShootsPlayerTears = exports.isFamiliarStolenBySiren = exports.getSirenHelper = exports.getPlayerFamiliars = exports.checkFamiliarFromCollectibles = exports.checkFamiliar = void 0;
const isaac_typescript_definitions_1 = require("isaac-typescript-definitions");
const cachedClasses_1 = require("../core/cachedClasses");
const familiarsThatShootPlayerTearsSet_1 = require("../sets/familiarsThatShootPlayerTearsSet");
const entities_1 = require("./entities");
const entitiesSpecific_1 = require("./entitiesSpecific");
const rng_1 = require("./rng");
/**
 * Instead of generating a new RNG object every time we need to spawn a new familiar, we instead
 * re-use the same RNG object. This makes it less likely that the `InitSeed` of the familiar will
 * overlap, since we are "nexting" instead of doing a fresh reroll.
 */
const familiarGenerationRNG = (0, rng_1.newRNG)();
/**
 * Helper function to add and remove familiars based on a target amount that you specify.
 *
 * This is a convenience wrapper around the `EntityPlayer.CheckFamiliar` method. Use this helper
 * function instead so that you do not have to retrieve the `ItemConfigItem` and so that you do not
 * specify an incorrect RNG object. (The vanilla method is bugged in that it does not increment the
 * RNG object; see the documentation of the method for more details.)
 *
 * This function is meant to be called in the `EVALUATE_CACHE` callback (when the cache flag is
 * equal to `CacheFlag.FAMILIARS`).
 *
 * Note that this function is only meant to be used in special circumstances where the familiar
 * count is completely custom and does not correspond to the amount of collectibles. For the general
 * case, use the `checkFamiliarFromCollectibles` helper function instead.
 *
 * Note that this will spawn familiars with a completely random `InitSeed`. When calculating random
 * events for this familiar, you should use a data structure that maps familiar `InitSeed` to RNG
 * objects that are initialized based on the seed from
 * `EntityPlayer.GetCollectibleRNG(collectibleType)`.
 *
 * @param player The player that owns the familiars.
 * @param collectibleType The collectible type of the collectible associated with this familiar.
 * @param targetCount The number of familiars that should exist. This function will add or remove
 *                    familiars until it matches the target count.
 * @param familiarVariant The variant of the familiar to spawn or remove.
 * @param familiarSubType Optional. The sub-type of the familiar to spawn or remove. If not
 *                        specified, it will search for existing familiars of all sub-types, and
 *                        spawn new familiars with a sub-type of 0.
 */
function checkFamiliar(player, collectibleType, targetCount, familiarVariant, familiarSubType) {
    familiarGenerationRNG.Next();
    const itemConfigItem = cachedClasses_1.itemConfig.GetCollectible(collectibleType);
    player.CheckFamiliar(familiarVariant, targetCount, familiarGenerationRNG, itemConfigItem, familiarSubType);
}
exports.checkFamiliar = checkFamiliar;
/**
 * Helper function to add and remove familiars based on the amount of associated collectibles that a
 * player has.
 *
 * Use this helper function instead of invoking the `EntityPlayer.CheckFamiliar` method directly so
 * that the target count is handled automatically.
 *
 * This function is meant to be called in the `EVALUATE_CACHE` callback (when the cache flag is
 * equal to `CacheFlag.FAMILIARS`).
 *
 * Use this function when the amount of familiars should be equal to the amount of associated
 * collectibles that the player has (plus any extras from having used Box of Friends or Monster
 * Manual). If you instead need to have a custom amount of familiars, use the `checkFamiliars`
 * function instead.
 *
 * Note that this will spawn familiars with a completely random `InitSeed`. When calculating random
 * events for this familiar, you should use a data structure that maps familiar `InitSeed` to RNG
 * objects that are initialized based on the seed from
 * `EntityPlayer.GetCollectibleRNG(collectibleType)`.
 *
 * @param player The player that owns the familiars and collectibles.
 * @param collectibleType The collectible type of the collectible associated with this familiar.
 * @param familiarVariant The variant of the familiar to spawn or remove.
 * @param familiarSubType Optional. The sub-type of the familiar to spawn or remove. If not
 *                        specified, it will search for existing familiars of all sub-types, and
 *                        spawn new familiars with a sub-type of 0.
 */
function checkFamiliarFromCollectibles(player, collectibleType, familiarVariant, familiarSubType) {
    // We need to include non-real collectibles (like Lilith's Incubus), so we omit the second
    // argument.
    const numCollectibles = player.GetCollectibleNum(collectibleType);
    const effects = player.GetEffects();
    // Whenever Box of Friends or Monster Manual is used, it will automatically increment the number
    // of collectible effects for this familiar.
    const numCollectibleEffects = effects.GetCollectibleEffectNum(collectibleType);
    const targetCount = numCollectibles + numCollectibleEffects;
    checkFamiliar(player, collectibleType, targetCount, familiarVariant, familiarSubType);
}
exports.checkFamiliarFromCollectibles = checkFamiliarFromCollectibles;
/** Helper function to get only the familiars that belong to a specific player. */
function getPlayerFamiliars(player) {
    const playerPtrHash = GetPtrHash(player);
    const familiars = (0, entitiesSpecific_1.getFamiliars)();
    return familiars.filter((familiar) => {
        const familiarPlayerPtrHash = GetPtrHash(familiar.Player);
        return familiarPlayerPtrHash === playerPtrHash;
    });
}
exports.getPlayerFamiliars = getPlayerFamiliars;
/**
 * Helper function to get the corresponding "Siren Helper" entity for a stolen familiar.
 *
 * When The Siren boss "steals" your familiars, a hidden "Siren Helper" entity is spawned to control
 * each familiar stolen. (Checking for the presence of this entity seems to be the only way to
 * detect when the Siren steals a familiar.)
 *
 * @param familiar The familiar to be checked.
 * @returns Returns the hidden "Siren Helper" entity corresponding to the given familiar, if it
 *          exists. Returns undefined otherwise.
 */
function getSirenHelper(familiar) {
    const familiarPtrHash = GetPtrHash(familiar);
    const sirenHelpers = (0, entities_1.getEntities)(isaac_typescript_definitions_1.EntityType.SIREN_HELPER);
    return sirenHelpers.find((sirenHelper) => sirenHelper.Target !== undefined &&
        GetPtrHash(sirenHelper.Target) === familiarPtrHash);
}
exports.getSirenHelper = getSirenHelper;
/**
 * Helper function to detect if the given familiar is "stolen" by The Siren boss.
 *
 * This function is useful because some familiars may need to behave differently when under The
 * Siren's control (e.g. if they auto-target enemies).
 */
function isFamiliarStolenBySiren(familiar) {
    const sirenHelper = getSirenHelper(familiar);
    return sirenHelper !== undefined;
}
exports.isFamiliarStolenBySiren = isFamiliarStolenBySiren;
/**
 * Helper function to check if a familiar is the type that shoots tears that mimic the players
 * tears, like Incubus, Fate's Reward, Sprinkler, and so on.
 */
function isFamiliarThatShootsPlayerTears(familiar) {
    return familiarsThatShootPlayerTearsSet_1.FAMILIARS_THAT_SHOOT_PLAYER_TEARS_SET.has(familiar.Variant);
}
exports.isFamiliarThatShootsPlayerTears = isFamiliarThatShootsPlayerTears;
