Jump to content
HartofARMA

Counting AI Casualties (How to?)

Recommended Posts

Hello all, 

 

I do apologize as I had a similar question on the ARMA 2 board, but due to the activity over there it appears that it is time for me to upgrade to ARMA 3 (which I have just!) 

 

I've been working on a personal campaign of sorts where I am using the HETMAN Artificial Commander (created to Rydygier) to fight a series of company vs. company level battles across a given map area.  At the conclusion of these engagements I am keeping track of larger unit formations  at the battalion level in an excel spreadsheet tallying casualties via the "camera exec" script at the conclusion of a fight.  The outcome here then influences how I set up the next engagement across the island...nothing too fancy

 

In short, counting casualties after each engagement using the camera to roam a large battlefield is getting time consuming.... and the end mission statistics screen only shows player kills and group casualties.  Is there a better way of doing global casualty/vehicle destroyed statistics at the end of a mission?  Is it possible to open the statistics to all global stats? 

 

Ideally I wouldn't even mind a stats list of KIA through "class names" such as:  KIA US_Soldier_Crew_EP1 (x5), CZ_Soldier_Pilot_EP1 (x11) or better standard naming KIA US Crew (x5)/Czech Pilot (x11)

 

Any help or ideas would be appreciated, Thanks!

  • Like 1

Share this post


Link to post
Share on other sites

Have you tried adding an eventhandler  "killed" to the AI? 

You can then get the unit type with typeof

Are the AI spawned or editor placed?

 

On my phone so can't link to Bi-wiki. Sorry

  • Like 2

Share this post


Link to post
Share on other sites
On 10/28/2018 at 3:13 PM, KevsNoTrev said:

Have you tried adding an eventhandler  "killed" to the AI? 

You can then get the unit type with typeof

Are the AI spawned or editor placed?

 

On my phone so can't link to Bi-wiki. Sorry

 

Thanks Kev!

 

All AI are editor placed 

 

I did take a look at the "killed" event handler https://community.bistudio.com/wiki/Arma_3:_Event_Handlers#Killed but I am still unsure how to "print" the results in a post mission statistics screen/notepad/AAR.  I do admit that I am a poor when it comes to calling a .sqf and better at understanding basic triggers.  I've had some success with printing individual important unit deaths to the main play screen via Hint message using the simple: !alive unitname... but that could get a little out of hand with each unit being called out with its own unique name.  Any ideas? 

 

Thank you for your time!

Share this post


Link to post
Share on other sites

Is this multiplayer or single player?

Do you want to count all player kills or any death of AI and player including he AI bad driving and friendly fire kills?

 

I can throw something together but it may take a few days.

 

On another thought pattern. The diary from to the map screen records each players kills and team deaths. Is this accessible from anywhere??? This might be easier to capture for an AAR!

 

  • Like 2

Share this post


Link to post
Share on other sites
3 hours ago, KevsNoTrev said:

Is this multiplayer or single player?

Do you want to count all player kills or any death of AI and player including he AI bad driving and friendly fire kills?

 

I can throw something together but it may take a few days.

 

On another thought pattern. The diary from to the map screen records each players kills and team deaths. Is this accessible from anywhere??? This might be easier to capture for an AAR!

 

Thank you very much for responding again!

I intend this to only be used for single player - editor made missions without any spawn scripts (the units on the map are the only ones, though they would be numerous).  Indeed I wish for the death count to tally all dead on the map for blue/red/independent forces regardless of how the unit was lost.  I thought along the same lines for using the diary... though I can’t seem to find a way to make those player/group stats not just for the player/group but global.  If you are willing to throw something together I would be very grateful, but don’t sweat it if it seems arduous!

  • Like 1

Share this post


Link to post
Share on other sites

If I understand correctly you want to essentially show a list of all of the dead units and destroyed assets at the end of/during the mission for later tally.

One way to do this might be a combination of event handlers and a missionNamespace variable, such as an array, you push a classname or identifier to when the object is destroyed. At the end of the mission you could display the list in the debrief screen with a custom section entry in the description.ext, or dump the array to your RPT file using diag_log if you're just interested in gathering the data for later.

 

I like the idea of logging the entry to the diary instead, but that might be a bit tricky. This post suggests that if you leave the diary entry name the same each time you call to createDiaryRecord it will update the existing entry, so that may be a good way to go about it; adding an eventhandler that just calls createDiaryRecord with the same diary entry name and classname of the object destroyed passed to it.

  • Like 2

Share this post


Link to post
Share on other sites

have you looked at the allDead or allDeadMen commands? These give you array of all dead units & vehicles and then just he dead 'men respectively.

 

This would be far easier for an AAR than an eventhandler as it will be simpler to iterate through at the end of the mission before the end mission screen. This iwll also avoid a lot of wrok being done through the mission possibly lowering framerates.

ALthough in SP i'm not sure how this would give you the information before you get the 'you are dead' screen.

  • Like 2

Share this post


Link to post
Share on other sites

Thanks Kev and Spartan, I will do some tinkering and reading over this weekend! 

Share this post


Link to post
Share on other sites

i had a test of alldeadmen and alldead - when the garbage collector runs (or Zeus deleted deadunits) it will remove dead units/vehicles from these arrays. I thought this might happen so I confirmed it.

Moving on to an evenhandler now!

Just planning it out.

 

 

EDIT: Added code below - it needs actual unit names but this counts each unit that is killed and adds to the array. This should work for modded units as it uses the classname.

If you are using scripts then the <unit> setvariable ["spawnedSide",side <unit>];, will need to be added to the script to allow the spawned side to be retained. For some reason Arma EH's call all dead units Civ immediately after death...

Spoiler

AllDeadUnits = [
	["east",[]],	//format will add East units here then count the "typeof" unit and count in the following format
	["west",[]],	// e.g. ["east",[["O_soldier_TL_F",1]]]
	["independent",[]],
	["civilian",[]]
];

{   
    _x setvariable ["spawnedSide",side _x];

}  foreach allunits;

{
    _x addEventHandler ["Killed", {
        params ["_unit"];
		_sideDeadindex=-1;

    switch (_unit getvariable "spawnedSide") do {
        case East:{
			_sideDeadindex =0;
        };
        case West:{
            _sideDeadindex =1;
        };
        case Independent:{
            _sideDeadindex =2;
        };
        case Civilian:{
            _sideDeadindex =3;
        };
    };
    _path = [alldeadunits select _sideDeadindex,typeof _unit] call BIS_fnc_findNestedElement;
    diag_log format["Path: %1:",_path];
    if (_path isEqualTo []) then {
        (alldeadunits select _sideDeadindex) select 1 pushback [(typeof _unit), 1];
    } else {
        _path set [2,1];
        _value = [alldeadunits select _sideDeadindex,_path] call bis_fnc_returnnestedelement;
        diag_log format["Path: %1    Value: %2",_path,_value + 1];
        [alldeadunits select _sideDeadindex,_path,_value + 1] call BIS_fnc_setNestedElement;
    };

    }];
} foreach allUnits;

 

 

Ultimately, this is just trying to do what the statistics page of the diary does for us each mission!!

  • Like 2

Share this post


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

i had a test of alldeadmen and alldead - when the garbage collector runs (or Zeus deleted deadunits) it will remove dead units/vehicles from these arrays. I thought this might happen so I confirmed it.

Moving on to an evenhandler now!

Just planning it out.

 

 

EDIT: Added code below - it needs actual unit names but this counts each unit that is killed and adds to the array. This should work for modded units as it uses the classname.

If you are using scripts then the <unit> setvariable ["spawnedSide",side <unit>];, will need to be added to the script to allow the spawned side to be retained. For some reason Arma EH's call all dead units Civ immediately after death...

  Reveal hidden contents


AllDeadUnits = [
	["east",[]],	//format will add East units here then count the "typeof" unit and count in the following format
	["west",[]],	// e.g. ["east",[["O_soldier_TL_F",1]]]
	["independent",[]],
	["civilian",[]]
];

{   
    _x setvariable ["spawnedSide",side _x];

}  foreach allunits;

{
    _x addEventHandler ["Killed", {
        params ["_unit"];
		_sideDeadindex=-1;

    switch (_unit getvariable "spawnedSide") do {
        case East:{
			_sideDeadindex =0;
        };
        case West:{
            _sideDeadindex =1;
        };
        case Independent:{
            _sideDeadindex =2;
        };
        case Civilian:{
            _sideDeadindex =3;
        };
    };
    _path = [alldeadunits select _sideDeadindex,typeof _unit] call BIS_fnc_findNestedElement;
    diag_log format["Path: %1:",_path];
    if (_path isEqualTo []) then {
        (alldeadunits select _sideDeadindex) select 1 pushback [(typeof _unit), 1];
    } else {
        _path set [2,1];
        _value = [alldeadunits select _sideDeadindex,_path] call bis_fnc_returnnestedelement;
        diag_log format["Path: %1    Value: %2",_path,_value + 1];
        [alldeadunits select _sideDeadindex,_path,_value + 1] call BIS_fnc_setNestedElement;
    };

    }];
} foreach allUnits;

 

 

Ultimately, this is just trying to do what the statistics page of the diary does for us each mission!!

 

Thank you so much Kev!  That is exactly what I was looking for! 

I'll be singing your praises every time that I use that eventhandler! 

Share this post


Link to post
Share on other sites

Glad you like it. be aware I did not put the details in a hint or diary system.

I tried diary but it kept scrolling the page and adding the info each time a death occurred. 

I would suggest rsc overlay might work better you'll have more artistic control.

Share this post


Link to post
Share on other sites

Hey there, I know this is a quite old post but a solution must have already been found (like the last post from HartOfARMA mentions).

 

Just wanted to present a quick alternative to simply count how many units died from each side. You could just create a variable or add one to the mission namespace. I will present the mission namespace case which may be a wee bit more generic.

 

So you can count the units that are alive on each at the beginning of the game and then again at the end of the game and get the difference as the dead units. This could possibly look something like

/*
 * This part goes in a file like the init.sqf or initServer.sqf
 */
// Set the variables with the units of each side
missionNamespace setVariable ["bluUnits", west countSide allUnits];
missionNamespace setVariable ["opUnits", east countSide allUnits];
missionNamespace setVariable ["indUnits", independent countSide allUnits];
missionNamespace setVariable ["civUnits", civilian countSide allUnits];

/*
 * This part must be executed at the end of the mission
 */
// Calculate the BluFor dead units
private _bluDead = missionNamespace getVariable "bluUnits";
_bluDead = _bluDead - (west countSide allUnits);

// Calculate the OpFor dead units
private _opDead = missionNamespace getVariable "opUnits";
_opDead = _opDead - (east countSide allUnits);

// Calculate the IndFor dead units
private _indDead = missionNamespace getVariable "indUnits";
_indDead = _indDead - (independent countSide allUnits);

// Calculate the civilian dead units
private _civDead = missionNamespace getVariable "civUnits";
_civDead = _civDead - (civilian countSide allUnits);

// Post the results
systemChat ("BluFor casaulties: " + (str _bluDead));
systemChat ("OpFor casaulties: " + (str _opDead));
systemChat ("IndFor casaulties: " + (str _indDead));
systemChat ("Civilian casaulties: " + (str _civDead));

Of course, you could choose another way to log or show the results, this is just a short example. Additionally, you may choose to one-line the dead calculation like

private _bluDead = (missionNamespace getVariable "bluUnits") - (west countSide allUnits);

And, of course you could also use the event handler approach in conjunction with the setVariable command (most probably set a variable in the mission namespace like in the example above). This could possibly look something like (I also present a way to add the event handler to all the units)

/*
 * This should be executed in a script like init.sqf or initServer.sqf
 */
// Set the namespace variables
missionNamespace setVariable ["bluDead", 0];
missionNamespace setVariable ["opDead", 0];
missionNamespace setVariable ["indDead", 0];
missionNamespace setVariable ["civDead", 0];

// Add the event handler to all the units on the map
allUnits apply {
	// Add the event handler
	_x addEventHandler ["Killed", {
		params["_unit"]; // Get the unit that was killed

		// Check the side of the dead unit
		switch (side _unit) do {
			// Increase the dead counter of the appropriate side
			case west: {missionNamespace setVariable ["bluDead", (missionNamespace getVariable "bluDead") + 1]};
			case east: {missionNamespace setVariable ["opDead", (missionNamespace getVariable "opDead") + 1]};
			case independent: {missionNamespace setVariable ["indDead", (missionNamespace getVariable "indDead") + 1]};
			case civilian: {missionNamespace setVariable ["civDead", (missionNamespace getVariable "civDead") + 1]};
		};
	}];
};


/* 
 * This part should be executed at the mission end
 */
// Post the results
systemChat ("BluFor casaulties: " + (str (missionNamespace getVariable "bluDead")));
systemChat ("OpFor casaulties: " + (str (missionNamespace getVariable "opDead")));
systemChat ("IndFor casaulties: " + (str (missionNamespace getVariable "indDead")));
systemChat ("Civilian casaulties: " + (str (missionNamespace getVariable "civDead")));

Finally, you could have used a local or global variable instead of setting one in the mission namespace. This could save you some long one-lined commands and make the code look a wee bit cleaner. The result should be the same though.

 

Please note that the presented code is not tested and should be treated with caution. I do hope that wit will, at least, provide some help to people who will have a look at this post in the future.

  • Thanks 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

×