Welcome to the Second Life Forums Archive

These forums are CLOSED. Please visit the new forums HERE

Intermediate scripter needs help. Object description modification.

yevka Laws
Commander Eva "Yevka" Law
Join date: 19 Jul 2006
Posts: 32
10-04-2009 00:59
My menus come up in the script below, but they fail to execute code that is below that is supposed to store some information in the prim description.

I know its primitive.

I started out with two scripts, but I wanted to consolidate to one so the object in question would only display menus when appropriate.

Anyway here it is.

Please feel free to point out what I did wrong or even take the code and test it in an object.

//
// Sound Coonsole
//
// 10-12-2008 added new wireless function commented out all statements.


integer count = 0;

integer run = FALSE;
//This code is to wirelessly enable the jump button.
integer mchan = 7 ; // Input channel
integer fchan = 9 ; // Input channel
integer bchan = 0 ; // "Secret" channel/frequency message is sent on.
// key toucherkey;
// New Wirless function.
encryptsend(integer chan, string msg) {
msg = llStringToBase64(msg); // encode to Base64
msg = llXorBase64StringsCorrect(msg, "EvaKelly";); // encrypt
llRegionSay(chan, msg); // send message
}
integer channel = 9393;
integer mchannel = -9393;
string name = "Soound console";
string soundtoplay = "Soound console";



// subroutine
playsound()
{
string what_to_play = "02" + soundtoplay;
llTriggerSound(soundtoplay, 1);
llRegionSay(-10770, what_to_play);
llRegionSay(-10771, what_to_play);
llRegionSay(-10772, what_to_play);
llStopSound();
// state default;
}

string bcode = "05";
list menuchoices = ["military","civilian","allfleet","mercury","wayfarer","celtic","fields","colonial1","rebel","marines","PTP","Control"];
string msg = "Choose a Function. \n [[Select communications function:";
string attn_sound = "a0a13972-9cde-460f-f596-007c1f6e60df";
integer chan = 0;


list menu2choices = ["whipser","normal","local","infleet","shutdown","back"];
string msg2 = "Choose a Control Function. Function works by ship. \n [[Select remote function:";

list menu3choices = ["Sickbay","Ready Room","CAG Office","brig","Port Pod","STB Pod","Mess","CIC","CO Quarters","XO Quarters","back"];
string msg3 = "Choose a station. \n [[Pick an alternative point to point station:";


list menu4choices = ["Condition 1","Condition 2","Condition 3","FTL Prep","Jump","Radiological","back"];
string msg4 = "Choose a condition. \n [[Set Fleet Condition:]]";




default
{
state_entry()
{
llSay(0, "PA deactivated.";);
llSetText("PA Off",<1,1,0>,1);
}

touch_start(integer total_number)
{

key toucherkey = llDetectedKey(0);
llListen(mchannel,"",toucherkey,"";);
llSetTimerEvent(5);
llDialog(toucherkey,msg,menuchoices,mchannel);
if (llSameGroup(llDetectedKey(0)))
{
state on;
}
}
listen(integer mchannel, string name, key id, string message)
{
if(message == "Control";)
{
llWhisper(0,"Eva was here...Menu control station.";);
// llDialog(ToucherID, msg,colourchoices, channel_dialog);

key toucherkey2 = llDetectedKey(0);
llDialog(toucherkey2,msg3,menu3choices,mchannel);
if(message == "Back";)
{
llWhisper(0,"Eva was here...";);
}
}
else if(message == "military";)
{
bcode="06";
}
else if(message == "civilian";)
{
bcode="07";

}
else if(message == "allfleet";)
{
bcode="05";

}
else if(message == "mercury";)
{
bcode="21";

}
else if(message == "wayfarer";)
{
bcode="24";

}
else if(message == "celtic";)
{
bcode="23";

}
else if(message == "fields";)
{
bcode="22";

}
else if(message == "colonial1";)
{
bcode="22";

}
else if(message == "rebel";)
{
bcode="25";

}
else if(message == "marines";)
{
bcode="26";

}

string msg_string;
msg_string = message+"code:"+bcode;
llWhisper(0,msg_string);
llSetObjectDesc(bcode);


}
} // End of default ...


