Decompiled source of TophatAccessoryLoader v3.0.0

Tophat.dll

Decompiled a month 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 BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Logging;
using Mirror;
using Newtonsoft.Json.Linq;
using UnityEngine;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("Tophat")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Tophat")]
[assembly: AssemblyCopyright("Copyright ©  2024")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("f02060ea-11ae-4930-be41-06bbb4ef1c09")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("1.0.0.0")]
[BepInPlugin("com.morsecodeguy.tophataccessoryloader", "Tophat Accessory Loader", "3.0.0")]
public class AccessoryLoader : BaseUnityPlugin
{
	private static ManualLogSource Logger;

	private AssetBundle accessoryBundle;

	private Dictionary<GameObject, Dictionary<string, GameObject>> activeAccessories = new Dictionary<GameObject, Dictionary<string, GameObject>>();

	private List<string> selectedFaceAccessories = new List<string>();

	private List<string> selectedHeadAccessories = new List<string>();

	private string selectedTorsoAccessory;

	private string accessoryBundlePath;

	private string[] faceAccessories;

	private string[] headAccessories;

	private string[] torsoAccessories;

	private bool showAccessoryMenu = false;

	private Vector2 scrollPositionFace;

	private Vector2 scrollPositionHead;

	private Vector2 scrollPositionTorso;

	private Dictionary<GameObject, Dictionary<GameObject, JObject>> specialAccessoryData = new Dictionary<GameObject, Dictionary<GameObject, JObject>>();

	private string storedPlayerName;

	private GameObject currentPlayer;

	private bool isAccessoryApplied = false;

	private string accessoryPresetFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Plebeian Studio/Gladio Mori/Accessory Presets");

	private Dictionary<GameObject, Dictionary<GameObject, float>> accessoryCooldownTimers = new Dictionary<GameObject, Dictionary<GameObject, float>>();

	private HashSet<GameObject> accessoryAppliedPlayers = new HashSet<GameObject>();

