Difference between revisions of "MLPV2 RLV Capture By Menu Plugin"

From Second Life Wiki
Jump to navigation Jump to search
(Created page with "* Back to MLPV2_Addons <lsl>//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // --- MLPV2 RLV-Relay Sensor, Capture to Poseball & Restrain by MLPV2 Menu Script --- \\ ~~~~~~~~~~~~~~~~~~~...")
 
 
(11 intermediate revisions by the same user not shown)
Line 2: Line 2:




<lsl>//15:43, 9 June 2016 (PDT)15:43, 9 June 2016 (PDT)15:43, 9 June 2016 (PDT)15:43, 9 June 2016 (PDT)15:43, 9 June 2016 (PDT)[[User:Gayngel Resident|Gayngel Resident]] ([[User talk:Gayngel Resident|talk]]) 15:43, 9 June 2016 (PDT) // --- MLPV2 RLV-Relay Sensor, Capture to Poseball & Restrain by MLPV2 Menu Script --- \\ 15:43, 9 June 2016 (PDT)15:43, 9 June 2016 (PDT)15:43, 9 June 2016 (PDT)15:43, 9 June 2016 (PDT)15:43, 9 June 2016 (PDT)15:43, 9 June 2016 (PDT)15:43, 9 June 2016 (PDT)~\\
This set of scripts are an RLV plugin to capture and force sit victims to the poseballs from the menu rather than touching the balls to capture.
 
Based on an RLV capture script by Betsy Hastings, it took 2 years to develop.
 
*Menu user selects Capture from the menu and the script scans for victims wearing an RLV relay, User can then select a victim and force sit them to the selected ball. Balls a identified by colour.
 
*A maximum of six victims can be captured to each of the six balls.
 
*User can release all victims or individual victims from the Release menu.
 
An example setup can be purchased on the marketplace free & full perm: https://marketplace.secondlife.com/p/MLPV2-RLV-Capture-by-Menu-Script/9311786
 
1. Create a new script in the MLPv2 run object. Name it ~capture.
 
2. Delete the default code in the new script and replace it with the code in the script.
 
3. Extract the ~ball object from the object by dragging it from the object to your inventory. Then rez the ~ball in your inventory to the ground.
 
4. Edit the ~ball on the ground and open the ~ball script in it.
 
5. Replace the code in the script with the ~script code provided with this script set or folder or the ~ball code below.
 
6. Take the ~ball back to inventory and in the MLPv2 object delete the previous~ball and replace it with the new ~ball just taken onto in your inventory.
 
7. In the .MENUITEMS.RLV notecard add LINKMSG Capture | 1,-1,-1819,Capture to MENU RLV.
 
 
 
 
Main object ~capture script:
 
<source lang="lsl2"> // --- MLPV2 RLV-Relay Sensor, Capture to Poseball & Restrain by MLPV2 Menu Script --- \\ 15:43, 9 June 2016 (PDT)15:43, 9 June 2016 (PDT)15:43, 9 June 2016 (PDT)15:43, 9 June 2016 (PDT)15:43, 9 June 2016 (PDT)15:43, 9 June 2016 (PDT)15:43, 9 June 2016 (PDT)~\\
//                                                          by: Gayngel Resident                                    \\
//                                                          by: Gayngel Resident                                    \\
//                                                          © The Script Yard                                      \\
//                                                          © The Script Yard                                      \\
Line 17: Line 48:
//  Menu user selects Capture from the menu and the script scans for victims wearing an RLV relay, User can then select a victim and force sit them to the selected ball. Balls a identified by colour.
//  Menu user selects Capture from the menu and the script scans for victims wearing an RLV relay, User can then select a victim and force sit them to the selected ball. Balls a identified by colour.


// A maxmum of six victims can be captured to each of the six balls.  
// A maximum of six victims can be captured to each of the six balls.  


// User can release all victims or individual victims from the Release menu.
// User can release all victims or individual victims from the Release menu.
Line 911: Line 942:
     }   
     }   
}
}
</lsl>
</source>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
~ball script:
 
<source lang="lsl2">
 
// MLPV2 Version 2 by Learjeff Innis.  Based on
// MLP MULTI-LOVE-POSE V1.2 - Copyright (c) 2006, by Miffy Fluffy (BSD License)
 
// To make ball phantom, put "*" as the first character in the ball's description
// or just make the ball phantom (which really works better for HUDs).
// The rest of the description, if any, is used for the sit pie menu and floating text.
// To make this take effect, use STOP to unrez the balls, and then select any pose.
 
// v2.4j - cancel old listen on rez
// v2.4m - handle control keys! To enable, begin ~ball description with a plus sign ("+"), following "*" if any.
// v2.4p - page up & down to enable/disable adjustment
// v2.4x - moved SitTarget to top.
 
 
 
 
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // --- MLPV2 RLV-Relay Sensor, Capture to Poseball & Restrain by MLPV2 Menu Script --- \\ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\\
//                                                          by: Gayngel Resident                                    \\
//                                                          © The Script Yard                                      \\
//                                                        Created on: 10/06/2016                                    \\
 
//______________________________________________________________________________________________\\
 
//                                  Notes and Instructions                                      \\
 
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\\
 
//  This script and the accompanying script for the poseball will capture a victim and force sit them to a poseball in an object running MLPv2 scripts.
 
//  Menu user selects Capture from the menu and the script scans for victims wearing an RLV relay, User can then select a victim and force sit them to the selected ball. Balls a identified by colour.
 
// A maxmum of six victims can be captured to each of the six balls.
 
// User can release all victims or individual victims from the Release menu.
 
 
 
 
// 1. Create a new script in the MLPv2 run object. Name it ~capture.
// 2. Delete the default code in the new script and replace it with the code in the script.
// 3. Extract the ~ball object from the object by dragging it from the object to your inventory. Then rez the ~ball in your inventory to the ground.
// 4. Edit the ~ball on the ground and open the ~ball script in it.
// 5. Replace the code in the script with the ~script code provided with this script set or folder or the ~ball code below.
// 6. Take the ~ball back to inventory and in the MLPv2 object delete the previous~ball and replace it with the new ~ball just taken onto in your inventory.
// 7. In the .MENUITEMS.RLV notecard add LINKMSG Capture | 1,-1,-1819,Capture to MENU RLV.
 
//_______________________________________________________________________________________________\\
 
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\\
//                                  Acknowledgements                                  \\
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ \\ 
 
 
 
// Script adapted and developed by Gayngel Resident for MLPV2.
 
// Based on scan and capture script by Betsy Hastings.
 
 
vector  SitTarget      = <0.,0.,.1>;  // Change this to <0.,0.,-.15> or lower, to raise balls above cushion
vector  SitTargetRef    = <0.,0.,.1>;  // Don't change this, unless you're replacing earlier versions
                                        // with a custom SitTarget.  In that case, set to match SitTarget.
 
integer ADJUSTABLE  = FALSE;
float  DELTA      = 0.01;        // cm to move
float  EPSILON    = 0.005;      // cm position error tolerance, should be about half delta
vector  Offset;
vector  BallPos;
rotation BallRot;
 
key    NullKey = NULL_KEY;
 
integer Chan;
integer Group;
integer visible = TRUE;
integer Adjusting;
key    Avatar;
key Victim;
string  Name;
integer ListenHandle;
 
integer ControlsActive;
 
integer relaychannel = -1812221819; // Channel of the victim's relay.
integer relay_handle;
 
