Decompiled source of TophatAccessoryLoader v2.0.1

Tophat.dll

Decompiled 2 weeks 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", "2.0.0")]
public class AccessoryLoader : BaseUnityPlugin
{
	private static ManualLogSource Logger;

	private AssetBundle accessoryBundle;

	private Dictionary<string, GameObject> activeAccessories = new 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, JObject> specialAccessoryData = new 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, float> accessoryCooldownTimers = new Dictionary<GameObject, float>();

	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)
	{
		ClearExistingAccessories();
		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()
	{
		Logger.LogInfo((object)"Waiting for local player...");
		while (true)
		{
			if (string.IsNullOrEmpty(storedPlayerName))
			{
				storedPlayerName = FindLocalPlayerName();
			}
			if (!string.IsNullOrEmpty(storedPlayerName))
			{
				if (!isAccessoryApplied)
				{
					Logger.LogInfo((object)("Stored local player name: " + storedPlayerName));
				}
				GameObject playerObject = FindPlayerObjectByName(storedPlayerName);
				if ((Object)(object)playerObject != (Object)null && !isAccessoryApplied)
				{
					Logger.LogInfo((object)("Local player object found: " + ((Object)playerObject).name));
					AttachSelectedAccessories(playerObject);
					currentPlayer = playerObject;
					isAccessoryApplied = true;
				}
			}
			yield return (object)new WaitForSeconds(1f);
		}
	}

	private string FindLocalPlayerName()
	{
		PlayerMultiplayerInputManager[] array = Object.FindObjectsOfType<PlayerMultiplayerInputManager>();
		PlayerMultiplayerInputManager[] array2 = array;
		foreach (PlayerMultiplayerInputManager val in array2)
		{
			if (((NetworkBehaviour)val).isLocalPlayer)
			{
				return val.playerName;
			}
		}
		if (string.IsNullOrEmpty(storedPlayerName))
		{
			Logger.LogWarning((object)"Local player not found.");
		}
		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)
	{
		ClearExistingAccessories();
		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()
	{
		foreach (GameObject value in activeAccessories.Values)
		{
			if ((Object)(object)value != (Object)null)
			{
				Object.Destroy((Object)(object)value);
			}
		}
		activeAccessories.Clear();
		accessoryCooldownTimers.Clear();
	}

	private void AttachAccessory(GameObject player, string accessoryPath, string accessoryType, string[] bonePath, bool applyRotation)
	{
		//IL_0071: Unknown result type (might be due to invalid IL or missing references)
		//IL_008c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0091: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
		if (activeAccessories.ContainsKey(accessoryPath))
		{
			Object.Destroy((Object)(object)activeAccessories[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[accessoryPath] = val3;
			LoadSpecialAccessoryData(accessoryPath, val3);
			accessoryCooldownTimers[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(string accessoryPath, GameObject accessoryInstance)
	{
		string text = Path.GetDirectoryName(accessoryPath) + "/special.json";
		if (accessoryBundle.Contains(text))
		{
			TextAsset val = accessoryBundle.LoadAsset<TextAsset>(text);
			if ((Object)(object)val != (Object)null)
			{
				JObject value = JObject.Parse(val.text);
				specialAccessoryData[accessoryInstance] = value;
			}
		}
	}

	private void HandleSpecialAccessory(GameObject accessoryInstance, JObject specialData, int keyPressed)
	{
		//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)
		if ((Object)(object)accessoryInstance == (Object)null || specialData == null || accessoryCooldownTimers[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[accessoryInstance] = Mathf.Max(accessoryCooldownTimers[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[accessoryInstance] = Mathf.Max(accessoryCooldownTimers[accessoryInstance], component2.clip.length);
					flag = true;
				}
			}
		}
		if (flag)
		{
			accessoryCooldownTimers[accessoryInstance] = Mathf.Max(accessoryCooldownTimers[accessoryInstance], 0.1f);
		}
	}

	private void Update()
	{
		List<GameObject> list = new List<GameObject>(accessoryCooldownTimers.Keys);
		foreach (GameObject item in list)
		{
			if (accessoryCooldownTimers[item] > 0f)
			{
				accessoryCooldownTimers[item] -= Time.deltaTime;
				if (accessoryCooldownTimers[item] <= 0f)
				{
					accessoryCooldownTimers[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, JObject> specialAccessoryDatum in specialAccessoryData)
			{
				HandleSpecialAccessory(specialAccessoryDatum.Key, specialAccessoryDatum.Value, i);
			}
		}
	}

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

	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_00dd: Unknown result type (might be due to invalid IL or missing references)
		//IL_0102: Unknown result type (might be due to invalid IL or missing references)
		//IL_0107: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b8: Unknown result type (might be due to invalid IL or missing references)
		//IL_01dd: Unknown result type (might be due to invalid IL or missing references)
		//IL_01e2: Unknown result type (might be due to invalid IL or missing references)
		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;
				}
				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;
				}
				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;
				AttachSelectedAccessories(currentPlayer);
			}
		}
		GUILayout.EndScrollView();
		if (GUILayout.Button("Clear Face Accessories", Array.Empty<GUILayoutOption>()))
		{
			selectedFaceAccessories.Clear();
			AttachSelectedAccessories(currentPlayer);
		}
		if (GUILayout.Button("Clear Head Accessories", Array.Empty<GUILayoutOption>()))
		{
			selectedHeadAccessories.Clear();
			AttachSelectedAccessories(currentPlayer);
		}
		if (GUILayout.Button("Clear Torso Accessories", Array.Empty<GUILayoutOption>()))
		{
			selectedTorsoAccessory = 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());
	}
}