Decompiled source of InscryptionWarning v0.2.1

plugins/InscryptionWarning.dll

Decompiled 3 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using InscryptionWarning.Patches;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("InscryptionWarning")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.2.1.0")]
[assembly: AssemblyInformationalVersion("0.2.1+34b502e7d78e490fd914d126c6c8849c0fb1bc61")]
[assembly: AssemblyProduct("InscryptionWarning")]
[assembly: AssemblyTitle("InscryptionWarning")]
[assembly: AssemblyVersion("0.2.1.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace InscryptionWarning
{
	[BepInPlugin("InscryptionWarning", "InscryptionWarning", "0.2.1")]
	public class Plugin : BaseUnityPlugin
	{
		internal static ManualLogSource Logger = new ManualLogSource("InscryptionWarning");

		public void Awake()
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			Logger = ((BaseUnityPlugin)this).Logger;
			Logger.LogInfo((object)"Plugin InscryptionWarning is loaded!");
			MapHintPatch.Init(((BaseUnityPlugin)this).Config);
			new Harmony("InscryptionWarning").PatchAll();
		}

		public void Update()
		{
			MapHintPatch.Tick();
		}
	}
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "InscryptionWarning";

		public const string PLUGIN_NAME = "InscryptionWarning";

		public const string PLUGIN_VERSION = "0.2.1";
	}
}
namespace InscryptionWarning.Patches
{
	internal sealed class SharedNodeMarker : MonoBehaviour
	{
	}
	internal static class MapHintPatch
	{
		internal enum CueColor
		{
			Gold,
			Red,
			Blue
		}

		internal enum CueStyle
		{
			Off,
			Diegetic,
			Icon,
			Debug
		}

		private static class EventStartLocator
		{
			private static readonly MethodInfo? ActiveSaveDataGetter = AccessTools.PropertyGetter(typeof(SaveManager), "ActiveSaveData");

			private static readonly FieldInfo? ConditionsField = AccessTools.Field(typeof(FollowupEventState), "conditions");

			private static readonly FieldInfo? ConditionDataField = AccessTools.Field(typeof(FollowupConditionState), "data");

			private static readonly FieldInfo? StartDistanceField = AccessTools.Field(typeof(FollowupConditionState), "startDistance");

			private static int resolvedFrame = -1;

			private static int? cached;

			internal static bool Available
			{
				get
				{
					if (ActiveSaveDataGetter != null && ConditionsField != null && ConditionDataField != null)
					{
						return StartDistanceField != null;
					}
					return false;
				}
			}

			internal static void LogAvailability()
			{
				if (!Available)
				{
					Plugin.Logger.LogError((object)("Cannot read the Inscryption event's start distance from the save — the game's internals have moved. No hint will be shown. Missing: " + $"SaveManager.ActiveSaveData={ActiveSaveDataGetter != null}, " + $"FollowupEventState.conditions={ConditionsField != null}, " + $"FollowupConditionState.data={ConditionDataField != null}, " + $"FollowupConditionState.startDistance={StartDistanceField != null}"));
				}
			}

			internal static int? Resolve(SaveManager saveManager)
			{
				if (resolvedFrame == Time.frameCount)
				{
					return cached;
				}
				resolvedFrame = Time.frameCount;
				int? num = Lookup(saveManager);
				if (num != cached)
				{
					Plugin.Logger.LogInfo((object)((!num.HasValue) ? $"Inscryption followup no longer active (was startDistance={cached})." : $"Inscryption followup active: startDistance={num}."));
					cached = num;
				}
				return cached;
			}

