Difference between revisions of "Dialog Control v1.8"

From Second Life Wiki
Jump to navigation Jump to search
(Created page with '<lsl> // ********** SIMPLE DIALOG MODULE ********** // // By Nargus Asturias // Version 1.58 // // Support only one dialog at a time. DO NOT request multiple dialog at once! ...')
 
m (<lsl> tag to <source>)
 
(8 intermediate revisions by one other user not shown)
Line 1: Line 1:
<lsl>
{{LSL Header}}
 
<source lang="lsl2">
// ********** SIMPLE DIALOG MODULE ********** //
// ********** SIMPLE DIALOG MODULE ********** //
// By Nargus Asturias
// By Nargus Asturias
 
// Version 1.86
// Version 1.58
 
//
//
// Support only one dialog at a time. DO NOT request multiple dialog at once!
// Support only one dialog at a time. DO NOT request multiple dialog at once!
// Use of provided functions are recommented. Instruction here are for hardcore programmers only!
// Use of provided functions are recommented. Instruction here are for hardcore programmers only!
//
//
// Request: Send MessageLinked to the script. There are 3 dialog modes:
// Request: Send MessageLinked to the script. There are 3 dialog modes:
//      lnkDialog              : Normal dialog with message and buttons
//      lnkDialog              : Normal dialog with message and buttons
//      lnkDialogNumericAdjust  : A dialog with buttons can be used to adjust numeric value
//      lnkDialogNumericAdjust  : A dialog with buttons can be used to adjust numeric value
 
//      lnkDialogTextBox        : A dialog with a text input field
//      lnkDialogNotify        : Just a simple notification dialog. No return value and no buttons.
//      lnkDialogNotify        : Just a simple notification dialog. No return value and no buttons.
//
//
// Send MessageLinked with code lnkDialogReshow to force active dialog to reappear.
// Send MessageLinked with code lnkDialogReshow to force active dialog to reappear.
// Send MessageLinked with code lnkDialogCancel to force cancel active dialog
// Send MessageLinked with code lnkDialogCancel to force cancel active dialog
//
//
// If a lnkDialog is requested with more than 12 buttons, multi-pages dialog is used to show the buttons.
// If a lnkDialog is requested with more than 12 buttons, multi-pages dialog is used to show the buttons.
//
//
 
// [ FOR lnkDialog ]
// [ For lnkDialog ]
 
// MessageLinked Format:
// MessageLinked Format:
//      String part: List dumped into string, each entry seperated by '||'
//      String part: List dumped into string, each entry seperated by '||'
//          Field 1:    Dialog message (512 bytes maximum)
//          Field 1:    Dialog message (512 bytes maximum)
//          Field 2:    Time-out data (integer)
//          Field 2:    Time-out data (integer)
//          Field 3-4:  Button#1 and return value pair
//          Field 3-4:  Button#1 and return value pair
//          Field 5-6:  Button#2 and return value pair
//          Field 5-6:  Button#2 and return value pair
//          And go on...
//          And go on...
//      Key part: Key of AV who attend this dialog
//      Key part: Key of AV who attend this dialog
//
//
// Response: MessageLinked to the prim that requested dialog (but no where else)
// Response: MessageLinked to the prim that requested dialog (but no where else)
//      num == lnkDialogResponse:  AV click on a button. The buttons value returned as a string
//      num == lnkDialogResponse:  AV click on a button. The buttons value returned as a string
//      num == lnkDialogTimeOut:    Dialog timeout.
//      num == lnkDialogTimeOut:    Dialog timeout.
//
//
 
// [ FOR lnkDialogTextbox ]
// [ For lnkDialogNumericAdjust ]
// MessageLinked Format:
 
//      String part: List dumped into string, each entry seperated by '||'
//          Field 1:    Dialog message (512 bytes maximum)
//          Field 2:    Time-out data (integer)
//          Field 3:    Return string prefix. If empty, only user's input will be returned.
//
// [ FOR lnkDialogNumericAdjust ]
// MessageLinked Format:
// MessageLinked Format:
//      String part: List dumped into string, each entry seperated by '||'
//      String part: List dumped into string, each entry seperated by '||'
//          Field 1:    Dialog message (512 bytes maximum)
//          Field 1:    Dialog message (512 bytes maximum)
//                          Put {VALUE} where you want the current value to be displayed)
//                          Put {VALUE} where you want the current value to be displayed)
//          Field 2:    Time-out data (integer)
//          Field 2:    Time-out data (integer)
//          Field 3:    Most significant value (ie. 100, for +/-100)
//          Field 3:    Most significant value (ie. 100, for +/-100)
//          Field 4:    String-casted numeric value to be adjusted
//          Field 4:    String-casted numeric value to be adjusted
//          Field 5:    2nd most significant value (ie. 10, for +/-10)
//          Field 5:    2nd most significant value (ie. 10, for +/-10)
//          Field 6:    Use '1' to enable "+/-" button, '0' otherwise.
//          Field 6:    Use '1' to enable "+/-" button, '0' otherwise.
//          Field 7:    3nd significant value (ie. 1, for +/-1)
//          Field 7:    3nd significant value (ie. 1, for +/-1)
//          Field 8:    Use '1' for integer, or '0' for float
//          Field 8:    Use '1' for integer, or '0' for float
//          Field 9:    Least significant value (ie. 0.1, for +/-0.1)
//          Field 9:    Least significant value (ie. 0.1, for +/-0.1)
//          Field 10:  Reserved. Not used.
//          Field 10:  Reserved. Not used.
//      Key part: Key of AV who attend this dialog
//      Key part: Key of AV who attend this dialog
//
//
// Response: MessageLinked to the prim that requested dialog (but no where else)
// Response: MessageLinked to the prim that requested dialog (but no where else)
//      num == lnkDialogResponse:  OK or Cancel button is clicked. The final value returned as string.
//      num == lnkDialogResponse:  OK or Cancel button is clicked. The final value returned as string.
//      num == lnkDialogTimeOut:    Dialog timeout.
//      num == lnkDialogTimeOut:    Dialog timeout.
//
//
// ******************************************* //
// ******************************************* //


// Constants
// Constants
integer lnkDialog = 14001;
integer lnkDialog = 14001;
integer lnkDialogNumericAdjust = 14005;
integer lnkDialogNumericAdjust = 14005;
integer lnkDialogNotify = 14004;
integer lnkDialogNotify = 14004;
 
integer lnkDialogTextBox = 14007;
 


integer lnkDialogReshow = 14011;
integer lnkDialogReshow = 14011;
integer lnkDialogCancel = 14012;
integer lnkDialogCancel = 14012;


integer lnkDialogResponse = 14002;      // A button is hit, or OK is hit for lnkDialogNumericAdjust
integer lnkDialogResponse = 14002;      // A button is hit, or OK is hit for lnkDialogNumericAdjust
integer lnkDialogCanceled = 14006;      // Cancel is hit for lnkDialogNumericAdjust
integer lnkDialogCanceled = 14006;      // Cancel is hit for lnkDialogNumericAdjust
integer lnkDialogTimeOut = 14003;      // No button is hit, or Ignore is hit
integer lnkDialogTimeOut = 14003;      // No button is hit, or Ignore is hit


