"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.isMineShaft = exports.isMegaSatanRoom = exports.isLRoom = exports.isHomeCloset = exports.isGenesisRoom = exports.isDoubleTrouble = exports.isDogmaRoom = exports.isDevilsCrownTreasureRoom = exports.isDeathCertificateArea = exports.isCrawlSpaceWithBlackMarketEntrance = exports.isCrawlSpace = exports.isBossRoomOf = exports.isBigRoom = exports.isBeastRoom = exports.isAngelShop = exports.isAllRoomsClear = exports.is2x1Room = exports.inStartingRoom = exports.inSecretShop = exports.inSecretExit = exports.inRoomType = exports.inRoomShape = exports.inMirrorRoom = exports.inMinibossRoomOf = exports.inMineShaft = exports.inMegaSatanRoom = exports.inLRoom = exports.inHomeCloset = exports.inGenesisRoom = exports.inDoubleTrouble = exports.inDogmaRoom = exports.inDevilsCrownTreasureRoom = exports.inDeathCertificateArea = exports.inCrawlSpaceWithBlackMarketEntrance = exports.inCrawlSpace = exports.inBossRoomOf = exports.inBigRoom = exports.inBeastRoom = exports.inAngelShop = exports.in2x1Room = exports.getRoomsOutsideGrid = exports.getRoomsOfDimension = exports.getRoomsInsideGrid = exports.getRooms = exports.getRoomTypeName = exports.getRoomItemPoolType = exports.getRoomDataForTypeVariant = exports.getReadOnlyRooms = exports.getNumRooms = exports.changeRoom = void 0;
exports.setRoomUncleared = exports.setRoomCleared = exports.setBackdrop = exports.roomUpdateSafe = exports.isSecretShop = exports.isSecretRoomType = exports.isSecretExit = exports.isRoomType = exports.isRoomShape = exports.isMirrorRoom = exports.isMinibossRoomOf = void 0;
const isaac_typescript_definitions_1 = require("isaac-typescript-definitions");
const cachedClasses_1 = require("../core/cachedClasses");
const constants_1 = require("../core/constants");
const roomTypeNames_1 = require("../objects/roomTypeNames");
const mineShaftRoomSubTypesSet_1 = require("../sets/mineShaftRoomSubTypesSet");
const ReadonlySet_1 = require("../types/ReadonlySet");
const dimensions_1 = require("./dimensions");
const doors_1 = require("./doors");
const entities_1 = require("./entities");
const flag_1 = require("./flag");
const positionVelocity_1 = require("./positionVelocity");
const roomData_1 = require("./roomData");
const roomShape_1 = require("./roomShape");
const roomTransition_1 = require("./roomTransition");
const stage_1 = require("./stage");
const utils_1 = require("./utils");
const SECRET_ROOM_TYPES = new ReadonlySet_1.ReadonlySet([
    isaac_typescript_definitions_1.RoomType.SECRET,
    isaac_typescript_definitions_1.RoomType.SUPER_SECRET,
    isaac_typescript_definitions_1.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 changeRoom(roomGridIndex) {
    const level = cachedClasses_1.game.GetLevel();
    const roomData = (0, roomData_1.getRoomData)(roomGridIndex);
    (0, utils_1.assertDefined)(roomData, `Failed to change the room to grid index ${roomGridIndex} because that room does not exist.`);
    // LeaveDoor must be set before every `Game.ChangeRoom` invocation or else the function can send
    // you to the wrong room.
    level.LeaveDoor = isaac_typescript_definitions_1.DoorSlot.NO_DOOR_SLOT;
    cachedClasses_1.game.ChangeRoom(roomGridIndex);
}
exports.changeRoom = changeRoom;
/**
 * 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 getNumRooms() {
    const roomsInsideGrid = getRoomsInsideGrid();
    return roomsInsideGrid.length;
}
exports.getNumRooms = getNumRooms;
/**
 * 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 getReadOnlyRooms() {
    const level = cachedClasses_1.game.GetLevel();
    const roomList = level.GetRooms();
    const readOnlyRoomDescriptors = [];
    for (let i = 0; i < roomList.Size; i++) {
        const readOnlyRoomDescriptor = roomList.Get(i);
        if (readOnlyRoomDescriptor !== undefined &&
            readOnlyRoomDescriptor.Data !== undefined) {
            readOnlyRoomDescriptors.push(readOnlyRoomDescriptor);
        }
    }
    return readOnlyRoomDescriptors;
}
exports.getReadOnlyRooms = getReadOnlyRooms;
/**
 * 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 getRoomDataForTypeVariant(roomType, roomVariant, cancelRoomTransition = true, useSpecialRoomsForRoomTypeDefault = false) {
    const command = (0, stage_1.getGotoCommand)(roomType, roomVariant, useSpecialRoomsForRoomTypeDefault);
    // We do not want to log the command execution, because this function will potentially be called
    // many times.
    Isaac.ExecuteCommand(command);
    const newRoomData = (0, roomData_1.getRoomData)(isaac_typescript_definitions_1.GridRoom.DEBUG);
    if (cancelRoomTransition) {
        (0, roomTransition_1.reloadRoom)();
    }
    return newRoomData;
}
exports.getRoomDataForTypeVariant = getRoomDataForTypeVariant;
/**
 * 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 getRoomItemPoolType() {
    const itemPool = cachedClasses_1.game.GetItemPool();
    const room = cachedClasses_1.game.GetRoom();
    const roomType = room.GetType();
    const roomSeed = room.GetSpawnSeed();
    return itemPool.GetPoolForRoom(roomType, roomSeed);
}
exports.getRoomItemPoolType = getRoomItemPoolType;
/**
 * Helper function to get the proper name of a room type.
 *
 * For example, `RoomType.TREASURE` will return "Treasure Room".
 */
function getRoomTypeName(roomType) {
    return roomTypeNames_1.ROOM_TYPE_NAMES[roomType];
}
exports.getRoomTypeName = getRoomTypeName;
/**
 * 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 getRooms(includeExtraDimensionalRooms = false) {
    // The naive way to get all of the rooms would be to iterate over the `RoomList` from the
    // `Level.GetRooms` method. However, this results in read-only data, and we want to return a
    // writable object. Instead, we let the heavy lifting be handled by other functions.
    const roomsInGrid = getRoomsInsideGrid(includeExtraDimensionalRooms);
    const roomsOutsideGrid = getRoomsOutsideGrid();
    return [...roomsInGrid, ...roomsOutsideGrid];
}
exports.getRooms = getRooms;
/**
 * 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 getRoomsInsideGrid(includeExtraDimensionalRooms = false) {
    const level = cachedClasses_1.game.GetLevel();
    const dimensions = includeExtraDimensionalRooms
        ? constants_1.DIMENSIONS
        : [isaac_typescript_definitions_1.Dimension.CURRENT];
    /** We use a map instead of an array because room shapes occupy more than one room grid index. */
    const roomDescriptorMap = new Map();
    for (const dimension of dimensions) {
        for (const roomGridIndex of (0, utils_1.iRange)(constants_1.MAX_LEVEL_GRID_INDEX)) {
            const roomDescriptor = level.GetRoomByIdx(roomGridIndex, dimension);
            if (roomDescriptor.Data !== undefined) {
                const ptrHash = GetPtrHash(roomDescriptor);
                roomDescriptorMap.set(ptrHash, roomDescriptor);
            }
        }
    }
    return [...roomDescriptorMap.values()];
}
exports.getRoomsInsideGrid = getRoomsInsideGrid;
/**
 * 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 getRoomsOfDimension(dimension) {
    const level = cachedClasses_1.game.GetLevel();
    /** We use a map instead of an array because room shapes occupy more than one room grid index. */
    const roomsMap = new Map();
    for (const roomGridIndex of (0, utils_1.iRange)(constants_1.MAX_LEVEL_GRID_INDEX)) {
        const roomDescriptor = level.GetRoomByIdx(roomGridIndex, dimension);
        if (roomDescriptor.Data !== undefined) {
            const ptrHash = GetPtrHash(roomDescriptor);
            roomsMap.set(ptrHash, roomDescriptor);
        }
    }
    return [...roomsMap.values()];
}
exports.getRoomsOfDimension = getRoomsOfDimension;
/**
 * 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 getRoomsOutsideGrid() {
    // We filter an array of all rooms instead of iterating over the `GridRoom` enum because it is
    // possible for mods to insert data at arbitrary negative room grid indexes.
    const readOnlyRooms = getReadOnlyRooms();
    const readOnlyRoomsOffGrid = readOnlyRooms.filter((readOnlyRoomDescriptor) => readOnlyRoomDescriptor.SafeGridIndex < 0);
    return readOnlyRoomsOffGrid.map((readOnlyRoomDescriptor) => (0, roomData_1.getRoomDescriptor)(readOnlyRoomDescriptor.SafeGridIndex));
}
exports.getRoomsOutsideGrid = getRoomsOutsideGrid;
/**
 * Helper function to determine if the current room shape is equal to `RoomShape.1x2` or
 * `RoomShape.2x1`.
 */
