local ____lualib = require("lualib_bundle")
local __TS__New = ____lualib.__TS__New
local __TS__SparseArrayNew = ____lualib.__TS__SparseArrayNew
local __TS__SparseArrayPush = ____lualib.__TS__SparseArrayPush
local __TS__SparseArraySpread = ____lualib.__TS__SparseArraySpread
local Map = ____lualib.Map
local __TS__Spread = ____lualib.__TS__Spread
local __TS__ArrayFilter = ____lualib.__TS__ArrayFilter
local __TS__ArrayMap = ____lualib.__TS__ArrayMap
local __TS__ArrayEvery = ____lualib.__TS__ArrayEvery
local __TS__StringIncludes = ____lualib.__TS__StringIncludes
local __TS__ArrayIncludes = ____lualib.__TS__ArrayIncludes
local ____exports = {}
local ____isaac_2Dtypescript_2Ddefinitions = require("isaac-typescript-definitions")
local AngelRoomSubType = ____isaac_2Dtypescript_2Ddefinitions.AngelRoomSubType
local Dimension = ____isaac_2Dtypescript_2Ddefinitions.Dimension
local DoorSlot = ____isaac_2Dtypescript_2Ddefinitions.DoorSlot
local DoorSlotFlag = ____isaac_2Dtypescript_2Ddefinitions.DoorSlotFlag
local DownpourRoomSubType = ____isaac_2Dtypescript_2Ddefinitions.DownpourRoomSubType
local DungeonSubType = ____isaac_2Dtypescript_2Ddefinitions.DungeonSubType
local GridRoom = ____isaac_2Dtypescript_2Ddefinitions.GridRoom
local HomeRoomSubType = ____isaac_2Dtypescript_2Ddefinitions.HomeRoomSubType
local RoomDescriptorFlag = ____isaac_2Dtypescript_2Ddefinitions.RoomDescriptorFlag
local RoomType = ____isaac_2Dtypescript_2Ddefinitions.RoomType
local SoundEffect = ____isaac_2Dtypescript_2Ddefinitions.SoundEffect
local StageID = ____isaac_2Dtypescript_2Ddefinitions.StageID
local ____cachedClasses = require("core.cachedClasses")
local game = ____cachedClasses.game
local sfxManager = ____cachedClasses.sfxManager
local ____constants = require("core.constants")
local DIMENSIONS = ____constants.DIMENSIONS
local MAX_LEVEL_GRID_INDEX = ____constants.MAX_LEVEL_GRID_INDEX
local ____roomTypeNames = require("objects.roomTypeNames")
local ROOM_TYPE_NAMES = ____roomTypeNames.ROOM_TYPE_NAMES
local ____mineShaftRoomSubTypesSet = require("sets.mineShaftRoomSubTypesSet")
local MINE_SHAFT_ROOM_SUB_TYPE_SET = ____mineShaftRoomSubTypesSet.MINE_SHAFT_ROOM_SUB_TYPE_SET
local ____ReadonlySet = require("types.ReadonlySet")
local ReadonlySet = ____ReadonlySet.ReadonlySet
local ____dimensions = require("functions.dimensions")
local inDimension = ____dimensions.inDimension
local ____doors = require("functions.doors")
local closeAllDoors = ____doors.closeAllDoors
local getDoors = ____doors.getDoors
local isHiddenSecretRoomDoor = ____doors.isHiddenSecretRoomDoor
local openDoorFast = ____doors.openDoorFast
local ____entities = require("functions.entities")
local getEntities = ____entities.getEntities
local ____flag = require("functions.flag")
local hasFlag = ____flag.hasFlag
local ____positionVelocity = require("functions.positionVelocity")
local getEntityPositions = ____positionVelocity.getEntityPositions
local getEntityVelocities = ____positionVelocity.getEntityVelocities
local setEntityPositions = ____positionVelocity.setEntityPositions
local setEntityVelocities = ____positionVelocity.setEntityVelocities
local ____roomData = require("functions.roomData")
local getRoomData = ____roomData.getRoomData
local getRoomDescriptor = ____roomData.getRoomDescriptor
local getRoomDescriptorReadOnly = ____roomData.getRoomDescriptorReadOnly
local getRoomGridIndex = ____roomData.getRoomGridIndex
local ____roomShape = require("functions.roomShape")
local is2x1RoomShape = ____roomShape.is2x1RoomShape
local isBigRoomShape = ____roomShape.isBigRoomShape
local isLRoomShape = ____roomShape.isLRoomShape
local ____roomTransition = require("functions.roomTransition")
local reloadRoom = ____roomTransition.reloadRoom
local ____stage = require("functions.stage")
local getGotoCommand = ____stage.getGotoCommand
local ____utils = require("functions.utils")
local assertDefined = ____utils.assertDefined
local iRange = ____utils.iRange
--- Helper function to get a read-only copy of the room descriptor for every room on the level. This
-- includes off-grid rooms, such as the Devil Room, and extra-dimensional rooms, if they are
-- generated and exist.
-- 
-- Room descriptors without any data are assumed to be non-existent and are not included.
-- 
-- Under the hood, this is performed by iterating over the `RoomList` from the `Level.GetRooms`
-- method. This is the best way to see if off-grid rooms have been initialized, since it is possible
-- for mods to insert room data at non-official negative room grid indexes.
function ____exports.getReadOnlyRooms(self)
    local level = game:GetLevel()
    local roomList = level:GetRooms()
    local readOnlyRoomDescriptors = {}
    do
        local i = 0
        while i < roomList.Size do
            local readOnlyRoomDescriptor = roomList:Get(i)
            if readOnlyRoomDescriptor ~= nil and readOnlyRoomDescriptor.Data ~= nil then
                readOnlyRoomDescriptors[#readOnlyRoomDescriptors + 1] = readOnlyRoomDescriptor
            end
            i = i + 1
        end
    end
    return readOnlyRoomDescriptors
end
--- Helper function to get the room descriptor for every room on the level that is on the grid. (For
-- example, Devil Rooms are excluded.)
-- 
-- Room descriptors without any data are assumed to be non-existent and are not included.
-- 
-- @param includeExtraDimensionalRooms Optional. On some floors (e.g. Downpour 2, Mines 2),
-- extra-dimensional rooms are automatically be generated. Default
-- is false.
function ____exports.getRoomsInsideGrid(self, includeExtraDimensionalRooms)
    if includeExtraDimensionalRooms == nil then
        includeExtraDimensionalRooms = false
    end
    local level = game:GetLevel()
    local dimensions = includeExtraDimensionalRooms and DIMENSIONS or ({Dimension.CURRENT})
    --- We use a map instead of an array because room shapes occupy more than one room grid index.
    local roomDescriptorMap = __TS__New(Map)
    for ____, dimension in ipairs(dimensions) do
        for ____, roomGridIndex in ipairs(iRange(nil, MAX_LEVEL_GRID_INDEX)) do
            local roomDescriptor = level:GetRoomByIdx(roomGridIndex, dimension)
            if roomDescriptor.Data ~= nil then
                local ptrHash = GetPtrHash(roomDescriptor)
                roomDescriptorMap:set(ptrHash, roomDescriptor)
            end
        end
    end
    return {__TS__Spread(roomDescriptorMap:values())}
end
--- Helper function to get the room descriptor for every room on the level that is outside of the
-- grid (like a Devil Room).
-- 
-- Room descriptors without any data are assumed to be non-existent and are not included.
function ____exports.getRoomsOutsideGrid(self)
    local readOnlyRooms = ____exports.getReadOnlyRooms(nil)
    local readOnlyRoomsOffGrid = __TS__ArrayFilter(
        readOnlyRooms,
        function(____, readOnlyRoomDescriptor) return readOnlyRoomDescriptor.SafeGridIndex < 0 end
    )
    return __TS__ArrayMap(
        readOnlyRoomsOffGrid,
        function(____, readOnlyRoomDescriptor) return getRoomDescriptor(nil, readOnlyRoomDescriptor.SafeGridIndex) end
    )
end
--- Helper function to determine if the provided room is equal to `RoomShape.1x2` or `RoomShape.2x1`.
function ____exports.is2x1Room(self, roomData)
    return is2x1RoomShape(nil, roomData.Shape)
end
--- Helper function to check to see if the current room is an angel shop.
-- 
-- Under the hood, this checks the room type being equal to `RoomType.ANGEL` (15) and the sub-type
-- being equal to `AngelRoomSubType.SHOP` (1).
function ____exports.isAngelShop(self, roomData)
    return roomData.Type == RoomType.ANGEL and roomData.Subtype == AngelRoomSubType.SHOP
end
--- Helper function to check to see if the provided room is the Boss Room for The Beast.
-- 
-- This function is useful because the `Room.GetBossID` method returns 0 for The Beast room.
-- 
-- Under the hood, this checks the room type being equal to `RoomType.DUNGEON` (16) and the sub-type
-- being equal to `DungeonSubType.BEAST_ROOM` (4).
function ____exports.isBeastRoom(self, roomData)
    return roomData.Type == RoomType.DUNGEON and roomData.Subtype == DungeonSubType.BEAST_ROOM
end
--- Helper function to detect if the provided room is big. Specifically, this is all 1x2 rooms, 2x2
-- rooms, and L rooms.
function ____exports.isBigRoom(self, roomData)
    return isBigRoomShape(nil, roomData.Shape)
end
--- Helper function to check if the provided room is the Boss Room for a particular boss. This will
-- only work for bosses that have dedicated boss rooms in the "00.special rooms.stb" file.
function ____exports.isBossRoomOf(self, roomData, bossID)
    return roomData.Type == RoomType.BOSS and roomData.StageID == StageID.SPECIAL_ROOMS and roomData.Subtype == bossID
end
--- Helper function for determining whether the provided room is a crawl space. Use this function
-- over comparing to `RoomType.DUNGEON` or `GridRoom.DUNGEON_IDX` since there is a special case of
-- the player being in a boss fight that takes place in a dungeon.
function ____exports.isCrawlSpace(self, roomData)
    return roomData.Type == RoomType.DUNGEON and roomData.Subtype == DungeonSubType.NORMAL
end
--- Helper function for checking whether the provided room is a crawl space with a door corresponding
-- to `DoorSlotFlag.RIGHT_0` (1 << 2).
function ____exports.isCrawlSpaceWithBlackMarketEntrance(self, roomData)
    return ____exports.isCrawlSpace(nil, roomData) and hasFlag(nil, roomData.Doors, DoorSlotFlag.RIGHT_0)
end
--- Helper function to detect if the provided room is one of the rooms in the Death Certificate area.
function ____exports.isDeathCertificateArea(self, roomData)
    return roomData.StageID == StageID.HOME and (roomData.Subtype == HomeRoomSubType.DEATH_CERTIFICATE_ENTRANCE or roomData.Subtype == HomeRoomSubType.DEATH_CERTIFICATE_ITEMS)
end
--- Helper function to detect if the provided room is a Treasure Room created when entering with a
-- Devil's Crown trinket.
-- 
-- Under the hood, this checks for `RoomDescriptorFlag.DEVIL_TREASURE`.
function ____exports.isDevilsCrownTreasureRoom(self, roomDescriptor)
    return hasFlag(nil, roomDescriptor.Flags, RoomDescriptorFlag.DEVIL_TREASURE)
end
--- Helper function to check to see if the provided room is the Boss Room for Dogma.
-- 
-- This function is useful because the `Room.GetBossID` method returns 0 for the Dogma room.
-- 
-- Note that the "living room" on the Home floor with the TV at the top of the room is not the Dogma
-- Boss Room, as the player is teleported to a different room after watching the TV cutscene.
-- 
-- Under the hood, this checks the stage ID being equal to `StageID.HOME` (35) and the room type
-- being equal to `RoomType.DEFAULT` (1) and the variant being equal to 1000 (which is the only
-- Dogma Boss Room that exists in vanilla) and the sub-type being equal to
-- `HomeRoomSubType.LIVING_ROOM` (3).
function ____exports.isDogmaRoom(self, roomData)
    return roomData.StageID == StageID.HOME and roomData.Type == RoomType.DEFAULT and roomData.Variant == 1000 and roomData.Subtype == HomeRoomSubType.LIVING_ROOM
end
--- Helper function to detect if the provided room is a Double Trouble Boss Room.
-- 
-- This is performed by checking for the string "Double Trouble" inside of the room name. The
-- vanilla game uses this convention for every Double Trouble Boss Room. Note that this method might
-- fail for mods that add extra Double Trouble rooms but do not follow the convention.
-- 
-- Internally, the game is coded to detect Double Trouble Boss Rooms by checking for the variant
-- range of 3700 through 3850. We intentionally do not use this method since it may not work as well
-- with modded rooms.
function ____exports.isDoubleTrouble(self, roomData)
    return roomData.Type == RoomType.BOSS and __TS__StringIncludes(roomData.Name, "Double Trouble")
end
--- Helper function to determine if the index of the provided room is equal to `GridRoom.GENESIS`.
function ____exports.isGenesisRoom(self, roomGridIndex)
    return roomGridIndex == GridRoom.GENESIS
end
--- Helper function to check if the provided room is either the left Home closet (behind the red
-- door) or the right Home closet (with one random pickup).
-- 
-- Home closets have a unique shape that is different from any other room in the game.
function ____exports.isHomeCloset(self, roomData)
    return roomData.StageID == StageID.HOME and (roomData.Subtype == HomeRoomSubType.CLOSET_LEFT or roomData.Subtype == HomeRoomSubType.CLOSET_RIGHT)
end
--- Helper function to determine if the provided room is one of the four L room shapes.
function ____exports.isLRoom(self, roomData)
    return isLRoomShape(nil, roomData.Shape)
end
--- Helper function to determine if the index of the provided room is equal to `GridRoom.MEGA_SATAN`.
function ____exports.isMegaSatanRoom(self, roomGridIndex)
    return roomGridIndex == GridRoom.MEGA_SATAN
end
--- Helper function to determine if the provided room is part of the Repentance "escape sequence" in
-- the Mines/Ashpit.
function ____exports.isMineShaft(self, roomData)
    return (roomData.StageID == StageID.MINES or roomData.StageID == StageID.ASHPIT) and MINE_SHAFT_ROOM_SUB_TYPE_SET:has(roomData.Subtype)
end
--- Helper function to check if the provided room is a miniboss room for a particular miniboss. This
-- will only work for mini-bosses that have dedicated boss rooms in the "00.special rooms.stb" file.
function ____exports.isMinibossRoomOf(self, roomData, minibossID)
    return roomData.Type == RoomType.MINI_BOSS and roomData.StageID == StageID.SPECIAL_ROOMS and roomData.Subtype == minibossID
end
--- Helper function to check if the provided room is a "mirror room" in Downpour or Dross. (These
-- rooms are marked with a specific sub-type.)
function ____exports.isMirrorRoom(self, roomData)
    return roomData.Type == RoomType.DEFAULT and (roomData.StageID == StageID.DOWNPOUR or roomData.StageID == StageID.DROSS) and roomData.Subtype == DownpourRoomSubType.MIRROR
end
--- Helper function to check if the provided room matches one of the given room shapes.
-- 
-- This function is variadic, which means you can pass as many room shapes as you want to match for.
function ____exports.isRoomShape(self, roomData, ...)
    local roomShapes = {...}
    return __TS__ArrayIncludes(roomShapes, roomData.Shape)
end
--- Helper function to check if the provided room matches one of the given room types.
-- 
-- This function is variadic, which means you can pass as many room types as you want to match for.
function ____exports.isRoomType(self, roomData, ...)
    local roomTypes = {...}
    return __TS__ArrayIncludes(roomTypes, roomData.Type)
end
--- Helper function for checking if the provided room is a secret exit that leads to a Repentance
-- floor.
function ____exports.isSecretExit(self, roomGridIndex)
    return roomGridIndex == GridRoom.SECRET_EXIT
end
--- Helper function for checking if the provided room is a secret shop (from the Member Card
-- collectible).
-- 
-- Secret shops are simply copies of normal shops, but with the backdrop of a secret room. In other
-- words, they will have the same room type, room variant, and room sub-type of a normal shop. Thus,
-- the only way to detect them is by using the grid index.
function ____exports.isSecretShop(self, roomGridIndex)
    return roomGridIndex == GridRoom.SECRET_SHOP
end
local SECRET_ROOM_TYPES = __TS__New(ReadonlySet, {RoomType.SECRET, RoomType.SUPER_SECRET, RoomType.ULTRA_SECRET})
--- Helper function for quickly switching to a new room without playing a particular animation. Use
-- this helper function over invoking the `Game.ChangeRoom` method directly to ensure that you do
-- not forget to set the `LeaveDoor` field and to prevent crashing on invalid room grid indexes.
function ____exports.changeRoom(self, roomGridIndex)
    local level = game:GetLevel()
    local roomData = getRoomData(nil, roomGridIndex)
    assertDefined(
        nil,
        roomData,
        ("Failed to change the room to grid index " .. tostring(roomGridIndex)) .. " because that room does not exist."
    )
    level.LeaveDoor = DoorSlot.NO_DOOR_SLOT
    game:ChangeRoom(roomGridIndex)
end
--- Helper function to get the number of rooms that are currently on the floor layout. This does not
-- include off-grid rooms, like the Devil Room.
function ____exports.getNumRooms(self)
    local roomsInsideGrid = ____exports.getRoomsInsideGrid(nil)
    return #roomsInsideGrid
end
--- Helper function to get the room data for a specific room type and variant combination. This is
-- accomplished by using the "goto" console command to load the specified room into the
-- `GridRoom.DEBUG` slot.
-- 
-- Returns undefined if the provided room type and variant combination were not found. (A warning
-- message will also appear on the console, since the "goto" command will fail.)
-- 
-- Note that the side effect of using the "goto" console command is that it will trigger a room
-- transition after a short delay. By default, this function cancels the incoming room transition by
-- using the `Game.StartRoomTransition` method to travel to the same room.
-- 
-- @param roomType The type of room to retrieve.
-- @param roomVariant The room variant to retrieve. (The room variant is the "ID" of the room in
-- Basement Renovator.)
-- @param cancelRoomTransition Optional. Whether to cancel the room transition by using the
-- `Game.StartRoomTransition` method to travel to the same room. Default
-- is true. Set this to false if you are getting the data for many rooms
-- at the same time, and then use the `teleport` helper function when
-- you are finished.
-- @param useSpecialRoomsForRoomTypeDefault Optional. Whether to use `s.default` as the prefix for
-- the `goto` command (instead of `d`) if the room type is
-- `RoomType.DEFAULT` (1). False by default.
function ____exports.getRoomDataForTypeVariant(self, roomType, roomVariant, cancelRoomTransition, useSpecialRoomsForRoomTypeDefault)
    if cancelRoomTransition == nil then
        cancelRoomTransition = true
    end
    if useSpecialRoomsForRoomTypeDefault == nil then
        useSpecialRoomsForRoomTypeDefault = false
    end
    local command = getGotoCommand(nil, roomType, roomVariant, useSpecialRoomsForRoomTypeDefault)
    Isaac.ExecuteCommand(command)
    local newRoomData = getRoomData(nil, GridRoom.DEBUG)
    if cancelRoomTransition then
        reloadRoom(nil)
    end
    return newRoomData
end
--- Helper function to get the item pool type for the current room. For example, this returns
-- `ItemPoolType.ItemPoolType.POOL_ANGEL` if you are in an Angel Room.
function ____exports.getRoomItemPoolType(self)
    local itemPool = game:GetItemPool()
    local room = game:GetRoom()
    local roomType = room:GetType()
    local roomSeed = room:GetSpawnSeed()
    return itemPool:GetPoolForRoom(roomType, roomSeed)
end
--- Helper function to get the proper name of a room type.
-- 
-- For example, `RoomType.TREASURE` will return "Treasure Room".
function ____exports.getRoomTypeName(self, roomType)
    return ROOM_TYPE_NAMES[roomType]
end
--- Helper function to get the room descriptor for every room on the level. This includes off-grid
-- rooms, such as the Devil Room.
-- 
-- Room without any data are assumed to be non-existent and are not included.
-- 
-- - If you want just the rooms inside of the grid, use the `getRoomsInsideGrid` helper function.
-- - If you want just the rooms outside of the grid, use the `getRoomsOutsideGrid` helper function.
-- 
-- @param includeExtraDimensionalRooms Optional. On some floors (e.g. Downpour 2, Mines 2),
-- extra-dimensional rooms are automatically generated. Default is
-- false.
function ____exports.getRooms(self, includeExtraDimensionalRooms)
    if includeExtraDimensionalRooms == nil then
        includeExtraDimensionalRooms = false
    end
    local roomsInGrid = ____exports.getRoomsInsideGrid(nil, includeExtraDimensionalRooms)
    local roomsOutsideGrid = ____exports.getRoomsOutsideGrid(nil)
    local ____array_0 = __TS__SparseArrayNew(table.unpack(roomsInGrid))
    __TS__SparseArrayPush(
        ____array_0,
        table.unpack(roomsOutsideGrid)
    )
    return {__TS__SparseArraySpread(____array_0)}
end
--- Helper function to get the room descriptor for every room on the level in a specific dimension.
-- This will not include any off-grid rooms, such as the Devil Room.
-- 
-- Room descriptors without any data are assumed to be non-existent and are not included.
function ____exports.getRoomsOfDimension(self, dimension)
    local level = game:GetLevel()
    --- We use a map instead of an array because room shapes occupy more than one room grid index.
    local roomsMap = __TS__New(Map)
    for ____, roomGridIndex in ipairs(iRange(nil, MAX_LEVEL_GRID_INDEX)) do
        local roomDescriptor = level:GetRoomByIdx(roomGridIndex, dimension)
        if roomDescriptor.Data ~= nil then
            local ptrHash = GetPtrHash(roomDescriptor)
            roomsMap:set(ptrHash, roomDescriptor)
        end
    end
    return {__TS__Spread(roomsMap:values())}
end
--- Helper function to determine if the current room shape is equal to `RoomShape.1x2` or
-- `RoomShape.2x1`.
function ____exports.in2x1Room(self)
    local roomData = getRoomData(nil)
    return ____exports.is2x1Room(nil, roomData)
end
--- Helper function to check to see if the current room is an angel shop.
-- 
-- Under the hood, this checks the room type being equal to `RoomType.ANGEL` (15) and the sub-type
-- being equal to `AngelRoomSubType.SHOP` (1).
function ____exports.inAngelShop(self)
    local roomData = getRoomData(nil)
    return ____exports.isAngelShop(nil, roomData)
end
--- Helper function to check to see if the current room is the Boss Room for The Beast.
-- 
-- This function is useful because the `Room.GetBossID` method returns 0 for The Beast room.
-- 
-- Under the hood, this checks the room type being equal to `RoomType.DUNGEON` (16) and the sub-type
-- being equal to `DungeonSubType.BEAST_ROOM` (4).
function ____exports.inBeastRoom(self)
    local roomData = getRoomData(nil)
    return ____exports.isBeastRoom(nil, roomData)
end
--- Helper function to detect if the current room is big. Specifically, this is all 1x2 rooms, 2x2
-- rooms, and L rooms.
function ____exports.inBigRoom(self)
    local roomData = getRoomData(nil)
    return ____exports.isBigRoom(nil, roomData)
end
--- Helper function to check if the current room is the Boss Room for a particular boss. This will
-- only work for bosses that have dedicated boss rooms in the "00.special rooms.stb" file.
function ____exports.inBossRoomOf(self, bossID)
    local roomData = getRoomData(nil)
    return ____exports.isBossRoomOf(nil, roomData, bossID)
end
--- Helper function for determining whether the current room is a crawl space. Use this function over
-- comparing to `RoomType.DUNGEON` or `GridRoom.DUNGEON_IDX` since there is a special case of the
-- player being in a boss fight that takes place in a dungeon.
function ____exports.inCrawlSpace(self)
    local roomData = getRoomData(nil)
    return ____exports.isCrawlSpace(nil, roomData)
end
--- Helper function for checking whether the current room is a crawl space with a door corresponding
-- to `DoorSlotFlag.RIGHT_0` (1 << 2).
function ____exports.inCrawlSpaceWithBlackMarketEntrance(self)
    local roomData = getRoomData(nil)
    return ____exports.isCrawlSpaceWithBlackMarketEntrance(nil, roomData)
end
--- Helper function to detect if the current room is one of the rooms in the Death Certificate area.
function ____exports.inDeathCertificateArea(self)
    local roomData = getRoomData(nil)
    return ____exports.isDeathCertificateArea(nil, roomData)
end
--- Helper function to detect if the current room is a Treasure Room created when entering with a
-- Devil's Crown trinket.
-- 
-- Under the hood, this checks for `RoomDescriptorFlag.DEVIL_TREASURE`.
function ____exports.inDevilsCrownTreasureRoom(self)
    local roomDescriptor = getRoomDescriptorReadOnly(nil)
    return ____exports.isDevilsCrownTreasureRoom(nil, roomDescriptor)
end
--- Helper function to check to see if the current room is the Boss Room for Dogma.
-- 
-- This function is useful because the `Room.GetBossID` method returns 0 for the Dogma room.
-- 
-- Note that the "living room" on the Home floor with the TV at the top of the room is not the Dogma
-- Boss Room, as the player is teleported to a different room after watching the TV cutscene.
-- 
-- Under the hood, this checks the stage ID being equal to `StageID.HOME` (35) and the room type
-- being equal to `RoomType.DEFAULT` (1) and the variant being equal to 1000 (which is the only
-- Dogma Boss Room that exists in vanilla) and the sub-type being equal to
-- `HomeRoomSubType.LIVING_ROOM` (3).
function ____exports.inDogmaRoom(self)
    local roomData = getRoomData(nil)
    return ____exports.isDogmaRoom(nil, roomData)
end
--- Helper function to detect if the current room is a Double Trouble Boss Room.
-- 
-- This is performed by checking for the string "Double Trouble" inside of the room name. The
-- vanilla game uses this convention for every Double Trouble Boss Room. Note that this method might
-- fail for mods that add extra Double Trouble rooms but do not follow the convention.
-- 
-- Internally, the game is coded to detect Double Trouble Boss Rooms by checking for the variant
-- range of 3700 through 3850. We intentionally do not use this method since it may not work as well
-- with modded rooms.
function ____exports.inDoubleTrouble(self)
    local roomData = getRoomData(nil)
    return ____exports.isDoubleTrouble(nil, roomData)
end
--- Helper function to determine if the current room index is equal to `GridRoom.GENESIS`.
function ____exports.inGenesisRoom(self)
    local roomGridIndex = getRoomGridIndex(nil)
    return ____exports.isGenesisRoom(nil, roomGridIndex)
end
--- Helper function to check if the current room is either the left Home closet (behind the red door)
-- or the right Home closet (with one random pickup).
-- 
-- Home closets have a unique shape that is different from any other room in the game.
function ____exports.inHomeCloset(self)
    local roomData = getRoomData(nil)
    return ____exports.isHomeCloset(nil, roomData)
end
--- Helper function to determine if the current room shape is one of the four L room shapes.
function ____exports.inLRoom(self)
    local roomData = getRoomData(nil)
    return ____exports.isLRoom(nil, roomData)
end
--- Helper function to determine if the current room index is equal to `GridRoom.MEGA_SATAN`.
function ____exports.inMegaSatanRoom(self)
    local roomGridIndex = getRoomGridIndex(nil)
    return ____exports.isMegaSatanRoom(nil, roomGridIndex)
end
--- Helper function to determine if the current room is part of the Repentance "escape sequence" in
-- the Mines/Ashpit.
function ____exports.inMineShaft(self)
    local roomData = getRoomData(nil)
    return ____exports.isMineShaft(nil, roomData)
end
--- Helper function to check if the current room is a miniboss room for a particular miniboss. This
-- will only work for mini-bosses that have dedicated boss rooms in the "00.special rooms.stb" file.
function ____exports.inMinibossRoomOf(self, minibossID)
    local roomData = getRoomData(nil)
    return ____exports.isMinibossRoomOf(nil, roomData, minibossID)
end
--- Helper function to check if the current room is a "mirror room" in Downpour or Dross. (These
-- rooms are marked with a specific sub-type.)
function ____exports.inMirrorRoom(self)
    local roomData = getRoomData(nil)
    return ____exports.isMirrorRoom(nil, roomData)
end
--- Helper function to check if the current room shape matches one of the given room shapes.
-- 
-- This function is variadic, which means you can pass as many room shapes as you want to match for.
function ____exports.inRoomShape(self, ...)
    local roomData = getRoomData(nil)
    return ____exports.isRoomShape(nil, roomData, ...)
end
--- Helper function to check if the current room matches one of the given room types.
-- 
-- This function is variadic, which means you can pass as many room types as you want to match for.
function ____exports.inRoomType(self, ...)
    local roomData = getRoomData(nil)
    return ____exports.isRoomType(nil, roomData, ...)
end
--- Helper function for checking if the current room is a secret exit that leads to a Repentance
-- floor.
function ____exports.inSecretExit(self)
    local roomGridIndex = getRoomGridIndex(nil)
    return ____exports.isSecretExit(nil, roomGridIndex)
end
--- Helper function for checking if the current room is a secret shop (from the Member Card
-- collectible).
-- 
-- Secret shops are simply copies of normal shops, but with the backdrop of a secret room. In other
-- words, they will have the same room type, room variant, and room sub-type of a normal shop. Thus,
-- the only way to detect them is by using the grid index.
function ____exports.inSecretShop(self)
    local roomGridIndex = getRoomGridIndex(nil)
    return ____exports.isSecretShop(nil, roomGridIndex)
end
--- Helper function to determine whether the current room is the starting room of a floor. It only
-- returns true for the starting room of the primary dimension (meaning that being in the starting
-- room of the mirror world does not count).
function ____exports.inStartingRoom(self)
    local level = game:GetLevel()
    local startingRoomGridIndex = level:GetStartingRoomIndex()
    local roomGridIndex = getRoomGridIndex(nil)
    return roomGridIndex == startingRoomGridIndex and inDimension(nil, Dimension.MAIN)
end
--- Helper function to loop through every room on the floor and see if it has been cleared.
-- 
-- This function will only check rooms inside the grid and inside the current dimension.
-- 
-- @param onlyCheckRoomTypes Optional. A whitelist of room types. If specified, room types not in
-- the array will be ignored. If not specified, then all rooms will be
-- checked. Undefined by default.
-- @param includeSecretRoom Optional. Whether to include the Secret Room. Default is false.
-- @param includeSuperSecretRoom Optional. Whether to include the Super Secret Room. Default is
-- false.
-- @param includeUltraSecretRoom Optional. Whether to include the Ultra Secret Room. Default is
-- false.
-- @allowEmptyVariadic
function ____exports.isAllRoomsClear(self, onlyCheckRoomTypes, includeSecretRoom, includeSuperSecretRoom, includeUltraSecretRoom)
    if includeSecretRoom == nil then
        includeSecretRoom = false
    end
    if includeSuperSecretRoom == nil then
        includeSuperSecretRoom = false
    end
    if includeUltraSecretRoom == nil then
        includeUltraSecretRoom = false
    end
    local roomsInsideGrid = ____exports.getRoomsInsideGrid(nil)
    local matchingRooms
    if onlyCheckRoomTypes == nil then
        matchingRooms = roomsInsideGrid
    else
        local roomTypeWhitelist = __TS__New(ReadonlySet, onlyCheckRoomTypes)
        matchingRooms = __TS__ArrayFilter(
            roomsInsideGrid,
            function(____, roomDescriptor) return roomDescriptor.Data ~= nil and roomTypeWhitelist:has(roomDescriptor.Data.Type) end
        )
    end
    if not includeSecretRoom then
        matchingRooms = __TS__ArrayFilter(
            matchingRooms,
            function(____, roomDescriptor) return roomDescriptor.Data ~= nil and roomDescriptor.Data.Type ~= RoomType.SECRET end
        )
    end
    if not includeSuperSecretRoom then
        matchingRooms = __TS__ArrayFilter(
            matchingRooms,
            function(____, roomDescriptor) return roomDescriptor.Data ~= nil and roomDescriptor.Data.Type ~= RoomType.SUPER_SECRET end
        )
    end
    if not includeUltraSecretRoom then
        matchingRooms = __TS__ArrayFilter(
            matchingRooms,
            function(____, roomDescriptor) return roomDescriptor.Data ~= nil and roomDescriptor.Data.Type ~= RoomType.ULTRA_SECRET end
        )
    end
    return __TS__ArrayEvery(
        matchingRooms,
        function(____, roomDescriptor) return roomDescriptor.Clear end
    )
end
--- Helper function to detect if a room type is a Secret Room, a Super Secret Room, or an Ultra
-- Secret Room.
function ____exports.isSecretRoomType(self, roomType)
    return SECRET_ROOM_TYPES:has(roomType)
end
--- If the `Room.Update` method is called in a `POST_NEW_ROOM` callback, then some entities will
-- slide around (such as the player). Since those entity velocities are already at zero, setting
-- them to zero will have no effect. Thus, a generic solution is to record all of the entity
-- positions/velocities before updating the room, and then restore those positions/velocities.
function ____exports.roomUpdateSafe(self)
    local room = game:GetRoom()
    local entities = getEntities(nil)
    local entityPositions = getEntityPositions(nil, entities)
    local entityVelocities = getEntityVelocities(nil, entities)
    room:Update()
    setEntityPositions(nil, entityPositions, entities)
    setEntityVelocities(nil, entityVelocities, entities)
end
--- Helper function to set the backdrop (i.e. background) of the current room.
function ____exports.setBackdrop(self, backdropType)
    game:ShowHallucination(0, backdropType)
    sfxManager:Stop(SoundEffect.DEATH_CARD)
end
--- Helper function to convert an uncleared room to a cleared room in the `POST_NEW_ROOM` callback.
-- This is useful because if enemies are removed in this callback, a room drop will be awarded and
-- the doors will start closed and then open.
function ____exports.setRoomCleared(self)
    local room = game:GetRoom()
    local roomClear = room:IsClear()
    if roomClear then
        return
    end
    room:SetClear(true)
    for ____, door in ipairs(getDoors(nil)) do
        do
            if isHiddenSecretRoomDoor(nil, door) then
                goto __continue86
            end
            openDoorFast(nil, door)
            door.ExtraVisible = false
        end
        ::__continue86::
    end
    sfxManager:Stop(SoundEffect.DOOR_HEAVY_OPEN)
    game:ShakeScreen(0)
end
--- Helper function to emulate what happens when you bomb an Angel Statue or push a Reward Plate that
-- spawns an NPC.
function ____exports.setRoomUncleared(self)
    local room = game:GetRoom()
    room:SetClear(false)
    closeAllDoors(nil)
end
return ____exports
