"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.spawnBossWithSeed = exports.spawnBoss = exports.isSin = exports.isRepentanceBoss = exports.getEntityTypeVariantFromBossID = exports.getBosses = exports.getBossStageIDs = exports.getBossName = exports.getBossIDsForStageID = exports.getBossIDsForStage = exports.getBossIDFromEntityTypeVariant = exports.getBossID = exports.getAllNonStoryBosses = exports.getAllBosses = exports.getAliveBosses = void 0;
const isaac_typescript_definitions_1 = require("isaac-typescript-definitions");
const cachedClasses_1 = require("../core/cachedClasses");
const constants_1 = require("../core/constants");
const entityTypeVariantToBossIDMap_1 = require("../maps/entityTypeVariantToBossIDMap");
const bossIDToEntityTypeVariant_1 = require("../objects/bossIDToEntityTypeVariant");
const bossNames_1 = require("../objects/bossNames");
const bossSets_1 = require("../sets/bossSets");
const repentanceBossIDsSet_1 = require("../sets/repentanceBossIDsSet");
const sinEntityTypesSet_1 = require("../sets/sinEntityTypesSet");
const ReadonlySet_1 = require("../types/ReadonlySet");
const entities_1 = require("./entities");
const entitiesSpecific_1 = require("./entitiesSpecific");
const npcs_1 = require("./npcs");
const rng_1 = require("./rng");
const rooms_1 = require("./rooms");
const utils_1 = require("./utils");
const BOSSES_THAT_REQUIRE_MULTIPLE_SPAWNS = new ReadonlySet_1.ReadonlySet([
    isaac_typescript_definitions_1.EntityType.LARRY_JR,
    isaac_typescript_definitions_1.EntityType.CHUB,
    isaac_typescript_definitions_1.EntityType.LOKI,
    isaac_typescript_definitions_1.EntityType.GURGLING,
    isaac_typescript_definitions_1.EntityType.TURDLET, // 918
]);
const DEFAULT_BOSS_MULTI_SEGMENTS = 4;
/**
 * Helper function to get all of the non-dead bosses in the room.
 *
 * This function will not include bosses on an internal blacklist, such as Death's scythes or Big
 * Horn holes.
 *
 * @param entityType Optional. If specified, will only get the bosses that match the type. Default
 *                   is -1, which matches every type.
 * @param variant Optional. If specified, will only get the bosses that match the variant. Default
 *                is -1, which matches every variant.
 * @param subType Optional. If specified, will only get the bosses that match the sub-type. Default
 *                is -1, which matches every sub-type.
 * @param ignoreFriendly Optional. Default is false.
 */
function getAliveBosses(entityType = -1, variant = -1, subType = -1, ignoreFriendly = false) {
    const aliveNPCs = (0, npcs_1.getAliveNPCs)(entityType, variant, subType, ignoreFriendly);
    return aliveNPCs.filter((aliveNPC) => aliveNPC.IsBoss());
}
exports.getAliveBosses = getAliveBosses;
/**
 * Helper function to get an array with every boss in the game. This is derived from the `BossID`
 * enum.
 *
 * This includes:
 * - Ultra Greed
 * - Ultra Greedier
 *
 * This does not include:
 * - mini-bosses (e.g. Ultra Pride, Krampus)
 * - bosses that do not appear in Boss Rooms (e.g. Uriel, Gabriel)
 * - the second phase of multi-phase bosses (e.g. Mega Satan 2)
 * - sub-bosses of The Beast fight (e.g. Ultra Famine, Ultra Pestilence, Ultra War, Ultra Death)
 * - bosses that do not have any Boss Rooms defined due to being unfinished (e.g. Raglich)
 *
 * Also see the `getAllNonStoryBosses` function.
 */
function getAllBosses() {
    return bossSets_1.ALL_BOSSES;
}
exports.getAllBosses = getAllBosses;
/**
 * Helper function to get an array with every boss in the game. This is derived from the `BossID`
 * enum. This is the same thing as the `getAllBosses` helper function, but with story bosses
 * filtered out.
 */
