worldbox-observer-mod/IdleSpectator/TraitEventPatches.cs

204 lines
5.3 KiB
C#

using System;
using System.Collections.Generic;
using HarmonyLib;
using UnityEngine;
namespace IdleSpectator;
/// <summary>Actor trait add/remove interest patches from mutation discovery.</summary>
[HarmonyPatch]
public static class TraitEventPatches
{
/// <summary>
/// Birth/spawn endowment traits that are still worth a camera beat.
/// Routine species defaults (poisonous frog, bloodlust beetle, …) are not.
/// </summary>
private static readonly HashSet<string> LegendaryBirthTraits = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
"immortal",
"chosen_one",
"miracle_born",
"miracle_bearer",
"scar_of_divinity",
"giant",
"whirlwind",
"death_nuke",
"death_bomb",
"bomberman"
};
[HarmonyPatch(typeof(Actor), nameof(Actor.addTrait), new Type[] { typeof(string), typeof(bool) })]
[HarmonyPostfix]
public static void PostfixAddTraitString(Actor __instance, string pTraitID, bool __result)
{
if (!__result || __instance == null || !__instance.isAlive() || string.IsNullOrEmpty(pTraitID))
{
return;
}
Emit(pTraitID, __instance, gained: true);
}
[HarmonyPatch(typeof(Actor), nameof(Actor.addTrait), new Type[] { typeof(ActorTrait), typeof(bool) })]
[HarmonyPostfix]
public static void PostfixAddTraitAsset(Actor __instance, ActorTrait pTrait, bool __result)
{
if (!__result || __instance == null || !__instance.isAlive() || pTrait == null)
{
return;
}
string id = "";
try
{
id = pTrait.id ?? "";
}
catch
{
id = "";
}
if (string.IsNullOrEmpty(id))
{
return;
}
Emit(id, __instance, gained: true);
}
[HarmonyPatch(typeof(Actor), nameof(Actor.removeTrait), new Type[] { typeof(string) })]
[HarmonyPostfix]
public static void PostfixRemoveTraitString(Actor __instance, string pTraitID)
{
if (__instance == null || !__instance.isAlive() || string.IsNullOrEmpty(pTraitID))
{
return;
}
Emit(pTraitID, __instance, gained: false);
}
[HarmonyPatch(typeof(Actor), nameof(Actor.removeTrait), new Type[] { typeof(ActorTrait) })]
[HarmonyPostfix]
public static void PostfixRemoveTraitAsset(Actor __instance, ActorTrait pTrait, bool __result)
{
if (!__result || __instance == null || !__instance.isAlive() || pTrait == null)
{
return;
}
string id = "";
try
{
id = pTrait.id ?? "";
}
catch
{
id = "";
}
if (string.IsNullOrEmpty(id))
{
return;
}
Emit(id, __instance, gained: false);
}
private static void Emit(string traitId, Actor actor, bool gained)
{
if (AgentHarness.Busy)
{
return;
}
DiscreteEventEntry entry = TraitInterestCatalog.GetOrFallback(traitId);
if (!entry.CreatesInterest)
{
return;
}
// Quiet-world Ambient traits must not steal the camera.
if (entry.EventStrength < InterestScoringConfig.W.noticeScoreMin)
{
return;
}
// Spawn/birth endowment: only legendary rarities.
if (gained && IsSpawnTraitWindow(actor) && !LegendaryBirthTraits.Contains(entry.Id))
{
return;
}
float strength = entry.EventStrength;
if (!gained)
{
strength = Mathf.Min(strength, 45f);
// Trait loss is quieter unless it was dramatic.
if (entry.EventStrength < InterestScoringConfig.W.hotScoreMin)
{
return;
}
}
string label = EventReason.Trait(actor, entry.Id, gained);
string phase = gained ? "gains" : "loses";
string key = "trait:" + entry.Id + ":" + phase + ":" + EventFeedUtil.SafeId(actor);
EventFeedUtil.Register(
key,
"trait",
entry.Category,
label,
entry.Id,
strength,
actor.current_position,
actor);
}
/// <summary>True while traits are still being stamped onto a brand-new unit.</summary>
private static bool IsSpawnTraitWindow(Actor actor)
{
if (actor == null)
{
return true;
}
try
{
if (actor.isBaby())
{
return true;
}
}
catch
{
// ignore
}
try
{
double created = actor.data != null ? actor.data.created_time : 0;
if (created <= 0)
{
return false;
}
double now = 0;
if (World.world != null)
{
now = World.world.getCurWorldTime();
}
else if (MapBox.instance != null)
{
now = MapBox.instance.getCurWorldTime();
}
// Within ~2 world-years of creation = spawn endowment, not a mid-life gain.
return now > 0 && (now - created) < 2.0;
}
catch
{
return false;
}
}
}