nested menu problems
|
|
Lyndzay Meili
Registered User
Join date: 2 Sep 2007
Posts: 28
|
06-22-2008 14:23
I'm working on a menu that has a couple different layers of options and well, it's just not working.  I got the one from the library that gives you a menu and a sub menu and reverse scripted from there. I can make it go one layer deep but when I make a selection from the sub menu it does not work it just closes the dialog box without a word. each sub menu is suppose to call up a new menu (in one case it goes 3 deep, as you can see) with back buttons, which I know I'm not sure if I'm doing those right, since the same name is on each one "<==BACK" but is suppose to call up a different dialog box depending on which menu you are on. Any help, ideas or kicks in the right direction would be greatly appreciated. Thx Lyndz~ php //menu info integer channel; list MENU_MAIN = [ "Pose", "Toasted"]; // the main menu list MALEPOSE = ["Large_M", "Huge_M","Average_M", "Small_M", "Xtra Small_M", "<== BACK"]; list FEMALEPOSE = [ "Large_F" , "Huge_F" , "Average_F" , "Small_F", "Xtra Small_F","<==BACK"]; list FOOD = [ "Toast", "Waffle","<==BACK"]; list GENDER = [ "Male" , "Female" ,"<==BACK"]; default { state_entry() { { // Create random channel within range [-1000000000,-2000000000] channel= (integer)(llFrand(-1000000000.0)); llListen(channel, "", llGetOwner(), ""  ; // change to CHANNEL } } touch_start(integer x) { if (llDetectedOwner(0) ==llGetOwner()) { { llDialog(llDetectedOwner(0), "Lyndzmatic Toast Launcher Options", MENU_MAIN,channel); } } else if (llDetectedOwner(0) !=llGetOwner()) { return; } } listen(integer chan, string name, key id, string msg) { if (llListFindList(MENU_MAIN, [msg]) != -1) // verify dialog choice { if(msg == "Pose" { llDialog(id, "Avatar Size",GENDER, channel); llSay(0, "pose"  ; { if(msg == "Male"  { llSay (0,"Male"  ; llDialog(id,"Male Poses",MALEPOSE,channel); } else if(msg =="Female"  { llSay (0, "Female"  ; llDialog(id,"Female Poses",FEMALEPOSE,channel); } else if(msg== "<==BACK"  { llSay(0,"Back"  ; llDialog(id, "Lyndzmatic Toast Launcher Options", MENU_MAIN,channel); } } } else if(msg == "Toasted" { llDialog(id, "Choose your toasted treat", FOOD, channel); llSay(0, "Food"  ; { if(msg== "Waffle" { llSay(0, "Waffle"  ; } else if (msg=="Toast"  { llSay(0,"Toast"  ; } else if (msg== "<==Back"  { llSay(0, "Back"  ; llDialog(id, "Lyndzmatic Toast Launcher Options", MENU_MAIN, channel); } } } else llSay(0,name + " picked invalid option'" +llToLower(msg) +"'."  ; } } } /php
_____________________
"Be who you are and say what you feel, because those who matter don't mind, and those that mind, don't matter." —Dr. Seuss
|
|
Haruki Watanabe
llSLCrash(void);
Join date: 28 Mar 2007
Posts: 434
|
06-22-2008 15:00
Your listeners for the dialog check for the top-menu items only - and the backs are nested withing - so your script will never «hear» the back (nor will it hear the consecutive commands). Id recommend using states. After the user picks a main-menu item, you change to another state, which handles the sub-menu. There, you have to listen for <==Back AND for the regular menu-call (if the user hits «ignore», your script won't know that and therefore stay in the sub-menu state). The following is untested!
//menu info integer channel; list MENU_MAIN = [ "Pose", "Toasted"]; // the main menu list MALEPOSE = ["Large_M", "Huge_M","Average_M", "Small_M", "Xtra Small_M", "<== BACK"]; list FEMALEPOSE = [ "Large_F" , "Huge_F" , "Average_F" , "Small_F", "Xtra Small_F","<==BACK"]; list FOOD = [ "Toast", "Waffle","<==BACK"]; list GENDER = [ "Male" , "Female" ,"<==BACK"];
integer listener; // Always set up a handler for a listener
default{ state_entry{ channel = (integer)(llFrand(-1000000000.0)); } touch_start(integer x) { if (llDetectedOwner(0) ==llGetOwner()) { { state main_menu; } } else if (llDetectedOwner(0) !=llGetOwner()) { return; } } }
state main_menu { state_entry() { { listener = llListen(channel, "", llGetOwner(), ""); // change to CHANNEL llDialog(llDetectedOwner(0), "Lyndzmatic Toast Launcher Options", MENU_MAIN,channel); } }
touch_start(integer x) { if (llDetectedOwner(0) ==llGetOwner()) { { channel = (integer)(llFrand(-1000000000.0)); //Create a new channel for every call llDialog(llDetectedOwner(0), "Lyndzmatic Toast Launcher Options", MENU_MAIN,channel); } } else if (llDetectedOwner(0) !=llGetOwner()) { return; } }
listen(integer chan, string name, key id, string msg) { if (llListFindList(MENU_MAIN, [msg]) != -1) // verify dialog choice { llListenRemove(listener);
if(msg == "Pose") { state pose; } else if(msg == "Toasted") { state toasted; } else // You don't need this - the user can only choose between Pose & Toasted { llSay(0,name + " picked invalid option'" +llToLower(msg) +"'."); } } } }
state pose{ state_entry(){ // Show the GENDER-Menu llDialog(id, "Avatar Size",GENDER, channel); llSay(0, "pose"); listener = llListen(channel, "", llGetOwner(), ""); // change to CHANNEL } touch_start(integer x) // We need this, in case the User chooses «ignore» { if (llDetectedOwner(0) ==llGetOwner()) // only react if the owner clicks { state main_menu; // Show the main menu } } listen(integer chan, string name, key id, string msg) { // It is actually better to check for the name and the ID // than for the menu-selection - this could be mimicked by // anyone who knows the appropriate channel (although, this is unlikely) // And - you know what's supposed to come back from the menu, // so inappropriate selections are unlikely if (chan == channel && id == llGetOwner()) { llListenRemove(listener); if(msg == "Male") // Here, you could switch to the next state { state malepose; } else if(msg =="Female") { state femalepose; } else if(msg== "<==BACK") { llSay(0,"Back"); state main_menu; } } } }
state toasted{ state_entry(){ llDialog(id, "Choose your toasted treat", FOOD, channel); llSay(0, "Food"); listener = llListen(channel, "", llGetOwner(), ""); // change to CHANNEL } touch_start(integer x) // We need this, in case the User chooses «ignore» { if (llDetectedOwner(0) ==llGetOwner()) // only react if the owner clicks { state main_menu; // Show the main menu } } listen(integer chan, string name, key id, string msg) { if (chan == channel && id == llGetOwner()) { if(msg== "Waffle") { llSay(0, "Waffle"); } else if (msg=="Toast") { llSay(0,"Toast"); } else if(msg== "<==BACK") { llSay(0,"Back"); state main_menu; } } } }
state malepose{ state_entry(){ llSay (0,"Male"); llDialog(id,"Male Poses",MALEPOSE,channel); } touch_start(integer x) // We need this, in case the User chooses «ignore» { if (llDetectedOwner(0) ==llGetOwner()) // only react if the owner clicks { state main_menu; // Show the main menu } } listen(integer chan, string name, key id, string msg) { if (chan == channel && id == llGetOwner()) { if(msg== "<==BACK") { llSay(0,"Back"); state pose; // We bubble up to the last menu } // And so on... } } }
state femalepose{ state_entry(){ llSay (0, "Female"); llDialog(id,"Female Poses",FEMALEPOSE,channel); } touch_start(integer x) // We need this, in case the User chooses «ignore» { if (llDetectedOwner(0) ==llGetOwner()) // only react if the owner clicks { state main_menu; // Show the main menu } } listen(integer chan, string name, key id, string msg) { if (chan == channel && id == llGetOwner()) { if(msg== "<==BACK") { llSay(0,"Back"); state pose; // We bubble up to the last menu } // And so on... } } }
[Edit] Now that I look at this, this could become HUGE! I guess, there are more elegant ways to do that  I wrote a function that provides a dialog. You just pass the function(script) the menu-items and it handles the whole dialog-fuss, passing the chosen value back to the calling script... Maybe that's an option for ya?
|
|
Dora Gustafson
Registered User
Join date: 13 Mar 2007
Posts: 779
|
06-22-2008 15:01
You have just one listen event handler and all your responses go through that one in a linear fashion! You can not nest the action in the way you do. As I read it only "Pose" and *Toasted" will ever be recognized. To get around the fact that you have identical keys in the dialogs you can use different channel numbers. Add one to the channel number for each new level for instance, make llListen function calls for all your channels and test for channel in your listen event handler. Happy scripting
_____________________
From Studio Dora
|
|
Haruki Watanabe
llSLCrash(void);
Join date: 28 Mar 2007
Posts: 434
|
06-22-2008 15:25
Here's my Dialog Script (maybe, someone else finds this useful as well...  Put this in a separate script:
// Dialog-Maker // By Haruki Watanabe
// This script is public domain - use it, share it, do whatever you wanna do... :) // If you've got any improvements, please post them in the forum
// ******************************************************* // Constants // *******************************************************
integer REQUESTING_SCRIPT; // Number of the requesting script integer REQUESTING_LINK; // Link-Number where the request comes from
integer DLG_CHANNEL; // Channel on which the result of the dialog will be passed back integer LISTENER_DLG; // The Listener-ID integer LIST_OFFSET; // Offset of the List
integer TOTAL_ENTRIES; // Total Entries in the whole list to be presented
integer SHOW_ENTRIES; // BOOLEAN Whether or not to show the number of entries in the dialog-title (e.g. 'showing 1-9/25') integer BACKLINK; // BOOLEAN Whether or not give the user the option to go back (useful if you're in a Sub-Menu)
string DLG_TITLE; // Title of the dialog string CMD; // The Command that's passed to this function-script
key AGENT_REQUEST; // Key of the requesting Agent
list DLG_CONTENT; // The contents that the user can choose from list DLG_CONTENT_LONG; list MENU;
float MAX_LISTEN_TIME = 240.0; // Maximum seconds the Listener keeps listening for an answer
// ******************************************************* // Functions // *******************************************************
show_dlg(list PASS_MENU) // Presents the Dialog-Menu to the requesting user { llListenRemove(LISTENER_DLG); // Delete any old listener MENU = []; if(llGetListLength(PASS_MENU) == 0 || TOTAL_ENTRIES == 0) // If there are no entries in the list, send an error-Message to the User { llInstantMessage(AGENT_REQUEST, "There are no entries in the list..."); } else { string TITLE = DLG_TITLE; integer start; // number where the list starts integer end; // number where the list ends -> these two shouldn't be confused with the list offset! start = LIST_OFFSET + 1; end = start + 9; if(end > TOTAL_ENTRIES) end = TOTAL_ENTRIES; if(SHOW_ENTRIES == TRUE) { TITLE = TITLE + "\n showing entries " + (string)start + " - " + (string)end + " of " + (string)TOTAL_ENTRIES; } DLG_CONTENT_LONG = llList2List(PASS_MENU, LIST_OFFSET, LIST_OFFSET + 9); // Get the portion of the list that fits into a dialog // Prevent the Buttons from being too long integer ml = llGetListLength(DLG_CONTENT_LONG); integer i; // Check for menu-entries that are longer than 24 characters
for(i = 0;i<ml;i++){ MENU += [llGetSubString(llList2String(DLG_CONTENT_LONG, i), 0, 23)]; } // Give the user a 'back'-option if(BACKLINK == TRUE || LIST_OFFSET > 0) MENU += ["Back..."]; // if the list is too big to fit into one Menu, give the user a 'next'-option if(end < TOTAL_ENTRIES) MENU += ["Next..."]; // Show the dialog and initialize a listener LISTENER_DLG = llListen(DLG_CHANNEL, "", AGENT_REQUEST, ""); llDialog(AGENT_REQUEST, TITLE, MENU, DLG_CHANNEL); llSetTimerEvent(MAX_LISTEN_TIME); // Initialize a timer, in case the user clicks on 'ignore' } }
// Create an arbitrary dialog channel
integer randInt(integer n) { return (integer)llFrand(n + 1); }
integer randIntBetween(integer min, integer max) { return min + randInt(max - min); }
integer make_dlg_channel() { return randIntBetween(-65356, 0); }
// ******************************************************* // Main-Section // *******************************************************
default { link_message(integer sender, integer num, string str, key id) { AGENT_REQUEST = id; REQUESTING_LINK = sender; REQUESTING_SCRIPT = num; // split up the passed string into a list list temp = llParseString2List(str, ["|"], []); CMD = llList2String(temp, 0); if(CMD == "SHOW_DIALOG") { DLG_CONTENT = llCSV2List(llList2String(temp, 1)); DLG_TITLE = llList2String(temp, 2); SHOW_ENTRIES = llList2Integer(temp, 3); BACKLINK = llList2Integer(temp, 4); LIST_OFFSET = 0; // Set the List offset to the beginning of the list TOTAL_ENTRIES = llGetListLength(DLG_CONTENT); // Determine the total length of the list DLG_CHANNEL = make_dlg_channel(); // Create an arbitrary channel to listen for the dialog show_dlg(DLG_CONTENT); } } listen(integer channel, string name, key id, string message) { if(channel == DLG_CHANNEL && id == AGENT_REQUEST) // The User clicked on an option { if(message == "Back...") // The selected option was 'back' { if(LIST_OFFSET > 0) { // if we're not at the beginning of the options-list, // set the Offset back and call the dialog again LIST_OFFSET -= 10; show_dlg(DLG_CONTENT); } else { // We're at the beginning of the options list - send the back-msg back to the calling script llMessageLinked(REQUESTING_LINK, REQUESTING_SCRIPT, "SND_DIALOG|back", AGENT_REQUEST); } } else if(message == "Next...") { // The User wants to see the next portion of the options-list // increment the list-offset and show the dialog again LIST_OFFSET += 10; show_dlg(DLG_CONTENT); } else { // The user has made her/his choice - send the answer back to the calling script // Finde the choice id in the MENU-List integer i = llListFindList(MENU, [message]); string sendback = llList2String(DLG_CONTENT_LONG, i); llMessageLinked(REQUESTING_LINK, REQUESTING_SCRIPT, "SND_DIALOG|" + sendback, AGENT_REQUEST); } } } timer() { llListenRemove(LISTENER_DLG); } on_rez(integer bla) { llResetScript(); } }
************** Then, from the calling script, you do this: list lMainMenu = ["Option1", "Option2", "Option3"]; key gAgentRequest; // This is the key of the Agent who clicked the Object integer gThisScript = 100; // Use a different number for every script that calls the dialog
integer gLinkNum; // if we are in a linkset, this reflects the Linknumber - the dialog sends the message back to this link
default{ state_entry(){ gLinkNum = llGetLinkNumber(); } touch_start(integer detected_num){ // Show our Dialog gAgentRequest = llDetectedKey(0); string MenuTitle = "My Menu"; // Add the title your menu should show here integer ShowEntries = 1; // if this is set to 1, the dialog shows a 'Entries x/x' in the title (in case there are more than 12 entries) integer ShowBacklink = 1; // if this is set to 1, the dialog show as 'Back'-Option
llMessageLinked(gLinkNum, gThisScript, "SHOW_DIALOG|" + llList2CSV(lMainMenu) + "|" + title + "|" + (string)ShowEntries + "|" + (string)ShowBacklink, gAgentRequest); } // Now, all you have to do is wait for a Linked Message link_message(integer sender, integer num, string str, key id){
if(num == gThisScript && id == gAgentRequest){ list tmp = llParseStringKeepNulls(talkback, ["|"], []);
string gCmd = llStringTrim(llList2String(tmp, 0), STRING_TRIM); // if the link-message comes from the dialog, it will always be 'SND_DIALOG' string gValue = llStringTrim(llList2String(tmp, 1), STRING_TRIM); // is either the menu-selection or 'back', if the back-button was clicked if(gCmd == "SND_DIALOG"){ if(gValue == "back"){ // Do whatever you wanna do } if(gValue == "Option1"){ llInstantMessage(gAgentRequest, "Option1 was selected"); } } } } }
|
|
Johan Laurasia
Fully Rezzed
Join date: 31 Oct 2006
Posts: 1,394
|
06-22-2008 15:42
Ok, for starters, you have a space between the == and the word BACK in the male pose menu, which is going to cause trouble when comparing in the listen event... aside from that, all your logic is entirely off. You have an ungodly mess of if else's in there that are entirely unnecessary, not to mention, I think you're missing the point on how dialog menus work. The listen event gets triggered when a menu choice is made, and from there the msg variable isn't going to change until after the listen event is over, and re-triggered by another menu press. The way your logic is, all this crazy if else stuff has to come true, and then the event would have to display a menu, pause, then continue the event onward from there. It doesnt work that way. You don't need to code sub menu choices in sub if else statements, just put one if statement after another and bracket what you want done if that particular case is true, as such... //menu info integer channel; list MENU_MAIN = [ "Pose", "Toasted"]; // the main menu list MALEPOSE = ["Large_M", "Huge_M","Average_M", "Small_M", "Xtra Small_M", "<==BACK"]; list FEMALEPOSE = [ "Large_F" , "Huge_F" , "Average_F" , "Small_F", "Xtra Small_F","<==BACK"]; list FOOD = [ "Toast", "Waffle","<==BACK"]; list GENDER = [ "Male" , "Female" ,"<==BACK"];
default { state_entry() { llSay (0,"Ready."); // Create random channel within range [-1000000000,-2000000000] channel= (integer)(llFrand(-1000000000.0)); llListen(channel, "", llGetOwner(), ""); // change to CHANNEL } touch_start(integer x) { if (llDetectedOwner(0) ==llGetOwner()) { llDialog(llDetectedOwner(0), "Lyndzmatic Toast Launcher Options", MENU_MAIN,channel); } } listen(integer chan, string name, key id, string msg) { if (msg == "Pose") { llSay (0,"Pose"); llDialog(id, "Avatar Size",GENDER, channel); } if (msg == "Toasted") { llSay (0,"Food"); llDialog(id, "Choose your toasted treat", FOOD, channel); } if (msg == "Waffle") { llSay (0,"Waffle"); } if (msg == "Toast") { llSay (0,"Toast"); } if (msg == "Male") { llSay (0,"Male"); llDialog(id,"Male Poses",MALEPOSE,channel); } if (msg =="Female") { llSay (0, "Female"); llDialog(id,"Female Poses",FEMALEPOSE,channel); } if (msg == "<==BACK") { llSay (0, "Back"); llDialog(id, "Lyndzmatic Toast Launcher Options", MENU_MAIN, channel); } } }
Also, a few side things, one, you need to back off a bit on bracketing and indenting, you're going a little overboard... From: Lyndzay Meili default { state_entry() { { // <--- why are you open bracketing again? // Create random channel within range [-1000000000,-2000000000] channel= (integer)(llFrand(-1000000000.0)); llListen(channel, "", llGetOwner(), ""); // change to CHANNEL } // <--- line up brackets vertically... } <--// again, line up brackets vertically touch_start(integer x) { if (llDetectedOwner(0) ==llGetOwner()) { // <--- this bracket should line up under the if statment! { // <--again double bracketing for no reason! llDialog(llDetectedOwner(0), "Lyndzmatic Toast Launcher Options", MENU_MAIN,channel); } } else if (llDetectedOwner(0) !=llGetOwner()) // <-- ALL THIS IS NOT NEEDED, OVERKILL { return; } }
What you have above should be as such: default { state_entry() { // Create random channel within range [-1000000000,-2000000000] channel= (integer)(llFrand(-1000000000.0)); llListen(channel, "", llGetOwner(), ""); // change to CHANNEL }
touch_start(integer x) { if (llDetectedOwner(0) ==llGetOwner()) { llDialog(llDetectedOwner(0), "Lyndzmatic Toast Launcher Options", MENU_MAIN,channel); } }
The if (llDetectedOwner(0) != llGetOwner()) is entirely unnecessary, if the detected owner is not the owner, then that case will not execute, and the event will end as there's no more code. You don't need to script for what's not there with event driven programming! One last thing, if you plan to give this to someone else, I suggest you put an llResetScript(); in an on_rez() event so the owner (specidfied by llGetOwner() will change if object ownership changes... like so. on_rez (integer foo) { llResetScript(); }
http://www.secondscripter.com
_____________________
My tutes http://www.youtube.com/johanlaurasia
|
|
Lyndzay Meili
Registered User
Join date: 2 Sep 2007
Posts: 28
|
06-22-2008 19:47
Thanks for all the help, and examples I'll give them all a try  I 'll try the Dialog Maker Haruki since this is part of a larger thing. Appreciate it greatly  Johan.. I thought that by using the "if else" the script would stop checking once it found the true statement while it would have to check all of them if the "if" statement was used, and since that was how it was done in the sample script I was working from I did it that way. If that info is wrong then I've learned one more thing with this post. Once again thanks for your help Lyndz~
_____________________
"Be who you are and say what you feel, because those who matter don't mind, and those that mind, don't matter." —Dr. Seuss
|
|
Johan Laurasia
Fully Rezzed
Join date: 31 Oct 2006
Posts: 1,394
|
06-22-2008 21:58
From: Lyndzay Meili Thanks for all the help, and examples I'll give them all a try  I 'll try the Dialog Maker Haruki since this is part of a larger thing. Appreciate it greatly  Johan.. I thought that by using the "if else" the script would stop checking once it found the true statement while it would have to check all of them if the "if" statement was used, and since that was how it was done in the sample script I was working from I did it that way. If that info is wrong then I've learned one more thing with this post. Once again thanks for your help Lyndz~ Sure, no problem. I'm not sure exactly what was going on in the original script you were working off of, but, if you look at my script, you'll see they're not really required. If you were to have multiple layers of menus, and wanted to back up one level, you'd have to do it a bit different, perhaps a variable that points to how deep you're in, and and if statement to see where you are, and the appropriate dialog menu displayed, but for the most part, just remember that each time the dialog box button is pushed, the listen event gets triggered, and must complete before another listen is processed. http://www.secondscripter.com/
_____________________
My tutes http://www.youtube.com/johanlaurasia
|