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
Audience
Benefit
Players
Always-visible compass heading, player tracking, helper alerts
Server Admins
Works on dedicated servers, farm-based marker filtering
Mod Developers
60-function public API for custom marker integration
Compatibility
Environment
Supported
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
Section 2
// Features
Complete Feature List
Feature
Description
16-Point Compass Rose
Full N/NNE/NE/ENE/E/ESE/SE/SSE/S/SSW/SW/WSW/W/WNW/NW/NNW labels with smooth scrolling
Numeric Heading
Live 000°–359° heading displayed below the compass bar
Tick Marks
Subtle tick marks every 10° for precise heading reference
Gold Center Indicator
Fixed gold tick at screen center showing your exact look direction
Waypoint Tracking
Detects the FS25 navigation target and displays an orange marker with distance
Player Markers
Shows other players on the compass with their name and distance, colored by farm
Farm Filtering
Filter player/helper markers to show only your farm or all farms
Helper Markers
AI worker markers (H1, H2, etc.) with farm coloring
Blocked Helper Alerts
Stuck helpers flash red with an exclamation mark and elevated priority
Tornado Tracking
Scans the scene for tornado objects and shows a flashing red diamond with distance
Teleport Detection
Suppresses marker jitter for 0.5 seconds after a player teleports
Auto-Hide
Automatically hides when GUI screens, menus, or the ESC screen are open
Camera-Aware Heading
Works with free-look, 3rd person orbit, and interior camera
Farm Coloring
Player and helper markers use the farm's assigned color (brightened for visibility)
Marker Clamping
Off-screen markers clamp to bar edges with < / > indicators
Distance Display
Markers show distance in meters (< 1km) or kilometers (≥ 1km)
Settings Persistence
All settings saved to XML and restored on next load
Full Public API
60 functions for third-party mod integration
Provider Registry
Mods register as marker providers — zero-dependency integration
Standalone Markers
Add/remove/update individual markers by ID with animations
Marker Animations
Flash, pulse, fade-in, and urgent effects on any marker
Category System
Group markers into categories with per-category enable/disable and coloring
Event System
Subscribe to marker enter/exit view, add/remove, compass toggle, and settings change events
Distance Filtering
Markers can define min/max display distance
TTL Markers
Auto-expiring markers with configurable time-to-live
Configurable Layout
Bar position, width, vertical position, field of view, and opacity all adjustable
Configurable Colors
Every 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.
Section 3
// Requirements
Minimum Requirements
Requirement
Detail
Game
Farming Simulator 25, version 1.16 or newer
Platform
PC (Windows or Linux dedicated server)
Dependencies
None — 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.
Enable "Compass Heading Display" in the mod manager
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
Upload FS25_CompassHeading.zip to the server's mods directory:
/profile/mods/FS25_CompassHeading.zip
Add the mod to the server's active mod list
Restart the server
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
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
Setting
Type
Default
Description
enabled
boolean
true
Master enable/disable for the entire compass
barPositionX
float
0.500
Horizontal center position (0.0 = left, 1.0 = right)
barWidth
float
0.680
Width of the compass bar (fraction of screen width)
barVerticalY
float
0.974
Vertical center position (0.0 = bottom, 1.0 = top)
fieldOfView
float
160
Degrees of compass visible on the bar (30–360)
backgroundOpacity
float
0.62
Background bar opacity (0.0 = invisible, 1.0 = solid)
Compass Element Visibility
Setting
Type
Default
Description
showCardinalLabels
boolean
true
Show N/S/E/W labels
showIntercardinalLabels
boolean
true
Show NE/SE/SW/NW labels
showMinorLabels
boolean
true
Show NNE/ENE/ESE/etc. labels
showTickMarks
boolean
true
Show 10° tick marks
showCenterIndicator
boolean
true
Show gold center tick
showDegreeReadout
boolean
true
Show numeric heading below bar
Marker Visibility
Setting
Type
Default
Description
showPlayerMarkers
boolean
true
Show other player markers
playerMarkerFilter
string
"all"
"all" = all players, "farm" = same farm only
showWaypointMarker
boolean
true
Show navigation waypoint marker
showTornadoMarker
boolean
true
Show tornado detection marker
showHelperMarkers
boolean
true
Show AI helper markers
helperMarkerFilter
string
"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.
Setting
Default RGBA
Description
colCenterIndicator
1.00, 0.85, 0.15, 1.00
Gold center tick color
colDegreeReadout
1.00, 1.00, 1.00, 0.50
Heading number color
colCardinal
1.00, 1.00, 1.00, 1.00
N/S/E/W label color
colIntercardinal
0.88, 0.88, 0.88, 1.00
NE/SE/SW/NW label color
colMinor
0.65, 0.65, 0.65, 1.00
NNE/ENE/etc. label color
colPlayerMarker
0.25, 0.88, 1.00, 1.00
Default player marker color (cyan)
colWaypointMarker
1.00, 0.55, 0.05, 1.00
Waypoint marker color (orange)
colTornadoMarker
1.00, 0.15, 0.15, 1.00
Tornado marker color (red)
colHelperMarker
0.60, 0.90, 0.40, 1.00
Helper marker color (green)
colHelperBlocked
1.00, 0.25, 0.25, 1.00
Blocked helper color (red)
Example settings.xml
true
Section 6
// 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 StatusisReady()
Checks whether the Compass Heading Display system is loaded and ready to accept API calls.
Returns
boolean — true 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.
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.
Updates one or more properties of an existing standalone marker without removing and re-adding it.
Parameters
Name
Type
Required
Description
id
string
Yes
The marker ID to update
fields
table
Yes
Table of field names and new values to apply
Returns
boolean — true 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.
Returns a copy of a standalone marker's public properties.
Parameters
Name
Type
Required
Description
id
string
Yes
The 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.
Removes all standalone markers, optionally filtered by category.
Parameters
Name
Type
Required
Description
category
string
No
If 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.
If 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")
Returns the provider object registered with the given ID.
Parameters
Name
Type
Required
Description
id
string
Yes
The 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.
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
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
Name
Type
Required
Description
category
string
Yes
The category name
enabled
boolean
Yes
true to enable, false to disable
Returns
boolean — true 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.
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))
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
Registers a callback that fires when a standalone marker's TTL expires and it is auto-removed.
Parameters
Name
Type
Required
Description
callback
function
Yes
function(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)
Finds the nearest standalone marker to the player, optionally filtered by category.
Parameters
Name
Type
Required
Description
category
string
No
If 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
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
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.
Calculates the compass bearing from the player's current position to a world coordinate.
Parameters
Name
Type
Required
Description
x
number
Yes
Target world X coordinate
z
number
Yes
Target 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
Calculates the straight-line distance from the player to a world coordinate.
Parameters
Name
Type
Required
Description
x
number
Yes
Target world X coordinate
z
number
Yes
Target 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
Enables or disables urgent mode on a standalone marker. Urgent markers flash AND get a red color shift to draw attention.
Parameters
Name
Type
Required
Description
id
string
Yes
The marker ID
enabled
boolean
Yes
true for urgent mode, false for normal
Returns
boolean — true 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.
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
Registers a marker provider that the compass will poll every frame for points to display. Access via g_currentMission.compassHeadingRegistry.
Parameters
Name
Type
Required
Description
provider
table
Yes
Provider 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.
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 Name
Callback Signature
Fires When
markerEnterView
function(markerId)
A marker scrolls into the visible compass bar
markerExitView
function(markerId)
A marker scrolls out of the visible compass bar
compassToggle
function(enabled)
setEnabled() changes the compass visibility
settingChanged
function(key, newValue, oldValue)
setSetting() changes a setting
markerAdded
function(markerId)
addMarker() or addTemporaryMarker() creates a marker
markerRemoved
function(markerId)
removeMarker() or clearMarkers() removes a marker
markerExpired
function(markerId)
A marker's TTL expires and it is auto-removed
providerRegistered
function(providerId)
A provider is registered via the registry
providerUnregistered
function(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
Section 8
// 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
Section 9
// 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.
Section 10
// Changelog
Version
Date
Changes
1.0.0.5
2026-03-19
Fixed 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.4
2026-03-18
Position retrieval fix for vehicle mode (debug build).
1.0.0.3
2026-03-18
Added debug logging to diagnose vehicle marker position bug.
1.0.0.2
2026-03-18
Changed heading source to always use camera direction.
1.0.0.1
2026-03-12
Fixed 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.0
2026-03-10
Initial 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.