Decompiled source of Trait Control v0.1.1

plugins/TraitControl.dll

Decompiled 9 hours 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.RegularExpressions;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using MonoMod.Utils;
using Newtonsoft.Json;
using Shared;
using TraitControl.Patches;
using UnityEngine;
using UnityEngine.Serialization;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8.1", FrameworkDisplayName = ".NET Framework 4.8.1")]
[assembly: AssemblyCompany("p1xel8ted")]
[assembly: AssemblyConfiguration("Release-Thunderstore")]
[assembly: AssemblyDescription("TraitControl")]
[assembly: AssemblyFileVersion("0.1.1.0")]
[assembly: AssemblyInformationalVersion("0.1.1+985c746328f160bdc22b06eded5c672299f883bb")]
[assembly: AssemblyProduct("TraitControl")]
[assembly: AssemblyTitle("TraitControl")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.1.1.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.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 TraitControl
{
	[BepInPlugin("p1xel8ted.cotl.traitcontrol", "Trait Control", "0.1.1")]
	[BepInDependency("com.bepis.bepinex.configurationmanager", "18.4.1")]
	[BepInIncompatibility("NothingNegative")]
	public class Plugin : BaseUnityPlugin
	{
		[CompilerGenerated]
		private sealed class <RefreshConfigurationManager>d__52 : IEnumerator<object>, IDisposable, IEnumerator
		{
			private int <>1__state;

			private object <>2__current;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <RefreshConfigurationManager>d__52(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					<>2__current = null;
					<>1__state = 1;
					return true;
				case 1:
				{
					<>1__state = -1;
					BaseUnityPlugin configurationManager = GetConfigurationManager();
					if ((Object)(object)configurationManager == (Object)null)
					{
						Log.LogWarning((object)"ConfigurationManager plugin not found");
						return false;
					}
					Type type = ((object)configurationManager).GetType();
					PropertyInfo property = type.GetProperty("DisplayingWindow");
					if (property == null)
					{
						Log.LogWarning((object)"ConfigurationManager.DisplayingWindow property not found");
						return false;
					}
					if (!(bool)property.GetValue(configurationManager))
					{
						return false;
					}
					MethodInfo method = type.GetMethod("BuildSettingList");
					if (method == null)
					{
						Log.LogWarning((object)"ConfigurationManager.BuildSettingList method not found");
						return false;
					}
					method.Invoke(configurationManager, null);
					return false;
				}
				}
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		private const string PluginGuid = "p1xel8ted.cotl.traitcontrol";

		internal const string PluginName = "Trait Control";

		private const string PluginVer = "0.1.1";

		private const string TraitReplacementSection = "01. Trait Replacement";

		private const string TraitWeightsSection = "02. Trait Weights";

		private static bool? _isNothingNegativePresentCache;

		private static Plugin _instance;

		internal static ConfigEntry<bool> NoNegativeTraits { get; private set; }

		internal static ConfigEntry<bool> UseUnlockedTraitsOnly { get; private set; }

		internal static ConfigEntry<bool> IncludeImmortal { get; private set; }

		internal static ConfigEntry<bool> IncludeDisciple { get; private set; }

		internal static ConfigEntry<bool> ShowNotificationsWhenRemovingTraits { get; private set; }

		internal static ConfigEntry<bool> ShowNotificationsWhenAddingTraits { get; private set; }

		internal static ConfigEntry<bool> EnableTraitWeights { get; private set; }

		internal static Dictionary<TraitType, ConfigEntry<float>> TraitWeights { get; } = new Dictionary<TraitType, ConfigEntry<float>>();


		internal static ManualLogSource Log { get; private set; }

		private static ConfigFile ConfigInstance { get; set; }

		private void Awake()
		{
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Expected O, but got Unknown
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Expected O, but got Unknown
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0128: Expected O, but got Unknown
			//IL_0186: Unknown result type (might be due to invalid IL or missing references)
			//IL_0190: Expected O, but got Unknown
			//IL_01ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f8: Expected O, but got Unknown
			//IL_022d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0237: Expected O, but got Unknown
			//IL_0270: Unknown result type (might be due to invalid IL or missing references)
			//IL_027a: Expected O, but got Unknown
			_instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			ConfigInstance = ((BaseUnityPlugin)this).Config;
			NoNegativeTraits = ConfigInstance.Bind<bool>("01. Trait Replacement", "Enable Trait Replacement", false, new ConfigDescription("Replace negative traits with positive ones based on the configuration below.", (AcceptableValueBase)null, new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 10
				}
			}));
			NoNegativeTraits.SettingChanged += delegate
			{
				UpdateNoNegativeTraits();
			};
			UseUnlockedTraitsOnly = ConfigInstance.Bind<bool>("01. Trait Replacement", "Use Unlocked Traits Only", true, new ConfigDescription("Only use unlocked traits when replacing negative traits.", (AcceptableValueBase)null, new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 9
				}
			}));
			UseUnlockedTraitsOnly.SettingChanged += delegate
			{
				TraitControl.Patches.NoNegativeTraits.GenerateAvailableTraits();
			};
			IncludeImmortal = ConfigInstance.Bind<bool>("01. Trait Replacement", "Include Immortal", false, new ConfigDescription("Include the Immortal trait when replacing negative traits.", (AcceptableValueBase)null, new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 8
				}
			}));
			IncludeImmortal.SettingChanged += delegate
			{
				TraitControl.Patches.NoNegativeTraits.GenerateAvailableTraits();
			};
			IncludeDisciple = ConfigInstance.Bind<bool>("01. Trait Replacement", "Include Disciple", false, new ConfigDescription("Include the Disciple trait when replacing negative traits.", (AcceptableValueBase)null, new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 7
				}
			}));
			IncludeDisciple.SettingChanged += delegate
			{
				TraitControl.Patches.NoNegativeTraits.GenerateAvailableTraits();
			};
			ShowNotificationsWhenRemovingTraits = ConfigInstance.Bind<bool>("01. Trait Replacement", "Show Notifications When Removing Traits", false, new ConfigDescription("Show notifications when removing negative traits.", (AcceptableValueBase)null, new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 6
				}
			}));
			ShowNotificationsWhenAddingTraits = ConfigInstance.Bind<bool>("01. Trait Replacement", "Show Notifications When Adding Traits", false, new ConfigDescription("Show notifications when adding positive traits.", (AcceptableValueBase)null, new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 5
				}
			}));
			EnableTraitWeights = ConfigInstance.Bind<bool>("02. Trait Weights", "Enable Trait Weights", false, new ConfigDescription("Enable weighted random selection for starting traits. When enabled, you can configure how often each trait appears below. Set a weight to 0 to disable that trait entirely.", (AcceptableValueBase)null, new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 1000
				}
			}));
			EnableTraitWeights.SettingChanged += delegate
			{
				UpdateTraitWeightVisibility();
			};
			GenerateTraitWeightConfigs();
			Harmony.CreateAndPatchAll(Assembly.GetExecutingAssembly(), "p1xel8ted.cotl.traitcontrol");
			Log.LogInfo((object)"Trait Control loaded.");
		}

		private static void GenerateTraitWeightConfigs()
		{
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: 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_00f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: Expected O, but got Unknown
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			HashSet<TraitType> hashSet = new HashSet<TraitType>();
			hashSet.UnionWith(FollowerTrait.StartingTraits);
			hashSet.UnionWith(FollowerTrait.RareStartingTraits);
			int count = hashSet.Count;
			foreach (TraitType item in hashSet.OrderBy((TraitType t) => ((object)(TraitType)(ref t)).ToString()))
			{
				TraitType current = item;
				bool flag = !EnableTraitWeights.Value;
				string traitDescription = GetTraitDescription(current);
				string text = (string.IsNullOrEmpty(traitDescription) ? $"Weight for {current}. Higher = more likely. Set to 0 to disable. Default is 1.0." : (traitDescription + "\n\nWeight: Higher = more likely. Set to 0 to disable. Default is 1.0."));
				ConfigEntry<float> value = ConfigInstance.Bind<float>("02. Trait Weights", ((object)(TraitType)(ref current)).ToString(), 1f, new ConfigDescription(text, (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), new object[1]
				{
					new ConfigurationManagerAttributes
					{
						Order = count--,
						Browsable = !flag
					}
				}));
				TraitWeights[current] = value;
			}
		}

		private static string GetTraitDescription(TraitType trait)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				string localizedDescription = FollowerTrait.GetLocalizedDescription(trait, (FollowerBrain)null);
				if (string.IsNullOrWhiteSpace(localizedDescription) || localizedDescription.StartsWith("Traits/"))
				{
					return null;
				}
				return StripRichText(localizedDescription);
			}
			catch
			{
				return null;
			}
		}

		private static string StripRichText(string input)
		{
			if (string.IsNullOrEmpty(input))
			{
				return input;
			}
			return Regex.Replace(input, "<[^>]+>", string.Empty).Trim();
		}

		private static void UpdateTraitWeightVisibility()
		{
			bool value = EnableTraitWeights.Value;
			foreach (ConfigEntry<float> value2 in TraitWeights.Values)
			{
				ConfigDescription description = ((ConfigEntryBase)value2).Description;
				if (description != null && description.Tags?.Length > 0 && ((ConfigEntryBase)value2).Description.Tags[0] is ConfigurationManagerAttributes configurationManagerAttributes)
				{
					configurationManagerAttributes.Browsable = value;
				}
			}
			Plugin instance = _instance;
			if (instance != null)
			{
				((MonoBehaviour)instance).StartCoroutine(RefreshConfigurationManager());
			}
		}

		private static BaseUnityPlugin GetConfigurationManager()
		{
			return (from pluginInfo in Chainloader.PluginInfos.Values
				where pluginInfo.Metadata.GUID == "com.bepis.bepinex.configurationmanager"
				select pluginInfo.Instance).FirstOrDefault();
		}

		[IteratorStateMachine(typeof(<RefreshConfigurationManager>d__52))]
		private static IEnumerator RefreshConfigurationManager()
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <RefreshConfigurationManager>d__52(0);
		}

		private static void UpdateNoNegativeTraits()
		{
			if (IsNothingNegativePresent())
			{
				NoNegativeTraits.Value = false;
			}
			else if (NoNegativeTraits.Value)
			{
				TraitControl.Patches.NoNegativeTraits.UpdateAllFollowerTraits();
			}
			else
			{
				TraitControl.Patches.NoNegativeTraits.RestoreOriginalTraits();
			}
		}

		internal static bool IsNothingNegativePresent()
		{
			bool valueOrDefault = _isNothingNegativePresentCache.GetValueOrDefault();
			if (!_isNothingNegativePresentCache.HasValue)
			{
				valueOrDefault = Chainloader.PluginInfos.Any((KeyValuePair<string, PluginInfo> plugin) => plugin.Value.Instance.Info.Metadata.GUID.Equals("NothingNegative", StringComparison.OrdinalIgnoreCase));
				_isNothingNegativePresentCache = valueOrDefault;
			}
			return _isNothingNegativePresentCache.Value;
		}

		internal static void L(string message)
		{
			Log.LogInfo((object)message);
		}
	}
}
namespace TraitControl.Patches
{
	[Harmony]
	public static class NoNegativeTraits
	{
		[Serializable]
		public class FollowerTraitBackup
		{
			[FormerlySerializedAs("ID")]
			public int id;