// 15 color support, thanks to Liz Silverstein
// Color is passed as a string by object chat (from menu via poser*)
 
string restraints;
   
list colors = [ <0.0,0.0,0.0>,          // 0 = HIDE
                <0.835,0.345,0.482>,    // 1 = PINK
                <0.353,0.518,0.827>,    // 2 = BLUE
                <0.635,0.145,0.282>,    // 3 = PINK2 - Dark pink
                <0.153,0.318,0.627>,    // 4 = BLUE2 - Dark blue
                <0.128,0.500,0.128>,    // 5 = GREEN
                <1.000,0.000,1.000>,    // 6 = MAGENTA
                <1.000,0.000,0.000>,    // 7 = RED
                <1.000,0.500,0.000>,    // 8 = ORANGE
                <1.000,1.000,1.000>,    // 9 = WHITE
                <0.0,0.0,0.0>,          // 10 = BLACK
                <1.0,1.0,0.0>,          // 11 = YELLOW
                <0.0,0.8,0.8>,          // 12 = CYAN
                <0.5,0.0,0.0>,          // 13 = RED2
                <0.0,0.5,0.5>,          // 14 = TEAL
                <0.0,0.25,0.25>];      // 15 = GREEN2
               
vector color;
string ballcolor; 
string previousname;           
               
 
 
 
handle_control(key id, integer level, integer change) {
 
    if (Avatar == NullKey) {
        return;
    }
   
    if ((level & CONTROL_UP) && (level & CONTROL_DOWN)) {
        ControlsActive = ! ControlsActive;
        if (ControlsActive) {
            control_active();
        } else {
            control_ready();
        }
        return;
    }
   
    if (!ControlsActive) {
        return;
    }
 
 
    if (level & CONTROL_UP) {
        Offset.z += DELTA;
    } else if (level & CONTROL_DOWN) {
        Offset.z -= DELTA;
    } else if (level & CONTROL_LEFT) {
        Offset.y += DELTA;
    } else if (level & CONTROL_RIGHT) {
        Offset.y -= DELTA;
    } else if (level & CONTROL_FWD) {
        Offset.x += DELTA;
    } else if (level & CONTROL_BACK) {
        Offset.x -= DELTA;
    } else {
        return;
    }
 
    llSetPos(BallPos + Offset * BallRot + SitTargetRef - SitTarget);
}
 
// enter "control ready" state.
control_ready() {
    ControlsActive = 0;
    integer controls = CONTROL_UP | CONTROL_DOWN;
 
    llTakeControls(controls, TRUE, FALSE);
    llInstantMessage(Avatar, "Allow adjusting your position by pressing Pgup and Pgdn keys at the same time");
}
 
control_active() {
    integer controls = CONTROL_UP | CONTROL_DOWN | CONTROL_LEFT | CONTROL_RIGHT | CONTROL_FWD | CONTROL_BACK;
 
    llTakeControls(controls, TRUE, FALSE);
    llInstantMessage(Avatar, "Adjust your position using Pgup/Pgdn, Up/Down arrow, and shift-left/right arrow keys\nDisable by pressing Pgup and Pgdn keys at the same time");
    BallPos = llGetPos() + SitTarget - SitTargetRef;
    BallRot = llGetRot();
}
 
 
 
render() {
    if (!visible || (Avatar != NullKey && !Adjusting)) {
        // hidden
        llSetScale(<0.01,0.01,0.01>);
        llSetAlpha(0.0, ALL_SIDES);
        llSetText("",<1.0,1.0,1.0>,1.0);   
    } else if (Avatar != NullKey && Adjusting) {
        // sitting and adjusting
        llSetAlpha(0.2,ALL_SIDES);
        llSetText("Adjust",<1.0,1.0,1.0>,1.0);
        llSetScale(<0.1,0.1,5.0>);
    } else {
        // shown
        llSetAlpha(1.0, ALL_SIDES);
        llSetScale(<0.2,0.2,0.2>);
        if (Adjusting) {
            llSetText("Adjust",<1.0,1.0,1.0>,1.0);
        } else {
            llSetText(Name,<1.0,1.0,1.0>,1.0);
        }
    }
}
 
show() {
    visible = TRUE;
    render();
}
 
hide() {
    visible = FALSE;
    render();
}
 
