"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.willReviveFromSpiritShackles = exports.willReviveFromHeartbreak = exports.willPlayerRevive = exports.willMysteriousPaperRevive = exports.isDamageToPlayerFatal = void 0;
const isaac_typescript_definitions_1 = require("isaac-typescript-definitions");
const constants_1 = require("../core/constants");
const MysteriousPaperEffect_1 = require("../enums/MysteriousPaperEffect");
const characters_1 = require("./characters");
const frames_1 = require("./frames");
const playerHealth_1 = require("./playerHealth");
const players_1 = require("./players");
const sprites_1 = require("./sprites");
const trinketGive_1 = require("./trinketGive");
const trinkets_1 = require("./trinkets");
/**
 * Uses the player's current health and other miscellaneous things to determine if incoming damage
 * will be fatal.
 */
function isDamageToPlayerFatal(player, amount, source, lastDamageGameFrame) {
    const character = player.GetPlayerType();
    const effects = player.GetEffects();
    const isBerserk = effects.HasCollectibleEffect(isaac_typescript_definitions_1.CollectibleType.BERSERK);
    // If we are Tainted Jacob and the damage source is Dark Esau, this will not be fatal damage
    // (because we will transform into Tainted Jacob's lost form).
    if (character === isaac_typescript_definitions_1.PlayerType.JACOB_B &&
        source.Type === isaac_typescript_definitions_1.EntityType.DARK_ESAU) {
        return false;
    }
    // If we are berserk, no damage is fatal. (The death is deferred until the end of the berserk
    // effect.)
    if (isBerserk) {
        return false;
    }
    // If we are playing Tainted Samson and the incoming hit will cause us to become Berserk, then
    // this will not be fatal damage.
    const berserkChargeAfterHit = player.SamsonBerserkCharge +
        constants_1.TAINTED_SAMSON_BERSERK_CHARGE_FROM_TAKING_DAMAGE;
    if (character === isaac_typescript_definitions_1.PlayerType.SAMSON_B &&
        berserkChargeAfterHit >= constants_1.MAX_TAINTED_SAMSON_BERSERK_CHARGE) {
        return false;
    }
    // If Spirit Shackles is activated, no damage is fatal.
    if (willReviveFromSpiritShackles(player)) {
        return false;
    }
    // If we are Tainted Jacob in the Lost Form, we may have plenty of health left, but we will still
    // die in one hit to anything.
    if (character === isaac_typescript_definitions_1.PlayerType.JACOB_2_B) {
        return true;
    }
    // If we are in the "Lost Curse" form from touching a white fire, all damage will be fatal.
    if ((0, players_1.hasLostCurse)(player)) {
        return true;
    }
    const playerNumAllHearts = (0, players_1.getPlayerNumHitsRemaining)(player);
    if (amount < playerNumAllHearts) {
        return false;
    }
    // This will not be fatal damage if the player has Heartbreak and two slots open for broken
    // hearts.
    if (willReviveFromHeartbreak(player)) {
        return false;
    }
    // This will not be fatal damage if we have Glass Cannon and this is the second time we are taking
    // damage on the same frame.
    if (player.HasCollectible(isaac_typescript_definitions_1.CollectibleType.BROKEN_GLASS_CANNON) &&
        (0, frames_1.onGameFrame)(lastDamageGameFrame)) {
        return false;
    }
    // This will not be fatal damage if we have two different kinds of hearts. For example, a bomb
    // explosion deals 2 damage, but if the player has one half soul heart and one half red heart, the
    // game will only remove the soul heart.
    const hearts = player.GetHearts();
    const eternalHearts = player.GetEternalHearts();
    const soulHearts = player.GetSoulHearts();
    const boneHearts = player.GetBoneHearts();
    if ((hearts > 0 && soulHearts > 0) ||
        (hearts > 0 && boneHearts > 0) ||
        (soulHearts > 0 && boneHearts > 0) ||
        (soulHearts > 0 && eternalHearts > 0) ||
        boneHearts >= 2 // Two bone hearts and nothing else should not result in a death
    ) {
        return false;
    }
    return true;
}
exports.isDamageToPlayerFatal = isDamageToPlayerFatal;
/**
 * Assuming that we are on the frame of fatal damage, this function returns whether Mysterious Paper
 * would rotate to Missing Poster on the frame that the "Game Over" screen would appear (which would
 * subsequently save the player from fatal damage).
 *
 * Mysterious Paper rotates between the 4 items on every frame, in order. The formula for whether
 * Mysterious Paper be Missing Power is: `gameFrameCount % 4 === 3`
 */
