rocklandgames.tv Wiki Compass Heading Display

Compass Heading Display

v1.0.0.5 FS25 PC DOWNLOAD ON MODHUB →

// Overview

What It Does

Compass Heading Display adds a smooth, scrolling horizontal compass bar to the top of your screen in Farming Simulator 25. Inspired by the navigation HUDs in GTA RP, FiveM, and flight simulators, it provides constant situational awareness of your heading, nearby players, AI helpers, waypoints, and severe weather — all rendered directly on the game's HUD without any popup windows or menus.

Beyond the player-facing compass, Compass Heading Display is the open compass platform for the FS25 modding community. Any mod can display custom markers on the compass bar with a few lines of Lua and zero hard dependencies. The public API includes 60 functions for markers, categories, animations, events, distance filtering, and navigation utilities.

Why It Was Created

Farming Simulator 25 has no native compass or directional HUD. Players navigating large maps, coordinating in multiplayer, or managing AI helpers have to rely on the minimap or guess directions. Compass Heading Display solves this by providing an always-visible, non-intrusive heading reference that works on foot, in vehicles, and across all camera modes.

Who It's For

AudienceBenefit
PlayersAlways-visible compass heading, player tracking, helper alerts
Server AdminsWorks on dedicated servers, farm-based marker filtering
Mod Developers60-function public API for custom marker integration

Compatibility

EnvironmentSupported
Farming Simulator 25 (PC)✓ Yes
Singleplayer✓ Yes
Multiplayer (client)✓ Yes
Multiplayer (dedicated server)✓ Yes — renders on clients only
Console (PlayStation/Xbox)✗ No — console mods cannot include Lua
FS22 or earlier✗ No

// Features

Complete Feature List

FeatureDescription
16-Point Compass RoseFull N/NNE/NE/ENE/E/ESE/SE/SSE/S/SSW/SW/WSW/W/WNW/NW/NNW labels with smooth scrolling
Numeric HeadingLive 000°–359° heading displayed below the compass bar
Tick MarksSubtle tick marks every 10° for precise heading reference
Gold Center IndicatorFixed gold tick at screen center showing your exact look direction
Waypoint TrackingDetects the FS25 navigation target and displays an orange marker with distance
Player MarkersShows other players on the compass with their name and distance, colored by farm
Farm FilteringFilter player/helper markers to show only your farm or all farms
Helper MarkersAI worker markers (H1, H2, etc.) with farm coloring
Blocked Helper AlertsStuck helpers flash red with an exclamation mark and elevated priority
Tornado TrackingScans the scene for tornado objects and shows a flashing red diamond with distance
Teleport DetectionSuppresses marker jitter for 0.5 seconds after a player teleports
Auto-HideAutomatically hides when GUI screens, menus, or the ESC screen are open
Camera-Aware HeadingWorks with free-look, 3rd person orbit, and interior camera
Farm ColoringPlayer and helper markers use the farm's assigned color (brightened for visibility)
Marker ClampingOff-screen markers clamp to bar edges with < / > indicators
Distance DisplayMarkers show distance in meters (< 1km) or kilometers (≥ 1km)
Settings PersistenceAll settings saved to XML and restored on next load
Full Public API60 functions for third-party mod integration
Provider RegistryMods register as marker providers — zero-dependency integration
Standalone MarkersAdd/remove/update individual markers by ID with animations
Marker AnimationsFlash, pulse, fade-in, and urgent effects on any marker
Category SystemGroup markers into categories with per-category enable/disable and coloring
Event SystemSubscribe to marker enter/exit view, add/remove, compass toggle, and settings change events
Distance FilteringMarkers can define min/max display distance
TTL MarkersAuto-expiring markers with configurable time-to-live
Configurable LayoutBar position, width, vertical position, field of view, and opacity all adjustable
Configurable ColorsEvery color is individually customizable via settings.xml or API

What Makes It Unique

Unlike other compass mods that only display a heading number or a basic bar, Compass Heading Display is a full marker platform:

  • Other compass mods: Show a heading number. Period.
  • Compass Heading Display: Shows heading, cardinal labels, players, helpers, waypoints, tornados, custom mod markers, has a 60-function API, event system, animation engine, category management, distance filtering, and provider registry.

No other FS25 compass mod exposes a public API. Compass Heading Display is designed from the ground up as infrastructure that the entire modding community can build on.

// Requirements

Minimum Requirements

RequirementDetail
GameFarming Simulator 25, version 1.16 or newer
PlatformPC (Windows or Linux dedicated server)
DependenciesNone — fully standalone, zero dependencies

Multiplayer Compatibility

Compass Heading Display is fully multiplayer-compatible:

  • Each client renders its own compass independently
  • Player positions are read from the game's player system — no custom network events
  • Helper and vehicle data is read from the shared vehicle system
  • No server-side processing required

Dedicated Server

On a dedicated server (headless, no GPU): the mod loads without errors, overlay creation is silently skipped, no rendering occurs, and the mod has zero server-side impact. Clients connecting to the server render their own compass locally.

Mod Conflicts

No known conflicts. Compass Heading Display does not modify any vanilla game systems, intercept any input, or alter any game data. It only reads existing game state and renders its own HUD overlay.

// Installation

Single Player

  1. Download FS25_CompassHeading.zip from ModHub
  2. Place the zip file in your mods folder:
Documents/My Games/FarmingSimulator2025/mods/FS25_CompassHeading.zip
  1. Launch Farming Simulator 25
  2. Enable "Compass Heading Display" in the mod manager
  3. Start or load a save game — the compass appears at the top of the screen

Multiplayer Client

Same as singleplayer — place FS25_CompassHeading.zip in your mods folder. If the server has the mod installed, it will sync automatically and the compass renders locally on your client.

Dedicated Server

  1. Upload FS25_CompassHeading.zip to the server's mods directory:
/profile/mods/FS25_CompassHeading.zip
  1. Add the mod to the server's active mod list
  2. Restart the server
  3. Clients connecting will see the compass on their screens

File Structure

FS25_CompassHeading.zip
├── modDesc.xml              # Mod descriptor (version, descriptions, l10n)
├── CompassHeading.lua       # All mod logic in a single file
├── icon_compassheading.dds  # 512x512 mod icon (BC1_UNORM)
├── brand_rocklandusagaming.dds
├── white.dds                # 1x1 white pixel for overlay rendering
├── l10n_en.xml              # English localization
├── l10n_de.xml              # German localization
└── l10n_fr.xml              # French localization

