using System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using BepInEx.Unity.IL2CPP; using Fusion; using Il2CppInterop.Runtime; using Il2CppInterop.Runtime.Injection; using Il2CppInterop.Runtime.InteropTypes; using Il2CppInterop.Runtime.InteropTypes.Arrays; using Il2CppSystem.Collections.Generic; using Microsoft.CodeAnalysis; using Steamworks; using TMPro; using UnityEngine; using UnityEngine.Events; using UnityEngine.Rendering.Universal; using UnityEngine.SceneManagement; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyCompany("Disguise")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("Disguise")] [assembly: AssemblyTitle("Disguise")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [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 ShiftAtMidnightDisguise { internal sealed class BodyTarget { private const string BonePrefix = "mixamorig:"; public Transform Root; public List Parts; public bool IsPlayer; public bool Valid => (Object)(object)Root != (Object)null && Parts != null && Parts.Count > 0; public Animator Source => ((Object)(object)Root != (Object)null) ? ((Component)Root).GetComponent() : null; public static BodyTarget OfPlayer(ThirdPersonManager body) { if ((Object)(object)body == (Object)null || (Object)(object)body.bodyAnim == (Object)null) { return null; } Transform transform = ((Component)body.bodyAnim).transform; List list = new List(); foreach (SkinnedMeshRenderer item in Interop.Safe((Il2CppArrayBase)(object)body.models)) { Transform parent = ((Component)item).transform.parent; if ((Object)(object)parent != (Object)null && ((Il2CppObjectBase)parent).Pointer == ((Il2CppObjectBase)transform).Pointer) { list.Add(item); } } return new BodyTarget { Root = transform, Parts = list, IsPlayer = true }; } public static BodyTarget OfMannequin(GameObject root) { if ((Object)(object)root == (Object)null) { return null; } SkinnedMeshRenderer val = null; int num = int.MaxValue; foreach (SkinnedMeshRenderer item in Interop.Safe(root.GetComponentsInChildren(true))) { if (item.bones != null && !((Object)(object)item.rootBone == (Object)null) && ((Object)item.rootBone).name.StartsWith("mixamorig:", StringComparison.Ordinal)) { int num2 = Depth(((Component)item).transform); if (num2 <= num && (num2 != num || !((Object)(object)val != (Object)null) || ((Il2CppArrayBase)(object)item.bones).Length > ((Il2CppArrayBase)(object)val.bones).Length)) { val = item; num = num2; } } } if ((Object)(object)val == (Object)null || (Object)(object)((Component)val).transform.parent == (Object)null) { return null; } Transform parent = ((Component)val).transform.parent; List list = new List(); foreach (SkinnedMeshRenderer item2 in Interop.Safe(((Component)parent).GetComponentsInChildren(true))) { if ((Object)(object)((Component)item2).transform.parent != (Object)null && ((Il2CppObjectBase)((Component)item2).transform.parent).Pointer == ((Il2CppObjectBase)parent).Pointer) { list.Add(item2); } } return new BodyTarget { Root = parent, Parts = list }; } private static int Depth(Transform t) { int num = 0; while ((Object)(object)t.parent != (Object)null) { num++; t = t.parent; } return num; } } [BepInPlugin("clementinise.shiftatmidnight.disguise", "Disguise", "1.0.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] public class DisguisePlugin : BasePlugin { public const string Guid = "clementinise.shiftatmidnight.disguise"; private const string NuisanceGuid = "clementinise.shiftatmidnight.nuisance"; internal static ConfigEntry Model; public override void Load() { ModLog.Source = ((BasePlugin)this).Log; Model = ((BasePlugin)this).Config.Bind("Appearance", "Model", "", "Which NPC you appear as. Empty is your own body."); ClassInjector.RegisterTypeInIl2Cpp(); ((BasePlugin)this).AddComponent(); ModLog.Msg("Disguise ready."); } } public class DisguiseBehaviour : MonoBehaviour { private SkinSync _sync; private LockerPreview _locker; private LockerPicker _picker; private int _lastSceneHandle; public DisguiseBehaviour(IntPtr handle) : base(handle) { } private void Awake() { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) _sync = new SkinSync(); _locker = new LockerPreview(); _picker = new LockerPicker(_locker); Scene activeScene = SceneManager.GetActiveScene(); _lastSceneHandle = ((Scene)(ref activeScene)).handle; } private void Update() { try { DetectSceneChange(); _sync.LocalChoice = DisguisePlugin.Model.Value; _sync.Tick(); _picker.Tick(); _locker.Tick(_picker.Previewing ?? DisguisePlugin.Model.Value); } catch (Exception value) { ModLog.Error($"Update: {value}"); } } private void LateUpdate() { Pose(); try { _picker.TickBaking(); } catch (Exception value) { ModLog.Error($"LateUpdate: {value}"); } } private void Pose() { try { _sync.LateTick(); _locker.LateTick(); } catch (Exception value) { ModLog.Error($"Pose: {value}"); } } private void OnDestroy() { try { _sync.Clear(); } catch { } try { _locker.Clear(); } catch { } } private void DetectSceneChange() { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) Scene activeScene = SceneManager.GetActiveScene(); int handle = ((Scene)(ref activeScene)).handle; if (handle != _lastSceneHandle) { _lastSceneHandle = handle; _sync.Clear(); _locker.Clear(); _picker.Forget(); NpcCatalog.Invalidate(); } } } internal static class Interop { public static IEnumerable Safe(Il2CppArrayBase array) where T : Il2CppObjectBase { if (array == null) { yield break; } for (int i = 0; i < array.Length; i++) { T v = array[i]; if (v != null) { yield return v; } } } public static IEnumerable Safe(List list) { if (list == null) { yield break; } for (int i = 0; i < list.Count; i++) { T v = list[i]; if (v != null) { yield return v; } } } } internal static class LocalModel { private const int MaxComponents = 4000; public static GameObject Create(GameObject prefab, Transform parent) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Expected O, but got Unknown //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)prefab == (Object)null) { return null; } GameObject val = null; try { val = new GameObject("DisguiseHolder"); val.SetActive(false); Object.DontDestroyOnLoad((Object)(object)val); GameObject val2 = Object.Instantiate(prefab, val.transform); int length = val2.GetComponentsInChildren(true).Length; if (length > 4000) { ModLog.Warning($"'{((Object)prefab).name}' has {length} components, too large to strip " + "safely, skipping to avoid freezing the game."); return null; } Strip(val2); Activate(val2); val2.transform.SetParent(parent, false); val2.transform.localPosition = Vector3.zero; val2.transform.localRotation = Quaternion.identity; ((Object)val2).name = "Disguise_" + ((Object)prefab).name; val2.SetActive(true); return val2; } catch (Exception value) { ModLog.Error($"Failed to build the model for '{((Object)prefab).name}': {value}"); return null; } finally { if ((Object)(object)val != (Object)null) { Object.Destroy((Object)(object)val); } } } private static void Activate(GameObject root) { root.SetActive(true); foreach (SkinnedMeshRenderer componentsInChild in root.GetComponentsInChildren(true)) { if ((Object)(object)componentsInChild == (Object)null) { continue; } ((Renderer)componentsInChild).enabled = true; Transform val = ((Component)componentsInChild).transform; while ((Object)(object)val != (Object)null && (Object)(object)val != (Object)(object)root.transform) { if (!((Component)val).gameObject.activeSelf) { ((Component)val).gameObject.SetActive(true); } val = val.parent; } } } private static void Strip(GameObject root) { foreach (Component componentsInChild in root.GetComponentsInChildren(true)) { if (!((Object)(object)componentsInChild == (Object)null) && !((Object)(object)((Il2CppObjectBase)componentsInChild).TryCast() != (Object)null) && !((Object)(object)((Il2CppObjectBase)componentsInChild).TryCast() != (Object)null)) { try { Object.DestroyImmediate((Object)(object)componentsInChild, true); } catch { } } } foreach (Collider componentsInChild2 in root.GetComponentsInChildren(true)) { if ((Object)(object)componentsInChild2 != (Object)null) { try { Object.DestroyImmediate((Object)(object)componentsInChild2, true); } catch { } } } } public static void SetLayer(GameObject root, int layer) { foreach (Transform item in Interop.Safe(root.GetComponentsInChildren(true))) { ((Component)item).gameObject.layer = layer; } } } internal sealed class LockerPicker { private const string ContentPath = "Scroll View/Viewport/Content"; private const string SlotTemplate = "Scroll View/Viewport/Content/Slot"; private const string SlotButton = "HatSlotButton"; private const string SlotIcon = "Item Icon"; private const string SlotLocked = "LOCKED"; private const string LabelName = "Disguise Label"; private const string ExitButton = "ExitButton"; private const string EquipButton = "Equip Button"; private const string EquippedLabel = "Equipped"; private const string TitleLabel = "TITLE"; private const string NameLabel = "Name"; private const string GlyphPath = "HAT SLOT CONTROLLER BUTTON PROMPT/Make This Disable In KB&M/GlyphText"; private const float BackButtonGap = 4f; private const string BackGlyph = "<"; private const string ChangeText = "CHANGE"; private const string WearingText = "CURRENT"; private const string DefaultLabel = "DEFAULT"; private const string HatBadgeSprite = "Customizables Icon"; private const float HatBadgeGap = 6f; private static readonly Color BackTint = new Color(0.52f, 0.58f, 0.66f); private const string PreviewImage = "RawImage"; private readonly ThumbnailBaker _thumbnails; private readonly Dictionary _icons = new Dictionary(StringComparer.Ordinal); private readonly Dictionary _labels = new Dictionary(StringComparer.Ordinal); private GameObject _builtFor; private GameObject _locker; private GameObject _chooser; private GameObject _models; private TextMeshProUGUI _modelsName; private string _previewing; private string _previewingLabel; private GameObject _changeButton; private GameObject _appliedLabel; private GameObject _hatsRow; private string _hatsRowFor; private GameObject _hatBadge; private bool _inHats; public string Previewing => ((Object)(object)_models != (Object)null && _models.activeSelf) ? _previewing : null; public LockerPicker(LockerPreview preview) { _thumbnails = new ThumbnailBaker(preview); _thumbnails.OnBaked = SetIcon; } public void TickBaking() { _thumbnails.Tick(); } public void Tick() { try { PlayerManager localPlayerManager = NetworkContext.LocalPlayerManager; GameObject val = (((Object)(object)localPlayerManager != (Object)null) ? localPlayerManager.customizablesMenu : null); if (!((Object)(object)val == (Object)null)) { if ((Object)(object)_chooser == (Object)null || (Object)(object)_builtFor == (Object)null || ((Il2CppObjectBase)val).Pointer != ((Il2CppObjectBase)_builtFor).Pointer) { Build(val); } Intercept(localPlayerManager, val); } } catch (Exception value) { ModLog.Error($"Locker picker: {value}"); _builtFor = null; } } public void Forget() { _builtFor = null; _locker = null; _chooser = null; _models = null; _modelsName = null; _previewing = null; _previewingLabel = null; _changeButton = null; _appliedLabel = null; _hatsRow = null; _hatsRowFor = null; _hatBadge = null; _inHats = false; _icons.Clear(); _labels.Clear(); _thumbnails.Forget(); } private void Intercept(PlayerManager pm, GameObject menu) { if (!pm.customizablesMenuOpen) { if (_chooser.activeSelf) { _chooser.SetActive(false); } if (_models.activeSelf) { _models.SetActive(false); } _inHats = false; return; } if (!_inHats && !_thumbnails.Busy) { LockerPreview.NoteEquippedHat(); } RefreshHatsRow(); if (menu.activeSelf && !_inHats) { menu.SetActive(false); _chooser.SetActive(true); _thumbnails.Begin(LiveTexture(menu), NpcCatalog.Offered); } _thumbnails.Gentle = !_chooser.activeSelf; if (_thumbnails.Done && Previewing == null && NpcTuning.IsFitted(DisguisePlugin.Model.Value ?? string.Empty)) { _thumbnails.BeginHats(LiveTexture(menu), menu.GetComponent(), DisguisePlugin.Model.Value ?? string.Empty); } } private static RenderTexture LiveTexture(GameObject menu) { Transform val = menu.transform.Find("RawImage"); RawImage val2 = (((Object)(object)val != (Object)null) ? ((Component)val).GetComponent() : null); if ((Object)(object)val2 == (Object)null || (Object)(object)val2.texture == (Object)null) { return null; } return ((Il2CppObjectBase)val2.texture).TryCast(); } private void SetIcon(string name, Sprite sprite) { if (_icons.TryGetValue(name, out var value) && !((Object)(object)value == (Object)null) && !((Object)(object)sprite == (Object)null)) { value.sprite = sprite; ((Component)value).gameObject.SetActive(true); if (_labels.TryGetValue(name, out var value2) && (Object)(object)value2 != (Object)null) { ((Component)value2).gameObject.SetActive(false); } } } private void Build(GameObject menu) { Transform transform = menu.transform; Transform val = Require(transform, "Scroll View/Viewport/Content/Slot"); _locker = menu; _chooser = BuildChooser(menu, val); _models = BuildModels(menu, val); AddBackButton(menu.transform, delegate { LockerPreview.RestoreEquippedHat(); Show(_chooser, menu); }); SetText(transform, "TITLE", "HATS"); CustomizablesLocker component = menu.GetComponent(); if ((Object)(object)component != (Object)null && (Object)(object)component.equipButton != (Object)null) { ((UnityEvent)component.equipButton.onClick).AddListener(DelegateSupport.ConvertDelegate((Delegate)new Action(LockerPreview.NoteEquippedHat))); } _builtFor = menu; } private GameObject BuildChooser(GameObject menu, Transform rowTemplate) { GameObject val = Clone(menu, "Disguise Chooser"); Transform content = Require(val.transform, "Scroll View/Viewport/Content"); AddRow(content, rowTemplate, "MODELS", delegate { string name = DisguisePlugin.Model.Value ?? string.Empty; Preview(name, LabelFor(name)); Show(_models, _chooser); }); _hatsRow = AddRow(content, rowTemplate, "HATS", delegate { if (NpcTuning.IsFitted(DisguisePlugin.Model.Value ?? string.Empty)) { _inHats = true; Show(_locker, _chooser); } }); SetText(val.transform, "TITLE", "CUSTOMIZE"); SetText(val.transform, "Name", string.Empty); return val; } private GameObject BuildModels(GameObject menu, Transform slot) { GameObject panel = Clone(menu, "Disguise Models"); Transform content = Require(panel.transform, "Scroll View/Viewport/Content"); AddRow(content, slot, "DEFAULT", delegate { Preview(string.Empty, "DEFAULT"); }); Stopwatch stopwatch = Stopwatch.StartNew(); List offered = NpcCatalog.Offered; foreach (NpcEntry item in offered) { string name = item.Name; string label = item.Label; GameObject val = AddRow(content, slot, label, delegate { Preview(name, label); }); Transform val2 = val.transform.Find("Item Icon"); if ((Object)(object)val2 != (Object)null) { _icons[name] = ((Component)val2).GetComponent(); } Transform val3 = val.transform.Find("Disguise Label"); if ((Object)(object)val3 != (Object)null) { _labels[name] = val3; } SetIcon(name, _thumbnails.Get(name)); } stopwatch.Stop(); SetText(panel.transform, "TITLE", "MODELS"); _modelsName = Find(panel.transform, "Name"); Transform val4 = panel.transform.Find("Equip Button"); _changeButton = (((Object)(object)val4 != (Object)null) ? ((Component)val4).gameObject : null); Transform val5 = panel.transform.Find("Equipped"); _appliedLabel = (((Object)(object)val5 != (Object)null) ? ((Component)val5).gameObject : null); Unanimate(val4); Unanimate(val5); Relabel(val4, "CHANGE"); Relabel(val5, "CURRENT"); Wire(val4, Commit); _hatBadge = AddHatBadge(panel.transform, val4, slot); AddBackButton(panel.transform, delegate { _previewing = null; _previewingLabel = null; Show(_chooser, panel); }); return panel; } private GameObject Clone(GameObject menu, string name) { GameObject val = Object.Instantiate(menu, menu.transform.parent); ((Object)val).name = name; val.SetActive(false); CustomizablesLocker component = val.GetComponent(); if ((Object)(object)component != (Object)null) { Object.Destroy((Object)(object)component); } Transform val2 = Require(val.transform, "Scroll View/Viewport/Content"); for (int num = val2.childCount - 1; num >= 0; num--) { Object.DestroyImmediate((Object)(object)((Component)val2.GetChild(num)).gameObject); } Hide(val.transform.Find("Equip Button")); Hide(val.transform.Find("Equipped")); return val; } private GameObject AddRow(Transform content, Transform template, string label, Action onClick) { GameObject val = Object.Instantiate(((Component)template).gameObject, content); ((Object)val).name = "Disguise " + label; val.SetActive(true); Animator component = val.GetComponent(); if ((Object)(object)component != (Object)null) { Object.Destroy((Object)(object)component); } Hide(val.transform.Find("Item Icon")); Hide(val.transform.Find("LOCKED")); Label(val, label); Wire(val.transform.Find("HatSlotButton"), onClick); return val; } private static void Label(GameObject row, string text) { //IL_00cb: 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) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) Transform val = row.transform.Find("HAT SLOT CONTROLLER BUTTON PROMPT/Make This Disable In KB&M/GlyphText"); if ((Object)(object)val == (Object)null) { ModLog.Warning("[picker] no glyph to label '" + text + "' with."); return; } GameObject val2 = Object.Instantiate(((Component)val).gameObject, row.transform); ((Object)val2).name = "Disguise Label"; val2.SetActive(true); ChangeTextToKeybind component = val2.GetComponent(); if ((Object)(object)component != (Object)null) { Object.Destroy((Object)(object)component); } TextMeshProUGUI component2 = val2.GetComponent(); if (!((Object)(object)component2 == (Object)null)) { ((TMP_Text)component2).text = text; ((TMP_Text)component2).enableAutoSizing = true; ((TMP_Text)component2).alignment = (TextAlignmentOptions)514; RectTransform component3 = val2.GetComponent(); if (!((Object)(object)component3 == (Object)null)) { component3.anchorMin = Vector2.zero; component3.anchorMax = Vector2.one; component3.offsetMin = Vector2.zero; component3.offsetMax = Vector2.zero; ((Transform)component3).localScale = Vector3.one; val2.transform.SetAsLastSibling(); } } } private void AddBackButton(Transform panel, Action onClick) { //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Unknown result type (might be due to invalid IL or missing references) Transform val = panel.Find("ExitButton"); if ((Object)(object)val == (Object)null) { ModLog.Warning("[picker] no ExitButton to clone a back button from."); return; } GameObject val2 = Object.Instantiate(((Component)val).gameObject, panel); ((Object)val2).name = "Disguise Back"; val2.SetActive(true); TextMeshProUGUI componentInChildren = val2.GetComponentInChildren(true); if ((Object)(object)componentInChildren != (Object)null) { LanguageText component = ((Component)componentInChildren).GetComponent(); if ((Object)(object)component != (Object)null) { Object.Destroy((Object)(object)component); } ((TMP_Text)componentInChildren).enableWordWrapping = false; ((TMP_Text)componentInChildren).text = "<"; } foreach (Image item in Interop.Safe(val2.GetComponentsInChildren(true))) { ((Graphic)item).color = BackTint; } RectTransform component2 = val2.GetComponent(); RectTransform component3 = ((Component)val).GetComponent(); if ((Object)(object)component2 != (Object)null && (Object)(object)component3 != (Object)null) { Vector2 anchoredPosition = component3.anchoredPosition; Rect rect = component3.rect; component2.anchoredPosition = anchoredPosition + new Vector2(0f - (((Rect)(ref rect)).width + 4f), 0f); } Wire(val2.transform, onClick); } private static void Wire(Transform holder, Action onClick) { //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Expected O, but got Unknown if ((Object)(object)holder == (Object)null) { return; } Button val = ((Component)holder).GetComponent