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.
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.
}
}
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).
}
}
MapMarkerDynamicBase.layout file in Arma Reforger/UI/layouts/Map/MarkersYourModPrefix_CustomMarker.layout