← Home SEEBIE90 DOCS
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

A telescopic tripod Changed

Nine parts, three legs that extend by different amounts, a column and a ball head. The design rule: one calculator, one place where a mistake can show.

In plain wordsA tripod whose legs extend, with a centre column and a ball head. One component computes all the parts, and only a few numbers are sent.

One calculator for all parts

A single component on the root, GTG_SOFLAM_TripodComponent, moves all child entities. Nine parts cannot drift apart that way, and a fault has one place to appear. Each child carries a small marker, GTG_SOFLAM_TripodSegmentComponent, that says what the part is (enum EGTG_TripodSegment: LEG_MID, LEG_LOW, STEM, HEAD, FRAME), along which axis it moves and where it sits when retracted. Three independent quantities, each with its own control: leg extension, centre column, ball head.

The marker uses an enum and not a name search because child entities have no reliable name at runtime. The rest position (m_vBaseOffset) is an attribute and is not read from the entity: reading it in the first tick after OnPostInit returned garbage on a just-created child, and the legs lay flat at random angles.

Why the structure rises when the legs extend

The feet should stay on the ground while the head goes up. Mathematically those are the same thing seen from two sides, and you have to pick one. Chosen: the legs extend downwards, along their own slanted axes, and the structure above rises by exactly the distance the feet move down.

The alternative – hold the feet and push everything above up – fails on geometry. The frame is one rigid piece above all three legs and can only move vertically, but the tubes sit in sleeves inclined about 21°. At 0.5 m of lift every upper tube would run about 20 cm sideways out of its sleeve. It is obvious immediately.

What moves, though, is not the root. The first version lifted the whole entity. The gunner's seat hangs on the root and rose with it, measured:

Example code – free to use
legs 0.94 | lift 0.5045 | root above ground 0.5045 | gunner 0.5045

A compartment slot cannot be moved at runtime. So the root stays on the ground (and has no model any more), the yoke is a part of its own (role FRAME), and every part gets the lift added to its local offset. Same world positions, different frame of reference:

Example code – free to useGTG_SOFLAM_TripodComponent.c (excerpt)
protected float GTG_LegLift()
{
	foreach (GTG_SOFLAM_TripodSegmentComponent segment : m_aSegments)
	{
		if (segment.GetSegment() != EGTG_TripodSegment.LEG_LOW)
			continue;

		vector axis = segment.GetAxis();		// ~21 degrees from vertical: 0.93 m of height per metre of extension
		return m_fLegExtension * m_fLegStageTravel * segment.GetStageCount() * axis[1];
	}

	return 0;
}

// in GTG_ApplyNow, for every part: the root stays, the part rises
mat[3] = segment.GetBaseOffset() + offset + hub;
part.SetLocalTransform(mat);

Stage travel m_fLegStageTravel is 0.29 m in the turret prefab (the script default is 0.28), the column m_fStemTravel 0.25 m, the head tilt limit m_fHeadTiltLimit 45°. The inner tube (LEG_LOW) moves two stages. The limit is the telescope overlap: below 5 cm of overlap a tube holds nothing.

NoteThat the feet walk outwards while extending is not a bug. A real tripod does the same when extended without re-setting it.

Numbers on the wire

Almost nothing is a transform. Every machine computes the same nine positions from a few numbers. That is less traffic than nine transforms, and it cannot arrive partially: either the state is right or it is wrong everywhere.

There is deliberately no client-to-server Rpc in the tripod component. A client does not own the tripod, so a message to the server would be dropped (see Who may send). Operation goes through user actions that run on the server anyway; the result goes out as a broadcast.

The base always stands level

The tripod keeps its pitch and roll at zero whatever the ground does (m_fBasePitch = 0, m_fBaseRoll = 0); only the yaw comes from the placement. Taking over the slope of the ground meant the head tilt needed for a target changed just by panning, and the 45° clamp was reached much earlier in some directions. Like a real tripod with a spirit level, the legs make up the difference.

Every leg follows its own ground

On sloped ground one common extension makes at least one leg hang in the air or stand in the ground. The fix keeps the frame rigid and level, but gives each leg its own extension along its own unchanged axis – exactly what a telescopic leg with a fixed hinge angle does.

The first version asked for the ground height once, at the leg's mount point. But the leg extends along an axis inclined about 21°, so its foot lands 20–35 cm to the side of that point, and on a slope the ground is a different height there. The answer is a short fixed-point iteration: foot position from the current extension, ground at that foot, new extension, repeat. Four passes leave about 2 % error, and it converges as long as the slope times the tangent of the leg tilt (0.38) is below 1, which holds well past a 45° slope.

Example code – free to useGTG_SOFLAM_TripodComponent.c (excerpt)
// per leg, from GTG_BeinFraktionenBeiHub (shortened)
float fraktion = 0;
for (int durchlauf = 0; durchlauf < 4; durchlauf++)
{
	// where does the foot land with this extension?
	vector fussWelt = montageWelt - achseWelt * (fraktion * maxReichweite);

	// what is the ground there? (a trace, not the height map - see below)
	float bodenY = GTG_BodenY(welt, Vector(fussWelt[0], montageWelt[1] + 0.3, fussWelt[2]));

	// the new extension that reaches it
	fraktion = (montageWelt[1] - bodenY) / (achseLokal[1] * maxReichweite);
	if (fraktion < 0)
		fraktion = 0;
}

If a leg cannot reach the ground at full length, the common height is lowered in steps of 5 % of the wanted lift (at most 40 steps) until all three legs are within range. The lowest leg decides the maximum height, as on a real tripod.

Example code – free to useGTG_SOFLAM_TripodComponent.c (excerpt)
while (schlechtesteFraktion > 1.0 && effektiverHub > 0.0001 && versuch < 40)
{
	effektiverHub -= wunschHub * 0.05;
	if (effektiverHub < 0)
		effektiverHub = 0;

	schlechtesteFraktion = GTG_BeinFraktionenBeiHub(baseMat, effektiverHub, achsen, montagepunkte,
		maxReichweite, welt, fraktionen);
	versuch++;
}

This geometry is not recomputed for every aim update. It is only measured again when the wanted lift, the placement or the yaw changes; before that, aiming cost frame rate, because the tripod re-applied itself every frame and each pass fired a dozen traces.

Height map versus the real surfaceGetSurfaceY() is the height map. The surface a player actually stands on can be higher. In one measurement the player stood 0.55 m and the tripod root 0.63 m above the height-map value. If the feet or a door point follow the height map, they end up inside the surface. The Core measures the ground with a downward trace (SCR_TerrainHelper.GetTerrainY with a trace on the layers Terrain | Static, the tripod's own parts excluded; where the trace hits nothing it falls back to the height map).

A watchdog, honestly named

The device sitting on the ball head moves only on events: mounting, height change, aiming. If something else moves it (a weapon slot, the inventory, physics) nothing corrects it, and the log looks healthy while the device lies at the player's feet. A watchdog compares actual against expected position every 250 ms (tolerance 5 cm), reports it once per incident with both positions, and resets it. A summary follows every five seconds while corrections continue.

Example code – free to useformat of the report (values are examples)
[GTG_SOFLAM][WACHHUND] Aufgesetztes Geraet 0.8 m von seinem Platz -- ist <...> soll <...> (Kugelkopf <...>). Verschoben hat es NICHT diese Komponente; wird zurueckgesetzt.
NoteThe report stays on even when the watchdog corrected the position. As long as it appears, there is a cause that has not been found. It is a bandage, not a cure.
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