Decompiled source of Enemy AI Toggle v0.1.25

plugins/EnemyAiToggle.dll

Decompiled 4 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using AzNean.SotfCommon;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Core.Logging.Interpolation;
using BepInEx.Logging;
using BepInEx.Unity.IL2CPP;
using EnemyAiToggle.Logic;
using EnemyAiToggle.Patches;
using HarmonyLib;
using Il2CppInterop.Runtime.Injection;
using Il2CppInterop.Runtime.InteropTypes;
using Il2CppSystem.Collections.Generic;
using Microsoft.CodeAnalysis;
using Sons.Ai.Vail;
using Sons.Gameplay;
using UnityEngine;
using UnityEngine.Localization;
using UnityEngine.Localization.Settings;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("EnemyAiToggle")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.1.25.0")]
[assembly: AssemblyInformationalVersion("0.1.25+04f969363884d50e104bc4139452770beb67c5b6")]
[assembly: AssemblyProduct("EnemyAiToggle")]
[assembly: AssemblyTitle("EnemyAiToggle")]
[assembly: AssemblyVersion("0.1.25.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;
		}
	}
}
internal static class SettingMove
{
	internal static void Move(ConfigFile config, string oldSection, string oldKey, ConfigEntryBase to)
	{
		bool saveOnConfigSet = config.SaveOnConfigSet;
		config.SaveOnConfigSet = false;
		try
		{
			ConfigEntry<string> val = config.Bind<string>(oldSection, oldKey, "\u0001", "Old place of a moved setting.");
			bool num = val.Value != "\u0001";
			if (num)
			{
				to.SetSerializedValue(((ConfigEntryBase)val).GetSerializedValue());
			}
			config.Remove(((ConfigEntryBase)val).Definition);
			if (num)
			{
				config.Save();
			}
		}
		finally
		{
			config.SaveOnConfigSet = saveOnConfigSet;
		}
	}
}
namespace AzNean.SotfCommon
{
	internal static class ModLog
	{
		private static ManualLogSource _log;

		private static ConfigEntry<bool> _full;

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

		private static readonly Dictionary<(int, string), int> _state = new Dictionary<(int, string), int>();

		internal static bool Full => _full.Value;

		internal static void Init(ManualLogSource log, ConfigEntry<bool> full)
		{
			_log = log;
			_full = full;
		}

		internal static void Trace(string message)
		{
			if (_full.Value)
			{
				_log.LogInfo((object)message);
			}
		}

		internal static bool Changed(int id, string what, int state)
		{
			if (!_full.Value || (_state.TryGetValue((id, what), out var value) && value == state))
			{
				return false;
			}
			_state[(id, what)] = state;
			return true;
		}

		internal static void Once(string key, string message, LogLevel level = (LogLevel)16)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			if (_once.Add(key))
			{
				_log.Log(level, (object)message);
			}
		}
	}
}
namespace EnemyAiToggle
{
	[BepInPlugin("aznan.sotf.enemyaitoggle", "EnemyAiToggle", "0.1.25")]
	public class Plugin : BasePlugin
	{
		internal const string Version = "0.1.25";

		internal static ManualLogSource Logger;

		internal static ConfigEntry<bool> FullLogging;

		internal static ConfigEntry<bool> EnemiesFrozen;

		private static readonly Dictionary<string, ConfigEntry<bool>> ChosenTypes = new Dictionary<string, ConfigEntry<bool>>();

		internal static ConfigEntry<KeyCode> ToggleKey;

		internal static ConfigEntry<float> MessageSeconds;

		internal static ConfigEntry<string> MessageEnabled;

		internal static ConfigEntry<string> MessageDisabled;

		internal static ConfigEntry<string> MessageGuest;

		internal static ConfigEntry<float> MessageTop;

		internal static ConfigEntry<float> MessageMaxWidth;

		internal static ConfigEntry<bool> StopAnimations;

		internal static ConfigEntry<bool> FrozenAllowSleep;

		internal static bool IsTypeChosen(string typeId)
		{
			if (ChosenTypes.TryGetValue(typeId, out var value))
			{
				return value.Value;
			}
			return false;
		}