integer lnkMenuClear = 15001;
integer lnkMenuClear = 15001;
integer lnkMenuAdd = 15002;
integer lnkMenuAdd = 15002;
integer lnkMenuShow = 15003;
integer lnkMenuShow = 15003;
integer lnkMenuNotFound = 15010;
integer lnkMenuNotFound = 15010;
 
integer lnkMenuSetSound = 15021;
 


string seperator = "||";
string seperator = "||";


// Menus variables
// Menus variables
list menuNames = [];        // List of names of all menus
list menuNames = [];        // List of names of all menus
list menus = [];            // List of packed menus command, in order of menuNames
list menus = [];            // List of packed menus command, in order of menuNames


integer lastMenuIndex = 0;  // Latest called menu's index
integer lastMenuIndex = 0;  // Latest called menu's index


 
string menuSound;          // Sound to me played when a menu is shown
float menuSoundVol = 1;


// Dialog variables
// Dialog variables
integer dialogChannel;      // Channel number used to spawn this dialog
integer dialogChannel;      // Channel number used to spawn this dialog
string message;            // Message to be shown with the dialog
string message;            // Message to be shown with the dialog
integer timerOut;          // Dialog time-out
integer timerOut;          // Dialog time-out
key keyId;                  // Key of user who attending this dialog
key keyId;                  // Key of user who attending this dialog
integer requestedNum;      // Link-number of the requested prim
integer requestedNum;      // Link-number of the requested prim
list buttons;              // List of dialog buttons
list buttons;              // List of dialog buttons
list returns;              // List of results from dialog buttons
list returns;              // List of results from dialog buttons


float numericValue;
float numericValue;
integer useInteger;
integer useInteger;


// Other variables
// Other variables
integer buttonsCount;
integer buttonsCount;
integer startItemNo;
integer startItemNo;
integer listenId;
integer listenId;


string redirectState;
string redirectState;


integer responseInt = -1;
integer responseInt = -1;
string responseStr;
string responseStr;
key responseKey;
key responseKey;




// ********** String Functions **********
// ********** String Functions **********
string replaceString(string pattern, string replace, string source){
string replaceString(string pattern, string replace, string source){
     integer index = llSubStringIndex(source, pattern);
     integer index = llSubStringIndex(source, pattern);
     if(index < 0) return source;
     if(index < 0) return source;
      
      
     source = llDeleteSubString(source, index, (index + llStringLength(pattern) - 1));
     source = llDeleteSubString(source, index, (index + llStringLength(pattern) - 1));
     return llInsertString(source, index, replace);
     return llInsertString(source, index, replace);
}
}


// ********** Dialog Functions **********
// ********** Dialog Functions **********
// Function: createDialog
// Function: createDialog
// Create dialog with given message and buttons, direct to user with give id
// Create dialog with given message and buttons, direct to user with give id
integer createDialog(key id, string message, list buttons){
integer createDialog(key id, string message, list buttons){
     integer channel = -((integer)llFrand(8388608))*(255) - (integer)llFrand(8388608) - 11;
     integer channel = -((integer)llFrand(8388608))*(255) - (integer)llFrand(8388608) - 11;


     llListenRemove(listenId);
     llListenRemove(listenId);
     listenId = llListen(channel, "", keyId, "");
     listenId = llListen(channel, "", keyId, "");
 
    if(buttonsCount > 0)
    llDialog(keyId, message, buttons, channel);
        llDialog(keyId, message, buttons, channel);
 
    else llTextBox(keyId, message, channel);
 


     return channel;
     return channel;
}
}


// Function: createMultiDialog
// Function: createMultiDialog
// Create dialog with multiple pages. Each page has Back, Next, and a Close button. Otherwise same functionality as createDialog() function.
// Create dialog with multiple pages. Each page has Back, Next, and a Close button. Otherwise same functionality as createDialog() function.
integer createMultiDialog(key id, string message, list buttons, integer _startItemNo){
integer createMultiDialog(key id, string message, list buttons, integer _startItemNo){
     integer channel = -llRound(llFrand( llFabs(llSin(llGetGMTclock())) * 1000000 )) - 11;
     integer channel = -llRound(llFrand( llFabs(llSin(llGetGMTclock())) * 1000000 )) - 11;


     if(_startItemNo < 0) _startItemNo = 0;
     if(_startItemNo < 0) _startItemNo = 0;
     if(_startItemNo >= buttonsCount - 1) _startItemNo -= 9;
     if(_startItemNo >= buttonsCount - 1) _startItemNo -= 9;
     startItemNo = _startItemNo;
     startItemNo = _startItemNo;
      
      
     integer vButtonsCount = buttonsCount - 2;
     integer vButtonsCount = buttonsCount - 2;
      
      
     // Generate list of buttons to be shown
     // Generate list of buttons to be shown
     string closeButton = llList2String(buttons, buttonsCount - 1);
     string closeButton = llList2String(buttons, buttonsCount - 1);
      
      
     integer stopItemNo = startItemNo + 8;
     integer stopItemNo = startItemNo + 8;
     if(stopItemNo >= buttonsCount - 1) stopItemNo = vButtonsCount;
     if(stopItemNo >= buttonsCount - 1) stopItemNo = vButtonsCount;


     list thisButtons = llList2List(buttons, startItemNo, stopItemNo);
     list thisButtons = llList2List(buttons, startItemNo, stopItemNo);


     // Generate dialog navigation buttons
     // Generate dialog navigation buttons
     integer i = stopItemNo - startItemNo + 1;
     integer i = stopItemNo - startItemNo + 1;
     i = i % 3;
     i = i % 3;
     if(i > 0) while(i < 3){
     if(i > 0) while(i < 3){
         thisButtons += [" "];
         thisButtons += [" "];
         ++i;
         ++i;
     }
     }
      
      
     if(startItemNo > 0)
     if(startItemNo > 0)
         thisButtons += ["<< BACK"];
         thisButtons += ["<< BACK"];
     else thisButtons += [" "];
     else thisButtons += [" "];
      
      
     thisButtons += [closeButton];
     thisButtons += [closeButton];
      
      
     if(stopItemNo < vButtonsCount)
     if(stopItemNo < vButtonsCount)
         thisButtons += ["NEXT >>"];
         thisButtons += ["NEXT >>"];
     else thisButtons += [" "];
     else thisButtons += [" "];
      
      
     // Append page number to the message
     // Append page number to the message
     integer pageNumber = (integer)(stopItemNo / 9) + 1;
     integer pageNumber = (integer)(stopItemNo / 9) + 1;
     integer pagesCount = llCeil(vButtonsCount / 9.0);
     integer pagesCount = llCeil(vButtonsCount / 9.0);
     string vMessage = "PAGE: " + (string)pageNumber + " of " + (string)pagesCount + "\n" +
     string vMessage = "PAGE: " + (string)pageNumber + " of " + (string)pagesCount + "\n" +
         message;
         message;


     // Display dialog
     // Display dialog
     llListenRemove(listenId);
     llListenRemove(listenId);
     listenId = llListen(channel, "", keyId, "");
     listenId = llListen(channel, "", keyId, "");
     llDialog(keyId, vMessage, thisButtons, channel);
     llDialog(keyId, vMessage, thisButtons, channel);


     return channel;
     return channel;
}
}


