Validate stored vehicle/object poses, show positions in 'vehicles', admin: Satsuma to me
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -318,6 +318,19 @@ namespace MSCO
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>Admin: Satsuma neben sich holen (z.B. wenn er verschwunden oder im Boden ist).</summary>
|
||||||
|
public void BringCar()
|
||||||
|
{
|
||||||
|
if (!CanCheat || localPlayer == null) return;
|
||||||
|
Vector3 right = localPlayer.right;
|
||||||
|
Vector3 pos = localPlayer.position + right * 4f;
|
||||||
|
Quaternion rot = Quaternion.LookRotation(localPlayer.forward, Vector3.up);
|
||||||
|
string result = objects.BringVehicle("SATSUMA", pos, rot);
|
||||||
|
overlays.Log(result);
|
||||||
|
overlays.AddChat("", result, true);
|
||||||
|
if (result.EndsWith("next to you")) AnnounceCheat("brought the Satsuma to themselves");
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>Cheats für alle sichtbar im Chat melden.</summary>
|
/// <summary>Cheats für alle sichtbar im Chat melden.</summary>
|
||||||
void AnnounceCheat(string what)
|
void AnnounceCheat(string what)
|
||||||
{
|
{
|
||||||
|
|||||||
+57
-2
@@ -102,6 +102,8 @@ namespace MSCO
|
|||||||
{
|
{
|
||||||
SyncedBody b;
|
SyncedBody b;
|
||||||
if (!byId.TryGetValue(id, out b) || b.Rb == null || b.OwnerId != SyncedBody.NoOwner) return false;
|
if (!byId.TryGetValue(id, out b) || b.Rb == null || b.OwnerId != SyncedBody.NoOwner) return false;
|
||||||
|
if (!IsValidPose(pos, rot)) return false;
|
||||||
|
if (b.IsVehicle) pos = AboveGround(pos, b.Rb.transform); // gespeicherte Position evtl. knapp im Boden
|
||||||
b.Rb.transform.position = pos;
|
b.Rb.transform.position = pos;
|
||||||
b.Rb.transform.rotation = rot;
|
b.Rb.transform.rotation = rot;
|
||||||
if (!b.Rb.isKinematic)
|
if (!b.Rb.isKinematic)
|
||||||
@@ -132,7 +134,8 @@ namespace MSCO
|
|||||||
if (!b.IsVehicle) continue;
|
if (!b.IsVehicle) continue;
|
||||||
string owner = b.OwnerId == SyncedBody.NoOwner ? "free" : IsLocal(b) ? "ME" : "player " + b.OwnerId;
|
string owner = b.OwnerId == SyncedBody.NoOwner ? "free" : IsLocal(b) ? "ME" : "player " + b.OwnerId;
|
||||||
lines.Add(b.RootName + " id=" + b.Id + " owner=" + owner + (b.OwnerActive ? " (active)" : "") +
|
lines.Add(b.RootName + " id=" + b.Id + " owner=" + owner + (b.OwnerActive ? " (active)" : "") +
|
||||||
(b.Rb == null ? " [rigidbody gone]" : " kinematic=" + b.Rb.isKinematic + " speed=" + b.Rb.velocity.magnitude.ToString("0.0")));
|
(b.Rb == null ? " [rigidbody gone]" : " kinematic=" + b.Rb.isKinematic + " speed=" + b.Rb.velocity.magnitude.ToString("0.0") +
|
||||||
|
" pos=" + b.Rb.position.ToString("F0") + (b.Rb.gameObject.activeInHierarchy ? "" : " [inactive]")));
|
||||||
}
|
}
|
||||||
if (lines.Count == 1) lines.Add("No vehicles registered! (SATSUMA, HAYOSIKO, ... not found as root objects with a Rigidbody)");
|
if (lines.Count == 1) lines.Add("No vehicles registered! (SATSUMA, HAYOSIKO, ... not found as root objects with a Rigidbody)");
|
||||||
return lines;
|
return lines;
|
||||||
@@ -426,7 +429,7 @@ namespace MSCO
|
|||||||
int id; Vector3 pos; Quaternion rot; Vector3 vel;
|
int id; Vector3 pos; Quaternion rot; Vector3 vel;
|
||||||
ReadState(r, out id, out pos, out rot, out vel);
|
ReadState(r, out id, out pos, out rot, out vel);
|
||||||
SyncedBody b;
|
SyncedBody b;
|
||||||
if (!byId.TryGetValue(id, out b) || b.Rb == null) continue;
|
if (!byId.TryGetValue(id, out b) || b.Rb == null || !IsValidPose(pos, rot)) continue;
|
||||||
// Nur dem aktuellen Besitzer folgen (oder freie Objekte übernehmen)
|
// Nur dem aktuellen Besitzer folgen (oder freie Objekte übernehmen)
|
||||||
if (b.OwnerId != from && b.OwnerId != SyncedBody.NoOwner) continue;
|
if (b.OwnerId != from && b.OwnerId != SyncedBody.NoOwner) continue;
|
||||||
SetRemote(b, from, b.OwnerId == from && b.OwnerActive);
|
SetRemote(b, from, b.OwnerId == from && b.OwnerActive);
|
||||||
@@ -442,6 +445,7 @@ namespace MSCO
|
|||||||
ReadState(r, out id, out pos, out rot, out vel);
|
ReadState(r, out id, out pos, out rot, out vel);
|
||||||
SyncedBody b;
|
SyncedBody b;
|
||||||
if (!byId.TryGetValue(id, out b) || b.Rb == null || b.OwnerId != from) return;
|
if (!byId.TryGetValue(id, out b) || b.Rb == null || b.OwnerId != from) return;
|
||||||
|
if (!IsValidPose(pos, rot)) { SetFree(b); return; }
|
||||||
b.Rb.transform.position = pos;
|
b.Rb.transform.position = pos;
|
||||||
b.Rb.transform.rotation = rot;
|
b.Rb.transform.rotation = rot;
|
||||||
b.Rb.isKinematic = b.WasKinematic;
|
b.Rb.isKinematic = b.WasKinematic;
|
||||||
@@ -457,6 +461,57 @@ namespace MSCO
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>Position innerhalb der Karte, Drehung gültig (gleiche Regel wie auf dem Server).</summary>
|
||||||
|
static bool IsValidPose(Vector3 p, Quaternion q)
|
||||||
|
{
|
||||||
|
if (float.IsNaN(p.x) || float.IsNaN(p.y) || float.IsNaN(p.z) || float.IsInfinity(p.x) || float.IsInfinity(p.y) || float.IsInfinity(p.z))
|
||||||
|
return false;
|
||||||
|
if (Mathf.Abs(p.x) > 6000f || Mathf.Abs(p.z) > 6000f || p.y < -200f || p.y > 3000f) return false;
|
||||||
|
float len = Mathf.Sqrt(q.x * q.x + q.y * q.y + q.z * q.z + q.w * q.w);
|
||||||
|
return len > 0.9f && len < 1.1f;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Liegt die Position im Boden, auf die Bodenoberfläche heben. Kurzer Strahl ab 2 m über dem Punkt
|
||||||
|
/// (nicht aufs Garagendach), die eigenen Teile des Fahrzeugs werden ignoriert.
|
||||||
|
/// </summary>
|
||||||
|
static Vector3 AboveGround(Vector3 pos, Transform ignore)
|
||||||
|
{
|
||||||
|
float ground = float.MinValue;
|
||||||
|
foreach (RaycastHit hit in Physics.RaycastAll(pos + Vector3.up * 2f, Vector3.down, 4f))
|
||||||
|
{
|
||||||
|
if (hit.collider.isTrigger || (ignore != null && hit.transform.IsChildOf(ignore))) continue;
|
||||||
|
if (hit.transform.root.name == "PLAYER") continue;
|
||||||
|
ground = Mathf.Max(ground, hit.point.y);
|
||||||
|
}
|
||||||
|
return ground > pos.y - 0.2f ? new Vector3(pos.x, ground + 0.6f, pos.z) : pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Admin: Fahrzeug (Wurzelname beginnt mit rootPrefix) an eine Position holen. Fährt es gerade ein
|
||||||
|
/// anderer Spieler, geht das nicht. Gibt einen Statustext zurück.
|
||||||
|
/// </summary>
|
||||||
|
public string BringVehicle(string rootPrefix, Vector3 pos, Quaternion rot)
|
||||||
|
{
|
||||||
|
foreach (SyncedBody b in byId.Values)
|
||||||
|
{
|
||||||
|
if (!b.IsVehicle || b.Rb == null || !b.RootName.StartsWith(rootPrefix)) continue;
|
||||||
|
if (IsRemoteOwned(b) && b.OwnerActive) return rootPrefix + " is being driven by another player";
|
||||||
|
Claim(b, false); // wir besitzen es jetzt -> Position geht an alle und an den Server
|
||||||
|
b.Rb.transform.position = AboveGround(pos, b.Rb.transform);
|
||||||
|
b.Rb.transform.rotation = rot;
|
||||||
|
if (!b.Rb.isKinematic)
|
||||||
|
{
|
||||||
|
b.Rb.velocity = Vector3.zero;
|
||||||
|
b.Rb.angularVelocity = Vector3.zero;
|
||||||
|
}
|
||||||
|
b.StillTime = 0f;
|
||||||
|
b.LastSentTime = -100f;
|
||||||
|
return rootPrefix + " is next to you";
|
||||||
|
}
|
||||||
|
return rootPrefix + " not found (mp vehicles shows all vehicles)";
|
||||||
|
}
|
||||||
|
|
||||||
static void WriteState(BinaryWriter w, SyncedBody b)
|
static void WriteState(BinaryWriter w, SyncedBody b)
|
||||||
{
|
{
|
||||||
Vector3 p = b.Rb.position;
|
Vector3 p = b.Rb.position;
|
||||||
|
|||||||
@@ -294,6 +294,8 @@ namespace MSCO
|
|||||||
if (GUILayout.Button("+10.000 MK", primary, GUILayout.Height(30))) mp.CheatMoney(10000f);
|
if (GUILayout.Button("+10.000 MK", primary, GUILayout.Height(30))) mp.CheatMoney(10000f);
|
||||||
GUILayout.Space(6);
|
GUILayout.Space(6);
|
||||||
if (GUILayout.Button("REFILL NEEDS", primary, GUILayout.Height(30))) mp.CheatNeeds();
|
if (GUILayout.Button("REFILL NEEDS", primary, GUILayout.Height(30))) mp.CheatNeeds();
|
||||||
|
GUILayout.Space(6);
|
||||||
|
if (GUILayout.Button("SATSUMA TO ME", primary, GUILayout.Height(30))) mp.BringCar();
|
||||||
GUILayout.EndHorizontal();
|
GUILayout.EndHorizontal();
|
||||||
GUILayout.Label("Cheats are announced in the chat. GOTO / BRING teleport you to a player or a player to you.", small);
|
GUILayout.Label("Cheats are announced in the chat. GOTO / BRING teleport you to a player or a player to you.", small);
|
||||||
GUILayout.Space(10);
|
GUILayout.Space(10);
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ Multiplayer-Mod für **My Summer Car** (MSCLoader). Spiel mit bis zu 3 Spielern
|
|||||||
- **T** – Chat
|
- **T** – Chat
|
||||||
- **F8** – MSCO-Konsole mit allen Befehlen
|
- **F8** – MSCO-Konsole mit allen Befehlen
|
||||||
- **/admin** – Admin-Menü für Server-Admins bzw. den P2P-Host: Spieler kicken/bannen, GOTO/BRING (teleportieren),
|
- **/admin** – Admin-Menü für Server-Admins bzw. den P2P-Host: Spieler kicken/bannen, GOTO/BRING (teleportieren),
|
||||||
Cheats (+1.000 / +10.000 MK, Bedürfnisse auffüllen) – Cheats werden im Chat angezeigt
|
Cheats (+1.000 / +10.000 MK, Bedürfnisse auffüllen, Satsuma zu sich holen) – Cheats werden im Chat angezeigt
|
||||||
- **P** – als Beifahrer ein-/aussteigen
|
- **P** – als Beifahrer ein-/aussteigen
|
||||||
- Alle Tasten sind in den MSCLoader-Einstellungen der Mod änderbar
|
- Alle Tasten sind in den MSCLoader-Einstellungen der Mod änderbar
|
||||||
- Discord Rich Presence („In the Menus“, Server, Spielerzahl)
|
- Discord Rich Presence („In the Menus“, Server, Spielerzahl)
|
||||||
|
|||||||
Reference in New Issue
Block a user