Jump to content
Sign in to follow this  
galzohar

How to setDamage without affecting part damage? (for an improved damage/armor system)

Recommended Posts

In order to avoid irrelevant answers, a quick rundown of how things work. Skip this if you already know these stuff.

When you shoot a soldier/vehicle, you deal damage to the part you hit and possibly to other parts as well. Then, based on some config values the "overall" damage of the vehicle is updated as well.

In the end, each vehicle tracks the damage value of each of its parts, as well as an "overall" damage value.

When the "overall" damage reaches 1, vehicle is destroyed.

Each part can reach a maximum of 1 damage, which will then affect the vehicle behavior (maybe also with values of less than 1, my research didn't go that far), and some parts reaching 1 will also destroy the vehicle, but I'm not sure what it's based on either (config values? Hard-coded stuff? My research didn't go that far).

The calculation of the damage dealt to each part and the damage dealt to the "overall" seem to be 2 completely separate calculations dependent on different independent config values.

The question

Is there anything that can set the overall damage of a unit without affecting its individual part damage?

Using setDamage will also set the damage of all parts to the given value.

What I'm trying to do is prototype a damage system that overrides the current "passthrough" system (at least for infantry), and modify the "overall" damage based on damage dealt to parts in a way that works around needing to use the "passthrough" config value on one hand, while on the other hand allow taking the individual part's armor into account (which is currently ignored). This will then allow creating an improved and more intuitive representation of protective equipment, as the current system is completely messed up.

However, if I can't set the "overall" damage to a value of my choice without affecting the overall damage, I'll have to make something dirtier which will make the "overall" damage always 0 and set it to 1 once you're dead, which will break compatibility with any scripts that use getDamage to check for damage, and I'd like to avoid that if possible.

Thanks for any help!

Share this post


Link to post
Share on other sites

BIS_fnc_helicopterGetHitpoints can return a list of a vehicles hitpoints in a nice array. The easiest method I have found.

It works on all vehicles. It's from TOH so that's where the name comes from.

Share this post


Link to post
Share on other sites
setHitPointDamage is your best bet probably, though it could get a bit hairy since i'd assume it'll be modifying the overall damage as well.

That seems to be the Take On Helicopters variant of the setHit, which seems to only work with an actual selection, and not for the "overall" damage.

I think my solution will be to simply change the value returned by the handleDamage event handler, so the engine sets the damage itself. Hopefully it will achieve what I'm trying to achieve.

The problem so far is that you can't setHit to "" selection, and the event handler for the "" selection is fired before the one for "body" (in case of a body shot).

Edited by galzohar

Share this post


Link to post
Share on other sites

Not sure I understand what you need. If you want only general damage to pass through and block damage to parts then you can just:

[color="#FF8040"]car [color="#191970"][b]addEventHandler[/b][/color] [color="#8B3E2F"][b][[/b][/color][color="#7A7A7A"]"HandleDamage"[/color][color="#8B3E2F"][b],[/b][/color] [color="#8B3E2F"][b]{[/b][/color]
[color="#191970"][b]if[/b][/color] [color="#8B3E2F"][b]([/b][/color][color="#000000"]_this[/color] [color="#191970"][b]select[/b][/color] [color="#FF0000"]1[/color] [color="#8B3E2F"][b]=[/b][/color][color="#8B3E2F"][b]=[/b][/color] [color="#7A7A7A"]""[/color][color="#8B3E2F"][b])[/b][/color] [color="#191970"][b]then[/b][/color] [color="#8B3E2F"][b]{[/b][/color]
	[color="#000000"]_this[/color] [color="#191970"][b]select[/b][/color] [color="#FF0000"]2[/color]
[color="#8B3E2F"][b]}[/b][/color] [color="#191970"][b]else[/b][/color] [color="#8B3E2F"][b]{[/b][/color]
	[color="#FF0000"]0[/color]
[color="#8B3E2F"][b]}[/b][/color]
[color="#8B3E2F"][b]}[/b][/color][color="#8B3E2F"][b]][/b][/color][color="#8B3E2F"][b];[/b][/color][/color]

Made with KK's SQF to BBCode Converter

But you will never be able to shoot wheels of or break glass or whatever, and the amount of general damage passing through is small unless you stick c4 on it.

Share this post


Link to post
Share on other sites

The problem is that I want to change the damage passed to the "" selection based on the damage dealt to the parts via a script function, and work around the "passthrough" config value.

Since the "" selection is triggered before the other selections, I unfortunately can't just modify that because at that point the information for the specific part damage still does not exist.

A dirty alternative suggested by Adanteh is to setDamage and then setHit on all the parts back to what they were before, effectively setting the damage to the "" selection.

Edited by galzohar

Share this post


Link to post
Share on other sites

"Hit" EH triggers after "HandleDamage" with the same value as "" in "HandleDamage". What I don't get is how are you going to mix damage from parts with overall damage. For example:

[car,"",0.0461501,B Alpha 1-1:1 (KK),"GrenadeHand"]

[car,"wheel_1_1_steering",5.96743,B Alpha 1-1:1 (KK),"GrenadeHand"]

[car,"wheel_1_2_steering",5.96743,B Alpha 1-1:1 (KK),"GrenadeHand"]

[car,"wheel_2_1_steering",5.96743,B Alpha 1-1:1 (KK),"GrenadeHand"]

[car,"wheel_2_2_steering",5.96743,B Alpha 1-1:1 (KK),"GrenadeHand"]

[car,"palivo",0.372964,B Alpha 1-1:1 (KK),"GrenadeHand"]

[car,"motor",0.372964,B Alpha 1-1:1 (KK),"GrenadeHand"]

[car,"karoserie",0.0932411,B Alpha 1-1:1 (KK),"GrenadeHand"]

[car,"glass1",1.49186,B Alpha 1-1:1 (KK),"GrenadeHand"]

[car,"glass2",1.49186,B Alpha 1-1:1 (KK),"GrenadeHand"]

[car,"?",0,B Alpha 1-1:1 (KK),"GrenadeHand"]

[car,"?",0,B Alpha 1-1:1 (KK),"GrenadeHand"]

[car,"?",0,B Alpha 1-1:1 (KK),"GrenadeHand"]

[car,"?",0,B Alpha 1-1:1 (KK),"GrenadeHand"]

[car,"?",0,B Alpha 1-1:1 (KK),"GrenadeHand"]

[car,"?",0,B Alpha 1-1:1 (KK),"GrenadeHand"]

[car,"?",0,B Alpha 1-1:1 (KK),"GrenadeHand"]

[car,"?",0,B Alpha 1-1:1 (KK),"GrenadeHand"]

[car,"?",0,B Alpha 1-1:1 (KK),"GrenadeHand"]

[car,"?",0,B Alpha 1-1:1 (KK),"GrenadeHand"]

[car,"light_l",9.32411,B Alpha 1-1:1 (KK),"GrenadeHand"]

[car,"light_r",9.32411,B Alpha 1-1:1 (KK),"GrenadeHand"]

[car,"light_l",9.32411,B Alpha 1-1:1 (KK),"GrenadeHand"]

[car,"light_r",9.32411,B Alpha 1-1:1 (KK),"GrenadeHand"]

[car,"light_l",9.32411,B Alpha 1-1:1 (KK),"GrenadeHand"]

[car,"light_r",9.32411,B Alpha 1-1:1 (KK),"GrenadeHand"]

what your overall damage would be in this case?

Share this post


Link to post
Share on other sites

Obviously this would be vehicle-specific, and be some sort of weighted sum of the bunch.

The main purpose is adjusting how infantry armor protection works, and for that some kind of formula like 0.8 * body damage + 0.8 * head damage + 0.12 * leg damage + 0.12 * hand damage (may need tweaking) can work.

To make it more generic of course config values would need to be added to every vehicle, but that is out of the scope right now. Getting infantry to work in a more consistent fashion is the primary objective.

This is the script I'm testing right now which seems to work:

galz_fnc_realDamageHandler =
{
private ["_unit", "_selection", "_newDamage", "_projectile", "_oldHeadDamage", "_oldBodyDamage", "_oldHandsDamage", "_oldLegsDamage"];
_unit = _this select 0;
_selection = _this select 1;
_newDamage = _this select 2;
_projectile = _this select 3;
diag_log format ["%1 %2", str _this, if (_selection != "") then {p1 getHit _selection} else {getDammage p1}];

_retVal = _newDamage;

// Disable default damage handling to ""  selection (overall damage). It's handled later.
if (_selection == "") then
{
	_retVal = damage _unit;
}
else
{
	// Only handle damage if there is an actual projectile.
	// Not sure what triggers handleDamage without a projectile.
	if (!isNull _projectile) then
	{
		_addedOverallDamage = _newDamage - (_unit getHit _selection);;
		if (_selection == "head" || _selection == "body") then
		{
			_addedOverallDamage = _addedOverallDamage * 0.8;
		}
		else
		{
			_addedOverallDamage = _addedOverallDamage * 0.12;
		};
		if (_addedOverallDamage > 0.01) then
		{
			// Save old selection damage.
			_oldHeadDamage = _unit getHit "head";
			_oldBodyDamage = _unit getHit "body";
			_oldHandsDamage = _unit getHit "hands";
			_oldLegsDamage =  _unit getHit "legs";

			// Set overall damage.
			diag_log format ["old damage: %1, new damage: %2, selection: %3", damage _unit, (damage _unit) + _addedOverallDamage, _selection];
			_unit setDamage ((damage _unit) + _addedOverallDamage);

			// Restore part damage.
			_unit setHit ["head", _oldHeadDamage];
			_unit setHit ["body", _oldBodyDamage];
			_unit setHit ["hands", _oldHandsDamage];
			_unit setHit ["legs", _oldLegsDamage];
		};
	};
};

_retVal
};

p1 addEventHandler ["handleDamage", {_this call galz_fnc_realDamageHandler}];

Share this post


Link to post
Share on other sites

Unfortunately there is no such scripting command, plus even if you save all selections damages, then do setDamage and then restore selections damages back, some selection damages will be lost because there are unnamed selections as well as selections not listed in vehicle hitpoints or improperly configured selections (plenty in A2, not sure about A3) which you can not modify with setHit or setHitPointDamage. Here is said solution with saving and then restoring hit points damage that I use in King of the Hill, modified a bit for your needs:

func_getAllHitPointsFromConfig = {
private ["_hitpoints", "_hps_fnc", "_cfg", "_class", "_uniform", "_trt", "_trts", "_trts2"];
_hitpoints = [];

_hps_fnc = {
	private "_hitpoint";

	for "_i" from 0 to count(_this) - 1 do {
		_hitpoint = configName(_this select _i);
		if(_hitpoints find _hitpoint == -1) then {
			_hitpoints set [count _hitpoints, _hitpoint];
		};
	};
};

_uniform = (if(uniform _this == "") then {getText(configFile >> "CfgVehicles" >> (typeOf _this) >> "nakeduniform")} else {uniform _this});
_class = (if(_uniform == "") then {typeOf _this} else {getText(configFile >> "CfgWeapons" >> _uniform >> "ItemInfo" >> "uniformclass")});

_cfg = configFile >> "CfgVehicles" >> (typeOf _this);
for "_i" from 0 to 0 do {
	if(_i > 0) then {_cfg = inheritsFrom _cfg};
	if(!isClass(_cfg)) exitWith {};

	if(isClass(_cfg >> "HitPoints")) then {
		(_cfg >> "HitPoints") call _hps_fnc;

		_trts = _cfg >> "turrets";
		for "_i" from 0 to (count _trts - 1) do {
			_trt = _trts select _i;
			_trts2 = _trt >> "turrets";

			(_trt >> "HitPoints") call _hps_fnc;

			for "_j" from 0 to (count _trts2 - 1) do {
				((_trts2 select _j) >> "HitPoints") call _hps_fnc;
			};
		};
	};
};

_hitpoints
};

func_setOverallDamage = {
private ["_vehicle", "_damage", "_hitdamages"];
_vehicle = _this select 0;
_damage = _this select 1;
_hitdamages = [];
{
	_hitdamages pushBack [_x, (_vehicle getHitPointDamage _x)];
} forEach (_vehicle call func_getAllHitPointsFromConfig);

_vehicle setDamage _damage;

{
	_vehicle setHitPointDamage [_x select 0, _x select 1];
} forEach _hitdamages;
};

[cursorTarget, 0.9] call func_setOverallDamage;

This script scans config for all hit points, including uniform-specific hitpoints since they're sometimes different from hitpoints of base unit class, saves damage for all hitpoints, does setDamage and then restores hitpoints damage back. It is not ideal solution, as I said it will not restore damages of some selections for reasons listed above, but this is best of what we can do with sqf because of lack of proper damage manipulation commands.

Edited by SaMatra

Share this post


Link to post
Share on other sites

Thanks!

But is it really necessary to create arrays from config after every shot? Wouldn't it be better to check if uniform changed since last time and store the values for future use only when uniform changes? Or is this so performance inexpensive it's not worth bothering with? Considering it gets triggered on every handleDamage event handler.

Share this post


Link to post
Share on other sites
Thanks!

But is it really necessary to create arrays from config after every shot? Wouldn't it be better to check if uniform changed since last time and store the values for future use only when uniform changes? Or is this so performance inexpensive it's not worth bothering with? Considering it gets triggered on every handleDamage event handler.

Yes, I do "cache" it in setVariable on the vehicle, I didn't include this so script wouldn't be even more complicated. For units you'll need to check for previous uniform though.

Share this post


Link to post
Share on other sites

Thanks, will try implement something like that.

What is the purpose of the for "_i" from 0 to 0? Is this just a leftover from the original script or does the inner loop modify _i in the outer scope? I usually just try to avoid these questions by not using the same variable name twice in the same function and only use for loops for loops with a pre-determined trip count, so I don't really know how these cases would behave...

Additionally, I want head/body to pass more damage to the overall damage than hands/legs. Is there any way to do it while keeping it compatible with potential mods that add new units? Or will it require using a new config value that new units with new hit locations will need to define for the script to work?

Edited by galzohar

Share this post


Link to post
Share on other sites
Thanks, will try implement something like that.

What is the purpose of the for "_i" from 0 to 0? Is this just a leftover from the original script or does the inner loop modify _i in the outer scope? I usually just try to avoid these questions by not using the same variable name twice in the same function and only use for loops for loops with a pre-determined trip count, so I don't really know how these cases would behave...

Additionally, I want head/body to pass more damage to the overall damage than hands/legs. Is there any way to do it while keeping it compatible with potential mods that add new units? Or will it require using a new config value that new units with new hit locations will need to define for the script to work?

Sorry for late response, but here it is

for "_i" from 0 to 0 was used to include base class hit points, it is no longer used and you can remove it (as well as if(_i > 0) check).

I didn't quite understand your question, are you talking about HandleDamage and (possible) different selection names for body parts?

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
Sign in to follow this  

×