Table of Contents

About

Part of the Global Health activities consist in letting students discover various areas where they can interact with objects. In order to help them to find their way, we created an information heads-up display (HUD) that scans for various objects in range and displays some pre-defined text.

Setup

  • The script can be placed in either a worn attachment or in object that is worn as a HUD.
  • In the same primitive as the script, a notecard named info must be placed that contains the information to show. The syntax of the notecard follows the key-value data syntax:
name=Tree&info=That is a tree...
name=KittyCats - jojo&info=That's one large pussy!

in this example, when the HUD senses an object named "Tree", it will display the text: "That is a tree…". If it detects an object named "KittyCats - jojo" it will display the text "That's one large pussy!". Note that the name of the object to detect in the notecard must be exact or else the script will not find the object.

Each object is listed the same way line-by-line in the notecard info (remember that a blank line at the end of the notecard is necessary for the dataserver to read the notecard properly).

  • The script can be configured for a desired range by modifying the SCAN_RANGE parameter in the script. By default, the HUD detects an object if it is within a $5m$ radius.
  • The script will break the info lines in the notecard by default on the 30th character. This can be configured by editing the script and changing the BREAK_COLUMN to some value where you want the text to insert a newline and break the line.

Code

This script was tested and works on OpenSim version 0.7.4!

infohud.lsl
///////////////////////////////////////////////////////////////////////////
//  Copyright (C) Wizardry and Steamworks 2014 - License: GNU GPLv3      //
//  Please see: http://www.gnu.org/licenses/gpl.html for legal details,  //
//  rights of fair usage, the disclaimer and warranty conditions.        //
///////////////////////////////////////////////////////////////////////////
 
///////////////////////////////////////////////////////////////////////////
//                            CONFIGURATION                              //
///////////////////////////////////////////////////////////////////////////
 
// distance in meters to detect an information spot
float SCAN_RANGE = 5;
// break text on column number
integer BREAK_COLUMN = 30;
 
///////////////////////////////////////////////////////////////////////////
//                              INTERNALS                                //
///////////////////////////////////////////////////////////////////////////
 
///////////////////////////////////////////////////////////////////////////
//    Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3    //
///////////////////////////////////////////////////////////////////////////
string wasKeyValueGet(string k, string data) {
    if (llStringLength(data) == 0) return "";
    if (llStringLength(k) == 0) return "";
    list a = llParseStringKeepNulls(data, ["&", "="], []);
    integer i = llListFindList(a, [ k ]);
    if (i != -1) return llList2String(a, i + 1);
    return "";
}
 
///////////////////////////////////////////////////////////////////////////
//    Copyright (C) 2013 Wizardry and Steamworks - License: GNU GPLv3    //
///////////////////////////////////////////////////////////////////////////
vector wasPercentToGradient(float percent, string rgb) {
    if (llStringLength(rgb) != 2) {
        llSay(DEBUG_CHANNEL, "Assert failed, rgb parameter must consist of a pair of either r, g, or b.");
        return ZERO_VECTOR;
    }
    string a = llGetSubString(rgb, 0, 0);
    string b = llGetSubString(rgb, 1, 1);
    list col = [ "r", "g", "b" ];
    integer ax = llListFindList(col, (list)a);
    integer bx = llListFindList(col, (list)b);
    if (ax == -1 || bx == -1) {
        llSay(DEBUG_CHANNEL, "Asset failed, rgb parameters must contain either r, g, or b letters.");
        return ZERO_VECTOR;
    }
    col = llListReplaceList(col, (list)((100 - percent) / 100), ax, ax);
    col = llListReplaceList(col, (list)(percent / 100), bx, bx);
    return 2 * < llList2Float(col, 0), llList2Float(col, 1), llList2Float(col, 2) >;
}
 
///////////////////////////////////////////////////////////////////////////
//    Copyright (C) 2011 Wizardry and Steamworks - License: GNU GPLv3    //
///////////////////////////////////////////////////////////////////////////
// Requires: a string txt, a delimiter, a column number
// Provides: a string split at the first space after column
//////////////////////////////////////////////////////////
string wasSpaceWrap(string txt, string delimiter, integer column) {
    string ret = llGetSubString(txt, 0, 0);
    integer len = llStringLength(txt);
    integer itra = 1;
    integer itrb = 1;
    do {
        if (itrb % column == 0) {
            while (llGetSubString(txt, itra, itra) != " ") {
                ret += llGetSubString(txt, itra, itra);
                if (++itra > len) return ret;
            }
            ret += delimiter;
            itrb = 1;
            jump next;
        }
        ret += llGetSubString(txt, itra, itra);
        ++itrb;
        @next;
    } while (++itra < len);
    return ret;
}
 
list info = [];
list name = [];
integer line = 0;
 
default {
    state_entry() {
        llSetText("", ZERO_VECTOR, 0);
        if (llGetInventoryType("info") != INVENTORY_NOTECARD) {
            llSay(DEBUG_CHANNEL, "Failed to find a notecard named info in the primitive's inventory.");
            return;
        }
        llGetNotecardLine("info", line);
    }
    dataserver(key id, string data) {
        if (data == EOF) {
            state scan;
        }
        if (data == "") jump continue;
        string n = wasKeyValueGet("name", data);
        if (n == "") jump continue;
        string i = wasKeyValueGet("info", data);
        if (i == "") jump continue;
        info += i;
        name += n;
@continue;
        llGetNotecardLine("info", ++line);
    }
    on_rez(integer num) {
        llResetScript();
    }
    changed(integer change) {
        llResetScript();
    }
}
 
state scan {
    state_entry() {
        llSensorRepeat("", "", ACTIVE | PASSIVE, SCAN_RANGE, TWO_PI, 1);
    }
    sensor(integer num) {
        --num;
        do {
            integer i = llListFindList(name, (list)llDetectedName(num));
            if (i == -1) jump continue;
            llSetText(
                wasSpaceWrap(llList2String(info, i), "\n", BREAK_COLUMN),
                wasPercentToGradient(
                    100 * llVecDist(llDetectedPos(num), llGetPos()) / SCAN_RANGE,
                    "rg"
                    ),
                1);
            llSetTimerEvent(2);
@continue;
        } while (--num > -1);
    }
    timer() {
        llSetText("", ZERO_VECTOR, 0);
    }
    on_rez(integer num) {
        llResetScript();
    }
    changed(integer change) {
        llResetScript();
    }
}

secondlife/information_heads-up_display.txt · Last modified: 2022/11/24 07:46 by 127.0.0.1

Access website using Tor Access website using i2p Wizardry and Steamworks PGP Key


For the contact, copyright, license, warranty and privacy terms for the usage of this website please see the contact, license, privacy, copyright.