Quick Collar

From Second Life Wiki
Jump to: navigation, search
KBnote.png Note: Due to licensing issues, all contributions to this wiki have stopped and the articles that we posted are just being maintained. This is one of the projects that has gone further and for updates you are cordially invited to the project page on our wiki.

ChangeLog

06-03-2012

llTakeControls(0, FALSE, FALSE);

should be able to lock an avatar down.

4 March 2012

  • Bugfixes.
  • Added leash and lock options.

Motivation

This is a typical RLV collar I made to filter out some stuff which I consider nonsense such as teleport lures, instant message blocking, sending off data to an out-of-world database, and other stuff which is not really necessary for some fun. I wanted a simple collar that would give me access to clothes, sitting, unsitting, moving the avatar around, perhaps, at best locking them into place and releasing them but without infringing privacy or being excessively bulky - for example, I would never need animations from the collar itself because there are plenty (much better ones) in-world. I also wanted to make this script compatible with both OpenSIM and SecondLife so I had to refrain from my disgusting LSL jump habits and programming shorthands.

Technically, the script is really a variation of the endless menus implemented in Giver and hacked down to allow commands instead of handing out items. It seems that the Giver script is really flexible: what I do is clear the menu items every time a new menu has to be generated, then I add the next and back buttons and finally show the menu via a dialog. All the magic is really contained in the menu generation; the rest is just RLV viewer commands.

Features

  • Removing both wearables and attachments on the wearer.
  • Sitting and unsitting the wearer on objects in the vicinity.
  • Moving the wearer about, to the one controling the avatar or to objects in the vicinity.
  • Locking the movement of the avatar (like "stay") and then being able to release them.

Setup

By default, the collar reacts only to the owner if RLV is turned on. There is also a switch accessible to the one controlling as well as the owner called FFA which enables anybody to control the wearer of the collar. By default FFA is turned off and just the wearer and the controllers in the Masters notecard are able to manipulate the collar.

To set up:

  • Make sure you have RLV enabled. If proceed with the instructions below and notice some strange chat on the main channel, then you probably have not enabled RLV: In most viewers it is under the Advanced (ctrl+alt+D), called something like "Restricted Love". Usually you will have to restart the viewer once you enable or disable it.
  • [Optional] Create a notecard called "Masters" and add to it anybody who will have access to your collar line-by-line. For example, to add Darth Vader and Luke Skywalker to the collar, your Masters notecard would look like this:
Darth Vader
Luke Skywalker
  • Drop the Masters notecard into any attachment - it can be a collar, it can be an armband, it can be a ring or whatever else...
  • Copy the script below and drop it into the same attachment where you put the notecard.

That's it. Depending on your needs of the moment, you do not necessarily need the notecard Masters, hence being optional. You can click your collar and select "FFA: [ ]". This will toggle FFA on (when you click the collar again, the FFA will show a checkmark, indicating that it is on) and will allow anybody to control you.

TODO

  • Add a HUD for other people to control the wearer without having to click the collar itself.

Code

//////////////////////////////////////////////////////////
// [K] Kira Komarov - 2011, License: GPLv3              //
// Please see: http://www.gnu.org/licenses/gpl.html     //
// for legal details, rights of fair usage and          //
// the disclaimer and warranty conditions.              //
//////////////////////////////////////////////////////////
 
list _menuItems = [];
list _objectKeys = [];
list _mList = [];
key _aMaster = NULL_KEY;
integer _aMoveTarget = 0;
string _scanType = "";
string _FFA = "FFA: [✖]";
string _LOCK = "LOCK: [✔]";
key _leashedKey = NULL_KEY;
integer _mItra = 0;
integer _comChannel = 0;
integer _comHandle = 0;
list _cmList = [];
 
list mFwd() {
    if(_mItra+1>llGetListLength(_menuItems) || llGetListLength(_menuItems)==10) return _cmList;
    _cmList = llListInsertList(llListInsertList(llList2List(_menuItems, ++_mItra, (_mItra+=9)), 
      ["<= Back"], 0), ["Next =>"], 2);
    return _cmList;
} 
 