// Configuration

Settings File Location

Documents/My Games/FarmingSimulator2025/modSettings/FS25_CompassHeading/settings.xml

This file is created automatically on first load with default values. Edit it while the game is not running, or use the API to change settings at runtime.

Display Settings

SettingTypeDefaultDescription
enabledbooleantrueMaster enable/disable for the entire compass
barPositionXfloat0.500Horizontal center position (0.0 = left, 1.0 = right)
barWidthfloat0.680Width of the compass bar (fraction of screen width)
barVerticalYfloat0.974Vertical center position (0.0 = bottom, 1.0 = top)
fieldOfViewfloat160Degrees of compass visible on the bar (30–360)
backgroundOpacityfloat0.62Background bar opacity (0.0 = invisible, 1.0 = solid)

Compass Element Visibility

SettingTypeDefaultDescription
showCardinalLabelsbooleantrueShow N/S/E/W labels
showIntercardinalLabelsbooleantrueShow NE/SE/SW/NW labels
showMinorLabelsbooleantrueShow NNE/ENE/ESE/etc. labels
showTickMarksbooleantrueShow 10° tick marks
showCenterIndicatorbooleantrueShow gold center tick
showDegreeReadoutbooleantrueShow numeric heading below bar

Marker Visibility

SettingTypeDefaultDescription
showPlayerMarkersbooleantrueShow other player markers
playerMarkerFilterstring"all""all" = all players, "farm" = same farm only
showWaypointMarkerbooleantrueShow navigation waypoint marker
showTornadoMarkerbooleantrueShow tornado detection marker
showHelperMarkersbooleantrueShow AI helper markers
helperMarkerFilterstring"all""all" = all farms, "farm" = same farm only

Color Settings

All colors are RGBA values (0.0–1.0). Each color has r, g, b, and a attributes in settings.xml.

SettingDefault RGBADescription
colCenterIndicator1.00, 0.85, 0.15, 1.00Gold center tick color
colDegreeReadout1.00, 1.00, 1.00, 0.50Heading number color
colCardinal1.00, 1.00, 1.00, 1.00N/S/E/W label color
colIntercardinal0.88, 0.88, 0.88, 1.00NE/SE/SW/NW label color
colMinor0.65, 0.65, 0.65, 1.00NNE/ENE/etc. label color
colPlayerMarker0.25, 0.88, 1.00, 1.00Default player marker color (cyan)
colWaypointMarker1.00, 0.55, 0.05, 1.00Waypoint marker color (orange)
colTornadoMarker1.00, 0.15, 0.15, 1.00Tornado marker color (red)
colHelperMarker0.60, 0.90, 0.40, 1.00Helper marker color (green)
colHelperBlocked1.00, 0.25, 0.25, 1.00Blocked helper color (red)

Example settings.xml



  true
  
  
  
  
    
    
    
  

// API Reference

The public API is published at two locations after map load:

local API = g_currentMission.compassHeading  -- Recommended
local API = g_compassHeading                -- Global fallback
⚠ Note
Always check API.isReady() before calling other functions. The API is not available until loadMap has completed.
Showing 60 of 60 APIs
System Status
System Status isReady()
Checks whether the Compass Heading Display system is loaded and ready to accept API calls.
Returns
booleantrue if initialized and ready, false otherwise.
Example
local API = g_currentMission.compassHeading
if API and API.isReady() then
    print("Compass Heading Display is ready")
end
⚠ Note
Always call this before using any other API function. Returns false on dedicated servers and during the brief window before loadMap completes.
System Status getVersion()
Returns the current version string of the Compass Heading Display mod.
Returns
string — Version string in "major.minor.patch.build" format (e.g., "1.0.0.5").
Example
local API = g_currentMission.compassHeading
if API then
    print("Compass version: " .. API.getVersion())
end
⚠ Note
Useful for checking compatibility if your mod requires a specific minimum version.
Enable / Disable
Enable / Disable setEnabled()
Enables or disables the entire compass HUD. When disabled, nothing is rendered and no marker processing occurs.
Parameters
NameTypeRequiredDescription
enabledbooleanYestrue to show the compass, false to hide it
Returns
None
Example
local API = g_currentMission.compassHeading
API.setEnabled(false)  -- Hide the compass
API.setEnabled(true)   -- Show the compass
⚠ Note
Fires the compassToggle event when the state changes. The setting persists only for the current session — use saveSettings() to persist.
Enable / Disable isEnabled()
Returns whether the compass is currently enabled.
Returns
booleantrue if the compass is enabled and rendering, false if disabled.
Example
local API = g_currentMission.compassHeading
if API.isEnabled() then
    print("Compass is visible")
end
Settings
Settings getSetting()
Retrieves the current value of a single setting by its key name.
Parameters
NameTypeRequiredDescription
keystringYesThe setting key (e.g., "fieldOfView", "showPlayerMarkers")
Returns
The setting value (type varies: boolean, number, string, or table for colors). Returns nil if the key is invalid.
Example
local API = g_currentMission.compassHeading
local fov = API.getSetting("fieldOfView")
print("Current FOV: " .. tostring(fov))

local playerColor = API.getSetting("colPlayerMarker")
-- playerColor = {r=0.25, g=0.88, b=1.00, a=1.00}
⚠ Note
Color values are returned as a copy — modifying the returned table does not affect the setting.
Settings setSetting()
Updates a single setting by its key name.
Parameters
NameTypeRequiredDescription
keystringYesThe setting key to update
valueanyYesThe new value (must match the expected type for the key)
Returns
booleantrue if the setting was updated, false if the key is invalid.
Example
local API = g_currentMission.compassHeading
API.setSetting("fieldOfView", 120)
API.setSetting("backgroundOpacity", 0.8)
API.setSetting("colPlayerMarker", {r=1, g=0.5, b=0, a=1})
⚠ Note
Layout-related settings automatically trigger a layout recomputation. Fires the settingChanged event. Call saveSettings() to persist.
Settings getSettings()
Returns a shallow copy of all current settings as a key-value table.
Returns
table — A table containing all settings. Color values are copies (safe to modify).
Example
local API = g_currentMission.compassHeading
local settings = API.getSettings()
for key, value in pairs(settings) do
    print(key .. " = " .. tostring(value))