function in2x1Room() {
    const roomData = (0, roomData_1.getRoomData)();
    return is2x1Room(roomData);
}
exports.in2x1Room = in2x1Room;
/**
 * 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 inAngelShop() {
    const roomData = (0, roomData_1.getRoomData)();
    return isAngelShop(roomData);
}
exports.inAngelShop = inAngelShop;
/**
 * 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 inBeastRoom() {
    const roomData = (0, roomData_1.getRoomData)();
    return isBeastRoom(roomData);
}
exports.inBeastRoom = inBeastRoom;
/**
 * Helper function to detect if the current room is big. Specifically, this is all 1x2 rooms, 2x2
 * rooms, and L rooms.
 */
function inBigRoom() {
    const roomData = (0, roomData_1.getRoomData)();
    return isBigRoom(roomData);
}
exports.inBigRoom = inBigRoom;
/**
 * 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 inBossRoomOf(bossID) {
    const roomData = (0, roomData_1.getRoomData)();
    return isBossRoomOf(roomData, bossID);
}
exports.inBossRoomOf = inBossRoomOf;
/**
 * 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 inCrawlSpace() {
    const roomData = (0, roomData_1.getRoomData)();
    return isCrawlSpace(roomData);
}
exports.inCrawlSpace = inCrawlSpace;
/**
 * Helper function for checking whether the current room is a crawl space with a door corresponding
 * to `DoorSlotFlag.RIGHT_0` (1 << 2).
 */
function inCrawlSpaceWithBlackMarketEntrance() {
    const roomData = (0, roomData_1.getRoomData)();
    return isCrawlSpaceWithBlackMarketEntrance(roomData);
}
exports.inCrawlSpaceWithBlackMarketEntrance = inCrawlSpaceWithBlackMarketEntrance;
/**
 * Helper function to detect if the current room is one of the rooms in the Death Certificate area.
 */
function inDeathCertificateArea() {
    const roomData = (0, roomData_1.getRoomData)();
    return isDeathCertificateArea(roomData);
}
exports.inDeathCertificateArea = inDeathCertificateArea;
/**
 * 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 inDevilsCrownTreasureRoom() {
    const roomDescriptor = (0, roomData_1.getRoomDescriptorReadOnly)();
    return isDevilsCrownTreasureRoom(roomDescriptor);
}
exports.inDevilsCrownTreasureRoom = inDevilsCrownTreasureRoom;
/**
 * 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 inDogmaRoom() {
    const roomData = (0, roomData_1.getRoomData)();
    return isDogmaRoom(roomData);
}
exports.inDogmaRoom = inDogmaRoom;
/**
 * 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 inDoubleTrouble() {
    const roomData = (0, roomData_1.getRoomData)();
    return isDoubleTrouble(roomData);
}
exports.inDoubleTrouble = inDoubleTrouble;
/** Helper function to determine if the current room index is equal to `GridRoom.GENESIS`. */
function inGenesisRoom() {
    const roomGridIndex = (0, roomData_1.getRoomGridIndex)();
    return isGenesisRoom(roomGridIndex);
}
exports.inGenesisRoom = inGenesisRoom;
/**
 * 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 inHomeCloset() {
    const roomData = (0, roomData_1.getRoomData)();
    return isHomeCloset(roomData);
}
exports.inHomeCloset = inHomeCloset;
/** Helper function to determine if the current room shape is one of the four L room shapes. */
function inLRoom() {
    const roomData = (0, roomData_1.getRoomData)();
    return isLRoom(roomData);
}
exports.inLRoom = inLRoom;
/** Helper function to determine if the current room index is equal to `GridRoom.MEGA_SATAN`. */
function inMegaSatanRoom() {
    const roomGridIndex = (0, roomData_1.getRoomGridIndex)();
    return isMegaSatanRoom(roomGridIndex);
}
exports.inMegaSatanRoom = inMegaSatanRoom;
/**
 * Helper function to determine if the current room is part of the Repentance "escape sequence" in
 * the Mines/Ashpit.
 */
