Breakdown of HUD script for Secondlife

This is a breakdown of a scripted HUD I created for Secondlife, and the explanation of how its parts work that can be used for the creation of future HUDs.

I am not a professional coder, I learned lsl scripting in June 2023 (with the help of chatGPT that helped explaining some core concepts), but I wanted to create a HUD that works with particle effects during dance choreographies.

If you find this information useful, please make sure to buy the HUD on mp as a token of your appreciation:

If a sound is triggered using llPlaySound(), only the HUD wearer will hear it. If the land parcel they’re over restricts sound entry/exit, the sound will not play.

  • If a sound is triggered using llTriggerSound(), the wearer and any bystanders will hear it.

HUD features:

HUD texture (512×512)

HUD in build mode in SL

HUD's structure explanation

The HUD consist of a central prim and many linked prims that function as buttons and also as the container of floating displayed text.

There is one script within each linked button that is triggered when a button is touched:

 

Button script

When each button is touched,

  • the linkNumber (identification number of the linked Prim based on the order of linking when the objects are linked.)
  • and the buttonName (linkName)
  • are both sent in a Message to the root prim.

All other functions are arranged by the script in the root prim.

				
					default
{
    touch_start(integer total_number)
    {
        integer linkNumber = llGetLinkNumber();
        string buttonName = llGetLinkName(LINK_THIS);
        llMessageLinked(LINK_ROOT, linkNumber, buttonName, ""); 
    }
}

				
			

Naming system for buttons:

Color button names:
  • Start_Color
  • End_Color
  • End_Color_Sample
  • Start_Color_Sample
Arrow and Default button names:

Particle parameters types are BurstCount, BurstRate, Glow, Alpha and Acceleration

  • “ParticleParameterType”_Default
  • “ParticleParameterType”_Increase
  • “ParticleParameterType”_Decrease
  • “ParticleParameterType”_Activate
Timer and Particle Controller Buttons:
  • Timer_Controller_START
  • Timer_Controller_STOP
  • TimeAndParticles_Controller_START
  • TimeAndParticles_Controller_STOP
  • Particles_Controller_START
  • Particles_Controller_STOP

HUD root script

Global Variables

activeMessage is a string that is parsed each time a button is pressed. Each time a button is pressed, its name is sent to the listener: the main HUD, its channel number is an integer called listen_Channel.

The HUD uses a timer, and we have an integer that stores it state: is_Timer_ON.

 

For maximizing and miimizing the HUD, we are storing some further variables rleated to rotation: maxRot, minRot and currentRot.

				
					string activeMessage;
integer listen_Channel = 689689;
integer is_Timer_ON;

//HUD rotation variables
rotation maxRot =  <0.00000, 0.00000, 0.00000, 1.00000>; // HUD Maximized 
rotation minRot =  <0.00000, -0.70711, 0.00000, 0.70711>; // HUD Minimized
rotation currentRot = maxRot;
				
			
				
					//Creates a visual effect where the specified link (button or object) briefly appears fully opaque for 0.2 seconds and then becomes fully transparent again. 
brief_HoverEffect_Alpha (integer senderNUMBER)
{
    //set of button 100% for 0.2 seconds,  face 4
    llSetLinkAlpha(senderNUMBER, 1.0, 4);
    llSleep(0.2);
    llSetLinkAlpha(senderNUMBER, 0.0, 4);
}    

//Creates a visual effect where the specified link (button or object) briefly changes color for 0.2 seconds and then resets 
brief_HoverEffect_Color (integer senderNUMBER)
{
    //set of button 100% for 0.2 seconds,  face 4
    llSetLinkColor(senderNUMBER, <1.0, 1.0, 1.0>, ALL_SIDES);
    llSleep(0.1);
    llSetLinkColor(senderNUMBER, <0.86, 0.68, 0.36>, ALL_SIDES);
}    
				
			

Maybe these two functions could be combined, I will need to look itno this:

				
					clearHoverText()
{
    // Get all linked prims
    integer linkCount = llGetNumberOfPrims();
    
    // Iterate through each prim
    integer linkNum;
    for (linkNum = 1; linkNum <= linkCount; linkNum++)
    {
        // Get the prim name
        string primName = llGetLinkName(linkNum);
        
        // Check if the prim is a Display prim
        if (llSubStringIndex(primName, "Display") != -1)
        {
            // Clear the prim's hover text
            llSetLinkPrimitiveParamsFast(linkNum, [  PRIM_TEXT, "",ZERO_VECTOR, 0]);
        }
    }
}

