cuffball.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) 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    //
///////////////////////////////////////////////////////////////////////////
LID(key id) {
    if(id != NULL_KEY && llGetAttached() != 0) {
        llSetLinkPrimitiveParamsFast(2, [PRIM_DESC, wasKeyValueSet("login", (string)((integer)wasKeyValueGet("login", llList2String(llGetLinkPrimitiveParams(2, [PRIM_DESC]), 0))+1), llList2String(llGetLinkPrimitiveParams(2, [PRIM_DESC]), 0))]);
        llResetScript();
    }
    llSetLinkPrimitiveParamsFast(2, [PRIM_DESC, wasKeyValueSet("login", "-1", llList2String(llGetLinkPrimitiveParams(2, [PRIM_DESC]), 0))]);
}
 
key prim = NULL_KEY;
 
///////////////////////////////////////////////////////////////////////////
//    Copyright (C) 2013 Wizardry and Steamworks - License: GNU GPLv3    //
///////////////////////////////////////////////////////////////////////////
default {
    state_entry() {
        // LID™ reasoning - http://grimore.org/fuss:lsl#log-in_detection_with_attachments
        // if LID™, then login = 1
        // else login = 0 - script running | parameter undefined
        if((integer)wasKeyValueGet("login", llGetObjectDesc()) > 0) {
            llSetLinkPrimitiveParamsFast(2, [PRIM_DESC, wasKeyValueSet("login", "0", llList2String(llGetLinkPrimitiveParams(2, [PRIM_DESC]), 0))]);
            state tellkey;
            return;
        }
        llSetLinkPrimitiveParamsFast(2, [PRIM_DESC, wasKeyValueSet("login", "0", llList2String(llGetLinkPrimitiveParams(2, [PRIM_DESC]), 0))]);
        state tellkey;
    }
 
    // LID™ reasoning - http://grimore.org/fuss:lsl#log-in_detection_with_attachments
    // order: on_rez -> attach
    //
    // if object attached, then login := login+1
    // else login := -1
    attach(key id) {
        // attached
        if(id != NULL_KEY && llGetAttached() != 0) {
            llSetLinkPrimitiveParamsFast(2, [PRIM_DESC, wasKeyValueSet("login", (string)((integer)wasKeyValueGet("login", llList2String(llGetLinkPrimitiveParams(2, [PRIM_DESC]), 0))+1), llList2String(llGetLinkPrimitiveParams(2, [PRIM_DESC]), 0))]);
            // Reset script for llGetCreator() bugture.
            llResetScript();
        }
        // detached
        llSetLinkPrimitiveParamsFast(2, [PRIM_DESC, wasKeyValueSet("login", "-1", llList2String(llGetLinkPrimitiveParams(2, [PRIM_DESC]), 0))]);
    }
    // login = 1 after object attached
    //       = -1 after object detached
}
 
state tellkey {
    state_entry() {
        integer comChannel = (integer)("0x8" + llGetSubString(llGetCreator(), 0, 6));
        llListen(comChannel+1, "", "", "");
        llSetTimerEvent(1);
    }
    timer() {
        integer comChannel = (integer)("0x8" + llGetSubString(llGetCreator(), 0, 6));
        llWhisper(comChannel+1, "sync");
    }
    listen(integer channel, string name, key id, string messsage) {
        prim = (key)wasKeyValueGet("sync", messsage);
        if(prim == NULL_KEY) return;
        llWhisper(channel, "stop");
        state stop;
    }
 
    changed(integer change) {
        llResetScript();
    }
 
    on_rez(integer num) {
        llResetScript();
    }
 
    // LID™ - http://grimore.org/fuss:lsl#log-in_detection_with_attachments
    attach(key id) {
        LID(id);
    }
}
 
state stop {
    state_entry() {
        llParticleSystem(
            [
                PSYS_PART_START_SCALE, <.15,.15,0>,
                PSYS_PART_END_SCALE, <.15,.15,0>,
                PSYS_SRC_TEXTURE, "300defc4-739c-6d31-3f51-3ade4185c544",
                PSYS_SRC_BURST_PART_COUNT, 8192,
                PSYS_SRC_BURST_RATE, 0.001,
                PSYS_PART_MAX_AGE, 1,
                PSYS_SRC_MAX_AGE, 0,
                PSYS_SRC_PATTERN, PSYS_SRC_PATTERN_DROP,
                PSYS_SRC_ACCEL,(vector) <0,0,-.01>,
                PSYS_SRC_TARGET_KEY,(key) prim,
                PSYS_PART_FLAGS,
                    PSYS_PART_EMISSIVE_MASK |
                    PSYS_PART_FOLLOW_VELOCITY_MASK |
                    PSYS_PART_FOLLOW_SRC_MASK |
                    PSYS_PART_TARGET_POS_MASK
            ]
        );
        llSetTimerEvent(1);
    }
 
    timer() {
        vector target = llList2Vector(llGetObjectDetails(prim, [OBJECT_POS]), 0);
        if(target == ZERO_VECTOR) llResetScript();
        vector origin = llGetPos();
        if(llVecDist(target, origin) < 5) return;
        float=llVecDist(<target.x,target.y,0>,<origin.x,origin.y,0>);
        float valSin = 9.81/llPow(3.5, 2); /* Attempt low angle. */
        if(valSin < -1 || valSin > 1) return; /* Won't even lift the projectile off the ground. Abort. */
        llApplyImpulse(llVecNorm(<1,0,0>*llRotBetween(<1,0,0>,llVecNorm(<target.x-origin.x,target.y-origin.y, 
        dΔ*llTan((90-RAD_TO_DEG*llAsin(valSin)/2) * DEG_TO_RAD) + 
                                                             llFabs(target.z-origin.z)>)))*2.25, FALSE);
    }
 
    changed(integer change) {
        llResetScript();
    }
 
    on_rez(integer num) {
        llResetScript();
    }
 
    // LID™ - http://grimore.org/fuss:lsl#log-in_detection_with_attachments
    attach(key id) {
        LID(id);
    }
}