// Function: generateNumericAdjustButtons
// Function: generateNumericAdjustButtons
// Generate numeric adjustment dialog which adjustment values are in given list.
// Generate numeric adjustment dialog which adjustment values are in given list.
// If useNegative is TRUE, "+/-" button will be available.
// If useNegative is TRUE, "+/-" button will be available.
list generateNumericAdjustButtons(list adjustValues, integer useNegative){
list generateNumericAdjustButtons(list adjustValues, integer useNegative){
     list dialogControlButtons;
     list dialogControlButtons;
     list positiveButtons;
     list positiveButtons;
     list negativeButtons;
     list negativeButtons;
     list additionButtons;
     list additionButtons;


     dialogControlButtons = ["OK", "Cancel"];
     dialogControlButtons = ["OK", "Cancel"];


     // Config adjustment buttons
     // Config adjustment buttons
     integer count = llGetListLength(adjustValues);
     integer count = llGetListLength(adjustValues);
     integer index;
     integer index;
     for(index = 0; (index < count) && (index < 3); index++){
     for(index = 0; (index < count) && (index < 3); index++){
         string sValue = llList2String(adjustValues, index);
         string sValue = llList2String(adjustValues, index);


         if((float)sValue != 0){
         if((float)sValue != 0){
             positiveButtons += ["+" + sValue];
             positiveButtons += ["+" + sValue];
             negativeButtons += ["-" + sValue];
             negativeButtons += ["-" + sValue];
         }
         }
     }
     }


     // Check positive/negative button
     // Check positive/negative button
     if(useNegative)
     if(useNegative)
         additionButtons = ["+/-"];
         additionButtons = ["+/-"];
     else additionButtons = [];
     else additionButtons = [];


     // If there is fourth adjustment button
     // If there is fourth adjustment button
     if(count > 3){
     if(count > 3){
         if(llGetListLength(additionButtons) == 0) additionButtons = [" "];
         if(llGetListLength(additionButtons) == 0) additionButtons = [" "];


         string sValue = llList2String(adjustValues, index);
         string sValue = llList2String(adjustValues, index);
         additionButtons += ["+" + sValue, "-" + sValue];
         additionButtons += ["+" + sValue, "-" + sValue];
     }else if(additionButtons != []) additionButtons += [" ", " "];
     }else if(additionButtons != []) additionButtons += [" ", " "];
      
      
     // Return list dialog buttons
     // Return list dialog buttons
     return additionButtons + negativeButtons + positiveButtons + dialogControlButtons;
     return additionButtons + negativeButtons + positiveButtons + dialogControlButtons;
}
}


setResponse(integer int, string str, key id){
setResponse(integer int, string str, key id){
     responseInt = int;
     responseInt = int;
     responseStr = str;
     responseStr = str;
     responseKey = id;
     responseKey = id;
}
}


checkDialogRequest(integer sender_num, integer num, string str, key id){
checkDialogRequest(integer sender_num, integer num, string str, key id){
 
    // Common dialog requests
    if((num == lnkDialogNotify) || (num == lnkDialogNumericAdjust) || (num == lnkDialog)){
    if((num == lnkDialogNotify) || (num == lnkDialogNumericAdjust) || (num == lnkDialog)){
 
         list data = llParseStringKeepNulls(str, [seperator], []);
         list data = llParseStringKeepNulls(str, [seperator], []);
      
      
         message = llList2String(data, 0);
         message = llList2String(data, 0);
         timerOut = llList2Integer(data, 1);
         timerOut = llList2Integer(data, 1);
         keyId = id;
         keyId = id;
         requestedNum = sender_num;
         requestedNum = sender_num;
         buttons = [];
         buttons = [];
         returns = [];
         returns = [];
      
      
         if(message == "") message = " ";
         if(message == "") message = " ";
         if(timerOut > 7200) timerOut = 7200;
         if(timerOut > 7200) timerOut = 7200;
      
      
 
        // Generate buttons list
         integer i;
         integer i=2;
 
         integer count = llGetListLength(data);
         integer count = llGetListLength(data);
 
        if(count > 2){
        for(i=2; i<count;){
            buttonsCount = 0;
 
            for(; i<count;){
            buttons += [llList2String(data, i++)];
                buttons += [llList2String(data, i++)];
 
                returns += [llList2String(data, i++)];
            returns += [llList2String(data, i++)];
                ++buttonsCount;
 
            }
        }else{
            buttons = ["OK"];
            returns = [];
            buttonsCount = 1;
         }
         }
       
        buttonsCount = llGetListLength(buttons);
      
      
 
        // Determine the type of dialog
         if(num == lnkDialogNotify){
         if(num == lnkDialogNotify){
             dialogChannel = -((integer)llFrand(8388608))*(255) - (integer)llFrand(8388608) - 11;
             dialogChannel = -((integer)llFrand(8388608))*(255) - (integer)llFrand(8388608) - 11;
             llDialog(keyId, message, buttons, dialogChannel);
             llDialog(keyId, message, buttons, dialogChannel);
             return;
             return;
         }else if(num == lnkDialogNumericAdjust)
         }else if(num == lnkDialogNumericAdjust)
             redirectState = "NumericAdjustDialog";
             redirectState = "NumericAdjustDialog";
         else if(num == lnkDialog){
         else if(num == lnkDialog){
             if(buttonsCount > 12)
             if(buttonsCount > 12)
                 redirectState = "MultiDialog";
                 redirectState = "MultiDialog";
             else redirectState = "Dialog";
             else redirectState = "Dialog";
         }
         }
       
        if(TRUE) state StartDialog;
   
    // Text box request
    }else if(num == lnkDialogTextBox){
        list data = llParseStringKeepNulls(str, [seperator], []);


          
         message = llList2String(data, 0);
        timerOut = llList2Integer(data, 1);
        keyId = id;
        requestedNum = sender_num;
        buttons = [];
        returns = [llList2String(data, 2)];
        buttonsCount = 0;


        if(timerOut > 7200) timerOut = 7200;
   
        redirectState = "Dialog";
         if(TRUE) state StartDialog;
         if(TRUE) state StartDialog;
 
   
    // Otherwise; Check for menu requests
     }else checkMenuRequest(sender_num, num, str, id);
     }else checkMenuRequest(sender_num, num, str, id);
}
}


