Okay, now that we know how to add a simple condition, let's look at the differences in adding an action!
First of all, you won't be adding your command constant to the "AICOND" list... you'll want to add your action to the "AIACT" list, which can easily be found by doing a search for "AIACTLAST".
Since I see an interest in it, let's add an action called "settargetreticle". It's a bit more complex and plus we'll have the experience of seeing HOW targets are set for entities in the source!
Now, find the end of the action constant list and add your constant in there. It should look a bit like this:
Quote: "#constant AIACTSETTARGETRETICLE 1349"
Though, you may have a different number than I do. Since I add tons of commands, I have often found myself needing to increase the number given to the "LAST" constant so as to enlarge my range of free numbers to use for commands.
After this, we of course need to add in our command string identifier. Hit F3 if you already did a search for "AIACTLAST" and it should bring you to the top of the string list for actions. Here we can see that they are all stored into the array named "actword$" in a similar way to the condition strings.
Find the bottom of the list and add your string in. It will look like this:
Quote: "actword$(AIACTSETTARGETRETICLE)="settargetreticle"
"
Remember to type everything correctly. Just a single mistype can make everything go down the tube very quickly.
Alright, now for the fun part... CODING!!!
Find one of the other actions near the bottom of the list and do a search for their constant until you reach the "coding area".
Let's make some room underneath the other actions and set up our case block:
case AIACTSETTARGETRETICLE:
endcase
Awesome, now let us sit and think about how we're gonna do this.
The command we're putting in needs to do the following:
- Set the entity's target to whatever dynamic entity the player is looking at.
Sounds simple enough... but how would we program a method of doing that? How can we just "pick" the object in the middle of the screen like that?
Wait a second... DBO has a command to do that! In fact... it's called "pick object"! How convenient.
Now, this command requires 4 parameters. The first two are the X,Y coordinates of where on the screen you are wanting to look for an object. Since we want the center of the screen, we can use a few other DBP commands to get that. We'll just grab the screen's width and height and divide them by 2.
The last two parameters make up the "object list", or the range of object numbers that DBP will be looking through for a match. This means that so long as we don't tell it to look for the objects that make up segments, then it won't find segment walls and all that. And that's good! We just want to find entities, because they are the viable targets for this command.
And finally, this command returns the number of the object that it found.
But... what range of objects do we give it? Well we want to start at the beginning of all the entities and stop at the end of all the entities.
Hmmmm... well after a bit of searching around, I found that FPSC has two variables that fit our needs just nicely. The beginning of the entity object list is stored in a variable called "entityobjectoffset", and the LENGTH of our entity list is stored in a variable called "entityelementlist". From experience and because I saw it in another portion of code, I know for certain that the entity list ACTUALLY starts at "entityobjectoffset+1". And to find the end of our list, we can simply add the length of our list onto the beginning of our list, so we get "entityobjectoffset+entityelementlist".
Come up with some sort of variable to stick our return value in and we can come up with something like the following line of code:
tee=pick object(screen width()/2,screen height()/2,entityobjectoffset+1,entityobjectoffset+entityelementlist)
I chose "tee" because I abbreviate lots of things and this would stand for "temporary entity element".
You can pick your own, just so long as you stick with it.
Alright, so now we know the number of the object we are looking at... sort of. Now how do we target that object?
Hmmm... I don't think we can target it just yet! We don't even know which entity it belongs to!! So that would be our next step. To find out which entity the object belongs to and then THAT entity will be our target.
Now... since our "pick object" command won't ALWAYS come up with a number, we need to make sure that it has found something worth looking for. So we need to put a little check into our code:
tee=pick object(screen width()/2,screen height()/2,entityobjectoffset+1,entityobjectoffset+entityelementlist)
if tee<>0
endif
There, that oughta do it. Okay! So if we have found an object, then we know that we are looking at an entity. And if we know we are looking at an entity then we know that that entity must be somewhere inside our array of entity's.
Hoo boy! This is gonna take a little bit of explaining.
All entity information is stored in many arrays, not just one... but LOTS of the entity's information is stored in a single array, and that array is called "entityelement()".
Since I don't want to lengthen this post any more than it needs to be, I'm going to tell you how to find things yourself from now on.
Since we know it's an array, we know it needs to be "dimmed" first. So let's do a search in the source for:
dim entityelement
This will bring you to where the array is declared. You should see something like this:
Quote: "dim entityelement(entityelementmax) as entitytype"
Okay, this line tells us a few things. But first and foremost, we know that the array is a UDT array... and that UDT is named "entitytype"... so let's do a search for:
type entitytype
This will bring us to the beginning of the "entitytype" declaration. And it will look something like this... and it's big:
type entitytype
editorfixed as integer
servercontrolled as integer
fakeplrindex as integer
active as integer
dormant as integer
eleprof as entityeleproftype
mover as movertype
ai as aistatustype
spawn as spawntype
force as forcetype
maintype as integer
bankindex as integer
profileobj as integer
staticflag as integer
obj as integer
attachmentobj as integer
attachmentbaseobj as integer
attachmentweapontype as integer
attachmentobjfirespotlimb as integer
attachmentblobobj as integer
x as float
y as float
z as float
rx as float
ry as float
rz as float
norotate as integer
nogravity as integer
dry as float
floorposy as float
colr as integer
colg as integer
colb as integer
limbslerp as integer
logiccount as float
logiccountburst as integer
logictimestamp as DWORD
priorityai as integer
priorityduration as integer
raycastcount as integer
raycastlaststate as integer
nofloorlogic as integer
crouchprofile as integer
plrdist as float
decalindex as integer
decalmode as integer
decalloop as integer
decalslotused as integer
decalsizex as float
decalsizey as float
animset as integer
animdir as integer
animdo as integer
animtime as integer
animframe as float
animspeed as float
animonce as integer
destanimframe as float
animframeupdate as integer
spinrate as integer
spinvalue as float
floatrate as integer
floatvalue as float
possibletarget as integer
actualtarget as integer
losttargetcount as integer
actualtargetx as float
actualtargety as float
actualtargetz as float
plrtrailindex as integer
fakeplayerid as integer
strafemode as integer
currentweapon as integer
currentclipammo as integer
currentammo as integer
fireweapon as integer
firestrength as integer
firesoundloop as integer
firesoundloopremote as integer
firesoundlooptime as DWORD
health as integer
lifecode as integer
beenkilled as integer
shotdamage as integer
shotdamagesource as integer
delaydamagecount as integer
delaydamagesource as integer
delaydamage as integer
delayimpact as integer
delaydamagex# as float
delaydamagey# as float
delaydamagez# as float
delaydir as integer
collected as integer
activated as integer
collisionactive as integer
invincibleactive as integer
blockedby as integer
blockedtox as integer
blockedtoy as integer
blockedtoz as integer
soundset as integer
soundset1 as integer
soundlooping as integer
endtype
Wow... what a doozy... BUT!! This tells us what we need to know.. which is pretty much everything there IS to know about an entity.
Looking at it you may also notice that many parts of this UDT are UDTs themselves!!!
That just makes it much more complex. And sometimes that's a good thing, lol.
Now, what are we looking for in this mess? We're looking for any kind of information in this UDT that may help us determine which entity is linked to which object number.
You can look in there yourself for the answer, but for the sake of moving on, I'll just tell you that there's a variable in the UDT called "obj"... which, after a while of DBP programming you'll learn, is short for "object". Awesome! Now we know how to find which object each entity is linked to!
Okay, so all we gotta do is do a systematic search through this array, from beginning to end, until we find the entity that our object belongs to!
We can set up a for loop to do this. Our loop should go from the beginning of the array to the end. The beginning of the array is, in this case, 1 and the end of the array is going to be "entityelementlist".
Give the loop a variable and we have:
tee=pick object(screen width()/2,screen height()/2,entityobjectoffset+1,entityobjectoffset+entityelementlist)
if tee<>0
for te=1 to entityelementlist
next te
endif
Okay, now for each and every entity we need to cross-check their object number with the one we are looking for... and if they match, then we know we have the right entity!!!
Oh... while we're doing a check, we might also want to ONLY include "active" entities... which will keep us from picking up any static objects.
So, now we have the following:
tee=pick object(screen width()/2,screen height()/2,entityobjectoffset+1,entityobjectoffset+entityelementlist)
if tee<>0
for te=1 to entityelementlist
if entityelement(te).active=1 and entityelement(te).obj=tee
endif
next te
endif
Alright... now if those statements are TRUE, then we need to TARGET that entity!
Okay, for this we may need a bit of help. Let us take a look at one of the other targetting actions and see how it's done, shall we?
AIACTSETTARGETNAME:
case AIACTSETTARGETNAME:
tstrindex=aiactionseq(seq).value
tname$=actstring$(tstrindex)
gosub _entity_findname
if foundte>0
entityelement(e).actualtarget=1+foundte
entityelement(e).possibletarget=0
if entityelement(e).actualtarget>1
tte=entityelement(e).actualtarget-1
entityelement(e).actualtargetx=entityelement(tte).x
entityelement(e).actualtargety=entityelement(tte).y
entityelement(e).actualtargetz=entityelement(tte).z
entityelement(e).losttargetcount=0
endif
endif
endcase
I chose this specific action because it is the easiest to assimilate.
Now, before you start wondering "What is it doing in the "_entity_findname" subroutine... I'll tell you. It simply cross-checks all the entitys' names to that of the name it is looking for and stores the found entities ID into the variable "foundte".
Alright, let's copy everything inside of the "if foundte>0" block and paste it into our block. This is the code that we need.
Your code should now look a bit like this:
tee=pick object(screen width()/2,screen height()/2,entityobjectoffset+1,entityobjectoffset+entityelementlist)
if tee<>0
for te=1 to entityelementlist
if entityelement(te).active=1 and entityelement(te).obj=tee
entityelement(e).actualtarget=1+te
entityelement(e).possibletarget=0
if entityelement(e).actualtarget>1
tte=entityelement(e).actualtarget-1
entityelement(e).actualtargetx=entityelement(tte).x
entityelement(e).actualtargety=entityelement(tte).y
entityelement(e).actualtargetz=entityelement(tte).z
entityelement(e).losttargetcount=0
endif
exit
endif
next te
endif
Of course, we don't HAVE a "foundte" variable, so change that part to "te". And to save on processing time, I added a "exit" after we set the target. This command will jump out of the for loop without letting it continue to look for more amtches (which we know there is only ONE match).
Annnnnnd.... that's it!!
Compile the code and have fun with your newly coded action!
For completeness, you will note that the action we added this time did not require any parameters in the script. If you were to code one that did have parameters then you will need to know the following:
Action parameters are stored in the array "aiactionseq" at the array location "seq", so it will be "aiactionseq(seq)".
THIS array's UDT is different than the condition array's UDT, let's look at it:
Quote: "type aiactiontype
type as integer
value as float
filename as string
endtype"
This looks a little similar but a few differences... for starters, unless you do as I did and CHANGE everything there is about an action so that it can accept up to 3 parameters like a condition, you are stuck with only a SINGLE parameter. And that parameter is stored in "value".... that is, if it's numeric.
If the parameter is NOT numeric (Ex. sound=audiobank/blah/blah.wav) then the parameter is stored into "filename". It doesn't actually HAVE to be a filename, that's just what Lee called the variable because he had it planned only for filenames.
And THAT concludes our broadcast.
The one and only,
Whosoever says, "Don't sweat the small stuff," is obviously not a programmer.