Overview

This guide details the steps required to create a fully functional custom dynamic map marker in Enfusion. Custom markers allow you to display specialized icons on the map with unique behaviors and visual representations.

Prerequisites

Step-by-Step Process

1. Create Marker Entry Config Class

First, you need to create a class that extends SCR_MapMarkerEntryDynamic and define a custom marker type:

// YourModPrefix_CustomMapMarkerEntry.c
modded enum SCR_EMapMarkerType
{
    CUSTOM_MARKER_TYPE // Your custom marker type
}

[BaseContainerProps(), SCR_MapMarkerTitle()]
class YourModPrefix_CustomMapMarkerEntry : SCR_MapMarkerEntryDynamic
{
    [Attribute("", UIWidgets.Object, "Visual configuration")]
    protected ref SCR_MarkerSimpleConfig m_EntryConfig;

    // Define custom marker type
    override SCR_EMapMarkerType GetMarkerType()
    {
        return SCR_EMapMarkerType.CUSTOM_MARKER_TYPE;
    }

		//Initialize server config for markers.
		override void InitServerLogic()
    {
        super.InitServerLogic();
        
        // We should only create markers on the server
        if (!Replication.IsServer())
            return;
            
        // You can (not required) register this config to a system if you wanted to.
        AG0_YourSystem yourSystem = AG0_YourSystem.GetInstance();
        if (yourSystem)
        {
            // You might want to add a registration method to yourSystem
            yourSystem.RegisterMarkerCallback(this);
            //Then within that system, you can have components register to the system.
            //And then the system could call a method here to create the marker.
        }
        //Really, anything you want to run to initialize your marker config
        //You do it here as it runs on server.
    }

    // Initialize client-side marker appearance
    override void InitClientSettingsDynamic(notnull SCR_MapMarkerEntity marker, notnull SCR_MapMarkerDynamicWComponent widgetComp)
    {
        super.InitClientSettingsDynamic(marker, widgetComp);

        // Set marker appearance manually:
        ResourceName imgset = "{yourimagesetGUID}path/to/your/imageset.imageset";
        string icon = "your_icon_name";
        m_EntryConfig.GetIconResource(imgset, icon);
        //This method sets the imgset/icon based on config.
        //Setting them above this is not strictly required.

        widgetComp.SetImage(imgset, icon);
        widgetComp.SetColor(m_EntryConfig.GetColor());
        widgetComp.SetText(m_EntryConfig.GetText());
    }

		//In this example, a component gets passed to the config to create a marker for.
		//Components don't "exist" in 3D space, so we need to set the marker on an entity.
		//We use GetOwner() on the component to get the IEntity.
		//Your implementation may differ
		void OnRadioRegistered(SCR_YourCustomComponent component)
		{
			//Since we inherit from SCR_MapMarkerEntryDynamic,
			//m_MarkerMgr should exist.
	    if (!component || !m_MarkerMgr)
	        return;
	        
	    // Check if marker already exists for this component
	    if (m_MarkerMgr.GetDynamicMarkerByTarget(GetMarkerType(), component.GetOwner()))
	        return;
	    
		IEntity owner = component.GetOwner();
	  // Create globally visible marker
	  //NOTE: Marker visibility is controlled by if global AND local visibility is true.
	  //If you don't want a client to see certain markers,
	  //You must use SetLocalVisibility on the marker, and execute that on the client.
	  YourModPrefix_CustomMapMarkerEntity yourMarker = YourModPrefix_CustomMapMarkerEntity.Cast(m_MarkerMgr.InsertDynamicMarker(SCR_EMapMarkerType.CUSTOM_MARKER_TYPE, owner));
		if (!yourMarker) //Sometimes marker creation may fail.
			return;
		//Without this, markers will be invisible no matter what local visibility is set to.
	  yourMarker.SetGlobalVisible(true);
		
		//You could then call yourMarker.SomeCustomMethod(), creating that class is next. You can store custom data on the marker and replicate it so that clients can get info from that marker and dictate locally whether it should show/hide that marker.
	}
    
}

2. Create Marker Entity Class

Create a class that extends SCR_MapMarkerEntity to implement custom marker behavior:

// YourModPrefix_CustomMapMarkerEntity.c
[EntityEditorProps(category: "GameScripted/Markers")]
class YourModPrefix_CustomMapMarkerEntityClass : SCR_MapMarkerEntityClass
{
};

class YourModPrefix_CustomMapMarkerEntity : SCR_MapMarkerEntity
{
    // Add custom properties for your marker
    // This is crucial, because without RplProps when a client gets this marker,
    // It doesn't know what the marker is attached to.
    // So if you try using "GetTarget", it will be null on clients. Only server knows.
    // So a RplProp is a "cheap" way to store associated data.
    // Could be an ID stored as int, RplId, string, etc.
    // onRplName tells the client to trigger a method when that property is changed
    // So if you want to change visibility based if RplProp is true/false, you could do that there.
    [RplProp(onRplName: "OnCustomPropertyChanged")]
    protected bool m_CustomProperty;

    // Add callback for property updates
    void OnCustomPropertyChanged()
    {
        // Handle property change
        // Update visibility or other aspects
        // ex.
        // SetLocalVisible(m_CustomProperty);
        // Obviously a replicated bool will be the same for all clients.
        // You could use an int/RplId instead, and create a more complicated method to determine visibility.
    }

    // Override necessary methods
    override void OnCreateMarker()
    {
        super.OnCreateMarker();
        // Custom initialization
    }

    // Add custom methods
    void SetCustomProperty(int value)
    {
        m_CustomProperty = value;
        Replication.BumpMe(); // Important for replication!
        //Can only be done by server (they are authority).
    }
}

3. Duplicate Base Layout

  1. Find the MapMarkerDynamicBase.layout file in Arma Reforger/UI/layouts/Map/Markers
  2. Create a copy named YourModPrefix_CustomMarker.layout
  3. Modify as needed for your custom marker appearance
  4. Ensure widget hierarchy remains The same for compatibility.

4. Create Imageset

  1. Prepare your marker icons as a texture (PNG recommended) Areas on the texture that are white will be recolored based on marker config. You want your PNG to be a tile grid with your future icons. If you are only working on one, you can make your texture 256x256 and put your icon within the top left corner and leave the rest of the texture blank.
  2. Create an imageset file (.imageset) You will need to associate your edds image to this image set and create a “quad” in the quads tab named after your icon name, and with its coordinates set up correctly to center your marker.