Difference between revisions of "MLPV2 RLV Plugin"

From Second Life Wiki
Jump to navigation Jump to search
m
 
(8 intermediate revisions by 4 users not shown)
Line 1: Line 1:
* [[MLPV2]]
* [[MLPV2]]
* [[MLPV2_Addons]]
* Back to [[MLPV2_Addons]]


This Plugin combines the possibilities of MLPV2 with the commands of the Restrained Love Viewer.
This Plugin combines the possibilities of MLPV2 with the commands of the Restrained Love Viewer.
Line 6: Line 6:
It reads a notecard, which must me named .RLV to define a RLV command for a Pose.
It reads a notecard, which must me named .RLV to define a RLV command for a Pose.


<lsl>// ----------------------------------------------------------------------------------
<source lang="lsl2">
// MLPV2 Plugin for RLV Script V1.0
// ----------------------------------------------------------------------------------
// MLPV2 Plugin for RLV Script V1.02
//
//
// Use with a notecard named .RLV with the format:
// Use with a notecard named .RLV with the format:
Line 13: Line 14:
// e.g.
// e.g.
// Missionary|1|@unsit=n
// Missionary|1|@unsit=n
// stand|*|!release
// ----------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------
// Copyright (c) 2010, Jenni Eales. All rights reserved.
// Copyright (c) 2010, Jenni Eales. All rights reserved.
Line 40: Line 42:
// Changes:
// Changes:
// ----------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------
// Version 1.02
// - use | as seperator
// - support multiple lines for one pose
// - additional link message for direct call from ball menu
//
// Version 1.01
// - use ; instead of | as seperator
//
// Version 1.0
// Version 1.0
// - first version released
// - first version released
Line 45: Line 55:
//
//
// constants
// constants
string NOTECARD        = ".RLV";
string NOTECARD        = ".RLV"; // name of config notecard
integer DEBUG          = FALSE;
integer DEBUG          = FALSE; // switch debug on/off
integer RELAY_CHANNEL  = -1812221819;
integer RELAY_CHANNEL  = -1812221819; // channel for RLV relay
float TIMER_SEC        = 1800.0; // 30 min.
float TIMER_SEC        = 1800.0; // 30 min.
string CMD_RELEASE      = "!release";
string CMD_RELEASE      = "!release"; // release command
 
integer LM_NUMBER      = -1819;  // number for link message
// internal use
// internal use
key kQuery;
key kQuery;
integer iLine;
integer iLine;


string pose = "stand";
string pose = "stand"; // pose set by the Ball Menu
list poses    = [];
list poses    = [];   // configured poses
list balls    = [];
list balls    = [];   // configured balls
list commands = [];
list commands = [];   // configured commands
list avatars  = [NULL_KEY, NULL_KEY, NULL_KEY, NULL_KEY, NULL_KEY, NULL_KEY];
list avatars  = [];   // avatars sitting on balls


// log a message
// log a message
log(string message)
log(string message)
{
{
    if(DEBUG) llOwnerSay(message);
if(DEBUG) llOwnerSay(message);
}
 
// only for testing purposes
log_data()
{
log("List:");
integer total_number = llGetListLength(commands);
integer i;
for(i=0; i<total_number; i++)
{
log("- " + llList2String(poses, i) + ": (" + llList2String(balls, i) + ") "+ llList2String(commands, i));
}
}
}


Line 70: Line 93:
relay(key avatar, string message)
relay(key avatar, string message)
{
{
    log("RLV: MLPV2," + (string) avatar + "," + message);
list tokens = llParseString2List(message, ["|"], [""]);
    llSay(RELAY_CHANNEL, "MLPV2," + (string) avatar + "," + message);
integer total_number = llGetListLength(tokens);
integer i;
for(i=0; i<total_number; i++)
{
string token = llList2String(tokens, i);
log("RLV: MLPV2," + (string) avatar + "," + token);
llSay(RELAY_CHANNEL, "MLPV2," + (string) avatar + "," + token);
}
}
}


