Decompiled source of Trench Rat Reggie Modded v0.9.2

plugins/Reggie/quaternion.reggie.modded.dll

Decompiled a month ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using BepInEx;
using FistVR;
using FistVR.Ugc;
using HarmonyLib;
using UnityEngine;
using Valve.Newtonsoft.Json;
using Valve.Newtonsoft.Json.Linq;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyCompany("quaternion.reggie.modded")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.9.2.0")]
[assembly: AssemblyInformationalVersion("0.9.2")]
[assembly: AssemblyProduct("quaternion.reggie.modded")]
[assembly: AssemblyTitle("quaternion.reggie.modded")]
[assembly: AssemblyVersion("0.9.2.0")]
[BepInPlugin("quaternion.reggie.modded", "Trench Rat Reggie Modded", "0.9.2")]
public sealed class ReggieModded : BaseUnityPlugin
{
	public sealed class ReggieBox : MonoBehaviour
	{
	}

	public sealed class ReggieSpawn : MonoBehaviour
	{
		private IEnumerator Start()
		{
			yield return null;
			NormalizeSpawn(((Component)this).gameObject, afterStart: true);
		}
	}

	private sealed class AmmoInfo
	{
		public string id;

		public string error;

		public FireArmRoundType roundType;

		public FireArmRoundClass roundClass;

		public FVRObject round;

		public FVRFireArm prefab;

		public bool external;

		public List<FVRObject> supplies = new List<FVRObject>();

		public object Report()
		{
			return new
			{
				id = id,
				error = error,
				roundType = ((object)Unsafe.As<FireArmRoundType, FireArmRoundType>(ref roundType)/*cast due to .constrained prefix*/).ToString(),
				ordinaryClass = ((object)Unsafe.As<FireArmRoundClass, FireArmRoundClass>(ref roundClass)/*cast due to .constrained prefix*/).ToString(),
				round = (((Object)(object)round == (Object)null) ? null : round.ItemID),
				external = external,
				supplies = supplies.Select((FVRObject s) => s.ItemID).ToArray()
			};
		}
	}

