Decompiled source of Fancy v1.0.0

BepInEx/plugins/Fancy/Fancy.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Logging;
using ComputerysModdingUtilities;
using Fancy.Resources;
using FishNet;
using FishNet.Managing;
using FishNet.Object;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: StraftatMod(false)]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("Fancy")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("A custom cosmetics API for straftat")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("kestrel.straftat.fancy")]
[assembly: AssemblyTitle("Fancy")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace Fancy
{
	public class CosmeticBundleManifest : MonoBehaviour
	{
		[Tooltip("The name of the cosmetic bundle.")]
		public string bundleName;

		[Tooltip("The author of the cosmetic bundle. Use your Thunderstore team name if applicable.")]
		public string bundleAuthor;

		[Tooltip("A *UNIQUE* string that identifies your cosmetic bundle. It's suggested that this follows the format <author>.<bundle name> in all lowercase without spaces.\nFor example: \"kestrel.examplebundle\"")]
		public string bundleGuid;

		[Tooltip("The version of the cosmetic bundle. It's suggested that you use semantic versioning (<major>.<minor>.<patch>)")]
		public string bundleVersion = "1.0.0";

		[Tooltip("A list of references to every cosmetic the bundle includes. Only cosmetics referenced here will be loaded ingame.")]
		public CosmeticReferenceBase[] cosmeticReferences;

		public string outputDirectory = "Assets/BuiltCosmeticBundles";
	}
	public class CosmeticReferenceBase : MonoBehaviour
	{
		[Tooltip("The name of the cosmetic.")]
		public string cosmeticName;

		[Tooltip("The icon of the cosmetic.")]
		public Sprite cosmeticIcon;

		[Tooltip("Whether the cosmetic requires the DLC to unlock.")]
		public bool dlcOnly;

		[HideInInspector]
		public string sourceGuid;
	}
	public enum PrefabCosmeticType : byte
	{
		Hat,
		Cig
	}
	public class PrefabCosmeticReference : CosmeticReferenceBase
	{
		[Tooltip("The type of the cosmetic. Only determines which menu it will be shown in ingame, as both cigs and hats use the same transform root.")]
		public PrefabCosmeticType type;

		[Tooltip("The prefab to instantiate~ this will be instantiated ingame with its parent as the cosmetics anchor.")]
		public GameObject prefab;
	}
	public class SuitCosmeticReference : CosmeticReferenceBase
	{
		[Tooltip("The material that will be applied to the suit.")]
		public Material suitMaterial;

		[Tooltip("The material that will be applied to the suit's first person arms.")]
		public Material armsMaterial;

		[Tooltip("The dummy used to test these suits. Optional.")]
		public SuitTestDummy dummy;

		public void ApplyToDummy()
		{
			if (!Object.op_Implicit((Object)(object)dummy))
			{
				Debug.LogError((object)"[Fancy] No dummy assigned- assign one to test suits.");
				return;
			}
			SkinnedMeshRenderer[] suitTargets = dummy.suitTargets;
			for (int i = 0; i < suitTargets.Length; i++)
			{
				((Renderer)suitTargets[i]).material = suitMaterial;
			}
			suitTargets = dummy.armTargets;
			for (int i = 0; i < suitTargets.Length; i++)
			{
				((Renderer)suitTargets[i]).material = armsMaterial;
			}
		}
	}
	public class SuitTestDummy : MonoBehaviour
	{
		public SkinnedMeshRenderer[] suitTargets;

		public SkinnedMeshRenderer[] armTargets;

		public GameObject arms;

		public void ToggleArms()
		{
			GameObject obj = arms;
			if (obj != null)
			{
				obj.SetActive(!arms.activeSelf);
			}
		}
	}
	public static class BundleLocator
	{
		private static string m_bundleDirectory = Path.Combine(Paths.PluginPath, "CosmeticBundles");

		public static uint InstalledCode { get; private set; }

		public static IEnumerator DiscoverAndLoadBundles()
		{
			if (!Directory.Exists(m_bundleDirectory))
			{
				Directory.CreateDirectory(m_bundleDirectory);
			}
			string[] files = Directory.GetFiles(Paths.PluginPath, "*.cbundle", SearchOption.AllDirectories);
			foreach (string path in files)
			{
				AssetBundleCreateRequest request = AssetBundle.LoadFromFileAsync(path);
				yield return request;
				string fileName = Path.GetFileName(path);
				if (Object.op_Implicit((Object)(object)request.assetBundle))
				{
					if (!RegisterCosmeticsFromBundle(request.assetBundle))
					{
						Plugin.Logger.LogError((object)("Cosmetic manifest not found for \"" + fileName + "\", it will be skipped."));
					}
				}
				else
				{
					Plugin.Logger.LogError((object)("Failed to load bundle from \"" + fileName + "\", it will be skipped."));
				}
			}
			((MonoBehaviour)Plugin.Instance).StartCoroutine(CosmeticLoader.LoadAllWhenReady());
		}

		private static bool RegisterCosmeticsFromBundle(AssetBundle bundle)
		{
			CosmeticBundleManifest cosmeticBundleManifest = bundle.mainAsset as CosmeticBundleManifest;
			if (!Object.op_Implicit((Object)(object)cosmeticBundleManifest))
			{
				return false;
			}
			Plugin.Logger.LogInfo((object)("Registering cosmetics from " + cosmeticBundleManifest.bundleGuid + " (\"" + cosmeticBundleManifest.bundleName + "\") version " + cosmeticBundleManifest.bundleVersion));
			int hashCode = (cosmeticBundleManifest.bundleGuid + cosmeticBundleManifest.bundleVersion).GetHashCode();
			InstalledCode = (uint)((int)InstalledCode + hashCode) % 1000000000u;
			CosmeticReferenceBase[] cosmeticReferences = cosmeticBundleManifest.cosmeticReferences;
			for (int i = 0; i < cosmeticReferences.Length; i++)
			{
				CosmeticLoader.RegisterCosmetic(cosmeticReferences[i]);
			}
			return true;
		}
	}
	public static class CosmeticLoader
	{
		[HarmonyPatch(typeof(CosmeticInstance))]
		public static class CosmeticInsancePatch
		{
			[HarmonyPatch("Start")]
			[HarmonyPostfix]
			public static void SetHoverText(CosmeticInstance __instance, string ___cosmeticName)
			{
				((Component)__instance).GetComponent<ButtonSizeTween>().customText = ___cosmeticName.SplitCamelCase();
			}
		}

		[HarmonyPatch(typeof(CosmeticsManager))]
		internal static class CosmeticsManagerPatch
		{
			[HarmonyPatch("Start")]
			[HarmonyPostfix]
			public static void CaptureManager(CosmeticsManager __instance)
			{
				m_manager = __instance;
			}
		}

		[HarmonyPatch(typeof(NetworkManager))]
		internal static class NetworkManagerPatch
		{
			[HarmonyPatch("Awake")]
			[HarmonyPostfix]
			public static void RefreshPrefabs(NetworkManager __instance)
			{
				foreach (CosmeticReferenceBase reference in m_references)
				{
					if (reference is PrefabCosmeticReference prefabCosmeticReference)
					{
						NetworkObject component = prefabCosmeticReference.prefab.GetComponent<NetworkObject>();
						if (component != null)
						{
							__instance.SpawnablePrefabs.AddObject(component, false);
						}
					}
				}
			}
		}

		private static CosmeticsManager m_manager;

		private static readonly LayerMask m_dontCollideWithPlayerLayer = LayerMask.op_Implicit(LayerMask.NameToLayer("DontCollideWithPlayer"));

		private static GameObject m_cosmeticInstancePrefab;

		private static List<CosmeticReferenceBase> m_references = new List<CosmeticReferenceBase>();

		private static int m_suitCount;

		public static bool HasLoadedAllCosmetics { get; private set; }

		public static void RegisterCosmetic(CosmeticReferenceBase reference)
		{
			if (HasLoadedAllCosmetics)
			{
				Plugin.Logger.LogError((object)("CosmeticLoader: tried to register \"" + reference.cosmeticName + "\" after cosmetics have been loaded; it will be ignored."));
				return;
			}
			int index = m_references.TakeWhile((CosmeticReferenceBase r) => string.CompareOrdinal(r.sourceGuid, reference.sourceGuid) < 0).Count();
			m_references.Insert(index, reference);
			if (reference is SuitCosmeticReference)
			{
				m_suitCount++;
			}
		}

		public static IEnumerator LoadAllWhenReady()
		{
			while (!Object.op_Implicit((Object)(object)m_manager))
			{
				yield return null;
			}
			Plugin.Logger.LogInfo((object)"Loading all registered cosmetics...");
			m_cosmeticInstancePrefab = Assets.Load<GameObject>("CosmeticInstanceTemplate");
			int num = m_manager.mats.Length;
			Material[] array = (Material[])(object)new Material[m_suitCount];
			Material[] array2 = (Material[])(object)new Material[m_suitCount];
			int num2 = 0;
			NetworkManager networkManager = InstanceFinder.NetworkManager;
			foreach (CosmeticReferenceBase reference in m_references)
			{
				bool flag = false;
				bool flag2 = false;
				if (reference is PrefabCosmeticReference prefabCosmeticReference)
				{
					if (prefabCosmeticReference.type == PrefabCosmeticType.Hat)
					{
						flag = true;
						object obj = prefabCosmeticReference.prefab.GetComponent<NetworkObject>();
						if (obj == null)
						{
							GameObject prefab = prefabCosmeticReference.prefab;
							obj = ((prefab != null) ? prefab.AddComponent<NetworkObject>() : null);
						}
						NetworkObject val = (NetworkObject)obj;
						if (Object.op_Implicit((Object)(object)val))
						{
							networkManager.SpawnablePrefabs.AddObject(val, false);
						}
						Collider[] componentsInChildren = prefabCosmeticReference.prefab.GetComponentsInChildren<Collider>();
						foreach (Collider obj2 in componentsInChildren)
						{
							((Component)obj2).gameObject.tag = "Hat";
							((Component)obj2).gameObject.layer = LayerMask.op_Implicit(m_dontCollideWithPlayerLayer);
						}
					}
					else
					{
						flag2 = true;
					}
				}
				Transform val2 = (flag ? m_manager.hatsParent : ((!flag2) ? m_manager.suitsParent : m_manager.cigsParent));
				CosmeticInstance component = Object.Instantiate<GameObject>(m_cosmeticInstancePrefab, val2, false).GetComponent<CosmeticInstance>();
				component.isHat = flag;
				component.isCig = flag2;
				component.sprite = reference.cosmeticIcon ?? ((Component)((Component)component).transform.Find("Image")).GetComponent<Image>().sprite;
				component.hat = (reference as PrefabCosmeticReference)?.prefab;
				component.cosmeticName = reference.cosmeticName;
				if (reference.dlcOnly)
				{
					component.acquired = false;
					component.unlockWithDlc = true;
				}
				if (reference is SuitCosmeticReference suitCosmeticReference)
				{
					array[num2] = suitCosmeticReference.suitMaterial;
					array2[num2] = suitCosmeticReference.armsMaterial;
					component.index = num + num2;
					num2++;
				}
			}
			int num3 = num + m_suitCount;
			if (num3 > num)
			{
				Material[] array3 = (Material[])(object)new Material[num3];
				Material[] array4 = (Material[])(object)new Material[num3];
				m_manager.mats.CopyTo(array3, 0);
				m_manager.fparmsMats.CopyTo(array4, 0);
				array.CopyTo(array3, num);
				array2.CopyTo(array4, num);
				m_manager.mats = array3;
				m_manager.fparmsMats = array4;
			}
			m_manager.Start();
			m_manager.cigs = (GameObject[])(object)new GameObject[m_manager.cigsChildren.Length];
			for (int j = 0; j < m_manager.cigs.Length; j++)
			{
				m_manager.cigs[j] = m_manager.cigsChildren[j].hat;
				m_manager.cigsChildren[j].index = j;
			}
			Plugin.Logger.LogInfo((object)"All custom cosmetics loaded!");
			Plugin.Logger.LogWarning((object)$"Your cosmetics code is {BundleLocator.InstalledCode}. MAKE SURE THIS IS *IDENTICAL* TO THE ONE THAT PEOPLE YOU WANT TO PLAY WITH HAVE!");
			Plugin.Logger.LogWarning((object)"If your codes do not match, you do not have the same cosmetic bundles installed and strange behaviour may occur. You have been warned!");
			HasLoadedAllCosmetics = true;
			m_manager.LoadDress();
		}
	}
	[BepInPlugin("kestrel.straftat.fancy", "Fancy", "1.0.0")]
	public class Plugin : BaseUnityPlugin
	{
		internal static ManualLogSource Logger;

		public static readonly string loadBearingColonThree = ":3";

		public static Plugin Instance { get; private set; }

		private void Awake()
		{
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			if (loadBearingColonThree != ":3")
			{
				Application.Quit();
			}
			((Object)((Component)this).gameObject).hideFlags = (HideFlags)61;
			Instance = this;
			Logger = ((BaseUnityPlugin)this).Logger;
			Assets.LoadBundle();
			((MonoBehaviour)this).StartCoroutine(BundleLocator.DiscoverAndLoadBundles());
			new Harmony("kestrel.straftat.fancy").PatchAll();
			Logger.LogInfo((object)"Hiiiiiiiiiiii :3");
		}
	}
	internal static class Extensions
	{
		private static readonly Regex splitCamelCaseA = new Regex("(\\p{Lu})(\\p{Lu})(\\p{Ll})", RegexOptions.Compiled);

		private static readonly Regex splitCamelCaseB = new Regex("(\\p{Ll})(\\p{Lu})", RegexOptions.Compiled);

		public static string SplitCamelCase(this string str)
		{
			return splitCamelCaseB.Replace(splitCamelCaseA.Replace(str, "$1 $2"), "$1 $2");
		}
	}
	public static class PluginInfo
	{
		public const string PLUGIN_GUID = "kestrel.straftat.fancy";

		public const string PLUGIN_NAME = "Fancy";

		public const string PLUGIN_VERSION = "1.0.0";
	}
}
namespace Fancy.Resources
{
	public static class Assets
	{
		private static AssetBundle m_bundle;

		internal static T Load<T>(string assetName) where T : Object
		{
			return m_bundle.LoadAsset<T>(assetName);
		}

		internal static void LoadBundle()
		{
			m_bundle = AssetBundle.LoadFromStream(Assembly.GetExecutingAssembly().GetManifestResourceStream("Fancy.Fancy.Resources.Bundles.fancy"));
		}
	}
}
namespace Fancy.Patches
{
	[HarmonyPatch(typeof(CosmeticsManager))]
	public static class CosmeticsManagerFixes
	{
		[HarmonyPatch("LoadDress")]
		[HarmonyPrefix]
		public static void ProperBoundsCheck(ref int ___suitIndex, ref int ___hatIndex, ref int ___cigIndex, GameObject[] ___suitsChildren, GameObject[] ___hatsChildren, GameObject[] ___cigsChildren)
		{
			___suitIndex = Mathf.Clamp(___suitIndex, 0, ___suitsChildren.Length - 1);
			___hatIndex = Mathf.Clamp(___hatIndex, 0, ___hatsChildren.Length - 1);
			___cigIndex = Mathf.Clamp(___cigIndex, 0, ___suitsChildren.Length - 1);
		}

		[HarmonyPatch("ChangeDress")]
		[HarmonyTranspiler]
		public static IEnumerable<CodeInstruction> PatchChangeDressIndexException(IEnumerable<CodeInstruction> instructions)
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Expected O, but got Unknown
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Expected O, but got Unknown
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			CodeMatch[] array = (CodeMatch[])(object)new CodeMatch[2]
			{
				new CodeMatch((OpCode?)OpCodes.Ldfld, (object)AccessTools.Field(typeof(CosmeticsManager), "suitsChildren"), (string)null),
				new CodeMatch((OpCode?)OpCodes.Ldlen, (object)null, (string)null)
			};
			return new CodeMatcher(instructions, (ILGenerator)null).End().MatchBack(false, array).Set(OpCodes.Ldfld, (object)AccessTools.Field(typeof(CosmeticsManager), "cigsChildren"))
				.MatchBack(false, array)
				.Set(OpCodes.Ldfld, (object)AccessTools.Field(typeof(CosmeticsManager), "cigsChildren"))
				.InstructionEnumeration();
		}
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}