Jump to content
Luft08

passing a method name to be executed

Recommended Posts

I'm writing some generic code and as one of the parameters I wish to pass in a function name to be called.  Is it possible to put a function name into a string variable, pass that variable and then call that function?

 

So for example let's say I wish to spawn civilians and then assign those civilians actions. I don't want my framework to have to define every conceivable action that the programmer may wish to implement so I wish to have the programmer write the action function him/her self and pass it in. My code would then spawn the civilian and pass it to the programmer's function to assign whatever action he/she has decided to implement.

 

Never mind. It appears I can just do it. who would have thought it would be this easy? 🙂

Share this post


Link to post
Share on other sites

Already solved but here's some more things to know:
1) Pass the code directly (as you did)

tag_fnc_test = {
	hint "hello";
};

tag_fnc_callThis = {
	params ["_fnc"];
	[] call _fnc
};

[tag_fnc_test] call tag_fnc_callThis;

2) Use the variable from missionNamespace

tag_fnc_test = {
	hint "hello";
};

tag_fnc_callThis = {
	params ["_fnc"];
	[] call (missionNamespace getVariable [_fnc, {}]);
};

["tag_fnc_test"] call tag_fnc_callThis;

The second option might be marginally slower but the huge advantage of it is if you want to remoteExec tag_fnc_test on another client (or even all clients). In the first case you would send over the code which can be quite huge. in the second case you just pass the function name as a short string and get the code on the client. i.e. this scenario:

[tag_fnc_test] remoteExec ["tag_fnc_callThis"]; // code is sent over network
// vs.
["tag_fnc_test"] remoteExec ["tag_fnc_callThis"]; // string is sent over network

 

  • Like 3

Share this post


Link to post
Share on other sites
22 hours ago, Luft08 said:

Never mind. It appears I can just do it. who would have thought it would be this easy? 🙂

Would you care to provide your solution so that others may benefit in the future from it?

Share this post


Link to post
Share on other sites
2 hours ago, ZaellixA said:

Would you care to provide your solution so that others may benefit in the future from it?

Sure, I'm still experimenting with it but so far it appears that you can create a function in a separate sqf file and then just pass it in as a parameter. For example, I'm working with the new DLC Western Sierra and a mod called maxwomen that has Muslim women with burkas. I want the civilians to act more naturally depending on their type. Muslim women have restrictions in many areas of the world that I wish to model. Much of the code should be generic if written properly but some code must be specific to the scenario. So, I write the main code but allow some code to be passed in as a parameter.   I have one function called activateCivs that takes such a parameter:

if(!isServer) exitWith {};
params["_areaRecord", "_civArray", "_spawnCode"];

private _areaName = _areaRecord # 0;
private _areaCenter = _areaRecord # 1;
private _areaRadius = _areaRecord # 2;

// Get a list of houses in area.
private _houseList = [_areaCenter, _areaRadius] call getBuildingList;