	private void Awake()
	{
		Logger = ((BaseUnityPlugin)this).Logger;
		accessoryBundlePath = Path.Combine(Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location), "accessorys.accessory");
		LoadAccessoryBundle();
		((Object)Chainloader.ManagerObject).hideFlags = (HideFlags)61;
		Logger.LogInfo((object)"AccessoryLoader initialized.");
		SceneManager.sceneLoaded += OnSceneLoaded;
		SceneManager.sceneUnloaded += OnSceneUnloaded;
		if (!Directory.Exists(accessoryPresetFolder))
		{
			Directory.CreateDirectory(accessoryPresetFolder);
		}
	}

	private void OnDestroy()
	{
		SceneManager.sceneLoaded -= OnSceneLoaded;
		SceneManager.sceneUnloaded -= OnSceneUnloaded;
		UnloadAccessoryBundle();
	}

	private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
	{
		((MonoBehaviour)this).StartCoroutine(WaitForLocalPlayerAndAttachAccessories());
	}

	private void OnSceneUnloaded(Scene scene)
	{
		accessoryAppliedPlayers.Clear();
		activeAccessories.Clear();
		specialAccessoryData.Clear();
		accessoryCooldownTimers.Clear();
		isAccessoryApplied = false;
		storedPlayerName = null;
	}

	private void LoadAccessoryBundle()
	{
		if (File.Exists(accessoryBundlePath))
		{
			accessoryBundle = AssetBundle.LoadFromFile(accessoryBundlePath);
			if ((Object)(object)accessoryBundle != (Object)null)
			{
				Logger.LogInfo((object)"Accessory bundle loaded successfully.");
				LoadAccessories();
			}
			else
			{
				Logger.LogError((object)"Failed to load accessory bundle.");
			}
		}
		else
		{
			Logger.LogError((object)("Accessory bundle not found at path: " + accessoryBundlePath));
		}
	}

	private void UnloadAccessoryBundle()
	{
		if ((Object)(object)accessoryBundle != (Object)null)
		{
			accessoryBundle.Unload(true);
			accessoryBundle = null;
			Logger.LogInfo((object)"Accessory bundle unloaded.");
		}
	}

	private void LoadAccessories()
	{
		faceAccessories = (from asset in accessoryBundle.GetAllAssetNames()
			where asset.Contains("/face/") && asset.EndsWith("accessoryprefab.prefab")
			select asset).ToArray();
		headAccessories = (from asset in accessoryBundle.GetAllAssetNames()
			where asset.Contains("/head/") && asset.EndsWith("accessoryprefab.prefab")
			select asset).ToArray();
		torsoAccessories = (from asset in accessoryBundle.GetAllAssetNames()
			where asset.Contains("/torso/") && asset.EndsWith("accessoryprefab.prefab")
			select asset).ToArray();
	}

	private IEnumerator WaitForLocalPlayerAndAttachAccessories()
	{
		while (true)
		{
			if (string.IsNullOrEmpty(storedPlayerName))
			{
				storedPlayerName = FindLocalPlayerName();
			}
			if (!string.IsNullOrEmpty(storedPlayerName))
			{
				GameObject playerObject = FindPlayerObjectByName(storedPlayerName);
				if ((Object)(object)playerObject != (Object)null && !isAccessoryApplied)
				{
					AttachSelectedAccessories(playerObject);
					currentPlayer = playerObject;
					isAccessoryApplied = true;
				}
			}
			FindAndAttachAccessoriesToOtherPlayers();
			yield return (object)new WaitForSeconds(0.1f);
		}
	}

	private void FindAndAttachAccessoriesToOtherPlayers()
	{
		string[] array = new string[2] { "PlayerCharacter(Clone)", "PlayerCharacter(Clone)1" };
		string[] array2 = array;
		foreach (string playerName in array2)
		{
			IEnumerable<GameObject> enumerable = from go in Object.FindObjectsOfType<GameObject>()
				where ((Object)go).name == playerName
				select go;
			foreach (GameObject item in enumerable)
			{
				if (!accessoryAppliedPlayers.Contains(item))
				{
					PlayerHealth component = item.GetComponent<PlayerHealth>();
					if ((Object)(object)component != (Object)null && !component.ai)
					{
						AttachSelectedAccessories(item);
						accessoryAppliedPlayers.Add(item);
						Logger.LogInfo((object)("Accessory applied to player: " + ((Object)item).name));
					}
				}
			}
		}
	}

	private string FindLocalPlayerName()
	{
		PlayerMultiplayerInputManager[] array = Object.FindObjectsOfType<PlayerMultiplayerInputManager>();
		PlayerMultiplayerInputManager[] array2 = array;
		foreach (PlayerMultiplayerInputManager val in array2)
		{
			if (((NetworkBehaviour)val).isLocalPlayer)
			{
				return val.playerName;
			}
		}
		return null;
	}

	private GameObject FindPlayerObjectByName(string playerName)
	{
		IEnumerable<GameObject> enumerable = from go in Object.FindObjectsOfType<GameObject>()
			where ((Object)go).name == "PlayerCharacterMultiplayer(Clone)"
			select go;
		foreach (GameObject item in enumerable)
		{
			Transform val = item.transform.Find("PlayerModelPhysics/HIP/PlayerName(Clone)");
			if ((Object)(object)val != (Object)null)
			{
				TextMesh component = ((Component)val).GetComponent<TextMesh>();
				if ((Object)(object)component != (Object)null && component.text == playerName)
				{
					return item;
				}
			}
		}
		return null;
	}

	private void AttachSelectedAccessories(GameObject player)
	{
		if ((Object)(object)player == (Object)null)
		{
			Logger.LogWarning((object)"AttachSelectedAccessories called with null player.");
			return;
		}
		ClearExistingAccessories(player);
		if (!activeAccessories.ContainsKey(player))
		{
			activeAccessories[player] = new Dictionary<string, GameObject>();
		}
		if (!specialAccessoryData.ContainsKey(player))
		{
			specialAccessoryData[player] = new Dictionary<GameObject, JObject>();
		}
		if (!accessoryCooldownTimers.ContainsKey(player))
		{
			accessoryCooldownTimers[player] = new Dictionary<GameObject, float>();
		}
		if (selectedFaceAccessories.Count > 0)
		{
			foreach (string selectedFaceAccessory in selectedFaceAccessories)
			{
				AttachAccessory(player, selectedFaceAccessory, "Face", new string[6] { "PlayerModelPhysics", "HIP", "SPINE1", "SPINE2", "NECK", "MeshHead" }, applyRotation: true);
			}
		}
		if (selectedHeadAccessories.Count > 0)
		{
			foreach (string selectedHeadAccessory in selectedHeadAccessories)
			{
				AttachAccessory(player, selectedHeadAccessory, "Head", new string[6] { "PlayerModelPhysics", "HIP", "SPINE1", "SPINE2", "NECK", "MeshHead" }, applyRotation: true);
			}
		}
		if (!string.IsNullOrEmpty(selectedTorsoAccessory))
		{
			AttachAccessory(player, selectedTorsoAccessory, "Torso", new string[5] { "PlayerModelPhysics", "HIP", "SPINE1", "SPINE2", "MeshChest" }, applyRotation: true);
		}
	}

	private void ClearExistingAccessories(GameObject player)
	{
		if ((Object)(object)player == (Object)null)
		{
			Logger.LogWarning((object)"ClearExistingAccessories called with null player.");
			return;
		}
		if (activeAccessories.ContainsKey(player))
		{
			foreach (GameObject value in activeAccessories[player].Values)
			{
				if ((Object)(object)value != (Object)null)
				{
					Object.Destroy((Object)(object)value);
				}
			}
			activeAccessories[player].Clear();
		}
		if (accessoryCooldownTimers.ContainsKey(player))
		{
			accessoryCooldownTimers[player].Clear();
		}
	}

	private void AttachAccessory(GameObject player, string accessoryPath, string accessoryType, string[] bonePath, bool applyRotation)
	{
		//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
		//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_00d7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e3: 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)
		if (!activeAccessories.ContainsKey(player))
		{
			activeAccessories[player] = new Dictionary<string, GameObject>();
		}
		if (activeAccessories[player].ContainsKey(accessoryPath))
		{
			Object.Destroy((Object)(object)activeAccessories[player][accessoryPath]);
		}
		Transform val = FindBoneInHierarchy(player.transform, bonePath);
		if (!((Object)(object)val != (Object)null))
		{
			return;
		}
		GameObject val2 = accessoryBundle.LoadAsset<GameObject>(accessoryPath);
		if ((Object)(object)val2 != (Object)null)
		{
			GameObject val3 = Object.Instantiate<GameObject>(val2, val);
			val3.transform.localPosition = Vector3.zero;
			if (applyRotation)
			{
				Quaternion localRotation = val3.transform.localRotation;
				val3.transform.localRotation = Quaternion.Euler(90f, ((Quaternion)(ref localRotation)).eulerAngles.y, ((Quaternion)(ref localRotation)).eulerAngles.z);
			}
			((Object)val3).name = accessoryType + "Accessory";
			activeAccessories[player][accessoryPath] = val3;
			LoadSpecialAccessoryData(player, accessoryPath, val3);
			accessoryCooldownTimers[player][val3] = 0f;
		}
	}

	private Transform FindBoneInHierarchy(Transform root, string[] boneNames)
	{
		Transform val = root;
		foreach (string text in boneNames)
		{
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			val = val.Find(text);
		}
		return val;
	}

	private void LoadSpecialAccessoryData(GameObject player, string accessoryPath, GameObject accessoryInstance)
	{
		string text = Path.GetDirectoryName(accessoryPath) + "/special.json";
		if (!accessoryBundle.Contains(text))
		{
			return;
		}
		TextAsset val = accessoryBundle.LoadAsset<TextAsset>(text);
		if ((Object)(object)val != (Object)null)
		{
			JObject value = JObject.Parse(val.text);
			if (!specialAccessoryData.ContainsKey(player))
			{
				specialAccessoryData[player] = new Dictionary<GameObject, JObject>();
			}
			specialAccessoryData[player][accessoryInstance] = value;
		}
	}

	private void HandleSpecialAccessory(GameObject player, GameObject accessoryInstance, JObject specialData, int keyPressed)
	{
		//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)accessoryInstance == (Object)null || specialData == null || accessoryCooldownTimers[player][accessoryInstance] > 0f)
		{
			return;
		}
		string text = ((object)specialData["Type"])?.ToString();
		if (string.IsNullOrEmpty(text))
		{
			return;
		}
		bool flag = false;
		if (text == "Animation" || text == "Both")
		{
			Animator component = accessoryInstance.GetComponent<Animator>();
			if ((Object)(object)component != (Object)null)
			{
				string text2 = $"SpecialAnimation-{keyPressed}";
				if (component.HasState(0, Animator.StringToHash(text2)))
				{
					component.Play(text2, -1, 0f);
					AnimatorStateInfo currentAnimatorStateInfo = component.GetCurrentAnimatorStateInfo(0);
					float length = ((AnimatorStateInfo)(ref currentAnimatorStateInfo)).length;
					accessoryCooldownTimers[player][accessoryInstance] = Mathf.Max(accessoryCooldownTimers[player][accessoryInstance], length);
					flag = true;
				}
			}
		}
		if (text == "Sound" || text == "Both")
		{
			Transform val = accessoryInstance.transform.Find($"AccessoryPivot/SoundPivot-{keyPressed}");
			if ((Object)(object)val != (Object)null)
			{
				AudioSource component2 = ((Component)val).GetComponent<AudioSource>();
				if ((Object)(object)component2 != (Object)null)
				{
					component2.PlayOneShot(component2.clip);
					accessoryCooldownTimers[player][accessoryInstance] = Mathf.Max(accessoryCooldownTimers[player][accessoryInstance], component2.clip.length);
					flag = true;
				}
			}
		}
		if (flag)
		{
			accessoryCooldownTimers[player][accessoryInstance] = Mathf.Max(accessoryCooldownTimers[player][accessoryInstance], 0.1f);
		}
	}

	private void Update()
	{
		foreach (KeyValuePair<GameObject, Dictionary<GameObject, float>> accessoryCooldownTimer in accessoryCooldownTimers)
		{
			GameObject key = accessoryCooldownTimer.Key;
			Dictionary<GameObject, float> value = accessoryCooldownTimer.Value;
			List<GameObject> list = new List<GameObject>(value.Keys);
			foreach (GameObject item in list)
			{
				if (value[item] > 0f)
				{
					value[item] -= Time.deltaTime;
					if (value[item] <= 0f)
					{
						value[item] = 0f;
					}
				}
			}
		}
		if (Input.GetKeyDown((KeyCode)285))
		{
			showAccessoryMenu = !showAccessoryMenu;
		}
		for (int i = 1; i <= 9; i++)
		{
			if (!Input.GetKeyDown((KeyCode)(49 + (i - 1))))
			{
				continue;
			}
			foreach (KeyValuePair<GameObject, Dictionary<GameObject, JObject>> specialAccessoryDatum in specialAccessoryData)
			{
				GameObject key2 = specialAccessoryDatum.Key;
				Dictionary<GameObject, JObject> value2 = specialAccessoryDatum.Value;
				foreach (KeyValuePair<GameObject, JObject> item2 in value2)
				{
					HandleSpecialAccessory(key2, item2.Key, item2.Value, i);
				}
			}
		}
	}

	private void OnGUI()
	{
		//IL_002a: 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_004a: Expected O, but got Unknown
		//IL_0045: Unknown result type (might be due to invalid IL or missing references)
		if (showAccessoryMenu)
		{
			GUILayout.Window(1, new Rect((float)(Screen.width / 2 - 150), 50f, 300f, 400f), new WindowFunction(DrawAccessoryMenu), "Accessory Selection", Array.Empty<GUILayoutOption>());
		}
	}

	private void DrawAccessoryMenu(int windowID)
	{
		//IL_0013: Unknown result type (might be due to invalid IL or missing references)
		//IL_0038: Unknown result type (might be due to invalid IL or missing references)
		//IL_003d: 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_011c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0121: Unknown result type (might be due to invalid IL or missing references)
		//IL_01e9: Unknown result type (might be due to invalid IL or missing references)
		//IL_020e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0213: Unknown result type (might be due to invalid IL or missing references)
		GUILayout.Label("Face Accessories:", Array.Empty<GUILayoutOption>());
		scrollPositionFace = GUILayout.BeginScrollView(scrollPositionFace, (GUILayoutOption[])(object)new GUILayoutOption[2]
		{
			GUILayout.Width(280f),
			GUILayout.Height(150f)
		});
		string[] array = faceAccessories;
		foreach (string text in array)
		{
			string text2 = ToTitleCase(Path.GetFileName(Path.GetDirectoryName(text)));
			if (GUILayout.Button(text2, Array.Empty<GUILayoutOption>()))
			{
				if (selectedFaceAccessories.Count < 2)
				{
					selectedFaceAccessories.Add(text);
				}
				else
				{
					selectedFaceAccessories[0] = text;
				}
				if ((Object)(object)currentPlayer != (Object)null)
				{
					AttachSelectedAccessories(currentPlayer);
				}
			}
		}
		GUILayout.EndScrollView();
		GUILayout.Label("Head Accessories:", Array.Empty<GUILayoutOption>());
		scrollPositionHead = GUILayout.BeginScrollView(scrollPositionHead, (GUILayoutOption[])(object)new GUILayoutOption[2]
		{
			GUILayout.Width(280f),
			GUILayout.Height(150f)
		});
		string[] array2 = headAccessories;
		foreach (string text3 in array2)
		{
			string text4 = ToTitleCase(Path.GetFileName(Path.GetDirectoryName(text3)));
			if (GUILayout.Button(text4, Array.Empty<GUILayoutOption>()))
			{
				if (selectedHeadAccessories.Count < 2)
				{
					selectedHeadAccessories.Add(text3);
				}
				else
				{
					selectedHeadAccessories[0] = text3;
				}
				if ((Object)(object)currentPlayer != (Object)null)
				{
					AttachSelectedAccessories(currentPlayer);
				}
			}
		}
		GUILayout.EndScrollView();
		GUILayout.Label("Torso Accessories:", Array.Empty<GUILayoutOption>());
		scrollPositionTorso = GUILayout.BeginScrollView(scrollPositionTorso, (GUILayoutOption[])(object)new GUILayoutOption[2]
		{
			GUILayout.Width(280f),
			GUILayout.Height(150f)
		});
		string[] array3 = torsoAccessories;
		foreach (string path in array3)
		{
			string text5 = ToTitleCase(Path.GetFileName(Path.GetDirectoryName(path)));
			if (GUILayout.Button(text5, Array.Empty<GUILayoutOption>()))
			{
				selectedTorsoAccessory = path;
				if ((Object)(object)currentPlayer != (Object)null)
				{
					AttachSelectedAccessories(currentPlayer);
				}
			}
		}
		GUILayout.EndScrollView();
		if (GUILayout.Button("Clear Face Accessories", Array.Empty<GUILayoutOption>()))
		{
			selectedFaceAccessories.Clear();
			if ((Object)(object)currentPlayer != (Object)null)
			{
				AttachSelectedAccessories(currentPlayer);
			}
		}
		if (GUILayout.Button("Clear Head Accessories", Array.Empty<GUILayoutOption>()))
		{
			selectedHeadAccessories.Clear();
			if ((Object)(object)currentPlayer != (Object)null)
			{
				AttachSelectedAccessories(currentPlayer);
			}
		}
		if (GUILayout.Button("Clear Torso Accessories", Array.Empty<GUILayoutOption>()))
		{
			selectedTorsoAccessory = null;
			if ((Object)(object)currentPlayer != (Object)null)
			{
				AttachSelectedAccessories(currentPlayer);
			}
		}
	}

	private string ToTitleCase(string input)
	{
		CultureInfo cultureInfo = new CultureInfo("en-US", useUserOverride: false);
		TextInfo textInfo = cultureInfo.TextInfo;
		return textInfo.ToTitleCase(input.ToLower());
	}
}