Jump to content

Trouble with Random Greet Script


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

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

Recommended Posts

Hi. I am very new to scripting but I am trying my hardest to learn and develop a script.

I need a script that will sense people within a specified range, i.e. 5m, and then greet them with a random sentence generated from a list, that would be included in the script. I don't want the person that it greets to be bombarded with multiple greetings, so I was trying to incorporate an avatar list of specified number for the script to remember and not repeat, for example last 25 avatars. This is how far I have got so far, can anybody help me progress?

Thank you so much in advance to anyone who comes to my rescue! :)

____________

 

list greetings = ["Hi", "Hello", "Greetings", "Welcome"];

list recent_avatars;

add_avatar(string name) {
    if(!seen(name)) {
        recent_avatars += name;
        if (llGetListLength(recent_avatars) > 25) {
            recent_avatars = llDeleteSubList(recent_avatars,0,0);
        }
    }
}
integer seen(string name) {
    if(llListFindList(recent_avatars,[name]) > -1) { return TRUE; }
    return FALSE;
}


default
{

    state_entry() {
        llSensorRepeat("", NULL_KEY, AGENT, 5, PI, 5);
    }
    sensor(integer total_number) {
        if(!seen(llDetectedName(0))) {
            // speak out loud!
        llSay(0, llList2String(greetings,saying2say));

    add_avatar(llDetectedName(0));
    }
}

}

 

Link to post
Share on other sites

It looks pretty good so far. You're almost done.  My own preference would be to do away with the two user-defined functions and compress the script by in-lining them.  The script will run faster and be easier to read.   Something like this ...

