Jump to content

switch off touch_start


testgenord1
 Share

You are about to reply to a thread that has been inactive for 1388 days.

Please take a moment to consider if this thread is worth bumping.

Recommended Posts

Hi again!
I'm writing a quiz script.

The answers to the questions are typed into the chat.
The quiz is supposed to be played by one player at a time only.
This means the game must be restricted to the player who starts it first.
Besides, I want the Public Channel to be open only, when really necessary, to prevent lagging.

Hence, I decided to start the quiz by touch_start.
Thus, the player's identity is stored through llDetectedKey, so only the player can reply to the questions and nobody else.

The problem is, when one player is playing, a second player can trigger the touch_start event, too,
and also start playing the game, disturbing the game of the first player.

What I might need is a trick through which, for some time, the touch_start trigger is switched off,
so that as long as the first player is playing, no one else can trigger it.

Do you maybe have any idea of how to do this?
There might also be differnt solutions to the problem.

I'm posting the script below:

default
{
    touch_start(integer total_num)
    {
        llSay(0,"question");
        key id = llDetectedKey(0);
        llListen(0,"", id,"");
    }
        listen(integer channel, string name, key id, string message)
        {
            if(message == "this is the correct answer")
            {
                llSay(0,"correct!");
            }
        }
}

Thank you very much in advance!

Link to comment
Share on other sites

Set a flag when the test taker clicks the object to start the test and then do not allow other clicks until the flag is cleared -- presumably at the end of the test or after a timer has run out.

touch_start(integer num)
{
    if ( !iTouched )    // Be sure to make iTouched a global integer
    {
        iTouched = TRUE;
        // start the quiz
        llSetTimerEvent( 120.0 );     // For example, as a way to tell when the test has ended...
    }
}

timer()
{
    iTouched = FALSE;    // Ready for another test to start.
    llSetTimerEvent(0.0);
}

 

  • Like 1
Link to comment
Share on other sites

Bringing states into this may complicate the issue, but it is my favourite way of preventing unwanted clicks. And the OP did acknowledge there might be different solutions. :)

Were you to switch to an active state without a touch_start event when the quiz begins, other avatars wouldn't even have the option of clicking.

key user;

default
{
    touch_start(integer total_num)
    {
        user = llDetectedKey(0);
        state active;
    }
}

state active
{
    state_entry ()
    {
        llListen(0,"", user,"");
        llSay(0,"question");
    }

    listen(integer channel, string name, key id, string message)
    {
        if(message == "this is the correct answer")
        {
            llSay(0,"correct!");
            state default;
        }
    }
}

However, in this case it may be preferable to stick to a single state and use a flag. This does give you the option to send a "Please try again later" response.

Edited by KT Kingsley
  • Like 2
Link to comment
Share on other sites

Hi again!
Thank you very much for your support.
I chose Rolig Loon's method and it works fine.

KT Kingsley's idea worked fine, too (I actually had used that idea at first,
but forgot to define integer "user" as a global variable, preventing it from being used in the second state).

I also liked Lucia Nightfire's idea quite a bit but I couldn't find out how to actually switch off the touch_start trigger using this.
I don't actually need it at this point, but I like the idea, so, in case you could explain it to me, I would be grateful.

Anyway, here is the adjusted script using Rolig Loon's ideas:

key id;
integer iTouched;

default
{
    touch_start(integer total_num)
    {
        if ( !iTouched )    // Be sure to make iTouched a global integer
        {    
        iTouched = TRUE;
        llSay(0,"question");
        id = llDetectedKey(0);         
        llListen(0,"", id,"");
        llSetTimerEvent( 20.0 );     // For example, as a way to tell when the test has ended...
        }
    }
        listen(integer channel, string name, key id, string message)
        {
            if(message == "this is the correct answer")
            {
                llSay(0,"correct!");
            }
        }   
        timer()
        {
            iTouched = FALSE;    // Ready for another test to start.
            llSetTimerEvent(0.0);
        }    
}

Thanks again!
 

Link to comment
Share on other sites

7 hours ago, testgenord1 said:

Hi again!
Thank you very much for your support.
I chose Rolig Loon's method and it works fine.

KT Kingsley's idea worked fine, too (I actually had used that idea at first,
but forgot to define integer "user" as a global variable, preventing it from being used in the second state).

I also liked Lucia Nightfire's idea quite a bit but I couldn't find out how to actually switch off the touch_start trigger using this.
I don't actually need it at this point, but I like the idea, so, in case you could explain it to me, I would be grateful.

