Skip to content

Instantly share code, notes, and snippets.

@thegamecracks
Last active May 19, 2025 01:45
Show Gist options
  • Select an option

  • Save thegamecracks/9f564b8d4901ddd3902a20aeafc4dfef to your computer and use it in GitHub Desktop.

Select an option

Save thegamecracks/9f564b8d4901ddd3902a20aeafc4dfef to your computer and use it in GitHub Desktop.
A proof-of-concept approximation of missile notching in Arma 3
TGC_fnc_getAbsClosureSpeed = {
params ["_observer", "_target"];
private _dir = getPosATL _target vectorFromTo getPosATL _observer;
private _velocity = velocity _target;
private _angle = vectorNormalized _velocity vectorDotProduct _dir;
vectorMagnitude (_velocity vectorMultiply _angle)
};
TGC_fnc_isRadarGuided = {
params ["_ammo"];
private _property = configFile >> "CfgAmmo" >> _ammo >> "weaponLockSystem";
getNumber _property isEqualTo 8 || {"8" in getTextRaw _property}
};
TGC_missiles = [];
TGC_icon_vehicles = vehicles select {someAmmo _x};
TGC_notch_closure = 200 / 3.6; // Maximum closure speed required to break lock
TGC_notch_duration = 2; // Approximate duration required to break lock
TGC_notch_height = -50; // Minimum height difference required to break lock
// Consider adding a EntityCreated mission event handler here
{
_x addEventHandler ["Fired", {
params ["", "", "", "", "_ammo", "", "_projectile", "_vehicle"];
if !(_ammo isKindOf "MissileBase") exitWith {};
TGC_missiles pushBack _projectile;
// For testing convenience
if (local _vehicle) then {_vehicle setVehicleAmmo 1};
}];
} forEach vehicles;
addMissionEventHandler ["Draw3D", {
private _vehicle = objectParent focusOn;
if (isNull _vehicle) exitWith {};
private _threats = getSensorThreats _vehicle;
private _markingThreats = _threats select {_x # 1 == "marked"} apply {vehicle (_x # 0)};
private _lockingThreats = _threats select {_x # 1 == "locked"} apply {vehicle (_x # 0)};
{
if (!alive _x) then {continue};
if (_x isEqualTo _vehicle) then {continue};
private _closure = [_x, _vehicle] call TGC_fnc_getAbsClosureSpeed;
private _color = switch (true) do {
case (_x in _markingThreats): {[1, 0.9, 0, 1]};
case (_x in _lockingThreats): {[1, 0.5, 0, 1]};
default {[1, 1, 1, 1]};
};
drawIcon3D [
getTextRaw (configOf _x >> "icon"),
_color,
getPosATL _x,
1,
1,
0,
format [
"%1 km/h, %2km at %3m",
_closure * 3.6 toFixed 0,
(_vehicle distance _x) / 1000 toFixed 1,
getPos _x # 2 max 0 toFixed 0
],
2,
0.05
];
} forEach TGC_icon_vehicles;
{
if (!alive _x) then {continue};
private _closure = [_x, _vehicle] call TGC_fnc_getAbsClosureSpeed;
private _distance = _vehicle distance _x;
private _scale = linearConversion [100, 2000, _distance, 2, 0.5, true];
private _locked = missileTarget _x isEqualTo _vehicle;
private _color = if (_locked) then {[1, 0, 0, 1]} else {[0, 1, 0, 1]};
private _started = _x getVariable "TGC_notch_started";
private _text = format [
"%1 km/h, %2km at %3m%4%5",
_closure * 3.6 toFixed 0,
_distance / 1000 toFixed 1,
getPos _x # 2 max 0 toFixed 0,
endl,
if (isNil "_started") then {""} else {format ["%1s", time - _started toFixed 1]}
];
drawIcon3D [
"a3\ui_f\data\igui\cfg\cursors\explosive_ca.paa",
_color,
getPosATL _x,
_scale,
_scale,
time * 720,
_text,
2,
0.05
];
} forEach TGC_missiles;
}];
TGC_missileNotchScript = 0 spawn {while {true} do {
private _deadIndexes = [];
{
if (!alive _x) then {_deadIndexes pushBack _forEachIndex; continue};
private _target = missileTarget _x;
if (isNull _target) then {continue};
if (!local _target) then {continue};
if !(typeOf _x call TGC_fnc_isRadarGuided) then {continue};
// Check that vehicle is notching the missile
private _closure = [_x, _target] call TGC_fnc_getAbsClosureSpeed;
if (_closure > TGC_notch_closure) then {
_x setVariable ["TGC_notch_started", nil];
continue;
};
// Check that missile is looking towards ground (cheap heuristic)
private _altitudeDiff = getPos _x # 2 - getPos _target # 2;
if (_altitudeDiff <= TGC_notch_height) then {
_x setVariable ["TGC_notch_started", nil];
continue;
};
if (isNil {_x getVariable "TGC_notch_started"}) then {
_x setVariable ["TGC_notch_started", time];
continue;
};
private _started = _x getVariable "TGC_notch_started";
if (time - _started < TGC_notch_duration) then {continue};
[_x, objNull] remoteExec ["setMissileTarget", _x];
} forEach TGC_missiles;
{TGC_missiles deleteAt _x} forEachReversed _deadIndexes;
sleep 0.25;
}};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment