isaacscript-common
Version:
Helper functions and features for IsaacScript mods.
184 lines (183 loc) • 8.8 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.getJSONRoomDoorSlotFlags = getJSONRoomDoorSlotFlags;
exports.getJSONRoomOfVariant = getJSONRoomOfVariant;
exports.getJSONRoomsOfSubType = getJSONRoomsOfSubType;
exports.getRandomJSONEntity = getRandomJSONEntity;
exports.getRandomJSONRoom = getRandomJSONRoom;
const isaac_typescript_definitions_1 = require("isaac-typescript-definitions");
const array_1 = require("./array");
const doors_1 = require("./doors");
const enums_1 = require("./enums");
const flag_1 = require("./flag");
const log_1 = require("./log");
const random_1 = require("./random");
const types_1 = require("./types");
const utils_1 = require("./utils");
/**
* Helper function to calculate what the resulting `BitFlags<DoorSlotFlag>` value would be for a
* given JSON room.
*
* (A JSON room is an XML file converted to JSON so that it can be directly imported into your mod.)
*/
function getJSONRoomDoorSlotFlags(jsonRoom) {
const roomShapeString = jsonRoom.$.shape;
const roomShape = (0, types_1.parseIntSafe)(roomShapeString);
(0, utils_1.assertDefined)(roomShape, `Failed to parse the "shape" field of a JSON room: ${roomShapeString}`);
if (!(0, enums_1.isEnumValue)(roomShape, isaac_typescript_definitions_1.RoomShape)) {
error(`Failed to parse the "shape" field of a JSON room since it was an invalid number: ${roomShape}`);
}
let doorSlotFlags = isaac_typescript_definitions_1.DoorSlotFlagZero;
for (const door of jsonRoom.door) {
const existsString = door.$.exists;
if (existsString !== "True" && existsString !== "False") {
error(`Failed to parse the "exists" field of a JSON room door: ${existsString}`);
}
if (existsString === "False") {
continue;
}
const xString = door.$.x;
const x = (0, types_1.parseIntSafe)(xString);
(0, utils_1.assertDefined)(x, `Failed to parse the "x" field of a JSON room door: ${xString}`);
const yString = door.$.y;
const y = (0, types_1.parseIntSafe)(yString);
(0, utils_1.assertDefined)(y, `Failed to parse the "y" field of a JSON room door: ${yString}`);
const doorSlot = (0, doors_1.getRoomShapeDoorSlot)(roomShape, x, y);
(0, utils_1.assertDefined)(doorSlot, `Failed to retrieve the door slot for a JSON room door at coordinates: [${x}, ${y}]`);
const doorSlotFlag = (0, doors_1.doorSlotToDoorSlotFlag)(doorSlot);
doorSlotFlags = (0, flag_1.addFlag)(doorSlotFlags, doorSlotFlag);
}
return doorSlotFlags;
}
/**
* Helper function to find a specific room from an array of JSON rooms.
*
* (A JSON room is an XML file converted to JSON so that it can be directly imported into your mod.)
*
* @param jsonRooms The array of rooms to search through.
* @param variant The room variant to select. (The room variant can be thought of as the ID of the
* room.)
*/
function getJSONRoomOfVariant(jsonRooms, variant) {
const jsonRoomsOfVariant = jsonRooms.filter((jsonRoom) => {
const roomVariantString = jsonRoom.$.variant;
const roomVariant = (0, types_1.parseIntSafe)(roomVariantString);
if (roomVariant === undefined) {
error(`Failed to convert a JSON room variant to an integer: ${roomVariantString}`);
}
return roomVariant === variant;
});
// The room variant acts as an ID for the room. We assume that there should only be a single room
// per variant.
if (jsonRoomsOfVariant.length === 0) {
return undefined;
}
if (jsonRoomsOfVariant.length === 1) {
return jsonRoomsOfVariant[0];
}
error(`Found ${jsonRoomsOfVariant.length} JSON rooms with a variant of ${variant}, when there should only be 1.`);
}
/**
* Helper function to find all of the JSON rooms that match the sub-type provided.
*
* (A JSON room is an XML file converted to JSON so that it can be directly imported into your mod.)
*
* @param jsonRooms The array of rooms to search through.
* @param subType The sub-type to match.
*/
function getJSONRoomsOfSubType(jsonRooms, subType) {
return jsonRooms.filter((jsonRoom) => {
const roomSubTypeString = jsonRoom.$.subtype;
const roomSubType = (0, types_1.parseIntSafe)(roomSubTypeString);
if (roomSubType === undefined) {
error(`Failed to convert a JSON room sub-type to an integer: ${roomSubTypeString}`);
}
return roomSubType === subType;
});
}
/**
* Helper function to get a random JSON entity from an array of JSON entities.
*
* (A JSON entity is an entity inside of a JSON room. A JSON room is an XML file converted to JSON
* so that it can be directly imported into your mod.)
*
* Note that this function does not simply choose a random element in the provided array; it will
* properly account for each room weight using the algorithm from:
* https://stackoverflow.com/questions/1761626/weighted-random-numbers
*
* If you want an unseeded entity, you must explicitly pass `undefined` to the `seedOrRNG`
* parameter.
*
* @param jsonEntities The array of entities to randomly choose between.
* @param seedOrRNG The `Seed` or `RNG` object to use. If an `RNG` object is provided, the
* `RNG.Next` method will be called. If `undefined` is provided, it will default to
* a random seed.
* @param verbose Optional. If specified, will write entries to the "log.txt" file that describe
* what the function is doing. Default is false.
*/
function getRandomJSONEntity(jsonEntities, seedOrRNG, verbose = false) {
const totalWeight = getTotalWeightOfJSONObject(jsonEntities);
if (verbose) {
(0, log_1.log)(`Total weight of the JSON entities provided: ${totalWeight}`);
}
const chosenWeight = (0, random_1.getRandomFloat)(0, totalWeight, seedOrRNG);
if (verbose) {
(0, log_1.log)(`Randomly chose weight for JSON entity: ${chosenWeight}`);
}
const randomJSONEntity = getJSONObjectWithChosenWeight(jsonEntities, chosenWeight);
(0, utils_1.assertDefined)(randomJSONEntity, `Failed to get a JSON entity with chosen weight: ${chosenWeight}`);
return randomJSONEntity;
}
/**
* Helper function to get a random JSON room from an array of JSON rooms.
*
* (A JSON room is an XML file converted to JSON so that it can be directly imported into your mod.)
*
* Note that this function does not simply choose a random element in the provided array; it will
* properly account for each room weight using the algorithm from:
* https://stackoverflow.com/questions/1761626/weighted-random-numbers
*
* If you want an unseeded room, you must explicitly pass `undefined` to the `seedOrRNG` parameter.
*
* @param jsonRooms The array of rooms to randomly choose between.
* @param seedOrRNG The `Seed` or `RNG` object to use. If an `RNG` object is provided, the
* `RNG.Next` method will be called. If `undefined` is provided, it will default to
* a random seed.
* @param verbose Optional. If specified, will write entries to the "log.txt" file that describe
* what the function is doing. Default is false.
*/
function getRandomJSONRoom(jsonRooms, seedOrRNG, verbose = false) {
const totalWeight = getTotalWeightOfJSONObject(jsonRooms);
if (verbose) {
(0, log_1.log)(`Total weight of the JSON rooms provided: ${totalWeight}`);
}
const chosenWeight = (0, random_1.getRandomFloat)(0, totalWeight, seedOrRNG);
if (verbose) {
(0, log_1.log)(`Randomly chose weight for JSON room: ${chosenWeight}`);
}
const randomJSONRoom = getJSONObjectWithChosenWeight(jsonRooms, chosenWeight);
(0, utils_1.assertDefined)(randomJSONRoom, `Failed to get a JSON room with chosen weight: ${chosenWeight}`);
return randomJSONRoom;
}
function getTotalWeightOfJSONObject(jsonOjectArray) {
const weights = jsonOjectArray.map((jsonObject) => {
const weightString = jsonObject.$.weight;
const weight = tonumber(weightString);
(0, utils_1.assertDefined)(weight, `Failed to parse the weight of a JSON object: ${weightString}.`);
return weight;
});
return (0, array_1.sumArray)(weights);
}
function getJSONObjectWithChosenWeight(jsonOjectArray, chosenWeight) {
let weightAccumulator = 0;
for (const jsonObject of jsonOjectArray) {
const weightString = jsonObject.$.weight;
const weight = tonumber(weightString);
(0, utils_1.assertDefined)(weight, `Failed to parse the weight of a JSON object: ${weightString}`);
weightAccumulator += weight;
if (weightAccumulator >= chosenWeight) {
return jsonObject;
}
}
return undefined;
}