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.
HUD
.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).
SCAN_RANGE
parameter in the script. By default, the HUD
detects an object if it is within a radius.info
lines in the notecard by default on the 30
th 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.
/////////////////////////////////////////////////////////////////////////// // 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(); } }