Jump to content

Auto Turret with llCastRay();


EniarTenokei
 Share

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

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

Recommended Posts

llCastRay() returns a filtered list of keys and positions along a ray between start and end vectors.

 

Depends on if you want to use it for the tracking or for the shooting.

I don't think it would be particularly efficient for any tracking tasks.

It can be useful when simulating piercing projectiles from a turret and using the returned data to report hits to some other game object. 

Any other auto turret tasks are better realized with physical projectiles, llGetObjectDetails() or regular sensor calls. 

Link to comment
Share on other sites

You may be able use llCastRay() to check if your turret's firing solution will hit your ship and hold fire for that cycle.

Feed your turret's emitter location and your firing target's location into llCastRay() with the appropriate filters and test the first returned key against the key of your ship. 

If you're using llSensor() to get your target's location, you can limit the detection area to exclude your ship.

I'm not sure how useful this is nowadays, but you can also precalculate a volume that contains your ship then test it against your turret's local rotation. Basically does a cast ray the hard way without having to poll the sim. Came in handy when llCastRay()s were unavailable or failed more frequently.

Link to comment
Share on other sites

The turrets I use know their working angle. But if you have a complicated shape or wings there may be a problem to implement limits.

You can "shoot" with castray 1st and see if it hits the target or something else (like your ship). With that info you can decide to follow up with a "bullet" or wait for the next round.

But !

What is your target?

An avatar? In that case you will never hit the target but only the ship the avatar is sitting in.
In that case you can not test for a target hit but check the distance of the castray hit and the name of the hit.
If it's your ship's name then don't shoot.
if it's approximately the distance of your target then shoot.
if it's another distance - your decision - I prefer to shoot in doubt though :D

Oh ... another dirty trick is to fire the bullets as physical+phantom. They will flip to physical+non phantom after a short traveltime - preferably after they have minimal distance to your ship. Just saying. :)

 

 

Edited by Nova Convair
Link to comment
Share on other sites

Since I don't know the exact scenario (teams, ship naming conventions, etc.) or how the ship/turret is built, here are some basic examples of how you might want to use llCastRay in your shooting function. (Note: Not ALL at once. Pick what's best for you or add some other check that would be more accurate.)

default
{
    touch_start(integer n)
    {
        // [key, vector]
        list hit = llCastRay(turret_pos, target_pos, []);

        if( llKey2Name(llList2Key(hit, 0)) == "My ship's name" )
        {
            return; // Exit the current function/event to stop shooting.
        }

        if( llGetObjectDetails(llList2Key(hit, 0), [OBJECT_GROUP]) == group_key_to_ignore )
        {
            return; // Exit the current function/event to stop shooting.
        }

        if( llGetOwnerKey(llList2Key(hit, 0)) == llGetOwner() )
        {
            return; // Exit the current function/event to stop shooting.
        }

        // Shoot her!
    }
}

You could also add RC_GET_ROOT_KEY to the llCastRay list, that way if your ray might hit a seated avatar (if the ships were open enough for that), you could use the root key for all checks instead.

Edited by Wulfie Reanimator
Link to comment
Share on other sites

8 hours ago, Wulfie Reanimator said:

Since I don't know the exact scenario (teams, ship naming conventions, etc.) or how the ship/turret is built, here are some basic examples of how you might want to use llCastRay in your shooting function. (Note: Not ALL at once. Pick what's best for you or add some other check that would be more accurate.)


default
{
    touch_start(integer n)
    {
        // [key, vector]
        list hit = llCastRay(turret_pos, target_pos, []);

        if( llKey2Name(llList2Key(hit, 0)) == "My ship's name" )
        {
            return; // Exit the current function/event to stop shooting.
        }

        if( llGetObjectDetails(llList2Key(hit, 0), [OBJECT_GROUP]) == group_key_to_ignore )
        {
            return; // Exit the current function/event to stop shooting.
        }

        if( llGetOwnerKey(llList2Key(hit, 0)) == llGetOwner() )
        {
            return; // Exit the current function/event to stop shooting.
        }

        // Shoot her!
    }
}

You could also add RC_GET_ROOT_KEY to the llCastRay list, that way if your ray might hit a seated avatar (if the ships were open enough for that), you could use the root key for all checks instead.

May you show me an example of a simple auto turret script using this method because I can't find one to learn from? I have my script, but I have no idea of how to implement the llCastRay(); function like your example into mine. My Script uses a dialog to search an avatar, select the avatar, and then fire at the target. Here is my script: 

 

