diff --git a/CHANGELOG.md b/CHANGELOG.md
index 08b8fbd..57ce7db 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -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**
diff --git a/Cheats.cs b/Cheats.cs
index 33b330d..78dfa25 100644
--- a/Cheats.cs
+++ b/Cheats.cs
@@ -53,8 +53,10 @@ namespace MSCO
};
///
- /// 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).
///
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();
foreach (Renderer r in root.GetComponentsInChildren(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 points, int axis)
+ {
+ var values = new List(points.Count);
+ foreach (Vector3 p in points) values.Add(p[axis]);
+ values.Sort();
+ return values[values.Count / 2];
+ }
+
+ /// Wurzelobjekt eines Ortes (nur oberste Ebene - "YARD" o.ä. gibt es auch als Unterobjekt).
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())
- 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;
}
/// Höchster fester Boden unter einem Punkt (ohne Trigger).
diff --git a/MSCO.cs b/MSCO.cs
index f195a48..427439e 100644
--- a/MSCO.cs
+++ b/MSCO.cs
@@ -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.
///
- public const string ModVersion = "2.10.4";
+ public const string ModVersion = "2.10.5";
///
/// Netzwerk-Version (die ersten beiden Stellen). Nur erhöhen, wenn sich das Protokoll inkompatibel ändert - dann
/// auch GameServer.ModVersion im Server.