// ********** Menu Functions **********
// ********** Menu Functions **********
clearMenusList(){
clearMenusList(){
     menuNames = [];
     menuNames = [];
     menus = [];
     menus = [];


     lastMenuIndex = 0;
     lastMenuIndex = 0;
}
}


addMenu(string name, string message, list buttons, list returns){
addMenu(string name, string message, list buttons, list returns){
     // Reduced menu request time by packing request commands
     // Reduced menu request time by packing request commands
     string packed_message = message + seperator + "0";
     string packed_message = message + seperator + "0";
      
      
     integer i;
     integer i;
     integer count = llGetListLength(buttons);
     integer count = llGetListLength(buttons);
     for(i=0; i<count; i++) packed_message += seperator + llList2String(buttons, i) + seperator + llList2String(returns, i);
     for(i=0; i<count; i++) packed_message += seperator + llList2String(buttons, i) + seperator + llList2String(returns, i);


     // Add menu to the menus list
     // Add menu to the menus list
     integer index = llListFindList(menuNames, [name]);
     integer index = llListFindList(menuNames, [name]);
     if(index >= 0)
     if(index >= 0)
         menus = llListReplaceList(menus, [packed_message], index, index);
         menus = llListReplaceList(menus, [packed_message], index, index);
     else{
     else{
         menuNames += [name];
         menuNames += [name];
         menus += [packed_message];
         menus += [packed_message];
     }
     }
}
}


integer showMenu(string name, key id){
integer showMenu(string name, key id){
     if(llGetListLength(menuNames) <= 0) return FALSE;
     if(llGetListLength(menuNames) <= 0) return FALSE;
    
    
     integer index;
     integer index;
     if(name != ""){
     if(name != ""){
         index = llListFindList(menuNames, [name]);
         index = llListFindList(menuNames, [name]);
         if(index < 0) return FALSE;
         if(index < 0) return FALSE;
     }else index = lastMenuIndex;
     }else index = lastMenuIndex;
      
      
     lastMenuIndex = index;
     lastMenuIndex = index;
      
      
     // Load menu command and execute
     // Load menu command and execute
    string packed_message = llList2String(menus, index);


     string packed_message = llList2String(menus, index);  
     if(menuSound != "") llPlaySound(menuSound, menuSoundVol);
 
     llMessageLinked(LINK_THIS, lnkDialog, packed_message, id);
     llMessageLinked(LINK_THIS, lnkDialog, packed_message, id);
     return TRUE;
     return TRUE;
}
}


checkMenuRequest(integer sender_num, integer num, string str, key id){
checkMenuRequest(integer sender_num, integer num, string str, key id){
     // Menu response commands
     // Menu response commands
     if(num == lnkDialogResponse){
     if(num == lnkDialogResponse){
         if(llGetSubString(str, 0, 4) == "MENU_"){
         if(llGetSubString(str, 0, 4) == "MENU_"){
             str = llDeleteSubString(str, 0, 4);
             str = llDeleteSubString(str, 0, 4);
             showMenu(str, id);
             showMenu(str, id);
         }
         }
     }
     }
          
          
     // Menu management commands
     // Menu management commands
     else if(num == lnkMenuClear)
     else if(num == lnkMenuClear)
         clearMenusList();
         clearMenusList();
     else if(num == lnkMenuAdd){
     else if(num == lnkMenuAdd){
         list data = llParseString2List(str, [seperator], []);
         list data = llParseString2List(str, [seperator], []);


         string message = llList2String(data, 0);
         string message = llList2String(data, 0);
         list buttons = [];
         list buttons = [];
         list returns = [];
         list returns = [];


         integer i;
         integer i;
         integer count = llGetListLength(data);
         integer count = llGetListLength(data);
         for(i=2; i<count;){
         for(i=2; i<count;){
             buttons += [llList2String(data, i++)];
             buttons += [llList2String(data, i++)];
             returns += [llList2String(data, i++)];
             returns += [llList2String(data, i++)];
         }
         }


         addMenu((string)id, message, buttons, returns);
         addMenu((string)id, message, buttons, returns);


     }else if(num == lnkMenuShow){
     }else if(num == lnkMenuShow){
         if(!showMenu(str, id)) llMessageLinked(sender_num, lnkMenuNotFound, str, NULL_KEY);
         if(!showMenu(str, id)) llMessageLinked(sender_num, lnkMenuNotFound, str, NULL_KEY);


    }else if(num == lnkMenuSetSound){
        menuSound = str;
        menuSoundVol = (float)((string)id);
     }
     }
}
}


// ********** States **********
// ********** States **********
default{
default{
     state_entry(){
     state_entry(){
         if(responseInt > 0) llMessageLinked(requestedNum, responseInt, responseStr, responseKey);
         if(responseInt > 0) llMessageLinked(requestedNum, responseInt, responseStr, responseKey);
     }
     }
      
      
     link_message(integer sender_num, integer num, string str, key id){
     link_message(integer sender_num, integer num, string str, key id){
         checkDialogRequest(sender_num, num, str, id);
         checkDialogRequest(sender_num, num, str, id);
     }
     }
}
}


state StartDialog{
state StartDialog{
     state_entry(){
     state_entry(){
         if(redirectState == "Dialog")                  state Dialog;
         if(redirectState == "Dialog")                  state Dialog;
         else if(redirectState == "MultiDialog")        state MultiDialog;
         else if(redirectState == "MultiDialog")        state MultiDialog;
         else if(redirectState == "NumericAdjustDialog") state NumericAdjustDialog;
         else if(redirectState == "NumericAdjustDialog") state NumericAdjustDialog;
         else state default;
         else state default;
     }
     }
}
}


state Dialog{
state Dialog{
     state_entry(){
     state_entry(){
         responseInt = -1;
         responseInt = -1;
         dialogChannel = createDialog(keyId, message, buttons);
         dialogChannel = createDialog(keyId, message, buttons);
         llSetTimerEvent(timerOut);
         llSetTimerEvent(timerOut);
     }
     }
      
      
     state_exit(){
     state_exit(){
         llSetTimerEvent(0);
         llSetTimerEvent(0);
     }
     }
      
      
     on_rez(integer start_param){
     on_rez(integer start_param){
         state default;
         state default;
     }
     }
      
      
     timer(){
     timer(){
         setResponse(lnkDialogTimeOut, "", keyId);
         setResponse(lnkDialogTimeOut, "", keyId);
         state default;
         state default;
     }
     }
      
      
     link_message(integer sender_num, integer num, string str, key id){
     link_message(integer sender_num, integer num, string str, key id){
         if(num == lnkDialogReshow){
         if(num == lnkDialogReshow){
             dialogChannel = createDialog(keyId, message, buttons);
             dialogChannel = createDialog(keyId, message, buttons);
             llSetTimerEvent(timerOut);
             llSetTimerEvent(timerOut);
         }else if(num == lnkDialogCancel) state default;
         }else if(num == lnkDialogCancel) state default;


         else checkDialogRequest(sender_num, num, str, id);
         else checkDialogRequest(sender_num, num, str, id);
     }
     }


     listen(integer channel, string name, key id, string msg){
     listen(integer channel, string name, key id, string msg){
         if((channel != dialogChannel) || (id != keyId)) return;
         if((channel != dialogChannel) || (id != keyId)) return;
          
          
 
        if(buttonsCount > 0){
        integer index = llListFindList(buttons, [msg]);
            channel = llListFindList(buttons, [msg]);
 
            msg = llList2String(returns, channel);
         setResponse(lnkDialogResponse, llList2String(returns, index), keyId);
         }else msg = llList2String(returns, 0) + msg;
 
        setResponse(lnkDialogResponse, msg, keyId);
         state default;
         state default;
     }
     }
}
}


