Decompiled source of KindredMastery v1.0.4

BepInEx/plugins/KindredMastery/KindredMastery.dll

Decompiled 2 weeks ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using KindredMastery.Commands;
using KindredMastery.Configuration;
using KindredMastery.Services;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using UnityEngine;

[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("KindredMastery")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.4.0")]
[assembly: AssemblyInformationalVersion("1.0.4+55c6ba452194f646bb2f8addfe7fccaf8ffb1433")]
[assembly: AssemblyProduct("KindredMastery")]
[assembly: AssemblyTitle("KindredMastery")]
[assembly: AssemblyVersion("1.0.4.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

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

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

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

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace KindredMastery
{
	[BepInPlugin("com.oddlyforged.kindredmastery", "KindredMastery", "1.0.4")]
	public class KindredMasteryPlugin : BaseUnityPlugin
	{
		public const string PluginId = "com.oddlyforged.kindredmastery";

		public const string PluginName = "KindredMastery";

		public const string PluginVersion = "1.0.4";

		private Harmony? _harmony;

		internal static KindredMasteryCalculator Calculator { get; private set; }

		internal static KindredMasteryConfig Configuration { get; private set; }

		internal static ManualLogSource Log { get; private set; }

		public KindredMasteryPlugin()
		{
			Log = ((BaseUnityPlugin)this).Logger;
			((BaseUnityPlugin)this).Logger.LogInfo((object)"KindredMasteryPlugin is initializing!");
		}

		private void Awake()
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Expected O, but got Unknown
			Configuration = new KindredMasteryConfig(((BaseUnityPlugin)this).Config, Log);
			Calculator = new KindredMasteryCalculator(Configuration);
			_harmony = new Harmony("com.oddlyforged.kindredmastery");
			_harmony.PatchAll();
			KindredMasteryCommands.Register(Configuration);
			((BaseUnityPlugin)this).Logger.LogInfo((object)"KindredMastery is loaded!");
		}

		private void OnDestroy()
		{
			Harmony? harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			((BaseUnityPlugin)this).Logger.LogInfo((object)"KindredMastery is unloaded!");
		}
	}
	internal static class BuildVersion
	{
		public const string Value = "1.0.4";
	}
}
namespace KindredMastery.Services
{
	internal static class AffinityFormula
	{
		public static float CalculateMultiplier(float targetLevel, float sourceLevel, float affinityStrength, float globalModifier)
		{
			float num = Math.Max(0f, sourceLevel - targetLevel);
			return 1f + num / 100f * affinityStrength * globalModifier;
		}
	}
	internal static class FancyFeedbackProvider
	{
		private static readonly Random Random = new Random();

		private static readonly Dictionary<(SkillType Source, SkillType Target), string[]> SkillMessages = new Dictionary<(SkillType, SkillType), string[]>
		{
			[((SkillType)8, (SkillType)14)] = new string[3] { "It's a bow. With a trigger. How hard could it be?", "YOU'VE INVENTED BOW 2.0!", "Someone finally put a stock on your bow." },
			[((SkillType)14, (SkillType)8)] = new string[2] { "You removed the trigger. Bold choice.", "BACK TO BASICS! Who needs mechanical assistance?" },
			[((SkillType)1, (SkillType)2)] = new string[3] { "The sword got smaller. Your expertise didn't.", "HALF THE BLADE, ALL THE TALENT!", "Your sword shrank in the wash. Your skills survived." },
			[((SkillType)2, (SkillType)1)] = new string[3] { "You added a hilt. Your skill is still sharp.", "The knife grew. Your skill grew sharper.", "Same stabbing principle. Now with personal space." },
			[((SkillType)1, (SkillType)7)] = new string[1] { "The blade got heavier. The technique got questionable." },
			[((SkillType)7, (SkillType)1)] = new string[1] { "You discovered that blades can do more than chop trees." },
			[((SkillType)3, (SkillType)11)] = new string[1] { "The weapon was holding you back. Allegedly." }
		};

		private static readonly string[] GenericMessages = new string[4] { "BY ODIN'S BEARD! Your experience proves useful!", "Your brain has discovered synergy.", "The Viking Accreditation Board approves.", "HR would be proud of your transferable skills." };

		internal static string GetMessage(KindredMasteryResult affinity)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			SkillType? sourceSkill = affinity.SourceSkill;
			if (sourceSkill.HasValue)
			{
				SkillType valueOrDefault = sourceSkill.GetValueOrDefault();
				if (SkillMessages.TryGetValue((valueOrDefault, affinity.TargetSkill), out string[] value) && value.Length != 0)
				{
					return Pick(value);
				}
			}
			return Pick(GenericMessages);
		}

		private static string Pick(string[] messages)
		{
			return messages[Random.Next(messages.Length)];
		}
	}
	internal sealed class KindredMasteryCalculator
	{
		private readonly KindredMasteryConfig _config;

		public KindredMasteryCalculator(KindredMasteryConfig config)
		{
			_config = config;
		}

		public KindredMasteryResult Calculate(Skills skills, SkillType target)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0143: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: 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_00b0: Unknown result type (might be due to invalid IL or missing references)
			float skillLevel = skills.GetSkillLevel(target);
			float num = 1f;
			float sourceLevel = skillLevel;
			float affinityStrength = _config.DefaultAffinityStrength.Value;
			SkillType? sourceSkill = null;
			string domainName = null;
			if (_config.Enabled.Value)
			{
				foreach (SkillDomain activeDomain in _config.ActiveDomains)
				{
					if (!activeDomain.Skills.Contains(target))
					{
						continue;
					}
					float num2 = skillLevel;
					SkillType? val = null;
					foreach (SkillType skill in activeDomain.Skills)
					{
						if (skill != target)
						{
							float skillLevel2 = skills.GetSkillLevel(skill);
							if (skillLevel2 > num2)
							{
								num2 = skillLevel2;
								val = skill;
							}
						}
					}
					if (val.HasValue)
					{
						float effectiveStrength = activeDomain.GetEffectiveStrength(_config.DefaultAffinityStrength.Value);
						float num3 = AffinityFormula.CalculateMultiplier(skillLevel, num2, effectiveStrength, _config.GlobalModifier.Value);
						if (num3 > num)
						{
							num = num3;
							sourceLevel = num2;
							sourceSkill = val;
							domainName = activeDomain.Name;
							affinityStrength = effectiveStrength;
						}
					}
				}
			}
			return new KindredMasteryResult(target, sourceSkill, skillLevel, sourceLevel, num, domainName, affinityStrength);
		}

		public float CalculateMultiplier(Skills skills, SkillType target)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			return Calculate(skills, target).Multiplier;
		}
	}
	internal sealed class KindredMasteryResult
	{
		public SkillType TargetSkill { get; }

		public SkillType? SourceSkill { get; }

		public float CurrentLevel { get; }

		public float SourceLevel { get; }

		public float Multiplier { get; }

		public string? DomainName { get; }

		public float AffinityStrength { get; }

		public float BonusPercentage => (Multiplier - 1f) * 100f;

		public bool HasAffinity
		{
			get
			{
				if (SourceSkill.HasValue)
				{
					return Multiplier > 1f;
				}
				return false;
			}
		}

		public KindredMasteryResult(SkillType targetSkill, SkillType? sourceSkill, float currentLevel, float sourceLevel, float multiplier, string? domainName, float affinityStrength)
		{
			//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)
			TargetSkill = targetSkill;
			SourceSkill = sourceSkill;
			CurrentLevel = currentLevel;
			SourceLevel = sourceLevel;
			Multiplier = multiplier;
			DomainName = domainName;
			AffinityStrength = affinityStrength;
		}
	}
}
namespace KindredMastery.Patches
{
	[HarmonyPatch(typeof(Player), "Message", new Type[]
	{
		typeof(MessageType),
		typeof(string),
		typeof(int),
		typeof(Sprite),
		typeof(bool)
	})]
	internal static class PlayerMessagePatch
	{
		[HarmonyPrefix]
		private static void Prefix(ref string msg)
		{
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			KindredMasteryResult currentAffinity = SkillsRaiseSkillPatch.CurrentAffinity;
			if (currentAffinity != null && currentAffinity.HasAffinity && !string.IsNullOrEmpty(msg) && msg.Contains("$msg_skillup"))
			{
				msg += (string?)(KindredMasteryPlugin.Configuration.LevelUpFeedback.Value switch
				{
					LevelUpFeedbackType.Minimal => " (Kindred Mastery)", 
					LevelUpFeedbackType.Detailed => " (Kindred Mastery: " + $"+{currentAffinity.BonusPercentage:F0}% {currentAffinity.TargetSkill} XP " + $"from {currentAffinity.SourceSkill} in {currentAffinity.DomainName})", 
					LevelUpFeedbackType.Fancy => " (" + FancyFeedbackProvider.GetMessage(currentAffinity) + " " + $"+{currentAffinity.BonusPercentage:F0}% {currentAffinity.TargetSkill} XP " + $"from {currentAffinity.SourceSkill})", 
					_ => string.Empty, 
				});
			}
		}
	}
	[HarmonyPatch(typeof(Skills), "RaiseSkill")]
	internal static class SkillsRaiseSkillPatch
	{
		private sealed class RaiseSkillState
		{
			public float PreviousLevel { get; set; }