state on
{
state_entry()
{
llSetText("PA On",<0,1,0>,1);
llRegionSay(-10772, "02"+attn_sound);
llSay(0, "PA activated - Speak.";);
llListen(chan,"","","";);
llTriggerSound(attn_sound, 1);
}

listen(integer channel,string name,key id,string message)
{
string transmission = "XX";
llSay(0,transmission);
transmission = bcode+message;
llSay(0,transmission);
llRegionSay(-10772,transmission);
}

touch_start(integer total_number)
{
if (llSameGroup(llDetectedKey(0)))
{
state default;
}
}
}
Jeredin Denimore
Romani Ite Domum
Join date: 5 Jul 2008
Posts: 95
10-04-2009 01:22
If you are a member of the same group as the prim this script is in, you will be in state on when you click the menu generated in state default so the llDialog call will create a listener and then the move to state on will promptly erase it.

Basically in state default's touch_start you are creating a dialog box with a listener registered for it on channel mchannel. Then you are checking for same group, if yes you are moving to state on. This state change releases all listeners. This happens faster than your click on the dialog so the dialog is what we'd call an orphan dialog, with no listener left to hear the click.

Also, in state default's listen event you have this construct:

CODE

if(message == "Control")
{
llWhisper(0,"Eva was here...Menu control station.");
// llDialog(ToucherID, msg,colourchoices, channel_dialog);

key toucherkey2 = llDetectedKey(0);
llDialog(toucherkey2,msg3,menu3choices,mchannel);
if(message == "Back")
{
llWhisper(0,"Eva was here...");
}
}



When you create a dialog box, the answer will be another firing of the listen event, not a return value inside the same listen event. So your if (message == "Back";) will of course never get evaluated as true since you're inside a code block that has already determined that message = "Control".
Indeterminate Schism
Registered User
Join date: 24 May 2008
Posts: 236
10-04-2009 13:50
And here's another little 'gotcha' that isn't as widely known as it should be - don't change state from a *_start event-handler, like touch_start(). The event will happily change state but will then be looking for a corresponding _end() event (eg; touch_end()). This makes the queue get all confused and is the reason people sometimes have to touch twice to make things happen. It's easy to get caught out because if you don't change state you aren't required to have an _end event at all. Such is Second Life.

In most cases, including yours from my quick look at the code, all you need to do is change the handler definition to touch_end and you'll be ok.
yevka Laws
Commander Eva "Yevka" Law
Join date: 19 Jul 2006
Posts: 32
Wow Thanks
10-04-2009 15:59
Don't totally understand the replies but I will take a look and see.

Someone else told me it was spaghetti. Not very helpful even if true.

:-)
Innula Zenovka
Registered User
Join date: 20 Jun 2007
Posts: 1,825
10-05-2009 03:59
I can't understand what's going on in this code, I'm afraid. You have all these different menu lists, for example, and you say you are trying to combine two scripts because you want the different menus, but you only ever show the user the first menu, as far as I can see.

Anyway, I had a go at tidying things up, and here's something to start on, which does change the prim's description based on your choice and generates a message based on that plus what you then say in chat.
CODE
string caption ="please choose one";
string my_description;
string bcode;
string message_string ="XX";
list menu_choices =[
"military","06",
"civilian","07",
"allfleet","05",
"mercury","21",
"wayfarer","24",
"celtic","23",
"fields","22",
"colonial1","22",
"rebel","25",
"marines","26",
"PTP","99",//i had to put this in
"Control","100"//and this
];

list menu_buttons;


integer coms_channel = -107721;
integer dialog_channel;
integer open_channel=0;
integer handle;

key av;
float timeout = 20.0;

