Fix teleport to places landing in the wrong spot (2.10.5)
Use the median of a place's parts and the dense group around it instead of the bounds of everything (scattered/disabled children pulled the center into the forest); only take top-level objects as the place root. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,11 @@
|
||||
# Changelog
|
||||
|
||||
## 2.10.5
|
||||
|
||||
- **Teleport zu Orten** (Admin, Reiter TELEPORT) landet wieder am Ort: vorher zählten alle weit verstreuten Teile
|
||||
eines Ortes mit (Briefkasten, Wege, abgeschaltete Objekte) und man landete irgendwo im Wald; außerdem wurde
|
||||
manchmal ein gleichnamiges Unterobjekt statt des Ortes genommen
|
||||
|
||||
## 2.10.4
|
||||
|
||||
**Admin-Menü neu aufgebaut**
|
||||
|
||||
@@ -53,8 +53,10 @@ namespace MSCO
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Wohin bei einem Teleport-Ziel? Neben das Objekt (Mitte seiner sichtbaren Teile, ein Stück außerhalb, damit man
|
||||
/// nicht im Dach oder in einer Wand landet) und dort auf den Boden.
|
||||
/// Wohin bei einem Teleport-Ziel? Zu einem Ort gehören oft weit verstreute Teile (Briefkasten, Wege, Strecken,
|
||||
/// abgeschaltete Objekte) - deren gemeinsame Mitte liegt irgendwo im Wald. Deshalb: Median aller Teile (der Ort
|
||||
/// selbst, Ausreißer zählen nicht), darum die dichte Gruppe (60 m), daneben ein Stück außerhalb auf den Boden
|
||||
/// (nicht ins Dach oder in eine Wand).
|
||||
/// </summary>
|
||||
public static bool FindPlace(string objectName, out Vector3 position)
|
||||
{
|
||||
@@ -62,17 +64,28 @@ namespace MSCO
|
||||
GameObject root = FindRoot(objectName);
|
||||
if (root == null) return false;
|
||||
|
||||
Bounds bounds = new Bounds(root.transform.position, Vector3.zero);
|
||||
bool any = false;
|
||||
var centers = new List<Vector3>();
|
||||
foreach (Renderer r in root.GetComponentsInChildren<Renderer>(true))
|
||||
{
|
||||
if (r == null || r.bounds.size == Vector3.zero) continue;
|
||||
if (!any) { bounds = r.bounds; any = true; }
|
||||
else bounds.Encapsulate(r.bounds);
|
||||
if (r == null || r is ParticleRenderer || r is ParticleSystemRenderer) continue;
|
||||
Vector3 size = r.bounds.size;
|
||||
if (size == Vector3.zero || size.x > 150f || size.z > 150f) continue; // Gelände-/Wasserflächen
|
||||
centers.Add(r.bounds.center);
|
||||
}
|
||||
if (centers.Count == 0)
|
||||
{
|
||||
Vector3 ground0;
|
||||
position = Ground(root.transform.position, root.transform.position.y + 50f, out ground0) ? ground0 : root.transform.position;
|
||||
return true;
|
||||
}
|
||||
|
||||
Vector3 center = bounds.center;
|
||||
float ox = Mathf.Min(bounds.extents.x, 25f) + 3f, oz = Mathf.Min(bounds.extents.z, 25f) + 3f;
|
||||
Vector3 median = new Vector3(Median(centers, 0), Median(centers, 1), Median(centers, 2));
|
||||
Bounds cluster = new Bounds(median, Vector3.zero);
|
||||
foreach (Vector3 c in centers)
|
||||
if (new Vector2(c.x - median.x, c.z - median.z).sqrMagnitude < 60f * 60f) cluster.Encapsulate(c);
|
||||
|
||||
Vector3 center = cluster.center;
|
||||
float ox = Mathf.Min(cluster.extents.x, 20f) + 4f, oz = Mathf.Min(cluster.extents.z, 20f) + 4f;
|
||||
Vector3[] candidates =
|
||||
{
|
||||
center + new Vector3(ox, 0f, 0f), center + new Vector3(-ox, 0f, 0f),
|
||||
@@ -81,20 +94,32 @@ namespace MSCO
|
||||
foreach (Vector3 c in candidates)
|
||||
{
|
||||
Vector3 ground;
|
||||
if (Ground(c, bounds.max.y + 30f, out ground)) { position = ground; return true; }
|
||||
if (Ground(c, cluster.max.y + 30f, out ground)) { position = ground; return true; }
|
||||
}
|
||||
position = center;
|
||||
position = center + Vector3.up * 2f;
|
||||
return true;
|
||||
}
|
||||
|
||||
static float Median(List<Vector3> points, int axis)
|
||||
{
|
||||
var values = new List<float>(points.Count);
|
||||
foreach (Vector3 p in points) values.Add(p[axis]);
|
||||
values.Sort();
|
||||
return values[values.Count / 2];
|
||||
}
|
||||
|
||||
/// <summary>Wurzelobjekt eines Ortes (nur oberste Ebene - "YARD" o.ä. gibt es auch als Unterobjekt).</summary>
|
||||
static GameObject FindRoot(string name)
|
||||
{
|
||||
GameObject go = GameObject.Find(name);
|
||||
if (go != null) return go;
|
||||
// auch ausgeblendete Objekte (das Spiel schaltet entfernte Orte ab)
|
||||
GameObject found = null;
|
||||
// auch ausgeblendete Objekte (das Spiel schaltet entfernte Orte ab); aktive bevorzugt
|
||||
foreach (GameObject candidate in Resources.FindObjectsOfTypeAll<GameObject>())
|
||||
if (candidate.name == name && candidate.transform.parent == null && candidate.hideFlags == HideFlags.None) return candidate;
|
||||
return null;
|
||||
{
|
||||
if (candidate.name != name || candidate.transform.parent != null || candidate.hideFlags != HideFlags.None) continue;
|
||||
if (candidate.activeInHierarchy) return candidate;
|
||||
if (found == null) found = candidate;
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
/// <summary>Höchster fester Boden unter einem Punkt (ohne Trigger).</summary>
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace MSCO
|
||||
/// Version der Mod. Kleine Updates: nur die dritte Stelle erhöhen (2.9.1, 2.9.2 ...) - sie bleiben mit
|
||||
/// Servern und Mitspielern derselben ersten zwei Stellen kompatibel.
|
||||
/// </summary>
|
||||
public const string ModVersion = "2.10.4";
|
||||
public const string ModVersion = "2.10.5";
|
||||
/// <summary>
|
||||
/// Netzwerk-Version (die ersten beiden Stellen). Nur erhöhen, wenn sich das Protokoll inkompatibel ändert - dann
|
||||
/// auch GameServer.ModVersion im Server.
|
||||
|
||||
Reference in New Issue
Block a user