		public override void Load()
		{
			//IL_0363: Unknown result type (might be due to invalid IL or missing references)
			//IL_0377: Unknown result type (might be due to invalid IL or missing references)
			//IL_037c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0382: Expected O, but got Unknown
			//IL_0382: Unknown result type (might be due to invalid IL or missing references)
			//IL_039a: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a1: Expected O, but got Unknown
			Logger = ((BasePlugin)this).Log;
			EnemyFilter.SelfCheck();
			Texts.SelfCheck();
			EnemiesFrozen = ((BasePlugin)this).Config.Bind<bool>("General", "EnemiesFrozen", false, "True = the types ticked in [Frozen types] stand still.");
			ToggleKey = ((BasePlugin)this).Config.Bind<KeyCode>("General", "ToggleKey", (KeyCode)289, "Key that toggles enemy AI.");
			StopAnimations = ((BasePlugin)this).Config.Bind<bool>("General", "StopAnimations", true, "True = frozen creatures also stop animating (statue). False = only their AI stops.");
			FrozenAllowSleep = ((BasePlugin)this).Config.Bind<bool>("General", "FrozenAllowSleep", true, "True = frozen creatures near you no longer make the game say it is not safe to sleep. False = the game counts them like any nearby enemy. Default: true.");
			foreach (string item in Enum.GetNames(typeof(VailActorTypeId)).Where(EnemyFilter.IsListed))
			{
				string text = EnemyFilter.ShownName(item);
				string text2 = ((text == item) ? item : (text + " (" + item + " in the game files)"));
				ConfigEntry<bool> val = ((BasePlugin)this).Config.Bind<bool>("Frozen types", item, EnemyFilter.FrozenByDefault(item), "Freeze " + text2 + " when enemy AI is OFF.");
				val.SettingChanged += delegate
				{
					EnemyFreezer.ApplyToAll();
				};
				ChosenTypes[item] = val;
			}
			MessageEnabled = ((BasePlugin)this).Config.Bind<string>("Texts", "MessageEnabled", "", "Message shown when enemies move again. Empty = automatic, in the game's language.");
			MessageDisabled = ((BasePlugin)this).Config.Bind<string>("Texts", "MessageDisabled", "", "Message shown when the ticked types are frozen; {0} = their list. Empty = automatic, in the game's language.");
			MessageGuest = ((BasePlugin)this).Config.Bind<string>("Texts", "MessageGuest", "", "Message shown when a multiplayer guest presses the key (the host runs enemy AI, so only the host can freeze enemies). Empty = automatic, in the game's language.");
			MessageSeconds = ((BasePlugin)this).Config.Bind<float>("Texts", "MessageSeconds", 4f, "How long the on-screen message stays visible, in seconds. 0 = no message. Advised: 0 to 30.");
			MessageTop = ((BasePlugin)this).Config.Bind<float>("Texts", "MessageTop", 40f, "Distance of the message from the top of the screen, in pixels. Advised: 0 to 2000.");
			MessageMaxWidth = ((BasePlugin)this).Config.Bind<float>("Texts", "MessageMaxWidth", 900f, "Widest the message box gets, in pixels; longer text wraps onto more lines. Advised: 100 to 4000.");
			FullLogging = ((BasePlugin)this).Config.Bind<bool>("Debug", "FullLogging", false, "True = the BepInEx log gets every step useful to diagnose a problem (decisions, values read, object aimed at, failures with their cause). False = only loading, version and errors.");
			ModLog.Init(Logger, FullLogging);
			ConfigEntryBase[] array = (ConfigEntryBase[])(object)new ConfigEntryBase[5]
			{
				(ConfigEntryBase)MessageEnabled,
				(ConfigEntryBase)MessageDisabled,
				(ConfigEntryBase)MessageSeconds,
				(ConfigEntryBase)MessageTop,
				(ConfigEntryBase)MessageMaxWidth
			};
			foreach (ConfigEntryBase val2 in array)
			{
				SettingMove.Move(((BasePlugin)this).Config, "Message", val2.Definition.Key, val2);
			}
			string path = Path.Combine(Paths.ConfigPath, "aznan.sotf.enemyaitoggle.seconds-0.1.21.done");
			if (!File.Exists(path))
			{
				if (MessageSeconds.Value == 2f)
				{
					MessageSeconds.Value = 4f;
					Logger.LogInfo((object)"Texts/MessageSeconds was the old default 2 s: set to the new default 4 s (once only).");
				}
				File.WriteAllText(path, "MessageSeconds default 2 -> 4 migrated once (see NOTES).");
			}
			EnemiesFrozen.SettingChanged += delegate
			{
				EnemyFreezer.ApplyToAll();
			};
			new Harmony("aznan.sotf.enemyaitoggle").PatchAll();
			ClassInjector.RegisterTypeInIl2Cpp<KeyWatcher>();
			GameObject val3 = new GameObject("EnemyAiToggle");
			Object.DontDestroyOnLoad((Object)val3);
			((Object)val3).hideFlags = (HideFlags)61;
			val3.AddComponent<KeyWatcher>();
			ManualLogSource logger = Logger;
			bool flag = default(bool);
			BepInExInfoLogInterpolatedStringHandler val4 = new BepInExInfoLogInterpolatedStringHandler(21, 1, ref flag);
			if (flag)
			{
				((BepInExLogInterpolatedStringHandler)val4).AppendLiteral("EnemyAiToggle ");
				((BepInExLogInterpolatedStringHandler)val4).AppendFormatted<string>("0.1.25");
				((BepInExLogInterpolatedStringHandler)val4).AppendLiteral(" loaded");
			}
			logger.LogInfo(val4);
		}

		internal static string StateMessage()
		{
			Locale selectedLocale = LocalizationSettings.SelectedLocale;
			string locale = ((selectedLocale != null) ? selectedLocale.Identifier.Code : null);
			if (BoltNetwork.isClient)
			{
				return Pick(MessageGuest, "Guest");
			}
			if (!EnemiesFrozen.Value)
			{
				return Pick(MessageEnabled, "Enabled");
			}
			List<string> frozenTypes = (from t in ChosenTypes
				where t.Value.Value
				select EnemyFilter.ShownName(t.Key)).ToList();
			return Texts.WithTypes(Pick(MessageDisabled, "Disabled"), frozenTypes, Texts.Get(locale, "NoType"));
			string Pick(ConfigEntry<string> setting, string key)
			{
				if (!string.IsNullOrEmpty(setting.Value))
				{
					return setting.Value;
				}
				return Texts.Get(locale, key);
			}
		}
	}
	public class KeyWatcher : MonoBehaviour
	{
		private float _messageUntil;

		private string _message;

		public KeyWatcher(IntPtr ptr)
			: base(ptr)
		{
		}

		private void Update()
		{
			//IL_0005: 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_0040: Expected O, but got Unknown
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			if (Input.GetKeyDown(Plugin.ToggleKey.Value))
			{
				if (!BoltNetwork.isClient)
				{
					Plugin.EnemiesFrozen.Value = !Plugin.EnemiesFrozen.Value;
				}
				ManualLogSource logger = Plugin.Logger;
				bool flag = default(bool);
				BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(19, 3, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<KeyCode>(Plugin.ToggleKey.Value);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" pressed: enemy AI ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(Plugin.EnemiesFrozen.Value ? "OFF" : "ON");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(BoltNetwork.isClient ? " (multiplayer guest: the host's game runs the AI, nothing changed here)" : "");
				}
				logger.LogInfo(val);
				_message = Plugin.StateMessage();
				_messageUntil = Time.unscaledTime + Plugin.MessageSeconds.Value;
			}
		}

		private void OnGUI()
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Expected O, but got Unknown
			//IL_0031: 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_003e: Expected O, but got Unknown
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			if (!(Time.unscaledTime > _messageUntil) && !string.IsNullOrEmpty(_message))
			{
				GUIContent val = new GUIContent(_message);
				GUIStyle val2 = new GUIStyle(GUI.skin.box)
				{
					wordWrap = true
				};
				float num = Mathf.Min(val2.CalcSize(val).x, Plugin.MessageMaxWidth.Value);
				float num2 = val2.CalcHeight(val, num);
				GUI.Box(new Rect(((float)Screen.width - num) / 2f, Plugin.MessageTop.Value, num, num2), val, val2);
			}
		}
	}
}
namespace EnemyAiToggle.Patches
{
	[HarmonyPatch(typeof(VailActor), "Start")]
	public static class EnemyFreezer
	{
		private static readonly Dictionary<IntPtr, (VailActor actor, bool animatorStopped)> FrozenByUs = new Dictionary<IntPtr, (VailActor, bool)>();

