"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.useActiveItemTemp = exports.setActiveItem = exports.removeCollectibleFromAllPlayers = exports.removeCollectibleCostume = exports.removeCollectible = exports.removeAllActiveItems = exports.isActiveSlotEmpty = exports.hasOpenActiveItemSlot = exports.hasCollectibleInActiveSlot = exports.hasCollectible = exports.getTotalPlayerCollectibles = exports.getPlayersWithCollectible = exports.getPlayerCollectibleCount = exports.getAdjustedPrice = exports.getActiveItemSlots = exports.anyPlayerHasCollectible = exports.addCollectibleCostume = exports.addCollectible = void 0;
const isaac_typescript_definitions_1 = require("isaac-typescript-definitions");
const cachedEnumValues_1 = require("../cachedEnumValues");
const cachedClasses_1 = require("../core/cachedClasses");
const ReadonlySet_1 = require("../types/ReadonlySet");
const array_1 = require("./array");
const collectibles_1 = require("./collectibles");
const playerIndex_1 = require("./playerIndex");
const players_1 = require("./players");
/**
 * Helper function to add one or more collectibles to a player.
 *
 * This function is variadic, meaning that you can supply as many collectible types as you want to
 * add.
 */
function addCollectible(player, ...collectibleTypes) {
    for (const collectibleType of collectibleTypes) {
        player.AddCollectible(collectibleType);
    }
}
exports.addCollectible = addCollectible;
function addCollectibleCostume(player, collectibleType) {
    const itemConfigItem = cachedClasses_1.itemConfig.GetCollectible(collectibleType);
    if (itemConfigItem === undefined) {
        return;
    }
    player.AddCostume(itemConfigItem, false);
}
exports.addCollectibleCostume = addCollectibleCostume;
/**
 * Helper function to check to see if any player has a particular collectible.
 *
 * @param collectibleType The collectible type to check for.
 * @param ignoreModifiers If set to true, only counts collectibles the player actually owns and
 *                        ignores effects granted by items like Zodiac, 3 Dollar Bill and Lemegeton.
 *                        Default is false.
 */
function anyPlayerHasCollectible(collectibleType, ignoreModifiers) {
    const players = (0, playerIndex_1.getAllPlayers)();
    return players.some((player) => player.HasCollectible(collectibleType, ignoreModifiers));
}
exports.anyPlayerHasCollectible = anyPlayerHasCollectible;
/**
 * Helper function to find the active slots that the player has the corresponding collectible type
 * in. Returns an empty array if the player does not have the collectible in any active slot.
 */
function getActiveItemSlots(player, collectibleType) {
    return cachedEnumValues_1.ACTIVE_SLOT_VALUES.filter((activeSlot) => {
        const activeItem = player.GetActiveItem(activeSlot);
        return activeItem === collectibleType;
    });
}
exports.getActiveItemSlots = getActiveItemSlots;
/**
 * Helper function to get the adjusted price for a pickup, depending on how many Steam Sales all
 * players currently have. (For example, if Jacob has one Steam Sale and Esau has one Steam Sale,
 * the prices for items in the shop would be the same as if Isaac had two Steam Sales.)
 */
function getAdjustedPrice(basePrice) {
    const numSteamSales = getTotalPlayerCollectibles(isaac_typescript_definitions_1.CollectibleType.STEAM_SALE);
    return numSteamSales > 0
        ? Math.floor(basePrice / (numSteamSales + 1))
        : basePrice;
}
exports.getAdjustedPrice = getAdjustedPrice;
/**
 * Helper function to return the total amount of collectibles that a player has that match the
 * collectible type(s) provided.
 *
 * This function is variadic, meaning that you can specify N collectible types.
 *
 * Note that this will filter out non-real collectibles like Lilith's Incubus.
 */
function getPlayerCollectibleCount(player, ...collectibleTypes) {
    let numCollectibles = 0;
    for (const collectibleType of collectibleTypes) {
        // We specify "true" as the second argument to filter out things like Lilith's Incubus.
        numCollectibles += player.GetCollectibleNum(collectibleType, true);
    }
    return numCollectibles;
}
exports.getPlayerCollectibleCount = getPlayerCollectibleCount;
/**
 * Helper function to get only the players that have a certain collectible.
 *
 * This function is variadic, meaning that you can supply as many collectible types as you want to
 * check for. It only returns the players that have all of the collectibles.
 */
function getPlayersWithCollectible(...collectibleTypes) {
    const players = (0, playerIndex_1.getPlayers)();
    return players.filter((player) => collectibleTypes.every((collectibleType) => player.HasCollectible(collectibleType)));
}
exports.getPlayersWithCollectible = getPlayersWithCollectible;
/**
 * Returns the total number of collectibles amongst all players. For example, if player 1 has 1 Sad
 * Onion and player 2 has 2 Sad Onions, then this function would return 3.
 *
 * Note that this will filter out non-real collectibles like Lilith's Incubus.
 */
