← 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

For other modders

5. Optional integration without a hard dependency New

A script cannot say “use this class if it exists”: a reference to a class that is not there stops the compile. What does work is to keep the reference in an addon that only exists when both sides do.

In plain wordsIf your mod should work with or without the Core, put the glue in a third addon that depends on both.
1. Hook in your mod→2. Bridge addon→3. Register→4. Ask every time

What does not work

A reference to GTG_SOFLAM_LaserTargetManager in your main mod makes the Core a hard dependency, because the compiler resolves it. A runtime “if the Core is present” around it does not help – the line must compile first. A missing class breaks the script module, and "ClassName".ToType() can tell you whether a class exists but cannot call a method on it.

What works: a bridge addon

A patternThis is a pattern, not something the Core ships. The published mods that use the Core (the AH-64 and the AH-1Z) simply depend on it. Use a bridge only if your main mod has to stay usable without the Core.

Your main mod does not depend on the Core. It has a small base class, a provider, that answers one question: where is the laser point for this code? A separate addon, the bridge, depends on your mod and the Core and fills that hook. If a player loads the bridge, your weapons follow SOFLAM lasers. If not, nothing changes.

  1. In your main mod, define the question without knowing the Core: a base class with a static register/get and a few virtual methods that answer “no” by default. Your code asks the registered provider, and does something sensible when there is none.
    Example code – free to useyour main mod (the names are yours)
    class MyMod_LaserProvider
    {
    	protected static ref MyMod_LaserProvider s_Instance;
    
    	static void Register(MyMod_LaserProvider provider)
    	{
    		s_Instance = provider;
    	}
    
    	static MyMod_LaserProvider Get()
    	{
    		return s_Instance;
    	}
    
    	bool IsAvailable()
    	{
    		return false;
    	}
    
    	bool FindPointByCode(string code, vector fromPosition, out vector point)
    	{
    		return false;
    	}
    }
  2. In the bridge addon, list both in addon.gproj: your mod's GUID and the Core's 6A48B52029EA0F9A. Derive the provider and answer from the registry:
    Example code – free to usethe bridge addon (the registry calls are the Core's, the rest are your names)
    class MyMod_SoflamLaserProvider : MyMod_LaserProvider
    {
    	override bool IsAvailable()
    	{
    		return GTG_SOFLAM_LaserTargetManager.GetInstance() != null;	// ready once the world has a registry
    	}
    
    	override bool FindPointByCode(string code, vector fromPosition, out vector point)
    	{
    		point = vector.Zero;
    		if (code.IsEmpty())
    			return false;
    
    		GTG_SOFLAM_LaserTargetManager manager = GTG_SOFLAM_LaserTargetManager.GetInstance();
    		if (!manager)
    			return false;
    
    		array<GTG_SOFLAM_LaserTarget> targets = {};
    		if (manager.GetTargetsByCode(code, targets) == 0)
    			return false;
    
    		bool found = false;
    		float bestDistSq = 0;
    		foreach (GTG_SOFLAM_LaserTarget target : targets)
    		{
    			if (!target)
    				continue;
    
    			vector position = target.GetPosition();
    			if (position == vector.Zero)
    				continue;							// created, never reported
    
    			float distSq = vector.DistanceSq(fromPosition, position);
    			if (!found || distSq < bestDistSq)
    			{
    				found = true;
    				bestDistSq = distSq;
    				point = position;
    			}
    		}
    		return found;
    	}
    }
  3. Register it, and do it twice: once when the game mode starts (modded SCR_BaseGameMode.OnGameStart), once when the first character is created (modded SCR_ChimeraCharacter.EOnInit). An empty Workbench test world has no game mode, so only the second path runs there. Make the registration safe to repeat: do nothing if a provider of your kind already stands.
  4. Ask every time. The provider keeps no world state and fetches the registry for each call, so it survives a world change.

The other direction: your mod calls an optional mod by name

What can be done at runtime is calling a method on an object by its name. The Core does it to be friendly to RHS night-vision goggles without depending on RHS:

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 the method does not exist
		return !nvOff;

	return false;
}

If RHS is loaded, the call returns the real answer; if not, it fails and only the Core's own state counts. The vanilla scripts use the same call (the AI behaviour node SCR_AIDecoIsArrayEmpty calls IsEmpty on an object by name). Limits:

Not provenA way to reach GTG_SOFLAM_LaserTargetManager without a compile reference could not be shown from the code of the published mods; both use a dependency. If you find one, test it on a dedicated server first.
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