end
⚠ Note
The returned table is a snapshot — modifying it does not affect the live settings. Use setSetting() to apply changes.
Settings setFOV()
Sets the compass field of view — how many degrees of the compass are visible on the bar at once.
Parameters
NameTypeRequiredDescription
degreesnumberYesField of view in degrees (30–360)
Returns
booleantrue if set successfully, false if out of range.
Example
local API = g_currentMission.compassHeading
API.setFOV(90)   -- Narrow FOV for precise navigation
API.setFOV(200)  -- Wide FOV to see more of the compass
⚠ Note
Lower values make the compass appear more zoomed-in. Higher values show more compass but compress spacing. Default is 160.
Settings getFOV()
Returns the current compass field of view in degrees.
Returns
number — Current FOV in degrees.
Example
local API = g_currentMission.compassHeading
print("FOV: " .. API.getFOV() .. "°")
Settings setBarPosition()
Sets the horizontal and/or vertical center position of the compass bar on screen.
Parameters
NameTypeRequiredDescription
xnumberNoHorizontal center (0.0 = left, 1.0 = right). Pass nil to leave unchanged.
ynumberNoVertical center (0.0 = bottom, 1.0 = top). Pass nil to leave unchanged.
Returns
None
Example
local API = g_currentMission.compassHeading
API.setBarPosition(0.5, 0.05)   -- Bottom-center
API.setBarPosition(0.35, 0.97)  -- Top-left
⚠ Note
Triggers layout recomputation immediately. Changes are visual only — call saveSettings() to persist.
Settings setBarWidth()
Sets the width of the compass bar as a fraction of screen width.
Parameters
NameTypeRequiredDescription
wnumberYesBar width (0.0–1.0). Default is 0.680.
Returns
None
Example
local API = g_currentMission.compassHeading
API.setBarWidth(1.0)  -- Full-width
API.setBarWidth(0.4)  -- Compact
Settings setBackgroundOpacity()
Sets the opacity of the compass background bar.
Parameters
NameTypeRequiredDescription
anumberYesOpacity (0.0 = fully transparent, 1.0 = fully opaque). Default is 0.62.
Returns
None
Example
local API = g_currentMission.compassHeading
API.setBackgroundOpacity(0.3)  -- Subtle background
API.setBackgroundOpacity(1.0)  -- Solid background
Settings reloadSettings()
Reloads all settings from the settings.xml file on disk, discarding any runtime changes.
Returns
None
Example
local API = g_currentMission.compassHeading
API.reloadSettings()
print("Settings reloaded from disk")
⚠ Note
Re-reads the XML file and recomputes the layout. Useful if the user edits settings.xml externally.
Settings saveSettings()
Writes all current settings to the settings.xml file on disk.
Returns
None
Example
local API = g_currentMission.compassHeading
API.setSetting("fieldOfView", 120)
API.saveSettings()
⚠ Note
Settings are automatically saved when the game closes normally. Use this for immediate persistence after API-driven changes.
Marker Lifecycle
Marker Lifecycle addMarker()
Adds a standalone marker to the compass at a fixed world position. The marker persists until explicitly removed or its TTL expires.
Parameters
NameTypeRequiredDescription
idstringYesUnique identifier for this marker
optstableYesMarker configuration table (x, z required; label, color, style, scale, priority, category, maxDistance, minDistance, flash, flashHz, pulse, pulseHz, pulseMin, pulseMax, fadeIn, fadeInDur, urgent, ttl)
Returns
booleantrue if the marker was added successfully, false on invalid input.
Example
local API = g_currentMission.compassHeading

-- Simple marker at a world position
API.addMarker("my_shop", {
    x = 500, z = -300,
    label = "Shop",
    color = {r=1, g=0.9, b=0.3, a=1},
    style = "diamond",
    category = "shop"
})

-- Emergency marker with flashing and auto-expiry
API.addMarker("fire_01", {
    x = 1200, z = -800,
    label = "FIRE",
    color = {r=1, g=0.2, b=0.1, a=1},
    style = "exclaim",
    category = "emergency",
    urgent = true,
    ttl = 300,
    maxDistance = 5000
})
⚠ Note
If a marker with the same id already exists, it is overwritten. Fires the markerAdded event.
Marker Lifecycle addTemporaryMarker()
Convenience wrapper for addMarker() that sets a default TTL of 10 seconds. Ideal for transient notifications.
Parameters
NameTypeRequiredDescription
idstringYesUnique identifier for this marker
optstableYesSame options as addMarker(). If ttl is not specified, defaults to 10 seconds.
Returns
booleantrue if the marker was added, false on error.
Example
local API = g_currentMission.compassHeading
API.addTemporaryMarker("delivery_alert", {
    x = 800, z = -400,
    label = "Deliver",
    color = {r=1, g=0.75, b=0, a=1},
    style = "triangle",
    category = "delivery",
    ttl = 15,
    fadeIn = true
})
⚠ Note
The marker auto-removes when TTL expires, firing the markerExpired event.
Marker Lifecycle removeMarker()
Removes a standalone marker by its ID.
Parameters
NameTypeRequiredDescription
idstringYesThe marker ID to remove
Returns
booleantrue if found and removed, false if no marker with that ID exists.
Example
local API = g_currentMission.compassHeading
API.removeMarker("fire_01")
⚠ Note
Fires the markerRemoved event on successful removal.
Marker Lifecycle updateMarker()
Updates one or more properties of an existing standalone marker without removing and re-adding it.
Parameters
NameTypeRequiredDescription
idstringYesThe marker ID to update
fieldstableYesTable of field names and new values to apply
Returns
booleantrue if found and updated, false if marker does not exist.
Example
local API = g_currentMission.compassHeading
-- Move a marker to a new position
API.updateMarker("fire_01", {x = 1250, z = -820})