function inMineShaft() {
    const roomData = (0, roomData_1.getRoomData)();
    return isMineShaft(roomData);
}
exports.inMineShaft = inMineShaft;
/**
 * 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 inMinibossRoomOf(minibossID) {
    const roomData = (0, roomData_1.getRoomData)();
    return isMinibossRoomOf(roomData, minibossID);
}
exports.inMinibossRoomOf = inMinibossRoomOf;
/**
 * 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 inMirrorRoom() {
    const roomData = (0, roomData_1.getRoomData)();
    return isMirrorRoom(roomData);
}
exports.inMirrorRoom = inMirrorRoom;
/**
 * 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 inRoomShape(...roomShapes) {
    const roomData = (0, roomData_1.getRoomData)();
    return isRoomShape(roomData, ...roomShapes);
}
exports.inRoomShape = inRoomShape;
/**
 * 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 inRoomType(...roomTypes) {
    const roomData = (0, roomData_1.getRoomData)();
    return isRoomType(roomData, ...roomTypes);
}
exports.inRoomType = inRoomType;
/**
 * Helper function for checking if the current room is a secret exit that leads to a Repentance
 * floor.
 */
function inSecretExit() {
    const roomGridIndex = (0, roomData_1.getRoomGridIndex)();
    return isSecretExit(roomGridIndex);
}
exports.inSecretExit = inSecretExit;
/**
 * 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 inSecretShop() {
    const roomGridIndex = (0, roomData_1.getRoomGridIndex)();
    return isSecretShop(roomGridIndex);
}
exports.inSecretShop = inSecretShop;
/**
 * 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 inStartingRoom() {
    const level = cachedClasses_1.game.GetLevel();
    const startingRoomGridIndex = level.GetStartingRoomIndex();
    const roomGridIndex = (0, roomData_1.getRoomGridIndex)();
    return roomGridIndex === startingRoomGridIndex && (0, dimensions_1.inDimension)(isaac_typescript_definitions_1.Dimension.MAIN);
}
exports.inStartingRoom = inStartingRoom;
/**
 * Helper function to determine if the provided room is equal to `RoomShape.1x2` or `RoomShape.2x1`.
 */