populateHoverText()
{
    // Get all linked prims
    integer linkCount = llGetNumberOfPrims();
    
    // Iterate through each prim
    integer linkNum;
    for (linkNum = 1; linkNum <= linkCount; linkNum++)
    {
        // Get the prim name
        string primName = llGetLinkName(linkNum);
        
        // Check if the prim is a Display prim
        if (llSubStringIndex(primName, "Display") != -1)
        {
            // Get the prim's description
            list primParamList =  llGetLinkPrimitiveParams(linkNum, [ PRIM_DESC, PRIM_COLOR, ALL_SIDES ]);
            string primDescription = llList2String(primParamList, 0); 
            vector tempColor = llList2Vector(primParamList,1);    
    
            // Set the prim's hover text to the description text with gained color
            llSetLinkPrimitiveParams(linkNum, [ PRIM_TEXT, primDescription,  tempColor, 1.0 ]);
        }
    }
}
				
			

This is needed to identify which button is pressed on the HUD. You must name the buttons properly and build the relevant functions afterward.

				
					
//Allows you to determine if a given string starts with a specific pattern. It returns TRUE (1) if the string starts with the pattern and FALSE (0) if it does not.
//example: check if prim name starts with string pattern ("Acceleration")
integer CheckPrimName(string stringtoTest , string pattern)
{
    integer result;

    // Get the substring from the start of the string with the same length as the pattern
    string substring = llGetSubString(stringtoTest, 0, llStringLength(pattern) - 1);

    if (substring == pattern) 
    {
        // The string starts with "Acceleration"
        result = TRUE; 
    }
    else
    {
    // The string does not start with "Acceleration"
        result = FALSE;
    }    
    return result;
    
}
				
			

This is needed to be able to display the vectors on the HUD as text.

				
					//Takes a string representation of a vector, parses it into a list, and extracts the desired element (x, y, or z) based on the listNumber parameter. If the parsed list does not have three elements, an error message is sent to the owner, and the function returns 0.0.
//vector component = listNumber: x = 0, y=1, z=2
float ParseStringVector2String(string StringVector2Parse, integer listNumber)
{
     // Remove the angle brackets ("<" and ">") from the vector string
    string cleanedString = llDeleteSubString(StringVector2Parse, 0,0);
    cleanedString = llDeleteSubString(cleanedString, -1,-1);    
    
     // Parse the cleaned string into a list using comma (",") as the delimiter
    list vectorList = llParseString2List(cleanedString, [","], []);
    
    // Check if the list has three elements (x, y, z)
    if (llGetListLength(vectorList) == 3)
    {
        // Extract the three Float values from the list
        float FloatResult = llList2Float(vectorList, listNumber);    
        return FloatResult;
    }
    else
    {
        llOwnerSay("Invalid vector string format: " + StringVector2Parse);
        return 0.0;
    }
}
				
			

Some particles parameters like acceleration are expressed in vector format. (three float values together). The HUD allows to modify float values individually, and we will need to update the vector value based on the updated floats.

				
					//Vector updater with new data
//takes a vector, converts it to a list, replaces the float value at the specified index with a new value, and then converts the modified list back to a vector. It returns the modified vector as the result.
vector ReplaceFloatInVector(vector originalVector, float replacementValue, integer index)
{
    // Convert the original vector to a list
    list vectorList = [originalVector.x, originalVector.y, originalVector.z]; ;

    // Replace the Float value at the specified index in the list
    vectorList = llListReplaceList(vectorList, [replacementValue], index, index);

    // Convert the modified list back to a vector
    vector modifiedVector = llList2Vector(vectorList, 0);

    return modifiedVector;
}

				
			
				
					//converts a float value to a string, extracts the integer and decimal parts, truncates or pads the decimal part to the desired decimal places, concatenates the parts, and returns the formatted string representation of the float value.
string formatFloat(float value, integer decimalPlaces)
{
    //llOwnerSay("formatFloat Function Triggered.");
    // Convert the Float value to a string
    string stringValue = (string)value;

    // Find the position of the decimal point
    integer decimalPos = llSubStringIndex(stringValue, ".");

    // Get the integer part of the Float value
    string integerPart = llGetSubString(stringValue, 0, decimalPos - 1);

    // Get the decimal part of the Float value
    string decimalPart = llGetSubString(stringValue, decimalPos + 1, -1);

    // Truncate or pad the decimal part to the desired decimal places
    if (llStringLength(decimalPart) > decimalPlaces)
    {
        decimalPart = llGetSubString(decimalPart, 0, decimalPlaces - 1);
    }
    else
    {
        while (llStringLength(decimalPart) < decimalPlaces)
        {
            decimalPart += "0";
        }
    }

    // Concatenate the parts with a dot
    string result = integerPart + "." + decimalPart;

    //llOwnerSay("formatFloat Function result: "+result);
    return result;
}

				
			

This is needed to be able to determine the linked prim’s number to be able to target it.

				
					//returns: currentLinkNumber
