- Keep LifeSagaSession across Clear; wipe on map load/clear with bind key - Soft-bias Prefer/cross-MC/cast and prefer roster MCs in aftermath/recover/park - Resolve war/plot principals plus discrete/WorldLog labels from live libraries - Amortize roster world scans, gate Relayout, and add hitch probe/save-slot helpers
167 lines
4.6 KiB
C#
167 lines
4.6 KiB
C#
using System;
|
|
using System.IO;
|
|
using HarmonyLib;
|
|
using NeoModLoader.api;
|
|
using NeoModLoader.services;
|
|
using UnityEngine;
|
|
|
|
namespace IdleSpectator;
|
|
|
|
/// <summary>
|
|
/// Idle Spectator entry: I key AFK camera director over WorldLog events.
|
|
/// </summary>
|
|
public class ModClass : MonoBehaviour, IMod, IConfigurable, ILocalizable
|
|
{
|
|
private ModDeclare _declare;
|
|
private GameObject _gameObject;
|
|
private Harmony _harmony;
|
|
private ModConfig _config;
|
|
|
|
/// <summary>Absolute path to this mod's folder (parent of mod.json).</summary>
|
|
public static string ModFolder { get; private set; }
|
|
|
|
public static ModClass Instance { get; private set; }
|
|
|
|
public ModDeclare GetDeclaration()
|
|
{
|
|
return _declare;
|
|
}
|
|
|
|
public GameObject GetGameObject()
|
|
{
|
|
return _gameObject;
|
|
}
|
|
|
|
public string GetUrl()
|
|
{
|
|
return "";
|
|
}
|
|
|
|
public ModConfig GetConfig()
|
|
{
|
|
return _config;
|
|
}
|
|
|
|
public string GetLocaleFilesDirectory(ModDeclare pModDeclare)
|
|
{
|
|
return Path.Combine(pModDeclare.FolderPath, "Locales");
|
|
}
|
|
|
|
public void OnLoad(ModDeclare pModDecl, GameObject pGameObject)
|
|
{
|
|
Instance = this;
|
|
_declare = pModDecl;
|
|
_gameObject = pGameObject;
|
|
ModFolder = pModDecl.FolderPath;
|
|
_config = ModSettings.Load(pModDecl);
|
|
InterestScoringConfig.LoadFromModFolder(ModFolder);
|
|
EventCatalogConfig.LoadFromModFolder(ModFolder);
|
|
TryArmAutoloadSlot(pModDecl);
|
|
|
|
_harmony = new Harmony(pModDecl.UID);
|
|
ApplyHarmonyPatches(pModDecl.Name);
|
|
LogService.LogInfo($"[{pModDecl.Name}]: Harmony patches applied");
|
|
LogService.LogInfo($"[{pModDecl.Name}]: Press I for Idle Spectator, L for Lore");
|
|
Chronicle.EnsureWindow();
|
|
}
|
|
|
|
private void ApplyHarmonyPatches(string modName)
|
|
{
|
|
Exception firstFailure = null;
|
|
foreach (Type type in typeof(ModClass).Assembly.GetTypes())
|
|
{
|
|
try
|
|
{
|
|
new PatchClassProcessor(_harmony, type).Patch();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
LogService.LogError($"[{modName}]: Harmony failed on {type.FullName}: {ex}");
|
|
if (ex.InnerException != null)
|
|
{
|
|
LogService.LogError($"[{modName}]: Inner: {ex.InnerException}");
|
|
}
|
|
|
|
if (firstFailure == null)
|
|
{
|
|
firstFailure = ex;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (firstFailure != null)
|
|
{
|
|
throw firstFailure;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Dev/agent helper: <c>.harness/autoload_slot</c> containing a slot number (1-5)
|
|
/// sets Config.load_save_on_start so Box of Magic (slot 2) can come up after reboot.
|
|
/// </summary>
|
|
private static void TryArmAutoloadSlot(ModDeclare declare)
|
|
{
|
|
try
|
|
{
|
|
string path = Path.Combine(declare.FolderPath, ".harness", "autoload_slot");
|
|
if (!File.Exists(path))
|
|
{
|
|
return;
|
|
}
|
|
|
|
string raw = File.ReadAllText(path).Trim();
|
|
if (!int.TryParse(raw, out int slot) || slot < 1 || slot > 5)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var slotFi = AccessTools.Field(typeof(Config), "load_save_on_start_slot");
|
|
var onFi = AccessTools.Field(typeof(Config), "load_save_on_start");
|
|
if (slotFi == null || onFi == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
slotFi.SetValue(null, slot);
|
|
onFi.SetValue(null, true);
|
|
LogService.LogInfo($"[{declare.Name}]: autoload_slot armed slot={slot}");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
LogService.LogInfo($"[{declare.Name}]: autoload_slot failed: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
AgentHarness.Update();
|
|
SpectatorMode.PollInput();
|
|
Chronicle.PollInput();
|
|
|
|
IdleHitchProbe.BeginSlice();
|
|
InterestDirector.Update();
|
|
IdleHitchProbe.EndDirector();
|
|
|
|
IdleHitchProbe.BeginSlice();
|
|
if (!AgentHarness.FreezeDirector)
|
|
{
|
|
SpeciesDiscovery.Update();
|
|
}
|
|
|
|
IdleHitchProbe.EndDiscovery();
|
|
|
|
IdleHitchProbe.BeginSlice();
|
|
WatchCaption.Update();
|
|
IdleHitchProbe.EndCaption();
|
|
|
|
IdleHitchProbe.BeginSlice();
|
|
Chronicle.Update();
|
|
GraveMarkers.Update();
|
|
InspectUi.Update();
|
|
FocusRelationshipArrows.PinFocusActor();
|
|
StateProbe.Update();
|
|
AutoTest.Update();
|
|
IdleHitchProbe.EndOther();
|
|
IdleHitchProbe.AfterFrame();
|
|
}
|
|
}
|