UNPKG

isaacscript-common

Version:

Helper functions and features for IsaacScript mods.

141 lines (140 loc) 5.57 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.copyRNG = copyRNG; exports.deserializeRNG = deserializeRNG; exports.getRandomSeed = getRandomSeed; exports.isRNG = isRNG; exports.isSerializedRNG = isSerializedRNG; exports.newRNG = newRNG; exports.rngEquals = rngEquals; exports.serializeRNG = serializeRNG; exports.setAllRNGToSeed = setAllRNGToSeed; exports.setAllRNGToStartSeed = setAllRNGToStartSeed; exports.setSeed = setSeed; const cachedClasses_1 = require("../core/cachedClasses"); const SerializationBrand_1 = require("../enums/private/SerializationBrand"); const debugFunctions_1 = require("./debugFunctions"); const isaacAPIClass_1 = require("./isaacAPIClass"); const log_1 = require("./log"); const table_1 = require("./table"); const types_1 = require("./types"); const utils_1 = require("./utils"); /** * This is the ShiftIdx that Blade recommended after having reviewing the game's internal functions. * Any value between 0 and 80 should work equally well. * * @see https://www.jstatsoft.org/article/view/v008i14/xorshift.pdf */ const RECOMMENDED_SHIFT_IDX = 35; const OBJECT_NAME = "RNG"; const KEYS = ["seed"]; /** Helper function to copy an `RNG` Isaac API class. */ function copyRNG(rng) { if (!isRNG(rng)) { error(`Failed to copy a ${OBJECT_NAME} object since the provided object was not a userdata ${OBJECT_NAME} class.`); } const seed = rng.GetSeed(); return newRNG(seed); } /** * Helper function to convert a `SerializedRNG` object to a normal `RNG` object. (This is used by * the save data manager when reading data from the "save#.dat" file.) */ function deserializeRNG(rng) { if (!(0, types_1.isTable)(rng)) { error(`Failed to deserialize a ${OBJECT_NAME} object since the provided object was not a Lua table.`); } const [seed] = (0, table_1.getNumbersFromTable)(rng, OBJECT_NAME, ...KEYS); (0, utils_1.assertDefined)(seed, `Failed to deserialize a ${OBJECT_NAME} object since the provided object did not have a value for: seed`); return newRNG(seed); } /** * Helper function to get a random `Seed` value to be used in spawning entities and so on. Use this * instead of calling the `Random` function directly since that can return a value of 0 and crash * the game. */ function getRandomSeed() { const randomNumber = Random(); // eslint-disable-line @typescript-eslint/no-deprecated const safeRandomNumber = randomNumber === 0 ? 1 : randomNumber; return safeRandomNumber; } /** Helper function to check if something is an instantiated `RNG` object. */ function isRNG(object) { return (0, isaacAPIClass_1.isIsaacAPIClassOfType)(object, OBJECT_NAME); } /** * Used to determine is the given table is a serialized `RNG` object created by the `deepCopy` * function. */ function isSerializedRNG(object) { if (!(0, types_1.isTable)(object)) { return false; } return (0, table_1.tableHasKeys)(object, ...KEYS) && object.has(SerializationBrand_1.SerializationBrand.RNG); } /** * Helper function to initialize a new RNG object using Blade's recommended shift index. * * @param seed Optional. The seed to initialize it with. Default is a random seed. */ function newRNG(seed = getRandomSeed()) { const rng = RNG(); setSeed(rng, seed); return rng; } function rngEquals(rng1, rng2) { return (0, isaacAPIClass_1.isaacAPIClassEquals)(rng1, rng2, KEYS); } /** * Helper function to convert a `RNG` object to a `SerializedRNG` object. (This is used by the save * data manager when writing data from the "save#.dat" file.) */ function serializeRNG(rng) { if (!isRNG(rng)) { error(`Failed to serialize a ${OBJECT_NAME} object since the provided object was not a userdata ${OBJECT_NAME} class.`); } const seed = rng.GetSeed(); const rngTable = new LuaMap(); rngTable.set("seed", seed); rngTable.set(SerializationBrand_1.SerializationBrand.RNG, ""); return rngTable; } /** * Helper function to iterate over the provided object and set the seed for all of the values that * are RNG objects equal to a particular seed. */ function setAllRNGToSeed(object, seed) { if (!(0, types_1.isTable)(object)) { error(`Failed to iterate over the object containing RNG objects since the type of the provided object was: ${typeof object}`); } let setAtLeastOneSeed = false; for (const [_key, value] of object) { if (isRNG(value)) { setSeed(value, seed); setAtLeastOneSeed = true; } } if (!setAtLeastOneSeed) { error(`Failed to set all RNG objects to seed ${seed} because the parent object did not contain any RNG objects.`); } } /** * Helper function to iterate over the provided object and set the seed for all of the values that * are RNG objects equal to the start seed for the current run. */ function setAllRNGToStartSeed(object) { const seeds = cachedClasses_1.game.GetSeeds(); const startSeed = seeds.GetStartSeed(); setAllRNGToSeed(object, startSeed); } /** Helper function to set a seed to an RNG object using Blade's recommended shift index. */ function setSeed(rng, seed) { if (seed === 0) { seed = getRandomSeed(); (0, log_1.logError)("Failed to set a RNG object to a seed of 0. Using a random value instead."); (0, debugFunctions_1.traceback)(); } // The game expects seeds in the range of 1 to 4294967295 (1^32 - 1). // eslint-disable-next-line @typescript-eslint/no-deprecated rng.SetSeed(seed, RECOMMENDED_SHIFT_IDX); }