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. ![MapEnhancer gear menu showing follow submenu and settings panel](img/map/map_enhancer_follow_mode.png) +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 - all features available
Community build
Older official build - community features grayed out
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);