function getTotalPlayerCollectibles(collectibleType) {
    const players = (0, playerIndex_1.getPlayers)();
    const numCollectiblesArray = players.map((player) => 
    // We specify "true" as the second argument to filter out things like Lilith's Incubus.
    player.GetCollectibleNum(collectibleType, true));
    return (0, array_1.sumArray)(numCollectiblesArray);
}
exports.getTotalPlayerCollectibles = getTotalPlayerCollectibles;
/**
 * Helper function to check to see if a player has one or more collectibles.
 *
 * This function is variadic, meaning that you can supply as many collectible types as you want to
 * check for. Returns true if the player has any of the supplied collectible types.
 *
 * This function always passes `false` to the `ignoreModifiers` argument.
 */
function hasCollectible(player, ...collectibleTypes) {
    return collectibleTypes.some((collectibleType) => player.HasCollectible(collectibleType));
}
exports.hasCollectible = hasCollectible;
/**
 * Helper function to check to see if a player has a specific collectible in one or more active
 * slots.
 *
 * This function is variadic, meaning that you can specify as many active slots as you want to check
 * for. This function will return true if the collectible type is located in any of the active slots
 * provided.
 */
function hasCollectibleInActiveSlot(player, collectibleType, ...activeSlots) {
    const matchingActiveSlotsSet = new ReadonlySet_1.ReadonlySet(activeSlots);
    const activeItemSlots = getActiveItemSlots(player, collectibleType);
    return activeItemSlots.some((activeSlot) => matchingActiveSlotsSet.has(activeSlot));
}
exports.hasCollectibleInActiveSlot = hasCollectibleInActiveSlot;
/**
 * Returns whether the player can hold an additional active item, beyond what they are currently
 * carrying. This takes the Schoolbag into account.
 *
 * If the player is the Tainted Soul, this always returns false, since that character cannot pick up
 * items. (Only Tainted Forgotten can pick up items.)
 */
function hasOpenActiveItemSlot(player) {
    if ((0, players_1.isCharacter)(player, isaac_typescript_definitions_1.PlayerType.SOUL_B)) {
        return false;
    }
    const activeItemPrimary = player.GetActiveItem(isaac_typescript_definitions_1.ActiveSlot.PRIMARY);
    const activeItemSecondary = player.GetActiveItem(isaac_typescript_definitions_1.ActiveSlot.SECONDARY);
    const hasSchoolbag = player.HasCollectible(isaac_typescript_definitions_1.CollectibleType.SCHOOLBAG);
    if (hasSchoolbag) {
        return (activeItemPrimary === isaac_typescript_definitions_1.CollectibleType.NULL ||
            activeItemSecondary === isaac_typescript_definitions_1.CollectibleType.NULL);
    }
    return activeItemPrimary === isaac_typescript_definitions_1.CollectibleType.NULL;
}
exports.hasOpenActiveItemSlot = hasOpenActiveItemSlot;
/**
 * Helper function to check if the active slot of a particular player is empty.
 *
 * @param player The player to check.
 * @param activeSlot Optional. The active slot to check. Default is `ActiveSlot.PRIMARY`.
 */
function isActiveSlotEmpty(player, activeSlot = isaac_typescript_definitions_1.ActiveSlot.PRIMARY) {
    const activeCollectibleType = player.GetActiveItem(activeSlot);
    return activeCollectibleType === isaac_typescript_definitions_1.CollectibleType.NULL;
}
exports.isActiveSlotEmpty = isActiveSlotEmpty;
/**
 * Helper function to remove all of the active items from a player. This includes the Schoolbag item
 * and any pocket actives.
 */
function removeAllActiveItems(player) {
    for (const activeSlot of cachedEnumValues_1.ACTIVE_SLOT_VALUES) {
        const collectibleType = player.GetActiveItem(activeSlot);
        if (collectibleType === isaac_typescript_definitions_1.CollectibleType.NULL) {
            continue;
        }
        let stillHasCollectible;
        do {
            player.RemoveCollectible(collectibleType);
            stillHasCollectible = player.HasCollectible(collectibleType);
        } while (stillHasCollectible);
    }
}
exports.removeAllActiveItems = removeAllActiveItems;
/**
 * Helper function to remove one or more collectibles to a player.
 *
 * This function is variadic, meaning that you can supply as many collectible types as you want to
 * remove.
 */
function removeCollectible(player, ...collectibleTypes) {
    for (const collectibleType of collectibleTypes) {
        player.RemoveCollectible(collectibleType);
    }
}
exports.removeCollectible = removeCollectible;
/**
 * Helper function to remove a collectible costume from a player. Use this helper function to avoid
 * having to request the collectible from the item config.
 */
