ArmA 3 Alpha: TL_fnc_getParameter, a function for easily handling required and optional parameters in your scripts

Overview

I recently wanted to start customizing the gear my player was wearing in ArmA 3. In order to do that, I would first need to remove the default gear that my player started out with. I started writing a function to remove the default gear, when I decided I wanted to be able to specify which gear would be removed. With options, I could reuse the function in multiple missions to customize any unit’s gear depending on the situation.

I had never dealt with optional parameters in my functions before, and by default, .sqf functions don’t have a way to handle optional parameters nor do they assign a default value if the parameter isn’t provided. I wanted to quickly check for an optional parameter, but also didn’t want to code multiple parameter checks for every function I wrote.

The result is TL_fnc_getParameter. An easy way to handle required and optional parameters in any script.

Stop wasting time writing if-statements

If your function was passed an array of arguments, you have access to a magic variable named _this. _this is an array that contains all of the parameters that were passed to your script.

When I first started checking which parameters had been passed in _this, I started writing if-statements. I soon realized that the more optional parameters I wanted, the more time I was spending writing if-statements. I also began to think of what would happen if the parameter was passed, but wasn’t the data type that I expected it to be. I wanted the script to fail gracefully, which meant falling back on default values. This lead to writing more if-statements to check for those conditions.

I found this method caused me to spend more time writing supporting code, rather than the actual features of my project. I didn’t want to do this for every script I was going to create that used required or optional parameters.

Code faster with TL_fnc_getParameter

To include TL_fnc_getParameter in your scripts, use:

// Replace X-X with version number, or provide appropriate filepath
TL_fnc_getParameter = compile preprocessFile "TL_fnc_getParameter_X-X.sqf";

In it’s most basic form, TL_fnc_getParameter can quickly check if a single parameter was passed to your script. If it wasn’t passed, Nil will be returned. This allows for an easy check to see if the parameter exists or not. You can then fail gracefully with a debug message rather than a code error.

// Parameter 0 (Required)
_param0 = [_this] call TL_fnc_getParameter;

// Parameter 0 wasn't passed.
// Exit, and output a debug message
if(isNil "_param0") exitWith {
	if(_debug) then {
		titleText ["Parameter 0 is required.", "PLAIN"];
	};
};

You can easily check for multiple parameters by passing the index as the second argument.

// Parameter 0 (Required)
_param0 = [_this] call TL_fnc_getParameter;

// Parameter 1 (Required)
_param1 = [_this, 1] call TL_fnc_getParameter;

The third argument specifies the expected data type of the parameter if it exists. The parameter must match the data type in order to be returned. If it doesn’t match, then Null will be returned if no default value is specified as the fourth argument.

Visit the biki for a list of acceptable data types.

// Parameter 1 (Required Boolean)
_param0 = [_this, 1, "BOOL"] call TL_fnc_getParameter;

When setting the fourth argument, you can create optional parameters. If the parameter wasn’t passed, then the default value will be returned. In the example below, false will be returned if parameter 1 was not passed to the script.

// Parameter 1 (Optional)
_param0 = [_this, 1, "BOOL", false] call TL_fnc_getParameter;

Debugging

To quickly determine which value is being returned from TL_fnc_getParameter, you can pass true as the fifth argument. When debugging is enabled, messages will be displayed indicating which value has been returned.

// Parameter 1 (Optional)
_param0 = [_this, 1, "BOOL", false, true] call TL_fnc_getParameter;

Here are some screenshots of example debug messages:

TL_fnc_removeGear. A real-world example.

In the overview, I mentioned that I was creating a gear removal function. The function is called TL_fnc_removeGear, and it implements required and optional parameters by using TL_fnc_getParameter. This provides a real-world example of how this function can be useful for your scripts.

Read more about TL_fnc_removeGear and it’s use of TL_fnc_getParameter.

TL_fnc_getParameter.sqf

At the time of writing this, I have only tested this script with the TL_fnc_removeGear example. When implementing into your own scripts, if you experience any problems or errors, please leave a comment or send me an email with a way to replicate the problem. I will attempt to fix any problems the script has, and will update it if I develop it further.

I hope this function helps with your scripting, and please leave feedback if you think this script can be improved.

Changelog

April 5, 2013 – Version 1.2

  • Instead of returning objNull, nil is now returned.
  • Known Issue: isNull check only works for Objects, Controls, Displays, Groups, Locations, Tasks, Team Members. Using isNull to check for the objNull default value when expecting a string, results in an error. A work around is to set the default value to an empty string and use if(_param == “”) exitWith
    • Using if(isNil “_param”) should effectively check if a parameter has been passed regardless of type.
  • More accurate debug messages
  • If no parameters are passed, a debug message is displayed regardless of debug mode

April 3, 2013 – Version 1.1

  • Various bug fixes
  • More accurate checking if array doesn’t exist
  • Known Issue: isNull check only works for Objects, Controls, Displays, Groups, Locations, Tasks, Team Members. Using isNull to check for the objNull default value when expecting a string, results in an error. A work around is to set the default value to an empty string and use if(_param == “”) exitWith