			public KindredMasteryResult? Affinity { get; set; }
		}

		[ThreadStatic]
		private static KindredMasteryResult? _currentAffinity;

		internal static KindredMasteryResult? CurrentAffinity => _currentAffinity;

		[HarmonyPrefix]
		private static void Prefix(Skills __instance, SkillType skillType, ref float factor)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			KindredMasteryResult kindredMasteryResult = KindredMasteryPlugin.Calculator.Calculate(__instance, skillType);
			_currentAffinity = (kindredMasteryResult.HasAffinity ? kindredMasteryResult : null);
			factor *= kindredMasteryResult.Multiplier;
		}

		[HarmonyPostfix]
		private static void Postfix()
		{
			_currentAffinity = null;
		}
	}
}
namespace KindredMastery.Configuration
{
	internal static class DomainDefinitionValidator
	{
		private const float MaximumStrength = 10f;

		public static bool TryValidate(string? name, float? affinityStrength, IEnumerable<string>? skillNames, IEnumerable<string> validSkillNames, out ValidatedAffinityDomain? domain, out string error)
		{
			domain = null;
			string text = name?.Trim();
			if (string.IsNullOrWhiteSpace(text))
			{
				error = "Domain name cannot be empty.";
				return false;
			}
			if (affinityStrength.HasValue && !IsValidStrength(affinityStrength.Value))
			{
				error = $"Affinity strength must be between 0 and {10f}, inclusive.";
				return false;
			}
			if (skillNames == null)
			{
				error = "Skills must be an array of skill names.";
				return false;
			}
			Dictionary<string, string> dictionary = validSkillNames.ToDictionary<string, string, string>((string skill) => skill, (string skill) => skill, StringComparer.OrdinalIgnoreCase);
			HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			foreach (string skillName in skillNames)
			{
				string text2 = skillName?.Trim();
				if (string.IsNullOrWhiteSpace(text2) || !dictionary.TryGetValue(text2, out var value))
				{
					error = "Unknown or empty skill name: '" + (skillName ?? "<null>") + "'.";
					return false;
				}
				hashSet.Add(value);
			}
			if (hashSet.Count < 2)
			{
				error = "A domain must contain at least two distinct skills.";
				return false;
			}
			domain = new ValidatedAffinityDomain(text, affinityStrength, hashSet.ToArray());
			error = string.Empty;
			return true;
		}

