Face Hair growth

Got any custom JavaScript additions/tweaks you think other people would like to see? Post 'em here!
Post Reply
dragon slayer
UOX3 Guru
Posts: 776
Joined: Thu Dec 21, 2006 7:37 am
Has thanked: 4 times
Been thanked: 26 times

Face Hair growth

Post by dragon slayer »

in your global.js script anywhere in the login function add this
7526=custom/facial_hair_growth.js
TriggerEvent( 7526, "hairlogin", socket );
// ============================================================================
// Facial Hair Growth (Layer 0x10 items) — UOX3 / SpiderMonkey 1.8.5 (ES5)
// - Set growth cadence in MILLISECONDS via GROWTH_INTERVAL_MS
// - Starts a repeating per-character timer once; timer keeps running even if offline
// - Females remain clean-shaven
// - NEW: Beard hue auto-matches head-hair hue (layer 0x0B); falls back to old beard hue
// ============================================================================

/* --------------------------- Config --------------------------- */
// Change this for your cadence (milliseconds):
//var GROWTH_INTERVAL_MS = 9000; // testing
var GROWTH_INTERVAL_MS = 3 * 24 * 60 * 60 * 1000; // production example

var HAIR_LAYER     = 0x0B;   // head hair layer (11)
var FACIAL_LAYER   = 0x10;   // facial hair layer (16)
var TIMER_ID_BEARD = 7526;   // unique timer id for this script/feature
var TAG_TIMER_FLAG = "BeardTimerActive"; // "1" when we've started the loop for this char

// Growth ladder (None -> ... -> Terminal). IDs are beard itemIDs for layer 0x10.
var BEARD_SEQ = [
    0x0000, // None (no item)
    0x2040, // Goatee
    0x2041, // Mustache
    0x204D, // Vandyke
    0x203F, // Short Beard
    0x204B, // Medium Short Beard
    0x203E, // Long Beard
    0x204C  // Medium Long Beard (terminal)
];

// Quick lookup: itemID -> index
var BEARD_TO_IDX = (function () {
    var m = {};
    for (var i = 0; i < BEARD_SEQ.length; i++) m[BEARD_SEQ[i]] = i;
    return m;
})();

/* --------------------------- Helpers --------------------------- */
function getFacialItem(p) { return p.FindItemLayer(FACIAL_LAYER); }
function getHairItem(p)   { return p.FindItemLayer(HAIR_LAYER); }

function readHue(obj)
{
    if (!obj) return 0;
    if (obj.hue !== undefined)    return obj.hue | 0;
    if (obj.colour !== undefined) return obj.colour | 0;
    return 0;
}

function setHue(obj, hue)
{
    if (!obj || !hue) return;
    if (obj.hue !== undefined)    obj.hue = hue;
    else if (obj.colour !== undefined) obj.colour = hue;
}

function getCurrentIndex(p)
{
    var it = getFacialItem(p);
    if (!it) return 0; // none
    var id = it.id | 0;
    return (BEARD_TO_IDX[id] != null) ? BEARD_TO_IDX[id] : 0; // unknown -> treat as none
}

function nextIndex(idx) { return (idx + 1 < BEARD_SEQ.length) ? (idx + 1) : -1; }

/**
 * Equip beard at ladder index, coloring with:
 *   1) current head-hair hue (layer 0x0B) if present,
 *   2) else previous beard hue (if keepPrevBeardHue && beard existed),
 *   3) else default (no hue).
 */

function equipBeardIndex(p, idx, keepPrevBeardHue)
{
    var prevBeard = getFacialItem(p);
    var prevBeardHue = keepPrevBeardHue ? readHue(prevBeard) : 0;

    // Prefer matching head-hair hue, if any
    var hairHue = readHue(getHairItem(p));
    var finalHue = hairHue || prevBeardHue;

    // Remove existing beard
    if (prevBeard) prevBeard.Delete();

    // Index 0 = clean-shaven
    if (idx <= 0) return;

    var beardID = "0x" + (BEARD_SEQ[idx] >>> 0).toString(16);
    var made = CreateDFNItem(p.socket, p, beardID, 1, "ITEM", true);
    if (made)
    {
        made.container = p;
        made.layer = FACIAL_LAYER;
        if (finalHue) setHue(made, finalHue);
    }
}

function applyOneStep(p)
{
    // Females remain clean-shaven
    if (p.gender === 1) {
        var cur = getFacialItem(p);
        if (cur) cur.Delete();
        return false;
    }

    var idx = getCurrentIndex(p);
    var nxt = nextIndex(idx);
    if (nxt < 0) return false; // already at terminal

    // Keep old beard hue as fallback; function will override with hair hue if present
    var keepPrev = (idx > 0);
    equipBeardIndex(p, nxt, keepPrev);
    return (nxt + 1 < BEARD_SEQ.length); // true if more growth later
}

function scheduleNext(p)
{
    if (!ValidateObject(p) || !p.isChar) return;
    // Re-arm fixed delay; UOX3 timers accept milliseconds
    p.StartTimer(Math.max(1, GROWTH_INTERVAL_MS), TIMER_ID_BEARD, true);
}

/* --------------------------- Events --------------------------- */
// Start the loop once per character. Timers continue even if they log out.
function hairlogin(socket)
{
    var p = socket.currentChar;
    if (!ValidateObject(p) || !p.isChar) return;

    // Keep females clean-shaven on login too
    if (p.gender === 1) {
        var cur = getFacialItem(p);
        if (cur) cur.Delete();
    }

    // Only start the repeating timer once
    if (p.GetTag(TAG_TIMER_FLAG) !== "1") {
        p.SetTag(TAG_TIMER_FLAG, "1");
        scheduleNext(p);
    }
}

// Called each interval; apply one growth step, then re-schedule
function onTimer(timerObj, timerID)
{
    if (timerID !== TIMER_ID_BEARD) return false;

    applyOneStep(timerObj);
    scheduleNext(timerObj);
    return true;
}
Post Reply