Anyway, here is the adjusted script using Rolig Loon's ideas:

......

Thanks again!
 

There are several little other things missing :

Firstly : you are creating  a listener at every touch but without delete the others . So after a quizz of 100 questions , your script has 100 active listeners , with 99 who are useless . It s a waste of resources of the region

 

Secondly : you are generating a listener for any touch , even  when the user is too far to be able to answer . By chat , the user is limited to 20 meters , 96 meters if he s shouting , so it s useless to generate a listener and waiting some seconds for nothing . People can touch prims at more distance than they can talk to the prims , in using the zoom camera  . People can speak to the prims higher than 96 meters only  if they talk via one attachement scripted

 

Thirdly : when your player is answering correctly ( and not wrongly ) , your script  is not desactivating the timer and is not  restablishing the possibility to touch before 20 seconds . So people , after a good answer can t play .. and are obliged too wait the end of timer. Nearly a  bug

Fourthly , maybe  it s not necessary for the prim to talk in the public chat to anyone , but only to the active player . I am not sure about what you want exactly , but you may condider it  and using llregionsayto  .  

Sixthly In addition you don t solve the issue  when the user  goes out from the chat ange distance  when the prim talks in local chat . The player won t see the message from the pri . Again one time llregionsayto ispreffered , but as i am not sure about why you want ( maybe the quizz is to use with seveal people ) , i have not changed this point

 

integer idListen;
float TIME_ANSWER = 20.0;
default
{
    on_rez(integer start)
    {
        llSetClickAction( CLICK_ACTION_TOUCH );
    }
    state_entry()
    {
        llSetClickAction( CLICK_ACTION_TOUCH );
    }    
    touch_end(integer total_num)
    {
        // as the prim talks only to 20 meters , we limit the touchers to people inside 20 meters
        if ( llVecMag( llDetectedPos(0)-llGetPos() ) <= 20.0 )
        {
            llSay(0,"question");
            // remove old listeners to not waste resources of the sim 
            llListenRemove( idListen);
            idListen = llListen(0, llDetectedName(0),  llDetectedKey(0),"");
            // desactivate the touch event s: 8 is undocumentated value , but seems working , thanks to Lucia ; an alternative is using several states to cchange the event manager of the script
            llSetClickAction( 8 );
            llSetTimerEvent( TIME_ANSWER );     
        }
    }
        listen(integer channel, string name, key id, string message)
        {
            if(message == "good answer")
            {
                llSay(0,"deal the answer and congratz");
                // allows to go to the next question , so the touch event is freed . change of timer and llsetclickaction
                llSetClickAction( CLICK_ACTION_TOUCH );
                llSetTimerEvent(0.0);
            }
            else
            {
                llSay(0,"bad answer : try again");
                // wait again an another answer : so the touch event is again blocked: we want again an another answer from the same player : no change of the timer and llsetclickaction
            }
        }   
        timer()
        {
            llSay(0,"You have not given the good answer before the time . The good answer was ....");
            llSetClickAction( CLICK_ACTION_TOUCH );
            llSetTimerEvent(0.0);
        }    
}

 

 

Edited by Miranda Umino
  • Like 1
Link to comment
Share on other sites

Thank you so much for your time and your explanations!
I could never have come up with these ideas.
These are great improvements and will help me a lot in the future.
Again, thank you very much, I really appreciate this!
edit: Through trial and error I noticed that we probably need

llListenRemove( idListen);

or llResetScript() at the end, because otherwise the listener will remain open indefinitely after the end of the timer.
This means you could still answer the question although the timer has run already out.
Is that right?

Edited by testgenord1
Link to comment
Share on other sites

Yes, you should remove a listener when it's no longer wanted, even if only because scripts are limited to a maximum of 65 of them. But also because they use simulator resources, so it's only polite to do so.

(Incidentally, changing state as well as resetting the script will close any active listeners.)

ETA: there's also llListenControl, which lets you turn an existing listener on or off. Does anyone know if or when using this might be preferable to creating and then removing listeners? I'd guess it'd be the better option on a busy channel when otherwise you'd frequently be creating a new listener and then removing it.

Edited by KT Kingsley
  • Like 2
Link to comment
Share on other sites

4 hours ago, testgenord1 said:

Thank you so much for your time and your explanations!
I could never have come up with these ideas.
These are great improvements and will help me a lot in the future.
Again, thank you very much, I really appreciate this!
edit: Through trial and error I noticed that we probably need


llListenRemove( idListen);