-- Change color and enable flashing
API.updateMarker("fire_01", {
    color = {r=1, g=0, b=0, a=1},
    flash = true,
    flashHz = 4.0
})
⚠ Note
Only specified fields are changed — unspecified fields retain their current values.
Marker Lifecycle getMarker()
Returns a copy of a standalone marker's public properties.
Parameters
NameTypeRequiredDescription
idstringYesThe marker ID to retrieve
Returns
table|nil — A copy of the marker's properties, or nil if no marker with that ID exists.
Example
local API = g_currentMission.compassHeading
local marker = API.getMarker("my_shop")
if marker then
    print("Shop is at: " .. marker.x .. ", " .. marker.z)
    print("Category: " .. marker.category)
end
⚠ Note
The returned table is a copy — modifying it does not affect the actual marker. Use updateMarker() to make changes.
Marker Lifecycle hasMarker()
Checks whether a standalone marker with the given ID exists.
Parameters
NameTypeRequiredDescription
idstringYesThe marker ID to check
Returns
booleantrue if the marker exists, false otherwise.
Example
local API = g_currentMission.compassHeading
if not API.hasMarker("my_shop") then
    API.addMarker("my_shop", {x=500, z=-300, label="Shop"})
end
Marker Lifecycle clearMarkers()
Removes all standalone markers, optionally filtered by category.
Parameters
NameTypeRequiredDescription
categorystringNoIf provided, only markers in this category are removed. If nil, all markers are removed.
Returns
number — The number of markers removed.
Example
local API = g_currentMission.compassHeading
local count = API.clearMarkers("emergency")
print("Removed " .. count .. " emergency markers")
API.clearMarkers()  -- Remove ALL
⚠ Note
Does not affect built-in markers (players, helpers, waypoints, tornado) or provider markers.
Marker Lifecycle getMarkerCount()
Returns the number of active standalone markers.
Parameters
NameTypeRequiredDescription
categorystringNoIf provided, counts only markers in this category. If nil, counts all markers.
Returns
number — The count of matching markers.
Example
local API = g_currentMission.compassHeading
local total = API.getMarkerCount()
local emergencies = API.getMarkerCount("emergency")
print(total .. " total, " .. emergencies .. " emergency")
Provider Management
Provider Management getProvider()
Returns the provider object registered with the given ID.
Parameters
NameTypeRequiredDescription
idstringYesThe provider ID to look up
Returns
table|nil — The provider object, or nil if not registered.
Example
local API = g_currentMission.compassHeading
local provider = API.getProvider("my_delivery_mod")
if provider then
    print("Provider active: " .. tostring(provider.enabled ~= false))
end
⚠ Note
Returns the actual provider object (not a copy). Modifying it directly affects the provider.
Provider Management getProviders()
Returns a list of all registered providers with summary information.
Returns
table — Array of tables, each containing {id, label, enabled, category}.
Example
local API = g_currentMission.compassHeading
local providers = API.getProviders()
for _, p in ipairs(providers) do
    print(string.format("Provider: %s [%s] enabled=%s",
        p.id, p.category, tostring(p.enabled)))
end
Provider Management isProviderActive()
Checks if a provider is registered and enabled.
Parameters
NameTypeRequiredDescription
idstringYesThe provider ID
Returns
booleantrue if the provider exists and is enabled, false otherwise.
Example
local API = g_currentMission.compassHeading
if API.isProviderActive("firefighter_mod") then
    print("Fire mod markers are active on compass")
end
Provider Management setProviderEnabled()
Enables or disables a registered provider without unregistering it.
Parameters
NameTypeRequiredDescription
idstringYesThe provider ID
enabledbooleanYestrue to enable, false to disable
Returns
booleantrue if found and updated, false if not found.
Example
local API = g_currentMission.compassHeading
API.setProviderEnabled("delivery_mod", false)  -- Disable
API.setProviderEnabled("delivery_mod", true)   -- Re-enable
⚠ Note
When disabled, the provider's getPoints() method is not called and its markers are not rendered.
Category Management
Category Management setCategoryEnabled()
Enables or disables an entire category of markers. When disabled, no markers in that category are rendered — including built-in, standalone, and provider markers.
Parameters
NameTypeRequiredDescription
categorystringYesThe category name
enabledbooleanYestrue to enable, false to disable
Returns
booleantrue on success, false on invalid input.
Example
local API = g_currentMission.compassHeading
API.setCategoryEnabled("helper", false)     -- Hide all helper markers
API.setCategoryEnabled("emergency", false)  -- Hide tornado + fire
⚠ Note
The category is auto-created if it doesn't exist. Built-in categories: player, waypoint, emergency, helper.
Category Management isCategoryEnabled()
Checks whether a category is currently enabled.
Parameters
NameTypeRequiredDescription
categorystringYesThe category name
Returns
booleantrue if enabled (or never explicitly set), false if disabled.
Example
local API = g_currentMission.compassHeading
if API.isCategoryEnabled("emergency") then
    print("Emergency markers are visible")
end
Category Management setCategoryColor()
Sets the default color for markers in a category. New markers that don't specify a custom color will inherit this color.
Parameters
NameTypeRequiredDescription
categorystringYesThe category name
colortableYes{r, g, b, a} color table (0.0–1.0 each)
Returns
booleantrue on success, false on invalid input.
Example
local API = g_currentMission.compassHeading
API.setCategoryColor("delivery", {r=1.0, g=0.8, b=0.0, a=1.0})
Category Management getCategoryColor()
Returns the current default color for a category.
Parameters
NameTypeRequiredDescription
categorystringYesThe category name
Returns
table{r, g, b, a} color table.
Example
local API = g_currentMission.compassHeading
local color = API.getCategoryColor("emergency")
print(string.format("Emergency color: R=%.2f G=%.2f B=%.2f",
    color.r, color.g, color.b))
Category Management getCategories()
Returns a list of all active categories with their current state.
Returns
table — Array of tables, each containing {name, enabled, color}.
Example
local API = g_currentMission.compassHeading
local categories = API.getCategories()
for _, cat in ipairs(categories) do
    print(string.format("%s: enabled=%s", cat.name, tostring(cat.enabled)))