list mBwd() {
    if(_mItra-19<0) return _cmList;
    _cmList = llListInsertList(llListInsertList(llList2List(_menuItems, (_mItra-=19), (_mItra+=9)), 
      ["<= Back"], 0), ["Next =>"], 2);
    return _cmList;
}
 
//pragma inline
integer FPSrand(integer max) {
    integer r = (integer)(llGetRegionFPS() * 10000000.0) % max;
    if(max > 0) return r; else return -r;
}
 
//pragma inline
readMasters() {
    _mList = [];
    _mItra = llGetInventoryNumber(INVENTORY_NOTECARD)-1;
    do {
        if(llGetInventoryName(INVENTORY_NOTECARD, _mItra) == "Masters")
            jump found;
    } while(--_mItra>=0);
    llOwnerSay("No Masters notecard found, skipping...");
    return;
@found;
    _aMaster = llGetNotecardLine("Masters", _comHandle);
}
 
//pragma inline
moveTo(vector target, integer bb) {
    llStopMoveToTarget();
    llTargetRemove(_aMoveTarget);
    vector pointTo = target - llGetPos();
    llOwnerSay("@setrot:" + (string)llAtan2(pointTo.x, pointTo.y) + "=force");
    _aMoveTarget = llTarget(target, 2 + bb);
    llMoveToTarget(target, 2);
}
 
//pragma inline
stopMove() {
    llSensorRemove();
    llStopMoveToTarget();
    llTargetRemove(_aMoveTarget);
    _leashedKey = NULL_KEY;
    llParticleSystem([]);
}
 
