Decompiled source of SyncDiskSuite v1.4.0

BepInEx/plugins/SoDVanillaSplit/SoDVanillaSplit.dll

Decompiled 5 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Core.Logging.Interpolation;
using BepInEx.Logging;
using BepInEx.Unity.IL2CPP;
using HarmonyLib;
using Il2CppInterop.Runtime.InteropTypes;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppSystem.Collections.Generic;
using Microsoft.CodeAnalysis;
using SOD.Common;
using SOD.Common.Helpers.SyncDiskObjects;
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("SoDVanillaSplit")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.5.0.0")]
[assembly: AssemblyInformationalVersion("0.5.0")]
[assembly: AssemblyProduct("SoDVanillaSplit")]
[assembly: AssemblyTitle("SoDVanillaSplit")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.5.0.0")]
[module: UnverifiableCode]
[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.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace SoDVanillaSplit
{
	internal static class MotivesCompat
	{
		private static readonly Dictionary<int, Interactable> Index = new Dictionary<int, Interactable>();

		private static IntPtr indexedList = IntPtr.Zero;

		private static int indexedCount = -1;

		private static bool told;

		private static bool on = true;

		private static int calls;

		private static int window = -1;

		private static int lines;

		internal static void Hook(Harmony harmony)
		{
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Expected O, but got Unknown
			try
			{
				Assembly assembly = null;
				Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
				foreach (Assembly assembly2 in assemblies)
				{
					if (assembly2.GetName().Name == "SODMotives")
					{
						assembly = assembly2;
					}
				}
				if (!(assembly == null))
				{
					MethodInfo methodInfo = AccessTools.Method(assembly.GetType("SODMotives.Persistence"), "FindInteractable", (Type[])null, (Type[])null);
					if (methodInfo == null)
					{
						Plugin.L.LogInfo((object)"[COMPAT] SOD Motives found, but not its note lookup; left alone");
						return;
					}
					harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(MotivesCompat), "Find", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					on = Plugin.MotivesFix.Value;
					Plugin.L.LogInfo((object)(on ? "[COMPAT] SOD Motives note lookup now uses an index" : "[COMPAT] SOD Motives note lookup left as it is (FastMotivesNoteLookup off); only counted"));
				}
			}
			catch (Exception ex)
			{
				Plugin.L.LogWarning((object)("[COMPAT] SOD Motives hook: " + ex.Message));
			}
		}

		private static bool Find(CityData city, int id, ref Interactable __result)
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Expected O, but got Unknown
			//IL_0132: Unknown result type (might be due to invalid IL or missing references)
			//IL_0138: Expected O, but got Unknown
			calls++;
			int num = (int)Time.unscaledTime;
			if (num != window)
			{
				if (window >= 0 && calls > 1 && lines++ < 60)
				{
					ManualLogSource l = Plugin.L;
					bool flag = default(bool);
					BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(62, 1, ref flag);
					if (flag)
					{
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[COMPAT] SOD Motives looked up notes ");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(calls);
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" times in the last second");
					}
					l.LogInfo(val);
				}
				window = num;
				calls = 0;
			}
			if (!on)
			{
				return true;
			}
			try
			{
				List<Interactable> val2 = ((city != null) ? city.interactableDirectory : null);
				if (val2 == null)
				{
					return true;
				}
				if (((Il2CppObjectBase)val2).Pointer != indexedList || val2.Count != indexedCount)
				{
					Index.Clear();
					for (int i = 0; i < val2.Count; i++)
					{
						Interactable val3 = val2[i];
						if (val3 != null)
						{
							Index[val3.id] = val3;
						}
					}
					indexedList = ((Il2CppObjectBase)val2).Pointer;
					indexedCount = val2.Count;
					if (!told)
					{
						told = true;
						ManualLogSource l2 = Plugin.L;
						bool flag2 = default(bool);
						BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(41, 1, ref flag2);
						if (flag2)
						{
							((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[COMPAT] indexed ");
							((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(Index.Count);
							((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" objects for SOD Motives");
						}
						l2.LogInfo(val);
					}
				}
				Index.TryGetValue(id, out var value);
				__result = value;
				return false;
			}
			catch
			{
				return true;
			}
		}
	}
	[BepInPlugin("ta.sod.vanillasplit", "SoD Vanilla Split", "0.7.6")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class Plugin : BasePlugin
	{
		public const string GUID = "ta.sod.vanillasplit";

		internal static ManualLogSource L;

		internal static ConfigEntry<bool> RemoveParents;

		internal static ConfigEntry<bool> ClearParentSpawnData;

		internal static ConfigEntry<string> SaleLocation;

		internal static ConfigEntry<bool> VanillaBalance;

		internal static ConfigEntry<bool> MotivesFix;

		internal static readonly Dictionary<string, SyncDiskPreset> Made = new Dictionary<string, SyncDiskPreset>();

		internal static readonly Dictionary<string, SplitDef> Defs = new Dictionary<string, SplitDef>();

		internal static readonly Dictionary<string, int> Prices = new Dictionary<string, int>();

		internal static readonly Dictionary<string, Rebalance.Row> RebalanceByDisk = new Dictionary<string, Rebalance.Row>();

		internal static ConfigFile Cfg;

		internal static int DdsRegistered = 0;

		internal static int DdsThrew = 0;

		private ConfigFile OpenSettings(string newFileName)
		{
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Expected O, but got Unknown
			string text = Path.Combine(Paths.ConfigPath, newFileName);
			try
			{
				string text2 = Path.Combine(Paths.ConfigPath, "ta.sod.vanillasplit.cfg");
				if (File.Exists(text2))
				{
					if (!File.Exists(text))
					{
						File.Copy(text2, text);
					}
					if (File.ReadAllText(text2) == File.ReadAllText(text))
					{
						File.Delete(text2);
						((BasePlugin)this).Log.LogInfo((object)("Moved settings from the old config file to " + newFileName));
					}
				}
			}
			catch (Exception ex)
			{
				((BasePlugin)this).Log.LogWarning((object)("Could not move the old settings file: " + ex.Message));
			}
			return new ConfigFile(text, true);
		}

		public override void Load()
		{
			Cfg = OpenSettings("sm3gm.sod.vanillasplit.cfg");
			L = ((BasePlugin)this).Log;
			L.LogInfo((object)"=== SoDVanillaSplit 0.5.0 starting ===");
			RemoveParents = Cfg.Bind<bool>("General", "RemoveVanillaParents", false, "Remove the 17 split parent presets from Toolbox.allSyncDisks so only the split disks are sold. Starch-SugarDaddy is never removed. Leave false to run the splits ALONGSIDE vanilla.");
			ClearParentSpawnData = Cfg.Bind<bool>("General", "ClearParentSpawnData", true, "Also clear occupation and trait spawn data on the removed parents. Toolbox removal alone suppresses vendor stock but NOT world loot: a combined parent can still be found in the world. Only applies when RemoveVanillaParents is true.");
			SaleLocation = Cfg.Bind<string>("General", "SaleLocation", "SyncClinic", "Menu preset the split disks are added to. Blank for none.");
			VanillaBalance = Cfg.Bind<bool>("General", "VanillaBalance", false, "Revert every rebalanced payout, tier and price to the vanilla values shipped in 1.0.3. When true, the per-disk price keys below are ignored in favour of the original prices.");
			MotivesFix = Cfg.Bind<bool>("Compatibility", "FastMotivesNoteLookup", true, "With SOD Motives installed, its note lookup after loading a save uses a fast index, so a big city does not drop to about 1 fps for minutes. Off: Motives searches as it does on its own (newer Motives versions may not need this).");
			string loc = ((SaleLocation.Value == null) ? "" : SaleLocation.Value.Trim());
			Rebalance.Row[] table = Rebalance.Table;
			for (int i = 0; i < table.Length; i++)
			{
				Rebalance.Row value = table[i];
				RebalanceByDisk[value.Disk] = value;
			}
			int num = 0;
			SplitDef[] all = Splits.All;
			foreach (SplitDef splitDef in all)
			{
				if (!Cfg.Bind<bool>(splitDef.Parent, splitDef.DiskName, true, "Register this split disk.").Value)
				{
					L.LogInfo((object)("skipped: " + splitDef.DiskName));
					continue;
				}
				Rebalance.Row value2;
				int num2 = (RebalanceByDisk.TryGetValue(splitDef.DiskName, out value2) ? value2.NewPrice : splitDef.Price);
				Prices[splitDef.DiskName] = Cfg.Bind<int>(splitDef.Parent, splitDef.DiskName + " price", num2, "Credits. Split disks are priced for what the branch does, not what the parent cost, so a set of splits costs more in total than the disk they came from. Price is not a save key and can be changed on a live save.").Value;
				try
				{
					Register(splitDef, loc);
					num++;
				}
				catch (Exception ex)
				{
					L.LogError((object)("FAILED to register " + splitDef.DiskName + ": " + ex));
				}
			}
			Harmony.CreateAndPatchAll(typeof(SplitPatch), (string)null);
			Harmony.CreateAndPatchAll(typeof(LootRemovePatch), (string)null);
			Harmony.CreateAndPatchAll(typeof(UrbexRetro), (string)null);
			L.LogInfo((object)("=== SoDVanillaSplit phase 1 done, " + num + " of " + Splits.All.Length + " registered ==="));
		}

		private static void Register(SplitDef d, string loc)
		{
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			int num = default(int);
			SyncDiskBuilder val = Lib.SyncDisks.Builder(d.DiskName, "ta.sod.vanillasplit", true).AddEffect(d.DiskName, "Pending vanilla data.", ref num, (string)null);
			if (loc.Length > 0)
			{
				val.AddSaleLocation(new string[1] { loc });
			}
			if (d.Tiers > 0)
			{
				string text = d.DiskName + " upgrade one.";
				string text2 = d.DiskName + " upgrade two.";
				string text3 = d.DiskName + " upgrade three.";
				Options val2 = default(Options);
				if (d.Tiers == 1)
				{
					((Options)(ref val2))..ctor(text);
				}
				else if (d.Tiers == 2)
				{
					((Options)(ref val2))..ctor(text, text2);
				}
				else
				{
					((Options)(ref val2))..ctor(text, text2, text3);
				}
				OptionIds val3 = default(OptionIds);
				val.AddUpgradeOption(val2, ref val3);
			}
			SyncDiskPreset preset = val.CreateAndRegister().Preset;
			preset.mainEffect2 = (Effect)0;
			preset.mainEffect2Value = 0f;
			preset.mainEffect3 = (Effect)0;
			preset.mainEffect3Value = 0f;
			Made[d.DiskName] = preset;
			Defs[d.DiskName] = d;
			L.LogInfo((object)("shell registered: " + d.DiskName + " <- " + d.Parent + " branch " + d.Branch + " tiers " + d.Tiers + " customId " + num));
		}

		internal static void RegisterUpgradeString(string s)
		{
			try
			{
				Lib.DdsStrings["evidence.syncdisks", s] = s;
				DdsRegistered++;
			}
			catch (Exception ex)
			{
				DdsThrew++;
				L.LogWarning((object)("[DDSREG] " + ex.GetType().Name + " registering \"" + s + "\""));
			}
		}
	}
	[HarmonyPatch(typeof(Toolbox), "Start")]
	public static class SplitPatch
	{
		private static bool done;

		private static void Postfix()
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Expected O, but got Unknown
			if (!done)
			{
				done = true;
				MotivesCompat.Hook(new Harmony("sm3gm.sod.vanillasplit.compat"));
				Dictionary<string, SyncDiskPreset> parents = Index();
				Copy(parents);
				Rebalance.Apply(Plugin.Made);
				Remove(parents);
			}
		}

		private static Dictionary<string, SyncDiskPreset> Index()
		{
			Dictionary<string, SyncDiskPreset> dictionary = new Dictionary<string, SyncDiskPreset>();
			List<SyncDiskPreset> allSyncDisks = Toolbox.Instance.allSyncDisks;
			if (allSyncDisks == null)
			{
				Plugin.L.LogError((object)"=== Toolbox.allSyncDisks is null, phase 2 aborted ===");
				return dictionary;
			}
			Enumerator<SyncDiskPreset> enumerator = allSyncDisks.GetEnumerator();
			while (enumerator.MoveNext())
			{
				SyncDiskPreset current = enumerator.Current;
				if ((SoCustomComparison)(object)current == (SoCustomComparison)null)
				{
					continue;
				}
				string[] parents = Splits.Parents;
				foreach (string text in parents)
				{
					if (((Object)current).name == text && !dictionary.ContainsKey(text))
					{
						dictionary[text] = current;
					}
				}
			}
			Plugin.L.LogInfo((object)("=== indexed " + dictionary.Count + " of " + Splits.Parents.Length + " parent presets ==="));
			return dictionary;
		}

		private static void Copy(Dictionary<string, SyncDiskPreset> parents)
		{
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_013f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0176: Unknown result type (might be due to invalid IL or missing references)
			//IL_037d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0280: Unknown result type (might be due to invalid IL or missing references)
			//IL_0496: Unknown result type (might be due to invalid IL or missing references)
			//IL_049b: Unknown result type (might be due to invalid IL or missing references)
			//IL_04d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_04d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0511: Unknown result type (might be due to invalid IL or missing references)
			//IL_0516: Unknown result type (might be due to invalid IL or missing references)
			//IL_0209: Unknown result type (might be due to invalid IL or missing references)
			//IL_0212: 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_0413: Unknown result type (might be due to invalid IL or missing references)
			//IL_0313: Unknown result type (might be due to invalid IL or missing references)
			//IL_031c: Unknown result type (might be due to invalid IL or missing references)
			int num = 0;
			int num2 = 0;
			foreach (KeyValuePair<string, SyncDiskPreset> item in Plugin.Made)
			{
				SyncDiskPreset value = item.Value;
				SplitDef splitDef = Plugin.Defs[item.Key];
				if (!parents.TryGetValue(splitDef.Parent, out var value2) || (SoCustomComparison)(object)value2 == (SoCustomComparison)null)
				{
					Plugin.L.LogWarning((object)("parent NOT FOUND for " + splitDef.DiskName + " (" + splitDef.Parent + ") - disk left as placeholder"));
					num2++;
					continue;
				}
				if (Plugin.VanillaBalance.Value && Plugin.RebalanceByDisk.TryGetValue(splitDef.DiskName, out var value3))
				{
					value.price = value3.VanillaPrice;
				}
				else
				{
					value.price = (Plugin.Prices.TryGetValue(splitDef.DiskName, out var value4) ? value4 : value2.price);
				}
				value.rarity = value2.rarity;
				value.manufacturer = value2.manufacturer;
				value.uninstallCost = value2.uninstallCost;
				value.minimumWealthLevel = value2.minimumWealthLevel;
				value.canBeSideJobReward = value2.canBeSideJobReward;
				value.sideEffect = value2.sideEffect;
				value.sideEffectValue = value2.sideEffectValue;
				value.sideEffectDescription = value2.sideEffectDescription;
				if (splitDef.Branch == 1)
				{
					value.mainEffect1 = value2.mainEffect1;
					value.mainEffect1Value = value2.mainEffect1Value;
					value.mainEffect1Description = value2.mainEffect1Description;
					value.mainEffect1Icon = value2.mainEffect1Icon;
					List<UpgradeEffect> option1UpgradeEffects = value2.option1UpgradeEffects;
					List<float> option1UpgradeValues = value2.option1UpgradeValues;
					List<string> option1UpgradeNameReferences = value2.option1UpgradeNameReferences;
					List<UpgradeEffect> option1UpgradeEffects2 = value.option1UpgradeEffects;
					List<float> option1UpgradeValues2 = value.option1UpgradeValues;
					List<string> option1UpgradeNameReferences2 = value.option1UpgradeNameReferences;
					for (int i = 0; i < splitDef.Tiers && i < option1UpgradeEffects.Count && i < option1UpgradeEffects2.Count; i++)
					{
						option1UpgradeEffects2[i] = Fix(option1UpgradeEffects[i], splitDef, i);
						option1UpgradeValues2[i] = option1UpgradeValues[i];
						if (i < option1UpgradeNameReferences.Count && i < option1UpgradeNameReferences2.Count)
						{
							option1UpgradeNameReferences2[i] = option1UpgradeNameReferences[i];
						}
					}
				}
				else if (splitDef.Branch == 2)
				{
					value.mainEffect1 = value2.mainEffect2;
					value.mainEffect1Value = value2.mainEffect2Value;
					value.mainEffect1Description = value2.mainEffect2Description;
					value.mainEffect1Icon = value2.mainEffect2Icon;
					List<UpgradeEffect> option2UpgradeEffects = value2.option2UpgradeEffects;
					List<float> option2UpgradeValues = value2.option2UpgradeValues;
					List<string> option2UpgradeNameReferences = value2.option2UpgradeNameReferences;
					List<UpgradeEffect> option1UpgradeEffects3 = value.option1UpgradeEffects;
					List<float> option1UpgradeValues3 = value.option1UpgradeValues;
					List<string> option1UpgradeNameReferences3 = value.option1UpgradeNameReferences;
					for (int j = 0; j < splitDef.Tiers && j < option2UpgradeEffects.Count && j < option1UpgradeEffects3.Count; j++)
					{
						option1UpgradeEffects3[j] = Fix(option2UpgradeEffects[j], splitDef, j);
						option1UpgradeValues3[j] = option2UpgradeValues[j];
						if (j < option2UpgradeNameReferences.Count && j < option1UpgradeNameReferences3.Count)
						{
							option1UpgradeNameReferences3[j] = option2UpgradeNameReferences[j];
						}
					}
				}
				else
				{
					value.mainEffect1 = value2.mainEffect3;
					value.mainEffect1Value = value2.mainEffect3Value;
					value.mainEffect1Description = value2.mainEffect3Description;
					value.mainEffect1Icon = value2.mainEffect3Icon;
					List<UpgradeEffect> option3UpgradeEffects = value2.option3UpgradeEffects;
					List<float> option3UpgradeValues = value2.option3UpgradeValues;
					List<string> option3UpgradeNameReferences = value2.option3UpgradeNameReferences;
					List<UpgradeEffect> option1UpgradeEffects4 = value.option1UpgradeEffects;
					List<float> option1UpgradeValues4 = value.option1UpgradeValues;
					List<string> option1UpgradeNameReferences4 = value.option1UpgradeNameReferences;
					for (int k = 0; k < splitDef.Tiers && k < option3UpgradeEffects.Count && k < option1UpgradeEffects4.Count; k++)
					{
						option1UpgradeEffects4[k] = Fix(option3UpgradeEffects[k], splitDef, k);
						option1UpgradeValues4[k] = option3UpgradeValues[k];
						if (k < option3UpgradeNameReferences.Count && k < option1UpgradeNameReferences4.Count)
						{
							option1UpgradeNameReferences4[k] = option3UpgradeNameReferences[k];
						}
					}
				}
				num++;
				Plugin.L.LogInfo((object)("copied " + splitDef.DiskName + " | effect=" + ((object)value.mainEffect1/*cast due to .constrained prefix*/).ToString() + " value=" + value.mainEffect1Value + " mfr=" + ((object)value.manufacturer/*cast due to .constrained prefix*/).ToString() + " price=" + value.price + " side=" + ((object)value.sideEffect/*cast due to .constrained prefix*/).ToString() + " tiers=" + splitDef.Tiers));
			}
			Plugin.L.LogInfo((object)("=== copy pass complete: " + num + " copied, " + num2 + " missing parents ==="));
		}

		private static UpgradeEffect Fix(UpgradeEffect e, SplitDef d, int tier)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Invalid comparison between Unknown and I4
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			if ((int)e == 2)
			{
				Plugin.L.LogInfo((object)("override: " + d.DiskName + " tier " + (tier + 1) + " bothConfigurations -> modifyEffect"));
				return (UpgradeEffect)1;
			}
			return e;
		}

		private static void ClearSpawnData(Dictionary<string, SyncDiskPreset> parents)
		{
			if (!Plugin.ClearParentSpawnData.Value)
			{
				Plugin.L.LogInfo((object)"=== ClearParentSpawnData false, spawn data left intact ===");
				return;
			}
			int num = 0;
			foreach (KeyValuePair<string, SyncDiskPreset> parent in parents)
			{
				SyncDiskPreset value = parent.Value;
				if ((SoCustomComparison)(object)value == (SoCustomComparison)null)
				{
					continue;
				}
				try
				{
					int num2 = 0;
					int num3 = 0;
					try
					{
						if (value.occupation != null)
						{
							num2 = value.occupation.Count;
						}
					}
					catch
					{
					}
					try
					{
						if (value.traits != null)
						{
							num3 = value.traits.Count;
						}
					}
					catch
					{
					}
					if (value.occupation != null)
					{
						value.occupation.Clear();
					}
					value.occupationWeight = 0;
					if (value.traits != null)
					{
						value.traits.Clear();
					}
					value.traitWeight = 0;
					num++;
					Plugin.L.LogInfo((object)("=== SPAWN CLEARED: " + parent.Key + " occ " + num2 + "->0 traits " + num3 + "->0 ==="));
				}
				catch (Exception ex)
				{
					Plugin.L.LogError((object)("spawn clear FAILED for " + parent.Key + ": " + ex));
				}
			}
			Plugin.L.LogInfo((object)("=== spawn clear complete: " + num + " of " + parents.Count + " parents ==="));
		}

		private static void Remove(Dictionary<string, SyncDiskPreset> parents)
		{
			if (!Plugin.RemoveParents.Value)
			{
				Plugin.L.LogInfo((object)"=== RemoveVanillaParents false, parents left in place ===");
				return;
			}
			ClearSpawnData(parents);
			int num = 0;
			List<SyncDiskPreset> allSyncDisks = Toolbox.Instance.allSyncDisks;
			if (allSyncDisks != null)
			{
				for (int num2 = allSyncDisks.Count - 1; num2 >= 0; num2--)
				{
					SyncDiskPreset val = allSyncDisks[num2];
					if (!((SoCustomComparison)(object)val == (SoCustomComparison)null))
					{
						string[] parents2 = Splits.Parents;
						foreach (string text in parents2)
						{
							if (((Object)val).name == text)
							{
								allSyncDisks.RemoveAt(num2);
								num++;
								Plugin.L.LogInfo((object)("=== REMOVED from Toolbox.allSyncDisks: " + text + " ==="));
								break;
							}
						}
					}
				}
			}
			int num3 = 0;
			int num4 = 0;
			Il2CppArrayBase<MenuPreset> val2 = Resources.FindObjectsOfTypeAll<MenuPreset>();
			if (val2 != null)
			{
				foreach (MenuPreset item in val2)
				{
					if ((SoCustomComparison)(object)item == (SoCustomComparison)null || item.syncDisks == null)
					{
						continue;
					}
					num4++;
					for (int num5 = item.syncDisks.Count - 1; num5 >= 0; num5--)
					{
						SyncDiskPreset val3 = item.syncDisks[num5];
						if (!((SoCustomComparison)(object)val3 == (SoCustomComparison)null))
						{
							string[] parents2 = Splits.Parents;
							foreach (string text2 in parents2)
							{
								if (((Object)val3).name == text2)
								{
									item.syncDisks.RemoveAt(num5);
									num3++;
									break;
								}
							}
						}
					}
				}
			}
			Plugin.L.LogInfo((object)("=== remove pass complete: " + num + " from Toolbox, " + num3 + " across " + num4 + " menu presets ==="));
		}
	}
	[HarmonyPatch(typeof(InteriorCreator), "StartLoading")]
	public static class LootRemovePatch
	{
		private static bool _loggedFirstCall;

		private static void Prefix()
		{
			if (!_loggedFirstCall)
			{
				_loggedFirstCall = true;
				Toolbox instance = Toolbox.Instance;
				int valueOrDefault = ((instance == null) ? ((int?)null) : instance.allSyncDisks?.Count).GetValueOrDefault();
				int num = 0;
				Toolbox instance2 = Toolbox.Instance;
				List<SyncDiskPreset> val = ((instance2 != null) ? instance2.allSyncDisks : null);
				if (val != null)
				{
					Enumerator<SyncDiskPreset> enumerator = val.GetEnumerator();
					while (enumerator.MoveNext())
					{
						SyncDiskPreset current = enumerator.Current;
						if ((SoCustomComparison)(object)current == (SoCustomComparison)null)
						{
							continue;
						}
						string[] parents = Splits.Parents;
						foreach (string text in parents)
						{
							if (((Object)current).name == text)
							{
								num++;
								break;
							}
						}
					}
				}
				Plugin.L.LogInfo((object)("[LOOT] first call, allSyncDisks=" + valueOrDefault + ", parents present=" + num));
			}
			if (!Plugin.RemoveParents.Value)
			{
				return;
			}
			Toolbox instance3 = Toolbox.Instance;
			List<SyncDiskPreset> val2 = ((instance3 != null) ? instance3.allSyncDisks : null);
			if (val2 == null)
			{
				return;
			}
			int num2 = 0;
			for (int num3 = val2.Count - 1; num3 >= 0; num3--)
			{
				SyncDiskPreset val3 = val2[num3];
				if (!((SoCustomComparison)(object)val3 == (SoCustomComparison)null))
				{
					string[] parents = Splits.Parents;
					foreach (string text2 in parents)
					{
						if (((Object)val3).name == text2)
						{
							val2.RemoveAt(num3);
							num2++;
							break;
						}
					}
				}
			}
			if (num2 > 0)
			{
				Plugin.L.LogInfo((object)("=== LOOT PASS: removed " + num2 + " parents at chunk generation ==="));
			}
		}
	}
	public static class Rebalance
	{
		public struct Tier
		{
			public UpgradeEffect Effect;

			public float Value;

			public string Desc;

			public Tier(UpgradeEffect effect, float value, string desc = null)
			{
				//IL_0001: Unknown result type (might be due to invalid IL or missing references)
				//IL_0002: Unknown result type (might be due to invalid IL or missing references)
				Effect = effect;
				Value = value;
				Desc = desc;
			}
		}

		public struct Row
		{
			public string Disk;

			public bool ChangeMain;

			public Effect MainEffect;

			public float MainValue;

			public string MainDesc;

			public int NewPrice;

			public int VanillaPrice;

			public Tier?[] Tiers;

			public int[] Reorder;
		}

		public static readonly Row[] Table = new Row[30]
		{
			new Row
			{
				Disk = "Safecacker",
				NewPrice = 750,
				VanillaPrice = 500,
				ChangeMain = false,
				Tiers = new Tier?[3]
				{
					new Tier((UpgradeEffect)55, 10f, "Buyers pay a better rate for cracked passcodes"),
					new Tier((UpgradeEffect)55, 10f, "Your passcode work earns a premium"),
					new Tier((UpgradeEffect)55, 10f, "Top rate on every passcode you crack")
				}
			},
			new Row
			{
				Disk = "Mailing List",
				NewPrice = 650,
				VanillaPrice = 500,
				ChangeMain = false,
				Tiers = new Tier?[3]
				{
					new Tier((UpgradeEffect)54, 10f, "Buyers pay a better rate for logged addresses"),
					new Tier((UpgradeEffect)54, 10f, "Your address listings earn a premium"),
					new Tier((UpgradeEffect)54, 10f, "Top rate on every address you log")
				}
			},
			new Row
			{
				Disk = "Crawlspace Engineer",
				NewPrice = 700,
				VanillaPrice = 500,
				ChangeMain = false,
				Tiers = new Tier?[3]
				{
					new Tier((UpgradeEffect)53, 3f, "Buyers pay a better rate for mapped ducts"),
					new Tier((UpgradeEffect)53, 3f, "Your duct surveys earn a premium"),
					new Tier((UpgradeEffect)53, 4f, "Top rate on every duct you map")
				}
			},
			new Row
			{
				Disk = "Urbex Cartographer",
				NewPrice = 550,
				VanillaPrice = 500,
				ChangeMain = false,
				Tiers = new Tier?[3]
				{
					new Tier((UpgradeEffect)52, 1f, "Buyers pay a better rate for new locations"),
					new Tier((UpgradeEffect)52, 1f, "Your location reports earn a premium"),
					new Tier((UpgradeEffect)52, 1f, "Top rate on every location you discover")
				}
			},
			new Row
			{
				Disk = "Cash Flow",
				NewPrice = 600,
				VanillaPrice = 500,
				ChangeMain = false,
				Tiers = new Tier?[3]
				{
					new Tier((UpgradeEffect)27, 10f, "Your holdings return a little more each hour"),
					new Tier((UpgradeEffect)27, 10f, "Dividends improve again"),
					new Tier((UpgradeEffect)27, 10f, "Your portfolio pays out at its best rate")
				}
			},
			new Row
			{
				Disk = "Competitor Data Mining",
				NewPrice = 550,
				VanillaPrice = 500,
				ChangeMain = false,
				Tiers = new Tier?[3]
				{
					null,
					new Tier((UpgradeEffect)28, 25f, "Each installation harvests more data"),
					new Tier((UpgradeEffect)28, 25f, "Your malware returns its maximum yield")
				}
			},
			new Row
			{
				Disk = "Street Cleaner",
				NewPrice = 450,
				VanillaPrice = 500,
				ChangeMain = false,
				Tiers = new Tier?[3]
				{
					new Tier((UpgradeEffect)1, 8f, "The city pays a better rate for litter cleared"),
					new Tier((UpgradeEffect)1, 8f, "Sanitation contracts pay a premium"),
					new Tier((UpgradeEffect)45, 1f, "You never carry the smell of the job home")
				}
			},
			new Row
			{
				Disk = "Bookworm",
				NewPrice = 600,
				VanillaPrice = 500,
				ChangeMain = false,
				Tiers = new Tier?[3]
				{
					new Tier((UpgradeEffect)1, 10f, "Every book you finish pays a better rate"),
					new Tier((UpgradeEffect)3, 400f, "Completing a series pays a substantial bonus"),
					new Tier((UpgradeEffect)1, 10f, "Top rate on every book you finish")
				}
			},
			new Row
			{
				Disk = "Food Hygeine Inspector",
				NewPrice = 500,
				VanillaPrice = 500,
				ChangeMain = false,
				Tiers = new Tier?[3]
				{
					new Tier((UpgradeEffect)48, 50f, "Kitchen reports earn a better fee"),
					new Tier((UpgradeEffect)48, 50f, "Your inspections command a premium"),
					new Tier((UpgradeEffect)48, 50f, "Top fee on every kitchen you document")
				}
			},
			new Row
			{
				Disk = "Sanitary Hygeine Inspector",
				NewPrice = 500,
				VanillaPrice = 500,
				ChangeMain = false,
				Tiers = new Tier?[3]
				{
					new Tier((UpgradeEffect)49, 50f, "Bathroom reports earn a better fee"),
					new Tier((UpgradeEffect)49, 50f, "Your inspections command a premium"),
					new Tier((UpgradeEffect)49, 50f, "Top fee on every bathroom you document")
				}
			},
			new Row
			{
				Disk = "Spread the word!",
				NewPrice = 200,
				VanillaPrice = 500,
				ChangeMain = false,
				Tiers = new Tier?[3]
				{
					new Tier((UpgradeEffect)37, 8f, "Starch rewards your advocacy more generously"),
					new Tier((UpgradeEffect)37, 8f, "Your ambassador standing improves again"),
					new Tier((UpgradeEffect)37, 10f, "Maximum reward for every word you spread")
				}
			},
			new Row
			{
				Disk = "Put some life into it!",
				NewPrice = 200,
				VanillaPrice = 500,
				ChangeMain = false,
				Tiers = new Tier?[3]
				{
					new Tier((UpgradeEffect)38, 12f, "Every can you hand out returns more"),
					new Tier((UpgradeEffect)38, 12f, "Starch increases your distribution bonus"),
					new Tier((UpgradeEffect)38, 15f, "Maximum return on every can you give away")
				}
			},
			new Row
			{
				Disk = "Care in the Community",
				NewPrice = 800,
				VanillaPrice = 750,
				ChangeMain = false,
				Tiers = new Tier?[3]
				{
					new Tier((UpgradeEffect)16, 0.08f, "Side work pays a better rate"),
					new Tier((UpgradeEffect)16, 0.08f, "Clients offer a further premium"),
					new Tier((UpgradeEffect)16, 0.08f, "Top rate on every side job you take")
				}
			},
			new Row
			{
				Disk = "Gold Accident Cover",
				NewPrice = 750,
				VanillaPrice = 750,
				ChangeMain = false,
				Tiers = new Tier?[3]
				{
					new Tier((UpgradeEffect)6, 50f, "Your accident payout increases"),
					new Tier((UpgradeEffect)6, 50f, "Kensington raises your cover again"),
					new Tier((UpgradeEffect)6, 100f, "Full gold-tier accident settlement")
				}
			},
			new Row
			{
				Disk = "Statuesque",
				NewPrice = 600,
				VanillaPrice = 750,
				ChangeMain = false,
				Tiers = new Tier?[3]
				{
					new Tier((UpgradeEffect)51, 0.05f, "You stand taller still"),
					new Tier((UpgradeEffect)51, 0.05f, "Your frame extends further"),
					new Tier((UpgradeEffect)22, 0.1f, "Long arms reach what others cannot")
				}
			},
			new Row
			{
				Disk = "Compact",
				NewPrice = 900,
				VanillaPrice = 750,
				ChangeMain = false,
				Tiers = new Tier?[3]
				{
					new Tier((UpgradeEffect)51, -0.05f, "You fold down further"),
					new Tier((UpgradeEffect)51, -0.05f, "Your frame compacts again"),
					new Tier((UpgradeEffect)22, 0.1f, "You learn to stretch for what you cannot reach")
				}
			},
			new Row
			{
				Disk = "Lockpicker",
				NewPrice = 1750,
				VanillaPrice = 1750,
				ChangeMain = true,
				MainEffect = (Effect)39,
				MainValue = 0.15f,
				MainDesc = null,
				Tiers = new Tier?[3]
				{
					new Tier((UpgradeEffect)39, 0.1f, "Your picks find the pins faster"),
					new Tier((UpgradeEffect)39, 0.1f, "Practice shaves more time off every lock"),
					new Tier((UpgradeEffect)39, 0.15f, "Locks open almost as fast as you reach them")
				}
			},
			new Row
			{
				Disk = "Resourceful",
				NewPrice = 1750,
				VanillaPrice = 1750,
				ChangeMain = true,
				MainEffect = (Effect)40,
				MainValue = 0.15f,
				MainDesc = null,
				Tiers = new Tier?[3]
				{
					new Tier((UpgradeEffect)40, 0.1f, "Your picks survive more punishment"),
					new Tier((UpgradeEffect)40, 0.1f, "You waste fewer picks on every lock"),
					new Tier((UpgradeEffect)40, 0.15f, "A single pick lasts far longer than it should")
				}
			},
			new Row
			{
				Disk = "Invisible",
				NewPrice = 1750,
				VanillaPrice = 1250,
				ChangeMain = false,
				Tiers = new Tier?[3]
				{
					new Tier((UpgradeEffect)44, 0.25f, "Security takes longer to notice you"),
					new Tier((UpgradeEffect)43, 0.5f, "Breaker boxes yield to you more readily"),
					new Tier((UpgradeEffect)39, 0.2f, "Your picks find the pins noticeably faster")
				}
			},
			new Row
			{
				Disk = "Power",
				NewPrice = 2500,
				VanillaPrice = 2500,
				ChangeMain = true,
				MainEffect = (Effect)15,
				MainValue = 0.5f,
				MainDesc = "Doors give under your shoulder more often than they should",
				Tiers = new Tier?[3]
				{
					new Tier((UpgradeEffect)14, 0.5f, "Frames splinter more readily"),
					new Tier((UpgradeEffect)14, 0.5f, "Few doors hold against you"),
					new Tier((UpgradeEffect)14, 99999f, "No door in the city holds against you")
				}
			},
			new Row
			{
				Disk = "Stability",
				NewPrice = 3000,
				VanillaPrice = 3000,
				ChangeMain = true,
				MainEffect = (Effect)16,
				MainValue = -0.3f,
				MainDesc = "You land harder than most people and feel less of it",
				Tiers = new Tier?[3]
				{
					new Tier((UpgradeEffect)15, -0.2f, "Longer drops stop hurting"),
					new Tier((UpgradeEffect)15, -0.2f, "You shrug off falls that would break others"),
					new Tier((UpgradeEffect)15, -0.3f, "You land from any height without injury")
				}
			},
			new Row
			{
				Disk = "Rogue",
				NewPrice = 1750,
				VanillaPrice = 1750,
				ChangeMain = true,
				MainEffect = (Effect)42,
				MainValue = 0.5f,
				MainDesc = null,
				Tiers = new Tier?[3]
				{
					new Tier((UpgradeEffect)42, 0.25f, "Your targets stay down longer"),
					new Tier((UpgradeEffect)42, 0.25f, "They stay down longer still"),
					new Tier((UpgradeEffect)42, 0.5f, "Anyone you drop stays down for a long while")
				}
			},
			new Row
			{
				Disk = "Hacker",
				NewPrice = 1750,
				VanillaPrice = 1750,
				ChangeMain = true,
				MainEffect = (Effect)43,
				MainValue = 0.5f,
				MainDesc = null,
				Tiers = new Tier?[3]
				{
					new Tier((UpgradeEffect)43, 0.25f, "Breakers take less work to trip"),
					new Tier((UpgradeEffect)43, 0.25f, "Security systems fold faster"),
					new Tier((UpgradeEffect)43, 0.5f, "Breaker boxes barely slow you down")
				}
			},
			new Row
			{
				Disk = "Fortitude",
				NewPrice = 1750,
				VanillaPrice = 1750,
				ChangeMain = true,
				MainEffect = (Effect)10,
				MainValue = 0.1f,
				MainDesc = null,
				Tiers = new Tier?[3]
				{
					new Tier((UpgradeEffect)9, 0.05f, "You can take a little more punishment"),
					new Tier((UpgradeEffect)9, 0.05f, "Your constitution improves again"),
					new Tier((UpgradeEffect)11, 0.1f, "Your body mends itself faster")
				}
			},
			new Row
			{
				Disk = "Clout",
				NewPrice = 1500,
				VanillaPrice = 1500,
				ChangeMain = false,
				Tiers = null,
				Reorder = new int[3] { 2, 1, 0 }
			},
			new Row
			{
				Disk = "Brawn",
				NewPrice = 1250,
				VanillaPrice = 1250,
				ChangeMain = false,
				Tiers = new Tier?[3]
				{
					new Tier((UpgradeEffect)18, 0.2f, "Your throws land with real weight"),
					null,
					null
				}
			},
			new Row
			{
				Disk = "Physiological Perception",
				NewPrice = 1750,
				VanillaPrice = 1750,
				ChangeMain = false,
				Tiers = new Tier?[3]
				{
					null,
					null,
					new Tier((UpgradeEffect)13, 0.25f, "People open up to you again, whatever you have installed")
				}
			},
			new Row
			{
				Disk = "Socioeconomic Perception",
				NewPrice = 1000,
				VanillaPrice = 1000,
				ChangeMain = false,
				Tiers = new Tier?[3]
				{
					null,
					null,
					new Tier((UpgradeEffect)13, 0.25f, "People open up to you again, whatever you have installed")
				}
			},
			new Row
			{
				Disk = "Chemistry",
				NewPrice = 2000,
				VanillaPrice = 2000,
				ChangeMain = true,
				MainEffect = (Effect)27,
				MainValue = -0.1f,
				MainDesc = "A tailored blood chemistry that blunts what the city does to you",
				Tiers = new Tier?[3]
				{
					new Tier((UpgradeEffect)9, 0.2f, "Your body carries more damage before it fails"),
					new Tier((UpgradeEffect)26, -0.1f, "Blows land softer still"),
					new Tier((UpgradeEffect)46, 1f, "The cold no longer touches you")
				}
			},
			new Row
			{
				Disk = "Cardiovascular",
				NewPrice = 2000,
				VanillaPrice = 2000,
				ChangeMain = false,
				Tiers = new Tier?[3]
				{
					null,
					null,
					new Tier((UpgradeEffect)47, 1f, "Your stamina no longer runs out")
				}
			}
		};

		internal static void Apply(Dictionary<string, SyncDiskPreset> made)
		{
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			if (Plugin.VanillaBalance.Value)
			{
				return;
			}
			int num = 0;
			int num2 = 0;
			int num3 = 0;
			int num4 = 0;
			int num5 = 0;
			Row[] table = Table;
			for (int i = 0; i < table.Length; i++)
			{
				Row row = table[i];
				num++;
				if (!made.TryGetValue(row.Disk, out var value) || (SoCustomComparison)(object)value == (SoCustomComparison)null)
				{
					Plugin.L.LogWarning((object)("[REBAL] preset not found for " + row.Disk));
					num5++;
					continue;
				}
				if (row.Reorder != null)
				{
					ApplyReorder(value, row.Reorder);
					num4++;
				}
				else if (row.Tiers != null)
				{
					num3 += ApplyTiers(value, row.Tiers);
				}
				if (row.ChangeMain)
				{
					value.mainEffect1 = row.MainEffect;
					value.mainEffect1Value = row.MainValue;
					if (row.MainDesc != null)
					{
						value.mainEffect1Description = row.MainDesc;
						Plugin.RegisterUpgradeString(row.MainDesc);
					}
					num2++;
				}
			}
			Plugin.L.LogInfo((object)("[REBAL] rows=" + num + " mains=" + num2 + " tiers=" + num3 + " reorders=" + num4 + " skipped=" + num5));
			Plugin.L.LogInfo((object)("[DDSREG] registered=" + Plugin.DdsRegistered + " threw=" + Plugin.DdsThrew));
		}

		private static int ApplyTiers(SyncDiskPreset p, Tier?[] slots)
		{
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			List<UpgradeEffect> val = p.option1UpgradeEffects;
			List<float> val2 = p.option1UpgradeValues;
			List<string> val3 = p.option1UpgradeNameReferences;
			if (val == null)
			{
				val = (p.option1UpgradeEffects = new List<UpgradeEffect>());
			}
			if (val2 == null)
			{
				val2 = (p.option1UpgradeValues = new List<float>());
			}
			if (val3 == null)
			{
				val3 = (p.option1UpgradeNameReferences = new List<string>());
			}
			int num = 0;
			for (int i = 0; i < slots.Length; i++)
			{
				if (slots[i].HasValue)
				{
					Tier value = slots[i].Value;
					while (val.Count <= i)
					{
						val.Add((UpgradeEffect)0);
					}
					while (val2.Count <= i)
					{
						val2.Add(0f);
					}
					while (val3.Count <= i)
					{
						val3.Add("");
					}
					val[i] = value.Effect;
					val2[i] = value.Value;
					if (value.Desc != null)
					{
						val3[i] = value.Desc;
						Plugin.RegisterUpgradeString(value.Desc);
					}
					num++;
				}
			}
			return num;
		}

		private static void ApplyReorder(SyncDiskPreset p, int[] order)
		{
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Expected I4, but got Unknown
			List<UpgradeEffect> option1UpgradeEffects = p.option1UpgradeEffects;
			List<float> option1UpgradeValues = p.option1UpgradeValues;
			List<string> option1UpgradeNameReferences = p.option1UpgradeNameReferences;
			if (option1UpgradeEffects != null && option1UpgradeValues != null && option1UpgradeNameReferences != null)
			{
				UpgradeEffect[] array = (UpgradeEffect[])(object)new UpgradeEffect[order.Length];
				float[] array2 = new float[order.Length];
				string[] array3 = new string[order.Length];
				for (int i = 0; i < order.Length; i++)
				{
					int num = order[i];
					array[i] = (UpgradeEffect)(int)option1UpgradeEffects[num];
					array2[i] = option1UpgradeValues[num];
					array3[i] = option1UpgradeNameReferences[num];
				}
				for (int j = 0; j < order.Length; j++)
				{
					option1UpgradeEffects[j] = array[j];
					option1UpgradeValues[j] = array2[j];
					option1UpgradeNameReferences[j] = array3[j];
				}
			}
		}
	}
	[HarmonyPatch(typeof(UpgradesController), "InstallSyncDisk")]
	internal static class UrbexRetro
	{
		private static readonly Effect[] Kinds;

		private static Dictionary<string, int>? paid;

		private static string PaidFile => Path.Combine(Paths.ConfigPath, "sm3gm.sod.vanillasplit.retro.txt");

		private static Dictionary<string, int> Paid
		{
			get
			{
				if (paid != null)
				{
					return paid;
				}
				paid = new Dictionary<string, int>();
				try
				{
					if (File.Exists(PaidFile))
					{
						string[] array = File.ReadAllLines(PaidFile);
						foreach (string text in array)
						{
							int num = text.LastIndexOf('=');
							if (num > 0 && int.TryParse(text.Substring(num + 1), out var result))
							{
								paid[text.Substring(0, num)] = result;
							}
						}
					}
				}
				catch
				{
				}
				return paid;
			}
		}

		private static void Prefix(out float[] __state)
		{
			__state = new float[Kinds.Length];
			try
			{
				for (int i = 0; i < Kinds.Length; i++)
				{
					__state[i] = UpgradeEffectController.Instance.GetUpgradeEffect(Kinds[i]);
				}
			}
			catch
			{
			}
		}

		private static void Postfix(float[] __state)
		{
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0122: Expected O, but got Unknown
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Expected O, but got Unknown
			bool flag = default(bool);
			for (int i = 0; i < Kinds.Length; i++)
			{
				try
				{
					if (__state[i] > 0f)
					{
						continue;
					}
					int num = Mathf.RoundToInt(UpgradeEffectController.Instance.GetUpgradeEffect(Kinds[i]));
					if (num <= 0)
					{
						continue;
					}
					int num2 = Count(Kinds[i]);
					string key = Game() + "|" + ((object)Unsafe.As<Effect, Effect>(ref Kinds[i])/*cast due to .constrained prefix*/).ToString();
					Paid.TryGetValue(key, out var value);
					int num3 = num2 - value;
					Paid[key] = Mathf.Max(num2, value);
					Save();
					BepInExInfoLogInterpolatedStringHandler val;
					if (num3 <= 0)
					{
						ManualLogSource l = Plugin.L;
						val = new BepInExInfoLogInterpolatedStringHandler(52, 2, ref flag);
						if (flag)
						{
							((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[RETRO] ");
							((BepInExLogInterpolatedStringHandler)val).AppendFormatted<Effect>(Kinds[i]);
							((BepInExLogInterpolatedStringHandler)val).AppendLiteral(": nothing new since the last payout (");
							((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(num2);
							((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" known)");
						}
						l.LogInfo(val);
						continue;
					}
					GameplayController.Instance.AddMoney(num * num3, true, Reason(Kinds[i]));
					ManualLogSource l2 = Plugin.L;
					val = new BepInExInfoLogInterpolatedStringHandler(58, 5, ref flag);
					if (flag)
					{
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[RETRO] ");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<Effect>(Kinds[i]);
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral(": paid ");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(num * num3);
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" for ");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(num3);
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" found before the disk (");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(num);
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" each, ");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(num2);
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" known)");
					}
					l2.LogInfo(val);
				}
				catch (Exception ex)
				{
					Plugin.L.LogWarning((object)($"[RETRO] {Kinds[i]} payout failed: " + ex.Message));
				}
			}
		}

		private static int Count(Effect kind)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Invalid comparison between Unknown and I4
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Invalid comparison between Unknown and I4
			int num = 0;
			if ((int)kind == 53)
			{
				Enumerator<NewRoom> enumerator = CityData.Instance.roomDirectory.GetEnumerator();
				while (enumerator.MoveNext())
				{
					NewRoom current = enumerator.Current;
					if ((Object)(object)current != (Object)null && current.explorationLevel > 0)
					{
						num++;
					}
				}
			}
			else if ((int)kind == 55)
			{
				Enumerator<Fact> enumerator2 = GameplayController.Instance.factList.GetEnumerator();
				while (enumerator2.MoveNext())
				{
					Fact current2 = enumerator2.Current;
					if (current2 != null && ((CaseComponent)current2).isFound && (SoCustomComparison)(object)current2.preset != (SoCustomComparison)null && ((Object)current2.preset).name == "LivesAt")
					{
						num++;
					}
				}
			}
			else
			{
				num = GameplayController.Instance.acquiredPasscodes?.Count ?? 0;
			}
			return num;
		}

		private static string Reason(Effect kind)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Invalid comparison between Unknown and I4
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Invalid comparison between Unknown and I4
			if ((int)kind != 53)
			{
				if ((int)kind == 55)
				{
					return "moneyforaddresses";
				}
				return "moneyforpasscodes";
			}
			return "moneyforblueprints";
		}

		private static string Game()
		{
			string text = "";
			try
			{
				text = CityData.Instance.GetCurrentGameInstanceID();
			}
			catch
			{
			}
			if (string.IsNullOrEmpty(text))
			{
				text = CityData.Instance.cityName + "-" + CityData.Instance.seed;
			}
			return text.Replace("|", "/");
		}

		private static void Save()
		{
			try
			{
				List<string> list = new List<string>();
				foreach (KeyValuePair<string, int> item in Paid)
				{
					list.Add(item.Key + "=" + item.Value);
				}
				File.WriteAllLines(PaidFile, list);
			}
			catch (Exception ex)
			{
				Plugin.L.LogWarning((object)("[RETRO] could not save: " + ex.Message));
			}
		}

		static UrbexRetro()
		{
			Effect[] array = new Effect[3];
			RuntimeHelpers.InitializeArray(array, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/);
			Kinds = (Effect[])(object)array;
		}
	}
	public class SplitDef
	{
		public string DiskName;

		public string Parent;

		public int Branch;

		public int Tiers;

		public int Price;
	}
	public static class Splits
	{
		public static readonly SplitDef[] All = new SplitDef[37]
		{
			new SplitDef
			{
				DiskName = "Clout",
				Parent = "ElGen-Tenacity",
				Branch = 1,
				Tiers = 3,
				Price = 1500
			},
			new SplitDef
			{
				DiskName = "Brawn",
				Parent = "ElGen-Tenacity",
				Branch = 2,
				Tiers = 3,
				Price = 1250
			},
			new SplitDef
			{
				DiskName = "Reflexes",
				Parent = "ElGen-Tenacity",
				Branch = 3,
				Tiers = 3,
				Price = 2500
			},
			new SplitDef
			{
				DiskName = "Lockpicker",
				Parent = "BlackMarket-Trespasser",
				Branch = 1,
				Tiers = 0,
				Price = 1750
			},
			new SplitDef
			{
				DiskName = "Resourceful",
				Parent = "BlackMarket-Trespasser",
				Branch = 2,
				Tiers = 0,
				Price = 1750
			},
			new SplitDef
			{
				DiskName = "Invisible",
				Parent = "BlackMarket-Trespasser",
				Branch = 3,
				Tiers = 0,
				Price = 1250
			},
			new SplitDef
			{
				DiskName = "Gold Medical Cover",
				Parent = "Kensington-SpartanInsuranceSchemes",
				Branch = 1,
				Tiers = 2,
				Price = 1000
			},
			new SplitDef
			{
				DiskName = "Gold Legal Cover",
				Parent = "Kensington-SpartanInsuranceSchemes",
				Branch = 2,
				Tiers = 2,
				Price = 1250
			},
			new SplitDef
			{
				DiskName = "Gold Accident Cover",
				Parent = "Kensington-SpartanInsuranceSchemes",
				Branch = 3,
				Tiers = 2,
				Price = 750
			},
			new SplitDef
			{
				DiskName = "Street Cleaner",
				Parent = "Candor-ModelCitizen",
				Branch = 1,
				Tiers = 3,
				Price = 500
			},
			new SplitDef
			{
				DiskName = "Bookworm",
				Parent = "Candor-ModelCitizen",
				Branch = 2,
				Tiers = 3,
				Price = 500
			},
			new SplitDef
			{
				DiskName = "Power",
				Parent = "ElGen-Vigor",
				Branch = 1,
				Tiers = 0,
				Price = 2500
			},
			new SplitDef
			{
				DiskName = "Stability",
				Parent = "ElGen-Vigor",
				Branch = 2,
				Tiers = 0,
				Price = 3000
			},
			new SplitDef
			{
				DiskName = "Physiological Perception",
				Parent = "Kaizen-DovePlus",
				Branch = 1,
				Tiers = 3,
				Price = 1750
			},
			new SplitDef
			{
				DiskName = "Socioeconomic Perception",
				Parent = "Kaizen-DovePlus",
				Branch = 2,
				Tiers = 3,
				Price = 1000
			},
			new SplitDef
			{
				DiskName = "Food Hygeine Inspector",
				Parent = "Candor-PublicService",
				Branch = 1,
				Tiers = 3,
				Price = 500
			},
			new SplitDef
			{
				DiskName = "Sanitary Hygeine Inspector",
				Parent = "Candor-PublicService",
				Branch = 2,
				Tiers = 3,
				Price = 500
			},
			new SplitDef
			{
				DiskName = "Rogue",
				Parent = "BlackMarket-Infiltrator",
				Branch = 1,
				Tiers = 1,
				Price = 1750
			},
			new SplitDef
			{
				DiskName = "Hacker",
				Parent = "BlackMarket-Infiltrator",
				Branch = 2,
				Tiers = 1,
				Price = 1750
			},
			new SplitDef
			{
				DiskName = "Cash Flow",
				Parent = "Kensington-AmbassadorScheme",
				Branch = 1,
				Tiers = 3,
				Price = 500
			},
			new SplitDef
			{
				DiskName = "Competitor Data Mining",
				Parent = "Kensington-AmbassadorScheme",
				Branch = 2,
				Tiers = 3,
				Price = 500
			},
			new SplitDef
			{
				DiskName = "Fortitude",
				Parent = "ElGen-Physique",
				Branch = 1,
				Tiers = 1,
				Price = 1750
			},
			new SplitDef
			{
				DiskName = "Vitality",
				Parent = "ElGen-Physique",
				Branch = 2,
				Tiers = 1,
				Price = 1500
			},
			new SplitDef
			{
				DiskName = "Allure",
				Parent = "ElGen-Beauty",
				Branch = 1,
				Tiers = 3,
				Price = 1500
			},
			new SplitDef
			{
				DiskName = "Charm",
				Parent = "ElGen-Beauty",
				Branch = 2,
				Tiers = 3,
				Price = 1500
			},
			new SplitDef
			{
				DiskName = "Chemistry",
				Parent = "ElGen-Constitution",
				Branch = 1,
				Tiers = 2,
				Price = 2000
			},
			new SplitDef
			{
				DiskName = "Cardiovascular",
				Parent = "ElGen-Constitution",
				Branch = 2,
				Tiers = 2,
				Price = 2000
			},
			new SplitDef
			{
				DiskName = "Spread the word!",
				Parent = "Starch-BrandAmbassador",
				Branch = 1,
				Tiers = 2,
				Price = 500
			},
			new SplitDef
			{
				DiskName = "Put some life into it!",
				Parent = "Starch-BrandAmbassador",
				Branch = 2,
				Tiers = 2,
				Price = 500
			},
			new SplitDef
			{
				DiskName = "Urbex Cartographer",
				Parent = "Candor-Cartographer",
				Branch = 1,
				Tiers = 0,
				Price = 500
			},
			new SplitDef
			{
				DiskName = "Crawlspace Engineer",
				Parent = "Candor-Cartographer",
				Branch = 2,
				Tiers = 0,
				Price = 500
			},
			new SplitDef
			{
				DiskName = "Care in the Community",
				Parent = "Candor-Community",
				Branch = 1,
				Tiers = 1,
				Price = 750
			},
			new SplitDef
			{
				DiskName = "Heavy Lifter",
				Parent = "Candor-Community",
				Branch = 2,
				Tiers = 1,
				Price = 1500
			},
			new SplitDef
			{
				DiskName = "Statuesque",
				Parent = "ElGen-Frame",
				Branch = 1,
				Tiers = 2,
				Price = 750
			},
			new SplitDef
			{
				DiskName = "Compact",
				Parent = "ElGen-Frame",
				Branch = 2,
				Tiers = 2,
				Price = 750
			},
			new SplitDef
			{
				DiskName = "Mailing List",
				Parent = "BlackMarket-Interceptor",
				Branch = 1,
				Tiers = 0,
				Price = 500
			},
			new SplitDef
			{
				DiskName = "Safecacker",
				Parent = "BlackMarket-Interceptor",
				Branch = 2,
				Tiers = 0,
				Price = 500
			}
		};

		public static readonly string[] Parents = new string[17]
		{
			"ElGen-Tenacity", "BlackMarket-Trespasser", "Kensington-SpartanInsuranceSchemes", "Candor-ModelCitizen", "ElGen-Vigor", "Kaizen-DovePlus", "Candor-PublicService", "BlackMarket-Infiltrator", "Kensington-AmbassadorScheme", "ElGen-Physique",
			"ElGen-Beauty", "ElGen-Constitution", "Starch-BrandAmbassador", "Candor-Cartographer", "Candor-Community", "ElGen-Frame", "BlackMarket-Interceptor"
		};
	}
}

BepInEx/plugins/SoDDiskPack/SoDDiskPack.dll

Decompiled 5 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Core.Logging.Interpolation;
using BepInEx.Logging;
using BepInEx.Unity.IL2CPP;
using HarmonyLib;
using Il2CppInterop.Runtime;
using Il2CppInterop.Runtime.InteropTypes;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppSystem.Collections.Generic;
using Microsoft.CodeAnalysis;
using SOD.Common;
using SOD.Common.Helpers.DialogObjects;
using SOD.Common.Helpers.SyncDiskObjects;
using UnityEngine;
using UnityStandardAssets.Characters.FirstPerson;

[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("SoDDiskPack")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.12.1.0")]
[assembly: AssemblyInformationalVersion("0.12.1")]
[assembly: AssemblyProduct("SoDDiskPack")]
[assembly: AssemblyTitle("SoDDiskPack")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.12.1.0")]
[module: UnverifiableCode]
[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.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace SoDDiskPack
{
	internal static class BigInventory
	{
		internal const int GameMax = 12;

		internal const int Max = 18;

		private static int target;

		private static Vector3? baseScale;

		private static Vector2? baseSize;

		private static bool IsNormal(InventorySlot s)
		{
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Invalid comparison between Unknown and I4
			if (s != null)
			{
				return (int)s.isStatic == 0;
			}
			return false;
		}

		internal static List<InventorySlot> HoldAside(FirstPersonItemController fpc, int size)
		{
			target = size;
			List<InventorySlot> list = new List<InventorySlot>();
			List<InventorySlot> slots = fpc.slots;
			int num = 0;
			for (int i = 0; i < slots.Count; i++)
			{
				InventorySlot val = slots[i];
				if (IsNormal(val))
				{
					num++;
					if (num > 12)
					{
						list.Add(val);
						slots.RemoveAt(i);
						i--;
					}
				}
			}
			return list;
		}

		internal static void Restore(FirstPersonItemController fpc, List<InventorySlot>? held)
		{
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Expected O, but got Unknown
			try
			{
				List<InventorySlot> slots = fpc.slots;
				if (held != null)
				{
					foreach (InventorySlot item in held)
					{
						slots.Add(item);
					}
					int i = 0;
					for (int j = 0; j < slots.Count; j++)
					{
						if (IsNormal(slots[j]))
						{
							i++;
						}
					}
					for (; i < target; i++)
					{
						InventorySlot val = new InventorySlot();
						val.interactableID = -1;
						slots.Add(val);
					}
					for (int k = 0; k < slots.Count; k++)
					{
						if (i <= target)
						{
							break;
						}
						InventorySlot val2 = slots[k];
						if (IsNormal(val2) && val2.interactableID < 0)
						{
							if ((Object)(object)val2.spawnedSegment != (Object)null)
							{
								Object.Destroy((Object)(object)((Component)val2.spawnedSegment).gameObject);
							}
							slots.RemoveAt(k);
							k--;
							i--;
						}
					}
					for (int l = 1; l < 2; l++)
					{
						if (i <= target)
						{
							break;
						}
						int num = slots.Count - 1;
						while (num >= 0 && i > target)
						{
							InventorySlot val3 = slots[num];
							if (IsNormal(val3))
							{
								if (val3.interactableID >= 0)
								{
									fpc.EmptySlot(val3, false, false, true, true);
								}
								if ((Object)(object)val3.spawnedSegment != (Object)null)
								{
									Object.Destroy((Object)(object)((Component)val3.spawnedSegment).gameObject);
								}
								slots.RemoveAt(num);
								i--;
							}
							num--;
						}
					}
					BioScreenController instance = BioScreenController.Instance;
					for (int m = 0; m < slots.Count; m++)
					{
						InventorySlot val4 = slots[m];
						val4.index = m;
						if ((Object)(object)val4.spawnedSegment == (Object)null && (Object)(object)instance != (Object)null)
						{
							InventorySquareController spawnedSegment = instance.SpawnSlotObject(val4);
							if ((Object)(object)val4.spawnedSegment == (Object)null)
							{
								val4.spawnedSegment = spawnedSegment;
							}
						}
						if ((Object)(object)val4.spawnedSegment != (Object)null)
						{
							val4.spawnedSegment.OnUpdateContent();
						}
					}
				}
				Layout(fpc);
			}
			catch (Exception ex)
			{
				Plugin.L.LogError((object)("[SLOTS] could not restore the extra slots: " + ex));
			}
		}

		private static void Layout(FirstPersonItemController fpc)
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: 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_004f: 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)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: 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_00dd: 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)
			BioScreenController instance = BioScreenController.Instance;
			RectTransform val = ((instance != null) ? instance.itemsParentRect : null);
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			Vector3 valueOrDefault = baseScale.GetValueOrDefault();
			if (!baseScale.HasValue)
			{
				valueOrDefault = ((Transform)val).localScale;
				baseScale = valueOrDefault;
			}
			Vector2 valueOrDefault2 = baseSize.GetValueOrDefault();
			if (!baseSize.HasValue)
			{
				Rect rect = val.rect;
				valueOrDefault2 = ((Rect)(ref rect)).size;
				baseSize = valueOrDefault2;
			}
			int num = 0;
			for (int i = 0; i < fpc.slots.Count; i++)
			{
				if (IsNormal(fpc.slots[i]))
				{
					num++;
				}
			}
			float num2 = ((num > 12) ? 1.5f : 1f);
			((Transform)val).localScale = baseScale.Value / num2;
			val.SetSizeWithCurrentAnchors((Axis)0, baseSize.Value.x * num2);
			val.SetSizeWithCurrentAnchors((Axis)1, baseSize.Value.y * num2);
		}
	}
	public class DiskDef
	{
		public string Key;

		public string DiskName;

		public string EffectName;

		public string EffectDesc;

		public Effect Effect;

		public float Value;

		public int Price;

		public Rarity Rarity;

		public Manufacturer Manufacturer;

		public string[] SaleLocations;

		public string[] TierDescs;

		public UpgradeEffect[] TierEffects;

		public float[] TierValues;

		public Effect SideEffect;

		public float SideValue;

		public string SideName;

		public string SideDesc;

		public bool Enabled = true;

		public bool TiersInCode;

		public bool Retired;
	}
	public static class Disks
	{
		public static readonly DiskDef[] All = new DiskDef[27]
		{
			new DiskDef
			{
				Key = "Quickstep",
				DiskName = "Quickstep",
				EffectName = "Fleet Foot",
				EffectDesc = "You move faster on foot.",
				Effect = (Effect)57,
				Value = 0.15f,
				Price = 2500,
				Rarity = (Rarity)2,
				Manufacturer = (Manufacturer)0,
				SaleLocations = new string[1] { "SyncClinic" },
				TierDescs = new string[3] { "You move faster still.", "Your pace improves further.", "You move considerably faster." },
				TiersInCode = true
			},
			new DiskDef
			{
				Key = "SquattersRights",
				DiskName = "Squatter's Rights",
				Enabled = false,
				EffectName = "Belong Here",
				EffectDesc = "You are given longer before trespassing is reported.",
				Effect = (Effect)63,
				Value = 0.5f,
				Price = 1000,
				Rarity = (Rarity)2,
				Manufacturer = (Manufacturer)5,
				SaleLocations = new string[1] { "BlackmarketSyncClinic" },
				TierDescs = new string[3] { "You are given longer still.", "Further grace before you are reported.", "A considerable delay before you are reported." }
			},
			new DiskDef
			{
				Key = "Homebound",
				DiskName = "Homebound",
				Enabled = false,
				EffectName = "Homebound",
				EffectDesc = "You may travel directly to your apartment.",
				Effect = (Effect)65,
				Value = 1f,
				Price = 1500,
				Rarity = (Rarity)3,
				Manufacturer = (Manufacturer)2,
				SaleLocations = new string[1] { "SyncClinic" }
			},
			new DiskDef
			{
				Key = "Signpost",
				DiskName = "Signpost",
				Enabled = false,
				EffectName = "Signpost",
				EffectDesc = "You may travel using street signage.",
				Effect = (Effect)67,
				Value = 1f,
				Price = 1500,
				Rarity = (Rarity)3,
				Manufacturer = (Manufacturer)2,
				SaleLocations = new string[1] { "SyncClinic" }
			},
			new DiskDef
			{
				Key = "PersonaNonGrata",
				DiskName = "Persona Non Grata",
				Enabled = false,
				EffectName = "Persona Non Grata",
				EffectDesc = "Loitering is no longer held against you.",
				Effect = (Effect)69,
				Value = 1f,
				Price = 1000,
				Rarity = (Rarity)2,
				Manufacturer = (Manufacturer)5,
				SaleLocations = new string[1] { "BlackmarketSyncClinic" }
			},
			new DiskDef
			{
				Key = "FreeRein",
				DiskName = "Free Rein",
				Enabled = false,
				EffectName = "Free Rein",
				EffectDesc = "Your presence at crime scenes is permitted.",
				Effect = (Effect)61,
				Value = 1f,
				Price = 1500,
				Rarity = (Rarity)3,
				Manufacturer = (Manufacturer)2,
				SaleLocations = new string[1] { "SyncClinic" }
			},
			new DiskDef
			{
				Key = "DeepPockets",
				DiskName = "Deep Pockets",
				EffectName = "Deep Pockets",
				EffectDesc = "Increase your inventory capacity.",
				Effect = (Effect)11,
				Value = 1f,
				Price = 4000,
				Rarity = (Rarity)3,
				Manufacturer = (Manufacturer)5,
				SaleLocations = new string[1] { "BlackmarketSyncClinic" },
				TierDescs = new string[2] { "Your pockets hold more.", "You find room for more again." },
				TiersInCode = true
			},
			new DiskDef
			{
				Key = "TailoredCoat",
				DiskName = "Tailored Coat",
				EffectName = "Tailored Coat",
				EffectDesc = "A coat cut with extra pockets.",
				Effect = (Effect)11,
				Value = 1f,
				Price = 3000,
				Rarity = (Rarity)2,
				Manufacturer = (Manufacturer)2,
				SaleLocations = new string[1] { "SyncClinic" },
				TierDescs = new string[1] { "A second inside pocket is sewn in." },
				TiersInCode = true
			},
			new DiskDef
			{
				Key = "SmugglersLining",
				DiskName = "Smuggler's Lining",
				EffectName = "Hidden Lining",
				EffectDesc = "Your clothes hide pockets nobody would think to check.",
				Effect = (Effect)11,
				Value = 1f,
				Price = 3500,
				Rarity = (Rarity)2,
				Manufacturer = (Manufacturer)5,
				SaleLocations = new string[1] { "BlackmarketSyncClinic" },
				TierDescs = new string[1] { "The lining hides a second compartment." },
				TiersInCode = true
			},
			new DiskDef
			{
				Key = "SprintersCurse",
				DiskName = "Sprinter's Curse",
				EffectName = "Fast Twitch",
				EffectDesc = "You move considerably faster on foot.",
				Effect = (Effect)57,
				Value = 0.2f,
				SideEffect = (Effect)27,
				SideValue = 0.25f,
				SideName = "Brittle",
				SideDesc = "Your reworked frame takes damage badly. This cannot be removed.",
				Price = 2500,
				Rarity = (Rarity)2,
				Manufacturer = (Manufacturer)0,
				SaleLocations = new string[1] { "SyncClinic" },
				TierDescs = new string[3] { "You run faster, and tire sooner for it.", "You run faster again. Your skin splits more easily.", "You run faster still, and the ground meets you harder." },
				TiersInCode = true
			},
			new DiskDef
			{
				Key = "IronLung",
				DiskName = "Iron Lung",
				EffectName = "Dense Tissue",
				EffectDesc = "You take considerably less damage.",
				Effect = (Effect)27,
				Value = -0.25f,
				SideEffect = (Effect)57,
				SideValue = -0.2f,
				SideName = "Heavy Gait",
				SideDesc = "Your added mass slows you down.",
				Price = 2500,
				Rarity = (Rarity)2,
				Manufacturer = (Manufacturer)0,
				SaleLocations = new string[1] { "SyncClinic" },
				TierDescs = new string[3] { "You tire far more slowly.", "Your body knits itself back together faster.", "Your gait is corrected." },
				TiersInCode = true
			},
			new DiskDef
			{
				Key = "Leap",
				DiskName = "Leap",
				EffectName = "Leap",
				EffectDesc = "You jump considerably higher, but land harder.",
				Effect = (Effect)0,
				Value = 0f,
				Price = 3500,
				Rarity = (Rarity)2,
				Manufacturer = (Manufacturer)0,
				SaleLocations = new string[1] { "SyncClinic" },
				TierDescs = new string[3] { "You jump higher still.", "Your leap improves again.", "You clear considerable heights." },
				TiersInCode = true
			},
			new DiskDef
			{
				Key = "DeadAir",
				DiskName = "Dead Air",
				EffectName = "Slow Response",
				EffectDesc = "Security systems take longer to react to you.",
				Effect = (Effect)44,
				Value = 0.5f,
				Price = 1250,
				Rarity = (Rarity)1,
				Manufacturer = (Manufacturer)5,
				SaleLocations = new string[1] { "BlackmarketSyncClinic" },
				TierDescs = new string[2] { "Tripped breakers stay down longer.", "Disabled security is slower to wake." },
				TiersInCode = true
			},
			new DiskDef
			{
				Key = "PlusOne",
				DiskName = "Plus One",
				EffectName = "On The List",
				EffectDesc = "You obtain guest passes more readily.",
				Effect = (Effect)64,
				Value = 0.25f,
				Price = 2000,
				Rarity = (Rarity)1,
				Manufacturer = (Manufacturer)2,
				SaleLocations = new string[1] { "SyncClinic" },
				TierDescs = null
			},
			new DiskDef
			{
				Key = "Muckraker",
				DiskName = "Muckraker",
				EffectName = "Expose",
				EffectDesc = "Earn money photographing illegal operations.",
				Effect = (Effect)50,
				Value = 40f,
				Price = 500,
				Rarity = (Rarity)0,
				Manufacturer = (Manufacturer)4,
				SaleLocations = new string[1] { "SyncClinic" },
				TierDescs = null
			},
			new DiskDef
			{
				Key = "BlankFace",
				DiskName = "Blank Face",
				Enabled = false,
				EffectName = "Unmemorable",
				EffectDesc = "Citizens are far less easily alarmed by you.",
				Effect = (Effect)62,
				Value = -0.3f,
				SideEffect = (Effect)14,
				SideValue = -0.3f,
				SideName = "Forgettable",
				SideDesc = "Citizens are less willing to talk to you.",
				Price = 2000,
				Rarity = (Rarity)2,
				Manufacturer = (Manufacturer)5,
				SaleLocations = new string[1] { "BlackmarketSyncClinic" },
				TierDescs = null
			},
			new DiskDef
			{
				Key = "CompanyMan",
				DiskName = "Company Man",
				EffectName = "Staff Discount",
				EffectDesc = "Everything you buy costs considerably less.",
				Effect = (Effect)13,
				Value = 0.2f,
				SideEffect = (Effect)14,
				SideValue = -0.2f,
				SideName = "Corporate Stink",
				SideDesc = "Citizens trust you less. This cannot be removed.",
				Price = 1500,
				Rarity = (Rarity)1,
				Manufacturer = (Manufacturer)2,
				SaleLocations = null,
				Retired = true,
				TierDescs = null
			},
			new DiskDef
			{
				Key = "Thumbscrews",
				DiskName = "Thumbscrews",
				EffectName = "Thumbscrews",
				EffectDesc = "When a witness says your description is not enough to go on, you can press them, and they name someone they know who fits it. They are not always right.",
				Effect = (Effect)0,
				Value = 0f,
				Price = 1750,
				Rarity = (Rarity)2,
				Manufacturer = (Manufacturer)5,
				SaleLocations = new string[1] { "BlackmarketSyncClinic" },
				TierDescs = null
			},
			new DiskDef
			{
				Key = "LowProfileDisk",
				DiskName = "Low Profile",
				EffectName = "Low Profile",
				EffectDesc = "Fines cost you less.",
				Effect = (Effect)0,
				Value = 0f,
				Price = 2000,
				Rarity = (Rarity)2,
				Manufacturer = (Manufacturer)5,
				SaleLocations = new string[1] { "BlackmarketSyncClinic" },
				TierDescs = new string[2] { "Your footsteps carry less far.", "Frightened people sometimes cower instead of running for help or joining a fight against you." },
				TiersInCode = true
			},
			new DiskDef
			{
				Key = "SecondWind",
				DiskName = "Second Wind",
				EffectName = "Second Wind",
				EffectDesc = "You recover health more quickly.",
				Effect = (Effect)12,
				Value = 0.1f,
				Price = 2000,
				Rarity = (Rarity)2,
				Manufacturer = (Manufacturer)0,
				SaleLocations = new string[1] { "SyncClinic" },
				TierDescs = new string[3] { "Your wounds close faster still.", "Your reserves last longer.", "Your flesh bruises less readily." },
				TiersInCode = true
			},
			new DiskDef
			{
				Key = "LongArm",
				DiskName = "Long Arm",
				EffectName = "Long Arm",
				EffectDesc = "You can reach further.",
				Effect = (Effect)23,
				Value = 0.1f,
				Price = 1000,
				Rarity = (Rarity)0,
				Manufacturer = (Manufacturer)0,
				SaleLocations = new string[1] { "SyncClinic" },
				TierDescs = new string[2] { "You can reach further still.", "Nothing on the shelf is out of your reach." },
				TiersInCode = true
			},
			new DiskDef
			{
				Key = "LightsOut",
				DiskName = "Lights Out",
				EffectName = "Lights Out",
				EffectDesc = "Citizens you knock out stay unconscious longer.",
				Effect = (Effect)42,
				Value = 0.5f,
				Price = 2000,
				Rarity = (Rarity)2,
				Manufacturer = (Manufacturer)5,
				SaleLocations = new string[1] { "BlackmarketSyncClinic" },
				TierDescs = null
			},
			new DiskDef
			{
				Key = "Prowler",
				DiskName = "Prowler",
				EffectName = "Prowler",
				EffectDesc = "You move faster while crouched.",
				Effect = (Effect)0,
				Value = 0f,
				Price = 1500,
				Rarity = (Rarity)1,
				Manufacturer = (Manufacturer)5,
				SaleLocations = new string[1] { "BlackmarketSyncClinic" },
				TierDescs = null,
				TiersInCode = true
			},
			new DiskDef
			{
				Key = "Heavyweight",
				DiskName = "Fractured Tranquility",
				EffectName = "Fractured Tranquility",
				EffectDesc = "Your blows send people flying.",
				Effect = (Effect)0,
				Value = 0f,
				Price = 2000,
				Rarity = (Rarity)2,
				Manufacturer = (Manufacturer)5,
				SaleLocations = new string[1] { "BlackmarketSyncClinic" },
				TierDescs = new string[3] { "Your hits land harder.", "People fly further when you hit them.", "Your blows throw people across the room." },
				TiersInCode = true
			},
			new DiskDef
			{
				Key = "SlowBurn",
				DiskName = "Slow Burn",
				EffectName = "Slow Burn",
				EffectDesc = "Your alertness and energy fade more slowly.",
				Effect = (Effect)0,
				Value = 0f,
				Price = 1500,
				Rarity = (Rarity)1,
				Manufacturer = (Manufacturer)1,
				SaleLocations = new string[1] { "SyncClinic" },
				TierDescs = new string[3] { "A cup of coffee keeps you going longer.", "Your reserves fade more slowly still.", "You stay sharp long after others flag." },
				TiersInCode = true
			},
			new DiskDef
			{
				Key = "Connoisseur",
				DiskName = "Connoisseur",
				EffectName = "Connoisseur",
				EffectDesc = "Food and drink do you more good.",
				Effect = (Effect)0,
				Value = 0f,
				Price = 1200,
				Rarity = (Rarity)0,
				Manufacturer = (Manufacturer)3,
				SaleLocations = new string[1] { "SyncClinic" },
				TierDescs = new string[3] { "You get more from every meal.", "Every drink refreshes you further.", "You get the very most out of everything you eat and drink." },
				TiersInCode = true
			},
			new DiskDef
			{
				Key = "Deadlift",
				DiskName = "Deadlift",
				EffectName = "Deadlift",
				EffectDesc = "You drag bodies faster.",
				Effect = (Effect)0,
				Value = 0f,
				Price = 1500,
				Rarity = (Rarity)1,
				Manufacturer = (Manufacturer)5,
				SaleLocations = new string[1] { "BlackmarketSyncClinic" },
				TierDescs = new string[3] { "You haul a body with less effort.", "Dead weight barely slows you down.", "You drag a body almost as fast as you walk." },
				TiersInCode = true
			}
		};
	}
	internal static class LowProfile
	{
		internal static ConfigEntry<float> FineCut = null;

		internal static ConfigEntry<float> StepCut = null;

		internal static ConfigEntry<float> CowerChance = null;

		private static int level = -1;

		private static float levelAt = -10f;

		internal static void Bind()
		{
			FineCut = Plugin.Cfg.Bind<float>("LowProfileDisk", "FineReduction", 0.25f, "Installed: fines are this much cheaper (0.25 = 25% off).");
			StepCut = Plugin.Cfg.Bind<float>("LowProfileDisk", "FootstepRangeCut", 3f, "Tier 1: how many metres less far your footsteps carry. A walk is usually heard 5 to 15 m away depending on the floor, crouching 5 m less, running 5 m more.");
			CowerChance = Plugin.Cfg.Bind<float>("LowProfileDisk", "CowerChance", 0.4f, "Tier 2: chance that a frightened citizen cowers instead of running, raising the alarm, calling the enforcers or joining a fight against you (0.4 = 40%).");
		}

		internal static int Level()
		{
			if (Time.unscaledTime - levelAt > 1f)
			{
				levelAt = Time.unscaledTime;
				level = Plugin.LevelOf("_Low Profile");
			}
			return level;
		}
	}
	[HarmonyPatch]
	internal static class LowProfileHooks
	{
		private static readonly HashSet<string> Swappable = new HashSet<string> { "RunAway", "TriggerAlarm", "CallEnforcers", "Attack", "Chase" };

		private static readonly Dictionary<int, (bool cower, float until)> decided = new Dictionary<int, (bool, float)>();

		private static AIActionPreset? cowering;

		[HarmonyPostfix]
		[HarmonyPatch(typeof(StatusCount), "GetPenaltyAmount")]
		private static void Fine(ref int __result)
		{
			try
			{
				if (__result > 0 && LowProfile.Level() >= 0)
				{
					__result = Mathf.RoundToInt((float)__result * (1f - Mathf.Clamp01(LowProfile.FineCut.Value)));
				}
			}
			catch
			{
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(AudioController), "PlayWorldFootstep")]
		private static void StepOn(AudioEvent eventPreset, Actor actor, out float __state)
		{
			__state = float.NaN;
			try
			{
				if (!((SoCustomComparison)(object)eventPreset == (SoCustomComparison)null) && !((Object)(object)actor == (Object)null) && actor.isPlayer && LowProfile.Level() >= 1)
				{
					__state = eventPreset.hearingRange;
					eventPreset.hearingRange = __state - Mathf.Max(0f, LowProfile.StepCut.Value);
				}
			}
			catch
			{
			}
		}

		[HarmonyFinalizer]
		[HarmonyPatch(typeof(AudioController), "PlayWorldFootstep")]
		private static void StepOff(AudioEvent eventPreset, float __state)
		{
			try
			{
				if (!float.IsNaN(__state) && (SoCustomComparison)(object)eventPreset != (SoCustomComparison)null)
				{
					eventPreset.hearingRange = __state;
				}
			}
			catch
			{
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(NewAIController), "CreateNewAction")]
		private static void Action(NewAIController __instance, NewAIGoal newGoal, ref AIActionPreset newPreset)
		{
			try
			{
				if ((SoCustomComparison)(object)newPreset == (SoCustomComparison)null || !Swappable.Contains(((Object)newPreset).name) || LowProfile.Level() < 2)
				{
					return;
				}
				Human human = __instance.human;
				if ((Object)(object)human == (Object)null || ((Actor)human).isPlayer || ((Actor)human).isEnforcer)
				{
					return;
				}
				object obj;
				if (newGoal == null)
				{
					obj = null;
				}
				else
				{
					AIGoalPreset preset = newGoal.preset;
					obj = ((preset != null) ? ((Object)preset).name : null);
				}
				if (obj == null)
				{
					obj = "";
				}
				switch ((string)obj)
				{
				default:
					return;
				case "Investigate":
					if (!((Object)(object)__instance.persuitTarget != (Object)null) || !__instance.persuitTarget.isPlayer)
					{
						return;
					}
					break;
				case "Flee":
				case "ReactToBody":
					break;
				}
				float unscaledTime = Time.unscaledTime;
				if (!decided.TryGetValue(human.humanID, out (bool, float) value) || unscaledTime > value.Item2)
				{
					value = (Random.value < Mathf.Clamp01(LowProfile.CowerChance.Value), unscaledTime + 60f);
					decided[human.humanID] = value;
				}
				if (value.Item1)
				{
					if (cowering == null)
					{
						cowering = Find("Cowering");
					}
					if (!((SoCustomComparison)(object)cowering == (SoCustomComparison)null))
					{
						newPreset = cowering;
					}
				}
			}
			catch
			{
			}
		}

		private static AIActionPreset? Find(string name)
		{
			try
			{
				foreach (Object item in (Il2CppArrayBase<Object>)(object)Resources.FindObjectsOfTypeAll(Il2CppType.Of<AIActionPreset>()))
				{
					AIActionPreset val = ((Il2CppObjectBase)item).TryCast<AIActionPreset>();
					if ((SoCustomComparison)(object)val != (SoCustomComparison)null && ((Object)val).name == name)
					{
						return val;
					}
				}
			}
			catch
			{
			}
			Plugin.L.LogWarning((object)("Low Profile: no '" + name + "' action found; citizens will not cower"));
			return null;
		}
	}
	internal static class NewDisks
	{
		internal static ConfigEntry<float> HeavyPush = null;

		internal static ConfigEntry<string> ProwlerTiers = null;

		internal static ConfigEntry<string> HeavyTiers = null;

		internal static ConfigEntry<string> SlowBurnTiers = null;

		internal static ConfigEntry<string> ConnoisseurTiers = null;

		internal static ConfigEntry<string> DeadliftTiers = null;

		internal static float CrouchMult = 1f;

		internal static float HitMult = 1f;

		internal static float DragMult = 1f;

		private static float? baseKOForce;

		private static readonly Dictionary<IntPtr, (RetailItemPreset p, float n, float h, float a, float e)> food = new Dictionary<IntPtr, (RetailItemPreset, float, float, float, float)>();

		private static float foodMult = 1f;

		internal static void Bind(ConfigFile cfg)
		{
			ProwlerTiers = cfg.Bind<string>("Prowler", "CrouchSpeed", "1.75", "Crouch-walk speed multiplier while Prowler is installed (it has no upgrades).");
			if (ProwlerTiers.Value == "1.25,1.4,1.55,1.75")
			{
				ProwlerTiers.Value = "1.75";
			}
			HeavyPush = cfg.Bind<float>("FracturedTranquility", "PushPerStep", 4f, "Extra speed (metres per second) given to a body you knock out, per step of HitForce above 1. At 3 that is 8.");
			HeavyTiers = cfg.Bind<string>("FracturedTranquility", "HitForce", "1.5,2,2.5,3", "How hard your hits throw people (ragdoll force multiplier): installed, tier 1, tier 2, tier 3.");
			SlowBurnTiers = cfg.Bind<string>("SlowBurn", "DrainFraction", "-0.15,-0.25,-0.35,-0.45", "Change to how fast alertness and energy drain (-0.15 = 15% slower): installed, tier 1, tier 2, tier 3. Summed with the other disks that write the same fields.");
			DeadliftTiers = cfg.Bind<string>("Deadlift", "DragSpeed", "1.3,1.5,1.75,2", "Walk and run speed multiplier while dragging a body: installed, tier 1, tier 2, tier 3. The game slows you to about a third while dragging, so 2 roughly doubles that. Capped at 3.");
			ConnoisseurTiers = cfg.Bind<string>("Connoisseur", "FoodMultiplier", "1.2,1.3,1.4,1.5", "Multiplier on what food and drink give (nourishment, hydration, alertness, energy): installed, tier 1, tier 2, tier 3.");
		}

		internal static float Pick(ConfigEntry<string> e, int level, float neutral)
		{
			if (level < 0)
			{
				return neutral;
			}
			string[] array = (e.Value ?? "").Split(',');
			int num = Math.Min(level, array.Length - 1);
			if (num < 0 || !float.TryParse(array[num].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
			{
				return neutral;
			}
			return result;
		}

		internal static void Apply(GameplayControls gc, int prowler, int heavy, int connoisseur, int deadlift)
		{
			//IL_022b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0232: Expected O, but got Unknown
			DragMult = Mathf.Clamp(Pick(DeadliftTiers, deadlift, 1f), 1f, 3f);
			CrouchMult = Pick(ProwlerTiers, prowler, 1f);
			HitMult = Pick(HeavyTiers, heavy, 1f);
			float valueOrDefault = baseKOForce.GetValueOrDefault();
			if (!baseKOForce.HasValue)
			{
				valueOrDefault = gc.playerKOPunchForce;
				baseKOForce = valueOrDefault;
			}
			gc.playerKOPunchForce = baseKOForce.Value * HitMult;
			float num = Pick(ConnoisseurTiers, connoisseur, 1f);
			if (food.Count == 0)
			{
				foreach (Object item in (Il2CppArrayBase<Object>)(object)Resources.FindObjectsOfTypeAll(Il2CppType.Of<RetailItemPreset>()))
				{
					RetailItemPreset val = ((Il2CppObjectBase)item).TryCast<RetailItemPreset>();
					if ((SoCustomComparison)(object)val != (SoCustomComparison)null)
					{
						food[((Il2CppObjectBase)val).Pointer] = (val, val.nourishment, val.hydration, val.alertness, val.energy);
					}
				}
			}
			if (Math.Abs(num - foodMult) > 0.0001f)
			{
				foodMult = num;
				foreach (var value in food.Values)
				{
					try
					{
						value.p.nourishment = ((value.n > 0f) ? (value.n * num) : value.n);
						value.p.hydration = ((value.h > 0f) ? (value.h * num) : value.h);
						value.p.alertness = ((value.a > 0f) ? (value.a * num) : value.a);
						value.p.energy = ((value.e > 0f) ? (value.e * num) : value.e);
					}
					catch
					{
					}
				}
			}
			ManualLogSource l = Plugin.L;
			bool flag = default(bool);
			BepInExInfoLogInterpolatedStringHandler val2 = new BepInExInfoLogInterpolatedStringHandler(105, 10, ref flag);
			if (flag)
			{
				((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("[TIER] prowler=");
				((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<int>(prowler);
				((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" crouch x");
				((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<float>(CrouchMult);
				((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" | heavyweight=");
				((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<int>(heavy);
				((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" hits x");
				((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<float>(HitMult);
				((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" koForce=");
				((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<float>(gc.playerKOPunchForce);
				((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" | connoisseur=");
				((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<int>(connoisseur);
				((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" food x");
				((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<float>(foodMult);
				((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" (");
				((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<int>(food.Count);
				((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" items) | deadlift=");
				((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<int>(deadlift);
				((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" drag x");
				((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<float>(DragMult);
			}
			l.LogInfo(val2);
		}
	}
	public class ProwlerMover : MonoBehaviour
	{
		private float nextLog;

		private float? baseWalk;

		private bool raised;

		public ProwlerMover(IntPtr p)
			: base(p)
		{
		}

		private void Update()
		{
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Expected O, but got Unknown
			try
			{
				Player instance = Player.Instance;
				FirstPersonController val = ((instance != null) ? instance.fps : null);
				Player instance2 = Player.Instance;
				if ((Object)(object)val == (Object)null || (Object)(object)instance2 == (Object)null)
				{
					return;
				}
				bool flag = ((Actor)instance2).isCrouched || instance2.crouchedTransition > 0.5f;
				if (NewDisks.CrouchMult > 1f && flag)
				{
					if (!raised)
					{
						baseWalk = val.m_WalkSpeed;
						raised = true;
					}
					val.m_WalkSpeed = baseWalk.Value * NewDisks.CrouchMult;
					if (Time.unscaledTime > nextLog)
					{
						nextLog = Time.unscaledTime + 3f;
						ManualLogSource l = Plugin.L;
						bool flag2 = default(bool);
						BepInExInfoLogInterpolatedStringHandler val2 = new BepInExInfoLogInterpolatedStringHandler(41, 3, ref flag2);
						if (flag2)
						{
							((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("[PROWLER] crouched, walk ");
							((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<float>(baseWalk.Value, "0.00");
							((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" -> ");
							((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<float>(val.m_WalkSpeed, "0.00");
							((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(", speed now ");
							((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<float>(val.speed, "0.00");
						}
						l.LogInfo(val2);
					}
				}
				else if (raised)
				{
					val.m_WalkSpeed = baseWalk.Value;
					raised = false;
				}
			}
			catch (Exception ex)
			{
				if (Time.unscaledTime > nextLog)
				{
					nextLog = Time.unscaledTime + 10f;
					Plugin.L.LogInfo((object)("[PROWLER] " + ex.Message));
				}
			}
		}
	}
	[HarmonyPatch(typeof(Citizen), "RecieveDamage")]
	internal static class HeavyweightPatch
	{
		internal static bool PlayerHit;

		private static int logged;

		private static void Prefix(Actor fromWho, ref float ragdollForceMP)
		{
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Expected O, but got Unknown
			PlayerHit = false;
			if (NewDisks.HitMult == 1f || (Object)(object)fromWho == (Object)null)
			{
				return;
			}
			try
			{
				if (!fromWho.isPlayer)
				{
					return;
				}
				PlayerHit = true;
				ragdollForceMP *= NewDisks.HitMult;
				if (logged++ < 5)
				{
					ManualLogSource l = Plugin.L;
					bool flag = default(bool);
					BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(35, 1, ref flag);
					if (flag)
					{
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[HEAVY] player hit, ragdoll force x");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<float>(NewDisks.HitMult);
					}
					l.LogInfo(val);
				}
			}
			catch
			{
			}
		}

		private static void Postfix()
		{
			PlayerHit = false;
		}
	}
	[HarmonyPatch(typeof(NewAIController), "SetKO")]
	internal static class HeavyweightKOPatch
	{
		private static int logged;

		private static void Prefix(NewAIController __instance, bool val, Vector3 impactDirection, ref float forceMultiplier)
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: 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_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Expected O, but got Unknown
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			if (!val || !HeavyweightPatch.PlayerHit || NewDisks.HitMult <= 1f)
			{
				return;
			}
			forceMultiplier *= NewDisks.HitMult;
			try
			{
				Vector3 val2 = impactDirection;
				if (((Vector3)(ref val2)).sqrMagnitude < 0.01f && (Object)(object)Player.Instance != (Object)null)
				{
					val2 = ((Component)Player.Instance).transform.forward;
				}
				val2.y = Mathf.Max(val2.y, 0f) + 0.25f;
				HeavyweightPush.Queue(__instance.human, ((Vector3)(ref val2)).normalized * ((NewDisks.HitMult - 1f) * NewDisks.HeavyPush.Value));
				if (logged++ < 5)
				{
					ManualLogSource l = Plugin.L;
					bool flag = default(bool);
					BepInExInfoLogInterpolatedStringHandler val3 = new BepInExInfoLogInterpolatedStringHandler(36, 2, ref flag);
					if (flag)
					{
						((BepInExLogInterpolatedStringHandler)val3).AppendLiteral("[HEAVY] knockout x");
						((BepInExLogInterpolatedStringHandler)val3).AppendFormatted<float>(NewDisks.HitMult);
						((BepInExLogInterpolatedStringHandler)val3).AppendLiteral(": push ");
						((BepInExLogInterpolatedStringHandler)val3).AppendFormatted<float>((NewDisks.HitMult - 1f) * NewDisks.HeavyPush.Value, "0.0");
						((BepInExLogInterpolatedStringHandler)val3).AppendLiteral(" m/s queued");
					}
					l.LogInfo(val3);
				}
			}
			catch (Exception ex)
			{
				Plugin.L.LogInfo((object)("[HEAVY] " + ex.Message));
			}
		}
	}
	public class HeavyweightPush : MonoBehaviour
	{
		private static readonly List<(Human who, Vector3 v, int frames)> queue = new List<(Human, Vector3, int)>();

		private static int logged;

		public HeavyweightPush(IntPtr p)
			: base(p)
		{
		}

		internal static void Queue(Human who, Vector3 v)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)who != (Object)null)
			{
				queue.Add((who, v, 2));
			}
		}

		private void FixedUpdate()
		{
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Expected O, but got Unknown
			bool flag = default(bool);
			for (int num = queue.Count - 1; num >= 0; num--)
			{
				(Human, Vector3, int) value = queue[num];
				if (--value.Item3 > 0)
				{
					queue[num] = value;
				}
				else
				{
					queue.RemoveAt(num);
					try
					{
						Il2CppArrayBase<Rigidbody> componentsInChildren = ((Component)value.Item1).GetComponentsInChildren<Rigidbody>();
						int num2 = 0;
						foreach (Rigidbody item in componentsInChildren)
						{
							if (!((Object)(object)item == (Object)null) && !item.isKinematic)
							{
								item.AddForce(value.Item2, (ForceMode)2);
								num2++;
							}
						}
						if (logged++ < 5)
						{
							ManualLogSource l = Plugin.L;
							BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(38, 3, ref flag);
							if (flag)
							{
								((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[HEAVY] pushed ");
								((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(num2);
								((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" of ");
								((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(componentsInChildren.Length);
								((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" body parts at ");
								((BepInExLogInterpolatedStringHandler)val).AppendFormatted<float>(((Vector3)(ref value.Item2)).magnitude, "0.0");
								((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" m/s");
							}
							l.LogInfo(val);
						}
					}
					catch (Exception ex)
					{
						Plugin.L.LogInfo((object)("[HEAVY] push: " + ex.Message));
					}
				}
			}
		}
	}
	public class DeadliftMover : MonoBehaviour
	{
		private float baseWalk = -1f;

		private float baseRun = -1f;

		private float nextLog;

		public DeadliftMover(IntPtr p)
			: base(p)
		{
		}

		private void Update()
		{
			//IL_010b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0112: Expected O, but got Unknown
			try
			{
				Player instance = Player.Instance;
				FirstPersonController val = ((instance != null) ? instance.fps : null);
				if ((Object)(object)val == (Object)null || (Object)(object)instance == (Object)null)
				{
					return;
				}
				InteractionController instance2 = InteractionController.Instance;
				RigidbodyDragObject val2 = ((instance2 != null) ? instance2.currentlyDragging : null);
				if (NewDisks.DragMult > 1f && (Object)(object)val2 != (Object)null && val2.dragIsActive)
				{
					if (baseWalk < 0f)
					{
						float desiredWalkSpeed = instance.desiredWalkSpeed;
						float desiredRunSpeed = instance.desiredRunSpeed;
						if (desiredWalkSpeed > 0.1f && desiredWalkSpeed < 20f && desiredRunSpeed > 0.1f && desiredRunSpeed < 40f)
						{
							baseWalk = desiredWalkSpeed;
							baseRun = desiredRunSpeed;
						}
					}
					if (!(baseWalk > 0f))
					{
						return;
					}
					val.m_WalkSpeed = baseWalk * NewDisks.DragMult;
					val.m_RunSpeed = baseRun * NewDisks.DragMult;
					if (Time.unscaledTime > nextLog)
					{
						nextLog = Time.unscaledTime + 5f;
						ManualLogSource l = Plugin.L;
						bool flag = default(bool);
						BepInExInfoLogInterpolatedStringHandler val3 = new BepInExInfoLogInterpolatedStringHandler(30, 2, ref flag);
						if (flag)
						{
							((BepInExLogInterpolatedStringHandler)val3).AppendLiteral("[DEADLIFT] dragging, walk ");
							((BepInExLogInterpolatedStringHandler)val3).AppendFormatted<float>(baseWalk, "0.00");
							((BepInExLogInterpolatedStringHandler)val3).AppendLiteral(" -> ");
							((BepInExLogInterpolatedStringHandler)val3).AppendFormatted<float>(val.m_WalkSpeed, "0.00");
						}
						l.LogInfo(val3);
					}
				}
				else if (baseWalk > 0f)
				{
					val.m_WalkSpeed = instance.desiredWalkSpeed;
					val.m_RunSpeed = instance.desiredRunSpeed;
					baseWalk = (baseRun = -1f);
				}
			}
			catch (Exception ex)
			{
				if (Time.unscaledTime > nextLog)
				{
					nextLog = Time.unscaledTime + 10f;
					Plugin.L.LogInfo((object)("[DEADLIFT] " + ex.Message));
				}
			}
		}
	}
	[BepInPlugin("ta.sod.syncdiskpack", "SoD Disk Pack", "0.15.1")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class Plugin : BasePlugin
	{
		public const string GUID = "ta.sod.syncdiskpack";

		public const string VER = "0.18.0";

		internal static ManualLogSource L;

		internal static ConfigEntry<string> DisableList;

		internal static ConfigEntry<string> RemoveList;

		internal static ConfigEntry<float> QsTier1;

		internal static ConfigEntry<float> QsTier2;

		internal static ConfigEntry<float> QsTier3;

		internal static ConfigEntry<float> LeapJump0;

		internal static ConfigEntry<float> LeapJump1;

		internal static ConfigEntry<float> LeapJump2;

		internal static ConfigEntry<float> LeapJump3;

		internal static ConfigEntry<float> LeapFall0;

		internal static ConfigEntry<float> LeapFall1;

		internal static ConfigEntry<float> LeapFall2;

		internal static ConfigEntry<float> LeapFall3;

		internal static ConfigEntry<float> IlTiredness;

		internal static ConfigEntry<float> IlRecovery;

		internal static ConfigEntry<float> IlHunger;

		internal static ConfigEntry<float> IlThirst;

		internal static ConfigEntry<float> IlGaitCure;

		internal static ConfigEntry<float> ScSpeedPerTier;

		internal static ConfigEntry<float> ScTiredness;

		internal static ConfigEntry<float> ScBleeding;

		internal static ConfigEntry<float> ScFallDamage;

		internal static ConfigEntry<float> DaBreakerReset;

		internal static ConfigEntry<float> DaSecurityReset;

		internal static ConfigEntry<int> DaTamperGrace;

		internal static ConfigEntry<float> LaInteractionRange;

		internal static ConfigEntry<float> LaCarryDistance;

		internal static ConfigEntry<float> LaThrowForce;

		internal static ConfigEntry<float> LaInteractionRange2;

		internal static ConfigEntry<float> SwRecovery;

		internal static ConfigEntry<float> SwEnergy;

		internal static ConfigEntry<float> SwBruised;

		internal static ConfigEntry<string> DpTierSlots;

		internal static ConfigEntry<int> CoatUpgradeSlots;

		internal static ConfigEntry<int> LiningUpgradeSlots;

		internal static ConfigEntry<int> MaxInventorySlots;

		internal static float? BaseRunSpeed;

		internal static float? BaseJumpHeight;

		internal static float? BaseFallDamage;

		internal static float? BaseTiredness;

		internal static float? BaseRecovery;

		internal static float? BaseHunger;

		internal static float? BaseThirst;

		internal static float? BaseBleeding;

		internal static float? BaseBreakerReset;

		internal static float? BaseSecurityReset;

		internal static int? BaseTamperGrace;

		internal static float? BaseInteractionRange;

		internal static float? BaseCarryDistance;

		internal static float? BaseThrowForce;

		internal static float? BaseEnergyRate;

		internal static float? BaseBruised;

		internal static ConfigFile Cfg;

		private ConfigFile OpenSettings(string newFileName)
		{
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Expected O, but got Unknown
			string text = Path.Combine(Paths.ConfigPath, newFileName);
			try
			{
				string text2 = Path.Combine(Paths.ConfigPath, "ta.sod.syncdiskpack.cfg");
				if (File.Exists(text2))
				{
					if (!File.Exists(text))
					{
						File.Copy(text2, text);
					}
					if (File.ReadAllText(text2) == File.ReadAllText(text))
					{
						File.Delete(text2);
						((BasePlugin)this).Log.LogInfo((object)("Moved settings from the old config file to " + newFileName));
					}
				}
			}
			catch (Exception ex)
			{
				((BasePlugin)this).Log.LogWarning((object)("Could not move the old settings file: " + ex.Message));
			}
			return new ConfigFile(text, true);
		}

		public override void Load()
		{
			Cfg = OpenSettings("sm3gm.sod.syncdiskpack.cfg");
			L = ((BasePlugin)this).Log;
			L.LogInfo((object)"=== SoDDiskPack 0.18.0 starting ===");
			DisableList = Cfg.Bind<string>("_Experimental", "DisableVanillaDisks", "", "TEST ONLY. Comma-separated vanilla preset names to set disabled=true. Leave empty to change nothing.");
			RemoveList = Cfg.Bind<string>("_Experimental", "RemoveVanillaDisks", "", "TEST ONLY. Comma-separated vanilla preset names to REMOVE from Toolbox.allSyncDisks and from every MenuPreset stock list. Suppresses VENDOR stock only; world loot in an existing city is unaffected. Throwaway saves only. Leave empty.");
			QsTier1 = Cfg.Bind<float>("Quickstep", "Tier1RunSpeedFraction", 0.05f, "Quickstep tier 1. Fraction added to base playerRunSpeed. Cumulative.");
			QsTier2 = Cfg.Bind<float>("Quickstep", "Tier2RunSpeedFraction", 0.05f, "Quickstep tier 2. Fraction added to base playerRunSpeed. Cumulative.");
			QsTier3 = Cfg.Bind<float>("Quickstep", "Tier3RunSpeedFraction", 0.1f, "Quickstep tier 3. Fraction added to base playerRunSpeed. Cumulative.");
			LeapJump0 = Cfg.Bind<float>("Leap", "JumpHeightInstalled", 6f, "GameplayControls.jumpHeight while Leap is installed, unupgraded. Game baseline is 4.5. Absolute value, not a fraction.");
			LeapJump1 = Cfg.Bind<float>("Leap", "JumpHeightTier1", 7f, "jumpHeight at tier 1. Absolute.");
			LeapJump2 = Cfg.Bind<float>("Leap", "JumpHeightTier2", 8f, "jumpHeight at tier 2. Absolute.");
			LeapJump3 = Cfg.Bind<float>("Leap", "JumpHeightTier3", 9f, "jumpHeight at tier 3. Absolute. 9.0 was play-tested as a good ceiling.");
			LeapFall0 = Cfg.Bind<float>("Leap", "FallDamageInstalled", 1f, "fallDamageMultiplier while Leap is installed. Game baseline is 0.85. This is Leap's cost. The cure is the Stability split disk, not a tier.");
			LeapFall1 = Cfg.Bind<float>("Leap", "FallDamageTier1", 1.1f, "fallDamageMultiplier at tier 1.");
			LeapFall2 = Cfg.Bind<float>("Leap", "FallDamageTier2", 1.2f, "fallDamageMultiplier at tier 2.");
			LeapFall3 = Cfg.Bind<float>("Leap", "FallDamageTier3", 1.3f, "fallDamageMultiplier at tier 3.");
			IlTiredness = Cfg.Bind<float>("IronLung", "Tier1TirednessFraction", -0.3f, "Iron Lung tier 1. Fraction applied to base playerTirednessRate (0.06). Negative tires you more slowly. Deliberately ASYMMETRIC against Sprinter's Curse tier 1 (+0.25): if the two were equal and opposite a player running both disks would see an upgrade do literally nothing.");
			IlRecovery = Cfg.Bind<float>("IronLung", "Tier2RecoveryFraction", 0.25f, "Iron Lung tier 2. Fraction applied to base playerRecoveryRate (0.225). Positive heals faster. SUMMED with Second Wind tier 1 if both installed.");
			IlHunger = Cfg.Bind<float>("IronLung", "Tier3HungerFraction", -0.2f, "Iron Lung tier 3. Fraction applied to base playerHungerRate (0.125).");
			IlThirst = Cfg.Bind<float>("IronLung", "Tier3ThirstFraction", -0.2f, "Iron Lung tier 3. Fraction applied to base playerThirstRate (0.15).");
			IlGaitCure = Cfg.Bind<float>("IronLung", "Tier3GaitCureFraction", 0.25f, "Iron Lung tier 3 compensates for the Heavy Gait side effect. The enum gives maxSpeedModifier -0.2 (x0.8); this writes playerRunSpeed +25% on top. The two compose multiplicatively, so in ISOLATION 0.8 x 1.25 returns you to normal exactly. NOTE: run-speed fractions from all disks are summed and applied once to the baseline, so the cure is DILUTED when Quickstep or Sprinter's Curse are also installed - roughly 5% short with both at tier 3. This is expected behaviour.");
			ScSpeedPerTier = Cfg.Bind<float>("SprintersCurse", "SpeedFractionPerTier", 0.08f, "Sprinter's Curse. Fraction added to base playerRunSpeed per tier. Cumulative.");
			ScTiredness = Cfg.Bind<float>("SprintersCurse", "Tier1TirednessFraction", 0.25f, "Sprinter's Curse tier 1 cost. Fraction applied to base playerTirednessRate. Positive tires you faster. See IronLung Tier1TirednessFraction for why these two are not equal and opposite.");
			ScBleeding = Cfg.Bind<float>("SprintersCurse", "Tier2BleedingFraction", 0.5f, "Sprinter's Curse tier 2 cost. Fraction applied to base combatHitChanceOfBleeding (0.1). combatHitChanceOfBrokenLeg was rejected for this: at a 0.01 base it is imperceptible even tripled.");
			ScFallDamage = Cfg.Bind<float>("SprintersCurse", "Tier3FallDamageFraction", 0.3f, "Sprinter's Curse tier 3 cost. Fraction applied to whatever fallDamageMultiplier already is, so it stacks on top of Leap.");
			DaBreakerReset = Cfg.Bind<float>("DeadAir", "Tier1BreakerResetFraction", 0.5f, "Dead Air tier 1. Fraction applied to base breakerResetTime (0.5). POSITIVE means tripped breakers stay down LONGER, which is the stealth benefit. The sign was inverted before 0.10.0 and shortened the outage instead.");
			DaSecurityReset = Cfg.Bind<float>("DeadAir", "Tier2SecurityResetFraction", 0.5f, "Dead Air tier 2. Fraction applied to base securityResetTime (3). POSITIVE means disabled security takes LONGER to come back online. Same sign inversion as tier 1, fixed in 0.10.0.");
			DaTamperGrace = Cfg.Bind<int>("DeadAir", "Tier3TamperGraceAbsolute", 5, "UNUSED as of 0.10.0. Dead Air ships as a two-tier chain. tamperGrace is unobservable by construction: the tampering action completes in under 3 seconds, so the grace window never expires at any value. The key is retained so existing configs are not disturbed.");
			LaInteractionRange = Cfg.Bind<float>("LongArm", "Tier1InteractionRangeFraction", 0.2f, "Long Arm tier 1. Fraction applied to base interactionRange (1.585). This is interaction reach, and it stacks with the disk's own reachModifier main effect. Confirmed read live and observed in play.");
			LaCarryDistance = Cfg.Bind<float>("LongArm", "Tier2CarryDistanceFraction", 0.4f, "UNUSED as of 0.10.0. carryDistance is owned by PlacementPlus while that mod is enabled, so writes to it do nothing. The key is retained so existing configs are not disturbed.");
			LaInteractionRange2 = Cfg.Bind<float>("LongArm", "Tier2InteractionRangeFraction", 0.1f, "Long Arm tier 2 as of 0.17.1. Fraction applied to base interactionRange (1.585), on top of tier 1.");
			LaThrowForce = Cfg.Bind<float>("LongArm", "Tier3ThrowForceFraction", 0.4f, "UNUSED as of 0.17.1 (tier 2 is reach now; Brawn covers throwing). Before that: Long Arm tier 2 as of 0.10.0, despite the key name, which is kept to avoid orphaning the value on existing installs. Fraction applied to base throwForce (6.4). Raised from 0.25 because 0.25 was too weak to feel in play.");
			SwRecovery = Cfg.Bind<float>("SecondWind", "Tier1RecoveryFraction", 0.2f, "Second Wind tier 1. Fraction applied to base playerRecoveryRate (0.225). SUMMED with Iron Lung tier 2, which writes the same field.");
			SwEnergy = Cfg.Bind<float>("SecondWind", "Tier2EnergyFraction", -0.2f, "Second Wind tier 2. Fraction applied to base playerEnergyRate (0.116). UNTESTED IN PLAY: this field has never been confirmed to be read live rather than consumed once at startup. It will log correctly either way.");
			SwBruised = Cfg.Bind<float>("SecondWind", "Tier3BruisedFraction", -0.4f, "Second Wind tier 3. Fraction applied to base combatHitChanceOfBruised (0.1). Negative means you bruise less often. A small effect by design; no vanilla disk claims this field.");
			NewDisks.Bind(Cfg);
			DpTierSlots = Cfg.Bind<string>("DeepPockets", "TierSlots", "1,1", "Deep Pockets. Extra inventory slots added by upgrade tiers 1 and 2, on top of the disk's own slot (two tiers since 1.3.0).");
			CoatUpgradeSlots = Cfg.Bind<int>("TailoredCoat", "UpgradeSlots", 1, "Tailored Coat. Extra inventory slots added by its upgrade, on top of the disk's own slot.");
			LiningUpgradeSlots = Cfg.Bind<int>("SmugglersLining", "UpgradeSlots", 1, "Smuggler's Lining. Extra inventory slots added by its upgrade, on top of the disk's own slot.");
			if (DpTierSlots.Value == "1,1,1" || DpTierSlots.Value == "2,2,3")
			{
				DpTierSlots.Value = "1,1";
			}
			if (CoatUpgradeSlots.Value == 2)
			{
				CoatUpgradeSlots.Value = 1;
			}
			if (LiningUpgradeSlots.Value == 2)
			{
				LiningUpgradeSlots.Value = 1;
			}
			MaxInventorySlots = Cfg.Bind<int>("Inventory", "MaxInventorySlots", 18, "Ceiling for the inventory size from all disks together (at most 18). Above 12 the inventory screen shows a third row. Never lowers a size set by the game or another mod.");
			int num = 0;
			int num2 = 0;
			DiskDef[] all = Disks.All;
			foreach (DiskDef diskDef in all)
			{
				if (!Cfg.Bind<bool>(diskDef.Key, "Enabled", diskDef.Enabled, "Register this disk.").Value)
				{
					L.LogInfo((object)("skipped: " + diskDef.DiskName));
					continue;
				}
				int value = Cfg.Bind<int>(diskDef.Key, "Price", diskDef.Price, "Purchase price.").Value;
				float value2 = Cfg.Bind<float>(diskDef.Key, "Value", diskDef.Value, "Base effect value. Fractions are percentages (0.1 = 10%). Ignored on disks whose effect is applied in code.").Value;
				if (diskDef.TierDescs != null && diskDef.TierDescs.Length != 0 && !diskDef.TiersInCode)
				{
					num2++;
					L.LogWarning((object)("*** " + diskDef.DiskName + " has upgrade options but no code in RecomputeAll. Those upgrades will do NOTHING. ***"));
				}
				try
				{
					Register(diskDef, value, value2);
					num++;
				}
				catch (Exception ex)
				{
					L.LogError((object)("FAILED " + diskDef.DiskName + ": " + ex));
				}
			}
			Thumbscrews.Register();
			LowProfile.Bind();
			Harmony.CreateAndPatchAll(typeof(LowProfileHooks), (string)null);
			Harmony.CreateAndPatchAll(typeof(ThumbscrewsHooks), (string)null);
			Harmony.CreateAndPatchAll(typeof(DisablePatch), (string)null);
			Harmony.CreateAndPatchAll(typeof(TierPatch), (string)null);
			Harmony.CreateAndPatchAll(typeof(InventoryPatch), (string)null);
			Harmony.CreateAndPatchAll(typeof(HeavyweightPatch), (string)null);
			Harmony.CreateAndPatchAll(typeof(HeavyweightKOPatch), (string)null);
			((BasePlugin)this).AddComponent<ProwlerMover>();
			((BasePlugin)this).AddComponent<DeadliftMover>();
			((BasePlugin)this).AddComponent<HeavyweightPush>();
			L.LogInfo((object)("=== SoDDiskPack done: " + num + " registered, " + num2 + " with unimplemented tiers ==="));
		}

		private static void Register(DiskDef d, int price, float value)
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_021d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0222: Unknown result type (might be due to invalid IL or missing references)
			//IL_023e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0243: 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_0110: Invalid comparison between Unknown and I4
			//IL_0156: Unknown result type (might be due to invalid IL or missing references)
			//IL_0162: Unknown result type (might be due to invalid IL or missing references)
			//IL_0187: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_0190: 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)
			int num = default(int);
			SyncDiskBuilder val = Lib.SyncDisks.Builder(d.DiskName, "ta.sod.syncdiskpack", true).AddEffect(d.EffectName, d.EffectDesc, ref num, (string)null);
			if ((int)d.SideEffect != 0)
			{
				int num2 = default(int);
				val.AddSideEffect(d.SideName, d.SideDesc, ref num2);
			}
			if (d.SaleLocations != null && d.SaleLocations.Length != 0)
			{
				val.AddSaleLocation(d.SaleLocations);
			}
			if (d.Retired)
			{
				val.SetWorldSpawnOption(false).SetCanBeSideJobReward(false);
			}
			if (d.TierDescs != null && d.TierDescs.Length != 0)
			{
				Options val2 = default(Options);
				if (d.TierDescs.Length == 1)
				{
					((Options)(ref val2))..ctor(d.TierDescs[0]);
				}
				else if (d.TierDescs.Length == 2)
				{
					((Options)(ref val2))..ctor(d.TierDescs[0], d.TierDescs[1]);
				}
				else
				{
					((Options)(ref val2))..ctor(d.TierDescs[0], d.TierDescs[1], d.TierDescs[2]);
				}
				OptionIds val3 = default(OptionIds);
				val.AddUpgradeOption(val2, ref val3);
			}
			SyncDiskPreset preset = val.CreateAndRegister().Preset;
			bool flag = (int)d.Effect == 0;
			if (!flag)
			{
				preset.mainEffect1 = d.Effect;
				preset.mainEffect1Value = value;
			}
			preset.mainEffect2 = (Effect)0;
			preset.mainEffect2Value = 0f;
			preset.mainEffect3 = (Effect)0;
			preset.mainEffect3Value = 0f;
			preset.price = price;
			preset.rarity = d.Rarity;
			preset.manufacturer = d.Manufacturer;
			preset.canBeSideJobReward = !d.Retired;
			preset.minimumWealthLevel = 0f;
			if ((int)d.SideEffect != 0)
			{
				preset.sideEffect = d.SideEffect;
				preset.sideEffectValue = d.SideValue;
			}
			int num3 = 0;
			try
			{
				num3 = preset.option1UpgradeEffects.Count;
			}
			catch
			{
			}
			L.LogInfo((object)("registered " + d.DiskName + " | effect=" + ((object)preset.mainEffect1/*cast due to .constrained prefix*/).ToString() + " value=" + preset.mainEffect1Value + " mfr=" + ((object)preset.manufacturer/*cast due to .constrained prefix*/).ToString() + " side=" + ((object)preset.sideEffect/*cast due to .constrained prefix*/).ToString() + " sideVal=" + preset.sideEffectValue + " price=" + preset.price + " optionSlots=" + num3 + " customId=" + num + (flag ? " CODE-DRIVEN" : "") + (d.TiersInCode ? " TIERS-IN-CODE" : "")));
		}

		internal static int LevelOf(string suffix)
		{
			try
			{
				UpgradesController instance = UpgradesController.Instance;
				if ((Object)(object)instance == (Object)null || instance.upgrades == null)
				{
					return -1;
				}
				Enumerator<Upgrades> enumerator = instance.upgrades.GetEnumerator();
				while (enumerator.MoveNext())
				{
					Upgrades current = enumerator.Current;
					if (current != null && !((SoCustomComparison)(object)current.preset == (SoCustomComparison)null))
					{
						string name = ((Object)current.preset).name;
						if (name != null && name.EndsWith(suffix))
						{
							return current.level;
						}
					}
				}
			}
			catch
			{
			}
			return -1;
		}

		internal static void RecomputeAll()
		{
			try
			{
				GameplayControls instance = GameplayControls.Instance;
				if (!((Object)(object)instance == (Object)null))
				{
					if (!BaseRunSpeed.HasValue)
					{
						BaseRunSpeed = instance.playerRunSpeed;
						BaseJumpHeight = instance.jumpHeight;
						BaseFallDamage = instance.fallDamageMultiplier;
						BaseTiredness = instance.playerTirednessRate;
						BaseRecovery = instance.playerRecoveryRate;
						BaseHunger = instance.playerHungerRate;
						BaseThirst = instance.playerThirstRate;
						BaseBleeding = instance.combatHitChanceOfBleeding;
						BaseBreakerReset = instance.breakerResetTime;
						BaseSecurityReset = instance.securityResetTime;
						BaseTamperGrace = instance.tamperGrace;
						BaseInteractionRange = instance.interactionRange;
						BaseCarryDistance = instance.carryDistance;
						BaseThrowForce = instance.throwForce;
						BaseEnergyRate = instance.playerEnergyRate;
						BaseBruised = instance.combatHitChanceOfBruised;
						ManualLogSource l = L;
						string[] array = new string[32];
						array[0] = "[TIER] captured baselines runSpeed=";
						float? baseRunSpeed = BaseRunSpeed;
						array[1] = baseRunSpeed.ToString();
						array[2] = " jump=";
						baseRunSpeed = BaseJumpHeight;
						array[3] = baseRunSpeed.ToString();
						array[4] = " fall=";
						baseRunSpeed = BaseFallDamage;
						array[5] = baseRunSpeed.ToString();
						array[6] = " tired=";
						baseRunSpeed = BaseTiredness;
						array[7] = baseRunSpeed.ToString();
						array[8] = " recovery=";
						baseRunSpeed = BaseRecovery;
						array[9] = baseRunSpeed.ToString();
						array[10] = " hunger=";
						baseRunSpeed = BaseHunger;
						array[11] = baseRunSpeed.ToString();
						array[12] = " thirst=";
						baseRunSpeed = BaseThirst;
						array[13] = baseRunSpeed.ToString();
						array[14] = " bleeding=";
						baseRunSpeed = BaseBleeding;
						array[15] = baseRunSpeed.ToString();
						array[16] = " breakerReset=";
						baseRunSpeed = BaseBreakerReset;
						array[17] = baseRunSpeed.ToString();
						array[18] = " securityReset=";
						baseRunSpeed = BaseSecurityReset;
						array[19] = baseRunSpeed.ToString();
						array[20] = " tamperGrace=";
						int? baseTamperGrace = BaseTamperGrace;
						array[21] = baseTamperGrace.ToString();
						array[22] = " interactionRange=";
						baseRunSpeed = BaseInteractionRange;
						array[23] = baseRunSpeed.ToString();
						array[24] = " carryDistance=";
						baseRunSpeed = BaseCarryDistance;
						array[25] = baseRunSpeed.ToString();
						array[26] = " throwForce=";
						baseRunSpeed = BaseThrowForce;
						array[27] = baseRunSpeed.ToString();
						array[28] = " energy=";
						baseRunSpeed = BaseEnergyRate;
						array[29] = baseRunSpeed.ToString();
						array[30] = " bruised=";
						baseRunSpeed = BaseBruised;
						array[31] = baseRunSpeed.ToString();
						l.LogInfo((object)string.Concat(array));
					}
					int num = LevelOf("_Quickstep");
					int num2 = LevelOf("_Leap");
					int num3 = LevelOf("_Iron Lung");
					int num4 = LevelOf("_Sprinter's Curse");
					int num5 = LevelOf("_Dead Air");
					int num6 = LevelOf("_Long Arm");
					int num7 = LevelOf("_Second Wind");
					int level = LevelOf("_Slow Burn");
					NewDisks.Apply(instance, LevelOf("_Prowler"), LevelOf("_Fractured Tranquility"), LevelOf("_Connoisseur"), LevelOf("_Deadlift"));
					float num8 = NewDisks.Pick(NewDisks.SlowBurnTiers, level, 0f);
					float num9 = 0f;
					if (num >= 1)
					{
						num9 += QsTier1.Value;
					}
					if (num >= 2)
					{
						num9 += QsTier2.Value;
					}
					if (num >= 3)
					{
						num9 += QsTier3.Value;
					}
					if (num4 >= 1)
					{
						num9 += ScSpeedPerTier.Value;
					}
					if (num4 >= 2)
					{
						num9 += ScSpeedPerTier.Value;
					}
					if (num4 >= 3)
					{
						num9 += ScSpeedPerTier.Value;
					}
					if (num3 >= 3)
					{
						num9 += IlGaitCure.Value;
					}
					instance.playerRunSpeed = BaseRunSpeed.Value * (1f + num9);
					float value = BaseJumpHeight.Value;
					float num10 = BaseFallDamage.Value;
					if (num2 == 0)
					{
						value = LeapJump0.Value;
						num10 = LeapFall0.Value;
					}
					else if (num2 == 1)
					{
						value = LeapJump1.Value;
						num10 = LeapFall1.Value;
					}
					else if (num2 == 2)
					{
						value = LeapJump2.Value;
						num10 = LeapFall2.Value;
					}
					else if (num2 >= 3)
					{
						value = LeapJump3.Value;
						num10 = LeapFall3.Value;
					}
					if (num4 >= 3)
					{
						num10 *= 1f + ScFallDamage.Value;
					}
					instance.jumpHeight = value;
					instance.fallDamageMultiplier = num10;
					float num11 = 0f;
					if (num3 >= 1)
					{
						num11 += IlTiredness.Value;
					}
					if (num4 >= 1)
					{
						num11 += ScTiredness.Value;
					}
					num11 += num8;
					instance.playerTirednessRate = BaseTiredness.Value * (1f + num11);
					float num12 = 0f;
					if (num3 >= 2)
					{
						num12 += IlRecovery.Value;
					}
					if (num7 >= 1)
					{
						num12 += SwRecovery.Value;
					}
					instance.playerRecoveryRate = BaseRecovery.Value * (1f + num12);
					instance.playerHungerRate = BaseHunger.Value * (1f + ((num3 >= 3) ? IlHunger.Value : 0f));
					instance.playerThirstRate = BaseThirst.Value * (1f + ((num3 >= 3) ? IlThirst.Value : 0f));
					instance.combatHitChanceOfBleeding = BaseBleeding.Value * (1f + ((num4 >= 2) ? ScBleeding.Value : 0f));
					instance.breakerResetTime = BaseBreakerReset.Value * (1f + ((num5 >= 1) ? DaBreakerReset.Value : 0f));
					instance.securityResetTime = BaseSecurityReset.Value * (1f + ((num5 >= 2) ? DaSecurityReset.Value : 0f));
					instance.tamperGrace = BaseTamperGrace.Value;
					instance.interactionRange = BaseInteractionRange.Value * (1f + ((num6 >= 1) ? LaInteractionRange.Value : 0f) + ((num6 >= 2) ? LaInteractionRange2.Value : 0f));
					instance.carryDistance = BaseCarryDistance.Value;
					instance.throwForce = BaseThrowForce.Value;
					instance.playerEnergyRate = BaseEnergyRate.Value * (1f + ((num7 >= 2) ? SwEnergy.Value : 0f) + num8);
					instance.combatHitChanceOfBruised = BaseBruised.Value * (1f + ((num7 >= 3) ? SwBruised.Value : 0f));
					L.LogInfo((object)("[TIER] qs=" + num + " leap=" + num2 + " il=" + num3 + " sc=" + num4 + " da=" + num5 + " la=" + num6 + " sw=" + num7 + " | runSpeed=" + instance.playerRunSpeed + " jump=" + instance.jumpHeight + " fall=" + instance.fallDamageMultiplier + " tired=" + instance.playerTirednessRate + " recovery=" + instance.playerRecoveryRate + " hunger=" + instance.playerHungerRate + " thirst=" + instance.playerThirstRate + " bleeding=" + instance.combatHitChanceOfBleeding + " breakerReset=" + instance.breakerResetTime + " securityReset=" + instance.securityResetTime + " tamperGrace=" + instance.tamperGrace + " interactionRange=" + instance.interactionRange + " carryDistance=" + instance.carryDistance + " throwForce=" + instance.throwForce + " energy=" + instance.playerEnergyRate + " bruised=" + instance.combatHitChanceOfBruised));
				}
			}
			catch (Exception ex)
			{
				L.LogError((object)("[TIER] RecomputeAll failed: " + ex));
			}
		}
	}
	[HarmonyPatch(typeof(UpgradeEffectController), "OnSyncDiskChange")]
	public static class TierPatch
	{
		private static void Postfix()
		{
			Plugin.RecomputeAll();
		}
	}
	[HarmonyPatch(typeof(FirstPersonItemController), "SetSlotSize")]
	public static class InventoryPatch
	{
		private static int lastLogged = -1;

		private static int TierSlots(int level)
		{
			int num = 0;
			string[] array = (Plugin.DpTierSlots.Value ?? "").Split(',');
			for (int i = 0; i < level && i < array.Length; i++)
			{
				if (int.TryParse(array[i].Trim(), out var result))
				{
					num += result;
				}
			}
			return num;
		}

		private static void LogInventoryDisks()
		{
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Invalid comparison between Unknown and I4
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Invalid comparison between Unknown and I4
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Invalid comparison between Unknown and I4
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f1: Invalid comparison between Unknown and I4
			//IL_019c: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a1: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				UpgradesController instance = UpgradesController.Instance;
				if (((instance != null) ? instance.upgrades : null) == null)
				{
					return;
				}
				Enumerator<Upgrades> enumerator = instance.upgrades.GetEnumerator();
				while (enumerator.MoveNext())
				{
					Upgrades current = enumerator.Current;
					SyncDiskPreset val = ((current != null) ? current.preset : null);
					List<string> parts;
					if (!((SoCustomComparison)(object)val == (SoCustomComparison)null))
					{
						parts = new List<string>();
						if ((int)val.mainEffect1 == 11)
						{
							parts.Add("main1 " + val.mainEffect1Value);
						}
						if ((int)val.mainEffect2 == 11)
						{
							parts.Add("main2 " + val.mainEffect2Value);
						}
						if ((int)val.mainEffect3 == 11)
						{
							parts.Add("main3 " + val.mainEffect3Value);
						}
						if ((int)val.sideEffect == 11)
						{
							parts.Add("side " + val.sideEffectValue);
						}
						Opt("opt1", val.option1UpgradeEffects, val.option1UpgradeValues);
						Opt("opt2", val.option2UpgradeEffects, val.option2UpgradeValues);
						Opt("opt3", val.option3UpgradeEffects, val.option3UpgradeValues);
						if (parts.Count > 0)
						{
							Plugin.L.LogInfo((object)("[SLOTS] disk " + ((Object)val).name + " state " + ((object)current.state/*cast due to .constrained prefix*/).ToString() + " level " + current.level + ": " + string.Join(", ", parts)));
						}
					}
					void Opt(string tag, List<UpgradeEffect> fx, List<float> vals)
					{
						//IL_0009: 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_0046: Unknown result type (might be due to invalid IL or missing references)
						//IL_004b: Unknown result type (might be due to invalid IL or missing references)
						int num = 0;
						while (fx != null && num < fx.Count)
						{
							if (((object)fx[num]/*cast due to .constrained prefix*/).ToString().IndexOf("nventory", StringComparison.OrdinalIgnoreCase) >= 0)
							{
								parts.Add(tag + " " + ((object)fx[num]/*cast due to .constrained prefix*/).ToString() + " " + ((vals != null && num < vals.Count) ? vals[num] : 0f));
							}
							num++;
						}
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.L.LogInfo((object)("[SLOTS] disk list failed: " + ex.Message));
			}
		}

		private static void Prefix(FirstPersonItemController __instance, ref int newSize, out List<InventorySlot> __state)
		{
			__state = null;
			int num = newSize;
			int level = Plugin.LevelOf("_Deep Pockets");
			int num2 = TierSlots(level);
			if (Plugin.LevelOf("_Tailored Coat") >= 1)
			{
				num2 += Plugin.CoatUpgradeSlots.Value;
			}
			if (Plugin.LevelOf("_Smuggler's Lining") >= 1)
			{
				num2 += Plugin.LiningUpgradeSlots.Value;
			}
			int val = Math.Min(Plugin.MaxInventorySlots.Value, 18);
			int num3 = Math.Max(num, Math.Min(num + num2, val));
			if (num3 != lastLogged)
			{
				lastLogged = num3;
				LogInventoryDisks();
				ManualLogSource l = Plugin.L;
				string[] obj = new string[11]
				{
					"[SLOTS] game asked ",
					num.ToString(),
					", Deep Pockets tier ",
					level.ToString(),
					", upgrades add ",
					num2.ToString(),
					", size ",
					num3.ToString(),
					" (default ",
					null,
					null
				};
				GameplayControls instance = GameplayControls.Instance;
				obj[9] = ((instance != null) ? instance.defaultInventorySlots : (-1)).ToString();
				obj[10] = ")";
				l.LogInfo((object)string.Concat(obj));
			}
			newSize = num3;
			if (num3 > 12)
			{
				__state = BigInventory.HoldAside(__instance, num3);
				newSize = 12;
			}
		}

		private static void Postfix(FirstPersonItemController __instance, List<InventorySlot> __state)
		{
			BigInventory.Restore(__instance, __state);
		}
	}
	[HarmonyPatch(typeof(Toolbox), "Start")]
	public static class DisablePatch
	{
		private static bool done;

		private static List<string> ParseNames(string raw)
		{
			List<string> list = new List<string>();
			if (string.IsNullOrWhiteSpace(raw))
			{
				return list;
			}
			string[] array = raw.Split(',');
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i].Trim();
				if (text.Length > 0)
				{
					list.Add(text);
				}
			}
			return list;
		}

		private static void Postfix()
		{
			if (!done)
			{
				done = true;
				DoDisable();
				DoRemove();
			}
		}

		private static void DoDisable()
		{
			List<string> list = ParseNames(Plugin.DisableList.Value);
			if (list.Count == 0)
			{
				Plugin.L.LogInfo((object)"=== DisableVanillaDisks empty, nothing flagged ===");
				return;
			}
			int num = 0;
			Enumerator<SyncDiskPreset> enumerator = Toolbox.Instance.allSyncDisks.GetEnumerator();
			while (enumerator.MoveNext())
			{
				SyncDiskPreset current = enumerator.Current;
				foreach (string item in list)
				{
					if (((Object)current).name == item)
					{
						current.disabled = true;
						num++;
						Plugin.L.LogInfo((object)("=== DISABLED vanilla preset: " + ((Object)current).name + " ==="));
					}
				}
			}
			Plugin.L.LogInfo((object)("=== disable pass complete, " + num + " of " + list.Count + " names matched ==="));
		}

		private static void DoRemove()
		{
			List<string> list = ParseNames(Plugin.RemoveList.Value);
			if (list.Count == 0)
			{
				Plugin.L.LogInfo((object)"=== RemoveVanillaDisks empty, nothing removed ===");
				return;
			}
			int num = 0;
			Toolbox instance = Toolbox.Instance;
			if ((Object)(object)instance != (Object)null && instance.allSyncDisks != null)
			{
				for (int num2 = instance.allSyncDisks.Count - 1; num2 >= 0; num2--)
				{
					SyncDiskPreset val = instance.allSyncDisks[num2];
					if (!((SoCustomComparison)(object)val == (SoCustomComparison)null))
					{
						foreach (string item in list)
						{
							if (((Object)val).name == item)
							{
								instance.allSyncDisks.RemoveAt(num2);
								num++;
								Plugin.L.LogInfo((object)("=== REMOVED from Toolbox.allSyncDisks: " + item + " ==="));
								break;
							}
						}
					}
				}
			}
			else
			{
				Plugin.L.LogWarning((object)"=== Toolbox.allSyncDisks null, toolbox removal skipped ===");
			}
			int num3 = 0;
			int num4 = 0;
			Il2CppArrayBase<MenuPreset> val2 = Resources.FindObjectsOfTypeAll<MenuPreset>();
			if (val2 != null)
			{
				foreach (MenuPreset item2 in val2)
				{
					if ((SoCustomComparison)(object)item2 == (SoCustomComparison)null || item2.syncDisks == null)
					{
						continue;
					}
					num4++;
					for (int num5 = item2.syncDisks.Count - 1; num5 >= 0; num5--)
					{
						SyncDiskPreset val3 = item2.syncDisks[num5];
						if (!((SoCustomComparison)(object)val3 == (SoCustomComparison)null))
						{
							foreach (string item3 in list)
							{
								if (((Object)val3).name == item3)
								{
									item2.syncDisks.RemoveAt(num5);
									num3++;
									break;
								}
							}
						}
					}
				}
			}
			Plugin.L.LogInfo((object)("=== remove pass complete: " + num + " from Toolbox, " + num3 + " across " + num4 + " menu presets ==="));
		}
	}
	internal static class Thumbscrews
	{
		internal static ConfigEntry<int> MinClues = null;

		internal static Human? Witness;

		internal static Human? Target;

		internal static List<DataKey> Keys = new List<DataKey>();

		internal static float At = -1f;

		internal static bool Fresh(Citizen saysTo)
		{
			if ((Object)(object)Witness != (Object)null && (Object)(object)saysTo != (Object)null && Witness.humanID == ((Human)saysTo).humanID)
			{
				return Time.unscaledTime - At < 120f;
			}
			return false;
		}

		internal static bool Installed()
		{
			return Plugin.LevelOf("_Thumbscrews") >= 0;
		}

		internal static void Register()
		{
			MinClues = Plugin.Cfg.Bind<int>("Thumbscrews", "MinimumClues", 2, "How many things about the person's looks or whereabouts the card must show before the witness can name anyone. Any part of the name is enough on its own.");
			try
			{
				ThumbscrewsDialog.MsgId = Lib.Dialogs.Builder((string)null).SetText("[Thumbscrews] Think harder. Anyone you know fit this?").ModifyDialogOptions((Action<DialogPreset>)delegate(DialogPreset p)
				{
					p.ranking = int.MaxValue;
				})
					.SetDialogLogic((IDialogLogic)(object)new ThumbscrewsDialog())
					.CreateAndRegister()
					.Preset.msgID;
				ThumbscrewsDialog.Tabbed = ThumbscrewsDialog.Tab();
			}
			catch (Exception ex)
			{
				Plugin.L.LogError((object)("Thumbscrews dialog: " + ex));
			}
		}
	}
	[HarmonyPatch]
	internal static class ThumbscrewsHooks
	{
		private static PhotoSelectButtonController? clicking;

		private const string NotEnough = "a6815309-f9d4-40b0-8a1e-3ec3550c64a2";

		[HarmonyPrefix]
		[HarmonyPatch(typeof(PhotoSelectButtonController), "OnLeftClick")]
		private static void ClickOn(PhotoSelectButtonController __instance)
		{
			clicking = __instance;
		}

		[HarmonyFinalizer]
		[HarmonyPatch(typeof(PhotoSelectButtonController), "OnLeftClick")]
		private static void ClickOff()
		{
			clicking = null;
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(SpeechController), "Speak", new Type[]
		{
			typeof(string),
			typeof(bool),
			typeof(bool),
			typeof(Human),
			typeof(SideJob),
			typeof(InteractionDialogInstance)
		})]
		private static void Speak(SpeechController __instance, string ddsMessage)
		{
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if ((Object)(object)clicking == (Object)null || ddsMessage != "a6815309-f9d4-40b0-8a1e-3ec3550c64a2" || !Thumbscrews.Installed())
				{
					return;
				}
				Human val = (((Object)(object)__instance.actor != (Object)null) ? ((Il2CppObjectBase)__instance.actor).TryCast<Human>() : null);
				Human citizen = clicking.citizen;
				if ((Object)(object)val == (Object)null || (Object)(object)citizen == (Object)null || ((Actor)val).isPlayer)
				{
					return;
				}
				List<DataKey> list = new List<DataKey>();
				List<DataKey> val2 = ((clicking.element != null) ? clicking.element.dk : null);
				EvidenceWitness evidenceEntry = ((Actor)citizen).evidenceEntry;
				if (val2 != null && evidenceEntry != null)
				{
					List<DataKey> tiedKeys = ((Evidence)evidenceEntry).GetTiedKeys(val2);
					int num = 0;
					while (tiedKeys != null && num < tiedKeys.Count)
					{
						if (!list.Contains(tiedKeys[num]))
						{
							list.Add(tiedKeys[num]);
						}
						num++;
					}
				}
				Thumbscrews.Witness = val;
				Thumbscrews.Target = citizen;
				Thumbscrews.Keys = list;
				Thumbscrews.At = Time.unscaledTime;
			}
			catch (Exception ex)
			{
				Plugin.L.LogInfo((object)("Thumbscrews: " + ex.Message));
			}
		}
	}
	internal sealed class ThumbscrewsDialog : IDialogLogic
	{
		internal static string MsgId = "";

		internal static bool Tabbed;

		internal static bool Retried;

		internal static bool Tab()
		{
			try
			{
				Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
				for (int i = 0; i < assemblies.Length; i++)
				{
					MethodInfo methodInfo = assemblies[i].GetType("DialogUIRework.TabbedDialogUI")?.GetMethod("AddIDs");
					if (!(methodInfo == null))
					{
						methodInfo.Invoke(null, new object[2]
						{
							"Investigation",
							new string[1] { MsgId }
						});
						return true;
					}
				}
			}
			catch
			{
			}
			return false;
		}

		public bool IsDialogShown(DialogPreset preset, Citizen saysTo, SideJob jobRef)
		{
			if (!Tabbed && !Retried)
			{
				Retried = true;
				Tabbed = Tab();
			}
			if (Thumbscrews.Installed())
			{
				return Thumbscrews.Fresh(saysTo);
			}
			return false;
		}

		public ForceSuccess ShouldDialogSucceedOverride(DialogController instance, DialogOption dialog, Citizen saysTo, NewNode where, Actor saidBy)
		{
			return (ForceSuccess)1;
		}

		public void OnDialogExecute(DialogController instance, Citizen saysTo, Interactable saysToInteractable, NewNode where, Actor saidBy, bool success, NewRoom roomRef, SideJob jobRef)
		{
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: 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_00b7: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (!Thumbscrews.Fresh(saysTo) || (Object)(object)Thumbscrews.Target == (Object)null)
				{
					Lib.Dialogs.Speak(saysTo, "Who are we talking about again?", true);
					return;
				}
				Human target = Thumbscrews.Target;
				List<DataKey> keys = Thumbscrews.Keys;
				Thumbscrews.Witness = null;
				if (target.humanID == ((Human)saysTo).humanID)
				{
					Lib.Dialogs.Speak(saysTo, "Are you serious? That's me.", true);
					return;
				}
				if (((Actor)target).isPlayer)
				{
					Lib.Dialogs.Speak(saysTo, "Is this a joke? That's you.", true);
					return;
				}
				List<DataKey> list = new List<DataKey>();
				foreach (DataKey item in keys)
				{
					if (ThumbscrewsMatch.IsClue(item))
					{
						list.Add(item);
					}
				}
				if (list.Count < Thumbscrews.MinClues.Value && !list.Exists(ThumbscrewsMatch.IsName))
				{
					Lib.Dialogs.Speak(saysTo, "Ease up! That could be half the city.", true);
					return;
				}
				List<(Human, float)> list2 = new List<(Human, float)>();
				float num = 0f;
				List<Acquaintance> acquaintances = ((Human)saysTo).acquaintances;
				int num2 = 0;
				while (acquaintances != null && num2 < acquaintances.Count)
				{
					Acquaintance val = acquaintances[num2];
					if (val != null)
					{
						Human val2 = null;
						try
						{
							val2 = val.GetOther((Human)(object)saysTo);
						}
						catch
						{
						}
						if (!((Object)(object)val2 == (Object)null) && val2.humanID != ((Human)saysTo).humanID && !((Actor)val2).isPlayer && ThumbscrewsMatch.Fits(val2, target, list))
						{
							float num3 = Mathf.Max(0.05f, val.known);
							list2.Add((val2, num3));
							num += num3;
						}
					}
					num2++;
				}
				if (ThumbscrewsMatch.Fits(target, target, list) && !list2.Exists(((Human h, float w) p) => p.h.humanID == target.humanID))
				{
					try
					{
						Acquaintance val3 = default(Acquaintance);
						if (target.FindAcquaintanceExists((Human)(object)saysTo, ref val3) && val3 != null)
						{
							float num4 = Mathf.Max(0.05f, val3.known);
							list2.Add((target, num4));
							num += num4;
						}
					}
					catch
					{
					}
				}
				if (list2.Count == 0)
				{
					Lib.Dialogs.Speak(saysTo, "I swear, I don't know anyone like that!", true);
					return;
				}
				float num5 = Random.Range(0f, num);
				Human val4 = list2[list2.Count - 1].Item1;
				foreach (var item2 in list2)
				{
					num5 -= item2.Item2;
					if (num5 <= 0f)
					{
						(val4, _) = item2;
						break;
					}
				}
				Lib.Dialogs.Speak(saysTo, "Alright, alright! Sounds like " + val4.GetCitizenName() + ".", false);
				try
				{
					((Human)saysTo).RevealSighting(val4, true, false, ((Actor)saysTo).speechController, true);
				}
				catch (Exception ex)
				{
					Plugin.L.LogInfo((object)("Thumbscrews sighting: " + ex.Message));
				}
			}
			catch (Exception ex2)
			{
				Plugin.L.LogInfo((object)("Thumbscrews: " + ex2));
			}
		}
	}
	internal static class ThumbscrewsMatch
	{
		internal static bool IsName(DataKey k)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Invalid comparison between Unknown and I4
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Invalid comparison between Unknown and I4
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Invalid comparison between Unknown and I4
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Invalid comparison between Unknown and I4
			if ((int)k != 39 && (int)k != 38 && (int)k != 40)
			{
				return (int)k == 41;
			}
			return true;
		}

		internal static bool IsClue(DataKey k)
		{
			//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)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Expected I4, but got Unknown
			bool flag = IsName(k);
			if (!flag)
			{
				bool flag2;
				switch (k - 5)
				{
				case 0:
				case 1:
				case 2:
				case 3:
				case 4:
				case 5:
				case 7:
				case 8:
				case 9:
				case 10:
				case 13:
				case 14:
				case 19:
				case 20:
				case 25:
				case 27:
				case 29:
				case 30:
					flag2 = true;
					break;
				default:
					flag2 = false;
					break;
				}
				flag = flag2;
			}
			return flag;
		}

		internal static bool Fits(Human c, Human t, List<DataKey> clues)
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: 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_00c6: Expected I4, but got Unknown
			//IL_014d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0153: 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_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0162: 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_0188: Unknown result type (might be due to invalid IL or missing references)
			//IL_018e: 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_0106: Unknown result type (might be due to invalid IL or missing references)
			//IL_0138: Unknown result type (might be due to invalid IL or missing references)
			//IL_013e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0170: Unknown result type (might be due to invalid IL or missing references)
			//IL_0176: Unknown result type (might be due to invalid IL or missing references)
			Descriptors descriptors = c.descriptors;
			Descriptors descriptors2 = t.descriptors;
			if (descriptors == null || descriptors2 == null)
			{
				return false;
			}
			foreach (DataKey clue in clues)
			{
				bool flag;
				int num3;
				int num2;
				int num;
				switch (clue - 5)
				{
				case 3:
					flag = c.gender == t.gender;
					break;
				case 2:
					flag = Mathf.Abs(c.GetAge() - t.GetAge()) <= 2;
					break;
				case 19:
					flag = c.GetAgeGroup() == t.GetAgeGroup();
					break;
				case 0:
					flag = Mathf.Abs(descriptors.heightCM - descriptors2.heightCM) <= 3f;
					break;
				case 25:
					flag = descriptors.height == descriptors2.height;
					break;
				case 1:
					flag = descriptors.build == descriptors2.build;
					break;
				case 4:
					flag = descriptors.hairColourCategory == descriptors2.hairColourCategory && descriptors.hairType == descriptors2.hairType;
					break;
				case 5:
					flag = descriptors.eyeColour == descriptors2.eyeColour;
					break;
				case 7:
					flag = descriptors.shoeSize == descriptors2.shoeSize;
					break;
				case 13:
					flag = Math.Abs(descriptors.shoeSize - descriptors2.shoeSize) <= 1;
					break;
				case 20:
					flag = Initial(c) == Initial(t);
					break;
				case 14:
					flag = Has(descriptors, (FacialFeature)5) == Has(descriptors2, (FacialFeature)5);
					break;
				case 8:
					flag = Has(descriptors, (FacialFeature)1) == Has(descriptors2, (FacialFeature)1) && Has(descriptors, (FacialFeature)2) == Has(descriptors2, (FacialFeature)2);
					break;
				case 9:
					flag = (Object)(object)c.home != (Object)null && (Object)(object)t.home != (Object)null && c.home.id == t.home.id;
					break;
				case 29:
				{
					NewAddress home = c.home;
					if ((Object)(object)((home != null) ? ((NewGameLocation)home).building : null) != (Object)null)
					{
						NewAddress home2 = t.home;
						if ((Object)(object)((home2 != null) ? ((NewGameLocation)home2).building : null) != (Object)null)
						{
							num3 = ((((Il2CppObjectBase)((NewGameLocation)c.home).building).Pointer == ((Il2CppObjectBase)((NewGameLocation)t.home).building).Pointer) ? 1 : 0);
							goto IL_02c0;
						}
					}
					num3 = 0;
					goto IL_02c0;
				}
				case 27:
				{
					NewAddress home3 = c.home;
					if ((Object)(object)((home3 != null) ? ((NewGameLocation)home3).floor : null) != (Object)null)
					{
						NewAddress home4 = t.home;
						if ((Object)(object)((home4 != null) ? ((NewGameLocation)home4).floor : null) != (Object)null)
						{
							num2 = ((((Il2CppObjectBase)((NewGameLocation)c.home).floor).Pointer == ((Il2CppObjectBase)((NewGameLocation)t.home).floor).Pointer) ? 1 : 0);
							goto IL_0323;
						}
					}
					num2 = 0;
					goto IL_0323;
				}
				case 10:
				{
					Occupation job = c.job;
					if (((job != null) ? job.employer : null) != null)
					{
						Occupation job2 = t.job;
						if (((job2 != null) ? job2.employer : null) != null)
						{
							num = ((((Il2CppObjectBase)c.job.employer).Pointer == ((Il2CppObjectBase)t.job.employer).Pointer) ? 1 : 0);
							goto IL_037a;
						}
					}
					num = 0;
					goto IL_037a;
				}
				case 30:
					flag = (Object)(object)Work(c) != (Object)null && (Object)(object)Work(t) != (Object)null && ((Il2CppObjectBase)Work(c)).Pointer == ((Il2CppObjectBase)Work(t)).Pointer;
					break;
				case 34:
					flag = Same(c.GetSurName(), t.GetSurName());
					break;
				case 33:
					flag = Same(c.GetFirstName(), t.GetFirstName());
					break;
				case 35:
					flag = Same(c.GetInitialledName(), t.GetInitialledName());
					break;
				case 36:
					flag = Same(c.GetInitials(), t.GetInitials());
					break;
				default:
					{
						flag = true;
						break;
					}
					IL_0323:
					flag = (byte)num2 != 0;
					break;
					IL_02c0:
					flag = (byte)num3 != 0;
					break;
					IL_037a:
					flag = (byte)num != 0;
					break;
				}
				if (!flag)
				{
					return false;
				}
			}
			return true;
		}

		private static bool Same(string a, string b)
		{
			if (!string.IsNullOrEmpty(a))
			{
				return string.Equals(a.Trim(), b?.Trim(), StringComparison.OrdinalIgnoreCase);
			}
			return false;
		}

		private static char Initial(Human h)
		{
			string firstName = h.GetFirstName();
			if (!string.IsNullOrEmpty(firstName))
			{
				return char.ToUpperInvariant(firstName[0]);
			}
			return ' ';
		}

		private static bool Has(Descriptors d, FacialFeature f)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			List<FacialFeaturesSetting> facialFeatures = d.facialFeatures;
			int num = 0;
			while (facialFeatures != null && num < facialFeatures.Count)
			{
				if (facialFeatures[num].feature == f)
				{
					return true;
				}
				num++;
			}
			return false;
		}

		private static NewBuilding? Work(Human h)
		{
			try
			{
				Occupation job = h.job;
				object result;
				if (job == null)
				{
					result = null;
				}
				else
				{
					Company employer = job.employer;
					if (employer == null)
					{
						result = null;
					}
					else
					{
						NewAddress address = employer.address;
						result = ((address != null) ? ((NewGameLocation)address).building : null);
					}
				}
				return (NewBuilding?)result;
			}
			catch
			{
				return null;
			}
		}
	}
}

BepInEx/plugins/SoDDiskAvailability/SoDDiskAvailability.dll

Decompiled 5 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Core.Logging.Interpolation;
using BepInEx.Logging;
using BepInEx.Unity.IL2CPP;
using HarmonyLib;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppSystem.Collections.Generic;
using Microsoft.CodeAnalysis;
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("SoDDiskAvailability")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.3.1.0")]
[assembly: AssemblyInformationalVersion("0.3.1")]
[assembly: AssemblyProduct("SoDDiskAvailability")]
[assembly: AssemblyTitle("SoDDiskAvailability")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.3.1.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;
		}
	}
}
[HarmonyPatch(typeof(Toolbox), "LoadAll")]
[HarmonyPriority(0)]
public static class DedupePatch
{
	public static bool Enabled = true;

	public static ManualLogSource Log;

	private static void Postfix()
	{
		if (!Enabled)
		{
			return;
		}
		int num = 0;
		int num2 = 0;
		Il2CppArrayBase<MenuPreset> val = Resources.FindObjectsOfTypeAll<MenuPreset>();
		if (val == null)
		{
			return;
		}
		for (int i = 0; i < val.Length; i++)
		{
			MenuPreset val2 = val[i];
			if ((SoCustomComparison)(object)val2 == (SoCustomComparison)null || val2.syncDisks == null || val2.syncDisks.Count == 0)
			{
				continue;
			}
			HashSet<string> hashSet = new HashSet<string>();
			List<SyncDiskPreset> list = new List<SyncDiskPreset>();
			int num3 = 0;
			for (int j = 0; j < val2.syncDisks.Count; j++)
			{
				SyncDiskPreset val3 = val2.syncDisks[j];
				if (!((SoCustomComparison)(object)val3 == (SoCustomComparison)null))
				{
					string text = ((Object)val3).name;
					if (text == null)
					{
						text = "null_" + j;
					}
					if (hashSet.Add(text))
					{
						list.Add(val3);
					}
					else
					{
						num3++;
					}
				}
			}
			if (num3 != 0)
			{
				val2.syncDisks.Clear();
				for (int k = 0; k < list.Count; k++)
				{
					val2.syncDisks.Add(list[k]);
				}
				num += num3;
				num2++;
			}
		}
		if (num > 0 && Log != null)
		{
			Log.LogInfo((object)("[DEDUPE] removed " + num + " duplicate entries across " + num2 + " menu presets"));
		}
	}
}
namespace SoDDiskAvailability
{
	[BepInPlugin("ta.sod.diskavailability", "SoD Disk Availability", "0.7.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class Plugin : BasePlugin
	{
		public const string GUID = "ta.sod.diskavailability";

		public const string VER = "0.7.0";

		internal static ManualLogSource L;

		internal static ConfigEntry<string> Mode;

		internal static ConfigEntry<int> Salt;

		internal static ConfigEntry<int> MinPrice;

		internal static ConfigEntry<string> Retired;

		internal static ConfigEntry<bool> LogStock;

		public static ConfigEntry<bool> DedupeMenus;

		internal static readonly List<Pool> Pools = new List<Pool>();

		internal static ConfigFile Cfg;

		private ConfigFile OpenSettings(string newFileName)
		{
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Expected O, but got Unknown
			string text = Path.Combine(Paths.ConfigPath, newFileName);
			try
			{
				string text2 = Path.Combine(Paths.ConfigPath, "ta.sod.diskavailability.cfg");
				if (File.Exists(text2))
				{
					if (!File.Exists(text))
					{
						File.Copy(text2, text);
					}
					if (File.ReadAllText(text2) == File.ReadAllText(text))
					{
						File.Delete(text2);
						((BasePlugin)this).Log.LogInfo((object)("Moved settings from the old config file to " + newFileName));
					}
				}
			}
			catch (Exception ex)
			{
				((BasePlugin)this).Log.LogWarning((object)("Could not move the old settings file: " + ex.Message));
			}
			return new ConfigFile(text, true);
		}

		public override void Load()
		{
			Cfg = OpenSettings("sm3gm.sod.diskavailability.cfg");
			L = ((BasePlugin)this).Log;
			L.LogInfo((object)"=== SoD Disk Availability 0.7.0 starting ===");
			Mode = Cfg.Bind<string>("General", "Mode", "Rotation", "Unrestricted = every disk stocked everywhere, always. Vanilla = restore the stock lists as the game shipped them. Rotation = each vendor stocks a small set that changes daily.");
			Salt = Cfg.Bind<int>("General", "RotationSalt", 0, "Change this number to get a different rotation sequence. Rotation is derived from the in-game day plus this salt, so the same day always produces the same stock and reloading never rerolls it.");
			LogStock = Cfg.Bind<bool>("General", "LogStockToConsole", true, "Write the chosen stock to the BepInEx log on every rotation.");
			DedupeMenus = Cfg.Bind<bool>("General", "FixVendorDuplicates", true, "Removes duplicate sync disk entries that build up in vendor stock lists when a city is loaded more than once in the same session. Works around a known issue in SOD.Common 2.1.4. Leave this on unless it conflicts with another mod.");
			MinPrice = Cfg.Bind<int>("General", "MinimumPrice", 200, "No sync disk costs less than this, whatever its source (vanilla, split, added by this suite or another mod). Stops cheap disks from being resold for profit. 0 = off.");
			Retired = Cfg.Bind<string>("General", "RetiredDisks", "Company Man", "Disks that were taken out of the suite and are never stocked again, comma separated. Copies already installed keep working.");
			DedupePatch.Enabled = DedupeMenus.Value;
			DedupePatch.Log = L;
			Add("SyncClinic", "SyncClinics", 4, 0, "The ordinary sync clinics. There are usually six or seven in a city and they all share one stock list, so they will show the same disks as each other.");
			Add("BlackmarketSyncClinic", "BlackMarketClinic", 6, 0, "The black market doctor. Stocks independently of the ordinary clinics.");
			Add("BlackmarketTrader", "BlackMarketTrader", 3, 0, "The black market trader. Vanilla sells almost no disks here.");
			Add("WeaponsDealer", "WeaponsDealer", 3, 0, "The arms dealer. Vanilla sells no disks here at all, so this gives a reason to visit in person.");
			Add("Newsstand", "Newsstands", 2, 0, "Street newsstands. Vanilla draws one random CandorNews disk.");
			Add("NewspaperBox", "NewspaperBoxes", 2, 0, "Coin-operated newspaper boxes. Vanilla sells no disks here.");
			Harmony.CreateAndPatchAll(typeof(StockPatch), (string)null);
			Harmony.CreateAndPatchAll(typeof(DedupePatch), (string)null);
			Harmony.CreateAndPatchAll(typeof(LoadAllPatch), (string)null);
			((BasePlugin)this).AddComponent<DayWatcher>();
			L.LogInfo((object)("=== SoD Disk Availability ready, mode=" + Mode.Value + " ==="));
		}

		private void Add(string preset, string section, int count, int slots, string blurb)
		{
			Pool pool = new Pool
			{
				Preset = preset,
				Enabled = Cfg.Bind<bool>(section, "Enabled", true, blurb + " Set false to leave this vendor exactly as the game left it."),
				Count = Cfg.Bind<int>(section, "DisksInStock", count, "How many disks this vendor offers at once in Rotation mode. 0 for none."),
				Slots = Cfg.Bind<int>(section, "ExtraRandomSlots", slots, "Additional disks the game draws at random on top of the list above, filtered by the manufacturers below. Vanilla uses 1 for sync clinics."),
				Manufacturers = Cfg.Bind<string>(section, "RestrictToManufacturers", "", "Comma separated. Blank means draw from every disk in the game (ordinary shops leave out BlackMarket disks unless it is named). Valid names: ElGen, Kaizen, KensingtonIndigo, StarchKola, CandorNews, BlackMarket.")
			};
			pool.Legal = !preset.StartsWith("Blackmarket") && preset != "WeaponsDealer";
			Pools.Add(pool);
		}
	}
	internal class Pool
	{
		public string Preset;

		public ConfigEntry<bool> Enabled;

		public ConfigEntry<int> Count;

		public ConfigEntry<int> Slots;

		public ConfigEntry<string> Manufacturers;

		public bool Legal;
	}
	[HarmonyPatch(typeof(Toolbox), "Start")]
	public static class StockPatch
	{
		private static bool captured;

		[HarmonyPriority(0)]
		private static void Postfix()
		{
			if (captured)
			{
				return;
			}
			captured = true;
			try
			{
				Stock.ApplyMinimumPrice();
				Stock.CaptureOriginals();
				Stock.Apply(force: true);
			}
			catch (Exception ex)
			{
				Plugin.L.LogError((object)("initial stock pass failed: " + ex));
			}
		}
	}
	[HarmonyPatch(typeof(Toolbox), "LoadAll")]
	public static class LoadAllPatch
	{
		[HarmonyPriority(0)]
		private static void Postfix()
		{
			try
			{
				Stock.ApplyMinimumPrice();
				Stock.Apply(force: true);
			}
			catch
			{
			}
		}
	}
	public class DayWatcher : MonoBehaviour
	{
		private float _next;

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

		private void Update()
		{
			if (Time.time < _next)
			{
				return;
			}
			_next = Time.time + 1f;
			try
			{
				Stock.Apply(force: false);
			}
			catch
			{
			}
		}
	}
	internal static class Stock
	{
		private static readonly List<SyncDiskPreset> MasterPool = new List<SyncDiskPreset>();

		private static readonly Dictionary<string, List<SyncDiskPreset>> Originals = new Dictionary<string, List<SyncDiskPreset>>();

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

		private static int _lastDay = int.MinValue;

		private static string _lastMode = null;

		private static bool _warnedNoPool = false;

		public static void ApplyMinimumPrice()
		{
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Expected O, but got Unknown
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Expected O, but got Unknown
			int value = Plugin.MinPrice.Value;
			Toolbox instance = Toolbox.Instance;
			List<SyncDiskPreset> val = ((instance != null) ? instance.allSyncDisks : null);
			if (value <= 0 || val == null)
			{
				return;
			}
			int num = 0;
			bool flag = default(bool);
			for (int i = 0; i < val.Count; i++)
			{
				SyncDiskPreset val2 = val[i];
				if ((SoCustomComparison)(object)val2 == (SoCustomComparison)null || val2.price >= value)
				{
					continue;
				}
				if (val2.price > 0)
				{
					ManualLogSource l = Plugin.L;
					BepInExInfoLogInterpolatedStringHandler val3 = new BepInExInfoLogInterpolatedStringHandler(14, 3, ref flag);
					if (flag)
					{
						((BepInExLogInterpolatedStringHandler)val3).AppendLiteral("[PRICE] ");
						((BepInExLogInterpolatedStringHandler)val3).AppendFormatted<string>(((Object)val2).name);
						((BepInExLogInterpolatedStringHandler)val3).AppendLiteral(": ");
						((BepInExLogInterpolatedStringHandler)val3).AppendFormatted<int>(val2.price);
						((BepInExLogInterpolatedStringHandler)val3).AppendLiteral(" -> ");
						((BepInExLogInterpolatedStringHandler)val3).AppendFormatted<int>(value);
					}
					l.LogInfo(val3);
				}
				val2.price = value;
				num++;
			}
			if (num > 0)
			{
				ManualLogSource l2 = Plugin.L;
				BepInExInfoLogInterpolatedStringHandler val3 = new BepInExInfoLogInterpolatedStringHandler(40, 2, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val3).AppendLiteral("[PRICE] raised ");
					((BepInExLogInterpolatedStringHandler)val3).AppendFormatted<int>(num);
					((BepInExLogInterpolatedStringHandler)val3).AppendLiteral(" disks to the minimum of ");
					((BepInExLogInterpolatedStringHandler)val3).AppendFormatted<int>(value);
				}
				l2.LogInfo(val3);
			}
		}

		private static bool IsRetired(SyncDiskPreset p)
		{
			string text = ((Object)p).name ?? "";
			string[] array = (Plugin.Retired.Value ?? "").Split(',');
			for (int i = 0; i < array.Length; i++)
			{
				string text2 = array[i].Trim();
				if (text2.Length > 0 && text.EndsWith("_" + text2, StringComparison.OrdinalIgnoreCase))
				{
					return true;
				}
			}
			return false;
		}

		public static void CaptureOriginals()
		{
			MasterPool.Clear();
			Originals.Clear();
			OriginalSlots.Clear();
			List<SyncDiskPreset> allSyncDisks = Toolbox.Instance.allSyncDisks;
			if (allSyncDisks == null)
			{
				Plugin.L.LogError((object)"Toolbox.allSyncDisks null - availability disabled this session");
				return;
			}
			for (int i = 0; i < allSyncDisks.Count; i++)
			{
				SyncDiskPreset val = allSyncDisks[i];
				if (!((SoCustomComparison)(object)val == (SoCustomComparison)null) && !val.disabled && !IsRetired(val))
				{
					MasterPool.Add(val);
				}
			}
			Il2CppArrayBase<MenuPreset> val2 = Resources.FindObjectsOfTypeAll<MenuPreset>();
			if (val2 != null)
			{
				foreach (MenuPreset item in val2)
				{
					if ((SoCustomComparison)(object)item == (SoCustomComparison)null || ((Object)item).name == null || Originals.ContainsKey(((Object)item).name))
					{
						continue;
					}
					List<SyncDiskPreset> list = new List<SyncDiskPreset>();
					if (item.syncDisks != null)
					{
						for (int j = 0; j < item.syncDisks.Count; j++)
						{
							SyncDiskPreset val3 = item.syncDisks[j];
							if ((SoCustomComparison)(object)val3 != (SoCustomComparison)null)
							{
								list.Add(val3);
							}
						}
					}
					Originals[((Object)item).name] = list;
					OriginalSlots[((Object)item).name] = item.syncDiskSlots;
				}
			}
			Plugin.L.LogInfo((object)("=== captured " + MasterPool.Count + " disks in the master pool, " + Originals.Count + " menu presets snapshotted ==="));
		}

		private static int CurrentDay()
		{
			try
			{
				return SessionData.Instance.dayInt;
			}
			catch
			{
				return 0;
			}
		}

		public static void Apply(bool force)
		{
			if (MasterPool.Count == 0)
			{
				if (!_warnedNoPool)
				{
					_warnedNoPool = true;
					Plugin.L.LogInfo((object)"[STOCK] apply skipped, master pool not captured yet");
				}
				return;
			}
			string text = (Plugin.Mode.Value ?? "Rotation").Trim();
			int num = CurrentDay();
			if (!force && num == _lastDay && text == _lastMode)
			{
				return;
			}
			_lastDay = num;
			_lastMode = text;
			bool flag = text.Equals("Unrestricted", StringComparison.OrdinalIgnoreCase);
			bool flag2 = text.Equals("Vanilla", StringComparison.OrdinalIgnoreCase);
			foreach (Pool pool in Plugin.Pools)
			{
				if (!pool.Enabled.Value)
				{
					continue;
				}
				Il2CppArrayBase<MenuPreset> val = Resources.FindObjectsOfTypeAll<MenuPreset>();
				if (val == null)
				{
					continue;
				}
				foreach (MenuPreset item in val)
				{
					if ((SoCustomComparison)(object)item == (SoCustomComparison)null || ((Object)item).name != pool.Preset || item.syncDisks == null)
					{
						continue;
					}
					if (flag2)
					{
						WriteList(item, Originals.ContainsKey(((Object)item).name) ? Originals[((Object)item).name] : new List<SyncDiskPreset>());
						if (OriginalSlots.ContainsKey(((Object)item).name))
						{
							item.syncDiskSlots = OriginalSlots[((Object)item).name];
						}
						continue;
					}
					if (flag)
					{
						WriteList(item, MasterPool);
						item.syncDiskSlots = pool.Slots.Value;
						continue;
					}
					List<SyncDiskPreset> list = Filter(pool.Manufacturers.Value);
					if (pool.Legal && string.IsNullOrWhiteSpace(pool.Manufacturers.Value))
					{
						list = NoBlackMarket(list);
					}
					List<SyncDiskPreset> list2 = Pick(list, pool.Count.Value, num, pool.Preset);
					WriteList(item, list2);
					item.syncDiskSlots = pool.Slots.Value;
					if (!Plugin.LogStock.Value)
					{
						continue;
					}
					StringBuilder stringBuilder = new StringBuilder();
					foreach (SyncDiskPreset item2 in list2)
					{
						if (stringBuilder.Length > 0)
						{
							stringBuilder.Append(", ");
						}
						stringBuilder.Append(Short(((Object)item2).name));
					}
					Plugin.L.LogInfo((object)("[STOCK] day " + num + " " + pool.Preset + " (" + list2.Count + "): " + stringBuilder));
				}
			}
		}

		private static string Short(string n)
		{
			if (string.IsNullOrEmpty(n))
			{
				return "?";
			}
			int num = n.LastIndexOf('_');
			if (num < 0 || num >= n.Length - 1)
			{
				return n;
			}
			return n.Substring(num + 1);
		}

		private static void WriteList(MenuPreset m, List<SyncDiskPreset> items)
		{
			m.syncDisks.Clear();
			foreach (SyncDiskPreset item in items)
			{
				if ((SoCustomComparison)(object)item != (SoCustomComparison)null)
				{
					m.syncDisks.Add(item);
				}
			}
		}

		private static List<SyncDiskPreset> NoBlackMarket(List<SyncDiskPreset> from)
		{
			//IL_0018: 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)
			List<SyncDiskPreset> list = new List<SyncDiskPreset>();
			foreach (SyncDiskPreset item in from)
			{
				string a;
				try
				{
					a = ((object)item.manufacturer/*cast due to .constrained prefix*/).ToString();
				}
				catch
				{
					continue;
				}
				if (!string.Equals(a, "BlackMarket", StringComparison.OrdinalIgnoreCase))
				{
					list.Add(item);
				}
			}
			if (list.Count <= 0)
			{
				return from;
			}
			return list;
		}

		private static List<SyncDiskPreset> Filter(string csv)
		{
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			if (string.IsNullOrEmpty(csv) || csv.Trim().Length == 0)
			{
				return MasterPool;
			}
			List<string> list = new List<string>();
			string[] array = csv.Split(',');
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i].Trim();
				if (text.Length > 0)
				{
					list.Add(text);
				}
			}
			if (list.Count == 0)
			{
				return MasterPool;
			}
			List<SyncDiskPreset> list2 = new List<SyncDiskPreset>();
			foreach (SyncDiskPreset item in MasterPool)
			{
				string a;
				try
				{
					a = ((object)item.manufacturer/*cast due to .constrained prefix*/).ToString();
				}
				catch
				{
					continue;
				}
				foreach (string item2 in list)
				{
					if (string.Equals(a, item2, StringComparison.OrdinalIgnoreCase))
					{
						list2.Add(item);
						break;
					}
				}
			}
			if (list2.Count <= 0)
			{
				return MasterPool;
			}
			return list2;
		}

		private static List<SyncDiskPreset> Pick(List<SyncDiskPreset> from, int count, int day, string vendor)
		{
			List<SyncDiskPreset> list = new List<SyncDiskPreset>();
			if (count <= 0 || from.Count == 0)
			{
				return list;
			}
			if (count >= from.Count)
			{
				list.AddRange(from);
				return list;
			}
			uint num = (uint)(day * 2654435761u);
			num ^= (uint)(Plugin.Salt.Value * 40503);
			foreach (char c in vendor)
			{
				num = num * 31 + c;
			}
			if (num == 0)
			{
				num = 2463534242u;
			}
			List<int> list2 = new List<int>();
			for (int j = 0; j < from.Count; j++)
			{
				list2.Add(j);
			}
			for (int num2 = list2.Count - 1; num2 > 0; num2--)
			{
				num ^= num << 13;
				num ^= num >> 17;
				num ^= num << 5;
				int index = (int)(num % (uint)(num2 + 1));
				int value = list2[num2];
				list2[num2] = list2[index];
				list2[index] = value;
			}
			for (int k = 0; k < count; k++)
			{
				list.Add(from[list2[k]]);
			}
			return list;
		}
	}
}