default {
    on_rez(integer channel) {
      Name = llGetObjectDesc();
        if (Name == "" || Name == "(No Description)") {
            Name = "LOVE";
        } else {
            if (llSubStringIndex(Name, "*") == 0) {
                llSetPrimitiveParams([PRIM_PHANTOM, TRUE]);
                Name = llGetSubString(Name, 1, -1);
            }
            if (llSubStringIndex(Name, "+") == 0) {
                ADJUSTABLE = TRUE;
                Name = llGetSubString(Name, 1, -1);
            }
            if (Name == "none") {
                Name = "";
            }
        }
        llSitTarget(SitTarget, ZERO_ROTATION);
        if (Name != "") {
            llSetSitText(Name);
        } else {
            llSetSitText("");
        }
        Avatar = NullKey;
        Chan = channel;
       
     
        Group = 0;
       
        if (ListenHandle) {
            llListenRemove(ListenHandle);
            ListenHandle = 0;
        }
 
        if (Chan != 0) {
            ListenHandle = llListen(Chan,"",NullKey,"");
            // start timer unless ball was dragged from inv
            llSetTimerEvent(180.0);
        }
       
        if (llGetObjectDesc() == "ADJUSTABLE") {
            ADJUSTABLE = TRUE;
        }
       
     
    }
   
 
    changed(integer change) {
        if (change != CHANGED_LINK) return;
        Avatar = llAvatarOnSitTarget();
        if (Avatar != NullKey) {
 
            if (Group) {
                if (!llSameGroup(Avatar)) {
                    llUnSit(Avatar);
                    llWhisper(0,"no permission to use poseball");
                    return;
                }
            }
            if (ADJUSTABLE) {
                llRequestPermissions(Avatar, PERMISSION_TAKE_CONTROLS);
            }
        }
        llSay(Chan+8,(string)Avatar);    //requests perm, sets animation
        render();
       
    }
 
    listen(integer channel, string name, key object, string str) {
       
     
       
        if(channel == relaychannel)
        {
           
          list rlvmsg = llParseStringKeepNulls(str,[","],[]);
         
         
   
            if (llGetListLength(rlvmsg) > 3)
            {
                    string cmd = llList2String(rlvmsg,2);
                    string resp = llList2String(rlvmsg,3);
                   
           
        if (cmd == "ping")
            {
             
                //pong if no one sat
                llRegionSay(relaychannel,"pong," + (string)llGetOwnerKey(object) +",!pong");
               
               
 
            }
           
        else if( llList2String(rlvmsg,0) == "Capture")
        {
        if(resp == "ko")
        {
           
            llWhisper(0,llKey2Name(Victim) + "'s RLV Relay denied the restriction.");
            llSetObjectName(previousname);
            llSay(-657, "CaptureDenied");
           
        }
    }
       
    }
           
        }
           
            else
            {
        integer ix;
        ix = llSubStringIndex(str,">");   
        if (ix != -1) {
           
         
            if(llGetSubString(str,0,ix-1) == "Capture")
            {
           
              if(llAvatarOnSitTarget())
              {
                 
                 
                llSay(-657, "AvatarOnSitTarget");
                 
              }
             
              else
              { 
              llSay(-657, "Captured"); 
              Victim = llGetSubString(str,ix+1,-1);
              previousname = llGetObjectName();
              llSetObjectName(name);
              relay_handle = llListen(relaychannel,"","","");
              llSay(relaychannel, "Capture" + "," + (string)Victim+ "," + "@sit:" + (string)llGetKey() + "=force");
             
              }
               
            }
           
            else if(llGetSubString(str,0,ix-1) == "Restrain" && llAvatarOnSitTarget() == Victim)
            {
             
              restraints =  llGetSubString(str,ix+1,-1);
                llSay(relaychannel, "Restrain" + "," + (string)Victim+ "," + restraints);
                llSetObjectName(previousname);
            }
           
            else
            {
           
            BallPos = (vector)llGetSubString(str,0,ix);
            BallRot = (rotation)llGetSubString(str,ix+1,-1);
            llSetPos(BallPos + SitTargetRef - SitTarget);
            llSetRot(BallRot);
            }
        } else if (str == "0") {    //HIDE
            hide();
        } else if (str == "SHOW") { //SHOW
            show();
        } else if (str == "ADJUST|1") {
            Adjusting = TRUE;
            render();
        } else if (str == "ADJUST|0") {
            Adjusting = FALSE;
            render();
        } else if (str == "SAVE") {
            llSay(Chan+16,(string)(llGetPos() + SitTarget - SitTargetRef)+(string)llGetRot());
        } else if (str == "GROUP") {
            Group = 1;
        } else if (str == "ALL") {
            Group = 0;
        } else if (str == "DIE") {
           
            if(Victim)
            {
           
              llSay(relaychannel, "Release" + "," + (string)Victim+ ",!release|@clear"); 
               
            }
            llSay(Chan+8, (string)NullKey);    //msg to poser (don't reanimate after STOP)
            llDie();
        } else if (str == "LIVE") {
            llSetTimerEvent(180.0);
            // llSay(Chan+8,"ALIVE");    //msg to poser -> to menu
        } else if(str == "Release") {
           
       
          llSay(relaychannel, "Release" + "," + (string)Victim+ ",!release|@clear");
          Victim = NULL_KEY;
           
        } else {
         
            list ldata = llParseString2List(str, ["|"], []);
         
            integer colorIx = (integer) llList2String(ldata,0);
           
            string ballIx = llList2String(ldata,1);
           
            Adjusting = (integer) llList2String(ldata,2);
            if ((colorIx > 0) && (colorIx < 16)) { // this must be a color setting 
            color = llList2Vector(colors,colorIx);   
                llSetColor(color,ALL_SIDES);    //pull the color out of the list
         
                render();
                BallPos = llGetPos() + SitTarget - SitTargetRef;
                BallRot = llGetRot();
               
                llSleep((0.1*(float)ballIx));
                llSetObjectName("~ball" + ballIx);
                llSay(-657,(string)color);
             
     
     
             
   
   
             
                }
               
               
            }
           
           
        }
    }
 
    timer() {                      //not heard "LIVE" from menu for a while: suicide
        llDie();
    }
 
    run_time_permissions(integer perm)
    {
        if (llKey2Name(Avatar) == "") {
            return;
        }
        if (Avatar != llGetPermissionsKey()) {
            return;
        }
 
        if (perm & PERMISSION_TAKE_CONTROLS) {
            control_ready();
        }
    }
 
    moving_end() {
        vector newpos = llGetPos() + SitTarget - SitTargetRef;
        if (llVecMag(newpos - (BallPos + Offset)) > EPSILON) {
            // we didn't move it, someone else did.  Deal with it.
            BallPos = newpos;
        }
        BallRot = llGetRot();
    }
 
    control(key id, integer level, integer change) {
        handle_control(id, level, change);
    }
}


</source>


[[Category:MLPV2]]
[[Category:MLPV2]]

Latest revision as of 18:58, 10 June 2016


This set of scripts are an RLV plugin to capture and force sit victims to the poseballs from the menu rather than touching the balls to capture.

Based on an RLV capture script by Betsy Hastings, it took 2 years to develop.

  • Menu user selects Capture from the menu and the script scans for victims wearing an RLV relay, User can then select a victim and force sit them to the selected ball. Balls a identified by colour.
  • A maximum of six victims can be captured to each of the six balls.
  • User can release all victims or individual victims from the Release menu.

An example setup can be purchased on the marketplace free & full perm: https://marketplace.secondlife.com/p/MLPV2-RLV-Capture-by-Menu-Script/9311786

1. Create a new script in the MLPv2 run object. Name it ~capture.

2. Delete the default code in the new script and replace it with the code in the script.

3. Extract the ~ball object from the object by dragging it from the object to your inventory. Then rez the ~ball in your inventory to the ground.

4. Edit the ~ball on the ground and open the ~ball script in it.

5. Replace the code in the script with the ~script code provided with this script set or folder or the ~ball code below.

6. Take the ~ball back to inventory and in the MLPv2 object delete the previous~ball and replace it with the new ~ball just taken onto in your inventory.

7. In the .MENUITEMS.RLV notecard add LINKMSG Capture | 1,-1,-1819,Capture to MENU RLV.



Main object ~capture script:

 // --- MLPV2 RLV-Relay Sensor, Capture to Poseball & Restrain by MLPV2 Menu Script --- \\ 15:43, 9 June 2016 (PDT)15:43, 9 June 2016 (PDT)15:43, 9 June 2016 (PDT)15:43, 9 June 2016 (PDT)15:43, 9 June 2016 (PDT)15:43, 9 June 2016 (PDT)15:43, 9 June 2016 (PDT)~\\
//                                                          by: Gayngel Resident                                    \\
//                                                          © The Script Yard                                       \\
//                                                        Created on: 10/06/2016                                    \\

//______________________________________________________________________________________________\\

//                                  Notes and Instructions                                      \\

//15:43, 9 June 2016 (PDT)15:43, 9 June 2016 (PDT)15:43, 9 June 2016 (PDT)15:43, 9 June 2016 (PDT)15:43, 9 June 2016 (PDT)15:43, 9 June 2016 (PDT)15:43, 9 June 2016 (PDT)15:43, 9 June 2016 (PDT)15:43, 9 June 2016 (PDT)15:43, 9 June 2016 (PDT)15:43, 9 June 2016 (PDT)15:43, 9 June 2016 (PDT)15:43, 9 June 2016 (PDT)15:43, 9 June 2016 (PDT)15:43, 9 June 2016 (PDT)15:43, 9 June 2016 (PDT)15:43, 9 June 2016 (PDT)15:43, 9 June 2016 (PDT)[[User:Gayngel Resident|Gayngel Resident]] ([[User talk:Gayngel Resident|talk]]) 15:43, 9 June 2016 (PDT)\\

//  This script and the accompanying script for the poseball will capture a victim and force sit them to a poseball in an object running MLPv2 scripts.

//  Menu user selects Capture from the menu and the script scans for victims wearing an RLV relay, User can then select a victim and force sit them to the selected ball. Balls a identified by colour.

// A maximum of six victims can be captured to each of the six balls. 

// User can release all victims or individual victims from the Release menu.


  