default
{
    state_entry() {
        if(_scanType == "clothes") {
            _comHandle = llListen(_comChannel+1, "", _aMaster, "");
            return;
        }
        if(_scanType == "attachments") {
            _comHandle = llListen(_comChannel+2, "", _aMaster, "");
            return;
        }
    }
 
    on_rez(integer param) {
        llOwnerSay("@detach=n");
        readMasters();
    }
 
    sensor(integer num_detected) {
        llListenRemove(_comHandle);
 
        _menuItems = [];
        _objectKeys = [];
        _mItra = num_detected-1;
        do {
            _menuItems += llGetSubString(llDetectedName(_mItra), 0, 23);
            _objectKeys += llDetectedKey(_mItra);
        } while(--_mItra>=0);
 
        _cmList = llListInsertList(llListInsertList(llList2List(_menuItems, _mItra=0, (_mItra+=9)), ["<= Back"], 0), ["Next =>"], 2);
 
        if(_scanType == "sit") {
            _comHandle = llListen(_comChannel+3, "", _aMaster, "");
            llDialog(_aMaster, "Please select an object to sit the wearer on:\n", _cmList, _comChannel+3);
            return;
        }
 
        if(_scanType == "go") {
            _comHandle = llListen(_comChannel+4, "", _aMaster, "");
            llDialog(_aMaster, "Please select the destination to send the wearer to:\n", _cmList, _comChannel+4);
            return;
        }
 
        if(_scanType == "leash") {
            _comHandle = llListen(_comChannel+5, "", _aMaster, "");
            llDialog(_aMaster, "Please select the object or avatar to leash the wearer to:\n", _cmList, _comChannel+5);
            return;
        }
    }
 
    at_target( integer tnum, vector targetpos, vector ourpos ) {
        if(tnum != _aMoveTarget) return;
        llStopMoveToTarget();
        vector pointTo = targetpos - llGetPos();
        llOwnerSay("@setrot:" + (string)llAtan2(pointTo.x, pointTo.y) + "=force");
        llTargetRemove(_aMoveTarget);
    }
 
    run_time_permissions(integer perm)
    {
        if (perm & PERMISSION_TAKE_CONTROLS)
        {
            llTakeControls(0, FALSE, FALSE);
            llOwnerSay(llKey2Name(_aMaster) + " has made you stay.");
        }
    }
 
    listen(integer channel, string name, key id, string message) {
 
         if(message == "<= Back") {
            _aMaster = id;
            llSetTimerEvent(60);
            llDialog(id, "Please browse the available items:\n", mBwd(), channel);
            return;
        }
 
        if(message == "Next =>") {
            _aMaster = id;
            llSetTimerEvent(60);
            llDialog(id, "Please browse the available items:\n", mFwd(), channel);
            return;
        }
 
        if(message == "FFA: [✖]" || message == "FFA: [✔]") {
            llListenRemove(_comHandle);
            if(_FFA == "FFA: [✖]") {
                _FFA = "FFA: [✔]";
                return;
            }
            if(_FFA == "FFA: [✔]") {
                _FFA = "FFA: [✖]";
                return;
            }
        }
 
        if(message == "LOCK: [✖]" || message == "LOCK: [✔]") {
            llListenRemove(_comHandle);
            if(_LOCK == "LOCK: [✖]") {
                llOwnerSay("@detach=n");
                _LOCK = "LOCK: [✔]";
                return;
            }
            if(_LOCK == "LOCK: [✔]") {
                llOwnerSay("@detach=y");
                _LOCK = "LOCK: [✖]";
                return;
            }
        }
 
        if(message == "[ Sits ]" ) {
            _aMaster = id;
            llSetTimerEvent(60);
            llDialog(id, "Please select whether to sit or unsit:\n", ["Sit", "Unsit"], _comChannel);
            return;
        }
 
        if(message == "Unsit") {
            llListenRemove(_comHandle);
            llOwnerSay("@unsit=force");
            return;
        }
 
        if(message == "Sit") {
            llListenRemove(_comHandle);
            llInstantMessage(id, "Scanning for nearby objects, please wait...");
            _aMaster = id;
            _scanType = "sit";
            llSensor("", "", (PASSIVE | ACTIVE), 10.0, PI);
            return;        
        }
 
        if(message == "[ Moves ]") {
            _aMaster = id;
            llSetTimerEvent(60);
            llDialog(id, "Please select the desired movement:\n", ["Go To", "Stay", "UnStay"], _comChannel);
            return;
        }
 
        if(message == "UnStay") {
            llListenRemove(_comHandle);
            llReleaseControls();
            llOwnerSay(llKey2Name(id) + " has allowed you to move freely...");
            return;
        }
 
        if(message == "Stay") {
            llListenRemove(_comHandle);
            _aMaster = id;
            llRequestPermissions(llGetOwner(), PERMISSION_TAKE_CONTROLS);
            return;
        }
 
        if(message == "Go To") {
            llListenRemove(_comHandle);
            llInstantMessage(id, "Scanning for nearby targets, please wait...");
            _aMaster = id;
            _scanType = "go";
            llSensor("", "", (AGENT | PASSIVE | ACTIVE), 10.0, PI);
            return;
        }
 
        if(message == "[ Clothes ]" ) {
            _aMaster = id;
            llSetTimerEvent(60);
            llDialog(id, "Please select the type of clothing to remove:\n", ["Wearables", "Attachments"], _comChannel);
            return;
        }
 
        if(message == "Wearables") {
            _aMaster = id;
            state show_wearables;
            return;
        }
 
        if(message == "Attachments") {
            _aMaster = id;
            state show_attachments;
            return;
        }
 
        if(message == "[ Leash ]" ) {
            _aMaster = id;
            llSetTimerEvent(60);
            llDialog(id, "Please select whether to leash or unleash the wearer:\n", ["Leash", "Unleash"], _comChannel);
            return;
        }
 
        if(message == "Leash" ) {
            llListenRemove(_comHandle);
            llInstantMessage(id, "Scanning for nearby objects, please wait...");
            _aMaster = id;
            _scanType = "leash";
            llSensor("", "", (AGENT | PASSIVE | ACTIVE), 10.0, PI);
            return;    
        }
 
        if(message == "Unleash" ) {
            llListenRemove(_comHandle);
            llSensorRemove();
            llStopMoveToTarget();
            llTargetRemove(_aMoveTarget);
            _leashedKey = NULL_KEY;
            llParticleSystem([]);
            return;    
        }
 
        if(channel == _comChannel + 1 && message != "Next =>" && message != "<= Back") {
            llListenRemove(_comHandle);
            llOwnerSay("@remoutfit:" + message + "=force");
            return;
        }
 
        if(channel == _comChannel + 2 && message != "Next =>" && message != "<= Back") {
            llListenRemove(_comHandle);
            llOwnerSay("@detach:" + message + "=force");
            return;
        }
 
        if(channel == _comChannel + 3 && message != "Next =>" && message != "<= Back") {
            llListenRemove(_comHandle);
            integer idx = llGetListLength(_menuItems)-1;
            do {
                if(~llSubStringIndex(llList2String(_menuItems, idx), message))
                jump rlvsit;
            } while(--idx>=0);
            return;
@rlvsit;
            llOwnerSay("@sit:" + llList2String(_objectKeys, idx) + "=force");
            return;
        }
 
        if(channel == _comChannel + 4 && message != "Next =>" && message != "<= Back") {
            llListenRemove(_comHandle);
            integer idx = llGetListLength(_menuItems)-1;
            do {
                if(~llSubStringIndex(llList2String(_menuItems, idx), message))
                jump rlvmove;
            } while(--idx>=0);
            return;
@rlvmove;
            llParticleSystem([]);
            llSensorRemove();
            _leashedKey = NULL_KEY;
            vector target = llList2Vector(llGetObjectDetails(llList2String(_objectKeys, idx), [OBJECT_POS]), 0);
            integer bb = (integer)(llVecDist(llList2Vector(llGetBoundingBox(llList2String(_objectKeys, idx)), 1), llList2Vector(llGetBoundingBox(llList2String(_objectKeys, idx)), 0))/2.0);
            moveTo(target, bb);
            return;
        }
 
        if(channel == _comChannel + 5 && message != "Next =>" && message != "<= Back") {
            llListenRemove(_comHandle);
            integer idx = llGetListLength(_menuItems)-1;
            do {
                if(~llSubStringIndex(llList2String(_menuItems, idx), message))
                jump rlvleash;
            } while(--idx>=0);
            return;
@rlvleash;
            _leashedKey = llList2Key(_objectKeys, idx);
            llSensorRepeat("", NULL_KEY, AGENT, 0.1, 0, 1.2-llGetRegionTimeDilation());
            llParticleSystem([12,"0560a0ad-ee6e-9a87-6a27-9322bad689ce",5,<3.0e-2,3.0e-2,3.0e-2>,6,<5.0e-2,5.0e-2,5.0e-2>,1,<1.0,1.0,1.0>,3,<1.0,1.0,1.0>,2,((float)1.0),4,((float)1.0),15,((integer)10),13,((float)1.0e-3),7,((float)10.0),9,((integer)1),8,<0.0,0.0,-0.1>,0,((integer)355),20,_leashedKey]);
        }
    }
 
    no_sensor() {
        if(_leashedKey) {
            vector target = llList2Vector(llGetObjectDetails(_leashedKey, [OBJECT_POS]), 0);
            integer distance = (integer)llVecDist(target, llGetPos());
            if(distance > 5 && distance < 65) {
                integer bb = (integer)(llVecDist(llList2Vector(llGetBoundingBox(_leashedKey), 1), llList2Vector(llGetBoundingBox(_leashedKey), 0))/2.0);
                moveTo(target, bb);
                return;
            }
            if(distance >= 65) {
                stopMove();
                return;
            }
        }
    }
 
    timer() {
        llSetTimerEvent(0);
        llInstantMessage(_aMaster, "Menu timed out...");
        _aMaster = NULL_KEY;
        llListenRemove(_comHandle);
    }
 
    touch_start(integer total_number)
    {
        //if((llDetectedKey(0) != llGetOwner() && llListFindList(_mList, (list)llDetectedName(0)) == -1) || _FFA != "FFA: [✔]") return;
        _comChannel = ((integer)("0x"+llGetSubString((string)llGetKey(),-8,-1)) & 0x3FFFFFFF) ^ 0xBFFFFFFF;
        _aMaster = llDetectedKey(0);
        _comHandle = llListen(_comChannel, llDetectedName(0), _aMaster, "");
        llSetTimerEvent(60);
        llDialog(_aMaster, "Please select from the following options:\n\nClothes - will offer a menu for stripping the wearer of the collar.\nSits - will allow you to sit the wearer of the collar on objects.\nMoves - will enable you to move the wearer of the collar around.\nLeash - will offer a menu to leash the wearer to an object or avatar.\n\nFFA - if this is on, it will allow other people to use the collar menu.\nLOCK - will make sure that the collar cannot be detached (for example, for outfit changes).", ["[ Clothes ]", "[ Sits ]", "[ Moves ]", "[ Leash ]", _FFA, _LOCK], _comChannel);
    }
 
    changed(integer change) {
        if(change & CHANGED_INVENTORY) {
            readMasters();
            return;
        }
        if(change & CHANGED_REGION) {
            stopMove();
            return;
        }
    }
 
    dataserver(key query_id, string data) {
        if(query_id != _aMaster) return;
        if(data == EOF) {
            llOwnerSay("Masters notecard loaded...");
            return;
        }
        if(data == "") jump continue;
        _mList += (list) data;
@continue;
        _aMaster = llGetNotecardLine("Masters", ++_comHandle);
    }
}
 