state MultiDialog {
state MultiDialog {
     state_entry(){
     state_entry(){
         responseInt = -1;
         responseInt = -1;
         startItemNo = 0;
         startItemNo = 0;
         dialogChannel = createMultiDialog(keyId, message, buttons, startItemNo);
         dialogChannel = createMultiDialog(keyId, message, buttons, startItemNo);
         llSetTimerEvent(timerOut);
         llSetTimerEvent(timerOut);
     }
     }
      
      
     state_exit(){
     state_exit(){
         llSetTimerEvent(0);
         llSetTimerEvent(0);
     }
     }
      
      
     on_rez(integer start_param){
     on_rez(integer start_param){
         state default;
         state default;
     }
     }
      
      
     timer(){
     timer(){
         setResponse(lnkDialogTimeOut, "", keyId);
         setResponse(lnkDialogTimeOut, "", keyId);
         state default;
         state default;
     }
     }
      
      
     link_message(integer sender_num, integer num, string str, key id){
     link_message(integer sender_num, integer num, string str, key id){
         if(num == lnkDialogReshow){
         if(num == lnkDialogReshow){
             dialogChannel = createMultiDialog(keyId, message, buttons, startItemNo);
             dialogChannel = createMultiDialog(keyId, message, buttons, startItemNo);
             llSetTimerEvent(timerOut);
             llSetTimerEvent(timerOut);
         }else if(num == lnkDialogCancel) state default;
         }else if(num == lnkDialogCancel) state default;


         else checkDialogRequest(sender_num, num, str, id);
         else checkDialogRequest(sender_num, num, str, id);
     }
     }


     listen(integer channel, string name, key id, string msg){
     listen(integer channel, string name, key id, string msg){
         if((channel != dialogChannel) || (id != keyId)) return;
         if((channel != dialogChannel) || (id != keyId)) return;


         // Dialog control buttons
         // Dialog control buttons
         if(msg == "<< BACK"){
         if(msg == "<< BACK"){
             dialogChannel = createMultiDialog(keyId, message, buttons, startItemNo - 9);
             dialogChannel = createMultiDialog(keyId, message, buttons, startItemNo - 9);
             llSetTimerEvent(timerOut);
             llSetTimerEvent(timerOut);
         }else if(msg == "NEXT >>"){
         }else if(msg == "NEXT >>"){
             dialogChannel = createMultiDialog(keyId, message, buttons, startItemNo + 9);
             dialogChannel = createMultiDialog(keyId, message, buttons, startItemNo + 9);
             llSetTimerEvent(timerOut);
             llSetTimerEvent(timerOut);
         }else if(msg == " "){
         }else if(msg == " "){
             dialogChannel = createMultiDialog(keyId, message, buttons, startItemNo);
             dialogChannel = createMultiDialog(keyId, message, buttons, startItemNo);
             llSetTimerEvent(timerOut);
             llSetTimerEvent(timerOut);
          
          
         // Response buttons
         // Response buttons
         }else{
         }else{
             integer index = llListFindList(buttons, [msg]);
             integer index = llListFindList(buttons, [msg]);
             setResponse(lnkDialogResponse, llList2String(returns, index), keyId);
             setResponse(lnkDialogResponse, llList2String(returns, index), keyId);
             state default;
             state default;
         }
         }
     }
     }
}
}


state NumericAdjustDialog {
state NumericAdjustDialog {
     state_entry(){
     state_entry(){
         responseInt = -1;
         responseInt = -1;


         numericValue = llList2Float(returns, 0);
         numericValue = llList2Float(returns, 0);
         useInteger = llList2Integer(returns, 2);
         useInteger = llList2Integer(returns, 2);
         buttons = generateNumericAdjustButtons(buttons, llList2Integer(returns, 1));
         buttons = generateNumericAdjustButtons(buttons, llList2Integer(returns, 1));
          
          
         string vMessage;
         string vMessage;
         if(useInteger)
         if(useInteger)
             vMessage = replaceString("{VALUE}", (string)((integer)numericValue), message);
             vMessage = replaceString("{VALUE}", (string)((integer)numericValue), message);
         else vMessage = replaceString("{VALUE}", (string)numericValue, message);
         else vMessage = replaceString("{VALUE}", (string)numericValue, message);
          
          
         dialogChannel = createDialog(keyId, vMessage, buttons);
         dialogChannel = createDialog(keyId, vMessage, buttons);
         llSetTimerEvent(timerOut);
         llSetTimerEvent(timerOut);
     }
     }
      
      
     state_exit(){
     state_exit(){
         llSetTimerEvent(0);
         llSetTimerEvent(0);
     }
     }
      
      
     on_rez(integer start_param){
     on_rez(integer start_param){
         state default;
         state default;
     }
     }
      
      
     timer(){
     timer(){
         setResponse(lnkDialogTimeOut, "", keyId);
         setResponse(lnkDialogTimeOut, "", keyId);
         state default;
         state default;
     }
     }
      
      
     link_message(integer sender_num, integer num, string str, key id){
     link_message(integer sender_num, integer num, string str, key id){
         if(num == lnkDialogReshow){
         if(num == lnkDialogReshow){
             dialogChannel = createDialog(keyId, message, buttons);
             dialogChannel = createDialog(keyId, message, buttons);
             llSetTimerEvent(timerOut);
             llSetTimerEvent(timerOut);
         }else if(num == lnkDialogCancel) state default;
         }else if(num == lnkDialogCancel) state default;


         else checkDialogRequest(sender_num, num, str, id);
         else checkDialogRequest(sender_num, num, str, id);
     }
     }


     listen(integer channel, string name, key id, string msg){
     listen(integer channel, string name, key id, string msg){
         if((channel != dialogChannel) || (id != keyId)) return;
         if((channel != dialogChannel) || (id != keyId)) return;
          
          
         // Dialog control button is hit
         // Dialog control button is hit
         if(msg == "OK"){
         if(msg == "OK"){
             setResponse(lnkDialogResponse, (string)numericValue, keyId);
             setResponse(lnkDialogResponse, (string)numericValue, keyId);
             state default;
             state default;
         }else if(msg == "Cancel"){
         }else if(msg == "Cancel"){
             setResponse(lnkDialogCanceled, (string)numericValue, keyId);
             setResponse(lnkDialogCanceled, (string)numericValue, keyId);
             llMessageLinked(requestedNum, lnkDialogCanceled, (string)numericValue, keyId);
             llMessageLinked(requestedNum, lnkDialogCanceled, (string)numericValue, keyId);
             state default;
             state default;
          
          
         // Value adjustment button is hit
         // Value adjustment button is hit
         }else if(msg == "+/-")
         }else if(msg == "+/-")
             numericValue = -numericValue;
             numericValue = -numericValue;
         else if(llSubStringIndex(msg, "+") == 0)
         else if(llSubStringIndex(msg, "+") == 0)
             numericValue += (float)llDeleteSubString(msg, 0, 0);
             numericValue += (float)llDeleteSubString(msg, 0, 0);
         else if(llSubStringIndex(msg, "-") == 0)
         else if(llSubStringIndex(msg, "-") == 0)
             numericValue -= (float)llDeleteSubString(msg, 0, 0);
             numericValue -= (float)llDeleteSubString(msg, 0, 0);
          
          
         // Spawn another dialog if no OK nor Cancel is hit
         // Spawn another dialog if no OK nor Cancel is hit
         string vMessage;
         string vMessage;
         if(useInteger)
         if(useInteger)
             vMessage = replaceString("{VALUE}", (string)((integer)numericValue), message);
             vMessage = replaceString("{VALUE}", (string)((integer)numericValue), message);
         else vMessage = replaceString("{VALUE}", (string)numericValue, message);
         else vMessage = replaceString("{VALUE}", (string)numericValue, message);
         dialogChannel = createDialog(keyId, vMessage, buttons);
         dialogChannel = createDialog(keyId, vMessage, buttons);
         llSetTimerEvent(timerOut);
         llSetTimerEvent(timerOut);
   }
   }
}
</source>


}
{{LSLC|Library|Dialog Control}}
</lsl>

