worldbox-observer-mod/IdleSpectator/RelationshipInterestFeed.cs
2026-07-15 22:16:24 -05:00

197 lines
6.4 KiB
C#

using UnityEngine;
namespace IdleSpectator;
/// <summary>Registers relationship lifecycle interest candidates under the ownership contract.</summary>
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;
}
}