			[FormerlySerializedAs("Name")]
			public string name;

			[FormerlySerializedAs("Traits")]
			public List<string> traits;
		}

		private static HashSet<TraitType> _allTraits;

		private static List<FollowerTraitBackup> _followerTraitBackups = new List<FollowerTraitBackup>();

		private static string DataPath => Path.Combine(Application.persistentDataPath, "saves", $"slot_{SaveAndLoad.SAVE_SLOT}_follower_trait_backups.json");

		private static void LoadBackupFromFile()
		{
			if (!Plugin.IsNothingNegativePresent() && File.Exists(DataPath))
			{
				_followerTraitBackups = JsonConvert.DeserializeObject<List<FollowerTraitBackup>>(File.ReadAllText(DataPath));
				Plugin.L($"Loaded {_followerTraitBackups.Count} follower trait backups.");
			}
		}

		private static HashSet<TraitType> InitializeAllTraits()
		{
			//IL_0158: Unknown result type (might be due to invalid IL or missing references)
			//IL_015d: Unknown result type (might be due to invalid IL or missing references)
			HashSet<TraitType> hashSet = new HashSet<TraitType>();
			hashSet.UnionWith(FollowerTrait.SingleTraits);
			hashSet.UnionWith(FollowerTrait.GoodTraits);
			hashSet.UnionWith(FollowerTrait.SinTraits);
			hashSet.UnionWith(FollowerTrait.RareStartingTraits);
			hashSet.UnionWith(FollowerTrait.StartingTraits);
			hashSet.UnionWith(FollowerTrait.ExcludedFromMating);
			List<TraitType> cultTraits = DataManager.Instance.CultTraits;
			if (Plugin.UseUnlockedTraitsOnly.Value && cultTraits != null && cultTraits.Count > 0)
			{
				Plugin.L("Using unlocked traits only for available traits.");
				HashSet<TraitType> hashSet2 = hashSet.Where((TraitType a) => cultTraits.Contains(a) && FollowerTrait.IsPositiveTrait(a)).ToHashSet();
				if (hashSet2.Count > 0)
				{
					hashSet = hashSet2;
				}
				else
				{
					Plugin.L("No unlocked positive traits found. Falling back to all positive traits.");
				}
			}
			hashSet.RemoveWhere((TraitType a) => !FollowerTrait.IsPositiveTrait(a));
			hashSet.RemoveWhere((TraitType a) => (int)a == 81);
			if (!Plugin.IncludeImmortal.Value)
			{
				Plugin.L("Removing Immortal trait from available traits.");
				hashSet.Remove((TraitType)32);
			}
			if (!Plugin.IncludeDisciple.Value)
			{
				Plugin.L("Removing Disciple trait from available traits.");
				hashSet.Remove((TraitType)35);
			}
			Plugin.L("All 'positive' traits currently available based on your configuration:");
			foreach (TraitType item in hashSet)
			{
				TraitType current = item;
				Plugin.L("\t" + ((object)(TraitType)(ref current)).ToString());
			}
			return hashSet;
		}

		internal static void UpdateAllFollowerTraits()
		{
			if (Plugin.IsNothingNegativePresent())
			{
				return;
			}
			foreach (Follower item in FollowerManager.Followers.Values.SelectMany((List<Follower> list) => list))
			{
				ProcessTraitReplacement(item.Brain);
			}
		}

		internal static void RestoreOriginalTraits()
		{
			if (Plugin.IsNothingNegativePresent() || _followerTraitBackups == null)
			{
				return;
			}
			foreach (FollowerTraitBackup followerTraitBackup in _followerTraitBackups)
			{
				Follower val = FollowerManager.FindFollowerByID(followerTraitBackup.id);
				if ((Object)(object)val == (Object)null || val.Brain == null)
				{
					Plugin.L($"Could not find follower with ID {followerTraitBackup.id} to restore traits. Is the follower dead?");
				}
				else if (followerTraitBackup.traits == null)
				{
					Plugin.L("No traits to restore for " + val.Brain._directInfoAccess.Name);
				}
				else
				{
					RestoreFollowerTraits(val, followerTraitBackup.traits);
				}
			}
		}

		private static void ClearAllTraits(Follower follower)
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			if (Plugin.IsNothingNegativePresent())
			{
				return;
			}
			foreach (TraitType item in follower.Brain._directInfoAccess.Traits.ToList())
			{
				follower.Brain.RemoveTrait(item, ShouldShowRemoveNotification(item));
			}
		}