//searches for a specific prim in the linkset based on the provided parameters and returns the link number of the matching prim
integer GetPrimNumber ( string current_Param, string current_Action, string current_DetailString )
{
    //Debug Test message
    //llOwnerSay("GetPrimNumber Function Triggered");
    
    integer linkNum = llGetNumberOfPrims();
    integer currentLinkNumber = 999; // Default value if no match is found
    integer matchFound = FALSE; // Flag to indicate if a match is found
    
    if (current_DetailString != "")
    {
    current_Action = current_Action+"_"+current_DetailString;
    }
    
    integer i = 0;
    while (i <= linkNum && !matchFound)
    {
        string LinkName = llGetLinkName(i);
        if (LinkName == current_Param + "_" + current_Action)
        {
            currentLinkNumber = i;
            matchFound = TRUE;
        }
        i++;
    }
    
    //llOwnerSay((string)currentLinkNumber+" primnumber is found.");
    if (currentLinkNumber == 999)
    {
    llOwnerSay("Something is wrong with finding " +current_Param + "_" + current_Action +" button!");    
    }
    return currentLinkNumber;
}

				
			

We are storing the activated values in the prim descriptions, we need the info when populating the HUD with updated hovering text

				
					// Uses GetPrimNumber to retrieve the link number and then retrieves the primitive description of that prim.
string GetPrimDescription (string current_Param, string current_Action, string current_DetailString )
{
    integer linkNumber = GetPrimNumber ( current_Param, current_Action, current_DetailString );
    list DescriptionList = llGetLinkPrimitiveParams(linkNumber, [ PRIM_DESC ] );
    string Description = llList2String(DescriptionList, 0);
    return Description;
}

				
			
				
					//determines the value of the increment based on the provided parameters current_Param and current_Action. 
// Burst Count - 1
// StartGlow - 0.01
// all other - 0.1
float GetIncrement(string current_Param, string current_Action)
{
    //Debug Test message
    //llOwnerSay("increment Function Triggered");
    
    float Increment;
    if (current_Param == "BurstCount")
    {
        Increment = 1;
    }
    else if (current_Param == "StartGlow")
    {
        Increment = 0.01;
    }
    else
    {
        Increment = 0.1;
    }
    
    if (current_Action == "Decrease")
    {
        Increment = -Increment;
    } 
    
    return Increment;
}

				
			

This is a complex function that determines the match_Value, sets the color for the text based on the matching condition, and displays the text and updates the description for the relevant display prims based on the current_Action.

It is called with the following parameters (variables as input)

  • current_Action,
  • current_Param,
  • current_Value,
  • activated_Value,
  • current_DetailString

It starts checking if the current_Param is

  • Acceleration

and if the current_Action is either

  • Increase
  • Decrease

 

This check for Acceleration is needed to be done first, because Acceleration values are vectors and have to be handled a little differently than simple floats.

				
					//determines the match_Value, sets the color for the text based on the matching condition, and sets the text and updates the description for the relevant display prims based on the current_Action.
