Jump to content
Tankbuster

fired hit ratio for players

Recommended Posts

For a bit of fun, my mission scoreboard displays (in a hint) the players kill death ratio and it's become a bit of fun for players, especially those who will happily wait for a long time to be revived rather than record a death.

 

It got me wondering, could I get a players fired hit ration too? IE, how many of their bullets fired actually hit an AI man target.  All of my AI already has a handledamage EH on them, so I've probably got some of the pieces of this puzzle already in place. There might be a problem though. Handledamage almost always fires multiple times when a man target is hit by a bullet type round. It happens so fast, I've never managed to get the first HD and filter out the remaining. Someone else might have an approach to this that I haven't tried yet?

 

KIlled EH won't record nonlethal hits and Hit EH won't record lethal hits (according to wiki) so I'd have to use a combination of those two. As it happens there is already a killed EH on all my enemy AI, but not a Hit EH. I'd be keen to avoid another new EH if possible. Or am I being too precious about server CPU here?

 

All thoughts gladly accepted.

 

 

  • Like 1

Share this post


Link to post
Share on other sites

Hello! Yes this can be done. It's fairly complicated and will take me an hour or so to write up a working example for you. If no one has done this by the time I have finished I will post it. Be back with you in a bit!

 

  • Like 1

Share this post


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

Hello! Yes this can be done. It's fairly complicated and will take me an hour or so to write up a working example for you. If no one has done this by the time I have finished I will post it. Be back with you in a bit!

 

Thanks! Don't need a working example as such - just a few pointers 🙂

  • Like 1

Share this post


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

Thanks! Don't need a working example as such - just a few pointers 🙂

Hello again. I quickly realized that without a working example to show you, it would be nigh impossible for me to explain myself. Don't worry, nothing crazy😎

 

initPlayerLocal.sqf

Spoiler

profilenamespace setVariable ["myShotsFired",0.00];
profilenamespace setVariable ["myShotsHit",0.00];

player addEventHandler ["Fired", {
	params ["_unit", "_weapon", "_muzzle", "_mode", "_ammo", "_magazine", "_projectile", "_gunner"];
	["addShot.sqf"] remoteExec ["execVM", (owner _unit)];
}];

player addAction 
[
	"<t color='#FF0000'>My Accuracy Rating</t><img size='1' image='\a3\ui_f\data\IGUI\Cfg\simpleTasks\types\use_ca'/>",  
	"
	_target = (_this select 0);
	_caller = (_this select 1);
	_targetPC = Owner _target;
	_A = profileNamespace getVariable 'myShotsFired';
	_B = profileNamespace getVariable 'myShotsHit';
	_shotsMissed = _A - _B;
	_ratio = _B / _shotsMissed;
	_accRate = _ratio *100;
	
	parseText format ['<t size=''1.0'' font=''PuristaMedium'' color=''#ef2525''>Accuracy: %1 Percent</t>', _accRate] remoteExec ['Hint',_targetPC,false];
	",
	[],1,false,true,"", ""
];

 

 

addShot.sqf

Spoiler

_val = profileNamespace getVariable "myShotsFired";
_val = _val + 1.00;
profileNamespace setVariable ["myShotsFired",_val];

 

 

addHit.sqf

Spoiler

_val = profileNamespace getVariable "myShotsHit";
_val = _val + 1.00;
profileNamespace setVariable ["myShotsHit",_val];

 

 

Code added to the initiation fields of all opfor units placed in the editor:

Spoiler

this disableAI "MOVE"; 
this disableAI "ANIM"; 
this disableAI "TARGET"; 
this disableAI "AUTOTARGET"; 
this disableAI "FSM"; 
this setUnitPos "UP"; 
this addEventHandler ["Hit", { 
 params ["_unit", "_source", "_damage", "_instigator"]; 
 ["addHit.sqf"] remoteExec ["execVM", (owner _instigator)]; 
}];

 

 

Explanation:

You're smart enough to understand what's going on, quicker by reading it, than by me trying to word it up and act smart😁. Let me know if you have any question. Happy to help!

  • Like 2

Share this post


Link to post
Share on other sites

That's pretty much what I had in mind, yes, thank you.

Note that a single shot kill won't register using your code though. Hit EH doesn't activate when a unit is killed.

  • Like 1

Share this post


Link to post
Share on other sites
2 hours ago, Tankbuster said:

That's pretty much what I had in mind, yes, thank you.

Note that a single shot kill won't register using your code though. Hit EH doesn't activate when a unit is killed.

 

You're right. I read that in the event handler description. You could use the same approach with a "Dammaged" event handler. If the damage received is enough to kill the unit out right , then remoteExec the addHit.sqf. That might work. 

Share this post


Link to post
Share on other sites

Use the HitPart EH.

this addEventHandler ["HitPart", { 
 (_this select 0) params ["_target", "_shooter", "_projectile", "_position", "_velocity", "_selection", "_ammo", "_vector", "_radius", "_surfaceType", "_isDirect"]; 
 hint format ["%1 hit %2 in the %3", name _shooter, name _target, _selection#0]; 
}];

Rx9q1jb.png

 

You can pull all sorts of interesting details from it, just be sure to note the information regarding locality in the Biki entry.

  • Like 1
  • Thanks 2

Share this post


Link to post
Share on other sites
On 2/28/2022 at 3:54 AM, Harzach said:

Use the HitPart EH.


this addEventHandler ["HitPart", { 
 (_this select 0) params ["_target", "_shooter", "_projectile", "_position", "_velocity", "_selection", "_ammo", "_vector", "_radius", "_surfaceType", "_isDirect"]; 
 hint format ["%1 hit %2 in the %3", name _shooter, name _target, _selection#0]; 
}];

Rx9q1jb.png

 

You can pull all sorts of interesting details from it, just be sure to note the information regarding locality in the Biki entry.

Apologies for the late reply and thanks. 🙂 I've just returned to this and yes, hitpart works for both one shot kills and a wounding. Excellent!
However, I notice that https://community.bistudio.com/wiki/Arma_3:_Event_Handlers#Projectile_Event_Handlers is coming with 2.10 where we can add HitPart to a projectile which might really simplify this. I think I might wait for that.

*edit.. actually, I'm not sure about waiting now... it's about whether it's best to add the EH to an enemy unit or a projectile. Hmmm

Share this post


Link to post
Share on other sites

I'm getting occasional duplicate activations of the EH when shooting single shot. 😞 That's going to make this extra tricky. Can't have hits > shots.

12:34:56 "&&& Tankbuster hit Rahim Amin in the spine1 "
12:35:19 "&&& Tankbuster hit Jamal Kakar in the leftleg "
12:35:30 "&&& Tankbuster hit Khairullah Kakar in the leftleg "
12:35:42 "&&& Tankbuster hit Akbar Bahadur in the leftlegroll "
12:35:45 "&&& Tankbuster hit Akbar Bahadur in the spine2 "
12:35:45 "&&& Tankbuster hit Akbar Bahadur in the spine3 "
12:35:51 "&&& Tankbuster hit Khairullah Kakar in the spine2 "
12:35:51 "&&& Tankbuster hit Khairullah Kakar in the spine1 "

 

Share this post


Link to post
Share on other sites

It might work to save the bullet object and check against it in further events, no other bullet should ever equal the one that was just fired

_handledShot = _target getVariable ["LastProjectile", objNull];
if (_projectile != _handledShot) then {
  	_target setVariable ["LastProjectile", _projectile];
  	//increment hit counter
};

 

  • Like 1
  • Thanks 1

Share this post


Link to post
Share on other sites
2 hours ago, Tankbuster said:

I'm getting occasional duplicate activations of the EH when shooting single shot.

 

Penetration!

 

@dreadedentity's idea to save/check each projectile is the most logical way I can think of to return just the first hit of each shot, but then that first hit may not be the killing blow, e.g. hit hand then head.

 

  • Like 1

Share this post


Link to post
Share on other sites
3 minutes ago, Harzach said:

but then that first hit may not be the killing blow

I think this is ok because he wanted to measure hits vs misses, not kills. Although he did mention kills/deaths ratio early in the thread which could add some confusion

  • Like 2

Share this post


Link to post
Share on other sites
4 hours ago, dreadedentity said:

It might work to save the bullet object and check against it in further events, no other bullet should ever equal the one that was just fired


_handledShot = _target getVariable ["LastProjectile", objNull];
if (_projectile != _handledShot) then {
  	_target setVariable ["LastProjectile", _projectile];
  	//increment hit counter
};

 

Yes, right. I see. Will this handle a unit getting hit by a burst of full auto?

Share this post


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

I think this is ok because he wanted to measure hits vs misses, not kills. Although he did mention kills/deaths ratio early in the thread which could add some confusion

Yeah, KD ratio is easy, that can be done from the scoreboard. I'm trying to get fired/hit ratio

Share this post


Link to post
Share on other sites
12 minutes ago, Tankbuster said:

Yes, right. I see. Will this handle a unit getting hit by a burst of full auto?

I would imagine that the actual firing of the event handler runs unscheduled, in other words my thinking is that all events are run until done (blocking everything else including frame rendering). So if that is the case then all events should happen in 1 frame and you will be more or less completely safe from automatic fire mixing up the code. Something that could happen however is player throws a grenade, then shoots somebody at the exact time the grenade explodes, but in that case I think there would be a different _projectile etc either way it should be easy to fix that if it causes a problem

  • Like 1

Share this post


Link to post
Share on other sites
5 minutes ago, dreadedentity said:

Something that could happen however is player throws a grenade

 

You can filter out splash damage if so desired:

this addEventHandler ["HitPart", { 
	(_this#0) params ["_target", "_shooter", "_projectile", "_position", "_velocity", "_selection", "_ammo", "_vector", "_radius", "_surfaceType", "_isDirect"]; 
	if (_isDirect) then {
		systemChat format ["%1 hit %2 in the %3", name _shooter, name _target, _selection#0]; 
	};
}];

 

Share this post


Link to post
Share on other sites
3 minutes ago, Harzach said:

 

You can filter out splash damage if so desired:


this addEventHandler ["HitPart", { 
	(_this#0) params ["_target", "_shooter", "_projectile", "_position", "_velocity", "_selection", "_ammo", "_vector", "_radius", "_surfaceType", "_isDirect"]; 
	if (_isDirect) then {
		systemChat format ["%1 hit %2 in the %3", name _shooter, name _target, _selection#0]; 
	};
}];

 

Oh! The isdirect! I didn't think of that. Testing it now.

Share this post


Link to post
Share on other sites

It won't filter the multi-hits, though.

Share this post


Link to post
Share on other sites
37 minutes ago, Tankbuster said:

Will this handle a unit getting hit by a burst of full auto?

 

How do you mean? The EH will fire any time the unit is hit. If the player fires a three round burst, and two rounds hit, the EH will fire every time those two rounds hit a body part.

Share this post


Link to post
Share on other sites
9 minutes ago, Harzach said:

It won't filter the multi-hits, though.

That's right, it doesn't. Single shot;

19:37:44 "&&& Tankbuster hit Arif Nazari in the leftarmroll, with a 1781187: tracer_red.p3d direct? true "
19:37:44 "&&& Tankbuster hit Arif Nazari in the spine3, with a 1781187: tracer_red.p3d direct? true "

But hopefully, I can only count the first hit with any given projectile

Share this post


Link to post
Share on other sites
10 minutes ago, Tankbuster said:

But hopefully, I can only count the first hit with any given projectile

 

This prints a systemChat on the first hit of each projectile, filtering out splash damage:

this addEventHandler ["HitPart", { 

	(_this#0) params ["_target", "_shooter", "_projectile", "_position", "_velocity", "_selection", "_ammo", "_vector", "_radius", "_surfaceType", "_isDirect"]; 
	
	_handledShot = _target getVariable ["LastProjectile", objNull];
	
	if (_projectile != _handledShot) then {
		_target setVariable ["LastProjectile", _projectile];
		
		if (_isDirect) then {
			systemChat format ["%1 hit %2 in the %3", name _shooter, name _target, _selection#0]; 
		};
	};
}];

 

  • Thanks 1

Share this post


Link to post
Share on other sites

Yes, thank you, guys! It's working 🙂

Tested full auto and it handles that just fine as you said it would. I added an alive clause because it's possible to shoot deads to increase your fired/hit ratio and I know players who would do that lol

		_x addEventHandler ["HitPart", { 

				(_this#0) params ["_target", "_shooter", "_projectile", "_position", "_velocity", "_selection", "_ammo", "_vector", "_radius", "_surfaceType", "_isDirect"]; 
				
				_handledShot = _target getVariable ["LastProjectile", objNull];
				
				if (_projectile != _handledShot and {alive _target}) then {
					_target setVariable ["LastProjectile", _projectile];
					
					if (_isDirect) then {
						diag_log format ["%1 hit %2 in the %3", name _shooter, name _target, _selection#0]; 
					};
				};
			}];

I'll store this info in an array on the server, one record per player. I already have a firedMan EH on players so I can count their shots. When players open the scoretable (that's when I display the KDR and FHR) the client will get the array from the server and work out the FHR and display it.

Once again, thank you, everyone.

  • Like 1

Share this post


Link to post
Share on other sites

Ah, yeah. I would wrap the whole code block in the alive check, though - no reason to go further if the target is dead.

 

		_x addEventHandler ["HitPart", { 

				(_this#0) params ["_target", "_shooter", "_projectile", "_position", "_velocity", "_selection", "_ammo", "_vector", "_radius", "_surfaceType", "_isDirect"]; 
				
				if (alive _target) then {
				
					_handledShot = _target getVariable ["LastProjectile", objNull];
				
					if (_projectile != _handledShot) then {
						_target setVariable ["LastProjectile", _projectile];
					
						if (_isDirect) then {
							diag_log format ["%1 hit %2 in the %3", name _shooter, name _target, _selection#0]; 
						};
					};
				};
			}];

 

  • Like 2

Share this post


Link to post
Share on other sites

Just wanted to drop in again to say instead of checking if the unit is alive every time, just check until it dies then remove the event handler

if (alive _target) {
  	//do EH code
} else {
	_target removeEventHandler [_thisEvent, _thisEventHandler];
};

A side note, it definitely sounds like this is for MP so make sure you remoteExec since event handlers have local effects

  • Like 2

Share this post


Link to post
Share on other sites

It is for MP. I did read the wiki notes on locality.

The units that are being hit are server local and I want the code to run there.

I've only tested localhost so far so will look out for issues.

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

×