Latest revision as of 09:57, 25 January 2015

// ********** SIMPLE DIALOG MODULE ********** //
// By Nargus Asturias
// Version 1.86
//
// Support only one dialog at a time. DO NOT request multiple dialog at once!
// Use of provided functions are recommented. Instruction here are for hardcore programmers only!
//
// Request: Send MessageLinked to the script. There are 3 dialog modes:
//      lnkDialog               : Normal dialog with message and buttons
//      lnkDialogNumericAdjust  : A dialog with buttons can be used to adjust numeric value
//      lnkDialogTextBox        : A dialog with a text input field
//      lnkDialogNotify         : Just a simple notification dialog. No return value and no buttons.
//
// Send MessageLinked with code lnkDialogReshow to force active dialog to reappear.
// Send MessageLinked with code lnkDialogCancel to force cancel active dialog
//
// If a lnkDialog is requested with more than 12 buttons, multi-pages dialog is used to show the buttons.
//
// [ FOR lnkDialog ]
// MessageLinked Format:
//      String part: List dumped into string, each entry seperated by '||'
//          Field 1:    Dialog message (512 bytes maximum)
//          Field 2:    Time-out data (integer)
//          Field 3-4:  Button#1 and return value pair
//          Field 5-6:  Button#2 and return value pair
//          And go on...
//      Key part: Key of AV who attend this dialog
//
// Response: MessageLinked to the prim that requested dialog (but no where else)
//      num == lnkDialogResponse:   AV click on a button. The buttons value returned as a string
//      num == lnkDialogTimeOut:    Dialog timeout.
//
// [ FOR lnkDialogTextbox ]
// MessageLinked Format:
//      String part: List dumped into string, each entry seperated by '||'
//          Field 1:    Dialog message (512 bytes maximum)
//          Field 2:    Time-out data (integer)
//          Field 3:    Return string prefix. If empty, only user's input will be returned.
//
// [ FOR lnkDialogNumericAdjust ]
// MessageLinked Format:
//      String part: List dumped into string, each entry seperated by '||'
//          Field 1:    Dialog message (512 bytes maximum)
//                          Put {VALUE} where you want the current value to be displayed)
//          Field 2:    Time-out data (integer)
//          Field 3:    Most significant value (ie. 100, for +/-100)
//          Field 4:    String-casted numeric value to be adjusted
//          Field 5:    2nd most significant value (ie. 10, for +/-10)
//          Field 6:    Use '1' to enable "+/-" button, '0' otherwise.
//          Field 7:    3nd significant value (ie. 1, for +/-1)
//          Field 8:    Use '1' for integer, or '0' for float
//          Field 9:    Least significant value (ie. 0.1, for +/-0.1)
//          Field 10:   Reserved. Not used.
//      Key part: Key of AV who attend this dialog
//
// Response: MessageLinked to the prim that requested dialog (but no where else)
//      num == lnkDialogResponse:   OK or Cancel button is clicked. The final value returned as string.
//      num == lnkDialogTimeOut:    Dialog timeout.
//
// ******************************************* //

// Constants
integer lnkDialog = 14001;
integer lnkDialogNumericAdjust = 14005;
integer lnkDialogNotify = 14004;
integer lnkDialogTextBox = 14007;

integer lnkDialogReshow = 14011;
integer lnkDialogCancel = 14012;

integer lnkDialogResponse = 14002;      // A button is hit, or OK is hit for lnkDialogNumericAdjust
integer lnkDialogCanceled = 14006;      // Cancel is hit for lnkDialogNumericAdjust
integer lnkDialogTimeOut = 14003;       // No button is hit, or Ignore is hit

integer lnkMenuClear = 15001;
integer lnkMenuAdd = 15002;
integer lnkMenuShow = 15003;
integer lnkMenuNotFound = 15010;
integer lnkMenuSetSound = 15021;

string seperator = "||";

// Menus variables
list menuNames = [];        // List of names of all menus
list menus = [];            // List of packed menus command, in order of menuNames

integer lastMenuIndex = 0;  // Latest called menu's index

string menuSound;           // Sound to me played when a menu is shown
float menuSoundVol = 1;

// Dialog variables
integer dialogChannel;      // Channel number used to spawn this dialog
string message;             // Message to be shown with the dialog
integer timerOut;           // Dialog time-out
key keyId;                  // Key of user who attending this dialog
integer requestedNum;       // Link-number of the requested prim
list buttons;               // List of dialog buttons
list returns;               // List of results from dialog buttons

float numericValue;
integer useInteger;

// Other variables
integer buttonsCount;
integer startItemNo;
integer listenId;

string redirectState;

integer responseInt = -1;
string responseStr;
key responseKey;


// ********** String Functions **********
string replaceString(string pattern, string replace, string source){
    integer index = llSubStringIndex(source, pattern);
    if(index < 0) return source;
    
    source = llDeleteSubString(source, index, (index + llStringLength(pattern) - 1));
    return llInsertString(source, index, replace);
}

