UNPKG

isaacscript-common

Version:

Helper functions and features for IsaacScript mods.

208 lines (207 loc) • 9.75 kB
"use strict"; 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);