// 1. Create a new script in the MLPv2 run object. Name it ~capture.
// 2. Delete the default code in the new script and replace it with the code in the script.
// 3. Extract the ~ball object from the object by dragging it from the object to your inventory. Then rez the ~ball in your inventory to the ground.
// 4. Edit the ~ball on the ground and open the ~ball script in it.
// 5. Replace the code in the script with the ~script code provided with this script set or folder or the ~ball code below.
// 6. Take the ~ball back to inventory and in the MLPv2 object delete the previous~ball and replace it with the new ~ball just taken onto in your inventory.
// 7. In the .MENUITEMS.RLV notecard add LINKMSG Capture | 1,-1,-1819,Capture to MENU RLV.

//_______________________________________________________________________________________________\\

// 15:43, 9 June 2016 (PDT)15:43, 9 June 2016 (PDT)15:43, 9 June 2016 (PDT)15:43, 9 June 2016 (PDT)15:43, 9 June 2016 (PDT)15:43, 9 June 2016 (PDT)15:43, 9 June 2016 (PDT)15:43, 9 June 2016 (PDT)15:43, 9 June 2016 (PDT)15:43, 9 June 2016 (PDT)15:43, 9 June 2016 (PDT)15:43, 9 June 2016 (PDT)15:43, 9 June 2016 (PDT)15:43, 9 June 2016 (PDT)15:43, 9 June 2016 (PDT)15:43, 9 June 2016 (PDT)~~\\
//                                  Acknowledgements                                  \\
// 15:43, 9 June 2016 (PDT)15:43, 9 June 2016 (PDT)15:43, 9 June 2016 (PDT)15:43, 9 June 2016 (PDT)15:43, 9 June 2016 (PDT)15:43, 9 June 2016 (PDT)15:43, 9 June 2016 (PDT)15:43, 9 June 2016 (PDT)15:43, 9 June 2016 (PDT)15:43, 9 June 2016 (PDT)15:43, 9 June 2016 (PDT)15:43, 9 June 2016 (PDT)15:43, 9 June 2016 (PDT)15:43, 9 June 2016 (PDT)15:43, 9 June 2016 (PDT)15:43, 9 June 2016 (PDT)[[User:Gayngel Resident|Gayngel Resident]] ([[User talk:Gayngel Resident|talk]]) \\   



// Script adapted and developed by Gayngel Resident for MLPV2.

 // Based on scan and capture script by Betsy Hastings.




integer victimactionschannel; //Channel to do something to a victim.
integer selectvictimchannel; //Channel of caught/seated victim
integer releasevictimchannel;
integer ballschannel;
integer ch;
integer i;  // Global counting variable i. Used for nearby avatar name retreival in for loop.
integer index; //Index of nearby avatar names presented to dialog menu user.
integer index1;
integer listener;
integer listener2;
integer listener3;
integer LM_Channel = -1819; //Link Message Channel to call the script from the menu via .RLV Notecard.
integer relaychannel = -1812221819; // Channel of the victim's relay.
integer timeout = 60; //Timeout for listen handle. Can be changed to allow user more time idling the menu.
integer touched = 0;
integer x;
integer y;
integer ScanOn = FALSE;
integer menuindex;
integer colorlisten;
integer balllisten;
string first_name;
string ping;
string prompt;
string restraints = "@unsit=n|@sittp=n";
string victim_name;
string SCAN = "Scan";
string BACK = "Back";
string RELEASE = "Release";
string RELEASEALL = "Release All";

key owner;
key seated_avi;
key toucher;
key victimkey;
key foundball;


float range = 20.0; //Range to scan for nearby avatars. Can be increased to scan over a wider area to find a victim within that range.


list buttons;
list victims = [];//2 strided list in form of name,key used to create menu of avatars to grab, and process responses. i.e. List of nearby people to catch and force onto the object.

list ballsfound;
list seatedavs;


// -------------------- functions ---------------------




AvMenu(key id)// Button creator. Creates list of people wearing an RLVrelay in victims list as a button in the dialog menu. Finding victims wearing an RLV relay and selection of a victim handled further down the script.
{
 
    buttons = []; // Local list. Presents each name found as a button in the dialog menu allowing the user to select a name and to catch that avatar.
    index = llGetListLength(victims); // Indexes  the victims list and sources the length of that list. i.e. If there are 5 nearby victims then the list length = 5.
    for (i = 0; i < index; i = i+ 2) // For loop defines i as a variable to count the number of victims nearby. Repeats adding victim names to buttons until it reaches the length of the index. E.g. If there are 5 nearby victims add 5 buttons and no more. Increment i / repeat adding victim names until length of index is met.
    {
        
    
     buttons += llBase64ToString(llGetSubString(llStringToBase64(llGetSubString(llList2String(victims, i),0,(llSubStringIndex(llList2String(victims, i)," ")+1))), 0, 31));
       
//buttons += llGetSubString(llList2String(victims, i),0,(llSubStringIndex(llList2String(victims, i)," ")+1));
    } // Presents each name of victims as a string. Creates a button for each name. Names of each button are sourced from the list of victims. Uses the for loop to determine number of buttons to create.  llList2String(victims, i) defines the the index source, 0 defines the starting integer (starts at no victims and creates victim buttons one by one), llSubStringIndex(llList2String(victims, i) defines the ending integer.
    buttons += BACK;
    prompt = "Pick someone to attempt to restrain.\n";
    prompt += "Choose from these " + (string)(index/2) + " uncaptured avis, who use a RLVrelay.\n";
    prompt += "Menu will time out in " + (string)timeout + " seconds.\n";
    listener = llListen(selectvictimchannel, "", id, "");
    llSetTimerEvent(0.0); 
    llSetTimerEvent(timeout);
    buttons = RestackMenu(buttons);
    llDialog(id, prompt, buttons, selectvictimchannel); //When user selects a victim from the menu script listens for channel called selectvictimschannel. This channel will force sit the victim. Listen event further down.  
}



list RestackMenu(list in)
{
    //re-orders a list so dialog buttons start in the top row
    list out = llList2List(in, 9, 11);
    out += llList2List(in, 6, 8);
    out += llList2List(in, 3, 5);
    out += llList2List(in, 0, 2);
    return out;
}


GrabAv(integer idx,key id) // Capture victim to the poseball.
{
   
    
     llInstantMessage(toucher, "Trying to capture " + llKey2Name(id) + ".");
     llRegionSayTo(foundball,ch+idx, "Capture>" +(string)id);
   
victims =[];
victim_name = "";

}




ScanMenu(key id)
{
    llListenRemove(listener);
    llSetTimerEvent(0.0);
    llSetTimerEvent(timeout);
    
    
    
   
    
    buttons = [];
    prompt = "Pick an option. ";
    prompt += "Menu will time out in " + (string)timeout + " seconds.\n";

    
    listener2 = llListen(victimactionschannel, "", id, "");
    if(seatedavs == [])
    {
    buttons += [SCAN, BACK];
    }
    
    else
    {
       buttons += [SCAN, RELEASE, BACK]; 
       
    }
    buttons = llListSort(buttons,1,FALSE);
    
    llDialog(id, prompt, buttons, victimactionschannel);
}
 


ReleaseMenu(key id)
{

    
 buttons = []; 
 
 
buttons = [RELEASEALL,BACK];

buttons += llList2ListStrided(seatedavs,0,-1,3);

 
    
    prompt = "\nPick a victim to release.\n";
    prompt += "Menu will time out in " + (string)timeout + " seconds.\n";
    listener = llListen(releasevictimchannel, "", id, "");
    llSetTimerEvent(0.0);
    llSetTimerEvent(timeout);
    buttons = RestackMenu(buttons);
    
    llDialog(id, prompt, buttons, releasevictimchannel);   
}


