Welcome to the Second Life Forums Archive

These forums are CLOSED. Please visit the new forums HERE

Using llSensor to locate specific scripts

Cherry Hotaling
Registered User
Join date: 25 Feb 2007
Posts: 86
01-09-2008 08:43
Hello,

I am doing a small combat system and I was wondering if it is possible to script a sensor to locate a particular script in an object (Attached or on the ground) and list those objects.

Sort of a way to locate and target NPC's and Player characters if they have a particular script on them?

Im trying to make an easy way to target a NPC and/or PC for a scripted combat syste.

If anyone can help steer me in the right Direction or give an example I would appreciate it Greatly.

-Cherry Hotaling
Qie Niangao
Coin-operated
Join date: 24 May 2006
Posts: 7,138
01-09-2008 09:16
Sensors probably won't do what you want. You can of course filter the sensor to only look for SCRIPTED objects, but that won't work for attachments, which are hidden from sensors by the AGENT to which they're attached. And even then you can't see inside the object, other than what scripts in that object want to tell you. And if they're willing to "confess", you don't really need a sensor to get them to do that.

So, I think you're limited to detecting scripts that you have made willing "confessors" and I'd just do that with an llListen() on some known obscure channel, and have the detecting script either llShout or llRegionSay (depending whether sim-border-crossing or sim-wide range is required) to see which things reply.

Unlikely to be useful: For the non-attached within-sim subset of the problem, one could theoretically have a script contained in the detected object modify the object's description, run a sensor for scripted objects, and filter those results through llGetObjectDetails() for OBJECT_DESC. (I can't imagine this ever actually being useful, though, if I understand the intent; only mention it because it lets the detected objects be completely passive and without an llListen once they've set their description. But if any other scripts or the owner changes the description, this would fail anyway.)
Cherry Hotaling
Registered User
Join date: 25 Feb 2007
Posts: 86
01-09-2008 11:20
So for example.

I hit the attack button on my HUD I create. It sends out a tell to a listener on some (Negative Channel ID)
within the object or attachment saying hey im here. (possibly giving its coordinates in the sim (This can be done for the avatar this is attached to also I would think)

Then select your target (Possibly a list created for llDialog of nearest targets)

Would I need a sensor to tell how far away from an object/enemy is? Or would a whisper suffice? (I think a whisper is a 10M Range though)

In the end I would like it to know if I am facing the enemy or not.
But if this isnt possibly thats understandable.

-Cherry Hotaling
Qie Niangao
Coin-operated
Join date: 24 May 2006
Posts: 7,138
01-09-2008 12:10
From: Cherry Hotaling
Would I need a sensor to tell how far away from an object/enemy is?
Shouldn't need a sensor: the listen() event will include the key of the thing that replied, on which a call to llGetObjectDetails will get its its position, rotation, velocity, etc. (Can't use llDetected* in a listen() handler.)
From: someone
In the end I would like it to know if I am facing the enemy or not.
If the sensing object is an attachment, I think you'd use llGetRootRotation to figure out which way the wearer was facing. If not an attachment, I think another call to llGetObjectDetails on the avatar itself would give its rotation.
Cherry Hotaling
Registered User
Join date: 25 Feb 2007
Posts: 86
01-09-2008 12:29
Ok thank you,

I will try out some tests tonight and see how it goes.

-Cherry Hotaling
Cherry Hotaling
Registered User
Join date: 25 Feb 2007
Posts: 86
01-10-2008 07:33
I have a question.

If I do a listener event in the Enemy Prim NPC for damage Dealt from a dagger weapon 1d3 (One 3 sided Dice)
But I want the enemy only to listen if its the target of the say.


This is what the Test Attack button looks like now:



integer AttackRoll = -57; //Channel for attack Roll



default
{
state_entry()
{

}

touch_start(integer total_number)
{
integer Dagger = llFloor(llFrand(3)+1); //Dagger is 1d3
llSay(AttackRoll, (string)Dagger);
llSay(0, "You Swipe with your Dagger";);
}

//listen(integer channel, string name, key id, string message)
// {

// }


}


Heres what the enemy script looks like now:

integer AttackRoll = -57; //Channel for attack Roll


integer AC = 12;

default
{
state_entry()
{

// Set up listener for return message
llListen(AttackRoll, "", "", "";);
}

listen(integer channel, string name, key id, string message)
{
if (channel == AttackRoll)
{
integer d20 = llFloor(llFrand(20)+1);
if (d20 >= AC)
{
llSay(0, "Attack Hit Because AC= " + (string)AC + " and d20 Rolled: " + (string)d20 + " Hit for: " + (string)message + " Damage!";);
}else if (d20 <= AC)
{
llSay(0, "Attack Missed Because AC= " + (string)AC + " and d20 Rolled: " + (string)d20);
}
}
}


}


