Consolidates two standalone Railroader mods into one UMM "everything mod" with an optional-module framework. Modules are disabled by default and toggled per-module from the S3 settings page. Core framework: - IModule contract plus ModuleRegistry, with each module owning a Harmony instance scoped by its id so only enabled modules patch the game - Per-module flat JSON settings (SettingsStore). On this Mono runtime JsonUtility silently drops nested custom-class fields, so settings stay flat - Foldout-per-module settings panel plus a detector that offers to disable the old standalone mods if they are still installed Modules (moved over to parity, verified in-game): - Physics Optimizer (was RailroaderPhysicsOverhaul): LOD fast-path and auto-freeze, profiler overlay, debug car tinting, /rpf console commands - Map Popout (was RRPopout): native map detach window. Pure-UMM install that drops the winhttp proxy and LoadLibrary's RRPopout.dll from the mod folder. Native Win32 + D3D11 + Dear ImGui engine included. Fixes a latent break where the now-private MapBuilder.UpdateForZoom() is reached via Traverse. Build: dotnet for the managed assembly (netstandard2.1) and CMake for the native DLL. build-local.ps1 installs into the game, build-release.ps1 packages the UMM drag-install zip.
54 lines
2.1 KiB
C#
54 lines
2.1 KiB
C#
using UI;
|
|
using UI.Builder;
|
|
using UI.Map;
|
|
using UnityEngine;
|
|
using UnityEngine.UI;
|
|
|
|
namespace S3.Modules.Popout {
|
|
|
|
// Adds a "Pop Out / Re-attach" button to the in-game map window title bar
|
|
// using the game's own UIPanel / UIPanelBuilder system so it matches the
|
|
// factory UI styling exactly.
|
|
internal static class MapWindowButton {
|
|
|
|
private static Button? s_button;
|
|
|
|
internal static void TryInstall() {
|
|
if (s_button != null) return;
|
|
|
|
var win = PanelFinder.GetMapWindowUI();
|
|
// Don't install until the map has been opened at least once.
|
|
if (win == null || !win.IsShown) return;
|
|
|
|
// Already added — recover reference after a hot-reload
|
|
var existing = win.transform.Find("RRPopout_Btn");
|
|
if (existing != null) {
|
|
s_button = existing.GetComponentInChildren<Button>();
|
|
return;
|
|
}
|
|
|
|
var pwc = UnityEngine.Object.FindObjectOfType<ProgrammaticWindowCreator>();
|
|
if (pwc == null) return;
|
|
|
|
// Container rect in the title bar — same y-level as the close button.
|
|
var container = new GameObject("RRPopout_Btn").AddComponent<RectTransform>();
|
|
container.SetParent(win.transform, false);
|
|
container.SetInsetAndSizeFromParentEdge(RectTransform.Edge.Top, 3f, 22f);
|
|
container.SetInsetAndSizeFromParentEdge(RectTransform.Edge.Right, 40f, 70f);
|
|
|
|
// Let the game's UIPanel system create a native-styled button.
|
|
UIPanel.Create(container, pwc.builderAssets, builder => {
|
|
var el = builder.AddButtonCompact("Pop Out", PopoutModule.Toggle);
|
|
s_button = el?.RectTransform?.GetComponentInChildren<Button>();
|
|
});
|
|
}
|
|
|
|
internal static void UpdateLabel(bool isPopped) {
|
|
if (s_button == null) return;
|
|
// UIPanelBuilder puts the label in a TMP_Text child of the button.
|
|
var label = s_button.GetComponentInChildren<TMPro.TMP_Text>();
|
|
if (label != null)
|
|
label.text = isPopped ? "Re-attach" : "Pop Out";
|
|
}
|
|
}
|
|
}
|