Decompiled source of HighContrastEnemiesFixed v1.1.0

HighContrastEnemiesFixed.dll

Decompiled a week ago
using System;
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.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using Microsoft.CodeAnalysis;
using RiskOfOptions;
using RiskOfOptions.OptionConfigs;
using RiskOfOptions.Options;
using RoR2;
using UnityEngine;
using UnityEngine.SceneManagement;

[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("HighContrastEnemiesFixed")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.1.0.0")]
[assembly: AssemblyInformationalVersion("1.1.0")]
[assembly: AssemblyProduct("HighContrastEnemiesFixed")]
[assembly: AssemblyTitle("HighContrastEnemiesFixed")]
[assembly: AssemblyVersion("1.1.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[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 HighContrastEnemiesFixed
{
	[BepInPlugin("HighContrastEnemiesFixed", "HighContrastEnemiesFixed", "1.1.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public sealed class HighContrastEnemiesPlugin : BaseUnityPlugin
	{
		public const string PluginGUID = "HighContrastEnemiesFixed";

		public const string PluginName = "HighContrastEnemiesFixed";

		public const string PluginVersion = "1.1.0";

		private OutlineSettings settings;

		private bool drawerInstalled;

		private DateTime configFileTime;

		private void Awake()
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			ConfigFile config = ((BaseUnityPlugin)this).Config;
			ManualLogSource logger = ((BaseUnityPlugin)this).Logger;
			Scene activeScene = SceneManager.GetActiveScene();
			settings = new OutlineSettings(config, logger, ((Scene)(ref activeScene)).name);
			configFileTime = GetConfigFileTime();
			((BaseUnityPlugin)this).Config.SettingChanged += OnSettingChanged;
			SceneManager.activeSceneChanged += OnActiveSceneChanged;
			try
			{
				drawerInstalled = OutlineDrawer.Install(settings, ((BaseUnityPlugin)this).Logger, (Object)(object)this);
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Couldn't start the outline drawer; this game version is probably too different from the one this mod was written for. Please share this error:\n" + ex));
			}
			if (drawerInstalled)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)("Enemy outlines are ready. Settings file: " + ((BaseUnityPlugin)this).Config.ConfigFilePath));
			}
			AddInGameMenu();
		}

		private void AddInGameMenu()
		{
			if (!RiskOfOptionsMenu.IsInstalled)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)"Risk of Options isn't installed, so there's no in-game settings page; the config file works as usual.");
				return;
			}
			try
			{
				RiskOfOptionsMenu.Register(settings);
				((BaseUnityPlugin)this).Logger.LogInfo((object)"Added the in-game settings page (Settings > Mod Options).");
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Couldn't add the in-game settings page; the config file still works. Details: " + ex));
			}
		}

		private void Update()
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			if (settings == null)
			{
				return;
			}
			KeyboardShortcut value = settings.ToggleKey.Value;
			if (((KeyboardShortcut)(ref value)).IsDown())
			{
				settings.Enabled.Value = !settings.Enabled.Value;
				string text = (settings.Enabled.Value ? "on" : "off");
				((BaseUnityPlugin)this).Logger.LogInfo((object)("Enemy outlines " + text));
				if (drawerInstalled)
				{
					OutlineDrawer.ShowChatMessage("High contrast outlines: " + text);
				}
			}
		}

		private void OnDestroy()
		{
			((BaseUnityPlugin)this).Config.SettingChanged -= OnSettingChanged;
			SceneManager.activeSceneChanged -= OnActiveSceneChanged;
			if (drawerInstalled)
			{
				OutlineDrawer.Uninstall();
			}
		}

		private void OnSettingChanged(object sender, SettingChangedEventArgs e)
		{
			settings.Rebuild();
			configFileTime = GetConfigFileTime();
			if (drawerInstalled)
			{
				OutlineDrawer.Refresh();
			}
		}

		private void OnActiveSceneChanged(Scene previous, Scene next)
		{
			ReloadConfigIfEditedOnDisk();
			settings.SetScene(((Scene)(ref next)).name);
		}

		private void ReloadConfigIfEditedOnDisk()
		{
			try
			{
				if (!(GetConfigFileTime() == configFileTime))
				{
					((BaseUnityPlugin)this).Config.Reload();
					settings.Rebuild();
					configFileTime = GetConfigFileTime();
					if (drawerInstalled)
					{
						OutlineDrawer.Refresh();
					}
					((BaseUnityPlugin)this).Logger.LogInfo((object)"The settings file was edited; reloaded it.");
				}
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Couldn't reload the settings file: " + ex.Message));
			}
		}

		private DateTime GetConfigFileTime()
		{
			string configFilePath = ((BaseUnityPlugin)this).Config.ConfigFilePath;
			if (!File.Exists(configFilePath))
			{
				return DateTime.MinValue;
			}
			return File.GetLastWriteTimeUtc(configFilePath);
		}
	}
	internal static class OutlineDrawer
	{
		private const int MaxFailedFrames = 600;

		private static readonly Dictionary<int, EnemyRule> rulesByBodyIndex = new Dictionary<int, EnemyRule>();

		private static readonly HashSet<string> loggedBodyNames = new HashSet<string>();

		private static OutlineSettings settings;

		private static ManualLogSource log;

		private static Object requester;

		private static bool installed;

		private static bool requested;

		private static bool requestApiMissing;

		private static bool disabled;

		private static bool viewerLookupBroken;

		private static bool bodyColorBroken;

		private static bool loggedDrawError;

		private static bool loggedFirstDraw;

		private static int failedFrames;

		private static int rulesVersion = -1;

		public static bool Install(OutlineSettings settings, ManualLogSource log, Object requester)
		{
			OutlineDrawer.settings = settings;
			OutlineDrawer.log = log;
			OutlineDrawer.requester = requester;
			try
			{
				Subscribe();
			}
			catch (Exception ex)
			{
				log.LogError((object)("Couldn't find the game's outline effect (RoR2.OutlineHighlight); a game update must have changed it, so there are no outlines this session. Please share this error:\n" + ex));
				return false;
			}
			installed = true;
			Refresh();
			return true;
		}

		public static void Uninstall()
		{
			if (installed)
			{
				SetRequest(want: false);
				try
				{
					Unsubscribe();
				}
				catch (Exception)
				{
				}
				installed = false;
			}
		}

		public static void Refresh()
		{
			SetRequest(installed && !disabled && settings.Enabled.Value);
		}

		public static void ShowChatMessage(string message)
		{
			try
			{
				AddChatMessage(message);
			}
			catch (Exception)
			{
			}
		}

		private static void SetRequest(bool want)
		{
			if (want == requested)
			{
				return;
			}
			requested = want;
			if (requestApiMissing)
			{
				return;
			}
			try
			{
				if (want)
				{
					AddRequest();
				}
				else
				{
					ReleaseRequest();
				}
			}
			catch (Exception ex)
			{
				requestApiMissing = true;
				log.LogWarning((object)("Couldn't ask the game's outline effect to stay on (OutlineHighlight.AddPreRenderHighlightRequest). Outlines may only show while you look at a chest or a ping. Details: " + ex.Message));
			}
		}

		private static void OnPreRenderOutlineHighlight(OutlineHighlight outlineHighlight)
		{
			if (disabled || (Object)(object)outlineHighlight == (Object)null || !settings.Enabled.Value)
			{
				return;
			}
			try
			{
				Draw(outlineHighlight);
				failedFrames = 0;
			}
			catch (Exception ex) when (IsGameMismatch(ex))
			{
				TurnOff("This game version changed something the outlines rely on, so they're off for this session. Please share this error so the mod can be updated:\n" + ex);
			}
			catch (Exception ex2)
			{
				if (!loggedDrawError)
				{
					loggedDrawError = true;
					log.LogWarning((object)("Hit an error while drawing outlines (will keep trying):\n" + ex2));
				}
				if (++failedFrames >= 600)
				{
					TurnOff("Drawing outlines kept failing, so they're off for this session. Last error:\n" + ex2);
				}
			}
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		private static void Draw(OutlineHighlight outlineHighlight)
		{
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Invalid comparison between Unknown and I4
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			if (rulesVersion != settings.RulesVersion)
			{
				rulesByBodyIndex.Clear();
				rulesVersion = settings.RulesVersion;
			}
			CharacterBody val = (settings.OutlinePlayers.Value ? FindViewerBody(outlineHighlight) : null);
			bool flag = false;
			List<CharacterModel> instancesList = InstanceTracker.GetInstancesList<CharacterModel>();
			for (int i = 0; i < instancesList.Count; i++)
			{
				CharacterModel val2 = instancesList[i];
				if (!Object.op_Implicit((Object)(object)val2) || (int)val2.visibility <= 1)
				{
					continue;
				}
				CharacterBody body = val2.body;
				if (Object.op_Implicit((Object)(object)body) && !((Object)(object)body == (Object)(object)val))
				{
					HealthComponent healthComponent = body.healthComponent;
					if ((!Object.op_Implicit((Object)(object)healthComponent) || healthComponent.alive) && TryGetOutlineColor(body, out var color, out var isEnemy))
					{
						AddRenderers(outlineHighlight, val2, color);
						flag = flag || isEnemy;
					}
				}
			}
			if (flag)
			{
				settings.ReportUnlistedStageOnce();
				if (!loggedFirstDraw)
				{
					loggedFirstDraw = true;
					log.LogInfo((object)"Outlines are being drawn (first enemy outlined this session).");
				}
			}
		}

		private static bool TryGetOutlineColor(CharacterBody body, out Color color, out bool isEnemy)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Invalid comparison between Unknown and I4
			//IL_0092: 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_0097: Invalid comparison between Unknown and I4
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: 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_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			color = default(Color);
			isEnemy = false;
			TeamComponent teamComponent = body.teamComponent;
			TeamIndex val = (TeamIndex)((!Object.op_Implicit((Object)(object)teamComponent)) ? (-1) : ((int)teamComponent.teamIndex));
			if ((int)val == 1)
			{
				if (body.isPlayerControlled)
				{
					if (!settings.OutlinePlayers.Value)
					{
						return false;
					}
					color = (settings.UsePlayerBodyColor.Value ? GetPlayerColor(body) : settings.AllyOutlineColor);
					return true;
				}
				if (!settings.OutlineAllies.Value)
				{
					return false;
				}
				color = settings.AllyOutlineColor;
				return true;
			}
			if ((int)val == 0 || (int)val == -1)
			{
				return false;
			}
			EnemyRule enemyRule = GetEnemyRule(body);
			if (!enemyRule.Outline)
			{
				return false;
			}
			color = (enemyRule.HasOwnColor ? enemyRule.Color : settings.EnemyColor);
			isEnemy = true;
			return true;
		}

		private static EnemyRule GetEnemyRule(CharacterBody body)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Expected I4, but got Unknown
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			int num = (int)body.bodyIndex;
			if (num >= 0 && rulesByBodyIndex.TryGetValue(num, out var value))
			{
				return value;
			}
			string text = ((num >= 0) ? BodyCatalog.GetBodyName(body.bodyIndex) : null);
			if (string.IsNullOrEmpty(text))
			{
				text = ((Object)body).name.Replace("(Clone)", string.Empty).Trim();
			}
			EnemyRule enemyRule = settings.GetEnemyRule(text);
			if (num >= 0)
			{
				rulesByBodyIndex[num] = enemyRule;
			}
			if (settings.LogEnemyNames.Value && loggedBodyNames.Add(text))
			{
				log.LogInfo((object)("Enemy body name: " + text + (enemyRule.Outline ? string.Empty : " (not outlined)")));
			}
			return enemyRule;
		}

		private static void AddRenderers(OutlineHighlight outlineHighlight, CharacterModel model, Color color)
		{
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			RendererInfo[] baseRendererInfos = model.baseRendererInfos;
			if (baseRendererInfos == null)
			{
				return;
			}
			for (int i = 0; i < baseRendererInfos.Length; i++)
			{
				if (!baseRendererInfos[i].ignoreOverlays)
				{
					Renderer renderer = baseRendererInfos[i].renderer;
					if (Object.op_Implicit((Object)(object)renderer) && renderer.enabled && ((Component)renderer).gameObject.activeInHierarchy && (renderer is SkinnedMeshRenderer || renderer is MeshRenderer))
					{
						outlineHighlight.AddHighlight(renderer, color);
					}
				}
			}
		}

		private static Color GetPlayerColor(CharacterBody body)
		{
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			if (!bodyColorBroken)
			{
				try
				{
					Color val = ReadBodyColor(body);
					val.a = 1f;
					return val * settings.Strength;
				}
				catch (Exception ex) when (IsGameMismatch(ex))
				{
					bodyColorBroken = true;
					log.LogWarning((object)("Couldn't read survivor colors, so players use the Ally Color. Details: " + ex.Message));
				}
			}
			return settings.AllyOutlineColor;
		}

		private static CharacterBody FindViewerBody(OutlineHighlight outlineHighlight)
		{
			if (viewerLookupBroken)
			{
				return null;
			}
			try
			{
				return ReadViewerBody(outlineHighlight);
			}
			catch (Exception ex) when (IsGameMismatch(ex))
			{
				viewerLookupBroken = true;
				log.LogWarning((object)("Couldn't tell which character is yours, so with Highlight Players on, your own character may get an outline too. Details: " + ex.Message));
				return null;
			}
		}

		private static void TurnOff(string reason)
		{
			disabled = true;
			log.LogError((object)reason);
			Refresh();
		}

		private static bool IsGameMismatch(Exception e)
		{
			if (!(e is MissingMemberException) && !(e is TypeLoadException) && !(e is BadImageFormatException))
			{
				return e is InvalidProgramException;
			}
			return true;
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		private static void Subscribe()
		{
			OutlineHighlight.onPreRenderOutlineHighlight = (Action<OutlineHighlight>)Delegate.Combine(OutlineHighlight.onPreRenderOutlineHighlight, new Action<OutlineHighlight>(OnPreRenderOutlineHighlight));
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		private static void Unsubscribe()
		{
			OutlineHighlight.onPreRenderOutlineHighlight = (Action<OutlineHighlight>)Delegate.Remove(OutlineHighlight.onPreRenderOutlineHighlight, new Action<OutlineHighlight>(OnPreRenderOutlineHighlight));
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		private static void AddRequest()
		{
			OutlineHighlight.AddPreRenderHighlightRequest(requester);
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		private static void ReleaseRequest()
		{
			OutlineHighlight.ReleasePreRenderHighlightRequest(requester);
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		private static void AddChatMessage(string message)
		{
			Chat.AddMessage(message);
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		private static Color ReadBodyColor(CharacterBody body)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			return body.bodyColor;
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		private static CharacterBody ReadViewerBody(OutlineHighlight outlineHighlight)
		{
			SceneCamera sceneCamera = outlineHighlight.sceneCamera;
			if ((Object)(object)sceneCamera == (Object)null)
			{
				return null;
			}
			CameraRigController cameraRigController = sceneCamera.cameraRigController;
			if ((Object)(object)cameraRigController == (Object)null)
			{
				return null;
			}
			LocalUser localUserViewer = cameraRigController.localUserViewer;
			if (localUserViewer == null)
			{
				return null;
			}
			CharacterMaster cachedMaster = localUserViewer.cachedMaster;
			if (!Object.op_Implicit((Object)(object)cachedMaster))
			{
				return null;
			}
			return cachedMaster.GetBody();
		}
	}
	public enum OutlineColorMode
	{
		PerStage,
		SingleColor
	}
	internal struct EnemyRule
	{
		public bool Outline;

		public bool HasOwnColor;

		public Color Color;

		public static readonly EnemyRule Skip = new EnemyRule
		{
			Outline = false
		};

		public static readonly EnemyRule UseStageColor = new EnemyRule
		{
			Outline = true
		};
	}
	internal sealed class OutlineSettings
	{
		private struct StageList
		{
			public ConfigEntry<string> Stages;

			public string ColorName;
		}

		private struct EnemyEntry
		{
			public bool HasColor;

			public Color Color;
		}

		public const float MinStrength = 0.25f;

		public const float MaxStrength = 3f;

		private const string General = "General Settings";

		private const string ColorHelp = "Use a color name (Blue, Cyan, Green, Red, White, Yellow, Pink, Purple, Custom1, Custom2, Custom3) or a hex code (#RRGGBB, or #RRGGBBAA with transparency).";

		private readonly ManualLogSource log;

		private readonly Dictionary<ConfigDefinition, string> unboundFileValues;

		private readonly List<StageList> stageLists = new List<StageList>();

		private readonly Dictionary<string, Color> stageColors = new Dictionary<string, Color>(StringComparer.OrdinalIgnoreCase);

		private readonly Dictionary<string, EnemyEntry> enemyEntries = new Dictionary<string, EnemyEntry>(StringComparer.OrdinalIgnoreCase);

		private readonly HashSet<string> warningsShown = new HashSet<string>(StringComparer.Ordinal);

		private readonly HashSet<string> reportedStages = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		private string sceneName;

		private string unlistedStage;

		public ConfigEntry<bool> Enabled { get; }

		public ConfigEntry<KeyboardShortcut> ToggleKey { get; }

		public ConfigEntry<OutlineColorMode> ColorMode { get; }

		public ConfigEntry<Color> SingleColor { get; }

		public ConfigEntry<Color> FallbackColor { get; }

		public ConfigEntry<float> OutlineStrength { get; }

		public ConfigEntry<Color> Custom1 { get; }

		public ConfigEntry<Color> Custom2 { get; }

		public ConfigEntry<Color> Custom3 { get; }

		public ConfigEntry<bool> OnlyListedEnemies { get; }

		public ConfigEntry<string> EnemyColors { get; }

		public ConfigEntry<bool> LogEnemyNames { get; }

		public ConfigEntry<bool> OutlineAllies { get; }

		public ConfigEntry<Color> AllyColor { get; }

		public ConfigEntry<bool> OutlinePlayers { get; }

		public ConfigEntry<bool> UsePlayerBodyColor { get; }

		public float Strength { get; private set; } = 1f;

		public Color EnemyColor { get; private set; } = Color.white;

		public Color AllyOutlineColor { get; private set; } = Color.green;

		public int RulesVersion { get; private set; }

		public OutlineSettings(ConfigFile config, ManualLogSource log, string initialScene)
		{
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: 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_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_013b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0179: Unknown result type (might be due to invalid IL or missing references)
			//IL_0183: Expected O, but got Unknown
			//IL_01a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0209: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_045a: Unknown result type (might be due to invalid IL or missing references)
			this.log = log;
			sceneName = initialScene ?? string.Empty;
			unboundFileValues = GetUnboundFileValues(config);
			string text = TakeOldValue("General Settings", "Outline Color");
			Enabled = config.Bind<bool>("General Settings", "Enabled", true, "Draw the enemy outlines.");
			ToggleKey = config.Bind<KeyboardShortcut>("General Settings", "Toggle Key", KeyboardShortcut.Empty, "Key that turns the outlines on and off in game, for example F7. Empty means no key.");
			ColorMode = config.Bind<OutlineColorMode>("General Settings", "Color Mode", OutlineColorMode.PerStage, "PerStage: each stage gets a color chosen to stand out there, from the per-stage lists in the config file. SingleColor: every stage uses Single Color.");
			SingleColor = BindColor(config, "General Settings", "Single Color", new Color(1f, 0.2f, 0.8f), "Outline color for every stage when Color Mode is SingleColor.");
			FallbackColor = BindColor(config, "General Settings", "Fallback Color", Color.white, "Outline color in PerStage mode for stages that aren't in any list, such as new DLC stages. The BepInEx log names each such stage so you can add it to a list.");
			OutlineStrength = config.Bind<float>("General Settings", "Outline Strength", 1f, new ConfigDescription("How bright and solid the outlines are. 1 is normal. The width of the line is set by the game.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.25f, 3f), Array.Empty<object>()));
			Custom1 = BindColor(config, "Custom Outline Colors", "Custom Color 1", new Color(1f, 1f, 1f, 0.67f), "Color for the stages in the Custom Color 1 Stages list (that list is edited in the config file).");
			Custom2 = BindColor(config, "Custom Outline Colors", "Custom Color 2", new Color(1f, 0.55f, 0f), "Color for the stages in the Custom Color 2 Stages list (that list is edited in the config file).");
			Custom3 = BindColor(config, "Custom Outline Colors", "Custom Color 3", new Color(0.6f, 1f, 0.2f), "Color for the stages in the Custom Color 3 Stages list (that list is edited in the config file).");
			AddStageList(config, "Per-Stage Outline Settings", "Blue Stages", "Blue", "moon,moon2,itmoon,meridian,solutionalhaunt");
			AddStageList(config, "Per-Stage Outline Settings", "Green Stages", "Green", "");
			AddStageList(config, "Per-Stage Outline Settings", "Cyan Stages", "Cyan", "sulfurpools,goolake,helminthroost,dampcavesimple,itdampcave,itgoolake,ancientloft,ironalluvium,ironalluvium2,itancientloft,lemuriantemple,repurposedcrater,wispgraveyard");
			AddStageList(config, "Per-Stage Outline Settings", "Red Stages", "Red", "frozenwall,itfrozenwall,snowyforest,nest,habitatfall,limbo");
			AddStageList(config, "Per-Stage Outline Settings", "White Stages", "White", "artifactworld,artifactworld01,artifactworld02,artifactworld03,arena,voidstage,voidraid,voidoutro,goldshores");
			AddStageList(config, "Per-Stage Outline Settings", "Pink Stages", "Pink", "golemplains,golemplains2,itgolemplains,rootjungle,foggyswamp,habitat,village,villagenight,FBLScene");
			AddStageList(config, "Per-Stage Outline Settings", "Purple Stages", "Purple", "lakes,lakesnight,shipgraveyard");
			AddStageList(config, "Per-Stage Outline Settings", "Yellow Stages", "Yellow", "blackbeach,blackbeach2,conduitcanyon,skymeadow,itskymeadow");
			AddStageList(config, "Per-Stage Outline Settings", "Custom Color 1 Stages", "Custom1", "");
			AddStageList(config, "Per-Stage Outline Settings", "Custom Color 2 Stages", "Custom2", "");
			AddStageList(config, "Per-Stage Outline Settings", "Custom Color 3 Stages", "Custom3", "");
			OnlyListedEnemies = config.Bind<bool>("Specific Highlights", "Use Only Specified Highlights", false, "Only outline the enemies listed in Enemies to Outline.");
			EnemyColors = config.Bind<string>("Specific Highlights", "Enemies to Outline", "MimicBody,#00000000", "Colors for specific enemies, separated by semicolons: BodyName,Color;OtherBody,Color. A fully transparent color (alpha 00, like #00000000) means never outline that enemy; the default keeps Mimics disguised. Leave the color off (just BodyName) to use the normal color. Turn on Log Enemy Names to find body names. Use a color name (Blue, Cyan, Green, Red, White, Yellow, Pink, Purple, Custom1, Custom2, Custom3) or a hex code (#RRGGBB, or #RRGGBBAA with transparency).");
			LogEnemyNames = config.Bind<bool>("Specific Highlights", "Log Enemy Names", false, "Write each enemy's body name to the BepInEx log the first time it shows up, for use in Enemies to Outline.");
			OutlineAllies = config.Bind<bool>("Ally Highlights", "Highlight Allies", false, "Also outline allied drones, turrets and other minions, in the Ally Color.");
			AllyColor = BindColor(config, "Ally Highlights", "Ally Color", new Color(0.4f, 1f, 0.4f), "Outline color for allies, and for players when Use Body Color is off.");
			OutlinePlayers = config.Bind<bool>("Ally Highlights", "Highlight Players", false, "Also outline the other players. Your own character is never outlined.");
			UsePlayerBodyColor = config.Bind<bool>("Ally Highlights", "Use Body Color", true, "Outline each player in their survivor's color instead of the Ally Color.");
			if (!string.IsNullOrWhiteSpace(text) && !text.Trim().Equals("PerStage", StringComparison.OrdinalIgnoreCase) && TryParseColor(text, out var color))
			{
				ColorMode.Value = OutlineColorMode.SingleColor;
				SingleColor.Value = color;
			}
			Rebuild();
		}

		public void Rebuild()
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			Strength = Mathf.Clamp(OutlineStrength.Value, 0.25f, 3f);
			AllyOutlineColor = AllyColor.Value * Strength;
			stageColors.Clear();
			string[] array;
			foreach (StageList stageList in stageLists)
			{
				string value = stageList.Stages.Value;
				if (string.IsNullOrWhiteSpace(value) || !TryParseColor(stageList.ColorName, out var color))
				{
					continue;
				}
				array = value.Split(',');
				for (int i = 0; i < array.Length; i++)
				{
					string text = array[i].Trim();
					if (text.Length > 0)
					{
						stageColors[text] = color;
					}
				}
			}
			enemyEntries.Clear();
			array = (EnemyColors.Value ?? string.Empty).Split(';');
			foreach (string text2 in array)
			{
				if (string.IsNullOrWhiteSpace(text2))
				{
					continue;
				}
				string[] array2 = text2.Split(',');
				string text3 = array2[0].Trim();
				if (text3.Length == 0)
				{
					continue;
				}
				if (!text3.EndsWith("Body", StringComparison.OrdinalIgnoreCase))
				{
					text3 += "Body";
				}
				EnemyEntry value2 = default(EnemyEntry);
				if (array2.Length > 1 && !string.IsNullOrWhiteSpace(array2[1]))
				{
					if (!TryParseColor(array2[1], out value2.Color))
					{
						Warn("Enemies to Outline", "\"" + array2[1].Trim() + "\" for " + text3 + " isn't a color, so that entry is ignored. Use a color name (Blue, Cyan, Green, Red, White, Yellow, Pink, Purple, Custom1, Custom2, Custom3) or a hex code (#RRGGBB, or #RRGGBBAA with transparency).");
						continue;
					}
					value2.HasColor = true;
				}
				enemyEntries[text3] = value2;
			}
			UpdateEnemyColor();
			RulesVersion++;
		}

		public void SetScene(string name)
		{
			sceneName = name ?? string.Empty;
			UpdateEnemyColor();
		}

		public EnemyRule GetEnemyRule(string bodyName)
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			if (!string.IsNullOrEmpty(bodyName) && enemyEntries.TryGetValue(bodyName, out var value))
			{
				if (!value.HasColor)
				{
					return EnemyRule.UseStageColor;
				}
				if (value.Color.a <= 0f)
				{
					return EnemyRule.Skip;
				}
				return new EnemyRule
				{
					Outline = true,
					HasOwnColor = true,
					Color = value.Color * Strength
				};
			}
			if (!OnlyListedEnemies.Value)
			{
				return EnemyRule.UseStageColor;
			}
			return EnemyRule.Skip;
		}

		public void ReportUnlistedStageOnce()
		{
			if (!string.IsNullOrEmpty(unlistedStage))
			{
				if (reportedStages.Add(unlistedStage))
				{
					log.LogInfo((object)("Stage \"" + unlistedStage + "\" isn't in any per-stage color list, so its enemies use the Fallback Color. To choose a color for it, add " + unlistedStage + " to one of the lists under [Per-Stage Outline Settings]."));
				}
				unlistedStage = null;
			}
		}

		private void AddStageList(ConfigFile config, string section, string key, string colorName, string defaultStages)
		{
			ConfigEntry<string> stages = config.Bind<string>(section, key, defaultStages, "Stages whose enemies get the " + colorName + " outline when Color Mode is PerStage. Comma-separated internal stage names, like golemplains,blackbeach.");
			stageLists.Add(new StageList
			{
				Stages = stages,
				ColorName = colorName
			});
		}

		private ConfigEntry<Color> BindColor(ConfigFile config, string section, string key, Color defaultColor, string description)
		{
			//IL_000c: 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)
			string text = TakeOldValue(section, key);
			ConfigEntry<Color> val = config.Bind<Color>(section, key, defaultColor, description);
			if (!string.IsNullOrWhiteSpace(text))
			{
				if (TryParseHex(text, out var color))
				{
					val.Value = color;
				}
				else
				{
					Warn(key, "\"" + text.Trim() + "\" isn't a hex color, so the default is used.");
				}
			}
			return val;
		}

		private string TakeOldValue(string section, string key)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Expected O, but got Unknown
			if (unboundFileValues == null)
			{
				return null;
			}
			ConfigDefinition key2 = new ConfigDefinition(section, key);
			if (!unboundFileValues.TryGetValue(key2, out var value))
			{
				return null;
			}
			unboundFileValues.Remove(key2);
			return value;
		}

		private static Dictionary<ConfigDefinition, string> GetUnboundFileValues(ConfigFile config)
		{
			try
			{
				return typeof(ConfigFile).GetProperty("OrphanedEntries", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(config) as Dictionary<ConfigDefinition, string>;
			}
			catch (Exception)
			{
				return null;
			}
		}

		private void UpdateEnemyColor()
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: 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_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: 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)
			unlistedStage = null;
			Color value;
			if (ColorMode.Value == OutlineColorMode.SingleColor)
			{
				value = SingleColor.Value;
			}
			else if (!stageColors.TryGetValue(sceneName, out value))
			{
				value = FallbackColor.Value;
				unlistedStage = sceneName;
			}
			EnemyColor = value * Strength;
		}

		private bool TryParseColor(string text, out Color color)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_01db: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0235: Unknown result type (might be due to invalid IL or missing references)
			//IL_023a: Unknown result type (might be due to invalid IL or missing references)
			//IL_023f: Unknown result type (might be due to invalid IL or missing references)
			//IL_024d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0252: Unknown result type (might be due to invalid IL or missing references)
			//IL_0260: Unknown result type (might be due to invalid IL or missing references)
			//IL_0265: Unknown result type (might be due to invalid IL or missing references)
			//IL_0273: Unknown result type (might be due to invalid IL or missing references)
			//IL_0278: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0213: Unknown result type (might be due to invalid IL or missing references)
			//IL_0218: Unknown result type (might be due to invalid IL or missing references)
			//IL_021d: Unknown result type (might be due to invalid IL or missing references)
			color = Color.white;
			if (string.IsNullOrWhiteSpace(text))
			{
				return false;
			}
			string text2 = text.Trim();
			switch (text2.ToLowerInvariant())
			{
			case "blue":
				color = Color.blue;
				return true;
			case "cyan":
				color = Color.cyan;
				return true;
			case "green":
				color = Color.green;
				return true;
			case "red":
				color = Color.red;
				return true;
			case "white":
				color = Color.white;
				return true;
			case "yellow":
				color = Color.yellow;
				return true;
			case "pink":
				color = Color32.op_Implicit(new Color32(byte.MaxValue, (byte)105, (byte)180, byte.MaxValue));
				return true;
			case "purple":
				color = Color32.op_Implicit(new Color32((byte)128, (byte)0, (byte)128, byte.MaxValue));
				return true;
			case "custom1":
				color = Custom1.Value;
				return true;
			case "custom2":
				color = Custom2.Value;
				return true;
			case "custom3":
				color = Custom3.Value;
				return true;
			default:
				return TryParseHex(text2, out color);
			}
		}

		private static bool TryParseHex(string text, out Color color)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			color = Color.white;
			if (string.IsNullOrWhiteSpace(text))
			{
				return false;
			}
			string text2 = text.Trim();
			if (text2[0] != '#')
			{
				text2 = "#" + text2;
			}
			return ColorUtility.TryParseHtmlString(text2, ref color);
		}

		private void Warn(string setting, string problem)
		{
			if (warningsShown.Add(setting + "|" + problem))
			{
				log.LogWarning((object)("Setting \"" + setting + "\": " + problem));
			}
		}
	}
	internal static class RiskOfOptionsMenu
	{
		public const string Guid = "com.rune580.riskofoptions";

		private const string IconResource = "HighContrastEnemiesFixed.icon.png";

		public static bool IsInstalled => Chainloader.PluginInfos.ContainsKey("com.rune580.riskofoptions");

		[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
		public static void Register(OutlineSettings settings)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Expected O, but got Unknown
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Expected O, but got Unknown
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Expected O, but got Unknown
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Expected O, but got Unknown
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: 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_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Expected O, but got Unknown
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Expected O, but got Unknown
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Expected O, but got Unknown
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Expected O, but got Unknown
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Expected O, but got Unknown
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Expected O, but got Unknown
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Expected O, but got Unknown
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Expected O, but got Unknown
			//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Expected O, but got Unknown
			//IL_010b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0115: Expected O, but got Unknown
			ModSettingsManager.SetModDescription("Bright outlines on living enemies so they're easier to see. Changes here apply right away, even mid-run. The per-stage lists and per-enemy colors are in the config file.");
			Sprite val = LoadIcon();
			if ((Object)(object)val != (Object)null)
			{
				ModSettingsManager.SetModIcon(val);
			}
			ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(settings.Enabled));
			ModSettingsManager.AddOption((BaseOption)new ChoiceOption((ConfigEntryBase)(object)settings.ColorMode));
			ModSettingsManager.AddOption((BaseOption)new ColorOption(settings.SingleColor));
			ModSettingsManager.AddOption((BaseOption)new ColorOption(settings.FallbackColor));
			ModSettingsManager.AddOption((BaseOption)new SliderOption(settings.OutlineStrength, new SliderConfig
			{
				min = 0.25f,
				max = 3f,
				formatString = "{0:0.00}x"
			}));
			ModSettingsManager.AddOption((BaseOption)new KeyBindOption(settings.ToggleKey));
			ModSettingsManager.AddOption((BaseOption)new ColorOption(settings.Custom1));
			ModSettingsManager.AddOption((BaseOption)new ColorOption(settings.Custom2));
			ModSettingsManager.AddOption((BaseOption)new ColorOption(settings.Custom3));
			ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(settings.OutlineAllies));
			ModSettingsManager.AddOption((BaseOption)new ColorOption(settings.AllyColor));
			ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(settings.OutlinePlayers));
			ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(settings.UsePlayerBodyColor));
		}

		private static Sprite LoadIcon()
		{
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Expected O, but got Unknown
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				byte[] array;
				using (Stream stream = typeof(RiskOfOptionsMenu).Assembly.GetManifestResourceStream("HighContrastEnemiesFixed.icon.png"))
				{
					if (stream == null)
					{
						return null;
					}
					using MemoryStream memoryStream = new MemoryStream();
					stream.CopyTo(memoryStream);
					array = memoryStream.ToArray();
				}
				Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false);
				if (!ImageConversion.LoadImage(val, array))
				{
					return null;
				}
				return Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f));
			}
			catch (Exception)
			{
				return null;
			}
		}
	}
}