Line 77: Line 107:
rlv(string pose)
rlv(string pose)
{
{
    log("command for " + pose);
log("command for: " + pose);
    llSetTimerEvent(TIMER_SEC);
llSetTimerEvent(TIMER_SEC);


    // get index for pose in poses
integer total_number = llGetListLength(poses);
    integer index = llListFindList(poses, [pose]);
integer i;
for(i=0; i<total_number; i++)
{
if (pose == llList2String(poses, i))
{
string ball = llList2String(balls, i);
log("command for pose: " + pose + " (" + ball + ")");
if (ball == "*")
{
integer b;
for (b=0; b<6; b++)
{
// get avatar for ball index
key avatar = llList2Key(avatars, b);
if(avatar != NULL_KEY) relay(avatar, llList2String(commands, i));
}
}
else
{
key avatar = llList2Key(avatars, (integer) ball);
if(avatar != NULL_KEY) relay(avatar, llList2String(commands, i));
}
}
}
}


    // get command from entries
// new find: pose and ball needs to match
    string command = llList2String(commands, index);
integer find_pose(string pose, string ball)
{
integer total_number = llGetListLength(poses);
integer i;
for(i=0; i<total_number; i++)
{
if (pose == llList2String(poses, i) && ball == llList2String(balls, i))
{
return i;
}
}
return -1;
}


    // get ball from balls
// add a pose/ball/command entry
    string ball = llList2String(balls, index);
add_pose(string pose, string ball, string command)
{
// look for existing entry for pose
integer found = find_pose(pose, ball);
if (found == -1)
{
// add a new pose
poses += [pose];
balls += [ball];
commands += [command];


    if (ball == "*")
log("Added from " + NOTECARD + ": " + pose + " | " + ball + " | " + command);
    {
}
        integer i;
else
        for (i=0; i<6; i++)
{
        {
// append to existing pose
            // get avatar for ball index
string c = llList2String(commands, found) + "|" + command;
            key avatar = llList2Key(avatars, i);
commands = llListReplaceList(commands, [c], found, found);


            // if not null send command
log("Added " + command + " to pose " + pose);
            if(avatar != NULL_KEY)
}
            {
                relay(avatar, command);
            }
        }
    }
    else
    {
        // get avatar for ball index
        key avatar = llList2Key(avatars, (integer) ball);
 
        // if not null send command
        if(avatar != NULL_KEY)
        {
            relay(avatar, command);
        }
    }
}
}


Line 120: Line 179:
process_line(string line)
process_line(string line)
{
{
    list tokens = llParseString2List(line, ["|"], [""]);
list tokens = llParseString2List(line, ["  |  ","  | "," |  "," | "," |","| ","|"],[""]);
    if(llGetListLength(tokens) != 3) return;
if(llGetListLength(tokens) < 3) return;


    string pose = llList2String(tokens, 0);
string pose = llList2String(tokens, 0);
    string ball = llList2String(tokens, 1);
string ball = llList2String(tokens, 1);
    string command = llList2String(tokens, 2);


    poses += [pose];
integer total_number = llGetListLength(tokens);
    balls += [ball];
integer i;
    commands += [command];
for(i=2; i<total_number; i++)
 
{
    log("Added from " + NOTECARD + ": " + pose + " | " + ball + "  | " + command);
add_pose(pose, ball, llList2String(tokens, i));
}
}
}


// check if object is in the inventory
// check if object is in the inventory
integer exists_notecard(string notecard) {
integer exists_notecard(string notecard) {
    return llGetInventoryType(notecard) == INVENTORY_NOTECARD;
return llGetInventoryType(notecard) == INVENTORY_NOTECARD;
}
}


// initialize me
// initialize me
reset()
initialize()
{
{
    poses = [];
log("Initializing...");
    balls = [];
poses = [];
    commands = [];
balls = [];
    avatars = [NULL_KEY, NULL_KEY, NULL_KEY, NULL_KEY, NULL_KEY, NULL_KEY];
commands = [];
    log("Reset");
avatars = [NULL_KEY, NULL_KEY, NULL_KEY, NULL_KEY, NULL_KEY, NULL_KEY];


    if (exists_notecard(NOTECARD))
if (exists_notecard(NOTECARD))
    {
{
        log("Reading " + NOTECARD + "...");
log("Reading " + NOTECARD + "...");
        iLine = 0;
iLine = 0;
        kQuery = llGetNotecardLine(NOTECARD, iLine);
kQuery = llGetNotecardLine(NOTECARD, iLine);
    }
}
    else
else
    {
{
        llOwnerSay("Not found: " + NOTECARD );
llOwnerSay("Not found: " + NOTECARD );
    }
}
}
}


