← 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

Integrating a helicopter or aircraft Changed

Adding laser guidance to an aircraft is mostly not about the laser. It is about racks that do not launch, seats that are called something else, keys that were renamed, and state that only one machine knows. This is the list of things that went wrong in the AH-64 and AH-1Z mods, in the order you will meet them.

In plain wordsWhat went wrong when laser-guided missiles were put on helicopters: weapon racks, mode keys, phantom explosions, the missile camera, and how to test it.
Where this fitsThe how-to for the Core part (code holder, dials, injector, seeker) is on Aircraft, vehicles and turrets. This page is what went wrong around it. The classes named here are from the AH-64 mod (GTG_AH64_*); the base classes WCS_Armament_* belong to WCS Armaments, not to the Core.

What an aircraft needs

Five small pieces, each with one job. None of them knows the designator: they only know the registry and a code. All of them are built by the aircraft mod; the Core provides only the registry.

PieceSits onJobClass in the AH-64 mod
Code holderThe turretHolds the aircraft's four-digit code, replicates it on every change and hands it to late joinersGTG_AH64_LaserCodeComponent
Cockpit code dialsFour user actions, positions on the vehicleLet the crew change the code; the action and the holder must be findable from each otherGTG_AH64_CockpitLaserTuneAction (m_iLaserCodeDigit 0..3)
SeekerThe missileAsks the registry for the nearest target with the aircraft's codeGTG_AH64_LaserSeekerComponent, a subclass of WCS_Armament_LaserSeekerComponent
Flight computerThe missileOwns the attack modes and flies them (four: DIR, LOBL, LO, HI)GTG_AH64_LaserFlightComputerComponent
Code injectorThe turretCopies the code onto every missile as it leaves the rackGTG_AH64_LaserCodeInjectorComponent
Crew sets code→Injector copies it→Seeker asks registry→Flight computer flies the mode
NoteGive every class an aircraft prefix. Two aircraft mods then run side by side and keep separate codes, and a name clash cannot take the script module down. The AH-64 and AH-1Z mods carry their own copies of the same scripts under GTG_AH64_ and GTG_AH1Z_.

Weapon racks

The upper row that never leaves the rail

A missile pod can carry its rounds in two rows. If the upper row sits so close to the pod body that the engine refuses to apply the launch impulse, those missiles leave the rail carrying only the aircraft's own speed and fall. The signature is unmistakable: of sixteen missiles exactly eight drop, and it is always the same eight, because the launcher serves the upper row of every pod first.

The fix is one attribute on the pod's weapon component: push the missile clear before the engine launches it. Measure it. The AH-64 flight computer writes the start state of each missile once (including the velocity vector, which tells “never launched” from “launched and stopped” – its length alone cannot).

Example code – free to usePod_M299_AH64D.et (excerpt)
WCS_Armament_WeaponComponent "{629D11C992611C79}" {
 m_fLaunchOffset 2		// the base value in Pod_M299.et is 0 and leaves the upper row without impulse
}
Upper rowLower row
Launch speed before1.6 - 2.1 m/s, then free fall77 - 93 m/s
Launch speed after78 - 94 m/s78 - 94 m/s

The AH-1Z mod's own pod prefab (Pod_M299_AH1Z.et) sets the same value, with a comment that exactly half of the 16 missiles fall off without it. If your pod is derived from the stock M299, check it.

How high the tube sits

On a helicopter standing on the ground the tubes are under a metre above the terrain (0.9 m on the AH-64). A flat attack mode then grazes the ground in front of the aircraft. That is geometry, not a fault in the rack, and it does not occur in flight. Test from the air before you chase it.

Override or new variant

The first AH-64 mod built a complete new vehicle variant. Its code dials and the third munition choice then did not appear on the Apache that was already in the test world. The fix was to make the turret and pod prefabs claim the same resource identity (GUID, through the .et.meta file) as the WCS originals: the unchanged vehicle prefab already refers to them by GUID, and that reference now points at the mod's version. Read the cost, too: such an override has to be compared with the original at every update of the base mod; the AH-64 mod keeps a copy of the original turret base under its own GUID for that reason.

Seats and turret controls

The seat has a name

A component that registers a key only while the local player sits in a named seat has to be told the right name. It differs between aircraft: in the AH-64 the weapon seat is gunner, in the AH-1Z copilot. Read it from the vehicle prefab, do not assume. And do not confuse the compartment name with the interaction context a user action hangs on (step 5).

Do not keep a second counter

The first version counted every press of the mode key to know which attack mode was selected. It drifted: WCS only switches the mode while the missile weapon is selected, so every press with the rocket pod or the cannon in hand moved the private count one place ahead of the screen. The report was “HI flies like DIR, LO flies highest” – everything shifted by one. Read the mode WCS itself keeps; it is public and the server's copy is kept up to date.

