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());
}
}