
Lets get into it...

As you would already know (because you read the Introduction to Functions), functions are limited in terms of:

(a) what you can do with them and...

(b) the commands that work in them.

So why use them?  Well...

Functions do not replace scripts.  They supplement scripts.

In fact, you don't have to use functions at all.  In scripts, you can mimic any function that is created in a .sqf file.
However, through the use of functions you will find your scripts become more efficent, legible and intuitive, because you will be breaking down complex code into manageable portions.

Remember, one function - one purpose.
This is the key to writing functions.  Keep 'em small, keep 'em sweet.

So, first off I would like to explain the programming paradigm (programming method) you need to use in order to effectively create and employ functions.


--------------------------------------------------------------------------------


One function, one purpose - programming from the ground up.
===========================================================


Imagine, for a moment, that you are about to write a massive script for your new mission.  It requires a number of complex calculations, multiple nested loops, control structures that wait for outside variables to change etc. etc. - this is a total monster.

There are two ways that you could approach the writing of this script:

1. You could jump straight in, hitting each problem as you come to it and eventually coding the entire script into one big mutha of a file.

2. You could break down the script into it's logical components, and plan before you even write a single line of code.

If you prefer option 1 - stop reading now.
You are destined for failure, and perhaps functions are not for you.
You'll eventually design the worlds biggest, most complicated script but during debugging your head will explode.

Option 2 is really the only way to go (take it from me, as this applies to all sorts of programming, not just OFP scripting).


Lets suppose you were faced with a particular problem in your OFP mission, and you decide to solve that problem with a script. I will use the most recent problem I came across, that being to force a number of helicopters to land without relying on AI.  Here is how I approached the problem:


Big problem:  Making a whole heap of choppers land without AI (argh!)


Breaking it down further:

a) We need to break the 'whole heap' of choppers down and look at each individual chopper.

b) For each chopper we are going to need to know where it has to land, and where it is going    to start it's descent from.

c) We need to work out a way to make the chopper descend slowly over a distance, until it    lands (after testing I decided to use a bit of math, which determines the chopper's    height dependant on how far away it is from a central point).

d) We need to work out the steps to make this single chopper land.


	Determine when the chopper is to commence it's descent.

	Measure distance to landing point, calculate speed of descent.

	Adjust the chopper's height according to how far away from the landing point it is
	(using flyinheight, not setpos).

	Is the chopper close to landing point?  If not, loop again until it is
	(adjusting height appropriately).

	The chopper is close to it's landing point, so wait for it's speed to slow down.

	Speed has slowed, so land the chopper.

e) Determine small scale, tricky problems that can't be done simply using the commands provided by BIS, and write    your own functions.


We need to calculate two-dimensional distance (between the chopper and the landing point). No existing command can do this (At this point we need to think about writing a function that can measure the distance between two points, rather than two objects).

We need to calculate the ratio of descent for a single chopper.  This also could be done in a function.

We then start coding from the bottom up.
We write the two functions, and test them.
We make a single chopper land over a given distance.
We add in another loop that prevents the descent from starting until the chopper is a certain distance away from the landing position.
We could then think about writing a seperate script that calls our initial one, that manages a whole heap of choppers.
 
You see how we broke the problem down into component parts, and then built the script(s) from the bottom up?  This is very good coding practice, and will result in efficent, easy to understand scripting.

Hopefully you also see where functions 'fit in' with regards to scripting, they are at the lowest level, and each function has a single, defined purpose.

Ok, now that you know when to use functions, how the hell do you write them?


--------------------------------------------------------------------------------


Function syntax
===============


Firstly, some definitions:


Syntax - The language constructs or rules according to which a given programming language
	 is written.

Parameter - A value passed to a function or a script as an input.

Operand - The 'type' of a parameter, ie String, Array etc.

Control structure - An expression used to control the execution flow of a script or
		    function.

Variable scope - Defines the visibility of a variable, that is either local
		 (private variables) or global (public variables).


Before you delve into function syntax, you need to understand the way in which OFP interprets script syntax.

In script files, you put each different command (or instruction) on a seperate line.  That way, OFP knows that it can expect a single instruction per line, and the interpeter behaves accordingly.

The interpreter that processes a script file after it is executed is designed specifically for the OFP scripting language, in accordance with the script syntax.

Function files do not use the same interpreter, and hence use a slightly different syntax. 

The most important thing to realise is that function syntax is different from script syntax, and if you jump into writing your first function as you would a script then you are going to run into trouble.


Here are some of the main syntax rules when writing functions (not definitive):