	[HarmonyPatch]
	private static class RewardScope
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			yield return AccessTools.Method(typeof(TNH_Manager), "SpawnSosigLoot", (Type[])null, (Type[])null);
			yield return AccessTools.Method(typeof(TNH_Manager), "SpawnBoxLoot", (Type[])null, (Type[])null);
		}

		private static void Postfix(TNH_Manager __instance, ref IEnumerator __result)
		{
			if (IsReggie(__instance))
			{
				__result = Scoped(__result);
			}
		}
	}

	[HarmonyPatch(typeof(TNH_SupplyPoint), "SpawnLoadoutEntry")]
	private static class StarterScope
	{
		private static void Postfix(TNH_CharacterDef __1, ref IEnumerator __result)
		{
			if ((Object)(object)__1 != (Object)null && __1.UgcId == "quaternion.reggiemodded:WW1_Looter")
			{
				__result = Scoped(__result);
			}
		}
	}

	[HarmonyPatch(typeof(LootBox), "ConfigureFromID")]
	private static class MarkBox
	{
		private static void Postfix(LootBox __instance)
		{
			if (spawnDepth > 0 && (Object)(object)((Component)__instance).GetComponent<ReggieBox>() == (Object)null)
			{
				((Component)__instance).gameObject.AddComponent<ReggieBox>();
			}
		}
	}

	[HarmonyPatch(typeof(LootBox), "OpenBox")]
	private static class OpenBoxScope
	{
		private static void Prefix(LootBox __instance, out bool __state)
		{
			__state = (Object)(object)((Component)__instance).GetComponent<ReggieBox>() != (Object)null;
			if (__state)
			{
				spawnDepth++;
			}
		}

		private static Exception Finalizer(Exception __exception, bool __state)
		{
			if (__state)
			{
				spawnDepth--;
			}
			return __exception;
		}
	}

	[HarmonyPatch(typeof(TNH_Manager), "GetSeededRandomAmmoObject")]
	private static class StarterAmmo
	{
		private static bool Prefix(TNH_Manager __instance, FVRObject __0, int __1, int __2, ref FVRObject __result)
		{
			if (!IsReggie(__instance))
			{
				return true;
			}
			__result = Supply(__0, __1, __2, (int n) => __instance.GetRandomAmmoObjectIndexNext(__0, n));
			return false;
		}
	}

	[HarmonyPatch(typeof(TNH_Manager), "CanGetSmartLoot")]
	private static class CanSupply
	{
		private static bool Prefix(TNH_Manager __instance, ref bool __result)
		{
			if (!IsReggie(__instance))
			{
				return true;
			}
			__result = KnownGuns(__instance).Any((FVRObject o) => ResolveAmmo(o).error == null);
			return false;
		}
	}

	[HarmonyPatch(typeof(TNH_Manager), "GetSmartAmmoObject")]
	private static class SmartSupply
	{
		private static bool Prefix(TNH_Manager __instance, SosigLootGroup __0, ref FVRObject __result)
		{
			if (!IsReggie(__instance))
			{
				return true;
			}
			List<FVRObject> list = (from o in KnownGuns(__instance)
				where ResolveAmmo(o).error == null
				select o).ToList();
			__result = ((list.Count == 0) ? null : Supply(list[Random.Range(0, list.Count)], (int)__0.MinMaxCapacity.x, (int)__0.MinMaxCapacity.y, (int n) => Random.Range(0, n)));
			return false;
		}
	}

	[HarmonyPatch(typeof(TNH_Utilities), "GetValidRandomRoundClass")]
	private static class OrdinaryLoot
	{
		private unsafe static bool Prefix(FireArmRoundType __0, ref FireArmRoundClass __result)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			if (!AmmoContext)
			{
				return true;
			}
			if (!OrdinaryRound(__0, out var _, out __result))
			{
				throw new InvalidOperationException("Reggie: no ordinary round for " + ((object)(*(FireArmRoundType*)(&__0))/*cast due to .constrained prefix*/).ToString());
			}
			return false;
		}
	}

	[HarmonyPatch(typeof(TNH_Utilities), "GenerateProcessedLootObject")]
	private static class ProcessWeapon
	{
		private static bool Prefix(string __0, Vector3 __1, Quaternion __2, Vector2 __3, ref GameObject __result)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Invalid comparison between Unknown and I4
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_011c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_014e: Unknown result type (might be due to invalid IL or missing references)
			if (spawnDepth == 0)
			{
				return true;
			}
			if (!IM.OD.TryGetValue(__0, out var value))
			{
				return true;
			}
			if (!ammoCache.TryGetValue(__0, out var value2))
			{
				if ((int)value.Category != 1)
				{
					return true;
				}
				value2 = ResolveAmmo(value);
			}
			if (value2.error != null)
			{
				throw new InvalidOperationException("Reggie cannot spawn " + __0 + ": " + value2.error);
			}
			GameObject val = Object.Instantiate<GameObject>(((Component)value2.prefab).gameObject, __1, __2);
			try
			{
				FVRFireArm component = val.GetComponent<FVRFireArm>();
				((FVRPhysicalObject)component).ObjectWrapper = value;
				int num = Random.Range((int)__3.x, (int)__3.y + 1);
				if (value2.external && (Object)(object)component.Magazine == (Object)null)
				{
					Object.Instantiate<GameObject>(((AnvilAsset)value2.supplies.OrderBy((FVRObject o) => o.MagazineCapacity).First()).GetGameObject(), component.MagazineMountPos.position, component.MagazineMountPos.rotation).GetComponent<FVRFireArmMagazine>().Load(component);
				}
				if ((Object)(object)component.Magazine != (Object)null)
				{
					component.Magazine.ReloadMagWithTypeUpToAmount(value2.roundClass, num);
				}
				else
				{
					foreach (FVRFireArmChamber item in component.GetChambers().Take(Math.Max(0, num)))
					{
						item.Autochamber(value2.roundClass);
					}
				}
				NormalizeSpawn(val);
				__result = val;
				return false;
			}
			catch
			{
				Object.Destroy((Object)(object)val);
				throw;
			}
		}
	}

	[HarmonyPatch(typeof(TNH_Manager), "AddObjectToTrackedList")]
	private static class NormalizeNewObject
	{
		private static void Prefix(GameObject __0)
		{
			if (spawnDepth > 0)
			{
				NormalizeSpawn(__0);
			}
		}
	}

	[HarmonyPatch(typeof(SosigWeaponPlayerInterface), "TryToConvertIntoRelevantAmmoObject")]
	private static class ConversionScope
	{
		private static void Prefix(out bool __state)
		{
			__state = IsReggie(GM.TNH_Manager);
			if (__state)
			{
				conversionDepth++;
			}
		}

		private static Exception Finalizer(Exception __exception, bool __state)
		{
			if (__state)
			{
				conversionDepth--;
			}
			return __exception;
		}
	}

	[HarmonyPatch(typeof(AM), "GetDefaultRoundClass")]
	private static class ConversionClass
	{
		private static bool Prefix(FireArmRoundType __0, ref FireArmRoundClass __result)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			FVRObject round;
			if (conversionDepth != 0)
			{
				return !OrdinaryRound(__0, out round, out __result);
			}
			return true;
		}
	}

	[HarmonyPatch(typeof(FVRObject), "GetFirstCompatibleRound")]
	private static class OrdinaryCartridge
	{
		private static bool Prefix(FVRObject __0, ref FVRObject __result)
		{
			if (!AmmoContext)
			{
				return true;
			}
			AmmoInfo ammoInfo = ResolveAmmo(__0);
			__result = ammoInfo.round;
			return false;
		}
	}

	[HarmonyPatch(typeof(FVRObject), "GetRandomAmmoObject")]
	private static class ConversionSupply
	{
		private static bool Prefix(FVRObject __0, int __2, int __3, ref FVRObject __result)
		{
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Invalid comparison between Unknown and I4
			if (conversionDepth == 0)
			{
				return true;
			}
			AmmoInfo ammoInfo = ResolveAmmo(__0);
			__result = ((ammoInfo.error == null) ? Supply(__0, __2, __3, (int n) => Random.Range(0, n)) : ammoInfo.round);
			if ((Object)(object)__result != (Object)null && (int)__result.Category != 2)
			{
				__result = ammoInfo.round;
			}
			return false;
		}
	}

	public sealed class Catalog
	{
		public string version;

		public List<Candidate> items;

		public List<Candidate> excluded;
	}

	public sealed class Candidate
	{
		public string id;

		public string pool;

		public string package;

		public string name;

		public List<string> requiredPeerIds;
	}

	[HarmonyPatch(typeof(UgcManager), "DiscoverFromLocalStudio")]
	private static class Discover
	{
		private static void Postfix(List<IUgcItem> __0)
		{
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Expected O, but got Unknown
			Dictionary<string, UgcModule> dictionary = (Dictionary<string, UgcModule>)AccessTools.Field(typeof(UgcManager), "_contentModulesById").GetValue(null);
			if (dictionary.ContainsKey("quaternion.reggiemodded"))
			{
				((BaseUnityPlugin)instance).Logger.LogWarning((object)"Another copy of the modded module was found; using that copy without registering a duplicate.");
				return;
			}
			string text = Path.Combine(Path.Combine(directory, "studio"), "quaternion.reggiemodded");
			JObject val = JObject.Parse(File.ReadAllText(Path.Combine(text, "module.json")));
			if ((string)val["Id"] != "quaternion.reggiemodded")
			{
				throw new InvalidOperationException("Incorrect bundled module ID.");
			}
			MethodInfo obj = AccessTools.Method(typeof(UgcManager), "DiscoverFromDirectoryRecurse", new Type[5]
			{
				typeof(List<IUgcItem>),
				typeof(UgcModule),
				typeof(string),
				typeof(string),
				typeof(bool)
			}, (Type[])null) ?? throw new MissingMethodException("Unsupported H3VR native discovery API.");
			UgcModule val2 = new UgcModule("quaternion.reggiemodded", (string)val["Name"]);
			dictionary.Add("quaternion.reggiemodded", val2);
			obj.Invoke(null, new object[5] { __0, val2, text, text, false });
			((BaseUnityPlugin)instance).Logger.LogInfo((object)"Registered standalone native modded character from this profile.");
		}
	}

	[HarmonyPatch(typeof(TNH_LevelLoader), "ValidateCharacter")]
	private static class Validate
	{
		private static void Prefix(TNH_CharacterDef __0)
		{
			Prepare(__0);
		}
	}

	[HarmonyPatch(typeof(TNH_Manager), "InitTables")]
	private static class Initialize
	{
		private static void Prefix(TNH_Manager __instance)
		{
			Prepare(__instance.C);
		}
	}

	[HarmonyPatch(typeof(TNH_Manager), "SpawnWeaponCase")]
	private static class MarkStarterCase
	{
		private static void Postfix(TNH_Manager __instance, GameObject __result)
		{
			if (IsReggie(__instance) && !((Object)(object)__result == (Object)null))
			{
				__result.AddComponent<ReggieBox>();
				__result.GetComponent<TNH_WeaponCrate>().M = __instance;
			}
		}
	}

	[HarmonyPatch(typeof(TNH_WeaponCrate), "SpawnItemsInCrate")]
	private static class OpenStarterCase
	{
		private static void Postfix(TNH_WeaponCrate __instance, ref IEnumerator __result)
		{
			if ((Object)(object)((Component)__instance).GetComponent<ReggieBox>() != (Object)null)
			{
				__result = Scoped(SpawnStarterContents(__instance));
			}
		}
	}

	[HarmonyPatch(typeof(TNH_Manager), "SpawnEnemy")]
	private static class EnemyUniform
	{
		private static void Postfix(TNH_Manager __instance, SosigEnemyTemplate __0, Sosig __result)
		{
			if (IsReggie(__instance) && (Object)(object)__0 != (Object)null && __0.UgcId.StartsWith("quaternion.reggiemodded:") && (Object)(object)__result != (Object)null)
			{
				LightenUniform(((Component)__result).gameObject);
			}
		}
	}

	[HarmonyPatch(typeof(TNH_CharacterDisplay), "OnLoadComplete")]
	private static class MenuUniform
	{
		private static void Postfix(TNH_CharacterDisplay __instance, List<FVRPhysicalObject> __0)
		{
			TNH_CharacterDef val = default(TNH_CharacterDef);
			if (__0 == null || !UgcManager.TryGetItem<TNH_CharacterDef>("quaternion.reggiemodded:WW1_Looter", ref val) || (Object)(object)val.DisplayCharacter == (Object)null || AccessTools.Field(typeof(TNH_CharacterDisplay), "_displayedFile").GetValue(__instance) != val.DisplayCharacter.vf)
			{
				return;
			}
			foreach (FVRPhysicalObject item in __0)
			{
				if ((Object)(object)item != (Object)null)
				{
					LightenUniform(((Component)item).gameObject);
				}
			}
		}
	}

	[ThreadStatic]
	private static int spawnDepth;

	[ThreadStatic]
	private static int conversionDepth;

	private static readonly Dictionary<string, AmmoInfo> ammoCache = new Dictionary<string, AmmoInfo>();

	private static int indexedCount = -1;

	private static int indexedRounds = -1;

	private static List<FVRObject> loaders = new List<FVRObject>();

	private static HashSet<string> excludedAmmo = new HashSet<string>();

	private static readonly HashSet<string> ordinarySP = new HashSet<string>(new string[12]
	{
		"106x25mmRCartridgeSP", "45LongColtCartridgeSP", "577_450_MartiniHenry", "44Russian", "8mmOrdnance", "107558mmR", "115x60mmr", "8mmGasser", "9x20mm.Billiam", "a113x36mmR",
		"primevr.11mm.delvigne", "potatoes11x58mmRSP"
	});

	private static readonly Dictionary<string, string[]> bayonetRecipes = new Dictionary<string, string[]>
	{
		{
			"MosinM1891",
			new string[1] { "BayonetMosin" }
		},
		{
			"Springfield1903",
			new string[1] { "Ult_M1905_Bayonet" }
		},
		{
			"EnfieldP14.Muzz",
			new string[1] { "EnfieldP14Bayonet.Muzz" }
		},
		{
			"USM1917.Muzz",
			new string[1] { "EnfieldP14Bayonet.Muzz" }
		},
		{
			"Ult_Ross_Rifle_MK3",
			new string[1] { "Ult_M1908_Bayonet" }
		},
		{
			"Werndl.Billiam",
			new string[1] { "WerndlBayo.Billiam" }
		},
		{
			"RifBALeeEnfieldMkIII",
			new string[2] { "P1907", "BayonetLeeEnfield" }
		},
		{
			"Volks_CLLEEnfield_Rifle",
			new string[2] { "P1907", "BayonetLeeEnfield" }
		},
		{
			"Volks_MLE_Rifle",
			new string[2] { "P1907", "BayonetLeeEnfield" }
		},
		{
			"Volks_Win1895_3006",
			new string[1] { "Volks_Win1895_Bayonet" }
		},
		{
			"Volks_Win1895_762R",
			new string[1] { "Volks_Win1895_Bayonet" }
		},
		{
			"Ult_M1903",
			new string[1] { "Ult_M1905_Bayonet" }
		}
	};

	private static readonly Dictionary<string, string[]> scopeRecipes = new Dictionary<string, string[]>
	{
		{
			"Volks_Carcano1981Rifle",
			new string[1] { "Volks_Carcano1981Scope" }
		},
		{
			"Ult_Gew98 Sniper",
			new string[1] { "Ult_G98_Scope" }
		}
	};

	public const string Module = "quaternion.reggiemodded";

	public const string Character = "quaternion.reggiemodded:WW1_Looter";

	private static ReggieModded instance;

	private static string directory;

	private static Catalog catalog;

	private static readonly Dictionary<ObjectTableDef, List<string>> originals = new Dictionary<ObjectTableDef, List<string>>();

	private static readonly Dictionary<TNH_LootPoolDef, List<PoolEntry>> originalPools = new Dictionary<TNH_LootPoolDef, List<PoolEntry>>();

	private static ObjectTableDef equipment;

	private static readonly Dictionary<TNH_CharacterDef, List<OTagEra>> originalEras = new Dictionary<TNH_CharacterDef, List<OTagEra>>();

	private static readonly Dictionary<TNH_CharacterDef, List<OTagSet>> originalSets = new Dictionary<TNH_CharacterDef, List<OTagSet>>();

	private static string lastReport;

	private static bool AmmoContext
	{
		get
		{
			if (spawnDepth <= 0)
			{
				return conversionDepth > 0;
			}
			return true;
		}
	}

	private static bool IsReggie(TNH_Manager m)
	{
		if ((Object)(object)m != (Object)null && (Object)(object)m.C != (Object)null)
		{
			return m.C.UgcId == "quaternion.reggiemodded:WW1_Looter";
		}
		return false;
	}

	private static void IndexAmmo()
	{
		if (indexedCount != IM.OD.Count || indexedRounds != AM.SRoundDisplayDataDic.Count)
		{
			indexedCount = IM.OD.Count;
			indexedRounds = AM.SRoundDisplayDataDic.Count;
			ammoCache.Clear();
			excludedAmmo = new HashSet<string>((catalog.excluded ?? new List<Candidate>()).Select((Candidate c) => c.id));
			HashSet<string> declaredAmmo = new HashSet<string>(from c in catalog.items
				where c.pool == "Ammo"
				select c.id);
			loaders = IM.OD.Values.Where((FVRObject o) => (Object)(object)o != (Object)null && !excludedAmmo.Contains(o.ItemID) && ((int)o.Category == 2 || (int)o.Category == 3 || (int)o.Category == 6 || declaredAmmo.Contains(o.ItemID))).OrderBy<FVRObject, string>((FVRObject o) => o.ItemID, StringComparer.Ordinal).ToList();
		}
	}

	private static bool OrdinaryRound(FireArmRoundType type, out FVRObject round, out FireArmRoundClass cls)
	{
		//IL_000b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0057: Unknown result type (might be due to invalid IL or missing references)
		//IL_006e: 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_00d7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00dd: Invalid comparison between Unknown and I4
		//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a9: Expected I4, but got Unknown
		//IL_0112: Unknown result type (might be due to invalid IL or missing references)
		//IL_0118: Expected I4, but got Unknown
		round = null;
		cls = (FireArmRoundClass)1;
		if (!AM.SRoundDisplayDataDic.TryGetValue(type, out var value) || (Object)(object)value == (Object)null || value.Classes == null)
		{
			return false;
		}
		FireArmRoundClass[] array = (FireArmRoundClass[])(object)new FireArmRoundClass[8]
		{
			(FireArmRoundClass)1,
			default(FireArmRoundClass),
			(FireArmRoundClass)8,
			(FireArmRoundClass)13,
			(FireArmRoundClass)14,
			(FireArmRoundClass)27,
			(FireArmRoundClass)18,
			(FireArmRoundClass)19
		};
		DisplayDataClass[] classes;
		foreach (FireArmRoundClass val in array)
		{
			classes = value.Classes;
			foreach (DisplayDataClass val2 in classes)
			{
				if (val2.Class == val && (Object)(object)val2.ObjectID != (Object)null && !excludedAmmo.Contains(val2.ObjectID.ItemID))
				{
					round = val2.ObjectID;
					cls = (FireArmRoundClass)(int)val;
					return true;
				}
			}
		}
		classes = value.Classes;
		foreach (DisplayDataClass val3 in classes)
		{
			if ((int)val3.Class == 3 && (Object)(object)val3.ObjectID != (Object)null && ordinarySP.Contains(val3.ObjectID.ItemID))
			{
				round = val3.ObjectID;
				cls = (FireArmRoundClass)(int)val3.Class;
				return true;
			}
		}
		return false;
	}

	private static AmmoInfo ResolveAmmo(FVRObject item)
	{
		//IL_008d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0092: Unknown result type (might be due to invalid IL or missing references)
		//IL_009d: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
		//IL_0300: Unknown result type (might be due to invalid IL or missing references)
		//IL_0311: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d2: Unknown result type (might be due to invalid IL or missing references)
		//IL_01e2: Unknown result type (might be due to invalid IL or missing references)
		//IL_031a: Unknown result type (might be due to invalid IL or missing references)
		//IL_032b: Unknown result type (might be due to invalid IL or missing references)
		//IL_01eb: Unknown result type (might be due to invalid IL or missing references)
		//IL_01fb: Unknown result type (might be due to invalid IL or missing references)
		IndexAmmo();
		if (ammoCache.TryGetValue(item.ItemID, out var value))
		{
			return value;
		}
		value = new AmmoInfo
		{
			id = item.ItemID
		};
		try
		{
			value.prefab = ((AnvilAsset)item).GetGameObject().GetComponent<FVRFireArm>();
			if ((Object)(object)value.prefab == (Object)null)
			{
				value.error = "Registered item is not a playable firearm prefab.";
				return value;
			}
			FVRFireArm gun = value.prefab;
			value.roundType = gun.RoundType;
			if (!OrdinaryRound(gun.RoundType, out value.round, out value.roundClass))
			{
				value.error = "No registered ordinary round for actual prefab caliber " + ((object)Unsafe.As<FireArmRoundType, FireArmRoundType>(ref gun.RoundType)/*cast due to .constrained prefix*/).ToString();
				return value;
			}
			value.external = (int)gun.MagazineType != 0 && (gun.UsesMagazines || gun.UsesBeltBoxes) && ((Object)(object)gun.Magazine == (Object)null || !gun.Magazine.IsIntegrated);
			if (value.external)
			{
				List<FVRObject> explicitMags = item.CompatibleMagazines ?? new List<FVRObject>();
				foreach (FVRObject item2 in loaders.Where((FVRObject o) => explicitMags.Contains(o) || ((int)gun.MagazineType != 0 && o.MagazineType == gun.MagazineType) || (item.ItemID == "RifBRRSC" && o.ItemID == "MagRSCClip")))
				{
					FVRFireArmMagazine component = ((AnvilAsset)item2).GetGameObject().GetComponent<FVRFireArmMagazine>();
					if ((Object)(object)component != (Object)null && !component.IsIntegrated && component.RoundType == gun.RoundType && component.MagazineType == gun.MagazineType)
					{
						value.supplies.Add(item2);
					}
				}
				if (value.supplies.Count == 0)
				{
					value.error = "No detachable magazine/en-bloc loader for actual prefab magazine type " + ((object)Unsafe.As<FireArmMagazineType, FireArmMagazineType>(ref gun.MagazineType)/*cast due to .constrained prefix*/).ToString();
				}
			}
			else if (gun.UsesClips)
			{
				List<FVRObject> explicitClips = item.CompatibleClips ?? new List<FVRObject>();
				foreach (FVRObject item3 in loaders.Where((FVRObject o) => explicitClips.Contains(o) || ((int)gun.ClipType != 0 && o.ClipType == gun.ClipType)))
				{
					FVRFireArmClip component2 = ((AnvilAsset)item3).GetGameObject().GetComponent<FVRFireArmClip>();
					if ((Object)(object)component2 != (Object)null && component2.RoundType == gun.RoundType && component2.ClipType == gun.ClipType)
					{
						value.supplies.Add(item3);
					}
				}
				if (value.supplies.Count == 0)
				{
					value.error = "No compatible clip for actual prefab clip type " + ((object)Unsafe.As<FireArmClipType, FireArmClipType>(ref gun.ClipType)/*cast due to .constrained prefix*/).ToString();
				}
			}
			else if (item.CompatibleSpeedLoaders != null)
			{
				foreach (FVRObject compatibleSpeedLoader in item.CompatibleSpeedLoaders)
				{
					if (!((Object)(object)compatibleSpeedLoader == (Object)null) && !excludedAmmo.Contains(compatibleSpeedLoader.ItemID))
					{
						Speedloader component3 = ((AnvilAsset)compatibleSpeedLoader).GetGameObject().GetComponent<Speedloader>();
						if ((Object)(object)component3 != (Object)null && component3.Chambers.Count > 0 && component3.Chambers.All((SpeedloaderChamber c) => c.Type == gun.RoundType))
						{
							value.supplies.Add(compatibleSpeedLoader);
						}
					}
				}
			}
			if (value.supplies.Count == 0 && value.error == null)
			{
				value.supplies.Add(value.round);
			}
		}
		catch (Exception ex)
		{
			value.error = "Prefab/ammo inspection failed: " + ex.GetType().Name + ": " + ex.Message;
		}
		finally
		{
			ammoCache[item.ItemID] = value;
		}
		return value;
	}

	private static FVRObject Supply(FVRObject gun, int min, int max, Func<int, int> random)
	{
		AmmoInfo ammoInfo = ResolveAmmo(gun);
		if (ammoInfo.error != null)
		{
			return null;
		}
		List<FVRObject> list = ammoInfo.supplies.Where((FVRObject o) => (int)o.Category != 2 || ((min < 0 || o.MagazineCapacity >= min) && (max < 0 || o.MagazineCapacity <= max))).ToList();
		if (list.Count == 0)
		{
			list = ammoInfo.supplies;
		}
		return list[random(list.Count)];
	}

	private static IEnumerator Scoped(IEnumerator source)
	{
		try
		{
			while (true)
			{
				object obj = null;
				spawnDepth++;
				bool flag;
				try
				{
					flag = source.MoveNext();
					if (flag)
					{
						obj = source.Current;
					}
				}
				finally
				{
					spawnDepth--;
				}
				if (flag)
				{
					yield return (!(obj is IEnumerator source2)) ? obj : Scoped(source2);
					continue;
				}
				break;
			}
		}
		finally
		{
			if (source is IDisposable disposable)
			{
				disposable.Dispose();
			}
		}
	}

	private static IEnumerable<FVRObject> KnownGuns(TNH_Manager m)
	{
		return from o in (List<FVRPhysicalObject>)AccessTools.Field(typeof(TNH_Manager), "m_knownObjs").GetValue(m)
			where (Object)(object)o != (Object)null && o is FVRFireArm && (Object)(object)o.ObjectWrapper != (Object)null
			select o.ObjectWrapper;
	}

	private static void NormalizeSpawn(GameObject g, bool afterStart = false)
	{
		//IL_003f: 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_007c: 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_00ce: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
		//IL_0119: Unknown result type (might be due to invalid IL or missing references)
		//IL_012b: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)g == (Object)null || (!afterStart && (Object)(object)g.GetComponent<ReggieSpawn>() != (Object)null))
		{
			return;
		}
		if (!afterStart)
		{
			g.AddComponent<ReggieSpawn>();
		}
		FVRFireArmMagazine[] componentsInChildren = g.GetComponentsInChildren<FVRFireArmMagazine>(true);
		foreach (FVRFireArmMagazine val in componentsInChildren)
		{
			if (val.m_numRounds > 0 && OrdinaryRound(val.RoundType, out var _, out var cls))
			{
				val.ReloadMagWithTypeUpToAmount(cls, val.m_numRounds);
			}
		}
		FVRFireArmClip[] componentsInChildren2 = g.GetComponentsInChildren<FVRFireArmClip>(true);
		foreach (FVRFireArmClip val2 in componentsInChildren2)
		{
			if (OrdinaryRound(val2.RoundType, out var _, out var cls2))
			{
				val2.ReloadClipWithTypeUpToAmount(cls2, val2.m_numRounds);
			}
		}
		FVRFireArmChamber[] componentsInChildren3 = g.GetComponentsInChildren<FVRFireArmChamber>(true);
		foreach (FVRFireArmChamber val3 in componentsInChildren3)
		{
			if (val3.IsFull && !val3.IsSpent && OrdinaryRound(val3.RoundType, out var _, out var cls3))
			{
				val3.Autochamber(cls3);
			}
		}
		SpeedloaderChamber[] componentsInChildren4 = g.GetComponentsInChildren<SpeedloaderChamber>(true);
		foreach (SpeedloaderChamber val4 in componentsInChildren4)
		{
			if (val4.IsLoaded && !val4.IsSpent && OrdinaryRound(val4.Type, out var _, out var cls4))
			{
				val4.Load(cls4, false);
			}
		}
		FVRFireArm component = g.GetComponent<FVRFireArm>();
		if ((Object)(object)component != (Object)null)
		{
			AttachBayonet(component);
			AttachScope(component);
		}
	}

	private static void AttachBayonet(FVRFireArm gun)
	{
		if (!((Object)(object)((FVRPhysicalObject)gun).ObjectWrapper == (Object)null) && !((Object)(object)gun.CurrentAttachableMeleeWeapon != (Object)null) && bayonetRecipes.TryGetValue(((FVRPhysicalObject)gun).ObjectWrapper.ItemID, out var value))
		{
			AttachMatchingAttachment(gun, value, "Bayonet");
		}
	}

	private static void AttachScope(FVRFireArm gun)
	{
		if (!((Object)(object)((FVRPhysicalObject)gun).ObjectWrapper == (Object)null) && scopeRecipes.TryGetValue(((FVRPhysicalObject)gun).ObjectWrapper.ItemID, out var value))
		{
			AttachMatchingAttachment(gun, value, "Scope");
		}
	}

	private static void AttachMatchingAttachment(FVRFireArm gun, string[] candidates, string kind)
	{
		//IL_00ba: 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)
		if (((Component)gun).GetComponentsInChildren<FVRFireArmAttachment>(true).Any((FVRFireArmAttachment a) => (Object)(object)a.curMount != (Object)null && (Object)(object)((FVRPhysicalObject)a).ObjectWrapper != (Object)null && candidates.Contains(((FVRPhysicalObject)a).ObjectWrapper.ItemID)))
		{
			return;
		}
		string[] array = candidates;
		foreach (string text in array)
		{
			if (!IM.OD.TryGetValue(text, out var value))
			{
				continue;
			}
			GameObject val = null;
			try
			{
				FVRFireArmAttachment prefab = ((AnvilAsset)value).GetGameObject().GetComponent<FVRFireArmAttachment>();
				if ((Object)(object)prefab == (Object)null)
				{
					continue;
				}
				FVRFireArmAttachmentMount val2 = ((IEnumerable<FVRFireArmAttachmentMount>)((Component)gun).GetComponentsInChildren<FVRFireArmAttachmentMount>(true)).FirstOrDefault((Func<FVRFireArmAttachmentMount, bool>)((FVRFireArmAttachmentMount m) => m.Type == prefab.Type && ((Component)m).gameObject.activeInHierarchy && m.isMountableOn(prefab)));
				if ((Object)(object)val2 == (Object)null)
				{
					continue;
				}
				val = Object.Instantiate<GameObject>(((Component)prefab).gameObject, val2.Point_Front.position, ((Component)val2).transform.rotation);
				FVRFireArmAttachment component = val.GetComponent<FVRFireArmAttachment>();
				((FVRPhysicalObject)component).ObjectWrapper = value;
				component.AttachToMount(val2, false);
				if ((Object)(object)component.curMount != (Object)(object)val2)
				{
					throw new InvalidOperationException("Attachment did not remain on its mount.");
				}
				((BaseUnityPlugin)instance).Logger.LogInfo((object)(kind + " attached: " + ((FVRPhysicalObject)gun).ObjectWrapper.ItemID + " -> " + text));
				break;
			}
			catch (Exception ex)
			{
				if ((Object)(object)val != (Object)null)
				{
					Object.Destroy((Object)(object)val);
				}
				((BaseUnityPlugin)instance).Logger.LogWarning((object)(kind + " skipped for " + ((FVRPhysicalObject)gun).ObjectWrapper.ItemID + ": " + ex.Message));
			}
		}
	}

	private void Awake()
	{
		//IL_005b: Unknown result type (might be due to invalid IL or missing references)
		instance = this;
		directory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
		catalog = JsonConvert.DeserializeObject<Catalog>(File.ReadAllText(Path.Combine(directory, "optional-items.json")));
		if (catalog == null || catalog.items == null)
		{
			throw new InvalidOperationException("Optional item catalog is missing.");
		}
		new Harmony("quaternion.reggie.modded").PatchAll(typeof(ReggieModded).Assembly);
		((BaseUnityPlugin)this).Logger.LogInfo((object)("Optional catalog loaded: " + catalog.items.Count + " exact IDs. Missing item packs are optional."));
	}

	public static void Prepare(TNH_CharacterDef character)
	{
		//IL_014e: Unknown result type (might be due to invalid IL or missing references)
		//IL_015d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0452: Unknown result type (might be due to invalid IL or missing references)
		//IL_0457: Unknown result type (might be due to invalid IL or missing references)
		//IL_0462: Unknown result type (might be due to invalid IL or missing references)
		//IL_0469: Unknown result type (might be due to invalid IL or missing references)
		//IL_0471: Unknown result type (might be due to invalid IL or missing references)
		//IL_0481: Expected O, but got Unknown
		//IL_042f: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)character == (Object)null || character.UgcId != "quaternion.reggiemodded:WW1_Looter")
		{
			return;
		}
		Dictionary<string, List<string>> dictionary = new Dictionary<string, List<string>>(StringComparer.Ordinal);
		List<object> list = new List<object>();
		List<object> list2 = new List<object>();
		if (!originalEras.ContainsKey(character))
		{
			originalEras.Add(character, new List<OTagEra>(character.ValidAmmoEras));
		}
		if (!originalSets.ContainsKey(character))
		{
			originalSets.Add(character, new List<OTagSet>(character.ValidAmmoSets));
		}
		HashSet<OTagEra> hashSet = new HashSet<OTagEra>(originalEras[character]);
		HashSet<OTagSet> hashSet2 = new HashSet<OTagSet>(originalSets[character]);
		foreach (Candidate item in catalog.items)
		{
			if (!IM.OD.TryGetValue(item.id, out var value) || (Object)(object)value == (Object)null || (item.requiredPeerIds != null && !item.requiredPeerIds.Any((string id) => IM.OD.ContainsKey(id))))
			{
				continue;
			}
			if (IsGunPool(item.pool) && !HasAmmo(value, out var reason))
			{
				list2.Add(new { item.id, reason });
				continue;
			}
			hashSet.Add(value.TagEra);
			hashSet2.Add(value.TagSet);
			if (item.pool != "Ammo")
			{
				if (!dictionary.TryGetValue(item.pool, out var value2))
				{
					dictionary.Add(item.pool, value2 = new List<string>());
				}
				if (!value2.Contains(item.id))
				{
					value2.Add(item.id);
				}
			}
			list.Add(new { item.id, item.pool, item.package });
		}
		character.ValidAmmoEras = hashSet.ToList();
		character.ValidAmmoSets = hashSet2.ToList();
		string[] array = new string[6] { "Sidearms", "Rifles", "Shotguns", "Support", "Melee", "Grenades" };
		ObjectTableDef val = default(ObjectTableDef);
		foreach (string text in array)
		{
			if (!UgcManager.TryGetItem<ObjectTableDef>("quaternion.reggiemodded:WW1_" + text, ref val))
			{
				throw new InvalidOperationException("Bundled pool missing: " + text);
			}
			if (!originals.TryGetValue(val, out var value3))
			{
				originals.Add(val, value3 = new List<string>(val.IDOverride));
			}
			List<string> list3 = new List<string>();
			foreach (string item2 in value3)
			{
				if (!IsGunPool(text) || (IM.OD.TryGetValue(item2, out var value4) && HasAmmo(value4, out var _)))
				{
					list3.Add(item2);
				}
				else
				{
					list2.Add(new
					{
						id = item2,
						reason = "Native fallback has no usable ordinary ammunition."
					});
				}
			}
			if (dictionary.TryGetValue(text, out var value5))
			{
				foreach (string item3 in value5)
				{
					if (!list3.Contains(item3))
					{
						list3.Add(item3);
					}
				}
			}
			val.IDOverride = list3;
		}
		TNH_LootPoolDef equipPool = character.SosigLootTable.LootGroup_Boxes.EquipPool;
		if (!originalPools.TryGetValue(equipPool, out var value6))
		{
			originalPools.Add(equipPool, value6 = new List<PoolEntry>(equipPool.Entries));
		}
		equipPool.Entries = new List<PoolEntry>(value6);
		if (dictionary.TryGetValue("Equipment", out var value7) && value7.Count > 0)
		{
			if ((Object)(object)equipment == (Object)null)
			{
				equipment = ScriptableObject.CreateInstance<ObjectTableDef>();
				((Object)equipment).name = "Reggie optional equipment";
				equipment.UgcId = "quaternion.reggiemodded:OptionalEquipment";
				equipment.Category = (ObjectCategory)9;
				equipment.UseIDListOverride = true;
			}
			equipment.IDOverride = value7;
			equipPool.Entries.Add(new PoolEntry
			{
				TableDef = equipment,
				MinLevelAppears = 0,
				MaxLevelAppears = 99,
				Rarity = 1f
			});
		}
		string text2 = JsonConvert.SerializeObject((object)new
		{
			character = "quaternion.reggiemodded:WW1_Looter",
			catalog = catalog.version,
			supportedIds = catalog.items.Count,
			registered = list,
			rejected = list2,
			ammo = ammoCache.Values.Select((AmmoInfo a) => a.Report()).ToArray(),
			pools = dictionary.ToDictionary((KeyValuePair<string, List<string>> p) => p.Key, (KeyValuePair<string, List<string>> p) => p.Value.Count)
		}, (Formatting)1);
		if (text2 != lastReport)
		{
			((BaseUnityPlugin)instance).Logger.LogInfo((object)("Reggie optional items: " + list.Count + " registered; " + list2.Count + " missing ammo. Vanilla fallbacks retained."));
			try
			{
				File.WriteAllText(Path.Combine(Paths.ConfigPath, "quaternion.reggie.modded.audit.json"), text2);
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)instance).Logger.LogWarning((object)("Could not write item report: " + ex.Message));
			}
			lastReport = text2;
		}
	}

	private static bool HasAmmo(FVRObject gun, out string reason)
	{
		AmmoInfo ammoInfo = ResolveAmmo(gun);
		reason = ammoInfo.error;
		return reason == null;
	}

	private static bool IsGunPool(string pool)
	{
		switch (pool)
		{
		default:
			return pool == "Support";
		case "Sidearms":
		case "Rifles":
		case "Shotguns":
			return true;
		}
	}

	private static T CaseField<T>(TNH_WeaponCrate crate, string name)
	{
		return (T)AccessTools.Field(typeof(TNH_WeaponCrate), name).GetValue(crate);
	}

	private static GameObject StarterObject(TNH_WeaponCrate crate, FVRObject item, Transform point)
	{
		//IL_0007: 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)
		GameObject val = Object.Instantiate<GameObject>(((AnvilAsset)item).GetGameObject(), point.position, point.rotation);
		FVRFireArm component = val.GetComponent<FVRFireArm>();
		if ((Object)(object)component != (Object)null)
		{
			((FVRPhysicalObject)component).ObjectWrapper = item;
		}
		NormalizeSpawn(val);
		if ((Object)(object)crate.M != (Object)null)
		{
			crate.M.AddObjectToTrackedList(val);
		}
		return val;
	}

	private static IEnumerator SpawnStarterContents(TNH_WeaponCrate crate)
	{
		FVRObject gun = CaseField<FVRObject>(crate, "m_storedObject_gun");
		if ((Object)(object)gun != (Object)null)
		{
			yield return ((AnvilAsset)gun).GetGameObjectAsync();
			if ((int)GM.TNHOptions.UseVaultGunsMode == 1 && VaultSystem.DoesVaultFileExistWithFVRObjectAsPrimaryObjectThatHasAttachments(gun))
			{
				VaultSystem.SpawnRandomVaultSavedVersionOfGunWithAtLeastOneAttachment(gun, ((Component)crate).transform);
			}
			else
			{
				StarterObject(crate, gun, crate.Point_Gun);
			}
		}
		string[] array = new string[2] { "A", "B" };
		foreach (string suffix in array)
		{
			FVRObject extra = CaseField<FVRObject>(crate, "m_storedObject_attach" + suffix);
			if (!((Object)(object)extra == (Object)null) && !excludedAmmo.Contains(extra.ItemID))
			{
				yield return ((AnvilAsset)extra).GetGameObjectAsync();
				StarterObject(crate, extra, crate.Points_Attachment[(!(suffix == "A")) ? 1 : 0]);
			}
		}
		FVRObject ammo = CaseField<FVRObject>(crate, "m_magazineClipSpeedLoaderRound");
		if ((Object)(object)ammo == (Object)null)
		{
			yield break;
		}
		yield return ((AnvilAsset)ammo).GetGameObjectAsync();
		FVRFireArmRound component = ((AnvilAsset)ammo).GetGameObject().GetComponent<FVRFireArmRound>();
		if ((Object)(object)component != (Object)null && CaseField<bool>(crate, "m_spawnAmmoAsBox"))
		{
			GameObject val = Object.Instantiate<GameObject>(AM.GetAmmoBox(component.RoundType), crate.Points_MagClipSpeedloader[0].position, Quaternion.identity);
			val.GetComponent<CartridgeBox>().ConfigureShapeForRoundType(component.RoundType, component.RoundClass);
			if ((Object)(object)crate.M != (Object)null)
			{
				crate.M.AddObjectToTrackedList(val);
			}
		}
		else
		{
			List<Transform> list = (((Object)(object)component != (Object)null) ? crate.Points_Cartridge : crate.Points_MagClipSpeedloader);
			int num = CaseField<int>(crate, "m_numClipSpeedLoaderRound");
			for (int j = 0; j < num; j++)
			{
				StarterObject(crate, ammo, list[j % list.Count]);
			}
		}
	}

	private static void LightenUniform(GameObject root)
	{
		//IL_003e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0044: Expected O, but got Unknown
		//IL_0051: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)root == (Object)null)
		{
			return;
		}
		Renderer[] componentsInChildren = root.GetComponentsInChildren<Renderer>(true);
		foreach (Renderer val in componentsInChildren)
		{
			if (((Object)val).name.StartsWith("Sosigaccesory_ww2_") && ((Object)val).name.Contains("_grey"))
			{
				MaterialPropertyBlock val2 = new MaterialPropertyBlock();
				val.GetPropertyBlock(val2);
				val2.SetColor("_Color", Color.white);
				val.SetPropertyBlock(val2);
			}
		}
	}
}