Example code – free to useGTG_AH64_DeliveryModeSyncComponent.c (excerpt)
int GetCurrentIndex()
{
	IEntity owner = GetOwner();
	if (owner)
	{
		WCS_Armament_LoadoutComponent loadout = WCS_Armament_LoadoutComponent.Cast(owner.FindComponent(WCS_Armament_LoadoutComponent));
		if (loadout)
			return loadout.GetDeliveryModeIndex();		// the mode WCS shows on the screen = the mode that has to fly
	}

	return m_iCurrentIndex;								// own counter only as a fallback
}

Input actions get renamed

WCS_Armaments 8.3.0 routes its own action name for cycling the attack mode to the vanilla turret action TurretWeaponNextFireMode and registers only that. A listener on the old name stayed silent: the screen switched modes, the server kept flying the first one, and nothing was logged. Resolve the name through the helper WCS uses itself, so the next change does not break you again.

Example code – free to useGTG_AH64_DeliveryModeSyncComponent.c (excerpt)
m_InputManager.AddActionListener(WCS_Armament_InputBindings.Action("WCS_Armament_CycleWeaponFireMode"), EActionTrigger.DOWN, OnCycle);
PitfallRe-check every hook after a WCS update. An override of a method that no longer exists does not just fail your mod: it takes the whole script module down for everybody.

Where the code runs

The key is handled on the client, the missile flies on the server. Anything the flight logic needs – the code, the mode – has to be replicated or sent to the server (the AH-64 mod's mode sync sends from client to server). A purely local variable is invisible to it, and in Workbench you will not notice, because client and server are the same process there.

The same split hides a second trap: on a client that does not own the aircraft, the frame events of a vehicle component never run. Anything that has to move every frame on every machine needs its own entity, or a call queue (the AH-64 mod's missile camera is such an entity; its seeker-camera component keeps a call-queue clock as a replacement).

The laser code

The missile in flight

A phantom explosion

WCS remembers the last terrain contact of a missile and, when the fuse fires, uses it as the explosion position without checking how old it is. A lofted missile that brushes a hillside on the way down then shows its explosion where it touched, up to 215 m before the warhead really detonates (measured: contact at 2315/98/2353, detonation 0.45 s later at 2507/150/2265). The fix is a guard on the method that publishes the impact: if the remembered contact is far from the missile, use the missile.

Example code – free to useGTG_AH64_MissileManeuverability.c (excerpt; the log lines are left out)
modded class WCS_Armament_MissileMovementComponent
{
	override protected void PublishImpact(IEntity hitEntity, vector position, vector normal, ResourceName effectOverride, bool useRecordedContact = false)
	{
		// At about 500 m/s a missile covers up to 8 m per frame; a real contact is far closer than 25 m.
		if (useRecordedContact && m_Projectile && GTG_IsOurHellfire() && vector.Distance(position, m_Projectile.GetOrigin()) > 25)
		{
			super.PublishImpact(null, m_Projectile.GetOrigin(), vector.Up, effectOverride, false);
			return;
		}

		super.PublishImpact(hitEntity, position, normal, effectOverride, useRecordedContact);
	}
}
NoteScope every change to the shared missile class with a test that only your own missile passes (here GTG_IsOurHellfire()). The class is the base of every missile in WCS.

A camera that follows the missile

A missile camera has to be its own entity, because of the ownership rule above. Update it right after the missile has moved in the same frame. Updated before, it trails: in the dedicated-server tests the camera hung 10 to 28 m behind the missile (one run measured 10 - 19 m, another 17 - 28 m) instead of one metre in front of it. The AH-64 camera is a ScriptCamera subclass that sets its world matrix in its own post-frame event, after the movement, and then lets the base class pass exactly that fresh matrix to the engine; there is no order race with the vehicle component.

Example code – free to useGTG_AH64_SeekerCameraDisplayComponent.c (excerpt; the body is shortened)
class GTG_AH64_MissileCamera : ScriptCamera
{
	protected IEntity m_Ziel;				// the missile
	protected vector m_vLokalPosition;		// where on it the camera sits

	override protected void EOnPostFrame(IEntity owner, float timeSlice)
	{
		if (m_Ziel)
		{
			// ... set this camera's world matrix from the missile's CURRENT world matrix ...
		}

		super.EOnPostFrame(owner, timeSlice);	// the base hands this matrix to the engine
	}
}

Testing it

CheckWhy
Run a real dedicated server and a clientWorkbench hides ownership, replication and the key-versus-flight split
Fire from the air, at 30 m or moreA parked helicopter adds a ground-contact effect that has nothing to do with your work
Fire every mode from every rack row, left and rightRack faults show as a pattern: half the rounds, or one side
Compare the peak height of each modeThe modes must differ in the order the screen shows them
Press the mode key with another weapon in hand, then select the missile againCatches any private counter that drifts from the screen
Use a unique code, and no other laser nearbyThe nearest matching target wins
Tag every log line, write the role of the machine into it, then switch the logging offA switch left on fills the log with thousands of lines (one HUD component once wrote about 500 lines a second)
NoteMeasure before changing. Most of the faults on this page had a plausible first explanation that was wrong. The right one came from the server log.

Working examples

Two aircraft that use these pieces on the Workshop:

Both list the SOFLAM Integration Core as a dependency.

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