isaacscript-common
Version:
Helper functions and features for IsaacScript mods.
171 lines (170 loc) • 7.39 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.CustomRevive = void 0;
const isaac_typescript_definitions_1 = require("isaac-typescript-definitions");
const cachedClasses_1 = require("../../../core/cachedClasses");
const ISCFeature_1 = require("../../../enums/ISCFeature");
const ModCallbackCustom_1 = require("../../../enums/ModCallbackCustom");
const external_1 = require("../../../functions/external");
const log_1 = require("../../../functions/log");
const playerIndex_1 = require("../../../functions/playerIndex");
const players_1 = require("../../../functions/players");
const Feature_1 = require("../../private/Feature");
const DEBUG = false;
var CustomReviveState;
(function (CustomReviveState) {
CustomReviveState[CustomReviveState["DISABLED"] = 0] = "DISABLED";
/**
* We can't immediately jump to waiting for an item animation because it is possible for a player
* to be holding an item above their head as they are dying (e.g. with Razor Blade).
*/
CustomReviveState[CustomReviveState["WAITING_FOR_ROOM_TRANSITION"] = 1] = "WAITING_FOR_ROOM_TRANSITION";
CustomReviveState[CustomReviveState["WAITING_FOR_ITEM_ANIMATION"] = 2] = "WAITING_FOR_ITEM_ANIMATION";
})(CustomReviveState || (CustomReviveState = {}));
const v = {
run: {
state: CustomReviveState.DISABLED,
revivalType: null,
dyingPlayerIndex: null,
},
};
class CustomRevive extends Feature_1.Feature {
v = v;
preCustomRevive;
postCustomRevive;
runInNFrames;
constructor(preCustomRevive, postCustomRevive, runInNFrames) {
super();
this.featuresUsed = [ISCFeature_1.ISCFeature.RUN_IN_N_FRAMES];
this.callbacksUsed = [
// 2
[isaac_typescript_definitions_1.ModCallback.POST_RENDER, this.postRender],
// 7
[
isaac_typescript_definitions_1.ModCallback.POST_FAMILIAR_INIT,
this.postFamiliarInitOneUp,
[isaac_typescript_definitions_1.FamiliarVariant.ONE_UP],
],
];
this.customCallbacksUsed = [
[ModCallbackCustom_1.ModCallbackCustom.POST_NEW_ROOM_REORDERED, this.postNewRoomReordered],
[
ModCallbackCustom_1.ModCallbackCustom.POST_PEFFECT_UPDATE_REORDERED,
this.postPEffectUpdateReordered,
],
[ModCallbackCustom_1.ModCallbackCustom.POST_PLAYER_FATAL_DAMAGE, this.postPlayerFatalDamage],
[ModCallbackCustom_1.ModCallbackCustom.PRE_BERSERK_DEATH, this.preBerserkDeath],
];
this.preCustomRevive = preCustomRevive;
this.postCustomRevive = postCustomRevive;
this.runInNFrames = runInNFrames;
}
// ModCallback.POST_RENDER (2)
postRender = () => {
if (v.run.state !== CustomReviveState.WAITING_FOR_ITEM_ANIMATION) {
return;
}
// The 1-up sound will fire before the item holding animation begins, so we mute it on every
// render frame.
cachedClasses_1.sfxManager.Stop(isaac_typescript_definitions_1.SoundEffect.ONE_UP);
};
// ModCallback.POST_FAMILIAR_INIT (7)
// FamiliarVariant.ONE_UP (41)
postFamiliarInitOneUp = (familiar) => {
if (v.run.state !== CustomReviveState.WAITING_FOR_ROOM_TRANSITION) {
return;
}
familiar.Remove();
};
// ModCallbackCustom.POST_NEW_ROOM_REORDERED
postNewRoomReordered = () => {
if (v.run.state !== CustomReviveState.WAITING_FOR_ROOM_TRANSITION) {
return;
}
v.run.state = CustomReviveState.WAITING_FOR_ITEM_ANIMATION;
this.logStateChanged();
};
// ModCallbackCustom.POST_PEFFECT_UPDATE_REORDERED
postPEffectUpdateReordered = (player) => {
this.checkWaitingForItemAnimation(player);
};
checkWaitingForItemAnimation(player) {
if (v.run.state !== CustomReviveState.WAITING_FOR_ITEM_ANIMATION) {
return;
}
if (v.run.dyingPlayerIndex === null) {
return;
}
const playerIndex = (0, playerIndex_1.getPlayerIndex)(player);
if (playerIndex !== v.run.dyingPlayerIndex) {
return;
}
let playerToCheckHoldingItem = player;
if ((0, players_1.isCharacter)(player, isaac_typescript_definitions_1.PlayerType.SOUL_B)) {
const forgottenBody = player.GetOtherTwin();
if (forgottenBody !== undefined) {
playerToCheckHoldingItem = forgottenBody;
}
}
if (!playerToCheckHoldingItem.IsHoldingItem()) {
return;
}
// The player is now playing the animation where they hold the 1-Up item overhead. The
// `EntityPlayer.StopExtraAnimation` method will not work to stop this animation. End-users are
// expected to play a new animation in the PostCustomRevive callback, which will overwrite the
// 1-Up animation.
if (v.run.revivalType !== null) {
this.postCustomRevive.fire(playerToCheckHoldingItem, v.run.revivalType);
}
v.run.state = CustomReviveState.DISABLED;
v.run.revivalType = null;
v.run.dyingPlayerIndex = null;
this.logStateChanged();
}
// ModCallbackCustom.POST_PLAYER_FATAL_DAMAGE
postPlayerFatalDamage = (player) => {
this.playerIsAboutToDie(player);
return undefined;
};
// ModCallbackCustom.PRE_BERSERK_DEATH
preBerserkDeath = (player) => {
this.playerIsAboutToDie(player);
};
/**
* The player is about to die, which will immediately delete the save data for the run. To prevent
* this from happening, we grant the 1-Up item.
*/
playerIsAboutToDie(player) {
const revivalType = this.preCustomRevive.fire(player);
if (revivalType === undefined) {
return;
}
v.run.state = CustomReviveState.WAITING_FOR_ROOM_TRANSITION;
v.run.revivalType = revivalType;
v.run.dyingPlayerIndex = (0, playerIndex_1.getPlayerIndex)(player);
this.logStateChanged();
player.AddCollectible(isaac_typescript_definitions_1.CollectibleType.ONE_UP, 0, false);
(0, external_1.rebirthItemTrackerRemoveCollectible)(isaac_typescript_definitions_1.CollectibleType.ONE_UP);
// The player should always be dead one frame from now. If they are not, then something has gone
// wrong, probably with the `isDamageToPlayerFatal` function. Since end-user code is already
// assuming that a custom revive is occurring, explicitly kill the player.
const playerIndex = (0, playerIndex_1.getPlayerIndex)(player);
this.runInNFrames.runNextGameFrame(() => {
const futurePlayer = (0, playerIndex_1.getPlayerFromIndex)(playerIndex);
if (futurePlayer === undefined) {
return;
}
if (futurePlayer.IsDead()) {
return;
}
(0, log_1.logError)("The player is still alive after initializing a custom revive. Explicitly killing the player.");
futurePlayer.Kill();
});
}
logStateChanged() {
if (DEBUG) {
(0, log_1.log)(`Custom revive state changed: ${CustomReviveState[v.run.state]} (${v.run.state})`);
}
}
}
exports.CustomRevive = CustomRevive;