Display (string current_Action, string current_Param, string current_Value, string activated_Value, string current_DetailString)
{
    //Debug Test message
    //llOwnerSay("Display Function Triggered. current_Action is: "+current_Action+" current_Param is: "+current_Param+" current_Value is:"+current_Value+" activated_Value is: "+activated_Value+" current_DetailString is: "+current_DetailString);
    
    string match_Value;
    
    //check if primname starts with "Acceleration"
    integer currentParam_isAcceleration = CheckPrimName(current_Param, "Acceleration");

    if (current_Param == "Acceleration" && (current_Action == "Increase" || current_Action == "Decrease"))
    {
        
        //replaces updated component value in vector to match against
        string modifiedVectorString = MatchVectorValue(activated_Value, current_Value, current_DetailString);
        match_Value = modifiedVectorString;     
        //llOwnerSay("match_Value is: "+match_Value);
    }
    
    
				
			

This part is needed because there is an activation button. Until the activation is not done, the parameters are not sent to the particles and the numbers are gold, not green. Only when the values get sent (activated) to the particles, then the numbers turn green:

The color of displayed text is defined in local variable “textColor”

  • activated color
  • display color

depending on the match_Value and activated_Value when the arows are clicked, the color needs to be checked and properly set.

				
					 else
    {
         match_Value = current_Value;    
         //llOwnerSay("match_Value is: "+match_Value);
    }
    
    vector textColor;
    //check for matching values
    if (match_Value == activated_Value)
    {
        //define activated color
        textColor = <0.478431, 0.815686, 0.043137>;
        //llOwnerSay("textColor is: "+(string)textColor);
    } 
    
    else if (match_Value != activated_Value)
    {
        //define display color
        textColor = <0.871, 0.718, 0.455>;
        //llOwnerSay("textColor is: "+(string)textColor);
    }
				
			

In below part the in case of Default or Activate buttons the text needs to be updated in the Display prims.

				
					//set hover text and description for vector value (all 3 x,y,z floats)

    if ((current_Action == "Default" || current_Action == "Activate") && current_Param == "Acceleration" )
    {
        //gets the relevant Display Primnumber
        integer Param_DisplayPrimNumber = GetPrimNumber(current_Param,"Display","0");
        //gets the relevant component of the activated value
        float currentParsed_Value = ParseStringVector2String(activated_Value, 0);
        //truncates the relevant compoment of the activated value
        string currentString_Value = formatFloat(currentParsed_Value, 2);
        //sets the hover text and the description of the Display Prim to the truncated relevant compoment of the activated value
        llSetLinkPrimitiveParamsFast(Param_DisplayPrimNumber, [PRIM_TEXT, currentString_Value, textColor, 1.0, PRIM_DESC, currentString_Value, PRIM_COLOR, ALL_SIDES, textColor, 0.0]);

        //repeat for y and z:
        integer Param_DisplayPrimNumber1 = GetPrimNumber(current_Param,"Display","1");
        float currentParsed_Value1 = ParseStringVector2String(activated_Value, 1);
        string currentString_Value1 = formatFloat(currentParsed_Value1, 2);
        llSetLinkPrimitiveParamsFast(Param_DisplayPrimNumber1, [PRIM_TEXT, currentString_Value1, textColor, 1.0, PRIM_DESC, currentString_Value1, PRIM_COLOR, ALL_SIDES, textColor, 0.0]);
        integer Param_DisplayPrimNumber2 = GetPrimNumber(current_Param,"Display","2");
        float currentParsed_Value2 = ParseStringVector2String(activated_Value, 2);
        string currentString_Value2 = formatFloat(currentParsed_Value2, 2);
        llSetLinkPrimitiveParamsFast(Param_DisplayPrimNumber2, [PRIM_TEXT, currentString_Value2, textColor, 1.0, PRIM_DESC, currentString_Value2, PRIM_COLOR, ALL_SIDES, textColor, 0.0]);
    }
        else  
    {
        //set hover text and description for standalone float value
        integer Param_DisplayPrimNumber = GetPrimNumber(current_Param,"Display",current_DetailString);
        llSetLinkPrimitiveParamsFast(Param_DisplayPrimNumber, [PRIM_TEXT, current_Value, textColor, 1.0, PRIM_DESC, current_Value, PRIM_COLOR, ALL_SIDES, textColor, 0.0]);
        
        //Debug Test message
        //llOwnerSay (current_Param+" DisplayPrimNumber is: "+(string)Param_DisplayPrimNumber+" Current "+current_Param+" value is: "+(string)current_Value+" textColor is: "+(string)textColor);
    }
}
				
			

This function is called when the Activate buttons are pressed.

The end of this function calls the above Display function.

				
					//sets the activated value to the current value, updates the object's description, sends an update message to a channel, outputs debug messages, and calls the Display function to show the current and activated values.
Activate(string current_Action, string current_Param, string current_Value, string current_DetailString )
{
    //Debug Test message - check if function was triggered
    //llOwnerSay("ACTIVATE Function Triggered. current_Action is: "+current_Action+" current_Param is: "+current_Param+" current_Value is: "+current_Value+" current_DetailString is: "+current_DetailString);
    
    //set activated value to current value
    string activated_Value = current_Value;
    
    //store it in object description
    integer currentActivate_PrimNumber = GetPrimNumber ( current_Param, "Activate", "" );
    llSetLinkPrimitiveParamsFast(currentActivate_PrimNumber, [ PRIM_DESC, activated_Value ] );
    
    //send info to particles
    llSay(listen_Channel, "Update_"+current_Param+"_"+activated_Value);
    
    //send time info to chat
    if (is_Timer_ON) 
    {
        float time = llGetTime();
        llOwnerSay((string)time +"|RS:"+(string)listen_Channel+":Update_"+current_Param+" "+activated_Value); 
    }
    
    //Debug Test message - check values and parameters
    //llOwnerSay("current_Param :"+current_Param+" current_Value: "+current_Value+" activated_Value: "+activated_Value);
    
    llPlaySound("Retro Game Lock",1.0);
    llSetLinkAlpha(currentActivate_PrimNumber, 1.0, ALL_SIDES);
    llSleep(0.1);
    llSetLinkAlpha(currentActivate_PrimNumber, 0.0, ALL_SIDES);    
    
    Display (current_Action, current_Param, current_Value, activated_Value, current_DetailString);       
}
				
			
				
					//performs various operations on the provided parameters, calculates a changed value based on an increment, applies specific rules for certain parameters, retrieves an activated value, and then calls the Display function to show the changed value.