/****************************************************************************************
 *
 * FUNCTION: TL_fnc_getParameter
 *
 * @description	Checks if a value exists in an array and is of the specified data type.
 *				Useful when using optional arguments in functions (See example).
 *				The default value will be returned if the array index does not exist.
 *				If the array index does exist, then the array value will be returned
 *				only if it matches the expected data type (if provided).
 *
 * @author 		TechLethal
 * @website 	http://www.techlethal.com
 * @version 	1.2
 *
 ****************************************************************************************
 * 
 * PARAMETERS { expected type, required/optional, default value }
 * 0 - { array, required, none } The array to check.
 * 1 - { integer, optional, 0 } The index to use.
 * 2 - { string, optional, "" } The data type to match.
 *   ACCEPTABLE VALUES:
 *		"ARRAY",
 *		"BOOL",
 *		"CODE",
 *		"CONFIG",
 *		"CONTROL",
 *		"DISPLAY",
 *		"GROUP",
 *		"LOCATION",
 *		"OBJECT",
 *		"SCALAR",
 *		"SCRIPT",
 *		"SIDE",
 *		"STRING",
 *		"TEXT" 
 * 3 - { any, optional, false } The default value to use if array value doesn't exist.
 * 4 - { bool, optional, false } Output debug messages.
 *
 ****************************************************************************************
 *
 * EXAMPLE 1 USAGE:
 * TL_fnc_getParameter = compile preprocessFile "TL_fnc_getParameter.sqf";
 * _default = true;
 * _param = [_this, 1, "BOOL", _default] call TL_fnc_getParameter;
 *
 * The above example checks if the parameter with an index of 1 was passed to the script.
 * If the parameter was passed and is a boolean, then the parameter’s value will be
 * assigned to _param. If the parameter does not exist, then the value of _default will
 * be assigned to _param.
 *
 ***************************************************************************************/

private[
	"_debug",			// Output debug messages. True or False.
	"_paramCount",		// Number of params passed
	"_array",			// The array to search in
	"_index",			// The index to search for
	"_defaultValue",	// The default value to return if index doesn't exist
	"_expectedType",	// The expected type that the index should be
	"_desiredValue",
	"_desiredType",
	"_returnValue"
];

/*********************************************************************
 * PARAMETERS
 ********************************************************************/

 // Number of params passed
_paramCount = count _this;

// Exit if no parameters were passed
if(_paramCount == 0) exitWith {
	titleText ["TL_fnc_getParameter: No arguments passed.", "PLAIN"];
};

// Parameter 0 - Array to search
if( typeName _this == "ARRAY" ) then {
	_array = _this select 0;
}
else {
	_array = _this;
};

// Parameter 1 - Index
_index = 0;
if(_paramCount > 1) then {
	_param = _this select 1;
	if(typeName _param == "SCALAR") then {
		_index = _param;
	};
};

// Parameter 2 - Expected Type
_expectedType = "";
if(_paramCount > 2) then {
	_expectedType = _this select 2;
};

// Parameter 3 - Default Value
if(_paramCount > 3) then {
	_defaultValue = _this select 3;
}
else {
	_defaultValue = nil;
};

// Parameter 4 - Debug
_debug = false;
if(_paramCount > 4) then {
	_param = _this select 4;
	if(typeName _param == "BOOL") then {
		_debug = _param;
	};
};

/*********************************************************************
 * DETERMINE PARAMETER OR DEFAULT
 ********************************************************************/

if( isNil {_array} ) exitWith {

	if(_debug) then {
		titleText ["TL_fnc_getParameter: Array is nil", "PLAIN"];
	};

	_returnValue = if(isNil "_defaultValue") then { nil } else { _defaultValue };

};

// If array isn't actually an array...
if( typeName _array != "ARRAY" ) then {

	// ...but requested index 0, so return the value
	if(_index == 0) then {

		if(_debug) then {
			titleText ["TL_fnc_getParameter: Parameter 0 is not an array, but index 0 requested.\nReturned parameter 0.", "PLAIN"];
		};

		_returnValue = _array;

	}

	// Not an array, and requested an index > 0. Return default value.
	else {
		_returnValue = if(isNil "_defaultValue") then { nil } else { _defaultValue };
	};

}
else {

	// If parameter found. Check the data type, if provided.
	if(count _array > _index) then {

		_desiredValue = _array select _index;

		// No data type check.
		if(_expectedType == "") then {

			if(_debug) then {
				titleText [ format[
					"TL_fnc_getParameter: Parameter found!\nReturning parameter %1.\nNo type check.\nReturning: %2",
					_index,
					_desiredValue
				], "PLAIN"];
			};

			_returnValue = _desiredValue;

		}

		// Check the data type.
		else {

			_desiredType = typeName _desiredValue;

			// Data type matches, return parameter.
			if(typeName _desiredValue == _expectedType) then {

				if(_debug) then {
					titleText [ format[
						"TL_fnc_getParameter: Parameter found!\nReturning parameter %2.\nType check for %1 PASSED.\nReturning: %3",
						_expectedType,
						_index,
						_desiredValue
					], "PLAIN"];
				};

				_returnValue = _desiredValue;

			}

			// Data type didn't match
			else {

				if(_debug) then {
					titleText [
						format["TL_fnc_getParameter: Parameter found!\nType check for %1 FAILED.\nReturning default.\nParameter %2 value = %3. Parameter %2 type = %4.\nReturning: %5",
							_expectedType,
							_index,
							_desiredValue,
							_desiredType,
							if(isNil "_defaultValue") then { "nil" } else { _defaultValue }
						], "PLAIN"];
				};

				_returnValue = if(isNil "_defaultValue") then { nil } else { _defaultValue };

			};
		};
	}

	// Parameter not found
	else {

		if(_debug) then {
			titleText [ format[
				"TL_fnc_getParameter: Parameter %1 NOT found!\nReturning default.\nReturning: %2",
				_index,
				if(isNil "_defaultValue") then { "nil" } else { _defaultValue }
			], "PLAIN"];
		};		

		_returnValue = if(isNil "_defaultValue") then { nil } else { _defaultValue };

	};
};

// Return nil if default value wasn't passed
if(isNil "_returnValue") exitWith {
	nil
};

// Return default value
_returnValue