default
default
{
{
    state_entry()
// on state entry initialize me
    {
state_entry()
        reset();
{
    }
initialize();
}


    // reset on Inventory change
// reset on Inventory change
    changed(integer change)
changed(integer change)
    {
{
        if (change & CHANGED_INVENTORY) {
if (change & CHANGED_INVENTORY) initialize();
            reset();
}
        }
    }


    // read a line from notecard
// read a line from notecard
    dataserver(key _query_id, string _data) {
dataserver(key _query_id, string _data) {
        // were we called to work on notecard?
// were we called to work on notecard?
        if (_query_id == kQuery) {
if (_query_id == kQuery) {
            // this is a line of our notecard
// this is a line of our notecard
            if (_data == EOF) {
if (_data == EOF) {
                log(NOTECARD + " read.");
if (DEBUG) log_data();
            } else {
log(NOTECARD + " read.");
                    // increment line count
} else {
                    // data has the current line from this notecard
// increment line count
                    if(_data != "") {
// data has the current line from this notecard
                        process_line(_data);
if(_data != "") {
                    }
process_line(_data);
}


                // request next line
// request next line
                // read another line when you can
// read another line when you can
                iLine++;
iLine++;
                kQuery = llGetNotecardLine(NOTECARD, iLine);
kQuery = llGetNotecardLine(NOTECARD, iLine);
            }
}
        }
}
    }
}


    timer()
timer()
    {
{
        integer i;
integer b;
        for (i=0; i<6; i++)
for (b=0; b<6; b++)
        {
{
            // get avatar for ball index
// get avatar for ball index
            key avatar = llList2Key(avatars, i);
key avatar = llList2Key(avatars, b);


            // if not null send command
// if not null send command
            if(avatar != NULL_KEY)
if(avatar != NULL_KEY) relay(avatar, CMD_RELEASE);
            {
}
                relay(avatar, CMD_RELEASE);
llSetTimerEvent(0.0);
            }
}
        }
        llSetTimerEvent(0.0);
    }


    // link message from MLPV2
// link message from MLPV2
    link_message(integer sender_number, integer number, string message, key id)
link_message(integer sender_number, integer number, string message, key id)
    {
{
        if (number == 0 && message == "POSEB") {
// pose from ball menu selected
            pose = (string)id;
if (number == 0 && message == "POSEB") {
            rlv(pose);
pose = (string)id;
            return;
rlv(pose);
        }
return;
}


        // unsit avatar
// direct call from ball menu
        if (number == -11001)
// configure in .MENUITEMS with
        {
// LINKMSG Resctriction | 0,-1,-1819,Restriction
            integer ball = (integer) message;
if (number == LM_NUMBER) {
            log("Avatar unsit from ball " + (string) ball +": " + (string) llList2Key(avatars, ball));
pose = message;
            relay(llList2Key(avatars, ball), CMD_RELEASE);
rlv(pose);
            avatars = llListReplaceList(avatars, [NULL_KEY], ball, ball);
return;
        }
}


        // sit avatar
// unsit avatar
        if (number == -11000)
if (number == -11001)
        {
{
            list tokens = llParseString2List(message, ["|"], [" "]);
integer ball = (integer) message;
            integer ball = llList2Integer(tokens, 0);
log("Avatar unsit from ball " + (string) ball +": " + (string) llList2Key(avatars, ball));
            log("Avatar sit on ball " + (string) ball +": " + (string) id);
relay(llList2Key(avatars, ball), CMD_RELEASE);
            avatars = llListReplaceList(avatars, [id], ball, ball);
avatars = llListReplaceList(avatars, [NULL_KEY], ball, ball);
            rlv(pose);
}
        }
 
    }
// sit avatar
}</lsl>
if (number == -11000)
{
list tokens = llParseString2List(message, ["|"], [" "]);
integer ball = llList2Integer(tokens, 0);
log("Avatar sit on ball " + (string) ball +": " + (string) id);
avatars = llListReplaceList(avatars, [id], ball, ball);
rlv(pose);
}
}
}</source>


The .RLV notecard might be look like this:
The .RLV notecard might be look like this:


  Trapped|0|@unsit=n
  Trapped | 0 | @unsit=n
  Strip|0|@remoutfit:undershirt=force|@remoutfit:underpants=force|@remoutfit:shirt=force|@remoutfit:pants=force|@remoutfit:socks=force
Strip | 0 | @remoutfit:shirt=force|@remoutfit:pants=force|@remoutfit:socks=force
  stand|*|!release
  Strip | 0 | @remoutfit:undershirt=force|@remoutfit:underpants=force
stand | * | !release
 
=== Fully Featured RLV Menu EXample ===
 
The following configuration adds a full RLV menu to your device. Make sure to add TOMENU RLV to your .MENUITEMS notecard to enable access to the RLV functions. This configuration applies the restrictions only to the avatar sitting on the poseball number 0. Replace the 0 with the appropriate number if your device has a different configuration. Devices that have the victim on different poseballs in different poses will have to be adjusted to use the same poseball for all poses. You also might want to add @touchworld=n|@fartouch=n to the above config for each pose to prevent the victim from calling up the MLP2 menu and touching other people's attachments and things out of reach.
 
Add this to your .RLV notecard:
 