say_it(string abc)
{
    
    llWhisper(0,abc);
    
}


SelectBall()
{
    
  llSetTimerEvent(0.0);
  llSetTimerEvent(timeout);
  llListenRemove(listener);
  llListenRemove(colorlisten);
     listener = llListen(ballschannel, "", toucher, "");
     balllisten = llListen(-657,"",NULL_KEY,"");
    listener2 = llListen(relaychannel,"","","");
    buttons = [];
    
   // if(colorsfound != [])
  //  {
   //     ballsfound = [];
  
   
   
   index = llGetListLength(ballsfound)/2;
    for (i = 0; i < index; ++i)
    { 

buttons += llList2String(llList2ListStrided(ballsfound,0,-1,2), i);

    }
   
    prompt = "Select a ball to capture your victim onto it";
   
    
    buttons = RestackMenu(buttons);
    llDialog(toucher, prompt, buttons, ballschannel);   
    
}


OpenMenu(key id)
{
           
  
            toucher = id;
           
            
            victimkey = llList2Key(seatedavs,llListFindList(seatedavs,[(string)toucher]));
            
            
            if (toucher == owner)
            {
                //give menu of things to do to victim
              ScanMenu(toucher);
               
                
            
            }
            else if (toucher == victimkey)
            {
                
                //seated av clicked.  Taunt them
                llInstantMessage(victimkey, "Menu restricted. You are captured and locked!");
            }
            else
            {
               ScanMenu(toucher);
        

            } 
    
    
}

default
{
     state_entry()
    {
       
         victimactionschannel =  -(integer) ( "0x" + (string) llGetKey() ) - 987211; //Channel to do something to a victim.
    selectvictimchannel =  -(integer) ( "0x" + (string) llGetKey() ) - 987212; //Channel of caught/seated victim
    releasevictimchannel=  -(integer) ( "0x" + (string) llGetKey() ) - 987213;
ballschannel = -(integer) ( "0x" + (string) llGetKey() ) - 987214;
ch = (integer)("0x"+llGetSubString((string)llGetKey(),-4,-1));
    listener = llListen(relaychannel, "", "", "");//listen for pings

if (ScanOn ==  TRUE)
{
    listener3 = llListen(374, "", NULL_KEY, "");
    ScanMenu(toucher);
    ScanOn = FALSE;
}

        owner = llGetOwner();
        
    }
    
    on_rez(integer num)
    {
        llResetScript();
    }
    
    
     object_rez(key balls)
            {
               colorlisten = llListen(-657,"",NULL_KEY,"");
              llSleep(0.5);
                  
  //     for(i=0; i < llGetListLength((list)balls); ++i)
     //            { 
                 
                 
            
                   ballsfound += [llKey2Name(balls)] + [(string)balls];

//ballsfound = ballsfound +[llKey2Name(balls)] + [(string)balls];
          //        }   
             
               
         if(~!llListFindList(ballsfound, ["~ball"]))
    {

     ballsfound = llListReplaceList(ballsfound,["~ball0"],0,0);
              
    }    
               
            }
            
            
    
     link_message(integer from, integer num, string str, key id)
    {
     
        
      if (num == -1819)
      {
        llSetTimerEvent(0.0);
        llSetTimerEvent(timeout);  
        
       
        
         OpenMenu(id);
              
        }
        
        
    else if(str == "STOP")
    {
        llSetTimerEvent(0.0);
        llResetScript();
       
       
    }
        
}


    sensor(integer total_number)
    {
        
        
        
        for(i=0;i<total_number;++i)
        {
            
        
            
        
            listener = llListen(relaychannel,"",NULL_KEY,"");
            llSay(relaychannel,"locator,"+(string)llDetectedKey(i)+",!version");//query for a RLVRS
         
        }
        llSetTimerEvent(0.0);
        llSetTimerEvent(5.0);
      

    }

    no_sensor()
    {
        say_it("There are no uncaptured avatars in range with an active RLV relay. Please try scanning again.");
        
    }



    listen(integer channel, string name, key id, string message)
    {
       
      string cmd = llGetSubString(message,0,6);
       if (cmd == "locator")
            {
                
               if(!~llListFindList(seatedavs,[(string)llGetOwnerKey(id)]))  
                    {
                    victims += [llKey2Name(llGetOwnerKey(id)),llGetOwnerKey(id)];// build 2 strided victims list that contains only avis we heared a response from the relay + key
                    }
                
                
            }
           
        
       
        else if (channel == selectvictimchannel)
        {
    
             llSetTimerEvent(0.0);
             
             if (message == BACK)
             {
             ScanMenu(toucher);    
             }

            else
            { 
            index1 = llStringLength(message);
            index = llGetListLength(victims);
            i=0;
            while(i < index)
            {
           
               victim_name = (llGetSubString(llList2String(victims,i),0,index1 -1));
               
               
                if(victim_name == message)
                {
                    victimkey = llList2Key(victims,i+1);
                }
                i = i+2;       
           }
         state FindBalls;
       }  
    }

        else if (channel == victimactionschannel)
        {
            if (message == SCAN)
            {
                if (ballsfound != [])
               { 
                victims = [];
                victimkey = NULL_KEY;
                message = "";
                
                     llWhisper(0,"Scanning for avatars with an active RLV relay within " + llGetSubString((string)range,0,3) + " Meters.");


                    llSensor("",NULL_KEY,AGENT,range,PI);
                    touched = 1;
                
                
              }
              
              else
              {
                 llWhisper(0,"There are no balls to sit on. Please select a pose first before capturing your victim!");
                }
            }
            
              
            
             else if(message == BACK)
              {
               llMessageLinked(LINK_THIS, llDetectedGroup(0), "PRIMTOUCH", id) ;   
              }
            
             else if (message == RELEASE)
               {
               ReleaseMenu(toucher);
               } 
               
     
  
        }
             
             
              else if (channel == 374)
            {
               
              if(message == "RED")
              {
                
                index = llListFindList(seatedavs, [(string)id]);
                victimkey =  llList2Key(seatedavs,index);
                llDialog(victimkey,"\nYou used your safeword and have been released from all restrictions.", [], -3);
                foundball = llList2Key(seatedavs,index+1);
                index1 = (llListFindList(ballsfound, [(string)foundball]));
                llRegionSayTo(foundball,(ch + llCeil(index1/2)), "Release");
       
                
           
         seatedavs = llDeleteSubList(seatedavs, index-1, index+1);
        
        victimkey = NULL_KEY;
        
         touched = 0;

                    llSay(0, llKey2Name(id) + " used their safeword and has been released from restrictions.");
              }    
            }
 
 
            
else if (channel == -657)
{
    llSleep(0.1);
    vector color = (vector)message;
    string ballcolor;
    if(color == <0.0,0.0,0.0>)
       {
         ballcolor = "Hidden";  
        }
        
      else if(color == <0.835,0.345,0.482>)
       {
         ballcolor = "PINK";  
        }
        
        else if(color ==  <0.353,0.518,0.827>)
       {
         ballcolor = "BLUE";  
        }
        
        else if(color ==  <0.635,0.145,0.282>)
       {
         ballcolor = "PINK2";  
        }
        
        else if(color ==  <0.153,0.318,0.627>)
       {
         ballcolor = "BLUE2";  
        }
        
        else if(color ==  <0.128,0.500,0.128>)
       {
         ballcolor = "GREEN";  
        }
        
        else if(color ==  <1.000,0.000,1.000>)
       {
         ballcolor = "MAGENTA";  
        }
        
        else if(color ==  <1.000,0.000,0.000>)
       {
         ballcolor = "RED";  
        }
        
        else if(color ==  <1.000,0.500,0.000>)
       {
         ballcolor = "ORANGE";  
        }
        
        else if(color ==  <1.000,1.000,1.000>)
       {
         ballcolor = "WHITE";  
        }
        
        else if(color ==  <0.0,0.0,0.0>)
       {
         ballcolor = "BLACK";  
        }
        
        else if(color ==  <1.0,1.0,0.0>)
       {
         ballcolor = "YELLOW";  
        }
        
        else if(color ==  <0.0,0.8,0.8>)
       {
         ballcolor = "CYAN";  
        }
        
        else if(color ==  <0.5,0.0,0.0>)
       {
         ballcolor = "RED2";  
        }
        
        else if(color ==  <0.0,0.5,0.5>)
       {
         ballcolor = "TEAL";  
        }
        
        else if(color ==  <0.0,0.25,0.25>)
       {
         ballcolor = "GREEN2";  
        }
    index = llListFindList(ballsfound,[(string)id]); 
    //replace at index with loop integer i
  ballsfound = llListReplaceList(ballsfound, [ballcolor], index -1, index -1); 
// ballsfound = ballsfound +[message];
 
 if(index == llGetListLength(ballsfound) - 1)
 {
     llListenRemove(colorlisten);
 
 
 };
 
 // ballsfound = ballsfound +[llKey2Name(balls)] + [(string)balls];
 
//  colorsfound += [message] + [(string)id];

 // if(llGetListLength(colorsfound) == llGetListLength(ballsfound))
//  {
 //     llListenRemove(colorlisten);
      
  //  }
   
}
    
            else if (channel == releasevictimchannel)
        
           {
            
                if(message == RELEASEALL)
          {
        
         for(i = 1; i < llGetListLength(seatedavs);i = i+3 )
         {
             
 
       victimkey = llList2Key(seatedavs,i);
        llDialog(victimkey,"\n" + llKey2Name(toucher) + " has released you.", [], -3);
        foundball = llList2Key(seatedavs,i+1);
        index1 = (llListFindList(ballsfound, [(string)foundball]));
       llRegionSayTo(foundball,(ch + llCeil(index1/2)), "Release");
                
        
        
              }  
              touched = 0;
              seatedavs = []; 
              victimkey = NULL_KEY;
              seated_avi = NULL_KEY;
              buttons = [];
              foundball = NULL_KEY;
              
              
   




        
        }  
             
        else  {
        index = llListFindList(seatedavs, [message]);
        victimkey =  llList2Key(seatedavs,index + 1);
        llDialog(victimkey,"\n" + llKey2Name(toucher) + " has released you.", [], -3);
    
        foundball = llList2Key(seatedavs,index+2);
        index1 = (llListFindList(ballsfound, [(string)foundball]));
       llRegionSayTo(foundball,(ch + llCeil(index1/2)), "Release");
       
                
           
         seatedavs = llDeleteSubList(seatedavs, index, index+2);
        
        victimkey = NULL_KEY;
        
         touched = 0;
        
       
        
          }
          
          ScanMenu(id);
        }
}
    
   

    timer()
    {
        
        llSetTimerEvent(0.0);
        llListenRemove(listener);
        llListenRemove(listener2);
        llListenRemove(colorlisten);
        
        if (touched)
        {
            if(victims == []) // we heared no message from a RLVrelay
            {
                say_it("There are no avatars in range with an active RLV relay. Please try scanning again.");
               
                
            }
           
            else
            {
                
                AvMenu(toucher);
                touched = 0;
            }
        }
    }
    
    state_exit()
  {
      llSetTimerEvent(0.0);
    }  
}