state show_wearables
{
    state_entry() {
        _mItra = 1+FPSrand(99);
        llListen(_mItra, "", llGetOwner(), "");
        llOwnerSay("@getoutfit=" + (string)_mItra);
    }
    listen(integer channel,string name,key id,string message) {
        list CLOTHES = [ "gloves","jacket","pants","shirt","shoes","skirt","socks","underpants","undershirt","skin","eyes","hair","shape", "alpha", "tattoo", "physics" ];
        _menuItems = [];
        _mItra = llStringLength(message)-1;
        do {
            if((integer)llGetSubString(message, _mItra, _mItra))
                _menuItems += llList2String(CLOTHES, _mItra);
        } while(--_mItra>=0);
        CLOTHES = [];
        _cmList = llListInsertList(llListInsertList(llList2List(_menuItems, _mItra=0, (_mItra+=9)), ["<= Back"], 0), ["Next =>"], 2);
        _scanType = "clothes";
        llDialog(_aMaster, "Please select the clothes to take off:\n", _cmList, _comChannel+1);
        state default;
    }
}
 
state show_attachments
{
    state_entry() {
        _mItra = 1+FPSrand(99);
        llListen(_mItra, "", llGetOwner(), "");
        llOwnerSay("@getattach=" + (string)_mItra);
    }
    listen(integer channel,string name,key id,string message) {
        list ATTACHMENTS = [ "none","chest","skull","left shoulder","right shoulder","left hand","right hand","left foot","right foot","spine","pelvis","mouth","chin","left ear","right ear","left eyeball","right eyeball","nose","r upper arm","r forearm","l upper arm","l forearm","right hip","r upper leg","r lower leg","left hip","l upper leg","l lower leg","stomach","left pec","right pec","center 2","top right","top","top left","center","bottom left","bottom","bottom right" ];
        _menuItems = [];
        _mItra = llStringLength(message)-1;
        do {
            if((integer)llGetSubString(message, _mItra, _mItra))
                _menuItems += llList2String(ATTACHMENTS, _mItra);
        } while(--_mItra>=0);
        ATTACHMENTS = [];
        _cmList = llListInsertList(llListInsertList(llList2List(_menuItems, _mItra=0, (_mItra+=9)), ["<= Back"], 0), ["Next =>"], 2);
        _scanType = "attachments";
        llDialog(_aMaster, "Please select the attachments to detach:\n", _cmList, _comChannel+2);
        state default;
    }
}