NoWear | 0 | @addoutfit=n
NoUnwear | 0 | @remoutfit=n
Wear | 0 | @addoutfit=y
Unwear | 0 | @remoutfit=y
StripUpper | 0 | @addoutfit=n|@remoutfit:shirt=force|@remoutfit:undershirt=force
StripUpper | 0 | @remoutfit:jacket=force|@remoutfit:gloves=force
StripLower | 0 | @addoutfit=n|@addattach=n|@remoutfit:pants=force
StripLower | 0 | @remoutfit:underpants=force|@remoutfit:skirt=force|@remoutfit:socks=force
StripLower | 0 | @remoutfit:shoes=force|@remattach:pelvis=force|@remattach:stomach=force
StripLower | 0 | @remattach:left foot=force|@remattach:right foot=force
StripAll | 0 | @addoutfit=n|@remoutfit=force
NoAttach | 0 | @addattach=n
NoDetach | 0 | @remattach=n
Attach | 0 | @addattach=y
Detach | 0 | @remattach=y
StripJacket | 0 | @addoutfit=n|@remoutfit:jacket=force
StripShirt | 0 | @addoutfit=n|@remoutfit:shirt=force
StripGloves | 0 | @addoutfit=n|@remoutfit:gloves=force
StripUndershirt | 0 | @addoutfit=n|@remoutfit:undershirt=force
StripPants | 0 | @addoutfit=n|@remoutfit:pants=force
StripSkirt | 0 | @addoutfit=n|@addattach=n|@remoutfit:skirt=force|@remattach:stomach=force
StripSkirt | 0 | @remattach:pelvis=force
StripSocks | 0 | @addoutfit=n|@remoutfit:socks=force
StripShoes | 0 | @addoutfit=n|@addattach=n|@remoutfit:shoes=force
StripShoes | 0 | @remattach:left foot=force|@remattach:right foot=force
NoShout | 0 | @chatshout=n
Shout | 0 | @chatshout=y
NoNormal | 0 | @chatnormal=n
Normal | 0 | @chatnormal=y
Mute | 0 | @sendchat=n|@emote=add
Unmute | 0 | @sendchat=y|@emote=rem
Deaf | 0 | @recvchat=n
Hear | 0 | @recvchat=y
MuteIM | 0 | @sendim=n
UnmuteIM | 0 | @sendim=y
DeafIM | 0 | @recvim=n
HearIM | 0 | @recvim=y
NoMinimap | 0 | @showminimap=n
Minimap | 0 | @showminimap=y
NoMap | 0 | @showworldmap=n
Map | 0 | @showworldmap=y
NoLocation | 0 | @showloc=n
Location | 0 | @showloc=y
NoNames | 0 | @shownames=n
Names | 0 | @shownames=y
NoInventory | 0 | @showinv=n
Inventory | 0 | @showinv=y
NoRez | 0 | @rez=n
Rez | 0 | @rez=y
NoEdit | 0 | @edit=n
Edit | 0 | @edit=y
NoNotecards | 0 | @viewnote=n
Notecards | 0 | @viewnote=y
NoScripts | 0 | @viewscript=n
Scripts | 0 | @viewscript=y
NoTextures | 0 | @vewtexture=n
Textures | 0 | @vewtexture=y
  stand | * | !release
 
.MENUITEMS.RLV notecard
 
