worldbox-observer-mod/IdleSpectator/Events/Feeds/PlotInterestFeed.cs
2026-07-17 01:54:59 -05:00

297 lines
8.6 KiB
C#

using System;
using System.Collections;
using System.Reflection;
using UnityEngine;
namespace IdleSpectator;
/// <summary>
/// Registers plot interest from Harmony hooks and World.plots polling.
/// Active plots with a target kingdom use sticky PlotCell tips; begin/end stay FixedDwell.
/// </summary>
public static class PlotInterestFeed
{
public static void Emit(string plotAssetId, Actor subject, string phase)
{
if (!AgentHarness.LiveFeedsAllowed)
{
return;
}
DiscreteEventEntry entry = EventCatalog.Plot.GetOrFallback(plotAssetId);
if (!EventCatalog.IsCameraWorthy(entry))
{
InterestDropLog.Record("createsInterest=false", "plot");
return;
}
Actor follow = subject;
if (follow == null || !follow.isAlive())
{
// Plot without a living author: skip rather than invent a stranger.
return;
}
string label = EventReason.Plot(follow, entry.LabelTemplate, phase, entry.Id);
string key = "plot:" + entry.Id + ":" + (phase ?? "active") + ":" + EventFeedUtil.SafeId(follow);
EventFeedUtil.Register(
key,
"plot",
entry.Category,
label,
entry.Id,
entry.EventStrength,
follow.current_position,
follow);
}
public static void EmitFromPlot(object plot, string phase)
{
if (plot == null || !AgentHarness.LiveFeedsAllowed)
{
return;
}
TryRegisterPlot(plot, phase ?? "active");
}
public static void Tick()
{
if (!AgentHarness.LiveFeedsAllowed)
{
return;
}
IEnumerable plots = ResolvePlotsEnumerable();
if (plots == null)
{
return;
}
int registered = 0;
foreach (object plot in plots)
{
if (plot == null || registered >= 8)
{
break;
}
if (TryRegisterPlot(plot, "active"))
{
registered++;
}
}
}
private static bool TryRegisterPlot(object plot, string phase)
{
string assetId = ReadPlotAssetId(plot);
DiscreteEventEntry entry = EventCatalog.Plot.GetOrFallback(assetId);
if (!EventCatalog.IsCameraWorthy(entry))
{
InterestDropLog.Record("createsInterest=false", "plot:" + (assetId ?? ""));
return false;
}
string phaseKey = string.IsNullOrEmpty(phase) ? "active" : phase;
LiveEnsemble ensemble = null;
if (plot is Plot typedPlot && phaseKey == "active")
{
LiveEnsemble.TryBuildPlot(typedPlot, out ensemble);
}
Actor follow = ensemble?.Focus ?? ReadPlotAuthor(plot);
Actor related = ensemble?.Related;
if (follow == null || !follow.isAlive())
{
return false;
}
Vector3 position = Vector3.zero;
try
{
position = follow.current_position;
}
catch
{
position = Vector3.zero;
}
string plotId = "";
if (plot is Plot pId)
{
try
{
plotId = pId.getID().ToString();
}
catch
{
plotId = "";
}
}
string key = "plot:" + phaseKey + ":" + (plotId ?? "") + ":" + (entry.Id ?? assetId ?? "plot")
+ ":" + EventFeedUtil.SafeId(follow);
string label;
InterestCompletionKind completion = InterestCompletionKind.FixedDwell;
float minWatch = 6f;
float maxWatch = 28f;
string registerAsset = entry.Id;
float strength = entry.EventStrength;
if (ensemble != null && phaseKey == "active")
{
label = EventReason.PlotCell(ensemble);
if (string.IsNullOrEmpty(label))
{
label = EventReason.Ensemble(ensemble);
}
completion = InterestCompletionKind.PlotActive;
registerAsset = "live_plot";
minWatch = 8f;
maxWatch = 40f;
strength = Mathf.Max(strength, 86f);
}
else
{
label = EventReason.Plot(follow, entry.LabelTemplate, phaseKey, entry.Id);
}
InterestCandidate registered = EventFeedUtil.Register(
key,
"plot",
entry.Category,
label,
registerAsset,
strength,
position,
follow,
related: related,
minWatch: minWatch,
maxWatch: maxWatch,
completion: completion);
if (registered == null)
{
return false;
}
if (ensemble != null)
{
registered.CorrelationKey = key;
StickyScoreboard.TryLockFromEnsemble(registered.Sticky, ensemble);
StickyScoreboard.Refresh(
registered,
ensemble,
position,
StickyScoreboard.PlotTheaterRadius);
registered.Label = EventReason.PlotCell(ensemble);
if (string.IsNullOrEmpty(registered.Label))
{
registered.Label = label;
}
registered.ParticipantCount = ensemble.ParticipantCount;
registered.AssetId = "live_plot";
InterestScoring.ScoreCheap(registered);
}
return true;
}
private static IEnumerable ResolvePlotsEnumerable()
{
try
{
object world = World.world;
if (world == null)
{
return null;
}
object plots = world.GetType().GetField("plots")?.GetValue(world)
?? world.GetType().GetProperty("plots")?.GetValue(world, null);
if (plots is IEnumerable direct)
{
return direct;
}
object manager = world.GetType().GetField("plots_manager")?.GetValue(world)
?? world.GetType().GetProperty("plots_manager")?.GetValue(world, null)
?? typeof(PlotManager);
if (manager is Type)
{
manager = null;
}
object mapBox = MapBox.instance;
if (manager == null && mapBox != null)
{
manager = mapBox.GetType().GetField("plots")?.GetValue(mapBox)
?? mapBox.GetType().GetProperty("plots")?.GetValue(mapBox, null);
}
if (manager == null)
{
return null;
}
MethodInfo getAll = manager.GetType().GetMethod("getSimpleList")
?? manager.GetType().GetMethod("getList")
?? manager.GetType().GetMethod("getPlots");
if (getAll != null)
{
return getAll.Invoke(manager, null) as IEnumerable;
}
return manager as IEnumerable;
}
catch
{
return null;
}
}
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);
if (asset != null)
{
object id = asset.GetType().GetField("id")?.GetValue(asset)
?? asset.GetType().GetProperty("id")?.GetValue(asset, null);
if (id != null)
{
return id.ToString();
}
}
object typeId = plot.GetType().GetField("type")?.GetValue(plot)
?? plot.GetType().GetProperty("type")?.GetValue(plot, null);
return typeId?.ToString() ?? "unknown";
}
catch
{
return "unknown";
}
}
private static Actor ReadPlotAuthor(object plot)
{
try
{
object author = plot.GetType().GetField("author")?.GetValue(plot)
?? plot.GetType().GetProperty("author")?.GetValue(plot, null)
?? plot.GetType().GetMethod("getAuthor")?.Invoke(plot, null)
?? plot.GetType().GetField("founder")?.GetValue(plot);
return author as Actor;
}
catch
{
return null;
}
}
}