function getAllNonStoryBosses() {
    return bossSets_1.NON_STORY_BOSSES;
}
exports.getAllNonStoryBosses = getAllNonStoryBosses;
/**
 * Helper function to get the boss ID corresponding to the current room. Returns undefined if the
 * current room is not a Boss Room.
 *
 * Use this instead of the vanilla `Room.GetBossID` method since it has a saner return type and it
 * correctly handles Dogma, The Beast, and Ultra Greedier.
 */
function getBossID() {
    if ((0, rooms_1.inDogmaRoom)()) {
        return isaac_typescript_definitions_1.BossID.DOGMA;
    }
    if ((0, rooms_1.inBeastRoom)()) {
        return isaac_typescript_definitions_1.BossID.BEAST;
    }
    const room = cachedClasses_1.game.GetRoom();
    // eslint-disable-next-line deprecation/deprecation
    const bossID = room.GetBossID();
    if (bossID === 0) {
        return undefined;
    }
    // The Ultra Greed room holds both Ultra Greed and Ultra Greedier.
    if (bossID === isaac_typescript_definitions_1.BossID.ULTRA_GREED &&
        (0, entities_1.doesEntityExist)(isaac_typescript_definitions_1.EntityType.ULTRA_GREED, isaac_typescript_definitions_1.UltraGreedVariant.ULTRA_GREEDIER)) {
        return isaac_typescript_definitions_1.BossID.ULTRA_GREEDIER;
    }
    return bossID;
}
exports.getBossID = getBossID;
function getBossIDFromEntityTypeVariant(entityType, variant) {
    const entityTypeVariant = `${entityType}.${variant}`;
    return entityTypeVariantToBossIDMap_1.ENTITY_TYPE_VARIANT_TO_BOSS_ID_MAP.get(entityTypeVariant);
}
exports.getBossIDFromEntityTypeVariant = getBossIDFromEntityTypeVariant;
/**
 * Helper function to get the set of vanilla bosses for a particular stage across all of the stage
 * types. For example, specifying `LevelStage.BASEMENT_2` will return a set with all of the bosses
 * for Basement, Cellar, Burning Basement, Downpour, and Dross.
 *
 * Also see the `getAllBossesSet` and `getBossIDsForStageID` functions.
 */
function getBossIDsForStage(stage) {
    return bossSets_1.STAGE_TO_COMBINED_BOSS_SET_MAP.get(stage);
}
exports.getBossIDsForStage = getBossIDsForStage;
/**
 * Helper function to get the set of vanilla bosses that can randomly appear on a particular stage
 * ID.
 *
 * Also see the `getAllBossesSet` and `getBossIDsForStage` functions.
 */
function getBossIDsForStageID(stageID) {
    return bossSets_1.STAGE_ID_TO_BOSS_IDS.get(stageID);
}
exports.getBossIDsForStageID = getBossIDsForStageID;
/**
 * Helper function to get the proper English name for a boss. For example, the name for
 * `BossID.WRETCHED` (36) is "The Wretched".
 */
function getBossName(bossID) {
    // Handle modded boss IDs.
    // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
    return bossNames_1.BOSS_NAMES[bossID] ?? bossNames_1.DEFAULT_BOSS_NAME;
}
exports.getBossName = getBossName;
/** Helper function to get the set of stage IDs that a particular boss naturally appears in. */
function getBossStageIDs(bossID) {
    return bossSets_1.BOSS_ID_TO_STAGE_IDS[bossID];
}
exports.getBossStageIDs = getBossStageIDs;
/**
 * Helper function to get all of the bosses in the room.
 *
 * @param entityType Optional. If specified, will only get the bosses that match the type. Default
 *                   is -1, which matches every type.
 * @param variant Optional. If specified, will only get the bosses that match the variant. Default
 *                is -1, which matches every variant.
 * @param subType Optional. If specified, will only get the bosses that match the sub-type. Default
 *                is -1, which matches every sub-type.
 * @param ignoreFriendly Optional. Default is false.
 */