state FindBalls
{
 
 state_entry()
{
    
SelectBall();        

}

 listen(integer channel, string name, key id, string message)
 {

  if (channel == ballschannel)
{
    llSetTimerEvent(0.0);
    llSetTimerEvent(timeout);
    
    
    
    
    index = llListFindList(ballsfound, [message]);
    foundball =  llList2Key(ballsfound,index+1);
  
    
   
    
  
    GrabAv(((index+1)/2),victimkey);
    
    
            }   
            
           
           else if(channel == -657)
           {
               
             
             if(message == "AvatarOnSitTarget")
             {
                 
                 
               llSay(0, "A victim is already sitting there, please select a different ball.");
               SelectBall();  
                 
                 
             }  
              
             else if(message == "Captured")
             {
                 
                 
                state seated; 


             } 
               
               
            }        
    }
timer()
{
 llSetTimerEvent(0.0); 
 state default;  
}
  
  state_exit()
  {
      llSetTimerEvent(0.0);
    }  
}


state seated
{
    state_entry()
    {
         llSetTimerEvent(0.0);
         llSetTimerEvent(timeout);
        listener2 =llListen(relaychannel,"","","");
       balllisten = llListen(-657,"","","");
      

    }
    
     listen(integer channel, string name, key id, string message)
     {
        
      if(channel == -657)
      {
          
       
       if(message == "CaptureDenied")
       {
           
         state default;
           
        }
          
      }    
         
     }

    link_message(integer from, integer num, string str, key id)
    {
      if (num == -1819)
      {
          
            
            OpenMenu(id);
    
        
        }
        
        else if(str == "STOP")
    {
        ballsfound =[];
        state default;
    }
    
    else if (num == -11000)
    {
     if(id == victimkey)
     {
         seated_avi = id;
         
          
      
           seatedavs = seatedavs + [llBase64ToString(llGetSubString(llStringToBase64(llKey2Name(seated_avi)), 0, 31))] + [(string)seated_avi] + foundball;
           
         
         // seatedavs = seatedavs + [llKey2Name(seated_avi)] + [(string)seated_avi];
          
         if(seated_avi == owner)
         {
            
             
              //restraints += "|@sendim:"+(string)toucher+"=add|@recvim:"+(string)toucher+"=add|@touchthis:" + (string)llGetKey() + "=rem|@tplm=n|@tploc=n|@tplure=n";
              restraints += "|@sendim:"+(string)toucher+"=add|@recvim:" + (string)llGetKey() + "=add|@tplm=n|@tploc=n|@tplure=n";



            }
            
       else{  
      
           // restraints += "|@sendim:"+(string)toucher+"=add|@recvim:"+(string)toucher+"=add|@touchthis:" + (string)llGetKey() + "=add|@tplm=n|@tploc=n|@tplure=n";
           restraints += "|@sendim:"+(string)toucher+"=add|@recvim:" + (string)llGetKey() + "=add|@tplm=n|@tploc=n|@tplure=n";

              }

            
           llRegionSayTo(foundball,ch+((index+1)/2), "Restrain>"+restraints);
            victim_name = llKey2Name(seated_avi);
            first_name = llGetSubString(victim_name,0,llSubStringIndex(victim_name," ")-1); 
            
            if(seated_avi != owner)
            {
prompt = "\nYou have been captured and locked! \n\nThe "+ llGetObjectName() + "'s menu is restricted. \n\nYour safeword is RED.
To trigger the safeword and free restrictions say it on channel 374.
\nExample: /374 RED \n\nYou can also use your OpenCollar relay safeword. Check Help/About in your collar menu for your safeword.";
llDialog(seated_avi,prompt,[],releasevictimchannel);
llInstantMessage(seated_avi,"Menu restricted, you are captured and locked! Your safeword is /374 RED.");
           }
restraints = "@unsit=n|@sittp=n";
state default; 
 
 
        }   
        
       
    }
    
        
    }


   
    timer()
    {
        
        llSetTimerEvent(0.0);
        state default;
    }
    
    state_exit()
  {
     
      llSetTimerEvent(0.0);
      ScanOn = TRUE;
    }  
}