Each expression must be terminated with a semicolon (';'), not simply an end-of-line as with scripts.

Comments are placed in a function by using either '//some text' or '/* some text */', as opposed to ';some text' as in scripts.

Control structures (if-then-else, foreach and do-while) are used in place of the goto statement (which does not work in functions, as you cannot change the execution point ad-hoc or stop execution using the exit command).

Variable assignment is the same as in scripts (varName = Anything).

Function calls are the same as in scripts (result = MyFunction(parameters) or result = parameters call MyFunction), however a range of default commands do not work in functions.

To evaluate in functions you use if-then-else, as opposed to ? (evaluation) : action.

Arrays are accessed and manipulated the same as in scripts ([1,2,3] select 0) - Would select the first element in the array, which in this case is 1.


--------------------------------------------------------------------------------


Parameters
==========

Parameters are inputs to a function.
Functions process parameters in the same way as scripts.

When calling a function, parameters are passed in via the reserved variable _this.

If your function was called with:

	player call MyFunction

Then within your function, _this would equal the player unit.

If your function was called with:

	[player,"snYpir"] call MyFunction

Then within your function, _this is an array.  _this select 0 would equal the player unit and _this select 1 would equal the string "snYpir".


--------------------------------------------------------------------------------


Control structures
==================

You would know just how important it is to make decisions in scripts.
Without decision making capability, a script would simply be a list of instructions that OFP would process one after the other.

In functions, you make decisions using the if-then-else statement.


If-Then-Else
------------

This statement should be fairly self-explanatory.

If 'snYpir is a jedi master' Then 'buy snYpir a beer' Else 'snYpir owes me a beer'.

It is literally that simple.  OFP expects the if-then-else statement to come in the following form:


If (<condition>) Then
	{
		<action to carry out on condition equal to true>
	}
Else
	{
		<action to carry out on condition equal to false>
	};


Notice the semicolon (';') which identifies the end of the statement.


If you have multiple statements to carry out within the if-then-else curly brackets, you use:

If (<condition>) Then
	{
		<action to carry out on condition equal to true>;
  		<action to carry out on condition equal to true>;
     		...(insert additional actions as required)...
	}
Else
	{
		<action to carry out on condition equal to false>;
  		<action to carry out on condition equal to false>;
     		...(insert additional actions as required)...
	};


To evaluate <condition>, you can use:


&& - AND
|| - OR
!= - Not equal to
!  - Not
== - Equal to
<  - Less than
>  - Greater than
<= - Less than or equal to
>= - Greater than or equal to


For example:


If (player == snYpir) Then
	{
		// action on true (the player is snYpir)
		hint "You are snYpir - unlucky for you!";
		player setpos [(getpos player) select 0,(getpos player) select 1,50];
	}
Else
	{
		// action on false (the player is not snYpir)
		hint format["You are %1",player]
	};


Notice the use of semicolons at the end of each statement within the curly brackets.  This is so OFP knows the difference between the different actions you want it to carry out depending on the <condition>.

If you have only one statement between the curly brackets then you don't need a semicolon.


You can also reverse the if-then-else statement using NOT ('!') as follows:


If (!(player == snYpir)) Then
	{
  
		// action on true (the player is not snYpir)
		hint format["You are %1",player]
	}
Else
	{
		// action on false (the player is snYpir)
		hint "You are snYpir - unlucky for you!";
		player setpos [(getpos player) select 0,(getpos player) select 1,50];
	};


Loops!
------

Forget using goto statements in functions.
Instead, you need to realise that the execution is going to move through the function one statement after the other, and the only way that you can iterate (loop back to do the same statement more than once) is by using the following:


While-Do
--------

You can use the while-do statement to re-run the same bit of code as many times as required.
You can also use this code to emulate a for loop (for those with programming experience).

Note, however, that you can only loop a certain number of times (any more that 10,000 or so loops and the function will crash with a generic error in expression).

The syntax for a while-do loop is as follows:


While {<condition equals true>}
	Do
		{
			<action>;
			<another action>;
      			...insert other actions as required...
		};


For example, to loop ten times:

var_counter = 0;
While { var_counter < 10 }
	Do
		{
			// increment var_counter by 1
			var_counter = var_counter + 1;
		};


To break out of the loop, all you need to do is make <condition> equal to false (in the above example this would be done by making var_counter equal to 10 or greater).


ForEach
-------

ForEach statments are use to iterate (loop through) arrays.
It is a fantastic little statement because you don't have to worry about keeping count of the array element you are up to - the statement does that for you.
If your array is 6 elements long, you know that a ForEach loop using that array will loop 6 times.

The syntax is as follows:

{
	<action>;
	<another action>;
      	...insert other actions as required...
}
ForEach <array>;


Within the loop, the variable _x represents the value of the current array element.

Suppose you have an array [1,2,3].
The first element is 1, the second is 2 and the third is 3.


If you have the following:

{
	var_current_element = _x
}
foreach [1,2,3];


var_current_element would equal 1 in the first loop, 2 in the second loop and 3 in the third loop.

ForEach is very powerful.
To loop through all of the soldiers in the player's squad:

{
	var_this_unit = _x
}
ForEach units (group player);

The units command returns an array of units from a given group.


--------------------------------------------------------------------------------


Variables in functions
======================


As the official documentation on functions is limited, my knowledge of the way variables work in functions is sketchy.

I *think*:

All variables in functions are initially global (visible outside the function).

Private variables must be declared using the Private statement.

As you would know, private variables cannot be seen or used outside the function where they are declared.  This means that all those little counters and misc variable you use within your function are erased from memory when the function has finished, rather than hanging around long after as global variables do.

The rule is: Unless you NEED it global, make it private!
I would suggest that when using functions, you will very rarely need to use global variables.

Declare private variables as follows:

	private ["_<variable name>","_<another variable name>"...];

You should always put an underscore ('_') in front of a private variable, so other people know immediately what is private and what is global.

If my function used three private variables (_a, _b and _c) I would declare them in the first line of my script/function as follows:

private ["_a","_b","_c"];


--------------------------------------------------------------------------------


Returning values
================

One of the main reasons for using functions is that they have the ability to return values. 

In summary, you pass the function some inputs, it processes those inputs and then ouputs the result to wherever the function was called from.
This output is called the return value.


The result of the last statement evaluated is always returned.

If I had a function saved in 'timestwo.sqf', and it looked as follows:


// start function (timestwo.sqf)
private ["_a"];

// get parameter
_a = _this;

// multiply by two
_a = _a * 2
// end function (timestwo.sqf)


You can see the the last statement evaluated is _a = _a * 2. The result of this statement (ie the new value of _a) is returned.


So, if I wanted to multiply a number by two using timestwo.sqf, I'd use the following (in a script):


// preprocess function file (in init.sqs)
TimesTwo = preprocessFile "timestwo.sqf"

// start script

_var_result = 2

// multiply _var_result by two
_var_result = _var_result call TimesTwo

// end script


_var_result now equals the result of the TimesTwo function, which is 4 (because TimesTwo calculated _a = 2 * 2 and returned _a).


So, suppose you had a function that returned true or false.

We'll call the function 'Dead', and save it in 'dead.sqf'.
This function will do the opposite of the 'Alive' command.

The function looks as follows:

// start function (dead.sqf)
private ["_unit","_return"];

// get parameter
_unit = _this;

// is the unit dead?
If (getdammage _unit >= 1) Then
	{
		_return = true
	}
Else
	{
		_return = false
	};

_return
// end function (dead.sqf)


The last statement evaluated is simply _return, so the value of '_return' is passed back to the location where the script was called.

For example (in a script):


// preprocess function file (in init.sqs)
Dead = preprocessFile "dead.sqf"

// start script

? player call Dead : hint "The player is dead!";

// end script

Your function should always return something.
If it doesn't really return anything, simply make it return true by putting the following in the last two lines of the function:

// return true
true


Important note!  Due to a bug introduced in OFP 1.91, the 'return' value in a function must not be terminated with a semicolon.  Leave the semicolon off the return value in your functions!


--------------------------------------------------------------------------------


Learn by example!
=================


Right, the theory is behind us.
Now it is time for you to write your first function.
You have most probaly used a script that makes a group of soldiers eject from a vehicle, right?

Well, we are going to do that in a function.

You may be thinking 'But I can do it in a script, why should I use a function?'.

Well, I need an example, don't I?
And I would rather use something that you could already do in a script because then you can compare the two methods.

Commands that you need to know are:

'units' (returns an array of the units in a group)
'leader' (returns the leader of a group)
'vehicle' (returns the name of the vehicle a unit is in)
'action' (forces a unit to do something)
'unassignvehicle' (prevents a unit from getting back into a vehicle they just got out of).


Thinking back to the bottom-up approach I mentioned earlier, we are going to firstly write a function that forces a single unit to get out of a vehicle, and then another function (that uses the first) and makes a group eject.