			private static int? Lookup(SaveManager saveManager)
			{
				if (!Available)
				{
					return null;
				}
				object? obj = ActiveSaveDataGetter.Invoke(saveManager, null);
				SaveData val = (SaveData)((obj is SaveData) ? obj : null);
				if (val == null)
				{
					return null;
				}
				List<FollowupEventState> activeFollowupEvents = val.GetActiveFollowupEvents();
				if (activeFollowupEvents == null)
				{
					return null;
				}
				int? num = null;
				foreach (FollowupEventState item in activeFollowupEvents)
				{
					if (item == null || !(ConditionsField.GetValue(item) is IEnumerable enumerable))
					{
						continue;
					}
					foreach (object item2 in enumerable)
					{
						if (item2 == null)
						{
							continue;
						}
						object? value = ConditionDataField.GetValue(item2);
						FollowupConditionData val2 = (FollowupConditionData)((value is FollowupConditionData) ? value : null);
						if (val2 != null && !(val2.ConditionName != "FollowupConditionInscryptionEvent"))
						{
							int num2 = (int)StartDistanceField.GetValue(item2);
							if (!num.HasValue || num2 > num)
							{
								num = num2;
							}
						}
					}
				}
				return num;
			}
		}

		[HarmonyPatch(typeof(MapSection), "SetUpNodesOnBranch")]
		private static class MarkSharedNodes
		{
			private static void Postfix(List<MapNodeUI> availableNodes, BranchSelection branch)
			{
				//IL_0000: Unknown result type (might be due to invalid IL or missing references)
				//IL_0002: Invalid comparison between Unknown and I4
				if ((int)branch != -1 || availableNodes == null)
				{
					return;
				}
				foreach (MapNodeUI availableNode in availableNodes)
				{
					if ((Object)(object)availableNode != (Object)null && (Object)(object)((Component)availableNode).GetComponent<SharedNodeMarker>() == (Object)null)
					{
						((Component)availableNode).gameObject.AddComponent<SharedNodeMarker>();
					}
				}
			}
		}

		[HarmonyPatch(typeof(MapNodeUI), "RefreshState")]
		private static class HintNode
		{
			private static void Postfix(MapNodeUI __instance, SaveManager saveManager)
			{
				lastSaveManager = saveManager;
				GameObject orCreateOverlay = GetOrCreateOverlay(__instance);
				if (!ShouldShowHint(__instance, saveManager))
				{
					orCreateOverlay.SetActive(false);
					return;
				}
				ApplyStyle(orCreateOverlay);
				orCreateOverlay.SetActive(true);
			}
		}

		private const string OverlayName = "InscryptionWarning_Hint";

		private const int GlowTextureSize = 128;

		private const float ConfigPollSeconds = 1f;

		private const float GlowCoreRadius = 0.2f;

		private const float DiegeticHaloScale = 1.5f;

		private static ConfigEntry<CueStyle> style = null;

		private static ConfigFile configFile = null;

		private static ConfigEntry<CueColor> cueColor = null;

		private static ConfigEntry<float> diegeticStrength = null;

		private static ConfigEntry<bool> verboseLogging = null;

		private static Sprite? softGlowSprite;

		private static readonly List<MapNodeUI> hintedNodes = new List<MapNodeUI>();

		private static SaveManager? lastSaveManager;

		private static DateTime configWriteTime;

		private static float nextConfigPoll;

		private static int loggedPlanFor = int.MinValue;

