Sorry your browser is not supported!

You are using an outdated browser that does not support modern web technologies, in order to use this site please update to a new browser.

Browsers supported include Chrome, FireFox, Safari, Opera, Internet Explorer 10+ or Microsoft Edge.

FPSC Classic Scripts / How to make your own FPI commands (A Mod Tutorial)

Author
Message
Plystire
21
Years of Service
User Offline
Joined: 18th Feb 2003
Location: Staring into the digital ether
Posted: 16th Aug 2008 05:13 Edited at: 17th Aug 2008 00:51
To anyone who has ever wanted to make their own FPI commands, this tutorial is for you.

First of all, a list of some things you'll need:

1) DarkBASIC Professional
2) DarkLIGHTS
3) The FPSC Source Code
4) A properly configured DBP Compiler folder to compile the source
5) A bit of programming know-how


#4 will be the most difficult to get straight. Some DLLs naturally conflict with each other. Here's a bit of insight into my own FPSC compiling setup:

plugins-licensed:
EnhancementsFREE.dll
LightMapper.dll

plugins-user:
DBProMultiplayerPlusDebug.dll
DBProODEDebug.dll

Those are the only ADDITIONAL dlls you should need in order to compile the V1.07 FPSC Source


I will not be covering or taking any questions regarding problems compiling. If you have a problem compiling, this is not the place to ask about it.


Okay... back to business.


Fire up DBP and open the Source Code.

!!!WARNING!!!
Do not lose yourself in how much code there is. Most of it does not NEED to be touched... in fact, very few portions of the code you should even CONSIDER modifying. For this tutorial, just follow along and don't add anything else, or the compile may fail, leaving you wondering what went wrong.


For learning purposes we're going to add a simple CONDITION to the source. I have named it simply "mouseclick".

Now, let us do a search through the source for the sections of code we are interested in altering.

So, hit Ctrl+F and type in the following:

AICONDLAST

Search for that and it will bring you to a list of constant keywords.

After a bit of staring, you may have noticed that all of these keywords resemble scripting commands! Yes, this is the first stop to making a script command.

The line that your search brought you to should look like this:
Quote: "#constant AICONDLAST 900"


Let me talk about what's going on here for a second... This line creates a constant variable with the value specified. In this case it is setting the variable "AICONDLAST" to 900. What's so special about these variables? Well, for one thing YOU CANNOT CHANGE THEM ONCE THEY HAVE BEEN CREATED! This is what a "constant" is. They serve only the purpose of providing a dull and meaningless number a purpose. It is much easier for the programmer to remember "AICONDLAST" than it is for them to remember that the LAST CONDITION is numbered 900. On top of this, the programmer can use the variable throughout the program and if necessary CHANGE it's value at only a single spot without needing to dig up every line of code that it's being used in.

Alright, so this section lists numbers associated with ALL of the scripting commands. You may have also noticed by now that all of the "AICOND" constants have different numbers given to them. THIS IS IMPORTANT! You do NOT want any two conditions sharing the same number, NOR do you want any two actions sharing the same number. However, a condition and an action can both have the same number.

One more thing to remember. Never go above the number given to the "LAST" constant. If you do, then that command will not work.

*Phew!*

Moving on... let's start putting in some code.

Since our "AICONDLAST" constant is numbered 900, we need a number lower than that. I'm going to pick a number between the previous command, "AICONDHUDHAVENAME", and the "LAST" one. So, somewhere between 823 and 900. That's a pretty broad range and I like to keep my distance... so I chose 850.

Let's give our command a proper name and keep it with the trend going here. Let's name our constant "AICONDMOUSECLICK" and give it the value 850.

When you're done, the section should look a bit like this:
Quote: "#constant AICONDHUDHAVENAME 823
#constant AICONDMOUSECLICK 850
#constant AICONDLAST 900"


Alright... onto the next section.

Press F3 to continue your search and you will be brought to the top of another list.

This section is what labels each scripting command. You can see that we are simply editting an array of command and storing each cell with a simple string. It is this string of text that tells FPSC what text to look for in an FPI script and with which command it is going to be associated with.

Scroll to the bottom of the "conword$" array list and let's add our line of code.

You can start to see the relevance of declaring our constant. It is that value we gave it that determines where in the command array our command string is going to be stored!