Lets say in the Hud attack button I store the ID of the enemy NPC prim.
how do I send a llSay command that includes the ID of the Prim enemy and the damage rolled?

On the flip side whats the best way to have the enemy NPC Prim to Listen and accept damage only when its ID is included in the damage Roll?

so from the HUD attack button I would try and send the following:
Attack Channel, Damage Sent, Enemy Prim ID

and the enemy Prim would listen for:
if Enemy Prim ID is My Prim ID and its coming from the attack channel im taking (damage Sent)

Thanks in advance.

-Cherry Hotaling
Hewee Zetkin
Registered User
Join date: 20 Jul 2006
Posts: 2,702
01-10-2008 09:04
It would probably be easiest to include the key of the destination object in the message, and have each listening object ignore it unless the key matches itself. That usually means separating multiple parameters of the message by a delimiter such as space or comma (I prefer the latter because llList2CSV() and llCSV2List() make things quite simple).
Cherry Hotaling
Registered User
Join date: 25 Feb 2007
Posts: 86
01-10-2008 09:15
hey Hewee,

Any way I can get an example of what you mean by that?

So the Attack button after getting the Key of the object also lists the damage?

for example:

llSay(AttackRoll, (string)NPCKeyID + "," +(string)Dagger);

where NPCKeyID is the stored Key of the Enemy Prims Key. and Dagger is the damage Dealt.

How would the Script look on the other end to recieve this info if it matches its Key?

Thanks

-Cherry
Hewee Zetkin
Registered User
Join date: 20 Jul 2006
Posts: 2,702
01-10-2008 09:46
In the receiving script's listen handler, you'd do something like:

CODE

list params = llCSV2List(message);
if (llGetListLength(params) < 2 || (key)llList2String(params, 0) != llGetKey())
{
return;
}
integer daggerDamage = (integer)llList2String(params, 1);
...


There's a slight assumption in that code: that the key used is that of the prim the script is in. If that's not the case, you may need to get the key of the root prim using llGetLinkKey() instead of just using llGetKey().
Cherry Hotaling
Registered User
Join date: 25 Feb 2007
Posts: 86
01-10-2008 10:03
Im going to have to understand the llCSV2List command a bit better.

So the NPCKeyID would be something like:

integer NPCKeyID = (key)llList2String(params, 0);

and have it check what the local key of the root is and if it matched then do something like:

if (NPCKeyID == llGetKey())
{
llSay(0, "it has hit me and hit me for: " + (string)daggerDamage + " Damage";);
}


Something like that?

-Cherry Hotaling
Cherry Hotaling
Registered User
Join date: 25 Feb 2007
Posts: 86
01-10-2008 10:42
I ended up doing the following in my Enemy script:

integer AttackRoll = -5738; //Channel for attack Roll

key MyKey = "djalkdjf";

integer AC = 12;

default
{
state_entry()
{

// Set up listener for return message
llListen(AttackRoll, "", "", "";);
}

// touch_start(integer total_number)
// {
// integer d20 = llFloor(llFrand(21));
// llSay(0, (string)d20);
// }

listen(integer channel, string name, key id, string message)
{

list params = llCSV2List(message);
//in the real script replace MyKey with: llGetKey()
if (llGetListLength(params) < 2 || (key)llList2String(params, 0) != MyKey)
{
return;
}
integer daggerDamage = (integer)llList2String(params, 1);
key NPCKeyID = (key)llList2String(params, 0);

if (channel == AttackRoll && NPCKeyID == MyKey)
{
integer d20 = llFloor(llFrand(20)+1);
if (d20 >= AC)
{
llSay(0, "Attack Hit Because AC= " + (string)AC + " and d20 Rolled: " + (string)d20 + " Hit for: " + (string)daggerDamage + " Damage!";);
}else if (d20 <= AC)
{
llSay(0, "Attack Missed Because AC= " + (string)AC + " and d20 Rolled: " + (string)d20);
}
}
}


}

Im using the LSL Editor so I cant do a check for Key so I replaced llGetKey() with MyKey in my script so I could compile it.

Is the above too wordy to work efficiently as far as turning the key into a variable from the list: key NPCKeyID = (key)llList2String(params, 0);

Thank you for all your time this is helping me alot to get a grasp of how im going to implement this in the end.

