using System.Collections.Generic; using HutongGames.PlayMaker; using UnityEngine; namespace MSCO { /// /// Admin-Cheats für den eigenen Spieler (Geld, Bedürfnisse). Nur aus dem Admin-Menü erreichbar, also nur für /// Server-Admins bzw. den P2P-Host. Geld wird über MoneySync automatisch an alle weitergegeben. /// public static class Cheats { // Globale PlayMaker-Variablen des Spielers: Wert, auf den "auffüllen" sie setzt static readonly KeyValuePair[] Needs = { new KeyValuePair("PlayerHunger", 0f), new KeyValuePair("PlayerThirst", 0f), new KeyValuePair("PlayerStress", 0f), new KeyValuePair("PlayerUrine", 0f), new KeyValuePair("PlayerFatigue", 0f), new KeyValuePair("PlayerDirtiness", 0f), }; public static string AddMoney(float amount) { FsmFloat money = FsmVariables.GlobalVariables.FindFsmFloat("PlayerMoney"); if (money == null) return "Money not found (only works in the game)"; money.Value += amount; return "+" + amount.ToString("0") + " MK (now " + money.Value.ToString("0") + " MK)"; } public static string FillNeeds() { int done = 0; foreach (KeyValuePair need in Needs) { FsmFloat v = FsmVariables.GlobalVariables.FindFsmFloat(need.Key); if (v == null) continue; v.Value = need.Value; done++; } return done > 0 ? "Hunger, thirst, stress, fatigue, bladder and dirt reset" : "Player needs not found (only works in the game)"; } /// Teleport-Ziele im Admin-Menü: Anzeigename und Objekt im Spiel. public static readonly string[][] Places = { new[] { "Home", "YARD" }, new[] { "Shop (Teimo)", "STORE" }, new[] { "Repair shop", "REPAIRSHOP" }, new[] { "Inspection", "INSPECTION" }, new[] { "Peräjärvi", "PERAJARVI" }, new[] { "Landfill", "LANDFILL" }, new[] { "Dance hall", "DANCEHALL" }, new[] { "Cottage", "COTTAGE" }, new[] { "Cabin", "CABIN" }, new[] { "Rally", "RALLY" }, new[] { "Drag race", "DRAGRACE" }, new[] { "Soccer field", "SOCCER" }, new[] { "Kilju buyer", "KILJUGUY" }, new[] { "Theatre", "THEATRE" }, new[] { "Satsuma", "SATSUMA" }, }; /// /// 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) { position = Vector3.zero; GameObject root = FindRoot(objectName); if (root == null) return false; var centers = new List(); foreach (Renderer r in root.GetComponentsInChildren(true)) { 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 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), center + new Vector3(0f, 0f, oz), center + new Vector3(0f, 0f, -oz), center, }; foreach (Vector3 c in candidates) { Vector3 ground; if (Ground(c, cluster.max.y + 30f, out ground)) { position = ground; return true; } } 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 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) continue; if (candidate.activeInHierarchy) return candidate; if (found == null) found = candidate; } return found; } /// Höchster fester Boden unter einem Punkt (ohne Trigger). static bool Ground(Vector3 at, float top, out Vector3 ground) { ground = at; bool found = false; foreach (RaycastHit hit in Physics.RaycastAll(new Vector3(at.x, top, at.z), Vector3.down, 1000f)) { if (hit.collider == null || hit.collider.isTrigger || hit.normal.y < 0.6f) continue; if (!found || hit.point.y > ground.y) { ground = hit.point; found = true; } } return found; } /// Eigenen Spieler an eine Position setzen (etwas darüber, damit er nicht im Boden steckt). public static bool Teleport(Transform player, Vector3 position) { if (player == null) return false; player.position = position + Vector3.up * 0.3f; Rigidbody rb = player.GetComponent(); if (rb != null && !rb.isKinematic) rb.velocity = Vector3.zero; return true; } } }