Jump to content
Sign in to follow this  
IndeedPete

Getting All Attributes from Config File

Recommended Posts

Hi there,

quick question: is it possible to dynamically pull all attributes from a config file? I'm not talking about sub classes, I mean the hard values. Background: I'm working on a small character template system (similar to BIS' from the campaign) but it'll feature child notes overwriting / replacing general values. Here's a fresh approach on a config format right from my notepad, totally WIP:

// Character Templates
class CharacterTemplates {
// Commander
class commander {
	// code = "";
	goggles = "G_Tactical_Black";
	identity = "commander";
	items[] = {"FirstAidKit"};
	uniform = "U_B_CTRG_3";
	texture = "Campaigns\IP_CMP_MERCS\txt\clothing1_mtp_coION.paa";
	variables = "[
		['IP_Faction', 'ION'],
		['IP_LiveFeed', true],
		['IP_Avatar', 'Campaigns\IP_CMP_MERCS\img\commanderAvatar.jpg'],
		['IP_ConvSpecial', 'Head of IONs Mediterranean operations.']
	]";

	class combat {
		vest = "V_PlateCarrierL_CTRG";
		weapons[] = {"arifle_Katiba_GL_Nstalker_pointer_F", "", "hgun_ACPC2_F"};
	};

	class hub {
		vest = "";
		weapons[] = {};
	};
};
};

It's just so you get where I'm going. Later on I will write a function like:

[unit, [class, subClass]] call IP_fnc_applyTemplate;

So, I'm looking for a way to get all of these values to loop trough them. Any ideas appreciated! :)

Share this post


Link to post
Share on other sites

_class = configFile >> "someclass";
for "_i" from 0 to count _class - 1 do
{
   systemChat getText (_class select _i);
};

That is an example of looping through class entries. that example assumes they are all text, but you can use isClass to determine if it is a subclass then pass that class through the function. There are also functions to test for text, arrays, etc. For example isText, isArray, isNumber, etc.

Share this post


Link to post
Share on other sites

I assume you're not talking about an addon but about defining those classes inside your mission.

You can use the config entries in description.ext the same way as the regular config, except you'll have to use a different command for that.

-> https://community.bistudio.com/wiki/missionConfigFile instead of (configFile)

Share this post


Link to post
Share on other sites

@albertfish: Ah, thanks man, didn't know about select within configs, that's perfect!

@Tajin: Yes, it's part of the missionConfigFile but actually used in a campaign environment. I knew how to access specific values but I just didn't know if there was a special command to get them all.

Share this post


Link to post
Share on other sites

I've managed to make some progress here. However, in the end I've decided to use a static approach. Just for future reference, here's the WIP character template script offering one general- and sub-category for a character.

Config Example:

// Character Templates
class CharacterTemplates {
// Captain
class captain {
	code = "removeHeadgear _this;";
	goggles = "G_Tactical_Black";
	identity = "captain";
	items = "[['FirstAidKit', 1]]";
	magazines = "[['30Rnd_556x45_Stanag', 10], ['16Rnd_9x21_Mag', 3], ['HandGrenade', 4], ['SmokeShell', 2], ['SmokeShellRed', 1], ['SmokeShellGreen', 1]]";
	rank = "CAPTAIN";
	skill = 5;
	texture = "Campaigns\IP_CMP_MERCS\txt\coveralls_urbancamo_coBKGabriel.paa";
	uniform = "U_C_WorkerCoveralls";
	variables = "[['IP_Faction', 'BritishKnights'], ['IP_LiveFeed', true], ['IP_Avatar', 'Campaigns\IP_CMP_MERCS\img\captainAvatar.jpg']]";
	vest = "V_PlateCarrierH_CTRG";
	weapons[] = {"arifle_TRG21_ARCO_pointer_F", "", "hgun_P07_F"};

	class combat {
		code = "[_this] call IP_fnc_automaticRearm; _this allowDamage false;";
		night[] = {"((dayTime < 7) OR (dayTime > 19))", "true"};
	};

	class hub {
		night[] = {"false", "false"};
	};
};

// Commander
class commander {
	goggles = "G_Tactical_Black";
	identity = "commander";
	items = "[['FirstAidKit', 1]]";
	rank = "CAPTAIN";
	skill = 5;
	texture = "Campaigns\IP_CMP_MERCS\txt\clothing1_mtp_coION.paa";
	uniform = "U_B_CTRG_3";
	variables = "[['IP_Faction', 'ION'], ['IP_LiveFeed', true], ['IP_Avatar', 'Campaigns\IP_CMP_MERCS\img\commanderAvatar.jpg'], ['IP_ConvSpecial', 'Head of IONs Mediterranean operations.']]";

	class combat {
		code = "[_this] call IP_fnc_automaticRearm; _this allowDamage false;";
		magazines = "[['30Rnd_65x39_caseless_green', 8], ['HandGrenade', 2], ['SmokeShell', 2], ['1Rnd_HE_Grenade_shell', 6]]";
		night[] = {"((dayTime < 7) OR (dayTime > 19))", "true"};
		weapons[] = {"arifle_Katiba_GL_Nstalker_pointer_F", "", "hgun_ACPC2_F"};
		vest = "V_PlateCarrierH_CTRG";
	};

	class hub {
		code = "removeAllWeapons _this;";
		night[] = {"false", "false"};
		vest = "V_PlateCarrierL_CTRG";
	};
};

// Lord
class lord {
	code = "removeAllWeapons _this; removeGoggles _this; removeVest _this;";
	headgear = "H_Beret_brn_SF";
	identity = "lord";
	items = "[['FirstAidKit', 1]]";
	rank = "MAJOR";
	skill = 5;
	texture = "Campaigns\IP_CMP_MERCS\txt\coveralls_urbancamo_coBKTitus.paa";
	uniform = "U_C_WorkerCoveralls";
	variables = "[['IP_Faction', 'BritishKnights'], ['IP_LiveFeed', true], ['IP_Avatar', 'Campaigns\IP_CMP_MERCS\img\lordAvatar.jpg'], ['IP_ConvSpecial', 'Head of the British Knights detachment on Altis.']]";
};

// Medic
class medic {
	goggles = "G_Tactical_Black";
	headgear = "H_Cap_khaki_specops_UK";
	identity = "medic";
	rank = "LIEUTENANT";
	skill = 5;
	texture = "Campaigns\IP_CMP_MERCS\txt\coveralls_urbancamo_coBKVictus.paa";
	uniform = "U_C_WorkerCoveralls";
	variables = "[['IP_Faction', 'BritishKnights'], ['IP_LiveFeed', true], ['IP_Avatar', 'Campaigns\IP_CMP_MERCS\img\medicAvatar.jpg']]";
	vest = "V_PlateCarrierL_CTRG";		

	class combat {
		backpack = "B_AssaultPack_mcamo";
		code = "[_this] call IP_fnc_automaticRearm; _this allowDamage false;";
		items = "[['FirstAidKit', 10], ['MediKit', 1]]";
		magazines = "[['30Rnd_556x45_Stanag', 10], ['16Rnd_9x21_Mag', 3], ['HandGrenade', 4], ['SmokeShell', 2], ['SmokeShellRed', 1], ['SmokeShellGreen', 1]]";
		night[] = {"((dayTime < 7) OR (dayTime > 19))", "true"};
		weapons[] = {"arifle_TRG20_Holo_F", "", "hgun_P07_F"};
	};

	class hub {
		code = "removeAllWeapons _this; removeBackpack _this;";
		items = "[['FirstAidKit', 1]]";
		night[] = {"false", "false"};
	};
};
};

applyTemplate.sqf:

_applyConfig = {
_unit = _this select 0;
_config = _this select 1;

_class = if (typeName _config == "STRING") then {
	(missionConfigFile >> "CharacterTemplates" >> _config)
} else {
	(missionConfigFile >> "CharacterTemplates" >> (_config select 0) >> (_config select 1))
};

if (isNumber(_class >> "skill")) then {
	_skill = (getNumber(_class >> "skill"));
	[_unit, _skill] call IP_fnc_setSkill;
};

if (isText(_class >> "rank")) then {
	_rank = getText(_class >> "rank");
	_unit setRank _rank;
};

if (isText(_class >> "identity")) then {
	_identity = getText(_class >> "identity");
	_unit setIdentity _identity;
};

if (isText(_class >> "uniform")) then {
	removeUniform _unit;
	_uniform = getText(_class >> "uniform");
	_unit forceAddUniform _uniform;
};

if (isArray(_class >> "texture")) then {
	_arr = getArray(_class >> "texture");
	_unit setObjectMaterial [0, (_arr select 0)];
	_unit setObjectTexture [0, (_arr select 1)];
} else {
	if (isText(_class >> "texture")) then {
		_txt = getText(_class >> "texture");
		_unit setObjectTexture [0, _txt];
	};
};

if (isText(_class >> "headgear")) then {
	removeHeadgear _unit;
	_headgear = getText(_class >> "headgear");
	_unit addHeadgear _headgear;
};

if (isText(_class >> "goggles")) then {
	removeGoggles _unit;
	_goggles = getText(_class >> "goggles");
	_unit addGoggles _goggles;
};

if (isText(_class >> "vest")) then {
	removeVest _unit;
	_vest = getText(_class >> "vest");
	_unit addVest _vest;
};

if (isText(_class >> "backpack")) then {
	removeBackpack _unit;
	_backpack = getText(_class >> "backpack");
	_unit addBackpack _backpack;
};

if ((isArray(_class >> "weapons")) && (isText(_class >> "magazines"))) then {
	removeAllWeapons _unit;
	_weapons = getArray(_class >> "weapons");
	_magazines = call(compile(getText(_class >> "magazines")));		

	{_unit addMagazines _x} forEach _magazines;		
	{
		if (_x != "") then {
			_unit addWeapon _x;
		};
	} forEach _weapons;

	_unit selectWeapon ((weapons _unit) select 0);
};

if (isText(_class >> "items")) then {
	_items = call(compile(getText(_class >> "items")));

	for "_i" from 0 to (count _items - 1) do {
		_item = (_items select _i) select 0;
		_count = (_items select _i) select 1;

		for "_i" from 1 to _count do {_unit addItem _item};
	};
};

if (isArray(_class >> "night")) then {
	{_unit unlinkItem _x} forEach ["NVGoggles", "NVGoggles_OPFOR", "NVGoggles_INDEP"];
	_night = getArray(_class >> "night");
	_atAll = call compile (_night select 0);
	_NV = call compile (_night select 1);

	if (_atAll) then {
		if (_NV) then {
			_unit linkItem "NVGoggles";
			_unit addPrimaryWeaponItem "acc_pointer_IR";
		} else {
			_unit addPrimaryWeaponItem "acc_flashlight";
			_unit enableGunLights "forceOn";
		};
	};
};

if (isText(_class >> "variables")) then {
	_variables = call(compile(getText(_class >> "variables")));		
	{_unit setVariable _x} forEach _variables;
};

if (isText(_class >> "code")) then {
	_code = compile(getText(_class >> "code"));		
	_unit spawn _code;
};
};

private ["_unit", "_config", "_subConfig", "_applyConfig"];

_unit = [_this, 0, objNull, [objNull]] call BIS_fnc_param;
_config = [_this, 1, "", [""]] call BIS_fnc_param;
_subConfig = [_this, 2, "", [""]] call BIS_fnc_param;

[_unit, _config] call _applyConfig;
if (_subConfig != "") then {[_unit, [_config, _subConfig]] call _applyConfig};

