From 0a9da7bc9c81863eba9a31d3de1e430cd261cfb1 Mon Sep 17 00:00:00 2001 From: MrMaho Date: Sat, 3 Oct 2026 17:41:08 +0200 Subject: [PATCH] oilcheck console command: show every oil leak source (2.10.6) Engine leak values, oilpan bolts incl. drain plug, oil filter item (items.txt). Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.md | 9 ++++ Commands.cs | 4 ++ MSCO.cs | 2 +- MSCO.csproj | 1 + OilCheck.cs | 117 +++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 132 insertions(+), 1 deletion(-) create mode 100644 OilCheck.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index 57ce7db..d5c6d10 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## 2.10.6 + +- Neuer Konsolenbefehl **oilcheck**: zeigt, woher Öl ausläuft – die Leck-Werte des Motors (Ölwanne, Ölfilter, + Zylinderkopfdichtung, Ventildeckel), die Schrauben der Ölwanne samt Ablassschraube und den Ölfilter (eingebaut, + Festigkeit). Wichtig: der Ölfilter ist ein gekauftes Teil und steht in **items.txt** – fehlt diese Datei im + Spielstand eines Servers, fehlt der Filter am Auto und Öl läuft aus +- Neue Beispiel-Mod **Show Bolt Size** (examples/ShowBoltSize); Mods lassen sich jetzt über den Launcher + veröffentlichen (siehe MODDING.md) + ## 2.10.5 - **Teleport zu Orten** (Admin, Reiter TELEPORT) landet wieder am Ort: vorher zählten alle weit verstreuten Teile diff --git a/Commands.cs b/Commands.cs index 0cd5f24..70ebaf7 100644 --- a/Commands.cs +++ b/Commands.cs @@ -27,6 +27,7 @@ namespace MSCO "vehicles | rig - debug info about vehicles / player models\n" + "model [game|classic] - other players as MSC characters or the MSCO figure\n" + "vars - synced vehicle values, e.g. vars oil (debug)\n" + + "oilcheck - where does oil leak out? (all leak sources of the engine)\n" + "debug - toggle sync debug output\n" + "clear - clear this console"; @@ -179,6 +180,9 @@ namespace MSCO // Diagnose: welche Fahrzeugwerte synchronisiert werden (z.B. "vars oil") foreach (string line in mp.DescribeVehicleValues(args.Length > 1 ? args[1] : "")) print(line); break; + case "oilcheck": + foreach (string line in OilCheck.Report()) print(line); + break; case "debug": FsmSync.Debug = !FsmSync.Debug; print("Sync debug output " + (FsmSync.Debug ? "ON" : "OFF")); diff --git a/MSCO.cs b/MSCO.cs index 427439e..c5232d3 100644 --- a/MSCO.cs +++ b/MSCO.cs @@ -11,7 +11,7 @@ namespace MSCO /// Version der Mod. Kleine Updates: nur die dritte Stelle erhöhen (2.9.1, 2.9.2 ...) - sie bleiben mit /// Servern und Mitspielern derselben ersten zwei Stellen kompatibel. /// - public const string ModVersion = "2.10.5"; + public const string ModVersion = "2.10.6"; /// /// Netzwerk-Version (die ersten beiden Stellen). Nur erhöhen, wenn sich das Protokoll inkompatibel ändert - dann /// auch GameServer.ModVersion im Server. diff --git a/MSCO.csproj b/MSCO.csproj index 32c57b3..4fe408c 100644 --- a/MSCO.csproj +++ b/MSCO.csproj @@ -98,6 +98,7 @@ + diff --git a/OilCheck.cs b/OilCheck.cs new file mode 100644 index 0000000..d6e82e6 --- /dev/null +++ b/OilCheck.cs @@ -0,0 +1,117 @@ +using System.Collections.Generic; +using System.Globalization; +using HutongGames.PlayMaker; +using UnityEngine; + +namespace MSCO +{ + /// + /// Konsole "oilcheck": woher läuft Öl aus? Das Spiel kennt fünf Quellen - die Motor-FSM rechnet LeakOilpan, + /// LeakOilfilter, LeakHeadgasket und LeakRockerCover aus den Teilen aus, dazu die Ablassschraube der Ölwanne + /// (Stufe 0-5 = offen, Objekt "Oil" aktiv). Der Ölfilter ist ein gekauftes Teil und steht in items.txt + /// (Installed/Tightness) - fehlt die Datei im Spielstand eines Servers, fehlt auch der Filter. + /// + public static class OilCheck + { + static readonly string[] PartWords = { "oilpan", "oilfilter", "oil filter", "headgasket", "rocker", "valvecover", "drain" }; + + public static List Report() + { + var lines = new List(); + if (!MenuHelper.InGame) { lines.Add("Only in the game."); return lines; } + + var fsms = Object.FindObjectsOfType(); + // 1. Leck-Werte der Motor-FSM + bool leakFsm = false; + foreach (PlayMakerFSM f in fsms) + { + if (f == null || f.FsmVariables.FindFsmFloat("LeakOilfilter") == null) continue; + leakFsm = true; + var parts = new List(); + foreach (FsmFloat v in f.FsmVariables.FloatVariables) + if (v != null && v.Name.StartsWith("Leak")) parts.Add(v.Name + "=" + Num(v.Value)); + lines.Add("Engine leaks (" + f.gameObject.name + "/" + f.FsmName + ", state " + f.ActiveStateName + "): " + string.Join(" ", parts.ToArray())); + } + if (!leakFsm) lines.Add("Engine leak values: not found (engine not assembled?)"); + + // 2. Teile in der Database (eingebaut, festgeschraubt, kaputt, Ölstand) + foreach (PlayMakerFSM f in Resources.FindObjectsOfTypeAll()) + { + if (f == null || f.FsmName != "Data" || f.gameObject.hideFlags != HideFlags.None) continue; + if (f.transform.root.name != "Database" || !Matches(f.gameObject.name)) continue; + lines.Add(" " + f.gameObject.name + ": " + Values(f.FsmVariables, "Installed", "Bolted", "Damaged", "Tightness", "Oil", "Wear")); + } + + // 3. Schrauben der Ölwanne (die Ablassschraube schaltet das Leck "Oil") + foreach (PlayMakerFSM f in fsms) + { + if (f == null || f.FsmName != "Screw") continue; + string path = FsmSync.PathOf(f.transform); + if (path.ToLower().IndexOf("oilpan", System.StringComparison.Ordinal) < 0) continue; + FsmInt stage = f.FsmVariables.FindFsmInt("Stage"); + lines.Add(" bolt " + Short(path) + ": stage " + (stage != null ? stage.Value.ToString() : "?") + (Switches(f) ? " (drain plug - 0-5 = open)" : "")); + } + foreach (GameObject go in Object.FindObjectsOfType()) + if (go.name == "Oil" && go.transform.parent != null && FsmSync.PathOf(go.transform).ToLower().Contains("oilpan")) + lines.Add(" drain leak object " + Short(FsmSync.PathOf(go.transform)) + " is ACTIVE (oil runs out of the drain plug)"); + + // 4. Ölfilter als Gegenstand (items.txt) + int filters = 0; + foreach (PlayMakerFSM f in fsms) + { + if (f == null || !f.gameObject.name.StartsWith("oil filter")) continue; + if (f.FsmVariables.FindFsmBool("Installed") == null && f.FsmVariables.FindFsmFloat("Tightness") == null) continue; + filters++; + lines.Add(" item " + f.gameObject.name + " (" + f.FsmName + ", under " + f.transform.root.name + "): " + + Values(f.FsmVariables, "Installed", "Tightness", "Consumed", "Dirt")); + } + if (filters == 0) lines.Add(" no oil filter item in the world - if the server's save has no items.txt, bought parts are missing"); + return lines; + } + + static bool Matches(string name) + { + string n = name.ToLower(); + foreach (string w in PartWords) if (n.Contains(w)) return true; + return false; + } + + static bool Switches(PlayMakerFSM f) + { + try + { + foreach (FsmState s in f.FsmStates) + { + if (!s.ActionsLoaded) s.LoadActions(); + if (s.Actions == null) continue; + foreach (FsmStateAction a in s.Actions) if (a != null && a.GetType().Name == "ActivateGameObject") return true; + } + } + catch { } + return false; + } + + static string Values(FsmVariables vars, params string[] names) + { + var parts = new List(); + foreach (string n in names) + { + FsmBool b = vars.FindFsmBool(n); + if (b != null) { parts.Add(n + "=" + b.Value); continue; } + FsmFloat f = vars.FindFsmFloat(n); + if (f != null) { parts.Add(n + "=" + Num(f.Value)); continue; } + FsmInt i = vars.FindFsmInt(n); + if (i != null) parts.Add(n + "=" + i.Value); + } + return parts.Count == 0 ? "-" : string.Join(" ", parts.ToArray()); + } + + static string Num(float v) { return v.ToString("0.###", CultureInfo.InvariantCulture); } + + static string Short(string path) + { + string[] s = path.Split('|'); + return s.Length <= 3 ? path : "…|" + string.Join("|", s, s.Length - 3, 3); + } + } +}