end
Event Callbacks
Event Callbacks on()
Registers a callback function for a named event. This is the generic event subscription function.
Parameters
NameTypeRequiredDescription
eventstringYesEvent name (see Events section for full list)
callbackfunctionYesFunction to call when the event fires
Returns
number|nil — Callback index, or nil if the compass is not ready.
Example
local API = g_currentMission.compassHeading
API.on("markerAdded", function(id)
    print("Marker added: " .. id)
end)
⚠ Note
Callbacks are wrapped in pcall — a failing callback will not crash the compass. There is no unsubscribe function; callbacks persist until deleteMap.
Event Callbacks onMarkerEnterView()
Registers a callback that fires when a marker scrolls into the visible compass bar.
Parameters
NameTypeRequiredDescription
callbackfunctionYesfunction(markerId) — called with the marker's ID string
Returns
number|nil — Callback index.
Example
local API = g_currentMission.compassHeading
API.onMarkerEnterView(function(id)
    print("Now visible: " .. id)
end)
Event Callbacks onMarkerExitView()
Registers a callback that fires when a marker scrolls out of the visible compass bar.
Parameters
NameTypeRequiredDescription
callbackfunctionYesfunction(markerId) — called with the marker's ID string
Returns
number|nil — Callback index.
Example
local API = g_currentMission.compassHeading
API.onMarkerExitView(function(id)
    print("No longer visible: " .. id)
end)
Event Callbacks onCompassToggle()
Registers a callback that fires when the compass is enabled or disabled via setEnabled().
Parameters
NameTypeRequiredDescription
callbackfunctionYesfunction(enabled) — called with true when enabled, false when disabled
Returns
number|nil — Callback index.
Example
local API = g_currentMission.compassHeading
API.onCompassToggle(function(enabled)
    print("Compass is now " .. (enabled and "ON" or "OFF"))
end)
Event Callbacks onProviderRegistered()
Registers a callback that fires when a new marker provider is registered.
Parameters
NameTypeRequiredDescription
callbackfunctionYesfunction(providerId) — called with the provider's ID string
Returns
number|nil — Callback index.
Example
local API = g_currentMission.compassHeading
API.onProviderRegistered(function(id)
    print("New compass provider: " .. id)
end)
Event Callbacks onProviderUnregistered()
Registers a callback that fires when a marker provider is unregistered.
Parameters
NameTypeRequiredDescription
callbackfunctionYesfunction(providerId) — called with the provider's ID string
Returns
number|nil — Callback index.
Example
local API = g_currentMission.compassHeading
API.onProviderUnregistered(function(id)
    print("Provider removed: " .. id)
end)
Event Callbacks onSettingChanged()
Registers a callback that fires when any setting is changed via setSetting().
Parameters
NameTypeRequiredDescription
callbackfunctionYesfunction(key, newValue, oldValue)
Returns
number|nil — Callback index.
Example
local API = g_currentMission.compassHeading
API.onSettingChanged(function(key, newVal, oldVal)
    print(string.format("Setting '%s' changed: %s -> %s",
        key, tostring(oldVal), tostring(newVal)))
end)
Event Callbacks onMarkerAdded()
Registers a callback that fires when a standalone marker is added.
Parameters
NameTypeRequiredDescription
callbackfunctionYesfunction(markerId)
Returns
number|nil — Callback index.
Example
local API = g_currentMission.compassHeading
API.onMarkerAdded(function(id)
    print("Marker created: " .. id)
end)
Event Callbacks onMarkerRemoved()
Registers a callback that fires when a standalone marker is removed (manually or via clearMarkers()).
Parameters
NameTypeRequiredDescription
callbackfunctionYesfunction(markerId)
Returns
number|nil — Callback index.
Example
local API = g_currentMission.compassHeading
API.onMarkerRemoved(function(id)
    print("Marker removed: " .. id)
end)
Event Callbacks onMarkerExpired()
Registers a callback that fires when a standalone marker's TTL expires and it is auto-removed.
Parameters
NameTypeRequiredDescription
callbackfunctionYesfunction(markerId)
Returns
number|nil — Callback index.
Example
local API = g_currentMission.compassHeading
API.onMarkerExpired(function(id)
    print("Marker expired: " .. id)
    -- Re-add or trigger follow-up logic here
end)
Distance & Proximity
Distance & Proximity getNearestMarker()
Finds the nearest standalone marker to the player, optionally filtered by category.
Parameters
NameTypeRequiredDescription
categorystringNoIf provided, only searches markers in this category
Returns
table|nil{id, x, z, label, category, distance} for the nearest marker, or nil if no markers exist.
Example
local API = g_currentMission.compassHeading
local nearest = API.getNearestMarker("delivery")
if nearest then
    print(string.format("Nearest delivery: %s at %dm",
        nearest.label, nearest.distance))
end
Distance & Proximity getMarkersInRange()
Returns all standalone markers within a specified distance from the player.
Parameters
NameTypeRequiredDescription
rangenumberYesMaximum distance in meters
categorystringNoIf provided, only returns markers in this category
Returns
table — Array of {id, x, z, label, category, distance} tables.
Example
local API = g_currentMission.compassHeading
local nearby = API.getMarkersInRange(2000, "emergency")
for _, m in ipairs(nearby) do
    print(string.format("%s: %dm away", m.label, m.distance))
end
Navigation Utilities
Navigation getBearing()
Returns the player's current compass bearing (the heading displayed on the compass).
Returns
number|nil — Heading in degrees (0–359.9), or nil if unavailable.
Example
local API = g_currentMission.compassHeading
local bearing = API.getBearing()
if bearing then
    print(string.format("Heading: %03d°", math.floor(bearing + 0.5) % 360))
end
⚠ Note
Reflects the camera's look direction. Works on foot, in vehicles, and in all camera modes.
Navigation getBearingTo()
Calculates the compass bearing from the player's current position to a world coordinate.
Parameters
NameTypeRequiredDescription
xnumberYesTarget world X coordinate
znumberYesTarget world Z coordinate
Returns
number|nil — Bearing in degrees (0–359.9), or nil if player position is unavailable or target is within 2 meters.
Example
local API = g_currentMission.compassHeading
local bearing = API.getBearingTo(1200, -800)
if bearing then
    print(string.format("Fire is at bearing %03d°", math.floor(bearing + 0.5)))
end
Navigation getDistanceTo()
Calculates the straight-line distance from the player to a world coordinate.
Parameters
NameTypeRequiredDescription
xnumberYesTarget world X coordinate
znumberYesTarget world Z coordinate
Returns
number|nil — Distance in meters, or nil if player position is unavailable.
Example
local API = g_currentMission.compassHeading
local dist = API.getDistanceTo(1200, -800)
if dist then
    print("Distance to target: " .. API.formatDistance(dist))
