Satsuma no longer falls apart when driving together; automatic passenger seat
- A driven vehicle is no longer released after 3 s without data (a short hitch of the driver, e.g. saving, stopped the car instantly on the passenger's side and ripped the parts off, which then synced to everyone): 30 s timeout, on release it keeps the driver's velocity and part joints stay unbreakable for 3 more seconds - Cargo in a moving vehicle belongs to its driver (passengers no longer claim items in the car, which then followed late and kinematic at the driver's end and knocked seats and parts off) - Passenger: sit down automatically by stepping into the open passenger door, also without a driver (seat from the game's DriverHeadPivot); Space or P to get out Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+115
-2
@@ -30,6 +30,14 @@ namespace MSCO
|
||||
// Gelenke der angebauten Teile, solange ein anderer das Fahrzeug fährt: unzerbrechlich (vorherige Bruchkraft/-moment)
|
||||
public Dictionary<Joint, Vector2> HeldJoints;
|
||||
public float NextPartScan;
|
||||
// Nach der Rückgabe erst so spät wieder zerbrechlich (bis dahin schwingen die Teile ein)
|
||||
public float JointRestoreAt = float.MaxValue;
|
||||
// Item liegt in diesem Fahrzeug (Kofferraum, Sitz ...): gehört dann dem, der das Fahrzeug fährt
|
||||
public SyncedBody Carrier;
|
||||
public float NextCarrierCheck;
|
||||
// Fahrzeug: Umriss in lokalen Koordinaten (für "liegt im Auto")
|
||||
public Bounds LocalBounds;
|
||||
public bool HasLocalBounds;
|
||||
public float StillTime;
|
||||
public float NoMotionClaimUntil; // kurz nach einer Übergabe nicht wegen Nachfedern/Nachrollen übernehmen
|
||||
// Am Auto angebautes Teil (Gelenk zum Fahrzeug oder unter einem anderen Rigidbody): bewegt sich mit dem Auto,
|
||||
@@ -82,6 +90,12 @@ namespace MSCO
|
||||
const float ReleaseAfterStill = 1f;
|
||||
const float SnapDistance = 10f;
|
||||
const float RemoteTimeout = 3f; // Besitzer meldet sich nicht mehr (z.B. Teil wurde dort montiert)
|
||||
// Ein gefahrenes Auto NICHT nach 3 s freigeben: hängt das Spiel des Fahrers kurz (z.B. beim Speichern), blieb es
|
||||
// bei den anderen schlagartig stehen und die Teile rissen ab. Trennt der Fahrer, gibt ReleaseAllOf es frei.
|
||||
const float DrivenVehicleTimeout = 30f;
|
||||
const float JointSettleTime = 3f; // so lange nach der Rückgabe bleiben die Gelenke der Teile unzerbrechlich
|
||||
const float CarrierCheckInterval = 0.4f;
|
||||
const float CarrierMinSpeed = 1.5f; // ab dieser Geschwindigkeit nimmt ein Fahrzeug seine Ladung mit
|
||||
const float RunawaySpeed = 90f; // m/s - schneller fährt in MSC nichts; darüber hat die Physik es weggeschleudert
|
||||
const float HandoverCooldown = 3f; // nach Freigabe/Übergabe keine Übernahme durch Bewegung
|
||||
const float MaxHoldDistance = 1.5f; // gehaltene Items sind so nah an der Kamera
|
||||
@@ -191,6 +205,9 @@ namespace MSCO
|
||||
|
||||
public bool IsRemoteOwnedBody(SyncedBody b) { return IsRemoteOwned(b); }
|
||||
|
||||
/// <summary>Alle erfassten Fahrzeuge (Liste wird alle 2 s erneuert, nicht verändern).</summary>
|
||||
public List<SyncedBody> AllVehicles() { return Vehicles(); }
|
||||
|
||||
/// <summary>Für "mp vehicles": Zustand aller erfassten Fahrzeuge (zur Fehlersuche).</summary>
|
||||
public List<string> DescribeVehicles()
|
||||
{
|
||||
@@ -258,7 +275,10 @@ namespace MSCO
|
||||
public void Reset()
|
||||
{
|
||||
foreach (SyncedBody b in byId.Values)
|
||||
{
|
||||
if (IsRemoteOwned(b) && b.Rb != null) RestoreKinematic(b);
|
||||
RestoreJoints(b);
|
||||
}
|
||||
byId.Clear();
|
||||
byRb.Clear();
|
||||
rejected.Clear();
|
||||
@@ -338,13 +358,25 @@ namespace MSCO
|
||||
b.LastPos = b.Rb.position;
|
||||
if (!b.Rb.gameObject.activeInHierarchy) continue;
|
||||
if (UpdateAttached(b)) continue; // angebaut: bewegt sich mit dem Auto
|
||||
if (b.HeldJoints != null && !IsRemoteOwned(b) && Time.time >= b.JointRestoreAt) RestoreJoints(b);
|
||||
|
||||
bool active = b == held || b == driven;
|
||||
if (!IsRemoteOwned(b)) StopRunaway(b);
|
||||
// Ladung eines fahrenden Autos gehört dessen Fahrer: nur bei ihm wird sie mit dem Auto simuliert.
|
||||
// Übernahm sie ein Mitfahrer, folgte sie beim Fahrer verspätet und kinematisch (unendlich schwer) und
|
||||
// schlug Sitze und Teile ab.
|
||||
SyncedBody carrier = active || b.IsVehicle ? null : CarrierOf(b);
|
||||
bool carrierRemote = carrier != null && IsRemoteOwned(carrier);
|
||||
bool carrierMine = carrier != null && IsLocal(carrier) && IsCarrying(carrier);
|
||||
if (b.OwnerId == SyncedBody.NoOwner)
|
||||
{
|
||||
if (active) Claim(b, true);
|
||||
else if (motionClaimAllowed && Time.time >= b.NoMotionClaimUntil && IsMoving(b) && IsNear(b, player)) Claim(b, false);
|
||||
else if (carrierMine && IsMoving(b)) Claim(b, false);
|
||||
else if (motionClaimAllowed && Time.time >= b.NoMotionClaimUntil && IsMoving(b) && IsNear(b, player) && !carrierRemote) Claim(b, false);
|
||||
}
|
||||
else if (IsLocal(b) && !active && carrierRemote && carrier.OwnerActive)
|
||||
{
|
||||
Release(b); // fährt ein anderer: der übernimmt die Ladung
|
||||
}
|
||||
else if (IsLocal(b))
|
||||
{
|
||||
@@ -367,7 +399,8 @@ namespace MSCO
|
||||
// zeigt oft noch auf ein altes Item; trägt es jemand an unserem Kopf vorbei, würden wir es ihm sonst entreißen
|
||||
bool grab = b == driven || !b.OwnerActive || JustPicked;
|
||||
if (active && grab && Wins(LocalId, true, b.OwnerId, b.OwnerActive)) Claim(b, true); // Spieler greift sich das Objekt
|
||||
else if (Time.time - b.LastRecvTime > RemoteTimeout) SetFree(b);
|
||||
else if (carrierMine && !b.OwnerActive) Claim(b, false); // liegt in unserem fahrenden Auto
|
||||
else if (Time.time - b.LastRecvTime > (b.IsVehicle && b.OwnerActive ? DrivenVehicleTimeout : RemoteTimeout)) SetFree(b);
|
||||
}
|
||||
}
|
||||
ReportConsumed(player);
|
||||
@@ -498,6 +531,7 @@ namespace MSCO
|
||||
b.Rb.isKinematic = true;
|
||||
if (b.IsVehicle)
|
||||
{
|
||||
b.JointRestoreAt = float.MaxValue;
|
||||
HoldParts(b);
|
||||
// Physik läuft mit 50 Hz, das Bild öfter: ohne Interpolation springt das Auto nur in Physik-Schritten
|
||||
// weiter - wer darin mitfährt, sieht die Welt ruckeln. Nur bei Fahrzeugen (Items setzt die Hand direkt).
|
||||
@@ -527,9 +561,24 @@ namespace MSCO
|
||||
{
|
||||
b.Rb.isKinematic = b.WasKinematic;
|
||||
if (b.InterpolationSet) b.Rb.interpolation = b.WasInterpolation;
|
||||
// Mitten in der Fahrt zurückgegeben (Fahrer übergibt, steigt aus ...): mit seiner Geschwindigkeit
|
||||
// weiterrollen statt schlagartig stehenzubleiben - die Teile wollen sonst mit Tempo weiter und reißen ab
|
||||
if (b.IsVehicle && !b.Rb.isKinematic)
|
||||
{
|
||||
b.Rb.velocity = Time.fixedTime - b.TargetTime < 0.5f ? b.TargetVel : Vector3.zero;
|
||||
b.Rb.angularVelocity = Vector3.zero;
|
||||
}
|
||||
}
|
||||
b.InterpolationSet = false;
|
||||
if (b.HeldJoints == null) return;
|
||||
if (b.IsVehicle) { b.JointRestoreAt = Time.time + JointSettleTime; return; } // erst nach dem Einschwingen
|
||||
RestoreJoints(b);
|
||||
}
|
||||
|
||||
void RestoreJoints(SyncedBody b)
|
||||
{
|
||||
b.JointRestoreAt = float.MaxValue;
|
||||
if (b.HeldJoints == null) return;
|
||||
foreach (KeyValuePair<Joint, Vector2> kv in b.HeldJoints)
|
||||
{
|
||||
if (kv.Key == null) continue; // Teil wurde inzwischen abgebaut
|
||||
@@ -539,6 +588,70 @@ namespace MSCO
|
||||
b.HeldJoints = null;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------- Ladung in Fahrzeugen
|
||||
|
||||
/// <summary>Fährt das Fahrzeug gerade (nimmt also seine Ladung mit)?</summary>
|
||||
bool IsCarrying(SyncedBody v)
|
||||
{
|
||||
if (v.Rb == null) return false;
|
||||
if (IsRemoteOwned(v)) return v.OwnerActive || v.TargetVel.sqrMagnitude > CarrierMinSpeed * CarrierMinSpeed;
|
||||
return v == Driven || (!v.Rb.isKinematic && v.Rb.velocity.sqrMagnitude > CarrierMinSpeed * CarrierMinSpeed);
|
||||
}
|
||||
|
||||
/// <summary>In welchem Fahrzeug liegt das Item gerade (null = in keinem)? Zwischengespeichert.</summary>
|
||||
SyncedBody CarrierOf(SyncedBody item)
|
||||
{
|
||||
if (Time.time < item.NextCarrierCheck) return item.Carrier != null && item.Carrier.Rb != null ? item.Carrier : null;
|
||||
item.NextCarrierCheck = Time.time + CarrierCheckInterval;
|
||||
item.Carrier = null;
|
||||
Vector3 p = item.Rb.worldCenterOfMass;
|
||||
foreach (SyncedBody v in Vehicles())
|
||||
{
|
||||
if (v.Rb == null || (v.Rb.position - p).sqrMagnitude > 15f * 15f) continue;
|
||||
if (!v.HasLocalBounds) ComputeLocalBounds(v);
|
||||
if (v.LocalBounds.Contains(v.Rb.transform.InverseTransformPoint(p))) { item.Carrier = v; break; }
|
||||
}
|
||||
return item.Carrier;
|
||||
}
|
||||
|
||||
readonly List<SyncedBody> vehicleList = new List<SyncedBody>();
|
||||
float nextVehicleList;
|
||||
|
||||
List<SyncedBody> Vehicles()
|
||||
{
|
||||
if (Time.time < nextVehicleList) return vehicleList;
|
||||
nextVehicleList = Time.time + 2f;
|
||||
vehicleList.Clear();
|
||||
foreach (SyncedBody b in byId.Values) if (b.IsVehicle && b.Rb != null) vehicleList.Add(b);
|
||||
return vehicleList;
|
||||
}
|
||||
|
||||
/// <summary>Umriss des Fahrzeugs (alle festen Collider) in seinen lokalen Koordinaten, etwas nach oben erweitert.</summary>
|
||||
static void ComputeLocalBounds(SyncedBody v)
|
||||
{
|
||||
Transform t = v.Rb.transform;
|
||||
bool any = false;
|
||||
var local = new Bounds();
|
||||
foreach (Collider c in v.Rb.GetComponentsInChildren<Collider>())
|
||||
{
|
||||
if (c == null || c.isTrigger || !c.enabled) continue;
|
||||
Bounds wb = c.bounds;
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
Vector3 corner = wb.center + Vector3.Scale(wb.extents, new Vector3((i & 1) == 0 ? -1 : 1, (i & 2) == 0 ? -1 : 1, (i & 4) == 0 ? -1 : 1));
|
||||
Vector3 lp = t.InverseTransformPoint(corner);
|
||||
if (!any) { local = new Bounds(lp, Vector3.zero); any = true; }
|
||||
else local.Encapsulate(lp);
|
||||
}
|
||||
}
|
||||
if (!any) local = new Bounds(Vector3.zero, new Vector3(2f, 1.5f, 4f));
|
||||
// Weltachsen-Umrisse sind bei schrägem Auto zu groß - etwas verkleinern, oben Platz für Ladung lassen
|
||||
local.Expand(new Vector3(-0.2f, 0f, -0.2f));
|
||||
local.max = new Vector3(local.max.x, local.max.y + 0.5f, local.max.z);
|
||||
v.LocalBounds = local;
|
||||
v.HasLocalBounds = true;
|
||||
}
|
||||
|
||||
void SendOwnedStates()
|
||||
{
|
||||
sendList.Clear();
|
||||
|
||||
Reference in New Issue
Block a user