MENU RLV
TOMENU Clothes
TOMENU Chat
TOMENU Disorient
TOMENU Other
BACK
MENU Clothes | ALL
LINKMSG No Wear | 0,-1,-1819,NoWear
LINKMSG No Unwear | 0,-1,-1819,NoUnwear
LINKMSG Wear | 0,-1,-1819,Wear
LINKMSG Unwear | 0,-1,-1819,Unwear
LINKMSG Strip Upper | 0,-1,-1819,StripUpper
LINKMSG Strip Lower | 0,-1,-1819,StripLower
LINKMSG Strip All | 0,-1,-1819,StripAll
TOMENU Strip Single
LINKMSG No Attach | 0,-1,-1819,NoAttach
LINKMSG No Detach | 0,-1,-1819,NoDetach
LINKMSG Attach | 0,-1,-1819,Attach
LINKMSG Detach | 0,-1,-1819,Detach
BACK
MENU Strip Single
LINKMSG Jacket | 0,-1,-1819,StripJacket
LINKMSG Shirt | 0,-1,-1819,StripShirt
LINKMSG Gloves | 0,-1,-1819,StripGloves
LINKMSG Undershirt | 0,-1,-1819,StripUndershirt
LINKMSG Pants | 0,-1,-1819,StripPants
LINKMSG Skirt | 0,-1,-1819,StripSkirt
LINKMSG Socks | 0,-1,-1819,StripSocks
LINKMSG Shoes | 0,-1,-1819,StripShoes
BACK
MENU Chat | ALL
LINKMSG No Shout | 0,-1,-1819,NoShout
LINKMSG Shout | 0,-1,-1819,Shout
LINKMSG No Normal | 0,-1,-1819,NoNormal
LINKMSG Normal | 0,-1,-1819,Normal
LINKMSG Mute | 0,-1,-1819,Mute
LINKMSG Unmute | 0,-1,-1819,Unmute
LINKMSG Deaf | 0,-1,-1819,Deaf
LINKMSG Hear | 0,-1,-1819,Hear
LINKMSG Mute IM| 0,-1,-1819,MuteIM
LINKMSG Unmute IM| 0,-1,-1819,UnmuteIM
LINKMSG Deaf IM| 0,-1,-1819,DeafIM
LINKMSG Hear IM| 0,-1,-1819,HearIM
BACK
MENU Disorient
LINKMSG No Minimap | 0,-1,-1819,NoMinimap
LINKMSG Minimap | 0,-1,-1819,Minimap
LINKMSG No Map | 0,-1,-1819,NoMap
LINKMSG Map | 0,-1,-1819,Map
LINKMSG No Location | 0,-1,-1819,NoLocation
LINKMSG Location | 0,-1,-1819,Location
LINKMSG No Names | 0,-1,-1819,NoNames
LINKMSG Names | 0,-1,-1819,Names
BACK
MENU Other
LINKMSG No Inventory | 0,-1,-1819,NoInventory
LINKMSG Inventory | 0,-1,-1819,Inventory
LINKMSG No Rez | 0,-1,-1819,NoRez
LINKMSG Rez | 0,-1,-1819,Rez
LINKMSG No Edit | 0,-1,-1819,NoEdit
LINKMSG Edit | 0,-1,-1819,Edit
LINKMSG No Notecards | 0,-1,-1819,NoNotecards
LINKMSG Notecards | 0,-1,-1819,Notecards
LINKMSG No Scripts | 0,-1,-1819,NoScripts
LINKMSG Scripts | 0,-1,-1819,Scripts
LINKMSG No Textures | 0,-1,-1819,NoTextures
LINKMSG Textures | 0,-1,-1819,Textures
BACK
 
See also:
* [[LSL Protocol/RestrainedLoveAPI]]
* [[LSL_Protocol/Restrained_Life_Relay/Specification]]
 
 
[[Category:MLPV2]]

Latest revision as of 08:01, 19 April 2016

This Plugin combines the possibilities of MLPV2 with the commands of the Restrained Love Viewer.

It reads a notecard, which must me named .RLV to define a RLV command for a Pose.

// ----------------------------------------------------------------------------------
// MLPV2 Plugin for RLV Script V1.02
//
// Use with a notecard named .RLV with the format:
// Pose|Ball no.: 0,1...*|@RLV-Command
// e.g.
// Missionary|1|@unsit=n
// stand|*|!release
// ----------------------------------------------------------------------------------
// Copyright (c) 2010, Jenni Eales. All rights reserved.
// ----------------------------------------------------------------------------------
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
//    * Redistributions of source code must retain the above copyright notice,
//      this list of conditions and the following disclaimer.
//    * Redistributions in binary form must reproduce the above copyright notice,
//      this list of conditions and the following disclaimer in the documentation
//      and/or other materials provided with the distribution.
//    * The names of its contributors may not be used to endorse or promote products
//      derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
// OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
// OF SUCH DAMAGE.
//
// Changes:
// ----------------------------------------------------------------------------------
// Version 1.02
// - use | as seperator
// - support multiple lines for one pose
// - additional link message for direct call from ball menu
//
// Version 1.01
// - use ; instead of | as seperator
//
// Version 1.0
// - first version released
// ----------------------------------------------------------------------------------
//
// constants
string NOTECARD         = ".RLV"; // name of config notecard
integer DEBUG           = FALSE;  // switch debug on/off
integer RELAY_CHANNEL   = -1812221819; // channel for RLV relay
float TIMER_SEC         = 1800.0; // 30 min.
string CMD_RELEASE      = "!release";  // release command
integer LM_NUMBER       = -1819;  // number for link message
	
// internal use
key kQuery;
integer iLine;

string pose = "stand"; // pose set by the Ball Menu
list poses    = [];    // configured poses
list balls    = [];    // configured balls
list commands = [];    // configured commands
list avatars  = [];    // avatars sitting on balls

