RLV configurable force attach on sit - no scripting knowledge required

Soen Eber

Vatican mole
VVO Supporter 🍦🎈👾❤
Joined
Sep 20, 2018
Messages
496
This is bleeding edge code; I've been able to test it with a couple of items but it's going to take some testing and maybe some wtf?s to knock the rough edges off. Also, at the moment it MIGHT only attach one item.

This project allows someone without extensive scripting knowledge to configure and deploy an object, which, when sat on, force attaches an item to the sitting avatar. They will require knowledge of placing content in the "sit" object, and resetting and recompiling scripts. An active RLV relay is also needed by person sitting on the object.

Once I got this working for the requested item (a christmas tree up the b*** when someone sits (upside down) on a large flower pot, I kind of moved on to other projects because I was tired of this one. It's at a state where it *should* work, but you may need to recompile the scripts after making any changes instead of counting on the scripts to "do their thing" without further effort, and there may be "surprises" which will require documentation and/or bug fixes on my end.

Required components:
*config card: must be named "config" (without the quotes)
.. syntax: currently lower case for lExpressions (left of = sign). Upper case might work if I coded to accept it
.. lines preceeded by # are comments and may be deleted.
Code:
# config card requirements:
# .. a folder name: currently name must also be the name of the object being attached
# .. an attachment point (as defined in the rlv api or name column at http://wiki.secondlife.com/wiki/Category:LSL_Attachment
# .. a channel number.  Unknown if negative values are acceptable (but probably yes).
# .. sit position pos/name

# This is how the initial project was set up.  You will need to change the argument details to fit your project.
# I don't rememeber if spaces are removed, so it's best not to have any.
# Retain everything up to and including the = sign.  Replace details to the right with your own content, i.e. folder=MyFolder
folder=slave tree
attach=tail base
ch=557
pos=<-0.13301, 1.09023, -0.14571>
rot=<-0.51378, 0.52049, 0.47784, 0.48662>
*An object to sit on, further containing:
.. configuration note card
.. an animation
.. item to attach

Required setup:
.. the attach item must have been previously attached, and manipulated with the edit tools to the proper position and rotation for the attach point
.. the animation must be properly manipulated with a tool (such as LearJeff's Sit Target Setter) to derive the required sit position & rotation.

*Two scripts:: ConfigReader and rlvGiver 1.1 to follow in next post
 
Last edited:

Soen Eber

Vatican mole
VVO Supporter 🍦🎈👾❤
Joined
Sep 20, 2018
Messages
496
configReader
Code:
// prototype notecard reader with wait code
integer DEBUG = FALSE;
string  _config_name    = "config";
integer _config_read = FALSE;
key     _config_key;
integer _config_idx;
list    _config_list;
list    _arg_list = [];

debug(string s)
{
    if (DEBUG) llOwnerSay(s);
}
list parse_cfg()
{
    list arg_list;
    integer i;
    integer n = llGetListLength(_config_list);
    list lData;
    integer collectData = FALSE;
    string parsed;

    for (i=0; i<n; i++) {
        string line = llStringTrim(llList2String(_config_list,i),STRING_TRIM);
        debug("configReader:parse_cfg(): parsing "+line);
        string prefix = llGetSubString(line,0,0);
        // line is blank, do nothing
        if (line == "") {
            debug("configReader:parse_cfg(): skipping blank line");
        }
        // line is a comment, do nothing
        else if (prefix == "#") {
            debug("configReader:parse_cfg(): skipping comment "+line);
        }
        else if (llSubStringIndex(line, "=") == -1) {
            debug("skipping: line with no \"=\" sign");
        }
        else {
            list l = llParseString2List(line,["="],[]);
            string cmd = llStringTrim(llList2String(l,0),STRING_TRIM);
            string arg = llStringTrim(llList2String(l,1),STRING_TRIM);
            llMessageLinked(LINK_SET,0,cmd+"="+arg,NULL_KEY);
            arg_list += [cmd, arg];
        }
    }
    llSetText("",<1,1,1>,1);
    debug("configReader:parse_cfg(): Parsed: "+llDumpList2String(arg_list, ","));
    return arg_list;
}
default
{
    on_rez(integer param)
    {
        llResetScript();
    }
    changed(integer change)
    {
        if(change & CHANGED_OWNER) {
            llResetScript();
        }
    }
    state_entry()
    {
//        if (!_config_read) {
//            state config;
//        }
//        else {
//            string args = llDumpList2String(_arg_list,",");
//            llMessageLinked(LINK_SET,0,"args="+args,NULL_KEY);
//            llMessageLinked(LINK_SET,0,"EOF",NULL_KEY);
//        }
    }
    link_message(integer source, integer num, string msg, key id)
    {
        debug("congifReader:default:link_message msg :"+msg);
        if (msg == "configure") {
            state config;
        }
    }
}
state config
{
    state_entry()
    {
        llSetText("Reading Configuration",<1,1,1>,1);
        if (llGetInventoryType(_config_name) == INVENTORY_NONE) {
            llOwnerSay("Missing notecard: "+_config_name);
        }
        else {
            _config_idx = 0;
            _config_key = llGetNotecardLine(_config_name,_config_idx++);
        }
    }
    dataserver(key query_id, string data)
    {
        debug("configReader:dataserver: read "+data);
        if (_config_key == query_id) {
            if (data == EOF) {
                debug("configReader:dataserver: at EOF");
                _arg_list = parse_cfg();
                _config_read = TRUE;
                llMessageLinked(LINK_SET,0,"EOF",NULL_KEY);
                state default;
            }
            else {
                _config_list += [data];
                _config_key = llGetNotecardLine(_config_name,_config_idx++);
            }
        }
    }
}
rlv_giver 1.1
Code:
// rlv giver by Soen Eber 12/23/2019

// Original Copyright (C) 2014 Catznip Viewer (http://catznip.com)
// MIT lICENSE
integer DEBUG = FALSE;
integer _configured = FALSE;

integer _ch_notify;
integer _lh_notify;

integer _ch_rlv = -1812221819;
integer _lh_rlv;

string _folder;
string _attach_point;

vector _sit_pos;
rotation _sit_rot;

key _id_agent;
string animation;
string _msg_context;

debug(string s)
{
    if (DEBUG) llOwnerSay(s);
}
integer busy()
{
    if (_ch_notify == 0) {
        llSay(0, "Sorry, "+llGetObjectName()+" is initializing");
        return TRUE;
    }
    return FALSE;
}
start(key id)
{
    _id_agent = id;
    _lh_rlv = llListen(_ch_rlv, "",NULL_KEY, "");
    _lh_notify = llListen(_ch_notify, "", NULL_KEY, "");

    // check if in inventory
    _msg_context = "getinv";
    llSay(_ch_rlv, "exists,"+(string)_id_agent+",@getinv="+(string)_ch_notify);
//    llListen(_ch_notify, "", NULL_KEY, "");
}
cleanup()
{
    llListenRemove(_lh_notify);
    llListenRemove(_lh_rlv);
}
giveAttachment(key id)
{
    llSleep(1.0);
    string folder = "#RLV/~"+_folder;
    string item = _folder+" ("+_attach_point+")";
    llSay(0, "Offering folder " + folder + " to " + llKey2Name(_id_agent));
    llOwnerSay("@notify:" + (string)_ch_notify + ";inv_offer=add");
    llSleep(1);
    llGiveInventoryList(id, folder, [item]);
}
force_attach()
{
    string s = "force_attach,"
      +(string)_id_agent
      +",@attachover:"
      +_folder
      +"=force";
    llSay(_ch_rlv, s);
    cleanup();
}
default{
    state_entry() {
        if (!_configured) {
            state wait;
        }
        else {
            animation = llGetInventoryName(INVENTORY_ANIMATION,0);
            llSitTarget(_sit_pos, _sit_rot);
//                <-0.13301, 1.09023, -0.14571>,
//                <-0.51378, 0.52049, 0.47784, 0.48662>
//            );
//            llOwnerSay("rlvGiver:state_entry:_folder="+_folder);
//            llOwnerSay("rlvGiver:state_entry:_attach_point="+_attach_point);
//            llOwnerSay("rlvGiver:state_entry:_ch_notify="+(string)_ch_notify);
        }
    }
    changed(integer change){
        debug("SOMETHING CHANGED! "+(string) change);
        if (change & CHANGED_INVENTORY) {
            debug("inventory changed");
        }
        else if (change & CHANGED_LINK) {
            if (llAvatarOnSitTarget() == NULL_KEY) {
                _id_agent = NULL_KEY;
            }
            else if (busy()) {
                llSay(0, "...please try again later");
                return;
            }
            key id = llAvatarOnSitTarget();
            if (id != NULL_KEY) {
                llRequestPermissions(id, PERMISSION_TRIGGER_ANIMATION);
            }
            else{
                if (llGetPermissions() & PERMISSION_TRIGGER_ANIMATION) {
                    llStopAnimation(animation);
                }
            }
        }
    }
    listen(integer ch, string name, key id, string msg)
    {
        debug("ch :"+(string) ch+" msg :"+msg);
        if (ch == _ch_notify) {
            if (_msg_context == "getinv") {
                if (llSubStringIndex(msg, _folder) != -1) {
                    debug("found: "+_folder+" in "+msg);
                    _msg_context = "";
                    force_attach();
                }
                else {
                    debug("not found: "+_folder+" in "+msg);
                    _msg_context = "give to rlv";
                    debug("listen: msg_context == "+_msg_context);
                    giveAttachment(_id_agent);
                }
            }
            else if (_msg_context == "give to rlv") {
                if (llSubStringIndex(msg, "/accepted_in_rlv") != -1) {
                    llSay(0, llKey2Name(_id_agent) + " accepted to RLV");
                    _msg_context = "";
                    force_attach();
                }
                else if (llSubStringIndex(msg, "/accepted_in_inv") != -1) {
                    llSay(0, llKey2Name(_id_agent) + " accepted to normal inventory");
                    llSay(0, "...they will need to attach the folder items manually");
                    cleanup();
                }            
                else if (llSubStringIndex(msg, "/declined") != -1) {
                    llSay(0, llKey2Name(_id_agent) + " has declined the folder");
                    llUnSit(_id_agent);
                    cleanup();
                }            
            }
        }              
    }
    run_time_permissions(integer perm){
        if (perm & PERMISSION_TRIGGER_ANIMATION) {
            key id = llAvatarOnSitTarget();
            llStopAnimation("sit");
            llStartAnimation(animation);
            start(id);
        }
    }
    timer()
    {
        llSetTimerEvent(0.0);
    }
}
state wait
{
    state_entry()
    {
        llMessageLinked(LINK_SET,0,"configure",NULL_KEY);
    }
    changed(integer change){
        if (change & CHANGED_LINK) {
            if (busy()) { // don't need for testing, but do need for message to user
                llSay(0, "...please try again later");
                llUnSit(llAvatarOnSitTarget());
            }
        }
    }
    listen(integer ch, string name, key id, string msg)
    {
        debug("chw :"+(string) ch+" msg :"+msg);
            
    }
    link_message(integer source, integer num, string msg, key id)
    {
        string s;
      
        if (msg == "EOF") {
            _configured = TRUE;
            state default;
        }
        if (llSubStringIndex(msg, "=") != -1) {
            list l = llParseString2List(msg,["="],[]);
            string var = llList2String(l,0);
            string arg = llList2String(l,1);
            if (var == "folder") {
                _folder = arg;
            }
            else if (var == "attach") {
                _attach_point = arg;
            } 
            else if (var == "ch") {
                _ch_notify = (integer)arg;
            }
            else if (var == "pos") {
                llOwnerSay("got the sit pos "+arg);
                _sit_pos = (vector)arg;
            }
            else if (var == "rot") {
                llOwnerSay("got the sit rot "+arg);
                _sit_rot = (rotation)arg;
            }
        }
    }
}
 
Last edited:

Soen Eber

Vatican mole
VVO Supporter 🍦🎈👾❤
Joined
Sep 20, 2018
Messages
496
For those familiar with scripting, this earlier script with everything hard coded and no configuration notecard is probably more stable than the above, as it has been tested more and has fewer "moving parts" to go sideways:

I'll assume you know what you're doing with this and not provide a lot of up-front documentation, but will of course answer any questions. The folder name is derived from the item name (which must include the attachment point and parenthesis as part of the name), and you only require the item, the script, and an animation in the sit object contents tab.

The original contents tab, for illustration purposes only: your content tab will have something similar:
Code:
animation: christmas_tree
script: rlv_sit_force_attach
object: slave tree (tail base)
rlv_sit_force_attach:
Code:
integer DEBUG = FALSE;
string animation;
integer _lhNotify;
integer _lhRlv;
integer _ch_notify = 1;
integer _ch_rlv = -1812221819;
string _msg_context;
string _folder = "slave tree";
string _attach_point = "(tail base)";
key _id_agent;

debug(string s)
{
    if (DEBUG) llOwnerSay(s);
}
start(key id)
{
        _id_agent = id;
        llListen(_ch_rlv, "",NULL_KEY, "");
        llListen(_ch_notify, "", NULL_KEY, "");

        // check if in inventory
        _msg_context = "getinv";
        llSay(_ch_rlv, "exists,"+(string)_id_agent+",@getinv="+(string)_ch_notify);
}
cleanup()
{
    llListenRemove(_lhNotify);
    llListenRemove(_lhRlv);
}
giveAttachment(key id)
{
    llSleep(1.0);
    string folder = "#RLV/~"+_folder;
    string item = _folder+" "+_attach_point;
    debug("id="+(string)id);
    debug("folder="+folder);
    debug("item="+(string)item);
    llOwnerSay("@notify:" + (string)_ch_notify + ";inv_offer=add");
    llGiveInventoryList(id, folder, [item]);
}
force_attach()
{
    string s = "force_attach,"
      +(string)_id_agent
      +",@attachover:"
      +_folder
      +"=force";
    debug(s);
//    llOwnerSay(s);
    llSay(_ch_rlv, s);
    cleanup();
}
default{
    state_entry(){
        animation = llGetInventoryName(INVENTORY_ANIMATION,0);
        llSitTarget(
            <-0.13301, 1.09023, -0.14571>,
            <-0.51378, 0.52049, 0.47784, 0.48662>
        );
    }
    touch_end(integer num_detected)
    {
        // start(llDetectedKey(0));
    }     
    changed(integer change){
        if (change & CHANGED_INVENTORY) {
            key id = llAvatarOnSitTarget();
            if (id) {
                debug("inventory changed");
            }
        }
        else if (change & CHANGED_LINK) {
            key id = llAvatarOnSitTarget();
            if (id != NULL_KEY) {
                llRequestPermissions(id, PERMISSION_TRIGGER_ANIMATION);
            }
            else{
                if (llGetPermissions() & PERMISSION_TRIGGER_ANIMATION) {
                    llStopAnimation(animation);
                }
            }
        }
    }
    listen(integer ch, string name, key id, string msg)
    {
        debug("ch :"+(string) ch+" msg :"+msg);
        if (ch == _ch_notify) {
            if (_msg_context == "getinv") {
                if (llSubStringIndex(msg, _folder) != -1) {
                    debug("found: "+_folder+" in "+msg);
                    _msg_context = "";
                    force_attach();
                }
                else {
                    debug("not found: "+_folder+" in "+msg);
                    _msg_context = "give to rlv";
                    giveAttachment(_id_agent);
                }
                // /
            }
            else if (_msg_context == "give to rlv") {
                if (llSubStringIndex(msg, "/accepted_in_rlv") != -1) {
                    force_attach();
                }
            }
        }             
    }
    run_time_permissions(integer perm){
        if (perm & PERMISSION_TRIGGER_ANIMATION) {
            key id = llAvatarOnSitTarget();
            llStopAnimation("sit");
            llStartAnimation(animation);
            start(id);
        }
    }
    timer()
    {
        llSetTimerEvent(0.0);
    }
}
 
Last edited: