using UnityEngine; namespace IdleSpectator; /// Registers relationship lifecycle interest candidates under the ownership contract. public static class RelationshipInterestFeed { public static void Emit(string eventId, Actor subject, Actor related, string labelOverride = null) { if (AgentHarness.Busy || subject == null || !subject.isAlive()) { return; } DiscreteEventEntry entry = RelationshipEventCatalog.GetOrFallback(eventId); if (!entry.CreatesInterest) { return; } Actor relatedAlive = related != null && related.isAlive() ? related : null; // Pack / join labels claim a second party - resolve live related or drop the dishonest claim. bool claimsPack = eventId == "family_group_new" || eventId == "family_group_join" || eventId == "family_group_leave" || eventId == "set_lover" || eventId == "add_child"; if (claimsPack && relatedAlive == null) { relatedAlive = ActorRelation.ResolvePackRelated(subject); } if (claimsPack && relatedAlive == null) { // Honest Ambient: subject only, no pack/lover claim in the label. string soloLabel = SoloLabel(eventId, EventFeedUtil.SafeName(subject)); string soloKey = "rel:" + entry.Id + ":" + EventFeedUtil.SafeId(subject) + ":solo"; EventFeedUtil.Register( soloKey, "relationship", entry.Category, soloLabel, entry.Id, Mathf.Min(entry.EventStrength, 40f), InterestOwnership.Ambient, subject.current_position, subject); return; } string a = EventFeedUtil.SafeName(subject); string b = EventFeedUtil.SafeName(relatedAlive); string label = string.IsNullOrEmpty(labelOverride) ? entry.MakeLabel(a, b) : labelOverride; string key = "rel:" + entry.Id + ":" + EventFeedUtil.SafeId(subject) + ":" + EventFeedUtil.SafeId(relatedAlive); EventFeedUtil.Register( key, "relationship", entry.Category, label, entry.Id, entry.EventStrength, entry.OwnsCamera, subject.current_position, subject, related: relatedAlive); } public static void EmitFamily(string eventId, object family, Actor anchor) { if (AgentHarness.Busy) { return; } DiscreteEventEntry entry = RelationshipEventCatalog.GetOrFallback(eventId); Actor follow = anchor != null && anchor.isAlive() ? anchor : null; if (follow == null) { follow = FirstFamilyMember(family); } if (follow == null) { // No owned member - skip rather than invent a stranger. return; } Actor related = ActorRelation.FirstFamilyPeer(follow); string name = ""; try { name = family?.GetType().GetMethod("getName")?.Invoke(family, null) as string ?? family?.GetType().GetProperty("name")?.GetValue(family, null) as string ?? ""; } catch { // ignore } if (string.IsNullOrEmpty(name)) { name = EventFeedUtil.SafeName(follow); } string key = "rel:" + entry.Id + ":" + EventFeedUtil.SafeId(follow) + ":" + (name ?? "family"); EventFeedUtil.Register( key, "relationship", entry.Category, entry.MakeLabel(name, EventFeedUtil.SafeName(related)), entry.Id, entry.EventStrength, entry.OwnsCamera, follow.current_position, follow, related: related); } private static string SoloLabel(string eventId, string name) { switch (eventId) { case "family_group_join": return "Gathers with kin: " + name; case "family_group_new": return "Starts a kin group: " + name; case "family_group_leave": return "Leaves kin: " + name; case "set_lover": return "Seeking a bond: " + name; case "add_child": return "Family grows: " + name; default: return name; } } private static Actor FirstFamilyMember(object family) { if (family == null) { return null; } try { foreach (Actor member in ActorRelation.EnumerateFamilyMembers( ActorRelation.ResolveActor( family.GetType().GetField("founder")?.GetValue(family) ?? family.GetType().GetProperty("founder")?.GetValue(family, null)) ?? null, max: 1)) { return member; } } catch { // fall through } // Direct units list on the family meta object. try { object list = family.GetType().GetField("units")?.GetValue(family) ?? family.GetType().GetProperty("units")?.GetValue(family, null) ?? family.GetType().GetMethod("getUnits")?.Invoke(family, null) ?? family.GetType().GetMethod("getSimpleList")?.Invoke(family, null); if (list is System.Collections.IEnumerable enumerable) { foreach (object raw in enumerable) { Actor a = ActorRelation.ResolveActor(raw); if (a != null && a.isAlive()) { return a; } } } Actor founder = family.GetType().GetField("founder")?.GetValue(family) as Actor ?? family.GetType().GetProperty("founder")?.GetValue(family, null) as Actor; if (founder != null && founder.isAlive()) { return founder; } } catch { // ignore } return null; } }