// log a message
log(string message)
{
	if(DEBUG) llOwnerSay(message);
}

// only for testing purposes
log_data()
{
	log("List:");
	integer total_number = llGetListLength(commands);
	integer i;
	for(i=0; i<total_number; i++)
	{
		log("- " + llList2String(poses, i) + ": (" + llList2String(balls, i) + ") "+ llList2String(commands, i));
	}
}

// write a message to the RLV Relay
relay(key avatar, string message)
{
	list tokens = llParseString2List(message, ["|"], [""]);
	integer total_number = llGetListLength(tokens);
	integer i;
	for(i=0; i<total_number; i++)
	{
		string token = llList2String(tokens, i);
		log("RLV: MLPV2," + (string) avatar + "," + token);
		llSay(RELAY_CHANNEL, "MLPV2," + (string) avatar + "," + token);
	}
}

// find commands for pose and play it for avatars on ball
rlv(string pose)
{
	log("command for: " + pose);
	llSetTimerEvent(TIMER_SEC);

	integer total_number = llGetListLength(poses);
	integer i;
	for(i=0; i<total_number; i++)
	{
		if (pose == llList2String(poses, i))
		{
			string ball = llList2String(balls, i);
			log("command for pose: " + pose + " (" + ball + ")");
			if (ball == "*")
			{
				integer b;
				for (b=0; b<6; b++)
				{
					// get avatar for ball index
					key avatar = llList2Key(avatars, b);
					if(avatar != NULL_KEY) relay(avatar, llList2String(commands, i));
				}
			}
			else
			{
				key avatar = llList2Key(avatars, (integer) ball);
				if(avatar != NULL_KEY) relay(avatar, llList2String(commands, i));
			}
		}
	}
}

// new find: pose and ball needs to match
integer find_pose(string pose, string ball)
{
	integer total_number = llGetListLength(poses);
	integer i;
	for(i=0; i<total_number; i++)
	{
		if (pose == llList2String(poses, i) && ball == llList2String(balls, i))
		{
			return i;
		}
	}
	return -1;
}

// add a pose/ball/command entry
add_pose(string pose, string ball, string command)
{
	// look for existing entry for pose
	integer found = find_pose(pose, ball);
	if (found == -1)
	{
		// add a new pose
		poses += [pose];
		balls += [ball];
		commands += [command];

		log("Added from " + NOTECARD + ": " + pose + " | " + ball + " | " + command);
	}
	else
	{
		// append to existing pose
		string c = llList2String(commands, found) + "|" + command;
		commands = llListReplaceList(commands, [c], found, found);

		log("Added " + command + " to pose " + pose);
	}
}

// parse and store a line
process_line(string line)
{
	list tokens = llParseString2List(line, ["  |  ","  | "," |  "," | "," |","| ","|"],[""]);
	if(llGetListLength(tokens) < 3) return;

	string pose = llList2String(tokens, 0);
	string ball = llList2String(tokens, 1);

	integer total_number = llGetListLength(tokens);
	integer i;
	for(i=2; i<total_number; i++)
	{
		add_pose(pose, ball, llList2String(tokens, i));
	}
}

// check if object is in the inventory
integer exists_notecard(string notecard) {
	return llGetInventoryType(notecard) == INVENTORY_NOTECARD;
}

// initialize me
initialize()
{
	log("Initializing...");
	poses = [];
	balls = [];
	commands = [];
	avatars = [NULL_KEY, NULL_KEY, NULL_KEY, NULL_KEY, NULL_KEY, NULL_KEY];

	if (exists_notecard(NOTECARD))
	{
		log("Reading " + NOTECARD + "...");
		iLine = 0;
		kQuery = llGetNotecardLine(NOTECARD, iLine);
	}
	else
	{
		llOwnerSay("Not found: " + NOTECARD );
	}
}