//menu listener
integer listener;
integer sensorChannel;
 
// sensor 
list avatarsKeys;
list avatarsNames;
list buttons = ["Cease"];

 
menu(key user,integer channel,string title,list buttons)
 
{
    listener = llListen(channel,"","","");
    llDialog(user,title,buttons,channel);
    //remove listener if there's no activity in menu
    llSetTimerEvent(20.0);
}
 
integer randomNumber()
{
    return (integer)(llFrand(99999.0) * -1);
}
 
default
{
    touch_start(integer total_number)
    {
        //only owner can access the menu
        if (llDetectedKey(0) == llGetOwner())
        {
            llSensor("","",AGENT|PASSIVE,96,PI);
        }
    }
    sensor(integer total_number)
    {
        integer i;
        key tempId;
        avatarsKeys = [];
        avatarsNames = [];
        i = 0;
        while ((i < total_number) && (i < 12))
        {
            tempId = llDetectedKey(i);
            avatarsKeys = avatarsKeys + tempId;
            avatarsNames = avatarsNames + llKey2Name(tempId);
            i = i+1;
        }
 
        sensorChannel = randomNumber();
        menu(llGetOwner(),sensorChannel,"Select an avatar...",avatarsNames);
    }
 
    listen(integer channel,string name,key id,string message)
    {
        if (channel == sensorChannel)
        {
            integer pos = llListFindList(avatarsNames,[message]);
  integer index = llListFindList(avatarsNames,[message]);
integer i;
           if(index != -1)
           for (i = 0; i < 1000; ++i)
            {
key kTarget = llList2Key(avatarsKeys,index);
vector vTargetPos = llList2Vector(llGetObjectDetails(kTarget,[OBJECT_POS]),0);
vector vPos=llGetPos();
float fDistance=llVecDist(<vTargetPos.x,vTargetPos.y,0>,<vPos.x,vPos.y,0>);

     llLookAt(vPos + (vTargetPos + <0.0, 0.0, 0.0> - vPos) / llGetRootRotation(), 1.0, 0);

llRezObject("bullet", llGetPos()+<0,0,3>*llGetRot(),<0,0,20>*llGetRot(),llGetRot(),0);
 }}}
    
    
    
    timer()
    {
        llListenRemove(listener);
        llSetTimerEvent(0.0);
    } 
   } 
 
 

Link to comment
Share on other sites

2 hours ago, EniarTenokei said:

May you show me an example of a simple auto turret script using this method because I can't find one to learn from? I have my script, but I have no idea of how to implement the llCastRay(); function like your example into mine. My Script uses a dialog to search an avatar, select the avatar, and then fire at the target. Here is my script:

Couple things about your script, first:

  • In the listen event
    • pos is unused, and the same as index. Remove it.
    • The loop rezzes 1000 objects before it stops? Is that.. reasonable?

Beyond that, I would suggest putting the raycast checks in to your listen event. Maybe before the loop starts at all, or in the loop before each rez.

list hit = llCastRay(vPos, vTargetPos, []);
if( llKey2Name(llList2Key(hit, 0)) != "My ship's name" )
{
    llRezObject("bullet", llGetPos()+<0,0,3>*llGetRot(),<0,0,20>*llGetRot(),llGetRot(),0);
}

 

Link to comment
Share on other sites

34 minutes ago, Rolig Loon said:

Take a serious look at the response I gave in your previous thread. I agree with Wulfie that you need to think very carefully about rezzing such a large number of bullets in a burst like that.

I want to use the llDialog(); function for this turret to select the avatar to fire at and I think it's a good idea to use a separate button to fire. May you show me an example of the timer event and maybe a completely new way of rezzing the bullet? Thank you :).

Edited by EniarTenokei
Link to comment
Share on other sites

touch_start (integer num)
{
   llSetTimerEvent( 0.1 * (iFire -= !iFire) );    // Where you have defined iFire as a global integer variable
}

timer()
{
    // Fire one bullet here, doing whatever you need to calculate vTargetPos and rotate your turret before rezzing a bullet.
}   

So your touch_start event acts a s a simple toggle switch.  One touch starts firing, the next one stops it.  You ought to make it respond only to touches on your "Fire" button, but that's the general idea.

Edited by Rolig Loon
Link to comment
Share on other sites

I added a timer to the llRezObject(); function but it still doesn't work, now how do I get the llLookAt(); function and llRezObject(); to work on a timer? 

Here is my script:

//menu listener
integer listener;
integer sensorChannel;
 
