adanteh 58 Posted August 30, 2013 Hello everyone, I was wondering if it's possible to get an array and check vars in a way like this: If I have an array like this [door_1, door_2, house_1, house_2, door_3, car_1]; is it possible to create a new var out of just the door entities, but doing this dynamically? Like testing the array for door_x and then it returns all parts that start with door_? One of the things I'm trying to do with this is figure out much doors a house has from the config. So I can getArray from the animationSources of buildings and then scan that array for Door_x_Source and then count that new array. Also want to use it for a few other things. Share this post Link to post Share on other sites
Zenophon 110 Posted August 30, 2013 (edited) You can convert any value to a string with the 'str' command. Then, use the function I wrote below to evaluate the string and determine if your keyword is in it. You must define the function by coping the code into the init.sqf. I have no experience with animationSources exactly, but this is a general function that works on all strings. This function is thoroughly tested and has worked well for me. This function is very useful for a lot of other things as well. Zen_StringIsInString = { /* * Determines if the first given string is a contiguous part of the second string, * may be case sensitive or not, use a # as a wildcard that will match any character * params: 1. String, to search for * 2. String, to search in * 3. Boolean, optional, true to be case sensitive (default false) * return: Boolean */ private ["_keyString", "_totalString", "_useCase", "_keyIsInTotal", "_stringPiece"]; _keyString = _this select 0; _totalString = _this select 1; _useCase = false; if ((count _this) > 2) then { _useCase = _this select 2; }; if !(_useCase) then { _keyString = toUpper _keyString; _totalString = toUpper _totalString; }; _keyString = toArray _keyString; _totalString = toArray _totalString; _keyIsInTotal = false; if ((count _keyString) <= (count _totalString)) then { { if ((count _keyString) > ((count _totalString) - _forEachIndex)) exitWith {}; if (_x == (_keyString select 0) || ((_keyString select 0) == 35)) then { _stringPiece = []; private "_i"; for "_i" from _forEachIndex to ((count _keyString) - 1 + _forEachIndex) do { _stringPiece set [(count _stringPiece), (_totalString select _i)]; }; { if (_x == 35) then { _stringPiece set [_forEachIndex, _x]; }; } forEach _keyString; _keyIsInTotal = [_stringPiece, _keyString] call BIS_fnc_arrayCompare; }; if (_keyIsInTotal) exitWith {}; } forEach _totalString; }; _keyIsInTotal }; You could use the function like so: _filteredArray = []; { if (["door_#", (str _x)] call Zen_StringIsInString then { _filteredArray set [(count _filteredArray), _x]; }; } forEach [door_1, door_2, house_1, house_2, door_3, car_1]; Just change the 'door_#' argument to anything you want. Edited August 30, 2013 by Zenophon Share this post Link to post Share on other sites
spunfin 34 Posted August 30, 2013 I also was looking a way to find doors and ended up counting UserActions and gathering their positions: //_building is my building obviously _doorPositions = []; _Cfg = (configFile >> "cfgVehicles" >> (typeOf _building) >> "UserActions"); if ((count _Cfg) <= 0) exitwith {}; for "_i" from 0 to ((count _Cfg) - 1) step 3 do{ if (_i >= (count _Cfg)) exitwith {}; _sel = _Cfg select _i; _position = getText (_sel >> "position"); _doorPositions set [(count _doorPositions),(_building modelToWorld (_building selectionPosition _position))]; }; And for my purposes it was enough just to go thru _doorPositions and observe distance to units and the animation states with: for "_i" from 1 to (count _doorPositions) step 1 do { if(_building animationPhase "door_" + str _i + "_rot" == 1)then{ ...blaablaa... So I used only door_x_rot and it seemed to be all I needed (at least in Stratis), but there's also hatch_x_rot and you might need that too. I hope this gets you going! :) Here's my door closer if you want to check out. Share this post Link to post Share on other sites
lifted86 10 Posted August 30, 2013 maybe this will help. just tested, and seems to work. One thing, im assuming the array will contain strings. _array = ["door_1", "door_2", "house_1", "house_2", "door_3", "car_1"]; _doorArray = []; { for "_i" from 1 to (count _array) do { _string = "door_"; _testString = (_string + (str _i)); if (_x == _testString) exitWith { _doorArray set [ count _doorArray, _testString]; }; }; }foreach _array; hint str _doorArray; Share this post Link to post Share on other sites
adanteh 58 Posted August 30, 2013 I also was looking a way to find doors and ended up counting UserActions and gathering their positions: //_building is my building obviously _doorPositions = []; _Cfg = (configFile >> "cfgVehicles" >> (typeOf _building) >> "UserActions"); Hold on a second...! How are you counting _Cfg there? You need an array for that right, but _Cfg is in that case just refering to the actual file. I'm asking, because I can't figure out how to build an array from all UserActions (Probably not enough sleep!) _array = ["door_1", "door_2", "house_1", "house_2", "door_3", "car_1"]; _doorArray = []; { for "_i" from 1 to (count _array) do { _string = "door_"; _testString = (_string + (str _i)); if (_x == _testString) exitWith { _doorArray set [ count _doorArray, _testString]; }; }; }foreach _array; hint str _doorArray; That looks quite clean and simple and should work, if only I could actually get the array with all children of userActions. Doesn't matter that you're assuming they are string, I can always just do (str _x) otherwise. You can convert any value to a string with the 'str' command. Then, use the function I wrote below to evaluate the string and determine if your keyword is in it. You must define the function by coping the code into the init.sqf. I have no experience with animationSources exactly, but this is a general function that works on all strings. This function is thoroughly tested and has worked well for me. This function is very useful for a lot of other things as well. Zen_StringIsInString = { /* * Determines if the first given string is a contiguous part of the second string, * may be case sensitive or not, use a # as a wildcard that will match any character * params: 1. String, to search for * 2. String, to search in * 3. Boolean, optional, true to be case sensitive (default false) * return: Boolean */ private ["_keyString", "_totalString", "_useCase", "_keyIsInTotal", "_stringPiece"]; _keyString = _this select 0; _totalString = _this select 1; _useCase = false; if ((count _this) > 2) then { _useCase = _this select 2; }; if !(_useCase) then { _keyString = toUpper _keyString; _totalString = toUpper _totalString; }; _keyString = toArray _keyString; _totalString = toArray _totalString; _keyIsInTotal = false; if ((count _keyString) <= (count _totalString)) then { { if ((count _keyString) > ((count _totalString) - _forEachIndex)) exitWith {}; if (_x == (_keyString select 0) || ((_keyString select 0) == 35)) then { _stringPiece = []; private "_i"; for "_i" from _forEachIndex to ((count _keyString) - 1 + _forEachIndex) do { _stringPiece set [(count _stringPiece), (_totalString select _i)]; }; { if (_x == 35) then { _stringPiece set [_forEachIndex, _x]; }; } forEach _keyString; _keyIsInTotal = [_stringPiece, _keyString] call Zen_ArraysMatch; }; if (_keyIsInTotal) exitWith {}; } forEach _totalString; }; _keyIsInTotal }; You could use the function like so: _filteredArray = []; { if (["door_#", (str _x)] call Zen_StringIsInString then { _filteredArray set [(count _filteredArray), _x]; }; } forEach [door_1, door_2, house_1, house_2, door_3, car_1]; Just change the 'door_#' argument to anything you want. And that looks way more advanced. It's calling Zen_Arraysmatch at one point though? Not exactly sure what exactly it's doing extra, compared to Lifted's post. Share this post Link to post Share on other sites
spunfin 34 Posted August 30, 2013 _Cfg is an array of those :) Then I just set the position of each doors UserAction into array (_doorPositions) here: for "_i" from 0 to ((count _Cfg) - 1) step 3 do{ if (_i >= (count _Cfg)) exitwith {}; _sel = _Cfg select _i; _position = getText (_sel >> "position"); _doorPositions set [(count _doorPositions),(_building modelToWorld (_building selectionPosition _position))]; }; Share this post Link to post Share on other sites
Zenophon 110 Posted August 30, 2013 And that looks way more advanced. It's calling Zen_Arraysmatch at one point though? Not exactly sure what exactly it's doing extra, compared to Lifted's post. It should call BIS_fnc_arrayCompare; that was my mistake. I edited my first post to change that, and the script should work now. The main difference between my function and Lifted86's is that my function is searching for the given key string anywhere in the other string. Lifted86's function is looking for the strings to match exactly. You could use my function to search for "r_", but it does not seem like you need to do that in the example you gave. My function can also be case sensitive (probably a useless feature), and accepts '#' as a wildcard to match any character if you are unsure. If Lifted86's function fits your needs then use. My function will be slightly slower because it is looping and checking more (and it calls BIS_fnc_arrayCompare). If you need to check for strings that may contain other characters before or after "door_x", then my function will help you there. Share this post Link to post Share on other sites
adanteh 58 Posted August 30, 2013 If Lifted86's function fits your needs then use. My function will be slightly slower because it is looping and checking more (and it calls BIS_fnc_arrayCompare). If you need to check for strings that may contain other characters before or after "door_x", then my function will help you there. I'll be using mostly his lines for this case, but that actually sounds like I could use it for a lot of things and makes things a hell of a lot easier. Quick question though: I tried adding it as a function, but I'm not sure how to make general functions for my mission. I put this in my description.ext class CfgFunctions { #include "cfgfunctions.hpp" }; Then cfgfunctions has class VES { tag = "KPW"; class functions { file = "\functions"; class stringIsInString {description = "Check if string is present in string. By Zenophon";}; class more things; }; }; Then in my functions folder I have a file called fn_stringIsInString.sqf with your code in it, but when I start the mission it says \functions\fn_stringIsInString.sqf is missing.... It's right there though! I was hoping I could just use getArray to get a fairly clean list of all the sub-entries of UserActions, but no luck there. To work around that I'm just using the full config name/location (Not sure even) and format that in front of what I'm comparing my values with. Works perfectly now! KPW_countDoors = { private ["_doorArray", "_Cfg", "_array"]; _doorArray = []; _array = []; _Cfg = (configFile >> "CfgVehicles" >> (typeOf _building) >> "UserActions"); hint str _Cfg; for "_i" from 0 to (count _Cfg) - 1 do { _action = _Cfg select _i; _array set [count _array, _action]; }; { for "_i" from 1 to (count _array) do { _testString = format ["%1/OpenDoor_%2", (str _Cfg), _i]; player sideChat _testString; if ((str _x) == _testString) then { _doorArray set [ count _doorArray, _testString]; }; }; } forEach _array; kpw_doorCount = count _doorArray; kpw_doorCount }; Share this post Link to post Share on other sites
Zenophon 110 Posted August 30, 2013 I'll be using mostly his lines for this case, but that actually sounds like I could use it for a lot of things and makes things a hell of a lot easier. Quick question though: I tried adding it as a function, but I'm not sure how to make general functions for my mission. I put this in my description.extclass CfgFunctions { #include "cfgfunctions.hpp" }; Then cfgfunctions has class VES { tag = "KPW"; class functions { file = "\functions"; class stringIsInString {description = "Check if string is present in string. By Zenophon";}; class more things; }; }; Then in my functions folder I have a file called fn_stringIsInString.sqf with your code in it, but when I start the mission it says \functions\fn_stringIsInString.sqf is missing.... It's right there though! I have never used that method to compile scripts (I am not very good with configs). There seems to be different ways to do it, according to this page: http://community.bistudio.com/wiki/Functions_Library_%28Arma_3%29#Adding_a_Function The only thing that looks wrong to me is 'file = "\functions";'. You might want to try 'file = "functions";' and 'file = "functions\";'. Also, the file name may be case sensitive, so make sure it matches exactly. I compile all of my scripts manually, at the beginning of the init, using code like this: Zen_StringIsInString = compileFinal preprocessFileLineNumbers "functions\Zen_StringIsInString.sqf"; This seems to do that same thing the game does automatically (use 'compileFinal' to store it permanently) and is faster for me, but it could get tedious listed dozens of functions like this. To fix that, I place the function compiles in separate files, then use something like this at the top of the init: #include "CompileFunctions.sqf" Share this post Link to post Share on other sites
Larrow 2822 Posted August 31, 2013 (edited) cfgFunctions.hpp class KPW { class KPWfunctions { file = "functions"; class stringIsInString {}; }; }; Will load file %ROOT%\functions\fn_stringIsInString.sqf Number of doors KPW_doorCount = getNumber(configFile >> "CfgVehicles" >> (typeOf _building) >> "numberOfDoors"); There is also a "numberOfHatches". Useless example function fnc_buildingInfo = { _building = _this; _cfg = (configFile >> "CfgVehicles" >> (typeOf _building)); _doorCount = getNumber (_cfg >> "numberOfDoors"); _hatchCount = getNumber(_cfg >> "numberOfHatches"); _doorInfo = []; for "_doorNum" from 1 to _doorCount do { _doorString = format["OpenDoor_%1", _doorNum]; _doorSelection = getText (_cfg >> "UserActions" >> _doorString >> "position"); _doorPos = (_building modelToWorld (_building selectionPosition _doorSelection)); _doorInfo = _doorInfo + [[ _doorNum, _doorPos, format["Door_%1_rot", _doorNum] ]]; }; _hatchInfo = []; for "_hatchNum" from 1 to _hatchCount do { _hatchString = format["OpenHatch_%1", _hatchNum]; _hatchSelection = getText (_cfg >> "UserActions" >> _hatchString >> "position"); _hatchPos = (_building modelToWorld (_building selectionPosition _hatchSelection)); _hatchInfo = _hatchInfo + [[_hatchNum, _hatchPos, format["Hatch_%1_rot", _hatchNum] ]]; }; _buildingInfo = [ typeOf _building, getPosATL _building, _doorInfo, _hatchInfo ]; _buildingInfo }; /*buildingInfo [ "Land_Cargo_HQ_V2_F", [0,0,0], [ [1, [0,0,0], "Door_1_rot"] , [2, [0,0,0], "Door_2_rot"] ], //doors [ [1, [0,0,0], "Hatch_1_rot"] ], //hatches ]*/ Edited August 31, 2013 by Larrow Share this post Link to post Share on other sites
adanteh 58 Posted September 2, 2013 Number of doors KPW_doorCount = getNumber(configFile >> "CfgVehicles" >> (typeOf _building) >> "numberOfDoors"); There is also a "numberOfHatches". Looks like I'm blind. I never noticed the amount of doors was configured in the building's config file. :D Share this post Link to post Share on other sites