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:
2026-10-03 12:16:29 +02:00
co-authored by Claude Opus 5.5
parent 7b4a89d50f
commit 29a3697cc8
6 changed files with 235 additions and 15 deletions
+34 -3
View File
@@ -270,7 +270,7 @@ namespace MSCO
void DrawAdmin(MultiplayerManager mp)
{
float width = Mathf.Min(720, Screen.width - 40), height = Mathf.Min(580, Screen.height - 40);
float width = Mathf.Min(adminTab == 2 ? 940 : 720, Screen.width - 40), height = Mathf.Min(adminTab == 2 ? 680 : 580, Screen.height - 40);
var rect = new Rect((Screen.width - width) / 2f, (Screen.height - height) / 2f, width, height);
UiTheme.DrawShadow(rect);
GUI.Box(rect, GUIContent.none, panel);
@@ -338,7 +338,21 @@ namespace MSCO
if (n > 0) mp.VehicleCheat("repaired the Satsuma (" + n + " parts)");
}
GUILayout.Space(6);
if (GUILayout.Button("REFRESH", secondary, GUILayout.Height(30), GUILayout.Width(100))) VehicleAdmin.Refresh(true);
if (GUILayout.Button("TIGHTEN ALL", secondary, GUILayout.Height(30), GUILayout.Width(110)))
{
int n = VehicleAdmin.SetAllBolts(true);
Log(n + " part(s) tightened");
if (n > 0) mp.VehicleCheat("tightened all bolts (" + n + " parts)");
}
GUILayout.Space(6);
if (GUILayout.Button("LOOSEN ALL", danger, GUILayout.Height(30), GUILayout.Width(106)))
{
int n = VehicleAdmin.SetAllBolts(false);
Log(n + " part(s) loosened");
if (n > 0) mp.VehicleCheat("loosened all bolts (" + n + " parts, testing)");
}
GUILayout.Space(6);
if (GUILayout.Button("REFRESH", secondary, GUILayout.Height(30), GUILayout.Width(90))) VehicleAdmin.Refresh(true);
GUILayout.Space(12);
GUILayout.Label("Filter", caption, GUILayout.Width(44));
partFilter = GUILayout.TextField(partFilter ?? "", 30, field, GUILayout.Height(30));
@@ -397,7 +411,24 @@ namespace MSCO
GUI.DrawTexture(back, texBarBack);
GUI.DrawTexture(new Rect(back.x, back.y, Mathf.Max(2f, back.width * frac), back.height), frac < 0.3f ? texBarLow : texBarFill);
}
GUILayout.Label(damaged ? "DAMAGED" : p.Wear != null ? p.Wear.Value.ToString("0") + " %" : "OK", damaged ? danger : small, GUILayout.Width(84));
string boltText = p.Tightness != null ? (p.Bolted != null && p.Bolted.Value ? " bolted" : " loose") : "";
GUILayout.Label((damaged ? "DAMAGED" : p.Wear != null ? p.Wear.Value.ToString("0") + " %" : "OK") + boltText, damaged ? danger : small, GUILayout.Width(120));
if (p.HasBolts && installed)
{
if (GUILayout.Button("TIGHTEN", secondary, GUILayout.Height(26), GUILayout.Width(80)))
{
VehicleAdmin.SetBolts(p, true);
mp.VehicleCheat("tightened " + p.Name);
}
GUILayout.Space(4);
if (GUILayout.Button("LOOSEN", secondary, GUILayout.Height(26), GUILayout.Width(76)))
{
VehicleAdmin.SetBolts(p, false);
mp.VehicleCheat("loosened " + p.Name);
}
GUILayout.Space(4);
}
else GUILayout.Space(164);
if (GUILayout.Button("REPAIR", secondary, GUILayout.Height(26), GUILayout.Width(76)))
{
VehicleAdmin.Repair(p);