		internal static int FrozenCount => FrozenByUs.Count;

		internal static bool IsFrozen(WorldSimActor worldActor)
		{
			foreach (var value in FrozenByUs.Values)
			{
				if ((Object)(object)value.actor != (Object)null && !((Behaviour)value.actor).enabled && value.actor._worldSimUniqueId == worldActor._uniqueId)
				{
					return true;
				}
			}
			return false;
		}

		public static void Postfix(VailActor __instance)
		{
			FrozenByUs.Remove(((Il2CppObjectBase)__instance).Pointer);
			Apply(__instance);
		}

		public static void ApplyToAll()
		{
			int num = 0;
			foreach (VailActor item in Object.FindObjectsOfType<VailActor>(true))
			{
				Apply(item);
				num++;
			}
			foreach (IntPtr item2 in (from e in FrozenByUs
				where (Object)(object)e.Value.actor == (Object)null
				select e.Key).ToList())
			{
				FrozenByUs.Remove(item2);
			}
			ModLog.Trace($"Enemy AI {(Plugin.EnemiesFrozen.Value ? "OFF" : "ON")}: {num} creature(s) checked, {FrozenByUs.Count} frozen by this mod, animations {(Plugin.StopAnimations.Value ? "stopped" : "kept")}");
		}

		private static void Apply(VailActor actor)
		{
			//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_00f1: 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)
			if (BoltNetwork.isClient)
			{
				if (ModLog.Changed(0, "guest", Plugin.EnemiesFrozen.Value ? 1 : 0))
				{
					ModLog.Trace($"Multiplayer guest: creatures left to the host's AI (EnemiesFrozen {Plugin.EnemiesFrozen.Value} here changes nothing).");
				}
				return;
			}
			string text = ((object)actor.TypeId/*cast due to .constrained prefix*/).ToString();
			bool flag = Plugin.EnemiesFrozen.Value && Plugin.IsTypeChosen(text);
			(VailActor, bool) value;
			bool flag2 = FrozenByUs.TryGetValue(((Il2CppObjectBase)actor).Pointer, out value);
			bool flag3 = value.Item2;
			if (flag == flag2)
			{
				return;
			}
			if (flag && !((Behaviour)actor).enabled)
			{
				if (ModLog.Changed(((Object)actor).GetInstanceID(), "gameoff", 1))
				{
					ModLog.Trace($"{((Object)actor).name} ({actor.ClassId}/{text}) not frozen: the game switched its AI off itself.");
				}
				return;
			}
			((Behaviour)actor).enabled = !flag;
			Animator componentInChildren = ((Component)actor).GetComponentInChildren<Animator>(true);
			if (flag)
			{
				flag3 = Plugin.StopAnimations.Value && (Object)(object)componentInChildren != (Object)null && ((Behaviour)componentInChildren).enabled;
				if (flag3)
				{
					((Behaviour)componentInChildren).enabled = false;
				}
				FrozenByUs[((Il2CppObjectBase)actor).Pointer] = (actor, flag3);
			}
			else
			{
				if (flag3 && (Object)(object)componentInChildren != (Object)null)
				{
					((Behaviour)componentInChildren).enabled = true;
				}
				FrozenByUs.Remove(((Il2CppObjectBase)actor).Pointer);
			}
			ModLog.Trace($"{((Object)actor).name} ({actor.ClassId}/{text}) frozen={flag}, animator {(((Object)(object)componentInChildren == (Object)null) ? "not found" : ((!flag3) ? "untouched" : (flag ? "stopped" : "restarted")))}");
		}
	}
	[HarmonyPatch(typeof(VailWorldSimulation), "SleepCheckEnemies")]
	internal static class SleepCheck
	{
		internal static bool Ran;

