Decompiled source of RandomAppearance v1.0.0

RandomAppearance.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Reflection;
using System.Runtime.CompilerServices;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;

[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: AssemblyTitle("RandomAppearance")]
[assembly: AssemblyProduct("RandomAppearance")]
[assembly: CompilationRelaxations(8)]
[assembly: AssemblyFileVersion("2.2.1.0")]
[assembly: AssemblyVersion("2.2.1.0")]
namespace RandomAppearance;

[BepInPlugin("marcelo.randomappearance", "RandomAppearance", "1.0.0")]
public class RandomAppearancePlugin : BaseUnityPlugin
{
	public const string PluginGuid = "marcelo.randomappearance";

	public const string PluginName = "RandomAppearance";

	public const string PluginVersion = "1.0.0";

	private const float Gap = 8f;

	internal static ManualLogSource Log;

	private static ConfigEntry<bool> _enabled;

	private static ConfigEntry<string> _label;

	private static ConfigEntry<float> _offsetX;

	private static ConfigEntry<float> _offsetY;

	private static ConfigEntry<float> _clickInterval;

	private static ConfigEntry<bool> _randomizeGender;

	private static readonly FieldInfo HairsField = AccessTools.Field(typeof(PlayerCustomizaton), "m_hairs");

	private static readonly FieldInfo BeardsField = AccessTools.Field(typeof(PlayerCustomizaton), "m_beards");

	private static readonly MethodInfo SetHairMethod = AccessTools.Method(typeof(PlayerCustomizaton), "SetHair", (Type[])null, (Type[])null);

	private static readonly MethodInfo SetBeardMethod = AccessTools.Method(typeof(PlayerCustomizaton), "SetBeard", (Type[])null, (Type[])null);

	private static readonly MethodInfo HairIndexMethod = AccessTools.Method(typeof(PlayerCustomizaton), "GetHairIndex", (Type[])null, (Type[])null);

	private static readonly MethodInfo BeardIndexMethod = AccessTools.Method(typeof(PlayerCustomizaton), "GetBeardIndex", (Type[])null, (Type[])null);

	private static readonly FieldInfo GamePadHintField = AccessTools.Field(typeof(UIGamePad), "m_hint");

	private static float _lastClick;

	private static PlayerCustomizaton _customization;

	private static GameObject _root;

	private static RectTransform _template;

	private static RectTransform _box;

