diff --git a/Info.json b/Info.json
index c0369be..8acd767 100644
--- a/Info.json
+++ b/Info.json
@@ -2,7 +2,7 @@
"Id": "S3",
"DisplayName": "S³ - Seton's Special Sauce",
"Author": "seton",
- "Version": "0.2.3",
+ "Version": "0.2.4",
"ManagerVersion": "0.27.0",
"GameVersionPoint": "0",
"AssemblyName": "S3.dll",
diff --git a/README.md b/README.md
index 31c2948..78c46c6 100644
--- a/README.md
+++ b/README.md
@@ -74,10 +74,19 @@ Pop the map into a detached native OS window. Drag it to any monitor, resize it
### MapEnhancer Integration
-If [MapEnhancer](https://github.com/Refizar08/rr-mapenhancer-fix) is installed, the toolbar gear menu expands with follow controls and a settings panel. The **Follow** submenu lets you follow the player camera, the selected locomotive, or any consist picked from a live-updated list. **Jump to Location** is also available. The **Settings** panel gives you live control over all MapEnhancer rendering options without leaving the map: marker visibility toggles, scale sliders for flares, junctions, track lines, and crossings, and bulk switch reset buttons.
+S³ auto-detects any installed version of MapEnhancer at startup with no configuration required. If MapEnhancer is installed, the toolbar gear menu expands with follow controls and a settings panel. The **Follow** submenu lets you follow the player camera, the selected locomotive, or any consist picked from a live-updated list. **Jump to Location** is also available. The **Settings** panel gives you live control over all MapEnhancer rendering options without leaving the map: marker visibility toggles, scale sliders for flares, junctions, track lines, and crossings, and bulk switch reset buttons.

+Both the original official builds and the [community fork](https://github.com/Refizar08/rr-mapenhancer-fix) are supported. Features added by the community fork - turntable control, road crossing markers, passenger stop tracking, industry area colors, modded spawn points, and bulk switch reset - are grayed out when an older build is detected. All controls that the installed version supports remain fully functional.
+
+
+
+ Community build |
+ Older build - community features grayed |
+
+
+
---
## Physics Optimizer
diff --git a/img/map/map_enhancer_full.png b/img/map/map_enhancer_full.png
new file mode 100644
index 0000000..044b4ee
Binary files /dev/null and b/img/map/map_enhancer_full.png differ
diff --git a/img/map/map_enhancer_grayedout.png b/img/map/map_enhancer_grayedout.png
new file mode 100644
index 0000000..c5790dc
Binary files /dev/null and b/img/map/map_enhancer_grayedout.png differ
diff --git a/native/src/d3d11_renderer.cpp b/native/src/d3d11_renderer.cpp
index f972673..e7e625f 100644
--- a/native/src/d3d11_renderer.cpp
+++ b/native/src/d3d11_renderer.cpp
@@ -667,7 +667,9 @@ static void BuildMapUI(PopoutWindow* win, ImVec2 origin, float w, float h,
win->inputQueue.push(ev);
};
- bool meOn = win->imMapEnhancerInstalled.load();
+ bool meOn = win->imMapEnhancerInstalled.load();
+ bool meTurntable = win->imMEHasTurntable.load(); // v1.6.0 or community
+ bool meCom = win->imMECommunity.load(); // community only
if (meOn) {
if (ImGui::BeginMenu("Map Enhancer")) {
if (ImGui::MenuItem("Toggle follow mode"))
@@ -709,11 +711,16 @@ static void BuildMapUI(PopoutWindow* win, ImVec2 origin, float w, float h,
auto meBit = [&](int b) { return (meFlags & (1u << b)) != 0; };
// -- Markers --
+ // Turntable Markers: available on v1.6.0 and community (ShowTurntableMarkers field).
+ // Everything else in this block is community-only.
ImGui::TextDisabled("Markers");
+ ImGui::BeginDisabled(!meTurntable);
{
bool v = meBit(MB_TurntableMarkers);
if (ImGui::MenuItem("Turntable Markers", nullptr, v)) pushMEBool(MB_TurntableMarkers, !v);
}
+ ImGui::EndDisabled();
+ ImGui::BeginDisabled(!meCom);
{
bool v = meBit(MB_TurntableControl);
if (ImGui::MenuItem("Turntable Control", nullptr, v)) pushMEBool(MB_TurntableControl, !v);
@@ -734,15 +741,18 @@ static void BuildMapUI(PopoutWindow* win, ImVec2 origin, float w, float h,
bool v = meBit(MB_IndustryAreaColors);
if (ImGui::MenuItem("Industry Area Colors", nullptr, v)) pushMEBool(MB_IndustryAreaColors, !v);
}
+ ImGui::EndDisabled();
ImGui::Separator();
// -- Behavior --
ImGui::TextDisabled("Behavior");
+ ImGui::BeginDisabled(!meCom);
{
bool v = meBit(MB_ModdedSpawnPoints);
if (ImGui::MenuItem("Modded Spawn Points", nullptr, v)) pushMEBool(MB_ModdedSpawnPoints, !v);
}
+ ImGui::EndDisabled();
{
bool v = meBit(MB_DoubleClick);
if (ImGui::MenuItem("Require Double Click", nullptr, v)) pushMEBool(MB_DoubleClick, !v);
@@ -776,6 +786,7 @@ static void BuildMapUI(PopoutWindow* win, ImVec2 origin, float w, float h,
if (ImGui::IsItemDeactivatedAfterEdit())
pushMEFloat(MF_TrackThickness, win->imMETrackThick.load());
}
+ ImGui::BeginDisabled(!meCom);
{
float v = win->imMECrossingSc.load();
ImGui::SetNextItemWidth(110.f);
@@ -784,15 +795,18 @@ static void BuildMapUI(PopoutWindow* win, ImVec2 origin, float w, float h,
if (ImGui::IsItemDeactivatedAfterEdit())
pushMEFloat(MF_CrossingScale, win->imMECrossingSc.load());
}
+ ImGui::EndDisabled();
ImGui::Separator();
- // -- Switch Reset --
+ // -- Switch Reset (community build only) --
+ ImGui::BeginDisabled(!meCom);
ImGui::TextDisabled("Switch Reset");
if (ImGui::MenuItem("All Switches - Normal"))
pushCmd(UICmd::MEResetSwitchesNormal);
if (ImGui::MenuItem("All Switches - Thrown"))
pushCmd(UICmd::MEResetSwitchesThrown);
+ ImGui::EndDisabled();
ImGui::EndMenu();
}
diff --git a/native/src/exports.cpp b/native/src/exports.cpp
index 8c2e5dc..b37738f 100644
--- a/native/src/exports.cpp
+++ b/native/src/exports.cpp
@@ -209,6 +209,17 @@ void RRPOPOUT_SetMapEnhancerInstalled(int windowHandle, bool installed) {
if (win) win->imMapEnhancerInstalled.store(installed);
}
+// Set ME capability flags.
+// hasTurntable — ShowTurntableMarkers field exists (v1.6.0 or community); gates Turntable Markers toggle.
+// community — full community build (crossing/spawn/switch-reset); gates all other community items.
+extern "C" __declspec(dllexport)
+void RRPOPOUT_SetMapEnhancerCaps(int windowHandle, bool hasTurntable, bool community) {
+ PopoutWindow* win = GetPopoutWindow(windowHandle);
+ if (!win) return;
+ win->imMEHasTurntable.store(hasTurntable);
+ win->imMECommunity.store(community);
+}
+
// Tell native whether panning the map should cancel follow mode (for the gear menu checkmark).
extern "C" __declspec(dllexport)
void RRPOPOUT_SetPanDisablesFollow(int windowHandle, bool active) {
diff --git a/native/src/popout_window.h b/native/src/popout_window.h
index 1871af0..36c63b0 100644
--- a/native/src/popout_window.h
+++ b/native/src/popout_window.h
@@ -75,6 +75,8 @@ struct PopoutWindow {
std::atomic imMapZoom {500.f};
std::atomic imMapSyncPlayer {false};
std::atomic imMapEnhancerInstalled {false};
+ std::atomic imMEHasTurntable {false}; // true if ShowTurntableMarkers exists (v1.6.0 or community)
+ std::atomic imMECommunity {false}; // true = community build (has crossing/spawn/switch-reset)
std::atomic imFollowPlayer {false};
std::atomic imAlwaysOnTop {false};
std::atomic imPanDisablesFollow {true};
diff --git a/src/Core/Ui/UiService.cs b/src/Core/Ui/UiService.cs
index 07a62b0..25e8ecd 100644
--- a/src/Core/Ui/UiService.cs
+++ b/src/Core/Ui/UiService.cs
@@ -696,6 +696,7 @@ internal sealed class UiHost : MonoBehaviour
_locationsSent = false;
_lastStatus = "";
Native.RRPOPOUT_SetMapEnhancerInstalled(_overlayHandle, MapEnhancerBridge.IsInstalled);
+ Native.RRPOPOUT_SetMapEnhancerCaps(_overlayHandle, MapEnhancerBridge.HasTurntable, MapEnhancerBridge.IsCommunity);
Native.RRPOPOUT_SetMapRotation(_overlayHandle, 0f);
Native.RRPOPOUT_SetMapSyncPlayer(_overlayHandle, false);
Native.RRPOPOUT_SetFollowPlayer(_overlayHandle, false);
diff --git a/src/Modules/Popout/DetachedPanel.cs b/src/Modules/Popout/DetachedPanel.cs
index 4ee9905..93549db 100644
--- a/src/Modules/Popout/DetachedPanel.cs
+++ b/src/Modules/Popout/DetachedPanel.cs
@@ -71,6 +71,7 @@ namespace S3.Modules.Popout {
_mapCamEulerZ = _mapCamera.transform.eulerAngles.z;
Native.RRPOPOUT_SetMapEnhancerInstalled(_windowHandle, MapEnhancerBridge.IsInstalled);
+ Native.RRPOPOUT_SetMapEnhancerCaps(_windowHandle, MapEnhancerBridge.HasTurntable, MapEnhancerBridge.IsCommunity);
Native.RRPOPOUT_SetPanDisablesFollow(_windowHandle, PopoutModule.Settings.panDisablesFollow);
Native.RRPOPOUT_SetRightClickRecenter(_windowHandle, PopoutModule.Settings.rightClickRecenter);
Native.RRPOPOUT_SetOverlayMapBgAlpha(PopoutModule.Settings.overlayMapBgAlpha);
diff --git a/src/Modules/Popout/MapEnhancerBridge.cs b/src/Modules/Popout/MapEnhancerBridge.cs
index c5615b2..a4d8d4f 100644
--- a/src/Modules/Popout/MapEnhancerBridge.cs
+++ b/src/Modules/Popout/MapEnhancerBridge.cs
@@ -22,6 +22,7 @@ namespace S3.Modules.Popout {
internal static class MapEnhancerBridge {
private static bool? _installed;
+ private static bool? _isCommunity;
private static MonoBehaviour? _instance;
private static object? _meSettings;
@@ -53,6 +54,31 @@ namespace S3.Modules.Popout {
}
}
+ // True if ShowTurntableMarkers exists — present in v1.6.0 (lstealth) and community,
+ // but NOT in the original official v1.5.2 (mricher-git). Gates the Turntable Markers toggle.
+ public static bool HasTurntable {
+ get {
+ if (_hasTurntable.HasValue) return _hasTurntable.Value;
+ if (!IsInstalled) { _hasTurntable = false; return false; }
+ _hasTurntable = Type.GetType("MapEnhancer.TurntableHelper, MapEnhancer") != null;
+ return _hasTurntable.Value;
+ }
+ }
+ private static bool? _hasTurntable;
+
+ // True if this is the Refizar08 community build — adds crossing/spawn/switch-reset
+ // features absent from both official builds. Detected by SwitchResetAuditState,
+ // a type that only exists in that fork.
+ public static bool IsCommunity {
+ get {
+ if (_isCommunity.HasValue) return _isCommunity.Value;
+ if (!IsInstalled) { _isCommunity = false; return false; }
+ _isCommunity = Type.GetType("MapEnhancer.SwitchResetAuditState, MapEnhancer") != null;
+ S3.Core.Log.Info($"[S3] MapEnhancer: HasTurntable={HasTurntable} IsCommunity={_isCommunity.Value}");
+ return _isCommunity.Value;
+ }
+ }
+
// Current value of the private mapFollowMode field.
public static bool FollowMode {
get {
diff --git a/src/Modules/Popout/NativeInterop.cs b/src/Modules/Popout/NativeInterop.cs
index b00dd73..096593f 100644
--- a/src/Modules/Popout/NativeInterop.cs
+++ b/src/Modules/Popout/NativeInterop.cs
@@ -123,6 +123,12 @@ namespace S3.Modules.Popout {
[DllImport(Dll, CallingConvention = CallingConvention.Cdecl)]
public static extern void RRPOPOUT_SetMapEnhancerInstalled(int windowHandle, bool installed);
+ // Push ME capability flags to native.
+ // hasTurntable — ShowTurntableMarkers exists (v1.6.0 or community); gates Turntable Markers.
+ // community — full community build (crossing/spawn/switch-reset); gates community items.
+ [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)]
+ public static extern void RRPOPOUT_SetMapEnhancerCaps(int windowHandle, bool hasTurntable, bool community);
+
// Update the compass display to reflect the current map rotation (degrees).
[DllImport(Dll, CallingConvention = CallingConvention.Cdecl)]
public static extern void RRPOPOUT_SetMapRotation(int windowHandle, float degrees);