Template for Agent Detection in Events

There are three questions concerning the nature of events which a scripter should answer to code the event properly:

  1. How is the capture of the event set up or requested?
  2. Exactly how is the event triggered?
  3. What information is available to the scripter when the event is triggered?

Well I don’t know about you, but I’m getting really flustered seeing examples of LSL on the wiki and other places for the detection events–touch, collision, and sensor–where the detected agent is identified only by a subscript of zero:
[lsl]
touch_start (integer numDetected) {
key oneAgent = llDetectedKey(0);
// Do something with the agent’s key here.
}
[/lsl]
Yes, this will work to reserve data about one agent who has triggered the event. After all, the event will not fire if there hasn’t been at least one agent detected, and the indexes (actually offsets) for the agents start at zero. And I understand the need for simplicity when the focus of the discussion is the action that results from the detection, not the detection itself.

But the touch and collision events are able to sense more than one agent who has been detected since the last time the event was triggered. (Sensor detects only at the time of the request.) That’s the purpose of the formal parameter in the event header, which here I have named numDetected. This parameter answers the third question above: you are given the number of agents who have touched the object since the last trigger in the same state, and they are identified by the subscripts zero through (numDetected - 1).

Now, oftentimes there is only one avatar around to touch or collide with an object.  Even if there is more than one av around, usually only one av does the touching within the space of a full second–far enough time for the system to react to a touch event. And even if more than one av is touching an object close to the same time, many scripted reactions don’t need to know how many people touched the object. In this last case, you don’t even need the llDetected*() function calls.

But you cannot rely on isolated touches or collisions.  Attend a class at your favorite virtual university, for instance; the first thing to happen is the distribution of class materials, usually performed by having the students touch an object, each of whom should then receive the materials in the object’s inventory. When there are 30-plus students in the class, all of whom are touching the object at about the same time, the chances become good that numDetected will be greater than 1.

Assuming you don’t want to miss anyone in the process, you’ll need to use a loop. Here is the frame I use for any production-oriented detection-event code, with a line of example action:
[lsl]
touch_start (integer numDetected) {
integer av;

for (av=0; av < numDetected; ++av) {
llSay (0, llDetectedName(av) + " has touched me.");
}
}
[/lsl]
A common restriction to the above is to perform an action only when the object’s owner is detected.  In this scenario, other agents are usually ignored. I augment the template as follows:
[lsl]
touch_start (integer numDetected) {
integer av;

for (av=0; av < numDetected; ++av) {
if (llDetectedKey(av) == llGetOwner()) {
// Do your thing here.

// No need to check for anyone else.
return;
}
}
}
[/lsl]
If the code the owner executes is substantial, then for readability I might take a less structured approach to recognize him, as below. In school I was taught all the good habits of structured programming in the C-family languages: no gotos, breaks (outside of switch statements), or multiple exits from functions; then I got out into the real world, and grew up. It all comes down to some very old and sage advice for any discipline: know what the rules are before you try to bend or break them.
[lsl]
touch_start (integer numDetected) {
integer av;
key owner = llGetOwner() ;
for (av=0; av < numDetected; ++av)
if (llDetectedKey(av) == owner) jump allowed;

// Owner did not touch object; just leave.
return;

// Only the owner gets to execute the following code:
@allowed;
// Lots of code goes here.
}
[/lsl]
And while we’re here, let’s augment this for the case where touch access would be allowed by the owner and by members of the group to which the object is assigned. This particular version doesn’t discriminate between these choices, and so the llDetected*() functions would not apply. And I am given to understand than it’s best to employ separate targets for separate jumps–something about one of those long-standing LSL bugs.
[lsl]
touch_start (integer numDetected) {
integer av;
key owner = llGetOwner() ;

for (av=0; av < numDetected; ++av) {
if (llDetectedKey (av) == owner) jump alphaAvatar;
if (llDetectedGroup (av)) jump beta_Avatar;
}
return;

@alphaAvatar;
@beta_Avatar;
// Lots of code goes here.
}
[/lsl]

Certainly an even better, structured version of this would employ a user-written function, which in turn would allow us to use the llDetected*() functions. Notice that you can use the llDetected*() functions in a subroutine called by the detection events.

[lsl]
Authorized_Action (integer agent) {
key who = llDetectedKey (av) ;
// …et cetera.
}

// —————————————————

default {
touch_start (integer numDetected) {
integer av;
key owner = llGetOwner() ;

for (av=0; av < numDetected; ++av) {
if ((llDetectedKey (av) == owner) ||
llDetectedGroup (av)) {

Authorized_Action (av) ;

// Use if identification of the avatar is unnecessary:
return ;
}
}
}
}
[/lsl]

Whew! I think that’s enough for now.

Certainly I cannot be the only person who has developed this simple technique. But I honestly have never seen it posted. Now it is!

Originally posted 22 Aug 2008
Last edited 26 Jan 2009