~ball script:

// MLPV2 Version 2 by Learjeff Innis.  Based on
// MLP MULTI-LOVE-POSE V1.2 - Copyright (c) 2006, by Miffy Fluffy (BSD License)

// To make ball phantom, put "*" as the first character in the ball's description
// or just make the ball phantom (which really works better for HUDs).
// The rest of the description, if any, is used for the sit pie menu and floating text.
// To make this take effect, use STOP to unrez the balls, and then select any pose.

// v2.4j - cancel old listen on rez
// v2.4m - handle control keys! To enable, begin ~ball description with a plus sign ("+"), following "*" if any.
// v2.4p - page up & down to enable/disable adjustment
// v2.4x - moved SitTarget to top.




//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // --- MLPV2 RLV-Relay Sensor, Capture to Poseball & Restrain by MLPV2 Menu Script --- \\ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\\
//                                                          by: Gayngel Resident                                    \\
//                                                          © The Script Yard                                       \\
//                                                        Created on: 10/06/2016                                    \\

//______________________________________________________________________________________________\\

//                                  Notes and Instructions                                      \\

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\\

//  This script and the accompanying script for the poseball will capture a victim and force sit them to a poseball in an object running MLPv2 scripts.

//  Menu user selects Capture from the menu and the script scans for victims wearing an RLV relay, User can then select a victim and force sit them to the selected ball. Balls a identified by colour.

// A maxmum of six victims can be captured to each of the six balls. 

// User can release all victims or individual victims from the Release menu.


  

// 1. Create a new script in the MLPv2 run object. Name it ~capture.
// 2. Delete the default code in the new script and replace it with the code in the script.
// 3. Extract the ~ball object from the object by dragging it from the object to your inventory. Then rez the ~ball in your inventory to the ground.
// 4. Edit the ~ball on the ground and open the ~ball script in it.
// 5. Replace the code in the script with the ~script code provided with this script set or folder or the ~ball code below.
// 6. Take the ~ball back to inventory and in the MLPv2 object delete the previous~ball and replace it with the new ~ball just taken onto in your inventory.
// 7. In the .MENUITEMS.RLV notecard add LINKMSG Capture | 1,-1,-1819,Capture to MENU RLV.

//_______________________________________________________________________________________________\\

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\\
//                                  Acknowledgements                                  \\
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ \\   



// Script adapted and developed by Gayngel Resident for MLPV2.

 // Based on scan and capture script by Betsy Hastings.


vector  SitTarget       = <0.,0.,.1>;   // Change this to <0.,0.,-.15> or lower, to raise balls above cushion
vector  SitTargetRef    = <0.,0.,.1>;   // Don't change this, unless you're replacing earlier versions
                                        // with a custom SitTarget.  In that case, set to match SitTarget.

integer ADJUSTABLE  = FALSE;
float   DELTA       = 0.01;        // cm to move
float   EPSILON     = 0.005;       // cm position error tolerance, should be about half delta
vector  Offset;
vector  BallPos;
rotation BallRot;

key     NullKey = NULL_KEY;

integer Chan;
integer Group;
integer visible = TRUE;
integer Adjusting;
key     Avatar;
key Victim;
string  Name;
integer ListenHandle;

integer ControlsActive;

integer relaychannel = -1812221819; // Channel of the victim's relay.
integer relay_handle;

// 15 color support, thanks to Liz Silverstein
// Color is passed as a string by object chat (from menu via poser*)

string restraints;
    
list colors = [ <0.0,0.0,0.0>,          // 0 = HIDE
                <0.835,0.345,0.482>,    // 1 = PINK
                <0.353,0.518,0.827>,    // 2 = BLUE
                <0.635,0.145,0.282>,    // 3 = PINK2 - Dark pink
                <0.153,0.318,0.627>,    // 4 = BLUE2 - Dark blue
                <0.128,0.500,0.128>,    // 5 = GREEN
                <1.000,0.000,1.000>,    // 6 = MAGENTA
                <1.000,0.000,0.000>,    // 7 = RED
                <1.000,0.500,0.000>,    // 8 = ORANGE
                <1.000,1.000,1.000>,    // 9 = WHITE
                <0.0,0.0,0.0>,          // 10 = BLACK
                <1.0,1.0,0.0>,          // 11 = YELLOW
                <0.0,0.8,0.8>,          // 12 = CYAN
                <0.5,0.0,0.0>,          // 13 = RED2
                <0.0,0.5,0.5>,          // 14 = TEAL
                <0.0,0.25,0.25>];       // 15 = GREEN2
                
vector color;
string ballcolor;  
string previousname;            
                



handle_control(key id, integer level, integer change) {

    if (Avatar == NullKey) {
        return;
    }
    
    if ((level & CONTROL_UP) && (level & CONTROL_DOWN)) {
        ControlsActive = ! ControlsActive;
        if (ControlsActive) {
            control_active();
        } else {
            control_ready();
        }
        return;
    }
    
    if (!ControlsActive) {
        return;
    }


    if (level & CONTROL_UP) {
        Offset.z += DELTA;
    } else if (level & CONTROL_DOWN) {
        Offset.z -= DELTA;
    } else if (level & CONTROL_LEFT) {
        Offset.y += DELTA;
    } else if (level & CONTROL_RIGHT) {
        Offset.y -= DELTA;
    } else if (level & CONTROL_FWD) {
        Offset.x += DELTA;
    } else if (level & CONTROL_BACK) {
        Offset.x -= DELTA;
    } else {
        return;
    }

    llSetPos(BallPos + Offset * BallRot + SitTargetRef - SitTarget);
}

// enter "control ready" state.
control_ready() {
    ControlsActive = 0;
    integer controls = CONTROL_UP | CONTROL_DOWN;

    llTakeControls(controls, TRUE, FALSE);
    llInstantMessage(Avatar, "Allow adjusting your position by pressing Pgup and Pgdn keys at the same time");
}

control_active() {
    integer controls = CONTROL_UP | CONTROL_DOWN | CONTROL_LEFT | CONTROL_RIGHT | CONTROL_FWD | CONTROL_BACK;

    llTakeControls(controls, TRUE, FALSE);
    llInstantMessage(Avatar, "Adjust your position using Pgup/Pgdn, Up/Down arrow, and shift-left/right arrow keys\nDisable by pressing Pgup and Pgdn keys at the same time");
    BallPos = llGetPos() + SitTarget - SitTargetRef;
    BallRot = llGetRot();
}



render() {
    if (!visible || (Avatar != NullKey && !Adjusting)) {
        // hidden
        llSetScale(<0.01,0.01,0.01>);
        llSetAlpha(0.0, ALL_SIDES);
        llSetText("",<1.0,1.0,1.0>,1.0);    
    } else if (Avatar != NullKey && Adjusting) {
        // sitting and adjusting
        llSetAlpha(0.2,ALL_SIDES);
        llSetText("Adjust",<1.0,1.0,1.0>,1.0);
        llSetScale(<0.1,0.1,5.0>);
    } else {
        // shown
        llSetAlpha(1.0, ALL_SIDES);
        llSetScale(<0.2,0.2,0.2>);
        if (Adjusting) {
            llSetText("Adjust",<1.0,1.0,1.0>,1.0);
        } else {
            llSetText(Name,<1.0,1.0,1.0>,1.0);
        }
    }
}