function removeCollectibleCostume(player, collectibleType) {
    const itemConfigItem = cachedClasses_1.itemConfig.GetCollectible(collectibleType);
    if (itemConfigItem === undefined) {
        return;
    }
    player.RemoveCostume(itemConfigItem);
}
exports.removeCollectibleCostume = removeCollectibleCostume;
/**
 * Helper function to remove one or more collectibles from all players. If any player has more than
 * one copy of the item, then all copies of it will be removed.
 *
 * This function is variadic, meaning that you can specify as many collectibles as you want to
 * remove.
 */
function removeCollectibleFromAllPlayers(...collectibleTypes) {
    for (const player of (0, playerIndex_1.getAllPlayers)()) {
        for (const collectibleType of collectibleTypes) {
            while (player.HasCollectible(collectibleType, true)) {
                player.RemoveCollectible(collectibleType);
            }
        }
    }
}
exports.removeCollectibleFromAllPlayers = removeCollectibleFromAllPlayers;
/**
 * Helper function to set an active collectible to a particular slot. This has different behavior
 * than calling the `player.AddCollectible` method with the `activeSlot` argument, because this
 * function will not shift existing items into the Schoolbag and it handles
 * `ActiveSlot.SLOT_POCKET2`.
 *
 * Note that if an item is set to `ActiveSlot.SLOT_POCKET2`, it will disappear after being used and
 * will be automatically removed upon entering a new room.
 *
 * @param player The player to give the item to.
 * @param collectibleType The collectible type of the item to give.
 * @param activeSlot Optional. The slot to set. Default is `ActiveSlot.PRIMARY`.
 * @param charge Optional. The argument of charges to set. If not specified, the item will be set
 *               with maximum charges.
 * @param keepInPools Optional. Whether to remove the item from pools. Default is false.
 */
function setActiveItem(player, collectibleType, activeSlot = isaac_typescript_definitions_1.ActiveSlot.PRIMARY, charge, keepInPools = false) {
    const itemPool = cachedClasses_1.game.GetItemPool();
    const primaryCollectibleType = player.GetActiveItem(isaac_typescript_definitions_1.ActiveSlot.PRIMARY);
    const primaryCharge = player.GetActiveCharge(isaac_typescript_definitions_1.ActiveSlot.PRIMARY);
    const secondaryCollectibleType = player.GetActiveItem(isaac_typescript_definitions_1.ActiveSlot.SECONDARY);
    if (charge === undefined) {
        charge = (0, collectibles_1.getCollectibleMaxCharges)(collectibleType);
    }
    if (!keepInPools) {
        itemPool.RemoveCollectible(collectibleType);
    }
    switch (activeSlot) {
        case isaac_typescript_definitions_1.ActiveSlot.PRIMARY: {
            // If there is a Schoolbag item, removing the primary item will shift the Schoolbag item to
            // the primary slot.
            if (primaryCollectibleType !== isaac_typescript_definitions_1.CollectibleType.NULL) {
                player.RemoveCollectible(primaryCollectibleType);
            }
            // If there was a Schoolbag item, adding a new primary item will shift it back into the
            // secondary slot.
            player.AddCollectible(collectibleType, charge, false);
            break;
        }
        case isaac_typescript_definitions_1.ActiveSlot.SECONDARY: {
            if (primaryCollectibleType !== isaac_typescript_definitions_1.CollectibleType.NULL) {
                player.RemoveCollectible(primaryCollectibleType);
            }
            if (secondaryCollectibleType !== isaac_typescript_definitions_1.CollectibleType.NULL) {
                player.RemoveCollectible(secondaryCollectibleType);
            }
            // Add the new item, which will go to the primary slot.
            player.AddCollectible(secondaryCollectibleType, charge, false);
            // Add back the original primary item, if any.
            if (primaryCollectibleType !== isaac_typescript_definitions_1.CollectibleType.NULL) {
                player.AddCollectible(primaryCollectibleType, primaryCharge, false);
            }
            break;
        }
        case isaac_typescript_definitions_1.ActiveSlot.POCKET: {
            player.SetPocketActiveItem(collectibleType, activeSlot, keepInPools);
            player.SetActiveCharge(charge, activeSlot);
            break;
        }
        case isaac_typescript_definitions_1.ActiveSlot.POCKET_SINGLE_USE: {
            player.SetPocketActiveItem(collectibleType, activeSlot, keepInPools);
            break;
        }
    }
}
exports.setActiveItem = setActiveItem;
/**
 * Helper function to use an active item without showing an animation, keeping the item, or adding
 * any costumes.
 */
function useActiveItemTemp(player, collectibleType) {
    player.UseActiveItem(collectibleType, false, false, true, false, -1);
}
exports.useActiveItemTemp = useActiveItemTemp;