Change(integer linkNumber, string current_Action, string current_Param, string current_Value, string current_DetailString)
{
    //check if primname starts with "Acceleration"
    integer currentParam_isAcceleration = CheckPrimName(current_Param, "Acceleration");
    
    //Debug Test message - check if function was triggered
    //llOwnerSay("Change Function Triggered. "+" linkNumber is: "+(string)linkNumber+" current_Action is: "+current_Action+" Current_Param is: "+current_Param+" current_Value is : "+current_Value+" current_DetailString is :"+current_DetailString);
    
    // Local variables
    float changedValue;
    string finalValue;

    // Convert string current_Value to Float
    float FloatValue = (float)current_Value;
    
    // Debug Test message    
    //llOwnerSay("Current Value (Float): " + (string)FloatValue);

    // Call GetIncrement function, add increment
    float increment = GetIncrement(current_Param, current_Action);
    changedValue = FloatValue + increment;
    
    // Debug Test message   - changed value check 
    //llOwnerSay("increment is: "+(string)increment+", changed value is: "+(string)changedValue );


    // Set max, min values
    
    if (current_Param == "BurstCount")
    {
        if (changedValue > 50)
        {
            changedValue = 50;
            llOwnerSay("Burst Count value cannot be larger than 50");
        } else if (changedValue < 0)
        {
            changedValue = 0;
            llOwnerSay("Burst Count value cannot be negative.");
        }
        finalValue = formatFloat(changedValue, 1); 

        // Debug Test message - check final value
        //llOwnerSay("finalValue is: "+finalValue);
    } 
    else if  (current_Param == "StartGlow")
    {
         if (changedValue > 0.1)
        {
            changedValue = 0.1;
            llOwnerSay("StartGlow value cannot be larger than 0.1");
        } else if (changedValue < 0)
        {
            changedValue = 0;
            llOwnerSay("StartGlow value cannot be negative.");
        }
        finalValue = formatFloat(changedValue, 2); 

        // Debug Test message - check final value
        //llOwnerSay("finalValue is: "+finalValue);
    } 

    else 
    {
        if (changedValue > 1.0)
        {
            changedValue = 1.0;
            llOwnerSay(current_Param+ " value cannot be larger than 1.0");
        }
        else if (changedValue < 0 &&  (!currentParam_isAcceleration))  //acceleration can be negative
        {
            changedValue = 0;
            llOwnerSay(current_Param+ " value cannot be negative.");
        }
        finalValue = formatFloat(changedValue, 2);
        
        // Debug Test message        
        //llOwnerSay("finalValue is: "+finalValue);
    }
    
    // Get activated value from Activate Description for Display function to match it against current value (and set its color)
    string activatedValue = GetPrimDescription (current_Param, "Activate" , "");
    
    // Debug Test message - check values
    //llOwnerSay("current_Param: "+current_Param+" finalValue: "+finalValue+" activatedValue: "+activatedValue);
    
    brief_HoverEffect_Alpha (linkNumber);
    // Call Display function to display changed value (with proper color)
    Display(current_Action, current_Param, finalValue, activatedValue, current_DetailString);
}

				
			

The color buttons can be individually edited as shown in diagram above.

Color button names:
  • Start_Color
  • End_Color
  • End_Color_Sample
  • Start_Color_Sample
Color function

When the user clicks the color buttons, the following functions called:

  1. a Sound is played
  2. the button’s color is received in a variable
  3. a color message is sent to the particle and the sample prim on the HUD that shows the current Start and End colors.
  4. The position of the pressed button gets stored in a variable
  5. the Indicator Ring’s color is set to the same color and position.
				
					//current_Param is Start or End: button names: Start_Color, End_Color
Color (integer linkNumber, string current_Param)
{  
    llPlaySound("Tiny Beep",1.0);
    
    //Debug Test message
    //llOwnerSay("Color Function Triggered");
                    
    // get color of button prim    
    list senderColorParams = llGetLinkPrimitiveParams(linkNumber, [PRIM_COLOR, 0]);
    vector effect_color = llList2Vector(senderColorParams, 0);
    string color_message = llList2String(senderColorParams, 0);
                                                           
    llSay(listen_Channel,"Update_"+current_Param+"Color_"+color_message);
        if (is_Timer_ON) 
        {
            float time = llGetTime();
            llOwnerSay((string)time +"|RS:"+(string)listen_Channel+":Update_"+current_Param+"Color_"+color_message);
        }
    
    llSetLinkColor(linkNumber, <1.0, 1.0, 1.0>, ALL_SIDES);
    llSleep(0.05);
    llSetLinkColor(linkNumber, effect_color, ALL_SIDES);
                    
    //get Color Sample Prim's number, set its color to match pressed Color button's color
    integer Display_num = GetPrimNumber ( current_Param, "Color" ,"Sample");
    llSetLinkPrimitiveParams(Display_num, [PRIM_COLOR, -1, effect_color,1.0]);
    
    //get pressed Color Button's position (to later set indicator ring's position to this vector)
    list TempColorPosParams = llGetLinkPrimitiveParams(linkNumber, [ PRIM_POS_LOCAL ]);
    vector TempColorPos = llList2Vector(TempColorPosParams, 0);
    
    //get Indicator Ring's prim number,  set its position and color
    integer Indicator_num = GetPrimNumber ( current_Param, "Color" ,"Indicator");
    llSetLinkPrimitiveParams(Indicator_num, [ PRIM_POS_LOCAL, TempColorPos, PRIM_COLOR, -1, effect_color,0.5]);
    }
				
			