end
Navigation getPlayerPosition()
Returns the player's current world position (X, Z coordinates).
Returns
number, number — World X and Z coordinates, or nil, nil if unavailable.
Example
local API = g_currentMission.compassHeading
local x, z = API.getPlayerPosition()
if x then
    print(string.format("Player at: (%.1f, %.1f)", x, z))
end
⚠ Note
Position source priority: controlled vehicle rootNode > g_localPlayer:getCurrentVehicle() > player vehicle fields > active camera > player rootNode.
Navigation formatDistance()
Formats a distance value as a human-readable string, using meters under 1km and kilometers over 1km.
Parameters
NameTypeRequiredDescription
metersnumberYesDistance in meters
Returns
string — Formatted distance (e.g., "450m", "2.3km").
Example
local API = g_currentMission.compassHeading
print(API.formatDistance(450))   -- "450m"
print(API.formatDistance(2345))  -- "2.3km"
print(API.formatDistance(50))    -- "50m"
Navigation getCardinalDirection()
Converts a numeric bearing to a 16-point cardinal direction string.
Parameters
NameTypeRequiredDescription
bearingnumberNoBearing in degrees (0–360). If nil, uses the player's current heading.
Returns
string|nil — Cardinal direction string (e.g., "N", "NNE", "NE"), or nil if bearing is unavailable.
Example
local API = g_currentMission.compassHeading
local dir = API.getCardinalDirection()
print("Facing: " .. tostring(dir))     -- "NNW"
print(API.getCardinalDirection(45))    -- "NE"
print(API.getCardinalDirection(270))   -- "W"
print(API.getCardinalDirection(337))   -- "NNW"
⚠ Note
The 16 cardinal points span 22.5° each: N (348.75°–11.25°), NNE (11.25°–33.75°), etc.
Visual Shortcuts
Visual Shortcuts setMarkerColor()
Changes the color of an existing standalone marker. Convenience wrapper for updateMarker().
Parameters
NameTypeRequiredDescription
idstringYesThe marker ID
colortableYes{r, g, b, a} color table
Returns
booleantrue if updated, false if marker not found.
Example
local API = g_currentMission.compassHeading
API.setMarkerColor("my_marker", {r=0, g=1, b=0, a=1})
Visual Shortcuts setMarkerLabel()
Changes the text label of an existing standalone marker.
Parameters
NameTypeRequiredDescription
idstringYesThe marker ID
textstringYesNew label text (max 8 characters displayed)
Returns
booleantrue if updated, false if marker not found.
Example
local API = g_currentMission.compassHeading
API.setMarkerLabel("fire_01", "GONE")
Visual Shortcuts setMarkerStyle()
Changes the glyph style of an existing standalone marker.
Parameters
NameTypeRequiredDescription
idstringYesThe marker ID
stylestringYesStyle name (see getMarkerStyles())
Returns
booleantrue if updated, false if marker not found.
Example
local API = g_currentMission.compassHeading
API.setMarkerStyle("my_marker", "diamond")
Visual Shortcuts setMarkerPosition()
Moves an existing standalone marker to a new world position.
Parameters
NameTypeRequiredDescription
idstringYesThe marker ID
xnumberYesNew world X coordinate
znumberYesNew world Z coordinate
Returns
booleantrue if updated, false if marker not found.
Example
local API = g_currentMission.compassHeading
-- Move a tracking marker each frame
API.setMarkerPosition("target_01", newX, newZ)
⚠ Note
Useful for markers that track moving objects. Call from your mod's update() loop.
Visual Shortcuts setMarkerFlash()
Enables or disables the flash animation on a standalone marker.
Parameters
NameTypeRequiredDescription
idstringYesThe marker ID
enabledbooleanYestrue to enable flashing, false to disable
hznumberNoFlash frequency in Hz (default: 2.0)
Returns
booleantrue if updated, false if marker not found.
Example
local API = g_currentMission.compassHeading
API.setMarkerFlash("alert_01", true, 4.0)  -- Fast flash
API.setMarkerFlash("alert_01", false)       -- Disable
Visual Shortcuts setMarkerPulse()
Enables or disables the pulse (smooth alpha fade) animation on a standalone marker.
Parameters
NameTypeRequiredDescription
idstringYesThe marker ID
enabledbooleanYestrue to enable pulsing, false to disable
hznumberNoPulse frequency in Hz (default: 1.0)
minnumberNoMinimum alpha (0.0–1.0, default: 0.3)
maxnumberNoMaximum alpha (0.0–1.0, default: 1.0)
Returns
booleantrue if updated, false if marker not found.
Example
local API = g_currentMission.compassHeading
API.setMarkerPulse("beacon_01", true, 0.5, 0.5, 1.0)
Visual Shortcuts setMarkerUrgent()
Enables or disables urgent mode on a standalone marker. Urgent markers flash AND get a red color shift to draw attention.
Parameters
NameTypeRequiredDescription
idstringYesThe marker ID
enabledbooleanYestrue for urgent mode, false for normal
Returns
booleantrue if updated, false if marker not found.
Example
local API = g_currentMission.compassHeading
API.setMarkerUrgent("fire_01", true)
⚠ Note
Urgent mode increases the red channel by 0.4 and dims green/blue channels to 40%, creating a strong red shift. Combined with flashing, this is the most attention-grabbing marker state.
Discovery
Discovery getMarkerStyles()
Returns a list of all available marker glyph style names.
Returns
table — Array of style name strings.
Example
local API = g_currentMission.compassHeading
local styles = API.getMarkerStyles()
for _, name in ipairs(styles) do
    print("Style: " .. name)
end
Style NameGlyphBest Used For
tick|Default, general markers
diamond<>Important locations, alerts
triangleVDelivery points, destinations
circleOZones, areas
square#Buildings, structures
chevron>>Direction indicators
star*Points of interest
cross+Medical, aid points
dot.Subtle background markers
arrow_up^Upward indicators
arrow_dnVDownward indicators
dash-Neutral, low-priority
exclaim!Warnings, alerts
Discovery getDefaultCategories()
Returns a list of all built-in default categories with their default colors.
Returns
table — Array of {name, color} tables.
Example
local API = g_currentMission.compassHeading
local cats = API.getDefaultCategories()
for _, c in ipairs(cats) do
    print(string.format("%s: rgba(%.2f, %.2f, %.2f, %.2f)",
        c.name, c.color.r, c.color.g, c.color.b, c.color.a))