function getBosses(entityType, variant, subType, ignoreFriendly = false) {
    const npcs = (0, entitiesSpecific_1.getNPCs)(entityType, variant, subType, ignoreFriendly);
    return npcs.filter((npc) => npc.IsBoss());
}
exports.getBosses = getBosses;
function getEntityTypeVariantFromBossID(bossID) {
    return bossIDToEntityTypeVariant_1.BOSS_ID_TO_ENTITY_TYPE_VARIANT[bossID];
}
exports.getEntityTypeVariantFromBossID = getEntityTypeVariantFromBossID;
/**
 * Helper function to check if a boss is only found on a Repentance floor such as Dross, Mines, and
 * so on.
 *
 * For example, The Pile is a boss that was added in Repentance, but since it can appear in
 * Necropolis, it is not considered a Repentance boss for the purposes of this function.
 */
function isRepentanceBoss(bossID) {
    return repentanceBossIDsSet_1.REPENTANCE_ONLY_BOSS_IDS_SET.has(bossID);
}
exports.isRepentanceBoss = isRepentanceBoss;
/** Helper function to check if the provided NPC is a Sin miniboss, such as Sloth or Lust. */
function isSin(npc) {
    return sinEntityTypesSet_1.SIN_ENTITY_TYPES_SET.has(npc.Type);
}
exports.isSin = isSin;
function getNumBossSegments(entityType, variant, numSegments) {
    if (numSegments !== undefined) {
        return numSegments;
    }
    switch (entityType) {
        // 28
        case isaac_typescript_definitions_1.EntityType.CHUB: {
            // Chub is always composed of 3 segments.
            return 3;
        }
        // 69
        case isaac_typescript_definitions_1.EntityType.LOKI: {
            return variant === isaac_typescript_definitions_1.LokiVariant.LOKII ? 2 : 1;
        }
        // 237
        case isaac_typescript_definitions_1.EntityType.GURGLING: {
            // Gurglings & Turdlings are always encountered in groups of 2.
            return 2;
        }
        default: {
            return DEFAULT_BOSS_MULTI_SEGMENTS;
        }
    }
}
/**
 * Helper function to spawn a boss.
 *
 * Use this function instead of `spawnNPC` since it handles automatically spawning multiple segments
 * for multi-segment bosses.
 *
 * By default, this will spawn Chub (and his variants) with 3 segments, Lokii with 2 copies,
 * Gurglings/Turdlings with 2 copies, and other multi-segment bosses with 4 segments. You can
 * customize this via the "numSegments" argument.
 */
function spawnBoss(entityType, variant, subType, positionOrGridIndex, velocity = constants_1.VectorZero, spawner = undefined, seedOrRNG = undefined, numSegments) {
    const seed = (0, rng_1.isRNG)(seedOrRNG) ? seedOrRNG.Next() : seedOrRNG;
    const npc = (0, entitiesSpecific_1.spawnNPC)(entityType, variant, subType, positionOrGridIndex, velocity, spawner, seed);
    if (BOSSES_THAT_REQUIRE_MULTIPLE_SPAWNS.has(entityType)) {
        const numBossSegments = getNumBossSegments(entityType, variant, numSegments);
        const remainingSegmentsToSpawn = numBossSegments - 1;
        (0, utils_1.repeat)(remainingSegmentsToSpawn, () => {
            (0, entitiesSpecific_1.spawnNPC)(entityType, variant, subType, positionOrGridIndex, velocity, spawner, seed);
        });
    }
    return npc;
}
exports.spawnBoss = spawnBoss;
/**
 * Helper function to spawn a boss with a specific seed.
 *
 * For more information, see the documentation for the `spawnBoss` function.
 */
function spawnBossWithSeed(entityType, variant, subType, positionOrGridIndex, seedOrRNG, velocity = constants_1.VectorZero, spawner = undefined, numSegments) {
    const seed = (0, rng_1.isRNG)(seedOrRNG) ? seedOrRNG.Next() : seedOrRNG;
    return spawnBoss(entityType, variant, subType, positionOrGridIndex, velocity, spawner, seed, numSegments);
}
exports.spawnBossWithSeed = spawnBossWithSeed;