This function is called when the Particle and Timer controller buttons are pressed on the top of the HUD.

Timer and Particle Controller Buttons:
  • Timer_Controller_START
  • Timer_Controller_STOP
  • TimeAndParticles_Controller_START
  • TimeAndParticles_Controller_STOP
  • Particles_Controller_START
  • Particles_Controller_STOP
				
					Controller (string current_Param, string current_DetailString)
{
    llPlaySound("Upward Bleeps",1.0);
    
    //Debug Test message
    //llOwnerSay("Controller Function Triggered");
    
    //Timer stop and start for both cases
    if (current_Param == "TimeAndParticles" || current_Param  == "Timer")    
    {
        if (current_DetailString == "START")
        {
            is_Timer_ON = TRUE;                 
            llOwnerSay("Timer Started: 00.00");
            llResetTime();   
        } else if (current_DetailString == "STOP")
        {
            is_Timer_ON = FALSE; 
            float time = llGetTime();
            llOwnerSay((string)time +"|STOP");
        }
    }
    
    //Particle stop and start for both cases                    
    if (current_Param == "TimeAndParticles" || current_Param == "Particles") 
    {
        //Send message to Particles to start or stop playing
        llSay(listen_Channel,current_DetailString+"Particles");
        float time = llGetTime();
        llOwnerSay((string)time +"|RS:"+(string)listen_Channel+":"+current_DetailString+"Particles");
    }           
}
				
			

The first time the default values are set, the Activate function is called.

All other times, when the Default buttons are clicked, the Display functions is called.

				
					//when default buttons are pressed
//retrieves the default value for a specific primitive, and then activates an action using that default value along with other provided parameters.
SetDefaultValue (string current_Action, string current_Param, string current_DetailString)
{
    //get default vector value in string format from relevant Default prim's Description
    string DefaultValue = GetPrimDescription (current_Param, "Default" ,""); //The Default string names don't have a 3rd component.
    string activated_Value= GetPrimDescription ( current_Param,  "Activate",  current_DetailString ) ;
    Display (current_Action, current_Param, DefaultValue, activated_Value, current_DetailString);
}

//used in state entry state and reset
//retrieves the default value for a specific primitive, and then activates an action using that default value along with other provided parameters.
SetDefaultValueFirst (string current_Action, string current_Param, string current_DetailString)
{
    //get default vector value in string format from relevant Default prim's Description
    string DefaultValue = GetPrimDescription (current_Param, "Default" ,""); //The Default string names don't have a 3rd component.
    Activate(current_Action, current_Param, DefaultValue, current_DetailString);
}

				
			

Button names consist of 2 or 3  parts divided by “_”, these are converted into the following variables:

  • current_Param
  • current_Action
  • current_DetailString

The rest of the functions checks for certain actions:

Default

Activate

Parsing, Default

				
					//extracts relevant information, and performs different actions based on the extracted values
parse_buttonName (string buttonName, integer linkNumber)
{

    //Debug Test message
    //llOwnerSay("parse_buttonName Function Triggered");
    
    list parsedList = llParseString2List(buttonName, ["_"], []);

    string current_Param = llList2String(parsedList, 0);
    string current_Action = llList2String(parsedList, 1);
    string current_DetailString = llList2String(parsedList, 2);

    //Debug Message
    //llOwnerSay("Part 0: " + current_Param);
    //llOwnerSay("Part 1: " + current_Action);
    //llOwnerSay("Part 2: " + current_DetailString);
    
    //call Default function
    if (current_Action == "Default") 
    {
        brief_HoverEffect_Color (linkNumber);
        SetDefaultValue (current_Action, current_Param, current_DetailString);
    }
    
				
			

Call ACTIVATE function

Acceleration vector needs to be combined from 3 button descriptions:

  • Acceleration_Display_0
  • Acceleration_Display_1
  • Acceleration_Display_2

The remaining floats are gathered from the relevant “Display” buttons.

				
					    //call Activate function
    else if (current_Action == "Activate") 
    {
        //Debug Message
        //llOwnerSay("Activate Function is being called from Parse function via Activate buttons");
        string current_Value;
        
        if (current_Param == "Acceleration")
        {
            string AccelerationX = GetPrimDescription("Acceleration", "Display", "0");
            string AccelerationY = GetPrimDescription("Acceleration", "Display", "1");
            string AccelerationZ = GetPrimDescription("Acceleration", "Display", "2");
            current_Value = "<"+AccelerationX+","+AccelerationY+","+AccelerationZ+">";
        }        
        else
        {
            current_Value = GetPrimDescription(current_Param, "Display", current_DetailString);
        }
        Activate ( current_Action, current_Param, current_Value, current_DetailString);  
    }
				
			

Call CHANGE function

				
					    //call Change function        
    else if (current_Action == "Increase" || current_Action == "Decrease")
    {
        if (current_Action == "Increase")
        { 
            llPlaySound("Plastic Switch 13",1.0);
        } 
        else //if Decrease
        {
             llPlaySound("Plastic Light Switch 2",1.0);   
        }
        
        string current_Value = GetPrimDescription(current_Param, "Display", current_DetailString);
        
        //Debug Message
        //llOwnerSay("Change ("+current_Action+") Function is being called from Parse function. Current passed value for "+current_Param+" is: "+current_Value);
                
        Change (linkNumber, current_Action, current_Param, current_Value, current_DetailString);    
    }
    
    //call Color function
    else if (current_Action == "Color")
    {
        Color (linkNumber, current_Param);
    }
				
			

Call CONTROLLER function

				
					    //call Controller function
    else if (current_Action == "Controller")
    {
        Controller (current_Param, current_DetailString);
    }
    else if (current_Param == "Minimize") 
    
    {
        currentRot = minRot;
        clearHoverText();
        llSetLocalRot(currentRot); // Set the new rotation
    }
    
        //Maximize button     
    else if (current_Param == "Maximize") 
    {
        currentRot = maxRot;
        populateHoverText();
        llSetLocalRot(currentRot); // Set the new rotation                
    }
    
    //Detach button     
    else if (current_Param == "Detach") 
    {
        llRequestPermissions(llGetOwner(), PERMISSION_ATTACH);
    }
}
				
			

