UNPKG

isaacscript-common

Version:

Helper functions and features for IsaacScript mods.

160 lines (159 loc) • 8.12 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.PreventGridEntityRespawn = void 0; const isaac_typescript_definitions_1 = require("isaac-typescript-definitions"); const cachedClasses_1 = require("../../../core/cachedClasses"); const decorators_1 = require("../../../decorators"); const ISCFeature_1 = require("../../../enums/ISCFeature"); const ModCallbackCustom_1 = require("../../../enums/ModCallbackCustom"); const array_1 = require("../../../functions/array"); const gridEntities_1 = require("../../../functions/gridEntities"); const players_1 = require("../../../functions/players"); const roomData_1 = require("../../../functions/roomData"); const DefaultMap_1 = require("../../DefaultMap"); const Feature_1 = require("../../private/Feature"); const v = { level: { roomListIndexToDecorationGridIndexes: new DefaultMap_1.DefaultMap(() => []), }, room: { manuallyUsingShovel: false, }, }; class PreventGridEntityRespawn extends Feature_1.Feature { /** @internal */ v = v; runInNFrames; /** @internal */ constructor(runInNFrames) { super(); this.featuresUsed = [ISCFeature_1.ISCFeature.RUN_IN_N_FRAMES]; this.callbacksUsed = [ // 23 [ isaac_typescript_definitions_1.ModCallback.PRE_USE_ITEM, this.preUseItemWeNeedToGoDeeper, [isaac_typescript_definitions_1.CollectibleType.WE_NEED_TO_GO_DEEPER], ], ]; this.customCallbacksUsed = [ [ModCallbackCustom_1.ModCallbackCustom.POST_NEW_ROOM_REORDERED, this.postNewRoomReordered], ]; this.runInNFrames = runInNFrames; } // ModCallback.PRE_USE_ITEM (23) // CollectibleType.WE_NEED_TO_GO_DEEPER (84) preUseItemWeNeedToGoDeeper = (_collectibleType, _rng, player, _useFlags, _activeSlot, _customVarData) => { if (v.room.manuallyUsingShovel) { return undefined; } const roomListIndex = (0, roomData_1.getRoomListIndex)(); if (!v.level.roomListIndexToDecorationGridIndexes.has(roomListIndex)) { return undefined; } // Since the room was filled with decorations to prevent any grid entities from respawning, if // the player uses a shovel, it will always reveal a crawl space. In order to restore the normal // shovel functionality, we cancel the shovel use, remove all the decorations, wait a frame, // manually use the shovel again, and then respawn the decorations. (We can't do it all on this // frame because updating the room causes two invocations of the shovel to happen.) const decorations = (0, gridEntities_1.getGridEntities)(isaac_typescript_definitions_1.GridEntityType.DECORATION); for (const decoration of decorations) { (0, gridEntities_1.removeGridEntity)(decoration, false); } const entityPtr = EntityPtr(player); this.runInNFrames.runNextGameFrame(() => { const futurePlayer = (0, players_1.getPlayerFromPtr)(entityPtr); if (futurePlayer === undefined) { return; } const futureRoomListIndex = (0, roomData_1.getRoomListIndex)(); if (futureRoomListIndex !== roomListIndex) { return; } v.room.manuallyUsingShovel = true; futurePlayer.UseActiveItem(isaac_typescript_definitions_1.CollectibleType.WE_NEED_TO_GO_DEEPER); v.room.manuallyUsingShovel = false; const decorationGridIndexes = v.level.roomListIndexToDecorationGridIndexes.getAndSetDefault(roomListIndex); (0, array_1.emptyArray)(decorationGridIndexes); this.preventGridEntityRespawn(); }); // Cancel the original effect. return true; }; // ModCallbackCustom.POST_NEW_ROOM_REORDERED postNewRoomReordered = () => { this.setDecorationsInvisible(); }; /** * Every time we re-enter the room, the sprites for all of the decorations will come back, so we * have to remove them again. */ setDecorationsInvisible() { const room = cachedClasses_1.game.GetRoom(); const roomListIndex = (0, roomData_1.getRoomListIndex)(); const decorationGridIndexes = v.level.roomListIndexToDecorationGridIndexes.get(roomListIndex); if (decorationGridIndexes === undefined) { return; } for (const gridIndex of decorationGridIndexes) { const gridEntity = room.GetGridEntity(gridIndex); if (gridEntity !== undefined) { // Other grid entities may have spawned, like trapdoors or crawl spaces. Thus, only make // decorations invisible. const gridEntityType = gridEntity.GetType(); if (gridEntityType === isaac_typescript_definitions_1.GridEntityType.DECORATION) { (0, gridEntities_1.setGridEntityInvisible)(gridEntity); } } } } /** * Helper function to prevent any removed grid entities from respawning if the player re-enters * the current room. * * This is accomplished by spawning a new grid entity on every tile that does not already have a * grid entity. This will force the game to spawn the new grid entity instead of the old one. The * natural grid entity to choose for this purpose is a decoration, since it is non-interacting. * Then, the decorations are made invisible and any shovel uses are intercepted to avoid creating * a crawl space (instead of a trapdoor). * * Another option besides decorations would be to use a pressure plates with a state of 1, which * is a state that is normally unused by the game and makes it invisible & persistent. However, * pickups will not be able to spawn on pressure plates, which lead to various bugs (e.g. pickups * spawning on top of pits). Thus, using a decoration is preferable. * * Yet another option to accomplish this would be to replace the room data with that of an empty * room. However, the room data must exactly match the room type, the room shape, and the doors, * so this is not possible to do in a robust way without adding empty rooms to the mod's `content` * folder to draw the data from. * * In order to use this function, you must upgrade your mod with * `ISCFeature.PREVENT_GRID_ENTITY_RESPAWN`. */ preventGridEntityRespawn() { const room = cachedClasses_1.game.GetRoom(); const roomListIndex = (0, roomData_1.getRoomListIndex)(); const decorationGridIndexes = v.level.roomListIndexToDecorationGridIndexes.getAndSetDefault(roomListIndex); for (const gridIndex of (0, gridEntities_1.getAllGridIndexes)()) { const existingGridEntity = room.GetGridEntity(gridIndex); if (existingGridEntity !== undefined) { continue; } const decoration = (0, gridEntities_1.spawnGridEntity)(isaac_typescript_definitions_1.GridEntityType.DECORATION, gridIndex); if (decoration !== undefined) { (0, gridEntities_1.setGridEntityInvisible)(decoration); } decorationGridIndexes.push(gridIndex); } } } exports.PreventGridEntityRespawn = PreventGridEntityRespawn; __decorate([ decorators_1.Exported ], PreventGridEntityRespawn.prototype, "preventGridEntityRespawn", null);