// sensor 
list avatarsKeys;
list avatarsNames;
list buttons = ["Cease"];

 
menu(key user,integer channel,string title,list buttons)
 
{
    listener = llListen(channel,"","","");
    llDialog(user,title,buttons,channel);
    //remove listener if there's no activity in menu
    llSetTimerEvent(20.0);
}
 
integer randomNumber()
{
    return (integer)(llFrand(99999.0) * -1);
}
 
default
{
    touch_start(integer total_number)
    {
   
        //only owner can access the menu
        if (llDetectedKey(0) == llGetOwner())
        {
            llSensor("","",AGENT,96,PI);
        }
 llSetTimerEvent( 0.1 * (randomNumber()) );  
  }

timer()
{
  llRezObject("bullet", llGetPos()+<0,0,3>*llGetRot(),<0,0,20>*llGetRot(),llGetRot(),0); 
} 
    
    sensor(integer total_number)
    {
        integer i;
        key tempId;
        avatarsKeys = [];
        avatarsNames = [];
        i = 0;
        while ((i < total_number) && (i < 12))
        {
            tempId = llDetectedKey(i);
            avatarsKeys = avatarsKeys + tempId;
            avatarsNames = avatarsNames + llKey2Name(tempId);
            i = i+1;
        }
 
        sensorChannel = randomNumber();
        menu(llGetOwner(),sensorChannel,"Select an avatar...",avatarsNames);
    }
 
    listen(integer channel,string name,key id,string message)
    {
        if (channel == sensorChannel)
        {
            integer pos = llListFindList(avatarsNames,[message]);
  integer index = llListFindList(avatarsNames,[message]);
integer i;
           if(index != -1)
          
            {
key kTarget = llList2Key(avatarsKeys,index);
vector vTargetPos = llList2Vector(llGetObjectDetails(kTarget,[OBJECT_POS]),0);
vector vPos=llGetPos();
float fDistance=llVecDist(<vTargetPos.x,vTargetPos.y,0>,<vPos.x,vPos.y,0>);

     llLookAt(vPos + (vTargetPos + <0.0, 0.0, 0.0> - vPos) / llGetRootRotation(), 1.0, 0);

 }
 }
 } 

Edited by EniarTenokei
Link to comment
Share on other sites

I won't write the script for you, because that's your job.  However, you have all of the pieces you need.  The only thing that you need the dialog menu for is to identify kTarget.  Once that's done, all of the business of tracking kTarget and firing bullets is a separate matter.  That's what you have the timer event for. So, take the targeting code out of the listen event and move it to the timer event
 

timer()
{
    vector vTargetPos = llList2Vector(llGetObjectDetails(kTarget,[OBJECT_POS]),0);
    vector vPos=llGetPos();
    float fDistance=llVecDist(<vTargetPos.x,vTargetPos.y,0>,<vPos.x,vPos.y,0>);
    llLookAt(vPos + (vTargetPos + <0.0, 0.0, 0.0> - vPos) / llGetRootRotation(), 1.0, 0);
    llRezObject("bullet", llGetPos()+<0,0,3>*llGetRot(),<0,0,20>*llGetRot(),llGetRot(),0); 
} 

The one thing you need to change is to make kTarget a global key variable instead of leaving it local to the listen event.  So, declare it at the top of the script and be sure not to redefine it in the listen event.  Just write it there as

     kTarget = llList2Key(avatarsKeys,index);

Then, go back and look at that touch_start event again.  Your statement

 llSetTimerEvent( 0.1 * (randomNumber()) );

will not work.  In fact, it doesn't make any sense, because it is no longer a simple toggle switch (or a switch of any kind).

Link to comment
Share on other sites

3 hours ago, EniarTenokei said:

When y'all first started learning to script, how did the process go if you don't mind me asking?

Two years of banging my head against a wall until either the wall or my head (haven't decided) broke, and LSL finally started to click.

Seriously though, one thing I don't think is going to help with learning is always asking for "a (working) example," because probably more often than not you'll skim through it, see if it works, and either start using it as-is or say that it doesn't work.

If you don't know where to put a given snippet of code, you don't yet have the necessary fundamental knowledge of how the system works, so most ready-made examples won't teach you what you actually need to learn.

Most importantly you need to understand events in LSL. Then you need to understand all the general programming basics, especially how loops work in this case. After that you are fully equipped to just practice your "logical thinking" (what should happen in order), and find out how to achieve those little steps. Over time you'll start memorizing how to do those little steps and you'll get faster and able to write more efficient steps.

Edited by Wulfie Reanimator
  • Like 3
Link to comment
Share on other sites

1 hour ago, EniarTenokei said:

When y'all first started learning to script, how did the process go if you don't mind me asking?

Slowly, in small steps.  The big problem with trying to learn by rewriting someone else's script is that until you understand enough logic to see how it works, you can't even tell whether it's worth rewriting.  It can get very frustrating.  Keep banging on it, though. Mistakes are great teachers.  Also, with a script that requires as much modification as this one, don't expect everything to work at once. No matter how experienced you are, it usually takes a lot of tweaking to get it all right.

Link to comment
Share on other sites

Where can I get the fundamental Knowledge for a script like this one? I know I can't get everything from a premade example but at the same time, I don't think there is enough documentation in the Wiki on this particular topic. It isn't something I can just search and boom, there's the answer. I've been piecing this script together for a while now and still can't get it to use the global variable and the llSetTimer(); switch, I still don't understand that 100% yet. And then there's the If Message for the Listen function that I have no idea about. I'm not asking anyone to write the script for me, I'm just looking for the rights steps to follow which probably won't clink until hours of studying.

 

integer kTarget = llList2Key(avatarsKeys,index);
//menu listener
integer listener;
integer sensorChannel;
 
// sensor 
list avatarsKeys;
list avatarsNames;


menu(key user,integer channel,string title,list buttons)
 
{
    listener = llListen(channel,"","","");
    llDialog(user,title,buttons,channel);
    //remove listener if there's no activity in menu
    llSetTimerEvent(20.0);
}
 
integer randomNumber()
{
    return (integer)(llFrand(99999.0) * -1);
}

 
default
{
    touch_start(integer total_number)
    {
   
        //only owner can access the menu
        if (llDetectedKey(0) == llGetOwner())
        { 
            llSensor("","",AGENT,96,PI);
        }
 llSetTimerEvent( 0.1 * (0) );  
  }


    
    sensor(integer total_number)
    {
        integer i;
        key tempId;
        avatarsKeys = [];
        avatarsNames = []; 
        i = 0;
        while ((i < total_number) && (i < 12))
        {
            tempId = llDetectedKey(i);
            avatarsKeys = avatarsKeys + tempId;
            avatarsNames = avatarsNames + llKey2Name(tempId);
            i = i+1;
        }
 
        sensorChannel = randomNumber();
        menu(llGetOwner(),sensorChannel,"Select an avatar...",avatarsNames);
    }
 
    listen(integer channel,string name,key id,string message)
    {
        if (channel == sensorChannel)
        {
           
          
            


    

 
 }}timer()
{
vector vTargetPos = llList2Vector(llGetObjectDetails(kTarget,[OBJECT_POS]),0);
vector vPos=llGetPos();
float fDistance=llVecDist(<vTargetPos.x,vTargetPos.y,0>,<vPos.x,vPos.y,0>);
llLookAt(vPos + (vTargetPos + <0.0, 0.0, 0.0> - vPos) / llGetRootRotation(), 1.0, 0);
llRezObject("bullet", llGetPos()+<0,0,3>*llGetRot(),<0,0,20>*llGetRot(),llGetRot(),0);      
} 
 } 

Link to comment
Share on other sites

15 minutes ago, Love Zhaoying said:

Can you initialize a global like this (with a function call), outside a function?

I don''t think its possible to do that unless I'm missing fundamental knowledge here, I was trying to follow Rolig Loon on that by trying integer, key, list in front of kTarget, but I couldn't get it to work. The Editor didn't understand this part:  = llList2Key(avatarsKeys,index);. I also tried  kTarget = llList2Key(avatarsKeys,index); by itself, but that didn't work either. I have no idea what I'm missing here. The only version of this script that has worked so far is this one:

//menu listener
integer listener;
integer sensorChannel;
 
// sensor 
list avatarsKeys;
list avatarsNames;

 
menu(key user,integer channel,string title,list buttons)
 
{
    listener = llListen(channel,"","","");
    llDialog(user,title,buttons,channel);
    //remove listener if there's no activity in menu
    llSetTimerEvent(20.0);
}
 
integer randomNumber()
{
    return (integer)(llFrand(99999.0) * -1);
}
 
default
{
    touch_start(integer total_number)
    {
        //only owner can access the menu
        if (llDetectedKey(0) == llGetOwner())
        {
            llSensor("","",AGENT|PASSIVE,96,PI);
        }
    }
    sensor(integer total_number)
    {
        integer i;
        key tempId;
        avatarsKeys = [];
        avatarsNames = [];
        i = 0;
        while ((i < total_number) && (i < 12))
        {
            tempId = llDetectedKey(i);
            avatarsKeys = avatarsKeys + tempId;
            avatarsNames = avatarsNames + llKey2Name(tempId);
            i = i+1;
        }
 
        sensorChannel = randomNumber();
        menu(llGetOwner(),sensorChannel,"Select an avatar...",avatarsNames);
    }
 
    listen(integer channel,string name,key id,string message)
    {
        if (channel == sensorChannel)
        {
            integer pos = llListFindList(avatarsNames,[message]);
  integer index = llListFindList(avatarsNames,[message]);
integer i;
           if(index != -1)
           for (i = 0; i < 1000; ++i)
            {
key kTarget = llList2Key(avatarsKeys,index);
vector vTargetPos = llList2Vector(llGetObjectDetails(kTarget,[OBJECT_POS]),0);
vector vPos=llGetPos();
float fDistance=llVecDist(<vTargetPos.x,vTargetPos.y,0>,<vPos.x,vPos.y,0>);

     llLookAt(vPos + (vTargetPos + <0.0, 0.0, 0.0> - vPos) / llGetRootRotation(), 1.0, 0);

llRezObject("bullet", llGetPos()+<0,0,3>*llGetRot(),<0,0,20>*llGetRot(),llGetRot(),0);
 }}}
    
    
    
    timer()
    {
        llListenRemove(listener);
        llSetTimerEvent(0.0);
    } 
   } 
 
 

Edited by EniarTenokei
Link to comment
Share on other sites

3 minutes ago, Love Zhaoying said:

Right - so, my point is, you can only assign variables to constants when declaring a global variable. You need to make that llList2Key() call inside of a user function or event.

*Could be wrong*

In this premade example, llList2Key is inside a function here, but I've never seen it like: integer kTarget = llList2Key(avatarsKeys,index);. So I have no idea about it.

// Best viewed in Chat History (ctrl-h)
default
{
    state_entry()
    {
        list my_list = ["a", 1, 2.0, <1,2,3>, <1,2,3,4>, llGetOwner()];
        integer i;
        for (i=0;i<llGetListLength(my_list); ++i)
        {
            llOwnerSay("string=" + llList2String(my_list,i)
                        + "\n   integer=" + (string)llList2Integer(my_list,i)
                        + "\n   float=" + (string)llList2Float(my_list,i)
                        + "\n   vector=" + (string)llList2Vector(my_list,i)
                        + "\n   rot=" + (string)llList2Rot(my_list,i)
                        + "\n   key=" + (string)llList2Key(my_list,i) );
        }
    }
}
Link to comment
Share on other sites

Yeah.  It's getting worse.  You are trying to fix too many things at once, and are creating new problems.  There are now several things to work on, but the very first one is understanding basic concepts of the structure and flow in LSL scripts.  I suggest working carefully through more than one tutorial.  Here are the issues you still have to deal with:

1. Creating a global variable.  This means understanding the concept of scope and the state/event structure of LSL.

2. Creating a switch.  I provided an example of a simple toggle switch that changes state between ON and OFF each time it's clicked. Somehow, that got lost in what you are doing now.

3. Getting the touch_start event to recognize more than one type of touch. That means telling it to run the sensor, collect target names, and build a dialog when you click on one button and then firing the toggle switch when you click a different button.  We haven't even gotten there yet.

Scripting is all about logic.  Unfortunately, when you are just starting, logic is hard to see through the unfamiliar commands and syntax.  Until you can back off and focus on logic, though, you will get mired in details and be frustrated.  It happens to all of us.  The solution is to work slowly and patiently, and to work on small things, one at a time.  And bang your head on the wall, like Wulfie.

  • Like 1
Link to comment
Share on other sites

When I started scripting - many years ago - I wouldn't have been able to make a turret system - even with a background in programming.
I made smaller tasks 1st and most gain of knowledge started with the question: "why this damn thing doesn't work?"
But thats about one thing not working and not 10 at the same time - that leads to nothing.

The op needs to understand how SL scripts work at 1st. Without that basic understandment there will be no chance to solve scripting tasks and problems.
There are a few scripting classes out there. Maybe take a few and script some more simple things for a start.

If you want turrets NOW you need to hire someone.

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

You are about to reply to a thread that has been inactive for 2060 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...