MSCO 2.10.3: bolt stages and part tightness synced, snap to pivot, admin tighten/loosen
- Every bolt (Screw FSM, stage 0-8) and every part's tightness (BoltCheck/Use "Tightness", counted by the tool) is synced as state - before only the bolt turned on other clients, the part stayed loose or tight there - Tightening a part (tool, admin, other player) snaps it exactly onto its pivot (installed parts sit at local 0), except screw-in parts and hinged doors/hoods - Admin VEHICLE tab: TIGHTEN/LOOSEN per part, TIGHTEN ALL/LOOSEN ALL, bolted/loose state - fsmdump "pivots": installed part offsets Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+114
-4
@@ -22,8 +22,14 @@ namespace MSCO
|
||||
public sealed class Part
|
||||
{
|
||||
public string Name, Category;
|
||||
public FsmBool Damaged, Installed;
|
||||
public FsmBool Damaged, Installed, Bolted;
|
||||
public FsmFloat Wear;
|
||||
// Schrauben des eingebauten Teils (BoltCheck zählt die Festigkeit, die Schrauben haben je eine Stufe 0-8)
|
||||
public PlayMakerFSM BoltCheck;
|
||||
public FsmFloat Tightness;
|
||||
public float MaxTightness;
|
||||
public readonly List<PlayMakerFSM> Bolts = new List<PlayMakerFSM>();
|
||||
public bool HasBolts { get { return Bolts.Count > 0 || Tightness != null; } }
|
||||
}
|
||||
|
||||
static readonly List<Fluid> fluids = new List<Fluid>();
|
||||
@@ -53,6 +59,7 @@ namespace MSCO
|
||||
parts.Clear();
|
||||
if (!MenuHelper.InGame) return;
|
||||
var byName = new Dictionary<string, PlayMakerFSM>();
|
||||
var partsByDb = new Dictionary<GameObject, Part>();
|
||||
foreach (PlayMakerFSM fsm in Resources.FindObjectsOfTypeAll<PlayMakerFSM>())
|
||||
{
|
||||
if (fsm == null || fsm.FsmName != "Data" || fsm.gameObject.hideFlags != HideFlags.None) continue;
|
||||
@@ -65,14 +72,17 @@ namespace MSCO
|
||||
FsmBool installed = fsm.FsmVariables.FindFsmBool("Installed");
|
||||
FsmBool damaged = fsm.FsmVariables.FindFsmBool("Damaged");
|
||||
FsmFloat wear = fsm.FsmVariables.FindFsmFloat("Wear");
|
||||
if (damaged == null && wear == null) continue;
|
||||
parts.Add(new Part
|
||||
var part = new Part
|
||||
{
|
||||
Name = Pretty(t.name),
|
||||
Category = category.Replace("Database", ""),
|
||||
Damaged = damaged, Installed = installed, Wear = wear,
|
||||
});
|
||||
Bolted = fsm.FsmVariables.FindFsmBool("Bolted"),
|
||||
};
|
||||
partsByDb[fsm.gameObject] = part;
|
||||
if (damaged != null || wear != null || part.Bolted != null) parts.Add(part);
|
||||
}
|
||||
FindBolts(partsByDb);
|
||||
parts.Sort((a, b) => string.Compare(a.Category + a.Name, b.Category + b.Name, StringComparison.Ordinal));
|
||||
|
||||
foreach (string[] def in FluidDefs)
|
||||
@@ -88,6 +98,106 @@ namespace MSCO
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Zu jedem Database-Teil das eingebaute Teil: dessen BoltCheck zeigt mit "db_ThisPart" auf den Database-Eintrag,
|
||||
/// die Schrauben (FSM "Screw") liegen darunter.
|
||||
/// </summary>
|
||||
static void FindBolts(Dictionary<GameObject, Part> partsByDb)
|
||||
{
|
||||
foreach (PlayMakerFSM fsm in UnityEngine.Object.FindObjectsOfType<PlayMakerFSM>())
|
||||
{
|
||||
if (fsm == null || fsm.FsmName != "BoltCheck") continue;
|
||||
FsmGameObject db = fsm.FsmVariables.FindFsmGameObject("db_ThisPart");
|
||||
Part part;
|
||||
if (db == null || db.Value == null || !partsByDb.TryGetValue(db.Value, out part)) continue;
|
||||
part.BoltCheck = fsm;
|
||||
part.Tightness = fsm.FsmVariables.FindFsmFloat("Tightness");
|
||||
part.MaxTightness = ClampMax(fsm);
|
||||
foreach (PlayMakerFSM bolt in fsm.GetComponentsInChildren<PlayMakerFSM>())
|
||||
if (bolt != null && bolt.FsmName == "Screw" && bolt.FsmVariables.FindFsmInt("Stage") != null) part.Bolts.Add(bolt);
|
||||
if (part.MaxTightness <= 0f) part.MaxTightness = Mathf.Max(1, part.Bolts.Count) * 8f;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Höchste Festigkeit eines Teils: das FloatClamp im BoltCheck ("Bolts OFF").</summary>
|
||||
static float ClampMax(PlayMakerFSM boltCheck)
|
||||
{
|
||||
try
|
||||
{
|
||||
foreach (FsmState state in boltCheck.FsmStates)
|
||||
{
|
||||
if (!state.ActionsLoaded) state.LoadActions();
|
||||
if (state.Actions == null) continue;
|
||||
foreach (FsmStateAction a in state.Actions)
|
||||
{
|
||||
if (a == null || a.GetType().Name != "FloatClamp") continue;
|
||||
var max = a.GetType().GetField("maxValue") != null ? a.GetType().GetField("maxValue").GetValue(a) as FsmFloat : null;
|
||||
if (max != null && max.Value > 0f) return max.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
return 0f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Eingebautes Teil exakt auf seinen Halter (pivot_...) setzen - dort sitzt es im Spiel immer bei Position/Drehung 0.
|
||||
/// Nicht bei Teilen, die selbst eingedreht werden (Zündkerze, Ölfilter: Tiefe = Stufe), und nicht bei Türen/Hauben
|
||||
/// (Scharnier). Gibt true zurück, wenn es verrutscht war.
|
||||
/// </summary>
|
||||
public static bool SnapToPivot(GameObject part)
|
||||
{
|
||||
if (part == null) return false;
|
||||
Transform t = part.transform;
|
||||
if (t.parent == null || !t.parent.name.StartsWith("pivot_")) return false;
|
||||
if (part.GetComponent<HingeJoint>() != null) return false;
|
||||
foreach (PlayMakerFSM fsm in part.GetComponents<PlayMakerFSM>()) if (fsm.FsmName == "Screw") return false;
|
||||
if (t.localPosition.sqrMagnitude < 0.0004f * 0.0004f && Quaternion.Angle(t.localRotation, Quaternion.identity) < 0.05f) return false;
|
||||
t.localPosition = Vector3.zero;
|
||||
t.localRotation = Quaternion.identity;
|
||||
Rigidbody rb = part.GetComponent<Rigidbody>();
|
||||
if (rb != null && !rb.isKinematic)
|
||||
{
|
||||
rb.velocity = Vector3.zero;
|
||||
rb.angularVelocity = Vector3.zero;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>Alle Schrauben eines Teils ganz fest (true) oder ganz lose (false) - wie mit dem Werkzeug.</summary>
|
||||
public static int SetBolts(Part p, bool tight)
|
||||
{
|
||||
int changed = 0;
|
||||
foreach (PlayMakerFSM bolt in p.Bolts)
|
||||
{
|
||||
if (bolt == null || !bolt.gameObject.activeInHierarchy) continue;
|
||||
FsmInt stage = bolt.FsmVariables.FindFsmInt("Stage");
|
||||
FsmState dispatch = bolt.Fsm.GetState("Stages 2") ?? bolt.Fsm.GetState("Stages");
|
||||
if (stage == null || dispatch == null) continue;
|
||||
int target = tight ? 8 : 0;
|
||||
if (stage.Value == target) continue;
|
||||
stage.Value = target;
|
||||
FsmSync.SwitchStateSilently(bolt, dispatch); // Schraube an ihre Stelle, Zähler des Teils (Array) aktualisieren
|
||||
changed++;
|
||||
}
|
||||
if (p.Tightness != null)
|
||||
{
|
||||
float target = tight ? p.MaxTightness : 0f;
|
||||
if (Mathf.Abs(p.Tightness.Value - target) > 0.01f) { p.Tightness.Value = target; changed++; }
|
||||
}
|
||||
if (tight && p.BoltCheck != null && SnapToPivot(p.BoltCheck.gameObject)) changed++;
|
||||
return changed;
|
||||
}
|
||||
|
||||
public static int SetAllBolts(bool tight)
|
||||
{
|
||||
Refresh(true);
|
||||
int n = 0;
|
||||
foreach (Part p in parts)
|
||||
if (p.HasBolts && (p.Installed == null || p.Installed.Value) && SetBolts(p, tight) > 0) n++;
|
||||
return n;
|
||||
}
|
||||
|
||||
/// <summary>"RadiatorHose1" -> "Radiator Hose 1", "Drumbrake_RR" -> "Drumbrake RR".</summary>
|
||||
static string Pretty(string name)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user