		private static void RestoreFollowerTraits(Follower follower, List<string> backupTraits)
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			Plugin.L("Restoring traits for " + follower.Brain._directInfoAccess.Name);
			ClearAllTraits(follower);
			foreach (string backupTrait in backupTraits)
			{
				if (Enum.TryParse<TraitType>(backupTrait, out TraitType result))
				{
					follower.Brain.AddTrait(result, ShouldShowAddNotification(result));
				}
				else
				{
					Plugin.L("Could not parse trait " + backupTrait + " for follower " + follower.Brain._directInfoAccess.Name);
				}
			}
		}

		private static void SaveBackupToFile(bool log = true)
		{
			if (Plugin.IsNothingNegativePresent())
			{
				return;
			}
			try
			{
				if (_followerTraitBackups == null || !_followerTraitBackups.Any())
				{
					if (log)
					{
						Plugin.L("No follower trait backups to save.");
					}
					return;
				}
				string contents = JsonConvert.SerializeObject((object)_followerTraitBackups, (Formatting)1);
				File.WriteAllText(DataPath, contents);
				if (log)
				{
					Plugin.L($"Saved {_followerTraitBackups.Count} follower trait backups to {DataPath}");
				}
			}
			catch (Exception ex)
			{
				if (log)
				{
					Plugin.L("Error saving follower trait backups: " + ex.Message);
				}
			}
		}

		private static void ProcessTraitReplacement(FollowerBrain brain, bool directManipulation = false)
		{
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: 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_010c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: 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_015a: 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_00a4: 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)
			//IL_00b1: 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_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0131: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: 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_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0142: Unknown result type (might be due to invalid IL or missing references)
			//IL_0125: Unknown result type (might be due to invalid IL or missing references)
			CreateTraitBackup(brain);
			List<TraitType> traits = brain._directInfoAccess.Traits;
			Plugin.L("Processing traits for " + brain._directInfoAccess.Name);
			foreach (TraitType item in traits.ToList())
			{
				if (FollowerTrait.IsPositiveTrait(item))
				{
					Plugin.L($"\tSkipping positive trait {item}");
					continue;
				}
				if (!Plugin.UseUnlockedTraitsOnly.Value && IsExclusiveTrait(item) && TryReplaceExclusiveTrait(item, out var replacement))
				{
					if (directManipulation)
					{
						traits.Remove(item);
						if (!traits.Contains(replacement))
						{
							traits.Add(replacement);
						}
					}
					else
					{
						brain.RemoveTrait(item, ShouldShowRemoveNotification(item));
						brain.AddTrait(replacement, ShouldShowAddNotification(replacement));
					}
					Plugin.L($"\tReplacing negative exclusive trait {item} with exclusive {replacement}");
					continue;
				}
				Plugin.L($"\tRemoving negative trait {item}");
				if (directManipulation)
				{
					traits.Remove(item);
				}
				else
				{
					brain.RemoveTrait(item, ShouldShowRemoveNotification(item));
				}
				TraitType val = FindPositiveReplacement(brain);
				if ((int)val != 0)
				{
					if (directManipulation)
					{
						if (!traits.Contains(val))
						{
							traits.Add(val);
						}
					}
					else
					{
						brain.AddTrait(val, ShouldShowAddNotification(val));
					}
					Plugin.L($"\tAdded replacement positive trait {val}");
				}
				else
				{
					Plugin.L($"\tNo replacement trait available for {item}");
				}
			}
		}

		private static void CreateTraitBackup(FollowerBrain brain)
		{
			List<string> traits = brain._directInfoAccess.Traits.Select((TraitType trait) => ((object)(TraitType)(ref trait)).ToString()).ToList();
			FollowerTraitBackup traitBackup = new FollowerTraitBackup
			{
				traits = traits,
				id = brain._directInfoAccess.ID,
				name = brain._directInfoAccess.Name
			};
			List<FollowerTraitBackup> followerTraitBackups = _followerTraitBackups;
			if (followerTraitBackups == null || !followerTraitBackups.Any((FollowerTraitBackup b) => b.id == traitBackup.id && b.name == traitBackup.name))
			{
				Plugin.L("Backing up original traits for " + brain._directInfoAccess.Name);
				_followerTraitBackups?.Add(traitBackup);
				SaveBackupToFile();
			}
		}

		private static bool TryReplaceExclusiveTrait(TraitType trait, out TraitType replacement)
		{
			//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_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Expected I4, but got Unknown
			if (FollowerTrait.ExclusiveTraits.TryGetValue(trait, out replacement))
			{
				return true;
			}
			KeyValuePair<TraitType, TraitType> keyValuePair = FollowerTrait.ExclusiveTraits.FirstOrDefault((KeyValuePair<TraitType, TraitType> x) => x.Value == trait);
			if ((int)keyValuePair.Key == 0)
			{
				return false;
			}
			replacement = (TraitType)(int)keyValuePair.Key;
			return true;
		}

		private static TraitType FindPositiveReplacement(FollowerBrain brain)
		{
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			if (_allTraits == null || _allTraits.Count == 0)
			{
				GenerateAvailableTraits();
			}
			if (_allTraits == null || _allTraits.Count == 0)
			{
				Plugin.L("Warning: No positive traits available for replacement.");
				return (TraitType)0;
			}
			List<TraitType> list = _allTraits.Where((TraitType t) => !brain.HasTrait(t)).ToList();
			if (list.Count == 0)
			{
				Plugin.L("Warning: Follower already has all available positive traits.");
				return (TraitType)0;
			}
			return list[Random.Range(0, list.Count)];
		}

		[HarmonyPostfix]
		[HarmonyPatch(/*Could not decode attribute arguments.*/)]
		private static void FollowerBrain_Constructor(ref FollowerBrain __instance)
		{
			if (!Plugin.IsNothingNegativePresent() && Plugin.NoNegativeTraits.Value)
			{
				ProcessTraitReplacement(__instance, directManipulation: true);
			}
		}

		private static bool IsExclusiveTrait(TraitType trait)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			if (!FollowerTrait.ExclusiveTraits.ContainsKey(trait))
			{
				return FollowerTrait.ExclusiveTraits.ContainsValue(trait);
			}
			return true;
		}

		private static bool HasValidLocalization(TraitType trait)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			string localizedTitle = FollowerTrait.GetLocalizedTitle(trait);
			if (!string.IsNullOrWhiteSpace(localizedTitle))
			{
				return !localizedTitle.StartsWith("Traits/");
			}
			return false;
		}

		private static bool ShouldShowAddNotification(TraitType trait)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			if (Plugin.ShowNotificationsWhenAddingTraits.Value)
			{
				return HasValidLocalization(trait);
			}
			return false;
		}

		private static bool ShouldShowRemoveNotification(TraitType trait)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			if (Plugin.ShowNotificationsWhenRemovingTraits.Value)
			{
				return HasValidLocalization(trait);
			}
			return false;
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(BiomeBaseManager), "Start")]
		private static void BiomeBaseManager_Start()
		{
			if (!Plugin.IsNothingNegativePresent())
			{
				_followerTraitBackups?.Clear();
				LoadBackupFromFile();
				GenerateAvailableTraits();
			}
		}

		internal static void GenerateAvailableTraits()
		{
			_allTraits = InitializeAllTraits();
		}
	}
	[Harmony]
	public static class TraitWeights
	{
		[HarmonyPrefix]
		[HarmonyPatch(typeof(FollowerTrait), "GetStartingTrait")]
		public static bool GetStartingTrait_Prefix(ref TraitType __result)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Expected I4, but got Unknown
			if (!Plugin.EnableTraitWeights.Value)
			{
				return true;
			}
			__result = (TraitType)(int)GetWeightedTrait(FollowerTrait.StartingTraits);
			return false;
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(FollowerTrait), "GetRareTrait")]
		public static bool GetRareTrait_Prefix(ref TraitType __result)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Expected I4, but got Unknown
			if (!Plugin.EnableTraitWeights.Value)
			{
				return true;
			}
			__result = (TraitType)(int)GetWeightedTrait(FollowerTrait.RareStartingTraits);
			return false;
		}

		private static TraitType GetWeightedTrait(List<TraitType> sourceTraits)
		{
			//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_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: 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_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			List<TraitType> list = new List<TraitType>(sourceTraits);
			for (int num = list.Count - 1; num >= 0; num--)
			{
				TraitType val = list[num];
				foreach (FollowerBrain allBrain in FollowerBrain.AllBrains)
				{
					if (allBrain.HasTrait(val) && FollowerTrait.SingleTraits.Contains(val))
					{
						list.RemoveAt(num);
						break;
					}
				}
			}
			int num2 = 0;
			while (++num2 < 100)
			{
				TraitType val2 = SelectWeightedTrait(list);
				if ((int)val2 == 0)
				{
					break;
				}
				if (!FollowerTrait.IsTraitUnavailable(val2) && !DataManager.Instance.CultTraits.Contains(val2))
				{
					return val2;
				}
			}
			return (TraitType)0;
		}

		private static TraitType SelectWeightedTrait(List<TraitType> traits)
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: 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_00ae: Unknown result type (might be due to invalid IL or missing references)
			if (traits.Count == 0)
			{
				return (TraitType)0;
			}
			List<float> list = new List<float>();
			List<TraitType> list2 = new List<TraitType>();
			float num = 0f;
			foreach (TraitType trait in traits)
			{
				float traitWeight = GetTraitWeight(trait);
				if (traitWeight > 0f)
				{
					list.Add(traitWeight);
					list2.Add(trait);
					num += traitWeight;
				}
			}
			if (list2.Count == 0 || num <= 0f)
			{
				return (TraitType)0;
			}
			float num2 = Random.Range(0f, num);
			float num3 = 0f;
			for (int i = 0; i < list2.Count; i++)
			{
				num3 += list[i];
				if (num2 <= num3)
				{
					return list2[i];
				}
			}
			return list2[list2.Count - 1];
		}

		private static float GetTraitWeight(TraitType trait)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			if (Plugin.TraitWeights.TryGetValue(trait, out var value))
			{
				return value.Value;
			}
			return 1f;
		}
	}
}
namespace Shared
{
	public static class Helpers
	{
		private static class StorefrontDetector
		{
			private static readonly string[] PiracyFiles = new string[65]
			{
				"SmartSteamEmu.ini", "codex.ini", "steam_emu.ini", "goldberg_emulator.dll", "steamclient_loader.dll", "steam_api64_o.dll", "steam_api.cdx", "steam_api64.cdx.dll", "steam_interfaces.txt", "local_save.txt",
				"valve.ini", "codex64.dll", "coldclient.dll", "ColdClientLoader.ini", "steamless.dll", "GreenLuma", "CreamAPI.dll", "cream_api.ini", "ScreamAPI.dll", "OnlineFix.dll",
				"OnlineFix.url", "online-fix.me", "CODEX", "SKIDROW", "CPY", "PLAZA", "HOODLUM", "EMPRESS", "TENOKE", "PROPHET",
				"REVOLT", "DARKSiDERS", "RAZOR1911", "FLT", "FLT.dll", "RUNE", "RUNE.ini", "TiNYiSO", "RELOADED", "RLD!",
				"DOGE", "BAT", "P2P", "ElAmigos", "FitGirl", "DODI", "xatab", "KaOs", "IGG", "Masquerade",
				"3dmgame.dll", "ALI213.dll", "crack", "crack.exe", "Crack.nfo", "crackfix", "CrackOnly", "fix.exe", "gamefix.dll", "SKIDROW.ini",
				"nosTEAM", "NoSteam", "FCKDRM", "Goldberg", "VALVEEMPRESS"
			};

			public static string DetectStorefront()
			{
				string dir = Directory.GetCurrentDirectory();
				string text = "Unknown";
				if (File.Exists(Path.Combine(dir, "steam_api.dll")) || File.Exists(Path.Combine(dir, "steam_api64.dll")) || File.Exists(Path.Combine(dir, "steam_appid.txt")) || Directory.Exists(Path.Combine(dir, "steam_settings")))
				{
					text = "Steam";
				}
				else if (Directory.GetFiles(dir, "goggame-*.info").Any() || File.Exists(Path.Combine(dir, "galaxy.dll")))
				{
					text = "GOG";
				}
				else if (File.Exists(Path.Combine(dir, "EOSSDK-Win64-Shipping.dll")) || File.Exists(Path.Combine(dir, "EpicOnlineServices.dll")) || Directory.Exists(Path.Combine(dir, ".egstore")))
				{
					text = "Epic";
				}
				else if (IsProcessRunning("steam"))
				{
					text = "Steam (process only)";
				}
				else if (IsProcessRunning("GalaxyClient"))
				{
					text = "GOG (process only)";
				}
				else if (IsProcessRunning("EpicGamesLauncher"))
				{
					text = "Epic (process only)";
				}
				if (PiracyFiles.Any((string pirate) => File.Exists(Path.Combine(dir, pirate)) || Directory.Exists(Path.Combine(dir, pirate))))
				{
					text += " + Possible Pirated/Cracked Files Found!";
				}
				return text;
			}

