local ____lualib = require("lualib_bundle")
local __TS__New = ____lualib.__TS__New
local __TS__ArraySome = ____lualib.__TS__ArraySome
local __TS__ArrayFilter = ____lualib.__TS__ArrayFilter
local __TS__StringSplit = ____lualib.__TS__StringSplit
local Set = ____lualib.Set
local Map = ____lualib.Map
local ____exports = {}
local getAllGridEntities, getGridEntityANM2Name, getGridEntityANM2NameDecoration, getRockPNGName
local ____isaac_2Dtypescript_2Ddefinitions = require("isaac-typescript-definitions")
local BackdropType = ____isaac_2Dtypescript_2Ddefinitions.BackdropType
local EffectVariant = ____isaac_2Dtypescript_2Ddefinitions.EffectVariant
local GridCollisionClass = ____isaac_2Dtypescript_2Ddefinitions.GridCollisionClass
local GridEntityType = ____isaac_2Dtypescript_2Ddefinitions.GridEntityType
local PoopGridEntityVariant = ____isaac_2Dtypescript_2Ddefinitions.PoopGridEntityVariant
local StatueVariant = ____isaac_2Dtypescript_2Ddefinitions.StatueVariant
local TrapdoorVariant = ____isaac_2Dtypescript_2Ddefinitions.TrapdoorVariant
local ____cachedEnumValues = require("arrays.cachedEnumValues")
local GRID_ENTITY_XML_TYPE_VALUES = ____cachedEnumValues.GRID_ENTITY_XML_TYPE_VALUES
local ____cachedClasses = require("core.cachedClasses")
local game = ____cachedClasses.game
local ____constants = require("core.constants")
local DISTANCE_OF_GRID_TILE = ____constants.DISTANCE_OF_GRID_TILE
local VectorOne = ____constants.VectorOne
local ____gridEntityTypeToBrokenStateMap = require("maps.gridEntityTypeToBrokenStateMap")
local GRID_ENTITY_TYPE_TO_BROKEN_STATE_MAP = ____gridEntityTypeToBrokenStateMap.GRID_ENTITY_TYPE_TO_BROKEN_STATE_MAP
local ____gridEntityXMLMap = require("maps.gridEntityXMLMap")
local GRID_ENTITY_XML_MAP = ____gridEntityXMLMap.GRID_ENTITY_XML_MAP
local ____roomShapeToTopLeftWallGridIndexMap = require("maps.roomShapeToTopLeftWallGridIndexMap")
local DEFAULT_TOP_LEFT_WALL_GRID_INDEX = ____roomShapeToTopLeftWallGridIndexMap.DEFAULT_TOP_LEFT_WALL_GRID_INDEX
local ROOM_SHAPE_TO_TOP_LEFT_WALL_GRID_INDEX_MAP = ____roomShapeToTopLeftWallGridIndexMap.ROOM_SHAPE_TO_TOP_LEFT_WALL_GRID_INDEX_MAP
local ____gridEntityTypeToANM2Name = require("objects.gridEntityTypeToANM2Name")
local GRID_ENTITY_TYPE_TO_ANM2_NAME = ____gridEntityTypeToANM2Name.GRID_ENTITY_TYPE_TO_ANM2_NAME
local ____poopGridEntityXMLTypesSet = require("sets.poopGridEntityXMLTypesSet")
local POOP_GRID_ENTITY_XML_TYPES_SET = ____poopGridEntityXMLTypesSet.POOP_GRID_ENTITY_XML_TYPES_SET
local ____ReadonlySet = require("types.ReadonlySet")
local ReadonlySet = ____ReadonlySet.ReadonlySet
local ____entities = require("functions.entities")
local removeEntities = ____entities.removeEntities
local ____entitiesSpecific = require("functions.entitiesSpecific")
local getEffects = ____entitiesSpecific.getEffects
local ____math = require("functions.math")
local isCircleIntersectingRectangle = ____math.isCircleIntersectingRectangle
local ____rooms = require("functions.rooms")
local roomUpdateSafe = ____rooms.roomUpdateSafe
local ____types = require("functions.types")
local asNumber = ____types.asNumber
local isInteger = ____types.isInteger
local ____utils = require("functions.utils")
local assertDefined = ____utils.assertDefined
local eRange = ____utils.eRange
local iRange = ____utils.iRange
local ____vector = require("functions.vector")
local isVector = ____vector.isVector
local vectorEquals = ____vector.vectorEquals
--- Helper function to get every legal grid index for the current room.
-- 
-- Under the hood, this uses the `Room.GetGridSize` method.
function ____exports.getAllGridIndexes(self)
    local room = game:GetRoom()
    local gridSize = room:GetGridSize()
    return eRange(nil, gridSize)