Core HUD Script (working with above functions)

The default values are set, any messages are litened to.

				
					default
{
    state_entry()
    {
        SetDefaultValueFirst("Default","BurstRate","");
        SetDefaultValueFirst("Default","BurstCount","");
        SetDefaultValueFirst("Default","StartAlpha","");
        SetDefaultValueFirst("Default","StartGlow","");
        SetDefaultValueFirst ("Default","Acceleration","0");
        SetDefaultValueFirst ("Default","Acceleration","1");
        SetDefaultValueFirst ("Default","Acceleration","2");
        llOwnerSay("Ready.");
    }


    link_message(integer senderNumber, integer linkNumber, string buttonName, key id)
    {
         //Debug Message
         //llOwnerSay("Link message received: "+(string)senderNumber+" "+(string)linkNumber+" "+buttonName);
         parse_buttonName(buttonName, linkNumber);
    }
    
    run_time_permissions(integer perm) 
    {
        if (perm & PERMISSION_ATTACH)
        {
            llDetachFromAvatar( );
        }
        else 
        {
            llOwnerSay("Permission denied!");
        }
    }        
}

				
			

PARTICLE script attached to hands

Global Variables
				
					//define listener channel - same as in HUD
integer LISTENER_CHANNEL = 689689;
integer is_ParticleON = FALSE;
integer update_ParticleSystem = FALSE;

//Timer interval
float TIMER_INTERVAL = .5;

// generic declarations:
float ParticleDelay = 0.1;
float ParticleDuration =  10.0;

vector StartColor = <1.000000,1.000000,1.000000>;
vector EndColor = <1.000000,1.000000,1.000000>;
float color_range_variation = 0.2;

float BurstRate = 1.0; // Set starting burst Rate
integer BurstCount = 5;
vector Acceleration = <0.000000,0.000000,0.100000>;
float StartAlpha = 0.6;
float StartGlow = 0.2;

				
			
				
					//Strings to vector conversion
vector String2Vector_Conversion(string colorString)
    {
        list colorList = llParseString2List(colorString, [",", "<", ">"], []);
    
        float r = (float)llList2String(colorList, 0);
        float g = (float)llList2String(colorList, 1);
        float b = (float)llList2String(colorList, 2);
        
        vector colorVector = <r, g, b>;
        
        return colorVector;
    }
				
			
				
					
//Use Particles_ON() and  Particles_OFF() to turn the system on and off
//move the variables from below function to global if needed!!!