		private static void Postfix(ref List<WorldSimActor> nearActors, ref int __result)
		{
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Expected O, but got Unknown
			if (nearActors == null)
			{
				ModLog.Trace("Sleep check: the game gave no enemy list, nothing left out.");
				return;
			}
			int count = nearActors.Count;
			int num = 0;
			if (Plugin.FrozenAllowSleep.Value)
			{
				for (int num2 = nearActors.Count - 1; num2 >= 0; num2--)
				{
					if (nearActors[num2] != null && EnemyFreezer.IsFrozen(nearActors[num2]))
					{
						nearActors.RemoveAt(num2);
						num++;
					}
				}
			}
			__result = nearActors.Count;
			Ran = true;
			ManualLogSource logger = Plugin.Logger;
			bool flag = default(bool);
			BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(88, 4, ref flag);
			if (flag)
			{
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Sleep check: the game counted ");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(count);
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" enemy(ies) near you, ");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(num);
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" frozen by this mod left out");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(Plugin.FrozenAllowSleep.Value ? "" : " (FrozenAllowSleep off)");
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral(", ");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(__result);
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" kept.");
			}
			logger.LogInfo(val);
		}
	}
	[HarmonyPatch(typeof(SleepingManager), "InterruptSleep")]
	internal static class SleepInterrupted
	{
		private static void Prefix(int __0, string __1)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Expected O, but got Unknown
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Expected O, but got Unknown
			ManualLogSource logger = Plugin.Logger;
			bool flag = default(bool);
			BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(40, 2, ref flag);
			if (flag)
			{
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Sleep interrupted by the game after ");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(__0);
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" h: ");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(__1);
			}
			logger.LogInfo(val);
			if (!SleepCheck.Ran && Plugin.FrozenAllowSleep.Value && EnemyFreezer.FrozenCount > 0)
			{
				ManualLogSource logger2 = Plugin.Logger;
				BepInExWarningLogInterpolatedStringHandler val2 = new BepInExWarningLogInterpolatedStringHandler(115, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("Sleep refused without this mod's sleep check while ");
					((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<int>(EnemyFreezer.FrozenCount);
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" creature(s) are frozen: they may have been counted as a threat.");
				}
				logger2.LogWarning(val2);
			}
			SleepCheck.Ran = false;
		}
	}
}
namespace EnemyAiToggle.Logic
{
	public static class EnemyFilter
	{
		private static readonly string[] NotFrozenByDefault = new string[25]
		{
			"None", "Robby", "Virginia", "Timmy", "PlayerRobby", "Helicopter", "Slug_Deprecated", "SpongeTest", "Rabbit", "Squirrel",
			"Turtle", "Shark", "Seagull", "Eagle", "Duck", "Moose", "Salmon", "Bat", "Deer", "Bluebird",
			"Hummingbird", "KillerWhale", "LandTurtle", "Raccoon", "Skunk"
		};

		private static readonly Dictionary<string, string> KnownAs = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase) { ["Robby"] = "Kelvin" };

		public static bool IsListed(string typeId)
		{
			return !string.Equals(typeId, "None", StringComparison.OrdinalIgnoreCase);
		}

		public static string ShownName(string typeId)
		{
			if (!KnownAs.TryGetValue(typeId, out var value))
			{
				return typeId;
			}
			return value;
		}

		public static bool FrozenByDefault(string typeId)
		{
			return !NotFrozenByDefault.Contains<string>(typeId, StringComparer.OrdinalIgnoreCase);
		}