		public static void Init(ConfigFile config)
		{
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Expected O, but got Unknown
			configFile = config;
			style = config.Bind<CueStyle>("Hint", "CueStyle", CueStyle.Diegetic, "How obvious the Inscryption path hint should be on the map: Off (disabled), Diegetic (a soft halo behind the correct node), Icon (a small visible badge), Debug (an unmissable magenta marker, for diagnosing rather than playing).");
			cueColor = config.Bind<CueColor>("Hint", "CueColor", CueColor.Gold, "Colour of the Diegetic halo and the Icon badge: Gold (the original warm tint), Red, or Blue. Debug keeps its own magenta marker so it always stands out no matter what this is set to.");
			diegeticStrength = config.Bind<float>("Hint", "DiegeticStrength", 0.6f, new ConfigDescription("Opacity of the Diegetic halo across its core and at the node icon's edge, fading to nothing at the halo's rim. Other cue styles ignore this.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			verboseLogging = config.Bind<bool>("Diagnostics", "VerboseLogging", false, "Log a line for every map node the hint is evaluated against. Very chatty — one burst per map refresh — so it is meant for diagnosing a missing cue, not for normal play. State changes (event found/lost, code, branch plan) are logged either way.");
			configWriteTime = ReadConfigWriteTime();
			EventStartLocator.LogAvailability();
			SelfCheck();
			Plugin.Logger.LogInfo((object)($"MapHintPatch ready: CueStyle={style.Value}, CueColor={cueColor.Value}, " + $"DiegeticStrength={diegeticStrength.Value}, " + $"VerboseLogging={verboseLogging.Value}"));
			if (style.Value == CueStyle.Off)
			{
				Plugin.Logger.LogWarning((object)"CueStyle is Off — no hint will ever be drawn.");
			}
		}

		internal static void Tick()
		{
			if (!(Time.realtimeSinceStartup < nextConfigPoll))
			{
				nextConfigPoll = Time.realtimeSinceStartup + 1f;
				DateTime dateTime = ReadConfigWriteTime();
				if (!(dateTime == configWriteTime))
				{
					configWriteTime = dateTime;
					ReloadConfig();
				}
			}
		}

		private static DateTime ReadConfigWriteTime()
		{
			try
			{
				return File.GetLastWriteTimeUtc(configFile.ConfigFilePath);
			}
			catch (Exception ex)
			{
				Plugin.Logger.LogDebug((object)("Could not stat the config file: " + ex.Message));
				return configWriteTime;
			}
		}

		private static void ReloadConfig()
		{
			(CueStyle, CueColor, float, bool) tuple = (style.Value, cueColor.Value, diegeticStrength.Value, verboseLogging.Value);
			try
			{
				configFile.Reload();
			}
			catch (Exception ex)
			{
				Plugin.Logger.LogWarning((object)("Could not reload the config file: " + ex.Message));
				return;
			}
			(CueStyle, CueColor, float, bool) other = (style.Value, cueColor.Value, diegeticStrength.Value, verboseLogging.Value);
			if (!tuple.Equals(other))
			{
				Plugin.Logger.LogInfo((object)($"Config reloaded: CueStyle={style.Value}, CueColor={cueColor.Value}, " + $"DiegeticStrength={diegeticStrength.Value}, VerboseLogging={verboseLogging.Value}"));
				RefreshLiveOverlays();
			}
		}

		private static void RefreshLiveOverlays()
		{
			for (int num = hintedNodes.Count - 1; num >= 0; num--)
			{
				MapNodeUI val = hintedNodes[num];
				if ((Object)(object)val == (Object)null)
				{
					hintedNodes.RemoveAt(num);
				}
				else
				{
					Transform val2 = ((Component)val).transform.Find("InscryptionWarning_Hint");
					if ((Object)(object)val2 == (Object)null)
					{
						hintedNodes.RemoveAt(num);
					}
					else if ((Object)(object)lastSaveManager != (Object)null && ShouldShowHint(val, lastSaveManager))
					{
						ApplyStyle(((Component)val2).gameObject);
						((Component)val2).gameObject.SetActive(true);
					}
					else
					{
						((Component)val2).gameObject.SetActive(false);
					}
				}
			}
		}

		internal static BranchSelection? GetHintedBranch(int startDistance, int nodeDistance, int code)
		{
			int num = nodeDistance - (startDistance + 1);
			if (num < 0 || num > 2)
			{
				return null;
			}
			return (BranchSelection)((((code >> 2 - num) & 1) != 0) ? 1 : 0);
		}

		private static void SelfCheck()
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Invalid comparison between Unknown and I4
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Invalid comparison between Unknown and I4
			if ((int)GetHintedBranch(10, 11, 5).GetValueOrDefault() != 1 || GetHintedBranch(10, 12, 5) != (BranchSelection?)0 || (int)GetHintedBranch(10, 13, 5).GetValueOrDefault() != 1 || GetHintedBranch(10, 10, 5).HasValue || GetHintedBranch(10, 14, 5).HasValue)
			{
				throw new InvalidOperationException("MapHintPatch.GetHintedBranch self-check failed");
			}
		}

		private static void LogBranchPlan(int startDistance, int code)
		{
			int num = startDistance * 8 + code;
			if (loggedPlanFor != num)
			{
				loggedPlanFor = num;
				string text = string.Empty;
				for (int i = 1; i <= 3; i++)
				{
					BranchSelection? hintedBranch = GetHintedBranch(startDistance, startDistance + i, code);
					text += $" d{startDistance + i}={hintedBranch}";
				}
				Plugin.Logger.LogInfo((object)($"Inscryption path code={code} (binary {Convert.ToString(code, 2).PadLeft(3, '0')}), " + $"startDistance={startDistance}, hinting:{text}"));
			}
		}

		private static bool ShouldShowHint(MapNodeUI node, SaveManager saveManager)
		{
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0138: Unknown result type (might be due to invalid IL or missing references)
			//IL_0160: Unknown result type (might be due to invalid IL or missing references)
			//IL_0165: Unknown result type (might be due to invalid IL or missing references)
			if (style.Value == CueStyle.Off || (Object)(object)saveManager == (Object)null)
			{
				return false;
			}
			int? num = EventStartLocator.Resolve(saveManager);
			if (!num.HasValue)
			{
				return false;
			}
			if (saveManager.GetInscryptionEventSuccessfullyCompleted())
			{
				return false;
			}
			Location location = node.GetLocation();
			if (location == null)
			{
				if (verboseLogging.Value)
				{
					Plugin.Logger.LogDebug((object)("Node '" + ((Object)node).name + "' has no location — skipped."));
				}
				return false;
			}
			if ((Object)(object)((Component)node).GetComponent<SharedNodeMarker>() != (Object)null)
			{
				if (verboseLogging.Value)
				{
					Plugin.Logger.LogDebug((object)($"Node '{((Object)node).name}' distance={location.Distance} is shared/pact " + "(GetBranch() reports LeftBranch for these regardless) -> hide"));
				}
				return false;
			}
			int inscryptionEventPath = saveManager.GetInscryptionEventPath();
			LogBranchPlan(num.Value, inscryptionEventPath);
			BranchSelection? hintedBranch = GetHintedBranch(num.Value, location.Distance, inscryptionEventPath);
			bool flag = hintedBranch.HasValue && (BranchSelection?)node.GetBranch() == hintedBranch;
			if (verboseLogging.Value)
			{
				Plugin.Logger.LogDebug((object)($"Node '{((Object)node).name}' distance={location.Distance} branch={node.GetBranch()} " + "hinted=" + ((hintedBranch.HasValue ? ((object)hintedBranch.GetValueOrDefault()/*cast due to .constrained prefix*/).ToString() : null) ?? "none") + " -> " + (flag ? "SHOW" : "hide")));
			}
			return flag;
		}

		private static GameObject GetOrCreateOverlay(MapNodeUI node)
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Expected O, but got Unknown
			Transform val = ((Component)node).transform.Find("InscryptionWarning_Hint");
			if ((Object)(object)val != (Object)null)
			{
				return ((Component)val).gameObject;
			}
			GameObject val2 = new GameObject("InscryptionWarning_Hint", new Type[2]
			{
				typeof(RectTransform),
				typeof(Image)
			});
			val2.transform.SetParent(((Component)node).transform, false);
			((Graphic)val2.GetComponent<Image>()).raycastTarget = false;
			hintedNodes.Add(node);
			return val2;
		}