You can simply copy and paste one of the lines here and just change the "AICOND" variable to the one we made and then change the string it's associated with.

Since we want our command to be "mouseclick" then our string should be just that.

When you're done the bottom of the list should look like this:
Quote: "conword$(AICONDHUDEDITDONE)="hudeditdone"
conword$(AICONDHUDHAVENAME)="hudhavename"
conword$(AICONDMOUSECLICK)="mouseclick""



Alright! So now our command has been entered into the list of commands properly! The only thing left to do NOW is to put some code behind it.


To find the proper section to put our code we can't just search for "AICONDLAST" as it won't bring us to the proper section. Since we know the next section is for coding commands let's do a search for one of the other conditions in the list.

Do a search for:

AICONDHUDHAVENAME

This will do a repeat search from the top of the source so you'll need to hit F3 a couple times to get to the section we're after.

Once you're there you'll see the section of code behind "AICONDHUDHAVENAME" and it will look like this:


If you are familiar with select blocks then this will look very friendly to you... if not, then no need to worry.

Let's start off by making some room beneath this block of code. There should be a few "`"s below it. I chose to put my code between a couple of those. Just make sure that your code comes after the "endcase" line but BEFORE the "endselect" line below.


Following the trend let us start off by making our case block, prepped and ready for coding:



Now comes the part where we need to consider what we want our command to do.

Well, as it is in Ply's Mod, "mouseclick" is supposed to be TRUE when a few things happen:
- the user left-clicks and X is 1
- the user right-clicks and X is 2
- the user left AND right-clicks and X is 3
- the user middle-clicks (press in the mouse wheel) and X is 4

Why did I choose to have this setup? Because it would be the easiest to code... in fact, like this, we need only put in a single line of code!

Using the DBP function "mouseclick()" everything above can be detected.


But before we go into programming let's take a moment to learn some thing about some values that the engine uses and what values it needs.

First up we have the "airesult" variable. If this variable is set to 1, then the condition will be considered to be true. This is the variable that all conditions revolve around, because once we can DETERMINE whether it should be 1 or 0, then our job is done!

Next up we have "aiconditionseq". This is actually an array which holds all of the values you see in a script. Let's say the script has the "state=0" condition. That "0" is what gets put into this array. But... where in the array is it? No need to worry that. FPSC is coded so that by the time YOUR command's code is executed it has already determined which section of the array you should be looking in, and it stores into a variable called "seq". So in our block of code we can find our value by checking "aiconditionseq(seq)". That sounds great, right? Well it is, but there a bit more to this array. It is ALSO a UDT (User Defined Type) array. What this means is that when the array was declared, it was told to hold MORE than just a single piece of information.

Here's what it was told to hold:

"type" as integer
"valuea" as float
"valueb" as float
"valuec" as float

You needn't worry about what the "type" portion holds, you should only be concerned with valuea, valueb and valuec for what we're doing here.

Now some of you may have seen some conditions that use TWO parameters... X and Y. But what most of you DON'T know is that conditions (because of our type definition) can have up to THREE parameters! It can have an X, a Y AND a Z!

It may be obvious right now, but to be clear on this:
The first parameter in a condition (X) is stored into "valuea", the second parameter (Y) is stored into "valueb", and of course the third possible parameter is stored into "valuec".


Alright!!! That's all we need to know in order to code our condition! Let's get to it!

For coding conditions we need to think like lawyers... so to speak. Instead of trying to prove a statement CORRECT, we need to try to determine if the statement is FALSE.

So, how would our condition be false? ... If the "mouseclick()" DBP function doesn't have the same value as the value in our script of course!

Reading the above statement into code we will find ourselves with:


And IF that statement's true, then we know that our condition in the script is FALSE. That's a weird way of saying it but that's what's going on! And what variable do all conditions revolve around? What variable are we wanting to set in order to consider our condition's code to be complete?

That's right : "airesult"

So now we have our code for our condition... it wasn't very hard at all, was it?

Our condition's block should now look like this:




Now that our condition has been declared with a constant as well as an accompanying string, and it has been coded... we have nothing left to do for it!



You are now free to compile the source! Once it has finished compiling then you go into the source code's folder, copy the compiled executable and replace the FPSC-Game.exe in your FPS Creator folder with it! (I strongly recommend making a backup of the original FPSC-Game.exe, just in case)


Have fun!


The one and only,


Whosoever says, "Don't sweat the small stuff," is obviously not a programmer.
CoffeeGrunt
17
Years of Service
User Offline
Joined: 5th Oct 2007
Location: England
Posted: 16th Aug 2008 11:44
I've only got the trial right now, so I can't compile and I don't have DarkLights....

I might try adding a few wishes of mine into the source, let's say, if I code 'em will you add them to your mod?

Thraxas
Retired Moderator
18
Years of Service
User Offline
Joined: 8th Feb 2006
Location: The Avenging Axe, Turai
Posted: 16th Aug 2008 12:43
Nice work Plystire
Rampage
16
Years of Service
User Offline
Joined: 4th Feb 2008
Location: New Zealand
Posted: 16th Aug 2008 13:13
Lol nice work man, I found this out a while ago lol, (took me like 3 weeks) but I caught on to the 'flow' of which it worked, but thanks to you I understand it more! Thanks again bro!

[url=][/url][href]http://www.rampagemod.webs.com[\href]
Dar13
16
Years of Service
User Offline
Joined: 12th May 2008
Location: Microsoft VisualStudio 2010 Professional
Posted: 16th Aug 2008 13:27
I was thinking of modding the source and you have now made it possible. Thanks Ply

I know not what WWIII will be fought but I do know that WWIV will be fought with sticks and stones-Albert Einstein
CoffeeGrunt
17
Years of Service
User Offline
Joined: 5th Oct 2007
Location: England
Posted: 16th Aug 2008 13:54
Meh, I just realised how hard it is....because I can't think of anything to add in...

Flatlander
FPSC Tool Maker
17
Years of Service
User Offline
Joined: 22nd Jan 2007
Location: The Flatlands
Posted: 16th Aug 2008 15:08
What a great contribution.

The past has a lot of memories to hold onto; but, today is chock full of new adventures, and, the future shouts out, "The best is yet to come!" -- TerryC
Hybrid
17
Years of Service
User Offline
Joined: 21st Nov 2007
Location: In the p-block, group 7.
Posted: 16th Aug 2008 18:28
Wow. This should be stickied. I assume this is how you implimented the setargetreticule command.

Dar13
16
Years of Service
User Offline
Joined: 12th May 2008
Location: Microsoft VisualStudio 2010 Professional
Posted: 16th Aug 2008 19:16
that's how he probably put all his conditions in Ply's Mod... now is there a difference between adding actions(other than the obvious ways) compared to conditions?

I know not what WWIII will be fought but I do know that WWIV will be fought with sticks and stones-Albert Einstein
CoffeeGrunt
17
Years of Service
User Offline
Joined: 5th Oct 2007
Location: England
Posted: 16th Aug 2008 20:25
Nope. The array works in the same way....it's just getting the right code to make it work......

Hybrid
17
Years of Service
User Offline
Joined: 21st Nov 2007
Location: In the p-block, group 7.
Posted: 16th Aug 2008 20:52
I would assume so.

Plystire
21
Years of Service
User Offline
Joined: 18th Feb 2003
Location: Staring into the digital ether
Posted: 17th Aug 2008 00:00
Thanks everyone! Hearing all that just made it all worth writing.


@CG:

If they work and are viable, then I don't see why not.


@Hybrid:

In a way, this is how I add commands. But "settargetreticle" is an action, so it would be a little different.


@Dar13:

Yes, actually there is a difference in adding an action. Read my next post.


The one and only,


Whosoever says, "Don't sweat the small stuff," is obviously not a programmer.
Plystire
21
Years of Service
User Offline
Joined: 18th Feb 2003
Location: Staring into the digital ether
Posted: 17th Aug 2008 00:50
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:


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:



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:



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:



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:


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:


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:


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:



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.
CoffeeGrunt
17
Years of Service
User Offline
Joined: 5th Oct 2007
Location: England
Posted: 17th Aug 2008 01:27
Heheheh, I'm just a little stumped on how to actually code the right stuff to make the actual command.......

I was wondering, would it break the engine in any way to add the ability to spawn misc objects?

I'm a little stuck for ideas for stuff I could code that's within my ability... :S