list greetings = ["Hi", "Hello", "Greetings", "Welcome"];list recent_avatars;default{    state_entry() {        llSensorRepeat("", NULL_KEY, AGENT, 5, PI, 5);    }    sensor(integer total_number) {        if(!~llListFindList(recent_avatars,[(llDetectedName(0))])) {            // speak out loud!            integer saying2say;            llSay(0, llList2String(greetings,saying2say));            recent_avatars += llDetectedName(0);            if ((recent_avatars !=[]) > 25)            {                recent_avatars = llDeleteSubList(recent_avatars,0,0);            }        }    }}

 To get a random greeting in place of the variable saying2say, all you need to do is randomize on the length of your greetings list.

integer saying2say = (integer) llFrand( (greetings !=[]) );

where greetings !=[] is another way of saying llGetListLength(greetings)

Link to post
Share on other sites

Thank you both so much for your replies!

The problem I was having with it was that when I tried to save the script each time, it was squeaking at me and telling me something was wrong in the writing.

So I shall give the suggested script a go! And reply back here how I get along...

Thank you thank you thank you !

Link to post
Share on other sites

That "squeaking" was just telling you that you forgot to define the variable saying2say.  Once you put that into your script, it should compile just fine.  If you use a good external editor, it will find things like that for you.  :smileywink:

Link to post
Share on other sites

Ok, having spent a little time playing around with this script I feel I understand it better now. I can see how it works and now see where I need to adapt it for its purpose better.

It does an avatar sensor every 60secs currently and then gathers a memory of avatars not to repeat to.

Is it possible to retain the random greet element and the sensor but also add onto it the ability to not repeat within a certain time frame to an individual avatar so like this:

Scans for avatars every 60secs

Greets Avatar X with random phrase, then won't repeat to this specific avatar for say 3 hours.

But will continue to scan for new avatars, greet them and then recall their name not to be repeated to for said time.

I'm not sure if it is possible but I am aware how annoying greeters can be if they are not set up well :)

 

Link to post
Share on other sites

It's possible but I think I might make a pig's ear out of trying to explain it...

First, as well as recording the name/UUID of each person that has been greeted you need to record the time they were greeted.  You can either use a parallel or strided list for that.  "Parallel" means two lists like: list Names = [<name>, <name>, <name> .....] and list Times = [<time>, <time>, <time> ....].  "Strided" means everything in one list like: Greeted = [<name>, <time>, <name>, <time>, <name>, <time> ...]

As you add people to the list(s) as they are greeted they are, naturally, in order of the times.

Whenever your sensor event fires go through the times from the first item deleting any that are less than llGetUnixTime() - (however many seconds you want to keep stuff).

That way people only stay in your 'already done' list(s) for the period you want - and all the rest of your processing stays exactly the same, of course.

Link to post
Share on other sites

Peter's laid out the plan nicely.  When you sit down to write your script -- any script -- take time to plant the logic in your head first.  Then worry about the details of syntax that will turn it into a script.  Think through the order in which actions ought to happen, occasionally looking back to see whether you should have prepared for them by collecting some necessary bit of information earlier. Start with the broad outline.....

1. Greeter detects person, looks at its watch

2. Greeter checks its list of previous visitors: Were any visits much than 3 hours ago? >> If so, remove names and visit times

3. Greeter checks to see whether detected person is on the remaining list of previous visitors >> If so, ignore.

4. Greeter can't find detected person on list >> Greets person, records name and time of visit.

Then go back and start filling in the details.  How does it "look at its watch"? How does it know when three hours ago was?  How does it know which person was here at a given time?   Once you have most of the pieces in hand, write your script.  You're going to forget something, but that's OK.  The script will tell you later when you have made a logical error, and you can use its results to tell you what's wrong.

Link to post
Share on other sites


Lili Renilo wrote:

so maybe I am getting in over my head... cos I really don't understand. Scary stuff! lol

 

This is what is called learning, and I did say I might not explain it very well :-) Thanks for the clarification and vote of confidence Rolig

Lili - any programmer will tell you that anything but the most trivial script won't work first time and even once it works can be re-written 'better' - any or all of clearer, faster, lower memory.

Writing something new (to you) is a process of working out a way to achieve the objective with the tools available.  Getting familiar with the tools helps a lot but isn't all of it.  And once you've worked out a way you find out it doesn't work, your second great idea isn't possible and Plan 64C would work except that there's an 'undocumented feature' in LSL/SL/The moon that means it's not like that.

Hehe, it's not quite that bad (sometimes).  Practice, practice, practice.  Enjoy yourself :-)

And ask again which bit(s) particularly you don't understand.  It's why we're here.

Link to post
Share on other sites

Ok. I will try and break it down into chunks. Thank you both for trying to help me. (Just for clarification, I didn't mean in any way to suggest Peter's explanation and reply was not adequate, as I said it's my newness to this that makes it difficult).

 

I'll try to see what I can do :)

Link to post
Share on other sites

whenerever I write a specific bit of code, and have less that one silly mistake every 50 lines I celebrate (like a missing semicolon or mispelled variable) if I write something that;s longer than 50 lines and it compiles the first time I wonder what I messed up =X

 

PS
if you want to greet the person only once ever (or as close to as possible), you can do away with storing the time. instead, remove their name from the list, and add it back to the end... that way when have to limit the size of the list it's less likely they'll be removed. (you can do the same thing with included times, to never great someone with X period of the last time they were there)

Link to post
Share on other sites

I've been trying to adapt this script to suit my requirement. I added in the random say element and attempted to change the collision to a sensor but I have an error now on line 12 or thereabouts, where there is a bracket.

The other thing I don't understand when I read this script is... I can see it is a 24 hour memory of if a person has visited but I don't understand the 12 hour part on the set timer event and lower down on the timer.

I'd be grateful for any feedback people can give me :) I already feel very lucky to have found this community, thanks in advance......

 

 

 

list greetings = ["Hi", "Hello", "Greetings", "Welcome"];
list gVisitors;  // Save Visitor UUIDs
list gVtime;  // Save visit times

default
{
    state_entry()
    {
        llSetTimerEvent(43200.0); // Start a 12-hour garbage collector
    }
   
    {
        llSensorRepeat("", NULL_KEY, AGENT, 5, PI, 5);
    }
    sensor(integer total_number)
    {
        integer temp = llListFindList(gVisitors,[llDetectedKey(0)]);
        if(~temp)  //Has the Av visited before?
        {
            //Subtract saved Unix time from current Unix time. Was it it > 86400 (1 day) ago?
            if((llGetUnixTime() - llList2Integer(gVtime,temp)) > 86400)
            {  // If yes .....
                gVisitors = llDeleteSubList(gVisitors,temp,temp); //Remove Av from visitor list
                gVtime = llDeleteSubList(gVtime,temp,temp);  // Remove Av's visit time too
            integer saying2say = (integer) llFrand( (greetings !=[]) );
            llSay(0, llList2String(greetings,saying2say));
            }
        }
        else  //No previous visit
        {
            gVisitors += [llDetectedKey(0)]; // Add to the Visitor list
            gVtime += [llGetUnixTime()];  // Add current Unix time to the time list
            integer saying2say = (integer) llFrand( (greetings !=[]) );
            llSay(0, llList2String(greetings,saying2say));
        }   
    }

    timer() //Every 12 hours ...
    {
        integer i;
        //(gVisitors !=[]) is the same thing as llGetListLength(gVisitors)
        for (i=((gVisitors !=[])-1);i>=0;--i) //Look through all the saved visitor times
        {  // For each one ...
            if((llGetUnixTime() - llList2Integer(gVtime,i))> 86400) // If it was more than a day ago ...
            {
                gVisitors = llDeleteSubList(gVisitors,i,i); // Remove the person from the Visitor list
                gVtime = llDeleteSubList(gVtime,i,i); //Remove the visit time from the list
            }
        }
    }
           
}

Link to post
Share on other sites

The error is easy to fix.  The llSensorRepeat statement near the top of your script isn't in an event. It's just hanging out there by itself.  If you want it to be in the state_entry event ( a logical spot), remove the extra }{ brackets just before it.

As for the timing ..... You want to clear the lists of any names and times that are more than 24 hours old. (Script memory is precious.)  However, suppose that you only ran the timer event once a day and someone wandered in 10 seconds after the timer fired?  The next time it fired, 24 hours later, that person would not be removed from the list because she had only been on it for 23 hours, 59 minutes, and 50 seconds.  She would still be on the list a whole day later.  If your shop isn't very busy, that's fine.  If you get a lot of traffic, though, you really want to get people like her off the lists faster to save memory.   So you run the timer every 12 hours (or some shorter period) instead. Now that person can't be more than 11 hours, 59 minutes and 50 seconds over your 24 hour limit.  The faster you fire the timer, the more frequently you clean out those extra people and the more memory you have available for new customers.

 

Link to post
Share on other sites

Nearly there but you're doing too much work for it.  This is where the tidying-up once you've worked it out starts.

If there are no more people detected then you're not recording new keys, not using extra memory and don't care if out-of-date greetings are still being remembered.  In contrast to what Rolig's said that means you just don't need the timer() event-handler at all.  Check for and remove old greets only when there are new people to process (in the sensor() event-handler).

In the sensor event-handler as you have it there is this check anyway, but after you've found the person in the list, this is duplicating the timer()'s work.  Once removed you re-greet, this is duplicating the 'not found' branch's work.  :-) We can get obsessive about only doing things once.  Obviously twice is more code but most importantly for coding it's two places where you've got to make sure the code is exactly the same.  As you maintain, change and grow programmes it is very easy to change one occurrence but not the other or to change them differently.

Combining the two you get the sensor() logic: remove outdated greets from lists, if each detected person is not in the list greet and add

Code follows

Link to post
Share on other sites
list Greetings = ["Hi", "Hello", "Greetings", "Welcome"];list Times;list Visitors;default{	sensor(integer HowMany){		key Avatar;		integer Counter;		integer Valid = (llGetUnixTime() - 10800);		// Are there any previous greetings to check?		if(llGetListLength(Times)){			if(llList2Integer(Times, -1) < Valid){				// None of the previous greets is still valid				Times = [];				Visitors = [];			}else if(llList2Integer(Times, 0) < Valid){				// Find first valid greet				do{					Counter++; 				}while(llList2Integer(Times, Counter) < Valid);				// Drop the invalid greets				Times = llList2List(Times, Counter, -1);				Visitors = llList2List(Visitors, Counter, -1);			} // Else all the previous greets are still valid so nothing needs to be done		}		// Process each detected avatar		for(Counter = 0; Counter < HowMany; Counter++){			Avatar = llDetectedKey(Counter);			if(llListFindList(Visitors, [Avatar]) == -1){				// Random greet and add to list				llRegionSayTo(Avatar, 0, llList2String(llListRandomize(Greetings, 1), 0));				Times += [llGetUnixTime()];				Visitors += [Avatar];			}		}	}	state_entry(){		// Scan at regular intervals		llSensorRepeat("", NULL_KEY, AGENT, 5.0, PI, 10.0);	}}

 

I've been "a bit clever" (ie; awkward) here, mainly in order to demonstrate some alternative methods and considerations.  Everything interesting is in the sensor() event-handler.

I've changed the way you're greeting, since the llSay() you had would just be putting "Hi" or similar several times into open chat without saying who it's too or any other personalisation.  That'd be pretty spammy for everyone else so I'm using the new function llRegionSayTo() which will mean no-one else sees the greeting.  I'm also using llListRandomize() to select a random greeting, instead of generating a random index into the list.  This is probably less efficient to be honest, but I did say I was being awkward!

Clearly the main difference here though is the check and clear-out of outdated (invalid) previous greetings.  List operations in LSL are slow so instead of looping-through and deleting each invalid one as I come to it I'm using extra logic to find what needs to be removed.  Firstly if the most recent greet, at the end of the list, is invalid then all the others must be too, so the lists are just re-initialised.  At the other extreme, if the first, oldest, greet is still valid then they all are so nothing needs to be done.  In between I find the first valid greeting by incrementing Counter and then update the lists in a single operation to skip-over the ones that are invalid.  In practice this sort of messing around may or may not improve the speed and/or memory-efficiency of a programme, it's very much dependent on circumstances (how many entries in the list, how many invalid, etc. etc.)

Link to post
Share on other sites

although you may not want to use sensor repeat since it has the annoying habit of scanning into neighboring regions on an irregular basis when used near a region border.

otherwise it looks very close to what I wrote on the wiki, ages back (really need to update it some day)

Link to post
Share on other sites

I like it, Pete.  It makes the garbage collection cycle more compact and eliminates the need for a timer event. It also introduces llRegionSayTo and llListRandomize as alternative ways to handle those tasks.

If nothing else, I hope the OP sees that there is more than one way to handle almost any scripting challenge. The method you choose ought to be the one that's most efficient and best matched to the setting where it will be used. In a very low traffic environment, in fact, a greeter script like this might not need a garbage collector at all.  It would simply check each new visitor against the two lists and either greet, ignore, or eliminate.  It could check llGetFreeMemory every once in a while and alert the owner when the lists finally got so full that the script needed restarting. (Not the best choice for a hands-free operation, but ..... ). The one I wrote for my first, unsuccessful shop was like that.  I never got a chance to restart it <sigh>. 

Link to post
Share on other sites
You are about to reply to a thread that has been inactive for 3540 days.

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

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...