		private static Sprite GetSoftGlowSprite()
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Expected O, but got Unknown
			//IL_012b: Unknown result type (might be due to invalid IL or missing references)
			//IL_013a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)softGlowSprite != (Object)null)
			{
				return softGlowSprite;
			}
			Texture2D val = new Texture2D(128, 128, (TextureFormat)4, false)
			{
				name = "InscryptionWarning_Hint_Glow",
				wrapMode = (TextureWrapMode)1,
				filterMode = (FilterMode)1,
				hideFlags = (HideFlags)61
			};
			Color32[] array = (Color32[])(object)new Color32[16384];
			float num = 63.5f;
			for (int i = 0; i < 128; i++)
			{
				for (int j = 0; j < 128; j++)
				{
					float num2 = ((float)j - num) / num;
					float num3 = ((float)i - num) / num;
					float num4 = Mathf.Clamp01(Mathf.Sqrt(num2 * num2 + num3 * num3));
					float num5 = Mathf.InverseLerp(0.2f, 1f, num4);
					float num6 = Mathf.SmoothStep(0f, 1f, 1f - num5);
					array[i * 128 + j] = new Color32(byte.MaxValue, byte.MaxValue, byte.MaxValue, (byte)Mathf.RoundToInt(num6 * 255f));
				}
			}
			val.SetPixels32(array);
			val.Apply(false);
			softGlowSprite = Sprite.Create(val, new Rect(0f, 0f, 128f, 128f), new Vector2(0.5f, 0.5f), 100f, 0u, (SpriteMeshType)0);
			((Object)softGlowSprite).hideFlags = (HideFlags)61;
			return softGlowSprite;
		}

