← Home SEEBIE90 DOCS
Download PDF
The SOFLAM Integration Core mod is published under the Arma Public License (APL)Example code is free to useSuggestions: Discord to the author

SOFLAM Integration Core

The seeker side Changed

A component on a missile, a turret or a fire-control computer. It does not care who is lasing or whether it is its own laser. It asks the registry and works out direction and angle.

In plain wordsThe seeker is the other half: it asks the registry for the nearest point with its code, checks the range, and hands back a direction.
How to hook this up in your own modTwo ways to use a seeker in your own mod – read the registry yourself, or attach this component – are in Make a weapon follow the laser.

Ask by code, then range

The seeker, GTG_SOFLAM_LaserSeekerComponent, takes the nearest registry point with its code and accepts it if it is within its range limit. Both decisions are protected methods you can override, so that nobody has to copy the component to choose differently:

Example code – free to useGTG_SOFLAM_LaserSeekerComponent.c (excerpt)
//! Which target does this seeker pick? Override this to choose differently - nearest to the line
//! of sight instead of to the origin, moving targets first.
protected GTG_SOFLAM_LaserTarget SelectTarget()
{
	GTG_SOFLAM_LaserTargetManager manager = GTG_SOFLAM_LaserTargetManager.GetInstance();
	if (!manager)
		return null;

	return manager.GetClosestTargetByCode(m_sLaserCode, m_vSeekerStart);
}

//! Does the seeker take this target? Default: the range limit only.
//! The ANGLE limits stay out on purpose: a target counts as found even if it is just outside the cone.
protected bool AcceptTarget(GTG_SOFLAM_LaserTarget target, float distance)
{
	return m_fRangeLimit <= 0 || distance <= m_fRangeLimit;
}

Whether the target can be flown to is a separate question, asked with IsTargetAnglesWithinConstraints(), or answered by GetDirectionToLaserTarget(true) returning the zero vector outside the limits:

Example code – free to useGTG_SOFLAM_LaserSeekerComponent.c (excerpt)
vector GetDirectionToLaserTarget(bool checkConstraints = true)
{
	if (!m_bTargetFound)
		return vector.Zero;

	vector angles = GTG_AnglesTo(m_vTargetPosition);

	if (checkConstraints)
	{
		vector seekerAngles = GTG_ToSeekerAngles(angles);
		if (Math.AbsFloat(seekerAngles[0]) > m_fHorizontalAngleLimit
			|| Math.AbsFloat(seekerAngles[1]) > m_fVerticalAngleLimit)
			return vector.Zero;		// found, but outside the cone
	}

	return angles;
}
AttributeDefaultMeaning
m_sSeekerIdAGMName of this seeker; several on one entity are told apart by it.
m_sDefaultLaserCode1688Code at start.
m_fHorizontalAngleLimit / m_fVerticalAngleLimit35 / 35Angle limits in degrees, relative to the entity's heading.
m_fRangeLimit40000Largest distance in metres at which a target is still accepted (0 or less = unlimited).
m_fCheckInterval0.0333Seconds between two checks (0.0333 = 30 Hz).
m_SeekerOriginemptyOrigin point of the seeker. Empty = the carrier entity's origin.
m_bDrawDebugfalseDraw line of sight and direction.

The public methods: SetSearchActive(bool) (with the shorthands ActivateSearch() and DeactivateSearch()), IsSeekerActive(), IsLaserTargetFound(), GetCode() / SetLaserCode(string), GetLaserSeekerID(), GetLaserTargetPosition(), GetLaserSeekerOrigin(), GetDistanceToTarget(), GetDirectionToLaserTarget(bool) (world angles), GetSeekerAngles() (angles relative to the carrier's heading, what a turret or a guidance law needs) and the static FindSeekerById(entity, id).

Off by defaultThe seeker does not search until it is switched on: SetSearchActive(true) sets the frame event. A freshly spawned seeker is off. State and code are replicated (Ask to the server, Do to everybody, RplSave / RplLoad for late joiners), and it warns once if it has to send without owning its entity: [GTG_SOFLAM] Suchkopf AGM sendet ohne Besitz der Traegerentitaet -- der Server verwirft diese Nachrichten. Siehe R1a in ENTWURFSVORGABEN.txt.
Zero-position entriesGetClosestTargetByCode does not skip entries at the zero vector (created, never reported), and neither does this component by default. Override SelectTarget() if that matters for your weapon.
NoteThe Core's seeker does not log why it has no target. The AH-64 mod's own seeker does, and tells four states apart, each pointing at a different place: OHNE CODE (the carrier never handed one over), KEIN ZIEL ZUM CODE (nobody is lasing, or under another code), AUSSERHALB SICHTFELD (the alignment at launch is wrong, not the target), GEFUEHRT (guided). A missile that simply falls out of the sky is one of the four. Build the same into yours.

Several seekers on one entity

A carrier can have more than one: give each an id (AGM, GBU, RKT) and look it up by that id with FindSeekerById. Mount, weapon and seeker then stay separate things, and a rocket pod and a missile on the same turret each keep their own code.

A range limit that is applied

Check that a limit you declare is actually read. The earlier seeker had an attribute for the maximum detection distance, set to a large number, and no line of code anywhere used it. It looked configurable and was decoration. Here, m_fRangeLimit is applied in AcceptTarget.

Resolve a fixed point once

The earlier seeker called PointInfo.Init() in every tick (30 times a second) to find its emitter point. Now it is called once, in EOnInit, and only if the prefab set an origin; later only the world matrix is read. For a point that is fixed on the model, once at start is enough. For a point on an animated bone it is the other way round: the cached world transform goes stale, and the point has to be resolved again in every frame you want it current. Know which of the two you have.

Example code on this page is free to use in your own projects (credit appreciated). The SOFLAM Integration Core mod is published under the Arma Public License (APL). Suggestions: Discord. Arma Reforger / Enfusion are trademarks of Bohemia Interactive.
Impressum · Privacy Policy