isaacscript-common
Version:
Helper functions and features for IsaacScript mods.
208 lines (207 loc) • 9.75 kB
JavaScript
;
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Pause = void 0;
const isaac_typescript_definitions_1 = require("isaac-typescript-definitions");
const cachedClasses_1 = require("../../../core/cachedClasses");
const constants_1 = require("../../../core/constants");
const decorators_1 = require("../../../decorators");
const entitiesSpecific_1 = require("../../../functions/entitiesSpecific");
const isaacAPIClass_1 = require("../../../functions/isaacAPIClass");
const log_1 = require("../../../functions/log");
const playerCollectibles_1 = require("../../../functions/playerCollectibles");
const playerIndex_1 = require("../../../functions/playerIndex");
const tstlClass_1 = require("../../../functions/tstlClass");
const utils_1 = require("../../../functions/utils");
const ReadonlySet_1 = require("../../../types/ReadonlySet");
const Feature_1 = require("../../private/Feature");
const v = {
run: {
isPseudoPaused: false,
shouldUnpause: false,
initialDescriptions: new Map(),
},
};
class Pause extends Feature_1.Feature {
/** @internal */
v = v;
disableInputs;
/** @internal */
constructor(disableInputs) {
super();
this.callbacksUsed = [
// 1
[isaac_typescript_definitions_1.ModCallback.POST_UPDATE, this.postUpdate],
// 13
[
isaac_typescript_definitions_1.ModCallback.INPUT_ACTION,
this.inputActionGetActionValue,
[isaac_typescript_definitions_1.InputHook.GET_ACTION_VALUE],
],
];
this.disableInputs = disableInputs;
}
// ModCallback.POST_UPDATE (1)
postUpdate = () => {
if (!v.run.isPseudoPaused) {
return;
}
const firstPlayer = Isaac.GetPlayer();
(0, playerCollectibles_1.useActiveItemTemp)(firstPlayer, isaac_typescript_definitions_1.CollectibleType.PAUSE);
if ((0, utils_1.isRepentancePlus)()) {
cachedClasses_1.sfxManager.Stop(isaac_typescript_definitions_1.SoundEffect.PAUSE_FREEZE);
}
this.stopTearsAndProjectilesFromMoving();
};
stopTearsAndProjectilesFromMoving() {
const tearsAndProjectiles = [...(0, entitiesSpecific_1.getTears)(), ...(0, entitiesSpecific_1.getProjectiles)()];
for (const tearOrProjectile of tearsAndProjectiles) {
const ptrHash = GetPtrHash(tearOrProjectile);
const initialDescription = v.run.initialDescriptions.get(ptrHash);
if (initialDescription === undefined) {
continue;
}
tearOrProjectile.Position = initialDescription.position;
tearOrProjectile.PositionOffset = initialDescription.positionOffset;
tearOrProjectile.Velocity = constants_1.VectorZero;
tearOrProjectile.Height = initialDescription.height;
tearOrProjectile.FallingSpeed = 0;
if ((0, isaacAPIClass_1.isTear)(tearOrProjectile)) {
tearOrProjectile.FallingAcceleration =
initialDescription.fallingAcceleration;
}
else {
tearOrProjectile.FallingAccel = initialDescription.fallingAcceleration;
}
}
}
// ModCallback.INPUT_ACTION (13)
// InputHook.GET_ACTION_VALUE (2)
inputActionGetActionValue = (_entity, _inputHook, buttonAction) => {
if (buttonAction !== isaac_typescript_definitions_1.ButtonAction.SHOOT_RIGHT) {
return undefined;
}
if (!v.run.shouldUnpause) {
return undefined;
}
v.run.shouldUnpause = false;
// Returning a value of 1 for a single sub-frame will be enough for the game to register an
// unpause but not enough for a tear to actually be fired.
return 1;
};
/**
* Helper function to check if the pause feature from `isaacscript-common` is currently
* pseudo-pausing the game.
*
* @public
*/
isPaused() {
return v.run.isPseudoPaused;
}
/**
* Helper function to emulate what happens when the player pauses the game. Use the `unpause`
* function to return things back to normal.
*
* Under the hood, this function:
* - uses the Pause collectible on every game frame
* - disables any player inputs (except for `ButtonAction.MENU_CONFIRM` and
* `ButtonAction.CONSOLE`)
*
* In order to use this function, you must upgrade your mod with `ISCFeature.PAUSE`.
*
* @public
*/
pause() {
if (v.run.isPseudoPaused) {
(0, log_1.logError)("Failed to pseudo-pause the game, since it was already pseudo-paused.");
return;
}
v.run.isPseudoPaused = true;
// Tears/projectiles in the room will move slightly on every frame, even when the Pause
// collectible is active. Thus, we manually reset the initial positions and heights on every
// frame.
v.run.initialDescriptions.clear();
const tearsAndProjectiles = [...(0, entitiesSpecific_1.getTears)(), ...(0, entitiesSpecific_1.getProjectiles)()];
for (const tearOrProjectile of tearsAndProjectiles) {
const ptrHash = GetPtrHash(tearOrProjectile);
const initialDescription = {
position: tearOrProjectile.Position,
positionOffset: tearOrProjectile.PositionOffset,
velocity: tearOrProjectile.Velocity,
height: tearOrProjectile.Height,
fallingSpeed: tearOrProjectile.FallingSpeed,
fallingAcceleration: (0, isaacAPIClass_1.isTear)(tearOrProjectile)
? tearOrProjectile.FallingAcceleration
: tearOrProjectile.FallingAccel,
};
v.run.initialDescriptions.set(ptrHash, initialDescription);
}
const firstPlayer = Isaac.GetPlayer();
(0, playerCollectibles_1.useActiveItemTemp)(firstPlayer, isaac_typescript_definitions_1.CollectibleType.PAUSE);
if ((0, utils_1.isRepentancePlus)()) {
cachedClasses_1.sfxManager.Stop(isaac_typescript_definitions_1.SoundEffect.PAUSE_FREEZE);
}
const tstlClassName = (0, tstlClass_1.getTSTLClassName)(this);
(0, utils_1.assertDefined)(tstlClassName, "Failed to get the class name for the pause feature.");
const buttonActionConsole = (0, utils_1.isRepentancePlus)()
? isaac_typescript_definitions_1.ButtonAction.CONSOLE_REPENTANCE_PLUS
: isaac_typescript_definitions_1.ButtonAction.CONSOLE_REPENTANCE;
const whitelist = new ReadonlySet_1.ReadonlySet([
isaac_typescript_definitions_1.ButtonAction.MENU_CONFIRM,
buttonActionConsole,
]);
this.disableInputs.disableAllInputsExceptFor(tstlClassName, whitelist);
for (const player of (0, playerIndex_1.getAllPlayers)()) {
// Disable the controls to prevent the players from moving, shooting, and so on. (We also
// disable the inputs in the `INPUT_ACTION` callback, but that does not prevent mouse inputs.)
player.ControlsEnabled = false;
// Prevent the players from leaving the room. (If we don't reset the velocity, they can
// continue to move towards a door.)
player.Velocity = constants_1.VectorZero;
}
this.stopTearsAndProjectilesFromMoving();
}
/**
* Helper function to put things back to normal after the `pause` function was used.
*
* In order to use this function, you must upgrade your mod with `ISCFeature.PAUSE`.
*
* @public
*/
unpause() {
if (!v.run.isPseudoPaused) {
(0, log_1.logError)("Failed to pseudo-unpause the game, since it was not already pseudo-paused.");
return;
}
v.run.isPseudoPaused = false;
v.run.shouldUnpause = true;
const tstlClassName = (0, tstlClass_1.getTSTLClassName)(this);
(0, utils_1.assertDefined)(tstlClassName, "Failed to find get the class name for the pause feature.");
this.disableInputs.enableAllInputs(tstlClassName);
for (const player of (0, playerIndex_1.getAllPlayers)()) {
player.ControlsEnabled = true;
}
// After a vanilla pause, the tears will not resume their normal velocity and will "stick" to
// the air. Even if we try to help the tears along by explicitly resetting all of the
// velocity-related variables to their initial values, this will not make a difference. Thus,
// revert to removing all of the tears and projectiles in the room. (If a Ludovico tear is
// removed, it will automatically be respawned on the next frame.)
(0, entitiesSpecific_1.removeAllTears)();
(0, entitiesSpecific_1.removeAllProjectiles)();
}
}
exports.Pause = Pause;
__decorate([
decorators_1.Exported
], Pause.prototype, "isPaused", null);
__decorate([
decorators_1.Exported
], Pause.prototype, "pause", null);
__decorate([
decorators_1.Exported
], Pause.prototype, "unpause", null);