Difference between revisions of "TMCP"
(TMCP - MLP Compatible Pose Scripts - Open source (BSD license)) |
|||
Line 8: | Line 8: | ||
Resident 'Camden McAndrews' voluntueered as new maintainer. I hope the MLPv2 team will pick it up. | Resident 'Camden McAndrews' voluntueered as new maintainer. I hope the MLPv2 team will pick it up. | ||
==Features== | |||
* MLPv2 notecard & plugin compatible | |||
* Fast & Stable operation | |||
* Intelligent menu | |||
* No poseballs | |||
* Plenty memory for larger animation sets | |||
==Limitations== | |||
* No facial animations (yet) | |||
* No properties | |||
(Both limitations are relative easily added, yet not implemented in version 0.37) | |||
==TMCP Sitmanager== | ==TMCP Sitmanager== |
Revision as of 11:36, 15 August 2012
TMCP - Tano's MLP Compatible Pose scripts
- a drop in replacement for MLPv2
- configuration notecard compatible
- Since august 14. 2012 released under the open source BSD license
Collection of scripts as of version 0.37 below, or available on marketplace for your convenience https://marketplace.secondlife.com/p/TMCP-Tanos-MLP-Compatible-Pose-Scripts-Source-Edition/2964611
Resident 'Camden McAndrews' voluntueered as new maintainer. I hope the MLPv2 team will pick it up.
Features
- MLPv2 notecard & plugin compatible
- Fast & Stable operation
- Intelligent menu
- No poseballs
- Plenty memory for larger animation sets
Limitations
- No facial animations (yet)
- No properties
(Both limitations are relative easily added, yet not implemented in version 0.37)
TMCP Sitmanager
<lsl> //keeps track of sitted target, position them and animate them.
list animPool; //pool of scripts that are able to pose targets
list poolCache; //try re-use same script for same agent, to avoid reasking permissions
list sitted; //strided, holds: AGENT_KEY, logical offset (`ball number`), [animscript?], current animation?, current position? integer sittedStride=3;
list sittedAgents; list sitScripts;
integer MSG_AGENT_UNSIT = 380; integer MSG_ALL_UNSIT =381; integer MSG_DISABLE_SITTING =382; integer MSG_REGISTER_SITSCRIPT =383; integer MSG_QUERY_SITSCRIPT = 385; integer MSG_RESET =0; integer MSG_ENABLE_SITTING=0; integer MSG_SET_SITTARGET=375; integer MSG_PLAY_ANIM=390; integer MSG_STOP_ANIM=391; integer MSG_AGENT_PLAY_ANIM=395; // to notify the slave script integer MSG_AGENT_STOP_ANIM=396; integer MSG_ATTACH_AGENT = 398; integer MSG_DETACH_AGENT = 399; integer MSG_RUN_ANIMS=379;
integer MSG_DO_STOP=353; integer MSG_DO_SWAP=354; integer MSG_MENU_SWAP=355;
integer MSG_MENU_META=333;
integer MSG_MLP_SITTED=-11000; integer MSG_MLP_UNSITTED=-11001;
integer MSG_MODE_BALLUSERS=366;
integer MSG_DO_DEFAULT_POSE = 358;
integer MSG_RLV_GRAB=308; integer MSG_RLV_RELEASE=309; integer MSG_RLV_VICTIM_LIST=307;
integer MSG_DO_MENU_CURRENTMENU = 305;
//keys to controlling:
//animname+index is key to both real animation name and it's position.
list agentAnims;
list agentScripts; list agentScriptCache;
list agentPoseIdx;
list usedIndexes; //logical pose positions, register in here.
integer MAX_SITTING=1;
string currentAnim; integer currentAnimSeats;
list menuMeta;
integer mode_ballusers;
key grabberID;
list potentionalVictims; list victimsWithRelay;
list grabbingVictims; //commanded to, but not yet suited list grabbedVictims; //victims tagged as victim, sitted
integer CAPTURE_RANGE = 35; integer RELAY_CHANNEL = -1812221819; integer relay_handle;
integer timer_reason_versioncheck; integer rlv_victim_dialog_handle; integer rlv_victim_dialog_channel;
integer timer_versioncheck=50; integer timer_closechannel=51; integer timer_closechannel2=53; integer timer_defaultpose=52; integer timer_cleargrabbing=55;
assignScript (key id) {
integer i=llListFindList (agentScripts, [id]); if (i>=0) //agent already got assignment return; i=llListFindList (agentScriptCache, [id]); string candidate; if (i>=0) { candidate=llList2String(agentScriptCache, i+1); if (-1!=llListFindList(agentScripts, [candidate])) candidate=""; } if (candidate);else { //try to find a new one for (i=0; i<llGetListLength(sitScripts); i++) { string ss=llList2String(sitScripts, i); if (-1==llListFindList(agentScripts, [ss])) {//found free one candidate=ss; jump break; } } @break; } if (candidate) { agentScripts+=[id, candidate]; i=llListFindList (agentScriptCache, [candidate]); if (-1<i) agentScriptCache=llDeleteSubList(agentScriptCache, i-1, i); agentScriptCache+=[id, candidate]; //llOwnerSay ((string)id+" attached to "+candidate); llMessageLinked(LINK_THIS, MSG_ATTACH_AGENT, candidate, id); } else { llRegionSayTo (id, 0, "Sorry, but there is no available script to animate your avatar."); }
}
integer assignPoseIdx (key id) {
//start counting at 1.. just because we can. integer i=llListFindList (agentPoseIdx, [id]); if (-1<i) return llList2Integer(agentPoseIdx, i+1); //agent already got a pose assigned for (i=1; i<100; i++) { if (-1==llListFindList(agentPoseIdx, [i])) //found empty one { agentPoseIdx+=[id, i]; //llInstantMessage (id, "You are assigned to pose index "+(string)i); return i; } } return 0;
}
dropAgent (key id) {
//clean up stuff agent had
}
stopAnims(key id) {
llMessageLinked (LINK_THIS, MSG_AGENT_STOP_ANIM, "", id);
}
applyAnims() {
//apply animation (and position?) for all agents involved //send list of keys with indexes to position. it should know which anim is current. llMessageLinked (LINK_THIS, MSG_RUN_ANIMS, llList2CSV(agentPoseIdx), ""); //llOwnerSay ("anims/poses "+llList2CSV(agentPoseIdx));
}
cleanUp(key id) {
stopAnims(id); integer i=llListFindList (agentScripts, [id]); if (-1<i) agentScripts = llDeleteSubList (agentScripts, i, i+1); i=llListFindList (agentPoseIdx, [id]); if (-1<i) agentPoseIdx = llDeleteSubList (agentPoseIdx, i, i+1);
i=llListFindList (grabbedVictims, [id]); if (-1<i) grabbedVictims = llDeleteSubList (grabbedVictims, i, i);
}
setSitTarget() {
//needs pose info.. so we calculate the first available index, and match info with current pose.. it's for position script, i think. //linkedmessage (setsittarget, [posename,] index).. //posename might be cached by positioner and/or set to defaults. integer idx=1; //find first available while (-1<llListFindList(agentPoseIdx, [idx])) idx++; if (idx>MAX_SITTING) { llSitTarget (ZERO_VECTOR, ZERO_ROTATION/llGetRot()); } else { llSitTarget (<0,0,0.01>, ZERO_ROTATION/llGetRot()); //backup option. llMessageLinked(LINK_THIS, MSG_SET_SITTARGET, (string)idx, ""); }
}
integer maySit(key id) {
if (!mode_ballusers) //everyone return TRUE; if (id==llGetOwner()) //always return TRUE; if (mode_ballusers==1) //group return llSameGroup(id); return FALSE; //nope
}
broadcastVictims() {
llMessageLinked (LINK_THIS, MSG_RLV_VICTIM_LIST, llList2CSV(grabbedVictims), "");
}
requestMenu(key id) {
llMessageLinked (LINK_THIS, MSG_DO_MENU_CURRENTMENU, "", id);
}
inventarise() {
//someone sitted or unsitted. do the bookkeeping list agents; integer i; //get sitted avatars for (i=0; i<=llGetNumberOfPrims(); i++) { key id=llGetLinkKey(i); if (llGetAgentSize(id)) { //this is an agent.. agents+=id; if (-1!=llListFindList(grabbingVictims, [id])) { //grabbingVictims=[]; //should really do this somewhere else if (-1==llListFindList(grabbedVictims, [id])) { rlv_lock(id); grabbedVictims+=id; broadcastVictims(); } } } } //now cross reference against existing agents //first see, who's all gone for (i=0;i<llGetListLength(sittedAgents); i++) { key id=llList2Key(sittedAgents, i); if (-1==llListFindList(agents, [id])) { //agent unsitted, remove him msgUnsit(id, llList2Integer(agentPoseIdx, llListFindList(agentPoseIdx, [id])+1)); if (-1!=llListFindList(grabbedVictims, [id])) { //rlv catch, unlock it rlv_release(id); } cleanUp(id); broadcastVictims(); } } //now, lets see who's all added for (i=0;i<llGetListLength(agents); i++) { key id=llList2Key(agents, i); if (-1==llListFindList(sittedAgents, [id])) { //freshy added, assign it a script, if possible, and a logical offset if (maySit(id)) { assignScript(id); msgSit(id, assignPoseIdx(id)); } else { llUnSit(id); llWhisper (0, "Sorry "+llKey2Name(id)+" but you cannot use this object."); } //play anim } }
// if (sittedAgents != agents) // setSitTarget();
sittedAgents = agents; setSitTarget(); applyAnims(); // default pose switching on idle if (agents==[]) scheduleEvent (timer_defaultpose, 120, ""); else cancelEvent (timer_defaultpose);
}
makeSwapMenu(key id, integer channel) {
//offer nice swap menu //list sitted avatars string text=""; integer i; integer m=10; list buttons=["<BACK"]; if (MAX_SITTING<m) m=MAX_SITTING; for (i=1; i<=m; i++) { integer j=llListFindList(agentPoseIdx, [i]); string agent = "<Nobody>"; key aid; if (-1<j) { aid=llList2Key(agentPoseIdx, j-1); agent=llKey2Name(aid); } text+=(string)i + ". " + llList2String(menuMeta, i) + " "+agent+"\n"; if (aid!=id) buttons+=["SWAP::"+(string)i]; } llDialog (id, text, buttons, channel);
}
//consideration: send out sit/unsit events or not.. i think, might as well not. in order not to confuse rlv. swapAgent (key id, integer newpos) {
//llSay (0, "Swapping "+llKey2Name(id)+" to position #"+(string)newpos); integer o=llListFindList(agentPoseIdx, [id]); if (o<0) { //llOwnerSay ("swap key not found "+(string)id); //agent not found. see if we have a rlv target if (llGetListLength(grabbedVictims)) { //just default to first best sitted. Stuff gets complex with multiple RLV but who cares that. id=llList2Key(grabbedVictims,0); o=llListFindList(agentPoseIdx, [id]); if (o<0) //OUCH, should nevah happen return; } else return; } integer oldidx=llList2Integer(agentPoseIdx, o+1); if (oldidx==newpos) { llRegionSayTo (id, 0, "You choosen the same position"); return; } agentPoseIdx=llDeleteSubList(agentPoseIdx, o, o+1); integer topos=llListFindList(agentPoseIdx, [newpos]); if (-1<topos) { agentPoseIdx=llDeleteSubList(agentPoseIdx, topos-1, topos) + [llList2Key(agentPoseIdx, topos-1),oldidx]; } agentPoseIdx += [id, newpos]; applyAnims();
}
unsitAll() {
integer i; for (i=0; i<=llGetNumberOfPrims(); i++) { key id=llGetLinkKey(i); if (llGetAgentSize(id)) llUnSit(id); }
}
msgSit(key id, integer poseidx) {
//we start count at 1, mlp at zero. fix right here llMessageLinked(LINK_THIS, MSG_MLP_SITTED, (string)(poseidx-1), id);
}
msgUnsit(key id, integer poseidx) {
//we start count at 1, mlp at zero. fix right here llMessageLinked(LINK_THIS, MSG_MLP_UNSITTED, (string)(poseidx-1), id);
}
log(string t){
//llOwnerSay(t);
}
RLVGrabMenu(key id) {
grabberID = id; llSensor("", NULL_KEY, AGENT, CAPTURE_RANGE, TWO_PI);
// llInstantMessage (id, "Scanning ... menu in 5.. 4.. 3.."); }
RLVReleaseAll() {
integer i; for (i=0; i<llGetListLength(grabbedVictims); i++) { key id=llList2Key(grabbedVictims,i); rlv_release (id); llWhisper (0,"releasing "+llKey2Name(id)); //unsit? llUnSit(id); } grabbedVictims=[];
}
rlv_checkversion (key id) {
rlv_relay(id, "!version=" + (string)1); //version_handle
}
rlv_capture(key avatar)
{
rlv_relay(avatar, "@sit:" + (string)llGetKey() + "=force"); //llSay (menu_ban_channel, "- "+(string)avatar); llWhisper (0, llKey2Name(grabberID)+" captures "+llKey2Name(avatar));
}
rlv_lock(key avatar)
{
rlv_relay(avatar, "@unsit=n");
}
rlv_release(key avatar) {
rlv_relay(avatar, "@unsit=y"); rlv_relay(avatar, "!release"); //llSay (menu_ban_channel, "- "+(string)avatar);
}
// write a message to the RLV Relay rlv_relay(key avatar, string message) {
if (avatar != NULL_KEY) { llSay(RELAY_CHANNEL, llEscapeURL(llGetObjectName()) + "," + (string) avatar + "," + message); log("RLV: " + llGetObjectName() + "," + (string) avatar + "," + message); }
}
//scheduler
list events = []; integer nextEvent = 0;
scheduleEvent(integer id, integer time, string data) {
//adjust timestamp time+=llGetUnixTime(); //cancel any previous requests, this case we only want one per type, really cancelEvent(id); events = llListSort(events + [time, id, data], 3, TRUE); setTimer(FALSE);
}
cancelEvent(integer id) {
//reasonable failsafe as long as you don't use id's that could be current timestamp. integer i=llListFindList(events, [id]); if ((-1<i) && ((i % 3) == 1)) { //it was an id events=llDeleteSubList(events, i-1, i+1); //recurse when found, delete all with said id... ? cancelEvent(id); setTimer(FALSE); }
}
integer setTimer(integer executing) {
if ((events != []) > 0) { // Are there any list items? integer id = llList2Integer(events, 1); integer time = llList2Integer(events, 0); nextEvent = id; float t = (float)(time - llGetUnixTime()); if (t <= 0.0) { if (executing) return TRUE; else t = 0.01; } llSetTimerEvent(t); } else { llSetTimerEvent(0.0); nextEvent = -1; } return FALSE;
}
handleEvent(integer id, string data) {
if (id==timer_versioncheck) { //time to display our dialog if ([]==victimsWithRelay) { llRegionSayTo (grabberID, 0, "No eligible victims found (they must wear a relay)."); requestMenu(grabberID); } else { list nn; integer i; for (i=0; (i<llGetListLength(victimsWithRelay)) && (i<23); i+=2) { string s=llKey2Name(llList2Key(victimsWithRelay, i)); if (s) nn+=llGetSubString(s,0,23); } llListenControl(rlv_victim_dialog_handle, TRUE); scheduleEvent (timer_closechannel, 90, (string)rlv_victim_dialog_handle); llDialog (grabberID, "Choose a victim", nn, rlv_victim_dialog_channel); //scheduleEvent (timer_versioncheck, 5, ""); }
//set timer to new timeout //llSetTimerEvent(TIMER_TIMEOUT); //llListenControl (version_handle, FALSE); //return; } else if ((id==timer_closechannel) || (id==timer_closechannel2)) { llListenControl ((integer)data, FALSE); } else if (id==timer_defaultpose) { llMessageLinked(LINK_THIS, MSG_DO_DEFAULT_POSE, "", ""); } else if (id==timer_cleargrabbing) { grabbingVictims=[]; }
}
upgrade() {string self = llGetScriptName(); string basename = self; if (llSubStringIndex(self, " ") >= 0) {integer start = 2; string tail = llGetSubString(self, llStringLength(self) - start, -1); while (llGetSubString(tail, 0, 0) != " ") {start++; tail = llGetSubString(self, llStringLength(self) - start, -1);} if ((integer)tail > 0) {basename = llGetSubString(self, 0, -llStringLength(tail) - 1);}} integer n = llGetInventoryNumber(INVENTORY_SCRIPT); while (n-- > 0) {string item = llGetInventoryName(INVENTORY_SCRIPT, n); if (item != self && 0 == llSubStringIndex(item, basename)) {llRemoveInventory(item);}}}
default {
state_entry() { upgrade(); llOwnerSay ("sitmanager memory free : "+(string)llGetFreeMemory()); state active; }
}
state active {
state_entry() { setSitTarget(); llMessageLinked (LINK_THIS, MSG_QUERY_SITSCRIPT, "", ""); rlv_victim_dialog_channel = 124904342+llFloor(llFrand(42902543)); rlv_victim_dialog_handle = llListen(rlv_victim_dialog_channel, "", "", ""); llListenControl (rlv_victim_dialog_handle, FALSE); relay_handle=llListen(RELAY_CHANNEL, "", "", ""); llListenControl(relay_handle, FALSE); } link_message(integer sn, integer n, string m, key id) { if (n==MSG_RESET) llResetScript(); else if (n==MSG_ENABLE_SITTING) { MAX_SITTING=llGetListLength(sitScripts); setSitTarget(); } else if (n==MSG_AGENT_UNSIT) { //unsit agent llUnSit(id); } else if (n==MSG_ALL_UNSIT) { //just kick everyone unsitAll(); } else if (n==MSG_DISABLE_SITTING) { //state inactive; MAX_SITTING=0; setSitTarget(); } else if (n==MSG_REGISTER_SITSCRIPT) { if (m) if (INVENTORY_SCRIPT==llGetInventoryType(m)) if (-1==llListFindList(sitScripts, [m])) { sitScripts+=m; MAX_SITTING=llGetListLength(sitScripts); } } else if (n==MSG_PLAY_ANIM) { currentAnim=m; applyAnims(); } else if (n==MSG_MENU_SWAP) { makeSwapMenu (id, (integer)m); } else if (n==MSG_DO_SWAP) { swapAgent(id, (integer)m); } else if (n==MSG_MENU_META) { menuMeta=llParseString2List(m, ["|", " "], []); } else if (n==MSG_DO_STOP) { llUnSit(id); //let changed event handle the rest } else if (n==MSG_MODE_BALLUSERS) { mode_ballusers=(integer)m; } else if (n==MSG_RLV_GRAB) { RLVGrabMenu(id); } else if (n==MSG_RLV_RELEASE) { RLVReleaseAll(); } } changed (integer c) { if (c & CHANGED_LINK) { //don't even bother about getting sitted or not or leaving.. //just fetch all current avatars and match them against our list of known avis inventarise(); } } timer() { // Clear timer or it might fire again before we're done llSetTimerEvent(0.0); do { // Fire the event handleEvent(nextEvent, llList2String(events, 2)); // Get rid of the first item as we've executed it integer l = events != []; if (l > 0) { if (l > 3) events = llList2List((events = []) + events, 3, -1); else events = []; } // Prepare the timer for the next event } while (setTimer(TRUE)); } /* timer() { /* default pose.. do somewhere else please llSetTimerEvent(0.); llMessageLinked(LINK_THIS, MSG_DO_DEFAULT_POSE, "", ""); */ /* } */ no_sensor() { llRegionSayTo (grabberID, 0, "No nearby potentional victims found"); requestMenu(grabberID); } // some av in sensor range sensor(integer total_number) { //llListenControl (rlv_victim_dialog_handle, TRUE); //scheduleEvent (timer_closechannel, 120, (string)rlv_victim_dialog_handle); llListenControl (relay_handle, TRUE); scheduleEvent (timer_closechannel2, 7, (string)relay_handle); potentionalVictims=[]; victimsWithRelay=[]; integer i; for(i=0; i < total_number; i++) { key id = llDetectedKey(i); string name = llKey2Name(id); if (llStringLength(name) > 24) { name = llGetSubString(name, 0, 23); } potentionalVictims+=[id, name]; rlv_checkversion(id); } // show dialog if list contains names /* if (llGetListLength(sensor_names) > 0) { llListenControl(dialog_handle, TRUE); llSetTimerEvent(TIMER_TIMEOUT); llDialog(avatar_menu, MSG_CHOOSE_AV, sensor_names, dialog_channel); } */ scheduleEvent (timer_versioncheck, 5, (string)grabberID); llRegionSayTo (grabberID, 0, "Verifying "+(string)total_number+" potentional victims. Menu in 5.. 4.. 3.."); } listen (integer ch, string n, key id, string m) { if (ch==RELAY_CHANNEL) { if (-1!=llSubStringIndex(m, "version=")) {//bit dirty, we might catch other objects too //victims+=llGetOwnerKey(id); //victim selected, grab it victimsWithRelay+=[llGetOwnerKey(id),llGetSubString(llKey2Name(llGetOwnerKey(id)),0,23)]; } } else if (ch==rlv_victim_dialog_channel) { //victim selected, grab it integer i=llListFindList (victimsWithRelay, [m]); if (-1<i) { key vid=llList2Key(victimsWithRelay, i-1); rlv_capture (vid); if (-1==llListFindList(grabbingVictims, [vid])) { grabbingVictims+=vid; } scheduleEvent (timer_cleargrabbing, 120, (string)id); } requestMenu(grabberID); } }
}
/* state inactive {
state_entry() { llSitTarget(ZERO_VECTOR, ZERO_ROTATION); } link_message(integer sn, integer n, string m, key id) { if (n==MSG_RESET) llResetScript(); else if (n=MSG_ENABLE_SITTING) state active; }
}
- /
</lsl>
TMCP - pose
<lsl> key ourAgent; string currentAnim;
integer hasPerms;
integer MSG_AGENT_PLAY_ANIM=395; // to notify the slave script integer MSG_AGENT_STOP_ANIM=396; integer MSG_ATTACH_AGENT = 398; integer MSG_DETACH_AGENT = 399; integer MSG_REGISTER_SITSCRIPT =383; integer MSG_QUERY_SITSCRIPT = 385; integer MSG_SYNC_ANIM=361;
integer MSG_ADJUST_POSROT=520; integer MSG_TEMPORARY_POSROT=521;
integer MSG_MODE_ADJUST=368;
vector o=ZERO_VECTOR;
vector r=ZERO_VECTOR;
integer hastimer;
integer is_adjusting; integer cursormode;
stopAnim() {
if (hasPerms) if (currentAnim) llStopAnimation(currentAnim);
}
runAnim() {
if (hasPerms) if (currentAnim) llStartAnimation(currentAnim);
}
integer checkPerms() {
if (llGetPermissionsKey()!=ourAgent) { hasPerms = FALSE; return FALSE; } hasPerms=(PERMISSION_TAKE_CONTROLS|PERMISSION_TRIGGER_ANIMATION) == ((PERMISSION_TAKE_CONTROLS|PERMISSION_TRIGGER_ANIMATION) & llGetPermissions()); return hasPerms;
}
queryPerms() {
if (!checkPerms()) { //must query them llRequestPermissions(ourAgent, PERMISSION_TAKE_CONTROLS|PERMISSION_TRIGGER_ANIMATION); } else { stopDefAnims(); if (currentAnim) llStartAnimation(currentAnim); getControls(); }
}
stopDefAnims() {
//easy way would be: //llStopAnimation("sit"); //let's go for the hard way list a=llGetAnimationList(ourAgent); integer i; for (i=0; i<llGetListLength(a); i++) llStopAnimation(llList2Key(a,i));
}
register() {
llMessageLinked (LINK_THIS, MSG_REGISTER_SITSCRIPT, llGetScriptName(), "");
}
getControls() {
llTakeControls (CONTROL_UP|CONTROL_DOWN|CONTROL_LEFT|CONTROL_RIGHT|CONTROL_FWD|CONTROL_BACK|CONTROL_ROT_LEFT|CONTROL_ROT_RIGHT, TRUE, FALSE); //llInstantMessage (ourAgent, "Adjust your position with PGUP, PGDOWN and cursorkeys. Shift rotates."); whispHelp(ourAgent);
}
parseControls (integer level, integer edge) {
integer l = level; //hold down if ((CONTROL_UP|CONTROL_DOWN)==(l & edge & (CONTROL_UP | CONTROL_DOWN))) { cursormode=!cursormode; whispHelp(ourAgent); return; } if (is_adjusting) { integer cm=cursormode; if (l & edge & CONTROL_LEFT) cursormode--; if (l & edge & CONTROL_RIGHT) cursormode++; if (cursormode<1) cursormode=3; if (cursormode>3) cursormode=1; if (cm!=cursormode) whispHelp(ourAgent); }
if (cursormode==1) {//fine adjust if (l & CONTROL_FWD) o+=<0,2,0>; if (l & CONTROL_BACK) o+=<0,-2,0>; if (l & CONTROL_ROT_LEFT) o+=<-2,0,0>; if (l & CONTROL_ROT_RIGHT) o+=<2,0,0>; if (l & CONTROL_UP) o+=<0,0,2>; if (l & CONTROL_DOWN) o+=<0,0,-2>; } else if (cursormode==2) {//90 degrees rotation if (l & edge & CONTROL_FWD) r+=<0,90,0>; if (l & edge & CONTROL_BACK) r+=<0,-90,0>; if (l & edge & CONTROL_ROT_LEFT) r+=<0,0,-90>; if (l & edge & CONTROL_ROT_RIGHT) r+=<0,0,90>; if (l & edge & CONTROL_UP) r+=<90,0,0>; if (l & edge & CONTROL_DOWN) r+=<-90,-0,0>; } else if (cursormode==3) {//fine rotation if (l & CONTROL_FWD) r+=<0,1,0>; if (l & CONTROL_BACK) r+=<0,-1,0>; if (l & CONTROL_ROT_LEFT) r+=<0,0,-1>; if (l & CONTROL_ROT_RIGHT) r+=<0,0,1>; if (l & CONTROL_UP) r+=<1,0,0>; if (l & CONTROL_DOWN) r+=<-1,0,0>; } //if (l & edge & CONTROL_LEFT) r += <0,0,90>; //if (l & edge & CONTROL_RIGHT) r += <0,-90,0>; if (!hastimer) { llSetTimerEvent (0.25); hastimer=TRUE; }
}
whispHelp (key id) {
if (!is_adjusting) { if (!cursormode) { llRegionSayTo (id, 0, "Press PAGE-UP and PAGE-DOWN simultanious to fine adjust position"); } else { llRegionSayTo (id, 0, "Use the cursor keys and up/down to adjust position. Changes made are temporary."); } } else { if (!cursormode) llRegionSayTo (id, 0, "Use PAGE-UP and PAGE-DOWN to start adjusting"); if (1==cursormode) llRegionSayTo (id, 0, "Mode: position (fine adjust). Use cursor keys and up/down to adjust position. Shift-left or shift-right to change mode"); if (2==cursormode) llRegionSayTo (id, 0, "Mode: rotation (90 degrees) . Use cursor keys and up/down to adjust rotation. Shift-left or shift-right to change mode"); if (3==cursormode) llRegionSayTo (id, 0, "Mode: rotation (fine adjust). Use cursor keys and up/down to adjust rotation. Shift-left or shift-right to change mode"); }
}
upgrade() {string self = llGetScriptName(); string basename = self; if (llSubStringIndex(self, " ") >= 0) {integer start = 2; string tail = llGetSubString(self, llStringLength(self) - start, -1); while (llGetSubString(tail, 0, 0) != " ") {start++; tail = llGetSubString(self, llStringLength(self) - start, -1);} if ((integer)tail > 0) {basename = llGetSubString(self, 0, -llStringLength(tail) - 1);}} integer n = llGetInventoryNumber(INVENTORY_SCRIPT); while (n-- > 0) {string item = llGetInventoryName(INVENTORY_SCRIPT, n); if (item != self && 0 == llSubStringIndex(item, basename)) {llRemoveInventory(item);}}}
default
{
state_entry() { //upgrade(); register(); }
link_message(integer sn, integer n, string m, key id) { if (n==MSG_ATTACH_AGENT) { if (m==llGetScriptName()) { if (ourAgent) stopAnim(); currentAnim=""; ourAgent = id; queryPerms(); } } else if (n==MSG_DETACH_AGENT) { if (ourAgent==id) { llReleaseControls(); stopAnim(); } ourAgent=NULL_KEY; currentAnim=""; } else if (n==MSG_AGENT_PLAY_ANIM) { if (ourAgent==id) { stopAnim(); currentAnim=m; runAnim(); } } else if (n==MSG_AGENT_STOP_ANIM) { if (ourAgent==id) { stopAnim(); currentAnim=""; } } else if (n==MSG_SYNC_ANIM) { if (ourAgent) { stopAnim(); runAnim(); } } else if (n==MSG_QUERY_SITSCRIPT) { register(); //llOwnerSay ("Registering"); } else if (n==MSG_MODE_ADJUST) { is_adjusting = (integer)m; if (!is_adjusting) cursormode=0; else cursormode=1; if (id==ourAgent) whispHelp(id); }
} run_time_permissions(integer p) { if (checkPerms()) { stopDefAnims(); if (currentAnim) llStartAnimation(currentAnim); getControls(); } } control (key id, integer level, integer edge) { if (id!=ourAgent) return; parseControls (level, edge); } timer () { llSetTimerEvent (0.); hastimer=FALSE; if (ZERO_VECTOR!=(o+r)) { if (is_adjusting) llMessageLinked (LINK_THIS, MSG_ADJUST_POSROT, (string)(0.005*o)+"&"+(string)r, ourAgent); else llMessageLinked (LINK_THIS, MSG_TEMPORARY_POSROT, (string)(0.005*o)+"&"+(string)r, ourAgent); } o=ZERO_VECTOR; r=ZERO_VECTOR; }
}
</lsl>
TMCP - positions
<lsl> //basically, we need a large datastore for our command set list poseNames; list poseData;
list positionNames; list positionData;
integer MSG_DATA_MENU=310;
integer MSG_DATA_TYPE=320;
integer MSG_DATA_POSE=330;
integer MSG_DATA_POSITION=331;
integer MSG_DATA_LM=335;
integer MSG_DO_POSE=350; integer MSG_DO_LM=351; integer MSG_DO_SPECIAL=352;
integer MSG_SET_RUNNING=390; integer MSG_STORAGE_RESET=391;
integer MSG_DATA_READY=309;
integer MSG_RUN_ANIMS=379;
integer MSG_AGENT_PLAY_ANIM=395;
integer MSG_SET_SITTARGET=375;
integer MSG_DUMP_POSITIONS=389; integer MSG_UPDATE_POS=363;
integer MSG_DO_DEFAULT_POSE = 358; integer MSG_SET_DEFAULT_POSE = 359;
integer MSG_MLP_SETTING = 1;
integer MSG_ADJUST_POSROT=520; integer MSG_TEMPORARY_POSROT=521;
string currentPose="default"; list currentAnims; list currentPositions;
list agentPoseIdx;
list agentAdjust;
string defaultPose="default";
runAnims() {
list data=agentPoseIdx; integer i; list idpos; for (i=0; i<llGetListLength(data); i+=2) { key id=(key)llList2String(data, i); integer idx=-1+(integer)llList2String(data, i+1); if (id) if (idx>=0) { if (idx>=llGetListLength(currentAnims)) llWhisper (0, "No animation available for "+llKey2Name(id)); else { llMessageLinked (LINK_THIS, MSG_AGENT_PLAY_ANIM, llList2String(currentAnims, idx), id); //llOwnerSay ("Playing anim "+llList2String(currentAnims, idx)+" on id "+(string)id); } if (idx/2>=llGetListLength(currentPositions)) llSay (0, "No position data for "+llKey2Name(id)); else { //set agents position idpos+=[id, (vector)llList2String(currentPositions, idx*2),(vector)llList2String(currentPositions, idx*2+1)]; } } else { //llOwnerSay ("Corrupt agent anim position request"); } } //apply positions for (i=0; i<=llGetNumberOfPrims(); i++) { integer j=llListFindList(idpos, [llGetLinkKey(i)]); if (-1<j) { vector pos = llList2Vector(idpos, j+1)+(<0,0,0.42+0.01*(integer)llGetObjectDesc()>/llGetRot()); vector rot = llList2Vector(idpos, j+2) * DEG_TO_RAD; integer l=llListFindList (agentAdjust, [llGetLinkKey(i)]); if (-1<l) { pos+=llList2Vector (agentAdjust, l+1); rot=llRot2Euler(llEuler2Rot(llList2Vector (agentAdjust, l+2)) * llEuler2Rot(rot)); } llSetLinkPrimitiveParamsFast(i, [PRIM_POS_LOCAL, pos,PRIM_ROT_LOCAL, llEuler2Rot(rot)]); //llOwnerSay ("Setting position for "+llKey2Name(llGetLinkKey(i))+" position: "+(string)pos+" rotation: "+ (string)rot ); } }
}
setSitTarget(integer poseidx) {
poseidx--; vector v=(vector)llList2String(currentPositions, poseidx*2); vector r=(vector)llList2String(currentPositions, poseidx*2+1); llSitTarget (v+(<0,0,0.42+0.01*(integer)llGetObjectDesc()>/llGetRot()) , llEuler2Rot(r*DEG_TO_RAD)); //llOwnerSay ("Sit target #"+(string)poseidx+" "+(string)(v+(<0,0,0.42>/llGetRot()))+" "+(string)(r));
}
string shortString (float n) {
string s=(string)n; while (llStringLength(s)>1) { integer hasdot=-1!=llSubStringIndex(s, "."); string d=llGetSubString(s,-1,-1); if ((hasdot && (d=="0")) || (d==".")) s=llDeleteSubString(s,-1,-1); else jump ok; } @ok; return s;
}
string shortVec (vector v) {
return "<"+shortString(v.x)+","+shortString(v.y)+","+shortString(v.z)+">";
}
dumpPos() {
llOwnerSay ("TMCP Position Dump"); integer i; for (i=0; i<llGetListLength(positionNames); i++) { //llOwnerSay ("{"+llList2String(positionNames, i)+"} "+llList2String(positionData, i)); list v=llParseString2List(llList2String(positionData, i), ["&"], []); string s="{"+llList2String(positionNames, i)+"}"; integer j; for (j=0; j<llGetListLength(v); j++) //notice that function shortvec takes string as param s+=" "+shortVec((vector)llList2String(v, j)); llOwnerSay (s); }
}
doPose (string m) {
if (m!=currentPose) agentAdjust=[]; currentPose=m; currentAnims=[]; currentPositions=[]; integer i=llListFindList(positionNames, [m]); integer j=llListFindList(poseNames, [m]); currentAnims=llParseString2List (llList2String(poseData, j), ["|"], []); currentPositions=llParseString2List (llList2String(positionData, i), ["&"], []); //fill missing data with defaults i = llListFindList(positionNames, ["default"]); j = llListFindList(poseNames, ["default"]); list animdef=llParseString2List (llList2String(poseData, j), ["|"], []); list posdef=llParseString2List (llList2String(positionData, i), ["&"], []); currentAnims+=llList2List (animdef, llGetListLength(currentAnims), -1); currentPositions+=llList2List(posdef, llGetListLength(currentPositions), -1); //this should be obsolete now: if (/*(i>=0) && */(j>=0)) { //list l=llParseString2List(llList2String(lmAction, i), [","],[]); //parse pose data. todo. if (i<0) { //need to fetch default data... just try it. i=llListFindList(positionNames, ["default"]); } //llOwnerSay ("Do pose "+m+" anims "+llList2String(poseData,j)+" data "+llList2String(positionData, i)); } else { //llOwnerSay ("No animations defined for "+m); } runAnims();
}
setAgentPosRot (key id, string data) {
//set temporary agent adjustment. list l=llParseString2List(data, ["&"], []); vector p=(vector)llList2String(l,0); vector r=DEG_TO_RAD * (vector)llList2String(l,1); integer i=llListFindList (agentAdjust, [id]); if (-1<i) { p+=llList2Vector(agentAdjust, i+1); r=llRot2Euler(llEuler2Rot(llList2Vector(agentAdjust, i+2))*llEuler2Rot(r)); agentAdjust = llDeleteSubList(agentAdjust, i, i+2); } if (llVecMag(p)>1) p=llVecNorm(p); agentAdjust+=[id,p,r]; runAnims();
}
adjustPosition (string name, integer index, vector p, vector r) {
//positionNames integer i=llListFindList (positionNames, [name]); if (-1<i) { string data=llList2String(positionData, i); list pos=llParseString2List(data, ["&"], []); if (index>0) { //alter only index integer j=(index-1)*2; if (j<llGetListLength(positionData)) { vector v=(vector)llList2String(pos, j); v+=p; vector R0=(vector)llList2String(pos, j+1); rotation R1=llEuler2Rot(DEG_TO_RAD *R0); rotation Rd=llEuler2Rot(DEG_TO_RAD * r); R1 = R1 * Rd; R0=RAD_TO_DEG * llRot2Euler(R1); pos=llListReplaceList(pos, [shortVec(v),shortVec(R0)], j, j+1); } } else { //alter all integer j; for (j=0; j<llGetListLength(pos); j+=2) { vector v=(vector)llList2String(pos, j); v+=p; vector R0=(vector)llList2String(pos, j+1); rotation R1=llEuler2Rot(DEG_TO_RAD *R0); rotation Rd=llEuler2Rot(DEG_TO_RAD * r); R1 = R1 * Rd; R0=RAD_TO_DEG * llRot2Euler(R1); pos=llListReplaceList(pos, [shortVec(v),shortVec(R0)], j, j+1); } } data=llDumpList2String(pos, "&"); //llOwnerSay ("new pos data for "+name+" : "+data); positionData=llListReplaceList (positionData, [data], i, i); }
}
adjustAllPositions (vector p, vector r) {
//loop all pose, adjust them integer i; for (i=0; i<llGetListLength(positionNames); i++) { adjustPosition (llList2String(positionNames, i),-1, p,r); }
}
parseSetting (string m, key id) {
//seperated by = //might contain ROT or POS adjustments list l=llParseString2List(m, [" = ", " =", "=", "= ","="],[]); string cmd=llList2String(l, 0); string cmd2=llList2String(l,1); if (cmd=="REORIENT") { vector v=(vector)llList2String(l,2); /* list lv=llParseString2List(llList2String(l,2), ["<",",",">"], []); //rotations come as vector too llOwnerSay ("P: "+llList2String(l,2)+" : "+llDumpList2String(lv, "*")); v.x=(float)llList2String(lv,0); v.y=(float)llList2String(lv,1); v.z=(float)llList2String(lv,2); */ llOwnerSay ("adjust "+cmd2+" "+(string)v); if (cmd2=="OFF") { adjustAllPositions (0.01*v, ZERO_VECTOR); } else if (cmd2=="ROT") { adjustAllPositions (ZERO_VECTOR,v); } doPose(currentPose); //runAnims(); }
}
adjustPosRot (key id, string data) {
list l=llParseString2List(data, ["&"], []); integer i=llListFindList (agentPoseIdx, [(string)id]); //llOwnerSay ("posrot "+(string)i+" "+(string)id+" "+data); if ((-1<i)&&(llGetListLength(l)==2)) { integer lidx=llList2Integer (agentPoseIdx, i+1); adjustPosition (currentPose, lidx, (vector)llList2String(l,0), (vector)llList2String(l,1)); doPose(currentPose); }
}
upgrade() {string self = llGetScriptName(); string basename = self; if (llSubStringIndex(self, " ") >= 0) {integer start = 2; string tail = llGetSubString(self, llStringLength(self) - start, -1); while (llGetSubString(tail, 0, 0) != " ") {start++; tail = llGetSubString(self, llStringLength(self) - start, -1);} if ((integer)tail > 0) {basename = llGetSubString(self, 0, -llStringLength(tail) - 1);}} integer n = llGetInventoryNumber(INVENTORY_SCRIPT); while (n-- > 0) {string item = llGetInventoryName(INVENTORY_SCRIPT, n); if (item != self && 0 == llSubStringIndex(item, basename)) {llRemoveInventory(item);}}}
default {
state_entry() { upgrade(); }
link_message (integer sn, integer n, string m, key id) { if (n==MSG_DATA_POSE) { //add posedata poseNames+=m; poseData+=(string)id; } else if (n==MSG_DATA_POSITION) { positionNames+=m; positionData+=(string)id; } else if (n==MSG_SET_DEFAULT_POSE) { defaultPose=m; } else if (n==MSG_SET_RUNNING) state running; }
}
state running {
state_entry() { llOwnerSay ("Position free memory: "+(string)llGetFreeMemory()); doPose(defaultPose); } link_message (integer sn, integer n, string m, key id) { if (n==MSG_DO_POSE) { doPose(m); } else if (n==MSG_STORAGE_RESET) llResetScript(); else if (n==MSG_RUN_ANIMS) { agentPoseIdx=llCSV2List(m); runAnims(); } else if (n==MSG_SET_SITTARGET) { setSitTarget((integer)m); } else if (n==MSG_DUMP_POSITIONS) { dumpPos(); } else if (n==MSG_UPDATE_POS) { runAnims(); } else if (n==MSG_SET_DEFAULT_POSE) defaultPose=m; else if (n==MSG_DO_DEFAULT_POSE) doPose(defaultPose); else if (n==MSG_MLP_SETTING) { parseSetting (m, id); } else if (n==MSG_ADJUST_POSROT) { adjustPosRot(id, m); } if (n==MSG_TEMPORARY_POSROT) { setAgentPosRot(id, m); }
}
} </lsl>
TMCP - actions
<lsl> //basically, we need a large datastore for our command set list lmNames; list lmAction;
integer MSG_DATA_MENU=310; integer MSG_DATA_TYPE=320; integer MSG_DATA_POSE=330; integer MSG_DATA_LM=335;
integer MSG_DO_POSE=350; integer MSG_DO_LM=351; integer MSG_DO_SPECIAL=352;
integer MSG_DO_SOUND=356;
integer MSG_SET_RUNNING=390; integer MSG_STORAGE_RESET=391; integer MSG_DATA_READY=309;
upgrade() {string self = llGetScriptName(); string basename = self; if (llSubStringIndex(self, " ") >= 0) {integer start = 2; string tail = llGetSubString(self, llStringLength(self) - start, -1); while (llGetSubString(tail, 0, 0) != " ") {start++; tail = llGetSubString(self, llStringLength(self) - start, -1);} if ((integer)tail > 0) {basename = llGetSubString(self, 0, -llStringLength(tail) - 1);}} integer n = llGetInventoryNumber(INVENTORY_SCRIPT); while (n-- > 0) {string item = llGetInventoryName(INVENTORY_SCRIPT, n); if (item != self && 0 == llSubStringIndex(item, basename)) {llRemoveInventory(item);}}}
default {
state_entry() { upgrade(); }
link_message (integer sn, integer n, string m, key id) { if (n==MSG_DATA_LM) { //add action lmNames+=m; lmAction+=(string)id; } else if (n==MSG_SET_RUNNING) state running; }
}
state running {
state_entry() { llOwnerSay ("Action manager free memory : "+(string)llGetFreeMemory()); } link_message (integer sn, integer n, string m, key id) { if (n==MSG_DO_LM) { integer i=llListFindList(lmNames, [m]); if (i>=0) { list l=llParseString2List(llList2String(lmAction, i), [","],[]); integer set=(integer)llList2String(l,1); integer num=(integer)llList2String(l,2); //string mess=llStringTrim(llList2String(l,3),STRING_TRIM); string mess=llDumpList2String(llList2List(l,3,-1), ","); integer nomenu=(integer)llList2String(l,0); //perform requested action llMessageLinked (set, num, mess, id); if (!nomenu) //this will fail when not found, which it shouldnt. llMessageLinked (LINK_THIS, 370, "", id); } } else if (n==MSG_DO_SOUND) { //LM list was abused for sound info integer i=llListFindList(lmNames, [m]); if (i>=0) llTriggerSound(llList2String(lmAction, i), 1.); } else if (n==MSG_STORAGE_RESET) llResetScript(); }
} </lsl>
<lsl> string version = "TMCP v0.3x";
//Render menu
list menuNames; list menuEntries; //menus that appear in 'auto' menu list menuDetails;
//some hardcoded stuff that this script adds, not any menu. prefill. list itemNames=["<<<",">>>","<BACK"]; list itemTypes=[7,7,7];
list menuHistory; string currentMenu; string currentPose="default"; string defaultPose="default";
integer channel=999;
integer listenhandle;
integer mode_chat=1; integer mode_adjust=0; integer mode_menu=3; //user integer mode_ball=0; //all
integer hibernate;
list rlvVictims;
integer TYPE_MENU=1;
integer TYPE_POSE=2;
integer TYPE_LM_NOMENU=3;
integer TYPE_LM_MENU=4;
integer TYPE_IGNORE=5;
integer TYPE_SPECIAL=6;
integer TYPE_UNKNOWN=7;
integer TYPE_SOUND=8;
integer MSG_DATA_MENU=310;
integer MSG_DATA_MENU_DETAILS=311;
integer MSG_DATA_TYPE=320; integer MSG_DATA_POSE=330; integer MSG_DATA_POSITION=331; integer MSG_DATA_LM=335;
integer MSG_DO_POSE=350; integer MSG_DO_LM=351; integer MSG_DO_SPECIAL=352;
integer MSG_DO_STOP=353; integer MSG_DO_SWAP=354; integer MSG_MENU_SWAP=355; integer MSG_DO_SOUND=356;
integer MSG_SET_RUNNING=390; integer MSG_STORAGE_RESET=391; integer MSG_DATA_READY=309;
integer MSG_DATA_START_READ=301;
integer MSG_MENU_META=333;
integer MSG_DUMP_POSITIONS=389; integer MSG_MODE_ADJUST=368; integer MSG_MODE_CHAT=367; integer MSG_MODE_BALLUSERS=366; integer MSG_MODE_OFF=365;
integer MSG_UPDATE_POS=363; integer MSG_ALL_UNSIT =381;
integer MSG_SET_DEFAULT_POSE = 359; integer MSG_DO_DEFAULT_POSE = 358;
integer MSG_RLV_GRAB=308; integer MSG_RLV_RELEASE=309; integer MSG_RLV_VICTIM_LIST=307;
integer MSG_DO_MENU_CURRENTMENU = 305;
//incoming integer MSG_DO_MENU_COMMAND = -12002;
verbose(string text) {
//llOwnerSay (text); if (mode_chat) llSay (0, text);
}
string onOff(integer value) {
if (value) return "On"; else return "Off";
}
string aclMode(integer value) {
if (0==value) return "ALL"; else if (1==value) return "GROUP"; else if (2==value) return "OWNER"; else if (3==value) return "USER"; else return "UNKNOWN";
}
reset(integer resetSelf) {
integer i; for (i=0; i<llGetInventoryNumber(INVENTORY_SCRIPT); i++) { string s=llGetInventoryName(INVENTORY_SCRIPT, i); if (-1<llSubStringIndex(s, "~TMCP ")) { if (s!=llGetScriptName()) { llResetOtherScript(s); } } } if (resetSelf) llResetScript();
}
setRunning(integer running) {
integer i; for (i=0; i<llGetInventoryNumber(INVENTORY_SCRIPT); i++) { string s=llGetInventoryName(INVENTORY_SCRIPT, i); if (-1<llSubStringIndex(s, "~TMCP ")) { if (s!=llGetScriptName()) { llSetScriptState (s, running); } } }
}
integer hasDialogAccess(key id) {
//OWNER ALWAYS if (id==llGetOwner()) return TRUE; if (-1<llListFindList(rlvVictims, [(string)id])) return FALSE; //ALL if ((!mode_menu)/* || (id==llGetOwner())*/) return TRUE; //little speed optimalization to avoid call to samegroup //USER if (mode_menu==3) { //user must be sitted to access, or no-one sitted at all integer someSit=FALSE; integer i; for (i=0; i<=llGetNumberOfPrims(); i++) { if (llGetLinkKey(i)==id) return TRUE; if (llGetAgentSize(llGetLinkKey(i))) someSit=TRUE; } return (!someSit); } //GROUP return (mode_menu==1) && llSameGroup(id);
}
integer hasMenuAccess (key id, string menu) {
//supposed to be trimmed already? if (id==llGetOwner()) return TRUE; string access= llStringTrim( llList2String( llParseString2List( llList2String(menuDetails, 1+llListFindList(menuDetails, [menu]) ) , ["|"], []) , 0) , STRING_TRIM); if (access=="OWNER") { return id==llGetOwner(); } if (access=="GROUP") { return (id==llGetOwner()) || llSameGroup(id); } if (access=="USER") { integer i; for (i=0; i<=llGetNumberOfPrims(); i++) if (llGetLinkKey(i)==id) return TRUE; return FALSE; } //defaults to all.. learn to edit notecards if you made an error. //future options might be a name or some. dunnow. return TRUE;
}
makeMenu(string name, key id) {
if (id); else return; //might be invoked by remote scripts. can't pop menu's to none-avi's. //present user a fine menu integer idx=0; integer offset; if ((name==">>>") || (name=="<<<")) { //offset making //rename to the last found menu /* if (name==">>>") offset+=10; if (name=="<<<") offset-=10; */ menuHistory+=name; //next step will fix this //llOwnerSay ("history: "+(string)menuHistory); } integer ho=llGetListLength(menuHistory)-1; while ((ho>1) && (-1<llListFindList(["<<<", ">>>"], [llList2String(menuHistory, ho)])) ) { //adjust offset string s=llList2String(menuHistory, ho); if (s=="<<<") offset-=10; if (s==">>>") offset+=10; name=llList2String(menuHistory, ho-1); ho--; } if (name) { idx=llListFindList(menuNames, [name]); if (idx<0) { //llOwnerSay ("No menu entry for "+name); return; } } else { //make it main menu name=llList2String(menuNames, 0); //assume at least 1 menu item please.. else its bugged anyways. menuHistory=[]; }
currentMenu=name; integer i=llListFindList (menuHistory, [name]); if (i>=0) { //truncate rest from history, maybe we went back. menuHistory = llList2List(menuHistory, 0, i); } else menuHistory += name;
list present=llCSV2List(llList2String(menuEntries, idx));
if (llAbs(offset)>=(llGetListLength(present))) offset=0; integer o=offset; while (o>0) { menuHistory += ">>>"; o-=10; if (o<0) o=0; } while (o<0) { menuHistory += "<<<"; o+=10; } string h=">>"; for (i=0; i<llGetListLength(menuHistory); i++) h+="> "+llList2String(menuHistory,i)+" "; if (currentPose) h+="\n\nCurrent pose: "+currentPose; //todo somewhere. some better wrap around. if ((offset) || (llGetListLength(present)>12)) present = llList2List(present, offset, offset+8) + ["<<<", ">>>", "BACK"]; if (llGetListLength(rlvVictims)) { h+="\nCaptured (RLV): "; for (i=0; i<llGetListLength(rlvVictims); i++) h+=llKey2Name((key)llList2String(rlvVictims, i))+"\n"; } //got our filled up menu here? prolly need to sort it, for now just present it //llOwnerSay (name+":"+llList2CSV(present)); //invert our list list p; for (i=0; i<12; i+=3) p = p+llList2List (present, -i-3, -i-1); llDialog (id, version+"\n"+h, p, channel);
}
processCommand (key id, string message) {
integer typeidx=llListFindList (itemNames, [message]); string param; integer newMenu=TRUE; if (typeidx<0) { list l=llParseString2List(message, ["::"], []); message=llList2String(l,0); param=llList2String(l,1); typeidx=llListFindList (itemNames, [message]); //llOwnerSay ("Message "+message+" got param "+param); } if (typeidx<0) { //llOwnerSay ("Error - no menu entry for "+message); return; } integer type=llList2Integer(itemTypes, typeidx); //llOwnerSay ("Type for "+message+" is "+(string)type); if (type==TYPE_MENU) { if (hasMenuAccess(id, message)) { makeMenu(message, id); newMenu=FALSE; } else { llRegionSayTo (id, 0, "Access to menu "+message+" is denied."); //makeMenu(currentMenu, id); //return; } } else if (type==TYPE_POSE) { llMessageLinked (LINK_THIS, MSG_DO_POSE, message, ""); currentPose=message; llMessageLinked (LINK_THIS, MSG_MENU_META,llList2String(menuDetails, 1+llListFindList(menuDetails, [currentMenu])), ""); } else if (type==TYPE_LM_NOMENU) { llMessageLinked (LINK_THIS, MSG_DO_LM, message, id); newMenu=FALSE; } else if (type==TYPE_LM_MENU) { llMessageLinked (LINK_THIS, MSG_DO_LM, message, id); } else if (type==TYPE_SOUND) { llMessageLinked (LINK_THIS, MSG_DO_SOUND, message, id); } else //some built in messages if (type=TYPE_SPECIAL) { if (message=="SWAP") { if ((integer)param) { llMessageLinked (LINK_THIS, MSG_DO_SWAP, param, id); } else { llMessageLinked (LINK_THIS, MSG_MENU_SWAP, (string)channel, id); newMenu=FALSE; } } else if (message=="STOP") { llMessageLinked (LINK_THIS, MSG_DO_STOP, "", id); newMenu=FALSE; } else if (message=="BACK") { //check menu history string newM=""; integer i=llGetListLength (menuHistory)-1; while ((i>0) && (-1<llListFindList(["<<<", ">>>"], [llList2String(menuHistory, i)]))) i--; if (i>0) { menuHistory=llList2List(menuHistory, 0, i); newM=llList2String(menuHistory,i-1); } makeMenu(newM, id); newMenu=FALSE; } else if ((message=="<<<") || (message==">>>")) { //previous and next. let menumaker solve this. makeMenu(message, id); newMenu=FALSE; } else if (message=="<BACK") { //makeMenu(currentMenu, id); } else //anything we don't know off for some reason. include it anyways. if (message=="DUMP") { llMessageLinked (LINK_THIS, MSG_DUMP_POSITIONS, "", ""); } else if (message=="CHAT") { mode_chat=!mode_chat; llMessageLinked (LINK_THIS, MSG_MODE_CHAT, (string)mode_chat, id); llRegionSayTo (id, 0, "Chat "+onOff(mode_chat)); } else if (message=="ADJUST") { mode_adjust=!mode_adjust; llMessageLinked (LINK_THIS, MSG_MODE_ADJUST, (string)mode_adjust, id); llRegionSayTo (id, 0, "Adjusting : "+onOff(mode_adjust)); } else if (message=="MENUUSERS") { mode_menu++; if (mode_menu>3) mode_menu=0; llRegionSayTo (id, 0, "Menu access set to "+aclMode(mode_menu)); } else if (message=="BALLUSERS") { mode_ball++; if (mode_ball>2) mode_ball=0; llRegionSayTo (id, 0, "Usage access set to "+aclMode(mode_ball)); llMessageLinked (LINK_THIS, MSG_MODE_BALLUSERS, (string)mode_ball, id); } else if (-1<llListFindList (["RESET", "RESTART", "RELOAD"], [message])) { llSay (0, "Reset sequence started... Touch me again in a few seconds to reload the configuration."); reset(TRUE); newMenu=FALSE; } else if (message=="Z") { if ("+"==llGetSubString(param,0,0)) param=llGetSubString(param,1,-1); llSetObjectDesc((string)((integer)llGetObjectDesc()+(integer)param)+llGetSubString(llGetObjectDesc(), llStringLength((string)((integer)llGetObjectDesc())), -1)); llMessageLinked (LINK_THIS, MSG_UPDATE_POS, (string)mode_ball, id); } else if (message=="OFF") { state Off; newMenu=FALSE; } else if (message=="SAVE") { defaultPose=currentPose; llMessageLinked (LINK_THIS, MSG_SET_DEFAULT_POSE, currentPose, ""); } else if (message=="GRAB") { llMessageLinked (LINK_THIS, MSG_RLV_GRAB, "", id); newMenu=FALSE; } else if (message=="RELEASE") { llMessageLinked (LINK_THIS, MSG_RLV_RELEASE, "", id); } else { //forward to whoever handles it llMessageLinked (LINK_THIS, MSG_DO_SPECIAL, message, id); } } if (newMenu) makeMenu(currentMenu, id);
}
upgrade() {string self = llGetScriptName(); string basename = self; if (llSubStringIndex(self, " ") >= 0) {integer start = 2; string tail = llGetSubString(self, llStringLength(self) - start, -1); while (llGetSubString(tail, 0, 0) != " ") {start++; tail = llGetSubString(self, llStringLength(self) - start, -1);} if ((integer)tail > 0) {basename = llGetSubString(self, 0, -llStringLength(tail) - 1);}} integer n = llGetInventoryNumber(INVENTORY_SCRIPT); while (n-- > 0) {string item = llGetInventoryName(INVENTORY_SCRIPT, n); if (item != self && 0 == llSubStringIndex(item, basename)) {llRemoveInventory(item);}}}
default {
state_entry() { upgrade(); state WaitTouch; }
}
state WaitTouch {
state_entry() { reset(FALSE); llSay (0,"TMCP resetted - Touch me to load the configuration"); } touch_start (integer n) { llSay (0,"TMCP startup - reading data"); state reading_data; }
}
state reading_data
{
state_entry() { llMessageLinked (LINK_THIS, MSG_DATA_START_READ, "", ""); } link_message (integer sn, integer n, string m, key id) { if (n==MSG_DATA_MENU) { //add action menuNames+=m; menuEntries+=(string)id; } else if (n==MSG_DATA_MENU_DETAILS) { //add action menuDetails+= [m, (string)id]; } else if (n==MSG_DATA_TYPE) { //set type itemNames+=m; itemTypes+=(integer)((string)id); } if (n==MSG_SET_RUNNING) state running; }
}
state running {
state_entry() { llOwnerSay ("Menu ready. "+(string)llGetFreeMemory()); channel=-(integer)llFrand(1309123)-492345; listenhandle=llListen (channel, "", "", ""); } touch_start(integer total_number) { key id=llDetectedKey(0); if (!hasDialogAccess(id)) { if (mode_chat) llWhisper (0, "Sorry "+llKey2Name(id)+", but menu usage is limited to "+aclMode(mode_menu)); return; } makeMenu("",id); } listen (integer ch, string name, key id, string message) { if (!hasDialogAccess(id)) { if (mode_chat) llWhisper (0, "Sorry "+llKey2Name(id)+", but menu usage is limited to "+aclMode(mode_menu)); return; } if (hibernate) { setRunning(TRUE); hibernate=FALSE; } //llSetTimerEvent (3600.); processCommand (id, message); } link_message (integer sn, integer n, string m, key id) { if (n==MSG_DO_MENU_COMMAND) { processCommand (id, m); } else if (n==MSG_DO_DEFAULT_POSE) { currentPose = defaultPose; } else if (n==MSG_RLV_VICTIM_LIST) { rlvVictims=llCSV2List(m); } else if (n==MSG_DO_MENU_CURRENTMENU) { makeMenu(currentMenu, id); } } timer() { //inactivity detected, sleep all other scripts. llSetTimerEvent(0.); hibernate=TRUE; setRunning(FALSE); }
}
state Off {
state_entry() { llMessageLinked (LINK_THIS, MSG_ALL_UNSIT, "", ""); llWhisper (0,"Switching off in 10... 9..."); llSleep (5.0); setRunning(FALSE); llSleep (5.0); llSitTarget(ZERO_VECTOR, ZERO_ROTATION); } touch_start(integer n) { key id=llDetectedKey(0); if (id==llGetOwner()) state running; if ((mode_menu==1) && llSameGroup(id)) state running; llRegionSayTo (id, 0, "Sorry, but this object is switched OFF. Ask the owner to turn it on."); } state_exit() { setRunning(TRUE); hibernate=FALSE; llWhisper (0, "Switching on"); }
} </lsl>
TMCP - notecard reader
<lsl> //read MLP notecards //see how much memory headroom we got after for other stuff
integer notecardidx; key notecardkey; integer notecardtype; integer notecardline; string notecardname; list positions; list tempMenu; list menuNames; list menuEntries; //menus that appear in 'auto' menu list toMenu; //menus with pointers list linkedMsg;
integer hasReset=FALSE; //indicates if there is a reset option in *some* menu. If absent, we add it for safety.
integer TYPE_MENU=1;
integer TYPE_POSE=2;
integer TYPE_LM_NOMENU=3;
integer TYPE_LM_MENU=4;
integer TYPE_IGNORE=5;
integer TYPE_SPECIAL=6;
integer TYPE_UNKNOWN=7;
integer TYPE_SOUND=8;
integer MSG_DATA_MENU=310; integer MSG_DATA_MENU_DETAILS=311; integer MSG_DATA_TYPE=320; integer MSG_DATA_POSE=330; integer MSG_DATA_POSITION=331; integer MSG_DATA_LM=335;
integer MSG_DO_POSE=350; integer MSG_DO_LM=351; integer MSG_DO_SPECIAL=352;
integer MSG_DO_STOP=353; integer MSG_DO_SWAP=354;
integer MSG_SET_RUNNING=390; integer MSG_STORAGE_RESET=391; integer MSG_DATA_READY=309;
integer MSG_DATA_START_READ=301;
verbose(string text) {
llOwnerSay (text);
}
startNotecard() {
integer i; notecardline=0; for (;notecardidx<llGetInventoryNumber(INVENTORY_NOTECARD); notecardidx++) { //loop until we found appropiate card notecardname=llGetInventoryName(INVENTORY_NOTECARD, notecardidx); if (llGetSubString(notecardname,0,9)==".MENUITEMS") { //neato. go fetch it notecardtype=1; notecardkey=llGetNotecardLine(notecardname, 0); verbose ("Reading menu from "+notecardname); return; } if (llGetSubString(notecardname,0,9)==".POSITIONS") { //neato. go fetch it notecardtype=2; notecardkey=llGetNotecardLine(notecardname, 0); verbose ("Reading positions from "+notecardname); return; } verbose ("Skipping "+notecardname); } //still here? then we'r done //verbose ("Done reading all notecards"); postFixMenu(); sendMenuItems(); llSleep (2.0); llMessageLinked (LINK_THIS, MSG_SET_RUNNING, "", ""); //set to running verbose ("Ready."); //llOwnerSay ("Notecard reader free memory: "+(string)llGetFreeMemory());
}
addPose (string name, list data) {
//add pose info.. link to position info exactly when again? //positions+=[name, data]; llMessageLinked (LINK_THIS, MSG_DATA_POSE, name, llDumpList2String(data, "|"));
}
addMenuDetails (string name, list data) {
//add menu details.. access, color of balls. llMessageLinked (LINK_THIS, MSG_DATA_MENU_DETAILS, name, llDumpList2String(data, "|"));
}
addLM (string name, string data) {
llMessageLinked (LINK_THIS, MSG_DATA_LM, name, data);
}
setType (string name, integer type) {
llMessageLinked (LINK_THIS, MSG_DATA_TYPE, name, (string)type);
}
parseMenu() {
//parse our temporary menu to a real menu; //this is where the real stuff happens. integer i; string menuName=""; //MLP 1 compatability: don't add stuff to menu as long as menu name is empty. add poses though. list options; for (i=0; i<llGetListLength(tempMenu); i++) { string data=llList2String(tempMenu, i); integer posspace=llSubStringIndex(data, " "); string cmd=data; string param=""; string option=""; if (posspace>0) { cmd=llGetSubString(data,0,posspace-1); param=llGetSubString(data, posspace+1, -1); } list paramsTmp=llParseString2List(param, ["|"], []); integer j; list params; for (j=0; j<llGetListLength(paramsTmp); j++) params+=llStringTrim(llList2String(paramsTmp, j), STRING_TRIM); //params are now nice seperated on |, still allowed to contain spaces in the middle but trimmed from the outside. string p1=llList2String(params, 0); string p2=llList2String(params, 1); string p3=llList2String(params, 2); //got our set of command and optional parameters now. let's test them. if (cmd=="MENU") { addMenuDetails (p1, llDeleteSubList(params,0,0)); //menuEntries+=p1; menuName=p1; //additional params hint the number of available seats setType (p1, TYPE_MENU); } else if (cmd=="POSE") { addPose (p1, llDeleteSubList(params,0,0)); option = p1; setType (p1, TYPE_POSE); } else if (cmd=="SOUND") { addLM (p1, p2); option = p1; setType (p1, TYPE_SOUND); } else if (-1 != llListFindList (["BACK", "STOP","SWAP","RESET","RESTART", "MENUUSERS", "BALLUSERS","ADJUST", "DUMP", "CHAT", "RELOAD", "OFF"], [cmd])) { //recognized simple options option = cmd; setType (cmd, TYPE_SPECIAL); if (-1!= llListFindList (["RESET","RESTART", "RELOAD"], [cmd])) hasReset=TRUE; } else if (-1 != llListFindList (["SAVE", "CHECK"], [cmd])) { //recognized simple options option = cmd; setType (cmd, TYPE_IGNORE); } else if (cmd=="TOMENU") { if (p1!="-") toMenu+=p1; option=p1; } else if (cmd=="LINKMSG") { //linkedMsg+=[p1,p2]; addLM (p1, p2); option=p1; if ((integer)p2) setType (p1, TYPE_LM_NOMENU); else setType (p1, TYPE_LM_MENU); } else if (-1<llListFindList(["Z+", "Z-"], [llGetSubString(cmd, 0, 1)])) { //Z command, we'll rewrite it option = "Z::"+llGetSubString(cmd, 1, -1); setType ("Z", TYPE_SPECIAL); } else if (-1<llListFindList (["GRAB", "Grab.Fem"], [cmd])) { option = "GRAB"; setType ("GRAB", TYPE_SPECIAL); } else if (-1<llListFindList (["Grab.Male","RELEASE"], [cmd])) { option = "RELEASE"; setType ("RELEASE", TYPE_SPECIAL); } //todo.. fill in a bunch of MLP commands here, implemented or not else { //we checked all commands. Anything defined here is considered undefined and we list it just as is. option = cmd; //don't set type. it may be defined elsewhere. //setType (cmd, TYPE_UNKNOWN); } if (menuName) if (option) options+=option; } tempMenu=[]; if (options!=[]) { llOwnerSay ("Menu "+menuName +" ["+(string)llGetListLength(options)+"]"); //menuAll+=options+"!--!"; //menuAll+=[menuName,llList2CSV(options)]; menuNames+=menuName; menuEntries+=llList2CSV(options); }
}
addMenuItem(string data) {
integer posslash=llSubStringIndex(data, "//"); if (posslash>=0) { data=llDeleteSubString(data, posslash, -1); } data=llStringTrim(data,STRING_TRIM); //llOwnerSay ("menu data: "+data); if (data=="") //comment or some but not empty line, silently ignore return; if (llGetSubString (data,0,3+1)=="MENU ") { //what a surprise, new menu. finish old one. parseMenu(); } //what's left, add it to our temp menu. We'l parse it later. tempMenu+=data;
}
postFixMenu() {
//on failed configurations - add some default stuff if (!llGetListLength(menuNames)) { llOwnerSay ("Warning - no reset function found in menu cards. Adding menu 'Reset'."); //add a default menu with a reset button. addMenuItem ("MENU Reset...|OWNER"); addMenuItem ("RESET"); parseMenu(); } //dont add mainmenu list entries=llDeleteSubList(menuNames,0,0); integer i; //llOwnerSay ("PF entries: "+(string)entries); //llOwnerSay ("PF tomenu: "+(string)toMenu); for (i=0; i<llGetListLength(toMenu); i++) { integer j=llListFindList (entries, [llList2String(toMenu, i)]); if (j>-1) { entries=llDeleteSubList(entries, j, j); } } //llOwnerSay ("PF entries after: "+(string)entries); list MainMenu=llCSV2List(llList2String(menuEntries, 0)); for (i=0; i<llGetListLength(entries); i++) { integer j=llListFindList(MainMenu, ["-"]); if (j>-1) { MainMenu=llListReplaceList(MainMenu, [llList2String(entries, i)], j, j); } } i=llListFindList(MainMenu, ["-"]); while (-1<i) { MainMenu=llDeleteSubList(MainMenu, i, i); i=llListFindList(MainMenu, ["-"]); } if (MainMenu) menuEntries = llListReplaceList(menuEntries, [llList2CSV(MainMenu)], 0, 0);
}
sendMenuItems() {
//llOwnerSay ("Applying menu information"); integer i; for (i=0; i<llGetListLength(menuNames); i++) { llMessageLinked (LINK_THIS, MSG_DATA_MENU, llList2String(menuNames, i), llList2String(menuEntries, i)); llSleep (0.25); } llMessageLinked (LINK_THIS, MSG_DATA_READY, "Done", "TTP");
}
addPosition(string data) {
//some memory optimalization possible here in last half. integer curlyopen = llSubStringIndex(data, "{"); integer curlyclose = llSubStringIndex(data, "}"); if ((curlyopen<0) || (curlyclose<curlyopen)) return; string pose=llGetSubString(data, curlyopen+1, curlyclose-1); data=llGetSubString(data, curlyclose+1, -1); list posrotsraw=llParseString2List(data, [">"], []); //llOwnerSay ("raw pos data "+data+" convert to "+(string)posrotsraw); list posrots; integer i; for (i=0; i<llGetListLength(posrotsraw); i++) { if (-1<llSubStringIndex (llList2String(posrotsraw,i), "<")) { list v=llParseString2List(llList2String(posrotsraw,i), ["<", ",", " "], []); posrots += <(float)llList2String(v,0), (float)llList2String(v,1),(float)llList2String(v,2)>; } } //llOwnerSay ("position data for "+pose+" : "+(string)posrots); //positions+=[pose, llList2CSV(posrots)]; llMessageLinked (LINK_THIS,MSG_DATA_POSITION, pose, llDumpList2String(posrots,"&"));
}
upgrade() {string self = llGetScriptName(); string basename = self; if (llSubStringIndex(self, " ") >= 0) {integer start = 2; string tail = llGetSubString(self, llStringLength(self) - start, -1); while (llGetSubString(tail, 0, 0) != " ") {start++; tail = llGetSubString(self, llStringLength(self) - start, -1);} if ((integer)tail > 0) {basename = llGetSubString(self, 0, -llStringLength(tail) - 1);}} integer n = llGetInventoryNumber(INVENTORY_SCRIPT); while (n-- > 0) {string item = llGetInventoryName(INVENTORY_SCRIPT, n); if (item != self && 0 == llSubStringIndex(item, basename)) {llRemoveInventory(item);}}}
default
{
state_entry() { upgrade(); //state ReadMenu; } link_message (integer sn, integer n, string m, key id) { if (n==301) state ReadMenu; }
}
state ReadMenu {
state_entry() { llMessageLinked (LINK_THIS, MSG_STORAGE_RESET, "", ""); //reset data storage scripts llSleep (2.0); //fetch first notecard, go on from there startNotecard (); } dataserver(key id, string data) { //llOwnerSay ("card "+notecardname+" line "+(string)notecardline+" data "+data); if (id!=notecardkey) return; //not for us if (data==EOF) { if (notecardtype==1) parseMenu(); //clean up any menu leftovers //read next notecard notecardidx++; startNotecard(); } else { if (notecardtype==1) addMenuItem(data); else addPosition(data); notecardkey=llGetNotecardLine(notecardname, ++notecardline);
} }
} </lsl>
TMCP RLV extensions (optional)
<lsl> //Changes by Tano Toll for seamless MLCP compatability, and to dodge 'double locking':
//*sit and unsit events do not trigger an RLV command (master script already does this) //*identifier is changed to object name, url encoded. //*delayed but automatic notecard read on inventory change
// ---------------------------------------------------------------------------------- // 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
integer wants_init;
// 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, llEscapeURL(llGetObjectName())+"," + (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 ); }
}
upgrade() {string self = llGetScriptName(); string basename = self; if (llSubStringIndex(self, " ") >= 0) {integer start = 2; string tail = llGetSubString(self, llStringLength(self) - start, -1); while (llGetSubString(tail, 0, 0) != " ") {start++; tail = llGetSubString(self, llStringLength(self) - start, -1);} if ((integer)tail > 0) {basename = llGetSubString(self, 0, -llStringLength(tail) - 1);}} integer n = llGetInventoryNumber(INVENTORY_SCRIPT); while (n-- > 0) {string item = llGetInventoryName(INVENTORY_SCRIPT, n); if (item != self && 0 == llSubStringIndex(item, basename)) {llRemoveInventory(item);}}}
default
{
// on state entry initialize me state_entry() { upgrade(); initialize(); } // reset on Inventory change changed(integer change) { if (change & CHANGED_INVENTORY) { wants_init=TRUE; llSetTimerEvent (15.0); } } // 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() { if (wants_init) { wants_init = FALSE; initialize(); llSetTimerEvent (0.0); return; } 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) { //llOwnerSay ("RLV: -- "+message); 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); } }
} </lsl>