		private static bool IsValidStrength(float strength)
		{
			if (!float.IsNaN(strength) && !float.IsInfinity(strength) && strength >= 0f)
			{
				return strength <= 10f;
			}
			return false;
		}
	}
	internal static class DomainJsonParser
	{
		private sealed class DomainJsonEntry
		{
			[JsonProperty("name")]
			public string? Name { get; set; }

			[JsonProperty("affinityStrength")]
			public float? AffinityStrength { get; set; }

			[JsonProperty("skills")]
			public List<string>? Skills { get; set; }
		}

		public static DomainJsonParseResult Parse(string json, IEnumerable<string> validSkillNames)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			string[] validSkillNames2 = validSkillNames.Distinct<string>(StringComparer.OrdinalIgnoreCase).ToArray();
			JToken obj = JToken.Parse(json);
			JToken obj2 = ((obj is JArray) ? obj : null) ?? throw new JsonSerializationException("The document must contain a JSON array of domains.");
			List<ValidatedAffinityDomain> list = new List<ValidatedAffinityDomain>();
			List<string> list2 = new List<string>();
			HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			foreach (JToken item in (JArray)obj2)
			{
				DomainJsonEntry domainJsonEntry;
				try
				{
					domainJsonEntry = item.ToObject<DomainJsonEntry>();
				}
				catch (Exception ex)
				{
					list2.Add("Skipping malformed domain entry: " + ex.Message);
					continue;
				}
				if (!DomainDefinitionValidator.TryValidate(domainJsonEntry?.Name, domainJsonEntry?.AffinityStrength, domainJsonEntry?.Skills, validSkillNames2, out ValidatedAffinityDomain domain, out string error))
				{
					string text = (string.IsNullOrWhiteSpace(domainJsonEntry?.Name) ? "<unnamed>" : domainJsonEntry.Name);
					list2.Add("Skipping domain '" + text + "': " + error);
				}
				else if (!hashSet.Add(domain.Name))
				{
					list2.Add("Skipping domain '" + domain.Name + "': Duplicate domain name.");
				}
				else
				{
					list.Add(domain);
				}
			}
			return new DomainJsonParseResult(list, list2);
		}
	}
	internal sealed class DomainJsonParseResult
	{
		public IReadOnlyList<ValidatedAffinityDomain> Domains { get; }

		public IReadOnlyList<string> Diagnostics { get; }

		public DomainJsonParseResult(IReadOnlyList<ValidatedAffinityDomain> domains, IReadOnlyList<string> diagnostics)
		{
			Domains = domains;
			Diagnostics = diagnostics;
		}
	}
	internal sealed class ValidatedAffinityDomain
	{
		public string Name { get; }

		public float? AffinityStrength { get; }

		public IReadOnlyList<string> Skills { get; }

		public ValidatedAffinityDomain(string name, float? affinityStrength, IReadOnlyList<string> skills)
		{
			Name = name;
			AffinityStrength = affinityStrength;
			Skills = skills;
		}
	}
	internal sealed class KindredMasteryConfig
	{
		private const string DefaultDomainsResourceName = "KindredMastery.Configuration.DefaultDomains.json";

		private const float MaximumStrength = 10f;

		private readonly ConfigFile _config;

		private readonly ManualLogSource _logger;

		public ConfigEntry<bool> Enabled { get; }

		public ConfigEntry<float> GlobalModifier { get; }

		public ConfigEntry<float> DefaultAffinityStrength { get; }

		public ConfigEntry<LevelUpFeedbackType> LevelUpFeedback { get; }

		public IReadOnlyList<SkillDomain> ActiveDomains { get; private set; } = Array.Empty<SkillDomain>();

		public string DomainsFilePath { get; }

		public KindredMasteryConfig(ConfigFile config, ManualLogSource logger)
		{
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Expected O, but got Unknown
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Expected O, but got Unknown
			_config = config;
			_logger = logger;
			DomainsFilePath = Path.ChangeExtension(config.ConfigFilePath, ".domains.json");
			Enabled = config.Bind<bool>("1 - General", "Enabled", true, "Enable or disable the Kindred Mastery plugin.");
			GlobalModifier = config.Bind<float>("1 - General", "Global Modifier", 1f, new ConfigDescription("Scales every affinity bonus. 1.0 applies the full configured strength.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), Array.Empty<object>()));
			DefaultAffinityStrength = config.Bind<float>("2 - Affinities", "Default Affinity Strength", 1f, new ConfigDescription("Affinity strength used by domains without an explicit affinityStrength.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), Array.Empty<object>()));
			LevelUpFeedback = config.Bind<LevelUpFeedbackType>("3 - Feedback", "Level Up Feedback", LevelUpFeedbackType.Detailed, "Controls the detail shown in skill level-up notifications.");
			InitializeDomainsFile();
			LoadDomains();
		}

		private void InitializeDomainsFile()
		{
			if (File.Exists(DomainsFilePath))
			{
				return;
			}
			try
			{
				using Stream stream = typeof(KindredMasteryConfig).Assembly.GetManifestResourceStream("KindredMastery.Configuration.DefaultDomains.json");
				if (stream == null)
				{
					throw new InvalidOperationException("Embedded default domains resource 'KindredMastery.Configuration.DefaultDomains.json' was not found.");
				}
				using StreamReader streamReader = new StreamReader(stream);
				File.WriteAllText(DomainsFilePath, streamReader.ReadToEnd());
				_logger.LogInfo((object)("Created default skill domains file '" + DomainsFilePath + "'."));
			}
			catch (Exception arg)
			{
				_logger.LogError((object)$"Could not initialize the domain JSON file '{DomainsFilePath}': {arg}");
			}
		}

		private void LoadDomains()
		{
			DomainJsonParseResult domainJsonParseResult;
			try
			{
				domainJsonParseResult = DomainJsonParser.Parse(File.ReadAllText(DomainsFilePath), Enum.GetNames(typeof(SkillType)));
			}
			catch (Exception ex)
			{
				_logger.LogError((object)("Could not read skill domains from '" + DomainsFilePath + "'. Keeping the current domains. " + ex.Message));
				return;
			}
			foreach (string diagnostic in domainJsonParseResult.Diagnostics)
			{
				_logger.LogWarning((object)diagnostic);
			}
			List<SkillDomain> list = (List<SkillDomain>)(ActiveDomains = domainJsonParseResult.Domains.Select((ValidatedAffinityDomain domain) => new SkillDomain(domain.Name, domain.AffinityStrength, ((IEnumerable<string>)domain.Skills).Select((Func<string, SkillType>)((string name) => (SkillType)Enum.Parse(typeof(SkillType), name))).ToArray())).ToList());
			_logger.LogInfo((object)$"KindredMastery: {list.Count} domains loaded, {domainJsonParseResult.Diagnostics.Count} skipped.");
			foreach (SkillDomain item in list)
			{
				string text = (item.AffinityStrength.HasValue ? item.AffinityStrength.Value.ToString("0.##") : "default");
				_logger.LogInfo((object)("   [OK] " + item.Name + " (strength: " + text + "): " + string.Join(", ", item.Skills)));
			}
		}

		public void Reload()
		{
			_config.Reload();
			LoadDomains();
		}
	}
	public enum LevelUpFeedbackType
	{
		None,
		Minimal,
		Detailed,
		Fancy
	}
	internal sealed class SkillDomain
	{
		public string Name { get; }

		public float? AffinityStrength { get; }

		public IReadOnlyList<SkillType> Skills { get; }

		public SkillDomain(string name, float? affinityStrength, IReadOnlyList<SkillType> skills)
		{
			Name = name;
			AffinityStrength = affinityStrength;
			Skills = skills;
		}

		public float GetEffectiveStrength(float defaultStrength)
		{
			return AffinityStrength ?? defaultStrength;
		}
	}
}
namespace KindredMastery.Commands
{
	internal static class KindredMasteryCommands
	{
		private static bool _registered;

		public static void Register(KindredMasteryConfig config)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Expected O, but got Unknown
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			if (!_registered)
			{
				new ConsoleCommand("kindredmastery", "Manage KindredMastery skill affinities", (ConsoleEvent)delegate(ConsoleEventArgs args)
				{
					Execute(args, config);
				}, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
				_registered = true;
			}
		}

		private static void Execute(ConsoleEventArgs args, KindredMasteryConfig config)
		{
			switch ((args.Length > 1) ? args[1].ToLowerInvariant() : "help")
			{
			case "list":
				PrintDomains(args.Context, config);
				break;
			case "status":
				PrintStatus(args.Context, config);
				break;
			case "reload":
				config.Reload();
				args.Context.AddString("KindredMastery configuration reloaded.");
				PrintStatus(args.Context, config);
				break;
			case "inspect":
				Inspect(args, config);
				break;
			default:
				args.Context.AddString("KindredMastery Commands:");
				args.Context.AddString("  kindredmastery list - List all skill domains and their related skills.");
				args.Context.AddString("  kindredmastery status - Show the current status of the KindredMastery plugin.");
				args.Context.AddString("  kindredmastery reload - Reload settings and skill domains.");
				break;
			}
		}

		private static void PrintDomains(Terminal terminal, KindredMasteryConfig config)
		{
			terminal.AddString($"KindredMastery: {config.ActiveDomains.Count} active domains");
			foreach (SkillDomain activeDomain in config.ActiveDomains)
			{
				string text = (activeDomain.AffinityStrength.HasValue ? activeDomain.AffinityStrength.Value.ToString("0.##") : $"default ({config.DefaultAffinityStrength.Value:0.##})");
				terminal.AddString("  " + activeDomain.Name + " [strength: " + text + "]: " + string.Join(", ", activeDomain.Skills));
			}
		}

		private static void PrintStatus(Terminal terminal, KindredMasteryConfig config)
		{
			terminal.AddString("KindredMastery status:");
			terminal.AddString($"  Enabled: {config.Enabled.Value}");
			terminal.AddString($"  Global modifier: {config.GlobalModifier.Value:0.##}");
			terminal.AddString($"  Default affinity strength: {config.DefaultAffinityStrength.Value:0.##}");
			terminal.AddString($"  Active domains: {config.ActiveDomains.Count}");
			terminal.AddString("  Domains file: " + config.DomainsFilePath);
		}

		private static void Inspect(ConsoleEventArgs args, KindredMasteryConfig config)
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			if (args.Length < 3)
			{
				args.Context.AddString("Usage: kindredmastery inspect <skill>");
				return;
			}
			if (!Enum.TryParse<SkillType>(args[2], ignoreCase: true, out SkillType result) || !Enum.IsDefined(typeof(SkillType), result))
			{
				args.Context.AddString("Unknown skill: " + args[2]);
				return;
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				args.Context.AddString("A character must be loaded to inspect skills.");
				return;
			}
			KindredMasteryResult kindredMasteryResult = new KindredMasteryCalculator(config).Calculate(((Character)localPlayer).GetSkills(), result);
			args.Context.AddString($"KindredMastery - {kindredMasteryResult.TargetSkill}");
			args.Context.AddString($"  Current level: {kindredMasteryResult.CurrentLevel:F1}");
			if (kindredMasteryResult.HasAffinity)
			{
				args.Context.AddString("  Strongest domain: " + kindredMasteryResult.DomainName + " " + $"(strength {kindredMasteryResult.AffinityStrength:0.##})");
				args.Context.AddString($"  Strongest related skill: {kindredMasteryResult.SourceSkill} " + $"({kindredMasteryResult.SourceLevel:F1})");
				args.Context.AddString($"  Affinity bonus: +{kindredMasteryResult.BonusPercentage:F0}%");
			}
			else
			{
				args.Context.AddString("  No active catch-up bonus.");
			}
			args.Context.AddString($"  Experience multiplier: {kindredMasteryResult.Multiplier:F2}x");
		}
	}
}