local ____lualib = require("lualib_bundle")
local __TS__ArraySome = ____lualib.__TS__ArraySome
local __TS__ArrayFilter = ____lualib.__TS__ArrayFilter
local __TS__ArrayEvery = ____lualib.__TS__ArrayEvery
local __TS__ArrayMap = ____lualib.__TS__ArrayMap
local __TS__New = ____lualib.__TS__New
local ____exports = {}
local ____isaac_2Dtypescript_2Ddefinitions = require("isaac-typescript-definitions")
local ActiveSlot = ____isaac_2Dtypescript_2Ddefinitions.ActiveSlot
local CollectibleType = ____isaac_2Dtypescript_2Ddefinitions.CollectibleType
local PlayerType = ____isaac_2Dtypescript_2Ddefinitions.PlayerType
local ____cachedEnumValues = require("arrays.cachedEnumValues")
local ACTIVE_SLOT_VALUES = ____cachedEnumValues.ACTIVE_SLOT_VALUES
local ____cachedClasses = require("core.cachedClasses")
local game = ____cachedClasses.game
local itemConfig = ____cachedClasses.itemConfig
local ____ReadonlySet = require("types.ReadonlySet")
local ReadonlySet = ____ReadonlySet.ReadonlySet
local ____array = require("functions.array")
local sumArray = ____array.sumArray
local ____collectibles = require("functions.collectibles")
local getCollectibleMaxCharges = ____collectibles.getCollectibleMaxCharges
local ____playerIndex = require("functions.playerIndex")
local getAllPlayers = ____playerIndex.getAllPlayers
local getPlayers = ____playerIndex.getPlayers
local ____players = require("functions.players")
local isCharacter = ____players.isCharacter
--- 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 ____exports.getTotalPlayerCollectibles(self, collectibleType)
    local players = getPlayers(nil)
    local numCollectiblesArray = __TS__ArrayMap(
        players,
        function(____, player) return player:GetCollectibleNum(collectibleType, true) end
    )
    return sumArray(nil, numCollectiblesArray)
end
--- 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 ____exports.addCollectible(self, player, ...)
    local collectibleTypes = {...}
    for ____, collectibleType in ipairs(collectibleTypes) do
        player:AddCollectible(collectibleType)
    end
end
function ____exports.addCollectibleCostume(self, player, collectibleType)
    local itemConfigItem = itemConfig:GetCollectible(collectibleType)
    if itemConfigItem == nil then
        return
    end
    player:AddCostume(itemConfigItem, false)
end
--- 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 ____exports.anyPlayerHasCollectible(self, collectibleType, ignoreModifiers)
    local players = getAllPlayers(nil)
    return __TS__ArraySome(
        players,
        function(____, player) return player:HasCollectible(collectibleType, ignoreModifiers) end
    )
end
--- 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 ____exports.getActiveItemSlots(self, player, collectibleType)
    return __TS__ArrayFilter(
        ACTIVE_SLOT_VALUES,
        function(____, activeSlot)
            local activeItem = player:GetActiveItem(activeSlot)
            return activeItem == collectibleType
        end
    )
end
--- 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 ____exports.getAdjustedPrice(self, basePrice)
    local numSteamSales = ____exports.getTotalPlayerCollectibles(nil, CollectibleType.STEAM_SALE)
    return numSteamSales > 0 and math.floor(basePrice / (numSteamSales + 1)) or basePrice
end
--- 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 ____exports.getPlayerCollectibleCount(self, player, ...)
    local collectibleTypes = {...}
    local numCollectibles = 0
    for ____, collectibleType in ipairs(collectibleTypes) do
        numCollectibles = numCollectibles + player:GetCollectibleNum(collectibleType, true)
    end
    return numCollectibles
end
--- 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 ____exports.getPlayersWithCollectible(self, ...)
    local collectibleTypes = {...}
    local players = getPlayers(nil)
    return __TS__ArrayFilter(
        players,
        function(____, player) return __TS__ArrayEvery(
            collectibleTypes,
            function(____, collectibleType) return player:HasCollectible(collectibleType) end
        ) end
    )
end
--- 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 ____exports.hasCollectible(self, player, ...)
    local collectibleTypes = {...}
    return __TS__ArraySome(
        collectibleTypes,
        function(____, collectibleType) return player:HasCollectible(collectibleType) end
    )
end
--- 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 ____exports.hasCollectibleInActiveSlot(self, player, collectibleType, ...)
    local activeSlots = {...}
    local matchingActiveSlotsSet = __TS__New(ReadonlySet, activeSlots)
    local activeItemSlots = ____exports.getActiveItemSlots(nil, player, collectibleType)
    return __TS__ArraySome(
        activeItemSlots,
        function(____, activeSlot) return matchingActiveSlotsSet:has(activeSlot) end
    )