	private void Awake()
	{
		//IL_007e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0088: Expected O, but got Unknown
		//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c6: Expected O, but got Unknown
		//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
		//IL_0104: Expected O, but got Unknown
		Log = ((BaseUnityPlugin)this).Logger;
		_enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Turns the button off without uninstalling.");
		_label = ((BaseUnityPlugin)this).Config.Bind<string>("Button", "Label", "", "Button text. Leave empty to use Aleatório for Brazilian Portuguese or Random for other game languages.");
		_offsetX = ((BaseUnityPlugin)this).Config.Bind<float>("Button", "OffsetX", 0f, new ConfigDescription("Nudge right from the spot under the appearance box, in the game's own UI units.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-1000f, 1000f), new object[0]));
		_offsetY = ((BaseUnityPlugin)this).Config.Bind<float>("Button", "OffsetY", 0f, new ConfigDescription("Nudge up, same units.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-1000f, 1000f), new object[0]));
		_clickInterval = ((BaseUnityPlugin)this).Config.Bind<float>("Randomizing", "MinSecondsBetweenClicks", 0.3f, new ConfigDescription("Shortest gap between two rolls. Humanoid.SetHair destroys and reinstantiates the model attachment, so every roll is a model rebuild, and rebuilds in quick succession make the character visibly flash. Measured on a 4K setup: at 0.2 a held click produced five rebuilds a second, which still flickers. Raise it if that bothers you, lower it if the button feels sluggish. 0 removes the limit.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 2f), new object[0]));
		_randomizeGender = ((BaseUnityPlugin)this).Config.Bind<bool>("Randomizing", "RandomizeGender", false, "Also flips between male and female. Off by default: sex is usually the one choice someone has already made before reaching for a random look.");
		_offsetX.SettingChanged += delegate
		{
			Place();
		};
		_offsetY.SettingChanged += delegate
		{
			Place();
		};
		_label.SettingChanged += delegate
		{
			ApplyLabel();
		};
		Localization.OnLanguageChange = (Action)Delegate.Combine(Localization.OnLanguageChange, new Action(ApplyLabel));
		if (HairsField == null || BeardsField == null || SetHairMethod == null || SetBeardMethod == null)
		{
			Log.LogError((object)("PlayerCustomizaton members not found (" + ((HairsField == null) ? "m_hairs " : "") + ((BeardsField == null) ? "m_beards " : "") + ((SetHairMethod == null) ? "SetHair " : "") + ((SetBeardMethod == null) ? "SetBeard " : "") + "). Hair and beard will not randomize."));
		}
		Log.LogInfo((object)"RandomAppearance 1.0.0 loaded.");
	}

	private void OnDestroy()
	{
		Localization.OnLanguageChange = (Action)Delegate.Remove(Localization.OnLanguageChange, new Action(ApplyLabel));
	}

	private void Update()
	{
		FejdStartup instance = FejdStartup.instance;
		if ((Object)(object)instance == (Object)null)
		{
			Teardown();
			return;
		}
		GameObject newCharacterPanel = instance.m_newCharacterPanel;
		if ((Object)(object)newCharacterPanel == (Object)null || !newCharacterPanel.activeInHierarchy)
		{
			return;
		}
		if (!_enabled.Value)
		{
			if ((Object)(object)_root != (Object)null && _root.activeSelf)
			{
				_root.SetActive(false);
			}
			return;
		}
		if ((Object)(object)_root == (Object)null)
		{
			CreateButton(instance, newCharacterPanel);
		}
		if ((Object)(object)_root != (Object)null && !_root.activeSelf)
		{
			_root.SetActive(true);
		}
	}

	private static PlayerCustomizaton Customization()
	{
		if ((Object)(object)_customization != (Object)null)
		{
			return _customization;
		}
		FejdStartup instance = FejdStartup.instance;
		if ((Object)(object)instance != (Object)null && (Object)(object)instance.m_newCharacterPanel != (Object)null)
		{
			_customization = instance.m_newCharacterPanel.GetComponentInChildren<PlayerCustomizaton>(true);
		}
		if ((Object)(object)_customization == (Object)null)
		{
			_customization = Object.FindAnyObjectByType<PlayerCustomizaton>((FindObjectsInactive)1);
		}
		return _customization;
	}

	private static void CreateButton(FejdStartup fs, GameObject panel)
	{
		//IL_0042: Unknown result type (might be due to invalid IL or missing references)
		//IL_004c: Expected O, but got Unknown
		//IL_010b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0115: Expected O, but got Unknown
		//IL_0123: Unknown result type (might be due to invalid IL or missing references)
		//IL_012d: Expected O, but got Unknown
		Button val = fs.m_csNewCharacterDone;
		if ((Object)(object)val == (Object)null)
		{
			val = panel.GetComponentInChildren<Button>(true);
		}
		if ((Object)(object)val == (Object)null)
		{
			Log.LogError((object)"No native button found on the character panel; Random button not created.");
			_enabled.Value = false;
			return;
		}
		_template = (RectTransform)((Component)val).transform;
		_box = FindAppearanceBox(panel.transform);
		Transform val2 = (((Object)(object)_box != (Object)null) ? ((Transform)_box).parent : ((Transform)_template).parent);
		_root = Object.Instantiate<GameObject>(((Component)val).gameObject, val2, false);
		((Object)_root).name = "RandomAppearanceButton";
		UIGamePad[] componentsInChildren = _root.GetComponentsInChildren<UIGamePad>(true);
		foreach (UIGamePad val3 in componentsInChildren)
		{
			GameObject val4 = (GameObject)((GamePadHintField != null) ? /*isinst with value type is only supported in some contexts*/: null);
			if ((Object)(object)val4 != (Object)null)
			{
				Object.Destroy((Object)(object)val4);
			}
			Object.Destroy((Object)(object)val3);
		}
		Button component = _root.GetComponent<Button>();
		component.onClick = new ButtonClickedEvent();
		((UnityEvent)component.onClick).AddListener(new UnityAction(Randomize));
		((Selectable)component).interactable = true;
		LayoutElement val5 = _root.GetComponent<LayoutElement>();
		if ((Object)(object)val5 == (Object)null)
		{
			val5 = _root.AddComponent<LayoutElement>();
		}
		val5.ignoreLayout = true;
		ApplyLabel();
		Place();
		Describe(val);
	}

	private static RectTransform FindAppearanceBox(Transform stop)
	{
		PlayerCustomizaton val = Customization();
		if ((Object)(object)val == (Object)null || (Object)(object)val.m_selectedHair == (Object)null || (Object)(object)val.m_hairTone == (Object)null)
		{
			return null;
		}
		Transform val2 = null;
		Transform val3 = val.m_selectedHair.transform;
		while ((Object)(object)val3 != (Object)null)
		{
			if (((Component)val.m_hairTone).transform.IsChildOf(val3))
			{
				val2 = val3;
				break;
			}
			val3 = val3.parent;
		}
		if ((Object)(object)val2 == (Object)null)
		{
			return null;
		}
		Canvas.ForceUpdateCanvases();
		Transform val4 = val2;
		while ((Object)(object)val4 != (Object)null && (Object)(object)val4 != (Object)(object)stop)
		{
			RectTransform val5 = (RectTransform)(object)((val4 is RectTransform) ? val4 : null);
			if (!((Object)(object)val5 == (Object)null) && !((Object)(object)((Component)val4).GetComponent<Image>() == (Object)null))
			{
				if (ScreenWidth(val5) >= (float)Screen.width * 0.5f)
				{
					break;
				}
				return val5;
			}
			val4 = val4.parent;
		}
		return (RectTransform)(object)((val2 is RectTransform) ? val2 : null);
	}

	private static float ScreenWidth(RectTransform rt)
	{
		//IL_001f: 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_004f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0054: Unknown result type (might be due to invalid IL or missing references)
		Vector3[] array = (Vector3[])(object)new Vector3[4];
		rt.GetWorldCorners(array);
		Canvas componentInParent = ((Component)rt).GetComponentInParent<Canvas>();
		Camera val = (((Object)(object)componentInParent != (Object)null && (int)componentInParent.renderMode != 0) ? componentInParent.worldCamera : null);
		return RectTransformUtility.WorldToScreenPoint(val, array[3]).x - RectTransformUtility.WorldToScreenPoint(val, array[0]).x;
	}

	private static void Place()
	{
		//IL_0025: Unknown result type (might be due to invalid IL or missing references)
		//IL_002b: Expected O, but got Unknown
		//IL_0030: 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_00a9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bd: 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_00d4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
		//IL_0119: Unknown result type (might be due to invalid IL or missing references)
		//IL_0169: 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_0073: Unknown result type (might be due to invalid IL or missing references)
		//IL_0078: Unknown result type (might be due to invalid IL or missing references)
		if (!((Object)(object)_root == (Object)null) && !((Object)(object)_template == (Object)null))
		{
			RectTransform val = (RectTransform)_root.transform;
			Rect rect = _template.rect;
			float height = ((Rect)(ref rect)).height;
			if ((Object)(object)_box == (Object)null)
			{
				val.anchoredPosition = _template.anchoredPosition + new Vector2(_offsetX.Value, height + 8f + _offsetY.Value);
				return;
			}
			Canvas.ForceUpdateCanvases();
			Vector3[] array = (Vector3[])(object)new Vector3[4];
			_box.GetWorldCorners(array);
			Transform parent = ((Transform)val).parent;
			Vector3 val2 = parent.InverseTransformPoint(array[0]);
			Vector3 val3 = parent.InverseTransformPoint(array[3]);
			val.anchorMin = new Vector2(0.5f, 0.5f);
			val.anchorMax = new Vector2(0.5f, 0.5f);
			val.pivot = new Vector2(0.5f, 0.5f);
			val.sizeDelta = new Vector2(val3.x - val2.x, height);
			((Transform)val).localPosition = new Vector3((val2.x + val3.x) * 0.5f + _offsetX.Value, val2.y - 8f - height * 0.5f + _offsetY.Value, 0f);
		}
	}

	private static void ApplyLabel()
	{
		if (!((Object)(object)_root == (Object)null))
		{
			string text = _label.Value;
			if (string.IsNullOrEmpty(text))
			{
				string a = ((Localization.instance != null) ? Localization.instance.GetSelectedLanguage() : null);
				text = (string.Equals(a, "Portuguese_Brazilian", StringComparison.Ordinal) ? "Aleatório" : "Random");
			}
			TMP_Text componentInChildren = _root.GetComponentInChildren<TMP_Text>(true);
			if ((Object)(object)componentInChildren != (Object)null)
			{
				componentInChildren.text = text;
			}
		}
	}

	private static void Describe(Button template)
	{
		//IL_000a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0010: Expected O, but got Unknown
		//IL_004d: 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_0056: 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_0092: Expected O, but got Unknown
		//IL_0110: Unknown result type (might be due to invalid IL or missing references)
		//IL_0115: Unknown result type (might be due to invalid IL or missing references)
		//IL_012d: Unknown result type (might be due to invalid IL or missing references)
		RectTransform val = (RectTransform)_root.transform;
		object obj;
		if (!((Object)(object)_box == (Object)null))
		{
			object[] array = new object[4]
			{
				"'",
				Path((Transform)(object)_box),
				"' size ",
				null
			};
			Rect rect = _box.rect;
			array[3] = ((Rect)(ref rect)).size;
			obj = string.Concat(array);
		}
		else
		{
			obj = "NOT FOUND (fallback above Done)";
		}
		string text = (string)obj;
		string text2 = "";
		foreach (Transform item in ((Transform)val).parent)
		{
			Transform val2 = item;
			text2 = text2 + ((text2.Length > 0) ? ", " : "") + ((Object)val2).name;
		}
		Log.LogInfo((object)string.Concat("button cloned from '", ((Object)template).name, "', appearance box ", text, ", placed at ", Vector2.op_Implicit(((Transform)val).localPosition), " size ", val.sizeDelta, ", siblings [", text2, "]"));
	}

	private static string Path(Transform t)
	{
		string text = ((Object)t).name;
		Transform parent = t.parent;
		while ((Object)(object)parent != (Object)null)
		{
			text = ((Object)parent).name + " > " + text;
			parent = parent.parent;
		}
		return text;
	}

	private static void Teardown()
	{
		if ((Object)(object)_root != (Object)null)
		{
			Object.Destroy((Object)(object)_root);
		}
		_root = null;
		_template = null;
		_box = null;
		_customization = null;
	}

	private static void Randomize()
	{
		if (Time.unscaledTime - _lastClick < _clickInterval.Value)
		{
			return;
		}
		_lastClick = Time.unscaledTime;
		PlayerCustomizaton val = Customization();
		if ((Object)(object)val == (Object)null)
		{
			Log.LogWarning((object)"Random clicked, but the customization screen was not found.");
			return;
		}
		if (_randomizeGender.Value)
		{
			val.SetPlayerModel(Random.Range(0, 2));
		}
		SetIndexed(val, HairsField, SetHairMethod, HairIndexMethod);
		SetIndexed(val, BeardsField, SetBeardMethod, BeardIndexMethod);
		Shuffle(val.m_skinHue, (Action<float>)val.OnSkinHueChange);
		Shuffle(val.m_hairLevel, (Action<float>)val.OnHairHueChange);
		Shuffle(val.m_hairTone, (Action<float>)val.OnHairHueChange);
	}

	private static void SetIndexed(PlayerCustomizaton pc, FieldInfo field, MethodInfo setter, MethodInfo getter)
	{
		if (field == null || setter == null || !(field.GetValue(pc) is ICollection { Count: not 0, Count: var count }))
		{
			return;
		}
		int num = Random.Range(0, count);
		if (getter != null && count > 1)
		{
			object obj = getter.Invoke(pc, null);
			if (obj is int && (int)obj == num)
			{
				num = (num + Random.Range(1, count)) % count;
			}
		}
		setter.Invoke(pc, new object[1] { num });
	}

	private static void Shuffle(Slider slider, Action<float> handler)
	{
		if (!((Object)(object)slider == (Object)null))
		{
			float value = slider.value;
			float obj = (slider.value = Random.Range(slider.minValue, slider.maxValue));
			if (handler != null && Mathf.Approximately(value, slider.value))
			{
				handler(obj);
			}
		}
	}
}