Color Receiver Script

Written by Kitsune

This script was released courtesy of Xugu Madison

// $Id: color receiver.lsl 2 2009-07-04 16:29:22Z jrn $
// Script for receiving and acting on changes to color sent from the
// matching HUD object.
//
// Configuration is loaded from a notecard called "Configuration" in prim
// inventory which can define the sides/links are to be changed.

string CONFIGURATION_NOTECARD_NAME = "Configuration";

// These are the basic messages that the HUD can send. The strings given below
// all are the start of the message, which should then be followed by the value
// to set.
string MSG_SET_COLOR = "xm:re-color:set:";

// This is the channel the script listens on. You can change it, but it MUST match
// the channel in the HUD script.
integer g_Channel = -40378;

// Holds the listen handle; not currently really useful, more for future expansion.
integer g_ListenHandle;

// Used while loading the configuration notecard
key g_NotecardQuery;
integer g_NotecardLine;

// Links to apply texture/color changes. Uses LINK_THIS if empty.
list g_Links = [];

// Sides to apply texture/color changes to. Uses ALL_SIDES if empty.
list g_Sides = [];

list generateParams(integer param, list values)
{    
    integer sideCount = llGetListLength(g_Sides);
    
    if (sideCount == 0)
    {
        return [param, ALL_SIDES] + values;
    }
    else
    {
        list params = [];
        integer sideIdx;

        for (sideIdx = 0; sideIdx < sideCount; sideIdx++)
        {
            params = params + [param, ALL_SIDES] + values;
        }
        
        return values;
    }
}

list generateColorParams(vector color)
{
    return generateParams(PRIM_COLOR, [color, 1.0]);
}

parseConfigurationLine(string data)
{
    list parts;
    string name;
    string value;
    
    if (data == "" ||
        llGetSubString(data, 0, 0) == "#")
    {
        // Comment, ignore
        return;
    }
    
    parts = llParseString2List(data, ["="], []);
    if (llGetListLength(parts) != 2)
    {
        llOwnerSay("Configuration notecard line \""
            + data + "\" (from \""
            + CONFIGURATION_NOTECARD_NAME + "\") could not be parsed.");
        return;
    }
    
    name = llStringTrim(llToLower(llList2String(parts, 0)), STRING_TRIM);
    value = llStringTrim(llList2String(parts, 1), STRING_TRIM);

    if (name == "links")
    {
        if (value == "all" ||
            value == "linkset" ||
            value == "set")
        {
            g_Links = [LINK_SET];
        }
        else
        {
            g_Links = parseIntegerCommaList(value, "link", llGetNumberOfPrims());
        }
    }
    else if (name == "sides")
    {
        g_Sides = parseIntegerCommaList(value, "side", llGetNumberOfSides());
    }
    else 
    {
        llOwnerSay("Unknown parameter \""
            + name + "\"; valid names are; \"links\" or \"sides\".");
    }    
    
    return;
}

// Parses a comma separated list of integers, such as for sides or links.
// "value" is the value to be parsed, "noun" is the name of the thing being
// parsed (for example "side" or "link"), "maxValid" is the highest valid
// value (for example link or side count).
list parseIntegerCommaList(string value, string noun, integer maxValid)
{
    integer partIdx;
    list parts = llParseString2List(value, [","], [" "]);
    list sides = [];
    
    if (llGetListLength(parts) == 1 &&
        llList2String(parts, 0) == "")
    {
        return [];
    }
    
    for (partIdx = 0; partIdx < llGetListLength(parts); partIdx++)
    {
        // We pull it out of the list as a string because it is a string
        // initially.
        string sideStr = llList2String(parts, partIdx);
        integer side = (integer)sideStr;
        
        if (((string)side) != sideStr)
        {
            llOwnerSay("Unable to parse "
                + noun + " \""
                + sideStr + "\"; expected a positive integer between 0 and "
                + (string)llGetNumberOfSides() + ".");
        }
        else if (side < 0)
        {
            llOwnerSay("Encountered negative "
                + noun + " #"
                + sideStr + "; expected a positive integer between 0 and "
                + (string)maxValid + " inclusive.");
        }
        else if (side > maxValid)
        {
            llOwnerSay("Encountered "
                + noun + " #"
                + sideStr + " over the maximum value of "
                + (string)maxValid + ".");
        }
        else
        {
            sides += side;
        }
    }
    
    return sides;
}

default
{
    changed(integer change)
    {
        if (change & CHANGED_INVENTORY)
        {
            llResetScript();
        }
    }
    
    dataserver(key queryID, string data)
    {
        if (queryID == g_NotecardQuery)
        {
            if (data == EOF)
            {    
                state ready;
            }
            
            parseConfigurationLine(data);
 
            // Request the next line
            g_NotecardQuery = llGetNotecardLine(CONFIGURATION_NOTECARD_NAME, ++g_NotecardLine);
        }
    }

    state_entry()
    {
        if (llGetInventoryType(CONFIGURATION_NOTECARD_NAME) != INVENTORY_NOTECARD)
        {
            state ready;
        }
        
        if (llGetInventoryKey(CONFIGURATION_NOTECARD_NAME) == NULL_KEY)
        {
            llOwnerSay("I cannot retrieve the UUID for the configuration notecard. This can mean either that it's not full perms, or that is empty. I'm going to try reading it anyway, but if you get errors about the notecard not existing it's because SL doesn't differentiate properly between empty and non-existent.");
        }

        g_NotecardLine = 0;
        g_NotecardQuery = llGetNotecardLine(CONFIGURATION_NOTECARD_NAME, g_NotecardLine);
    }
}

state ready
{
    // On inventory change, reset to load any configuration changes
    changed(integer change)
    {
        if (change & CHANGED_INVENTORY)
        {
            llResetScript();
        }
    }
    
    listen(integer channel, string name, key id, string msg)
    {
        list params;
        
        if (channel != g_Channel)
        {
            // Unexpected message, ignore
            return;
        }
        
        if (llGetOwnerKey(id) != llGetOwner())
        {
            // Chatter from an object not owned by our owner, ignore.
            return;
        }
        
        if (llSubStringIndex(msg, MSG_SET_COLOR) != 0)
        {
            return;
        }
        
        string colorStr = llGetSubString(msg, llStringLength(MSG_SET_COLOR), llStringLength(msg));
        vector color = (vector)colorStr;
        integer linkCount = llGetListLength(g_Links);
        integer linkIdx;
            
        if (color.x < 0.0 ||
            color.y < 0.0 ||
            color.z < 0.0 ||
            color.x > 1.0 ||
            color.y > 1.0 ||
            color.z > 1.0)
        {
            llOwnerSay("Invalid color \""
                + colorStr + "\", expected a vector of values between 0.0 and 1.0.");
            return;
        }
            
        params = generateColorParams(color);
        if (linkCount == 0)
        {
            llSetPrimitiveParams(params);
            return;
        }
        
        for (linkIdx = 0; linkIdx < linkCount; linkIdx++)
        {
            llSetLinkPrimitiveParams(llList2Integer(g_Links, linkIdx), params);
        }
    }
    
    state_entry()
    {
        g_ListenHandle = llListen(g_Channel, "", NULL_KEY, "");
    }
    
    state_exit()
    {
        llListenRemove(g_ListenHandle);
    }
}