UNPKG

isaacscript-common

Version:

Helper functions and features for IsaacScript mods.

794 lines (793 loc) • 36.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.changeRoom = changeRoom; exports.getNumRooms = getNumRooms; exports.getReadOnlyRooms = getReadOnlyRooms; exports.getRoomDataForTypeVariant = getRoomDataForTypeVariant; exports.getRoomItemPoolType = getRoomItemPoolType; exports.getRoomTypeName = getRoomTypeName; exports.getRooms = getRooms; exports.getRoomsInsideGrid = getRoomsInsideGrid; exports.getRoomsOfDimension = getRoomsOfDimension; exports.getRoomsOutsideGrid = getRoomsOutsideGrid; exports.in2x1Room = in2x1Room; exports.inAngelShop = inAngelShop; exports.inBeastRoom = inBeastRoom; exports.inBigRoom = inBigRoom; exports.inBossRoomOf = inBossRoomOf; exports.inCrawlSpace = inCrawlSpace; exports.inCrawlSpaceWithBlackMarketEntrance = inCrawlSpaceWithBlackMarketEntrance; exports.inDeathCertificateArea = inDeathCertificateArea; exports.inDevilsCrownTreasureRoom = inDevilsCrownTreasureRoom; exports.inDogmaRoom = inDogmaRoom; exports.inDoubleTrouble = inDoubleTrouble; exports.inGenesisRoom = inGenesisRoom; exports.inHomeCloset = inHomeCloset; exports.inLRoom = inLRoom; exports.inMegaSatanRoom = inMegaSatanRoom; exports.inMineShaft = inMineShaft; exports.inMinibossRoomOf = inMinibossRoomOf; exports.inMirrorRoom = inMirrorRoom; exports.inRoomShape = inRoomShape; exports.inRoomType = inRoomType; exports.inSecretExit = inSecretExit; exports.inSecretShop = inSecretShop; exports.inStartingRoom = inStartingRoom; exports.is2x1Room = is2x1Room; exports.isAllRoomsClear = isAllRoomsClear; exports.isAngelShop = isAngelShop; exports.isBeastRoom = isBeastRoom; exports.isBigRoom = isBigRoom; exports.isBossRoomOf = isBossRoomOf; exports.isCrawlSpace = isCrawlSpace; exports.isCrawlSpaceWithBlackMarketEntrance = isCrawlSpaceWithBlackMarketEntrance; exports.isDeathCertificateArea = isDeathCertificateArea; exports.isDevilsCrownTreasureRoom = isDevilsCrownTreasureRoom; exports.isDogmaRoom = isDogmaRoom; exports.isDoubleTrouble = isDoubleTrouble; exports.isGenesisRoom = isGenesisRoom; exports.isHomeCloset = isHomeCloset; exports.isLRoom = isLRoom; exports.isMegaSatanRoom = isMegaSatanRoom; exports.isMineShaft = isMineShaft; exports.isMinibossRoomOf = isMinibossRoomOf; exports.isMirrorRoom = isMirrorRoom; exports.isRoomShape = isRoomShape; exports.isRoomType = isRoomType; exports.isSecretExit = isSecretExit; exports.isSecretRoomType = isSecretRoomType; exports.isSecretShop = isSecretShop; exports.roomUpdateSafe = roomUpdateSafe; exports.setBackdrop = setBackdrop; exports.setRoomCleared = setRoomCleared; exports.setRoomUncleared = setRoomUncleared; 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); } /** * 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; } /** * 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; } /** * 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; } /** * 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); } /** * 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]; } /** * 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]; } /** * 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 will 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()]; } /** * 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()]; } /** * 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)); } /** * 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); } /** * 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); } /** * 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); } /** * 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); } /** * 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); } /** * 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); } /** * 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); } /** * 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); } /** * 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); } /** * 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); } /** * 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); } /** 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); } /** * 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); } /** 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); } /** 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); } /** * 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); } /** * 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); } /** * 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); } /** * 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); } /** * 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); } /** * 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); } /** * 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); } /** * 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); } /** * 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); } /** * 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); } /** * 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 // eslint-disable-next-line @typescript-eslint/no-unsafe-enum-comparison && roomData.Subtype === isaac_typescript_definitions_1.AngelRoomSubType.SHOP); } /** * 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 // eslint-disable-next-line @typescript-eslint/no-unsafe-enum-comparison && roomData.Subtype === isaac_typescript_definitions_1.DungeonSubType.BEAST_ROOM); } /** * 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); } /** * 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 // eslint-disable-next-line @typescript-eslint/no-unsafe-enum-comparison && roomData.Subtype === bossID); } /** * 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 // eslint-disable-next-line @typescript-eslint/no-unsafe-enum-comparison && roomData.Subtype === isaac_typescript_definitions_1.DungeonSubType.NORMAL); } /** * 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)); } /** * 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 // eslint-disable-next-line @typescript-eslint/no-unsafe-enum-comparison && (roomData.Subtype === isaac_typescript_definitions_1.HomeRoomSubType.DEATH_CERTIFICATE_ENTRANCE // eslint-disable-next-line @typescript-eslint/no-unsafe-enum-comparison || roomData.Subtype === isaac_typescript_definitions_1.HomeRoomSubType.DEATH_CERTIFICATE_ITEMS)); } /** * 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); } /** * 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 // eslint-disable-next-line @typescript-eslint/no-unsafe-enum-comparison && roomData.Subtype === isaac_typescript_definitions_1.HomeRoomSubType.LIVING_ROOM); } /** * 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")); } /** * Helper function to determine if the index of the provided room is equal to `GridRoom.GENESIS`. */ function isGenesisRoom(roomGridIndex) { // eslint-disable-next-line @typescript-eslint/no-unsafe-enum-comparison return roomGridIndex === isaac_typescript_definitions_1.GridRoom.GENESIS; } /** * 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 // eslint-disable-next-line @typescript-eslint/no-unsafe-enum-comparison && (roomData.Subtype === isaac_typescript_definitions_1.HomeRoomSubType.CLOSET_LEFT // eslint-disable-next-line @typescript-eslint/no-unsafe-enum-comparison || roomData.Subtype === isaac_typescript_definitions_1.HomeRoomSubType.CLOSET_RIGHT)); } /** 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); } /** * Helper function to determine if the index of the provided room is equal to `GridRoom.MEGA_SATAN`. */ function isMegaSatanRoom(roomGridIndex) { // eslint-disable-next-line @typescript-eslint/no-unsafe-enum-comparison return roomGridIndex === isaac_typescript_definitions_1.GridRoom.MEGA_SATAN; } /** * 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 complete/strict-enums && mineShaftRoomSubTypesSet_1.MINE_SHAFT_ROOM_SUB_TYPE_SET.has(roomData.Subtype)); } /** * 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 // eslint-disable-next-line @typescript-eslint/no-unsafe-enum-comparison && roomData.Subtype === minibossID); } /** * 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) // eslint-disable-next-line @typescript-eslint/no-unsafe-enum-comparison && roomData.Subtype === isaac_typescript_definitions_1.DownpourRoomSubType.MIRROR); } /** * 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); } /** * 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); } /** * Helper function for checking if the provided room is a secret exit that leads to a Repentance * floor. */ function isSecretExit(roomGridIndex) { // eslint-disable-next-line @typescript-eslint/no-unsafe-enum-comparison return roomGridIndex === isaac_typescript_definitions_1.GridRoom.SECRET_EXIT; } /** * 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); } /** * 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) { // eslint-disable-next-line @typescript-eslint/no-unsafe-enum-comparison return roomGridIndex === isaac_typescript_definitions_1.GridRoom.SECRET_SHOP; } /** * 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); } /** 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); } /** * 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); } /** * 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)(); }