Share this post


Link to post
Share on other sites

EDIT: DISREGARD THIS POST. I DID NOT DIG DEEP ENOUGH BEFORE POSTING. THE SOLUTION IS TO USE THE configProperties COMMAND INTRODUCED IN VERSION 1.36.

My apologies for resurrecting this old thread.

I am currently running into the same problem as IP. I need a way to get an array all of the attributes (not classes) within a class. I could work around this by replacing each attribute with a class with a single attribute inside and use existing commands, but that solution is mega-extreme-nastiness and I know I would instantly regret forcing my fingers to type such a coding abomination. I know about BIS_fnc_getCfgDataPool[/url; however, since this command loads and returns the actual data of all the attributes rather than an array of names that I can manually use in code, this is not an ideal solution (I expect each attribute to be at least 200 characters long, and it is necessary that an unlimited amount of attributes can be added to the parent class for this project)

I'm very interested in the solution you were working at before changing directions, Pete.

Edited by DreadedEntity

Share this post


Link to post
Share on other sites

Ha, that's cool. Didn't know about that configProperties command.

That's rather handy, although reading the config wasn't too hard/bad without it (granted, also "visiting"/traversing inherited stuff is a bit more tricky I suppose...). You just need to understand that you can "walk" (and count!) a config entry pretty much just like an array (note the "count _cfg" in the function below).

For example to visit all entries below some config entry point, I like to use:

scriptName "RUBE3\functions\Configs\fn_configVisit.sqf";
/*
Author:
 rübe

Description:
 visits all classes/config entries under the given path.

Parameter(s):
 _this select 0: config/path (configFile/-Path)
 _this select 1: visitor (code)
				 the visitor gets passed:

					_this select 0: path/config
					_this select 1: classname

				 ...from where you can getNumber/getText/...
				 the attributes you need.

Returns:
 void
*/

private ["_cfg", "_visitor", "_i", "_n"];

_cfg = _this select 0;
_visitor = _this select 1;
_n = (count _cfg) - 1;

for "_i" from 0 to _n do
{
if (isClass (_cfg select _i)) then
{
	[
		(_cfg select _i),
		(configName (_cfg select _i))
	] call _visitor;
};
};

From there, and with a little wrapper function to extract the config values on some class:

scriptName "RUBE3\functions\Configs\fn_configGet.sqf";
/*
Author:
 rübe

Description:
 savely retrieves a config value of type number, text, array
 or class. The class(-name) is returned in the latter case.

Parameter(s):
 _this: path to a config value (config)

	or

 _this select 0: path to a config value (config)
 _this select 1: default value (any; optional)
				 if not set, objNull is returned as default

Returns:
 any
*/

private ["_cfg", "_default"];

_default = objNull;

if ((typeName _this) == "ARRAY") then
{
_cfg = _this select 0;

if ((count _this) > 1) then
{
	_default = _this select 1;
};
} else
{
_cfg = _this;
};

if (isNumber _cfg) exitWith { (getNumber _cfg) };
if (isText _cfg) exitWith { (getText _cfg) };
if (isArray _cfg) exitWith { (getArray _cfg) };
if (isClass _cfg) exitWith { (configName _cfg) };

_default

...it's super easy to read, filter and extract stuff from the config:

_classes = [];

[(configFile >> "CfgVehicles"), {
private ["_cfg", "_class", "_scope"];
_cfg = _this select 0;
_class = _this select 1;
//_scope = (_cfg >> "scope") call RUBE_configGet;
// or to be safe, maybe better:
_scope = [(_cfg >> "scope"), 0] call RUBE_configGet;

if (_scope > 1) then
{
	_classes pushBack _class;
};
}] call RUBE_configVisit;

// voila, you now are in the possession of a list of all classes 
// under CfgVehicles with a scope larger than 1 (i.e. 2).
// Of course you could extract/read and pushback an array
// with more information or what not...

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
Sign in to follow this  

×