Jump to content
SlovakianLynx

Check if player killed more than 2 friendly units

Recommended Posts

I have been searching for this, but I didn't find a satisfying answer. In an SP scenario I want the player to be punished, when he kills more, than two friendly soldiers. Right now the limit seems to be set to 3, until friendlies start shooting at you, which seems too tolerant to me. So, what's the best way to count friendly fire and then end the mission with a failure?

Share this post


Link to post
Share on other sites

Units will be considered hostile when their rating drops below -2000: https://community.bistudio.com/wiki/ArmA:_Armed_Assault:_Rating_Values

You could use the HandleRating EH like this:

// initPlayerLocal.sqf
player addEventHandler ["HandleRating", {
	params ["_unit", "_rating"];
	if (_rating < 0) then {
		2 * _rating
	} else {
		_rating
	};
}];

According to the table on that page it should be enough to kill two firendlies already, you might have to use another value than 2 to get your desired result

  • Like 1

Share this post


Link to post
Share on other sites

Hey there, 7erra gave you a good option there. Alternatively you could possibly use the "Killed" event handler to count how many friendly units one has killed. This could possibly look something like

// In initPlayerLocal.sqf

// Add the event handler to the player
player addEventHandler ["Killed", {
	// Get some variables
	private _instigator = _this select 2; // The one who killed the player
	private _nKills = _instigator getVariable ["YOU_nKills", 0]; // Get the amount of kills of the instigator

	// Check if the player has killed at least one player before
	if (_nKills >= 1) then {
		// Do whatever you want to do
	};

	// Increment the number of kills of the instigator
	_instigator setVariable ["YOU_nKills", _nKills + 1];
}];

Please keep in mind that this is a simple example and that you may have to do some more things to take care of the possibility of someone driving over someone else, or being a UAV operator and stuff like that.

 

Additionally, you may have to use remoteExec if you want to do something to the instigator.

 

Finally,  note that this snippet is untested and has to be treated with caution.

 

Do not hesitate to ask for more help and/or directions if you either require more help or want to extend the solution.

  • Like 1

Share this post


Link to post
Share on other sites

7erra  Thanks, but for a couple of reasons I want the mission to end with a failure, rather, than AI shooting the player.

 

ZaellixA That does nothing, or I just don't understand how to use it properly. Does initPlayerLocal.sqf even work, when it is just a SP mission? 

Share this post


Link to post
Share on other sites

Oh, I am sorry, I didn't realise you were to use it on SP 😞.  I am not sure initPlayerLocal.sqf runs on SP, but in this case you could place it in your init.sqf file. You should get the same results.

 

But now that I think about it, most probably this won't be of much help, since you don't want to find out when the player shoots other players but when the player shoots other friendly AI units. I apologise for all that, I didn't correctly understand your intend.

 

So, in this case, apart from checking the rating of the player, which is what 7erra very well suggested, you could use the mission event handler "EntityKilled", which runs when an entity is killed. This way you could check, and save in a variable like I suggested, the amount of kills of the player towards friendly units. This could possibly look like

/*
 * Inside init.sqf
 */

// Add the mission event handler
addMissionEventHandler ["EntityKilled", {
	// Get the passed parameters of interest
	params ["_killed", "", "_instigator"];

	// Check if instigator is player and the killed unit was on the same side with the instigator/player
	if ((_instigator isEqualTo player) && ((side _instigator) isEqualTo (side _killed))) then {
		// Get the amount of friendly kills of the player
		private _nKills = player getVariable ["YOU_nKills", 0];

		// Check amount of kills
		if (_nKills >= 1) then {
          		systemChat "Game Over..."; // DEBUGGING MESSAGE
          	
          		/*
            		 * Instead of the message finish the game or do whatever you intend to do
             		 */
		};

		// Set the variable with the new number of kills
		player setVariable ["YOU_nKills", _nKills + 1];
	};
}];

Please keep in mind that, once more, the presented code is untested and should be treated with caution.

 

Do not hesitate to send again if this does not work or needs further refinement and improvement.

 

Best,

Z.

Edited by ZaellixA
Improved the code a bit
  • Like 1
  • Thanks 1

Share this post


Link to post
Share on other sites

The side of killed units is always civillian. You have to check the side of the group the unit belonged to:

if ((_instigator isEqualTo player) && (playerSide isEqualTo (side group _killed))) then {

 

  • Like 3
  • Thanks 1

Share this post


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

/*
 * Inside init.sqf
 */

// Add the mission event handler
addMissionEventHandler ["EntityKilled", {
	// Get the passed parameters of interest
	params ["_killed", "", "_instigator"];

	// Check if instigator is player and the killed unit was on the same side with the instigator/player
	if ((_instigator isEqualTo player) && ((side _instigator) isEqualTo (side _killed))) then {
		// Get the amount of friendly kills of the player
		private _nKills = player getVariable ["YOU_nKills", 0];

		// Check amount of kills
		if (_nKills >= 1) then {
          		systemChat "Game Over..."; // DEBUGGING MESSAGE
          	
          		/*
            		 * Instead of the message finish the game or do whatever you intend to do
             		 */
		};

		// Set the variable with the new number of kills
		player setVariable ["YOU_nKills", _nKills + 1];
	};
}];

 

Thank you very much, that works!

 

 

51 minutes ago, 7erra said:

if ((_instigator isEqualTo player) && (playerSide isEqualTo (side group _killed))) then {

 

With this it does nothing. 

  • Like 1

Share this post


Link to post
Share on other sites

Can you check the output of the variables like this?

diag_log [_instigator isEqualTo player, playerSide, side player, side group player, side group _killed];

From my own experience I am pretty sure that the side of the killed unit in the EH is ALWAYS civillian because it's dead

  • Like 1

Share this post


Link to post
Share on other sites

Hey 7erra, that's good to know. I apologise for not being able to provide that info in advance, I was not aware of that subtlety :(...

 

You could easily verify the dead unit's side just by placing

systemChat str (side _killed);

in the event handler after acquiring the passed parameters. This way you don't even have to check the diag_log file. Please let us know if this worked for you somehow, it could possibly benefit more people in the future.

Share this post


Link to post
Share on other sites

OK, I take that back. The side of the killed unit is indeed civilian and 7erra's fix is the one that works. I must have confused the codes.

But now I noticed, that the script keeps restarting itself each time the player kills a new unit:

How to avoid this? For now I just used removeAllWeapons command on him, so he doesn't continue shooting.

Also, the scripts runs multiple times, when the player kills more units at once with a grenade.

 

Furthermore, when I use it directly in the mission I want with additional script lines, it runs fine, but it pops up an error:

Arma3-error.jpg

 

Here's the code:

	// Check if instigator is player and the killed unit was on the same side with the instigator/player
	if ((_instigator isEqualTo player) && (playerSide isEqualTo (side group _killed))) then {
		// Get the amount of friendly kills of the player
		private _nKills = player getVariable ["YOU_nKills", 0];

		_nKills = _nKills + 1; // Increment (friendly) kills

		// Check amount of kills
		if (_nKills >= 2) then {
			removeAllWeapons player;
			hint parseText "<t color='#FF0014'><t size='1.1'>!FRIENDLY FIRE WILL NOT BE TOLERATED!</t>";
			playSound "alarm";
			sleep 1;
			v1 sideChat "Vančo have you gone completely nuts?! You'll face consequences for this!";
			sleep 3;
			["lose",false,5,false] spawn BIS_fnc_endMission;
		};

		// Set the variable with the new number of kills (this is in case you didn't finish the game of course)
		player setVariable ["YOU_nKills", _nKills];
	};
}];

 

  • Like 1

Share this post


Link to post
Share on other sites

The error is triggered because you are trying to suspend the script with sleep in a non-scheduled environment. To avoid this you can spawn the EH code:

_this spawn {
  	params [...
	// Check if instigator is player and the killed unit was on the same side with the instigator/player
	if ((_instigator isEqualTo player) && (playerSide isEqualTo (side group _killed))) then {
		// Get the amount of friendly kills of the player
		private _nKills = player getVariable ["YOU_nKills", 0];

		_nKills = _nKills + 1; // Increment (friendly) kills

		// Check amount of kills
		if (_nKills >= 2) then {
			removeAllWeapons player;
			hint parseText "<t color='#FF0014'><t size='1.1'>!FRIENDLY FIRE WILL NOT BE TOLERATED!</t>";
			playSound "alarm";
			sleep 1;
			v1 sideChat "Vančo have you gone completely nuts?! You'll face consequences for this!";
			sleep 3;
			["lose",false,5,false] spawn BIS_fnc_endMission;
		};

		// Set the variable with the new number of kills (this is in case you didn't finish the game of course)
		player setVariable ["YOU_nKills", _nKills];
	};
};

NOTE: This code is not copy-pasteable because you probably missed the first lines when copy pasting

  • Like 3

Share this post


Link to post
Share on other sites

Yep 7erra is right about it. Most often, the reason of getting a "Generic Error" for a sleep command is because you use it in an unscheduled environment. And, once more, what 7erra suggests is the simplest and most direct solution to that. An alternative to that, in order to make your code a wee bit more generic (or not...) is to use canSuspend command like

// Some code

if (canSuspend) then {
	uiSleep 1; // Or you can use either sleep or waitUntil
};

This way, you won't get any errors from your code and will still wait for the specified amount of time if this is possible.

  • Like 1

Share this post


Link to post
Share on other sites

7erra that pops up an error, even though I've written the code by hand (I'm pretty sure the problem is in my receiver), but it doesn't matter. I've used the previous version without sleep, as that sentence is not really necessary.

 

One more unrelated thing (I do not want to create a new thread and you guys seem capable).

I have a camp defense mission, with limitation triggers, which fire when, when the player leaves the camp. One is just warning and the other one is mission failure.

In the scenario the player can team switch to his buddy, so I wanted to make the trigger fire, when this guy is the player, but not when he is AI, nor did I wanted other AI in the camp to activate it.

 

For that I used condition: !(player in thisList) and alive player

 

It works well until the player's score drops bellow -2000 (for example I blow up several teammates with 100g TNT) and then the triggers fire, as if the players has left them - the commander starts screaming at the player to get back and the mission ends with a loss.

What condition should I use to avoid this?

Share this post


Link to post
Share on other sites

Hhhmmm, I am not really sure why this happens. I believe that your conditions shouldn't really have anything to do with the rating of the player...

 

Nevertheless, you could possibly try to use the rating too, although as I just said I am not sure this is the issue. Try adding something like

(rating player) > -2000

I am not sure this will achieve what you want to do though because in this way if you kill all those friendlies and then leave the area the trigger will not fire. This kinda breaks the code rather than fixes it.

 

I would suggest you check what thisList contains and whether the player is in the list after the rating goes below -2000, or there is another condition (from the game engine point of view) that removes the player from thisList.

  • Thanks 1

Share this post


Link to post
Share on other sites

I have been thinking what to do with restarting of the event handler script recently and came up with executing another script, that removes the MissionEventHandler right after its conditions are met (Sorry I don't know how to describe it better in english).

 

So now it looks like this:

in init.sqf:

// Add the mission event handler
addMissionEventHandler ["EntityKilled", {
	// Get the passed parameters of interest
	params ["_killed", "", "_instigator"];

	// Check if instigator is player and the killed unit was on the same side with the instigator/player
	if ((_instigator isEqualTo player) && (playerSide isEqualTo (side group _killed))) then {
		// Get the amount of friendly kills of the player
		private _nKills = player getVariable ["YOU_nKills", 0];

		_nKills = _nKills + 1; // Increment (friendly) kills

		// Check amount of kills
		if (_nKills >= 2) then {
			execVM "friendly_Fire.sqf";
		};

		// Set the variable with the new number of kills (this is in case you didn't finish the game of course)
		player setVariable ["YOU_nKills", _nKills];
	};
}];

 

friendly_Fire.sqf:

removeMissionEventHandler ["EntityKilled", 0];
hint parseText "<t color='#FF0014'><t size='1.1'>!FRIENDLY FIRE WILL NOT BE TOLERATED!</t>";
playSound "alarm";
sleep 1;
v1 sideChat "Vančo have you gone completely nuts?! You'll face consequences for this!";
sleep 3;
["lose",false,5,false] spawn BIS_fnc_endMission;

 

Works like a charm.

 

As for the other issue I forgot to add, that condition !(player in thisList) and alive player and (rating player) > -2000 works perffectly. The trigger fires even when player eliminates friendlies and even, if it did't, it does not matter, as it would be game over anyway.

 

Thanks very much for your help.

 

 

  • Like 2

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

×