MSCO 2.10.2: passenger only while moving, drain plug sync, admin VEHICLE tab, profile layout

- Passenger: auto ride-along only when the vehicle moves (speed from its motion, also for
  kinematic remote cars) - no more getting stuck while working in the engine bay or re-captured
- Bolts with a function (drain plug: stage 0-5 = oil leak on) are synced as stage and applied
  through the bolt's own stage logic; world authority sends its state once after joining.
  Fixes endless oil leaking when players had the drain plug in different states
- Admin menu VEHICLE tab: fluids with FILL/EMPTY, all Database parts with wear/damage,
  REPAIR/BREAK per part, REPAIR ALL, filter
- Server profile: info and players as two equal cards, no wrapping
- fsmdump also lists FSM variables

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-03 12:02:21 +02:00
co-authored by Claude Opus 5.5
parent adc6eff9e8
commit 7b4a89d50f
11 changed files with 462 additions and 41 deletions
+34 -3
View File
@@ -4,8 +4,9 @@ using UnityEngine;
namespace MSCO
{
/// <summary>
/// Mitfahren: Wer mit dem Kopf im Fahrzeug ist (Kabine, offene Tür, Ladefläche ...), wird genau dort festgehalten und
/// fährt mit - egal ob schon jemand fährt oder das Auto noch steht. Aussteigen mit Leertaste oder der Beifahrer-Taste.
/// Mitfahren: Wer mit dem Kopf im Fahrzeug ist (Kabine, offene Tür, Ladefläche ...), wird genau dort festgehalten,
/// sobald das Fahrzeug FÄHRT (egal wer fährt). Am stehenden Auto wird niemand automatisch festgehalten - sonst hing
/// man fest, sobald man sich zum Schrauben in den Motorraum beugte. Aussteigen mit Leertaste oder der Beifahrer-Taste.
/// Steht man per Taste neben dem Auto, kommt man auf den Beifahrersitz (Fahrerposition des Spiels, gespiegelt).
/// Während der Fahrt wird der Spieler jedes Frame an seine Stelle im Fahrzeug gesetzt und dreht mit; umschauen geht.
/// </summary>
@@ -15,7 +16,8 @@ namespace MSCO
const float AutoEnterRange = 1.0f; // automatisch: Kopf so nah am Beifahrersitz ...
const float AutoEnterSide = 0.6f; // ... und seitlich höchstens so weit außerhalb des Sitzes - an der
// geschlossenen Tür steht man weiter draußen, nur in der offenen Tür so nah
const float AutoEnterDwell = 0.35f; // ... so lange (nicht beim bloßen Vorbeilaufen)
const float AutoEnterDwell = 0.15f; // ... so lange
const float MovingSpeed = 1.2f; // m/s: ab hier "fährt" das Fahrzeug (dann wird man festgehalten)
const float AutoRearmRange = 2f; // nach dem Aussteigen erst wieder, wenn man so weit weg war
const float AutoCheckInterval = 0.15f;
const float CenterSeatThreshold = 0.2f;
@@ -40,6 +42,10 @@ namespace MSCO
const float AfterDrivingPause = 4f; // nach dem Fahren (Ein-/Aussteigen am Fahrersitz) nicht automatisch
SyncedBody dwellVehicle;
bool autoArmed = true;
// Geschwindigkeit je Fahrzeug aus der Bewegung (Fahrzeuge anderer sind kinematisch - deren velocity ist 0)
readonly Dictionary<Rigidbody, Vector3> lastVehiclePos = new Dictionary<Rigidbody, Vector3>();
readonly Dictionary<Rigidbody, float> vehicleSpeed = new Dictionary<Rigidbody, float>();
float lastSpeedSample = -1f;
Vector3 lastExitSeat;
public bool IsRiding { get { return vehicle != null; } }
@@ -101,6 +107,7 @@ namespace MSCO
if ((cc != null && !cc.enabled) || player.parent != null) { lastDriving = Time.time; dwellStart = -1f; return null; }
if (Time.time < nextAutoCheck) return null;
nextAutoCheck = Time.time + AutoCheckInterval;
SampleSpeeds(objects);
Vector3 head = cam.transform.position;
// Nach dem Aussteigen erst einmal aus dem Fahrzeug heraus, sonst säße man sofort wieder drin
@@ -111,6 +118,7 @@ namespace MSCO
}
SyncedBody found = VehicleAround(objects, head);
if (found != null && SpeedOf(found) < MovingSpeed) found = null; // steht: frei bewegen (z.B. schrauben)
if (found == null || found != dwellVehicle)
{
dwellVehicle = found;
@@ -124,6 +132,26 @@ namespace MSCO
return "Riding along (Space or the passenger key to get out)";
}
/// <summary>Geschwindigkeit aller Fahrzeuge in der Nähe aus ihrer Bewegung seit der letzten Messung.</summary>
void SampleSpeeds(ObjectSync objects)
{
float now = Time.time, dt = lastSpeedSample < 0f ? 0f : now - lastSpeedSample;
lastSpeedSample = now;
foreach (SyncedBody b in objects.AllVehicles())
{
if (b.Rb == null) continue;
Vector3 pos = b.Rb.position, last;
if (dt > 0.01f && lastVehiclePos.TryGetValue(b.Rb, out last)) vehicleSpeed[b.Rb] = (pos - last).magnitude / dt;
lastVehiclePos[b.Rb] = pos;
}
}
float SpeedOf(SyncedBody b)
{
float v;
return b.Rb != null && vehicleSpeed.TryGetValue(b.Rb, out v) ? v : 0f;
}
/// <summary>Fahrzeug, in dessen Umriss der Kopf gerade ist (null = in keinem).</summary>
SyncedBody VehicleAround(ObjectSync objects, Vector3 head)
{
@@ -280,6 +308,9 @@ namespace MSCO
dwellStart = -1f;
driverHeads.Clear();
innerBounds.Clear();
lastVehiclePos.Clear();
vehicleSpeed.Clear();
lastSpeedSample = -1f;
SetMovementEnabled(null, true);
}