254 lines
8.7 KiB
C#
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"));
|
|
}
|
|
}
|