using System; using System.Collections; using System.Collections.Generic; using System.Reflection; namespace IdleSpectator; /// /// Live-game relation resolver. Labels that claim a second party must resolve through here. /// public static class ActorRelation { public static Actor GetLover(Actor actor) { if (actor == null) { return null; } try { if (!actor.hasLover()) { return null; } } catch { // fall through to field/property } try { Actor lover = null; try { lover = actor.lover; } catch { lover = null; } if (lover == null) { lover = ReadActorMember(actor, "lover", "get_lover", "getLover"); } if (lover != null && lover.isAlive() && lover != actor) { return lover; } } catch { // ignore } return null; } public static bool TryGetLover(Actor actor, out Actor lover) { lover = GetLover(actor); return lover != null; } public static Actor GetBestFriend(Actor actor) { if (actor == null) { return null; } try { long id = 0; object data = actor.data; if (data != null) { object raw = data.GetType().GetProperty("best_friend_id")?.GetValue(data, null) ?? data.GetType().GetField("best_friend_id")?.GetValue(data); if (raw is long l) { id = l; } else if (raw is int i) { id = i; } } if (id == 0) { return null; } return FindUnitById(id); } catch { return null; } } /// First living peer in the same family (not self). public static Actor FirstFamilyPeer(Actor actor) { foreach (Actor peer in EnumerateFamilyMembers(actor, max: 8)) { if (peer != null && peer != actor && peer.isAlive()) { return peer; } } return null; } public static IEnumerable EnumerateFamilyMembers(Actor actor, int max = 12) { if (actor == null || max <= 0) { yield break; } object family = ReadMember(actor, "family", "get_family", "getFamily"); if (family == null) { yield break; } int yielded = 0; foreach (Actor member in EnumerateActorsFromMeta(family)) { if (member == null || !member.isAlive()) { continue; } yield return member; yielded++; if (yielded >= max) { yield break; } } } public static IEnumerable EnumerateChildren(Actor actor, int max = 12) { if (actor == null || max <= 0) { yield break; } IEnumerable children = null; try { MethodInfo getChildren = typeof(Actor).GetMethod("getChildren", Type.EmptyTypes); if (getChildren != null) { children = getChildren.Invoke(actor, null) as IEnumerable; } } catch { children = null; } if (children == null) { yield break; } int yielded = 0; foreach (object raw in children) { Actor child = raw as Actor ?? ResolveActor(raw); if (child == null || !child.isAlive()) { continue; } yield return child; yielded++; if (yielded >= max) { yield break; } } } /// /// Resolve a related unit for pack/family labels. Prefers lover, then family peer, then child. /// public static Actor ResolvePackRelated(Actor subject) { if (subject == null) { return null; } Actor lover = GetLover(subject); if (lover != null) { return lover; } Actor peer = FirstFamilyPeer(subject); if (peer != null) { return peer; } foreach (Actor child in EnumerateChildren(subject, max: 1)) { return child; } return null; } public static Actor ResolveActor(object raw) { if (raw == null) { return null; } if (raw is Actor actor) { return actor; } try { if (raw is BaseSimObject sim && sim.isActor()) { return sim.a; } } catch { // fall through } return ReadActorMember(raw, "actor", "Actor", "a", "getActor", "GetActor"); } private static IEnumerable EnumerateActorsFromMeta(object meta) { if (meta == null) { yield break; } // Common: units / members / getUnits / getSimpleList object list = ReadMember(meta, "units", "members", "getUnits", "getSimpleList", "getList"); if (list is IEnumerable enumerable) { foreach (object raw in enumerable) { Actor a = ResolveActor(raw); if (a != null) { yield return a; } } yield break; } Actor founder = ReadActorMember(meta, "founder", "creator", "leader", "getFounder"); if (founder != null) { yield return founder; } } private static Actor FindUnitById(long id) { if (id == 0 || World.world?.units == null) { return null; } try { foreach (Actor unit in World.world.units) { if (unit == null) { continue; } try { if (unit.getID() == id && unit.isAlive()) { return unit; } } catch { // skip } } } catch { // ignore } return null; } private static Actor ReadActorMember(object target, params string[] names) { object raw = ReadMember(target, names); return raw as Actor; } private static object ReadMember(object target, params string[] names) { if (target == null || names == null) { return null; } Type type = target as Type ?? target.GetType(); object instance = target is Type ? null : target; foreach (string name in names) { if (string.IsNullOrEmpty(name)) { continue; } try { PropertyInfo prop = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (prop != null) { return prop.GetValue(instance, null); } FieldInfo field = type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { return field.GetValue(instance); } MethodInfo method = type.GetMethod(name, Type.EmptyTypes); if (method != null) { return method.Invoke(instance, null); } } catch { // try next } } return null; } }