UNPKG

isaacscript-common

Version:

Helper functions and features for IsaacScript mods.

169 lines (168 loc) • 7.96 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.PersistentEntities = void 0; const isaac_typescript_definitions_1 = require("isaac-typescript-definitions"); const decorators_1 = require("../../../decorators"); const ISCFeature_1 = require("../../../enums/ISCFeature"); const ModCallbackCustom_1 = require("../../../enums/ModCallbackCustom"); const entities_1 = require("../../../functions/entities"); const roomData_1 = require("../../../functions/roomData"); const Feature_1 = require("../../private/Feature"); const v = { run: { /** Iterates upward as new persistent entities are created. */ persistentEntityIndexCounter: 0, }, level: { /** * Indexed by persistent entity index. * * When the entity is spawned in the currently room, its corresponding entry in this map will be * temporarily deleted (until the entity itself is despawned). */ persistentEntities: new Map(), }, room: { spawnedPersistentEntities: new Map(), }, }; class PersistentEntities extends Feature_1.Feature { /** @internal */ v = v; roomHistory; /** @internal */ constructor(roomHistory) { super(); this.featuresUsed = [ISCFeature_1.ISCFeature.ROOM_HISTORY]; this.callbacksUsed = [ // 67 [isaac_typescript_definitions_1.ModCallback.POST_ENTITY_REMOVE, this.postEntityRemove], ]; this.customCallbacksUsed = [ [ModCallbackCustom_1.ModCallbackCustom.POST_NEW_ROOM_REORDERED, this.postNewRoomReordered], ]; this.roomHistory = roomHistory; } // ModCallback.POST_ENTITY_REMOVE (67) postEntityRemove = (entity) => { const ptrHash = GetPtrHash(entity); const tuple = v.room.spawnedPersistentEntities.get(ptrHash); if (tuple === undefined) { return; } // A persistent entity is being removed, either because it was killed / manually despawned, or // the player left the room. const index = tuple[0]; if (this.roomHistory.isLeavingRoom()) { this.trackDespawningPickupPosition(entity, index); } else { this.removePersistentEntity(index, false); } }; /** * The persistent entity is despawning because the player is in the process of leaving the room. * Keep track of the position for later. */ trackDespawningPickupPosition(entity, index) { // (The "latest" room description is really the previous room, because the `POST_NEW_ROOM` // callback was not fired yet.) const previousRoomDescription = this.roomHistory.getLatestRoomDescription(); if (previousRoomDescription === undefined) { return; } const persistentEntityDescription = { entityType: entity.Type, variant: entity.Variant, subType: entity.SubType, dimension: previousRoomDescription.dimension, roomListIndex: previousRoomDescription.roomListIndex, position: entity.Position, }; v.level.persistentEntities.set(index, persistentEntityDescription); } // ModCallbackCustom.POST_NEW_ROOM_REORDERED postNewRoomReordered = () => { const roomListIndex = (0, roomData_1.getRoomListIndex)(); const persistentEntities = [...v.level.persistentEntities.entries()]; const persistentEntitiesInThisRoom = persistentEntities.filter(([_index, description]) => roomListIndex === description.roomListIndex); for (const [index, description] of persistentEntitiesInThisRoom) { v.level.persistentEntities.delete(index); this.spawnAndTrack(description.entityType, description.variant, description.subType, description.position, index, true); } }; spawnAndTrack(entityType, variant, subType, position, index, respawning = false) { const entity = (0, entities_1.spawn)(entityType, variant, subType, position); if (respawning) { entity.ClearEntityFlags(isaac_typescript_definitions_1.EntityFlag.APPEAR); } const ptrHash = GetPtrHash(entity); // Keep track that we spawned it so that we can respawn it if the player re-enters the room. const tuple = [index, EntityPtr(entity)]; v.room.spawnedPersistentEntities.set(ptrHash, tuple); return entity; } /** * Helper function to stop an entity spawned with the `spawnPersistentEntity` helper function from * respawning. * * In order to use this function, you must upgrade your mod with `ISCFeature.PERSISTENT_ENTITIES`. * * @param persistentEntityIndex The index that was returned by the `spawnPersistentEntity` * function. * @param removeEntity Optional. True by default. Set to false if you want to stop an entity from * being persistent but you don't want to actually remove the * currently-spawned entity from the room. * @public */ removePersistentEntity(persistentEntityIndex, removeEntity = true) { v.level.persistentEntities.delete(persistentEntityIndex); for (const [ptrHash, tuple] of v.room.spawnedPersistentEntities) { const [index, entityPtr] = tuple; if (index !== persistentEntityIndex) { continue; } v.room.spawnedPersistentEntities.delete(ptrHash); if (removeEntity && entityPtr.Ref !== undefined) { entityPtr.Ref.Remove(); } } } /** * Helper function to spawn an entity that will have persistence similar to a pickup. * * By default, as soon as you leave a room, any spawned entities will be despawned and will not * return if the player revisits the room. This means that if you want to have an entity like a * pickup, you have to manually respawn it when the player re-enters the room. Use this helper * function to avoid having to do any tracking on your own. * * Conventionally, the word "persistent" refers to `EntityFlag.FLAG_PERSISTENT`, which is used on * e.g. familiars to make them appear in every room. On the other hand, pickups are also * persistent, but they are not present in every room, only one specific room. This function * spawns entities like pickups, not familiars. * * In order to use this function, you must upgrade your mod with `ISCFeature.PERSISTENT_ENTITIES`. * * @returns An object containing the entity and the persistent entity index. You can use the index * with the `removePersistentEntity` function. * @public */ spawnPersistentEntity(entityType, variant, subType, position) { v.run.persistentEntityIndexCounter++; const entity = this.spawnAndTrack(entityType, variant, subType, position, v.run.persistentEntityIndexCounter); return { entity, persistentIndex: v.run.persistentEntityIndexCounter }; } } exports.PersistentEntities = PersistentEntities; __decorate([ decorators_1.Exported ], PersistentEntities.prototype, "removePersistentEntity", null); __decorate([ decorators_1.Exported ], PersistentEntities.prototype, "spawnPersistentEntity", null);