"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.isRaglingDeathPatch = exports.isDyingEggyWithNoSpidersLeft = exports.isDyingDump = exports.isDaddyLongLegsChildStompEntity = exports.isAliveExceptionNPC = exports.getAliveNPCs = void 0;
const isaac_typescript_definitions_1 = require("isaac-typescript-definitions");
const constants_1 = require("../core/constants");
const ReadonlySet_1 = require("../types/ReadonlySet");
const entitiesSpecific_1 = require("./entitiesSpecific");
/**
 * Used to filter out certain NPCs when determining of an NPC is "alive" and/or should keep the
 * doors open.
 */
const NON_ALIVE_NPCS_TYPE_VARIANT = new ReadonlySet_1.ReadonlySet([
    `${isaac_typescript_definitions_1.EntityType.VIS}.${isaac_typescript_definitions_1.VisVariant.CHUBBER_PROJECTILE}`,
    `${isaac_typescript_definitions_1.EntityType.DEATH}.${isaac_typescript_definitions_1.DeathVariant.DEATH_SCYTHE}`,
    `${isaac_typescript_definitions_1.EntityType.PEEP}.${isaac_typescript_definitions_1.PeepVariant.PEEP_EYE}`,
    `${isaac_typescript_definitions_1.EntityType.PEEP}.${isaac_typescript_definitions_1.PeepVariant.BLOAT_EYE}`,
    `${isaac_typescript_definitions_1.EntityType.BEGOTTEN}.${isaac_typescript_definitions_1.BegottenVariant.BEGOTTEN_CHAIN}`,
    `${isaac_typescript_definitions_1.EntityType.MAMA_GURDY}.${isaac_typescript_definitions_1.MamaGurdyVariant.LEFT_HAND}`,
    `${isaac_typescript_definitions_1.EntityType.MAMA_GURDY}.${isaac_typescript_definitions_1.MamaGurdyVariant.RIGHT_HAND}`,
    `${isaac_typescript_definitions_1.EntityType.BIG_HORN}.${isaac_typescript_definitions_1.BigHornVariant.SMALL_HOLE}`,
    `${isaac_typescript_definitions_1.EntityType.BIG_HORN}.${isaac_typescript_definitions_1.BigHornVariant.BIG_HOLE}`,
    `${isaac_typescript_definitions_1.EntityType.DARK_ESAU}.${isaac_typescript_definitions_1.DarkEsauVariant.DARK_ESAU}`,
    `${isaac_typescript_definitions_1.EntityType.DARK_ESAU}.${isaac_typescript_definitions_1.DarkEsauVariant.PIT}`, // 866.1
]);
/**
 * Used to filter out certain NPCs when determining of an NPC is "alive" and/or should keep the
 * doors open.
 */
const NON_ALIVE_NPCS_TYPE_VARIANT_SUB_TYPE = new ReadonlySet_1.ReadonlySet([
    `${isaac_typescript_definitions_1.EntityType.CHARGER}.${isaac_typescript_definitions_1.ChargerVariant.CHARGER}.${isaac_typescript_definitions_1.ChargerSubType.MY_SHADOW}`,
    `${isaac_typescript_definitions_1.EntityType.MOTHER}.${isaac_typescript_definitions_1.MotherVariant.MOTHER_1}.${isaac_typescript_definitions_1.MotherSubType.PHASE_2}`, // 912
]);
/**
 * Helper function to get all of the non-dead NPCs in the room.
 *
 * This function will not include NPCs on an internal blacklist, such as Death's scythes or Big Horn
 * holes.
 *
 * @param entityType Optional. If specified, will only get the NPCs that match the type. Default is
 *                   -1, which matches every type.
 * @param variant Optional. If specified, will only get the NPCs that match the variant. Default is
 *                -1, which matches every variant.
 * @param subType Optional. If specified, will only get the NPCs that match the sub-type. Default is
 *                -1, which matches every sub-type.
 * @param ignoreFriendly Optional. Default is false.
 */
function getAliveNPCs(entityType = -1, variant = -1, subType = -1, ignoreFriendly = false) {
    const npcs = (0, entitiesSpecific_1.getNPCs)(entityType, variant, subType, ignoreFriendly);
    return npcs.filter((npc) => !npc.IsDead() && !isAliveExceptionNPC(npc));
}
exports.getAliveNPCs = getAliveNPCs;
/**
 * Checks for specific NPCs that have "CanShutDoors" set to true naturally by the game, but should
 * not actually keep the doors closed (like Death's scythes).
 */