default {
state_entry() {
menu_buttons = llList2ListStrided(menu_choices,0,-1,2);//pull out the first element of the pairs in the menu_choices list and make a list of buttons
dialog_channel = (integer)("0x"+llGetSubString((string)llGetKey(),-8,-1));//calculate channel from my uuid -- likely to be unique on the sim
// handle = llListen(dialog_channel, "",NULL_KEY,"");//create a listener // wrong place
// llListenControl(handle, FALSE);//and turn it off until i need it


}

touch_start(integer num){
if (llDetectedGroup(0)==TRUE){ // if the toucher is in the same group
handle = llListen(dialog_channel, "",NULL_KEY,"");//create a listener
llListenControl(handle,TRUE);//start listening
llSetTimerEvent(timeout);//and start the timer in case i don't hear anything
av = llDetectedKey(0); // grab the toucher's key
llDialog(av,caption,menu_buttons,dialog_channel); //and present a dialog
}
}

listen(integer channel, string name, key id, string message){
llListenControl(handle, FALSE); // turn off the listener
llSetTimerEvent(0.0); //and the timer
if(channel == dialog_channel){ // someone's used the menu

if(~llListFindList(menu_buttons,[message])){//check I understand it
//find the choice in my main list and set bcode to the appropriate value
bcode = llList2String(menu_choices,llListFindList(menu_choices,[message]+1));
my_description = bcode;// give this a value
handle = llListen(open_channel,"",av,""); // start listening to messages in chat from the user
llSetTimerEvent(timeout);
llInstantMessage(av,"Please speak your message in chat");
}
}
else if (channel == open_channel){ // the user said something in chat
message_string += bcode+message;
llSetObjectDesc(my_description); // set the object description to bcode, though i don't understand why we're doing this
llRegionSay(coms_channel,message_string);
llInstantMessage(av,"I've just sent the message\n"+message_string);
}
}

timer(){
llSetTimerEvent(0.0);
llListenControl(handle,FALSE);
llInstantMessage(av,"sorry, but you've taken too long to do anything. Please start over");
}






}



Hope this helps

ETA .. sorry.. just realised I should state listening on the dialog channel when the prim is touched, not state entry ... code corrected to reflect this
Lee Ponzu
What Would Steve Do?
Join date: 28 Jun 2006
Posts: 1,770
10-05-2009 08:17
From: yevka Laws
Don't totally understand the replies but I will take a look and see.

Someone else told me it was spaghetti. Not very helpful even if true.

:-)

It *is* helpful. You now know that you need to learn to write code that contains less spaghetti. That is, it is better structured and easier to follow.

Takes time, but you can learn by looking at other people's code.
_____________________
So many monkeys, so little Shakespeare.
Hewee Zetkin
Registered User
Join date: 20 Jul 2006
Posts: 2,702
10-05-2009 10:12
I wouldn't say, "spaghetti", exactly, but I WOULD say you have a lot of dead code (variables and such that are never actually used, and serve to obfuscate the actual functioning program logic) and a lot of unintuitive abbreviations in your global variables names (e.g. what is 'bchan' supposed to mean again? It's SOME kind of channel I take it, but I'm not going to be reminded of what it is for by that name).

Save an old copy of the script just in case you need some of that, and then simply DELETE stuff that isn't used. Strip it out. Give yourself (and us) just the stuff that's needed to get the job done. That'll make this a lot easier. Also try to rename your variables (especially global ones) more descriptively, according to their specific function. For example, if you happen to actually use both 'menuchoices' and 'menu2choices', use something other than a number so we know what each of them is for. For example, maybe 'mainMenuChoices' and 'controlSubmenuChoices' or something.

I agree about the listen. In order for a user to get to the real functionality, it appears they'd have to touch it in the default state, then without using the menu touch it again in the 'on' state to take it back to the default state, then make their choice from the menu. Not a very intuitive user interface.
yevka Laws
Commander Eva "Yevka" Law
Join date: 19 Jul 2006
Posts: 32
10-07-2009 10:24
I am going back to the original code and attempting to make the small change recommended to make it functional.

Clean up is something I do at project completion.

Right now I'm not sure what can be cut.
Innula Zenovka
Registered User
Join date: 20 Jun 2007
Posts: 1,825
corected a typo in the comments
10-07-2009 13:09
From: yevka Laws
I am going back to the original code and attempting to make the small change recommended to make it functional.

Clean up is something I do at project completion.

Right now I'm not sure what can be cut.

If you don't mind my saying so, I think you're going about it the wrong way round. Taking other different scripts, trying to glue them together so they work, and then, when you've got them working, trying to figure out what you can cut and still have them working, seems a really laborious process and making life far more difficult for yourself than you need to.

I've always found it so much more difficult to edit and alter other people's scripts than to start from scratch. The way I would approach it is by asking myself what I want to happen when people use the script -- and I suspect that's something on the lines of building up a message string based on the choices the avatar makes from a series of different menus you give him or her, and then you invite the avatar to say something, and the script then relays the message string plus what the avatar's just said.

At some point you apparently want to set the prim's description field, too, but why, or what you want to do with it, I can't tell from what you've posted.

The basic mechanism would be something like this -- add the user's choice to a string message_to_send and then present another menu. You always know where you are, because you know which menu the message you've just received came from.
CODE
list menu_1=["Red","Blue","Green"];
list menu_2=["Cats","Dogs","Monkeys"];
list menu_3=["Tea", "Coffee", "Milk"];

