← 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

Night and thermal vision Changed

An infrared laser should be visible to everybody who has night vision, whatever the operator selected. And a sight with night and thermal modes needs to put its filter on the right camera – and take it off the same one.

In plain wordsEach player decides for himself whether he can see infrared. The sight can switch between normal, night and thermal, per camera.
How to hook this up in your own modTo give your own optic these modes, add GTG_SOFLAM_VisionModeComponent to the device and call SetInputEnabled(true) when the player starts looking through it and false when they stop. If you have your own night-vision optic, call GTG_SOFLAM_NVState.SetTemporaryNVState(true) while it is on, and infrared lights become visible for that viewer.

Infrared visibility is a per-viewer fact

Whether this client can currently see infrared light is a purely local state. A real IR laser is visible to anyone with their own night vision, independent of what the operator of the device selected. So there is nothing to replicate: each machine keeps one flag, set when the player looks through a night-vision optic. It is a static field of GTG_SOFLAM_NVState.

Example code – free to useGTG_SOFLAM_NVState.c (excerpt)
class GTG_SOFLAM_NVState
{
	protected static bool s_bTemporaryNV;

	// set by devices: true = this client is looking through a night-vision optic and can see IR light
	static void SetTemporaryNVState(bool state)
	{
		s_bTemporaryNV = state;
	}

	static bool IsTemporaryNVState() { return s_bTemporaryNV; }
	static bool CanLocalPlayerSeeIR() { ... }		// below
}
A static flag that stays onThe flag is static: it holds for the whole client and the rest of the session. In SetNightVision it used to be set at the end, behind the check “is there a renderer”. Whenever the method left early, the flag kept its old value, and that player saw every IR light without goggles, day and night. The beam of the test tripod showed as a red line to the naked eye. The flag is pure state and has nothing to do with drawing; it is set first, before any early return.

An optional mod without a compile dependency

If the viewer wears the night-vision goggles of another mod, that should count too – but a compile reference to that mod would make yours unbuildable without it. The Core calls it by name at runtime (RHS-StatusQuo's RHS_IsNVOff on the player controller): if the mod is loaded the call returns the real answer, if not it fails and only your own state counts.

Example code – free to useGTG_SOFLAM_NVState.c
static bool CanLocalPlayerSeeIR()
{
	if (s_bTemporaryNV)
		return true;

	SCR_PlayerController playerController = SCR_PlayerController.Cast(GetGame().GetPlayerController());
	if (!playerController)
		return false;

	ScriptModule scriptModule = GetGame().GetScriptModule();
	if (!scriptModule)
		return false;

	bool nvOff = true;
	if (scriptModule.Call(playerController, "RHS_IsNVOff", false, nvOff))	// false if there is no such method
		return !nvOff;

	return false;
}
PitfallThis runs where it is asked – the beam asks every frame while a target exists. The Core does not remember a failed call; it asks again every time. If you copy the pattern into something hot, remember the first failure and stop asking.

The light itself, GTG_SOFLAM_LightEntity, has the gate built in: SetEnabledWithIRCheck(true) only switches an IR light on (prefab attribute m_bIsIR) if CanLocalPlayerSeeIR() says so.

Which modes exist is data

Two attributes of GTG_SOFLAM_VisionModeComponent, m_bAllowNightVision and m_bAllowThermal (both default true), decide which modes a device has at all, and the cycle is built from them: both on gives normal → night → thermal, night only gives normal → night, none switches the whole component off (it hooks up no keys). An older device without thermal needs a tick in the prefab editor, not a class. The mode is the enum GTG_SOFLAM_EVisionMode (NORMAL, NIGHT_VISION, THERMAL); GetOnVisionModeChanged() returns a ScriptInvoker that fires with the component and the new mode, so an optic can update its display without asking every frame. A mode that is switched off on the prefab is quietly taken back to normal when a script asks for it.

The camera-id trap

The thermal filter hangs on a camera index. Remember the index in use when you switch the filter on, and clear that one when you switch it off. GetCurrentCameraId() can have changed in between – the player left the optic quickly, or changed zoom, which in some sights is a different camera. Clearing on the wrong id left the thermal filter on the active camera and the screen stayed white until switching again released it.

Example code – free to useGTG_SOFLAM_VisionModeComponent.c (excerpt)
protected void SetThermal(bool state)
{
	if (!GTG_HasRendering())
		return;

	BaseWorld world = GetGame().GetWorld();
	if (!world)
		return;

	if (state)
	{
		m_iThermalCamId = world.GetCurrentCameraId();		// REMEMBER it
		world.SetCameraPostProcessEffect(m_iThermalCamId, m_iThermalSlot,
			PostProcessEffectType.ThermalImaging, GTG_CurrentThermalFilter());
		return;
	}

	int camId = m_iThermalCamId;
	if (camId == -1)
		camId = world.GetCurrentCameraId();

	// clear THE camera we set it on...
	world.SetCameraPostProcessEffect(camId, m_iThermalSlot, PostProcessEffectType.ThermalImaging, string.Empty);

	// ...and, to be safe, the current one too if it differs. Costs nothing where nothing was set.
	int currentCamId = world.GetCurrentCameraId();
	if (currentCamId != camId)
		world.SetCameraPostProcessEffect(currentCamId, m_iThermalSlot, PostProcessEffectType.ThermalImaging, string.Empty);

	m_iThermalCamId = -1;
}

Night vision uses a vanilla HDR slot. The Core adds one method to the vanilla SCR_ScreenEffectsManager (modded class), GTG_SOFLAM_SetHDR(ResourceName material, bool enable), writing slot/priority 16 (the same as RHS, so that both together do not stack two effects). An empty material clears the slot instead of putting the vanilla HDR on it: it used to set the vanilla material without the parameters the game runs it with, the slot stayed occupied, and in daylight the whole picture burned out after leaving night vision.

Smaller measured choices

ChoiceReason
Own copies of the thermal filters (GTG_ThermalFilter_WhiteAsHot, _BlackAsHot) with a fixed min/max contrast (AutomaticMinMax)The stock filter (RHS's AutomaticHistogramPLHE) recomputes its contrast curve periodically, and that shows as short, irregular dropouts of the picture.
Thermal slot m_iThermalSlot = 12RHS's vehicle thermal uses slot 11. Two effects on the same slot overwrite each other. Pick a number others do not, and note it down.
Night-vision gain levels 0.01 … 30, start on index 5 (= 1.0)Taken from the AH-64 mod, where they were flown in.
Mode key J (GTG_SOFLAM_VisionModeAction), not NIn a collection of about 70 mods N did not reach the action at all, not even with shift. Measured in a multiplayer test.
Other keys: GTG_SOFLAM_ToggleNvThermalAction (night ↔ thermal), _GainUpAction / _GainDownAction (LSHIFT + wheel: night-vision gain or thermal filter), _LaserPowerUpAction / _LaserPowerDownAction (H + wheel)All six are attributes of the component, so another device can use other action names.
Skip everything visual on the dedicated server (RplSession.Mode() != RplMode.Dedicated)No render engine, no point doing material work.
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