Rampage
16
Years of Service
User Offline
Joined: 4th Feb 2008
Location: New Zealand
Posted: 17th Aug 2008 02:14
Woo boy! (lol i love that!)

Nice tutorial again Plystire! Thanks heaps!
I have now perfected my widescreen function!!! (I think)
But I want more!!!!!
Your a legend bro!

Thanks,

[url=][/url][href]http://www.rampagemod.webs.com[\href]
Plystire
21
Years of Service
User Offline
Joined: 18th Feb 2003
Location: Staring into the digital ether
Posted: 17th Aug 2008 02:18
@CG:

Errrr... "spawning" objects? If you mean to create entirely new entities via a script command... then I'd suggest looking into the code on how the engine actually goes about reading in, initialising, setting up, creating, and handling entities.

It's a big job and it's the reason I never wanted to get around to making a better "plrdrop" command for dropping weapons. (You know... one that would let you drop the weapon itself and not just something you picked up during the level)

Perhaps some smaller and easier to implement commands would suffice for starting off.


@Rampage:

You want more?

What did you have in mind?


The one and only,


Whosoever says, "Don't sweat the small stuff," is obviously not a programmer.
CoffeeGrunt
17
Years of Service
User Offline
Joined: 5th Oct 2007
Location: England
Posted: 17th Aug 2008 02:27
Lol, I just meant using the MAKE OBJECT commands to spawn random meshes in.......

I ain't touching the array system.......no way...not a chance in hell.....

I dunno about any simple to implement commands, so far my SBP knowledge is basic...

Rampage
16
Years of Service
User Offline
Joined: 4th Feb 2008
Location: New Zealand
Posted: 17th Aug 2008 02:31
Hmm, how an about implementing some extream water commands?
Just a thought because I !!!REALLY!!! want some kick a** water commands in there!!! Lol I don't mind though, I will figure it out by my self as time persists, but it would be help full.
Don't feel forced to do it though!
Lol thanks again!

-Rampage

[url=][/url][href]http://www.rampagemod.webs.com[\href]
CoffeeGrunt
17
Years of Service
User Offline
Joined: 5th Oct 2007
Location: England
Posted: 17th Aug 2008 03:01
What do you mean by "extreme water"

bond1
19
Years of Service
User Offline
Joined: 27th Oct 2005
Location:
Posted: 17th Aug 2008 03:25
Oooh, this information is pure GOLD.

Thanks Plystire!

----------------------------------------
"Your mom goes to college."
Rampage
16
Years of Service
User Offline
Joined: 4th Feb 2008
Location: New Zealand
Posted: 17th Aug 2008 04:47
Quote: "What do you mean by "extreme water""

Figure of speech, as in an expression.

[url=][/url][href]http://www.rampagemod.webs.com[\href]
CoffeeGrunt
17
Years of Service
User Offline
Joined: 5th Oct 2007
Location: England
Posted: 17th Aug 2008 14:05
Just placed my order for the Full Version of DBP! With luck and an efficient postal service, I'll be modding the source within a week or two!

Dar13
16
Years of Service
User Offline
Joined: 12th May 2008
Location: Microsoft VisualStudio 2010 Professional
Posted: 17th Aug 2008 14:34 Edited at: 17th Aug 2008 14:42
thanks Ply for the tut
you've won this prize and this .

I know not what WWIII will be fought but I do know that WWIV will be fought with sticks and stones-Albert Einstein
Hybrid
17
Years of Service
User Offline
Joined: 21st Nov 2007
Location: In the p-block, group 7.
Posted: 17th Aug 2008 21:03
I see now why modding can be such a headache.

Plystire
21
Years of Service
User Offline
Joined: 18th Feb 2003
Location: Staring into the digital ether
Posted: 18th Aug 2008 00:21
Thanks, bond1.


@Hybrid:

It can be a real nightmare sometimes. Just imagine people constantly asking for more when you do get to the point of being satisfied with your work.


The one and only,


Whosoever says, "Don't sweat the small stuff," is obviously not a programmer.
Hybrid
17
Years of Service
User Offline
Joined: 21st Nov 2007
Location: In the p-block, group 7.
Posted: 18th Aug 2008 00:51
I know what you are talking about. Good work, and I still think this shoukd be stickied.

Login to post a reply

Server time is: 2024-11-24 11:52:50
Your offset time is: 2024-11-24 11:52:50