function willMysteriousPaperRevive(player) {
    const sprite = player.GetSprite();
    // We want to explicitly check the length of the death animation because we might be playing on a
    // modded character that has a custom death animation.
    const character = player.GetPlayerType();
    const animation = (0, characters_1.getCharacterDeathAnimationName)(character);
    const deathAnimationFrames = (0, sprites_1.getLastFrameOfAnimation)(sprite, animation);
    const frameOfDeath = player.FrameCount + deathAnimationFrames;
    const mysteriousPaperEffect = (0, trinkets_1.getMysteriousPaperEffectForFrame)(player, frameOfDeath);
    if (mysteriousPaperEffect === undefined) {
        return false;
    }
    return mysteriousPaperEffect === MysteriousPaperEffect_1.MysteriousPaperEffect.MISSING_POSTER;
}
exports.willMysteriousPaperRevive = willMysteriousPaperRevive;
/**
 * The `EntityPlayer.WillPlayerRevive` method does not properly account for Mysterious Paper, so use
 * this helper function instead for more robust revival detection.
 */
function willPlayerRevive(player) {
    const trinketSituation = (0, trinketGive_1.temporarilyRemoveTrinket)(player, isaac_typescript_definitions_1.TrinketType.MYSTERIOUS_PAPER);
    const willRevive = player.WillPlayerRevive() ||
        (trinketSituation !== undefined && willMysteriousPaperRevive(player));
    (0, trinketGive_1.giveTrinketsBack)(player, trinketSituation);
    return willRevive;
}
exports.willPlayerRevive = willPlayerRevive;
/**
 * Helper function to determine if the player will be revived by the Heartbreak collectible if they
 * take fatal damage. This is contingent on the character that they are playing as and the amount of
 * broken hearts that they already have.
 */
function willReviveFromHeartbreak(player) {
    if (!player.HasCollectible(isaac_typescript_definitions_1.CollectibleType.HEARTBREAK)) {
        return false;
    }
    const maxHeartContainers = (0, playerHealth_1.getPlayerMaxHeartContainers)(player);
    const numBrokenHeartsThatWillBeAdded = (0, players_1.isKeeper)(player) ? 1 : 2;
    const brokenHearts = player.GetBrokenHearts();
    const numBrokenHeartsAfterRevival = numBrokenHeartsThatWillBeAdded + brokenHearts;
    return maxHeartContainers > numBrokenHeartsAfterRevival;
}
exports.willReviveFromHeartbreak = willReviveFromHeartbreak;
/**
 * Helper function to determine if the Spirit Shackles item is in an enabled state. (It can be
 * either enabled or disabled.)
 */
function willReviveFromSpiritShackles(player) {
    if (!player.HasCollectible(isaac_typescript_definitions_1.CollectibleType.SPIRIT_SHACKLES)) {
        return false;
    }
    const effects = player.GetEffects();
    const spiritShacklesEnabled = !effects.HasNullEffect(isaac_typescript_definitions_1.NullItemID.SPIRIT_SHACKLES_DISABLED);
    const playerInSoulForm = effects.HasNullEffect(isaac_typescript_definitions_1.NullItemID.SPIRIT_SHACKLES_SOUL);
    return spiritShacklesEnabled && !playerInSoulForm;
}
exports.willReviveFromSpiritShackles = willReviveFromSpiritShackles;