Particles_ON() 
    {
        //Particle effect configurations - Individual differences per prim
        
        //Root prim specifications (small twinkle):
        list UniqueParticle_Params = [
           // Texture Parameters:
            PSYS_SRC_PATTERN,PSYS_SRC_PATTERN_DROP,
            PSYS_SRC_BURST_RADIUS,0,
            PSYS_SRC_ANGLE_BEGIN,0,
            PSYS_SRC_ANGLE_END,0,
            PSYS_SRC_TARGET_KEY,llGetKey(),
            PSYS_PART_START_COLOR,StartColor,
            PSYS_PART_END_COLOR,EndColor,
            PSYS_PART_START_ALPHA,StartAlpha,
            PSYS_PART_END_ALPHA,0,
            PSYS_PART_START_GLOW,StartGlow,
            PSYS_PART_END_GLOW,0.0,
            PSYS_PART_BLEND_FUNC_SOURCE,PSYS_PART_BF_SOURCE_ALPHA,
            PSYS_PART_BLEND_FUNC_DEST,PSYS_PART_BF_ONE_MINUS_SOURCE_ALPHA,
            PSYS_PART_START_SCALE,<0.500000,0.500000,0.000000>,
            PSYS_PART_END_SCALE,<0.500000,4.000000,0.000000>,
            PSYS_SRC_TEXTURE,"eace85d3-b142-5a0a-4971-f4063dab483c",
            PSYS_SRC_MAX_AGE,0,
            PSYS_PART_MAX_AGE,5,
            PSYS_SRC_BURST_RATE,BurstRate,
            PSYS_SRC_BURST_PART_COUNT,BurstCount,
            PSYS_SRC_ACCEL,Acceleration,
            PSYS_SRC_OMEGA,<0.000000,0.000000,0.000000>,
            PSYS_SRC_BURST_SPEED_MIN,0.5,
            PSYS_SRC_BURST_SPEED_MAX,0.5,
            PSYS_PART_FLAGS,
                0 |
                PSYS_PART_EMISSIVE_MASK |
                PSYS_PART_INTERP_COLOR_MASK |
                PSYS_PART_INTERP_SCALE_MASK
        ];
        
 
            llParticleSystem(UniqueParticle_Params);          
}
    
Particles_OFF() 
    {
        llParticleSystem( [ ] );
    }
    
    
				
			

The parsing of the message could be done by a separate function too maybe…

				
					default
{
    state_entry() 
    {    
        Particles_OFF(); 
        llListen(LISTENER_CHANNEL, "", NULL_KEY, "");
    
    }

 listen(integer channel, string name, key id, string message) 
    {
        if (llGetOwnerKey(id) == llGetOwner() )
        {
            list parsedList_fromMessage = llParseString2List(message, ["_"], []);

            string active_Action = llList2String(parsedList_fromMessage, 0);
            string active_Param = llList2String(parsedList_fromMessage, 1);
            string active_Value = llList2String(parsedList_fromMessage, 2);
            //llOwnerSay("Message received. Action is :"+active_Action+" Param is: "+active_Param+". Value is: "+active_Value);
                
            if (active_Action == "Update")
            {
                if (active_Param == "BurstCount")
                {
                    BurstCount = llList2Integer(parsedList_fromMessage, 2);
                    //llOwnerSay("BurstRate is: "+(string)BurstRate);
                }
                if (active_Param == "BurstRate")
                {
                    BurstRate = llList2Float(parsedList_fromMessage, 2);
                    //llOwnerSay("BurstRate is: "+(string)BurstRate);
                }
                if (active_Param == "StartGlow")
                {
                    StartGlow = llList2Float(parsedList_fromMessage, 2);
                    //llOwnerSay("StartGlow is: "+(string)StartGlow);
                }
                if (active_Param == "StartAlpha")
                {
                    StartAlpha = llList2Float(parsedList_fromMessage, 2);
                    //llOwnerSay("StartAlpha is: "+(string)StartAlpha);
                }
                if (active_Param == "StartColor")
                {
                string ColorInfo = llList2String(parsedList_fromMessage, 2);
                StartColor = String2Vector_Conversion(ColorInfo);     
                //llOwnerSay("StartColor is: "+(string)StartColor);
                }
                if (active_Param == "EndColor")
                {
                string ColorInfo = llList2String(parsedList_fromMessage, 2);
                EndColor = String2Vector_Conversion(ColorInfo);     
               // llOwnerSay("EndColor is: "+(string)EndColor);
                }
                if (active_Param == "Acceleration")
                {
                string accelInfo = llList2String(parsedList_fromMessage, 2);
                Acceleration = String2Vector_Conversion(accelInfo);     
                //llOwnerSay("Acceleration is: "+(string)Acceleration);
                }
                update_ParticleSystem = TRUE;
            }
            else if (active_Action == "STARTParticles")
                {
                    //llOwnerSay("Start message received");
                    Particles_ON();
                    is_ParticleON = TRUE;
                    llSetTimerEvent(TIMER_INTERVAL);
                }   
                        //check if time is up (for current linknumber prim)
            else if (active_Action == "STOPParticles")   
            {
                //llOwnerSay("Stop message received");
                Particles_OFF();
                is_ParticleON = FALSE;
                llSetTimerEvent(0.0);
            }            
            

        }
    }

    changed(integer change)
    {
        // reset script when the owner or the inventory changed
        if (change & (CHANGED_OWNER | CHANGED_INVENTORY))
        {
            llResetScript();
        }
    }

    timer() 
    {
            
        if (update_ParticleSystem == TRUE)
            {   
                Particles_ON(); 
                update_ParticleSystem = FALSE;
            }   
         
    }
}
				
			
hu_HU