function is2x1Room(roomData) {
    return (0, roomShape_1.is2x1RoomShape)(roomData.Shape);
}
exports.is2x1Room = is2x1Room;
/**
 * 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 isAllRoomsClear(onlyCheckRoomTypes, includeSecretRoom = false, includeSuperSecretRoom = false, includeUltraSecretRoom = false) {
    const roomsInsideGrid = getRoomsInsideGrid();
    let matchingRooms;
    if (onlyCheckRoomTypes === undefined) {
        matchingRooms = roomsInsideGrid;
    }
    else {
        const roomTypeWhitelist = new ReadonlySet_1.ReadonlySet(onlyCheckRoomTypes);
        matchingRooms = roomsInsideGrid.filter((roomDescriptor) => roomDescriptor.Data !== undefined &&
            roomTypeWhitelist.has(roomDescriptor.Data.Type));
    }
    if (!includeSecretRoom) {
        matchingRooms = matchingRooms.filter((roomDescriptor) => roomDescriptor.Data !== undefined &&
            roomDescriptor.Data.Type !== isaac_typescript_definitions_1.RoomType.SECRET);
    }
    if (!includeSuperSecretRoom) {
        matchingRooms = matchingRooms.filter((roomDescriptor) => roomDescriptor.Data !== undefined &&
            roomDescriptor.Data.Type !== isaac_typescript_definitions_1.RoomType.SUPER_SECRET);
    }
    if (!includeUltraSecretRoom) {
        matchingRooms = matchingRooms.filter((roomDescriptor) => roomDescriptor.Data !== undefined &&
            roomDescriptor.Data.Type !== isaac_typescript_definitions_1.RoomType.ULTRA_SECRET);
    }
    return matchingRooms.every((roomDescriptor) => roomDescriptor.Clear);
}
exports.isAllRoomsClear = isAllRoomsClear;
/**
 * 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 isAngelShop(roomData) {
    return (roomData.Type === isaac_typescript_definitions_1.RoomType.ANGEL &&
        roomData.Subtype === isaac_typescript_definitions_1.AngelRoomSubType.SHOP);
}
exports.isAngelShop = isAngelShop;
/**
 * 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 isBeastRoom(roomData) {
    return (roomData.Type === isaac_typescript_definitions_1.RoomType.DUNGEON &&
        roomData.Subtype === isaac_typescript_definitions_1.DungeonSubType.BEAST_ROOM);
}
exports.isBeastRoom = isBeastRoom;
/**
 * Helper function to detect if the provided room is big. Specifically, this is all 1x2 rooms, 2x2
 * rooms, and L rooms.
 */
function isBigRoom(roomData) {
    return (0, roomShape_1.isBigRoomShape)(roomData.Shape);
}
exports.isBigRoom = isBigRoom;
/**
 * 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 isBossRoomOf(roomData, bossID) {
    return (roomData.Type === isaac_typescript_definitions_1.RoomType.BOSS &&
        roomData.StageID === isaac_typescript_definitions_1.StageID.SPECIAL_ROOMS &&
        roomData.Subtype === bossID);
}
exports.isBossRoomOf = isBossRoomOf;
/**
 * 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 isCrawlSpace(roomData) {
    return (roomData.Type === isaac_typescript_definitions_1.RoomType.DUNGEON &&
        roomData.Subtype === isaac_typescript_definitions_1.DungeonSubType.NORMAL);
}
exports.isCrawlSpace = isCrawlSpace;
/**
 * Helper function for checking whether the provided room is a crawl space with a door corresponding
 * to `DoorSlotFlag.RIGHT_0` (1 << 2).
 */
function isCrawlSpaceWithBlackMarketEntrance(roomData) {
    return (isCrawlSpace(roomData) && (0, flag_1.hasFlag)(roomData.Doors, isaac_typescript_definitions_1.DoorSlotFlag.RIGHT_0));
}
exports.isCrawlSpaceWithBlackMarketEntrance = isCrawlSpaceWithBlackMarketEntrance;
/**
 * Helper function to detect if the provided room is one of the rooms in the Death Certificate area.
 */
