Jump to content
Gunter Severloh

[Release] AI Medic Auto Heal

Recommended Posts

AI Medic Auto Heal

by

Rydygier & Gunter Severloh

 

Description

AI Medic auto heal is a code where an ai medic in your squad you command, will automatically

heal each member of the squad or group including the player if they are injured without commanding them to do so.

    The code was written by Rydygier, which was a major upgrade of the code i had presented to him with.

 

 

How it works:

1. Every 10 seconds the code checks, if units in the group are damaged >0.1;

 

2. If so, the code looks for a valid medic unit in wounded unit's group (wounded unit itself excluded).

Valid medic must be alive, not on the move (unitReady), AI, with a trait "Medic" (so only certain classes, not any), closer, than 500m from the wounded unit,

and not busy with healing already. 

3. Closest of valid medics is chosen.


4. If both medic and wounded are in the same vehicle, auto heal should occur after 5 seconds (no FAK or medikit required), unless medic or wounded is killed

     or deleted during that 5 seconds. If not:

5. If wounded unit's vehicle is moving fast or not touching ground - the code breaks and no healing action will occur. Otherwise:

6. Medic gets doMove towards wounded unit. Medic will not try to use his current vehicle in order to reach wounded unit's position. Instead, it will disembark and move on foot.

If wounded unit is in a vehicle, medic's actual destination is only in rough vicinity though.   If in the meantime medic gets stuck, the distance from the wounded increase by

       additional 200m above initial distance for any reason or any mentioned above conditions become not OK, code breaks, medic is stopped, no healing will occur.

Otherwise:

7. The movement is concluded, if at the end medic is close enough to the wounded - healing action will occur.

If wounded is in the vehicle, likely medic will be teleported in the direct vicinity of the wounded (so may end within vehicle's model box until healing is over).

        "HealSoldier" action requires/consumes a FAK or medikit, but my code adds "setDamage 0" auto heal at the end of this action, so even, if medic is out of FAKs, wounded should be healed.

Without it healing wounded sitting in a vehicle could fail (would need more testing to confirm for sure). 

If described procedure is interrupted without healing action and the player is still alive and wounded, in the next cycle (10 seconds) it will be attempted again. 

 

8. Medics now can also heal themselves with this script.

9. Wounded are prioritized by distance from closest available medic and severity of the wound (both factors are weighted together).
10. Modified wound detection. Now it is based on hitPoints damage, not overall damage (so limping due to fall from above should trigger a medic).


Note - if both, player and medic are subordinates of an AI TL, TL may override medic's movements with other commands, like "return to formation".

Hence it is recommended for player TL groups. Otherwise player may be forced to approach the medic close enough (not tested).

Installation and Setup

Setup - Script

1. Create a notepad/notepad++ document and copy and paste the following code into it:

2. Save and name the document:  init.sqf and put it into your scenario folder.

[] spawn
	{
	RYD_SM_EnemyCloseDistance = 100;//if medics must move to the wounded, but he is engaged in combat with some enemy being closer than this value, medic is considered not available and will interrupt pending movement
	
	waitUntil
		{
		sleep 1;
		(alive player)
		};
		
	RYD_SM_HealCheck = 
		{
		params ["_unit","_medics"];
		
		if ((time - (_unit getVariable ["RYD_SM_LastCheck",0])) < 10) exitWith {};//check only once per 10 seconds 
		
		_unit setVariable ["RYD_SM_LastCheck",time];
		
		if not (alive _unit) exitWith {};//just in case, "Hit" would trigger for a dead player
		
		_vehicleUnit = vehicle _unit;
		
		if (alive (_unit getVariable ["RYD_SM_HealedBy",objNull])) exitWith {};//exit if already treated
		//potential medic is one of _unit's team mates, _unit excluded, that are alive, unitReady, AI, of proper medic class, closer, than 500m and not busy with healing already
				
		if (_unit in _medics) exitWith
			{
			_unit setVariable ["RYD_SM_Busy",true];
			_unit setVariable ["RYD_SM_HealedBy",_unit];
			
			[_unit,_vehicleUnit] spawn
				{
				params ["_unit","_vehicleUnit"];
				
				_unit action ["HealSoldierSelf",_unit];
				
				if not (_unit isEqualTo _vehicleUnit) exitWith
					{
					sleep 5;
					
					_unit setVariable ["RYD_SM_HealedBy",nil];
					_unit setVariable ["RYD_SM_Busy",nil];
					
					if not (alive _unit) exitWith {};
					
					_unit setDamage 0;
					};
				
				_time = time;
				
				waitUntil
					{
					sleep 1;
					
					if not (alive _unit) exitWith {true};
					
					((((toLower (animationState _unit)) find "medic") >= 0) or {(time - _time) > 5})
					};
				
				if not (alive _unit) exitWith {};
					
				_time = time;
				
				waitUntil
					{
					sleep 1;
					
					if not (alive _unit) exitWith {true};
					
					((((toLower (animationState _unit)) find "medic") < 0) or {(time - _time) > 5})
					};
					
				_unit setVariable ["RYD_SM_HealedBy",nil];
				_unit setVariable ["RYD_SM_Busy",nil];
				};
			};
			
		_medics = _medics select {(_x distance _unit) < 500};
		
		if ((count _medics) < 1) exitWith {};//exit if no valid medics
		
		_medics = _medics apply {[_x distance _unit,_x]};
		_medics sort true;
		
		_medic = (_medics select 0) select 1;//closest of medics is chosen
		_vehicleMedic = vehicle _medic;
		
		_sameVehicle = _vehicleUnit isEqualTo _vehicleMedic;
		
		if (not (_sameVehicle) and {((abs (speed _vehicleUnit)) > 10)}) exitWith {};//exit if _unit is moving fast
		if (not (_sameVehicle) and {not (isTouchingGround _vehicleUnit)}) exitWith {};//exit if _unit is flying
		
		_unit setVariable ["RYD_SM_HealedBy",_medic];
		_medic setVariable ["RYD_SM_Busy",true];
		
		if (_sameVehicle) exitWith//if _medic and _unit are in the same vehicle, just automatically conclude healing with 5 sec delay 
			{
			[_unit,_medic] spawn
				{
				params ["_unit","_medic"];
				
				_medic action ["HealSoldier",_unit];
				
				sleep 5;
				
				_unit setVariable ["RYD_SM_HealedBy",nil];
				_medic setVariable ["RYD_SM_Busy",nil];
				
				if not (alive _unit) exitWith {};
				if not (alive _medic) exitWith {};//if during the delay _medic or _unit are killed or delted - no healing. Otherwise healing occurs even if one or both leave vehicle in the meantime.
				
				_unit setDamage 0;
				};
			};
		
		_unitPosition = getPosATL _vehicleUnit;
		_medicPosition = getPosATL _vehicleMedic;
		
		_distance = _unitPosition distance _medicPosition;
		
		_threshold = if (_unit isEqualTo _vehicleUnit) then
			{
			2.5
			}
		else
			{
			((sizeOf (typeOf _vehicleUnit))/2)
			};
		
		if (_distance > _threshold) then
			{
			if not (_medic isEqualTo _vehicleMedic) then
				{
				_medic action ["GetOut",_vehicleMedic];
				};
				
			_medic doMove _unitPosition;
			
			[_unit,_medic,_vehicleUnit,_distance,_threshold] spawn
				{
				params ["_unit","_medic","_vehicleUnit","_distance","_threshold"];
				
				sleep 1;
				
				_stuck = 0;
				_inFight = false;
				
				waitUntil
					{
					_pos1 = getPosATL _medic;
					
					sleep 2;
					
					_pos2 = getPosATL _medic;
					
					if not (alive _unit) exitWith {true};
					if not (alive _medic) exitWith {true};
					
					_enemy = getAttackTarget _medic;
					
					if ((alive _enemy) and {((_medic distance _enemy) < RYD_SM_EnemyCloseDistance) and {((_medic knowsAbout _enemy) > 1.5)}}) exitWith {_inFight = true;true};
					
					if ((abs (speed _vehicleUnit)) > 10) exitWith {true};
					if not (isTouchingGround _vehicleUnit) exitWith {true};
					
					if ((_pos1 distance _pos2) < 0.1) then
						{
						_stuck = _stuck + 1;
						};

					_threshold = if (_unit isEqualTo _vehicleUnit) then
						{
						2.5
						}
					else
						{
						((sizeOf (typeOf _vehicleUnit))/2)
						};
						
					if ((unitReady _medic) and {((_medic distance _vehicleUnit) >= _threshold)}) then
						{
						_medic doMove (getPosATL _vehicleUnit);
						};
					
					((unitReady _medic) or {(_stuck > 5) or {((_medic distance _vehicleUnit) > (_distance + 200))}})
					};
					
				if (_inFight) exitWith 
					{
					_medic doMove (getPosATL _medic);
					_unit setVariable ["RYD_SM_HealedBy",nil];
					_medic setVariable ["RYD_SM_Busy",nil];
					};
					
				if not (alive _unit) exitWith 
					{
					_medic doMove (getPosATL _medic);
					_unit setVariable ["RYD_SM_HealedBy",nil];
					_medic setVariable ["RYD_SM_Busy",nil];
					};
					
				if not (alive _medic) exitWith 
					{
					_unit setVariable ["RYD_SM_HealedBy",nil];
					_medic setVariable ["RYD_SM_Busy",nil];
					};
					
				if (_stuck > 10) exitWith 
					{
					_medic doMove (getPosATL _medic);
					_unit setVariable ["RYD_SM_HealedBy",nil];
					_medic setVariable ["RYD_SM_Busy",nil];
					};
					
				if ((abs (speed _vehicleUnit)) > 10) exitWith 
					{
					_medic doMove (getPosATL _medic);
					_unit setVariable ["RYD_SM_HealedBy",nil];
					_medic setVariable ["RYD_SM_Busy",nil];
					};//exit if _unit is moving fast
					
				if not (isTouchingGround _vehicleUnit) exitWith 
					{
					_medic doMove (getPosATL _medic);
					_unit setVariable ["RYD_SM_HealedBy",nil];
					_medic setVariable ["RYD_SM_Busy",nil];
					};//exit if _unit is flying
					
				if ((_medic distance _vehicleUnit) > (_distance + 200)) exitWith
					{
					_medic doMove (getPosATL _medic);
					_unit setVariable ["RYD_SM_HealedBy",nil];
					_medic setVariable ["RYD_SM_Busy",nil];
					};//if for any reason distance increased too much (troublesome pathfinding for example) 
				
				_medic doMove (getPosATL _medic);

				if ((_medic distance _vehicleUnit) < _threshold) then
					{
					_medic action ["HealSoldier",_unit];
					
					_time = time;
					
					waitUntil
						{
						sleep 1;
						
						if not (alive _unit) exitWith {true};
						if not (alive _medic) exitWith {true};
						
						((((toLower (animationState _medic)) find "medic") >= 0) or {(time - _time) > 5})
						};
					
					if not (alive _unit) exitWith {};
					if not (alive _medic) exitWith {};
						
					_time = time;
					
					waitUntil
						{
						sleep 1;
						
						if not (alive _unit) exitWith {true};
						if not (alive _medic) exitWith {true};
						
						((((toLower (animationState _medic)) find "medic") < 0) or {(time - _time) > 5})
						};
						
					_unit setVariable ["RYD_SM_HealedBy",nil];
					_medic setVariable ["RYD_SM_Busy",nil];
					
					if not (alive _unit) exitWith {};
					if not (alive _medic) exitWith {};
						
					_unit setDamage 0;
					};
				};
			}
		else
			{
			[_unit,_medic] spawn
				{
				params ["_unit","_medic"];
				
				_medic doMove (getPosATL _medic);
				
				_medic action ["HealSoldier",_unit];
				
				_time = time;
				
				waitUntil
					{
					sleep 1;
					
					if not (alive _unit) exitWith {true};
					if not (alive _medic) exitWith {true};
					
					((((toLower (animationState _medic)) find "medic") >= 0) or {(time - _time) > 5})
					};
				
				if not (alive _unit) exitWith {};
				if not (alive _medic) exitWith {};
					
				_time = time;
				
				waitUntil
					{
					sleep 1;
					
					if not (alive _unit) exitWith {true};
					if not (alive _medic) exitWith {true};
					
					((((toLower (animationState _medic)) find "medic") < 0) or {(time - _time) > 5})
					};
					
				_unit setVariable ["RYD_SM_HealedBy",nil];
				_medic setVariable ["RYD_SM_Busy",nil];
				
				if not (alive _unit) exitWith {};
				if not (alive _medic) exitWith {};
					
				_unit setDamage 0;
				};
			};
		};
		
	while {alive player} do
		{
		_units = (units (group player)) select {(alive _x)};
		_wounded = _units select {((((getAllHitPointsDamage _x) select 2) findIf {_x > 0.1}) >= 0)};
		
		if ((count _wounded) > 0) then
			{
			_medics = _units select {(unitReady _x) and {not (isPlayer _x) and {(_x getUnitTrait "Medic") and {not (_x getVariable ["RYD_SM_Busy",false]) and {not (((alive (getAttackTarget _x)) and {((_x distance (getAttackTarget _x)) < RYD_SM_EnemyCloseDistance) and {((_x knowsAbout (getAttackTarget _x)) > 1.5)}}))}}}}};
			
			if ((count _medics) > 0) then
				{
				_wounded = _wounded apply
					{
					_unit = _x;
					_medics2 = _medics apply {[_x distance _unit,_x]};
					_medics2 sort true;
					
					_medicD = (_medics2 select 0) select 0;
					_val = (damage _x)/(_medicD max 1);
					[_val,_x]
					};
					
				_wounded sort false;

					{
					[(_x select 1),_medics] call RYD_SM_HealCheck;
					_medics = _medics select {not (_x getVariable ["RYD_SM_Busy",false])};
					
					if ((count _medics) == 0) exitWith {};
					}
				foreach _wounded;
				};
			};
		
		sleep 10;
		};
	};

Setup - External Script - (optional)

Instead of using a init.sqf for the code, you can setup an external script that can be used instead:

 

1. Create a notepad/notepad++ document and copy and paste the code from above into it:

2. Save and name the document:  RYD_AI_AutoMedic.sqf and put it into your scenario folder.

3. Create a notepad/notepad++ document and add the following code to it:  execVM "RYD_AI_AutoMedic.sqf";

4. Save and name the document: init.sqf

5. Add both RYD_AI_AutoMedic.sqf and the init.sqf to your scenario folder where the mission.sqm is located.

 

Credits

Rydygier wrote the code.

Both Gunter's and Rydygier's ideas.

Edited by Gunter Severloh
Updated code
  • Like 6
  • Thanks 3

Share this post


Link to post
Share on other sites

Update

 

v1.01

From Rydygier

Code updated in the OP.

 

Changes:

- medics now can also heal themselves with this script;

- wounded are prioritized by distance from closest available medic and severity of the wound (both factors are weighted together);
- modified wound detection. Now it is based on hitPoints damage, not overall damage (so limping due to fall from above should trigger a medic);

Currently damage threshold is set here:

_wounded = _units select {((((getAllHitPointsDamage _x) select 2) findIf {_x > 0.1}) >= 0)};

- added an exception for medics engaged in combat. Reliable combat detection isn't that simple to do (unclear and not always measurable criteria),

so this is experimental, comes with a global var parameter:

RYD_SM_EnemyCloseDistance = 100;//if medics must move to the wounded, but he is engaged in combat with some enemy being closer than this value, medic

is considered not available and will interrupt pending movement

So in practice, medic currently in fight with a close enough enemy unit should not aid wounded comrades. If not desired, reduce or set this parameter to 0. 

- slightly changed healing wounded in vehicles code. 

  • Like 2
  • 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

×