worldbox-observer-mod/IdleSpectator/Events/Patches/PlotEventPatches.cs
DazedAnon 4b6c829bd0 feat(saga): ship adaptive dossier tabs with durable memory
- Add Dossier/Saga tabs, Design A panel, and hover preview with read-pause
- Persist observed saga facts across roster churn; pin subject without stale fallback
- Restore Saga after Open Lore; hide redundant Kind · Climax spine under matching tips
- Densify dossier header/chips and keep the cast rail as stable top chrome
- Extend harness coverage for hover restore, empty pick, Evidence, and Lore return
2026-07-22 03:27:46 -05:00

180 lines
5.1 KiB
C#

using HarmonyLib;
namespace IdleSpectator;
/// <summary>Plot lifecycle patches from mutation discovery.</summary>
[HarmonyPatch]
public static class PlotEventPatches
{
[HarmonyPatch(typeof(PlotManager), nameof(PlotManager.newPlot))]
[HarmonyPostfix]
public static void PostfixNewPlot(Plot __result)
{
if (__result == null)
{
return;
}
PlotInterestFeed.EmitFromPlot(__result, "new");
try
{
Actor author = __result.getAuthor();
if (author != null && author.isAlive())
{
string plotKey = __result.getID().ToString();
string assetId = ReadPlotAssetId(__result);
LifeSagaMemory.RecordPlot(author, plotKey, assetId, finished: false, provenance: "newPlot");
}
}
catch
{
// ignore
}
}
[HarmonyPatch(typeof(PlotManager), nameof(PlotManager.cancelPlot))]
[HarmonyPostfix]
public static void PostfixCancelPlot(Plot pPlot)
{
if (pPlot == null)
{
return;
}
PlotInterestFeed.EmitFromPlot(pPlot, "cancel");
}
[HarmonyPatch(typeof(Actor), nameof(Actor.setPlot))]
[HarmonyPostfix]
public static void PostfixSetPlot(Actor __instance, Plot pObject)
{
if (__instance == null || !__instance.isAlive() || pObject == null)
{
return;
}
PlotInterestFeed.EmitFromPlot(pObject, "join");
try
{
string plotKey = pObject.getID().ToString();
string assetId = ReadPlotAssetId(pObject);
LifeSagaMemory.RecordPlot(__instance, plotKey, assetId, finished: false, provenance: "setPlot");
}
catch
{
// ignore
}
}
[HarmonyPatch(typeof(Actor), nameof(Actor.leavePlot))]
[HarmonyPrefix]
public static void PrefixLeavePlot(Actor __instance, out string __state)
{
__state = "";
try
{
object plot = __instance?.GetType().GetField("plot")?.GetValue(__instance)
?? __instance?.GetType().GetProperty("plot")?.GetValue(__instance, null);
if (plot != null)
{
__state = ReadPlotAssetId(plot);
}
}
catch
{
__state = "";
}
}
[HarmonyPatch(typeof(Actor), nameof(Actor.leavePlot))]
[HarmonyPostfix]
public static void PostfixLeavePlot(Actor __instance, string __state)
{
if (__instance == null || !__instance.isAlive() || string.IsNullOrEmpty(__state))
{
return;
}
PlotInterestFeed.Emit(__state, __instance, "leave");
}
[HarmonyPatch(typeof(Plot), nameof(Plot.finishPlot))]
[HarmonyPostfix]
public static void PostfixFinishPlot(Plot __instance, PlotState pState, Actor pActor)
{
if (__instance == null || pState != PlotState.Finished)
{
return;
}
// Prefer finishing actor; fall back to plot author inside EmitFromPlot.
if (pActor != null && pActor.isAlive())
{
string assetId = ReadPlotAssetId(__instance);
if (string.IsNullOrEmpty(assetId))
{
PlotInterestFeed.EmitFromPlot(__instance, "complete");
return;
}
PlotInterestFeed.Emit(assetId, pActor, "complete");
try
{
string plotKey = __instance.getID().ToString();
LifeSagaMemory.RecordPlot(pActor, plotKey, assetId, finished: true, provenance: "finishPlot");
}
catch
{
// ignore
}
return;
}
PlotInterestFeed.EmitFromPlot(__instance, "complete");
try
{
Actor author = __instance.getAuthor();
if (author != null && author.isAlive())
{
string plotKey = __instance.getID().ToString();
LifeSagaMemory.RecordPlot(
author,
plotKey,
ReadPlotAssetId(__instance),
finished: true,
provenance: "finishPlot");
}
}
catch
{
// ignore
}
}
private static string ReadPlotAssetId(object plot)
{
try
{
object asset = plot.GetType().GetField("asset")?.GetValue(plot)
?? plot.GetType().GetProperty("asset")?.GetValue(plot, null)
?? plot.GetType().GetMethod("getAsset")?.Invoke(plot, null)
?? plot.GetType().GetField("plot_asset")?.GetValue(plot);
if (asset != null)
{
object id = asset.GetType().GetField("id")?.GetValue(asset)
?? asset.GetType().GetProperty("id")?.GetValue(asset, null);
if (id != null)
{
return id.ToString();
}
}
}
catch
{
// ignore
}
return "";
}
}