Decompiled source of MemeRebellionScrap v1.0.3

plugins\MemeRebellionScrap\MemeRebellionScrap.dll

Decompiled 2 months ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using GameNetcodeStuff;
using HarmonyLib;
using LethalLib.Modules;
using MemeRebellionScrap.CustomEffects;
using MemeRebellionScrap.Items;
using MemeRebellionScrap.Utils;
using Microsoft.CodeAnalysis;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
using UnityEngine.Rendering;

[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("MemeRebellionScrap")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Unified scrap pack: drop in OBJs (shotgun cats) + PNGs (flat cards), each becomes an item.")]
[assembly: AssemblyFileVersion("1.0.3.0")]
[assembly: AssemblyInformationalVersion("1.0.3")]
[assembly: AssemblyProduct("MemeRebellionScrap")]
[assembly: AssemblyTitle("MemeRebellionScrap")]
[assembly: AssemblyVersion("1.0.3.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace MemeRebellionScrap
{
	public class ModConfig
	{
		public readonly ConfigEntry<int> PngDefaultMinValue;

		public readonly ConfigEntry<int> PngDefaultMaxValue;

		public readonly ConfigEntry<int> PngDefaultSpawnWeight;

		public readonly ConfigEntry<int> PngDefaultSpawnCopies;

		public readonly ConfigEntry<float> PngCardHeightMeters;

		public readonly ConfigEntry<int> ObjDefaultMinValue;

		public readonly ConfigEntry<int> ObjDefaultMaxValue;

		public readonly ConfigEntry<int> ObjDefaultSpawnWeight;

		public readonly ConfigEntry<int> ObjDefaultSpawnCopies;

		public readonly ConfigEntry<float> ObjDefaultSizeMeters;

		public readonly ConfigEntry<int> ShotgunPellets;

		public readonly ConfigEntry<int> ShotgunDamage;

		public readonly ConfigEntry<float> ShotgunRange;

		public readonly ConfigEntry<float> ShotgunSpread;

		public readonly ConfigEntry<float> ShotgunCooldown;

		public readonly ConfigEntry<bool> LogVanillaItemNamesOnAwake;

		public readonly ConfigEntry<bool> LogSpawnPoolBeforeRoll;

		public ModConfig(ConfigFile cfg)
		{
			PngDefaultMinValue = cfg.Bind<int>("PngCards (defaults)", "DefaultMinValue", 40, "Default minimum credit value for PNG card scrap when no .value.txt sidecar exists.");
			PngDefaultMaxValue = cfg.Bind<int>("PngCards (defaults)", "DefaultMaxValue", 140, "Default maximum credit value for PNG card scrap when no .value.txt sidecar exists.");
			PngDefaultSpawnWeight = cfg.Bind<int>("PngCards (defaults)", "DefaultSpawnWeight", 15, "Default per-copy spawn weight when no .spawn.txt sidecar exists. Vanilla scrap weights are ~5-50 (commons ~25-40); 15 makes PNG cards slightly rarer than the average vanilla item.");
			PngDefaultSpawnCopies = cfg.Bind<int>("PngCards (defaults)", "DefaultSpawnCopies", 1, "Default number of scrap-pool entries per moon for each PNG card. Each card already has its own weighted entry; copies multiplies that. Keep at 1 so individual cards stay rare.");
			PngCardHeightMeters = cfg.Bind<float>("PngCards (defaults)", "CardHeightMeters", 1f, "Visual height (meters) of each PNG card. Width is this × image aspect ratio.");
			ObjDefaultMinValue = cfg.Bind<int>("ObjItems (defaults)", "DefaultMinValue", 80, "Default minimum credit value for OBJ scrap when no .value.txt sidecar exists.");
			ObjDefaultMaxValue = cfg.Bind<int>("ObjItems (defaults)", "DefaultMaxValue", 260, "Default maximum credit value for OBJ scrap when no .value.txt sidecar exists.");
			ObjDefaultSpawnWeight = cfg.Bind<int>("ObjItems (defaults)", "DefaultSpawnWeight", 8, "Default per-copy spawn weight when no .spawn.txt sidecar exists. Tony Gat & co. are the rarest tier — set lower than the PNG default (15) so OBJ items are slightly rarer than the meme cards.");
			ObjDefaultSpawnCopies = cfg.Bind<int>("ObjItems (defaults)", "DefaultSpawnCopies", 1, "Default scrap-pool entries per moon for each OBJ item. Keep at 1 for true rarity; raise to seed multiple chances per moon.");
			ObjDefaultSizeMeters = cfg.Bind<float>("ObjItems (defaults)", "DefaultSizeMeters", 0.45f, "Auto-fit size (meters) for OBJ models on their longest axis. .scale.txt multiplies this.");
			ShotgunPellets = cfg.Bind<int>("Shotgun (defaults)", "Pellets", 8, "Default pellets per shot when .shotgun.txt doesn't set 'pellets'.");
			ShotgunDamage = cfg.Bind<int>("Shotgun (defaults)", "DamagePerPellet", 2, "Default damage per pellet when .shotgun.txt doesn't set 'damage'.");
			ShotgunRange = cfg.Bind<float>("Shotgun (defaults)", "RangeMeters", 12f, "Default max pellet travel distance when .shotgun.txt doesn't set 'range'.");
			ShotgunSpread = cfg.Bind<float>("Shotgun (defaults)", "SpreadDegrees", 10f, "Default cone width when .shotgun.txt doesn't set 'spread'. 0 = laser beam.");
			ShotgunCooldown = cfg.Bind<float>("Shotgun (defaults)", "CooldownSeconds", 1f, "Default seconds between shots when .shotgun.txt doesn't set 'cooldown'.");
			LogVanillaItemNamesOnAwake = cfg.Bind<bool>("Debug", "LogVanillaItemNamesOnAwake", false, "If true, dump every vanilla Item.itemName to BepInEx log on first StartOfRound.Awake. Useful for picking a different BaseItemName template.");
			LogSpawnPoolBeforeRoll = cfg.Bind<bool>("Debug", "LogSpawnPoolBeforeRoll", true, "If true, log the full contents of SelectableLevel.spawnableScrap right before the game rolls scrap for the round. Tells you exactly which items + weights are in the pool at spawn time, so you can see if our items survived or were filtered out by another mod.");
		}
	}
	[BepInPlugin("com.willott.memerebellionscrap", "MemeRebellionScrap", "1.0.3")]
	public class Plugin : BaseUnityPlugin
	{
		public const string ModGuid = "com.willott.memerebellionscrap";

		public const string ModName = "MemeRebellionScrap";

		public const string ModVersion = "1.0.3";

		private readonly Harmony _harmony = new Harmony("com.willott.memerebellionscrap");

		public static Plugin Instance { get; private set; }

		public static ModConfig Config { get; private set; }

		public static string PluginFolder { get; private set; }

		internal static ManualLogSource Logger { get; private set; }

		private void Awake()
		{
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			Instance = this;
			Logger = ((BaseUnityPlugin)this).Logger;
			Config = new ModConfig(((BaseUnityPlugin)this).Config);
			PluginFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
			Logger.LogInfo((object)"MemeRebellionScrap v1.0.3 loading...");
			_harmony.PatchAll(typeof(Plugin).Assembly);
			Logger.LogInfo((object)string.Format("{0} loaded. Dev hotkeys: PNG={1}, OBJ={2} (host only).", "MemeRebellionScrap", DevSpawn.PngKey, DevSpawn.ObjKey));
		}

		private void Update()
		{
			DevSpawn.CheckInput();
		}
	}
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "MemeRebellionScrap";

		public const string PLUGIN_NAME = "MemeRebellionScrap";

		public const string PLUGIN_VERSION = "1.0.3";
	}
}
namespace MemeRebellionScrap.Utils
{
	internal static class CentralItemConfig
	{
		private const string FileName = "items.cfg";

		private static bool _loaded;

		private static readonly Dictionary<string, Dictionary<string, string>> _sections = new Dictionary<string, Dictionary<string, string>>(StringComparer.OrdinalIgnoreCase);

		public static void EnsureLoaded()
		{
			if (_loaded)
			{
				return;
			}
			_loaded = true;
			string pluginFolder = Plugin.PluginFolder;
			if (string.IsNullOrEmpty(pluginFolder))
			{
				Plugin.Logger.LogWarning((object)"CentralItemConfig: Plugin.PluginFolder is unset; skipping items.cfg load.");
				return;
			}
			string text = Path.Combine(pluginFolder, "items.cfg");
			if (!File.Exists(text))
			{
				Plugin.Logger.LogInfo((object)"CentralItemConfig: no items.cfg in plugin folder, using sidecars + BepInEx defaults only.");
				return;
			}
			string text2 = null;
			int num = 0;
			int num2 = 0;
			try
			{
				string[] array = File.ReadAllLines(text);
				for (int i = 0; i < array.Length; i++)
				{
					string text3 = array[i].Trim();
					if (text3.Length == 0 || text3.StartsWith("#") || text3.StartsWith(";"))
					{
						continue;
					}
					if (text3.StartsWith("[") && text3.EndsWith("]") && text3.Length >= 3)
					{
						text2 = text3.Substring(1, text3.Length - 2).Trim().ToLowerInvariant();
						if (text2.Length == 0)
						{
							text2 = null;
						}
						else if (!_sections.ContainsKey(text2))
						{
							_sections[text2] = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
							num++;
						}
					}
					else
					{
						if (text2 == null)
						{
							continue;
						}
						int num3 = text3.IndexOf('=');
						if (num3 > 0)
						{
							string text4 = text3.Substring(0, num3).Trim().ToLowerInvariant();
							string value = text3.Substring(num3 + 1).Trim();
							if (text4.Length != 0)
							{
								_sections[text2][text4] = value;
								num2++;
							}
						}
					}
				}
				Plugin.Logger.LogInfo((object)($"CentralItemConfig: loaded {num} item section(s), " + string.Format("{0} key(s) from {1}.", num2, "items.cfg")));
			}
			catch (Exception ex)
			{
				Plugin.Logger.LogWarning((object)("CentralItemConfig: failed to parse " + text + ": " + ex.GetType().Name + ": " + ex.Message));
			}
		}

		public static bool TryGet(string stem, string key, out string value)
		{
			value = null;
			if (string.IsNullOrEmpty(stem) || string.IsNullOrEmpty(key))
			{
				return false;
			}
			EnsureLoaded();
			if (!_sections.TryGetValue(stem, out var value2))
			{
				return false;
			}
			return value2.TryGetValue(key, out value);
		}
	}
	[HarmonyPatch]
	internal static class DevSpawnInputHook
	{
		[HarmonyPatch(typeof(PlayerControllerB), "Update")]
		[HarmonyPostfix]
		private static void PollDevSpawn()
		{
			DevSpawn.CheckInput();
		}
	}
	internal static class DevSpawn
	{
		public static readonly Key PngKey = (Key)10;

		public static readonly Key ObjKey = (Key)12;

		private static readonly KeyCode PngKeyLegacy = (KeyCode)92;

		private static readonly KeyCode ObjKeyLegacy = (KeyCode)93;

		private static int _pngRotation;

		private static int _objRotation;

		private static int _lastSpawnFrame = -1;

		private static bool _pngHeldLast;

		private static bool _objHeldLast;

		public static void CheckInput()
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: 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)
			Keyboard current = Keyboard.current;
			bool flag = ((current != null) ? ((ButtonControl)current[PngKey]).isPressed : SafeLegacy(PngKeyLegacy));
			bool num = ((current != null) ? ((ButtonControl)current[ObjKey]).isPressed : SafeLegacy(ObjKeyLegacy));
			bool flag2 = flag && !_pngHeldLast;
			bool flag3 = num && !_objHeldLast;
			_pngHeldLast = flag;
			_objHeldLast = num;
			if ((flag2 || flag3) && Time.frameCount != _lastSpawnFrame)
			{
				_lastSpawnFrame = Time.frameCount;
				if (flag2)
				{
					TryCycleSpawn("PNG", PngScrapLoader.CustomItems, ref _pngRotation);
				}
				if (flag3)
				{
					TryCycleSpawn("OBJ", ObjScrapLoader.CustomItems, ref _objRotation);
				}
			}
		}

		private static void TryCycleSpawn(string label, IReadOnlyList<Item> items, ref int rotation)
		{
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: 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_0105: 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_010b: Unknown result type (might be due to invalid IL or missing references)
			Plugin.Logger.LogInfo((object)("DevSpawn[" + label + "]: key press detected."));
			PlayerControllerB val = (((Object)(object)GameNetworkManager.Instance != (Object)null) ? GameNetworkManager.Instance.localPlayerController : null);
			if ((Object)(object)val == (Object)null)
			{
				Plugin.Logger.LogInfo((object)("DevSpawn[" + label + "]: no local player (not in-game yet)."));
				return;
			}
			if ((Object)(object)NetworkManager.Singleton == (Object)null || (!NetworkManager.Singleton.IsHost && !NetworkManager.Singleton.IsServer))
			{
				Plugin.Logger.LogInfo((object)("DevSpawn[" + label + "]: host-only. Ignoring press."));
				return;
			}
			if (items.Count == 0)
			{
				Plugin.Logger.LogInfo((object)("DevSpawn[" + label + "]: no items registered (drop assets into the plugin folder)."));
				return;
			}
			Item item = items[rotation % items.Count];
			rotation++;
			Vector3 position = ((Component)val).transform.position + ((Component)val).transform.forward * 1.5f + Vector3.up * 0.5f;
			SpawnItem(item, position, label);
		}

		private static void SpawnItem(Item item, Vector3 position, string label)
		{
			//IL_004f: 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_0111: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)item.spawnPrefab == (Object)null)
			{
				Plugin.Logger.LogWarning((object)("DevSpawn[" + label + "]: '" + item.itemName + "' has no spawnPrefab."));
				return;
			}
			GameObject obj = Object.Instantiate<GameObject>(item.spawnPrefab, position, Quaternion.identity);
			obj.SetActive(true);
			GrabbableObject component = obj.GetComponent<GrabbableObject>();
			int num = 0;
			if ((Object)(object)component != (Object)null)
			{
				component.fallTime = 0f;
				num = (component.scrapValue = Random.Range(item.minValue, item.maxValue + 1));
				component.SetScrapValue(num);
			}
			NetworkObject component2 = obj.GetComponent<NetworkObject>();
			if ((Object)(object)component2 != (Object)null)
			{
				component2.Spawn(true);
			}
			else
			{
				Plugin.Logger.LogWarning((object)("DevSpawn[" + label + "]: '" + item.itemName + "' prefab has no NetworkObject. Spawned locally only — won't sync to clients."));
			}
			Plugin.Logger.LogInfo((object)$"DevSpawn[{label}]: spawned '{item.itemName}' at {position} (value {num}).");
		}

		private static bool SafeLegacy(KeyCode kc)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				return Input.GetKey(kc);
			}
			catch
			{
				return false;
			}
		}
	}
	internal static class NetworkHash
	{
		private const string CatPrefix = "MemeRebellionScrap.Obj:";

		private const string PngPrefix = "MemeRebellionScrap.Png:";

		public static void AssignForObjItem(GameObject prefab, string itemName)
		{
			Assign(prefab, "MemeRebellionScrap.Obj:" + itemName);
		}

		public static void AssignForPngItem(GameObject prefab, string itemName)
		{
			Assign(prefab, "MemeRebellionScrap.Png:" + itemName);
		}

		private static void Assign(GameObject prefab, string seed)
		{
			NetworkObject component = prefab.GetComponent<NetworkObject>();
			if (!((Object)(object)component == (Object)null))
			{
				uint num = Fnv1a(seed);
				if (num == 0)
				{
					num = 1u;
				}
				typeof(NetworkObject).GetField("GlobalObjectIdHash", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(component, num);
			}
		}

		private static uint Fnv1a(string s)
		{
			uint num = 2166136261u;
			foreach (char c in s)
			{
				num ^= c;
				num *= 16777619;
			}
			return num;
		}
	}
	internal static class ObjLoader
	{
		public static Mesh LoadObj(string objPath, out string textureFile)
		{
			//IL_012c: 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_03bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_03c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_03cf: Expected O, but got Unknown
			//IL_0197: Unknown result type (might be due to invalid IL or missing references)
			textureFile = null;
			if (!File.Exists(objPath))
			{
				Plugin.Logger.LogWarning((object)("OBJ: file not found: " + objPath));
				return null;
			}
			List<Vector3> positions = new List<Vector3>();
			List<Vector2> uvs = new List<Vector2>();
			List<Vector3> normals = new List<Vector3>();
			List<List<(int, int, int)>> list = new List<List<(int, int, int)>>();
			string text = null;
			string[] array = File.ReadAllLines(objPath);
			for (int i = 0; i < array.Length; i++)
			{
				string text2 = array[i].Trim();
				if (text2.Length == 0 || text2[0] == '#')
				{
					continue;
				}
				string[] array2 = text2.Split(new char[2] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
				if (array2.Length == 0)
				{
					continue;
				}
				switch (array2[0])
				{
				case "v":
					if (array2.Length >= 4)
					{
						positions.Add(new Vector3(ParseFloat(array2[1]), ParseFloat(array2[2]), ParseFloat(array2[3])));
					}
					break;
				case "vt":
					if (array2.Length >= 3)
					{
						uvs.Add(new Vector2(ParseFloat(array2[1]), ParseFloat(array2[2])));
					}
					break;
				case "vn":
					if (array2.Length >= 4)
					{
						normals.Add(new Vector3(ParseFloat(array2[1]), ParseFloat(array2[2]), ParseFloat(array2[3])));
					}
					break;
				case "f":
				{
					List<(int, int, int)> list2 = new List<(int, int, int)>(array2.Length - 1);
					for (int j = 1; j < array2.Length; j++)
					{
						string[] array3 = array2[j].Split('/');
						int item = ParseIndex((array3.Length != 0) ? array3[0] : "", positions.Count);
						int item2 = ((array3.Length > 1) ? ParseIndex(array3[1], uvs.Count) : (-1));
						int item3 = ((array3.Length > 2) ? ParseIndex(array3[2], normals.Count) : (-1));
						list2.Add((item, item2, item3));
					}
					if (list2.Count >= 3)
					{
						list.Add(list2);
					}
					break;
				}
				case "mtllib":
					if (array2.Length >= 2)
					{
						text = text2.Substring(text2.IndexOf(' ') + 1).Trim();
					}
					break;
				}
			}
			if (positions.Count == 0 || list.Count == 0)
			{
				Plugin.Logger.LogWarning((object)("OBJ: " + Path.GetFileName(objPath) + " has no geometry."));
				return null;
			}
			Dictionary<long, int> vertexIndex = new Dictionary<long, int>();
			List<Vector3> outPositions = new List<Vector3>();
			List<Vector2> outUvs = new List<Vector2>();
			List<Vector3> outNormals = new List<Vector3>();
			List<int> list3 = new List<int>();
			bool anyUv = uvs.Count > 0;
			bool anyNormal = normals.Count > 0;
			foreach (List<(int, int, int)> item7 in list)
			{
				int item4 = Remap(item7[0]);
				for (int k = 1; k < item7.Count - 1; k++)
				{
					int item5 = Remap(item7[k]);
					int item6 = Remap(item7[k + 1]);
					list3.Add(item4);
					list3.Add(item5);
					list3.Add(item6);
				}
			}
			Mesh val = new Mesh
			{
				name = Path.GetFileNameWithoutExtension(objPath)
			};
			val.indexFormat = (IndexFormat)1;
			val.SetVertices(outPositions);
			if (anyUv)
			{
				val.SetUVs(0, outUvs);
			}
			val.SetTriangles(list3, 0);
			if (anyNormal && AllNormalsValid(outNormals))
			{
				val.SetNormals(outNormals);
			}
			else
			{
				val.RecalculateNormals();
			}
			val.RecalculateTangents();
			val.RecalculateBounds();
			Plugin.Logger.LogInfo((object)("OBJ: loaded '" + Path.GetFileName(objPath) + "' — " + $"{outPositions.Count} verts, {list3.Count / 3} tris."));
			if (!string.IsNullOrEmpty(text))
			{
				string path = Path.GetDirectoryName(objPath) ?? "";
				string text3 = Path.Combine(path, text);
				if (!File.Exists(text3))
				{
					text3 = Path.Combine(path, Path.GetFileName(text));
				}
				if (File.Exists(text3))
				{
					textureFile = ParseMtlForDiffuse(text3);
					if (textureFile != null)
					{
						Plugin.Logger.LogInfo((object)("OBJ: MTL map_Kd -> '" + textureFile + "'."));
					}
				}
				else
				{
					Plugin.Logger.LogInfo((object)("OBJ: mtllib '" + text + "' not found next to OBJ — skipping material."));
				}
			}
			return val;
			int Remap((int p, int u, int n) key)
			{
				//IL_0054: Unknown result type (might be due to invalid IL or missing references)
				//IL_0088: Unknown result type (might be due to invalid IL or missing references)
				//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
				//IL_009b: Unknown result type (might be due to invalid IL or missing references)
				//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
				long key2 = ((long)(key.p + 1) << 42) ^ ((long)(key.u + 1) << 21) ^ (key.n + 1);
				if (vertexIndex.TryGetValue(key2, out var value))
				{
					return value;
				}
				value = outPositions.Count;
				outPositions.Add(positions[key.p]);
				outUvs.Add((anyUv && key.u >= 0 && key.u < uvs.Count) ? uvs[key.u] : Vector2.zero);
				outNormals.Add((anyNormal && key.n >= 0 && key.n < normals.Count) ? normals[key.n] : Vector3.zero);
				vertexIndex[key2] = value;
				return value;
			}
		}

		private static string ParseMtlForDiffuse(string mtlPath)
		{
			string[] array = File.ReadAllLines(mtlPath);
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i].Trim();
				if (text.Length != 0 && text[0] != '#' && text.StartsWith("map_Kd", StringComparison.OrdinalIgnoreCase))
				{
					string[] array2 = text.Split(new char[2] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
					if (array2.Length >= 2)
					{
						return array2[^1];
					}
				}
			}
			return null;
		}

		private static float ParseFloat(string s)
		{
			if (!float.TryParse(s, NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
			{
				return 0f;
			}
			return result;
		}

		private static int ParseIndex(string s, int currentCount)
		{
			if (string.IsNullOrEmpty(s))
			{
				return -1;
			}
			if (!int.TryParse(s, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result))
			{
				return -1;
			}
			if (result > 0)
			{
				return result - 1;
			}
			if (result < 0)
			{
				return currentCount + result;
			}
			return -1;
		}

		private static bool AllNormalsValid(List<Vector3> normals)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			foreach (Vector3 normal in normals)
			{
				Vector3 current = normal;
				if (((Vector3)(ref current)).sqrMagnitude < 0.0001f)
				{
					return false;
				}
			}
			return true;
		}
	}
	internal static class Sidecars
	{
		public static string ResolveDisplayName(string dir, string stem, string fallbackIfEmpty = "Mystery Item")
		{
			string path = Path.Combine(dir, stem + ".txt");
			if (File.Exists(path))
			{
				string[] array = File.ReadAllLines(path);
				for (int i = 0; i < array.Length; i++)
				{
					string text = array[i].Trim();
					if (text.Length > 0)
					{
						return text;
					}
				}
			}
			if (CentralItemConfig.TryGet(stem, "name", out var value) && !string.IsNullOrWhiteSpace(value))
			{
				return value.Trim();
			}
			string text2 = stem.Replace('_', ' ').Replace('-', ' ').Trim();
			if (text2.Length == 0)
			{
				return fallbackIfEmpty;
			}
			string[] array2 = text2.Split(' ');
			for (int j = 0; j < array2.Length; j++)
			{
				if (array2[j].Length > 0)
				{
					array2[j] = char.ToUpper(array2[j][0]) + array2[j].Substring(1);
				}
			}
			return string.Join(" ", array2);
		}

		public static (int min, int max) ResolveValueRange(string dir, string stem, int defMin, int defMax)
		{
			string path = Path.Combine(dir, stem + ".value.txt");
			if (File.Exists(path))
			{
				string[] array = File.ReadAllLines(path);
				for (int i = 0; i < array.Length; i++)
				{
					string text = array[i].Trim();
					if (text.Length != 0)
					{
						if (!TryParseRange(text, out var min, out var max))
						{
							break;
						}
						return (min, max);
					}
				}
			}
			if (CentralItemConfig.TryGet(stem, "value", out var value) && TryParseRange(value, out var min2, out var max2))
			{
				return (min2, max2);
			}
			return (defMin, defMax);
		}

		private static bool TryParseRange(string s, out int min, out int max)
		{
			min = (max = 0);
			if (string.IsNullOrEmpty(s))
			{
				return false;
			}
			string[] array = s.Split('-');
			if (array.Length != 2)
			{
				return false;
			}
			if (!int.TryParse(array[0].Trim(), out min))
			{
				return false;
			}
			if (!int.TryParse(array[1].Trim(), out max))
			{
				return false;
			}
			if (min >= 0)
			{
				return max >= min;
			}
			return false;
		}

		public static float ResolveScale(string dir, string stem)
		{
			string path = Path.Combine(dir, stem + ".scale.txt");
			if (File.Exists(path))
			{
				string[] array = File.ReadAllLines(path);
				for (int i = 0; i < array.Length; i++)
				{
					string text = array[i].Trim();
					if (text.Length != 0)
					{
						if (!float.TryParse(text, NumberStyles.Float, CultureInfo.InvariantCulture, out var result) || !(result > 0f))
						{
							break;
						}
						return result;
					}
				}
			}
			if (CentralItemConfig.TryGet(stem, "scale", out var value) && float.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out var result2) && result2 > 0f)
			{
				return result2;
			}
			return 1f;
		}

		public static (int weight, int copies) ResolveSpawnConfig(string dir, string stem, int defWeight, int defCopies)
		{
			int item = defWeight;
			int item2 = defCopies;
			bool flag = false;
			bool flag2 = false;
			string path = Path.Combine(dir, stem + ".spawn.txt");
			if (File.Exists(path))
			{
				string[] array = File.ReadAllLines(path);
				for (int i = 0; i < array.Length; i++)
				{
					string text = array[i].Trim();
					if (text.Length == 0 || text.StartsWith("#"))
					{
						continue;
					}
					int num = text.IndexOf('=');
					if (num <= 0)
					{
						continue;
					}
					string text2 = text.Substring(0, num).Trim().ToLowerInvariant();
					string s = text.Substring(num + 1).Trim();
					switch (text2)
					{
					case "weight":
					{
						if (int.TryParse(s, out var result2) && result2 >= 0)
						{
							item = result2;
							flag = true;
						}
						break;
					}
					case "copies":
					case "boost":
					{
						if (int.TryParse(s, out var result) && result >= 0 && result <= 50)
						{
							item2 = result;
							flag2 = true;
						}
						break;
					}
					}
				}
			}
			else
			{
				string path2 = Path.Combine(dir, stem + ".weight.txt");
				if (File.Exists(path2))
				{
					string[] array = File.ReadAllLines(path2);
					for (int i = 0; i < array.Length; i++)
					{
						string text3 = array[i].Trim();
						if (text3.Length != 0)
						{
							if (int.TryParse(text3, out var result3) && result3 > 0)
							{
								item = result3;
								flag = true;
							}
							break;
						}
					}
				}
			}
			if (!flag && CentralItemConfig.TryGet(stem, "spawn_weight", out var value) && int.TryParse(value, out var result4) && result4 >= 0)
			{
				item = result4;
			}
			if (!flag2 && CentralItemConfig.TryGet(stem, "spawn_copies", out var value2) && int.TryParse(value2, out var result5) && result5 >= 0 && result5 <= 50)
			{
				item2 = result5;
			}
			return (item, item2);
		}

		public static bool ApplyHoldConfig(string dir, string stem, Item item)
		{
			bool flag = false;
			flag |= ApplyHoldFromCentral(stem, item);
			string path = Path.Combine(dir, stem + ".hold.txt");
			if (File.Exists(path))
			{
				string[] array = File.ReadAllLines(path);
				for (int i = 0; i < array.Length; i++)
				{
					string text = array[i].Trim();
					if (text.Length == 0 || text.StartsWith("#"))
					{
						continue;
					}
					int num = text.IndexOf('=');
					if (num > 0)
					{
						string text2 = text.Substring(0, num).Trim().ToLowerInvariant();
						string val = text.Substring(num + 1).Trim();
						if (ApplyHoldKey(item, text2, val))
						{
							flag = true;
						}
						if (text2 == "weight" && ApplyHoldKey(item, "held_weight", val))
						{
							flag = true;
						}
					}
				}
			}
			return flag;
		}

		private static bool ApplyHoldFromCentral(string stem, Item item)
		{
			bool result = false;
			string[] array = new string[6] { "positionoffset", "rotationoffset", "restingrotation", "verticaloffset", "twohanded", "held_weight" };
			foreach (string key in array)
			{
				if (CentralItemConfig.TryGet(stem, key, out var value) && ApplyHoldKey(item, key, value))
				{
					result = true;
				}
			}
			return result;
		}

		private static bool ApplyHoldKey(Item item, string key, string val)
		{
			//IL_011f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			switch (key)
			{
			case "positionoffset":
			{
				if (TryParseVector3(val, out var v3))
				{
					item.positionOffset = v3;
					return true;
				}
				break;
			}
			case "rotationoffset":
			{
				if (TryParseVector3(val, out var v))
				{
					item.rotationOffset = v;
					return true;
				}
				break;
			}
			case "restingrotation":
			{
				if (TryParseVector3(val, out var v2))
				{
					item.restingRotation = v2;
					return true;
				}
				break;
			}
			case "verticaloffset":
			{
				if (float.TryParse(val, NumberStyles.Float, CultureInfo.InvariantCulture, out var result2))
				{
					item.verticalOffset = result2;
					return true;
				}
				break;
			}
			case "twohanded":
			{
				if (bool.TryParse(val, out var result3))
				{
					item.twoHanded = result3;
					item.twoHandedAnimation = result3;
					return true;
				}
				break;
			}
			case "held_weight":
			case "weight":
			{
				if (float.TryParse(val, NumberStyles.Float, CultureInfo.InvariantCulture, out var result) && result >= 1f && result <= 5f)
				{
					item.weight = result;
					return true;
				}
				break;
			}
			}
			return false;
		}

		public static bool TryParseVector3(string s, out Vector3 v)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			v = Vector3.zero;
			string[] array = s.Split(',');
			if (array.Length != 3)
			{
				return false;
			}
			if (!float.TryParse(array[0].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
			{
				return false;
			}
			if (!float.TryParse(array[1].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out var result2))
			{
				return false;
			}
			if (!float.TryParse(array[2].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out var result3))
			{
				return false;
			}
			v = new Vector3(result, result2, result3);
			return true;
		}
	}
	internal static class WavLoader
	{
		public static AudioClip Load(string path)
		{
			byte[] array;
			try
			{
				array = File.ReadAllBytes(path);
			}
			catch (Exception ex)
			{
				Plugin.Logger.LogWarning((object)("WAV: couldn't read " + Path.GetFileName(path) + ": " + ex.Message));
				return null;
			}
			if (array.Length < 44 || array[0] != 82 || array[1] != 73 || array[2] != 70 || array[3] != 70 || array[8] != 87 || array[9] != 65 || array[10] != 86 || array[11] != 69)
			{
				Plugin.Logger.LogWarning((object)("WAV: " + Path.GetFileName(path) + " is not a valid WAV."));
				return null;
			}
			int num = 1;
			int num2 = 44100;
			int num3 = 16;
			int num4 = -1;
			int num5 = 0;
			bool flag = false;
			int num6 = 12;
			while (num6 + 8 <= array.Length)
			{
				string @string = Encoding.ASCII.GetString(array, num6, 4);
				int num7 = BitConverter.ToInt32(array, num6 + 4);
				int num8 = num6 + 8;
				if (@string == "fmt ")
				{
					int num9 = BitConverter.ToInt16(array, num8);
					num = BitConverter.ToInt16(array, num8 + 2);
					num2 = BitConverter.ToInt32(array, num8 + 4);
					num3 = BitConverter.ToInt16(array, num8 + 14);
					if (num9 != 1)
					{
						Plugin.Logger.LogWarning((object)($"WAV: {Path.GetFileName(path)} isn't PCM (format code {num9}). " + "Re-export as 16-bit PCM WAV."));
						return null;
					}
					flag = true;
				}
				else if (@string == "data")
				{
					num4 = num8;
					num5 = num7;
					break;
				}
				num6 = num8 + num7;
				if ((num7 & 1) == 1)
				{
					num6++;
				}
			}
			if (!flag || num4 < 0)
			{
				Plugin.Logger.LogWarning((object)("WAV: " + Path.GetFileName(path) + " missing fmt/data chunks."));
				return null;
			}
			if (num3 != 16)
			{
				Plugin.Logger.LogWarning((object)$"WAV: {Path.GetFileName(path)} is {num3}-bit; need 16-bit PCM.");
				return null;
			}
			int num10 = num5 / 2;
			int num11 = num10 / num;
			float[] array2 = new float[num10];
			for (int i = 0; i < num10; i++)
			{
				short num12 = BitConverter.ToInt16(array, num4 + i * 2);
				array2[i] = (float)num12 / 32768f;
			}
			AudioClip obj = AudioClip.Create(Path.GetFileNameWithoutExtension(path), num11, num, num2, false);
			obj.SetData(array2, 0);
			return obj;
		}
	}
}
namespace MemeRebellionScrap.Items
{
	[HarmonyPatch]
	internal static class GrabbableObjectGuard
	{
		private static readonly HashSet<string> _loggedStartFor = new HashSet<string>();

		private static readonly HashSet<string> _loggedFallFor = new HashSet<string>();

		private static readonly HashSet<string> _loggedFallSampleFor = new HashSet<string>();

		private static readonly Dictionary<int, bool> _wasHeldLastFrame = new Dictionary<int, bool>();

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

		private static bool _loggedFirstUpdateTick = false;

		private static readonly HashSet<string> _seenOursInUpdate = new HashSet<string>();

		private static readonly HashSet<string> _seenHeldInUpdate = new HashSet<string>();

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

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

		public static (Dictionary<string, int> awakes, Dictionary<string, int> starts) DrainCounters()
		{
			Dictionary<string, int> item = new Dictionary<string, int>(_awakeCount);
			Dictionary<string, int> item2 = new Dictionary<string, int>(_startCount);
			_awakeCount.Clear();
			_startCount.Clear();
			return (item, item2);
		}

		[HarmonyPatch(typeof(GrabbableObject), "Start")]
		[HarmonyPrefix]
		[HarmonyPriority(800)]
		private static void CountStartEntry(GrabbableObject __instance)
		{
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)__instance == (Object)null) && IsOurs(__instance) && !IsTemplate(__instance))
			{
				string text = (((Object)(object)__instance.itemProperties != (Object)null) ? __instance.itemProperties.itemName : "<no-name>");
				_awakeCount.TryGetValue(text, out var value);
				_awakeCount[text] = value + 1;
				Plugin.Logger.LogInfo((object)("GrabbableObjectGuard: Start ENTRY on OUR clone '" + text + "' " + $"pos={((Component)__instance).transform.position} " + "parent=" + (((Object)(object)((Component)__instance).transform.parent != (Object)null) ? ((Object)((Component)__instance).transform.parent).name : "<none>")));
			}
		}

		[HarmonyPatch(typeof(GrabbableObject), "Start")]
		[HarmonyPostfix]
		private static void CountStartSuccess(GrabbableObject __instance)
		{
			if (!((Object)(object)__instance == (Object)null) && IsOurs(__instance) && !IsTemplate(__instance))
			{
				string key = (((Object)(object)__instance.itemProperties != (Object)null) ? __instance.itemProperties.itemName : "<no-name>");
				_startCount.TryGetValue(key, out var value);
				_startCount[key] = value + 1;
			}
		}

		[HarmonyPatch(typeof(GrabbableObject), "FallWithCurve")]
		[HarmonyPrefix]
		private static bool SkipFallCurveOnTemplates(GrabbableObject __instance)
		{
			if ((Object)(object)__instance == (Object)null)
			{
				return true;
			}
			if (!IsOurs(__instance))
			{
				return true;
			}
			if (IsTemplate(__instance))
			{
				return false;
			}
			return true;
		}

		[HarmonyPatch(typeof(GrabbableObject), "Start")]
		[HarmonyPrefix]
		private static bool SkipStartOnTemplates(GrabbableObject __instance)
		{
			if ((Object)(object)__instance == (Object)null)
			{
				return true;
			}
			if (!IsOurs(__instance))
			{
				return true;
			}
			if (IsTemplate(__instance))
			{
				return false;
			}
			return true;
		}

		[HarmonyPatch(typeof(GrabbableObject), "Start")]
		[HarmonyFinalizer]
		private static Exception StartFinalizer(GrabbableObject __instance, Exception __exception)
		{
			//IL_01c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ff: Unknown result type (might be due to invalid IL or missing references)
			if (__exception == null)
			{
				return null;
			}
			if (!IsOurs(__instance))
			{
				return __exception;
			}
			string text = (((Object)(object)__instance.itemProperties != (Object)null) ? __instance.itemProperties.itemName : "<no itemProperties>");
			if (_loggedStartFor.Add(text))
			{
				string arg = (((Object)(object)((Component)__instance).transform.parent != (Object)null) ? ((Object)((Component)__instance).transform.parent).name : "<null>");
				bool flag = (Object)(object)__instance.mainObjectRenderer != (Object)null;
				string text2 = (((Object)(object)RoundManager.Instance != (Object)null) ? "ok" : "<null>");
				string text3 = (((Object)(object)RoundManager.Instance != (Object)null && (Object)(object)RoundManager.Instance.mapPropsContainer != (Object)null) ? ((Object)RoundManager.Instance.mapPropsContainer).name : "<null>");
				string text4 = (((Object)(object)StartOfRound.Instance != (Object)null) ? "ok" : "<null>");
				string text5 = LookupNamedField(StartOfRound.Instance, "itemRadarIconPrefab");
				Plugin.Logger.LogError((object)("GrabbableObject.Start NRE on OUR item '" + text + "': " + __exception.GetType().Name + ": " + __exception.Message + "\n" + $"  state: parent={arg} mainObjectRenderer={flag} " + "RoundManager.Instance=" + text2 + " mapPropsContainer=" + text3 + " StartOfRound.Instance=" + text4 + " itemRadarIconPrefab=" + text5 + "\n" + __exception.StackTrace));
			}
			TrySet(__instance, "propColliders", ((Component)__instance).gameObject.GetComponentsInChildren<Collider>());
			TrySet(__instance, "originalScale", ((Component)__instance).transform.localScale);
			TrySet(__instance, "startFallingPosition", ((Component)__instance).transform.localPosition);
			TrySet(__instance, "targetFloorPosition", ((Component)__instance).transform.localPosition);
			return null;
		}

		[HarmonyPatch(typeof(GrabbableObject), "FallWithCurve")]
		[HarmonyFinalizer]
		private static Exception FallWithCurveFinalizer(GrabbableObject __instance, Exception __exception)
		{
			if (__exception == null)
			{
				return null;
			}
			if (!IsOurs(__instance))
			{
				return __exception;
			}
			string text = (((Object)(object)__instance.itemProperties != (Object)null) ? __instance.itemProperties.itemName : "<no itemProperties>");
			if (_loggedFallFor.Add(text))
			{
				Plugin.Logger.LogWarning((object)("GrabbableObject.FallWithCurve NRE on OUR item '" + text + "' (silenced after first): " + __exception.Message));
			}
			return null;
		}

		[HarmonyPatch(typeof(GrabbableObject), "Update")]
		[HarmonyPostfix]
		private static void LogDropTransitions(GrabbableObject __instance)
		{
			//IL_022e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0246: Unknown result type (might be due to invalid IL or missing references)
			//IL_0263: Unknown result type (might be due to invalid IL or missing references)
			//IL_0319: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_03b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d6: Unknown result type (might be due to invalid IL or missing references)
			if (!_loggedFirstUpdateTick)
			{
				_loggedFirstUpdateTick = true;
				Plugin.Logger.LogInfo((object)("GrabbableObjectGuard: Update postfix is alive (first tick on '" + (((Object)(object)__instance != (Object)null && (Object)(object)__instance.itemProperties != (Object)null) ? __instance.itemProperties.itemName : "<unknown>") + "')."));
			}
			if ((Object)(object)__instance == (Object)null || !IsOurs(__instance) || IsTemplate(__instance))
			{
				return;
			}
			int instanceID = ((Object)__instance).GetInstanceID();
			bool isHeld = __instance.isHeld;
			bool value;
			bool flag = _wasHeldLastFrame.TryGetValue(instanceID, out value) && value;
			_wasHeldLastFrame[instanceID] = isHeld;
			string text = (((Object)(object)__instance.itemProperties != (Object)null) ? __instance.itemProperties.itemName : "<no-name>");
			if (_seenOursInUpdate.Add(text))
			{
				Plugin.Logger.LogInfo((object)("GrabbableObjectGuard: Update sees OUR clone of '" + text + "' " + $"(isHeld={isHeld}, hasHit={__instance.hasHitGround}, " + "playerHeldBy=" + (((Object)(object)__instance.playerHeldBy != (Object)null) ? ((Object)__instance.playerHeldBy).name : "<null>") + ")."));
			}
			if (isHeld && _seenHeldInUpdate.Add(text))
			{
				Plugin.Logger.LogInfo((object)("GrabbableObjectGuard: '" + text + "' is now held (playerHeldBy=" + (((Object)(object)__instance.playerHeldBy != (Object)null) ? ((Object)__instance.playerHeldBy).name : "<null>") + ")."));
			}
			if (flag && !isHeld)
			{
				_framesSinceDrop[instanceID] = 0;
				Plugin.Logger.LogInfo((object)("[DROP] '" + text + "' " + $"hasHit={__instance.hasHitGround} " + $"reachedFloor={__instance.reachedFloorTarget} " + $"fallTime={__instance.fallTime:F2} " + $"start={__instance.startFallingPosition} " + $"target={__instance.targetFloorPosition} " + $"pos={((Component)__instance).transform.position} " + "parent=" + (((Object)(object)((Component)__instance).transform.parent != (Object)null) ? ((Object)((Component)__instance).transform.parent).name : "<null>") + " layer=" + LayerMask.LayerToName(((Component)__instance).gameObject.layer)));
			}
			if (_framesSinceDrop.TryGetValue(instanceID, out var value2))
			{
				if (__instance.hasHitGround)
				{
					Plugin.Logger.LogInfo((object)($"[GROUNDED] '{text}' after {value2} frame(s) " + $"fallTime={__instance.fallTime:F2} pos={((Component)__instance).transform.position}"));
					_framesSinceDrop.Remove(instanceID);
				}
				else if (value2 >= 60)
				{
					Plugin.Logger.LogWarning((object)("[STUCK] '" + text + "' 60 frames after drop with no grounding. " + $"hasHit={__instance.hasHitGround} " + $"fallTime={__instance.fallTime:F2} " + $"start={__instance.startFallingPosition} " + $"target={__instance.targetFloorPosition} " + $"pos={((Component)__instance).transform.position} " + "layer=" + LayerMask.LayerToName(((Component)__instance).gameObject.layer)));
					_framesSinceDrop.Remove(instanceID);
				}
				else
				{
					_framesSinceDrop[instanceID] = value2 + 1;
				}
			}
		}

		[HarmonyPatch(typeof(GrabbableObject), "FallWithCurve")]
		[HarmonyPostfix]
		private static void LogFallWithCurveSample(GrabbableObject __instance)
		{
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)__instance == (Object)null) && IsOurs(__instance) && !IsTemplate(__instance))
			{
				string text = (((Object)(object)__instance.itemProperties != (Object)null) ? __instance.itemProperties.itemName : "<no-name>");
				if (_loggedFallSampleFor.Add(text))
				{
					Plugin.Logger.LogInfo((object)("FallWithCurve sample '" + text + "': " + $"start={__instance.startFallingPosition} " + $"target={__instance.targetFloorPosition} " + $"fallTime={__instance.fallTime:F3} " + $"pos={((Component)__instance).transform.position} " + $"hasHitGround={__instance.hasHitGround} " + $"reachedFloor={__instance.reachedFloorTarget} " + "layer=" + LayerMask.LayerToName(((Component)__instance).gameObject.layer)));
				}
			}
		}

		private static bool IsOurs(GrabbableObject g)
		{
			if ((Object)(object)g == (Object)null || (Object)(object)g.itemProperties == (Object)null)
			{
				return false;
			}
			foreach (Item customItem in ObjScrapLoader.CustomItems)
			{
				if (customItem == g.itemProperties)
				{
					return true;
				}
			}
			foreach (Item customItem2 in PngScrapLoader.CustomItems)
			{
				if (customItem2 == g.itemProperties)
				{
					return true;
				}
			}
			return false;
		}

		private static bool IsTemplate(GrabbableObject g)
		{
			if ((Object)(object)g == (Object)null || (Object)(object)g.itemProperties == (Object)null)
			{
				return false;
			}
			return ((Component)g).gameObject == g.itemProperties.spawnPrefab;
		}

		private static void TrySet(object target, string fieldName, object value)
		{
			try
			{
				FieldInfo field = target.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (!(field == null) && (value == null || field.FieldType.IsAssignableFrom(value.GetType())))
				{
					field.SetValue(target, value);
				}
			}
			catch
			{
			}
		}

		private static string LookupNamedField(object target, string fieldName)
		{
			if (target == null)
			{
				return "<target-null>";
			}
			try
			{
				FieldInfo field = target.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (field == null)
				{
					return "<no-such-field>";
				}
				object value = field.GetValue(target);
				if (value == null)
				{
					return "<null>";
				}
				Object val = (Object)((value is Object) ? value : null);
				if (val != null)
				{
					return (val == (Object)null) ? "<destroyed>" : val.name;
				}
				return value.ToString();
			}
			catch (Exception ex)
			{
				return "<err:" + ex.GetType().Name + ">";
			}
		}
	}
	[HarmonyPatch]
	internal static class ObjScrapLoader
	{
		[CompilerGenerated]
		private sealed class <LogSpawnedScrapNextFrame>d__12 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public RoundManager mgr;

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

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

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

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

			private bool MoveNext()
			{
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					<>2__current = null;
					<>1__state = 1;
					return true;
				case 1:
					<>1__state = -1;
					<>2__current = null;
					<>1__state = 2;
					return true;
				case 2:
					<>1__state = -1;
					LogSpawnedScrapBody(mgr);
					return false;
				}
			}

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

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

		private const string BaseItemName = "Large axle";

		private static readonly List<Item> _customItems = new List<Item>();

		private static readonly Dictionary<Item, (int weight, int copies)> _spawnConfig = new Dictionary<Item, (int, int)>();

		private static bool _builtPrefabs;

		public static IReadOnlyList<Item> CustomItems => _customItems;

		[HarmonyPatch(typeof(GameNetworkManager), "Start")]
		[HarmonyPostfix]
		private static void OnGameNetworkManagerStart()
		{
			TryBuildPrefabs(null);
		}

		[HarmonyPatch(typeof(StartOfRound), "Awake")]
		[HarmonyPrefix]
		[HarmonyPriority(800)]
		private static void OnStartOfRoundAwake(StartOfRound __instance)
		{
			TryBuildPrefabs(__instance);
		}

		private static void TryBuildPrefabs(StartOfRound startOfRoundForLookup)
		{
			if (_builtPrefabs)
			{
				return;
			}
			string directoryName = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
			if (string.IsNullOrEmpty(directoryName))
			{
				Plugin.Logger.LogWarning((object)"ObjScrapLoader: couldn't locate mod folder; skipping OBJ scrap.");
				_builtPrefabs = true;
				return;
			}
			string[] files = Directory.GetFiles(directoryName, "*.obj", SearchOption.TopDirectoryOnly);
			Plugin.Logger.LogInfo((object)$"ObjScrapLoader: scanning '{directoryName}', found {files.Length} OBJ model(s).");
			if (files.Length == 0)
			{
				_builtPrefabs = true;
				return;
			}
			Item val = null;
			if ((Object)(object)startOfRoundForLookup != (Object)null && (Object)(object)startOfRoundForLookup.allItemsList != (Object)null && startOfRoundForLookup.allItemsList.itemsList != null)
			{
				val = ((IEnumerable<Item>)startOfRoundForLookup.allItemsList.itemsList).FirstOrDefault((Func<Item, bool>)((Item i) => (Object)(object)i != (Object)null && i.itemName == "Large axle"));
			}
			if ((Object)(object)val == (Object)null)
			{
				val = ((IEnumerable<Item>)Resources.FindObjectsOfTypeAll<Item>()).FirstOrDefault((Func<Item, bool>)((Item i) => (Object)(object)i != (Object)null && i.itemName == "Large axle"));
			}
			if ((Object)(object)val == (Object)null || (Object)(object)val.spawnPrefab == (Object)null)
			{
				Plugin.Logger.LogWarning((object)"ObjScrapLoader: base item 'Large axle' not found yet — will retry on next hook.");
				return;
			}
			_builtPrefabs = true;
			string[] array = files;
			foreach (string text in array)
			{
				try
				{
					Item val2 = BuildItemFromObj(val, text);
					if (!((Object)(object)val2 == (Object)null))
					{
						NetworkHash.AssignForObjItem(val2.spawnPrefab, val2.itemName);
						int num;
						int num2;
						if (_spawnConfig.TryGetValue(val2, out (int, int) value))
						{
							(num, num2) = value;
						}
						else
						{
							int value2 = Plugin.Config.ObjDefaultSpawnWeight.Value;
							int value3 = Plugin.Config.ObjDefaultSpawnCopies.Value;
							num2 = value3;
							num = value2;
						}
						int num3 = num * num2;
						Items.RegisterScrap(val2, num3, (LevelTypes)(-1));
						if (PrefabPreRegistrar.ObjPending.TryGetValue(text, out var value4))
						{
							value4.RealPrefab = val2.spawnPrefab;
						}
						else
						{
							Plugin.Logger.LogWarning((object)("ObjScrapLoader: built '" + val2.itemName + "' but no pre-registered placeholder found for " + Path.GetFileName(text) + " — clients will not be able to spawn this item. (This usually means the file was added to the plugin folder after the game started.)"));
						}
						_customItems.Add(val2);
						Plugin.Logger.LogInfo((object)("ObjScrapLoader: built '" + val2.itemName + "' from " + Path.GetFileName(text) + " — " + $"registered with LethalLib (rarity {num3} = {num}×{num2})."));
					}
				}
				catch (Exception arg)
				{
					Plugin.Logger.LogError((object)$"ObjScrapLoader: failed on {Path.GetFileName(text)}: {arg}");
				}
			}
		}

		[HarmonyPatch(typeof(StartOfRound), "Awake")]
		[HarmonyPostfix]
		[HarmonyPriority(0)]
		private static void FallbackSeedAfterLethalLib(StartOfRound __instance)
		{
			//IL_017c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0186: Expected O, but got Unknown
			TryBuildPrefabs(__instance);
			if (__instance?.allItemsList?.itemsList != null)
			{
				foreach (Item customItem in _customItems)
				{
					if (!__instance.allItemsList.itemsList.Contains(customItem))
					{
						__instance.allItemsList.itemsList.Add(customItem);
						Plugin.Logger.LogInfo((object)("ObjScrapLoader [fallback]: added '" + customItem.itemName + "' to allItemsList."));
					}
				}
			}
			if (__instance?.levels == null)
			{
				return;
			}
			SelectableLevel[] levels = __instance.levels;
			foreach (SelectableLevel val in levels)
			{
				if (val?.spawnableScrap == null)
				{
					continue;
				}
				foreach (Item item in _customItems)
				{
					int num;
					int num2;
					if (_spawnConfig.TryGetValue(item, out (int, int) value))
					{
						(num, num2) = value;
					}
					else
					{
						int value2 = Plugin.Config.ObjDefaultSpawnWeight.Value;
						int value3 = Plugin.Config.ObjDefaultSpawnCopies.Value;
						num2 = value3;
						num = value2;
					}
					int num3 = val.spawnableScrap.Count((SpawnableItemWithRarity s) => (Object)(object)s.spawnableItem == (Object)(object)item);
					int num4 = num2 - num3;
					if (num4 > 0)
					{
						for (int j = 0; j < num4; j++)
						{
							val.spawnableScrap.Add(new SpawnableItemWithRarity(item, num));
						}
						Plugin.Logger.LogInfo((object)($"ObjScrapLoader [fallback]: seeded '{item.itemName}' x{num4} on " + $"'{val.PlanetName}' (weight {num}). Total for this item: {num3 + num4}."));
					}
				}
			}
		}

		[HarmonyPatch(typeof(RoundManager), "SpawnScrapInLevel")]
		[HarmonyPrefix]
		private static void LogSpawnPoolBeforeRoll(RoundManager __instance)
		{
			if (!Plugin.Config.LogSpawnPoolBeforeRoll.Value)
			{
				return;
			}
			SelectableLevel val = __instance?.currentLevel;
			if (val?.spawnableScrap == null)
			{
				Plugin.Logger.LogWarning((object)"SpawnScrapInLevel: currentLevel or spawnableScrap was null.");
				return;
			}
			int count = val.spawnableScrap.Count;
			int num = val.spawnableScrap.Count((SpawnableItemWithRarity s) => _customItems.Contains(s.spawnableItem));
			Plugin.Logger.LogInfo((object)($"SpawnScrapInLevel({val.PlanetName}): spawnableScrap has {count} entries, " + $"{num} of which are ours. minScrap={val.minScrap}, maxScrap={val.maxScrap}."));
			IOrderedEnumerable<string> values = from s in val.spawnableScrap
				group s by s.spawnableItem?.itemName ?? "<null>" into g
				select $"{g.Key}:{g.Count()}x@w{g.First().rarity}" into s
				orderby s
				select s;
			Plugin.Logger.LogInfo((object)("  pool: " + string.Join(", ", values)));
		}

		[HarmonyPatch(typeof(RoundManager), "SpawnScrapInLevel")]
		[HarmonyPostfix]
		private static void LogSpawnedScrapAfterRoll(RoundManager __instance)
		{
			if (Plugin.Config.LogSpawnPoolBeforeRoll.Value)
			{
				if ((Object)(object)Plugin.Instance != (Object)null)
				{
					((MonoBehaviour)Plugin.Instance).StartCoroutine(LogSpawnedScrapNextFrame(__instance));
				}
				else
				{
					LogSpawnedScrapBody(__instance);
				}
			}
		}

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

		private static void LogSpawnedScrapBody(RoundManager __instance)
		{
			//IL_02b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_02bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0324: Unknown result type (might be due to invalid IL or missing references)
			//IL_0329: Unknown result type (might be due to invalid IL or missing references)
			//IL_032d: Unknown result type (might be due to invalid IL or missing references)
			//IL_037e: Unknown result type (might be due to invalid IL or missing references)
			HashSet<Item> ourItems = new HashSet<Item>(_customItems);
			foreach (Item customItem in PngScrapLoader.CustomItems)
			{
				ourItems.Add(customItem);
			}
			List<GrabbableObject> list = (from g in Object.FindObjectsOfType<GrabbableObject>(true)
				where (Object)(object)g != (Object)null && (Object)(object)g.itemProperties != (Object)null && g.itemProperties.isScrap
				select g).ToList();
			List<GrabbableObject> list2 = list.Where((GrabbableObject g) => (((Object)((Component)g).gameObject).hideFlags & 0x3D) == 0).ToList();
			int num = list.Count((GrabbableObject g) => ourItems.Contains(g.itemProperties));
			int num2 = list.Count((GrabbableObject g) => ourItems.Contains(g.itemProperties) && (((Object)((Component)g).gameObject).hideFlags & 0x3D) > 0);
			int num3 = num - num2;
			Plugin.Logger.LogInfo((object)($"After SpawnScrapInLevel: {list2.Count} non-template scrap in scene. " + $"Ours: {num3} clones + {num2} templates = {num} total."));
			IOrderedEnumerable<string> values = from g in list2
				group g by g.itemProperties.itemName into g
				select $"{g.Key}:{g.Count()}" into s
				orderby s
				select s;
			Plugin.Logger.LogInfo((object)("  in world (non-template): " + string.Join(", ", values)));
			var (dictionary, dictionary2) = GrabbableObjectGuard.DrainCounters();
			if (dictionary.Count > 0 || dictionary2.Count > 0)
			{
				string text = string.Join(", ", dictionary.Select((KeyValuePair<string, int> kv) => kv.Key + ":" + kv.Value));
				string text2 = string.Join(", ", dictionary2.Select((KeyValuePair<string, int> kv) => kv.Key + ":" + kv.Value));
				Plugin.Logger.LogInfo((object)("  during spawn loop: Start-entries [" + text + "]   Start-successes [" + text2 + "]"));
			}
			else
			{
				Plugin.Logger.LogInfo((object)"  during spawn loop: NO Start calls on any of our items — RoundManager never Instantiated our prefabs this roll.");
			}
			foreach (GrabbableObject item in list)
			{
				if (ourItems.Contains(item.itemProperties) && (((Object)((Component)item).gameObject).hideFlags & 0x3D) == 0)
				{
					MeshRenderer componentInChildren = ((Component)item).GetComponentInChildren<MeshRenderer>(true);
					string text3 = (((Object)(object)((Component)item).transform.parent != (Object)null) ? ((Object)((Component)item).transform.parent).name : "<none>");
					object obj;
					if (!((Object)(object)componentInChildren == (Object)null))
					{
						object arg = ((Renderer)componentInChildren).enabled;
						object arg2 = ((Renderer)componentInChildren).isVisible;
						Bounds bounds = ((Renderer)componentInChildren).bounds;
						obj = $"renderer(enabled={arg},visible={arg2},bounds={((Bounds)(ref bounds)).size})";
					}
					else
					{
						obj = "no-renderer";
					}
					string text4 = (string)obj;
					Plugin.Logger.LogInfo((object)("  [ours] '" + item.itemProperties.itemName + "' " + $"pos={((Component)item).transform.position} " + $"active={((Component)item).gameObject.activeInHierarchy} " + "parent=" + text3 + " " + text4));
				}
			}
		}

		private static Item BuildItemFromObj(Item baseItem, string objPath)
		{
			//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)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: 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_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_018f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0194: Unknown result type (might be due to invalid IL or missing references)
			//IL_019b: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0289: Unknown result type (might be due to invalid IL or missing references)
			//IL_028e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0292: Unknown result type (might be due to invalid IL or missing references)
			//IL_029f: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a8: Unknown result type (might be due to invalid IL or missing references)
			string text = Path.GetDirectoryName(objPath) ?? string.Empty;
			string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(objPath);
			string textureFile;
			Mesh val = ObjLoader.LoadObj(objPath, out textureFile);
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			float num = Sidecars.ResolveScale(text, fileNameWithoutExtension);
			float value = Plugin.Config.ObjDefaultSizeMeters.Value;
			Bounds bounds = val.bounds;
			float num2 = Mathf.Max(((Bounds)(ref bounds)).size.x, Mathf.Max(((Bounds)(ref bounds)).size.y, ((Bounds)(ref bounds)).size.z));
			float num3 = ((num2 > 0.0001f) ? (value / num2) : 1f) * num;
			Vector3 center = ((Bounds)(ref bounds)).center;
			Vector3[] vertices = val.vertices;
			for (int i = 0; i < vertices.Length; i++)
			{
				vertices[i] = (vertices[i] - center) * num3;
			}
			val.vertices = vertices;
			val.RecalculateBounds();
			Texture2D val2 = TryLoadTexture(text, fileNameWithoutExtension, textureFile);
			string text2 = Sidecars.ResolveDisplayName(text, fileNameWithoutExtension, "Gray Cat");
			(int min, int max) tuple = Sidecars.ResolveValueRange(text, fileNameWithoutExtension, Plugin.Config.ObjDefaultMinValue.Value, Plugin.Config.ObjDefaultMaxValue.Value);
			int item = tuple.min;
			int item2 = tuple.max;
			Item val3 = Object.Instantiate<Item>(baseItem);
			((Object)val3).name = text2;
			val3.itemName = text2;
			val3.minValue = item;
			val3.maxValue = item2;
			val3.weight = 1f;
			val3.isScrap = true;
			val3.canBeGrabbedBeforeGameStart = false;
			val3.twoHanded = false;
			val3.twoHandedAnimation = false;
			val3.rotationOffset = Vector3.zero;
			val3.restingRotation = Vector3.zero;
			val3.positionOffset = Vector3.zero;
			Bounds bounds2 = val.bounds;
			val3.verticalOffset = ((Bounds)(ref bounds2)).extents.y;
			Sidecars.ApplyHoldConfig(text, fileNameWithoutExtension, val3);
			_spawnConfig[val3] = Sidecars.ResolveSpawnConfig(text, fileNameWithoutExtension, Plugin.Config.ObjDefaultSpawnWeight.Value, Plugin.Config.ObjDefaultSpawnCopies.Value);
			GameObject val4 = Object.Instantiate<GameObject>(baseItem.spawnPrefab);
			Object.DontDestroyOnLoad((Object)(object)val4);
			((Object)val4).hideFlags = (HideFlags)61;
			((Object)val4).name = text2;
			SharedBuilder.NormaliseTransformScales(val4);
			GrabbableObject component = val4.GetComponent<GrabbableObject>();
			if ((Object)(object)component != (Object)null)
			{
				component.itemProperties = val3;
			}
			SharedBuilder.StripTemplateVisuals(val4);
			val4.AddComponent<MeshFilter>().sharedMesh = val;
			MeshRenderer val5 = val4.AddComponent<MeshRenderer>();
			((Renderer)val5).sharedMaterial = SharedBuilder.CreateDiffuseMaterial(val2, text2);
			BoxCollider val6 = val4.AddComponent<BoxCollider>();
			bounds2 = val.bounds;
			val6.size = ((Bounds)(ref bounds2)).size;
			bounds2 = val.bounds;
			val6.center = ((Bounds)(ref bounds2)).center;
			((Collider)val6).isTrigger = false;
			SharedBuilder.RegisterMainRenderer(component, val5, (Collider)(object)val6);
			SharedBuilder.EnsureAudioSource(val4);
			SharedBuilder.ConfigureScanNodes(val4, text2, item);
			CatShotgunMarker catShotgunMarker = val4.AddComponent<CatShotgunMarker>();
			catShotgunMarker.Pellets = Plugin.Config.ShotgunPellets.Value;
			catShotgunMarker.DamagePerPellet = Plugin.Config.ShotgunDamage.Value;
			catShotgunMarker.Range = Plugin.Config.ShotgunRange.Value;
			catShotgunMarker.SpreadDegrees = Plugin.Config.ShotgunSpread.Value;
			catShotgunMarker.CooldownSeconds = Plugin.Config.ShotgunCooldown.Value;
			ApplyShotgunConfig(text, fileNameWithoutExtension, catShotgunMarker);
			string path = Path.Combine(text, fileNameWithoutExtension + ".fire.wav");
			string path2 = Path.Combine(text, fileNameWithoutExtension + ".wav");
			if (File.Exists(path))
			{
				catShotgunMarker.FireSfx = WavLoader.Load(path);
			}
			if (File.Exists(path2))
			{
				AudioClip val7 = WavLoader.Load(path2);
				if ((Object)(object)val7 != (Object)null)
				{
					val3.grabSFX = val7;
					if ((Object)(object)catShotgunMarker.FireSfx == (Object)null)
					{
						catShotgunMarker.FireSfx = val7;
					}
				}
			}
			val3.dropSFX = null;
			val3.pocketSFX = null;
			val3.throwSFX = null;
			ManualLogSource logger = Plugin.Logger;
			string[] obj = new string[8]
			{
				$"ObjScrapLoader: '{text2}' — value {item}-{item2}, ",
				$"shotgun(pellets={catShotgunMarker.Pellets}, dmg={catShotgunMarker.DamagePerPellet}, ",
				$"range={catShotgunMarker.Range}m, spread={catShotgunMarker.SpreadDegrees}°, cd={catShotgunMarker.CooldownSeconds}s), ",
				"fireSfx=",
				null,
				null,
				null,
				null
			};
			AudioClip fireSfx = catShotgunMarker.FireSfx;
			obj[4] = ((fireSfx != null) ? ((Object)fireSfx).name : null) ?? "<none>";
			obj[5] = ", tex=";
			obj[6] = ((val2 != null) ? ((Object)val2).name : null) ?? "<none>";
			obj[7] = ".";
			logger.LogInfo((object)string.Concat(obj));
			val3.spawnPrefab = val4;
			return val3;
		}

		private static Texture2D TryLoadTexture(string dir, string stem, string mtlTextureFile)
		{
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Expected O, but got Unknown
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Expected O, but got Unknown
			string text = null;
			if (!string.IsNullOrEmpty(mtlTextureFile))
			{
				string text2 = Path.Combine(dir, mtlTextureFile);
				if (File.Exists(text2))
				{
					text = text2;
				}
				else
				{
					text2 = Path.Combine(dir, Path.GetFileName(mtlTextureFile));
					if (File.Exists(text2))
					{
						text = text2;
					}
				}
			}
			if (text == null)
			{
				string text3 = Path.Combine(dir, stem + ".png");
				if (File.Exists(text3))
				{
					text = text3;
				}
			}
			if (text == null)
			{
				return null;
			}
			try
			{
				byte[] array = File.ReadAllBytes(text);
				Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false);
				ImageConversion.LoadImage(val, array);
				((Texture)val).filterMode = (FilterMode)1;
				((Texture)val).wrapMode = (TextureWrapMode)1;
				((Object)val).name = Path.GetFileNameWithoutExtension(text);
				return val;
			}
			catch (Exception ex)
			{
				Plugin.Logger.LogWarning((object)("ObjScrapLoader: failed to load texture '" + text + "': " + ex.Message));
				return null;
			}
		}

		private static void ApplyShotgunConfig(string dir, string stem, CatShotgunMarker marker)
		{
			ApplyShotgunFromCentral(stem, marker);
			string path = Path.Combine(dir, stem + ".shotgun.txt");
			if (!File.Exists(path))
			{
				return;
			}
			string[] array = File.ReadAllLines(path);
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i].Trim();
				if (text.Length != 0 && !text.StartsWith("#"))
				{
					int num = text.IndexOf('=');
					if (num > 0)
					{
						string key = text.Substring(0, num).Trim().ToLowerInvariant();
						string val = text.Substring(num + 1).Trim();
						ApplyShotgunKey(marker, key, val);
					}
				}
			}
		}

		private static void ApplyShotgunFromCentral(string stem, CatShotgunMarker marker)
		{
			string[] array = new string[5] { "pellets", "damage", "range", "spread", "cooldown" };
			foreach (string key in array)
			{
				if (CentralItemConfig.TryGet(stem, key, out var value))
				{
					ApplyShotgunKey(marker, key, value);
				}
			}
		}

		private static void ApplyShotgunKey(CatShotgunMarker marker, string key, string val)
		{
			switch (key)
			{
			case "pellets":
			{
				if (int.TryParse(val, out var result2) && result2 > 0)
				{
					marker.Pellets = result2;
				}
				break;
			}
			case "damage":
			{
				if (int.TryParse(val, out var result5) && result5 >= 0)
				{
					marker.DamagePerPellet = result5;
				}
				break;
			}
			case "range":
			{
				if (float.TryParse(val, NumberStyles.Float, CultureInfo.InvariantCulture, out var result3) && result3 > 0f)
				{
					marker.Range = result3;
				}
				break;
			}
			case "spread":
			{
				if (float.TryParse(val, NumberStyles.Float, CultureInfo.InvariantCulture, out var result4) && result4 >= 0f)
				{
					marker.SpreadDegrees = result4;
				}
				break;
			}
			case "cooldown":
			{
				if (float.TryParse(val, NumberStyles.Float, CultureInfo.InvariantCulture, out var result) && result >= 0f)
				{
					marker.CooldownSeconds = result;
				}
				break;
			}
			}
		}
	}
	[HarmonyPatch]
	internal static class PngScrapLoader
	{
		private const string BaseItemName = "Large axle";

		private const float CardThicknessMeters = 0.05f;

		private static readonly List<Item> _customItems = new List<Item>();

		private static readonly Dictionary<Item, (int weight, int copies)> _spawnConfig = new Dictionary<Item, (int, int)>();

		private static bool _builtPrefabs;

		private static bool _loggedVanillaNames;

		public static IReadOnlyList<Item> CustomItems => _customItems;

		[HarmonyPatch(typeof(GameNetworkManager), "Start")]
		[HarmonyPostfix]
		private static void OnGameNetworkManagerStart()
		{
			TryBuildPrefabs(null);
		}

		[HarmonyPatch(typeof(StartOfRound), "Awake")]
		[HarmonyPrefix]
		[HarmonyPriority(800)]
		private static void OnStartOfRoundAwake(StartOfRound __instance)
		{
			TryBuildPrefabs(__instance);
		}

		private static void TryBuildPrefabs(StartOfRound startOfRoundForLookup)
		{
			if (_builtPrefabs)
			{
				return;
			}
			string directoryName = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
			if (string.IsNullOrEmpty(directoryName))
			{
				Plugin.Logger.LogWarning((object)"PngScrapLoader: couldn't locate mod folder; skipping PNG scrap.");
				_builtPrefabs = true;
				return;
			}
			string[] files = Directory.GetFiles(directoryName, "*.png", SearchOption.TopDirectoryOnly);
			Plugin.Logger.LogInfo((object)$"PngScrapLoader: scanning '{directoryName}', found {files.Length} PNG(s).");
			if (files.Length == 0)
			{
				_builtPrefabs = true;
				return;
			}
			HashSet<string> hashSet = new HashSet<string>(from p in Directory.GetFiles(directoryName, "*.obj", SearchOption.TopDirectoryOnly)
				select Path.GetFileNameWithoutExtension(p), StringComparer.OrdinalIgnoreCase);
			Item val = null;
			if ((Object)(object)startOfRoundForLookup != (Object)null && (Object)(object)startOfRoundForLookup.allItemsList != (Object)null && startOfRoundForLookup.allItemsList.itemsList != null)
			{
				val = ((IEnumerable<Item>)startOfRoundForLookup.allItemsList.itemsList).FirstOrDefault((Func<Item, bool>)((Item i) => (Object)(object)i != (Object)null && i.itemName == "Large axle"));
			}
			if ((Object)(object)val == (Object)null)
			{
				val = ((IEnumerable<Item>)Resources.FindObjectsOfTypeAll<Item>()).FirstOrDefault((Func<Item, bool>)((Item i) => (Object)(object)i != (Object)null && i.itemName == "Large axle"));
			}
			if ((Object)(object)val == (Object)null || (Object)(object)val.spawnPrefab == (Object)null)
			{
				Plugin.Logger.LogWarning((object)"PngScrapLoader: base item 'Large axle' not found yet — will retry on next hook. Enable Debug > LogVanillaItemNamesOnAwake in config to see available names if it still fails.");
				return;
			}
			_builtPrefabs = true;
			string[] array = files;
			foreach (string text in array)
			{
				string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(text);
				if (hashSet.Contains(fileNameWithoutExtension))
				{
					Plugin.Logger.LogInfo((object)("PngScrapLoader: skipping '" + Path.GetFileName(text) + "' — belongs to " + fileNameWithoutExtension + ".obj as its texture."));
					continue;
				}
				try
				{
					Item val2 = BuildItemFromPng(val, text);
					if (!((Object)(object)val2 == (Object)null))
					{
						NetworkHash.AssignForPngItem(val2.spawnPrefab, val2.itemName);
						int num;
						int num2;
						if (_spawnConfig.TryGetValue(val2, out (int, int) value))
						{
							(num, num2) = value;
						}
						else
						{
							int value2 = Plugin.Config.PngDefaultSpawnWeight.Value;
							int value3 = Plugin.Config.PngDefaultSpawnCopies.Value;
							num2 = value3;
							num = value2;
						}
						int num3 = num * num2;
						Items.RegisterScrap(val2, num3, (LevelTypes)(-1));
						if (PrefabPreRegistrar.PngPending.TryGetValue(text, out var value4))
						{
							value4.RealPrefab = val2.spawnPrefab;
						}
						else
						{
							Plugin.Logger.LogWarning((object)("PngScrapLoader: built '" + val2.itemName + "' but no pre-registered placeholder found for " + Path.GetFileName(text) + " — clients will not be able to spawn this item."));
						}
						_customItems.Add(val2);
						Plugin.Logger.LogInfo((object)("PngScrapLoader: built '" + val2.itemName + "' from " + Path.GetFileName(text) + " — " + $"registered with LethalLib (rarity {num3} = {num}×{num2})."));
					}
				}
				catch (Exception arg)
				{
					Plugin.Logger.LogError((object)$"PngScrapLoader: failed on {Path.GetFileName(text)}: {arg}");
				}
			}
		}

		[HarmonyPatch(typeof(StartOfRound), "Awake")]
		[HarmonyPostfix]
		[HarmonyPriority(0)]
		private static void FallbackSeedAfterLethalLib(StartOfRound __instance)
		{
			//IL_0205: Unknown result type (might be due to invalid IL or missing references)
			//IL_020f: Expected O, but got Unknown
			TryBuildPrefabs(__instance);
			if (__instance?.allItemsList?.itemsList == null)
			{
				return;
			}
			if (!_loggedVanillaNames && Plugin.Config.LogVanillaItemNamesOnAwake.Value)
			{
				_loggedVanillaNames = true;
				Plugin.Logger.LogInfo((object)"PngScrapLoader: vanilla item names (for reference):");
				foreach (Item items in __instance.allItemsList.itemsList)
				{
					if ((Object)(object)items != (Object)null)
					{
						Plugin.Logger.LogInfo((object)("  - \"" + items.itemName + "\""));
					}
				}
			}
			foreach (Item customItem in _customItems)
			{
				if (!__instance.allItemsList.itemsList.Contains(customItem))
				{
					__instance.allItemsList.itemsList.Add(customItem);
					Plugin.Logger.LogInfo((object)("PngScrapLoader [fallback]: added '" + customItem.itemName + "' to allItemsList."));
				}
			}
			if (__instance.levels == null)
			{
				return;
			}
			SelectableLevel[] levels = __instance.levels;
			foreach (SelectableLevel val in levels)
			{
				if (val?.spawnableScrap == null)
				{
					continue;
				}
				foreach (Item item in _customItems)
				{
					int num;
					int num2;
					if (_spawnConfig.TryGetValue(item, out (int, int) value))
					{
						(num, num2) = value;
					}
					else
					{
						int value2 = Plugin.Config.PngDefaultSpawnWeight.Value;
						int value3 = Plugin.Config.PngDefaultSpawnCopies.Value;
						num2 = value3;
						num = value2;
					}
					int num3 = val.spawnableScrap.Count((SpawnableItemWithRarity s) => (Object)(object)s.spawnableItem == (Object)(object)item);
					int num4 = num2 - num3;
					if (num4 > 0)
					{
						for (int j = 0; j < num4; j++)
						{
							val.spawnableScrap.Add(new SpawnableItemWithRarity(item, num));
						}
						Plugin.Logger.LogInfo((object)($"PngScrapLoader [fallback]: seeded '{item.itemName}' x{num4} on " + $"'{val.PlanetName}' (weight {num}). Total for this item: {num3 + num4}."));
					}
				}
			}
		}

		private static Item BuildItemFromPng(Item baseItem, string pngPath)
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Expected O, but got Unknown
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_0108: Unknown result type (might be due to invalid IL or missing references)
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_020b: Unknown result type (might be due to invalid IL or missing references)
			string dir = Path.GetDirectoryName(pngPath) ?? string.Empty;
			string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(pngPath);
			byte[] array = File.ReadAllBytes(pngPath);
			Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false);
			ImageConversion.LoadImage(val, array);
			((Texture)val).filterMode = (FilterMode)1;
			((Texture)val).wrapMode = (TextureWrapMode)1;
			((Object)val).name = fileNameWithoutExtension;
			string text = Sidecars.ResolveDisplayName(dir, fileNameWithoutExtension, "Mystery Photo");
			(int min, int max) tuple = Sidecars.ResolveValueRange(dir, fileNameWithoutExtension, Plugin.Config.PngDefaultMinValue.Value, Plugin.Config.PngDefaultMaxValue.Value);
			int item = tuple.min;
			int item2 = tuple.max;
			Item val2 = Object.Instantiate<Item>(baseItem);
			((Object)val2).name = text;
			val2.itemName = text;
			val2.minValue = item;
			val2.maxValue = item2;
			val2.weight = 1f;
			val2.isScrap = true;
			val2.canBeGrabbedBeforeGameStart = false;
			val2.twoHanded = false;
			val2.twoHandedAnimation = false;
			val2.rotationOffset = new Vector3(0f, 180f, 0f);
			val2.restingRotation = Vector3.zero;
			val2.positionOffset = Vector3.zero;
			float value = Plugin.Config.PngCardHeightMeters.Value;
			val2.verticalOffset = value * 0.5f;
			Sidecars.ApplyHoldConfig(dir, fileNameWithoutExtension, val2);
			GameObject val3 = Object.Instantiate<GameObject>(baseItem.spawnPrefab);
			Object.DontDestroyOnLoad((Object)(object)val3);
			((Object)val3).hideFlags = (HideFlags)61;
			((Object)val3).name = text;
			SharedBuilder.NormaliseTransformScales(val3);
			GrabbableObject component = val3.GetComponent<GrabbableObject>();
			if ((Object)(object)component != (Object)null)
			{
				component.itemProperties = val2;
			}
			SharedBuilder.StripTemplateVisuals(val3);
			float num = ((((Texture)val).height > 0) ? ((float)((Texture)val).width / (float)((Texture)val).height) : 1f);
			float num2 = value * num;
			float num3 = 0.05f;
			Mesh sharedMesh = BuildFlatCardMesh(num2, value, num3);
			val3.AddComponent<MeshFilter>().sharedMesh = sharedMesh;
			MeshRenderer val4 = val3.AddComponent<MeshRenderer>();
			((Renderer)val4).sharedMaterial = SharedBuilder.CreateDiffuseMaterial(val, text);
			BoxCollider val5 = val3.AddComponent<BoxCollider>();
			val5.size = new Vector3(num2, value, num3);
			val5.center = Vector3.zero;
			((Collider)val5).isTrigger = false;
			SharedBuilder.RegisterMainRenderer(component, val4, (Collider)(object)val5);
			SharedBuilder.EnsureAudioSource(val3);
			SharedBuilder.ConfigureScanNodes(val3, text, item);
			TryLoadSidecarAudio(dir, fileNameWithoutExtension, val2);
			_spawnConfig[val2] = Sidecars.ResolveSpawnConfig(dir, fileNameWithoutExtension, Plugin.Config.PngDefaultSpawnWeight.Value, Plugin.Config.PngDefaultSpawnCopies.Value);
			ManualLogSource logger = Plugin.Logger;
			string[] obj = new string[5]
			{
				$"PngScrapLoader: '{val2.itemName}' — value {item}-{item2}, ",
				$"card {num2:F2}×{value:F2}m, ",
				"grabSFX=",
				null,
				null
			};
			AudioClip grabSFX = val2.grabSFX;
			obj[3] = ((grabSFX != null) ? ((Object)grabSFX).name : null) ?? "<none>";
			obj[4] = ".";
			logger.LogInfo((object)string.Concat(obj));
			val2.spawnPrefab = val3;
			return val2;
		}

		private static void TryLoadSidecarAudio(string dir, string stem, Item item)
		{
			string path = Path.Combine(dir, stem + ".wav");
			string path2 = Path.Combine(dir, stem + ".drop.wav");
			if (File.Exists(path))
			{
				AudioClip val = WavLoader.Load(path);
				if ((Object)(object)val != (Object)null)
				{
					AssignSfx(item, "grabSFX", val);
					Plugin.Logger.LogInfo((object)("PngScrapLoader: loaded '" + Path.GetFileName(path) + "' " + $"({val.length:F2}s) for '{item.itemName}'."));
				}
			}
			AssignSfx(item, "dropSFX", null);
			AssignSfx(item, "pocketSFX", null);
			AssignSfx(item, "throwSFX", null);
			if (File.Exists(path2))
			{
				AudioClip val2 = WavLoader.Load(path2);
				if ((Object)(object)val2 != (Object)null)
				{
					AssignSfx(item, "dropSFX", val2);
					Plugin.Logger.LogInfo((object)("PngScrapLoader: drop SFX '" + Path.GetFileName(path2) + "' for '" + item.itemName + "'."));
				}
			}
		}

		private static void AssignSfx(Item item, string fieldName, AudioClip clip)
		{
			if (!((Object)(object)item == (Object)null))
			{
				FieldInfo field = typeof(Item).GetField(fieldName, BindingFlags.Instance | BindingFlags.Public);
				if (!(field == null) && !(field.FieldType != typeof(AudioClip)))
				{
					field.SetValue(item, clip);
				}
			}
		}

		private static Mesh BuildFlatCardMesh(float w, float h, float d)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: 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_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: 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_00ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_0129: Unknown result type (might be due to invalid IL or missing references)
			//IL_0135: Unknown result type (might be due to invalid IL or missing references)
			//IL_013a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0147: Unknown result type (might be due to invalid IL or missing references)
			//IL_014c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0158: Unknown result type (might be due to invalid IL or missing references)
			//IL_015d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0168: Unknown result type (might be due to invalid IL or missing references)
			//IL_016d: Unknown result type (might be due to invalid IL or missing references)
			//IL_017b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0180: Unknown result type (might be due to invalid IL or missing references)
			//IL_018d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0192: Unknown result type (might be due to invalid IL or missing references)
			//IL_019e: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d6: 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_01ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0204: Unknown result type (might be due to invalid IL or missing references)
			//IL_0216: Unknown result type (might be due to invalid IL or missing references)
			//IL_021b: Unknown result type (might be due to invalid IL or missing references)
			//IL_022d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0232: Unknown result type (might be due to invalid IL or missing references)
			//IL_0244: Unknown result type (might be due to invalid IL or missing references)
			//IL_0249: Unknown result type (might be due to invalid IL or missing references)
			//IL_025b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0260: Unknown result type (might be due to invalid IL or missing references)
			//IL_0272: Unknown result type (might be due to invalid IL or missing references)
			//IL_0277: Unknown result type (might be due to invalid IL or missing references)
			//IL_028f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0294: Unknown result type (might be due to invalid IL or missing references)
			//IL_0317: 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)
			//IL_0327: Unknown result type (might be due to invalid IL or missing references)
			//IL_032e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0336: Unknown result type (might be due to invalid IL or missing references)
			//IL_033e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0344: Unknown result type (might be due to invalid IL or missing references)
			//IL_034a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0351: Expected O, but got Unknown
			float num = w * 0.5f;
			float num2 = h * 0.5f;
			float num3 = d * 0.5f;
			Vector3[] vertices = (Vector3[])(object)new Vector3[24]
			{
				new Vector3(0f - num, 0f - num2, num3),
				new Vector3(num, 0f - num2, num3),
				new Vector3(num, num2, num3),
				new Vector3(0f - num, num2, num3),
				new Vector3(num, 0f - num2, 0f - num3),
				new Vector3(0f - num, 0f - num2, 0f - num3),
				new Vector3(0f - num, num2, 0f - num3),
				new Vector3(num, num2, 0f - num3),
				new Vector3(0f - num, num2, num3),
				new Vector3(num, num2, num3),
				new Vector3(num, num2, 0f - num3),
				new Vector3(0f - num, num2, 0f - num3),
				new Vector3(num, 0f - num2, num3),
				new Vector3(0f - num, 0f - num2, num3),
				new Vector3(0f - num, 0f - num2, 0f - num3),
				new Vector3(num, 0f - num2, 0f - num3),
				new Vector3(num, 0f - num2, num3),
				new Vector3(num, 0f - num2, 0f - num3),
				new Vector3(num, num2, 0f - num3),
				new Vector3(num, num2, num3),
				new Vector3(0f - num, 0f - num2, 0f - num3),
				new Vector3(0f - num, 0f - num2, num3),
				new Vector3(0f - num, num2, num3),
				new Vector3(0f - num, num2, 0f - num3)
			};
			Vector2[] array = (Vector2[])(object)new Vector2[24]
			{
				new Vector2(0f, 0f),
				new Vector2(1f, 0f),
				new Vector2(1f, 1f),
				new Vector2(0f, 1f),
				new Vector2(0f, 0f),
				new Vector2(1f, 0f),
				new Vector2(1f, 1f),
				new Vector2(0f, 1f),
				default(Vector2),
				default(Vector2),
				default(Vector2),
				default(Vector2),
				default(Vector2),
				default(Vector2),
				default(Vector2),
				default(Vector2),
				default(Vector2),
				default(Vector2),
				default(Vector2),
				default(Vector2),
				default(Vector2),
				default(Vector2),
				default(Vector2),
				default(Vector2)
			};
			for (int i = 8; i < 24; i++)
			{
				array[i] = new Vector2(0.5f, 0.5f);
			}
			int[] array2 = new int[36];
			int num4 = 0;
			for (int j = 0; j < 6; j++)
			{
				int num5 = j * 4;
				array2[num4++] = num5;
				array2[num4++] = num5 + 2;
				array2[num4++] = num5 + 1;
				array2[num4++] = num5;
				array2[num4++] = num5 + 3;
				array2[num4++] = num5 + 2;
			}
			Mesh val = new Mesh
			{
				name = "PngCardMesh",
				vertices = vertices,
				uv = array,
				triangles = array2
			};
			val.RecalculateNormals();
			val.RecalculateTangents();
			val.RecalculateBounds();
			return val;
		}
	}
	[HarmonyPatch]
	internal static class PrefabPreRegistrarPatches
	{
		private static bool _ran;

		[HarmonyPatch(typeof(GameNetworkManager), "Start")]
		[HarmonyPostfix]
		[HarmonyPriority(800)]
		private static void OnGameNetworkManagerStart()
		{
			if (!_ran)
			{
				_ran = true;
				PrefabPreRegistrar.PreRegisterAll(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location));
			}
		}
	}
	internal static class PrefabPreRegistrar
	{
		public class PendingItem
		{
			public string FilePath;

			public string ItemName;

			public uint Hash;

			public GameObject Placeholder;

			public GameObject RealPrefab;
		}

		public enum Kind
		{
			Obj,
			Png
		}

		private sealed class LateBoundPrefabHandler : INetworkPrefabInstanceHandler
		{
			private readonly PendingItem _pending;

			public LateBoundPrefabHandler(PendingItem pending)
			{
				_pending = pending;
			}

			public NetworkObject Instantiate(ulong ownerClientId, Vector3 position, Quaternion rotation)
			{
				//IL_0062: 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)
				GameObject obj = (((Object)(object)_pending.RealPrefab != (Object)null) ? _pending.RealPrefab : _pending.Placeholder);
				if ((Object)(object)_pending.RealPrefab == (Object)null)
				{
					Plugin.Logger.LogWarning((object)("PrefabPreRegistrar: spawn requested for '" + _pending.ItemName + "' but real prefab not yet hydrated — instantiating placeholder (item will be invisible until next session)."));
				}
				GameObject obj2 = Object.Instantiate<GameObject>(obj, position, rotation);
				obj2.SetActive(true);
				return obj2.GetComponent<NetworkObject>();
			}

			public void Destroy(NetworkObject networkObject)
			{
				if ((Object)(object)networkObject != (Object)null && (Object)(object)((Component)networkObject).gameObject != (Object)null)
				{
					Object.Destroy((Object)(object)((Component)networkObject).gameObject);
				}
			}
		}

		private static readonly Dictionary<string, PendingItem> _objByPath = new Dictionary<string, PendingItem>();

		private static readonly Dictionary<string, PendingItem> _pngByPath = new Dictionary<string, PendingItem>();

		public static IReadOnlyDictionary<string, PendingItem> ObjPending => _objByPath;

		public static IReadOnlyDictionary<string, PendingItem> PngPending => _pngByPath;

		public static void PreRegisterAll(string modFolder)
		{
			if (string.IsNullOrEmpty(modFolder))
			{
				return;
			}
			if ((Object)(object)NetworkManager.Singleton == (Object)null)
			{
				Plugin.Logger.LogWarning((object)"PrefabPreRegistrar: NetworkManager.Singleton is null at pre-register time — placeholders not created. Multiplayer will not sync our items.");
				return;
			}
			string[] files = Directory.GetFiles(modFolder, "*.obj", SearchOption.TopDirectoryOnly);
			for (int i = 0; i < files.Length; i++)
			{
				PreRegisterOne(files[i], Kind.Obj, _objByPath);
			}
			HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			files = Directory.GetFiles(modFolder, "*.obj", SearchOption.TopDirectoryOnly);
			foreach (string path in files)
			{
				hashSet.Add(Path.GetFileNameWithoutExtension(path));
			}
			files = Directory.GetFiles(modFolder, "*.png", SearchOption.TopDirectoryOnly);
			foreach (string text in files)
			{
				if (!hashSet.Contains(Path.GetFileNameWithoutExtension(text)))
				{
					PreRegisterOne(text, Kind.Png, _pngByPath);
				}
			}
		}

		private static void PreRegisterOne(string filePath, Kind kind, Dictionary<string, PendingItem> bucket)
		{
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Expected O, but got Unknown
			if (bucket.ContainsKey(filePath))
			{
				return;
			}
			try
			{
				string? dir = Path.GetDirectoryName(filePath) ?? string.Empty;
				string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(filePath);
				string text = Sidecars.ResolveDisplayName(dir, fileNameWithoutExtension, fileNameWithoutExtension);
				GameObject val = new GameObject($"MemeRebellionScrap.Placeholder.{kind}.{text}");
				val.SetActive(false);
				Object.DontDestroyOnLoad((Object)(object)val);
				((Object)val).hideFlags = (HideFlags)61;
				val.AddComponent<NetworkObject>();
				if (kind == Kind.Obj)
				{
					NetworkHash.AssignForObjItem(val, text);
				}
				else
				{
					NetworkHash.AssignForPngItem(val, text);
				}
				uint num = ReadHash(val.GetComponent<NetworkObject>());
				NetworkManager.Singleton.AddNetworkPrefab(val);
				PendingItem pending = (bucket[filePath] = new PendingItem
				{
					FilePath = filePath,
					ItemName = text,
					Hash = num,
					Placeholder = val,
					RealPrefab = null
				});
				NetworkManager.Singleton.PrefabHandler.AddHandler(val, (INetworkPrefabInstanceHandler)(object)new LateBoundPrefabHandler(pending));
				Plugin.Logger.LogInfo((object)($"PrefabPreRegistrar: pre-registered {kind} placeholder for '{text}' " + $"(hash={num}, file={Path.GetFileName(filePath)})."));
			}
			catch (Exception ex)
			{
				Plugin.Logger.LogError((object)("PrefabPreRegistrar: pre-register failed for " + Path.GetFileName(filePath) + ": " + ex.GetType().Name + ": " + ex.Message));
			}
		}

		private static uint ReadHash(NetworkObject netObj)
		{
			if ((Object)(object)netObj == (Object)null)
			{
				return 0u;
			}
			FieldInfo field = typeof(NetworkObject).GetField("GlobalObjectIdHash", BindingFlags.Instance | BindingFlags.NonPublic);
			if (field == null)
			{
				return 0u;
			}
			object value = field.GetValue(netObj);
			if (value is uint)
			{
				return (uint)value;
			}
			return 0u;
		}
	}
	internal static class SharedBuilder
	{
		public static void StripTemplateVisuals(GameObject prefab)
		{
			MeshFilter[] componentsInChildren = prefab.GetComponentsInChildren<MeshFilter>(true);
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				Object.DestroyImmediate((Object)(object)componentsInChildren[i]);
			}
			MeshRenderer[] componentsInChildren2 = prefab.GetComponentsInChildren<MeshRenderer>(true);
			for (int i = 0; i < componentsInChildren2.Length; i++)
			{
				Object.DestroyImmediate((Object)(object)componentsInChildren2[i]);
			}
			SkinnedMeshRenderer[] componentsInChildren3 = prefab.GetComponentsInChildren<SkinnedMeshRenderer>(true);
			for (int i = 0; i < componentsInChildren3.Length; i++)
			{
				Object.DestroyImmediate((Object)(object)componentsInChildren3[i]);
			}
			LODGroup[] componentsInChildren4 = prefab.GetComponentsInChildren<LODGroup>(true);
			for (int i = 0; i < componentsInChildren4.Length; i++)
			{
				Object.DestroyImmediate((Object)(object)componentsInChildren4[i]);
			}
			Collider[] componentsInChildren5 = prefab.GetComponentsInChildren<Collider>(true);
			foreach (Collider val in componentsInChildren5)
			{
				if (!val.isTrigger)
				{
					Object.DestroyImmediate((Object)(object)val);
				}
			}
		}

		public static void NormaliseTransformScales(GameObject prefab)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			prefab.transform.localScale = Vector3.one;
			Transform[] componentsInChildren = prefab.GetComponentsInChildren<Transform>(true);
			foreach (Transform val in componentsInChildren)
			{
				if (!((Object)(object)val == (Object)(object)prefab.transform))
				{
					val.localScale = Vector3.one;
				}
			}
		}

		public static AudioSource EnsureAudioSource(GameObject prefab)
		{
			AudioSource obj = prefab.GetComponent<AudioSource>() ?? prefab.AddComponent<AudioSource>();
			obj.playOnAwake = false;
			obj.loop = false;
			obj.spatialBlend = 1f;
			obj.rolloffMode = (AudioRolloffMode)1;
			obj.minDistance = 2f;
			obj.maxDistance = 25f;
			obj.volume = 1f;
			return obj;
		}

		public static Material CreateDiffuseMaterial(Texture2D tex, string displayName)
		{
			//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_004b: Expected O, but got Unknown
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_0125: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			Material val = new Material(Shader.Find("HDRP/Lit") ?? Shader.Find("HDRP/Unlit") ?? Shader.Find("Standard") ?? Shader.Find("Sprites/Default"))
			{
				name = displayName + "Mat"
			};
			if ((Object)(object)tex != (Object)null)
			{
				if (val.HasProperty("_BaseColorMap"))
				{
					val.SetTexture("_BaseColorMap", (Texture)(object)tex);
				}
				if (val.HasProperty("_MainTex"))
				{
					val.SetTexture("_MainTex", (Texture)(object)tex);
				}
				val.mainTexture = (Texture)(object)tex;
				if (val.HasProperty("_AlphaCutoffEnable"))
				{
					val.SetFloat("_AlphaCutoffEnable", 1f);
				}
				if (val.HasProperty("_AlphaCutoff"))
				{
					val.SetFloat("_AlphaCutoff", 0.5f);
				}
				val.EnableKeyword("_ALPHATEST_ON");
			}
			else
			{
				Color val2 = default(Color);
				((Color)(ref val2))..ctor(0.55f, 0.55f, 0.55f, 1f);
				if (val.HasProperty("_BaseColor"))
				{
					val.SetColor("_BaseColor", val2);
				}
				if (val.HasProperty("_Color"))
				{
					val.SetColor("_Color", val2);
				}
				val.color = val2;
			}
			return val;
		}

		public static void RegisterMainRenderer(GrabbableObject grabbable, MeshRenderer mr, Collider primaryCollider)
		{
			//IL_018a: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)grabbable == (Object)null)
			{
				return;
			}
			Type typeFromHandle = typeof(GrabbableObject);
			if ((Object)(object)mr != (Object)null)
			{
				FieldInfo field = typeFrom