// ********** Dialog Functions **********
// Function: createDialog
// Create dialog with given message and buttons, direct to user with give id
integer createDialog(key id, string message, list buttons){
    integer channel = -((integer)llFrand(8388608))*(255) - (integer)llFrand(8388608) - 11;

    llListenRemove(listenId);
    listenId = llListen(channel, "", keyId, "");
    if(buttonsCount > 0)
        llDialog(keyId, message, buttons, channel);
    else llTextBox(keyId, message, channel);

    return channel;
}

// Function: createMultiDialog
// Create dialog with multiple pages. Each page has Back, Next, and a Close button. Otherwise same functionality as createDialog() function.
integer createMultiDialog(key id, string message, list buttons, integer _startItemNo){
    integer channel = -llRound(llFrand( llFabs(llSin(llGetGMTclock())) * 1000000 )) - 11;

    if(_startItemNo < 0) _startItemNo = 0;
    if(_startItemNo >= buttonsCount - 1) _startItemNo -= 9;
    startItemNo = _startItemNo;
    
    integer vButtonsCount = buttonsCount - 2;
    
    // Generate list of buttons to be shown
    string closeButton = llList2String(buttons, buttonsCount - 1);
    
    integer stopItemNo = startItemNo + 8;
    if(stopItemNo >= buttonsCount - 1) stopItemNo = vButtonsCount;

    list thisButtons = llList2List(buttons, startItemNo, stopItemNo);

    // Generate dialog navigation buttons
    integer i = stopItemNo - startItemNo + 1;
    i = i % 3;
    if(i > 0) while(i < 3){
        thisButtons += [" "];
        ++i;
    }
    
    if(startItemNo > 0)
        thisButtons += ["<< BACK"];
    else thisButtons += [" "];
    
    thisButtons += [closeButton];
    
    if(stopItemNo < vButtonsCount)
        thisButtons += ["NEXT >>"];
    else thisButtons += [" "];
    
    // Append page number to the message
    integer pageNumber = (integer)(stopItemNo / 9) + 1;
    integer pagesCount = llCeil(vButtonsCount / 9.0);
    string vMessage = "PAGE: " + (string)pageNumber + " of " + (string)pagesCount + "\n" +
        message;

    // Display dialog
    llListenRemove(listenId);
    listenId = llListen(channel, "", keyId, "");
    llDialog(keyId, vMessage, thisButtons, channel);

    return channel;
}

// Function: generateNumericAdjustButtons
// Generate numeric adjustment dialog which adjustment values are in given list.
// If useNegative is TRUE, "+/-" button will be available.
list generateNumericAdjustButtons(list adjustValues, integer useNegative){
    list dialogControlButtons;
    list positiveButtons;
    list negativeButtons;
    list additionButtons;

    dialogControlButtons = ["OK", "Cancel"];

    // Config adjustment buttons
    integer count = llGetListLength(adjustValues);
    integer index;
    for(index = 0; (index < count) && (index < 3); index++){
        string sValue = llList2String(adjustValues, index);

        if((float)sValue != 0){
            positiveButtons += ["+" + sValue];
            negativeButtons += ["-" + sValue];
        }
    }

    // Check positive/negative button
    if(useNegative)
        additionButtons = ["+/-"];
    else additionButtons = [];

    // If there is fourth adjustment button
    if(count > 3){
        if(llGetListLength(additionButtons) == 0) additionButtons = [" "];

        string sValue = llList2String(adjustValues, index);
        additionButtons += ["+" + sValue, "-" + sValue];
    }else if(additionButtons != []) additionButtons += [" ", " "];
    
    // Return list dialog buttons
    return additionButtons + negativeButtons + positiveButtons + dialogControlButtons;
}

setResponse(integer int, string str, key id){
    responseInt = int;
    responseStr = str;
    responseKey = id;
}

checkDialogRequest(integer sender_num, integer num, string str, key id){
    // Common dialog requests
    if((num == lnkDialogNotify) || (num == lnkDialogNumericAdjust) || (num == lnkDialog)){
        list data = llParseStringKeepNulls(str, [seperator], []);
    
        message = llList2String(data, 0);
        timerOut = llList2Integer(data, 1);
        keyId = id;
        requestedNum = sender_num;
        buttons = [];
        returns = [];
    
        if(message == "") message = " ";
        if(timerOut > 7200) timerOut = 7200;
    
        // Generate buttons list
        integer i=2;
        integer count = llGetListLength(data);
        if(count > 2){
            buttonsCount = 0;
            for(; i<count;){
                buttons += [llList2String(data, i++)];
                returns += [llList2String(data, i++)];
                ++buttonsCount;
            }
        }else{
            buttons = ["OK"];
            returns = [];
            buttonsCount = 1;
        }
    
        // Determine the type of dialog
        if(num == lnkDialogNotify){
            dialogChannel = -((integer)llFrand(8388608))*(255) - (integer)llFrand(8388608) - 11;
            llDialog(keyId, message, buttons, dialogChannel);
            return;
        }else if(num == lnkDialogNumericAdjust)
            redirectState = "NumericAdjustDialog";
        else if(num == lnkDialog){
            if(buttonsCount > 12)
                redirectState = "MultiDialog";
            else redirectState = "Dialog";
        }
        
        if(TRUE) state StartDialog;
    
    // Text box request
    }else if(num == lnkDialogTextBox){
        list data = llParseStringKeepNulls(str, [seperator], []);

        message = llList2String(data, 0);
        timerOut = llList2Integer(data, 1);
        keyId = id;
        requestedNum = sender_num;
        buttons = [];
        returns = [llList2String(data, 2)];
        buttonsCount = 0;

        if(timerOut > 7200) timerOut = 7200;
    
        redirectState = "Dialog";
        if(TRUE) state StartDialog;
    
    // Otherwise; Check for menu requests
    }else checkMenuRequest(sender_num, num, str, id);
}

// ********** Menu Functions **********
clearMenusList(){
    menuNames = [];
    menus = [];

    lastMenuIndex = 0;
}

addMenu(string name, string message, list buttons, list returns){
    // Reduced menu request time by packing request commands
    string packed_message = message + seperator + "0";
    
    integer i;
    integer count = llGetListLength(buttons);
    for(i=0; i<count; i++) packed_message += seperator + llList2String(buttons, i) + seperator + llList2String(returns, i);

    // Add menu to the menus list
    integer index = llListFindList(menuNames, [name]);
    if(index >= 0)
        menus = llListReplaceList(menus, [packed_message], index, index);
    else{
        menuNames += [name];
        menus += [packed_message];
    }
}

integer showMenu(string name, key id){
    if(llGetListLength(menuNames) <= 0) return FALSE;
   
    integer index;
    if(name != ""){
        index = llListFindList(menuNames, [name]);
        if(index < 0) return FALSE;
    }else index = lastMenuIndex;
    
    lastMenuIndex = index;
    
    // Load menu command and execute
    string packed_message = llList2String(menus, index);

    if(menuSound != "") llPlaySound(menuSound, menuSoundVol);
    llMessageLinked(LINK_THIS, lnkDialog, packed_message, id);
    return TRUE;
}