show() {
    visible = TRUE;
    render();
}

hide() {
    visible = FALSE;
    render();
}

default {
    on_rez(integer channel) {
       Name = llGetObjectDesc();
        if (Name == "" || Name == "(No Description)") {
            Name = "LOVE";
        } else {
            if (llSubStringIndex(Name, "*") == 0) {
                llSetPrimitiveParams([PRIM_PHANTOM, TRUE]);
                Name = llGetSubString(Name, 1, -1);
            }
            if (llSubStringIndex(Name, "+") == 0) {
                ADJUSTABLE = TRUE;
                Name = llGetSubString(Name, 1, -1);
            }
            if (Name == "none") {
                Name = "";
            }
        }
        llSitTarget(SitTarget, ZERO_ROTATION);
        if (Name != "") {
            llSetSitText(Name);
        } else {
            llSetSitText("");
        }
        Avatar = NullKey;
        Chan = channel;
        
       
        Group = 0;
        
        if (ListenHandle) {
            llListenRemove(ListenHandle);
            ListenHandle = 0;
        }

        if (Chan != 0) {
            ListenHandle = llListen(Chan,"",NullKey,"");
            // start timer unless ball was dragged from inv
            llSetTimerEvent(180.0);
        }
        
        if (llGetObjectDesc() == "ADJUSTABLE") {
            ADJUSTABLE = TRUE;
        }
        
       
    }
    

    changed(integer change) {
        if (change != CHANGED_LINK) return;
        Avatar = llAvatarOnSitTarget();
        if (Avatar != NullKey) {

            if (Group) {
                if (!llSameGroup(Avatar)) {
                    llUnSit(Avatar);
                    llWhisper(0,"no permission to use poseball");
                    return;
                }
            }
            if (ADJUSTABLE) {
                llRequestPermissions(Avatar, PERMISSION_TAKE_CONTROLS);
            }
        }
        llSay(Chan+8,(string)Avatar);     //requests perm, sets animation
        render();
        
    }

    listen(integer channel, string name, key object, string str) {
        
       
        
        if(channel == relaychannel)
        {
            
           list rlvmsg = llParseStringKeepNulls(str,[","],[]);
           
          
     
            if (llGetListLength(rlvmsg) > 3)
            {
                    string cmd = llList2String(rlvmsg,2); 
                    string resp = llList2String(rlvmsg,3);
                    
            
         if (cmd == "ping")
            {
               
                //pong if no one sat
                llRegionSay(relaychannel,"pong," + (string)llGetOwnerKey(object) +",!pong");
                
                

            } 
            
        else if( llList2String(rlvmsg,0) == "Capture")
        {
        if(resp == "ko")
        {
            
            llWhisper(0,llKey2Name(Victim) + "'s RLV Relay denied the restriction.");
            llSetObjectName(previousname);
             llSay(-657, "CaptureDenied"); 
             
        }
     }
        
    }
            
        }
            
            else
            {
        integer ix;
        ix = llSubStringIndex(str,">");    
        if (ix != -1) {
            
           
            if(llGetSubString(str,0,ix-1) == "Capture")
            {
             
              if(llAvatarOnSitTarget())
              {
                  
                  
                 llSay(-657, "AvatarOnSitTarget"); 
                  
              }
              
              else
              {  
               llSay(-657, "Captured");  
              Victim = llGetSubString(str,ix+1,-1); 
              previousname = llGetObjectName();
              llSetObjectName(name);
              relay_handle = llListen(relaychannel,"","","");
              llSay(relaychannel, "Capture" + "," + (string)Victim+ "," + "@sit:" + (string)llGetKey() + "=force"); 
              
              }
                
            }
            
            else if(llGetSubString(str,0,ix-1) == "Restrain" && llAvatarOnSitTarget() == Victim)
            {
               
               restraints =  llGetSubString(str,ix+1,-1); 
                llSay(relaychannel, "Restrain" + "," + (string)Victim+ "," + restraints); 
                llSetObjectName(previousname);
            }
            
            else
            {
            
            BallPos = (vector)llGetSubString(str,0,ix);
            BallRot = (rotation)llGetSubString(str,ix+1,-1);
            llSetPos(BallPos + SitTargetRef - SitTarget);
            llSetRot(BallRot);
            }
        } else if (str == "0") {    //HIDE
            hide();
        } else if (str == "SHOW") { //SHOW
            show();
        } else if (str == "ADJUST|1") {
            Adjusting = TRUE;
            render();
        } else if (str == "ADJUST|0") {
            Adjusting = FALSE;
            render();
        } else if (str == "SAVE") {
            llSay(Chan+16,(string)(llGetPos() + SitTarget - SitTargetRef)+(string)llGetRot());
        } else if (str == "GROUP") {
            Group = 1;
        } else if (str == "ALL") {
            Group = 0;
        } else if (str == "DIE") {
            
            if(Victim)
            {
             
              llSay(relaychannel, "Release" + "," + (string)Victim+ ",!release|@clear");   
                
            }
            llSay(Chan+8, (string)NullKey);    //msg to poser (don't reanimate after STOP)
            llDie();
        } else if (str == "LIVE") {
            llSetTimerEvent(180.0);
            // llSay(Chan+8,"ALIVE");    //msg to poser -> to menu
        } else if(str == "Release") {
            
         
          llSay(relaychannel, "Release" + "," + (string)Victim+ ",!release|@clear");
          Victim = NULL_KEY;
            
        } else {
           
            list ldata = llParseString2List(str, ["|"], []);
           
            integer colorIx = (integer) llList2String(ldata,0);
            
            string ballIx = llList2String(ldata,1);
            
            Adjusting = (integer) llList2String(ldata,2);
            if ((colorIx > 0) && (colorIx < 16)) { // this must be a color setting  
            color = llList2Vector(colors,colorIx);    
                llSetColor(color,ALL_SIDES);     //pull the color out of the list
           
                render();
                BallPos = llGetPos() + SitTarget - SitTargetRef;
                BallRot = llGetRot();
                
                llSleep((0.1*(float)ballIx)); 
                llSetObjectName("~ball" + ballIx);
                llSay(-657,(string)color); 
               
       
       
               
    
    
               
                }
                
                 
            }
            
            
        }
    }

    timer() {                       //not heard "LIVE" from menu for a while: suicide
        llDie();
    }

    run_time_permissions(integer perm) 
    { 
        if (llKey2Name(Avatar) == "") {
            return;
        }
        if (Avatar != llGetPermissionsKey()) {
            return;
        }

        if (perm & PERMISSION_TAKE_CONTROLS) {
            control_ready();
        }
    }

    moving_end() {
        vector newpos = llGetPos() + SitTarget - SitTargetRef;
        if (llVecMag(newpos - (BallPos + Offset)) > EPSILON) {
            // we didn't move it, someone else did.  Deal with it.
            BallPos = newpos;
        }
        BallRot = llGetRot();
    }

    control(key id, integer level, integer change) {
        handle_control(id, level, change);
    }
}