-Cherry Hotaling
Hewee Zetkin
Registered User
Join date: 20 Jul 2006
Posts: 2,702
01-10-2008 11:04
Not too wordy, no (there's almost no such thing), but I think you're doing the work twice as you have it now. You've got the idea though. :)
Cherry Hotaling
Registered User
Join date: 25 Feb 2007
Posts: 86
01-10-2008 11:59
ahhhhhh!!!!

I get it, so I can actually remove this:
// if (llGetListLength(params) < 2 || (key)llList2String(params, 0) != MyKey)
// {
// return;
// }

since I put the if statement below it.

and
params, 0 is the first place before the comma and: params, 1 is the second place after the comma right!?

Also does the return; function help lower possible lagg if the event doesnt belong to that particular enemy? (For example 15 rats all listening and you target one)


Thank you this is awesome
-Cherry Hotaling
Hewee Zetkin
Registered User
Join date: 20 Jul 2006
Posts: 2,702
01-10-2008 15:26
You got it on the parameters. The return? Yeah, it might help reduce processing time a tad. But it is also clear that processing STOPS there, which I find to be useful from a maintenance/readability standpoint. Then again, there are those whose philosophy is that a block of code should have one and only one exit point. So to each his own.
Cherry Hotaling
Registered User
Join date: 25 Feb 2007
Posts: 86
01-10-2008 18:27
Okies I have a new question.

What if I want to send out a llSay message (which all scripts in a 20M Radius will hear)

Basically so that all enemies(Prims) return with their keys and names and a llDialog box opens with all creatures names in that 20M radius (using the listen not a sensor) when I select the name from the llDialog (For example RAT A)

it will pick and store the Key for that RAT A

I have seen this done with sensors but I wanted to use an llSay listen event to listen for all prims that call out their Keys and generate a list for llDialog.

Again I appreciate the help

-Cherry Hotaling
Hewee Zetkin
Registered User
Join date: 20 Jul 2006
Posts: 2,702
01-10-2008 21:51
This should be a piece of cake. Just decide on a common channel to use, and two message strings: one a query and one a response (make sure they're different strings, or use a different channel for the responses than for the query!). You could even just use the literal strings "query" and "response". The weapon or whatever sends out the query. The enemies listen for the query and send the response when they hear it. For each response, you get the key and name automatically from the system in the listen event. :-)
Cherry Hotaling
Registered User
Join date: 25 Feb 2007
Posts: 86
01-11-2008 06:00
there wouldnt happen to be an example of this using llDialog to show the response from a llSay query would there?

-Cherry
Cherry Hotaling
Registered User
Join date: 25 Feb 2007
Posts: 86
01-11-2008 22:47
there wouldnt happen to be an example of this using llDialog to show the response from a llSay query would there?

-Cherry
Hewee Zetkin
Registered User
Join date: 20 Jul 2006
Posts: 2,702
01-11-2008 23:57
I haven't compiled or tested this, but it should give you an idea of how to approach it.

Query script:
CODE

integer PING_CHANNEL = -893397710;
string PING_QUERY = "query";
string PING_RESPONSE = "response";
float PING_TIMEOUT_SECONDS = 0.4;
integer MAX_TARGETS = 12;

integer DIALOG_CHANNEL = -893397711;

list targetKeys = [];
list targetNames = [];
integer nTargets = 0;

giveDialog()
{
list choices = [];
integer i;
for (i = 0; i < nTargets; ++i)
{
string choice = (string)(i+1)+" "+llList2String(targetNames, i);
choices = (choices=[])+choices+choice;
}

llDialog(
llGetOwner(),
"Choose a target",
choices,
DIALOG_CHANNEL
);
}

state default
{
state_entry()
{
llListen(PING_CHANNEL, "", NULL_KEY, PING_RESPONSE);
llListen(DIALOG_CHANNEL, "", llGetOwner(), "");

llSay(PING_CHANNEL, PING_QUERY);
llSetTimerEvent(PING_TIMEOUT_SECONDS);
}

timer()
{
llSetTimerEvent(0.0);
giveDialog();
}

listen(integer channel, key id, string name, string message)
{
if (channel == PING_CHANNEL && message == PING_RESPONSE)
{
if (nTargets < MAX_TARGETS &&
llListFindList(targetKeys, [ id ]) < 0)
{
targetKeys = (targetKeys=[])+targetKeys+[ id ];
targetNames = (targetNames=[])+targetNames+[ name ];
++nTargets;

if (nTargets >= MAX_TARGETS)
{
llSetTimerEvent(0.0);
giveDialog();
}
}
} else if (channel == DIALOG_CHANNEL && id == llGetOwner())
{
integer spacePos = llGetSubStringIndex(message, " ");
if (spacePos <= 0)
{
return;
}

integer index = (integer)(llGetSubString(message, 0, spacePos-1))-1;
key targetKey = llList2Key(targetKeys, index);
key targetName = llList2Key(targetNames, index);

llSay("Chosen target: "+targetName+" ("+(string)targetKey+")");
}
}
}


Response script:
CODE

integer PING_CHANNEL = -893397710;
string PING_QUERY = "query";
string PING_RESPONSE = "response";
float PING_TIMEOUT_SECONDS = 0.4;
integer MAX_TARGETS = 12;

default
{
listen(integer channel, key id, string name, string message)
{
if (channel == PING_CHANNEL && message == PING_QUERY)
{
llSay(PING_CHANNEL, PING_RESPONSE);
}
}
}
Cherry Hotaling
Registered User
Join date: 25 Feb 2007
Posts: 86
01-12-2008 14:37
Hey hun It looks like im gettiing an error here only so far:

//integer spacePos = llGetSubStringIndex(message, " ";);

Did you mean something like this:

integer spacePos = llGetSubString(message, " ";);

Seems I dont understand what that line is for.

Thank you
-Cherry Hotaling
Cherry Hotaling
Registered User
Join date: 25 Feb 2007
Posts: 86
01-12-2008 16:02
So I changed what you gave me for the MOCK HUD to this:

integer PING_CHANNEL = -893397710;
string PING_QUERY = "query";
string PING_RESPONSE = "response";
float PING_TIMEOUT_SECONDS = 0.4;
integer MAX_TARGETS = 12;

integer DIALOG_CHANNEL = -893397711;

list targetKeys = [];
list targetNames = [];
integer nTargets = 0;

giveDialog()
{
list choices = [];
integer i;
for (i = 0; i < nTargets; ++i)
{
string choice = (string)(i+1)+" "+llList2String(targetNames, i);
choices = (choices=[])+choices+choice;
}

llDialog(
llGetOwner(),
"Choose a target",
choices,
DIALOG_CHANNEL
);
}

default
{
state_entry()
{
llListen(PING_CHANNEL, "", NULL_KEY, PING_RESPONSE);
llListen(DIALOG_CHANNEL, "", llGetOwner(), "";);


}

touch_start(integer total_number)
{
llSay(PING_CHANNEL, PING_QUERY);
llSetTimerEvent(PING_TIMEOUT_SECONDS);
}

timer()
{
llSetTimerEvent(0.0);
giveDialog();
}

listen(integer channel, string name, key id, string message)
{
if (channel == PING_CHANNEL && message == PING_RESPONSE)
{
if (nTargets < MAX_TARGETS &&
llListFindList(targetKeys, [ id ]) < 0)
{
targetKeys = (targetKeys=[])+targetKeys+[ id ];
targetNames = (targetNames=[])+targetNames+[ name ];
++nTargets;

if (nTargets >= MAX_TARGETS)
{
llSetTimerEvent(0.0);
giveDialog();
}
}
}
else if (channel == DIALOG_CHANNEL && id == llGetOwner())
{
string spacePos = llGetSubString(" ", 0, -1);
//integer spacePos = llGetSubStringIndex(message, " ";);
if ((integer)spacePos <= 0)
{

return;
}

integer index = (integer)(llGetSubString(message, 0, (integer)spacePos-1))-1;
key targetKey = llList2Key(targetKeys, index);
key targetName = llList2Key(targetNames, index);

llSay(0, "Chosen target: "+ (string)targetName + (string)targetKey);
}
}
}



And I changed the Responding NPC Prim to this:

integer PING_CHANNEL = -893397710;
string PING_QUERY = "query";
string PING_RESPONSE = "response";
float PING_TIMEOUT_SECONDS = 0.4;
integer MAX_TARGETS = 12;

default
{
listen(integer channel, string name, key id, string message)
{
if (channel == PING_CHANNEL && message == PING_QUERY)
{
llSay(PING_CHANNEL, PING_RESPONSE);
}
}
}


Seems like Its not working correctly.

Its just coming up with an OK dialog.

what would need to be fixed here?

thanks
-Cherry
Qie Niangao
Coin-operated
Join date: 24 May 2006
Posts: 7,138
01-12-2008 18:46
Try this in the Responding NPC thingie:

CODE

integer PING_CHANNEL = -893397710;
string PING_QUERY = "query";
string PING_RESPONSE = "response";

default
{
state_entry()
{
llListen(PING_CHANNEL, "", "", PING_QUERY);
}
listen(integer channel, string name, key id, string message)
{
llSay(PING_CHANNEL, PING_RESPONSE);
}
}
Cherry Hotaling
Registered User
Join date: 25 Feb 2007
Posts: 86
01-12-2008 18:54
Oh Awesome Qie Niangao,

I think I only have 3 more questions. How do I get it to give me a next feature if theres more then 12 objects.

And Second When I delete the objects and click on the Sender HUD prim it still has the objects that are no longer there in it. How would I set it up so it would remove the ones that no longer exist from its list? Would I put a simple reset after I pick the one I want from the list? I put a simple llResetScript in but is this the best method?

Third im not sure what the function of these lines are, as it prevents the llSay with them:
string spacePos = llGetSubString(" ", 0, -1);
if ((integer)spacePos <= 0)
{
return;
}

This is what I have so Far:

Main HUD Prim button to find objects Key ID that have the script in it:

integer PING_CHANNEL = -893397710;
string PING_QUERY = "query";
string PING_RESPONSE = "response";
float PING_TIMEOUT_SECONDS = 0.4;
integer MAX_TARGETS = 12;

integer DIALOG_CHANNEL = -893397711;

list targetKeys = [];
list targetNames = [];
integer nTargets = 0;

giveDialog()
{
list choices = [];
integer i;
for (i = 0; i < nTargets; ++i)
{
string choice = (string)(i+1)+" "+llList2String(targetNames, i);
choices = (choices=[])+choices+choice;
}

llDialog(
llGetOwner(),
"Choose a target",
choices,
DIALOG_CHANNEL
);
}

default
{
state_entry()
{
llListen(PING_CHANNEL, "", NULL_KEY, PING_RESPONSE);
llListen(DIALOG_CHANNEL, "", llGetOwner(), "";);


}

touch_start(integer total_number)
{
llSay(PING_CHANNEL, PING_QUERY);
llSetTimerEvent(PING_TIMEOUT_SECONDS);
}

timer()
{
llSetTimerEvent(0.0);
giveDialog();
}

listen(integer channel, string name, key id, string message)
{
if (channel == PING_CHANNEL && message == PING_RESPONSE)
{
if (nTargets < MAX_TARGETS &&
llListFindList(targetKeys, [ id ]) < 0)
{
targetKeys = (targetKeys=[])+targetKeys+[ id ];
targetNames = (targetNames=[])+targetNames+[ name ];
++nTargets;

if (nTargets >= MAX_TARGETS)
{
llSetTimerEvent(0.0);
giveDialog();
}
}
}
else if (channel == DIALOG_CHANNEL && id == llGetOwner())
{
string spacePos = llGetSubString(" ", 0, -1);
//integer spacePos = llGetSubStringIndex(message, " ";);
if ((integer)spacePos <= 0)
{

return;
}

integer index = (integer)(llGetSubString(message, 0, (integer)spacePos-1))-1;
key targetKey = llList2Key(targetKeys, index);
key targetName = llList2Key(targetNames, index);

llSay(0, "Chosen target: "+ (string)targetName + (string)targetKey);
}
}
}



NPC Object that Responds to the Say:

integer PING_CHANNEL = -893397710;
string PING_QUERY = "query";
string PING_RESPONSE = "response";

default
{
state_entry()
{
llListen(PING_CHANNEL, "", "", PING_QUERY);
}
listen(integer channel, string name, key id, string message)
{
llSay(PING_CHANNEL, PING_RESPONSE);
}
}


Thanks again

-Cherry Hotaling
Qie Niangao
Coin-operated
Join date: 24 May 2006
Posts: 7,138
01-12-2008 19:07
From: Cherry Hotaling
How do I get it to give me a next feature if theres more then 12 objects.
Well, it's messy just because llDialog can only handle 12 buttons at a time... so if you have more than 12 objects, then you have to sacrifice at least one of those buttons for a "Next"--and usually two, for a "Back", too... and then add a bunch of code to page around the dialog lists. (Alicia had a recent post with some code that does all that.)
From: someone
When I delete the objects and click on the Sender HUD prim it still has the objects that are no longer there in it. How would I set it up so it would remove the ones that no longer exist from its list?
Right. I think you could get by with just setting nTargets to 0 again at the top of touch_start.
Cherry Hotaling
Registered User
Join date: 25 Feb 2007
Posts: 86
01-12-2008 19:18
ahhh okies great,

I will have to figure out how to Merge Alicia's post here: /54/e7/234124/1.html

With what we have here allready.

Thank you if I get stuck I will let you know.

-Cherry
1 2