Before we start, open the mission editor and save an empty Desert Island mission.
Save it as 'GetOut_Test'.

So, firstly, lets write the GetOut function which will make a single soldier eject from whatever vehicle they are in.


We want the function to:


	Take the name of a unit (passed in as parameter).

	Work out what vehicle it is in (using the vehicle command).

	If the unit is not in a vehicle, don't do anything (if the vehicle of the unit
	equals the unit).

	Eject it from that vehicle (using unit action ["EJECT", vehicle).

	Return true if the unit was ejected, otherwise return false.


Have a go at writing the function yourself in notepad.
Think about how you would do it in a script, and then apply the following rules:


Use semicolons (';') at the end of each statement.

Declare private variables using private [].

Instead of ? <condition> : <action> use if-then-else.

Return true or false (the value of the last expression evaluated).


The solution for you is in the Function Library.
Don't look until you have a go yourself!


Save your function in 'getout.sqf' in the GetOut_Test.Intro directory.
Make a new file called 'init.sqs' in the GetOut_Test.Intro directory, and put in the line which will preprocess your function:

	GetOut preprocessFile "getout.sqf"

Save init.sqs, and head back to the mission editor.
Make an empty jeep, and name it 'jeep'.
Make a soldier outside the jeep.
In the unit's init field put 'this moveindriver jeep'.

Make a trigger that triggers on Radio Alpha, and put 'Get out' in the text field.
In the 'on activation' field put 'driver jeep call GetOut'.
Preview the mission, and hit radio alpha.
If you have done everything correctly, the driver of the jeep will eject.

Well done!  You have written your first function.


Now, we are going to write another function that ejects a whole squad.
Head back to notepad, and start a new text file.

Obviously we already have a function that ejects a single unit.
In this new function, we want to:


	Take the name of a group (passed in as a parameter).

	Loop through the entire group ejecting soldiers
	(using { _x call GetOut } ForEach units <group>).

	Return true when done (true;).


Save this new function as EjectSquad.sqf in the GetOut_Test.Intro directory.


Thought that was easy?
Well, lets make it more complicated.
Obviously, at the moment, you have a function that makes the entire group eject, regardless of the vehicle they are in.
You might have a dude in a chopper, and his leader might be a grunt in the back of an APC. 
Perhaps the user of this function doesn't want the entire group to eject, but only those units that are in the same vehicle as the leader.


Make the following additions:

The function is to accept a second parameter, which is a string ([<group>,<string>] call EjectSquad).

    - If this string is "LEADVEH", only those units in the leader's vehicle are to eject.
    - If this string is "ALLVEH", all units in the group are to eject.
    - If the string is blank, ie only one parameter is passed into the function, assume
      that all units in the group are to eject.


Place the following line under the one already in init.sqs:

	EjectSquad = preprocessFile "ejectsquad.sqf"

To test the new script, go back to the editor and place another unit on the map (in the same group as the first unit).
In the init field of the new unit put 'this moveincargo jeep'.
Name the first unit you added 'theleader' and promote him to Corporal.

In the 'on activation' field of the trigger, remove the old command, and put
'[group theleader,"ALLVEH"] call EjectSquad'.

Fiddle around with the parameters, and experiment by putting different units in different vehicles.

Don't forget to also try:

	[group theleader,"LEADVEH"] call EjectSquad

and:

	[group theleader] call EjectSquad


--------------------------------------------------------------------------------


Uploading to the Function Library
=================================


(You have to be logged in as a member to do this).

Once you write a function, please (pretty pretty please) upload it to our Function Library. You'll be credited with the work, and forever immortalised as an OFPEC submitter.

You can add a function by clicking 'add function' from the Function Library front page.
Or you can go straight there.

I will briefly explain some of the text boxes:

	Function name - Please make your function name as descriptive as possible, without
			spaces.  Not too long!


	Version - Update the version number of your function as you update the function, so
		  people know if there is a new version out.


	Operands - These allow people to know what types of variables to pass to your
		   function.  You can use [] to define arrays, and commas between types.
		   Use <> around optional operands.  For example, the EjectSquad function's
		   operands are:   [Group<,String>].

	Return Type - The type returned by your function (ie the result of the last
		      evaluation).

With regards to uploading files, you must provide a .sqf file that is the same name as your function name.
You can also provide an optional example mission.
If you do provide an example mission, please keep it very very simple and on Desert Island.


That wraps up this tutorial.
Hopefully you are ready to go out and use functions in your missions, and upload them to the OFPEC Function Library.
Please help us expand this resource, so other people can save time by download functions that have already been written.