or llResetScript() at the end, because otherwise the listener will remain open indefinitely after the end of the timer.
This means you could still answer the question although the timer has run already out.
Is that right?

Indeed you will need to delete it . .if you want that the player has ony an allocated time for all his answers .

We were not sure  about what you were expecting .

Indeed the player  could be confused that while he continues his answers  he is stopped brutally after some time  and he is forced to click again , even when he golds the hand on the game . He ill think about lag  or bug when in fact it s the design of your script

 

If you want nevertheless to force the player to click again after his 20 seconds allocated for his answers   , i suggest you to warn him when tje timer expires and inviting him to click again . Either you can do by a message , either you can do visually ( change of colors , sounds playes , particles effect ) 

Your first version could have as much active  listeners than the number of times  someone has touched the prim . ( so it xan grows )

The version i have fiven you keeps only one listener max active  , but of course , you can have zero listener when ther is nearby

An another solution , is to create a llsensorrepeat  who scans if the player has not left . On no_sensor event you remove the sensorrepeat and you remove too the listener and you enable the CLICK_ACTION_TOUCH  and something informs the palyer he has left the playground and needs to touch again . 

Resetting the script won t be my preferred solution in your cas :  the versions we post here are drafts , but your final version does certainly a lot of things at the start of the game we have not written yet in this forum , but who should be written in your final version  . . For instance reading a notecard or a website to fetch the questions and answers to ask to players .  With a reset script you  will need to read again theses notecards or to fetch questions/answers on external urls .  It takes too much time , so , i will avoid llresetscript in your case . There are other ways to remove listeners ( changing state , call a removelisterner  ..) and who don t erase the initialisations of your script

        timer()
        {
            llSay(0,"You have not given the good answer before the time . The good answer was ....
Click again ");
            llSetClickAction( CLICK_ACTION_TOUCH );
            llSetTimerEvent(0.0);
            llListenRomeve(idListen);
        } 
7 hours ago, Lucia Nightfire said:

BTW, the value 8 for llSetClickAction() may be undocumented, but it was implemented by LL this year so it is not a hack. The wiki page just needs updating.

Thank you

Edited by Miranda Umino
  • Like 1
Link to comment
Share on other sites

7 hours ago, Lucia Nightfire said:

BTW, the value 8 for llSetClickAction() may be undocumented, but it was implemented by LL this year so it is not a hack. The wiki page just needs updating.

I'd be interested in reading up more on the specifics of flag value 8.  Do you know off the top of your head the jira or release notes addressing this?   Thanks.

Link to comment
Share on other sites

11 hours ago, KT Kingsley said:

ETA: there's also llListenControl, which lets you turn an existing listener on or off. Does anyone know if or when using this might be preferable to creating and then removing listeners? I'd guess it'd be the better option on a busy channel when otherwise you'd frequently be creating a new listener and then removing it.

intuitively I would agree that turning an existing listen off and on rather than creating a new listen each time would be more efficient time-wise from our own script's perspective

a fast-paced multi-player first responder game would I think be a candidate

  • Like 1
Link to comment
Share on other sites

FWIW click action 8 is honored only viewer side. Server side enforcement was never implemented.

This means older/illegal viewers can still bypass it. So ATM, it is unwise to hinge critical content/operation on its use.

I filed a jira just now to hopefully get server side support as well as a dedicated constant name for llSetClickAction().

  • Like 1
  • Thanks 2
Link to comment
Share on other sites

On 8/16/2019 at 3:20 AM, Lucia Nightfire said:

FWIW click action 8 is honored only viewer side. Server side enforcement was never implemented.

This means older/illegal viewers can still bypass it. So ATM, it is unwise to hinge critical content/operation on its use.

I filed a jira just now to hopefully get server side support as well as a dedicated constant name for llSetClickAction().

Wow, big thanks! The touch event is (at least according to my scripting experience) the only lsl use case when it makes sense to change states. If your request would be properly implemented, LL could finally get rid of lsl states entirely. States unnecessarily confuse and put off newcomers who are interested in scripting. My first big progress in LSL was the realization that states exist to be ignored... (ok, so far with the exception:  touch events on/off).

Edited by Estelle Pienaar
Link to comment
Share on other sites

On 8/14/2019 at 12:21 AM, Rolig Loon said:

Set a flag when the test taker clicks the object to start the test and then do not allow other clicks until the flag is cleared -- presumably at the end of the test or after a timer has run out.

 

One thing to consider is if the object should be clickable at all as long as someone else is playing. I prefer to have a floating text stating that the object is in use by another player and only make it clickable again after the turn of the previous player ended (one state with touch event and another state without the touch event). After all, if other people can click the object but nothing happens, then they might think that it is broken. Even if they get a message, they might be confused on when they should or should not click again.

Edited by Estelle Pienaar
  • Like 1
Link to comment
Share on other sites

3 minutes ago, Estelle Pienaar said:

Wow, big thanks! This was (at least according to my scripting experience) the only use case when changing states made sense. If your request would be properly implemented, LL could finally get rid of lsl states entirely. States unnecessarily confuse and put off newcomers who are interested in scripting. My first big progress in LSL was the realization that states exist to be ignored... (with the exception:  touch events on/off).

Some people use states for code readability.

Some people change state to cull queued events that can't be culled with functions.

States are good for going to a state that can't have event triggers/queuing that would occur in a prior state simply because the event handlers are not present in that state.

Link to comment
Share on other sites

21 minutes ago, Lucia Nightfire said:

Some people use states for code readability.

States are good for going to a state that can't have event triggers/queuing that would occur in a prior state simply because the event handlers are not present in that state.

Of course you can use states, but that doesn't mean that it is more conveniant. I haven't seen any convincing examples so far appart from touch_event on/off. However my statement will always stay an unfullfilled wish anyway. Because of legacy content... 😉

And at least I can chose to ignore states and maybe ignore them even more in the future thanks to your JIRA.

EDIT:

21 minutes ago, Lucia Nightfire said:

Some people change state to cull queued events that can't be culled with functions.

Do you (or anyone else) have a concrete example for this use case?

Edited by Estelle Pienaar
Link to comment
Share on other sites

From the user's standpoint, states with and without touch-related events may be convenient, but there are also efficiency concerns with having irrelevant event handlers open. It's common enough for a script to read a settings notecard at startup, for example, and once that's over there's a real benefit gained by switching to a state where dataserver events are no longer handled -- especially if there are other scripts in the same object that are still receiving dataserver events, which wake up every script in a state to handle dataserver events. Same with other event classes that may be only briefly relevant.

State change can also be handy for closing any open listens all at once.

But this llSetClickAction(8) is different inasmuch as it should only ever affect how the simple left mouse click should be processed; it isn't (and shouldn't be) enforced server-side to prevent choice of "Touch" from the right-click menu as a non-touch state will do. As such it represents distinct, potentially useful UI semantics.

