Think of the dialog and the response as independent things.
There's no specific limit to the number of dialogs you can post (well, none I've encountered). When you post a dialog, that dialog is handled by the avatar you posted it to. All it does is it gives them an easy way to send your object one of a set of messages on a given chat channel.
There is no specific need to timeout a dialog. Sometimes I do this, but only when I'm keeping track that a dialog is open for some reason. While this can be a good thing, it's usually better to avoid it and keep things simpler.
So, post dialogs with abandon, don't worry about them. Focus on handling the response messages and what it means.
If the menu is always the same, this simple approach works best.
However, if the contents of the menu depends on the state of the object (the LSL state, or the value of any global variables), then things can get more complicated, because folks can leave a menu up for a while and then when they select, their message might be based on "old news". Therefore, for simplicity, things work best when the menu is always the same (at least, always the same for any given avatar). Of course, you can work around this 9if necessary by carefully making sure that any posts based on old news get handled appropriately -- usually by ignoring them.
Remember the KISS principle, "Keep it simple, stupid!"
- As mentioned above, post the listen before the llDialog. I generally post the listen in the entry handler for each state where I'll be posting dialogs. Remember that a listen doesn't survive a state transition, so no need to cancel them or save the handler, in this case.
- No timeout unless there's an important reason for it.
- Use the same channel for all dialogs.
- don't track the llDialog calls you make. Just call 'em and let what happens happen. You'll either get a message from them or not.
- Use the 'id' field to differentiate between users, not the channel.
- Use a chat channel selected using llFrand() -- do this at rez time, so if there are multiple of the same object, you don't get interference. A simple way is to add a rez handler in every state, and call llResetScript(). Call llFrand in the default state's state_entry() handler, like this:
integer MenuChan;
default
{
state_entry()
{
MenuChan = 1000 + (integer) llFrand(64535.0);
}
...
}
I add 1000 just to keep it out of the range folks often use for hard-coded chat channels (and thus the limit of 64535 rather than 65535.)
HTH and good luck
Jeff