worldbox-observer-mod/IdleSpectator/Events/Patches/RelationshipEventPatches.cs

254 lines
8.7 KiB
C#

using System;
using ai.behaviours;
using HarmonyLib;
namespace IdleSpectator;
/// <summary>
/// Relationship lifecycle sensors. Thin Harmony only - outcomes confirmed via
/// <see cref="EventOutcome"/> / <see cref="EventObservation"/> before Emit.
/// Prefer state setters (<c>setLover</c>, <c>setParent*</c>, <c>newFamily</c>) over Beh Continue.
/// </summary>
[HarmonyPatch]
public static class RelationshipEventPatches
{
[HarmonyPatch(typeof(Actor), nameof(Actor.setLover))]
[HarmonyPostfix]
public static void PostfixSetLover(Actor __instance, Actor pActor)
{
if (!EventOutcome.IsLiving(__instance))
{
return;
}
// setLover is already the confirmed state transition.
if (pActor == null)
{
RelationshipInterestFeed.Emit("clear_lover", __instance, null);
return;
}
RelationshipInterestFeed.Emit("set_lover", __instance, pActor);
}
/// <summary>
/// Surviving partners clear dead lovers by field assignment in
/// <see cref="MapBox.checkEventUnitsDestroy"/>, which bypasses <see cref="Actor.setLover"/>.
/// Emit clear_lover for the living partner before those fields are nulled.
/// </summary>
[HarmonyPatch(typeof(MapBox), "checkEventUnitsDestroy")]
[HarmonyPrefix]
public static void PrefixDeadLoverCleanup(MapBox __instance)
{
try
{
if (__instance == null || __instance.units == null || !__instance.units.event_destroy)
{
return;
}
foreach (Actor unit in __instance.units)
{
try
{
if (unit == null || !unit.isAlive() || !unit.hasLover())
{
continue;
}
Actor lover = unit.lover;
if (lover == null)
{
continue;
}
bool loverAlive = false;
try
{
loverAlive = lover.isAlive();
}
catch
{
loverAlive = false;
}
if (loverAlive)
{
continue;
}
RelationshipInterestFeed.Emit("clear_lover", unit, null);
}
catch
{
// never break vanilla destroy cleanup
}
}
}
catch
{
// never break vanilla destroy cleanup
}
}
/// <summary>
/// Real parent/child link. Vanilla <c>addChild</c> attaches avatar components (Dragon, Boat), not kids.
/// </summary>
[HarmonyPatch(typeof(Actor), nameof(Actor.setParent1))]
[HarmonyPostfix]
public static void PostfixSetParent1(Actor __instance, Actor pParentActor, bool pIncreaseChildren)
{
EmitAddChild(pParentActor, __instance, pIncreaseChildren);
}
[HarmonyPatch(typeof(Actor), nameof(Actor.setParent2))]
[HarmonyPostfix]
public static void PostfixSetParent2(Actor __instance, Actor pActor, bool pIncreaseChildren)
{
EmitAddChild(pActor, __instance, pIncreaseChildren);
}
private static void EmitAddChild(Actor parent, Actor child, bool increaseChildren)
{
EventObservation.Confirm(
"setParent",
"add_child",
increaseChildren
&& EventOutcome.IsLiving(parent)
&& EventOutcome.IsLiving(child),
() => RelationshipInterestFeed.Emit("add_child", parent, child));
}
[HarmonyPatch(typeof(FamilyManager), nameof(FamilyManager.newFamily))]
[HarmonyPostfix]
public static void PostfixNewFamily(Family __result, Actor pActor)
{
Actor anchor = EventOutcome.IsLiving(pActor) ? pActor : null;
RelationshipInterestFeed.EmitFamily("new_family", __result, anchor);
}
[HarmonyPatch(typeof(FamilyManager), "removeObject", new Type[] { typeof(Family) })]
[HarmonyPostfix]
public static void PostfixRemoveFamily(Family pObject)
{
RelationshipInterestFeed.EmitFamily("family_removed", pObject, null);
}
[HarmonyPatch(typeof(ActorManager), "createBabyActorFromData")]
[HarmonyPostfix]
public static void PostfixCreateBaby(Actor __result)
{
EventObservation.Confirm(
"createBaby",
"baby_created",
EventOutcome.IsLiving(__result),
() => RelationshipInterestFeed.Emit("baby_created", __result, null));
}
/// <summary>
/// Seeking only. Beh Continue is not success - confirm still !hasLover.
/// Success is covered by set_lover / love status.
/// </summary>
[HarmonyPatch(typeof(BehFindLover), nameof(BehFindLover.execute))]
[HarmonyPostfix]
public static void PostfixFindLover(Actor pActor, BehResult __result)
{
EventObservation.Confirm(
"BehFindLover",
"find_lover",
EventOutcome.StillSeekingLover(pActor, __result),
() => RelationshipInterestFeed.Emit("find_lover", pActor, null));
}
[HarmonyPatch(typeof(BehFamilyGroupNew), nameof(BehFamilyGroupNew.execute))]
[HarmonyPrefix]
public static void PrefixFamilyNew(Actor pActor, out EventOutcome.ActorFlags __state)
{
__state = EventOutcome.Snapshot(pActor);
}
[HarmonyPatch(typeof(BehFamilyGroupNew), nameof(BehFamilyGroupNew.execute))]
[HarmonyPostfix]
public static void PostfixFamilyNew(Actor pActor, BehResult __result, EventOutcome.ActorFlags __state)
{
// Shares founding key with new_family when both fire.
EventObservation.ConfirmAfter(
"BehFamilyGroupNew",
"family_group_new",
__state,
pActor,
__result,
(before, actor, result) =>
EventOutcome.BehaviourRan(result) && EventOutcome.GainedFamily(before, actor),
() => RelationshipInterestFeed.Emit(
"family_group_new",
pActor,
ActorRelation.ResolvePackRelated(pActor)));
}
[HarmonyPatch(typeof(BehFamilyGroupJoin), nameof(BehFamilyGroupJoin.execute))]
[HarmonyPrefix]
public static void PrefixFamilyJoin(Actor pActor, out EventOutcome.ActorFlags __state)
{
__state = EventOutcome.Snapshot(pActor);
}
[HarmonyPatch(typeof(BehFamilyGroupJoin), nameof(BehFamilyGroupJoin.execute))]
[HarmonyPostfix]
public static void PostfixFamilyJoin(Actor pActor, BehResult __result, EventOutcome.ActorFlags __state)
{
// Vanilla returns Continue even when getNearbyFamily was null - require GainedFamily.
EventObservation.ConfirmAfter(
"BehFamilyGroupJoin",
"family_group_join",
__state,
pActor,
__result,
(before, actor, result) =>
EventOutcome.BehaviourRan(result) && EventOutcome.GainedFamily(before, actor),
() => RelationshipInterestFeed.Emit(
"family_group_join",
pActor,
ActorRelation.ResolvePackRelated(pActor)));
}
[HarmonyPatch(typeof(BehFamilyGroupLeave), nameof(BehFamilyGroupLeave.execute))]
[HarmonyPrefix]
public static void PrefixFamilyLeave(Actor pActor, out EventOutcome.ActorFlags __state)
{
__state = EventOutcome.Snapshot(pActor);
}
[HarmonyPatch(typeof(BehFamilyGroupLeave), nameof(BehFamilyGroupLeave.execute))]
[HarmonyPostfix]
public static void PostfixFamilyLeave(Actor pActor, BehResult __result, EventOutcome.ActorFlags __state)
{
EventObservation.ConfirmAfter(
"BehFamilyGroupLeave",
"family_group_leave",
__state,
pActor,
__result,
(before, actor, result) =>
EventOutcome.BehaviourRan(result) && EventOutcome.LostFamily(before, actor),
() => RelationshipInterestFeed.Emit(
"family_group_leave",
pActor,
ActorRelation.ResolvePackRelated(pActor)));
}
/// <summary>
/// Alpha succession is happiness-gated by emotions in vanilla. Patch the family API so
/// animals without emotions still get a Layer A / Activity-capable beat.
/// </summary>
[HarmonyPatch(typeof(Family), "setAlpha")]
[HarmonyPostfix]
public static void PostfixSetAlpha(Actor pActor, bool pNew)
{
EventObservation.Confirm(
"Family.setAlpha",
"become_alpha",
pNew && EventOutcome.IsLiving(pActor),
() => InterestFeeds.EmitAlpha(pActor, "family_set_alpha"));
}
}