default
{
	// on state entry initialize me
	state_entry()
	{
		initialize();
	}

	// reset on Inventory change
	changed(integer change)
	{
		if (change & CHANGED_INVENTORY) initialize();
	}

	// read a line from notecard
	dataserver(key _query_id, string _data) {
		// were we called to work on notecard?
		if (_query_id == kQuery) {
			// this is a line of our notecard
			if (_data == EOF) {
				if (DEBUG) log_data();
				log(NOTECARD + " read.");
			} else {
					// increment line count
					// data has the current line from this notecard
					if(_data != "") {
						process_line(_data);
					}

				// request next line
				// read another line when you can
				iLine++;
				kQuery = llGetNotecardLine(NOTECARD, iLine);
			}
		}
	}

	timer()
	{
		integer b;
		for (b=0; b<6; b++)
		{
			// get avatar for ball index
			key avatar = llList2Key(avatars, b);

			// if not null send command
			if(avatar != NULL_KEY) relay(avatar, CMD_RELEASE);
		}
		llSetTimerEvent(0.0);
	}

	// link message from MLPV2
	link_message(integer sender_number, integer number, string message, key id)
	{
		// pose from ball menu selected
		if (number == 0 && message == "POSEB") {
			pose = (string)id;
			rlv(pose);
			return;
		}

		// direct call from ball menu
		// configure in .MENUITEMS with
		// LINKMSG Resctriction | 0,-1,-1819,Restriction
		if (number == LM_NUMBER) {
			pose = message;
			rlv(pose);
			return;
		}

		// unsit avatar
		if (number == -11001)
		{
			integer ball = (integer) message;
			log("Avatar unsit from ball " + (string) ball +": " + (string) llList2Key(avatars, ball));
			relay(llList2Key(avatars, ball), CMD_RELEASE);
			avatars = llListReplaceList(avatars, [NULL_KEY], ball, ball);
		}

		// sit avatar
		if (number == -11000)
		{
			list tokens = llParseString2List(message, ["|"], [" "]);
			integer ball = llList2Integer(tokens, 0);
			log("Avatar sit on ball " + (string) ball +": " + (string) id);
			avatars = llListReplaceList(avatars, [id], ball, ball);
			rlv(pose);
		}
	}
}

The .RLV notecard might be look like this:

Trapped | 0 | @unsit=n
Strip | 0 | @remoutfit:shirt=force|@remoutfit:pants=force|@remoutfit:socks=force
Strip | 0 | @remoutfit:undershirt=force|@remoutfit:underpants=force
stand | * | !release

Fully Featured RLV Menu EXample

The following configuration adds a full RLV menu to your device. Make sure to add TOMENU RLV to your .MENUITEMS notecard to enable access to the RLV functions. This configuration applies the restrictions only to the avatar sitting on the poseball number 0. Replace the 0 with the appropriate number if your device has a different configuration. Devices that have the victim on different poseballs in different poses will have to be adjusted to use the same poseball for all poses. You also might want to add @touchworld=n|@fartouch=n to the above config for each pose to prevent the victim from calling up the MLP2 menu and touching other people's attachments and things out of reach.

Add this to your .RLV notecard:

NoWear | 0 | @addoutfit=n
NoUnwear | 0 | @remoutfit=n
Wear | 0 | @addoutfit=y
Unwear | 0 | @remoutfit=y
StripUpper | 0 | @addoutfit=n|@remoutfit:shirt=force|@remoutfit:undershirt=force
StripUpper | 0 | @remoutfit:jacket=force|@remoutfit:gloves=force
StripLower | 0 | @addoutfit=n|@addattach=n|@remoutfit:pants=force
StripLower | 0 | @remoutfit:underpants=force|@remoutfit:skirt=force|@remoutfit:socks=force
StripLower | 0 | @remoutfit:shoes=force|@remattach:pelvis=force|@remattach:stomach=force
StripLower | 0 | @remattach:left foot=force|@remattach:right foot=force
StripAll | 0 | @addoutfit=n|@remoutfit=force
NoAttach | 0 | @addattach=n
NoDetach | 0 | @remattach=n
Attach | 0 | @addattach=y
Detach | 0 | @remattach=y

StripJacket | 0 | @addoutfit=n|@remoutfit:jacket=force
StripShirt | 0 | @addoutfit=n|@remoutfit:shirt=force
StripGloves | 0 | @addoutfit=n|@remoutfit:gloves=force
StripUndershirt | 0 | @addoutfit=n|@remoutfit:undershirt=force
StripPants | 0 | @addoutfit=n|@remoutfit:pants=force
StripSkirt | 0 | @addoutfit=n|@addattach=n|@remoutfit:skirt=force|@remattach:stomach=force
StripSkirt | 0 | @remattach:pelvis=force
StripSocks | 0 | @addoutfit=n|@remoutfit:socks=force
StripShoes | 0 | @addoutfit=n|@addattach=n|@remoutfit:shoes=force
StripShoes | 0 | @remattach:left foot=force|@remattach:right foot=force

NoShout | 0 | @chatshout=n
Shout | 0 | @chatshout=y
NoNormal | 0 | @chatnormal=n
Normal | 0 | @chatnormal=y
Mute | 0 | @sendchat=n|@emote=add
Unmute | 0 | @sendchat=y|@emote=rem
Deaf | 0 | @recvchat=n
Hear | 0 | @recvchat=y
MuteIM | 0 | @sendim=n
UnmuteIM | 0 | @sendim=y
DeafIM | 0 | @recvim=n
HearIM | 0 | @recvim=y