end
function getAllGridEntities(self)
    local room = game:GetRoom()
    local gridEntities = {}
    for ____, gridIndex in ipairs(____exports.getAllGridIndexes(nil)) do
        local gridEntity = room:GetGridEntity(gridIndex)
        if gridEntity ~= nil then
            gridEntities[#gridEntities + 1] = gridEntity
        end
    end
    return gridEntities
end
function getGridEntityANM2Name(self, gridEntityType)
    repeat
        local ____switch33 = gridEntityType
        local ____cond33 = ____switch33 == GridEntityType.DECORATION
        if ____cond33 then
            do
                return getGridEntityANM2NameDecoration(nil)
            end
        end
        do
            do
                return GRID_ENTITY_TYPE_TO_ANM2_NAME[gridEntityType]
            end
        end
    until true
end
function getGridEntityANM2NameDecoration(self)
    local room = game:GetRoom()
    local backdropType = room:GetBackdropType()
    repeat
        local ____switch37 = backdropType
        local ____cond37 = ____switch37 == BackdropType.BASEMENT or ____switch37 == BackdropType.CELLAR or ____switch37 == BackdropType.BURNING_BASEMENT or ____switch37 == BackdropType.DOWNPOUR_ENTRANCE or ____switch37 == BackdropType.ISAACS_BEDROOM or ____switch37 == BackdropType.CLOSET
        if ____cond37 then
            do
                return "Props_01_Basement.anm2"
            end
        end
        ____cond37 = ____cond37 or (____switch37 == BackdropType.CAVES or ____switch37 == BackdropType.CATACOMBS or ____switch37 == BackdropType.FLOODED_CAVES or ____switch37 == BackdropType.MINES_ENTRANCE)
        if ____cond37 then
            do
                return "Props_03_Caves.anm2"
            end
        end
        ____cond37 = ____cond37 or (____switch37 == BackdropType.DEPTHS or ____switch37 == BackdropType.NECROPOLIS or ____switch37 == BackdropType.DANK_DEPTHS or ____switch37 == BackdropType.SACRIFICE or ____switch37 == BackdropType.MAUSOLEUM or ____switch37 == BackdropType.MAUSOLEUM_ENTRANCE or ____switch37 == BackdropType.CORPSE_ENTRANCE or ____switch37 == BackdropType.MAUSOLEUM_2 or ____switch37 == BackdropType.MAUSOLEUM_3 or ____switch37 == BackdropType.MAUSOLEUM_4 or ____switch37 == BackdropType.CLOSET_B or ____switch37 == BackdropType.DARK_CLOSET)
        if ____cond37 then
            do
                return "Props_05_Depths.anm2"
            end
        end
        ____cond37 = ____cond37 or (____switch37 == BackdropType.WOMB or ____switch37 == BackdropType.SCARRED_WOMB)
        if ____cond37 then
            do
                return "Props_07_The Womb.anm2"
            end
        end
        ____cond37 = ____cond37 or ____switch37 == BackdropType.UTERO
        if ____cond37 then
            do
                return "Props_07_Utero.anm2"
            end
        end
        ____cond37 = ____cond37 or (____switch37 == BackdropType.BLUE_WOMB or ____switch37 == BackdropType.BLUE_WOMB_PASS)
        if ____cond37 then
            do
                return "Props_07_The Womb_blue.anm2"
            end
        end
        ____cond37 = ____cond37 or (____switch37 == BackdropType.SHEOL or ____switch37 == BackdropType.GEHENNA)
        if ____cond37 then
            do
                return "Props_09_Sheol.anm2"
            end
        end
        ____cond37 = ____cond37 or ____switch37 == BackdropType.CATHEDRAL
        if ____cond37 then
            do
                return "Props_10_Cathedral.anm2"
            end
        end
        ____cond37 = ____cond37 or ____switch37 == BackdropType.CHEST
        if ____cond37 then
            do
                return "Props_11_The Chest.anm2"
            end
        end
        ____cond37 = ____cond37 or ____switch37 == BackdropType.GREED_SHOP
        if ____cond37 then
            do
                return "Props_12_Greed.anm2"
            end
        end
        ____cond37 = ____cond37 or ____switch37 == BackdropType.DOWNPOUR
        if ____cond37 then
            do
                return "props_01x_downpour.anm2"
            end
        end
        ____cond37 = ____cond37 or (____switch37 == BackdropType.MINES or ____switch37 == BackdropType.ASHPIT or ____switch37 == BackdropType.MINES_SHAFT or ____switch37 == BackdropType.ASHPIT_SHAFT)
        if ____cond37 then
            do
                return "props_03x_mines.anm2"
            end
        end
        ____cond37 = ____cond37 or (____switch37 == BackdropType.CORPSE or ____switch37 == BackdropType.CORPSE_2 or ____switch37 == BackdropType.CORPSE_3 or ____switch37 == BackdropType.MORTIS)
        if ____cond37 then
            do
                return "props_07_the corpse.anm2"
            end
        end
        ____cond37 = ____cond37 or ____switch37 == BackdropType.DROSS
        if ____cond37 then
            do
                return "props_02x_dross.anm2"
            end
        end
        do
            do
                return "Props_01_Basement.anm2"
            end
        end
    until true
end
--- Helper function to get the top left and bottom right corners of a given grid entity.
function ____exports.getGridEntityCollisionPoints(self, gridEntity)
    local topLeft = Vector(gridEntity.Position.X - DISTANCE_OF_GRID_TILE / 2, gridEntity.Position.Y - DISTANCE_OF_GRID_TILE / 2)
    local bottomRight = Vector(gridEntity.Position.X + DISTANCE_OF_GRID_TILE / 2, gridEntity.Position.Y + DISTANCE_OF_GRID_TILE / 2)
    return {topLeft = topLeft, bottomRight = bottomRight}
end
function getRockPNGName(self)
    local room = game:GetRoom()
    local backdropType = room:GetBackdropType()
    repeat
        local ____switch60 = backdropType
        local ____cond60 = ____switch60 == BackdropType.BASEMENT or ____switch60 == BackdropType.CHEST
        if ____cond60 then
            do
                return "rocks_basement.png"
            end
        end
        ____cond60 = ____cond60 or ____switch60 == BackdropType.CELLAR
        if ____cond60 then
            do
                return "rocks_cellar.png"
            end
        end
        ____cond60 = ____cond60 or ____switch60 == BackdropType.BURNING_BASEMENT
        if ____cond60 then
            do
                return "rocks_burningbasement.png"
            end
        end
        ____cond60 = ____cond60 or ____switch60 == BackdropType.CAVES
        if ____cond60 then
            do
                return "rocks_caves.png"
            end
        end
        ____cond60 = ____cond60 or ____switch60 == BackdropType.CATACOMBS
        if ____cond60 then
            do
                return "rocks_catacombs.png"
            end
        end
        ____cond60 = ____cond60 or ____switch60 == BackdropType.FLOODED_CAVES
        if ____cond60 then
            do
                return "rocks_drownedcaves.png"
            end
        end
        ____cond60 = ____cond60 or (____switch60 == BackdropType.DEPTHS or ____switch60 == BackdropType.NECROPOLIS or ____switch60 == BackdropType.DANK_DEPTHS or ____switch60 == BackdropType.SACRIFICE or ____switch60 == BackdropType.DARK_CLOSET)
        if ____cond60 then
            do
                return "rocks_depths.png"
            end
        end
        ____cond60 = ____cond60 or ____switch60 == BackdropType.WOMB
        if ____cond60 then
            do
                return "rocks_womb.png"
            end
        end
        ____cond60 = ____cond60 or ____switch60 == BackdropType.UTERO
        if ____cond60 then
            do
                return "rocks_utero.png"
            end
        end
        ____cond60 = ____cond60 or ____switch60 == BackdropType.SCARRED_WOMB
        if ____cond60 then
            do
                return "rocks_scarredwomb.png"
            end
        end
        ____cond60 = ____cond60 or (____switch60 == BackdropType.BLUE_WOMB or ____switch60 == BackdropType.BLUE_WOMB_PASS)
        if ____cond60 then
            do
                return "rocks_bluewomb.png"
            end
        end
        ____cond60 = ____cond60 or (____switch60 == BackdropType.SHEOL or ____switch60 == BackdropType.DARK_ROOM)
        if ____cond60 then
            do
                return "rocks_sheol.png"
            end
        end
        ____cond60 = ____cond60 or (____switch60 == BackdropType.CATHEDRAL or ____switch60 == BackdropType.PLANETARIUM)
        if ____cond60 then
            do
                return "rocks_cathedral.png"
            end
        end
        ____cond60 = ____cond60 or (____switch60 == BackdropType.SECRET or ____switch60 == BackdropType.MINES or ____switch60 == BackdropType.MINES_ENTRANCE or ____switch60 == BackdropType.MINES_SHAFT)
        if ____cond60 then
            do
                return "rocks_secretroom.png"
            end
        end
        ____cond60 = ____cond60 or (____switch60 == BackdropType.DOWNPOUR or ____switch60 == BackdropType.DOWNPOUR_ENTRANCE)
        if ____cond60 then
            do
                return "rocks_downpour.png"
            end
        end
        ____cond60 = ____cond60 or (____switch60 == BackdropType.MAUSOLEUM or ____switch60 == BackdropType.MAUSOLEUM_ENTRANCE or ____switch60 == BackdropType.MAUSOLEUM_2 or ____switch60 == BackdropType.MAUSOLEUM_3 or ____switch60 == BackdropType.MAUSOLEUM_4)
        if ____cond60 then
            do
                return "rocks_mausoleum.png"
            end
        end
        ____cond60 = ____cond60 or (____switch60 == BackdropType.CORPSE or ____switch60 == BackdropType.MORTIS)
        if ____cond60 then
            do
                return "rocks_corpse.png"
            end
        end
        ____cond60 = ____cond60 or ____switch60 == BackdropType.CORPSE_ENTRANCE
        if ____cond60 then
            do
                return "rocks_corpseentrance.png"
            end
        end
        ____cond60 = ____cond60 or ____switch60 == BackdropType.CORPSE_2
        if ____cond60 then
            do
                return "rocks_corpse2.png"
            end
        end
        ____cond60 = ____cond60 or ____switch60 == BackdropType.CORPSE_3
        if ____cond60 then
            do
                return "rocks_corpse3.png"
            end
        end
        ____cond60 = ____cond60 or ____switch60 == BackdropType.DROSS
        if ____cond60 then
            do
                return "rocks_dross.png"
            end
        end
        ____cond60 = ____cond60 or (____switch60 == BackdropType.ASHPIT or ____switch60 == BackdropType.ASHPIT_SHAFT)
        if ____cond60 then
            do
                return "rocks_ashpit.png"
            end
        end
        ____cond60 = ____cond60 or ____switch60 == BackdropType.GEHENNA
        if ____cond60 then
            do
                return "rocks_gehenna.png"
            end
        end
        do
            do
                return "rocks_basement.png"
            end
        end
    until true
end
--- Helper function to get the grid indexes on the surrounding tiles from the provided grid index.
-- 
-- There are always 8 grid indexes returned (e.g. top-left + top + top-right + left + right +
-- bottom-left + bottom + right), even if the computed values would be negative or otherwise
-- invalid.
function ____exports.getSurroundingGridIndexes(self, gridIndex)
    local room = game:GetRoom()
    local gridWidth = room:GetGridWidth()
    return {
        gridIndex - gridWidth - 1,
        gridIndex - gridWidth,
        gridIndex - gridWidth + 1,
        gridIndex - 1,
        gridIndex + 1,
        gridIndex + gridWidth - 1,
        gridIndex + gridWidth,
        gridIndex + gridWidth + 1
    }
end
--- Helper function to get the grid index of the top left wall. (This will depend on what the current
-- room shape is.)
-- 
-- This function can be useful in certain situations to determine if the room is currently loaded.
function ____exports.getTopLeftWallGridIndex(self)
    local room = game:GetRoom()
    local roomShape = room:GetRoomShape()
    local topLeftWallGridIndex = ROOM_SHAPE_TO_TOP_LEFT_WALL_GRID_INDEX_MAP:get(roomShape)
    return topLeftWallGridIndex or DEFAULT_TOP_LEFT_WALL_GRID_INDEX
end
--- Helper function to remove a grid entity by providing the grid entity object or the grid index
-- inside of the room.
-- 
-- If removing a Devil Statue or an Angel Statue, this will also remove the associated effect
-- (`EffectVariant.DEVIL` (6) or `EffectVariant.ANGEL` (9), respectively.)
-- 
-- @param gridEntityOrGridIndex The grid entity or grid index to remove.
-- @param updateRoom Whether to update the room after the grid entity is removed. This is generally
-- a good idea because if the room is not updated, you will be unable to spawn
-- another grid entity on the same tile until a frame has passed. However, doing
-- this is expensive, since it involves a call to `Isaac.GetRoomEntities`, so set
-- this to false if you need to run this function multiple times.
function ____exports.removeGridEntity(self, gridEntityOrGridIndex, updateRoom)
    local room = game:GetRoom()
    local ____isInteger_result_2
    if isInteger(nil, gridEntityOrGridIndex) then
        ____isInteger_result_2 = room:GetGridEntity(gridEntityOrGridIndex)
    else
        ____isInteger_result_2 = gridEntityOrGridIndex
    end
    local gridEntity = ____isInteger_result_2
    if gridEntity == nil then
        return
    end
    local gridEntityType = gridEntity:GetType()
    local variant = gridEntity:GetVariant()
    local position = gridEntity.Position
    local gridIndex = isInteger(nil, gridEntityOrGridIndex) and gridEntityOrGridIndex or gridEntityOrGridIndex:GetGridIndex()
    room:RemoveGridEntity(gridIndex, 0, false)
    if updateRoom then
        roomUpdateSafe(nil)
    end
    if gridEntityType == GridEntityType.STATUE then
        local effectVariant = variant == asNumber(nil, StatueVariant.DEVIL) and EffectVariant.DEVIL or EffectVariant.ANGEL
        local effects = getEffects(nil, effectVariant)
        local effectsOnTile = __TS__ArrayFilter(
            effects,
            function(____, effect) return vectorEquals(nil, effect.Position, position) end
        )
        removeEntities(nil, effectsOnTile)
    end
end
--- Helper function to spawn a grid entity with a specific variant.
-- 
-- Use this instead of the `Isaac.GridSpawn` method since it:
-- - handles giving pits collision
-- - removes existing grid entities on the same tile, if any
-- - allows you to specify the grid index or the position
-- 
-- @param gridEntityType The `GridEntityType` to use.
-- @param variant The variant to use.
-- @param gridIndexOrPosition The grid index or position in the room that you want to spawn the grid
-- entity at. If a position is specified, the closest grid index will be
-- used.
-- @param removeExistingGridEntity Optional. Whether to remove the existing grid entity on the same
-- tile, if it exists. Defaults to true. If false, this function
-- will do nothing, since spawning a grid entity on top of another
-- grid entity will not replace it.
function ____exports.spawnGridEntityWithVariant(self, gridEntityType, variant, gridIndexOrPosition, removeExistingGridEntity)
    if removeExistingGridEntity == nil then
        removeExistingGridEntity = true
    end
    local room = game:GetRoom()
    local ____isVector_result_3
    if isVector(nil, gridIndexOrPosition) then
        ____isVector_result_3 = room:GetGridEntityFromPos(gridIndexOrPosition)
    else
        ____isVector_result_3 = room:GetGridEntity(gridIndexOrPosition)
    end
    local existingGridEntity = ____isVector_result_3
    if existingGridEntity ~= nil then
        if removeExistingGridEntity then
            ____exports.removeGridEntity(nil, existingGridEntity, true)
        else
            return
        end
    end
    local position = isVector(nil, gridIndexOrPosition) and gridIndexOrPosition or room:GetGridPosition(gridIndexOrPosition)
    local gridEntity = Isaac.GridSpawn(gridEntityType, variant, position)
    if gridEntity == nil then
        return gridEntity
    end
    if gridEntityType == GridEntityType.PIT then
        local pit = gridEntity:ToPit()
        if pit ~= nil then
            pit:UpdateCollision()
        end
    elseif gridEntityType == GridEntityType.WALL then
        gridEntity.CollisionClass = GridCollisionClass.WALL
    end
    return gridEntity
end
--- For some specific grid entities, the variant defined in the XML is what is used by the actual
-- game (which is not the case for e.g. poops).
local GRID_ENTITY_TYPES_THAT_KEEP_GRID_ENTITY_XML_VARIANT = __TS__New(ReadonlySet, {GridEntityType.SPIKES_ON_OFF, GridEntityType.PRESSURE_PLATE, GridEntityType.TELEPORTER})
local BREAKABLE_GRID_ENTITY_TYPES_BY_EXPLOSIONS = __TS__New(ReadonlySet, {
    GridEntityType.ROCK,
    GridEntityType.ROCK_TINTED,
    GridEntityType.ROCK_BOMB,
    GridEntityType.ROCK_ALT,
    GridEntityType.SPIDER_WEB,
    GridEntityType.TNT,
    GridEntityType.POOP,
    GridEntityType.ROCK_SUPER_SPECIAL,
    GridEntityType.ROCK_SPIKED,
    GridEntityType.ROCK_ALT_2,
    GridEntityType.ROCK_GOLD
})
local BREAKABLE_GRID_ENTITY_TYPES_VARIANTS_BY_EXPLOSIONS = __TS__New(
    ReadonlySet,
    {(tostring(GridEntityType.STATUE) .. ".") .. tostring(StatueVariant.ANGEL)}
)
local GRID_ENTITY_XML_TYPES_SET = __TS__New(ReadonlySet, GRID_ENTITY_XML_TYPE_VALUES)
--- Helper function to convert the grid entity type found in a room XML file to the corresponding
-- grid entity type and variant normally used by the game. For example, `GridEntityXMLType.ROCK` is
-- 1000 (in a room XML file), but `GridEntityType.ROCK` is equal to 2 (in-game).
function ____exports.convertXMLGridEntityType(self, gridEntityXMLType, gridEntityXMLVariant)
    local gridEntityArray = GRID_ENTITY_XML_MAP:get(gridEntityXMLType)
    assertDefined(
        nil,
        gridEntityArray,
        "Failed to find an entry in the grid entity map for XML entity type: " .. tostring(gridEntityXMLType)
    )
    local gridEntityType = gridEntityArray[1]
    local variant = GRID_ENTITY_TYPES_THAT_KEEP_GRID_ENTITY_XML_VARIANT:has(gridEntityType) and gridEntityXMLVariant or gridEntityArray[2]
    return {gridEntityType, variant}
end
--- Helper function to check if one or more of a specific kind of grid entity is present in the
-- current room.
-- 
-- @param gridEntityType The grid entity type to match.
-- @param variant Optional. Default is -1, which matches every variant.
function ____exports.doesGridEntityExist(self, gridEntityType, variant)
    if variant == nil then
        variant = -1
    end
    local room = game:GetRoom()
    local gridIndexes = ____exports.getAllGridIndexes(nil)
    return __TS__ArraySome(
        gridIndexes,
        function(____, gridIndex)
            local gridEntity = room:GetGridEntity(gridIndex)
            if gridEntity == nil then
                return false
            end
            local thisGridEntityType = gridEntity:GetType()
            local thisVariant = gridEntity:GetVariant()
            return gridEntityType == thisGridEntityType and (variant == -1 or variant == thisVariant)
        end
    )
end
--- Gets the entities that have a hitbox that overlaps with any part of the square that the grid
-- entity is on.
-- 
-- This function is useful because the vanilla collision callbacks do not work with grid entities.
-- This is used by `POST_GRID_ENTITY_COLLISION` custom callback.
-- 
-- Note that this function will not work properly in the `POST_NEW_ROOM` callback since entities do
-- not have collision yet in that callback.
function ____exports.getCollidingEntitiesWithGridEntity(self, gridEntity)
    local ____exports_getGridEntityCollisionPoints_result_0 = ____exports.getGridEntityCollisionPoints(nil, gridEntity)
    local topLeft = ____exports_getGridEntityCollisionPoints_result_0.topLeft
    local bottomRight = ____exports_getGridEntityCollisionPoints_result_0.bottomRight
    local closeEntities = Isaac.FindInRadius(gridEntity.Position, DISTANCE_OF_GRID_TILE * 2)
    return __TS__ArrayFilter(
        closeEntities,
        function(____, entity) return entity:CollidesWithGrid() and isCircleIntersectingRectangle(
            nil,
            entity.Position,
            entity.Size + 0.1,
            topLeft,
            bottomRight
        ) end
    )
end
--- Helper function to get the grid entity type and variant from a `GridEntityID`.
function ____exports.getConstituentsFromGridEntityID(self, gridEntityID)
    local parts = __TS__StringSplit(gridEntityID, ".")
    if #parts ~= 2 then
        error("Failed to get the constituents from grid entity ID: " .. gridEntityID)
    end
    local gridEntityTypeString, variantString = table.unpack(parts)
    local gridEntityType = tonumber(gridEntityTypeString)
    assertDefined(
        nil,
        gridEntityType,
        "Failed to convert the grid entity type to a number: " .. tostring(gridEntityTypeString)
    )
    local variant = tonumber(variantString)
    assertDefined(
        nil,
        variant,
        "Failed to convert the grid entity variant to a number: " .. tostring(variantString)
    )
    return {gridEntityType, variant}
end
--- Helper function to get every grid entity in the current room.
-- 
-- Use this function with no arguments to get every grid entity, or specify a variadic amount of
-- arguments to match specific grid entity types.
-- 
-- For example:
-- 
-- ```ts
-- for (const gridEntity of getGridEntities()) {
--   print(gridEntity.GetType())
-- }
-- ```
-- 
-- For example:
-- 
-- ```ts
-- const rocks = getGridEntities(
--   GridEntityType.ROCK,
--   GridEntityType.BLOCK,
--   GridEntityType.ROCK_TINTED,
-- );
-- ```
-- 
-- @allowEmptyVariadic
function ____exports.getGridEntities(self, ...)
    local gridEntityTypes = {...}
    local gridEntities = getAllGridEntities(nil)
    if #gridEntityTypes == 0 then
        return gridEntities
    end
    local gridEntityTypesSet = __TS__New(ReadonlySet, gridEntityTypes)
    return __TS__ArrayFilter(
        gridEntities,
        function(____, gridEntity)
            local gridEntityType = gridEntity:GetType()
            return gridEntityTypesSet:has(gridEntityType)
        end
    )
end
--- Helper function to get every grid entity in the current room except for certain specific types.
-- 
-- This function is variadic, meaning that you can specify as many grid entity types as you want to
-- exclude.
function ____exports.getGridEntitiesExcept(self, ...)
    local gridEntityTypes = {...}
    local gridEntities = getAllGridEntities(nil)
    if #gridEntityTypes == 0 then
        return gridEntities
    end
    local gridEntityTypesSet = __TS__New(ReadonlySet, gridEntityTypes)
    return __TS__ArrayFilter(
        gridEntities,
        function(____, gridEntity)
            local gridEntityType = gridEntity:GetType()
            return not gridEntityTypesSet:has(gridEntityType)
        end
    )
end
--- Helper function to get all grid entities in a given radius around a given point.
function ____exports.getGridEntitiesInRadius(self, targetPosition, radius)
    radius = math.abs(radius)
    local topLeftOffset = VectorOne * -radius
    local mostTopLeftPosition = targetPosition + topLeftOffset
    local room = game:GetRoom()
    local diameter = radius * 2
    local iterations = math.ceil(diameter / DISTANCE_OF_GRID_TILE)
    local separation = diameter / iterations
    local gridEntities = {}
    local registeredGridIndexes = __TS__New(Set)
    for ____, x in ipairs(iRange(nil, iterations)) do
        for ____, y in ipairs(iRange(nil, iterations)) do
            do
                local position = mostTopLeftPosition + Vector(x * separation, y * separation)
                local gridIndex = room:GetGridIndex(position)
                local gridEntity = room:GetGridEntityFromPos(position)
                if gridEntity == nil or registeredGridIndexes:has(gridIndex) then
                    goto __continue23
                end
                registeredGridIndexes:add(gridIndex)
                local ____exports_getGridEntityCollisionPoints_result_1 = ____exports.getGridEntityCollisionPoints(nil, gridEntity)
                local topLeft = ____exports_getGridEntityCollisionPoints_result_1.topLeft
                local bottomRight = ____exports_getGridEntityCollisionPoints_result_1.bottomRight
                if isCircleIntersectingRectangle(
                    nil,
                    targetPosition,
                    radius,
                    topLeft,
                    bottomRight
                ) then
                    gridEntities[#gridEntities + 1] = gridEntity
                end
            end
            ::__continue23::
        end
    end
    return gridEntities
end
--- Helper function to get a map of every grid entity in the current room. The indexes of the map are
-- equal to the grid index. The values of the map are equal to the grid entities.
-- 
-- Use this function with no arguments to get every grid entity, or specify a variadic amount of
-- arguments to match specific grid entity types.
-- 
-- @allowEmptyVariadic
function ____exports.getGridEntitiesMap(self, ...)
    local gridEntities = ____exports.getGridEntities(nil, ...)
    local gridEntityMap = __TS__New(Map)
    for ____, gridEntity in ipairs(gridEntities) do
        local gridIndex = gridEntity:GetGridIndex()
        gridEntityMap:set(gridIndex, gridEntity)
    end
    return gridEntityMap
end
--- Helper function to get the ANM2 path for a grid entity type.
function ____exports.getGridEntityANM2Path(self, gridEntityType)
    local gridEntityANM2Name = getGridEntityANM2Name(nil, gridEntityType)
    return "gfx/grid/" .. tostring(gridEntityANM2Name)
end
--- Helper function to get a string containing the grid entity's type and variant.
function ____exports.getGridEntityID(self, gridEntity)
    local gridEntityType = gridEntity:GetType()
    local variant = gridEntity:GetVariant()
    return (tostring(gridEntityType) .. ".") .. tostring(variant)
end
--- Helper function to get a formatted string in the format returned by the `getGridEntityID`
-- function.
function ____exports.getGridEntityIDFromConstituents(self, gridEntityType, variant)
    return (tostring(gridEntityType) .. ".") .. tostring(variant)
end
--- Helper function to get all of the grid entities in the room that specifically match the type and
-- variant provided.
-- 
-- If you want to match every variant, use the `getGridEntities` function instead.
function ____exports.getMatchingGridEntities(self, gridEntityType, variant)
    local gridEntities = ____exports.getGridEntities(nil, gridEntityType)
    return __TS__ArrayFilter(
        gridEntities,
        function(____, gridEntity) return gridEntity:GetVariant() == variant end
    )
end
--- Helper function to get the PNG path for a rock. This depends on the current room's backdrop. The
-- values are taken from the "backdrops.xml" file.
-- 
-- All of the rock PNGs are in the "gfx/grid" directory.
function ____exports.getRockPNGPath(self)
    local rockPNGName = getRockPNGName(nil)
    return "gfx/grid/" .. rockPNGName
end
--- Helper function to get the grid entities on the surrounding tiles from the provided grid entity.
-- 
-- For example, if a rock was surrounded by rocks on all sides, this would return an array of 8
-- rocks (e.g. top-left + top + top-right + left + right + bottom-left + bottom + right).
function ____exports.getSurroundingGridEntities(self, gridEntity)
    local room = game:GetRoom()
    local gridIndex = gridEntity:GetGridIndex()
    local surroundingGridIndexes = ____exports.getSurroundingGridIndexes(nil, gridIndex)
    local surroundingGridEntities = {}
    for ____, surroundingGridIndex in ipairs(surroundingGridIndexes) do
        local surroundingGridEntity = room:GetGridEntity(surroundingGridIndex)
        if surroundingGridEntity ~= nil then
            surroundingGridEntities[#surroundingGridEntities + 1] = surroundingGridEntity
        end
    end
    return surroundingGridEntities
end
--- Helper function to get the top left wall in the current room.
-- 
-- This function can be useful in certain situations to determine if the room is currently loaded.
function ____exports.getTopLeftWall(self)
    local room = game:GetRoom()
    local topLeftWallGridIndex = ____exports.getTopLeftWallGridIndex(nil)
    return room:GetGridEntity(topLeftWallGridIndex)
end
--- Helper function to detect if a particular grid entity would "break" if it was touched by an
-- explosion.
-- 
-- For example, rocks and pots are breakable by explosions, but blocks are not.
function ____exports.isGridEntityBreakableByExplosion(self, gridEntity)
    local gridEntityType = gridEntity:GetType()
    local variant = gridEntity:GetVariant()
    local gridEntityTypeVariant = (tostring(gridEntityType) .. ".") .. tostring(variant)
    return BREAKABLE_GRID_ENTITY_TYPES_BY_EXPLOSIONS:has(gridEntityType) or BREAKABLE_GRID_ENTITY_TYPES_VARIANTS_BY_EXPLOSIONS:has(gridEntityTypeVariant)
end
--- Helper function to see if the provided grid entity is in its respective broken state. See the
-- `GRID_ENTITY_TYPE_TO_BROKEN_STATE_MAP` constant for more details.
-- 
-- Note that in the case of `GridEntityType.LOCK` (11), the state will turn to being broken before
-- the actual collision for the entity is removed.
function ____exports.isGridEntityBroken(self, gridEntity)
    local gridEntityType = gridEntity:GetType()
    local brokenState = GRID_ENTITY_TYPE_TO_BROKEN_STATE_MAP:get(gridEntityType)
    return gridEntity.State == brokenState
end
--- Helper function to see if an arbitrary number is a valid `GridEntityXMLType`. This is useful in
-- the `PRE_ROOM_ENTITY_SPAWN` callback for narrowing the type of the first argument.
function ____exports.isGridEntityXMLType(self, num)
    return GRID_ENTITY_XML_TYPES_SET:has(num)
end
--- Helper function to check if the provided grid index has a door on it or if the surrounding 8 grid
-- indexes have a door on it.
function ____exports.isGridIndexAdjacentToDoor(self, gridIndex)
    local room = game:GetRoom()
    local surroundingGridIndexes = ____exports.getSurroundingGridIndexes(nil, gridIndex)
    local gridIndexes = {
        gridIndex,
        table.unpack(surroundingGridIndexes)
    }
    for ____, gridIndexToInspect in ipairs(gridIndexes) do
        local gridEntity = room:GetGridEntity(gridIndexToInspect)
        if gridEntity ~= nil then
            local door = gridEntity:ToDoor()
            if door ~= nil then
                return true
            end
        end
    end
    return false
end
--- Helper function to see if a `GridEntityXMLType` is some kind of poop.
function ____exports.isPoopGridEntityXMLType(self, gridEntityXMLType)
    return POOP_GRID_ENTITY_XML_TYPES_SET:has(gridEntityXMLType)
end
--- Helper function to detect whether a given Void Portal is one that randomly spawns after a boss is
-- defeated or is one that naturally spawns in the room after Hush.
-- 
-- Under the hood, this is determined by looking at the `VarData` of the entity:
-- - The `VarData` of Void Portals that are spawned after bosses will be equal to 1.
-- - The `VarData` of the Void Portal in the room after Hush is equal to 0.
function ____exports.isPostBossVoidPortal(self, gridEntity)
    local saveState = gridEntity:GetSaveState()
    return saveState.Type == GridEntityType.TRAPDOOR and saveState.Variant == TrapdoorVariant.VOID_PORTAL and saveState.VarData == 1
end
--- Helper function to all grid entities in the room except for ones matching the grid entity types
-- provided.
-- 
-- Note that this function will automatically update the room. (This means that you can spawn new
-- grid entities on the same tile on the same frame, if needed.)
-- 
-- For example:
-- 
-- ```ts
-- removeAllGridEntitiesExcept(
--   GridEntityType.WALL,
--   GridEntityType.DOOR,
-- );
-- ```
-- 
-- @returns The grid entities that were removed.
function ____exports.removeAllGridEntitiesExcept(self, ...)
    local gridEntityTypes = {...}
    local gridEntityTypeExceptions = __TS__New(ReadonlySet, gridEntityTypes)
    local gridEntities = ____exports.getGridEntities(nil)
    local removedGridEntities = {}
    for ____, gridEntity in ipairs(gridEntities) do
        local gridEntityType = gridEntity:GetType()
        if not gridEntityTypeExceptions:has(gridEntityType) then
            ____exports.removeGridEntity(nil, gridEntity, false)
            removedGridEntities[#removedGridEntities + 1] = gridEntity
        end
    end
    if #removedGridEntities > 0 then
        roomUpdateSafe(nil)
    end
    return removedGridEntities
end
--- Helper function to remove all of the grid entities in the room that match the grid entity types
-- provided.
-- 
-- Note that this function will automatically update the room. (This means that you can spawn new
-- grid entities on the same tile on the same frame, if needed.)
-- 
-- For example:
-- 
-- ```ts
-- removeAllMatchingGridEntities(
--   GridEntityType.ROCK,
--   GridEntityType.BLOCK,
--   GridEntityType.ROCK_TINTED,
-- );
-- ```
-- 
-- @returns An array of the grid entities removed.
function ____exports.removeAllMatchingGridEntities(self, ...)
    local gridEntities = ____exports.getGridEntities(nil, ...)
    if #gridEntities == 0 then
        return {}
    end
    for ____, gridEntity in ipairs(gridEntities) do
        ____exports.removeGridEntity(nil, gridEntity, false)
    end
    roomUpdateSafe(nil)
    return gridEntities
end
--- Helper function to remove all entities that just spawned from a grid entity breaking.
-- Specifically, this is any entities that overlap with the position of a grid entity and are on
-- frame 0.
-- 
-- You must specify an array of entities to look through.
function ____exports.removeEntitiesSpawnedFromGridEntity(self, entities, gridEntity)
    local entitiesFromGridEntity = __TS__ArrayFilter(
        entities,
        function(____, entity) return entity.FrameCount == 0 and vectorEquals(nil, entity.Position, gridEntity.Position) end
    )
    removeEntities(nil, entitiesFromGridEntity)
end
--- Helper function to remove all of the grid entities in the supplied array.
-- 
-- @param gridEntities The array of grid entities to remove.
-- @param updateRoom Whether to update the room after the grid entities are removed. This is
-- generally a good idea because if the room is not updated, you will be unable to
-- spawn another grid entity on the same tile until a frame has passed. However,
-- doing this is expensive, since it involves a call to `Isaac.GetRoomEntities`,
-- so set this to false if you need to run this function multiple times.
-- @param cap Optional. If specified, will only remove the given amount of entities.
-- @returns An array of the entities that were removed.
function ____exports.removeGridEntities(self, gridEntities, updateRoom, cap)
    if #gridEntities == 0 then
        return {}
    end
    local gridEntitiesRemoved = {}
    for ____, gridEntity in ipairs(gridEntities) do
        ____exports.removeGridEntity(nil, gridEntity, false)
        gridEntitiesRemoved[#gridEntitiesRemoved + 1] = gridEntity
        if cap ~= nil and #gridEntitiesRemoved >= cap then
            break
        end
    end
    if updateRoom then
        roomUpdateSafe(nil)
    end
    return gridEntitiesRemoved
end
--- Helper function to make a grid entity invisible. This is accomplished by resetting the sprite.
-- 
-- Note that this function is destructive such that once you make a grid entity invisible, it can no
-- longer become visible. (This is because the information about the sprite is lost when it is
-- reset.)
function ____exports.setGridEntityInvisible(self, gridEntity)
    local sprite = gridEntity:GetSprite()
    sprite:Reset()
end
--- Helper function to change the type of a grid entity to another type. Use this instead of the
-- `GridEntity.SetType` method since that does not properly handle updating the sprite of the grid
-- entity after the type is changed.
-- 
-- Setting the new type to `GridEntityType.NULL` (0) will have no effect.
function ____exports.setGridEntityType(self, gridEntity, gridEntityType)
    gridEntity:SetType(gridEntityType)
    local sprite = gridEntity:GetSprite()
    local anm2Path = ____exports.getGridEntityANM2Path(nil, gridEntityType)
    if anm2Path == nil then
        return
    end
    sprite:Load(anm2Path, false)
    if gridEntityType == GridEntityType.ROCK then
        local pngPath = ____exports.getRockPNGPath(nil)
        sprite:ReplaceSpritesheet(0, pngPath)
    end
    sprite:LoadGraphics()
    local defaultAnimation = sprite:GetDefaultAnimation()
    sprite:Play(defaultAnimation, true)
end
--- Helper function to spawn a giant poop. This is performed by spawning each of the four quadrant
-- grid entities in the appropriate positions.
-- 
-- @returns Whether spawning the four quadrants was successful.
function ____exports.spawnGiantPoop(self, topLeftGridIndex)
    local room = game:GetRoom()
    local gridWidth = room:GetGridWidth()
    local topRightGridIndex = topLeftGridIndex + 1
    local bottomLeftGridIndex = topLeftGridIndex + gridWidth
    local bottomRightGridIndex = bottomLeftGridIndex + 1
    for ____, gridIndex in ipairs({topLeftGridIndex, topRightGridIndex, bottomLeftGridIndex, bottomRightGridIndex}) do
        local gridEntity = room:GetGridEntity(gridIndex)
        if gridEntity ~= nil then
            return false
        end
    end
    local topLeft = ____exports.spawnGridEntityWithVariant(nil, GridEntityType.POOP, PoopGridEntityVariant.GIANT_TOP_LEFT, topLeftGridIndex)
    local topRight = ____exports.spawnGridEntityWithVariant(nil, GridEntityType.POOP, PoopGridEntityVariant.GIANT_TOP_RIGHT, topRightGridIndex)
    local bottomLeft = ____exports.spawnGridEntityWithVariant(nil, GridEntityType.POOP, PoopGridEntityVariant.GIANT_BOTTOM_LEFT, bottomLeftGridIndex)
    local bottomRight = ____exports.spawnGridEntityWithVariant(nil, GridEntityType.POOP, PoopGridEntityVariant.GIANT_BOTTOM_RIGHT, bottomRightGridIndex)
    return topLeft ~= nil and topLeft:GetType() == GridEntityType.POOP and topLeft:GetVariant() == PoopGridEntityVariant.GIANT_TOP_LEFT and topRight ~= nil and topRight:GetType() == GridEntityType.POOP and topRight:GetVariant() == PoopGridEntityVariant.GIANT_TOP_RIGHT and bottomLeft ~= nil and bottomLeft:GetType() == GridEntityType.POOP and bottomLeft:GetVariant() == PoopGridEntityVariant.GIANT_BOTTOM_LEFT and bottomRight ~= nil and bottomRight:GetType() == GridEntityType.POOP and bottomRight:GetVariant() == PoopGridEntityVariant.GIANT_BOTTOM_RIGHT
end
--- Helper function to spawn a grid entity with a specific type.
-- 
-- This function assumes you want to give the grid entity a variant of 0. If you want to specify a
-- variant, use the `spawnGridEntityWithVariant` helper function instead.
-- 
-- Use this instead of the `Isaac.GridSpawn` method since it:
-- - handles giving pits collision
-- - removes existing grid entities on the same tile, if any
-- - allows you to specify either the grid index or the position
-- 
-- @param gridEntityType The `GridEntityType` to use.
-- @param gridIndexOrPosition The grid index or position in the room that you want to spawn the grid
-- entity at. If a position is specified, the closest grid index will be
-- used.
-- @param removeExistingGridEntity Optional. Whether to remove the existing grid entity on the same
-- tile, if it exists. Defaults to true. If false, this function
-- will do nothing, since spawning a grid entity on top of another
-- grid entity will not replace it.
function ____exports.spawnGridEntity(self, gridEntityType, gridIndexOrPosition, removeExistingGridEntity)
    if removeExistingGridEntity == nil then
        removeExistingGridEntity = true
    end
    return ____exports.spawnGridEntityWithVariant(
        nil,
        gridEntityType,
        0,
        gridIndexOrPosition,
        removeExistingGridEntity
    )
end
--- Helper function to spawn a Void Portal. This is more complicated than simply spawning a trapdoor
-- with the appropriate variant, as the game does not give it the correct sprite automatically.
function ____exports.spawnVoidPortal(self, gridIndex)
    local voidPortal = ____exports.spawnGridEntityWithVariant(nil, GridEntityType.TRAPDOOR, TrapdoorVariant.VOID_PORTAL, gridIndex)
    if voidPortal == nil then
        return voidPortal
    end
    voidPortal.VarData = 1
    local sprite = voidPortal:GetSprite()
    sprite:Load("gfx/grid/voidtrapdoor.anm2", true)
    return voidPortal
end
return ____exports
