LREMisc - guiled/LRE GitHub Wiki
Misc
LRE provides many general utility functions that can be useful in your character sheet scripts. This page documents all the available tools.
Sheet Initialization
firstInit
Like the basic init() function, LRE provides a way to run an initialization only once for a sheet. This is useful if you want to prepare some components or set up default values when a sheet is first created.
// At the same level as init
firstInit = function (sheet) {
// Set default values for new characters
sheet.get("characterName").value("New Character");
sheet.get("level").value(1);
// Initialize inventory
sheet.get("inventory").value({});
return true; // true means the firstInit has been done and won't run again
};
Return value:
true: The firstInit has been completed and won't be launched again on this sheetfalse: The firstInit will be launched again on the next table reload
Number and String Utilities
numToAlpha
lre.numToAlpha(n: number): string
Converts a number to a string of letters (A-Z, a-z, AA-ZZ, etc.). Useful for generating IDs or converting numbers to column names.
log(lre.numToAlpha(0)); // Output: "Aa"
log(lre.numToAlpha(25)); // Output: "Az"
log(lre.numToAlpha(26)); // Output: "Ba"
log(lre.numToAlpha(51)); // Output: "Bz"
alphaToNum
lre.alphaToNum(s: string): number
Converts a string of letters back to a number. This is the inverse of numToAlpha().
log(lre.alphaToNum("Aa")); // Output: 0
log(lre.alphaToNum("Az")); // Output: 25
log(lre.alphaToNum("Ba")); // Output: 26
extractNumber
lre.extractNumber(value: string): number | null
Extracts the trailing number from a string. Useful for getting component numbers from IDs.
log(lre.extractNumber("weapon1")); // Output: 1
log(lre.extractNumber("skill_5")); // Output: 5
log(lre.extractNumber("noNumber")); // Output: null
Example:
// Get all weapon components
const weapons = [];
for (let i = 1; i <= 10; i++) {
const weapon = sheet.get("weapon" + i);
if (weapon.exists()) {
weapons.push(weapon);
}
}
getRandomId
lre.getRandomId(length?: number): string
Generates a random alphanumeric ID. If length is provided, generates an ID of that length. Otherwise, generates a very long unique ID.
// Generate a random ID of length 5
const itemId = lre.getRandomId(5);
log(itemId); // Output: something like "Xk3mP"
// Generate a very long unique ID
const uniqueId = lre.getRandomId();
log(uniqueId); // Output: a very long random string
Object Manipulation
deepMerge
lre.deepMerge(target, ...sources): object
Deeply merges multiple objects into the target object. Nested objects are merged recursively.
const baseStats = {
strength: 10,
dexterity: 10,
equipment: {
weapon: "sword",
armor: "leather"
}
};
const bonuses = {
strength: 2,
equipment: {
weapon: "magic sword"
}
};
const merged = lre.deepMerge({}, baseStats, bonuses);
// Result:
// {
// strength: 2, // overwritten
// dexterity: 10, // kept from base
// equipment: {
// weapon: "magic sword", // overwritten
// armor: "leather" // kept from base
// }
// }
deepEqual
lre.deepEqual(x, y): boolean
Compares two values deeply, checking if they are equal (works with objects, arrays, and nested structures).
const obj1 = { a: 1, b: { c: 2 } };
const obj2 = { a: 1, b: { c: 2 } };
const obj3 = { a: 1, b: { c: 3 } };
log(lre.deepEqual(obj1, obj2)); // Output: true
log(lre.deepEqual(obj1, obj3)); // Output: false
structuredClone
structuredClone(value): any
Creates a deep copy of a value. Useful when you need to modify data without affecting the original.
const originalInventory = sheet.get("inventory").value();
const copy = structuredClone(originalInventory);
// Modify the copy
copy.newItem = "sword";
// Original is unchanged
log(originalInventory.newItem); // Output: undefined
stringify
stringify(obj, indent?: boolean): string | undefined
Converts an object to a JSON-like string representation. Useful for debugging.
const character = {
name: "Gandalf",
level: 10,
stats: { str: 12, dex: 14 }
};
log(stringify(character));
// Output: {"name":"Gandalf","level":10,"stats":{"str":12,"dex":14}}
log(stringify(character, true));
// Output: formatted with indentation
Iteration
each
lre.each(value, callback): value
Iterates over a value (array, object, or string) and transforms it using a callback function. Returns a new value of the same type.
// Transform an array
const numbers = [1, 2, 3, 4];
const doubled = lre.each(numbers, function(value) {
return value * 2;
});
// Result: [2, 4, 6, 8]
// Transform an object
const stats = { str: 10, dex: 12, con: 14 };
const modified = lre.each(stats, function(value, key) {
return value + 2;
});
// Result: { str: 12, dex: 14, con: 16 }
Value Transformation
value
lre.value(value): transformedValue
Automatically transforms values based on autoNum and autoTransl settings. Converts string numbers to actual numbers, and translates strings if auto-translation is enabled.
// If autoNum is enabled
const numString = "42";
const num = lre.value(numString); // Returns: 42 (number)
// If autoTransl is enabled
const text = "Hello";
const translated = lre.value(text); // Returns: translated version
// Works with objects and arrays too
const data = {
level: "5",
name: "Character"
};
const transformed = lre.value(data);
// Result: { level: 5, name: "Character" } (if autoNum enabled)
autoNum
lre.autoNum(enabled: boolean = true): void
Enables or disables automatic conversion of string numbers to actual numbers when using lre.value(). See the complete documentation here.
// Enable auto-number conversion
lre.autoNum(true);
// Now string numbers are automatically converted
const level = lre.value("5"); // Returns: 5 (number)
// Disable it
lre.autoNum(false);
const level2 = lre.value("5"); // Returns: "5" (string)
autoTransl
lre.autoTransl(enabled: boolean = true): void
Enables or disables automatic translation of strings when using lre.value(). This uses Let's Role's i18n system. See the complete documentation here.
// Enable auto-translation
lre.autoTransl(true);
// Now strings are automatically translated
const text = lre.value("Hello"); // Returns: translated version
// Disable it
lre.autoTransl(false);
const text2 = lre.value("Hello"); // Returns: "Hello" (untranslated)
Global Utilities
isNaN
isNaN(value): boolean
Checks if a value is "Not a Number". This is a workaround because JavaScript's isNaN() is not available in Let's Role.
log(isNaN("42")); // Output: false (it's a number)
log(isNaN("hello")); // Output: true (it's not a number)
log(isNaN(42)); // Output: false
mt_rand
mt_rand(min: number, max: number): number
Generates a random integer between min and max (inclusive). Inspired by PHP's mt_rand() function.
// Roll a d20
const d20 = mt_rand(1, 20);
log("Rolled: " + d20);
// Random damage between 1 and 6
const damage = mt_rand(1, 6);
// Random ability score (3d6)
const abilityScore = mt_rand(3, 18);
strlen
strlen(string): number
Gets the length of a string. This is a workaround because String.length always returns 0 in Let's Role due to SB-62.
⚠️ Warning: This function is quite slow, so don't use it too frequently.
const name = sheet.get("characterName").value();
const nameLength = strlen(name);
log("Name has " + nameLength + " characters");
Logging and Debugging
setLogLevel
lre.setLogLevel(level: string): void
Sets the logging level for LRE debug messages. Useful for controlling how much debug information is displayed.
// Set to show only errors
lre.setLogLevel("error");
// Set to show all debug information
lre.setLogLevel("debug");
Timing and Delays
wait
lre.wait(delay: number, callback: function, waitName?: string): void
Delays the execution of a function. Useful for ensuring components are ready or for creating animations.
// Wait 100 milliseconds before executing
lre.wait(100, function() {
sheet.get("message").value("Ready!");
}, "Show message");
// Wait 500ms before hiding a notification
lre.wait(500, function() {
sheet.get("notification").hide();
}, "Hide notification");
Note: The waitName parameter is optional and used for debugging purposes.
Type Checking Functions
These functions help you check what type of value you're working with.
isSheet
lre.isSheet(value): boolean
Checks if a value is a sheet object.
function processSheet(sheetOrComponent) {
if (lre.isSheet(sheetOrComponent)) {
log("This is a sheet!");
}
}
isComponent
lre.isComponent(value): boolean
Checks if a value is a component object.
function handleValue(value) {
if (lre.isComponent(value)) {
log("Component value: " + value.value());
} else {
log("Regular value: " + value);
}
}
isObject
lre.isObject(value): boolean
Checks if a value is a plain object (not an array, not null).
const data = sheet.get("characterData").value();
if (lre.isObject(data)) {
log("This is an object with keys: " + Object.keys(data).join(", "));
}
isObjectEmpty
lre.isObjectEmpty(value): boolean
Checks if an object has no properties.
const inventory = sheet.get("inventory").value();
if (lre.isObjectEmpty(inventory)) {
log("Inventory is empty!");
}
isDataProvider
lre.isDataProvider(value): boolean
Checks if a value is a DataProvider.
const data = Tables.get("items");
if (lre.isDataProvider(data)) {
log("This is a DataProvider!");
log("Count: " + data.count());
}
isIndex
lre.isIndex(value): boolean
Checks if a value is a valid index (number or string).
const index = sheet.get("repeaterIndex").value();
if (lre.isIndex(index)) {
log("Valid index: " + index);
}
isIterableByEach
lre.isIterableByEach(value): boolean
Checks if a value can be iterated with the each() function (array, object, or string).
const data = sheet.get("listData").value();
if (lre.isIterableByEach(data)) {
lre.each(data, function(value, key) {
log(key + ": " + value);
});
}
isAvatarValue
lre.isAvatarValue(value): boolean
Checks if a value is an avatar object (contains an "avatar" property).
const avatar = sheet.get("characterAvatar").value();
if (lre.isAvatarValue(avatar)) {
log("Avatar URL: " + avatar.avatar);
}
isRepeaterValue
lre.isRepeaterValue(value): boolean
Checks if a value is a repeater value (object but not an avatar).
const repeaterData = sheet.get("inventoryRepeater").value();
if (lre.isRepeaterValue(repeaterData)) {
log("This is repeater data with " + Object.keys(repeaterData).length + " entries");
}
Complete Example
Here's a complete example showing several utilities in action:
init = function(sheet) {
// Use type checking
const data = sheet.get("characterData").value();
if (lre.isObject(data)) {
// Deep clone to avoid modifying original
const safeCopy = structuredClone(data);
// Transform values
const transformed = lre.each(safeCopy, function(value, key) {
if (lre.isObject(value)) {
return lre.deepMerge({}, value, { modified: true });
}
return value;
});
// Check if empty
if (!lre.isObjectEmpty(transformed)) {
// Generate random ID for new entry
const newId = lre.getRandomId(8);
transformed.id = newId;
// Use deep equality to check if changed
if (!lre.deepEqual(data, transformed)) {
sheet.get("characterData").value(transformed);
}
}
}
// Random number generation
sheet.get("randomRoll").on("click", function() {
const roll = mt_rand(1, 20);
sheet.get("rollResult").value(roll);
});
// Extract number from component ID
sheet.get("weapon1").on("update", function(weapon) {
const weaponNum = lre.extractNumber(weapon.realId());
log("Weapon #" + weaponNum + " updated");
});
// Use wait for delayed actions
sheet.get("showMessage").on("click", function() {
sheet.get("message").show();
lre.wait(3000, function() {
sheet.get("message").hide();
}, "Auto-hide message");
});
};