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:
@@ -9,6 +9,20 @@
|
||||
Web-Panel (Einstellungen), **OFFICIAL** vergeben nur die MSCO-Admins und wird hervorgehoben
|
||||
- Braucht keinen neuen Server: die Infos kommen vom Master (msco.mbm.wtf) und funktionieren mit 2.8-Servern
|
||||
|
||||
**Beifahrer**
|
||||
- **Automatisch einsteigen**: in die offene Beifahrertür an den Sitz treten - man setzt sich von selbst hin, keine Taste
|
||||
nötig. Aussteigen mit Leertaste oder der Beifahrer-Taste (P)
|
||||
- Geht jetzt auch, **wenn noch niemand fährt** (Sitzposition kommt aus dem Fahrersitz des Spiels); fährt danach jemand
|
||||
los, fährt man mit. Setzt man sich selbst ans Steuer, endet der Beifahrer-Modus
|
||||
|
||||
**Satsuma zerfällt nicht mehr beim gemeinsamen Fahren**
|
||||
- Hing das Spiel des Fahrers kurz (z.B. beim Speichern für den Server), gab der Mitfahrer das Auto nach 3 s frei: es
|
||||
blieb bei ihm schlagartig stehen, die Teile wollten mit Tempo weiter und rissen ab - und das „Teil abgefallen“ ging an
|
||||
alle. Ein gefahrenes Auto wird jetzt erst nach 30 s ohne Daten freigegeben, rollt dann mit der Geschwindigkeit des
|
||||
Fahrers weiter, und die Teile bleiben noch 3 s fest, bis alles eingeschwungen ist
|
||||
- Ladung im Auto (Kisten, Werkzeug, Teile im Kofferraum oder auf dem Sitz) gehört jetzt immer dem Fahrer. Vorher
|
||||
übernahm sie oft der Mitfahrer; beim Fahrer folgte sie dann verspätet und „unendlich schwer“ und schlug Sitze und Teile ab
|
||||
|
||||
**Mitfahren ohne Ruckeln**
|
||||
- Fahrer und Beifahrer sitzen beim anderen fest auf dem Sitz: die Figur wird relativ zum Auto übertragen und geglättet,
|
||||
statt in Weltkoordinaten hinter dem fahrenden Auto herzuhinken (vorher wurde der Beifahrer bei Tempo nach hinten gezogen)
|
||||
|
||||
+11
-5
@@ -1850,14 +1850,20 @@ namespace MSCO
|
||||
StartCoroutine(ApplyWorldSnapshot(snapshot));
|
||||
}
|
||||
|
||||
string passengerResult = null;
|
||||
if (MSCO.PassengerKey != null && !overlays.AnyOpen && MSCO.PassengerKey.GetKeybindDown())
|
||||
passengerResult = passenger.Toggle(localPlayer, playerCamera, objects, players);
|
||||
else if (passenger.IsRiding && !overlays.AnyOpen && Input.GetKeyDown(KeyCode.Space))
|
||||
{
|
||||
string result = passenger.Toggle(localPlayer, playerCamera, objects, players);
|
||||
if (result != null)
|
||||
{
|
||||
SetStatus(result);
|
||||
overlays.AddChat("", result, true); // im Spiel sichtbar, nicht nur in der Konsole
|
||||
passenger.Exit(localPlayer, playerCamera); // aussteigen wie zu Fuß losspringen
|
||||
passengerResult = "Got out";
|
||||
}
|
||||
else if (IsPeerReady && MenuHelper.InGame && localPlayer != null && !overlays.AnyOpen)
|
||||
passengerResult = passenger.AutoUpdate(localPlayer, playerCamera, objects, players); // in die Beifahrertür treten
|
||||
if (passengerResult != null)
|
||||
{
|
||||
SetStatus(passengerResult);
|
||||
overlays.AddChat("", passengerResult, true); // im Spiel sichtbar, nicht nur in der Konsole
|
||||
}
|
||||
|
||||
bool ready = IsPeerReady;
|
||||
|
||||
+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();
|
||||
|
||||
+121
-26
@@ -4,14 +4,21 @@ using UnityEngine;
|
||||
namespace MSCO
|
||||
{
|
||||
/// <summary>
|
||||
/// Beifahrer: Neben einem Fahrzeug, das ein anderer Spieler fährt, per Taste einsteigen.
|
||||
/// Der Sitz ist die Fahrerposition, auf die andere Seite gespiegelt (bei Fahrzeugen mit
|
||||
/// Sitz in der Mitte, z.B. Traktor oder Moped: dahinter). Während der Fahrt wird der
|
||||
/// Spieler jedes Frame auf den Sitz gesetzt und dreht mit dem Fahrzeug mit; umschauen geht weiter.
|
||||
/// Beifahrer: Wer in die offene Beifahrertür an den Sitz tritt, setzt sich automatisch hin - egal ob schon jemand
|
||||
/// fährt oder das Auto noch steht (die Taste geht weiterhin, auch zum Aussteigen; Leertaste steigt ebenfalls aus).
|
||||
/// Der Sitz ist die Fahrerposition des Spiels (DriverHeadPivot), auf die andere Seite gespiegelt (bei Fahrzeugen mit
|
||||
/// Sitz in der Mitte, z.B. Traktor oder Moped: dahinter). Während der Fahrt wird der Spieler jedes Frame auf den Sitz
|
||||
/// gesetzt und dreht mit dem Fahrzeug mit; umschauen geht weiter.
|
||||
/// </summary>
|
||||
public class Passenger
|
||||
{
|
||||
const float EnterRange = 3f; // so nah muss man an der Karosserie stehen
|
||||
const float EnterRange = 3f; // per Taste: so nah muss man an der Karosserie stehen
|
||||
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 AutoRearmRange = 2f; // nach dem Aussteigen erst wieder, wenn man so weit weg war
|
||||
const float AutoCheckInterval = 0.15f;
|
||||
const float CenterSeatThreshold = 0.2f;
|
||||
const float BehindCenterSeat = 0.7f;
|
||||
const float ExitSideOffset = 1.4f; // beim Aussteigen so weit neben den Sitz
|
||||
@@ -27,6 +34,13 @@ namespace MSCO
|
||||
// Feste Collider des Spielers, die während der Fahrt aus sind (sonst stecken sie in Sitzen und Teilen des Autos)
|
||||
readonly List<Collider> disabledColliders = new List<Collider>();
|
||||
readonly List<Rigidbody> frozenBodies = new List<Rigidbody>();
|
||||
// Fahrerkopf je Fahrzeug (lokal), aus DriverHeadPivot - null = nicht gefunden
|
||||
readonly Dictionary<Rigidbody, Vector3?> driverHeads = new Dictionary<Rigidbody, Vector3?>();
|
||||
|
||||
float nextAutoCheck, dwellStart = -1f;
|
||||
SyncedBody dwellVehicle;
|
||||
bool autoArmed = true;
|
||||
Vector3 lastExitSeat;
|
||||
|
||||
public bool IsRiding { get { return vehicle != null; } }
|
||||
/// <summary>Fahrzeug, in dem wir mitfahren (sonst null).</summary>
|
||||
@@ -42,38 +56,109 @@ namespace MSCO
|
||||
Exit(player, cam);
|
||||
return "Got out";
|
||||
}
|
||||
if (cam == null) return null;
|
||||
if (cam == null || objects.IsDriving) return null;
|
||||
|
||||
SyncedBody best = null;
|
||||
RemotePlayer driver = null;
|
||||
float bestDist = EnterRange * EnterRange;
|
||||
List<SyncedBody> driven = objects.RemoteDrivenVehicles();
|
||||
float nearest = float.MaxValue;
|
||||
foreach (SyncedBody b in driven)
|
||||
Vector3 bestSeat = Vector3.zero;
|
||||
float bestDist = EnterRange * EnterRange, nearest = float.MaxValue;
|
||||
foreach (SyncedBody b in objects.AllVehicles())
|
||||
{
|
||||
if (b.Rb == null || (b.Rb.position - player.position).sqrMagnitude > 30f * 30f) continue;
|
||||
float d = DistanceToVehicleSq(b, player.position);
|
||||
Vector3? seat = SeatFor(b, objects, players);
|
||||
if (seat == null) continue;
|
||||
nearest = Mathf.Min(nearest, d);
|
||||
PlayerInfo owner;
|
||||
if (!players.TryGetValue(b.OwnerId, out owner) || owner.Avatar == null) continue;
|
||||
if (d < bestDist) { bestDist = d; best = b; driver = owner.Avatar; }
|
||||
if (d < bestDist) { bestDist = d; best = b; bestSeat = seat.Value; }
|
||||
}
|
||||
if (best == null)
|
||||
{
|
||||
// Genau sagen, woran es liegt
|
||||
if (driven.Count == 0) return "Passenger: nobody is driving a vehicle right now (the driver must sit in the driver's seat)";
|
||||
if (nearest == float.MaxValue) return "Passenger: no vehicle nearby";
|
||||
return "Passenger: get closer to the car (" + Mathf.Sqrt(nearest).ToString("0") + " m away, max " + EnterRange.ToString("0") + " m)";
|
||||
}
|
||||
Enter(best, bestSeat, player);
|
||||
return "Riding as passenger (press again or Space to get out)";
|
||||
}
|
||||
|
||||
Transform vt = best.Rb.transform;
|
||||
Vector3 driverHead = vt.InverseTransformPoint(driver.HeadPosition);
|
||||
seatHeadLocal = Mathf.Abs(driverHead.x) < CenterSeatThreshold
|
||||
? driverHead - new Vector3(0f, 0f, BehindCenterSeat) // Sitz in der Mitte: dahinter
|
||||
: new Vector3(-driverHead.x, driverHead.y, driverHead.z); // gegenüber vom Fahrer
|
||||
/// <summary>
|
||||
/// Jedes Frame (zu Fuß): steht man mit dem Kopf am Beifahrersitz (in der offenen Tür), automatisch hinsetzen.
|
||||
/// Gibt einen Statustext zurück, wenn man eingestiegen ist.
|
||||
/// </summary>
|
||||
public string AutoUpdate(Transform player, Camera cam, ObjectSync objects, IDictionary<int, PlayerInfo> players)
|
||||
{
|
||||
if (IsRiding || cam == null || player == null || objects.IsDriving) { dwellStart = -1f; return null; }
|
||||
if (Time.time < nextAutoCheck) return null;
|
||||
nextAutoCheck = Time.time + AutoCheckInterval;
|
||||
Vector3 head = cam.transform.position;
|
||||
|
||||
vehicle = best;
|
||||
lastYaw = vt.eulerAngles.y;
|
||||
// Nach dem Aussteigen erst einmal weggehen, sonst säße man sofort wieder drin
|
||||
if (!autoArmed)
|
||||
{
|
||||
if ((head - lastExitSeat).sqrMagnitude > AutoRearmRange * AutoRearmRange) autoArmed = true;
|
||||
else return null;
|
||||
}
|
||||
|
||||
SyncedBody found = null;
|
||||
Vector3 foundSeat = Vector3.zero;
|
||||
foreach (SyncedBody b in objects.AllVehicles())
|
||||
{
|
||||
if (b.Rb == null || (b.Rb.position - head).sqrMagnitude > 8f * 8f) continue;
|
||||
Vector3? seat = SeatFor(b, objects, players);
|
||||
if (seat == null) continue;
|
||||
if ((b.Rb.transform.TransformPoint(seat.Value) - head).sqrMagnitude > AutoEnterRange * AutoEnterRange) continue;
|
||||
Vector3 local = b.Rb.transform.InverseTransformPoint(head);
|
||||
if (Mathf.Abs(local.x) > Mathf.Abs(seat.Value.x) + AutoEnterSide) continue;
|
||||
found = b;
|
||||
foundSeat = seat.Value;
|
||||
break;
|
||||
}
|
||||
if (found == null || found != dwellVehicle)
|
||||
{
|
||||
dwellVehicle = found;
|
||||
dwellStart = found != null ? Time.time : -1f;
|
||||
return null;
|
||||
}
|
||||
if (Time.time - dwellStart < AutoEnterDwell) return null;
|
||||
dwellStart = -1f;
|
||||
dwellVehicle = null;
|
||||
Enter(found, foundSeat, player);
|
||||
return "Riding as passenger (Space or the passenger key to get out)";
|
||||
}
|
||||
|
||||
void Enter(SyncedBody b, Vector3 seat, Transform player)
|
||||
{
|
||||
seatHeadLocal = seat;
|
||||
vehicle = b;
|
||||
lastYaw = b.Rb.transform.eulerAngles.y;
|
||||
SetMovementEnabled(player, false);
|
||||
return "Riding as passenger (press again to get out)";
|
||||
}
|
||||
|
||||
/// <summary>Beifahrersitz (Kopfposition, lokal im Fahrzeug) - aus dem Fahrersitz des Spiels oder dem Fahrer.</summary>
|
||||
Vector3? SeatFor(SyncedBody b, ObjectSync objects, IDictionary<int, PlayerInfo> players)
|
||||
{
|
||||
Vector3? driverHead;
|
||||
if (!driverHeads.TryGetValue(b.Rb, out driverHead))
|
||||
{
|
||||
driverHead = null;
|
||||
foreach (Transform t in b.Rb.GetComponentsInChildren<Transform>(true))
|
||||
{
|
||||
if (t.name != "DriverHeadPivot") continue;
|
||||
driverHead = b.Rb.transform.InverseTransformPoint(t.position);
|
||||
break;
|
||||
}
|
||||
driverHeads[b.Rb] = driverHead;
|
||||
}
|
||||
// Ohne DriverHeadPivot: nur, wenn gerade jemand fährt (dessen Kopf ist die Fahrerposition)
|
||||
if (driverHead == null && objects.IsRemoteOwnedBody(b) && b.OwnerActive)
|
||||
{
|
||||
PlayerInfo owner;
|
||||
if (players.TryGetValue(b.OwnerId, out owner) && owner.Avatar != null)
|
||||
driverHead = b.Rb.transform.InverseTransformPoint(owner.Avatar.HeadPosition);
|
||||
}
|
||||
if (driverHead == null) return null;
|
||||
Vector3 d = driverHead.Value;
|
||||
return Mathf.Abs(d.x) < CenterSeatThreshold
|
||||
? d - new Vector3(0f, 0f, BehindCenterSeat) // Sitz in der Mitte: dahinter
|
||||
: new Vector3(-d.x, d.y, d.z); // gegenüber vom Fahrer
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -96,12 +181,17 @@ namespace MSCO
|
||||
public void LateUpdate(Transform player, Camera cam, ObjectSync objects)
|
||||
{
|
||||
if (!IsRiding) return;
|
||||
// Fahrer ausgestiegen oder Fahrzeug weg: automatisch aussteigen
|
||||
if (vehicle.Rb == null || cam == null || !objects.IsRemoteOwnedBody(vehicle))
|
||||
if (vehicle.Rb == null || cam == null)
|
||||
{
|
||||
Exit(player, cam);
|
||||
return;
|
||||
}
|
||||
// Selbst ans Steuer gesetzt (Fahrersitz des Spiels): Beifahrer-Modus ohne Umsetzen beenden
|
||||
if (objects.IsDriving)
|
||||
{
|
||||
Reset();
|
||||
return;
|
||||
}
|
||||
|
||||
Transform vt = vehicle.Rb.transform;
|
||||
float yaw = vt.eulerAngles.y;
|
||||
@@ -117,6 +207,8 @@ namespace MSCO
|
||||
if (!IsRiding) return;
|
||||
if (player != null && vehicle.Rb != null)
|
||||
{
|
||||
lastExitSeat = vehicle.Rb.transform.TransformPoint(seatHeadLocal);
|
||||
autoArmed = false;
|
||||
// Auf der Seite des Sitzes neben das Fahrzeug stellen
|
||||
float side = seatHeadLocal.x >= 0f ? 1f : -1f;
|
||||
Vector3 outside = vehicle.Rb.transform.TransformPoint(seatHeadLocal + new Vector3(side * ExitSideOffset, 0f, 0f));
|
||||
@@ -131,6 +223,9 @@ namespace MSCO
|
||||
public void Reset()
|
||||
{
|
||||
vehicle = null;
|
||||
dwellVehicle = null;
|
||||
dwellStart = -1f;
|
||||
driverHeads.Clear();
|
||||
SetMovementEnabled(null, true);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user