checkMenuRequest(integer sender_num, integer num, string str, key id){
    // Menu response commands
    if(num == lnkDialogResponse){
        if(llGetSubString(str, 0, 4) == "MENU_"){
            str = llDeleteSubString(str, 0, 4);
            showMenu(str, id);
        }
    }
        
    // Menu management commands
    else if(num == lnkMenuClear)
        clearMenusList();
    else if(num == lnkMenuAdd){
        list data = llParseString2List(str, [seperator], []);

        string message = llList2String(data, 0);
        list buttons = [];
        list returns = [];

        integer i;
        integer count = llGetListLength(data);
        for(i=2; i<count;){
            buttons += [llList2String(data, i++)];
            returns += [llList2String(data, i++)];
        }

        addMenu((string)id, message, buttons, returns);

    }else if(num == lnkMenuShow){
        if(!showMenu(str, id)) llMessageLinked(sender_num, lnkMenuNotFound, str, NULL_KEY);

    }else if(num == lnkMenuSetSound){
        menuSound = str;
        menuSoundVol = (float)((string)id);
    }
}

// ********** States **********
default{
    state_entry(){
        if(responseInt > 0) llMessageLinked(requestedNum, responseInt, responseStr, responseKey);
    }
    
    link_message(integer sender_num, integer num, string str, key id){
        checkDialogRequest(sender_num, num, str, id);
    }
}

state StartDialog{
    state_entry(){
        if(redirectState == "Dialog")                   state Dialog;
        else if(redirectState == "MultiDialog")         state MultiDialog;
        else if(redirectState == "NumericAdjustDialog") state NumericAdjustDialog;
        else state default;
    }
}

state Dialog{
    state_entry(){
        responseInt = -1;
        dialogChannel = createDialog(keyId, message, buttons);
        llSetTimerEvent(timerOut);
    }
    
    state_exit(){
        llSetTimerEvent(0);
    }
    
    on_rez(integer start_param){
        state default;
    }
    
    timer(){
        setResponse(lnkDialogTimeOut, "", keyId);
        state default;
    }
    
    link_message(integer sender_num, integer num, string str, key id){
        if(num == lnkDialogReshow){
            dialogChannel = createDialog(keyId, message, buttons);
            llSetTimerEvent(timerOut);
        }else if(num == lnkDialogCancel) state default;

        else checkDialogRequest(sender_num, num, str, id);
    }

    listen(integer channel, string name, key id, string msg){
        if((channel != dialogChannel) || (id != keyId)) return;
        
        if(buttonsCount > 0){
            channel = llListFindList(buttons, [msg]);
            msg = llList2String(returns, channel);
        }else msg = llList2String(returns, 0) + msg;
        setResponse(lnkDialogResponse, msg, keyId);
        state default;
    }
}

state MultiDialog {
    state_entry(){
        responseInt = -1;
        startItemNo = 0;
        dialogChannel = createMultiDialog(keyId, message, buttons, startItemNo);
        llSetTimerEvent(timerOut);
    }
    
    state_exit(){
        llSetTimerEvent(0);
    }
    
    on_rez(integer start_param){
        state default;
    }
    
    timer(){
        setResponse(lnkDialogTimeOut, "", keyId);
        state default;
    }
    
    link_message(integer sender_num, integer num, string str, key id){
        if(num == lnkDialogReshow){
            dialogChannel = createMultiDialog(keyId, message, buttons, startItemNo);
            llSetTimerEvent(timerOut);
        }else if(num == lnkDialogCancel) state default;

        else checkDialogRequest(sender_num, num, str, id);
    }

    listen(integer channel, string name, key id, string msg){
        if((channel != dialogChannel) || (id != keyId)) return;

        // Dialog control buttons
        if(msg == "<< BACK"){
            dialogChannel = createMultiDialog(keyId, message, buttons, startItemNo - 9);
            llSetTimerEvent(timerOut);
        }else if(msg == "NEXT >>"){
            dialogChannel = createMultiDialog(keyId, message, buttons, startItemNo + 9);
            llSetTimerEvent(timerOut);
        }else if(msg == " "){
            dialogChannel = createMultiDialog(keyId, message, buttons, startItemNo);
            llSetTimerEvent(timerOut);
        
        // Response buttons
        }else{
            integer index = llListFindList(buttons, [msg]);
            setResponse(lnkDialogResponse, llList2String(returns, index), keyId);
            state default;
        }
    }
}

state NumericAdjustDialog {
    state_entry(){
        responseInt = -1;

        numericValue = llList2Float(returns, 0);
        useInteger = llList2Integer(returns, 2);
        buttons = generateNumericAdjustButtons(buttons, llList2Integer(returns, 1));
        
        string vMessage;
        if(useInteger)
            vMessage = replaceString("{VALUE}", (string)((integer)numericValue), message);
        else vMessage = replaceString("{VALUE}", (string)numericValue, message);
        
        dialogChannel = createDialog(keyId, vMessage, buttons);
        llSetTimerEvent(timerOut);
    }
    
    state_exit(){
        llSetTimerEvent(0);
    }
    
    on_rez(integer start_param){
        state default;
    }
    
    timer(){
        setResponse(lnkDialogTimeOut, "", keyId);
        state default;
    }
    
    link_message(integer sender_num, integer num, string str, key id){
        if(num == lnkDialogReshow){
            dialogChannel = createDialog(keyId, message, buttons);
            llSetTimerEvent(timerOut);
        }else if(num == lnkDialogCancel) state default;

        else checkDialogRequest(sender_num, num, str, id);
    }

    listen(integer channel, string name, key id, string msg){
        if((channel != dialogChannel) || (id != keyId)) return;
        
        // Dialog control button is hit
        if(msg == "OK"){
            setResponse(lnkDialogResponse, (string)numericValue, keyId);
            state default;
        }else if(msg == "Cancel"){
            setResponse(lnkDialogCanceled, (string)numericValue, keyId);
            llMessageLinked(requestedNum, lnkDialogCanceled, (string)numericValue, keyId);
            state default;
        
        // Value adjustment button is hit
        }else if(msg == "+/-")
            numericValue = -numericValue;
        else if(llSubStringIndex(msg, "+") == 0)
            numericValue += (float)llDeleteSubString(msg, 0, 0);
        else if(llSubStringIndex(msg, "-") == 0)
            numericValue -= (float)llDeleteSubString(msg, 0, 0);
        
        // Spawn another dialog if no OK nor Cancel is hit
        string vMessage;
        if(useInteger)
            vMessage = replaceString("{VALUE}", (string)((integer)numericValue), message);
        else vMessage = replaceString("{VALUE}", (string)numericValue, message);
        dialogChannel = createDialog(keyId, vMessage, buttons);
        llSetTimerEvent(timerOut);
   }
}