368 lines
8.3 KiB
C#
368 lines
8.3 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Reflection;
|
|
|
|
namespace IdleSpectator;
|
|
|
|
/// <summary>
|
|
/// Live-game relation resolver. Labels that claim a second party must resolve through here.
|
|
/// </summary>
|
|
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;
|
|
}
|
|
}
|
|
|
|
/// <summary>First living peer in the same family (not self).</summary>
|
|
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<Actor> 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<Actor> 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;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Resolve a related unit for pack/family labels. Prefers lover, then family peer, then child.
|
|
/// </summary>
|
|
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<Actor> 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;
|
|
}
|
|
}
|