// Get odds that house will have civs.
private _civOdds = 100;
{
	if((_x find _areaName) != -1) exitWith {_civOdds = _x # 1;}; 
} forEach civOdds;

if(_civOdds > 0) then {
	// forEach house spawn civs and assign ACT_IDLE if odds are met.
	//private _time = daytime; // time of day/night will affect civ action choice.
	{ // forEach _houseList
		private _die = floor random 100;
		if(_die < _civOdds) then {
			private _civilianArray = [_x] call _spawnCode; // Code to spawn civs.
			{
				private _actionArray = [ACT_IDLE,[]];
				private _civRecord = [_x, _actionArray];
				_civArray pushBack _civRecord;
			} forEach _civilianArray;
		};
	} forEach _houseList;
};

Notice it takes a _spawnCode parameter. This is a function that gets executed when a WS civ is spawned:

_spawnCode for WS:

if(!isServer) exitWith {};
params["_bldg"];

private _returnArray = [];
private _bldgPositions = [_bldg] call BIS_fnc_buildingPositions;
private _group = createGroup[civilian, true];
private _civType = selectRandom civMenTypeArray;
private _malePos = selectRandom _bldgPositions;
private _unit = _group createUnit [_civType, _malePos, [], 0, "FORM"];
_returnArray pushBack _unit;

private _wifeCount = floor random 3;
for "_c" from 1 to _wifeCount do {
	private _civType = selectRandom civWomenTypeArray;
	private _wifePos = selectRandom _bldgPositions;
	private _unit = _group createUnit [_civType, _wifePos, [], 0, "FORM"];
	doStop _unit;
	_returnArray pushBack _unit;	
};
_group setFormation "FILE";
_group setBehaviour "SAFE";
_group setSpeedMode "LIMITED";

_returnArray

Note that the code spawns a male (Husband) then spawns 0, 1 or 2 females (Wifes).

The code to spawn western civilians would be quite different but hopefully the main part will be the same.  Like I said, I'm still experimenting. 

 

  • Like 1

Share this post


Link to post
Share on other sites

I compile the code and pass it into a method that gets executed via a call to BIS_fnc_loop.

if(!isServer) exitWith {};
params ["_newActiveAreas", "_civArray", "_soldierArray", "_vehicleArray", "_vehCode", "_civcode", "_soldierCode"];

if(_newActiveAreas isEqualTo []) exitWith {};

{ // forEach _newActiveAreas
	_handle = [_x, _vehicleArray, _vehCode] spawn activateVehicles;
	waitUntil{scriptDone _handle};
	
	private _handle = [_x, _civArray, _civCode] spawn activateCivs;
	waitUntil{scriptDone _handle};
	
	_handle = [_x, _soldierArray, _soldierCode] spawn activateSoldiers;
	waitUntil{scriptDone _handle};
} forEach _newActiveAreas;

There's a lot of supporting code and arrays not shown but hopefully you get the idea.

  • Like 1

Share this post


Link to post
Share on other sites

yeah in this case just passing the code is fine. Another unrelated tip: you don't have to use spawn and waitUntil. this can be simplified to

if(!isServer) exitWith {};
params ["_newActiveAreas", "_civArray", "_soldierArray", "_vehicleArray", "_vehCode", "_civcode", "_soldierCode"];

if(_newActiveAreas isEqualTo []) exitWith {};

{ // forEach _newActiveAreas
	[_x, _vehicleArray, _vehCode] call activateVehicles;
	
	[_x, _civArray, _civCode] call activateCivs;
	
	[_x, _soldierArray, _soldierCode] call activateSoldiers;
} forEach _newActiveAreas;

 

  • Like 1

Share this post


Link to post
Share on other sites
1 hour ago, 7erra said:

"yeah in this case just passing the code is fine. Another unrelated tip: you don't have to use spawn and waitUntil. this can be simplified to ..."

 

True, but I use spawn when I think that the function might take too long and cause a momentary screen freeze. Spawn will run the code as scheduled but call will run the code as unscheduled thus risking that freeze I mentioned.  Deciding when to use spawn vs call is what I would consider art rather than science and I may be have made the wrong choice. Testing will tell. 🙂

 

Share this post


Link to post
Share on other sites

If you are calling code from a scheduled function (which you are otherwise you could not use waitUntil) then that called code will also run scheduled. to run unscheduled code from scheduled environment you have to use isNil. Kind of weird but that's how it is.

Share this post


Link to post
Share on other sites
8 hours ago, 7erra said:

If you are calling code from a scheduled function (which you are otherwise you could not use waitUntil) then that called code will also run scheduled. to run unscheduled code from scheduled environment you have to use isNil. Kind of weird but that's how it is.

I don't think I follow. So if I have a scheduled method called scheduledFoo and I want to run an unscheduled  function called unscheduledFoo from within scheduledFoo normally I would just [] call unscheduledFoo; or _returnValue = [] call unscheduledFoo;

I don't understand where NIL would come into play.

  • Like 1

Share this post


Link to post
Share on other sites

No, I meant the isNil command (modified example 4 from the BIKI):

0 spawn {
	systemChat str canSuspend;		// chat shows true
  	[] call { systemChat str canSuspend }; // chat shows true
	isNil { hint str canSuspend };	// hint shows false
};

 

  • Like 2

Share this post


Link to post
Share on other sites
23 hours ago, Luft08 said:

True, but I use spawn when I think that the function might take too long and cause a momentary screen freeze.

 

In my experience it really takes overusing the most demanding commands to actually cause a freeze when called (like allMissionObjects or nearestTerrainObjects) or processing very large data, and usually such scripts will tank framerate when run scheduled.

  • Like 1

Share this post


Link to post
Share on other sites
4 hours ago, haleks said:

 

In my experience it really takes overusing the most demanding commands to actually cause a freeze when called (like allMissionObjects or nearestTerrainObjects) or processing very large data, and usually such scripts will tank framerate when run scheduled.

I guess "overuse" is subjective. The mission that I would like to release soon randomizes aspects that requires data from all buildings etc. So I spawn the function that does that as it's not critical that it complete prior to other things running.

  • Like 1

Share this post


Link to post
Share on other sites

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now

×