Decompiled source of BM Suits v1.0.2

BM _Suits.dll

Decompiled 7 hours 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.InteropServices;
using System.Runtime.Versioning;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Logging;
using GameNetcodeStuff;
using HarmonyLib;
using Unity.Collections;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.Networking;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("BM _Suits")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("BM _Suits")]
[assembly: AssemblyCopyright("Copyright ©  2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("6b42616d-1a9e-46f4-a488-7cc9797b51fb")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("1.0.0.0")]
internal static class AdvancedSuitMaterials
{
	[Serializable]
	private sealed class AdvancedJson
	{
		public AdvancedLayer[] Layers;
	}

	[Serializable]
	private sealed class AdvancedLayer
	{
		public string Name;

		public string Mask;

		public string Material;

		public float Threshold = 0.5f;

		public float Opacity = 1f;

		public float EmissiveIntensity = 1f;

		public string EmissiveColor = "1,1,1,1";
	}

	private sealed class AdvancedSuitDefinition
	{
		public string SuitName;

		public string SuitDirectory;

		public string JsonPath;

		public string BaseTexturePath;

		public AdvancedLayer[] Layers;
	}

	private sealed class LoadedLayer
	{
		public AdvancedLayer Definition;

		public Texture2D Mask;

		public Material Material;
	}

	private sealed class RendererState
	{
		public SkinnedMeshRenderer Renderer;

		public Mesh OriginalMesh;

		public Material[] OriginalMaterials;

		public Mesh GeneratedMesh;

		public Material[] GeneratedMaterials;
	}

	[HarmonyPatch(typeof(StartOfRound), "Start")]
	private static class StartOfRoundPatch
	{
		[HarmonyPostfix]
		private static void Postfix(StartOfRound __instance)
		{
			if (!((Object)(object)__instance == (Object)null))
			{
				((MonoBehaviour)__instance).StartCoroutine(InitializeAfterMoreSuits(__instance));
			}
		}
	}

	[HarmonyPatch(typeof(GameNetworkManager), "Disconnect")]
	private static class DisconnectPatch
	{
		[HarmonyPostfix]
		private static void Postfix()
		{
			RestoreAllRenderers();
		}
	}

	private static bool initialized;

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

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

	private static readonly List<string> temporaryCompatibilityFiles = new List<string>();

	private static ManualLogSource Logger => Plugin.Log;

	internal static void Initialize()
	{
		if (!initialized)
		{
			initialized = true;
			ScanAdvancedSuits();
			PrepareMoreSuitsCompatibilityFiles();
			Logger.LogInfo((object)("Advanced suit material system initialized. Found " + definitions.Count + " advanced suit definition(s)."));
		}
	}

	private static void ScanAdvancedSuits()
	{
		definitions.Clear();
		string moreSuitsDirectory = Plugin.MoreSuitsDirectory;
		if (!Directory.Exists(moreSuitsDirectory))
		{
			Logger.LogDebug((object)("MoreSuits directory not found: " + moreSuitsDirectory));
			return;
		}
		string[] directories = Directory.GetDirectories(moreSuitsDirectory, "*", SearchOption.TopDirectoryOnly);
		string[] array = directories;
		foreach (string text in array)
		{
			string fileName = Path.GetFileName(text);
			if (string.IsNullOrWhiteSpace(fileName))
			{
				continue;
			}
			string text2 = FindCaseInsensitiveFile(text, fileName + ".png");
			if (string.IsNullOrEmpty(text2))
			{
				continue;
			}
			string text3 = FindAdvancedDirectory(text);
			if (string.IsNullOrEmpty(text3))
			{
				continue;
			}
			string text4 = FindCaseInsensitiveFile(text3, fileName + ".json");
			if (!string.IsNullOrEmpty(text4))
			{
				AdvancedJson advancedJson = ReadJson(text4);
				if (advancedJson != null)
				{
					AdvancedSuitDefinition advancedSuitDefinition = new AdvancedSuitDefinition();
					advancedSuitDefinition.SuitName = fileName;
					advancedSuitDefinition.SuitDirectory = text;
					advancedSuitDefinition.JsonPath = text4;
					advancedSuitDefinition.BaseTexturePath = text2;
					advancedSuitDefinition.Layers = advancedJson.Layers ?? new AdvancedLayer[0];
					definitions[fileName] = advancedSuitDefinition;
					Logger.LogInfo((object)("Advanced suit found: " + fileName));
				}
			}
		}
	}

	private static AdvancedJson ReadJson(string path)
	{
		try
		{
			string text = File.ReadAllText(path);
			return JsonUtility.FromJson<AdvancedJson>(text);
		}
		catch (Exception ex)
		{
			Logger.LogError((object)("Failed reading advanced JSON '" + path + "': " + ex.Message));
			return null;
		}
	}

	private static void PrepareMoreSuitsCompatibilityFiles()
	{
		temporaryCompatibilityFiles.Clear();
		foreach (AdvancedSuitDefinition value in definitions.Values)
		{
			if (string.IsNullOrEmpty(value.BaseTexturePath))
			{
				continue;
			}
			string text = Path.Combine(Plugin.MoreSuitsDirectory, value.SuitName + ".png");
			try
			{
				File.Copy(value.BaseTexturePath, text, overwrite: true);
				if (!temporaryCompatibilityFiles.Contains<string>(text, StringComparer.OrdinalIgnoreCase))
				{
					temporaryCompatibilityFiles.Add(text);
				}
				Logger.LogDebug((object)("Prepared MoreSuits compatibility texture: " + text));
			}
			catch (Exception ex)
			{
				Logger.LogError((object)("Failed preparing MoreSuits compatibility texture for " + value.SuitName + ": " + ex.Message));
			}
		}
	}

	private static void RemoveTemporaryCompatibilityFiles()
	{
		foreach (string temporaryCompatibilityFile in temporaryCompatibilityFiles)
		{
			try
			{
				if (File.Exists(temporaryCompatibilityFile))
				{
					File.Delete(temporaryCompatibilityFile);
					Logger.LogDebug((object)("Removed temporary MoreSuits compatibility texture: " + temporaryCompatibilityFile));
				}
			}
			catch (Exception ex)
			{
				Logger.LogWarning((object)("Could not remove temporary MoreSuits compatibility texture '" + temporaryCompatibilityFile + "': " + ex.Message));
			}
		}
		temporaryCompatibilityFiles.Clear();
	}

	private static IEnumerator InitializeAfterMoreSuits(StartOfRound round)
	{
		yield return null;
		yield return null;
		ApplyRegisteredSuitMaterials(round);
		RemoveTemporaryCompatibilityFiles();
		yield return null;
		if (round.allPlayerScripts == null)
		{
			yield break;
		}
		PlayerControllerB[] allPlayerScripts = round.allPlayerScripts;
		foreach (PlayerControllerB player in allPlayerScripts)
		{
			if (!((Object)(object)player == (Object)null))
			{
				ApplyToPlayer(player);
			}
		}
	}

	private static void ApplyRegisteredSuitMaterials(StartOfRound round)
	{
		if ((Object)(object)round.unlockablesList == (Object)null || round.unlockablesList.unlockables == null)
		{
			return;
		}
		foreach (KeyValuePair<string, AdvancedSuitDefinition> definition in definitions)
		{
			UnlockableItem val = FindSuit(round, definition.Key);
			if (val == null)
			{
				Logger.LogWarning((object)("Could not find registered MoreSuits suit: " + definition.Key));
				continue;
			}
			if ((Object)(object)val.suitMaterial == (Object)null)
			{
				Logger.LogWarning((object)("Suit material is null for: " + definition.Key));
				continue;
			}
			ApplyBaseMaterialLayers(val.suitMaterial, definition.Value);
			Logger.LogInfo((object)("Applied advanced suit definition: " + definition.Key));
		}
	}

	private static UnlockableItem FindSuit(StartOfRound round, string suitName)
	{
		foreach (UnlockableItem unlockable in round.unlockablesList.unlockables)
		{
			if (unlockable != null)
			{
				string a = NormalizeSuitName(unlockable.unlockableName);
				if (string.Equals(a, suitName, StringComparison.OrdinalIgnoreCase))
				{
					return unlockable;
				}
			}
		}
		return null;
	}

	private static void ApplyBaseMaterialLayers(Material material, AdvancedSuitDefinition definition)
	{
		if ((Object)(object)material == (Object)null || definition == null || definition.Layers == null)
		{
			return;
		}
		AdvancedLayer[] layers = definition.Layers;
		foreach (AdvancedLayer advancedLayer in layers)
		{
			if (advancedLayer != null && advancedLayer.Name.Equals("Emissive", StringComparison.OrdinalIgnoreCase))
			{
				ApplyEmissiveLayer(material, definition, advancedLayer);
			}
		}
	}

	private static void ApplyEmissiveLayer(Material material, AdvancedSuitDefinition definition, AdvancedLayer layer)
	{
		//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
		if (string.IsNullOrWhiteSpace(layer.Mask))
		{
			return;
		}
		string text = FindAdvancedAsset(definition, layer.Mask);
		if (!File.Exists(text))
		{
			Logger.LogWarning((object)("Emissive mask not found: " + text));
			return;
		}
		Texture2D val = LoadTexture(text);
		if (!((Object)(object)val == (Object)null))
		{
			if (material.HasProperty("_EmissiveColorMap"))
			{
				material.SetTexture("_EmissiveColorMap", (Texture)(object)val);
			}
			if (material.HasProperty("_UseEmissiveIntensity"))
			{
				material.SetFloat("_UseEmissiveIntensity", 1f);
			}
			if (material.HasProperty("_EmissiveIntensity"))
			{
				material.SetFloat("_EmissiveIntensity", layer.EmissiveIntensity);
			}
			if (material.HasProperty("_EmissiveColor"))
			{
				material.SetColor("_EmissiveColor", ParseColor(layer.EmissiveColor));
			}
			material.EnableKeyword("_EMISSIVE_COLOR_MAP");
			Logger.LogInfo((object)("Applied emissive layer to " + definition.SuitName + ": " + layer.Mask));
		}
	}

	private static void ApplyToPlayer(PlayerControllerB player)
	{
		if ((Object)(object)player == (Object)null)
		{
			return;
		}
		RestorePlayerRenderers(player);
		UnlockableItem currentSuit = GetCurrentSuit(player);
		if (currentSuit == null)
		{
			return;
		}
		string key = NormalizeSuitName(currentSuit.unlockableName);
		if (!definitions.TryGetValue(key, out var value))
		{
			return;
		}
		SkinnedMeshRenderer[] array = (SkinnedMeshRenderer[])(object)new SkinnedMeshRenderer[3] { player.thisPlayerModel, player.thisPlayerModelLOD1, player.thisPlayerModelLOD2 };
		SkinnedMeshRenderer[] array2 = array;
		foreach (SkinnedMeshRenderer val in array2)
		{
			if (!((Object)(object)val == (Object)null))
			{
				ApplyDefinitionToRenderer(val, value);
			}
		}
	}

	private static UnlockableItem GetCurrentSuit(PlayerControllerB player)
	{
		if ((Object)(object)player == (Object)null || (Object)(object)StartOfRound.Instance == (Object)null || (Object)(object)StartOfRound.Instance.unlockablesList == (Object)null || StartOfRound.Instance.unlockablesList.unlockables == null)
		{
			return null;
		}
		int currentSuitID = player.currentSuitID;
		List<UnlockableItem> unlockables = StartOfRound.Instance.unlockablesList.unlockables;
		if (currentSuitID < 0 || currentSuitID >= unlockables.Count)
		{
			return null;
		}
		return unlockables[currentSuitID];
	}

	private static void ApplyDefinitionToRenderer(SkinnedMeshRenderer renderer, AdvancedSuitDefinition definition)
	{
		if ((Object)(object)renderer == (Object)null || (Object)(object)renderer.sharedMesh == (Object)null || definition == null)
		{
			return;
		}
		Mesh sharedMesh = renderer.sharedMesh;
		Material[] sharedMaterials = ((Renderer)renderer).sharedMaterials;
		if (sharedMesh.subMeshCount == 0 || sharedMesh.uv == null || sharedMesh.uv.Length == 0)
		{
			Logger.LogWarning((object)("Renderer has no usable UV data: " + ((Object)renderer).name));
			return;
		}
		List<LoadedLayer> list = LoadMaterialLayers(definition);
		if (list.Count == 0)
		{
			return;
		}
		RendererState rendererState = new RendererState();
		rendererState.Renderer = renderer;
		rendererState.OriginalMesh = sharedMesh;
		rendererState.OriginalMaterials = sharedMaterials;
		rendererStates[((Object)renderer).GetInstanceID()] = rendererState;
		Mesh val = Object.Instantiate<Mesh>(sharedMesh);
		((Object)val).name = ((Object)sharedMesh).name + "_BM_Suits_Layered_" + definition.SuitName;
		int subMeshCount = sharedMesh.subMeshCount;
		int count = list.Count;
		val.subMeshCount = subMeshCount + count;
		Material[] array = (Material[])(object)new Material[subMeshCount + count];
		for (int i = 0; i < subMeshCount; i++)
		{
			if (sharedMaterials != null && i < sharedMaterials.Length)
			{
				array[i] = sharedMaterials[i];
			}
		}
		for (int j = 0; j < count; j++)
		{
			array[subMeshCount + j] = list[j].Material;
		}
		Vector2[] uv = sharedMesh.uv;
		int[] array2 = BuildTriangleLayerAssignments(sharedMesh, uv, list);
		int[] array3 = new int[count];
		List<int>[] array4 = new List<int>[subMeshCount];
		for (int k = 0; k < subMeshCount; k++)
		{
			array4[k] = new List<int>();
		}
		List<int>[] array5 = new List<int>[count];
		for (int l = 0; l < count; l++)
		{
			array5[l] = new List<int>();
		}
		int num = 0;
		for (int m = 0; m < subMeshCount; m++)
		{
			int[] triangles = sharedMesh.GetTriangles(m);
			for (int n = 0; n + 2 < triangles.Length; n += 3)
			{
				int num2 = num;
				int num3 = -1;
				if (num2 >= 0 && num2 < array2.Length)
				{
					num3 = array2[num2];
				}
				if (num3 >= 0 && num3 < count)
				{
					array5[num3].Add(triangles[n]);
					array5[num3].Add(triangles[n + 1]);
					array5[num3].Add(triangles[n + 2]);
					array3[num3]++;
				}
				else
				{
					array4[m].Add(triangles[n]);
					array4[m].Add(triangles[n + 1]);
					array4[m].Add(triangles[n + 2]);
				}
				num++;
			}
		}
		for (int num4 = 0; num4 < subMeshCount; num4++)
		{
			val.SetTriangles(array4[num4], num4, false);
		}
		for (int num5 = 0; num5 < count; num5++)
		{
			val.SetTriangles(array5[num5], subMeshCount + num5, false);
		}
		val.RecalculateBounds();
		renderer.sharedMesh = val;
		((Renderer)renderer).sharedMaterials = array;
		rendererState.GeneratedMesh = val;
		rendererState.GeneratedMaterials = array;
		int num6 = 0;
		for (int num7 = 0; num7 < array3.Length; num7++)
		{
			num6 += array3[num7];
		}
		Logger.LogInfo((object)("Built layered renderer for " + definition.SuitName + " / " + ((Object)renderer).name + ": " + num6 + " triangle(s) assigned to advanced layers."));
		for (int num8 = 0; num8 < list.Count; num8++)
		{
			Logger.LogInfo((object)("  Layer " + list[num8].Definition.Name + ": " + array3[num8] + " triangle(s)"));
		}
	}

	private static int[] BuildTriangleLayerAssignments(Mesh mesh, Vector2[] uv, List<LoadedLayer> layers)
	{
		//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00db: 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_010c: Unknown result type (might be due to invalid IL or missing references)
		//IL_010e: Unknown result type (might be due to invalid IL or missing references)
		int num = 0;
		for (int i = 0; i < mesh.subMeshCount; i++)
		{
			num += (int)(mesh.GetIndexCount(i) / 3);
		}
		int[] array = new int[num];
		for (int j = 0; j < array.Length; j++)
		{
			array[j] = -1;
		}
		int num2 = 0;
		for (int k = 0; k < mesh.subMeshCount; k++)
		{
			int[] triangles = mesh.GetTriangles(k);
			for (int l = 0; l + 2 < triangles.Length; l += 3)
			{
				int num3 = triangles[l];
				int num4 = triangles[l + 1];
				int num5 = triangles[l + 2];
				if (num3 < 0 || num3 >= uv.Length || num4 < 0 || num4 >= uv.Length || num5 < 0 || num5 >= uv.Length)
				{
					num2++;
					continue;
				}
				Vector2 uv2 = uv[num3];
				Vector2 uv3 = uv[num4];
				Vector2 uv4 = uv[num5];
				for (int m = 0; m < layers.Count; m++)
				{
					LoadedLayer loadedLayer = layers[m];
					if (!((Object)(object)loadedLayer.Mask == (Object)null) && TriangleMatchesMask(loadedLayer.Mask, uv2, uv3, uv4, loadedLayer.Definition.Threshold))
					{
						array[num2] = m;
						break;
					}
				}
				num2++;
			}
		}
		return array;
	}

	private static bool TriangleMatchesMask(Texture2D mask, Vector2 uv0, Vector2 uv1, Vector2 uv2, float threshold)
	{
		//IL_0009: Unknown result type (might be due to invalid IL or missing references)
		//IL_000a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0011: Unknown result type (might be due to invalid IL or missing references)
		//IL_0012: Unknown result type (might be due to invalid IL or missing references)
		//IL_0019: Unknown result type (might be due to invalid IL or missing references)
		//IL_001a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0021: Unknown result type (might be due to invalid IL or missing references)
		//IL_0022: Unknown result type (might be due to invalid IL or missing references)
		//IL_0028: Unknown result type (might be due to invalid IL or missing references)
		//IL_002d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0034: Unknown result type (might be due to invalid IL or missing references)
		//IL_0035: 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_0040: Unknown result type (might be due to invalid IL or missing references)
		//IL_0047: Unknown result type (might be due to invalid IL or missing references)
		//IL_0048: Unknown result type (might be due to invalid IL or missing references)
		//IL_004e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0053: 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_005b: Unknown result type (might be due to invalid IL or missing references)
		//IL_005c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0061: Unknown result type (might be due to invalid IL or missing references)
		//IL_0062: Unknown result type (might be due to invalid IL or missing references)
		//IL_006c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0071: Unknown result type (might be due to invalid IL or missing references)
		//IL_0081: Unknown result type (might be due to invalid IL or missing references)
		Vector2[] array = (Vector2[])(object)new Vector2[7]
		{
			uv0,
			uv1,
			uv2,
			Vector2.Lerp(uv0, uv1, 0.5f),
			Vector2.Lerp(uv1, uv2, 0.5f),
			Vector2.Lerp(uv2, uv0, 0.5f),
			(uv0 + uv1 + uv2) / 3f
		};
		int num = 0;
		for (int i = 0; i < array.Length; i++)
		{
			float num2 = SampleMask(mask, array[i]);
			if (num2 >= threshold)
			{
				num++;
			}
		}
		return num >= 4;
	}

	private static float SampleMask(Texture2D texture, Vector2 uv)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_000d: Unknown result type (might be due to invalid IL or missing references)
		//IL_001c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0021: Unknown result type (might be due to invalid IL or missing references)
		//IL_0022: Unknown result type (might be due to invalid IL or missing references)
		//IL_0028: Unknown result type (might be due to invalid IL or missing references)
		//IL_002e: Unknown result type (might be due to invalid IL or missing references)
		float num = Mathf.Clamp01(uv.x);
		float num2 = Mathf.Clamp01(uv.y);
		Color pixelBilinear = texture.GetPixelBilinear(num, num2);
		return Mathf.Max(pixelBilinear.r, Mathf.Max(pixelBilinear.g, pixelBilinear.b));
	}

	private static List<LoadedLayer> LoadMaterialLayers(AdvancedSuitDefinition definition)
	{
		List<LoadedLayer> list = new List<LoadedLayer>();
		if (definition == null || definition.Layers == null)
		{
			return list;
		}
		AdvancedLayer[] layers = definition.Layers;
		foreach (AdvancedLayer advancedLayer in layers)
		{
			if (advancedLayer == null || string.IsNullOrWhiteSpace(advancedLayer.Name) || advancedLayer.Name.Equals("Emissive", StringComparison.OrdinalIgnoreCase) || string.IsNullOrWhiteSpace(advancedLayer.Mask))
			{
				continue;
			}
			string text = advancedLayer.Material;
			if (string.IsNullOrWhiteSpace(text))
			{
				if (advancedLayer.Name.Equals("Glass", StringComparison.OrdinalIgnoreCase))
				{
					text = "MoreMaterials-Glass";
				}
				else
				{
					if (!advancedLayer.Name.Equals("Transparent", StringComparison.OrdinalIgnoreCase))
					{
						Logger.LogWarning((object)("Layer '" + advancedLayer.Name + "' has no Material specified."));
						continue;
					}
					text = "MoreMaterials-Transparent";
				}
			}
			Material val = FindMoreMaterialsMaterial(text);
			if ((Object)(object)val == (Object)null)
			{
				Logger.LogWarning((object)("Could not find MoreMaterials material '" + text + "' for " + definition.SuitName));
				continue;
			}
			string text2 = FindAdvancedAsset(definition, advancedLayer.Mask);
			if (!File.Exists(text2))
			{
				Logger.LogWarning((object)("Layer mask not found: " + text2));
				continue;
			}
			Texture2D val2 = LoadTexture(text2);
			if ((Object)(object)val2 == (Object)null)
			{
				continue;
			}
			Material val3 = Object.Instantiate<Material>(val);
			((Object)val3).name = "BM_Suits_" + definition.SuitName + "_" + advancedLayer.Name;
			Texture2D val4 = LoadTexture(definition.BaseTexturePath);
			if ((Object)(object)val4 != (Object)null)
			{
				if (val3.HasProperty("_BaseColorMap"))
				{
					val3.SetTexture("_BaseColorMap", (Texture)(object)val4);
				}
				if (val3.HasProperty("_BaseMap"))
				{
					val3.SetTexture("_BaseMap", (Texture)(object)val4);
				}
				val3.mainTexture = (Texture)(object)val4;
			}
			LoadedLayer loadedLayer = new LoadedLayer();
			loadedLayer.Definition = advancedLayer;
			loadedLayer.Mask = val2;
			loadedLayer.Material = val3;
			list.Add(loadedLayer);
			Logger.LogInfo((object)("Loaded layer material: " + definition.SuitName + " -> " + advancedLayer.Name + " -> " + text));
		}
		return list;
	}

	private static void RestorePlayerRenderers(PlayerControllerB player)
	{
		if ((Object)(object)player == (Object)null)
		{
			return;
		}
		SkinnedMeshRenderer[] array = (SkinnedMeshRenderer[])(object)new SkinnedMeshRenderer[3] { player.thisPlayerModel, player.thisPlayerModelLOD1, player.thisPlayerModelLOD2 };
		SkinnedMeshRenderer[] array2 = array;
		foreach (SkinnedMeshRenderer val in array2)
		{
			if (!((Object)(object)val == (Object)null))
			{
				RestoreRenderer(val);
			}
		}
	}

	private static void RestoreRenderer(SkinnedMeshRenderer renderer)
	{
		if ((Object)(object)renderer == (Object)null)
		{
			return;
		}
		int instanceID = ((Object)renderer).GetInstanceID();
		if (!rendererStates.TryGetValue(instanceID, out var value))
		{
			return;
		}
		if ((Object)(object)value.OriginalMesh != (Object)null)
		{
			renderer.sharedMesh = value.OriginalMesh;
		}
		if (value.OriginalMaterials != null)
		{
			((Renderer)renderer).sharedMaterials = value.OriginalMaterials;
		}
		if ((Object)(object)value.GeneratedMesh != (Object)null)
		{
			Object.Destroy((Object)(object)value.GeneratedMesh);
		}
		if (value.GeneratedMaterials != null)
		{
			Material[] generatedMaterials = value.GeneratedMaterials;
			foreach (Material val in generatedMaterials)
			{
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				bool flag = false;
				if (value.OriginalMaterials != null)
				{
					Material[] originalMaterials = value.OriginalMaterials;
					foreach (Material val2 in originalMaterials)
					{
						if ((Object)(object)val == (Object)(object)val2)
						{
							flag = true;
							break;
						}
					}
				}
				if (!flag)
				{
					Object.Destroy((Object)(object)val);
				}
			}
		}
		rendererStates.Remove(instanceID);
	}

	private static Material FindMoreMaterialsMaterial(string requestedName)
	{
		if (string.IsNullOrWhiteSpace(requestedName))
		{
			return null;
		}
		Type type = FindMoreSuitsType();
		if (type == null)
		{
			return null;
		}
		FieldInfo field = type.GetField("customMaterials", BindingFlags.Static | BindingFlags.Public);
		if (field == null)
		{
			Logger.LogWarning((object)"MoreSuits.customMaterials was not found.");
			return null;
		}
		object value;
		try
		{
			value = field.GetValue(null);
		}
		catch (Exception ex)
		{
			Logger.LogWarning((object)("Could not read MoreSuits.customMaterials: " + ex.Message));
			return null;
		}
		if (!(value is IEnumerable enumerable))
		{
			return null;
		}
		Material result = null;
		foreach (object item in enumerable)
		{
			Material val = (Material)((item is Material) ? item : null);
			if (!((Object)(object)val == (Object)null))
			{
				if (string.Equals(((Object)val).name, requestedName, StringComparison.OrdinalIgnoreCase))
				{
					return val;
				}
				if (((Object)val).name.StartsWith(requestedName, StringComparison.OrdinalIgnoreCase))
				{
					result = val;
				}
			}
		}
		return result;
	}

	private static Type FindMoreSuitsType()
	{
		Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
		foreach (Assembly assembly in assemblies)
		{
			Type[] array;
			try
			{
				array = assembly.GetTypes();
			}
			catch (ReflectionTypeLoadException ex)
			{
				array = ex.Types.Where((Type t) => t != null).ToArray();
			}
			Type[] array2 = array;
			foreach (Type type in array2)
			{
				if (!(type == null) && type.GetField("customMaterials", BindingFlags.Static | BindingFlags.Public) != null)
				{
					return type;
				}
			}
		}
		return null;
	}

	private static Texture2D LoadTexture(string path)
	{
		//IL_002e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0034: Expected O, but got Unknown
		if (string.IsNullOrWhiteSpace(path) || !File.Exists(path))
		{
			return null;
		}
		try
		{
			byte[] array = File.ReadAllBytes(path);
			Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false);
			if (!ImageConversion.LoadImage(val, array, false))
			{
				Object.Destroy((Object)(object)val);
				return null;
			}
			((Object)val).name = "BM_Suits_" + Path.GetFileNameWithoutExtension(path);
			((Texture)val).filterMode = (FilterMode)1;
			((Texture)val).wrapMode = (TextureWrapMode)1;
			val.Apply(false, false);
			return val;
		}
		catch (Exception ex)
		{
			Logger.LogError((object)("Failed loading texture '" + path + "': " + ex.Message));
			return null;
		}
	}

	private static string FindAdvancedDirectory(string suitDirectory)
	{
		string[] directories = Directory.GetDirectories(suitDirectory, "*", SearchOption.TopDirectoryOnly);
		string[] array = directories;
		foreach (string text in array)
		{
			if (Path.GetFileName(text).Equals("Advanced", StringComparison.OrdinalIgnoreCase))
			{
				return text;
			}
		}
		return null;
	}

	private static string FindAdvancedAsset(AdvancedSuitDefinition definition, string fileName)
	{
		if (definition == null || string.IsNullOrWhiteSpace(fileName))
		{
			return string.Empty;
		}
		string directoryName = Path.GetDirectoryName(definition.JsonPath);
		return Path.Combine(directoryName, fileName);
	}

	private static string FindCaseInsensitiveFile(string directory, string expectedName)
	{
		string text = Path.Combine(directory, expectedName);
		if (File.Exists(text))
		{
			return text;
		}
		if (!Directory.Exists(directory))
		{
			return null;
		}
		string[] files = Directory.GetFiles(directory, "*", SearchOption.TopDirectoryOnly);
		foreach (string text2 in files)
		{
			if (Path.GetFileName(text2).Equals(expectedName, StringComparison.OrdinalIgnoreCase))
			{
				return text2;
			}
		}
		return null;
	}

	private static string NormalizeSuitName(string value)
	{
		if (string.IsNullOrWhiteSpace(value))
		{
			return value;
		}
		string text = value.Trim();
		if (text.EndsWith(" suit", StringComparison.OrdinalIgnoreCase))
		{
			text = text.Substring(0, text.Length - 5).TrimEnd(Array.Empty<char>());
		}
		return text;
	}

	private static Color ParseColor(string value)
	{
		//IL_000e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0013: Unknown result type (might be due to invalid IL or missing references)
		//IL_0039: Unknown result type (might be due to invalid IL or missing references)
		//IL_003e: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
		if (string.IsNullOrWhiteSpace(value))
		{
			return Color.white;
		}
		string[] array = value.Split(new char[1] { ',' });
		if (array.Length < 3)
		{
			return Color.white;
		}
		float result = 1f;
		if (!float.TryParse(array[0].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out var result2) || !float.TryParse(array[1].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out var result3) || !float.TryParse(array[2].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out var result4))
		{
			return Color.white;
		}
		if (array.Length >= 4)
		{
			float.TryParse(array[3].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out result);
		}
		return new Color(result2, result3, result4, result);
	}

	private static void RestoreAllRenderers()
	{
		foreach (RendererState item in rendererStates.Values.ToList())
		{
			if (item != null && (Object)(object)item.Renderer != (Object)null)
			{
				RestoreRenderer(item.Renderer);
			}
		}
		rendererStates.Clear();
		RemoveTemporaryCompatibilityFiles();
	}
}
[BepInPlugin("com.brox.BM_Suits", "BM_Suits", "1.5.0")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
public class Plugin : BaseUnityPlugin
{
	internal const string ModGuid = "com.brox.BM_Suits";

	internal const string ModName = "BM_Suits";

	internal const string ModVersion = "1.5.0";

	internal static ManualLogSource Log;

	internal static string PluginDirectory => Path.GetDirectoryName(typeof(Plugin).Assembly.Location);

	internal static string SoundsDirectory => Path.Combine(PluginDirectory, "sounds");

	internal static string MoreSuitsDirectory => Path.Combine(PluginDirectory, "moresuits");

	private void Awake()
	{
		//IL_002d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0033: Expected O, but got Unknown
		Log = ((BaseUnityPlugin)this).Logger;
		Log.LogInfo((object)"BM_Suits v1.5.0 initializing...");
		SuitAudio.Initialize();
		AdvancedSuitMaterials.Initialize();
		Harmony val = new Harmony("com.brox.BM_Suits");
		val.PatchAll(typeof(Plugin).Assembly);
		Log.LogInfo((object)"BM_Suits Harmony patches applied.");
		Log.LogInfo((object)"BM_Suits loaded successfully.");
	}
}
internal static class SuitAudio
{
	private sealed class SoundGroup
	{
		internal readonly List<AudioClip> Clips = new List<AudioClip>();

		private readonly Queue<int> shuffleQueue = new Queue<int>();

		private int lastPlayedIndex = -1;

		internal int Count => Clips.Count;

		internal AudioClip First
		{
			get
			{
				if (Clips.Count == 0)
				{
					return null;
				}
				return Clips[0];
			}
		}

		internal void Add(AudioClip clip)
		{
			if ((Object)(object)clip != (Object)null)
			{
				Clips.Add(clip);
			}
		}

		internal AudioClip GetClip(int index)
		{
			if (index < 0 || index >= Clips.Count)
			{
				return null;
			}
			return Clips[index];
		}

		internal int GetNextIndex()
		{
			if (Clips.Count == 0)
			{
				return -1;
			}
			if (Clips.Count == 1)
			{
				lastPlayedIndex = 0;
				return 0;
			}
			if (shuffleQueue.Count == 0)
			{
				RebuildShuffle();
			}
			return lastPlayedIndex = shuffleQueue.Dequeue();
		}

		private void RebuildShuffle()
		{
			List<int> list = new List<int>();
			for (int i = 0; i < Clips.Count; i++)
			{
				list.Add(i);
			}
			for (int num = list.Count - 1; num > 0; num--)
			{
				int index = Random.Range(0, num + 1);
				int value = list[num];
				list[num] = list[index];
				list[index] = value;
			}
			if (lastPlayedIndex >= 0 && list.Count > 1 && list[0] == lastPlayedIndex)
			{
				int index2 = Random.Range(1, list.Count);
				int value2 = list[0];
				list[0] = list[index2];
				list[index2] = value2;
			}
			foreach (int item in list)
			{
				shuffleQueue.Enqueue(item);
			}
		}
	}

	private sealed class PendingSuitUpSelection
	{
		internal ulong PlayerId;

		internal int SuitId;

		internal int ClipIndex;

		internal float Time;
	}

	private sealed class PendingSuitRpc
	{
		internal ulong PlayerId;

		internal int SuitId;

		internal float Time;
	}

	[HarmonyPatch(typeof(StartOfRound), "Start")]
	private static class StartOfRoundPatch
	{
		[HarmonyPostfix]
		private static void Postfix(StartOfRound __instance)
		{
			if (!((Object)(object)__instance == (Object)null))
			{
				RegisterNetworkHandlers();
				((MonoBehaviour)__instance).StartCoroutine(LoadSoundsForRound(__instance));
			}
		}
	}

	[HarmonyPatch(typeof(PlayerControllerB), "Jump_performed")]
	private static class JumpPatch
	{
		[HarmonyPrefix]
		private static void Prefix(PlayerControllerB __instance)
		{
			if (!((Object)(object)__instance == (Object)null) && !((Object)(object)GameNetworkManager.Instance == (Object)null) && !((Object)(object)GameNetworkManager.Instance.localPlayerController != (Object)(object)__instance))
			{
				ChooseJumpSound(__instance);
			}
		}
	}

	[HarmonyPatch(typeof(UnlockableSuit), "SwitchSuitToThis")]
	private static class SuitSwitchPatch
	{
		[HarmonyPrefix]
		private static void Prefix(UnlockableSuit __instance, PlayerControllerB playerWhoTriggered)
		{
			if (!((Object)(object)__instance == (Object)null))
			{
				PlayerControllerB val = playerWhoTriggered;
				if ((Object)(object)val == (Object)null && (Object)(object)GameNetworkManager.Instance != (Object)null)
				{
					val = GameNetworkManager.Instance.localPlayerController;
				}
				if (!((Object)(object)val == (Object)null) && !((Object)(object)GameNetworkManager.Instance == (Object)null) && !((Object)(object)GameNetworkManager.Instance.localPlayerController != (Object)(object)val) && val.currentSuitID != __instance.suitID)
				{
					ChooseSuitUpSound(val, __instance.suitID);
				}
			}
		}
	}

	[HarmonyPatch(typeof(UnlockableSuit), "SwitchSuitClientRpc")]
	private static class SuitSwitchRpcPatch
	{
		[HarmonyPostfix]
		private static void Postfix(int playerID)
		{
			PrunePending();
		}
	}

	private const string JumpRequestMessage = "BM_Suits.Jump.Request";

	private const string JumpBroadcastMessage = "BM_Suits.Jump.Broadcast";

	private const string SuitUpRequestMessage = "BM_Suits.SuitUp.Request";

	private const string SuitUpBroadcastMessage = "BM_Suits.SuitUp.Broadcast";

	private const float PendingEventLifetime = 5f;

	private static CustomMessagingManager registeredMessagingManager;

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

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

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

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

	private static readonly List<PendingSuitUpSelection> pendingSuitUpSelections = new List<PendingSuitUpSelection>();

	private static readonly List<PendingSuitRpc> pendingSuitRpcs = new List<PendingSuitRpc>();

	private static readonly Regex soundFilePattern = new Regex("^(.*)_(Jump|SuitUp)(?:_(\\d+))?$", RegexOptions.IgnoreCase | RegexOptions.Compiled);

	private static ManualLogSource Logger => Plugin.Log;

	internal static void Initialize()
	{
		ScanSoundDirectory();
	}

	private static void ScanSoundDirectory()
	{
		//IL_0070: Unknown result type (might be due to invalid IL or missing references)
		//IL_0076: Invalid comparison between Unknown and I4
		jumpSoundPaths.Clear();
		suitUpSoundPaths.Clear();
		if (!Directory.Exists(Plugin.SoundsDirectory))
		{
			Directory.CreateDirectory(Plugin.SoundsDirectory);
			return;
		}
		string[] files = Directory.GetFiles(Plugin.SoundsDirectory, "*.*", SearchOption.TopDirectoryOnly);
		Array.Sort(files, (IComparer<string>?)StringComparer.OrdinalIgnoreCase);
		int num = 0;
		int num2 = 0;
		string[] array = files;
		foreach (string path in array)
		{
			if ((int)GetAudioType(path) == 0)
			{
				continue;
			}
			string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(path);
			Match match = soundFilePattern.Match(fileNameWithoutExtension);
			if (match.Success)
			{
				string value = match.Groups[1].Value;
				string value2 = match.Groups[2].Value;
				if (value2.Equals("Jump", StringComparison.OrdinalIgnoreCase))
				{
					AddPath(jumpSoundPaths, value, path);
					num++;
				}
				else
				{
					AddPath(suitUpSoundPaths, value, path);
					num2++;
				}
			}
		}
		SortGroups(jumpSoundPaths);
		SortGroups(suitUpSoundPaths);
		Logger.LogInfo((object)("Found " + jumpSoundPaths.Count + " Jump group(s), " + num + " clip(s)."));
		Logger.LogInfo((object)("Found " + suitUpSoundPaths.Count + " SuitUp group(s), " + num2 + " clip(s)."));
	}

	private static void AddPath(Dictionary<string, List<string>> dictionary, string key, string path)
	{
		if (!dictionary.TryGetValue(key, out var value))
		{
			value = (dictionary[key] = new List<string>());
		}
		value.Add(path);
	}

	private static void SortGroups(Dictionary<string, List<string>> dictionary)
	{
		foreach (List<string> value in dictionary.Values)
		{
			value.Sort(StringComparer.OrdinalIgnoreCase);
		}
	}

	private static void RegisterNetworkHandlers()
	{
		//IL_0082: Unknown result type (might be due to invalid IL or missing references)
		//IL_008c: Expected O, but got Unknown
		//IL_009a: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a4: Expected O, but got Unknown
		//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bc: Expected O, but got Unknown
		//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d4: Expected O, but got Unknown
		NetworkManager singleton = NetworkManager.Singleton;
		if (!((Object)(object)singleton == (Object)null) && singleton.CustomMessagingManager != null)
		{
			CustomMessagingManager customMessagingManager = singleton.CustomMessagingManager;
			if (registeredMessagingManager != customMessagingManager)
			{
				registeredMessagingManager = customMessagingManager;
				TryUnregister(customMessagingManager, "BM_Suits.Jump.Request");
				TryUnregister(customMessagingManager, "BM_Suits.Jump.Broadcast");
				TryUnregister(customMessagingManager, "BM_Suits.SuitUp.Request");
				TryUnregister(customMessagingManager, "BM_Suits.SuitUp.Broadcast");
				customMessagingManager.RegisterNamedMessageHandler("BM_Suits.Jump.Request", new HandleNamedMessageDelegate(HandleJumpRequest));
				customMessagingManager.RegisterNamedMessageHandler("BM_Suits.Jump.Broadcast", new HandleNamedMessageDelegate(HandleJumpBroadcast));
				customMessagingManager.RegisterNamedMessageHandler("BM_Suits.SuitUp.Request", new HandleNamedMessageDelegate(HandleSuitUpRequest));
				customMessagingManager.RegisterNamedMessageHandler("BM_Suits.SuitUp.Broadcast", new HandleNamedMessageDelegate(HandleSuitUpBroadcast));
				Logger.LogInfo((object)"BM_Suits network handlers registered.");
			}
		}
	}

	private static void TryUnregister(CustomMessagingManager manager, string name)
	{
		try
		{
			manager.UnregisterNamedMessageHandler(name);
		}
		catch
		{
		}
	}

	private static IEnumerator LoadSoundsForRound(StartOfRound round)
	{
		yield return null;
		jumpSounds.Clear();
		suitUpSounds.Clear();
		foreach (KeyValuePair<string, List<string>> entry in jumpSoundPaths)
		{
			SoundGroup group = new SoundGroup();
			foreach (string path in entry.Value)
			{
				AudioClip clip = null;
				yield return ((MonoBehaviour)round).StartCoroutine(LoadAudioClip(path, delegate(AudioClip result)
				{
					clip = result;
				}));
				if ((Object)(object)clip != (Object)null)
				{
					group.Add(clip);
				}
			}
			if (group.Count > 0)
			{
				jumpSounds[entry.Key] = group;
			}
		}
		foreach (KeyValuePair<string, List<string>> entry2 in suitUpSoundPaths)
		{
			SoundGroup group2 = new SoundGroup();
			foreach (string path2 in entry2.Value)
			{
				AudioClip clip2 = null;
				yield return ((MonoBehaviour)round).StartCoroutine(LoadAudioClip(path2, delegate(AudioClip result)
				{
					clip2 = result;
				}));
				if ((Object)(object)clip2 != (Object)null)
				{
					group2.Add(clip2);
				}
			}
			if (group2.Count > 0)
			{
				suitUpSounds[entry2.Key] = group2;
			}
		}
		AssignInitialJumpAudio(round);
		Logger.LogInfo((object)("Audio ready: " + jumpSounds.Count + " Jump group(s) / " + CountClips(jumpSounds) + " clip(s), " + suitUpSounds.Count + " SuitUp group(s) / " + CountClips(suitUpSounds) + " clip(s)."));
	}

	private static int CountClips(Dictionary<string, SoundGroup> groups)
	{
		int num = 0;
		foreach (SoundGroup value in groups.Values)
		{
			num += value.Count;
		}
		return num;
	}

	private static void AssignInitialJumpAudio(StartOfRound round)
	{
		if ((Object)(object)round.unlockablesList == (Object)null || round.unlockablesList.unlockables == null)
		{
			return;
		}
		foreach (UnlockableItem unlockable in round.unlockablesList.unlockables)
		{
			if (unlockable != null && jumpSounds.TryGetValue(NormalizeSuitName(unlockable.unlockableName), out var value) && (Object)(object)value.First != (Object)null)
			{
				unlockable.jumpAudio = value.First;
			}
		}
	}

	private static void ChooseJumpSound(PlayerControllerB player)
	{
		UnlockableItem unlockable = GetUnlockable(player.currentSuitID);
		if (unlockable == null || !jumpSounds.TryGetValue(NormalizeSuitName(unlockable.unlockableName), out var value))
		{
			return;
		}
		int nextIndex = value.GetNextIndex();
		if (nextIndex >= 0)
		{
			AudioClip clip = value.GetClip(nextIndex);
			if (!((Object)(object)clip == (Object)null))
			{
				unlockable.jumpAudio = clip;
				SendJumpSelection(player.playerClientId, player.currentSuitID, nextIndex);
			}
		}
	}

	private unsafe static void SendJumpSelection(ulong playerId, int suitId, int clipIndex)
	{
		//IL_0054: 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_0068: Unknown result type (might be due to invalid IL or missing references)
		//IL_006e: Unknown result type (might be due to invalid IL or missing references)
		//IL_007c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0082: Unknown result type (might be due to invalid IL or missing references)
		//IL_0097: Unknown result type (might be due to invalid IL or missing references)
		NetworkManager singleton = NetworkManager.Singleton;
		if ((Object)(object)singleton == (Object)null || !singleton.IsListening)
		{
			return;
		}
		RegisterNetworkHandlers();
		if (singleton.IsServer)
		{
			BroadcastJumpSelection(playerId, suitId, clipIndex);
			return;
		}
		FastBufferWriter val = default(FastBufferWriter);
		((FastBufferWriter)(ref val))..ctor(32, (Allocator)2, -1);
		try
		{
			((FastBufferWriter)(ref val)).WriteValueSafe<ulong>(ref playerId, default(ForPrimitives));
			((FastBufferWriter)(ref val)).WriteValueSafe<int>(ref suitId, default(ForPrimitives));
			((FastBufferWriter)(ref val)).WriteValueSafe<int>(ref clipIndex, default(ForPrimitives));
			singleton.CustomMessagingManager.SendNamedMessage("BM_Suits.Jump.Request", 0uL, val, (NetworkDelivery)3);
		}
		finally
		{
			((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose();
		}
	}

	private static void HandleJumpRequest(ulong sender, FastBufferReader reader)
	{
		//IL_002b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0031: Unknown result type (might be due to invalid IL or missing references)
		//IL_003f: 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_0053: Unknown result type (might be due to invalid IL or missing references)
		//IL_0059: Unknown result type (might be due to invalid IL or missing references)
		if (!((Object)(object)NetworkManager.Singleton == (Object)null) && NetworkManager.Singleton.IsServer)
		{
			ulong num = default(ulong);
			((FastBufferReader)(ref reader)).ReadValueSafe<ulong>(ref num, default(ForPrimitives));
			int suitId = default(int);
			((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref suitId, default(ForPrimitives));
			int clipIndex = default(int);
			((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref clipIndex, default(ForPrimitives));
			if (sender == num)
			{
				BroadcastJumpSelection(num, suitId, clipIndex);
			}
		}
	}

	private unsafe static void BroadcastJumpSelection(ulong playerId, int suitId, int clipIndex)
	{
		//IL_004c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0052: Unknown result type (might be due to invalid IL or missing references)
		//IL_0060: 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_0074: Unknown result type (might be due to invalid IL or missing references)
		//IL_007a: Unknown result type (might be due to invalid IL or missing references)
		//IL_008d: Unknown result type (might be due to invalid IL or missing references)
		NetworkManager singleton = NetworkManager.Singleton;
		if ((Object)(object)singleton == (Object)null || !singleton.IsServer)
		{
			return;
		}
		if (singleton.IsClient)
		{
			ApplyJumpSelection(suitId, clipIndex);
		}
		FastBufferWriter val = default(FastBufferWriter);
		((FastBufferWriter)(ref val))..ctor(32, (Allocator)2, -1);
		try
		{
			((FastBufferWriter)(ref val)).WriteValueSafe<ulong>(ref playerId, default(ForPrimitives));
			((FastBufferWriter)(ref val)).WriteValueSafe<int>(ref suitId, default(ForPrimitives));
			((FastBufferWriter)(ref val)).WriteValueSafe<int>(ref clipIndex, default(ForPrimitives));
			singleton.CustomMessagingManager.SendNamedMessageToAll("BM_Suits.Jump.Broadcast", val, (NetworkDelivery)3);
		}
		finally
		{
			((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose();
		}
	}

	private static void HandleJumpBroadcast(ulong sender, FastBufferReader reader)
	{
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		//IL_000d: Unknown result type (might be due to invalid IL or missing references)
		//IL_001a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0020: Unknown result type (might be due to invalid IL or missing references)
		//IL_002d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0033: Unknown result type (might be due to invalid IL or missing references)
		ulong num = default(ulong);
		((FastBufferReader)(ref reader)).ReadValueSafe<ulong>(ref num, default(ForPrimitives));
		int suitId = default(int);
		((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref suitId, default(ForPrimitives));
		int clipIndex = default(int);
		((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref clipIndex, default(ForPrimitives));
		ApplyJumpSelection(suitId, clipIndex);
	}

	private static void ApplyJumpSelection(int suitId, int clipIndex)
	{
		UnlockableItem unlockable = GetUnlockable(suitId);
		if (unlockable != null && jumpSounds.TryGetValue(NormalizeSuitName(unlockable.unlockableName), out var value))
		{
			AudioClip clip = value.GetClip(clipIndex);
			if ((Object)(object)clip != (Object)null)
			{
				unlockable.jumpAudio = clip;
			}
		}
	}

	private static void ChooseSuitUpSound(PlayerControllerB player, int suitId)
	{
		UnlockableItem unlockable = GetUnlockable(suitId);
		if (unlockable != null && suitUpSounds.TryGetValue(NormalizeSuitName(unlockable.unlockableName), out var value))
		{
			int nextIndex = value.GetNextIndex();
			if (nextIndex >= 0)
			{
				SendSuitUpSelection(player.playerClientId, suitId, nextIndex);
			}
		}
	}

	private unsafe static void SendSuitUpSelection(ulong playerId, int suitId, int clipIndex)
	{
		//IL_0054: 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_0068: Unknown result type (might be due to invalid IL or missing references)
		//IL_006e: Unknown result type (might be due to invalid IL or missing references)
		//IL_007c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0082: Unknown result type (might be due to invalid IL or missing references)
		//IL_0097: Unknown result type (might be due to invalid IL or missing references)
		NetworkManager singleton = NetworkManager.Singleton;
		if ((Object)(object)singleton == (Object)null || !singleton.IsListening)
		{
			return;
		}
		RegisterNetworkHandlers();
		if (singleton.IsServer)
		{
			BroadcastSuitUpSelection(playerId, suitId, clipIndex);
			return;
		}
		FastBufferWriter val = default(FastBufferWriter);
		((FastBufferWriter)(ref val))..ctor(32, (Allocator)2, -1);
		try
		{
			((FastBufferWriter)(ref val)).WriteValueSafe<ulong>(ref playerId, default(ForPrimitives));
			((FastBufferWriter)(ref val)).WriteValueSafe<int>(ref suitId, default(ForPrimitives));
			((FastBufferWriter)(ref val)).WriteValueSafe<int>(ref clipIndex, default(ForPrimitives));
			singleton.CustomMessagingManager.SendNamedMessage("BM_Suits.SuitUp.Request", 0uL, val, (NetworkDelivery)3);
		}
		finally
		{
			((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose();
		}
	}

	private static void HandleSuitUpRequest(ulong sender, FastBufferReader reader)
	{
		//IL_002b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0031: Unknown result type (might be due to invalid IL or missing references)
		//IL_003f: 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_0053: Unknown result type (might be due to invalid IL or missing references)
		//IL_0059: Unknown result type (might be due to invalid IL or missing references)
		if (!((Object)(object)NetworkManager.Singleton == (Object)null) && NetworkManager.Singleton.IsServer)
		{
			ulong num = default(ulong);
			((FastBufferReader)(ref reader)).ReadValueSafe<ulong>(ref num, default(ForPrimitives));
			int suitId = default(int);
			((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref suitId, default(ForPrimitives));
			int clipIndex = default(int);
			((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref clipIndex, default(ForPrimitives));
			if (sender == num)
			{
				BroadcastSuitUpSelection(num, suitId, clipIndex);
			}
		}
	}

	private unsafe static void BroadcastSuitUpSelection(ulong playerId, int suitId, int clipIndex)
	{
		//IL_003e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0044: Unknown result type (might be due to invalid IL or missing references)
		//IL_0051: Unknown result type (might be due to invalid IL or missing references)
		//IL_0057: Unknown result type (might be due to invalid IL or missing references)
		//IL_0064: 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_007c: Unknown result type (might be due to invalid IL or missing references)
		NetworkManager singleton = NetworkManager.Singleton;
		if ((Object)(object)singleton == (Object)null || !singleton.IsServer)
		{
			return;
		}
		ApplySuitUpSelection(playerId, suitId, clipIndex);
		FastBufferWriter val = default(FastBufferWriter);
		((FastBufferWriter)(ref val))..ctor(32, (Allocator)2, -1);
		try
		{
			((FastBufferWriter)(ref val)).WriteValueSafe<ulong>(ref playerId, default(ForPrimitives));
			((FastBufferWriter)(ref val)).WriteValueSafe<int>(ref suitId, default(ForPrimitives));
			((FastBufferWriter)(ref val)).WriteValueSafe<int>(ref clipIndex, default(ForPrimitives));
			singleton.CustomMessagingManager.SendNamedMessageToAll("BM_Suits.SuitUp.Broadcast", val, (NetworkDelivery)3);
		}
		finally
		{
			((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose();
		}
	}

	private static void HandleSuitUpBroadcast(ulong sender, FastBufferReader reader)
	{
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		//IL_000d: Unknown result type (might be due to invalid IL or missing references)
		//IL_001a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0020: Unknown result type (might be due to invalid IL or missing references)
		//IL_002d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0033: Unknown result type (might be due to invalid IL or missing references)
		ulong playerId = default(ulong);
		((FastBufferReader)(ref reader)).ReadValueSafe<ulong>(ref playerId, default(ForPrimitives));
		int suitId = default(int);
		((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref suitId, default(ForPrimitives));
		int clipIndex = default(int);
		((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref clipIndex, default(ForPrimitives));
		ApplySuitUpSelection(playerId, suitId, clipIndex);
	}

	private static void ApplySuitUpSelection(ulong playerId, int suitId, int clipIndex)
	{
		PlayerControllerB player = GetPlayer(playerId);
		if ((Object)(object)player == (Object)null || (Object)(object)player.movementAudio == (Object)null)
		{
			return;
		}
		UnlockableItem unlockable = GetUnlockable(suitId);
		if (unlockable != null && suitUpSounds.TryGetValue(NormalizeSuitName(unlockable.unlockableName), out var value))
		{
			AudioClip clip = value.GetClip(clipIndex);
			if (!((Object)(object)clip == (Object)null))
			{
				player.movementAudio.PlayOneShot(clip);
			}
		}
	}

	private static PlayerControllerB GetPlayer(ulong playerId)
	{
		if ((Object)(object)StartOfRound.Instance == (Object)null || StartOfRound.Instance.allPlayerScripts == null)
		{
			return null;
		}
		PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts;
		foreach (PlayerControllerB val in allPlayerScripts)
		{
			if ((Object)(object)val != (Object)null && val.playerClientId == playerId)
			{
				return val;
			}
		}
		return null;
	}

	private static UnlockableItem GetUnlockable(int suitId)
	{
		if ((Object)(object)StartOfRound.Instance == (Object)null || (Object)(object)StartOfRound.Instance.unlockablesList == (Object)null || StartOfRound.Instance.unlockablesList.unlockables == null)
		{
			return null;
		}
		List<UnlockableItem> unlockables = StartOfRound.Instance.unlockablesList.unlockables;
		if (suitId < 0 || suitId >= unlockables.Count)
		{
			return null;
		}
		return unlockables[suitId];
	}

	private static void PrunePending()
	{
		float time = Time.time;
		for (int num = pendingSuitUpSelections.Count - 1; num >= 0; num--)
		{
			if (time - pendingSuitUpSelections[num].Time > 5f)
			{
				pendingSuitUpSelections.RemoveAt(num);
			}
		}
		for (int num2 = pendingSuitRpcs.Count - 1; num2 >= 0; num2--)
		{
			if (time - pendingSuitRpcs[num2].Time > 5f)
			{
				pendingSuitRpcs.RemoveAt(num2);
			}
		}
	}

	private static IEnumerator LoadAudioClip(string path, Action<AudioClip> callback)
	{
		AudioType type = GetAudioType(path);
		if ((int)type == 0)
		{
			callback(null);
			yield break;
		}
		string uri = new Uri(path).AbsoluteUri;
		UnityWebRequest request = UnityWebRequestMultimedia.GetAudioClip(uri, type);
		try
		{
			yield return request.SendWebRequest();
			if ((int)request.result != 1)
			{
				callback(null);
				yield break;
			}
			AudioClip clip = DownloadHandlerAudioClip.GetContent(request);
			if ((Object)(object)clip != (Object)null)
			{
				((Object)clip).name = Path.GetFileNameWithoutExtension(path);
			}
			callback(clip);
		}
		finally
		{
			((IDisposable)request)?.Dispose();
		}
	}

	private static AudioType GetAudioType(string path)
	{
		//IL_003a: 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_003f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0044: Unknown result type (might be due to invalid IL or missing references)
		//IL_0048: Unknown result type (might be due to invalid IL or missing references)
		return (AudioType)(Path.GetExtension(path).ToLowerInvariant() switch
		{
			".ogg" => 14, 
			".wav" => 20, 
			".mp3" => 13, 
			_ => 0, 
		});
	}

	private static string NormalizeSuitName(string value)
	{
		if (string.IsNullOrWhiteSpace(value))
		{
			return value;
		}
		string text = value.Trim();
		if (text.EndsWith(" suit", StringComparison.OrdinalIgnoreCase))
		{
			text = text.Substring(0, text.Length - 5).TrimEnd(Array.Empty<char>());
		}
		return text;
	}
}