Jump to content
civiciam

Spot all east units within trigger area

Recommended Posts

How can I activate a trigger only if the player has spotted (knows about) all east units within the trigger area? I can't find any post about this on this forum.

Share this post


Link to post
Share on other sites
if (((units east) inAreaArray _myTrigger) findIf {player knowsAbout _x == 0} < 0) then {
    systemChat "Player knows about every EAST unit in trigger";
} else {
    systemChat "Player doesn't know about every EAST unit in trigger";
};

 

  • Like 3

Share this post


Link to post
Share on other sites

You do have a nice answer by NunesSergio but I just wanna add a wee bit of suggestion. According to BIKI, knowsAbout "magic number" is 1.5. This means that when an AI knows about a player or another AI knowsAbout returns a number greater (maybe even equal to but I am not sure about it) to 1.5. You may want to check for that number instead of 0 in the findIf check.

 

So, the check could become

_array findIf {player knowsAbout _x < 1.5}

I am not sure whether this is "safer" but I believe it is a better approach in the sense that you imitate the engine's behaviour (at least of the AI).

 

Additionally, I believe that you could use the "magic variable" thisList to check for the units in the trigger's area like

private _eastUnits = thisList select {(size _x) isEqualTo east}; // East units in trigger area
_eastUnits = _eastUnits findIf {(player knowAbout _x) < 1.5}; // Reuse the same variable for to see if there's any AIs not known to the user

if (_eastUnits != -1) {
	systemChat ("There is at least one unit not known to the player");
} else {
  	systemChat "Player knows about all units in the trigger area";
};

where I have split the selection of east units known to the player into two parts to make the code less cluttered. Of course you can one-line it in the if-statement condition check if that suites you best.

 

Please note that these are mere suggestions based on my personal preferences and not some "optimisation" or "best practices" instructions. Furthermore, the above snippets are not tested and should be treated with caution.

Edited by ZaellixA
Corrected code
  • Like 4

Share this post


Link to post
Share on other sites

Thank you both for these answers. I ended up using this code in my SQF that is loaded from another trigger:

while {alive player} do {
    if (((units east) inAreaArray triggerAllSpotted) findIf {player knowsAbout _x == 0} < 0) then {
        uavDoneT = TRUE; // This is used to activate the condition set to uavDoneT in triggerAllSpotted
    } else {
        // nothing                                                                               
    };
sleep 3; // I never used while before but I made it this way to loop the search for knowsabout targets, please tell me if this is bad.
};

I didn't know how to include your code ZaellixA. I'm not good at these things, yet.

  • Like 1

Share this post


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

I didn't know how to include your code ZaellixA

Not something special to do here. You just replace your code with what I have presented above and in the first if-branch you do what you have to do. In you case you place the uavDoneT = TRUE; code.

 

Generally speaking, it is good practice to avoid writing code that does nothing. This goes specifically to your else branch. Although, technically it does no harm at all (the result is the same since the branch is empty), it can be misleading to people (or yourself) trying to debug and/or maintain your code. With the comments it may be clear that you don't intend to use the else branch but again this may lead someone (or, again, yourself) to think that you may wanted to add some functionality/feature and you either forgot or for some unknown reason (which can cause confusion as to what the "side-effects" the script may have) you didn't manage to implement it. It would be a lot clearer that you don't intend to use the else branch if it was not there at all!

It is very good practice to communicate your intend in the code as much as possible. Some "guru" saying go like "code with clear intent is that which does not need comments to be understood" or something like that. Although this may be exaggerated I believe that does communicate the need (?) to make your intentions clear with your code as much as possible. Wherever there is some ambiguity there is room for bugs.

 

Additionally, it is good practice to avoid infinite (or long lasting) loops if you can avoid them. For example, in your problem you could put the if-statement in the condition field of your trigger and avoid the while-loop and the sleep altogether. This is why I suggested the use of the thisList "magic" variable, since this readily available in the scope of the condition field of a trigger.

 

Please let us know if this is not clear and you would require clarifications and/or more help.

  • Like 3

Share this post


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

Generally speaking, it is good practice to avoid writing code that does nothing. This goes specifically to your else branch. Although, technically it does no harm at all (the result is the same since the branch is empty), it can be misleading to people (or yourself) trying to debug and/or maintain your code.

 

I did change that part of the code. I changed then to exitWith and removed the else statement.

My full code for uavTask.sqf is:
 

player addEventHandler ["WeaponAssembled", { 
    params ["_unit", "_staticWeapon"];  
    _unit connectterminaltoUAV _staticWeapon;
    _staticWeapon engineOn true;
    _staticWeapon setCaptive true;
    player action ["SwitchToUAVGunner", getConnectedUAV player];
    _grp = group _staticWeapon;
    _wp = _grp addWaypoint [getMarkerPos "markerUAV", 0];
    _wp setWaypointType "MOVE";
    _staticWeapon flyInHeight 200;
}];

while {alive player} do {
    if (((units east) inAreaArray triggerAllSpotted) findIf {player knowsAbout _x == 0} < 0) exitWith {
        hintSilent "Objective complete. UAV will now land. Disconnected Raven from UAV.";
        uavDoneT = TRUE;
        private _theDrone = getConnectedUAVUnit player;
        _grp = group _theDrone;
        _posToLand = getMarkerPos "uavLand";
        _landPad = createVehicle ["Land_HelipadEmpty_F", _posToLand];
        _wp = _grp addWaypoint [_posToLand, 0];
        _wp setWaypointType "MOVE";
        _wp setWaypointBehaviour "CARELESS";
        _wp setWaypointCombatMode "GREEN";
        _wp setWaypointStatements ["true","(vehicle this) land 'LAND'"];
        player connectTerminalToUAV objNull;
    };
sleep 2;
};

player addEventHandler ["WeaponDisassembled", {
	params ["_unit", "_primaryBag", "_secondaryBag"];
   	uavPickedUp = TRUE;
}];

This makes the player connect to the UAV instantly. The drone flies to the marker. When the player in the UAV spotted everyone in the trigger area, it disconnects the player and flies to land by itself. It does work, so I will leave it for now. But I do appreciate your skill and helpful advice. I promise you that I will test your code in the future to make sure it is optimized in future missions.
 

  • Like 1

Share this post


Link to post
Share on other sites
On 6/11/2022 at 6:33 PM, ZaellixA said:

You do have a nice answer by NunesSergio but I just wanna add a wee bit of suggestion. According to BIKI, knowsAbout "magic number" is 1.5. This means that when an AI knows about a player or another AI knowsAbout returns a number greater (maybe even equal to but I am not sure about it) to 1.5. You may want to check for that number instead of 0 in the findIf check.

 

So, the check could become


_array findIf {player knowsAbout _x < 1.5}

I am not sure whether this is "safer" but I believe it is a better approach in the sense that you imitate the engine's behaviour (at least of the AI).

 

Additionally, I believe that you could use the "magic variable" thisList to check for the units in the trigger's area like


private _eastUnits = thisList select {(size _x) isEqualTo east}; // East units in trigger area
_eastUnits = _eastUnits findIf {(player knowAbout _x) < 1.5}; // Reuse the same variable for number of east units not known to player

if (_eastUnits != -1) {
	systemChat ("There is " + (str _eastUnits) + " unit(s) not known to the player");
} else {
  	systemChat "Player knows about all units in the trigger area";
};

where I have split the selection of east units known to the player into two parts to make the code less cluttered. Of course you can one-line it in the if-statement condition check if that suites you best.

 

Please note that these are mere suggestions based on my personal preferences and not some "optimisation" or "best practices" instructions. Furthermore, the above snippets are not tested and should be treated with caution.

 

there is a typo in the first line of your solutions code. should be "side" instead of "size".

 

Also this will not return the number of unknown units:

_eastUnits = _eastUnits findIf {(player knowAbout _x) < 1.5}; // Reuse the same variable for number of east units not known to player

because findIf doesn't count but returns the index of the first found element. In this case the index of the first found unknown unit of side east in the trigger area/list.

It can be done like you wanted to with the count command but if one do not need the number of unknown units then the findIf would be faster.

But when using findIf then you can't output the number of unknown units but only if there are unknown units or not.

 

Maybe I misunderstand something but currently I guess not^^

Share this post


Link to post
Share on other sites

@sarogahtyp, thanks for the corrections. I did have the typo, thanks for noting out... I'll correct right away. Additionally, you are right, I have incorrectly typed my comment there, which I will correct too.

 

I will also have to change the systemChat command since the number of units is unknown (with findIf). Yet, the use of findIf is as intended and this is why I check for -1 in the condition. Thanks for the corrections and I apologise if I have caused any confusion. I'll try to be less careless in the future.

Share this post


Link to post
Share on other sites
16 hours ago, civiciam said:

 

I did change that part of the code. I changed then to exitWith and removed the else statement.

My full code for uavTask.sqf is:
 


player addEventHandler ["WeaponAssembled", { 
    params ["_unit", "_staticWeapon"];  
    _unit connectterminaltoUAV _staticWeapon;
    _staticWeapon engineOn true;
    _staticWeapon setCaptive true;
    player action ["SwitchToUAVGunner", getConnectedUAV player];
    _grp = group _staticWeapon;
    _wp = _grp addWaypoint [getMarkerPos "markerUAV", 0];
    _wp setWaypointType "MOVE";
    _staticWeapon flyInHeight 200;
}];

while {alive player} do {
    if (((units east) inAreaArray triggerAllSpotted) findIf {player knowsAbout _x == 0} < 0) exitWith {
        hintSilent "Objective complete. UAV will now land. Disconnected Raven from UAV.";
        uavDoneT = TRUE;
        private _theDrone = getConnectedUAVUnit player;
        _grp = group _theDrone;
        _posToLand = getMarkerPos "uavLand";
        _landPad = createVehicle ["Land_HelipadEmpty_F", _posToLand];
        _wp = _grp addWaypoint [_posToLand, 0];
        _wp setWaypointType "MOVE";
        _wp setWaypointBehaviour "CARELESS";
        _wp setWaypointCombatMode "GREEN";
        _wp setWaypointStatements ["true","(vehicle this) land 'LAND'"];
        player connectTerminalToUAV objNull;
    };
sleep 2;
};

player addEventHandler ["WeaponDisassembled", {
	params ["_unit", "_primaryBag", "_secondaryBag"];
   	uavPickedUp = TRUE;
}];

This makes the player connect to the UAV instantly. The drone flies to the marker. When the player in the UAV spotted everyone in the trigger area, it disconnects the player and flies to land by itself. It does work, so I will leave it for now. But I do appreciate your skill and helpful advice. I promise you that I will test your code in the future to make sure it is optimized in future missions.
 

I assume you use your code in either initPlayerLocal.sqf if in multiplayer or init.sqf if in single player. Instead of having the loop running "indefinitely" (as long as the player is alive of course), you could spawn the code from the WeaponAssembled event handler. You could also save the script handler to a variable in some namespace and then terminate it in the WeaponDisassembled event handler. This way, your while-loop would be running only when the UAV is assembled.

 

This could look like (reusing your code and denoting with comments what has been changed)

player addEventHandler ["WeaponAssembled", { 
    params ["_unit", "_staticWeapon"];  
    _unit connectterminaltoUAV _staticWeapon;
    _staticWeapon engineOn true;
    _staticWeapon setCaptive true;
    player action ["SwitchToUAVGunner", getConnectedUAV player];
    _grp = group _staticWeapon;
    _wp = _grp addWaypoint [getMarkerPos "markerUAV", 0];
    _wp setWaypointType "MOVE";
    _staticWeapon flyInHeight 200;
    
   /*
    * HERE YOU SPAWN THE CODE THAT CHECKS FOR THE UNITS FOUND IN THE TRIGGER AREA
    * AND GET THE HANDLER OF THE SCRIPT
    */
    private _scriptH = spawn [
    	while {alive player} do {
    		if (((units east) inAreaArray triggerAllSpotted) findIf {player knowsAbout _x == 0} < 0) exitWith {
       		hintSilent "Objective complete. UAV will now land. Disconnected Raven from UAV.";
        	uavDoneT = TRUE;
        	private _theDrone = getConnectedUAVUnit player;
        	_grp = group _theDrone;
        	_posToLand = getMarkerPos "uavLand";
        	_landPad = createVehicle ["Land_HelipadEmpty_F", _posToLand];
        	_wp = _grp addWaypoint [_posToLand, 0];
        	_wp setWaypointType "MOVE";
        	_wp setWaypointBehaviour "CARELESS";
        	_wp setWaypointCombatMode "GREEN";
        	_wp setWaypointStatements ["true","(vehicle this) land 'LAND'"];
        	player connectTerminalToUAV objNull;
    	};
		sleep 2;
	};];
  
    /*
     * HERE YOU PUT THE SCRIPT HANDLER INTO
     * A VARIABLE IN THE MISSION NAMESPACE
     * FOR LATER USE
     */
    missionNamespace setVariable ["YOU_ScriptHandle", _scriptH, true]; 
}];

player addEventHandler ["WeaponDisassembled", {
  	/*
     * HERE YOU CHECK IF THE SCRIPT IS DONE AND IF NOT
     * YOU TERMINATE IT TO SAVE THE LOOP OVERHEAD SINCE
     * IT IS NOT NEEDED ANYMORE
     */
  	private _scriptH = missionNamespace getVariable ["YOU_ScriptHandle", scriptNull]; // Get the script handle
  
  	// Check if script is done
  	if (!(scriptDone _scriptH)) {
      terminate _scriptH;
    }
  
	params ["_unit", "_primaryBag", "_secondaryBag"];
   	uavPickedUp = TRUE;
}];

As always (:|) please note that the code is not tested and should be treated with caution. You have already observed my previous mistakes/errors (which are corrected thanks to Sarogahtyp) so extra caution should be exhibited when trying to use the above code!

 

I understand that this may be overkill for use, since the check you perform doesn't seem to be very heavyweight but I though that it would be nice to present an alternative to lift some of the CPU burden when it is not needed. Feel free to use as seen fit and 

16 hours ago, civiciam said:

I promise you that I will test your code in the future

keep in mind that you don't have to use anything people suggest here. Feel free to use whatever you find useful here as this is the reason people share it here, but please don't feel any pressure to use/try everything just because people suggested it. We all provide as much help as we can because we want to build a nice and strong community that will support everyone in need (regardless of status, seniority and/or knowledge and skills).

 

Having said that, I do hope you'll come back with more questions if you have and with some answers/solutions if you can provide some 🙂.

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

×