end
CategoryDefault ColorTypical Use
defaultWhiteGeneral markers
emergencyRedFires, tornados, urgent alerts
deliveryOrangeDelivery points, packages
equipmentLight blueVehicles, tools
farmGreenFarm locations, fields
activityPurpleEvents, missions
playerCyanOther players
waypointOrangeNavigation targets
shopYellowStores, dealers
medicalRed-pinkHospitals, first aid
policeBlueLaw enforcement
fireRed-orangeFire stations, fire calls
mechanicBrownRepair shops
helperGreenAI workers
customGrayUser-defined
Provider Registry
Provider Registry register()
Registers a marker provider that the compass will poll every frame for points to display. Access via g_currentMission.compassHeadingRegistry.
Parameters
NameTypeRequiredDescription
providertableYesProvider object: id (required), getPoints function (required), plus optional enabled, label, color, style, scale, priority, category, maxDistance, minDistance
Returns
None
Example
local registry = g_currentMission.compassHeadingRegistry
if registry then
    registry:register({
        id = "my_delivery_mod",
        category = "delivery",
        label = "PKG",
        color = {r=1, g=0.75, b=0, a=1},
        style = "triangle",
        maxDistance = 3000,
        getPoints = function(self)
            local points = {}
            for _, delivery in ipairs(myActiveDeliveries) do
                points[#points + 1] = {
                    x = delivery.worldX,
                    z = delivery.worldZ,
                    label = delivery.name
                }
            end
            return points
        end
    })
end
⚠ Note
The getPoints() method is called every frame during draw() — keep it lightweight. Fires the providerRegistered event.
Provider Registry unregister()
Removes a registered provider by its ID. Access via g_currentMission.compassHeadingRegistry.
Parameters
NameTypeRequiredDescription
idstringYesThe provider ID to remove
Returns
None
Example
local registry = g_currentMission.compassHeadingRegistry
if registry then
    registry:unregister("my_delivery_mod")
end
⚠ Note
Fires the providerUnregistered event. Always unregister in your mod's deleteMap to prevent stale references.

// Events

Event System Overview

Compass Heading Display fires events that other mods can listen to using the on() API or the named convenience functions. Callbacks are wrapped in pcall — a failing callback will not crash the compass or affect other callbacks.

Available Events

Event NameCallback SignatureFires When
markerEnterViewfunction(markerId)A marker scrolls into the visible compass bar
markerExitViewfunction(markerId)A marker scrolls out of the visible compass bar
compassTogglefunction(enabled)setEnabled() changes the compass visibility
settingChangedfunction(key, newValue, oldValue)setSetting() changes a setting
markerAddedfunction(markerId)addMarker() or addTemporaryMarker() creates a marker
markerRemovedfunction(markerId)removeMarker() or clearMarkers() removes a marker
markerExpiredfunction(markerId)A marker's TTL expires and it is auto-removed
providerRegisteredfunction(providerId)A provider is registered via the registry
providerUnregisteredfunction(providerId)A provider is unregistered

Event Lifecycle Example

local API = g_currentMission.compassHeading
if API and API.isReady() then
    local visibleDeliveries = {}

    API.onMarkerEnterView(function(id)
        if id:find("^delivery_") then
            visibleDeliveries[id] = true
        end
    end)

    API.onMarkerExitView(function(id)
        visibleDeliveries[id] = nil
    end)

    API.onMarkerExpired(function(id)
        if id:find("^delivery_") then
            print("Delivery marker expired")
        end
    end)
end

// Integration Examples

Example 1: Simple Static Marker

Place a marker at a known world position (e.g., a shop or point of interest).

-- In your mod's loadMap():
local function addShopMarker()
    local API = g_currentMission.compassHeading
    if API == nil or not API.isReady() then return end

    API.addMarker("grain_elevator", {
        x = 850, z = -420,
        label = "Grain",
        color = {r=0.9, g=0.8, b=0.2, a=1.0},
        style = "square",
        category = "shop"
    })
end

Example 2: Delivery System with Moving Target

Track a moving delivery vehicle on the compass.

-- In your mod's update loop:
function MyDeliveryMod:update(dt)
    local API = g_currentMission.compassHeading
    if API == nil then return end

    if self.activeDelivery then
        local tx, _, tz = getWorldTranslation(self.activeDelivery.targetNode)
        if API.hasMarker("delivery_target") then
            API.setMarkerPosition("delivery_target", tx, tz)
        else
            API.addMarker("delivery_target", {
                x = tx, z = tz,
                label = "Deliver",
                color = {r=1, g=0.75, b=0, a=1},
                style = "triangle",
                category = "delivery",
                pulse = true
            })
        end
    else
        API.removeMarker("delivery_target")
    end
end

Example 3: Emergency Dispatch with Provider

A firefighter mod that shows active fire calls on the compass.

function FireMod:loadMap()
    self.activeFires = {}

    local registry = g_currentMission.compassHeadingRegistry
    if registry then
        registry:register({
            id = "firefighter_mod",
            category = "fire",
            label = "FIRE",
            color = {r=1, g=0.3, b=0.1, a=1},
            style = "exclaim",
            maxDistance = 10000,
            getPoints = function(provider)
                local points = {}
                for _, fire in ipairs(self.activeFires) do
                    points[#points + 1] = {
                        x = fire.x, z = fire.z,
                        label = fire.name,
                        urgent = fire.intensity > 0.8
                    }
                end
                return points
            end
        })
    end
end

function FireMod:deleteMap()
    local registry = g_currentMission.compassHeadingRegistry
    if registry then registry:unregister("firefighter_mod") end
end

Example 4: Navigation Helper Using Bearing Data