		private static Color ResolveCueColor(float alpha)
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			Color result = (Color)(cueColor.Value switch
			{
				CueColor.Gold => new Color(1f, 0.85f, 0.3f), 
				CueColor.Blue => new Color(0.25f, 0.55f, 1f), 
				_ => new Color(1f, 0.15f, 0.12f), 
			});
			result.a = alpha;
			return result;
		}

		private static void ApplyStyle(GameObject overlay)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Expected O, but got Unknown
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_0152: Unknown result type (might be due to invalid IL or missing references)
			//IL_015d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0168: Unknown result type (might be due to invalid IL or missing references)
			//IL_017d: Unknown result type (might be due to invalid IL or missing references)
			RectTransform val = (RectTransform)overlay.transform;
			Image component = overlay.GetComponent<Image>();
			switch (style.Value)
			{
			case CueStyle.Icon:
				overlay.transform.SetAsLastSibling();
				component.sprite = null;
				val.anchorMin = new Vector2(0.5f, 1f);
				val.anchorMax = new Vector2(0.5f, 1f);
				val.pivot = new Vector2(0.5f, 0.5f);
				val.sizeDelta = new Vector2(20f, 20f);
				val.anchoredPosition = Vector2.zero;
				((Graphic)component).color = ResolveCueColor(0.95f);
				break;
			case CueStyle.Debug:
				overlay.transform.SetAsLastSibling();
				component.sprite = null;
				val.anchorMin = Vector2.zero;
				val.anchorMax = Vector2.one;
				val.offsetMin = Vector2.zero;
				val.offsetMax = Vector2.zero;
				((Graphic)component).color = new Color(1f, 0.1f, 0.9f, 0.8f);
				break;
			default:
			{
				overlay.transform.SetAsFirstSibling();
				component.sprite = GetSoftGlowSprite();
				float num = 0.25f;
				val.anchorMin = new Vector2(0f - num, 0f - num);
				val.anchorMax = new Vector2(1f + num, 1f + num);
				val.offsetMin = Vector2.zero;
				val.offsetMax = Vector2.zero;
				((Graphic)component).color = ResolveCueColor(diegeticStrength.Value);
				break;
			}
			}
		}
	}
}