About

This is the converse of the environmental with medicine backpack applied to stress levels. In this variant, the bar starts at 0 on green and fills up progressively to 1000 on red. It is used in cases where you want the progress bar to start empty and eventually reach the top - perhaps something like danger meter.

Code

This script was tested and works on OpenSim version 0.7.4!

contagionstress.lsl
///////////////////////////////////////////////////////////////////////////
//  Copyright (C) Wizardry and Steamworks 2013 - License: GNU GPLv3      //
//  Please see: http://www.gnu.org/licenses/gpl.html for legal details,  //
//  rights of fair usage, the disclaimer and warranty conditions.        //
///////////////////////////////////////////////////////////////////////////
 
///////////////////////////////////////////////////////////////////////////
//    Copyright (C) 2011 Wizardry and Steamworks - License: GNU GPLv3    //
///////////////////////////////////////////////////////////////////////////
string wasProgress(integer percent, integer length, list symbols) {
    percent /= (integer)((float)100.0/(length));
    string p = llList2String(symbols,0);
    integer itra = 0;
    do {
        if(itra>percent-1) p += llList2String(symbols,2);
        else p += llList2String(symbols,1);
    } while(++itra<length);
    return p + llList2String(symbols,3);
}
 
///////////////////////////////////////////////////////////////////////////
//    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) 2012 Wizardry and Steamworks - License: GNU GPLv3    //
///////////////////////////////////////////////////////////////////////////
list wasDeleteSubListMatch(list in, string match) {
    if (llGetListLength(in) == 0) return [];
    string first = llList2String(in, 0);
    in = llDeleteSubList(in, 0, 0);
    if (llSubStringIndex(first, match) == -1) jump next;
    return wasDeleteSubListMatch(in, match);
@next;
    return first + wasDeleteSubListMatch(in, match);
}
 
///////////////////////////////////////////////////////////////////////////
//    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) 2014 Wizardry and Steamworks - License: GNU GPLv3    //
///////////////////////////////////////////////////////////////////////////
string wasKeyValueSet(string k, string v, string data) {
    if (llStringLength(data) == 0) return k + "=" + v;
    if (llStringLength(k) == 0) return "";
    if (llStringLength(v) == 0) return "";
    integer i = llListFindList(
        llList2ListStrided(
            llParseString2List(data, ["&", "="], []),
            0, -1, 2
            ),
        [ k ]);
    if (i != -1) return llDumpList2String(
            llListReplaceList(
                llParseString2List(data, ["&"], []),
                [ k + "=" + v ],
                i, i),
            "&");
    return data + "&" + k + "=" + v;
}
 
///////////////////////////////////////////////////////////////////////////
//    Copyright (C) 2013 Wizardry and Steamworks - License: GNU GPLv3    //
///////////////////////////////////////////////////////////////////////////
string wasKeyValueEncode(list data) {
    list k = llList2ListStrided(data, 0, -1, 2);
    list v = llList2ListStrided(llDeleteSubList(data, 0, 0), 0, -1, 2);
    data = [];
    do {
        data += llList2String(k, 0) + "=" + llList2String(v, 0);
        k = llDeleteSubList(k, 0, 0);
        v = llDeleteSubList(v, 0, 0);
    } while (llGetListLength(k) != 0);
    return llDumpList2String(data, "&");
}
 
// Quick function to list AoE.
string getExposures() {
    if (llGetListLength(exposures)) return "Exposures: " + "[ " + llDumpList2String(exposures, ",") + " ]";
    return "";
}
 
integer stress = 0;
integer damage = 0;
list exposures = [];
list penalties = [];
 
default
{
    state_entry() {
        stress = (integer)wasKeyValueGet("stress", llGetObjectDesc());
        integer comChannel = (integer)("0x8" + llGetSubString(llGetOwner(), 0, 6));
        llListen(comChannel, "Medicine Backpack", "", "");
        llSetTimerEvent(1);
        llSensorRepeat("", "", ACTIVE | PASSIVE, 96, TWO_PI, 1);
    }
 
    // When an AoE objects are detected, go through all off them and process them.
    sensor(integer num) {
        --num;
        do {
            // First grab the formatted name.
            string data = llDetectedName(num);
            // If the range of the AoE is smaller than the distance between the AV and the
            // trigger point, then remove the AoE exposure from the exposures list and continue.
            string dh = wasKeyValueGet("range", data);
            // If the object does not have a range set, it's not going to affect the avatar
            // and is most likely just a static object not part of the project. Quickly ignore.
            if (dh == "") jump continue;
            if (llVecDist(llGetPos(), llDetectedPos(num)) > (float)dh) jump continue;
            // Check if the exposure has a properly formatted name.
            dh = wasKeyValueGet("name", data);
            // If it doesn't ignore it, and jump to AoE processing.
            if (dh == "") jump exposure;
            // If it does, add it to the list of AoE exposures.
            if (llListFindList(exposures, (list)dh) == -1) {
                exposures += dh;
                jump exposure;
            }
            // If the exposure is already active, ignore the AoE (non-stacking AoE)
            return;
@exposure;
            // Check if the exposure has a properly formatted stress.
            dh = wasKeyValueGet("stress", data);
            // If it does not, ignore and continue and don't waste time.
            if (dh == "") jump continue;
            // Get the stress and add it to the current stress, either increasing or decreasing
            // while making sure that it doesn't underrun 0, respectively 100 (Hard cap).
            damage += (integer)dh;
            penalties += (integer)dh;
@continue;
        } while (--num > -1);
    }
 
    listen(integer channel, string name, key id, string message) {
        if (llGetOwnerKey(id) != llGetOwner()) return;
        string exposure = wasKeyValueGet(message, llGetObjectDesc());
        if (llStringLength(exposure) == 0) return;
        integer i = llListFindList(exposures, (list)exposure);
        if (i == -1) return;
        exposures = llDeleteSubList(exposures, i, i);
        integer d = llList2Integer(penalties, i);
        damage -= d;
        penalties = llDeleteSubList(penalties, i, i);
        llOwnerSay("You prevent the " + exposure + " exposure.");
    }
 
    // Every tick, display overhead status.
    timer() {
        stress += damage;
        if (stress <= 0) stress = 0;
        if (stress > 1000) stress = 1000;
        llSetObjectDesc(wasKeyValueSet("stress", (string)stress, llGetObjectDesc()));
        integer p = (integer)(100.0 * (float)stress / 1000.0);
        llSetText("Stress: " + wasProgress(p, 10, ["[", "█", "░", "]"]) + "\n" + getExposures(), wasPercentToGradient(p, "gr"), 1.0);
        llRegionSay(-2104156, wasKeyValueEncode(["owner", llKey2Name(llGetOwner()), "stress", p]));
    }
 
    // Restart the script on rez and attach to make sure that llGetOwner() returns a correct key (BUG)
    on_rez(integer num) {
        llResetScript();
    }
    attach(key id) {
        llResetScript();
    }
}