function NavMod:checkNavigation()
    local API = g_currentMission.compassHeading
    if API == nil or not API.isReady() then return end

    local targetX, targetZ = self.nextWaypoint.x, self.nextWaypoint.z
    local bearing = API.getBearingTo(targetX, targetZ)
    local heading = API.getBearing()
    local distance = API.getDistanceTo(targetX, targetZ)
    local direction = API.getCardinalDirection(bearing)

    if bearing and heading then
        local diff = ((bearing - heading + 180) % 360) - 180
        local turnDir = "straight"
        if diff > 10 then turnDir = "right"
        elseif diff < -10 then turnDir = "left" end

        print(string.format("Turn %s toward %s (%s away)",
            turnDir, direction, API.formatDistance(distance)))
    end
end

Example 5: Conditional Marker by Distance

local API = g_currentMission.compassHeading
if API and API.isReady() then
    API.addMarker("hidden_cache", {
        x = 300, z = -150,
        label = "Cache",
        style = "star",
        category = "activity",
        maxDistance = 500,
        fadeIn = true,
        fadeInDur = 1.0
    })
end

Example 6: Zero-Dependency Safety Pattern

The recommended pattern for mods that optionally integrate with Compass Heading Display:

-- Safe wrapper — works whether or not CompassHeading is installed
local function getCompassAPI()
    if g_currentMission and g_currentMission.compassHeading then
        local API = g_currentMission.compassHeading
        if API.isReady() then return API end
    end
    return nil
end

-- Usage anywhere in your mod:
local API = getCompassAPI()
if API then
    API.addMarker("my_marker", {x=100, z=-200, label="Hi"})
end
-- If CompassHeading isn't installed, this silently does nothing

// FAQ

General Questions

Does this mod work in multiplayer?

Yes. Each client renders its own compass independently. Player markers show other connected players with their names and farm colors. No custom network events are used — all data is read from the game's existing player and vehicle systems.

Does it work on dedicated servers?

Yes. On the server itself (headless, no GPU), the mod loads without errors and has zero overhead. The compass renders only on connected clients.

Does it conflict with other mods?

No known conflicts. Compass Heading Display does not modify any vanilla systems, intercept input, or alter game data. It only reads existing state and renders its own overlay.

Can I use this with other HUD mods?

Yes. The compass renders in its own screen region (top of screen by default). If another mod uses the same area, move the compass with setBarPosition() or by editing settings.xml.

How do I hide the compass temporarily?

Call API.setEnabled(false) from your mod, or set enabled to false in settings.xml. The compass also auto-hides when any GUI screen (map, menu, shop, etc.) is open.

Technical Questions

Why do player markers sometimes jitter after a teleport?

FS25 takes a few frames to sync a player's position after a teleport. Compass Heading Display detects position jumps larger than 100m and suppresses the marker for 0.5 seconds while the position settles. This is handled automatically.

The compass shows the wrong heading in a vehicle — what happened?

This was fixed in v1.0.0.5. The issue was that FS25 hides the player model underground at (0, -200, 0) when entering a vehicle, and g_currentMission.controlledVehicle can be nil for some vehicle types. The fix uses g_localPlayer:getCurrentVehicle() and camera position as fallbacks.

Can I change the compass position and size?

API.setBarPosition(0.5, 0.05)  -- Move to bottom center
API.setBarWidth(0.5)           -- Make narrower
API.setFOV(120)                -- Zoom in

How do I make a marker that follows a moving vehicle?

Use setMarkerPosition() in your mod's update() loop to move the marker every frame. See Integration Example 2.

What happens if I add a marker with an ID that already exists?

The existing marker is silently overwritten with the new configuration.

How do providers differ from standalone markers?

Standalone markers (addMarker) are individual markers you add/remove by ID. Providers (registry:register) are objects that return an array of active points every frame — ideal for mods with dynamic, changing sets of points. Providers are polled automatically.

Performance Questions

Does the compass affect FPS?

No measurable impact. The compass uses simple renderText calls and a single overlay. All marker calculations are lightweight arithmetic with no allocations.

Is the provider getPoints() called every frame?

Yes, during the draw phase. Keep the function lightweight — return a pre-computed table rather than iterating large datasets. For most mods (10-50 points), this has zero impact.

What's the maximum number of markers?

No hard limit. Practically, even 100+ markers render without issue since most are off-screen and culled by the FOV check.

// Changelog

VersionDateChanges
1.0.0.52026-03-19Fixed player/helper markers showing at wrong compass positions when inside a vehicle. Root cause: FS25 hides the player rootNode underground at (0,-200,0) when entering a vehicle. Fix: added getCurrentVehicle() fallback and prioritized camera position over player rootNode. Removed debug logging from v1.0.0.3/v1.0.0.4.
1.0.0.42026-03-18Position retrieval fix for vehicle mode (debug build).
1.0.0.32026-03-18Added debug logging to diagnose vehicle marker position bug.
1.0.0.22026-03-18Changed heading source to always use camera direction.
1.0.0.12026-03-12Fixed compass tracking camera direction instead of vehicle heading. Fixed multiplayer compass showing wrong directions after player teleport (0.5s settling delay for >100m jumps). Silenced overlay warning on dedicated servers.
1.0.0.02026-03-10Initial release. Full 16-point compass rose, player/helper/waypoint/tornado markers, 60-function public API, provider registry, event system, animation engine, settings persistence.

Breaking API Changes

None. The API has been stable since v1.0.0.0. All changes have been internal fixes. No function signatures, return types, or behaviors have changed.

// Credits & License

Author

RocklandUSA Gaming

License Terms for API Usage

  • Free to use: Any mod may call the Compass Heading Display API at no cost
  • No dependency required: Your mod should work gracefully whether or not Compass Heading Display is installed
  • No permission needed: You do not need to contact RocklandUSA Gaming to use the API
  • Attribution appreciated: While not required, a mention in your mod's description is appreciated

How to Credit (Optional)

If you use the Compass Heading Display API in your mod, you may include this in your mod description:

Compass integration powered by Compass Heading Display by RocklandUSA Gaming.

Redistribution

  • Do not redistribute FS25_CompassHeading.zip inside your own mod zip
  • Do not copy the CompassHeading.lua source code into your mod
  • Do not modify and redistribute the mod without permission
  • Do direct users to download Compass Heading Display from ModHub

This documentation is maintained by RocklandUSA Gaming. For corrections or API questions, join the Discord or email rocklandusa@gmail.com.