isaacscript-common
Version:
Helper functions and features for IsaacScript mods.
141 lines (140 loc) • 5.57 kB
JavaScript
;
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);
}