function isDeathCertificateArea(roomData) {
    return (roomData.StageID === isaac_typescript_definitions_1.StageID.HOME &&
        (roomData.Subtype === isaac_typescript_definitions_1.HomeRoomSubType.DEATH_CERTIFICATE_ENTRANCE ||
            roomData.Subtype === isaac_typescript_definitions_1.HomeRoomSubType.DEATH_CERTIFICATE_ITEMS));
}
exports.isDeathCertificateArea = isDeathCertificateArea;
/**
 * 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 isDevilsCrownTreasureRoom(roomDescriptor) {
    return (0, flag_1.hasFlag)(roomDescriptor.Flags, isaac_typescript_definitions_1.RoomDescriptorFlag.DEVIL_TREASURE);
}
exports.isDevilsCrownTreasureRoom = isDevilsCrownTreasureRoom;
/**
 * 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 isDogmaRoom(roomData) {
    return (roomData.StageID === isaac_typescript_definitions_1.StageID.HOME &&
        roomData.Type === isaac_typescript_definitions_1.RoomType.DEFAULT &&
        roomData.Variant === 1000 &&
        roomData.Subtype === isaac_typescript_definitions_1.HomeRoomSubType.LIVING_ROOM);
}
exports.isDogmaRoom = isDogmaRoom;
/**
 * 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 isDoubleTrouble(roomData) {
    return (roomData.Type === isaac_typescript_definitions_1.RoomType.BOSS && roomData.Name.includes("Double Trouble"));
}
exports.isDoubleTrouble = isDoubleTrouble;
/**
 * Helper function to determine if the index of the provided room is equal to `GridRoom.GENESIS`.
 */
function isGenesisRoom(roomGridIndex) {
    return roomGridIndex === isaac_typescript_definitions_1.GridRoom.GENESIS;
}
exports.isGenesisRoom = isGenesisRoom;
/**
 * 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 isHomeCloset(roomData) {
    return (roomData.StageID === isaac_typescript_definitions_1.StageID.HOME &&
        (roomData.Subtype === isaac_typescript_definitions_1.HomeRoomSubType.CLOSET_LEFT ||
            roomData.Subtype === isaac_typescript_definitions_1.HomeRoomSubType.CLOSET_RIGHT));
}
exports.isHomeCloset = isHomeCloset;
/** Helper function to determine if the provided room is one of the four L room shapes. */
function isLRoom(roomData) {
    return (0, roomShape_1.isLRoomShape)(roomData.Shape);
}
exports.isLRoom = isLRoom;
/**
 * Helper function to determine if the index of the provided room is equal to `GridRoom.MEGA_SATAN`.
 */
function isMegaSatanRoom(roomGridIndex) {
    return roomGridIndex === isaac_typescript_definitions_1.GridRoom.MEGA_SATAN;
}
exports.isMegaSatanRoom = isMegaSatanRoom;
/**
 * Helper function to determine if the provided room is part of the Repentance "escape sequence" in
 * the Mines/Ashpit.
 */
function isMineShaft(roomData) {
    return ((roomData.StageID === isaac_typescript_definitions_1.StageID.MINES ||
        roomData.StageID === isaac_typescript_definitions_1.StageID.ASHPIT) &&
        // eslint-disable-next-line isaacscript/strict-enums
        mineShaftRoomSubTypesSet_1.MINE_SHAFT_ROOM_SUB_TYPE_SET.has(roomData.Subtype));
}
exports.isMineShaft = isMineShaft;
/**
 * 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 isMinibossRoomOf(roomData, minibossID) {
    return (roomData.Type === isaac_typescript_definitions_1.RoomType.MINI_BOSS &&
        roomData.StageID === isaac_typescript_definitions_1.StageID.SPECIAL_ROOMS &&
        roomData.Subtype === minibossID);
}
exports.isMinibossRoomOf = isMinibossRoomOf;
/**
 * 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 isMirrorRoom(roomData) {
    return (roomData.Type === isaac_typescript_definitions_1.RoomType.DEFAULT &&
        (roomData.StageID === isaac_typescript_definitions_1.StageID.DOWNPOUR ||
            roomData.StageID === isaac_typescript_definitions_1.StageID.DROSS) &&
        roomData.Subtype === isaac_typescript_definitions_1.DownpourRoomSubType.MIRROR);
}
exports.isMirrorRoom = isMirrorRoom;
/**
 * 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 isRoomShape(roomData, ...roomShapes) {
    return roomShapes.includes(roomData.Shape);
}
exports.isRoomShape = isRoomShape;
/**
 * 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 isRoomType(roomData, ...roomTypes) {
    return roomTypes.includes(roomData.Type);
}
exports.isRoomType = isRoomType;
/**
 * Helper function for checking if the provided room is a secret exit that leads to a Repentance
 * floor.
 */
