MLPV2 RLV Plugin

* MLPV2
* Back to MLPV2_Addons
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:
* LSL Protocol/RestrainedLoveAPI
* LSL_Protocol/Restrained_Life_Relay/Specification
Category:MLPV2