end
--- 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 ____exports.hasOpenActiveItemSlot(self, player)
    if isCharacter(nil, player, PlayerType.SOUL_B) then
        return false
    end
    local activeItemPrimary = player:GetActiveItem(ActiveSlot.PRIMARY)
    local activeItemSecondary = player:GetActiveItem(ActiveSlot.SECONDARY)
    local hasSchoolbag = player:HasCollectible(CollectibleType.SCHOOLBAG)
    if hasSchoolbag then
        return activeItemPrimary == CollectibleType.NULL or activeItemSecondary == CollectibleType.NULL
    end
    return activeItemPrimary == CollectibleType.NULL
end
--- 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 ____exports.isActiveSlotEmpty(self, player, activeSlot)
    if activeSlot == nil then
        activeSlot = ActiveSlot.PRIMARY
    end
    local activeCollectibleType = player:GetActiveItem(activeSlot)
    return activeCollectibleType == CollectibleType.NULL
end
--- Helper function to remove all of the active items from a player. This includes the Schoolbag item
-- and any pocket actives.
function ____exports.removeAllActiveItems(self, player)
    for ____, activeSlot in ipairs(ACTIVE_SLOT_VALUES) do
        do
            local collectibleType = player:GetActiveItem(activeSlot)
            if collectibleType == CollectibleType.NULL then
                goto __continue29
            end
            local stillHasCollectible
            repeat
                do
                    player:RemoveCollectible(collectibleType)
                    stillHasCollectible = player:HasCollectible(collectibleType)
                end
            until not stillHasCollectible
        end
        ::__continue29::
    end
end
--- 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 ____exports.removeCollectible(self, player, ...)
    local collectibleTypes = {...}
    for ____, collectibleType in ipairs(collectibleTypes) do
        player:RemoveCollectible(collectibleType)
    end
end
--- 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 ____exports.removeCollectibleCostume(self, player, collectibleType)
    local itemConfigItem = itemConfig:GetCollectible(collectibleType)
    if itemConfigItem == nil then
        return
    end
    player:RemoveCostume(itemConfigItem)
end
--- 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 ____exports.removeCollectibleFromAllPlayers(self, ...)
    local collectibleTypes = {...}
    for ____, player in ipairs(getAllPlayers(nil)) do
        for ____, collectibleType in ipairs(collectibleTypes) do
            while player:HasCollectible(collectibleType, true) do
                player:RemoveCollectible(collectibleType)
            end
        end
    end
end
--- 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 ____exports.setActiveItem(self, player, collectibleType, activeSlot, charge, keepInPools)
    if activeSlot == nil then
        activeSlot = ActiveSlot.PRIMARY
    end
    if keepInPools == nil then
        keepInPools = false
    end
    local itemPool = game:GetItemPool()
    local primaryCollectibleType = player:GetActiveItem(ActiveSlot.PRIMARY)
    local primaryCharge = player:GetActiveCharge(ActiveSlot.PRIMARY)
    local secondaryCollectibleType = player:GetActiveItem(ActiveSlot.SECONDARY)
    if charge == nil then
        charge = getCollectibleMaxCharges(nil, collectibleType)
    end
    if not keepInPools then
        itemPool:RemoveCollectible(collectibleType)
    end
    repeat
        local ____switch47 = activeSlot
        local ____cond47 = ____switch47 == ActiveSlot.PRIMARY
        if ____cond47 then
            do
                if primaryCollectibleType ~= CollectibleType.NULL then
                    player:RemoveCollectible(primaryCollectibleType)
                end
                player:AddCollectible(collectibleType, charge, false)
                break
            end
        end
        ____cond47 = ____cond47 or ____switch47 == ActiveSlot.SECONDARY
        if ____cond47 then
            do
                if primaryCollectibleType ~= CollectibleType.NULL then
                    player:RemoveCollectible(primaryCollectibleType)
                end
                if secondaryCollectibleType ~= CollectibleType.NULL then
                    player:RemoveCollectible(secondaryCollectibleType)
                end
                player:AddCollectible(secondaryCollectibleType, charge, false)
                if primaryCollectibleType ~= CollectibleType.NULL then
                    player:AddCollectible(primaryCollectibleType, primaryCharge, false)
                end
                break
            end
        end
        ____cond47 = ____cond47 or ____switch47 == ActiveSlot.POCKET
        if ____cond47 then
            do
                player:SetPocketActiveItem(collectibleType, activeSlot, keepInPools)
                player:SetActiveCharge(charge, activeSlot)
                break
            end
        end
        ____cond47 = ____cond47 or ____switch47 == ActiveSlot.POCKET_SINGLE_USE
        if ____cond47 then
            do
                player:SetPocketActiveItem(collectibleType, activeSlot, keepInPools)
                break
            end
        end
    until true
end
--- Helper function to use an active item without showing an animation, keeping the item, or adding
-- any costumes.
function ____exports.useActiveItemTemp(self, player, collectibleType)
    player:UseActiveItem(
        collectibleType,
        false,
        false,
        true,
        false,
        -1
    )
end
return ____exports
