Jump to content
HartofARMA

Reduce Damage Modifier Globally (all spawned AI)

Recommended Posts

Hello all, 

 

I've posted some sources that I have referenced below, which have helped me to further my own understanding of EventHandlers/HandleDamage and reducing damage to specific units.   I am successfully able to use the scripts on individual units but I am struggling in my understanding as to how to apply these modifiers globally (to all units starting or spawned in later during a mission) via singleplayer init.sqf script or other mission start script.  

 

For instance, if I take one of the scripts like the one posted in the spoiler (removing the // of course) why can't I use instead of "player" use something like "allunits" to apply the damage modifier globally to... well... all units?  Additionally, if I did get this working would the script cause more lag/latency by being applied to every unit on the map?  Thanks for any advice!  My main goal is to toughen up some of the infantry units in the global mobilization DLC which do not have body armor like their 2035 cousins.  

 

Spoiler

 

// Damage reduction from https://forums.bistudio.com/forums/topic/198136-reducing-player-damage/

player removeAllEventHandlers 'HandleDamage';

// Only damage from last applied handleDamage EH is taken into consideration by the engine
// Apply a new EH so as we can override the damage applied
player addEventHandler [ "HandleDamage", {
    params ["_unit", "_hitSelection", "_damage","_source","_projectile","_hitPartIndex", "_instigator", "_hitPoint"];

    // Damage multipliers.  The damage of each projectile will be multiplied by this number.
    private _damageMultiplierHead = 0.3;
    private _damageMultiplierBody = 0.25;
    private _damageMultiplierLimbs = 0.15;
    private _damageMultiplierOverall = 0.25;

    // Damage limits.  Each projectile will be limited to a max of this much damage.
    private _limitHead = 1.0;
    private _limitBody = 0.25;
    private _limitLimbs = 0.1;
    private _limitOverall = 0.25;

    private _oldDamage = 0;
    if (_hitSelection isEqualTo "") then {_oldDamage = damage _unit} else {_oldDamage = _unit getHit _hitSelection};
    private _newDamage = _damage - _oldDamage max 0;
    private _incomingDamage = _newDamage;
    private _playerHealth = damage _unit;

    // Infantry selections
    // Keep in mind that if revive is enabled then incapacitation may occur at around 0.7 damage.
    // "": The overall damage that determines the damage value of the unit. Unit dies at damage equal to or above 1
    // "face_hub": Unit dies at damage equal to or above 1
    // "neck": Unit dies at damage equal to or above 1
    // "head": Unit dies at damage equal to or above 1
    // "pelvis": Unit dies at damage equal to or above 1
    // "spine1": Unit dies at damage equal to or above 1
    // "spine2": Unit dies at damage equal to or above 1
    // "spine3": Unit dies at damage equal to or above 1
    // "body": Unit dies at damage equal to or above 1
    // "arms": Unit doesn't die with damage to this part
    // "hands": Unit doesn't die with damage to this part
    // "legs": Unit doesn't die with damage to this part

    // Do any other damage calculations here
    // _damage is the previous damage plus any new damage and will be applied
    // as the total damage the unit has for this selection once this EH returns

    // Only modify damage if it is a known projectile (leave falling damage etc alone)
    if (_newDamage > 0 && !(_projectile isEqualTo "")) then {
        // Reduce damage by damage multiplier
        private _damageMultiplier = _damageMultiplierBody;
        private _upperLimit = _limitBody;
        switch (_hitSelection) do {
            case "face_hub";
            case "head": {
                _damageMultiplier = _damageMultiplierHead;
                _upperLimit = _limitHead;
            };
            case "arms";
            case "hands";
            case "legs": {
                _damageMultiplier = _damageMultiplierLimbs;
                _upperLimit = _limitLimbs;
            };
            case "": {
                _damageMultiplier = _damageMultiplierOverall;
                _upperLimit = _limitOverall;
            };
            default { 
                _damageMultiplier = _damageMultiplierBody;
                _upperLimit = _limitBody;
            };
        };
        _newDamage = _newDamage * _damageMultiplier;

        // Place an upper limit on projectile damage done at once
        if (_newDamage > _upperLimit) then {
            _newDamage = _upperLimit;
        };

        _damage = _oldDamage + _newDamage;
    };
    
    // For players ignore damage if they are incapacitated and pass damage to bis revive handler
    if ( isPlayer _unit ) then {
        if ( lifeState _unit == "INCAPACITATED" ) then {
            //if we are incapacitated take no additional damage
            _damage = _oldDamage;
        } else {
            _this set[ 2, _damage ];
            //Call BI REVIVE HandleDamage EH passing new _damage value
            _damage = _this call bis_fnc_reviveEhHandleDamage;
        };
    };


    _damage
    
}];

 

 

 

 

Share this post


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

to apply these modifiers globally (to all units starting or spawned in later during a mission) via singleplayer init.sqf script or other mission start script.  

 

How are these units created?  Do you spawn them with script, via zeus, ...?

 

The easiest way to add that EH to all units would be via a while true loop over allUnits that adds it to any unit that doesn't have it yet.  The best way to do it would be to create your own function that you call instead of createUnit when your mission wants to create a unit and apply the EH in that function. 

  • Like 2

Share this post


Link to post
Share on other sites

@stanhope very happy for your reply, as I noticed you commented in the source threads!   

 

To begin the mission there are about 30 groups at mission start and there are scripted triggers using BIS_fnc_spawnGroup for the rest that spawn in periodically throughout the mission.

 

Is the answer to this perhaps to add: _loopHandle = spawn "damage.sqf";  (where my own script is called by the player's init line?

 

and then add damage.sqf as follows: 

Spoiler

 

 

while {true} do {

 

allunits removeAllEventHandlers 'HandleDamage';


allunits addEventHandler [ "HandleDamage", {
    params ["_unit", "_hitSelection", "_damage","_source","_projectile","_hitPartIndex", "_instigator", "_hitPoint"];


    private _damageMultiplierHead = 0.3;
    private _damageMultiplierBody = 0.25;
    private _damageMultiplierLimbs = 0.15;
    private _damageMultiplierOverall = 0.25;


    private _limitHead = 1.0;
    private _limitBody = 0.25;
    private _limitLimbs = 0.1;
    private _limitOverall = 0.25;

    private _oldDamage = 0;
    if (_hitSelection isEqualTo "") then {_oldDamage = damage _unit} else {_oldDamage = _unit getHit _hitSelection};
    private _newDamage = _damage - _oldDamage max 0;
    private _incomingDamage = _newDamage;
    private _playerHealth = damage _unit;


    if (_newDamage > 0 && !(_projectile isEqualTo "")) then {
        private _damageMultiplier = _damageMultiplierBody;
        private _upperLimit = _limitBody;
        switch (_hitSelection) do {
            case "face_hub";
            case "head": {
                _damageMultiplier = _damageMultiplierHead;
                _upperLimit = _limitHead;
            };
            case "arms";
            case "hands";
            case "legs": {
                _damageMultiplier = _damageMultiplierLimbs;
                _upperLimit = _limitLimbs;
            };
            case "": {
                _damageMultiplier = _damageMultiplierOverall;
                _upperLimit = _limitOverall;
            };
            default { 
                _damageMultiplier = _damageMultiplierBody;
                _upperLimit = _limitBody;
            };
        };
        _newDamage = _newDamage * _damageMultiplier;


        if (_newDamage > _upperLimit) then {
            _newDamage = _upperLimit;
        };

        _damage = _oldDamage + _newDamage;
    };
    
    if ( isPlayer _unit ) then {
        if ( lifeState _unit == "INCAPACITATED" ) then {
            _damage = _oldDamage;
        } else {
            _this set[ 2, _damage ];
            _damage = _this call bis_fnc_reviveEhHandleDamage;
        };
    };


    _damage
    };
}];

 

 

I tested that out but no luck thus far... 

Share this post


Link to post
Share on other sites

Try something like:

while {true} do {
	{	
		if (_x getVariable ["damage_modifier_EH_applies", false]) then {continue;};
		_x addEventhandler ["HandleDamage", {
			/*Add your eventhandler code here*/
		}];
		_x setVariable ["damage_modifier_EH_applies", true];
		uiSleep 0.1;
	} forEach allUnits;
	uiSleep 5;
};

Untested

  • Like 2

Share this post


Link to post
Share on other sites

Thanks @stanhope,  I now simply call the script  in player inti:  [player] execVM "damage.sqf";

 

remove extras: 

allunits removeAllEventHandlers 'HandleDamage';

allunits addEventHandler [ "HandleDamage", {

 

and move on with the following but throwing a few errors...am I so far off below?: 

 

My thinking is that it is something to do with the lines near bis_fnc_reviveEhHandleDamage, to which is not really important to keep in there from the source code if causing an issue 

 

Spoiler

while {true} do {
    {    
        if (_x getVariable ["damage_modifier_EH_applies", false]) then {continue;};
        _x addEventhandler ["HandleDamage", {

        
 params ["_unit", "_hitSelection", "_damage","_source","_projectile","_hitPartIndex", "_instigator", "_hitPoint"];


    private _damageMultiplierHead = 0.3;
    private _damageMultiplierBody = 0.25;
    private _damageMultiplierLimbs = 0.15;
    private _damageMultiplierOverall = 0.25;


    private _limitHead = 1.0;
    private _limitBody = 0.25;
    private _limitLimbs = 0.1;
    private _limitOverall = 0.25;

    private _oldDamage = 0;
    if (_hitSelection isEqualTo "") then {_oldDamage = damage _unit} else {_oldDamage = _unit getHit _hitSelection};
    private _newDamage = _damage - _oldDamage max 0;
    private _incomingDamage = _newDamage;
    private _playerHealth = damage _unit;


    if (_newDamage > 0 && !(_projectile isEqualTo "")) then {
        private _damageMultiplier = _damageMultiplierBody;
        private _upperLimit = _limitBody;
        switch (_hitSelection) do {
            case "face_hub";
            case "head": {
                _damageMultiplier = _damageMultiplierHead;
                _upperLimit = _limitHead;
            };
            case "arms";
            case "hands";
            case "legs": {
                _damageMultiplier = _damageMultiplierLimbs;
                _upperLimit = _limitLimbs;
            };
            case "": {
                _damageMultiplier = _damageMultiplierOverall;
                _upperLimit = _limitOverall;
            };
            default { 
                _damageMultiplier = _damageMultiplierBody;
                _upperLimit = _limitBody;
            };
        };
        _newDamage = _newDamage * _damageMultiplier;


        if (_newDamage > _upperLimit) then {
            _newDamage = _upperLimit;
        };

        _damage = _oldDamage + _newDamage;
    };
    
    if ( isPlayer _unit ) then {
        if ( lifeState _unit == "INCAPACITATED" ) then {
            _damage = _oldDamage;
        } else {
            _this set[ 2, _damage ];
            _damage = _this call bis_fnc_reviveEhHandleDamage;
        };
    };


    _damage
  }];    
                
    
        }];
        _x setVariable ["damage_modifier_EH_applies", true];
        uiSleep 0.1;
    } forEach allUnits;
    uiSleep 5;
};

Share this post


Link to post
Share on other sites

The code you provided has an extra "}];" in it which will throw a syntax error, here's a version without the syntax error (still untested):

Spoiler

private _EHCode = {
    params ["_unit", "_hitSelection", "_damage","_source","_projectile","_hitPartIndex", "_instigator", "_hitPoint"];
    private _damageMultiplierHead = 0.3;
    private _damageMultiplierBody = 0.25;
    private _damageMultiplierLimbs = 0.15;
    private _damageMultiplierOverall = 0.25;

    private _limitHead = 1.0;
    private _limitBody = 0.25;
    private _limitLimbs = 0.1;
    private _limitOverall = 0.25;

    private _oldDamage = 0;
    if (_hitSelection isEqualTo "") then {_oldDamage = damage _unit} else {_oldDamage = _unit getHit _hitSelection};
    private _newDamage = _damage - _oldDamage max 0;
    private _incomingDamage = _newDamage;
    private _playerHealth = damage _unit;


    if (_newDamage > 0 && !(_projectile isEqualTo "")) then {
        private _damageMultiplier = _damageMultiplierBody;
        private _upperLimit = _limitBody;
        switch (_hitSelection) do {
            case "face_hub";
            case "head": {
                _damageMultiplier = _damageMultiplierHead;
                _upperLimit = _limitHead;
            };
            case "arms";
            case "hands";
            case "legs": {
                _damageMultiplier = _damageMultiplierLimbs;
                _upperLimit = _limitLimbs;
            };
            case "": {
                _damageMultiplier = _damageMultiplierOverall;
                _upperLimit = _limitOverall;
            };
            default {
                _damageMultiplier = _damageMultiplierBody;
                _upperLimit = _limitBody;
            };
        };
        _newDamage = _newDamage * _damageMultiplier;


        if (_newDamage > _upperLimit) then {
            _newDamage = _upperLimit;
        };

        _damage = _oldDamage + _newDamage;
    };

    if ( isPlayer _unit ) then {
        if ( lifeState _unit == "INCAPACITATED" ) then {
            _damage = _oldDamage;
        } else {
            _this set[ 2, _damage ];
            _damage = _this call bis_fnc_reviveEhHandleDamage;
        };
    };

    _damage
};

while {true} do {
    {
        if (_x getVariable ["damage_modifier_EH_applies", false]) then {continue;};
        _x addEventHandler ["HandleDamage", _EHCode];
        _x setVariable ["damage_modifier_EH_applies", true];
        uiSleep 0.1;
    } forEach allUnits;
    uiSleep 5;
};

 

 

If this also throws errors, go to "%localAppData%\Arma 3" (open windows explorer and put it in the bar at the top), open the newest .rpt file you find in there and find the error message in there and share it here.  (In a spoiler like I did with the code)

  • Like 2

Share this post


Link to post
Share on other sites

Thanks @stanhope I honestly completely forgot about the .rpt files and was only squinting at the black debugger text.  Here is the error being thrown and I can't seem to make sense of it... must be in reference to uiSleep 0.1;

 

Spoiler

Error in expression <p 0.1;
} forEach allUnits;
uiSleep 5;
};
>
 Error position: <
>
  Error Invalid number in expression

 

Share this post


Link to post
Share on other sites

Just take that sleep out then I guess

  • Thanks 1

Share this post


Link to post
Share on other sites

@stanhope That did the trick!  Thank you so much for the assist! 

 

Edit here is the final,  In the player init call the script: [player] execVM "damage.sqf"; and then...

Spoiler

 

private _EHCode = {
    params ["_unit", "_hitSelection", "_damage","_source","_projectile","_hitPartIndex", "_instigator", "_hitPoint"];
    private _damageMultiplierHead = 0.3;
    private _damageMultiplierBody = 0.25;
    private _damageMultiplierLimbs = 0.15;
    private _damageMultiplierOverall = 0.25;

    private _limitHead = 1.0;
    private _limitBody = 0.25;
    private _limitLimbs = 0.1;
    private _limitOverall = 0.25;

    private _oldDamage = 0;
    if (_hitSelection isEqualTo "") then {_oldDamage = damage _unit} else {_oldDamage = _unit getHit _hitSelection};
    private _newDamage = _damage - _oldDamage max 0;
    private _incomingDamage = _newDamage;
    private _playerHealth = damage _unit;


    if (_newDamage > 0 && !(_projectile isEqualTo "")) then {
        private _damageMultiplier = _damageMultiplierBody;
        private _upperLimit = _limitBody;
        switch (_hitSelection) do {
            case "face_hub";
            case "head": {
                _damageMultiplier = _damageMultiplierHead;
                _upperLimit = _limitHead;
            };
            case "arms";
            case "hands";
            case "legs": {
                _damageMultiplier = _damageMultiplierLimbs;
                _upperLimit = _limitLimbs;
            };
            case "": {
                _damageMultiplier = _damageMultiplierOverall;
                _upperLimit = _limitOverall;
            };
            default {
                _damageMultiplier = _damageMultiplierBody;
                _upperLimit = _limitBody;
            };
        };
        _newDamage = _newDamage * _damageMultiplier;


        if (_newDamage > _upperLimit) then {
            _newDamage = _upperLimit;
        };

        _damage = _oldDamage + _newDamage;
    };

    if ( isPlayer _unit ) then {
        if ( lifeState _unit == "INCAPACITATED" ) then {
            _damage = _oldDamage;
        } else {
            _this set[ 2, _damage ];
            _damage = _this call bis_fnc_reviveEhHandleDamage;
        };
    };

    _damage
};

while {true} do {
    {
        if (_x getVariable ["damage_modifier_EH_applies", false]) then {continue;};
        _x addEventHandler ["HandleDamage", _EHCode];
        _x setVariable ["damage_modifier_EH_applies", true];
           } forEach allUnits;
    uiSleep 5;
};

 

 

Share this post


Link to post
Share on other sites

Question, What is the reason for using continue?

while {true} do {
    {
        if (_x getVariable ["damage_modifier_EH_applies", false]) then {continue;};
        _x addEventHandler ["HandleDamage", _EHCode];
        _x setVariable ["damage_modifier_EH_applies", true];
           } forEach allUnits;
    uiSleep 5;
}; 

vs

while {true} do {
    {
        if (_x getVariable ["damage_modifier_EH_applies", false]) then {
        _x addEventHandler ["HandleDamage", _EHCode];
        _x setVariable ["damage_modifier_EH_applies", true];
           }} forEach allUnits;
    sleep 5;
}; 

 

  • Like 1

Share this post


Link to post
Share on other sites

Because it reduces code complexity and gitlab yells at me when I push code that's too complex (well, only for school assignments, course of habit I guess)

Share this post


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

Because it reduces code complexity and gitlab yells at me when I push code that's too complex (well, only for school assignments, course of habit I guess)

 

Don't

In such code, there is no added value, neither readability, nor performance.

 

Same for your conditional cascade (read elsewhere) like:
{ if (somethingTrue) then {continue}; if (somethingElseTrue) then {continue}; if (otherThings) then {continue}; call hollyGraal } forEach blahblah;
with is simply the lazy test conditions like:
{ if (! somethingTrue && {! somethingElseTrue} &  {! otherThings} ) then { call hollyGraal } } forEach blahblah; 

same as:
{ if ! (somethingTrue or {somethingElseTrue} or {otherThings}) then { call hollyGraal } } forEach blahblah; 

Share this post


Link to post
Share on other sites

There's also no value lost?  I just tested it with the code in Robustcolor's reply.  Sometimes the first is marginally faster, sometimes it's marginally slower.  It basically comes down to personal preference. 

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

×