Edited by Qie Niangao
Link to comment
Share on other sites

3 hours ago, Qie Niangao said:

 for example, and once that's over there's a real benefit gained by switching to a state where dataserver events are no longer handled -- especially if there are other scripts in the same object that are still receiving dataserver events, which wake up every script in a state to handle dataserver events.

Ok, that seems to make sense for some cases. Now I wonder how relevant this is for me.

Out of curiosity: what do you mean with "object". A prim or a linkset? It does not seem to make much sense if scripts in linked prims get active from a dataserver call in another prim, but from your statement I guess that it is the case?

Anyway so far I have succesfully avoided having more than one script with a dataserver event handler or http_response event handler in a linkset as I prefer a very clear seperation of responsabilities between my scripts in objects or HUDs.

It's probably my specific use case but in my HUDs the scripts (1) do read every now and then from notecards and not just one time and (2) manage several tasks in order to keep script count as low as possible. If I would create a state without a dataserver event handler, then I would therefore have to dublicate 500+ lines of code into the new state. Then the script needs much more memory (I am pretty sure that it would run out of memory too). And if I put the notecard reading in a seperate script, then there is one more script than necessary. All in all switching states seems to demand more resources than just leaving the existing (EDITED:) event handler idle until used again.

Edited by Estelle Pienaar
Link to comment
Share on other sites

1 hour ago, Estelle Pienaar said:

Out of curiosity: what do you mean with "object". A prim or a linkset?

Oh, I probably mindlessly copied the "object" term from somewhere, but I'm pretty sure it's just the prim not the whole linkset. (Easily enough testable, but sloth prevails.)

If I understand your case specifically, there'd be no reason to switch state to one without a dataserver handler unless there's another script (yours or somebody else's) in the same prim also doing dataserver stuff.

Even then, it's not end of the world for a script to get awakened and need to filter an irrelevant event. It's not ideal, but it's not all that uncommon either.

Link to comment
Share on other sites

You are about to reply to a thread that has been inactive for 1388 days.

Please take a moment to consider if this thread is worth bumping.

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now
 Share

×
×
  • Create New...