string message_to_send;
integer dialog_channel;
integer public_channel= 0;
integer comms_channel = -123557;
integer handle;
key av;
build_message_from_buttons(string message){
message_to_send+=message+" ";//start building the message to send
handle = llListen(dialog_channel,"",av,"");
llListenControl(handle,TRUE);
llSetTimerEvent(20.0);
}

default {
state_entry() {
dialog_channel = (integer)("0x"+llGetSubString((string)llGetKey(),-8,-1));
}

touch_start(integer num){
if(llDetectedGroup(0)==TRUE){
message_to_send="";//clear the message
handle = llListen(dialog_channel,"",llDetectedKey(0),"");
llListenControl(handle,TRUE);
llSetTimerEvent(20.0);
llDialog(llDetectedKey(0),"menu 1 -- please choose something", menu_1,dialog_channel);
}
}

listen(integer channel,string name, key id, string message){
av=id;
llListenControl(handle, FALSE);
llSetTimerEvent(0.0);
if(channel==dialog_channel){
if(~llListFindList(menu_1,(list)message)){//if the message is from the first menu
build_message_from_buttons(message);
llDialog(id,"menu 2 -- please choose something", menu_2,dialog_channel);
}
else if(~llListFindList(menu_2,(list)message)){//if the message is from the second menu
build_message_from_buttons(message);
llDialog(id,"menu 3 -- please choose something", menu_3,dialog_channel);
}
else if(~llListFindList(menu_3,(list)message)){//if the message is from the third menu
message_to_send+=message+" ";
handle=llListen(public_channel,"", av,"");
llListenControl(handle,TRUE);
llSetTimerEvent(20);
llInstantMessage(av,"please say your message in chat");
}
}
else if(channel==public_channel){
message_to_send=name+" chose "+message_to_send+"and said "+message;
llRegionSay(comms_channel, message_to_send);
llInstantMessage(av,"I've just sent this message: "+message_to_send);
llListenRemove(handle);
}
}

timer(){
llSetTimerEvent(0.0);
llListenRemove(handle);
}




}
Domchi Underwood
Registered User
Join date: 4 Aug 2007
Posts: 44
*_start() state change gotcha
10-09-2009 11:51
From: Indeterminate Schism
And here's another little 'gotcha' that isn't as widely known as it should be - don't change state from a *_start event-handler, like touch_start(). The event will happily change state but will then be looking for a corresponding _end() event (eg; touch_end()). This makes the queue get all confused and is the reason people sometimes have to touch twice to make things happen. It's easy to get caught out because if you don't change state you aren't required to have an _end event at all. Such is Second Life.

In most cases, including yours from my quick look at the code, all you need to do is change the handler definition to touch_end and you'll be ok.


Indeterminate, please do tell more. What do you mean "looking for corresponding _end() event", where, in old or in new state? What happens if you do have that event?
Innula Zenovka
Registered User
Join date: 20 Jun 2007
Posts: 1,825
10-09-2009 14:23
From: Domchi Underwood
Indeterminate, please do tell more. What do you mean "looking for corresponding _end() event", where, in old or in new state? What happens if you do have that event?
/me knows this 'cos it bit me the other day. It's described in https://jira.secondlife.com/browse/SVC-3017. What you need to do, rather than change states inside the touch_start event, is do the touch_start stuff there, and change states in a corresponding touch_end. Something like
CODE
default
{
state_entry(){
llOwnerSay("in state default");

}
touch_start(integer s){
llSetColor(< 1.0,0.0,0.0 >,ALL_SIDES);
}
touch_end(integer n){
state next;
}

}
state next
{
state_entry(){
llOwnerSay("in state next");
}
touch_start(integer s){
llSetColor(< 0.0,1.0,0.0 >,ALL_SIDES);
}
touch_end(integer n){
state default;
}
}

Domchi Underwood
Registered User
Join date: 4 Aug 2007
Posts: 44
10-09-2009 23:35
From: Innula Zenovka
/me knows this 'cos it bit me the other day. It's described in https://jira.secondlife.com/browse/SVC-3017.


Holly guacamole! I knew the touches were broken for ages, I just didn't know to reproduce it. Thanks for info, off to vote in JIRA.
yevka Laws
Commander Eva "Yevka" Law
Join date: 19 Jul 2006
Posts: 32
10-22-2009 21:58
I'm actually finally working on this.

