fishmod.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.        //
///////////////////////////////////////////////////////////////////////////
 
///////////////////////////////////////////////////////////////////////////
//    Copyright (C) 2015 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(llList2ListStrided(a, 0, -1, 2), [ k ]);
    if(i != -1) return llList2String(a, 2*i+1);
    return "";
}
 
///////////////////////////////////////////////////////////////////////////
//    Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3    //
///////////////////////////////////////////////////////////////////////////
string wasKeyValueSet(string k, string v, string data) {
    if(llStringLength(k) == 0) return "";
    if(llStringLength(v) == 0) return "";
    if(llStringLength(data) == 0) return k + "=" + v;
    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    //
///////////////////////////////////////////////////////////////////////////
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) Wizardry and Steamworks 2012 - 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    //
///////////////////////////////////////////////////////////////////////////
string wasKeyValueEncode(list kvp) {
    if(llGetListLength(kvp) < 2) return "";
    string k = llList2String(kvp, 0);
    kvp = llDeleteSubList(kvp, 0, 0);
    string v = llList2String(kvp, 0);
    kvp = llDeleteSubList(kvp, 0, 0);
    if(llGetListLength(kvp) < 2) return k + "=" + v;
    return k + "=" + v + "&" + wasKeyValueEncode(kvp);
}
 
///////////////////////////////////////////////////////////////////////////
//    Copyright (C) 2013 Wizardry and Steamworks - License: GNU GPLv3    //
///////////////////////////////////////////////////////////////////////////
list wasKeyValueDecode(string data) {
    return llParseString2List(data, ["&", "="], []);
}
 
integer MAX_CHARGES = 0;
integer RECHARGE_SECONDS = 0;
 
integer ticks = 0;
integer channel = -1;
integer mode = FALSE;
integer press = 0;
 