function isAliveExceptionNPC(npc) {
    const entityTypeVariant = `${npc.Type}.${npc.Variant}`;
    if (NON_ALIVE_NPCS_TYPE_VARIANT.has(entityTypeVariant)) {
        return true;
    }
    const entityTypeVariantSubType = `${npc.Type}.${npc.Variant}.${npc.SubType}`;
    if (NON_ALIVE_NPCS_TYPE_VARIANT_SUB_TYPE.has(entityTypeVariantSubType)) {
        return true;
    }
    // EntityType.HOPPER (29)
    // HopperVariant.EGGY (2)
    if (isDyingEggyWithNoSpidersLeft(npc)) {
        return true;
    }
    // EntityType.DADDY_LONG_LEGS (101)
    if (isDaddyLongLegsChildStompEntity(npc)) {
        return true;
    }
    // EntityType.RAGLING (256)
    if (isRaglingDeathPatch(npc)) {
        return true;
    }
    // EntityType.DUMP (876)
    if (isDyingDump(npc)) {
        return true;
    }
    return false;
}
exports.isAliveExceptionNPC = isAliveExceptionNPC;
/**
 * Helper function to distinguish between a normal Daddy Long Legs / Triachnid and the child entity
 * that is spawned when the boss does the multi-stomp attack.
 *
 * When this attack occurs, four extra copies of Daddy Long Legs will be spawned with the same
 * entity type, variant, and sub-type. The `Entity.Parent` field will be undefined in this case, so
 * the way to tell them apart is to check for a non-undefined `Entity.SpawnerEntity` field.
 */
function isDaddyLongLegsChildStompEntity(npc) {
    return (npc.Type === isaac_typescript_definitions_1.EntityType.DADDY_LONG_LEGS && npc.SpawnerEntity !== undefined);
}
exports.isDaddyLongLegsChildStompEntity = isDaddyLongLegsChildStompEntity;
/**
 * Helper function to detect the custom death state of a Dump. When Dumps die, they go to
 * `NPCState.SPECIAL`, spit out their head, and then slowly fade away while shooting a burst of
 * tears.
 */
function isDyingDump(npc) {
    return (npc.Type === isaac_typescript_definitions_1.EntityType.DUMP &&
        npc.Variant === isaac_typescript_definitions_1.DumpVariant.DUMP &&
        npc.State === isaac_typescript_definitions_1.NPCState.SPECIAL);
}
exports.isDyingDump = isDyingDump;
/**
 * Helper function to detect the custom death state of an Eggy. Eggies are never actually marked
 * dead by the game. Instead, when Eggies take fatal damage, they go into NPCState.STATE_SUICIDE and
 * spawn 14 Swarm Spiders while their StateFrame ticks upwards.
 */
function isDyingEggyWithNoSpidersLeft(npc) {
    return (npc.Type === isaac_typescript_definitions_1.EntityType.HOPPER &&
        npc.Variant === isaac_typescript_definitions_1.HopperVariant.EGGY &&
        npc.State === isaac_typescript_definitions_1.NPCState.SUICIDE &&
        npc.StateFrame >= constants_1.EGGY_STATE_FRAME_OF_FINAL_SPIDER);
}
exports.isDyingEggyWithNoSpidersLeft = isDyingEggyWithNoSpidersLeft;
/**
 * Helper function to detect the custom death state of a Rag Man Ragling. When Rag Man Raglings die,
 * they turn into a patch on the ground and can be revived by Rag Man at a later time. This causes
 * them to show up as an "alive" enemy, so they should usually be filtered out of lists of alive
 * enemies.
 */
function isRaglingDeathPatch(npc) {
    return (npc.Type === isaac_typescript_definitions_1.EntityType.RAGLING &&
        npc.Variant === isaac_typescript_definitions_1.RaglingVariant.RAG_MANS_RAGLING &&
        // They go to `STATE_SPECIAL` when they are patches on the ground.
        npc.State === isaac_typescript_definitions_1.NPCState.SPECIAL);
}
exports.isRaglingDeathPatch = isRaglingDeathPatch;