Trying to debug the thing.

Seems to default to the last choice in the menu, no matter what button I press.

Anyone sees anything obvious let me know.

Thinking very hard.

BTW, I use the object description to store some simple data the system needs to remember. That is why I change it. I can't figure out how to effectively use lists as a mini database.

From: Innula Zenovka
I can't understand what's going on in this code, I'm afraid. You have all these different menu lists, for example, and you say you are trying to combine two scripts because you want the different menus, but you only ever show the user the first menu, as far as I can see.

Anyway, I had a go at tidying things up, and here's something to start on, which does change the prim's description based on your choice and generates a message based on that plus what you then say in chat.
CODE
string caption ="please choose one";
string my_description;
string bcode;
string message_string ="XX";
list menu_choices =[
"military","06",
"civilian","07",
"allfleet","05",
"mercury","21",
"wayfarer","24",
"celtic","23",
"fields","22",
"colonial1","22",
"rebel","25",
"marines","26",
"PTP","99",//i had to put this in
"Control","100"//and this
];

list menu_buttons;


integer coms_channel = -107721;
integer dialog_channel;
integer open_channel=0;
integer handle;

key av;
float timeout = 20.0;

default {
state_entry() {
menu_buttons = llList2ListStrided(menu_choices,0,-1,2);//pull out the first element of the pairs in the menu_choices list and make a list of buttons
dialog_channel = (integer)("0x"+llGetSubString((string)llGetKey(),-8,-1));//calculate channel from my uuid -- likely to be unique on the sim
// handle = llListen(dialog_channel, "",NULL_KEY,"");//create a listener // wrong place
// llListenControl(handle, FALSE);//and turn it off until i need it


}

touch_start(integer num){
if (llDetectedGroup(0)==TRUE){ // if the toucher is in the same group
handle = llListen(dialog_channel, "",NULL_KEY,"");//create a listener
llListenControl(handle,TRUE);//start listening
llSetTimerEvent(timeout);//and start the timer in case i don't hear anything
av = llDetectedKey(0); // grab the toucher's key
llDialog(av,caption,menu_buttons,dialog_channel); //and present a dialog
}
}

listen(integer channel, string name, key id, string message){
llListenControl(handle, FALSE); // turn off the listener
llSetTimerEvent(0.0); //and the timer
if(channel == dialog_channel){ // someone's used the menu

if(~llListFindList(menu_buttons,[message])){//check I understand it
//find the choice in my main list and set bcode to the appropriate value
bcode = llList2String(menu_choices,llListFindList(menu_choices,[message]+1));
my_description = bcode;// give this a value
handle = llListen(open_channel,"",av,""); // start listening to messages in chat from the user
llSetTimerEvent(timeout);
llInstantMessage(av,"Please speak your message in chat");
}
}
else if (channel == open_channel){ // the user said something in chat
message_string += bcode+message;
llSetObjectDesc(my_description); // set the object description to bcode, though i don't understand why we're doing this
llRegionSay(coms_channel,message_string);
llInstantMessage(av,"I've just sent the message\n"+message_string);
}
}

timer(){
llSetTimerEvent(0.0);
llListenControl(handle,FALSE);
llInstantMessage(av,"sorry, but you've taken too long to do anything. Please start over");
}






}



Hope this helps

ETA .. sorry.. just realised I should state listening on the dialog channel when the prim is touched, not state entry ... code corrected to reflect this
Jeredin Denimore
Romani Ite Domum
Join date: 5 Jul 2008
Posts: 95
10-28-2009 22:52
CODE

bcode = llList2String(menu_choices,llListFindList(menu_choices,[message]+1));


In the above snippet, [message]+1 results in [message,1], a two element list.
That two element list will not be found in the list, menu_choices.
The result of llListFindList will always be -1 (not found).
Since -1 as an index = the last element in a list, llList2String will always return the
last element in menu_choices no matter what [message] actually was.
In your specific case bcode is always set to "100".
Replace the above code with:

CODE

bcode = llList2String(menu_choices,llListFindList(menu_choices,[message])+1);


The +1 will now correctly increment the index from the button label to its value one index higher in the menu_choices list.
yevka Laws
Commander Eva "Yevka" Law
Join date: 19 Jul 2006
Posts: 32
Good Fix.
11-02-2009 17:19
The script functions properly. Thanks for Jeriden, not only for posting a fix but for seeking me out online to let me know.

yev