function isSecretExit(roomGridIndex) {
    return roomGridIndex === isaac_typescript_definitions_1.GridRoom.SECRET_EXIT;
}
exports.isSecretExit = isSecretExit;
/**
 * Helper function to detect if a room type is a Secret Room, a Super Secret Room, or an Ultra
 * Secret Room.
 */
function isSecretRoomType(roomType) {
    return SECRET_ROOM_TYPES.has(roomType);
}
exports.isSecretRoomType = isSecretRoomType;
/**
 * 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 isSecretShop(roomGridIndex) {
    return roomGridIndex === isaac_typescript_definitions_1.GridRoom.SECRET_SHOP;
}
exports.isSecretShop = isSecretShop;
/**
 * 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 roomUpdateSafe() {
    const room = cachedClasses_1.game.GetRoom();
    const entities = (0, entities_1.getEntities)();
    const entityPositions = (0, positionVelocity_1.getEntityPositions)(entities);
    const entityVelocities = (0, positionVelocity_1.getEntityVelocities)(entities);
    room.Update();
    (0, positionVelocity_1.setEntityPositions)(entityPositions, entities);
    (0, positionVelocity_1.setEntityVelocities)(entityVelocities, entities);
}
exports.roomUpdateSafe = roomUpdateSafe;
/** Helper function to set the backdrop (i.e. background) of the current room. */
function setBackdrop(backdropType) {
    cachedClasses_1.game.ShowHallucination(0, backdropType);
    cachedClasses_1.sfxManager.Stop(isaac_typescript_definitions_1.SoundEffect.DEATH_CARD);
}
exports.setBackdrop = setBackdrop;
/**
 * 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 setRoomCleared() {
    const room = cachedClasses_1.game.GetRoom();
    const roomClear = room.IsClear();
    // If the room is already cleared, we don't have to do anything.
    if (roomClear) {
        return;
    }
    room.SetClear(true);
    for (const door of (0, doors_1.getDoors)()) {
        if ((0, doors_1.isHiddenSecretRoomDoor)(door)) {
            continue;
        }
        // We don't use the `EntityDoor.Open` method since that will cause the door to play an
        // animation.
        (0, doors_1.openDoorFast)(door);
        // If this is a mini-boss room, then the door would be barred in addition to being closed.
        // Ensure that the bar is not visible.
        door.ExtraVisible = false;
    }
    cachedClasses_1.sfxManager.Stop(isaac_typescript_definitions_1.SoundEffect.DOOR_HEAVY_OPEN);
    // If the room contained Mom's Hands, then a screen shake will be queued. Override it with a 0
    // frame shake.
    cachedClasses_1.game.ShakeScreen(0);
}
exports.setRoomCleared = setRoomCleared;
/**
 * Helper function to emulate what happens when you bomb an Angel Statue or push a Reward Plate that
 * spawns an NPC.
 */
function setRoomUncleared() {
    const room = cachedClasses_1.game.GetRoom();
    room.SetClear(false);
    (0, doors_1.closeAllDoors)();
}
exports.setRoomUncleared = setRoomUncleared;
