isaacscript-common
Version:
Helper functions and features for IsaacScript mods.
151 lines (150 loc) • 7.46 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.isDamageToPlayerFatal = isDamageToPlayerFatal;
exports.willMysteriousPaperRevive = willMysteriousPaperRevive;
exports.willPlayerRevive = willPlayerRevive;
exports.willReviveFromHeartbreak = willReviveFromHeartbreak;
exports.willReviveFromSpiritShackles = willReviveFromSpiritShackles;
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;
}
/**
* 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;
}
/**
* 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;
}
/**
* 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;
}
/**
* 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;
}