Decompiled source of SubSkillsDisplay v1.1.4

SubSkillsDisplay.dll

Decompiled a day ago
using System;
using System.Collections;
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 System.Security;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Logging;
using EntityStates;
using EntityStates.Acolyte;
using EntityStates.Chef;
using EntityStates.Drifter;
using EntityStates.Drifter.Bag;
using EntityStates.Railgunner.Backpack;
using EntityStates.Railgunner.Reload;
using EntityStates.Railgunner.Scope;
using EntityStates.Railgunner.Weapon;
using EntityStates.VoidSurvivor.CorruptMode;
using Microsoft.CodeAnalysis;
using Mono.Cecil;
using Mono.Cecil.Cil;
using Mono.Collections.Generic;
using On.RoR2.UI;
using RoR2;
using RoR2.ContentManagement;
using RoR2.Skills;
using RoR2.UI;
using TMPro;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.Events;
using UnityEngine.ResourceManagement.AsyncOperations;
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("SubSkillsDisplay")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.1.4.0")]
[assembly: AssemblyInformationalVersion("1.1.4+bf65db680d0bf3c1095f54e07a57bb8e880209c6")]
[assembly: AssemblyProduct("SubSkillsDisplay")]
[assembly: AssemblyTitle("SubSkillsDisplay")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.1.4.0")]
[module: UnverifiableCode]
[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 SubSkillsDisplay
{
	internal static class CharacterSelectDump
	{
		[Serializable]
		[CompilerGenerated]
		private sealed class <>c
		{
			public static readonly <>c <>9 = new <>c();

			public static hook_Awake <>9__1_0;

			internal void <Init>b__1_0(orig_Awake orig, CharacterSelectController self)
			{
				orig.Invoke(self);
				if (!logged)
				{
					((MonoBehaviour)self).StartCoroutine(DumpAfterLayout(self));
				}
			}
		}

		private static bool logged;

		internal static void Init()
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Expected O, but got Unknown
			object obj = <>c.<>9__1_0;
			if (obj == null)
			{
				hook_Awake val = delegate(orig_Awake orig, CharacterSelectController self)
				{
					orig.Invoke(self);
					if (!logged)
					{
						((MonoBehaviour)self).StartCoroutine(DumpAfterLayout(self));
					}
				};
				<>c.<>9__1_0 = val;
				obj = (object)val;
			}
			CharacterSelectController.Awake += (hook_Awake)obj;
		}

		private static IEnumerator DumpAfterLayout(CharacterSelectController controller)
		{
			yield return (object)new WaitForSecondsRealtime(2f);
			if (logged || !Object.op_Implicit((Object)(object)controller) || !Object.op_Implicit((Object)(object)controller.skillStripContainer))
			{
				yield break;
			}
			logged = true;
			Transform val = (Transform)(object)controller.skillStripContainer;
			for (int i = 0; i < 2; i++)
			{
				if (!Object.op_Implicit((Object)(object)val.parent))
				{
					break;
				}
				val = val.parent;
			}
			StringBuilder stringBuilder = new StringBuilder("Character select hierarchy:\n");
			Append(stringBuilder, val, 0);
			Log.Info(stringBuilder.ToString());
		}

		private static void Append(StringBuilder sb, Transform transform, int depth)
		{
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			sb.Append(' ', depth * 2).Append("- ").Append(((Object)transform).name);
			if (!((Component)transform).gameObject.activeSelf)
			{
				sb.Append(" (inactive)");
			}
			RectTransform val = (RectTransform)(object)((transform is RectTransform) ? transform : null);
			if (val != null)
			{
				StringBuilder stringBuilder = sb.Append("  rect=");
				Rect rect = val.rect;
				StringBuilder stringBuilder2 = stringBuilder.Append(Mathf.Round(((Rect)(ref rect)).width)).Append('x');
				rect = val.rect;
				stringBuilder2.Append(Mathf.Round(((Rect)(ref rect)).height));
			}
			LayoutElement component = ((Component)transform).GetComponent<LayoutElement>();
			if (Object.op_Implicit((Object)(object)component))
			{
				sb.Append("  LE(prefW=").Append(component.preferredWidth).Append(",prefH=")
					.Append(component.preferredHeight)
					.Append(",ignore=")
					.Append(component.ignoreLayout)
					.Append(')');
			}
			sb.Append('\n');
			for (int i = 0; i < transform.childCount; i++)
			{
				Append(sb, transform.GetChild(i), depth + 1);
			}
		}
	}
	internal readonly struct ExtraSkill
	{
		internal readonly SkillDef skill;

		internal readonly SkillSlot slot;

		internal readonly string lineToken;

		internal readonly string sourceToken;

		internal readonly string descriptionToken;

		internal readonly bool inLoadout;

		internal readonly bool unlocked;

		internal string LineKey
		{
			get
			{
				//IL_0001: Unknown result type (might be due to invalid IL or missing references)
				//IL_0007: Invalid comparison between Unknown and I4
				//IL_001a: Unknown result type (might be due to invalid IL or missing references)
				//IL_001f: Unknown result type (might be due to invalid IL or missing references)
				string? text;
				if ((int)slot == -1)
				{
					text = lineToken;
					if (text == null)
					{
						return string.Empty;
					}
				}
				else
				{
					text = ((object)slot/*cast due to .constrained prefix*/).ToString();
				}
				return text;
			}
		}

		internal ExtraSkill(SkillDef skill, SkillSlot slot, string sourceToken, bool inLoadout, bool unlocked, string descriptionToken = null, string lineToken = null)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Invalid comparison between Unknown and I4
			this.skill = skill;
			this.slot = slot;
			this.lineToken = (((int)slot == -1) ? lineToken : null);
			this.sourceToken = sourceToken;
			this.inLoadout = inLoadout;
			this.unlocked = unlocked;
			this.descriptionToken = (string.IsNullOrEmpty(descriptionToken) ? skill.skillDescriptionToken : descriptionToken);
		}
	}
	internal static class ExtraSkillCatalog
	{
		private sealed class StateRule
		{
			internal readonly Type parentState;

			private readonly Func<IEnumerable<(SkillSlot, SkillDef)>> resolve;

			private (SkillSlot slot, SkillDef skill)[] extras;

			internal (SkillSlot slot, SkillDef skill)[] Extras => extras ?? (extras = (from extra in resolve()
				where Object.op_Implicit((Object)(object)extra.Item2)
				select extra).ToArray());

			internal StateRule(Type parentState, Func<IEnumerable<(SkillSlot, SkillDef)>> resolve)
			{
				this.parentState = parentState;
				this.resolve = resolve;
			}
		}

		internal static bool includeModdedSurvivors = true;

		private static readonly StateRule[] stateRules = new StateRule[6]
		{
			new StateRule(typeof(WindUpScopeHeavy), ScopeShots<ActiveScopeHeavy>),
			new StateRule(typeof(WindUpScopeLight), ScopeShots<ActiveScopeLight>),
			new StateRule(typeof(ChargeSnipeSuper), () => new(SkillSlot, SkillDef)[1] { ((SkillSlot)0, ((BaseCharged)Configured<ChargedSuper>()).primaryOverride) }),
			new StateRule(typeof(ChargeSnipeCryo), () => new(SkillSlot, SkillDef)[1] { ((SkillSlot)0, ((BaseCharged)Configured<ChargedCryo>()).primaryOverride) }),
			new StateRule(typeof(AimRepossess), delegate
			{
				BaggedObject val = Configured<BaggedObject>();
				return new(SkillSlot, SkillDef)[2]
				{
					((SkillSlot)0, val.primaryOverride),
					((SkillSlot)2, val.utilityOverride)
				};
			}),
			new StateRule(typeof(ChargeTornadoSlam), () => new(SkillSlot, SkillDef)[1] { ((SkillSlot)0, Configured<TornadoSlam>().primaryOverride) })
		};

		internal static List<ExtraSkill> For(SurvivorLoadout body, bool includeOtherLoadouts)
		{
			List<ExtraSkill> found = new List<ExtraSkill>();
			if (!body.IsValid)
			{
				return found;
			}
			Collect("hidden loadout rows", delegate
			{
				AddHiddenSlots(body, found);
			});
			Collect("Acolyte forms", delegate
			{
				AddAcolyteForms(body, found);
			});
			Collect("CHEF boosts", delegate
			{
				AddChefBoosts(body, found);
			});
			Collect("Void Fiend corruption", delegate
			{
				AddCorruptedSkills(body, found);
			});
			StateRule[] array = stateRules;
			foreach (StateRule rule in array)
			{
				Collect(rule.parentState.Name, delegate
				{
					AddForState(body, rule.parentState, rule.Extras, null, found);
				});
			}
			if (includeModdedSurvivors)
			{
				Collect("modded survivors", delegate
				{
					ModdedExtraSkills.Add(body, found);
				});
			}
			HashSet<(string, string)> choices = new HashSet<(string, string)>();
			foreach (GenericSkill item2 in body.slots.Where((GenericSkill slot) => !slot.hideInCharacterSelect))
			{
				foreach (var item3 in body.Variants(item2))
				{
					SkillDef item = item3.skill;
					choices.Add((item.skillNameToken, item.skillDescriptionToken));
				}
			}
			HashSet<(string, string, string)> listed = new HashSet<(string, string, string)>();
			return (from extra in found
				where extra.unlocked && (extra.inLoadout || includeOtherLoadouts)
				where !choices.Contains((extra.skill.skillNameToken, extra.descriptionToken))
				where listed.Add((extra.LineKey, extra.skill.skillNameToken, extra.descriptionToken))
				select extra).ToList();
		}

		internal static void LogAll(UserProfile profile)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: 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)
			Loadout val = Loadout.RequestInstance();
			try
			{
				StringBuilder stringBuilder = new StringBuilder("Extra skills per unlocked survivor (all unlocked loadouts):");
				foreach (SurvivorDef orderedSurvivorDef in SurvivorCatalog.orderedSurvivorDefs)
				{
					if (!SurvivorCatalog.SurvivorIsUnlockedOnThisClient(orderedSurvivorDef.survivorIndex))
					{
						continue;
					}
					List<ExtraSkill> list = For(SurvivorLoadout.FromLoadout(SurvivorCatalog.GetBodyIndexFromSurvivorIndex(orderedSurvivorDef.survivorIndex), val, profile), includeOtherLoadouts: true);
					if (list.Count != 0)
					{
						stringBuilder.Append("\n  ").Append(Language.GetString(orderedSurvivorDef.displayNameToken)).Append(": ");
						stringBuilder.Append(string.Join(", ", list.Select((ExtraSkill extra) => extra.LineKey + ": " + Language.GetString(extra.skill.skillNameToken) + " [" + extra.skill.skillName + "] (" + Language.GetString(extra.sourceToken ?? "") + ")")));
					}
				}
				Log.Info(stringBuilder.ToString());
			}
			finally
			{
				Loadout.ReturnInstance(val);
			}
		}

		private static void AddHiddenSlots(SurvivorLoadout body, List<ExtraSkill> found)
		{
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			CaptainSupplyDropController component = body.GetComponent<CaptainSupplyDropController>();
			AcolyteFormController component2 = body.GetComponent<AcolyteFormController>();
			foreach (GenericSkill item in body.slots.Where((GenericSkill val) => val.hideInCharacterSelect))
			{
				if (Object.op_Implicit((Object)(object)component2) && (Object)(object)item == (Object)(object)component2.transformSkillSlot)
				{
					continue;
				}
				SkillSlot slot = (SkillSlot)(-1);
				string sourceToken = null;
				if (Object.op_Implicit((Object)(object)component) && ((Object)(object)item == (Object)(object)component.supplyDrop1Skill || (Object)(object)item == (Object)(object)component.supplyDrop2Skill))
				{
					slot = (SkillSlot)(!((Object)(object)item == (Object)(object)component.supplyDrop1Skill));
					sourceToken = body.Selected(component.prepSupplyDropSkill)?.skillNameToken;
				}
				string lineToken = (string.IsNullOrEmpty(item.loadoutTitleToken) ? null : item.loadoutTitleToken);
				foreach (var (skill, inLoadout, unlocked) in body.Variants(item))
				{
					found.Add(new ExtraSkill(skill, slot, sourceToken, inLoadout, unlocked, null, lineToken));
				}
			}
		}

		private static void AddAcolyteForms(SurvivorLoadout body, List<ExtraSkill> found)
		{
			//IL_0140: Unknown result type (might be due to invalid IL or missing references)
			//IL_0145: Unknown result type (might be due to invalid IL or missing references)
			//IL_0147: Unknown result type (might be due to invalid IL or missing references)
			//IL_0155: Unknown result type (might be due to invalid IL or missing references)
			//IL_0164: Unknown result type (might be due to invalid IL or missing references)
			//IL_0171: Unknown result type (might be due to invalid IL or missing references)
			//IL_0178: Unknown result type (might be due to invalid IL or missing references)
			//IL_0183: Unknown result type (might be due to invalid IL or missing references)
			AcolyteBodySwapController component = body.GetComponent<AcolyteBodySwapController>();
			if (!Object.op_Implicit((Object)(object)component))
			{
				return;
			}
			AcolyteFormController component2 = body.GetComponent<AcolyteFormController>();
			GenericSkill val = (Object.op_Implicit((Object)(object)component2) ? component2.transformSkillSlot : null);
			foreach (var (skill, inLoadout, unlocked) in body.Variants(val))
			{
				found.Add(new ExtraSkill(skill, (SkillSlot)3, val.loadoutTitleToken, inLoadout, unlocked));
			}
			List<(SkillSlot, SkillDef)> list = new List<(SkillSlot, SkillDef)>
			{
				((SkillSlot)0, component.primarySkillOverride),
				((SkillSlot)1, component.secondarySkillOverride),
				((SkillSlot)2, component.utilitySkillOverride),
				((SkillSlot)3, component.specialSkillOverride)
			};
			list.AddRange(component.reaperProjectileSkillOverrides.Select((ProjectileSkillOverrideData projectileOverride) => (body.KindOf(projectileOverride.skill), skillOverride: projectileOverride.skillOverride)));
			AddForState(body, typeof(EnterBodySwap), list, component.baseNameToken, found);
			ProjectileSkillOverrideData[] acolyteProjectileSkillOverrides = component.acolyteProjectileSkillOverrides;
			foreach (ProjectileSkillOverrideData val2 in acolyteProjectileSkillOverrides)
			{
				if (Object.op_Implicit((Object)(object)val2.skillOverride) && Object.op_Implicit((Object)(object)val2.skill))
				{
					found.Add(new ExtraSkill(val2.skillOverride, body.KindOf(val2.skill), body.Selected(val2.skill)?.skillNameToken, inLoadout: true, unlocked: true));
				}
			}
		}

		private static void AddChefBoosts(SurvivorLoadout body, List<ExtraSkill> found)
		{
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: Unknown result type (might be due to invalid IL or missing references)
			//IL_014a: Unknown result type (might be due to invalid IL or missing references)
			ChefController component = body.GetComponent<ChefController>();
			if (!Object.op_Implicit((Object)(object)component))
			{
				return;
			}
			(SkillSlot, SkillDef)[] array = new(SkillSlot, SkillDef)[5]
			{
				((SkillSlot)0, component.primarySkillOverrideDef),
				((SkillSlot)1, component.secondarySkillOverrideDef),
				((SkillSlot)1, component.secondaryAltSkillOverrideDef),
				((SkillSlot)2, component.utilitySkillOverrideDef),
				((SkillSlot)2, component.utilityAltSkillOverrideDef)
			};
			GenericSkill[] slots = body.slots;
			foreach (GenericSkill slot in slots)
			{
				foreach (var item4 in from variant in body.Variants(slot)
					where Enters(variant.skill, typeof(YesChef))
					select variant)
				{
					SkillDef item = item4.skill;
					bool item2 = item4.selected;
					bool item3 = item4.unlocked;
					(SkillSlot, SkillDef)[] array2 = array;
					for (int num = 0; num < array2.Length; num++)
					{
						var (val, val2) = array2[num];
						if (Object.op_Implicit((Object)(object)val2))
						{
							SkillDef val3 = body.Selected(val);
							bool flag = Object.op_Implicit((Object)(object)val3) && val3.skillName == val2.skillName;
							bool flag2 = body.IsUnlocked(val, val2.skillName);
							found.Add(new ExtraSkill(val2, val, item.skillNameToken, item2 && flag, item3 && flag2));
						}
					}
				}
			}
		}

		private static void AddCorruptedSkills(SurvivorLoadout body, List<ExtraSkill> found)
		{
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_0164: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)body.GetComponent<VoidSurvivorController>()))
			{
				return;
			}
			CorruptMode val = Configured<CorruptMode>();
			(SkillSlot, SkillDef)[] obj = new(SkillSlot, SkillDef)[4]
			{
				((SkillSlot)0, val.primaryOverrideSkillDef),
				((SkillSlot)1, val.secondaryOverrideSkillDef),
				((SkillSlot)2, val.utilityOverrideSkillDef),
				((SkillSlot)3, val.specialOverrideSkillDef)
			};
			SkillDef val2 = body.slots.Where((GenericSkill slot) => !slot.hideInCharacterSelect && (int)body.KindOf(slot) == -1).Select(body.Selected).FirstOrDefault((Func<SkillDef, bool>)((SkillDef skill) => Object.op_Implicit((Object)(object)skill)));
			(SkillSlot, SkillDef)[] array = obj;
			for (int num = 0; num < array.Length; num++)
			{
				var (val3, val4) = array[num];
				if (!Object.op_Implicit((Object)(object)val4))
				{
					continue;
				}
				SkillDef val5 = body.Selected(val3);
				string descriptionToken = null;
				if (Object.op_Implicit((Object)(object)val5) && val5.skillDescriptionToken == val4.skillDescriptionToken && val5.keywordTokens != null)
				{
					descriptionToken = val5.keywordTokens.FirstOrDefault((string token) => !token.StartsWith("KEYWORD_", StringComparison.Ordinal));
				}
				found.Add(new ExtraSkill(val4, val3, Object.op_Implicit((Object)(object)val2) ? val2.skillNameToken : null, inLoadout: true, unlocked: true, descriptionToken));
			}
		}

		private static void AddForState(SurvivorLoadout body, Type parentState, IEnumerable<(SkillSlot slot, SkillDef skill)> extras, string sourceToken, List<ExtraSkill> found)
		{
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			(SkillSlot, SkillDef)[] array = extras.Where(((SkillSlot slot, SkillDef skill) extra) => Object.op_Implicit((Object)(object)extra.skill)).ToArray();
			GenericSkill[] slots = body.slots;
			foreach (GenericSkill slot in slots)
			{
				foreach (var item4 in from variant in body.Variants(slot)
					where Enters(variant.skill, parentState)
					select variant)
				{
					SkillDef item = item4.skill;
					bool item2 = item4.selected;
					bool item3 = item4.unlocked;
					(SkillSlot, SkillDef)[] array2 = array;
					for (int num2 = 0; num2 < array2.Length; num2++)
					{
						var (slot2, skill) = array2[num2];
						found.Add(new ExtraSkill(skill, slot2, sourceToken ?? item.skillNameToken, item2, item3));
					}
				}
			}
		}

		private static IEnumerable<(SkillSlot, SkillDef)> ScopeShots<TActiveScope>() where TActiveScope : BaseActive, new()
		{
			SkillDef shot = ((BaseActive)Configured<TActiveScope>()).primaryOverride;
			yield return ((SkillSlot)0, shot);
			Type type = (Object.op_Implicit((Object)(object)shot) ? ((SerializableEntityStateType)(ref shot.activationState)).stateType : null);
			if (type != null)
			{
				EntityState obj = EntityStateCatalog.InstantiateState(type);
				BaseFireSnipe val = (BaseFireSnipe)(object)((obj is BaseFireSnipe) ? obj : null);
				if (val != null && val.queueReload)
				{
					yield return ((SkillSlot)0, Configured<Reloading>().primaryOverride);
				}
			}
		}

		private static bool Enters(SkillDef skill, Type state)
		{
			Type stateType = ((SerializableEntityStateType)(ref skill.activationState)).stateType;
			if (stateType != null)
			{
				if (!(stateType == state))
				{
					return stateType.IsSubclassOf(state);
				}
				return true;
			}
			return false;
		}

		private static T Configured<T>() where T : EntityState, new()
		{
			return new T();
		}

		private static void Collect(string source, Action collect)
		{
			try
			{
				collect();
			}
			catch (Exception arg)
			{
				Log.Warning($"Could not read the extra skills from {source}: {arg}");
			}
		}
	}
	internal static class ExtraSkillsTab
	{
		internal class ExtraSkillsView : MonoBehaviour
		{
			private RectTransform panel;

			private TMP_Text intro;

			private LanguageTextMeshController hoverText;

			private UILayerKey requiredTopLayer;

			private List<GameObject> rows = new List<GameObject>();

			internal void Init(RectTransform panel, TMP_Text intro, LanguageTextMeshController hoverText, UILayerKey requiredTopLayer)
			{
				this.panel = panel;
				this.intro = intro;
				this.hoverText = hoverText;
				this.requiredTopLayer = requiredTopLayer;
			}

			internal void Rebuild(CharacterSelectController controller)
			{
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				//IL_000b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0046: Unknown result type (might be due to invalid IL or missing references)
				//IL_0054: 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_005e: 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_0074: Unknown result type (might be due to invalid IL or missing references)
				//IL_0079: Unknown result type (might be due to invalid IL or missing references)
				//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
				//IL_008e: Unknown result type (might be due to invalid IL or missing references)
				List<ExtraSkill> extras = new List<ExtraSkill>();
				Color color = Color.gray;
				LocalUser localUser = controller.localUser;
				NetworkUser val = ((localUser != null) ? localUser.currentNetworkUser : null);
				SurvivorDef val2 = (Object.op_Implicit((Object)(object)val) ? val.GetSurvivorPreference() : null);
				if (Object.op_Implicit((Object)(object)val2) && localUser.userProfile != null && SurvivorCatalog.SurvivorIsUnlockedOnThisClient(val2.survivorIndex))
				{
					BodyIndex bodyIndexFromSurvivorIndex = SurvivorCatalog.GetBodyIndexFromSurvivorIndex(val2.survivorIndex);
					CharacterBody bodyPrefabBodyComponent = BodyCatalog.GetBodyPrefabBodyComponent(bodyIndexFromSurvivorIndex);
					if (Object.op_Implicit((Object)(object)bodyPrefabBodyComponent))
					{
						color = bodyPrefabBodyComponent.bodyColor;
					}
					Loadout val3 = Loadout.RequestInstance();
					try
					{
						localUser.userProfile.CopyLoadout(val3);
						extras = ExtraSkillCatalog.For(SurvivorLoadout.FromLoadout(bodyIndexFromSurvivorIndex, val3, localUser.userProfile), includeOtherLoadouts: true);
					}
					finally
					{
						Loadout.ReturnInstance(val3);
					}
				}
				foreach (GameObject row in rows)
				{
					Object.Destroy((Object)(object)row);
				}
				List<SkillLine> list = SkillLine.FromExtras(extras);
				rows = SkillLines.Build(panel, list, color, hoverText, requiredTopLayer);
				if (Object.op_Implicit((Object)(object)intro))
				{
					intro.SetText(Language.GetString((list.Count > 0) ? "SUBSKILLSDISPLAY_EXTRA_INTRO" : "SUBSKILLSDISPLAY_EXTRA_NONE"), true);
					intro.transform.SetAsFirstSibling();
				}
			}
		}

		[Serializable]
		[CompilerGenerated]
		private sealed class <>c
		{
			public static readonly <>c <>9 = new <>c();

			public static hook_Awake <>9__2_0;

			public static hook_RebuildLocal <>9__2_1;

			public static Predicate<Header> <>9__3_0;

			internal void <Init>b__2_0(orig_Awake orig, CharacterSelectController self)
			{
				orig.Invoke(self);
				Build(self);
				LocalUser firstLocalUser = LocalUserManager.GetFirstLocalUser();
				UserProfile val = ((firstLocalUser != null) ? firstLocalUser.userProfile : null);
				if (logPending && val != null)
				{
					logPending = false;
					ExtraSkillCatalog.LogAll(val);
				}
			}

			internal void <Init>b__2_1(orig_RebuildLocal orig, CharacterSelectController self, bool deselectButtons)
			{
				orig.Invoke(self, deselectButtons);
				ExtraSkillsView component = ((Component)self).GetComponent<ExtraSkillsView>();
				if (Object.op_Implicit((Object)(object)component))
				{
					component.Rebuild(self);
				}
			}

			internal bool <Build>b__3_0(Header header)
			{
				//IL_0000: 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)
				if (Object.op_Implicit((Object)(object)header.headerRoot))
				{
					return Object.op_Implicit((Object)(object)header.headerRoot.GetComponent<LoadoutPanelController>());
				}
				return false;
			}
		}

		private const string HeaderName = "SubSkillsDisplayExtra";

		private static bool logPending;

		internal static void Init(bool logExtraSkills)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Expected O, but got Unknown
			//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)
			//IL_0049: Expected O, but got Unknown
			logPending = logExtraSkills;
			object obj = <>c.<>9__2_0;
			if (obj == null)
			{
				hook_Awake val = delegate(orig_Awake orig, CharacterSelectController self)
				{
					orig.Invoke(self);
					Build(self);
					LocalUser firstLocalUser = LocalUserManager.GetFirstLocalUser();
					UserProfile val3 = ((firstLocalUser != null) ? firstLocalUser.userProfile : null);
					if (logPending && val3 != null)
					{
						logPending = false;
						ExtraSkillCatalog.LogAll(val3);
					}
				};
				<>c.<>9__2_0 = val;
				obj = (object)val;
			}
			CharacterSelectController.Awake += (hook_Awake)obj;
			object obj2 = <>c.<>9__2_1;
			if (obj2 == null)
			{
				hook_RebuildLocal val2 = delegate(orig_RebuildLocal orig, CharacterSelectController self, bool deselectButtons)
				{
					orig.Invoke(self, deselectButtons);
					ExtraSkillsView component = ((Component)self).GetComponent<ExtraSkillsView>();
					if (Object.op_Implicit((Object)(object)component))
					{
						component.Rebuild(self);
					}
				};
				<>c.<>9__2_1 = val2;
				obj2 = (object)val2;
			}
			CharacterSelectController.RebuildLocal += (hook_RebuildLocal)obj2;
		}

		private static void Build(CharacterSelectController controller)
		{
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_025b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0265: Expected O, but got Unknown
			//IL_0277: Unknown result type (might be due to invalid IL or missing references)
			//IL_0281: Expected O, but got Unknown
			//IL_02ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_0319: Unknown result type (might be due to invalid IL or missing references)
			//IL_0334: Unknown result type (might be due to invalid IL or missing references)
			//IL_0341: Unknown result type (might be due to invalid IL or missing references)
			//IL_034c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0365: Unknown result type (might be due to invalid IL or missing references)
			//IL_0397: Expected O, but got Unknown
			RectTransform skillPanel = controller.skillStripContainer;
			HGHeaderNavigationController navigation = null;
			int num = -1;
			if (Object.op_Implicit((Object)(object)skillPanel))
			{
				HGHeaderNavigationController[] componentsInChildren = ((Component)controller).GetComponentsInChildren<HGHeaderNavigationController>(true);
				foreach (HGHeaderNavigationController val in componentsInChildren)
				{
					num = val.headers.FindIndex((Header header) => (Object)(object)header.headerRoot == (Object)(object)((Component)skillPanel).gameObject);
					if (num >= 0)
					{
						navigation = val;
						break;
					}
				}
			}
			if (!Object.op_Implicit((Object)(object)navigation))
			{
				Log.Warning("Character select has no Skills tab to model the Extra Skills tab on; skipping it.");
				return;
			}
			ModTokens.Register();
			Header val2 = navigation.headers[num];
			int num2 = navigation.headers.FindIndex((Header header) => Object.op_Implicit((Object)(object)header.headerRoot) && Object.op_Implicit((Object)(object)header.headerRoot.GetComponent<LoadoutPanelController>()));
			MPButton headerButton = navigation.headers[(num2 >= 0) ? num2 : (navigation.headers.Count - 1)].headerButton;
			GameObject val3 = Object.Instantiate<GameObject>(((Component)skillPanel).gameObject, ((Transform)skillPanel).parent, false);
			((Object)val3).name = "ExtraSkillPanel";
			val3.SetActive(false);
			VerticalLayoutGroup component = val3.GetComponent<VerticalLayoutGroup>();
			if (Object.op_Implicit((Object)(object)component))
			{
				((HorizontalOrVerticalLayoutGroup)component).childControlHeight = true;
				((HorizontalOrVerticalLayoutGroup)component).childForceExpandHeight = false;
			}
			GameObject val4 = UiHierarchy.Counterpart((Transform)(object)skillPanel, val3.transform, controller.skillStripPrefab);
			HGButton val5 = (Object.op_Implicit((Object)(object)val4) ? val4.GetComponent<HGButton>() : null);
			object obj;
			if (!Object.op_Implicit((Object)(object)val4))
			{
				obj = null;
			}
			else
			{
				Transform obj2 = val4.transform.Find("Inner/SkillDescriptionPanel/SkillDescription");
				obj = ((obj2 != null) ? ((Component)obj2).GetComponent<TMP_Text>() : null);
			}
			TMP_Text val6 = (TMP_Text)obj;
			TMP_Text val7 = (Object.op_Implicit((Object)(object)val6) ? UiHierarchy.AddText(val6, val3.transform, "Intro") : null);
			if (Object.op_Implicit((Object)(object)val7))
			{
				val7.transform.SetAsFirstSibling();
			}
			MPButton headerButton2 = val2.headerButton;
			GameObject val8 = Object.Instantiate<GameObject>(((Component)headerButton2).gameObject, ((Component)headerButton2).transform.parent, false);
			((Object)val8).name = "GenericMenuButton (Extra Skills)";
			val8.transform.SetSiblingIndex(((Component)headerButton).transform.GetSiblingIndex() + 1);
			MPButton button = val8.GetComponent<MPButton>();
			((Button)button).onClick = new ButtonClickedEvent();
			((UnityEvent)((Button)button).onClick).AddListener((UnityAction)delegate
			{
				navigation.ChooseHeaderByButton(button);
			});
			LanguageTextMeshController component2 = val8.GetComponent<LanguageTextMeshController>();
			if (Object.op_Implicit((Object)(object)component2))
			{
				component2.token = "SUBSKILLSDISPLAY_TAB_EXTRA";
			}
			controller.primaryColorImages = WithCopies<Image>(controller.primaryColorImages, ((Component)headerButton2).transform, val8.transform);
			controller.primaryColorTexts = WithCopies<TextMeshProUGUI>(controller.primaryColorTexts, ((Component)headerButton2).transform, val8.transform);
			navigation.headers.Add(new Header
			{
				headerButton = button,
				headerName = "SubSkillsDisplayExtra",
				tmpHeaderText = UiHierarchy.Counterpart<TextMeshProUGUI>(((Component)headerButton2).transform, val8.transform, val2.tmpHeaderText),
				headerRoot = val3,
				AreConsolePlatformsSupported = val2.AreConsolePlatformsSupported,
				isPrimaryPlayerOnly = val2.isPrimaryPlayerOnly
			});
			((Component)controller).gameObject.AddComponent<ExtraSkillsView>().Init((RectTransform)val3.transform, val7, Object.op_Implicit((Object)(object)val5) ? val5.hoverLanguageTextMeshController : null, Object.op_Implicit((Object)(object)val5) ? ((MPButton)val5).requiredTopLayer : null);
			Log.Info("Added the Extra Skills tab to character select.");
		}

		private static T[] WithCopies<T>(T[] painted, Transform original, Transform copy) where T : Component
		{
			if (painted == null)
			{
				return null;
			}
			List<T> list = new List<T>(painted);
			foreach (T val in painted)
			{
				T val2 = ((Object.op_Implicit((Object)(object)val) && ((Component)val).transform.IsChildOf(original)) ? UiHierarchy.Counterpart(original, copy, val) : default(T));
				if (Object.op_Implicit((Object)(object)val2))
				{
					list.Add(val2);
				}
			}
			return list.ToArray();
		}
	}
	internal static class Keywords
	{
		private static readonly Regex keywordName = new Regex("<style=cKeywordName>(.*?)</style>", RegexOptions.Singleline);

		private static readonly Regex highlighted = new Regex("<style=c[A-Za-z]+>(.*?)</style>", RegexOptions.Singleline);

		private static readonly Regex tags = new Regex("<[^>]+>");

		private static Dictionary<string, string> tokenByName;

		private static string builtForLanguage;

		internal static List<string> For(string[] ownTokens, string description)
		{
			Build();
			List<string> list = (ownTokens ?? Array.Empty<string>()).Where((string token) => !string.IsNullOrEmpty(token)).Distinct().ToList();
			foreach (Match item in highlighted.Matches(description ?? string.Empty))
			{
				string input = tags.Replace(item.Groups[1].Value, string.Empty);
				foreach (KeyValuePair<string, string> item2 in tokenByName)
				{
					if (item2.Value != null && !list.Contains(item2.Value) && Regex.IsMatch(input, "\\b" + Regex.Escape(item2.Key) + "(s|es|d|ed)?\\b", RegexOptions.IgnoreCase))
					{
						list.Add(item2.Value);
					}
				}
			}
			return list;
		}

		private static void Build()
		{
			if (tokenByName != null && builtForLanguage == Language.currentLanguageName)
			{
				return;
			}
			builtForLanguage = Language.currentLanguageName;
			tokenByName = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
			foreach (string item in (from token in SkillCatalog.allSkillDefs.Where((SkillDef skill) => Object.op_Implicit((Object)(object)skill) && skill.keywordTokens != null).SelectMany((SkillDef skill) => skill.keywordTokens).Concat(from body in BodyCatalog.allBodyPrefabBodyBodyComponents
					where Object.op_Implicit((Object)(object)body) && Object.op_Implicit((Object)(object)((Component)body).GetComponent<SkillLocator>())
					select ((Component)body).GetComponent<SkillLocator>().passiveSkill.keywordToken)
				where !string.IsNullOrEmpty(token)
				select token).Distinct())
			{
				Match match = keywordName.Match(Language.GetString(item));
				string text = (match.Success ? tags.Replace(match.Groups[1].Value, string.Empty).Trim() : null);
				if (!string.IsNullOrEmpty(text) && text.Length >= 3)
				{
					if (!tokenByName.TryGetValue(text, out var value))
					{
						tokenByName[text] = item;
					}
					else if (value != null && value != item)
					{
						bool flag = value.StartsWith("KEYWORD_", StringComparison.Ordinal);
						bool flag2 = item.StartsWith("KEYWORD_", StringComparison.Ordinal);
						tokenByName[text] = ((flag == flag2) ? null : (flag ? value : item));
					}
				}
			}
		}
	}
	internal static class Log
	{
		private static ManualLogSource _source;

		internal static void Init(ManualLogSource source)
		{
			_source = source;
		}

		internal static void Debug(object data)
		{
			_source.LogDebug(data);
		}

		internal static void Info(object data)
		{
			_source.LogInfo(data);
		}

		internal static void Message(object data)
		{
			_source.LogMessage(data);
		}

		internal static void Warning(object data)
		{
			_source.LogWarning(data);
		}

		internal static void Error(object data)
		{
			_source.LogError(data);
		}

		internal static void Fatal(object data)
		{
			_source.LogFatal(data);
		}
	}
	internal static class ModdedExtraSkills
	{
		private sealed class ModdedSurvivor
		{
			internal BodyIndex bodyIndex;

			internal GameObject body;

			internal string tokenStem;

			internal HashSet<string> stateNamespaces;

			internal HashSet<Assembly> assemblies;

			internal readonly Dictionary<SkillDef, SkillSlot> unlinked = new Dictionary<SkillDef, SkillSlot>();

			internal void AddUnlinked(SkillDef skill, SkillSlot slot)
			{
				//IL_001b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0010: Unknown result type (might be due to invalid IL or missing references)
				//IL_0012: Invalid comparison between Unknown and I4
				if (!unlinked.TryGetValue(skill, out var value) || (int)value == -1)
				{
					unlinked[skill] = slot;
				}
			}
		}

		private static readonly string[] slotWords = new string[4] { "primary", "secondary", "utility", "special" };

		private static Dictionary<BodyIndex, ModdedSurvivor> survivors;

		private static HashSet<SkillDef> loadoutChoices;

		private static readonly Dictionary<Type, (SkillDef skill, SkillSlot slot)[]> skillsNamedByState = new Dictionary<Type, (SkillDef, SkillSlot)[]>();

		internal static void Add(SurvivorLoadout body, List<ExtraSkill> found)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cb: 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)
			//IL_00cc: 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_00d1: Invalid comparison between Unknown and I4
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0261: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Invalid comparison between Unknown and I4
			//IL_0108: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0126: Unknown result type (might be due to invalid IL or missing references)
			if (survivors == null)
			{
				FindSurvivors();
			}
			if (!survivors.TryGetValue(body.bodyIndex, out var value))
			{
				return;
			}
			Dictionary<SkillDef, (SkillSlot slot, string sourceToken, bool selected, bool unlocked)> links = new Dictionary<SkillDef, (SkillSlot, string, bool, bool)>();
			GenericSkill[] slots = body.slots;
			foreach (GenericSkill slot in slots)
			{
				foreach (var (val, flag, flag2) in body.Variants(slot))
				{
					foreach (var item3 in from entry in SkillsNamedBy(((SerializableEntityStateType)(ref val.activationState)).stateType)
						where IsCandidate(entry.skill)
						select entry)
					{
						SkillDef item = item3.skill;
						SkillSlot item2 = item3.slot;
						SkillSlot value2;
						SkillSlot val2 = (SkillSlot)(((int)item2 != -1) ? ((int)item2) : (value.unlinked.TryGetValue(item, out value2) ? ((int)value2) : (-1)));
						links[item] = (links.TryGetValue(item, out (SkillSlot, string, bool, bool) value3) ? (((int)value3.Item1 != -1) ? value3.Item1 : val2, flag ? val.skillNameToken : value3.Item2, value3.Item3 || flag, value3.Item4 || flag2) : (val2, val.skillNameToken, flag, flag2));
					}
				}
			}
			foreach (KeyValuePair<SkillDef, (SkillSlot, string, bool, bool)> item4 in links)
			{
				found.Add(new ExtraSkill(item4.Key, item4.Value.Item1, item4.Value.Item2, item4.Value.Item3, item4.Value.Item4));
			}
			foreach (KeyValuePair<SkillDef, SkillSlot> item5 in value.unlinked.Where((KeyValuePair<SkillDef, SkillSlot> entry) => !links.ContainsKey(entry.Key)))
			{
				found.Add(new ExtraSkill(item5.Key, item5.Value, null, inLoadout: true, unlocked: true));
			}
		}

		private static void FindSurvivors()
		{
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_032f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0133: Unknown result type (might be due to invalid IL or missing references)
			//IL_0135: Unknown result type (might be due to invalid IL or missing references)
			//IL_0138: Invalid comparison between Unknown and I4
			//IL_013f: Unknown result type (might be due to invalid IL or missing references)
			//IL_012e: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0404: Unknown result type (might be due to invalid IL or missing references)
			//IL_040a: Unknown result type (might be due to invalid IL or missing references)
			//IL_014d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0151: Unknown result type (might be due to invalid IL or missing references)
			survivors = new Dictionary<BodyIndex, ModdedSurvivor>();
			loadoutChoices = new HashSet<SkillDef>(from variant in SkillCatalog.allSkillFamilies.Where((SkillFamily family) => Object.op_Implicit((Object)(object)family)).SelectMany((SkillFamily family) => family.variants)
				select variant.skillDef into val3
				where Object.op_Implicit((Object)(object)val3)
				select val3);
			foreach (ReadOnlyContentPack item in ((IEnumerable<ReadOnlyContentPack>)(object)ContentManager.allLoadedContentPacks).Where((ReadOnlyContentPack pack) => !IsVanilla(((ReadOnlyContentPack)(ref pack)).identifier)))
			{
				ReadOnlyContentPack current = item;
				AssetEnumerator<SurvivorDef> enumerator2 = ((ReadOnlyContentPack)(ref current)).survivorDefs.GetEnumerator();
				try
				{
					while (enumerator2.MoveNext())
					{
						SurvivorDef current2 = enumerator2.Current;
						BodyIndex val = (BodyIndex)((!Object.op_Implicit((Object)(object)current2) || current2.hidden || !Object.op_Implicit((Object)(object)current2.bodyPrefab)) ? (-1) : ((int)BodyCatalog.FindBodyIndex(current2.bodyPrefab)));
						if ((int)val != -1 && !survivors.ContainsKey(val))
						{
							survivors[val] = Describe(current2, val);
						}
					}
				}
				finally
				{
					((IDisposable)enumerator2/*cast due to .constrained prefix*/).Dispose();
				}
			}
			if (survivors.Count == 0)
			{
				return;
			}
			foreach (Assembly assembly in survivors.Values.SelectMany((ModdedSurvivor survivor) => survivor.assemblies).Distinct())
			{
				List<ModdedSurvivor> list = survivors.Values.Where((ModdedSurvivor survivor) => survivor.assemblies.Contains(assembly)).ToList();
				List<OverrideCallScanner.Found> source;
				try
				{
					source = OverrideCallScanner.Scan(assembly, list.Select((ModdedSurvivor owner) => owner.body).ToArray());
				}
				catch (Exception ex)
				{
					Log.Warning("Could not read " + assembly.GetName().Name + "'s code for skill overrides: " + ex.Message);
					continue;
				}
				foreach (OverrideCallScanner.Found swap in source.Where((OverrideCallScanner.Found found) => IsCandidate(found.skill)))
				{
					(Object.op_Implicit((Object)(object)swap.body) ? list.FirstOrDefault((ModdedSurvivor survivor) => (Object)(object)survivor.body == (Object)(object)swap.body) : ((list.Count == 1) ? list[0] : Owner(swap.skill, list)))?.AddUnlinked(swap.skill, swap.slot);
				}
			}
			foreach (ModdedSurvivor value in survivors.Values)
			{
				MonoBehaviour[] componentsInChildren = value.body.GetComponentsInChildren<MonoBehaviour>(true);
				foreach (MonoBehaviour val2 in componentsInChildren)
				{
					if (!Object.op_Implicit((Object)(object)val2) || val2 is GenericSkill)
					{
						continue;
					}
					foreach (var (skill, slot) in from entry in SkillsIn(val2)
						where IsCandidate(entry.skill)
						select entry)
					{
						value.AddUnlinked(skill, slot);
					}
				}
			}
			Log.Info($"Searched {survivors.Count} modded survivor(s) for extra skills; found {survivors.Values.Sum((ModdedSurvivor survivor) => survivor.unlinked.Count)} outside their loadout states.");
		}

		private static ModdedSurvivor Describe(SurvivorDef survivorDef, BodyIndex bodyIndex)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0109: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Unknown result type (might be due to invalid IL or missing references)
			GameObject bodyPrefab = BodyCatalog.GetBodyPrefab(bodyIndex);
			CharacterBody bodyPrefabBodyComponent = BodyCatalog.GetBodyPrefabBodyComponent(bodyIndex);
			Type[] array = (from variant in (BodyCatalog.GetBodyPrefabSkillSlots(bodyIndex) ?? Array.Empty<GenericSkill>()).Where((GenericSkill slot) => Object.op_Implicit((Object)(object)slot) && Object.op_Implicit((Object)(object)slot.skillFamily)).SelectMany((GenericSkill slot) => slot.skillFamily.variants)
				select (!Object.op_Implicit((Object)(object)variant.skillDef)) ? null : ((SerializableEntityStateType)(ref variant.skillDef.activationState)).stateType into type
				where type != null
				select type).ToArray();
			IEnumerable<Type> second = from component in bodyPrefab.GetComponentsInChildren<MonoBehaviour>(true)
				where Object.op_Implicit((Object)(object)component)
				select ((object)component).GetType();
			return new ModdedSurvivor
			{
				bodyIndex = bodyIndex,
				body = bodyPrefab,
				tokenStem = (TokenStem(Object.op_Implicit((Object)(object)bodyPrefabBodyComponent) ? bodyPrefabBodyComponent.baseNameToken : null) ?? TokenStem(survivorDef.displayNameToken)),
				stateNamespaces = new HashSet<string>(from type in array
					select type.Namespace into name
					where !string.IsNullOrEmpty(name)
					select name),
				assemblies = new HashSet<Assembly>((from type in array.Concat(second)
					select type.Assembly).Where(IsModCode))
			};
		}

		private static bool IsCandidate(SkillDef skill)
		{
			if (!Object.op_Implicit((Object)(object)skill) || loadoutChoices.Contains(skill))
			{
				return false;
			}
			string skillNameToken = skill.skillNameToken;
			if (!string.IsNullOrEmpty(skillNameToken))
			{
				return Language.GetString(skillNameToken) != skillNameToken;
			}
			return false;
		}

		private static ModdedSurvivor Owner(SkillDef skill, List<ModdedSurvivor> candidates)
		{
			List<ModdedSurvivor> list = (from survivor in candidates
				where survivor.tokenStem != null && skill.skillNameToken.StartsWith(survivor.tokenStem, StringComparison.OrdinalIgnoreCase)
				orderby survivor.tokenStem.Length descending
				select survivor).ToList();
			if (list.Count == 1 || (list.Count > 1 && list[0].tokenStem.Length > list[1].tokenStem.Length))
			{
				return list[0];
			}
			string stateNamespace = ((SerializableEntityStateType)(ref skill.activationState)).stateType?.Namespace;
			List<ModdedSurvivor> list2 = ((stateNamespace == null) ? new List<ModdedSurvivor>() : candidates.Where((ModdedSurvivor survivor) => survivor.stateNamespaces.Contains(stateNamespace)).ToList());
			if (list2.Count != 1)
			{
				return null;
			}
			return list2[0];
		}

		private static string TokenStem(string token)
		{
			if (string.IsNullOrEmpty(token))
			{
				return null;
			}
			int num = token.LastIndexOf("BODY", StringComparison.OrdinalIgnoreCase);
			if (num < 0)
			{
				num = token.LastIndexOf("NAME", StringComparison.OrdinalIgnoreCase);
			}
			if (num < 3)
			{
				return null;
			}
			return token.Substring(0, num);
		}

		private static SkillSlot SlotFromName(string name)
		{
			for (int i = 0; i < slotWords.Length; i++)
			{
				if (name.IndexOf(slotWords[i], StringComparison.OrdinalIgnoreCase) >= 0)
				{
					return (SkillSlot)(sbyte)i;
				}
			}
			return (SkillSlot)(-1);
		}

		private static bool IsVanilla(string identifier)
		{
			if (identifier != null && identifier.StartsWith("RoR2.", StringComparison.Ordinal))
			{
				return identifier != "RoR2.LegacyModContent";
			}
			return false;
		}

		private static bool IsModCode(Assembly assembly)
		{
			if (assembly.IsDynamic || assembly == typeof(ModdedExtraSkills).Assembly)
			{
				return false;
			}
			try
			{
				string location = assembly.Location;
				return !string.IsNullOrEmpty(location) && Path.GetFullPath(location).StartsWith(Path.GetFullPath(Paths.PluginPath), StringComparison.OrdinalIgnoreCase);
			}
			catch (NotSupportedException)
			{
				return false;
			}
		}

		private static (SkillDef skill, SkillSlot slot)[] SkillsNamedBy(Type stateType)
		{
			if (stateType == null)
			{
				return Array.Empty<(SkillDef, SkillSlot)>();
			}
			if (skillsNamedByState.TryGetValue(stateType, out (SkillDef, SkillSlot)[] value))
			{
				return value;
			}
			List<(SkillDef, SkillSlot)> list = new List<(SkillDef, SkillSlot)>();
			try
			{
				List<FieldInfo> list2 = new List<FieldInfo>();
				Type type = stateType;
				while (type != null && type != typeof(EntityState))
				{
					list2.AddRange(from fieldInfo in type.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)
						where OverrideCallScanner.HoldsSkills(fieldInfo.FieldType)
						select fieldInfo);
					type = type.BaseType;
				}
				EntityState val = (list2.Any((FieldInfo fieldInfo) => !fieldInfo.IsStatic) ? EntityStateCatalog.InstantiateState(stateType) : null);
				foreach (FieldInfo field in list2)
				{
					object value2 = (field.IsStatic ? field.GetValue(null) : ((val != null) ? field.GetValue(val) : null));
					list.AddRange(from skill in Skills(value2)
						select (skill: skill, SlotFromName(field.Name)));
				}
			}
			catch (Exception ex)
			{
				Log.Debug("Could not read the skills " + stateType.FullName + " names: " + ex.Message);
			}
			(SkillDef, SkillSlot)[] array = list.Where(((SkillDef, SkillSlot) entry) => Object.op_Implicit((Object)(object)entry.Item1)).Distinct().ToArray();
			skillsNamedByState[stateType] = array;
			return array;
		}

		private static IEnumerable<(SkillDef skill, SkillSlot slot)> SkillsIn(MonoBehaviour component)
		{
			//IL_013c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0141: Unknown result type (might be due to invalid IL or missing references)
			List<(SkillDef, SkillSlot)> list = new List<(SkillDef, SkillSlot)>();
			try
			{
				Type type = ((object)component).GetType();
				while (type != null && type != typeof(MonoBehaviour))
				{
					FieldInfo[] fields = type.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					foreach (FieldInfo field in fields)
					{
						if (OverrideCallScanner.HoldsSkills(field.FieldType))
						{
							list.AddRange(from skill in Skills(field.GetValue(component))
								select (skill: skill, SlotFromName(field.Name)));
							continue;
						}
						FieldInfo[] array = (from inner in DataType(field.FieldType)?.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
							where OverrideCallScanner.HoldsSkills(inner.FieldType)
							select inner).ToArray();
						if (array == null || array.Length == 0)
						{
							continue;
						}
						object value = field.GetValue(component);
						IEnumerable enumerable3;
						if (!(value is IEnumerable enumerable))
						{
							IEnumerable enumerable2 = new object[1] { value };
							enumerable3 = enumerable2;
						}
						else
						{
							enumerable3 = enumerable;
						}
						foreach (object item in enumerable3)
						{
							if (item == null)
							{
								continue;
							}
							FieldInfo[] array2 = array;
							foreach (FieldInfo fieldInfo in array2)
							{
								SkillSlot slot = SlotFromName(fieldInfo.Name);
								list.AddRange(from skill in Skills(fieldInfo.GetValue(item))
									select (skill: skill, ((int)slot != -1) ? slot : SlotFromName(field.Name)));
							}
						}
					}
					type = type.BaseType;
				}
			}
			catch (Exception ex)
			{
				Log.Debug("Could not read the skills " + ((object)component).GetType().FullName + " names: " + ex.Message);
			}
			return list;
		}

		private static Type DataType(Type type)
		{
			Type type2 = (type.IsArray ? type.GetElementType() : ((type.IsGenericType && typeof(IEnumerable).IsAssignableFrom(type)) ? ((type.GetGenericArguments().Length == 1) ? type.GetGenericArguments()[0] : null) : type));
			if (type2 != null && !type2.IsPrimitive && !type2.IsEnum && type2 != typeof(string) && !typeof(Object).IsAssignableFrom(type2) && !typeof(Delegate).IsAssignableFrom(type2) && (type2.IsValueType || type2.IsClass))
			{
				string? text = type2.Namespace;
				if (text == null || !text.StartsWith("System", StringComparison.Ordinal))
				{
					string? text2 = type2.Namespace;
					if (text2 == null || !text2.StartsWith("UnityEngine", StringComparison.Ordinal))
					{
						return type2;
					}
				}
			}
			return null;
		}

		private static IEnumerable<SkillDef> Skills(object value)
		{
			SkillDef val = (SkillDef)((value is SkillDef) ? value : null);
			if (val == null)
			{
				if (!(value is IEnumerable source))
				{
					return Enumerable.Empty<SkillDef>();
				}
				return source.OfType<SkillDef>();
			}
			return (IEnumerable<SkillDef>)(object)new SkillDef[1] { val };
		}
	}
	internal static class ModTokens
	{
		internal const string ExtraTab = "SUBSKILLSDISPLAY_TAB_EXTRA";

		internal const string ExtraIntro = "SUBSKILLSDISPLAY_EXTRA_INTRO";

		internal const string ExtraNone = "SUBSKILLSDISPLAY_EXTRA_NONE";

		internal const string PauseAbilities = "SUBSKILLSDISPLAY_PAUSE_ABILITIES";

		internal const string PauseIntro = "SUBSKILLSDISPLAY_PAUSE_INTRO";

		internal const string PassiveLine = "SUBSKILLSDISPLAY_LINE_PASSIVE";

		internal const string Back = "SUBMENU_BACK";

		internal static void Register()
		{
			Language english = Language.english;
			if (english != null)
			{
				english.SetStringByToken("SUBSKILLSDISPLAY_TAB_EXTRA", "Extra Skills");
				english.SetStringByToken("SUBSKILLSDISPLAY_EXTRA_INTRO", "<style=cSub>What this survivor's skills turn into during a run, in the line of the skill they replace. Hover one for details. Skills from choices you haven't unlocked are hidden.</style>");
				english.SetStringByToken("SUBSKILLSDISPLAY_EXTRA_NONE", "<style=cSub>This survivor has no extra skills: everything it uses is on the Skills tab.</style>");
				english.SetStringByToken("SUBSKILLSDISPLAY_PAUSE_ABILITIES", "Abilities");
				english.SetStringByToken("SUBSKILLSDISPLAY_PAUSE_INTRO", "<style=cSub>Your skills, each followed by what it turns into during the run. Hover one for details.</style>");
				english.SetStringByToken("SUBSKILLSDISPLAY_LINE_PASSIVE", "Passive");
			}
		}
	}
	internal static class OverrideCallScanner
	{
		internal readonly struct Found
		{
			internal readonly SkillDef skill;

			internal readonly SkillSlot slot;

			internal readonly GameObject body;

			internal Found(SkillDef skill, SkillSlot slot, GameObject body)
			{
				//IL_0008: Unknown result type (might be due to invalid IL or missing references)
				//IL_0009: Unknown result type (might be due to invalid IL or missing references)
				this.skill = skill;
				this.slot = slot;
				this.body = body;
			}
		}

		private const BindingFlags Declared = BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;

		internal static List<Found> Scan(Assembly assembly, GameObject[] bodies)
		{
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			List<Found> list = new List<Found>();
			ModuleDefinition val = ModuleDefinition.ReadModule(assembly.Location);
			try
			{
				foreach (SkillOverrideSites.Site item in SkillOverrideSites.Find(val))
				{
					try
					{
						SkillSlot result;
						SkillSlot slot = (SkillSlot)((!Enum.TryParse<SkillSlot>(item.slot, out result)) ? (-1) : ((int)result));
						Read(assembly, bodies, item.load, slot, list);
					}
					catch (Exception ex)
					{
						Log.Debug("Could not read a skill override in " + ((MemberReference)item.method).FullName + ": " + ex.Message);
					}
				}
				return list;
			}
			finally
			{
				((IDisposable)val)?.Dispose();
			}
		}

		private static void Read(Assembly assembly, GameObject[] bodies, Instruction load, SkillSlot slot, List<Found> found)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Invalid comparison between Unknown and I4
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Invalid comparison between Unknown and I4
			//IL_0088: 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_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			OpCode opCode = load.OpCode;
			Code code = ((OpCode)(ref opCode)).Code;
			if ((int)code != 120)
			{
				if ((int)code == 123)
				{
					object operand = load.Operand;
					Add(ToReflection(assembly, (FieldReference)((operand is FieldReference) ? operand : null))?.GetValue(null), slot, null, found);
					return;
				}
				MethodInfo getter = default(MethodInfo);
				ref MethodInfo reference = ref getter;
				object operand2 = load.Operand;
				reference = ToReflection(assembly, (MethodReference)((operand2 is MethodReference) ? operand2 : null));
				if (!(getter != null) || getter.GetParameters().Length != 0 || !HoldsSkills(getter.ReturnType))
				{
					return;
				}
				if (getter.IsStatic)
				{
					Add(getter.Invoke(null, null), slot, null, found);
					return;
				}
				ReadFromInstances(assembly, bodies, getter.DeclaringType, (object instance) => getter.Invoke(instance, null), slot, found);
				return;
			}
			FieldInfo field = default(FieldInfo);
			ref FieldInfo reference2 = ref field;
			object operand3 = load.Operand;
			reference2 = ToReflection(assembly, (FieldReference)((operand3 is FieldReference) ? operand3 : null));
			if (field != null)
			{
				ReadFromInstances(assembly, bodies, field.DeclaringType, (object instance) => field.GetValue(instance), slot, found);
			}
		}

		private static void ReadFromInstances(Assembly assembly, GameObject[] bodies, Type type, Func<object, object> read, SkillSlot slot, List<Found> found)
		{
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			if (typeof(EntityState).IsAssignableFrom(type))
			{
				IEnumerable<Type> enumerable2;
				if (!type.IsAbstract)
				{
					IEnumerable<Type> enumerable = new Type[1] { type };
					enumerable2 = enumerable;
				}
				else
				{
					enumerable2 = from candidate in assembly.GetTypes()
						where !candidate.IsAbstract && type.IsAssignableFrom(candidate)
						select candidate;
				}
				{
					foreach (Type item in enumerable2)
					{
						Add(read(EntityStateCatalog.InstantiateState(item)), slot, null, found);
					}
					return;
				}
			}
			if (!typeof(Component).IsAssignableFrom(type))
			{
				return;
			}
			foreach (GameObject val in bodies)
			{
				Component[] componentsInChildren = val.GetComponentsInChildren(type, true);
				foreach (Component arg in componentsInChildren)
				{
					Add(read(arg), slot, val, found);
				}
			}
		}

		private static void Add(object value, SkillSlot slot, GameObject body, List<Found> found)
		{
			//IL_0007: 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_0021: Unknown result type (might be due to invalid IL or missing references)
			SkillDef val = (SkillDef)((value is SkillDef) ? value : null);
			if (val != null)
			{
				found.Add(new Found(val, slot, body));
			}
			else if (value is IEnumerable source)
			{
				found.AddRange(from item in source.OfType<SkillDef>()
					where Object.op_Implicit((Object)(object)item)
					select new Found(item, slot, body));
			}
		}

		private static FieldInfo ToReflection(Assembly assembly, FieldReference field)
		{
			if (field != null)
			{
				return ToReflection(assembly, ((MemberReference)field).DeclaringType)?.GetField(((MemberReference)field).Name, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			}
			return null;
		}

		private static MethodInfo ToReflection(Assembly assembly, MethodReference method)
		{
			return ((method == null) ? null : ToReflection(assembly, ((MemberReference)method).DeclaringType))?.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo candidate) => candidate.Name == ((MemberReference)method).Name && candidate.GetParameters().Length == method.Parameters.Count);
		}

		private static Type ToReflection(Assembly assembly, TypeReference type)
		{
			if (type == null || type.IsGenericInstance)
			{
				return null;
			}
			string name = ((MemberReference)type).FullName.Replace('/', '+');
			return assembly.GetType(name) ?? (from candidate in AppDomain.CurrentDomain.GetAssemblies()
				select candidate.GetType(name)).FirstOrDefault((Type match) => match != null);
		}

		internal static bool HoldsSkills(Type type)
		{
			if (!typeof(SkillDef).IsAssignableFrom(type) && (!type.IsArray || !typeof(SkillDef).IsAssignableFrom(type.GetElementType())))
			{
				if (type.IsGenericType && typeof(IEnumerable).IsAssignableFrom(type))
				{
					return type.GetGenericArguments().Any(typeof(SkillDef).IsAssignableFrom);
				}
				return false;
			}
			return true;
		}
	}
	internal static class PauseAbilitiesPanel
	{
		private class AbilitiesButton : MonoBehaviour
		{
			internal GameObject buttonObject;
		}

		[Serializable]
		[CompilerGenerated]
		private sealed class <>c
		{
			public static readonly <>c <>9 = new <>c();

			public static hook_Awake <>9__9_0;

			public static hook_OnEnable <>9__9_1;

			public static Func<SkillDef, bool> <>9__13_1;

			public static Func<ExtraSkill, bool> <>9__13_2;

			internal void <Init>b__9_0(orig_Awake orig, PauseScreenController self)
			{
				orig.Invoke(self);
				AddButton(self);
			}

			internal void <Init>b__9_1(orig_OnEnable orig, PauseScreenController self)
			{
				orig.Invoke(self);
				AbilitiesButton component = ((Component)self).GetComponent<AbilitiesButton>();
				if (Object.op_Implicit((Object)(object)component) && Object.op_Implicit((Object)(object)component.buttonObject))
				{
					GameObject buttonObject = component.buttonObject;
					int active;
					if (Object.op_Implicit((Object)(object)Run.instance) && Run.instance.isRunning)
					{
						LocalUser localUser = self.localUser;
						active = (Object.op_Implicit((Object)(object)((localUser != null) ? localUser.cachedMaster : null)) ? 1 : 0);
					}
					else
					{
						active = 0;
					}
					buttonObject.SetActive((byte)active != 0);
				}
			}

			internal bool <Lines>b__13_1(SkillDef skill)
			{
				return Object.op_Implicit((Object)(object)skill);
			}

			internal bool <Lines>b__13_2(ExtraSkill extra)
			{
				//IL_0001: Unknown result type (might be due to invalid IL or missing references)
				//IL_0007: Invalid comparison between Unknown and I4
				return (int)extra.slot == -1;
			}
		}

		private const string CharacterSelectAddress = "RoR2/Base/UI/CharacterSelectUIMain.prefab";

		private const string PanelName = "SubSkillsDisplayAbilitiesPanel";

		private const float ColumnWidth = 640f;

		private const float HoverBoxWidth = 520f;

		private const float BackButtonWidth = 320f;

		private const float BackButtonHeight = 48f;

		private const int LayerPriority = 1005;

		private static CharacterSelectController template;

		private static UILayer layer;

		internal static void Init()
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Expected O, but got Unknown
			//IL_0038: 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_0043: Expected O, but got Unknown
			object obj = <>c.<>9__9_0;
			if (obj == null)
			{
				hook_Awake val = delegate(orig_Awake orig, PauseScreenController self)
				{
					orig.Invoke(self);
					AddButton(self);
				};
				<>c.<>9__9_0 = val;
				obj = (object)val;
			}
			PauseScreenController.Awake += (hook_Awake)obj;
			object obj2 = <>c.<>9__9_1;
			if (obj2 == null)
			{
				hook_OnEnable val2 = delegate(orig_OnEnable orig, PauseScreenController self)
				{
					orig.Invoke(self);
					AbilitiesButton component = ((Component)self).GetComponent<AbilitiesButton>();
					if (Object.op_Implicit((Object)(object)component) && Object.op_Implicit((Object)(object)component.buttonObject))
					{
						GameObject buttonObject = component.buttonObject;
						int active;
						if (Object.op_Implicit((Object)(object)Run.instance) && Run.instance.isRunning)
						{
							LocalUser localUser = self.localUser;
							active = (Object.op_Implicit((Object)(object)((localUser != null) ? localUser.cachedMaster : null)) ? 1 : 0);
						}
						else
						{
							active = 0;
						}
						buttonObject.SetActive((byte)active != 0);
					}
				};
				<>c.<>9__9_1 = val2;
				obj2 = (object)val2;
			}
			PauseScreenController.OnEnable += (hook_OnEnable)obj2;
		}

		private static void AddButton(PauseScreenController controller)
		{
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Expected O, but got Unknown
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Expected O, but got Unknown
			GameObject inventoryButtonObject = controller.inventoryButtonObject;
			if (!Object.op_Implicit((Object)(object)inventoryButtonObject))
			{
				Log.Warning("The pause menu has no Inventory button to put Abilities after; skipping it.");
				return;
			}
			ModTokens.Register();
			GameObject val = Object.Instantiate<GameObject>(inventoryButtonObject, inventoryButtonObject.transform.parent, false);
			((Object)val).name = "GenericMenuButton (Abilities)";
			val.transform.SetSiblingIndex(inventoryButtonObject.transform.GetSiblingIndex() + 1);
			MPButton component = val.GetComponent<MPButton>();
			((Button)component).onClick = new ButtonClickedEvent();
			((UnityEvent)((Button)component).onClick).AddListener((UnityAction)delegate
			{
				Open(controller);
			});
			LanguageTextMeshController component2 = val.GetComponent<LanguageTextMeshController>();
			if (Object.op_Implicit((Object)(object)component2))
			{
				component2.token = "SUBSKILLSDISPLAY_PAUSE_ABILITIES";
			}
			((Component)controller).gameObject.AddComponent<AbilitiesButton>().buttonObject = val;
		}

		private static void Open(PauseScreenController controller)
		{
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: 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_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			LocalUser localUser = controller.localUser;
			CharacterMaster val = ((localUser != null) ? localUser.cachedMaster : null);
			if (!Object.op_Implicit((Object)(object)val))
			{
				return;
			}
			CharacterBody body = val.GetBody();
			Loadout val2 = null;
			SurvivorLoadout body2;
			BodyIndex val3;
			if (Object.op_Implicit((Object)(object)body))
			{
				body2 = SurvivorLoadout.FromBody(body, localUser.userProfile);
				val3 = body.bodyIndex;
			}
			else
			{
				val3 = (BodyIndex)((!Object.op_Implicit((Object)(object)val.bodyPrefab)) ? (-1) : ((int)BodyCatalog.FindBodyIndex(val.bodyPrefab)));
				val2 = Loadout.RequestInstance();
				UserProfile userProfile = localUser.userProfile;
				if (userProfile != null)
				{
					userProfile.CopyLoadout(val2);
				}
				body2 = SurvivorLoadout.FromLoadout(val3, val2, localUser.userProfile);
			}
			try
			{
				List<SkillLine> lines = Lines(body2, ExtraSkillCatalog.For(body2, includeOtherLoadouts: false));
				CharacterBody bodyPrefabBodyComponent = BodyCatalog.GetBodyPrefabBodyComponent(val3);
				string title = (Object.op_Implicit((Object)(object)bodyPrefabBodyComponent) ? Language.GetString(bodyPrefabBodyComponent.baseNameToken) : string.Empty);
				Color color = (Object.op_Implicit((Object)(object)bodyPrefabBodyComponent) ? bodyPrefabBodyComponent.bodyColor : Color.white);
				GameObject val4 = Build(controller, title, color, lines);
				if (Object.op_Implicit((Object)(object)val4))
				{
					Object.Destroy((Object)(object)controller.submenuObject);
					controller.submenuObject = val4;
					((Component)controller.pausePanel).gameObject.SetActive(false);
					((Component)controller.gameMenuPanel).gameObject.SetActive(true);
					((Component)controller.mainPanel).gameObject.SetActive(false);
					val4.SetActive(true);
					UILayerKey component = val4.GetComponent<UILayerKey>();
					if (!Object.op_Implicit((Object)(object)component._eventSystem))
					{
						component._eventSystem = val4.GetComponent<MPEventSystemLocator>().eventSystem;
					}
				}
			}
			catch (Exception arg)
			{
				Log.Error($"Could not open the Abilities panel: {arg}");
				Transform val5 = ((Transform)controller.rootPanel).Find("SubSkillsDisplayAbilitiesPanel");
				if (Object.op_Implicit((Object)(object)val5) && (Object)(object)((Component)val5).gameObject != (Object)(object)controller.submenuObject)
				{
					Object.Destroy((Object)(object)((Component)val5).gameObject);
				}
			}
			finally
			{
				if (val2 != null)
				{
					Loadout.ReturnInstance(val2);
				}
			}
		}

		private static List<SkillLine> Lines(SurvivorLoadout body, List<ExtraSkill> extras)
		{
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_0121: Unknown result type (might be due to invalid IL or missing references)
			//IL_0128: Unknown result type (might be due to invalid IL or missing references)
			//IL_0141: Unknown result type (might be due to invalid IL or missing references)
			List<SkillLine> list = new List<SkillLine>();
			if (!body.IsValid)
			{
				return list;
			}
			SkillLine skillLine = new SkillLine("SUBSKILLSDISPLAY_LINE_PASSIVE");
			if (body.skillLocator.passiveSkill.enabled)
			{
				skillLine.entries.Add(SkillEntry.Of(body.skillLocator.passiveSkill));
			}
			skillLine.entries.AddRange((from skill in body.slots.Where((GenericSkill slot) => !slot.hideInCharacterSelect && (int)body.KindOf(slot) == -1).Select(body.Selected)
				where Object.op_Implicit((Object)(object)skill)
				select skill).Select(SkillEntry.Of));
			if (skillLine.entries.Count > 0)
			{
				list.Add(skillLine);
			}
			SkillSlot[] array = new SkillSlot[4];
			RuntimeHelpers.InitializeArray(array, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/);
			SkillSlot[] array2 = (SkillSlot[])(object)array;
			foreach (SkillSlot kind in array2)
			{
				SkillLine skillLine2 = new SkillLine(SkillLine.TitleFor(kind));
				SkillDef val = body.Selected(kind);
				if (Object.op_Implicit((Object)(object)val))
				{
					skillLine2.entries.Add(SkillEntry.Of(val));
				}
				skillLine2.entries.AddRange(extras.Where((ExtraSkill extra) => extra.slot == kind).Select(SkillEntry.Of));
				if (skillLine2.entries.Count > 0)
				{
					list.Add(skillLine2);
				}
			}
			list.AddRange(SkillLine.FromExtras(extras.Where((ExtraSkill extra) => (int)extra.slot == -1)));
			return list;
		}

		private static GameObject Build(PauseScreenController controller, string title, Color color, List<SkillLine> lines)
		{
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Expected O, but got Unknown
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: 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_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: 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_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: 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_0172: Expected O, but got Unknown
			//IL_01d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01dc: Expected O, but got Unknown
			//IL_01de: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e8: Expected O, but got Unknown
			//IL_0212: Unknown result type (might be due to invalid IL or missing references)
			//IL_021c: Expected O, but got Unknown
			//IL_0228: Unknown result type (might be due to invalid IL or missing references)
			//IL_0232: Expected O, but got Unknown
			//IL_0128: Unknown result type (might be due to invalid IL or missing references)
			//IL_0135: Unknown result type (might be due to invalid IL or missing references)
			//IL_027c: Unknown result type (might be due to invalid IL or missing references)
			//IL_037b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0381: Unknown result type (might be due to invalid IL or missing references)
			//IL_038b: Expected O, but got Unknown
			//IL_046e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0478: Unknown result type (might be due to invalid IL or missing references)
			CharacterSelectController val = Template();
			RectTransform val2 = (Object.op_Implicit((Object)(object)val) ? val.skillStripContainer : null);
			if (!Object.op_Implicit((Object)(object)val2))
			{
				Log.Warning("Could not load character select's skill strips; the Abilities panel can't be shown.");
				return null;
			}
			GameObject root = new GameObject("SubSkillsDisplayAbilitiesPanel", new Type[1] { typeof(RectTransform) });
			root.SetActive(false);
			root.transform.SetParent((Transform)(object)controller.rootPanel, false);
			RectTransform val3 = (RectTransform)root.transform;
			Vector2 val4 = default(Vector2);
			((Vector2)(ref val4))..ctor(0.5f, 0.5f);
			val3.pivot = val4;
			Vector2 anchorMin = (val3.anchorMax = val4);
			val3.anchorMin = anchorMin;
			val3.anchoredPosition = Vector2.zero;
			Transform val6 = (Object.op_Implicit((Object)(object)val.survivorName) ? ((TMP_Text)val.survivorName).transform.parent : null);
			Image val7 = ((Object.op_Implicit((Object)(object)val6) && Object.op_Implicit((Object)(object)val6.parent)) ? ((Component)val6.parent).GetComponent<Image>() : null);
			if (Object.op_Implicit((Object)(object)val7))
			{
				Image obj = root.AddComponent<Image>();
				obj.sprite = val7.sprite;
				obj.type = val7.type;
				((Graphic)obj).color = ((Graphic)val7).color;
				((Graphic)obj).material = ((Graphic)val7).material;
				obj.pixelsPerUnitMultiplier = val7.pixelsPerUnitMultiplier;
			}
			VerticalLayoutGroup obj2 = root.AddComponent<VerticalLayoutGroup>();
			((LayoutGroup)obj2).padding = new RectOffset(6, 6, 6, 6);
			((HorizontalOrVerticalLayoutGroup)obj2).spacing = 6f;
			((LayoutGroup)obj2).childAlignment = (TextAnchor)1;
			((HorizontalOrVerticalLayoutGroup)obj2).childControlWidth = true;
			((HorizontalOrVerticalLayoutGroup)obj2).childControlHeight = true;
			((HorizontalOrVerticalLayoutGroup)obj2).childForceExpandWidth = true;
			((HorizontalOrVerticalLayoutGroup)obj2).childForceExpandHeight = false;
			ContentSizeFitter obj3 = root.AddComponent<ContentSizeFitter>();
			obj3.horizontalFit = (FitMode)2;
			obj3.verticalFit = (FitMode)2;
			UILayerKey val8 = root.AddComponent<UILayerKey>();
			val8.layer = Layer();
			val8.onBeginRepresentTopLayer = new UnityEvent();
			val8.onEndRepresentTopLayer = new UnityEvent();
			HGGamepadInputEvent obj4 = root.AddComponent<HGGamepadInputEvent>();
			obj4.actionName = "UICancel";
			obj4.requiredTopLayer = val8;
			obj4.enabledObjectsIfActive = Array.Empty<GameObject>();
			obj4.actionEvent = new UnityEvent();
			obj4.actionEvent.AddListener((UnityAction)delegate
			{
				Object.Destroy((Object)(object)root);
			});
			if (Object.op_Implicit((Object)(object)val6))
			{
				GameObject val9 = Object.Instantiate<GameObject>(((Component)val6).gameObject, root.transform, false);
				TMP_Text val10 = UiHierarchy.Counterpart<TMP_Text>(val6, val9.transform, (TMP_Text)(object)val.survivorName);
				if (Object.op_Implicit((Object)(object)val10))
				{
					val10.SetText(title, true);
					((Graphic)val10).color = color;
				}
			}
			GameObject val11 = Object.Instantiate<GameObject>(((Component)val2).gameObject, root.transform, false);
			((Object)val11).name = "SkillLines";
			VerticalLayoutGroup component = val11.GetComponent<VerticalLayoutGroup>();
			if (Object.op_Implicit((Object)(object)component))
			{
				((HorizontalOrVerticalLayoutGroup)component).childControlHeight = true;
				((HorizontalOrVerticalLayoutGroup)component).childForceExpandHeight = false;
			}
			GameObject val12 = UiHierarchy.Counterpart((Transform)(object)val2, val11.transform, val.skillStripPrefab);
			HGButton val13 = (Object.op_Implicit((Object)(object)val12) ? val12.GetComponent<HGButton>() : null);
			object obj5;
			if (!Object.op_Implicit((Object)(object)val12))
			{
				obj5 = null;
			}
			else
			{
				Transform obj6 = val12.transform.Find("Inner/SkillDescriptionPanel/SkillDescription");
				obj5 = ((obj6 != null) ? ((Component)obj6).GetComponent<TMP_Text>() : null);
			}
			TMP_Text val14 = (TMP_Text)obj5;
			if (Object.op_Implicit((Object)(object)val14))
			{
				UiHierarchy.AddText(val14, root.transform, "Intro", 640f).SetText(Language.GetString("SUBSKILLSDISPLAY_PAUSE_INTRO"), true);
				val11.transform.SetAsLastSibling();
			}
			LanguageTextMeshController val15 = (Object.op_Implicit((Object)(object)val13) ? val13.hoverLanguageTextMeshController : null);
			SkillLines.Build((RectTransform)val11.transform, lines, color, val15, val8);
			float num = (Object.op_Implicit((Object)(object)component) ? ((float)((LayoutGroup)component).padding.vertical) : 0f);
			float num2 = (Object.op_Implicit((Object)(object)component) ? ((HorizontalOrVerticalLayoutGroup)component).spacing : 0f);
			LayoutElement obj7 = LayoutElementOf(val11);
			float minWidth = (obj7.preferredWidth = 640f);
			obj7.minWidth = minWidth;
			minWidth = (obj7.preferredHeight = num + (float)Mathf.Max(1, lines.Count) * 96f + (float)Mathf.Max(0, lines.Count - 1) * num2);
			obj7.minHeight = minWidth;
			Transform val16 = (Object.op_Implicit((Object)(object)val15) ? ((Component)val15).transform : null);
			while (Object.op_Implicit((Object)(object)val16) && (Object)(object)val16.parent != (Object)(object)val11.transform)
			{
				val16 = val16.parent;
			}
			RectTransform val17 = (RectTransform)(object)((val16 is RectTransform) ? val16 : null);
			if (val17 != null)
			{
				val17.sizeDelta = new Vector2(520f, val17.sizeDelta.y);
			}
			AddBackButton(controller, root);
			MPButton[] componentsInChildren = root.GetComponentsInChildren<MPButton>(true);
			for (int num5 = 0; num5 < componentsInChildren.Length; num5++)
			{
				componentsInChildren[num5].requiredTopLayer = val8;
			}
			return root;
		}

		private static void AddBackButton(PauseScreenController controller, GameObject root)
		{
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Expected O, but got Unknown
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Expected O, but got Unknown
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ee: Expected O, but got Unknown
			AbilitiesButton component = ((Component)controller).GetComponent<AbilitiesButton>();
			if (Object.op_Implicit((Object)(object)component) && Object.op_Implicit((Object)(object)component.buttonObject))
			{
				GameObject val = new GameObject("BackRow", new Type[1] { typeof(RectTransform) });
				val.transform.SetParent(root.transform, false);
				HorizontalLayoutGroup obj = val.AddComponent<HorizontalLayoutGroup>();
				((LayoutGroup)obj).childAlignment = (TextAnchor)4;
				((HorizontalOrVerticalLayoutGroup)obj).childControlWidth = true;
				((HorizontalOrVerticalLayoutGroup)obj).childControlHeight = true;
				((HorizontalOrVerticalLayoutGroup)obj).childForceExpandWidth = false;
				((HorizontalOrVerticalLayoutGroup)obj).childForceExpandHeight = false;
				GameObject obj2 = Object.Instantiate<GameObject>(component.buttonObject, val.transform, false);
				((Object)obj2).name = "GenericMenuButton (Back)";
				obj2.SetActive(true);
				LayoutElement obj3 = LayoutElementOf(obj2);
				obj3.preferredWidth = 320f;
				obj3.preferredHeight = 48f;
				MPButton component2 = obj2.GetComponent<MPButton>();
				((Button)component2).onClick = new ButtonClickedEvent();
				((UnityEvent)((Button)component2).onClick).AddListener((UnityAction)delegate
				{
					Object.Destroy((Object)(object)root);
				});
				component2.defaultFallbackButton = true;
				LanguageTextMeshController component3 = obj2.GetComponent<LanguageTextMeshController>();
				if (Object.op_Implicit((Object)(object)component3))
				{
					component3.token = "SUBMENU_BACK";
				}
			}
		}

		private static LayoutElement LayoutElementOf(GameObject gameObject)
		{
			LayoutElement component = gameObject.GetComponent<LayoutElement>();
			if (!Object.op_Implicit((Object)(object)component))
			{
				return gameObject.AddComponent<LayoutElement>();
			}
			return component;
		}

		private static CharacterSelectController Template()
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			if (Object.op_Implicit((Object)(object)template))
			{
				return template;
			}
			AsyncOperationHandle<GameObject> val = Addressables.LoadAssetAsync<GameObject>((object)"RoR2/Base/UI/CharacterSelectUIMain.prefab");
			GameObject val2 = val.WaitForCompletion();
			template = (Object.op_Implicit((Object)(object)val2) ? val2.GetComponent<CharacterSelectController>() : null);
			if (!Object.op_Implicit((Object)(object)template))
			{
				Log.Error(string.Format("Could not load {0} ({1}): {2}", "RoR2/Base/UI/CharacterSelectUIMain.prefab", val.Status, val.OperationException?.Message ?? "no character select controller on it"));
			}
			return template;
		}

		private static UILayer Layer()
		{
			if (!Object.op_Implicit((Object)(object)layer))
			{
				layer = ScriptableObject.CreateInstance<UILayer>();
				((Object)layer).name = "uilSubSkillsDisplayAbilities";
				layer.priority = 1005;
				((Object)layer).hideFlags = (HideFlags)32;
			}
			return layer;
		}
	}
	internal readonly struct SkillEntry
	{
		internal readonly Sprite icon;

		private readonly string nameToken;

		private readonly string sourceToken;

		private readonly string descriptionToken;

		private readonly string[] keywordTokens;

		private static readonly Regex keywordTitle = new Regex("<style=cKeywordName>.*?</style>", RegexOptions.Singleline);

		private static readonly Regex subduedBody = new Regex("^<style=cSub>(.*)</style>$", RegexOptions.Singleline);

		internal string Title
		{
			get
			{
				string text = Language.GetString(nameToken);
				string text2 = (string.IsNullOrEmpty(sourceToken) ? null : Language.GetString(sourceToken));
				if (!string.IsNullOrEmpty(text2) && !(text2 == text))
				{
					return text + " (" + text2 + ")";
				}
				return text;
			}
		}

		internal string Description
		{
			get
			{
				string text = keywordTitle.Replace(Language.GetString(descriptionToken), string.Empty).Trim();
				Match match = subduedBody.Match(text);
				if (!match.Success)
				{
					return text;
				}
				return match.Groups[1].Value;
			}
		}

		private SkillEntry(Sprite icon, string nameToken, string sourceToken, string descriptionToken, string[] keywordTokens)
		{
			this.icon = icon;
			this.nameToken = nameToken;
			this.sourceToken = sourceToken;
			this.descriptionToken = descriptionToken;
			this.keywordTokens = keywordTokens;
		}

		internal static SkillEntry Of(SkillDef skill)
		{
			return new SkillEntry(skill.icon, skill.skillNameToken, null, skill.skillDescriptionToken, skill.keywordTokens);
		}

		internal static SkillEntry Of(ExtraSkill extra)
		{
			return new SkillEntry(extra.skill.icon, extra.skill.skillNameToken, extra.sourceToken, extra.descriptionToken, extra.skill.keywordTokens);
		}

		internal static SkillEntry Of(PassiveSkill passive)
		{
			//IL_0000: 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_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: 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)
			return new SkillEntry(passive.icon, passive.skillNameToken, null, passive.skillDescriptionToken, string.IsNullOrEmpty(passive.keywordToken) ? null : new string[1] { passive.keywordToken });
		}

		internal string HoverText(Color color)
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			string description = Description;
			color.a = 0.2f;
			string stringFormatted = Language.GetStringFormatted("LOGBOOK_HOVER_DESCRIPTION_FORMAT", new object[3]
			{
				Title,
				description,
				ColorUtility.ToHtmlStringRGBA(color)
			});
			string[] array = ((IEnumerable<string>)Keywords.For(keywordTokens, description)).Select((Func<string, string>)Language.GetString).ToArray();
			if (array.Length != 0)
			{
				return stringFormatted + "\n\n" + string.Join("\n\n", array);
			}
			return stringFormatted;
		}
	}
	internal sealed class SkillLine
	{
		internal readonly string titleToken;

		internal readonly List<SkillEntry> entries = new List<SkillEntry>();

		internal const string MiscTitleToken = "LOADOUT_SKILL_MISC";

		internal SkillLine(string titleToken)
		{
			this.titleToken = titleToken;
		}

		internal static string TitleFor(SkillSlot slot)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Expected I4, but got Unknown
			return (int)slot switch
			{
				0 => "LOADOUT_SKILL_PRIMARY", 
				1 => "LOADOUT_SKILL_SECONDARY", 
				2 => "LOADOUT_SKILL_UTILITY", 
				3 => "LOADOUT_SKILL_SPECIAL", 
				_ => "LOADOUT_SKILL_MISC", 
			};
		}

		internal static List<SkillLine> FromExtras(IEnumerable<ExtraSkill> extras)
		{
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Invalid comparison between Unknown and I4
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			List<SkillLine> list = new List<SkillLine>();
			foreach (IGrouping<string, ExtraSkill> item in from extra in extras
				group extra by extra.LineKey into @group
				orderby Order(@group.First().slot)
				select @group)
			{
				ExtraSkill extraSkill = item.First();
				SkillLine skillLine = new SkillLine(((int)extraSkill.slot != -1) ? TitleFor(extraSkill.slot) : (extraSkill.lineToken ?? "LOADOUT_SKILL_MISC"));
				skillLine.entries.AddRange(item.Select(SkillEntry.Of));
				list.Add(skillLine);
			}
			return list;
		}

		private static int Order(SkillSlot slot)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Invalid comparison between Unknown and I4
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Expected I4, but got Unknown
			if ((int)slot != -1)
			{
				return (int)slot;
			}
			return 4;
		}
	}
	internal static class SkillLines
	{
		internal const float LineHeight = 96f;

		internal static List<GameObject> Build(RectTransform container, IEnumerable<SkillLine> lines, Color color, LanguageTextMeshController hoverText, UILayerKey requiredTopLayer)
		{
			//IL_00d8: 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)
			List<GameObject> list = new List<GameObject>();
			GameObject rowPrefab = LoadoutPanelController.rowPrefab;
			GameObject loadoutButtonPrefab = LoadoutPanelController.loadoutButtonPrefab;
			if (!Object.op_Implicit((Object)(object)rowPrefab) || !Object.op_Implicit((Object)(object)loadoutButtonPrefab))
			{
				Log.Warning("The Loadout tab's row prefabs aren't loaded; skill lines can't be shown.");
				return list;
			}
			foreach (SkillLine line in lines)
			{
				GameObject val = Object.Instantiate<GameObject>(rowPrefab, (Transform)(object)container, false);
				((Object)val).name = "SkillLine";
				list.Add(val);
				Transform val2 = val.transform.Find("ButtonSelectionHighlight, Checkbox");
				if (Object.op_Implicit((Object)(object)val2))
				{
					Object.Destroy((Object)(object)((Component)val2).gameObject);
				}
				Transform val3 = val.transform.Find("SlotLabel");
				if (Object.op_Implicit((Object)(object)val3))
				{
					LanguageTextMeshController component = ((Component)val3).GetComponent<LanguageTextMeshController>();
					if (Object.op_Implicit((Object)(object)component))
					{
						component.token = line.titleToken;
					}
					HGTextMeshProUGUI component2 = ((Component)val3).GetComponent<HGTextMeshProUGUI>();
					if (Object.op_Implicit((Object)(object)component2))
					{
						((Graphic)component2).color = color;
					}
				}
				Transform val4 = val.transform.Find("ButtonContainer") ?? val.transform;
				foreach (SkillEntry entry in line.entries)
				{
					AddButton(loadoutButtonPrefab, val4, entry, color, hoverText, requiredTopLayer);
				}
				Transform obj = val4.Find("Spacer");
				if (obj != null)
				{
					obj.SetAsLastSibling();
				}
			}
			return list;
		}

		private static void AddButton(GameObject buttonPrefab, Transform parent, SkillEntry entry, Color color, LanguageTextMeshController hoverText, UILayerKey requiredTopLayer)
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = Object.Instantiate<GameObject>(buttonPrefab, parent, false);
			HGButton component = val.GetComponent<HGButton>();
			if (Object.op_Implicit((Object)(object)component))
			{
				Graphic targetGraphic = ((Selectable)component).targetGraphic;
				Image val2 = (Image)(object)((targetGraphic is Image) ? targetGraphic : null);
				if (val2 != null)
				{
					val2.sprite = entry.icon;
				}
				component.updateTextOnHover = true;
				component.hoverLanguageTextMeshController = hoverText;
				component.hoverToken = entry.HoverText(color);
				((MPButton)component).requiredTopLayer = requiredTopLayer;
				((Selectable)component).interactable = true;
				((MPButton)component).disablePointerClick = true;
				((MPButton)component).disableGamepadClick = true;
				ColorBlock colors = ((Selectable)component).colors;
				((ColorBlock)(ref colors)).colorMultiplier = 1f;
				((Selectable)component).colors = colors;
				TooltipProvider component2 = val.GetComponent<TooltipProvider>();
				if (Object.op_Implicit((Object)(object)component2))
				{
					Object.Destroy((Object)(object)component2);
				}
			}
		}
	}
	internal static class SkillOverrideSites
	{
		internal readonly struct Site
		{
			internal readonly MethodDefinition method;

			internal readonly Instruction load;

			internal readonly string slot;

			internal Site(MethodDefinition method, Instruction load, string slot)
			{
				this.method = method;
				this.load = load;
				this.slot = slot;
			}
		}

		private static readonly Dictionary<string, string> overrideHolderFields = new Dictionary<string, string>
		{
			["EntityStates.SkillStateOverrideData::primarySkillOverride"] = "Primary",
			["EntityStates.SkillStateOverrideData::secondarySkillOverride"] = "Secondary",
			["EntityStates.SkillStateOverrideData::utilitySkillOverride"] = "Utility",
			["EntityStates.SkillStateOverrideData::specialSkillOverride"] = "Special",
			["RoR2.SkillTransformationController/SkillDefRelationship::overrideSkillDef"] = null
		};

		private static readonly string[] slotNames = new string[4] { "Primary", "Secondary", "Utility", "Special" };

		private const int MaximumWrapperDepth = 2;

		internal static List<Site> Find(ModuleDefinition module)
		{
			//IL_007b: 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_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_0135: Unknown result type (might be due to invalid IL or missing references)
			//IL_019e: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ae: Invalid comparison between Unknown and I4
			List<MethodDefinition> list = (from method in module.GetTypes().SelectMany((TypeDefinition type) => (IEnumerable<MethodDefinition>)type.Methods)
				where method.HasBody
				select method).ToList();
			Dictionary<MethodDefinition, List<(MethodDefinition, Instruction)>> dictionary = new Dictionary<MethodDefinition, List<(MethodDefinition, Instruction)>>();
			foreach (MethodDefinition item in list)
			{
				Enumerator<Instruction> enumerator2 = item.Body.Instructions.GetEnumerator();
				try
				{
					while (enumerator2.MoveNext())
					{
						Instruction current2 = enumerator2.Current;
						if (!IsCall(current2))
						{
							continue;
						}
						object operand = current2.Operand;
						MethodDefinition val = (MethodDefinition)((operand is MethodDefinition) ? operand : null);
						if (val != null)
						{
							if (!dictionary.TryGetValue(val, out var value))
							{
								value = (dictionary[val] = new List<(MethodDefinition, Instruction)>());
							}
							value.Add((item, current2));
						}
					}
				}
				finally
				{
					((IDisposable)enumerator2/*cast due to .constrained prefix*/).Dispose();
				}
			}
			List<Site> list3 = new List<Site>();
			foreach (MethodDefinition item2 in list)
			{
				Enumerator<Instruction> enumerator2 = item2.Body.Instructions.GetEnumerator();
				try
				{
					while (enumerator2.MoveNext())
					{
						Instruction current4 = enumerator2.Current;
						if (IsCall(current4))
						{
							object operand2 = current4.Operand;
							MethodReference val2 = (MethodReference)((operand2 is MethodReference) ? operand2 : null);
							if (val2 != null && ((MemberReference)val2).Name == "SetSkillOverride" && ((MemberReference)((MemberReference)val2).DeclaringType).FullName == "RoR2.GenericSkill")
							{
								Follow(item2, current4, 1, 3, null, dictionary, 0, list3);
								continue;
							}
						}
						OpCode opCode = current4.OpCode;
						if ((int)((OpCode)(ref opCode)).Code == 122)
						{
							object operand3 = current4.Operand;
							FieldReference val3 = (FieldReference)((operand3 is FieldReference) ? operand3 : null);
							if (val3 != null && overrideHolderFields.TryGetValue(((MemberReference)((MemberReference)val3).DeclaringType).FullName + "::" + ((MemberReference)val3).Name, out var value2))
							{
								Follow(item2, current4, 0, -1, value2, dictionary, 0, list3);
							}
						}
					}
				}
				finally
				{
					((IDisposable)enumerator2/*cast due to .constrained prefix*/).Dispose();
				}
			}
			return (from site in list3
				group site by (method: site.method, load: site.load) into @group
				select @group.First()).ToList();
		}

		private static void Follow(MethodDefinition method, Instruction consumer, int skillFromTop, int slotFromTop, string knownSlot, Dictionary<MethodDefinition, List<(MethodDefinition caller, Instruction call)>> callSites, int depth, List<Site> sites)
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: 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_0047: Invalid comparison between Unknown and I4
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Invalid comparison between Unknown and I4
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Invalid comparison between Unknown and I4
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Invalid comparison between Unknown and I4
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Invalid comparison between Unknown and I4
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Invalid comparison between Unknown and I4
			string text = knownSlot;
			int parameter = -1;
			if (text == null && slotFromTop >= 0)
			{
				text = SlotAt(method, consumer, slotFromTop, out parameter);
			}
			foreach (Instruction item in Producers(method, consumer, skillFromTop))
			{
				OpCode opCode = item.OpCode;
				Code code = ((OpCode)(ref opCode)).Code;
				if ((int)code <= 110)
				{
					if ((int)code == 39 || (int)code == 110)
					{
						goto IL_006e;
					}
				}
				else
				{
					if ((int)code == 120 || (int)code == 123)
					{
						goto IL_006e;
					}
					if ((int)code == 151)
					{
						Fol