default {
    state_entry() {
        integer channel = (integer)wasKeyValueGet("channel", llGetObjectDesc());
        llListen(channel, "[WaS-K] Fishulus", "", "");
        MAX_CHARGES = (integer)wasKeyValueGet("charges", llGetObjectDesc());
        RECHARGE_SECONDS = (integer)wasKeyValueGet("recharge", llGetObjectDesc());
        if((integer)wasKeyValueGet("stock", llGetObjectDesc()) >= MAX_CHARGES) return;
        string m = wasKeyValueGet("mode", llGetObjectDesc());
        if(m == "open") mode = TRUE;
        if(mode == FALSE) llSetObjectDesc(wasKeyValueSet("mode", "restricted", llGetObjectDesc()));
        if(mode == TRUE) llSetObjectDesc(wasKeyValueSet("mode", "open", llGetObjectDesc()));
        llSensorRepeat("", "", ACTIVE, llVecMag(llGetScale()), TWO_PI, 1);
        llSetTimerEvent(1);
    }
    sensor(integer num) {
        --num;
        integer charge = (integer)wasKeyValueGet("stock", llGetObjectDesc());
        do {
            if(llDetectedType(num) == (ACTIVE|AGENT))jump continue;
            list name = wasKeyValueDecode(llDetectedName(num));
            if(llGetListLength(name) != 2) jump continue;
            if(llList2String(name, 1) != (string)llGetCreator()) jump continue;
            if(llVecMag(llDetectedVel(num)) < .01) jump continue;
            if(llGetOwner() != llDetectedOwner(num)) jump continue;
            if(llList2String(name, 0) == "Maggie") {
                --charge;
                jump continue;
            }
            if(llList2String(name, 0) == "Paul") {
                ++charge;
                jump continue;
            }
@continue;
        } while(--num>-1);
        if(charge <= 0) {
            llRegionSay(channel, wasKeyValueEncode(["die", llGetObjectName(), "creator", llGetCreator()]));
            llSleep(1.1-llGetRegionTimeDilation());
            llDie();
        }
        if(charge < MAX_CHARGES) {
            llSetObjectDesc(wasKeyValueSet("stock", (string)charge, llGetObjectDesc()));
            llSetTimerEvent(1);
            return;
        }
        if(charge >= MAX_CHARGES) charge = MAX_CHARGES;
        llSetObjectDesc(wasKeyValueSet("stock", (string)charge, llGetObjectDesc()));
        llSetText(llGetObjectName() + "\n" + "Stock: " + wasProgress(100*charge/MAX_CHARGES, MAX_CHARGES/2, ["[", "█", "░", "]"]) + " : " + (string)charge + "/" + (string)MAX_CHARGES + "\n" + "Mode: " + llToUpper(wasKeyValueGet("mode", llGetObjectDesc())), wasPercentToGradient(100*charge/MAX_CHARGES, "rg"), .8);
        if(charge >= MAX_CHARGES) llSetTimerEvent(0);
 
    }
    touch_start(integer num) {
        press = 1;
    }
    touch(integer num) {
        if(press > 32) return;
        press*=2;
    }
    touch_end(integer num) {
        if(press > 32) { 
            mode = !mode;
            if(mode == FALSE) llSetObjectDesc(wasKeyValueSet("mode", "restricted", llGetObjectDesc()));
            if(mode == TRUE) llSetObjectDesc(wasKeyValueSet("mode", "open", llGetObjectDesc()));
            return;
        }
        if(llDetectedKey(0) != llGetOwner() && mode == FALSE) return;
        llRegionSay(channel, wasKeyValueEncode(["use", llGetObjectName(), "creator", llGetCreator(), "user", llDetectedKey(0)]));
        integer charge = (integer)wasKeyValueGet("stock", llGetObjectDesc())-1;
        if(charge <= 0) {
            llRegionSay(channel, wasKeyValueEncode(["die", llGetObjectName(), "creator", llGetCreator()]));
            llSleep(1.1-llGetRegionTimeDilation());
            llDie();
        }
        llSetObjectDesc(wasKeyValueSet("stock", (string)charge, llGetObjectDesc()));
        llSetTimerEvent(1);
    }
    on_rez(integer num) {
        if(num == 0 && llGetOwner() != llGetCreator()) llDie();
        channel = num;
        llListen(channel, "[WaS-K] Fishulus", "", "");
        llSetObjectDesc(wasKeyValueSet("channel", (string)channel, llGetObjectDesc()));
        MAX_CHARGES = (integer)wasKeyValueGet("charges", llGetObjectDesc());
        RECHARGE_SECONDS = (integer)wasKeyValueGet("recharge", llGetObjectDesc());
        if((integer)wasKeyValueGet("stock", llGetObjectDesc()) <= 0) 
            llSetObjectDesc(wasKeyValueSet("stock", "1", llGetObjectDesc()));
    }
    listen(integer channel, string name, key id, string message) {
        if(llGetOwner() != llGetOwnerKey(id)) return;
        if(wasKeyValueGet("die", message) != llGetCreator()) return;
        llRegionSay(channel, wasKeyValueEncode(["die", llGetObjectName(), "creator", llGetCreator()]));
        llDie();
    }
    timer() {
        integer charge = (integer)wasKeyValueGet("stock", llGetObjectDesc());
        if(++ticks <= RECHARGE_SECONDS) {
            llSetText(llGetObjectName() + "\n" + "Stock: " + wasProgress(100*charge/MAX_CHARGES, MAX_CHARGES/2, ["[", "█", "░", "]"]) + " : " + (string)charge + "/" + (string)MAX_CHARGES + "\n" + "ETA: " + wasProgress(100*ticks/RECHARGE_SECONDS, 5, ["[", "█", "░", "]"]) + " : " + (string)ticks + "/" + (string)RECHARGE_SECONDS + "s" + "\n" + "Mode: " + llToUpper(wasKeyValueGet("mode", llGetObjectDesc())), wasPercentToGradient(100*charge/MAX_CHARGES, "rg"), .8);
            return;
        }
        if(++charge >= MAX_CHARGES) charge = MAX_CHARGES;
        llSetObjectDesc(wasKeyValueSet("stock", (string)charge, llGetObjectDesc()));
        llSetText(llGetObjectName() + "\n" + "Stock: " + wasProgress(100*charge/MAX_CHARGES, MAX_CHARGES/2, ["[", "█", "░", "]"]) + " : " + (string)charge + "/" + (string)MAX_CHARGES + "\n" + "Mode: " + llToUpper(wasKeyValueGet("mode", llGetObjectDesc())), wasPercentToGradient(100*charge/MAX_CHARGES, "rg"), .8);
        if(charge >= MAX_CHARGES) llSetTimerEvent(0);
        ticks = 0;
    }
}