			private static bool IsProcessRunning(string name)
			{
				return Process.GetProcessesByName(name).Length != 0;
			}
		}

		[CompilerGenerated]
		private sealed class <FilterEnumerator>d__5 : IEnumerator<object>, IDisposable, IEnumerator
		{
			private int <>1__state;

			private object <>2__current;

			public IEnumerator original;

			public Type[] typesToRemove;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <FilterEnumerator>d__5(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					break;
				case 1:
					<>1__state = -1;
					break;
				}
				while (original.MoveNext())
				{
					object current = original.Current;
					if (current != null && !ArrayExtensions.Contains<Type>(typesToRemove, current.GetType()))
					{
						<>2__current = current;
						<>1__state = 1;
						return true;
					}
				}
				return false;
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		internal static List<Follower> AllFollowers => FollowerManager.Followers.SelectMany((KeyValuePair<FollowerLocation, List<Follower>> followerList) => followerList.Value).ToList();

		public static void PrintModLoaded(string plugin, ManualLogSource logger)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			string text = Application.version.Replace("\r", "").Replace("\n", "");
			string buildGUID = Application.buildGUID;
			Platform current = PlatformHelper.Current;
			string text2 = StorefrontDetector.DetectStorefront();
			logger.LogInfo((object)"==========================================");
			logger.LogInfo((object)("  Plugin Loaded: " + plugin));
			logger.LogInfo((object)("  Version   : " + text + " (BuildGUID: " + buildGUID + ")"));
			logger.LogInfo((object)$"  Platform  : {current}");
			logger.LogInfo((object)("  Storefront: " + text2));
			logger.LogInfo((object)"==========================================");
		}

		public static bool IsMultiplierActive(float value)
		{
			return !Mathf.Approximately(value, 1f);
		}

		[IteratorStateMachine(typeof(<FilterEnumerator>d__5))]
		public static IEnumerator FilterEnumerator(IEnumerator original, Type[] typesToRemove)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <FilterEnumerator>d__5(0)
			{
				original = original,
				typesToRemove = typesToRemove
			};
		}
	}
	internal sealed class ConfigurationManagerAttributes
	{
		public delegate void CustomHotkeyDrawerFunc(ConfigEntryBase setting, ref bool isCurrentlyAcceptingInput);

		public bool? ShowRangeAsPercent;

		public Action<ConfigEntryBase> CustomDrawer;

		public CustomHotkeyDrawerFunc CustomHotkeyDrawer;

		public bool? Browsable;

		public string Category;

		public object DefaultValue;

		public bool? HideDefaultButton;

		public bool? HideSettingName;

		public string Description;

		public string DispName;

		public int? Order;

		public bool? ReadOnly;

		public bool? IsAdvanced;

		public Func<object, string> ObjToStr;

		public Func<string, object> StrToObj;
	}
}