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 sheet
  • false: 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");
    });
};