NoMinimap | 0 | @showminimap=n
Minimap | 0 | @showminimap=y
NoMap | 0 | @showworldmap=n
Map | 0 | @showworldmap=y
NoLocation | 0 | @showloc=n
Location | 0 | @showloc=y
NoNames | 0 | @shownames=n
Names | 0 | @shownames=y

NoInventory | 0 | @showinv=n
Inventory | 0 | @showinv=y
NoRez | 0 | @rez=n
Rez | 0 | @rez=y
NoEdit | 0 | @edit=n
Edit | 0 | @edit=y
NoNotecards | 0 | @viewnote=n
Notecards | 0 | @viewnote=y
NoScripts | 0 | @viewscript=n
Scripts | 0 | @viewscript=y
NoTextures | 0 | @vewtexture=n
Textures | 0 | @vewtexture=y

stand | * | !release

.MENUITEMS.RLV notecard

MENU RLV
TOMENU Clothes
TOMENU Chat
TOMENU Disorient
TOMENU Other
BACK

MENU Clothes | ALL
LINKMSG No Wear | 0,-1,-1819,NoWear
LINKMSG No Unwear | 0,-1,-1819,NoUnwear
LINKMSG Wear | 0,-1,-1819,Wear
LINKMSG Unwear | 0,-1,-1819,Unwear
LINKMSG Strip Upper | 0,-1,-1819,StripUpper
LINKMSG Strip Lower | 0,-1,-1819,StripLower
LINKMSG Strip All | 0,-1,-1819,StripAll
TOMENU Strip Single
LINKMSG No Attach | 0,-1,-1819,NoAttach
LINKMSG No Detach | 0,-1,-1819,NoDetach
LINKMSG Attach | 0,-1,-1819,Attach
LINKMSG Detach | 0,-1,-1819,Detach
BACK

MENU Strip Single
LINKMSG Jacket | 0,-1,-1819,StripJacket
LINKMSG Shirt | 0,-1,-1819,StripShirt
LINKMSG Gloves | 0,-1,-1819,StripGloves
LINKMSG Undershirt | 0,-1,-1819,StripUndershirt
LINKMSG Pants | 0,-1,-1819,StripPants
LINKMSG Skirt | 0,-1,-1819,StripSkirt
LINKMSG Socks | 0,-1,-1819,StripSocks
LINKMSG Shoes | 0,-1,-1819,StripShoes
BACK

MENU Chat | ALL
LINKMSG No Shout | 0,-1,-1819,NoShout
LINKMSG Shout | 0,-1,-1819,Shout
LINKMSG No Normal | 0,-1,-1819,NoNormal
LINKMSG Normal | 0,-1,-1819,Normal
LINKMSG Mute | 0,-1,-1819,Mute
LINKMSG Unmute | 0,-1,-1819,Unmute
LINKMSG Deaf | 0,-1,-1819,Deaf
LINKMSG Hear | 0,-1,-1819,Hear
LINKMSG Mute IM| 0,-1,-1819,MuteIM
LINKMSG Unmute IM| 0,-1,-1819,UnmuteIM
LINKMSG Deaf IM| 0,-1,-1819,DeafIM
LINKMSG Hear IM| 0,-1,-1819,HearIM
BACK

MENU Disorient
LINKMSG No Minimap | 0,-1,-1819,NoMinimap
LINKMSG Minimap | 0,-1,-1819,Minimap
LINKMSG No Map | 0,-1,-1819,NoMap
LINKMSG Map | 0,-1,-1819,Map
LINKMSG No Location | 0,-1,-1819,NoLocation
LINKMSG Location | 0,-1,-1819,Location
LINKMSG No Names | 0,-1,-1819,NoNames
LINKMSG Names | 0,-1,-1819,Names
BACK

MENU Other
LINKMSG No Inventory | 0,-1,-1819,NoInventory
LINKMSG Inventory | 0,-1,-1819,Inventory
LINKMSG No Rez | 0,-1,-1819,NoRez
LINKMSG Rez | 0,-1,-1819,Rez
LINKMSG No Edit | 0,-1,-1819,NoEdit
LINKMSG Edit | 0,-1,-1819,Edit
LINKMSG No Notecards | 0,-1,-1819,NoNotecards
LINKMSG Notecards | 0,-1,-1819,Notecards
LINKMSG No Scripts | 0,-1,-1819,NoScripts
LINKMSG Scripts | 0,-1,-1819,Scripts
LINKMSG No Textures | 0,-1,-1819,NoTextures
LINKMSG Textures | 0,-1,-1819,Textures
BACK

See also: