← Home SEEBIE90 DOCS
The RIM-116 & Phalanx mod is published under the Arma Public License (APL)Example code is free to useSuggestions: Discord to the author

RIM-116 & Phalanx

Radar coverage

One radar covers every mount in range. Shoot it and they fall back to their own sensor: much shorter range and worse accuracy. It is a target worth protecting – and worth taking out first.

In plain wordsA radar covers everything in range that it can see over the terrain. A mount without radar still fights, but with a fraction of its range.

Why a separate radar at all

A mount that carries its own full-performance sensor is a self-contained problem: kill it or do not. Splitting the sensor off gives the attacker a cheaper first objective and the defender something to put behind cover. It also matches the real systems – a close-in launcher is cued by the ship's radar.

StateRange against aircraft (example)Accuracy
Full radar coverage100 %as configured
No radar in range (emergency mode)about 30 %degraded
NoteKeep the emergency factor below every radar's own range factor. A radar that granted less than the fallback would make itself a liability.

Terrain blocks it – sample, do not raycast

The first version traced a physics ray. A ray ends at the target position, and that position is the helicopter's own origin – a point inside its hull. On the last metre the ray hit the target itself and reported “occluded”: a helicopter parked in the open blocked its own line of sight.

Sampling the terrain height along the line has no such problem, is cheaper, and models the thing better. A radar beam does not care about a fence, but it very much cares about a ridge.

Example code – free to use
bool MyAD_Radar_Sees(vector target)
{
	vector eye = GetOwner().GetOrigin();
	eye[1] = eye[1] + ANTENNA_HEIGHT;		// the beam starts at the antenna, not at the ground

	if (vector.Distance(eye, target) > m_fRange)
		return false;

	vector line = target - eye;
	int steps = Math.ClampInt(line.Length() / 40, 8, 100);

	for (int i = 1; i < steps; i++)
	{
		vector p = eye + line * ((float)i / steps);

		// only terrain that clearly rises above the line blocks it - a grazing crest does not
		if (GetGame().GetWorld().GetSurfaceY(p[0], p[2]) - p[1] > 5)
			return false;
	}
	return true;
}
PitfallThis ignores buildings and does not model beam spread. Against a ridge it is right; against a hangar it is optimistic.

Ground clutter, with hysteresis

Seeing a target is not the same as holding a track on it. Below a set height above ground a radar detects but cannot track. Without a margin that threshold is a knife edge: whoever flies along it flips the condition every second. In testing the lock dropped many times a minute with clear line of sight, and the log named the reason: a target at 29.99 m against a threshold of 30.

The cure is the thermostat principle: acquire above one value, lose only below a lower one. A track, once built, holds longer than it forms.

Example code – free to use
bool MyAD_Radar_InClutter(vector pos, bool alreadyTracking)
{
	float limit = m_fClutterHeight;
	if (alreadyTracking)
		limit = limit - 8;		// hysteresis: lose it only well below where it was gained

	float height = pos[1] - GetGame().GetWorld().GetSurfaceY(pos[0], pos[2]);
	return height < limit;
}

The rotation is a formula, not a state

If the dish turns at a fixed rate, its bearing at any moment follows from the time of day. Every machine computes the same answer and nothing has to be sent. That is what makes the client-side ping on the next page possible. The rate also has to match the visible animation of the radar prefab, or the audible ping and the dish the pilot can see drift apart.

Example code – free to use
float MyAD_Radar_Bearing()
{
	float seconds = SecondsSinceMidnight();
	float phase = SCR_Math.fmod(m_fDegreesPerSecond * seconds, 360.0);
	return SCR_Math.fmod(phase + GetOwner().GetYawPitchRoll()[0] + m_fModelOffset, 360.0);
}

float SecondsSinceMidnight()
{
	ChimeraWorld world = ChimeraWorld.CastFrom(GetGame().GetWorld());
	TimeAndWeatherManagerEntity mgr = world.GetTimeAndWeatherManager();

	// time of day scaled by day length - accelerated time cancels out of the rotation
	return mgr.GetTimeOfTheDay() / 24.0 * mgr.GetDayDuration();
}
NoteSCR_Math.fmod and not Math.Mod: the built-in returns a negative result for negative input (Math.Mod(-500, 360) is -140). The bearing adds a mast orientation and a model offset, and either may be negative.
Example code on this page is free to use in your own projects (credit appreciated). The RIM-116 & Phalanx mod is published under the Arma Public License (APL). Suggestions: Discord. Arma Reforger / Enfusion are trademarks of Bohemia Interactive.
Impressum · Privacy Policy