Dialog Module

This script is a standalone module for the handling of dialog boxes. All you, the script writer, have to do is build a linked message handler. Use the example access script as a starting point.if you are using a version prior to 9.3 please upgrade as there is a bug which could result in the responce not being registered8.x is not compatible with 9.x, if you are using a version prior to 8.4 please upgrade as there is a bug with bit 17Security: The dialog boxes each use a random unique negative channel, for each dialog box. Channel numbers range from -2147483648 to -255 with the possibility of the lower 8 bits being zero. The listens are bound to the user's key. This means that no script or other user can activate the interface. By using a negative number there is no way for the user to type in a command and have it mistaken as a response. Only responses that are valid buttons from the requested user (and in the mask) will result in a return; no 3rd party tampering is possible. It would be very difficult for anyone to predict the channel number to listen on (2 billion is a big range).The Request: The link message string format is a list. The first element of this list is the integer that will be used as the second integer in the return the link message. The rest of this list is string that will be returned (in list form with the same separating character). The link message key format is a list. The first element is the key of the user, if this is left blank (or NULL_KEY) the owner is assumed. The second element is the message, this can be any valid string. The third element is a float, which is the dialog box time to live. The forth element is an integer it is used as a mask for when a button is pressed if it will trigger a response. The rest of the list is for the buttons; an empty list can be used, but it will default to ["OK"] (there is no way around this, just cope).The Response: The link message string format is a list. It is whatever was passed as the second string minus the first entry of that list. The link message key format is a list. The first element of this list is the number of the button pressed (or error code). The second entry is the text of the button pressed. The third entry is the key it used for creating the dialog box. The forth entry is the user name. The fifth entry is how much time was left in the time to live of the dialog box (not very useful but maybe it is). The response always goes back to only the prim that requested the dialog box.
(string)TightListDump(llList2List(TightListParse(paramString),1,-1)),
(key)TightListDump([integer buttonNum, string buttonText, key user, string userName, float aproxTimeLeft],"")
 There is no way to create a dialog box that won't timeout. The value must be between 5.0 and 7200.0; or it will be replaced by the default timeout. The timeout won't make a box disapeer off the users screen, but the script will stop listening for results.Notes: Know your data; if your strings contain "|" then don't use "|" as the separator. If the script acts weirdly this is probably why. The separator can only be one character. The two different strings being passed in the linked message can use different separating characters.Misc: The ButtonMask has a few extra tricks. The first 12 bits corespond to the 12 buttons (bits 0 -> 11) Bits 16 through 22 are special bits for error handling and setup. All other bits are ignored, do with them as you please but be warned they may be used in the future.
Bit	Hex Value	Event 	Return Button
0 	0x000001 	Button 0 Pressed 	0
1 	0x000002 	Button 1 Pressed 	1
2 	0x000004 	Button 2 Pressed 	2
3 	0x000008 	Button 3 Pressed 	3
4 	0x000010 	Button 4 Pressed 	4
5 	0x000020 	Button 5 Pressed 	5
6 	0x000040 	Button 6 Pressed 	6
7 	0x000080 	Button 7 Pressed 	7
8 	0x000100 	Button 8 Pressed 	8
9 	0x000200 	Button 9 Pressed 	9
10 	0x000400 	Button 10 Pressed 	10
11 	0x000800 	Button 11 Pressed 	11
16 	0x010000 	Dialog box times out 	-1
17 	0x020000 	
Queue being cleared 	-2
18 	0x040000 	
Target agent not found 	-3
19 	0x080000 	No buttons to listen for, bad mask or bad return channel	-4
20 	0x100000 	Clears the queue then creates the dialog box 	-2 via Bit 17
21 	0x200000 	Same as bit 20 but only for boxes for the current user 	-2 via Bit 17
22 	0x400000 	
Target agent has left the sim 	-5
 The queue is cleared in two ways: on_rez or when creating a dialog box requests it. When the script is rez'ed, the old dialog box requests become meaningless (because the script cannot receive the response). By having bit 17 on, it allows for this event to be returned to the script requesting the box. By having bit 20 turned on also causes the queue to be reset (exactly like an on_rez). Using bit 20 may have undesired effects if you are using this module with multiple scripts. Using bit 21 causes a partial clear. It clears out all boxes for the requested user; it is advisable to use this option instead of bit 20.Todo: When bits 20 and 21 are true have the inverse result happen. All boxes not of the current user would be cleared from the queue.Tricks: Pass a list of useful values as the return list. Have them correspond to the button list. Then use the code below to get that value from the return (remember if you have any of bits 16 -> 19 enabled you will get negative index values on errors).
llList2String(TightListParse(c),(integer)llList2String(TightListParse(d),0));
----
 Example Access script to Dialog Module:
string sepchar = "|";
integer answer = 546;
integer DialogComm = 12;
dialog(key user, string message, float timeout, list buttons, integer buttonmask, integer retchan, list ret)
{
 llMessageLinked(llGetLinkNumber(), DialogComm, TightListDump([retchan] + ret, sepchar),
 TightListDump([user, message, timeout, buttonmask] + buttons, sepchar));
}
//TightList family of functions Version 1.0
//Copyright Strife Onizuka 2004-2006
//Free to use and distribute as long as this message is not removed.
string TightListDump(list input, string possibles)
{//TLD( complex) makes a string from a list using a seperator that is supposed to be unique to the string
 string buffer = (string)(input);//dump the list without a seperator
 integer counter = -39 - llStringLength(possibles);
 if(counter == -40)
 if(!~llSubStringIndex(buffer,possibles))
 jump end;//woot, we were given a unique seperator
 possibles += "|/?!@#$%^&*()_=:;~`'<>{}[],.\n\" qQxXzZ\\";//Good character set of rarely used letters.
 do; while(~llSubStringIndex(buffer,llGetSubString(possibles,counter,counter)) && (counter=-~counter));//search for unique seperator
 possibles = llGetSubString(possibles,counter,counter);
 @end;
 buffer = "";//save memory
 return possibles + llDumpList2String((input = []) + input, possibles);
}
list TightListParse(string a) {
 string b = llGetSubString(a,0,0);//save memory
 return llParseStringKeepNulls(llDeleteSubString(a,0,0), [a=b],[]);
}
default
{
 state_entry()
 {
 integer rint = 6;
 string question = "Would you like a drink?";
 list Answers = ["Yes", "No", "Strongest"];
 integer AnswerMask = 0x005;
 list ExtraPassback = ["extra info to pass back"];
 dialog(llGetOwner(), question, 30,Answers, AnswerMask , answer, ExtraPassback);
 }
 touch_start(integer a)
 {
 dialog(llDetectedKey(0), "Hello\nDo you like me?", 30, ["Yes", "No"], 0xf0fff, answer, ["I think they like us"]);
 }
 link_message(integer a, integer b, string c, key d)
 {
 if(b==answer)
 {
 llWhisper(0,(string)b);
 llWhisper(0,llList2CSV(TightListParse(c)));
 llWhisper(0,llList2CSV(TightListParse(d)));
 //c is the return list
 //d is a list [button number pressed, user, time left, user name, button text]
 }
 }
}
 Dialog Module:
//////////////////////////////////////////////////////////////////////////////////////
//
// Dialog Module
// Version 9.3 Release
// Copyright (C) 2004-2006, Strife Onizuka
// http://home.comcast.net/~mailerdaemon/
// http://secondlife.com/badgeo/wakka.php?wakka=LibraryDialogModule
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public License
// as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
//////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////
//Don't change anything else unless you *really* need to.
list handles;
list time;
list chans;
string users;//save memory
list answer;
list button;
list prim;
list mask;
list intchan;
integer ticks;
remove(integer a)
{
 llListenRemove(llList2Integer(handles,a));
 handles = llDeleteSubList(handles,a,a);
 time = llDeleteSubList(time, a,a);
 chans = llDeleteSubList(chans, a,a);
 answer = llDeleteSubList(answer, a,a);
 button = llDeleteSubList(button, a,a);
 prim = llDeleteSubList(prim, a,a);
 mask = llDeleteSubList(mask, a,a);
 intchan = llDeleteSubList(intchan,a,a);
 users = llDeleteSubString(users,a*=36,a+35);
}
//TightList family of functions Version 1.0
//Copyright Strife Onizuka 2004-2006
//Free to use and distribute as long as this message is not removed.
list TightListParse(string a) {
 string b = llGetSubString(a,0,0);//save memory
 return llParseStringKeepNulls(llDeleteSubString(a,0,0), [a=b],[]);
}
clear()
{
 integer a = llGetListLength(handles);
 while(a)
 {
 llListenRemove(llList2Integer(handles,--a));
 if(llList2Integer(mask,a)&0x20000)
 llMessageLinked(llList2Integer(prim,a), llList2Integer(intchan,a),
 llList2String(answer,a), "|-2||"+llGetSubString(users,a * 36,(a * 36) + 35)+"||-1");
 }
 llSetTimerEvent(ticks = 0);
}
default
{
 state_entry()
 {
 llOwnerSay("Dialog Module, Version 9.3, Released Under the GNU Lesser General Public License");
 llOwnerSay("Copyright (C) 2004-2006, Strife Onizuka, http://secondlife.com/badgeo/wakka.php?wakka=LibraryDialogModule");
 }
 on_rez(integer a)
 {
 clear();
 }
 link_message(integer a, integer b, string c, key d)
 {
 if(b == 12)
 {
 b = llSubStringIndex(llDeleteSubString(c,0,0), llGetSubString(c,0,0));
 list e = TightListParse(d);
 integer buttonmask = (integer)llList2String(e,3);
 string user = llList2String(e,0);
 list buttons = llDeleteSubList(e,0,3);
 float timeout = (float)llList2String(e,2);
 integer cat = (integer)llFrand(-2147483392.0) - 255;
 integer chan = (integer)llDeleteSubString(c, b + 1, 0);
 string ans = llDeleteSubString(c,0,b);
 if(buttonmask & 0x100000)
 clear();
 else if(buttonmask & 0x200000)
 {//clean out other box's that went to this user.
 while(1 + (b = llSubStringIndex(users, user)))
 {
 if(llList2Integer(mask,b/=36)&0x20000)
 llMessageLinked(llList2Integer(prim,b), llList2Integer(intchan,b),
 llList2String(answer,b), "|-2||"+llGetSubString(users,b * 36,(b * 36) + 35)+"||-1");
 remove(b);
 }
 if(time == [])
 llSetTimerEvent(ticks = 0);
 }
 if(user == "" || user == NULL_KEY) //lazy check
 user = llGetOwner();
 if(!llGetAgentInfo(user))
 {// target not in the sim
 if(buttonmask & 0x40000)
 llMessageLinked(a, chan, ans, "|-3||"+user+"||0");
 jump end1;//instead of a return, too many local variables to clear off the stack.
 }
 while(1+llListFindList(chans,[cat]))
 --cat;
 b = llListen(cat,"",user,"");
 llDialog(user, llList2String(e,1), buttons, cat);
 if(chan != 12)
 {//loopback catch
 if(buttons == []) // so we can match the ok button
 buttons = ["OK"];
 if(buttonmask & ((1<<llGetListLength(buttons)) - 1))
 { //we checked the mask to see if we should expect any values back
 chans += cat;
 handles += b;
 if(timeout < 5.0 || timeout > 7200)
 timeout = 5.0;
 time += (ticks + (timeout / 5.0));
 users += user;
 answer += ans;
 button += (llGetSubString(d,0,0) + llDumpList2String(buttons, llGetSubString(d,0,0)));
 prim += a;
 mask += buttonmask;
 intchan += chan;
 llSetTimerEvent(5.0);
 jump end2;//instead of a return, too many local variables to clear off the stack.
 }
 }
 llListenRemove(b);
 if(buttonmask & 0x80000)
 llMessageLinked(a, chan, ans, "|-4||"+(string)user+"||0");
 }
 @end1;@end2;
 }
 listen(integer a, string b, key c, string d)
 {
 if(a+1 && llGetSubString(users,36 * a=llListFindList(chans,[a]),(a * 36) + 35) == c)
 {//it's one of our listens
 integer f = llListFindList(TightListParse(llList2String(button,a)),[d]);
 if(f+1)
 {//we matched a button
 if(llList2Integer(mask,a)&(1<<f))
 {
 list ret = [f, d, c, b,(llList2Float(time,a) - ticks) * 5.0];
 if(llSubStringIndex(d = (string)ret,b = "|") + 1)
 {
 f = -37;
 b = "|\\/?!@#$%^&*()_=:;~{}[]<>`',\n\" qQxXzZ";
 do;while(1+llSubStringIndex(d,llGetSubString(b,f,f)) && ++f);
 b = llGetSubString(b,f,f);
 }
 d = "";
 llMessageLinked(llList2Integer(prim,a), llList2Integer(intchan,a),
 llList2String(answer,a), (b + llDumpList2String(ret, b)));
 }
 remove(a);
 if(time == [])
 llSetTimerEvent(ticks = 0);
 }
 }
 }
 timer()
 {
 ++ticks;
 integer a = llGetListLength(time);
 float c;
 key d;
 while(a)
 {
 if(((c = llList2Float(time,a)) <= ticks) || !llGetAgentInfo(d = llGetSubString(users,--a * 36,(a * 36) + 35)))
 {
 if(llList2Integer(mask,a)&0x10000)
 llMessageLinked(llList2Integer(prim,a), llList2Integer(intchan,a),
 llList2String(answer,a), "|-1||"+(string)d+"||"+(string)((ticks - c) * 5.0));
 remove(a);
 }
 }
 if(time == [])
 llSetTimerEvent(ticks = 0);
 }
}
 ESL Version:
//////////////////////////////////////////////////////////////////////////////////////
//
// Dialog Module
// Version 9.3 Release
// Copyright (C) 2004-2006, Strife Onizuka
// http://home.comcast.net/~mailerdaemon/
// http://secondlife.com/badgeo/wakka.php?wakka=LibraryDialogModule
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public License
// as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
//////////////////////////////////////////////////////////////////////////////////////
#ifndef DIALOGCOMM
 #define DIALOGCOMM 12
#endif
#ifdef USESCRIPTNAME
integer DialogComm = DIALOGCOMM;
#elif !defined DialogComm
#define DialogComm DIALOGCOMM
#endif
#ifndef TICKSIZE
 #define TICKSIZE 5.0
#endif
#ifndef DEFAULTTIMEOUT
 #define DEFAULTTIMEOUT TICKSIZE
#endif
//////////////////////////////////////////////////////////////////////////////////////
//Don't change anything else unless you *really* need to.
#define ZERO_INTEGER 0
list handles;
list time;
list chans;
string users;//save memory
list answer;
list button;
list prim;
list mask;
list intchan;
integer ticks;
remove(integer a)
{
 llListenRemove(llList2Integer(handles,a));
 handles = llDeleteSubList(handles,a,a);
 time = llDeleteSubList(time, a,a);
 chans = llDeleteSubList(chans, a,a);
 answer = llDeleteSubList(answer, a,a);
 button = llDeleteSubList(button, a,a);
 prim = llDeleteSubList(prim, a,a);
 mask = llDeleteSubList(mask, a,a);
 intchan = llDeleteSubList(intchan,a,a);
 users = llDeleteSubString(users,a*=36,a+35);
}
#define wipe() llSetTimerEvent(ticks = ZERO_INTEGER)
#define quickdump(a, b) (b + llDumpList2String(a, b))
#ifndef quickdump
string quickdump(list a, string b){ return b + llDumpList2String(a, b); }
#endif
#//include "tightlist.esl"
//Tightlist family of functions Version 1.0
//Copyright Strife Onizuka 2004-2006
//Free to use and distribute as long as this message is not removed.
list TightListParse(string a) {
 string b = llGetSubString(a,ZERO_INTEGER,ZERO_INTEGER);//save memory
 return llParseStringKeepNulls(llDeleteSubString(a,ZERO_INTEGER,ZERO_INTEGER), [a=b],[]);
}
clear()
{
 integer a = llGetListLength(handles);
 while(a)
 {
 llListenRemove(llList2Integer(handles,--a));
 if(llList2Integer(mask,a)&0x20000)
 llMessageLinked(llList2Integer(prim,a), llList2Integer(intchan,a),
 llList2String(answer,a), "|-2||"+llGetSubString(users,a * 36,(a * 36) + 35)+"||-1");
 }
 wipe();
}
default
{
 state_entry()
 {
 #ifdef USESCRIPTNAME
 if((DialogComm += (integer)llList2String(llParseString2List(llGetScriptName(), [" "],[]), -1)) == DialogComm)
 {
 #endif
 llOwnerSay("Dialog Module, Version 9.3, Released Under the GNU Lesser General Public License");
 llOwnerSay("Copyright (C) 2004-2006, Strife Onizuka, http://secondlife.com/badgeo/wakka.php?wakka=LibraryDialogModule");
 #ifdef USESCRIPTNAME
 }
 #endif
 }
 on_rez(integer a)
 {
 clear();
 }
 link_message(integer a, integer b, string c, key d)
 {
 if(b == DialogComm)
 {
 b = llSubStringIndex(llDeleteSubString(c,ZERO_INTEGER,ZERO_INTEGER), llGetSubString(c,ZERO_INTEGER,ZERO_INTEGER));
 list e = TightListParse(d);
 integer buttonmask = (integer)llList2String(e,3);
 string user = llList2String(e,ZERO_INTEGER);
 list buttons = llDeleteSubList(e,ZERO_INTEGER,3);
 float timeout = (float)llList2String(e,2);
 integer cat = (integer)llFrand(-2147483392.0) - 255;
 integer chan = (integer)llDeleteSubString(c, b + 1, ZERO_INTEGER);
 string ans = llDeleteSubString(c,ZERO_INTEGER,b);
 if(buttonmask & 0x100000)
 clear();
 else if(buttonmask & 0x200000)
 {//clean out other box's that went to this user.
 while(1 + (b = llSubStringIndex(users, user)))
 {
 if(llList2Integer(mask,b/=36)&0x20000)
 llMessageLinked(llList2Integer(prim,b), llList2Integer(intchan,b),
 llList2String(answer,b), "|-2||"+llGetSubString(users,b * 36,(b * 36) + 35)+"||-1");
 remove(b);
 }
 if(time == [])
 wipe();
 }
 if(user == "" || user == NULL_KEY) //lazy check
 user = llGetOwner();
 if(!llGetAgentInfo(user))
 {// target not in the sim
 if(buttonmask & 0x40000)
 llMessageLinked(a, chan, ans, "|-3||"+user+"||0");
 jump end1;//instead of a return, too many local variables to clear off the stack.
 }
 while(1+llListFindList(chans,[cat]))
 --cat;
 b = llListen(cat,"",user,"");
 llDialog(user, llList2String(e,1), buttons, cat);
 if(chan != DialogComm)
 {//loopback catch
 if(buttons == []) // so we can match the ok button
 buttons = ["OK"];
 if(buttonmask & ((1<<llGetListLength(buttons)) - 1))
 { //we checked the mask to see if we should expect any values back
 chans += cat;
 handles += b;
 if(timeout < 5.0 || timeout > 7200)
 timeout = DEFAULTTIMEOUT;
 time += (ticks + (timeout / TICKSIZE));
 users += user;
 answer += ans;
 button += quickdump(buttons,llGetSubString(d,ZERO_INTEGER,ZERO_INTEGER));
 prim += a;
 mask += buttonmask;
 intchan += chan;
 llSetTimerEvent(TICKSIZE);
 jump end2;//instead of a return, too many local variables to clear off the stack.
 }
 }
 llListenRemove(b);
 if(buttonmask & 0x80000)
 llMessageLinked(a, chan, ans, "|-4||"+(string)user+"||0");
 }
 @end1;@end2;
 }
 listen(integer a, string b, key c, string d)
 {
 if(a+1 && llGetSubString(users,36 * a=llListFindList(chans,[a]),(a * 36) + 35) == c)
 {//it's one of our listens
 integer f = llListFindList(TightListParse(llList2String(button,a)),[d]);
 if(f+1)
 {//we matched a button
 if(llList2Integer(mask,a)&(1<<f))
 {
 list ret = [f, d, c, b,(llList2Float(time,a) - ticks) * TICKSIZE];
 if(llSubStringIndex(d = (string)ret,b = "|") + 1)
 {
 f = -37;
 b = "|\\/?!@#$%^&*()_=:;~{}[]<>`',\n\" qQxXzZ";
 do;while(1+llSubStringIndex(d,llGetSubString(b,f,f)) && ++f);
 b = llGetSubString(b,f,f);
 }
 d = "";
 llMessageLinked(llList2Integer(prim,a), llList2Integer(intchan,a),
 llList2String(answer,a), quickdump(ret, b));
 }
 remove(a);
 if(time == [])
 wipe();
 }
 }
 }
 timer()
 {
 ++ticks;
 integer a = llGetListLength(time);
 float c;
 key d;
 while(a)
 {
 if(((c = llList2Float(time,a)) <= ticks) || !llGetAgentInfo(d = llGetSubString(users,--a * 36,(a * 36) + 35)))
 {
 if(llList2Integer(mask,a)&0x10000)
 llMessageLinked(llList2Integer(prim,a), llList2Integer(intchan,a),
 llList2String(answer,a), "|-1||"+(string)d+"||"+(string)((ticks - c) * TICKSIZE));
 remove(a);
 }
 }
 if(time == [])
 wipe();
 }
}
----
Inventory Menu Module