		public static void SelfCheck()
		{
			if (FrozenByDefault("Robby") || FrozenByDefault("Virginia") || !FrozenByDefault("Billy") || !FrozenByDefault("Fingers"))
			{
				throw new InvalidOperationException("EnemyFilter defaults are wrong.");
			}
			if (IsListed("None") || !IsListed("Billy") || ShownName("Robby") != "Kelvin" || ShownName("Billy") != "Billy")
			{
				throw new InvalidOperationException("EnemyFilter names are wrong.");
			}
		}
	}
	public static class Texts
	{
		public const string Disabled = "Disabled";

		public const string Enabled = "Enabled";

		public const string NoType = "NoType";

		public const string Guest = "Guest";

		private static readonly Dictionary<string, Dictionary<string, string>> ByLanguage = new Dictionary<string, Dictionary<string, string>>
		{
			["en"] = new Dictionary<string, string>
			{
				["Disabled"] = "Enemy AI disabled for: {0}",
				["Enabled"] = "Enemy AI enabled: every creature moves",
				["NoType"] = "no type ticked",
				["Guest"] = "Enemy AI runs on the host: only the host can freeze enemies"
			},
			["fr"] = new Dictionary<string, string>
			{
				["Disabled"] = "IA ennemie désactivée pour : {0}",
				["Enabled"] = "IA ennemie activée : toutes les créatures bougent",
				["NoType"] = "aucun type coché",
				["Guest"] = "L'IA ennemie tourne chez l'hôte : seul l'hôte peut figer les ennemis"
			},
			["zh"] = new Dictionary<string, string>
			{
				["Disabled"] = "已禁用以下敌人的 AI:{0}",
				["Enabled"] = "敌人 AI 已启用:所有生物恢复行动",
				["NoType"] = "未勾选任何类型",
				["Guest"] = "敌人 AI 由房主运行:仅房主可定住敌人"
			},
			["zh-hant"] = new Dictionary<string, string>
			{
				["Disabled"] = "已停用以下敵人的 AI:{0}",
				["Enabled"] = "敵人 AI 已啟用:所有生物恢復行動",
				["NoType"] = "未勾選任何類型",
				["Guest"] = "敵人 AI 由房主運行:僅房主可定住敵人"
			},
			["cs"] = new Dictionary<string, string>
			{
				["Disabled"] = "UI nepřátel vypnuta pro: {0}",
				["Enabled"] = "UI nepřátel zapnuta: všechny bytosti se pohybují",
				["NoType"] = "není zaškrtnut žádný typ",
				["Guest"] = "UI nepřátel běží na hostiteli: zmrazit nepřátele může pouze hostitel"
			},
			["fi"] = new Dictionary<string, string>
			{
				["Disabled"] = "Vihollisten tekoäly poistettu käytöstä kohteelta: {0}",
				["Enabled"] = "Vihollisten tekoäly käytössä: kaikki olennot liikkuvat",
				["NoType"] = "tyyppiä ei valittu",
				["Guest"] = "Vihollisten tekoäly toimii isännällä: vain isäntä voi pysäyttää viholliset"
			},
			["de"] = new Dictionary<string, string>
			{
				["Disabled"] = "Gegner-KI deaktiviert für: {0}",
				["Enabled"] = "Gegner-KI aktiviert: Alle Kreaturen bewegen sich",
				["NoType"] = "Kein Typ ausgewählt",
				["Guest"] = "Gegner-KI läuft auf dem Host: Nur der Host kann Gegner einfrieren"
			},
			["it"] = new Dictionary<string, string>
			{
				["Disabled"] = "IA nemica disattivata per: {0}",
				["Enabled"] = "IA nemica attivata: tutte le creature si muovono",
				["NoType"] = "nessun tipo selezionato",
				["Guest"] = "L'IA nemica gira sull'host: solo l'host può congelare i nemici"
			},
			["ja"] = new Dictionary<string, string>
			{
				["Disabled"] = "敵のAIを無効化: {0}",
				["Enabled"] = "敵のAIを有効化: すべての生物が行動",
				["NoType"] = "タイプ未選択",
				["Guest"] = "敵AIはホスト側で実行中: 敵を停止できるのはホストのみです"
			},
			["ko"] = new Dictionary<string, string>
			{
				["Disabled"] = "적 AI 비활성화됨: {0}",
				["Enabled"] = "적 AI 활성화됨: 모든 생명체가 움직입니다",
				["NoType"] = "선택된 유형 없음",
				["Guest"] = "적 AI는 호스트에서 실행됩니다: 호스트만 적을 멈출 수 있습니다"
			},
			["pl"] = new Dictionary<string, string>
			{
				["Disabled"] = "Wyłączono SI wrogów dla: {0}",
				["Enabled"] = "Włączono SI wrogów: wszystkie stworzenia się poruszają",
				["NoType"] = "nie zaznaczono typu",
				["Guest"] = "SI wrogów działa u hosta: tylko host może zamrozić wrogów"
			},
			["pt"] = new Dictionary<string, string>
			{
				["Disabled"] = "IA dos inimigos desativada para: {0}",
				["Enabled"] = "IA dos inimigos ativada: todas as criaturas se movem",
				["NoType"] = "nenhum tipo marcado",
				["Guest"] = "A IA dos inimigos roda no anfitrião: apenas o anfitrião pode congelar inimigos"
			},
			["ru"] = new Dictionary<string, string>
			{
				["Disabled"] = "ИИ врагов отключен для: {0}",
				["Enabled"] = "ИИ врагов включен: все существа двигаются",
				["NoType"] = "тип не выбран",
				["Guest"] = "ИИ врагов работает на хосте: заморозить врагов может только хост"
			},
			["es"] = new Dictionary<string, string>
			{
				["Disabled"] = "IA enemiga desactivada para: {0}",
				["Enabled"] = "IA enemiga activada: todas las criaturas se mueven",
				["NoType"] = "ningún tipo marcado",
				["Guest"] = "La IA enemiga se ejecuta en el anfitrión: solo el anfitrión puede congelar enemigos"
			},
			["sv"] = new Dictionary<string, string>
			{
				["Disabled"] = "Fiendens AI avstängd för: {0}",
				["Enabled"] = "Fiendens AI aktiverad: alla varelser rör sig",
				["NoType"] = "ingen typ markerad",
				["Guest"] = "Fiendens AI körs på värden: endast värden kan frysa fiender"
			},
			["tr"] = new Dictionary<string, string>
			{
				["Disabled"] = "Düşman yapay zekâsı devre dışı: {0}",
				["Enabled"] = "Düşman yapay zekâsı etkin: tüm yaratıklar hareket ediyor",
				["NoType"] = "hiçbir tür seçilmedi",
				["Guest"] = "Düşman yapay zekâsı kurucuda çalışır: düşmanları yalnızca kurucu dondurabilir"
			}
		};

		public static string Get(string localeCode, string key)
		{
			string text = (localeCode ?? "en").ToLowerInvariant();
			string key2 = (ByLanguage.ContainsKey(text) ? text : text.Split('-')[0]);
			if (!ByLanguage.TryGetValue(key2, out var value) || !value.TryGetValue(key, out var value2))
			{
				return ByLanguage["en"][key];
			}
			return value2;
		}

		public static string WithTypes(string template, IReadOnlyCollection<string> frozenTypes, string noType)
		{
			return template.Replace("{0}", (frozenTypes.Count > 0) ? string.Join(", ", frozenTypes) : noType);
		}

		public static void SelfCheck()
		{
			foreach (string key in ByLanguage["fr"].Keys)
			{
				if (!ByLanguage["en"].ContainsKey(key))
				{
					throw new InvalidOperationException("Texts: '" + key + "' missing in English.");
				}
			}
			if (Get("fr-FR", "Enabled") != ByLanguage["fr"]["Enabled"] || Get("xx", "Enabled") != ByLanguage["en"]["Enabled"] || WithTypes("x {0} {y}", (IReadOnlyCollection<string>)(object)new string[2] { "Billy", "Fingers" }, "-") != "x Billy, Fingers {y}" || WithTypes("{0}", (IReadOnlyCollection<string>)(object)new string[0], "-") != "-")
			{
				throw new InvalidOperationException("Texts lookup is wrong.");
			}
		}
	}
}