Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of Disguise v0.1.0
plugins/Disguise.dll
Decompiled 6 days ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security.Permissions; using System.Threading; using BepInEx; using BepInEx.Configuration; using HarmonyLib; using Jotunn.Managers; using Microsoft.CodeAnalysis; using TMPro; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.Events; using UnityEngine.SceneManagement; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("Disguise")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("Disguise")] [assembly: AssemblyCopyright("Copyright © 2021")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("e3243d22-4307-4008-ba36-9f326008cde5")] [assembly: AssemblyFileVersion("0.1.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.1.0.0")] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } } namespace Disguise { internal static class AllyView { private static ZDO? s_activeZdo; private static int s_savedValue; internal static int BuildRelation() { if (!DisguiseConfig.ConfigEnabled.Value || PrefabResolver.DesiredPrefabHash() == PrefabResolver.PlayerPrefabHash) { return 0; } return DisguiseConfig.ConfigShowAsAlly.Value ? 1 : 0; } internal static void Publish(ZDO? zdo) { if (zdo != null && zdo.IsValid()) { int num = BuildRelation(); if (zdo.GetInt("cd_rel", -1) != num) { zdo.Set("cd_rel", num); } } } internal static void OnSettingChanged() { Publish(GameAccess.GetLocalPlayerZDO()); } private static int Wanted(ZDO zdo) { int num = zdo.GetInt("cd_rel", -1); if (num >= 0) { return (num != 0) ? 1 : 0; } return DisguiseConfig.ConfigShowAsAlly.Value ? 1 : 0; } internal static bool BeginSerialize(ZDO? zdo) { if (s_activeZdo != null || zdo == null || !zdo.IsValid()) { return false; } if (!DisguiseApplier.HasMarker(zdo)) { return false; } s_activeZdo = zdo; try { s_savedValue = zdo.GetInt(ZDOVars.s_tamed, 0); GameAccess.SetZdoIntSilent(zdo, ZDOVars.s_tamed, Wanted(zdo)); } catch (Exception arg) { End(); Disguise.LogError($"AllyView serialize: {arg}"); return false; } return true; } internal static void End() { ZDO val = s_activeZdo; int value = s_savedValue; s_activeZdo = null; s_savedValue = 0; if (val == null) { return; } try { GameAccess.SetZdoIntSilent(val, ZDOVars.s_tamed, value); } catch (Exception arg) { Disguise.LogError($"AllyView restore: {arg}"); } } internal static void ClearStored(ZDO zdo) { if (zdo.GetInt(ZDOVars.s_tamed, 0) != 0) { GameAccess.SetZdoIntSilent(zdo, ZDOVars.s_tamed, 0); } } } internal static class CostumeGear { private static readonly int CdGearKey = Key("cd_costumeGear"); private static readonly int CdWeaponKey = Key("cd_whoseWeapon"); private const int GearArmor = 1; private const int GearPlayerWeapons = 2; private const int GearWeaponsOff = 4; private static readonly int[] s_rows = new int[8] { 0, 1, 2, 3, 4, 5, 8, 9 }; private static ZDO? s_activeZdo; private static int s_savedCount; private static readonly int[] s_savedKeys = new int[s_rows.Length * 2 + 1]; private static readonly int[] s_savedValues = new int[s_rows.Length * 2 + 1]; private static int s_cachedPrefab; private static int s_cachedSeed; private static int s_cachedForced; private static GearSkin.Gear? s_cachedGear; private static int Key(string name) { return StringExtensionMethods.GetStableHashCode(name); } internal static void Publish(ZDO? zdo) { if (zdo != null && zdo.IsValid()) { int num = Choice(); if (zdo.GetInt(CdGearKey, -1) != num) { zdo.Set(CdGearKey, num, false); } int num2 = GearSkin.LocalWeapon(); if (zdo.GetInt(CdWeaponKey, -1) != num2) { zdo.Set(CdWeaponKey, num2, false); } } } private static int Choice() { int num = 0; if (DisguiseConfig.ConfigShowArmor.Value) { num |= 1; } if (!DisguiseConfig.ConfigShowWeapons.Value) { num |= 4; } else if (DisguiseConfig.PlayerWeapons()) { num |= 2; } return num; } private static int Published(ZDO zdo) { int num = zdo.GetInt(CdGearKey, -1); if (num >= 0) { return num; } return Choice(); } internal static void OnSettingChanged() { Publish(GameAccess.GetLocalPlayerZDO()); } internal static bool BeginOutgoing(ZDO? zdo) { if (s_activeZdo != null || zdo == null || !zdo.IsValid()) { return false; } if (!DisguiseApplier.HasMarker(zdo)) { return false; } GearSkin.Gear gear = Roll(zdo); if (gear == null) { return false; } int num = Published(zdo); bool flag = (num & 1) != 0; bool flag2 = (num & 2) != 0; bool flag3 = (num & 4) != 0; s_activeZdo = zdo; bool flag4 = false; int num2 = zdo.GetInt(EquipmentMask.PublishKey, 0); int num3 = num2; try { for (int i = 0; i < s_rows.Length; i++) { int num4 = s_rows[i]; bool flag5 = num4 == 4 || num4 == 5; if (flag5 && flag2) { continue; } int num5 = EquipmentMask.s_vanillaKeys[num4]; int num6 = zdo.GetInt(num5, 0); int num7 = ((!flag5 && (num4 != 8 || !gear.UtilityWielded)) ? (flag ? Value(gear, i) : 0) : ((!flag3) ? Value(gear, i) : 0)); if (num6 != num7) { if ((num2 & (1 << num4)) == 0) { Swap(zdo, EquipmentMask.s_overrideKeys[num4], num6); num2 |= 1 << num4; } Swap(zdo, num5, num7); flag4 = true; } } if (num2 != num3) { Swap(zdo, EquipmentMask.PublishKey, num2); flag4 = true; } } catch (Exception arg) { End(); Disguise.LogError($"CostumeGear outgoing: {arg}"); return false; } if (!flag4) { s_activeZdo = null; } return flag4; } internal static void End() { ZDO val = s_activeZdo; int num = s_savedCount; s_activeZdo = null; s_savedCount = 0; if (val == null || num == 0) { return; } try { for (int i = 0; i < num; i++) { GameAccess.SetZdoIntSilent(val, s_savedKeys[i], s_savedValues[i]); } } catch (Exception arg) { Disguise.LogError($"CostumeGear restore: {arg}"); } } private static void Swap(ZDO zdo, int key, int value) { s_savedKeys[s_savedCount] = key; s_savedValues[s_savedCount] = zdo.GetInt(key, 0); s_savedCount++; GameAccess.SetZdoIntSilent(zdo, key, value); } private static GearSkin.Gear? Roll(ZDO zdo) { int prefab = zdo.GetPrefab(); int num = GearSkin.Seed(zdo); ZNetScene instance = ZNetScene.instance; GameObject val = (((Object)(object)instance != (Object)null) ? instance.GetPrefab(prefab) : null); if ((Object)(object)val == (Object)null) { return null; } int num2 = Forced(zdo, val); if (prefab == s_cachedPrefab && num == s_cachedSeed && num2 == s_cachedForced) { return s_cachedGear; } s_cachedPrefab = prefab; s_cachedSeed = num; s_cachedForced = num2; s_cachedGear = GearSkin.Compute(val, num, num2); return s_cachedGear; } private static int Forced(ZDO zdo, GameObject? prefab) { int num = zdo.GetInt(CdWeaponKey, -1); if (num < 0) { return GearSkin.LocalWeaponHash(prefab); } return GearSkin.WeaponHash(prefab, num); } private static int Value(GearSkin.Gear gear, int position) { return position switch { 0 => gear.Helmet, 1 => gear.Chest, 2 => gear.Leg, 3 => gear.Shoulder, 4 => gear.Left, 5 => gear.Right, 6 => gear.Utility, _ => gear.TrinketHash, }; } } internal static class CreatureAttacks { internal static (string? normal, string? special, string? block) Read(int prefabHash, HashSet<string> triggers) { if (triggers.Count == 0) { return (normal: null, special: null, block: null); } GameObject val = GhostFactory.Prefab(prefabHash); if ((Object)(object)val == (Object)null) { return (normal: null, special: null, block: null); } Humanoid componentInChildren = val.GetComponentInChildren<Humanoid>(true); if ((Object)(object)componentInChildren == (Object)null) { return (normal: null, special: null, block: null); } List<(string name, bool secondary, bool projectile, float interval, int order)> entries = new List<(string, bool, bool, float, int)>(); HashSet<string> seen = new HashSet<string>(StringComparer.Ordinal); int order = 0; foreach (GameObject item4 in Pool(componentInChildren)) { ItemData val2 = ItemDataOf(item4); if (val2 != null) { Add(val2, secondary: false); Add(val2, secondary: true); } } if (entries.Count == 0) { return (normal: null, special: null, block: null); } entries.Sort(delegate((string name, bool secondary, bool projectile, float interval, int order) a, (string name, bool secondary, bool projectile, float interval, int order) b) { int num3 = a.secondary.CompareTo(b.secondary); if (num3 == 0) { num3 = a.projectile.CompareTo(b.projectile); } if (num3 == 0) { num3 = a.interval.CompareTo(b.interval); } return (num3 == 0) ? a.order.CompareTo(b.order) : num3; }); string item = entries[0].name; List<(string, bool, bool, float, int)> list = new List<(string, bool, bool, float, int)>(entries); list.Sort(delegate((string name, bool secondary, bool projectile, float interval, int order) a, (string name, bool secondary, bool projectile, float interval, int order) b) { int num3 = b.secondary.CompareTo(a.secondary); if (num3 == 0) { num3 = b.interval.CompareTo(a.interval); } return (num3 == 0) ? a.order.CompareTo(b.order) : num3; }); string text = null; for (int num = 0; num < list.Count; num++) { if (list[num].Item1 != item) { text = list[num].Item1; break; } } List<string> list2 = new List<string>(); for (int num2 = 0; num2 < entries.Count; num2++) { string item2 = entries[num2].name; if (item2 != item && item2 != text) { list2.Add(item2); } } string item3 = ((list2.Count > 0) ? list2[(list2.Count - 1) / 2] : null); return (normal: item, special: text, block: item3); void Add(ItemData data, bool secondary) { //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Invalid comparison between Unknown and I4 //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Invalid comparison between Unknown and I4 Attack val3 = (secondary ? data.m_shared.m_secondaryAttack : data.m_shared.m_attack); if (val3 != null && !string.IsNullOrEmpty(val3.m_attackAnimation)) { string attackAnimation = val3.m_attackAnimation; if (seen.Add(attackAnimation) && triggers.Contains(attackAnimation)) { entries.Add((attackAnimation, secondary, (int)val3.m_attackType == 2 || (int)val3.m_attackType == 5, data.m_shared.m_aiAttackInterval, order)); order++; } } } } private static IEnumerable<GameObject> Pool(Humanoid humanoid) { GameObject[] array = humanoid.m_defaultItems ?? Array.Empty<GameObject>(); foreach (GameObject val in array) { if ((Object)(object)val != (Object)null) { yield return val; } } array = humanoid.m_randomWeapon ?? Array.Empty<GameObject>(); foreach (GameObject val2 in array) { if ((Object)(object)val2 != (Object)null) { yield return val2; } } RandomItem[] array2 = humanoid.m_randomItems ?? Array.Empty<RandomItem>(); foreach (RandomItem val3 in array2) { if (val3 != null && (Object)(object)val3.m_prefab != (Object)null) { yield return val3.m_prefab; } } ItemSet[] array3 = humanoid.m_randomSets ?? Array.Empty<ItemSet>(); foreach (ItemSet val4 in array3) { if (val4 == null || val4.m_items == null) { continue; } array = val4.m_items; foreach (GameObject val5 in array) { if ((Object)(object)val5 != (Object)null) { yield return val5; } } } if ((Object)(object)humanoid.m_unarmedWeapon != (Object)null) { yield return ((Component)humanoid.m_unarmedWeapon).gameObject; } } private static ItemData? ItemDataOf(GameObject item) { ItemDrop val = item.GetComponent<ItemDrop>() ?? item.GetComponentInChildren<ItemDrop>(true); if (!((Object)(object)val != (Object)null)) { return null; } return val.m_itemData; } } internal static class CreaturePreview { internal const float PictureWidth = 420f; internal const float PictureHeight = 300f; private static readonly Vector3 s_stageOrigin = new Vector3(0f, -20000f, 0f); private static readonly Vector3 s_keyAngles = new Vector3(28f, 22f, 0f); private static readonly Vector3 s_fillAngles = new Vector3(20f, -135f, 0f); private static readonly Color s_previewAmbient = new Color(0.42f, 0.42f, 0.47f); private const float KeyIntensity = 1.1f; private const float FillIntensity = 0.4f; private const float VerticalFov = 35f; private const float Tilt = 14f; private const float TurnDegreesPerSecond = 25f; private const float FrameMargin = 1.2f; private const float PoseSlack = 0.02f; private const float MinFramedDistance = 0.05f; private const float MinCameraDistance = 40f; private const int NotSearched = -2; private const int NoFreeLayer = -1; private static readonly int[] s_spareLayers = new int[3] { 7, 6, 3 }; private static int s_layer = -2; private static bool s_layerWarned; private static GameObject? s_root; private static Camera? s_camera; private static RenderTexture? s_texture; private static GameObject? s_creature; private static readonly List<KeyValuePair<Camera, int>> s_hiddenCameras = new List<KeyValuePair<Camera, int>>(); private static readonly List<KeyValuePair<Light, int>> s_hiddenLights = new List<KeyValuePair<Light, int>>(); private static Mesh? s_bake; private static readonly List<Vector3> s_bakeVertices = new List<Vector3>(); private static Vector3 s_center; private static float s_radius; private static float s_distance; private static float s_yaw; private static bool s_pictureFresh = true; internal static RenderTexture? Texture => s_texture; internal static void Show(string creature) { s_pictureFresh = false; GameObject val = Build(creature); DropCreature(); if (!((Object)(object)val == (Object)null)) { s_creature = val; val.SetActive(true); Settle(val); if (!Frame(val)) { DropCreature(); } } } private static GameObject? Build(string creature) { //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) if (creature.Length == 0 || !Ensure()) { return null; } ZNetScene instance = ZNetScene.instance; GameObject val = (((Object)(object)instance != (Object)null) ? instance.GetPrefab(creature) : null); if ((Object)(object)instance == (Object)null || (Object)(object)val == (Object)null) { Disguise.LogDebug("Preview: no prefab named \"" + creature + "\"."); return null; } GameObject val2 = GhostFactory.BuildInert(instance.GetPrefabHash(val), keepEquipment: true, GearSkin.Seed(GameAccess.GetLocalPlayerZDO())); if ((Object)(object)val2 == (Object)null) { return null; } val2.transform.SetParent(s_root.transform, false); val2.transform.localPosition = Vector3.zero; val2.transform.localRotation = Quaternion.identity; SetLayer(val2.transform, s_layer); SkinnedMeshRenderer[] componentsInChildren = val2.GetComponentsInChildren<SkinnedMeshRenderer>(true); foreach (SkinnedMeshRenderer val3 in componentsInChildren) { if ((Object)(object)val3 != (Object)null) { val3.updateWhenOffscreen = true; } } LevelSkin.Apply(val2, val, DisguiseConfig.ConfigCreatureLevel.Value); return val2; } private static void Settle(GameObject clone) { Animator[] componentsInChildren = clone.GetComponentsInChildren<Animator>(true); foreach (Animator val in componentsInChildren) { if (!((Object)(object)val == (Object)null) && ((Behaviour)val).isActiveAndEnabled) { val.applyRootMotion = false; val.fireEvents = false; if ((Object)(object)val.runtimeAnimatorController != (Object)null) { val.Update(0f); } ((Behaviour)val).enabled = false; } } } private static bool Frame(GameObject clone) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: 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_00eb: 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_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_0189: Unknown result type (might be due to invalid IL or missing references) //IL_019d: Unknown result type (might be due to invalid IL or missing references) //IL_01b1: 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_00c9: Unknown result type (might be due to invalid IL or missing references) Renderer[] componentsInChildren = clone.GetComponentsInChildren<Renderer>(true); bool flag = false; Bounds bounds = default(Bounds); int num = 0; int num2 = 0; foreach (Renderer renderer in componentsInChildren) { if (DrawsMesh(renderer)) { bool fromPose; Bounds val = DrawnBounds(renderer, out fromPose); num++; if (fromPose) { num2++; } if (!flag) { bounds = val; flag = true; } else { ((Bounds)(ref bounds)).Encapsulate(val); } } } if (!flag) { Disguise.LogWarning("Preview: " + ((Object)clone).name + " has no mesh to show."); return false; } Camera val2 = s_camera; if ((Object)(object)val2 == (Object)null) { return false; } float num3 = FramedDistance(bounds); float num4 = Mathf.Max(1f, 40f / num3); if (num4 > 1f) { Transform transform = clone.transform; transform.localScale *= num4; } Vector3 position = clone.transform.position; s_center = position + (((Bounds)(ref bounds)).center - position) * num4; Vector3 extents = ((Bounds)(ref bounds)).extents; s_radius = Mathf.Max(((Vector3)(ref extents)).magnitude, 0.05f) * num4; s_distance = num3 * num4; s_yaw = 0f; val2.nearClipPlane = Mathf.Max(0.1f, s_distance * 0.01f); val2.farClipPlane = s_distance + s_radius * 3f + 50f; Disguise.LogDebug($"Preview: {((Object)clone).name} measures {((Bounds)(ref bounds)).size.x:0.##} x {((Bounds)(ref bounds)).size.y:0.##} x {((Bounds)(ref bounds)).size.z:0.##} m" + $" ({num2} of {num} meshes measured from their pose)," + $" framed at {num3:0.##} m, grown {num4:0.#}x."); return true; } internal static bool DrawsMesh(Renderer renderer) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Invalid comparison between Unknown and I4 if ((Object)(object)renderer != (Object)null && renderer.enabled && !renderer.forceRenderingOff && SwitchedOn(((Component)renderer).transform) && (int)renderer.shadowCastingMode != 3) { if (!(renderer is MeshRenderer)) { return renderer is SkinnedMeshRenderer; } return true; } return false; } internal static bool SwitchedOn(Transform transform) { Transform val = transform; while ((Object)(object)val != (Object)null) { if (!((Component)val).gameObject.activeSelf) { return false; } val = val.parent; } return true; } internal static Bounds DrawnBounds(Renderer renderer, out bool fromPose) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) fromPose = false; Bounds bounds = renderer.bounds; SkinnedMeshRenderer val = (SkinnedMeshRenderer)(object)((renderer is SkinnedMeshRenderer) ? renderer : null); if (val != null && TryPoseBounds(val, bounds, out var bounds2)) { fromPose = true; return bounds2; } return bounds; } private static bool TryPoseBounds(SkinnedMeshRenderer skinned, Bounds culling, out Bounds bounds) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0027: 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_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0061: 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) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_0098: 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) bounds = default(Bounds); if ((Object)(object)skinned.sharedMesh == (Object)null || !Bake(skinned)) { return false; } Transform transform = ((Component)skinned).transform; Bounds bounds2; bool flag = BoxOf(transform.localToWorldMatrix, out bounds2) && Inside(bounds2, culling); Bounds bounds3; bool flag2 = BoxOf(Matrix4x4.TRS(transform.position, transform.rotation, Vector3.one), out bounds3) && Inside(bounds3, culling); if (flag && flag2) { Vector3 size = ((Bounds)(ref bounds3)).size; float sqrMagnitude = ((Vector3)(ref size)).sqrMagnitude; size = ((Bounds)(ref bounds2)).size; bounds = ((sqrMagnitude > ((Vector3)(ref size)).sqrMagnitude) ? bounds3 : bounds2); return true; } if (flag || flag2) { bounds = (flag ? bounds2 : bounds3); return true; } return false; } private static bool Bake(SkinnedMeshRenderer skinned) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Expected O, but got Unknown Mesh val = s_bake; if ((Object)(object)val == (Object)null) { val = (s_bake = new Mesh { name = "Disguise preview pose", hideFlags = (HideFlags)61 }); } val.Clear(); skinned.BakeMesh(val, true); s_bakeVertices.Clear(); val.GetVertices(s_bakeVertices); return s_bakeVertices.Count > 0; } private static bool BoxOf(Matrix4x4 matrix, out Bounds bounds) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_002d: 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) //IL_0033: 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) bounds = default(Bounds); int count = s_bakeVertices.Count; for (int i = 0; i < count; i++) { Vector3 val = ((Matrix4x4)(ref matrix)).MultiplyPoint3x4(s_bakeVertices[i]); if (i == 0) { bounds = new Bounds(val, Vector3.zero); } else { ((Bounds)(ref bounds)).Encapsulate(val); } } return count > 0; } private static bool Inside(Bounds inner, Bounds outer) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0049: 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_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) Vector3 val = ((Bounds)(ref outer)).size * 0.02f; Vector3 val2 = ((Bounds)(ref inner)).min - ((Bounds)(ref outer)).min; Vector3 val3 = ((Bounds)(ref outer)).max - ((Bounds)(ref inner)).max; if (val2.x >= 0f - val.x && val2.y >= 0f - val.y && val2.z >= 0f - val.z && val3.x >= 0f - val.x && val3.y >= 0f - val.y) { return val3.z >= 0f - val.z; } return false; } private static float FramedDistance(Bounds bounds) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) float x = ((Bounds)(ref bounds)).size.x; float y = ((Bounds)(ref bounds)).size.y; float z = ((Bounds)(ref bounds)).size.z; float num = Mathf.Sqrt(x * x + z * z); float num2 = 0.2443461f; float num3 = y * Mathf.Cos(num2) + num * Mathf.Sin(num2); float num4 = Mathf.Tan(0.30543262f); float num5 = num4 * 1.4f; float num6 = num3 * 0.5f / num4; float num7 = num * 0.5f / num5; return Mathf.Max(Mathf.Max(num6, num7) * 1.2f, 0.05f); } private static void DropCreature() { GameObject val = s_creature; s_creature = null; if (!((Object)(object)val == (Object)null)) { val.SetActive(false); Object.Destroy((Object)(object)val); } } private static void SetLayer(Transform transform, int layer) { ((Component)transform).gameObject.layer = layer; for (int i = 0; i < transform.childCount; i++) { SetLayer(transform.GetChild(i), layer); } } private static bool StageLayer(Transform transform) { bool flag = false; if (((Component)transform).gameObject.layer != s_layer) { ((Component)transform).gameObject.layer = s_layer; flag = true; } for (int i = 0; i < transform.childCount; i++) { flag |= StageLayer(transform.GetChild(i)); } return flag; } private static bool Ensure() { //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Expected O, but got Unknown //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Expected O, but got Unknown //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Unknown result type (might be due to invalid IL or missing references) if (s_layer == -2) { s_layer = FreeLayer(); } if (s_layer < 0) { return false; } if ((Object)(object)s_camera != (Object)null && (Object)(object)s_texture != (Object)null) { return true; } Forget(); s_root = new GameObject("Disguise preview stage"); s_root.transform.position = s_stageOrigin; HideStage(); GameObject val = new GameObject("Disguise preview camera"); val.transform.SetParent(s_root.transform, false); Camera obj = val.AddComponent<Camera>(); ((Behaviour)obj).enabled = false; obj.clearFlags = (CameraClearFlags)2; obj.backgroundColor = new Color(0f, 0f, 0f, 0f); obj.cullingMask = 1 << s_layer; obj.fieldOfView = 35f; obj.allowHDR = false; obj.allowMSAA = true; obj.useOcclusionCulling = false; s_camera = obj; RenderTexture val2 = new RenderTexture(420, 300, 24, (RenderTextureFormat)0); ((Object)val2).name = "Disguise preview"; val2.antiAliasing = 4; val2.Create(); s_texture = val2; obj.targetTexture = val2; AddLight(val.transform, s_keyAngles, 1.1f); AddLight(s_root.transform, s_fillAngles, 0.4f); return true; } private static void AddLight(Transform parent, Vector3 angles, float intensity) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("Disguise preview light"); val.transform.SetParent(parent, false); val.transform.localRotation = Quaternion.Euler(angles); Light obj = val.AddComponent<Light>(); obj.type = (LightType)1; obj.intensity = intensity; obj.shadows = (LightShadows)0; obj.cullingMask = 1 << s_layer; } private static int FreeLayer() { for (int num = 31; num >= 8; num--) { if (string.IsNullOrEmpty(LayerMask.LayerToName(num))) { return num; } } for (int i = 0; i < s_spareLayers.Length; i++) { int num2 = s_spareLayers[i]; if (string.IsNullOrEmpty(LayerMask.LayerToName(num2))) { return num2; } } if (!s_layerWarned) { s_layerWarned = true; Disguise.LogWarning("GUI: every layer is named (by a mod), so the 3D preview has nowhere to stand and stays off."); } return -1; } private static void HideStage() { //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Invalid comparison between Unknown and I4 int num = 1 << s_layer; s_hiddenCameras.Clear(); Camera[] allCameras = Camera.allCameras; foreach (Camera val in allCameras) { if (!((Object)(object)val == (Object)null) && (val.cullingMask & num) != 0) { s_hiddenCameras.Add(new KeyValuePair<Camera, int>(val, val.cullingMask)); val.cullingMask &= ~num; } } s_hiddenLights.Clear(); Light[] array = Object.FindObjectsByType<Light>((FindObjectsSortMode)0); foreach (Light val2 in array) { if (!((Object)(object)val2 == (Object)null) && (int)val2.type == 1 && (val2.cullingMask & num) != 0) { s_hiddenLights.Add(new KeyValuePair<Light, int>(val2, val2.cullingMask)); val2.cullingMask &= ~num; } } } private static void GiveLayerBack() { for (int i = 0; i < s_hiddenCameras.Count; i++) { KeyValuePair<Camera, int> keyValuePair = s_hiddenCameras[i]; if ((Object)(object)keyValuePair.Key != (Object)null) { keyValuePair.Key.cullingMask = keyValuePair.Value; } } s_hiddenCameras.Clear(); for (int j = 0; j < s_hiddenLights.Count; j++) { KeyValuePair<Light, int> keyValuePair2 = s_hiddenLights[j]; if ((Object)(object)keyValuePair2.Key != (Object)null) { keyValuePair2.Key.cullingMask = keyValuePair2.Value; } } s_hiddenLights.Clear(); } internal static void Render() { //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_013a: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Unknown result type (might be due to invalid IL or missing references) //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) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: 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_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: 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_0108: Unknown result type (might be due to invalid IL or missing references) //IL_0112: 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_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: 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_00c3: Unknown result type (might be due to invalid IL or missing references) Camera val = s_camera; GameObject val2 = s_creature; if ((Object)(object)val == (Object)null || ((Object)(object)val2 == (Object)null && s_pictureFresh)) { return; } if ((Object)(object)val2 != (Object)null) { if (StageLayer(val2.transform)) { Frame(val2); } s_yaw += 25f * Time.unscaledDeltaTime; float num = s_yaw * ((float)Math.PI / 180f); float num2 = 0.2443461f; Vector3 val3 = default(Vector3); ((Vector3)(ref val3))..ctor(Mathf.Sin(num) * Mathf.Cos(num2), Mathf.Sin(num2), Mathf.Cos(num) * Mathf.Cos(num2)); ((Component)val).transform.position = s_center + val3 * s_distance; ((Component)val).transform.LookAt(s_center); } bool fog = RenderSettings.fog; Color ambientLight = RenderSettings.ambientLight; Color ambientSkyColor = RenderSettings.ambientSkyColor; Color ambientEquatorColor = RenderSettings.ambientEquatorColor; Color ambientGroundColor = RenderSettings.ambientGroundColor; try { RenderSettings.fog = false; RenderSettings.ambientLight = s_previewAmbient; RenderSettings.ambientSkyColor = s_previewAmbient; RenderSettings.ambientEquatorColor = s_previewAmbient; RenderSettings.ambientGroundColor = s_previewAmbient * 0.7f; val.Render(); } finally { RenderSettings.fog = fog; RenderSettings.ambientLight = ambientLight; RenderSettings.ambientSkyColor = ambientSkyColor; RenderSettings.ambientEquatorColor = ambientEquatorColor; RenderSettings.ambientGroundColor = ambientGroundColor; } s_pictureFresh = true; } internal static void Forget() { //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)s_texture == (Object)null) || !((Object)(object)s_root == (Object)null)) { DropCreature(); GiveLayerBack(); if ((Object)(object)s_root != (Object)null) { Object.Destroy((Object)(object)s_root); } s_root = null; s_camera = null; if ((Object)(object)s_texture != (Object)null) { s_texture.Release(); Object.Destroy((Object)(object)s_texture); } s_texture = null; if ((Object)(object)s_bake != (Object)null) { Object.Destroy((Object)(object)s_bake); s_bake = null; } s_bakeVertices.Clear(); s_center = Vector3.zero; s_radius = 0f; s_distance = 0f; s_yaw = 0f; s_pictureFresh = true; } } } internal static class CreatureTriggers { private static readonly string[] s_attackWords = new string[25] { "swing", "slash", "stab", "poke", "thrust", "cleave", "smash", "bash", "punch", "kick", "slam", "swipe", "bite", "claw", "sword", "axe", "mace", "spear", "knife", "dual", "atgeir", "unarmed", "hit", "strike", "attack" }; private static readonly string[] s_notAttackWords = new string[11] { "abort", "throw", "shoot", "bow", "fire", "bolt", "cast", "staff", "spit", "hammer", "hoe" }; private static int s_creatureHash; private static HashSet<string> s_creatureTriggers = new HashSet<string>(StringComparer.Ordinal); private static string? s_normalAttack; private static string? s_specialAttack; private static string? s_blockAttack; private static HashSet<string> s_playerTriggers = new HashSet<string>(StringComparer.Ordinal); private static bool s_playerTriggersRead; private static readonly Dictionary<string, string> s_traced = new Dictionary<string, string>(StringComparer.Ordinal); internal static void Trace(string name, bool mine, string? resolved, string? kind) { if (!mine || resolved != null || LooksLikeAttack(name)) { string outcome = ((!mine) ? "skipped: the component is not the local player's" : ((resolved == null) ? $"no creature equivalent (player {s_playerTriggers.Count}, creature {s_creatureTriggers.Count} triggers)" : ((!(resolved == name)) ? ((kind != null) ? ("sent again as " + resolved + " (" + kind + ")") : ("sent again as " + resolved)) : "shared with the creature: nothing extra to send"))); Report(name, outcome); } } internal static void TraceBlock(string? resolved) { Report("block", (resolved != null) ? ("sent again as " + resolved + " (block attack)") : "no third attack (one or two attacks in the weapon pool, or none readable)"); } private static void Report(string name, string outcome) { if (!s_traced.TryGetValue(name, out string value) || !(value == outcome)) { s_traced[name] = outcome; Disguise.LogInfo("Attack bridge: " + name + " -> " + outcome); } } internal static (string? trigger, string? kind) Resolve(ZDO? zdo, string name) { if (zdo == null || string.IsNullOrEmpty(name)) { return (trigger: null, kind: null); } int prefab = zdo.GetPrefab(); if (prefab == 0 || prefab == PrefabResolver.PlayerPrefabHash) { return (trigger: null, kind: null); } if (!ReadPlayerTriggers()) { return (trigger: null, kind: null); } ReadCreatureTriggers(prefab); if (s_creatureTriggers.Contains(name)) { return (trigger: name, kind: null); } if (!LooksLikeAttack(name)) { return (trigger: null, kind: null); } if (IsSecondaryAttack(name) && s_specialAttack != null) { return (trigger: s_specialAttack, kind: "special attack"); } if (s_normalAttack != null) { return (trigger: s_normalAttack, kind: "normal attack"); } string? text = PickCompanion(name); return (trigger: text, kind: (text != null) ? "word match" : null); } internal static string? ResolveBlock(ZDO? zdo) { if (zdo == null) { return null; } int prefab = zdo.GetPrefab(); if (prefab == 0 || prefab == PrefabResolver.PlayerPrefabHash) { return null; } ReadCreatureTriggers(prefab); return s_blockAttack; } private static bool IsSecondaryAttack(string name) { Player localPlayer = Player.m_localPlayer; ItemData val = (((Object)(object)localPlayer != (Object)null) ? ((Humanoid)localPlayer).GetCurrentWeapon() : null); if (val == null || !val.HaveSecondaryAttack()) { return false; } string attackAnimation = val.m_shared.m_secondaryAttack.m_attackAnimation; if (string.IsNullOrEmpty(attackAnimation) || (val.HavePrimaryAttack() && val.m_shared.m_attack.m_attackAnimation == attackAnimation)) { return false; } int num = name.Length; while (num > 0 && char.IsDigit(name[num - 1])) { num--; } if (num == attackAnimation.Length) { return string.CompareOrdinal(name, 0, attackAnimation, 0, num) == 0; } return false; } private static void ReadCreatureTriggers(int hash) { if (s_creatureHash != hash) { s_creatureHash = hash; HashSet<string> hashSet = (s_creatureTriggers = Probe(hash)); if (hashSet.Count == 0) { s_normalAttack = null; s_specialAttack = null; s_blockAttack = null; s_creatureHash = 0; } else { (s_normalAttack, s_specialAttack, s_blockAttack) = CreatureAttacks.Read(hash, hashSet); } } } private static HashSet<string> Probe(int hash) { GameObject val = GhostFactory.BuildInert(hash, keepEquipment: false, 0); if ((Object)(object)val == (Object)null) { return new HashSet<string>(StringComparer.Ordinal); } try { val.SetActive(true); return ReadTriggers(val.GetComponentInChildren<Animator>(true)); } finally { val.SetActive(false); Object.Destroy((Object)(object)val); } } private static bool ReadPlayerTriggers() { if (s_playerTriggersRead) { return true; } HashSet<string> hashSet = ReadTriggers(((Object)(object)Player.m_localPlayer != (Object)null) ? ((Component)Player.m_localPlayer).GetComponentInChildren<Animator>(true) : null); if (hashSet.Count == 0) { return false; } s_playerTriggers = hashSet; s_playerTriggersRead = true; return true; } private static HashSet<string> ReadTriggers(Animator? animator) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Invalid comparison between Unknown and I4 HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal); if ((Object)(object)animator == (Object)null) { return hashSet; } AnimatorControllerParameter[] parameters = animator.parameters; foreach (AnimatorControllerParameter val in parameters) { if ((int)val.type == 9 && !string.IsNullOrEmpty(val.name)) { hashSet.Add(val.name); } } return hashSet; } private static string? PickCompanion(string source) { string text = FirstAttackWord(source) ?? string.Empty; string text2 = null; int num = int.MaxValue; foreach (string s_creatureTrigger in s_creatureTriggers) { if (!s_playerTriggers.Contains(s_creatureTrigger) && LooksLikeAttack(s_creatureTrigger)) { int num2 = ((text.Length <= 0 || s_creatureTrigger.IndexOf(text, StringComparison.OrdinalIgnoreCase) < 0) ? 1 : 0); if (text2 == null || num2 < num || (num2 == num && string.CompareOrdinal(s_creatureTrigger, text2) < 0)) { text2 = s_creatureTrigger; num = num2; } } } return text2; } private static bool LooksLikeAttack(string name) { string[] array = s_notAttackWords; foreach (string value in array) { if (name.IndexOf(value, StringComparison.OrdinalIgnoreCase) >= 0) { return false; } } return FirstAttackWord(name) != null; } private static string? FirstAttackWord(string name) { string[] array = s_attackWords; foreach (string text in array) { if (name.IndexOf(text, StringComparison.OrdinalIgnoreCase) >= 0) { return text; } } return null; } } [BepInPlugin("orax.Disguise", "Disguise", "0.1.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] public class Disguise : BaseUnityPlugin { public const string PluginGUID = "orax.Disguise"; public const string PluginName = "Disguise"; public const string PluginVersion = "0.1.0"; public const string MarkerKey = "cd_disguise"; public const string RefreshKey = "cd_refresh"; public const string HideKey = "cd_hide"; public const string RelationKey = "cd_rel"; public const string HideListKey = "cd_offlist"; public const string FakeKey = "cd_fake"; public const string FakePositionKey = "cd_fpos"; public const string FlyKey = "cd_fly"; public const string OffsetXKey = "cd_ox"; public const string OffsetYKey = "cd_oy"; public const string OffsetZKey = "cd_oz"; public const string RotationXKey = "cd_rx"; public const string RotationYKey = "cd_ry"; public const string RotationZKey = "cd_rz"; internal static Harmony? PatchHarmony; private static Disguise? s_instance; private static float s_nextReassertTime; internal static bool s_loggedSummary; internal static void LogDebug(string message) { if (DisguiseConfig.ConfigLogs.Value) { Disguise? disguise = s_instance; if (disguise != null) { ((BaseUnityPlugin)disguise).Logger.LogDebug((object)message); } } } internal static void LogInfo(string message) { if (DisguiseConfig.ConfigLogs.Value) { Disguise? disguise = s_instance; if (disguise != null) { ((BaseUnityPlugin)disguise).Logger.LogInfo((object)message); } } } internal static void LogWarning(string message) { Disguise? disguise = s_instance; if (disguise != null) { ((BaseUnityPlugin)disguise).Logger.LogWarning((object)message); } } internal static void LogError(string message) { Disguise? disguise = s_instance; if (disguise != null) { ((BaseUnityPlugin)disguise).Logger.LogError((object)message); } } public void Awake() { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Expected O, but got Unknown s_instance = this; DisguiseConfig.Bind(((BaseUnityPlugin)this).Config); PatchHarmony = new Harmony("orax.Disguise"); PatchHarmony.PatchAll(Assembly.GetExecutingAssembly()); if (GameAccess.s_zdoPrefabField == null) { ((BaseUnityPlugin)this).Logger.LogError((object)"ZDO.m_prefab not found: save protection is disabled."); } if (GameAccess.s_characterNViewField == null) { ((BaseUnityPlugin)this).Logger.LogError((object)"Character.m_nview not found: disguise is disabled."); } if (GameAccess.s_increaseDataRevisionMethod == null) { ((BaseUnityPlugin)this).Logger.LogWarning((object)"ZDO.IncreaseDataRevision not found: falling back to ZDO.Set()."); } if (GameAccess.s_zdoPositionField == null) { ((BaseUnityPlugin)this).Logger.LogWarning((object)"ZDO.m_position not found: observer refresh disabled (ForceObserverRefresh)."); } DisguiseCommand.RegisterConsoleCommands(); LogInfo(string.Format("{0} {1} loaded - disguise: {2}, creature: \"{3}\"", "Disguise", "0.1.0", DisguiseConfig.ConfigEnabled.Value, DisguiseConfig.ConfigCreature.Value)); } public void OnDestroy() { DisguiseGui.Shutdown(); SelfView.Shutdown(); PauseMenuEntry.Cleanup(); GroundProbe.Cleanup(); Harmony? patchHarmony = PatchHarmony; if (patchHarmony != null) { patchHarmony.UnpatchSelf(); } PatchHarmony = null; s_instance = null; } public void Update() { //IL_00cd: Unknown result type (might be due to invalid IL or missing references) FrameMeter.Sample(Time.unscaledDeltaTime); ObserverRefresh.TickObserverRefresh(); FlightControl.Tick(); PoseControl.Tick(); SelfView.Tick(); DisguiseGui.Tick(); if (Time.unscaledTime < s_nextReassertTime) { return; } s_nextReassertTime = Time.unscaledTime + Mathf.Max(0.25f, DisguiseConfig.ConfigReassert.Value); ZDO localPlayerZDO = GameAccess.GetLocalPlayerZDO(); if (localPlayerZDO != null && localPlayerZDO.IsValid()) { if (ObserverRefresh.s_ownerRefreshZdo == null && localPlayerZDO.GetInt(PrefabResolver.RefreshHash, 0) == 1) { localPlayerZDO.Set(PrefabResolver.RefreshHash, 0, false); GameAccess.BumpDataRevision(localPlayerZDO); LogInfo("Cleared a stale cd_refresh flag on your player ZDO."); } if (!s_loggedSummary) { s_loggedSummary = true; bool flag = (Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer(); LogInfo($"Local player (ZDO {localPlayerZDO.m_uid}) - current prefab: {PrefabResolver.PrefabName(localPlayerZDO.GetPrefab())}, " + "desired prefab: " + PrefabResolver.PrefabName(PrefabResolver.DesiredPrefabHash()) + (flag ? " [local world / host]" : " [client]") + ", marker: " + (DisguiseApplier.HasMarker(localPlayerZDO) ? "yes" : "no")); } DisguiseApplier.ApplyTo(localPlayerZDO, "reassert"); EquipmentMask.PublishMask(localPlayerZDO); CostumeGear.Publish(localPlayerZDO); AllyView.Publish(localPlayerZDO); FakePosition.Publish(localPlayerZDO); OffList.Publish(localPlayerZDO); } } public void LateUpdate() { SelfView.LateTick(); } } internal static class DisguiseApplier { internal static bool HasMarker(ZDO? zdo) { if (zdo != null && zdo.IsValid()) { return zdo.GetInt(PrefabResolver.MarkerHash, 0) == 1; } return false; } internal static void ApplyTo(ZDO? zdo, string reason) { //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Unknown result type (might be due to invalid IL or missing references) try { if (zdo == null || !zdo.IsValid()) { return; } int playerPrefabHash = PrefabResolver.PlayerPrefabHash; if (playerPrefabHash == 0) { return; } int num = PrefabResolver.DesiredPrefabHash(); if (num == 0) { return; } if (num != playerPrefabHash) { zdo.Set(PrefabResolver.MarkerHash, 1, false); } else if (zdo.GetInt(PrefabResolver.MarkerHash, 0) != 0) { zdo.Set(PrefabResolver.MarkerHash, 0, false); } ZDO localPlayerZDO = GameAccess.GetLocalPlayerZDO(); bool flag = localPlayerZDO != null && localPlayerZDO.m_uid == zdo.m_uid; bool flag2 = false; if (flag) { if (num != playerPrefabHash) { int value = DisguiseConfig.ConfigCreatureLevel.Value; flag2 = zdo.GetInt(PrefabResolver.LevelHash, 1) != value; zdo.Set(PrefabResolver.LevelHash, value, false); } else if (zdo.GetInt(PrefabResolver.LevelHash, 1) != 1) { zdo.Set(PrefabResolver.LevelHash, 1, false); } } int prefab = zdo.GetPrefab(); if (prefab == num) { if (flag && flag2) { ObserverRefresh.StartObserverRefresh(zdo, force: false); } return; } zdo.SetPrefab(num); Disguise.LogInfo($"[{reason}] ZDO {zdo.m_uid}: prefab {PrefabResolver.PrefabName(prefab)} -> {PrefabResolver.PrefabName(num)}"); if (flag) { ObserverRefresh.StartObserverRefresh(zdo, force: false); } } catch (Exception arg) { Disguise.LogError($"ApplyTo ({reason}): {arg}"); } } internal static GameObject InstantiateAs(ZDO zdo, int prefabHash) { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) GameObject prefab = ZNetScene.instance.GetPrefab(prefabHash); if ((Object)(object)prefab == (Object)null) { Disguise.LogWarning("Prefab not found: " + PrefabResolver.PrefabName(prefabHash)); return null; } bool useInitZDO = ZNetView.m_useInitZDO; try { ZNetView.m_useInitZDO = true; ZNetView.m_initZDO = zdo; GameObject result = Object.Instantiate<GameObject>(prefab, zdo.GetPosition(), zdo.GetRotation()); if (ZNetView.m_initZDO != null) { Disguise.LogWarning($"ZDO {zdo.m_uid} not consumed while creating {((Object)prefab).name}"); ZNetView.m_initZDO = null; } return result; } finally { ZNetView.m_useInitZDO = useInitZDO; } } } internal static class DisguiseCommand { private static bool s_consoleCommandRegistered; internal static void RegisterConsoleCommands() { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Expected O, but got Unknown //IL_0039: Expected O, but got Unknown //IL_0034: Unknown result type (might be due to invalid IL or missing references) try { if (!s_consoleCommandRegistered) { new ConsoleCommand("disguise", "Disguise: status | set <prefab> [level] | level <1-9> | enable | disable | refresh | view | logs | armor | weapons | offlist | fly | offset | rotation | setFakePosition | perf | list [filter] | favorite [prefab] | gui | help", new ConsoleEvent(OnDisguiseCommand), false, false, false, false, false, false, new ConsoleOptionsFetcher(DisguiseOptions), true, false, false); s_consoleCommandRegistered = true; } } catch (Exception arg) { Disguise.LogError($"Failed to register the console command: {arg}"); } } private static List<string> DisguiseOptions() { List<string> list = new List<string> { "status", "help", "enable", "disable", "level", "refresh", "view", "logs", "armor", "weapons", "offlist", "fly", "offset", "rotation", "setFakePosition", "perf", "list", "favorite", "gui" }; try { string value = DisguiseConfig.ConfigCreature.Value; if (!string.IsNullOrEmpty(value) && !list.Contains(value)) { list.Insert(1, value); } } catch { } return list; } private static void OnDisguiseCommand(ConsoleEventArgs args) { string[] array = new string[args.Length]; for (int i = 0; i < args.Length; i++) { array[i] = args[i]; } Execute(args.Context, array); } internal static void Run(string sub) { GuiStatus.Clear(); Execute(null, ("disguise " + sub).Split(new char[1] { ' ' })); } private static void Execute(Terminal? ctx, string[] words) { switch ((words.Length > 1) ? words[1].ToLowerInvariant() : "status") { case "enable": DisguiseConfig.ConfigEnabled.Value = true; ApplyNow(ctx, "enable"); Say(ctx, "[Disguise] Disguise enabled."); break; case "disable": DisguiseConfig.ConfigEnabled.Value = false; ApplyNow(ctx, "disable"); Say(ctx, "[Disguise] Disabled: your player looks normal again for everyone."); Say(ctx, "Keep ProtectSaveFile=true (default) or restart the server once you are undisguised."); Say(ctx, "Other players re-create your object automatically (ForceObserverRefresh); if one still shows the old look, run \"disguise refresh\"."); break; case "refresh": RefreshObservers(ctx); break; case "view": SetOwnView(ctx, (words.Length > 2) ? words[2] : null); break; case "logs": SetLogs(ctx, (words.Length > 2) ? words[2] : null); break; case "armor": SetArmor(ctx, (words.Length > 2) ? words[2] : null); break; case "weapons": SetWeapons(ctx, (words.Length > 2) ? words[2] : null); break; case "offlist": SetOffList(ctx, (words.Length > 2) ? words[2] : null); break; case "fly": SetFly(ctx, (words.Length > 2) ? words[2] : null); break; case "offset": SetOffset(ctx, words); break; case "rotation": SetRotation(ctx, words); break; case "gui": SetGui(ctx, (words.Length > 2) ? words[2] : null); break; case "setfakeposition": SetFakePosition(ctx, words); break; case "perf": { string text = FrameMeter.Report(); Say(ctx, (text != null) ? ("[Disguise] Frame time: " + text + " - window reset.") : "[Disguise] No frames measured yet."); break; } case "set": { if (words.Length < 3) { Say(ctx, "Usage: disguise set <prefab_name> [level 1-9] (disguise list for the names)"); break; } int level2 = 1; if (words.Length > 3 && !TryParseLevel(words[3], out level2)) { Say(ctx, NotALevel(words[3])); break; } DisguiseConfig.ConfigCreatureLevel.Value = level2; bool num = ApplyCreature(words[2], "set"); Say(ctx, $"[Disguise] Prefab set to \"{DisguiseConfig.ConfigCreature.Value}\" at level {DisguiseConfig.ConfigCreatureLevel.Value} (resolved prefab: {PrefabResolver.PrefabName(PrefabResolver.CreaturePrefabHash)})."); if (!num) { Say(ctx, "[Disguise] No local player (main menu): the change will be applied on the next spawn."); } if (PrefabResolver.CreaturePrefabHash == 0) { Say(ctx, "Warning: this prefab does not exist here (see disguise list)."); } break; } case "level": { int level; if (words.Length < 3) { Say(ctx, $"[Disguise] Current level: {DisguiseConfig.ConfigCreatureLevel.Value}."); Say(ctx, "Usage: disguise level <1-9> (2 and 3 are the starred creatures)"); } else if (!TryParseLevel(words[2], out level)) { Say(ctx, NotALevel(words[2])); } else { DisguiseConfig.ConfigCreatureLevel.Value = level; Say(ctx, $"[Disguise] Level set to {DisguiseConfig.ConfigCreatureLevel.Value}: the costume is drawn as a creature of that level - another skin at 2 and 3 where the prefab has one, a star, and the health of one."); ApplyNow(ctx, "level"); } break; } case "list": ListPrefabs(ctx, (words.Length > 2) ? words[2] : null); break; case "favorite": ToggleFavorite(ctx, RestOf(words, 2)); break; case "help": case "?": PrintHelp(ctx); break; case "status": PrintStatus(ctx); break; default: Say(ctx, "[Disguise] Unknown subcommand \"" + words[1] + "\"."); PrintHelp(ctx); break; } } private static void PrintHelp(Terminal? ctx) { Say(ctx, "[Disguise] Available commands:"); Say(ctx, " disguise status current state (prefab, marker, save protection)"); Say(ctx, " disguise list [filter] every vanilla prefab you can become (the window too)"); Say(ctx, " disguise favorite [name] mark / unmark a favorite (the window's Favorite"); Say(ctx, " button does this, on the selected prefab)"); Say(ctx, " disguise set <prefab> change the prefab and enable the disguise"); Say(ctx, " ([level 1-9] with it, and 1 when it is left out;"); Say(ctx, " a row of the window does the same, at your level)"); Say(ctx, " disguise level <1-9> the level the costume is drawn at: 2 and 3 are the"); Say(ctx, " starred creatures (the window's -/+ row does this)"); Say(ctx, " disguise enable enable the disguise"); Say(ctx, " disguise disable disable it: you become a normal viking again"); Say(ctx, " disguise refresh force other players to re-create your object now"); Say(ctx, " disguise view [on|off] show the creature on your own screen (local only)"); Say(ctx, " disguise logs [on|off] write the mod's own Info messages to the BepInEx log"); Say(ctx, " disguise armor [on|off] draw the costume with the armor a real creature of"); Say(ctx, " that prefab is born with (on by default)"); Say(ctx, " disguise weapons [on|off|creature|player|<weapon>]"); Say(ctx, " draw the costume with weapons (on by default) and,"); Say(ctx, " while it does, whose they are: the one it is born"); Say(ctx, " with (the default), yours, or ONE weapon of its own"); Say(ctx, " pool holding both hands (<weapon> is its name -"); Say(ctx, " \"disguise weapons\" alone lists them)"); Say(ctx, " disguise offlist [on|off] leave YOUR line out of the player list the other"); Say(ctx, " games receive: their F2 (Players), the session"); Say(ctx, " list of the map menu, their map pins - needs the"); Say(ctx, " DLL on the server (off by default: you are listed)"); Say(ctx, " disguise fly [on|off] fly: the creature is raised for the others (visual only)"); Say(ctx, " disguise offset <X Y Z> move the costume (world units, 1 = 1 metre, visual only)"); Say(ctx, " disguise rotation <X Y Z> turn the costume in place (degrees, visual only)"); Say(ctx, " disguise gui [on|off] open / close the window (same as the GuiKey chord)"); Say(ctx, " disguise setFakePosition fake the position the others see on their map ([x z] | off)"); Say(ctx, " disguise perf frame time of the last few seconds (resets it)"); Say(ctx, " disguise help show this help"); Say(ctx, " Config file: BepInEx\\config\\orax.Disguise.cfg"); Say(ctx, " Install on the dedicated server AND on your PC only, never for other players."); } internal static bool ApplyCreature(string prefabName, string reason) { DisguiseConfig.ConfigCreature.Value = prefabName; DisguiseConfig.ConfigEnabled.Value = true; return ApplyNowCore(reason); } private static bool ApplyNowCore(string reason) { ZDO localPlayerZDO = GameAccess.GetLocalPlayerZDO(); if (localPlayerZDO == null || !localPlayerZDO.IsValid()) { return false; } DisguiseApplier.ApplyTo(localPlayerZDO, reason); return true; } private static void ApplyNow(Terminal? ctx, string reason) { if (!ApplyNowCore(reason)) { Say(ctx, "[Disguise] No local player (main menu): the change will be applied on the next spawn."); } } private static void RefreshObservers(Terminal? ctx) { ZDO localPlayerZDO = GameAccess.GetLocalPlayerZDO(); if (localPlayerZDO == null || !localPlayerZDO.IsValid()) { Say(ctx, "[Disguise] No local player (main menu): nothing to refresh."); return; } DisguiseApplier.ApplyTo(localPlayerZDO, "refresh"); ObserverRefresh.StartObserverRefresh(localPlayerZDO, force: true); Say(ctx, "[Disguise] Observer refresh requested: other players re-create your object " + $"in ~{Mathf.Clamp(DisguiseConfig.ConfigRefreshHold.Value, 0.2f, 3f):0.0} s (this mod is required on the dedicated server)."); if (!DisguiseConfig.ConfigForceRefresh.Value) { Say(ctx, "Note: ForceObserverRefresh is false, so this refresh was forced manually."); } } private static void SetOwnView(Terminal? ctx, string? argument) { bool value = DisguiseConfig.ConfigSeeYourDisguise.Value; if (argument == null) { value = !value; } else if (!TryParseOnOff(argument, out value)) { Say(ctx, "Usage: disguise view [on|off]"); return; } DisguiseConfig.ConfigSeeYourDisguise.Value = value; if (value) { Say(ctx, "[Disguise] Own view ON: your screen shows the creature in your place, exactly like the other clients see you (your own view only)."); if (PrefabResolver.CreaturePrefabHash == 0) { Say(ctx, "Warning: the configured prefab does not exist here (see disguise list)."); } } else { Say(ctx, "[Disguise] Own view OFF: your screen shows your viking again."); } } private static void SetLogs(Terminal? ctx, string? argument) { bool value = DisguiseConfig.ConfigLogs.Value; if (argument == null) { value = !value; } else if (!TryParseOnOff(argument, out value)) { Say(ctx, "Usage: disguise logs [on|off]"); return; } DisguiseConfig.ConfigLogs.Value = value; Say(ctx, value ? "[Disguise] Logs ON: the mod writes its Info messages to the BepInEx log again." : "[Disguise] Logs OFF: no more Info messages from the mod (warnings and errors are still written)."); } private static void SetArmor(Terminal? ctx, string? argument) { bool value = DisguiseConfig.ConfigShowArmor.Value; if (argument == null) { value = !value; } else if (!TryParseOnOff(argument, out value)) { Say(ctx, "Usage: disguise armor [on|off]"); return; } DisguiseConfig.ConfigShowArmor.Value = value; Say(ctx, value ? "[Disguise] Armor ON: a humanoid costume is drawn with the armor it is born with (this machine and every packet leaving it)." : "[Disguise] Armor OFF: a humanoid costume is drawn without it - no helmet, no chest, no legs, no cape - while its hands keep their own half (disguise weapons)."); } private static void SetOffList(Terminal? ctx, string? argument) { bool value = DisguiseConfig.ConfigHideFromPlayerList.Value; if (argument == null) { value = !value; } else if (!TryParseOnOff(argument, out value)) { Say(ctx, "Usage: disguise offlist [on|off]"); return; } DisguiseConfig.ConfigHideFromPlayerList.Value = value; Say(ctx, value ? "[Disguise] Off the player list: the server leaves your entry out of the list the other games receive (their F2 overlay, the session list of the map menu, their map pins) - published on your player ZDO, on the next list the server writes." : "[Disguise] On the player list again: the other games receive your entry with everyone's, at the next list the server writes."); } private static void SetWeapons(Terminal? ctx, string? argument) { if (argument == null) { DisguiseConfig.ReconcileWhoseWeapons(); string text = DisguiseConfig.FixedWeapon(); string text2 = ((!DisguiseConfig.ConfigShowWeapons.Value) ? "hidden" : (DisguiseConfig.PlayerWeapons() ? "yours" : ((text != null) ? ("its own, " + text) : "its own"))); Say(ctx, "[Disguise] Weapons of the costume: " + text2 + "."); SayWeapons(ctx); Say(ctx, "Usage: disguise weapons [on|off|creature|player|<weapon>]"); return; } if (TryParseOnOff(argument, out var value)) { DisguiseConfig.ConfigShowWeapons.Value = value; Say(ctx, value ? "[Disguise] Weapons ON: a humanoid costume is drawn with weapons again - WhoseWeapons says which (disguise weapons <creature|player|weapon>)." : "[Disguise] Weapons OFF: a humanoid costume is drawn with EMPTY hands, and your own held items stay hidden from the machines running no DLL of this mod as well."); return; } if (string.Equals(argument, "Creature", StringComparison.OrdinalIgnoreCase)) { DisguiseConfig.ConfigWhoseWeapons.Value = "Creature"; Say(ctx, "[Disguise] Whose weapons = creature: a humanoid costume is held with the weapon it is born with, and yours stay hidden from the machines running no DLL of this mod."); } else if (string.Equals(argument, "Player", StringComparison.OrdinalIgnoreCase)) { DisguiseConfig.ConfigWhoseWeapons.Value = "Player"; Say(ctx, "[Disguise] Whose weapons = player: the costume is held with the ones you carry - your own items are what every machine draws on it now."); } else { string text3 = GearSkin.WeaponName(PrefabResolver.CreaturePrefab(), argument); if (text3 == null) { Say(ctx, "[Disguise] " + PrefabResolver.PrefabName(PrefabResolver.CreaturePrefabHash) + " cannot hold \"" + argument + "\"."); SayWeapons(ctx); Say(ctx, "Usage: disguise weapons [on|off|creature|player|<weapon>]"); return; } DisguiseConfig.ConfigWhoseWeapons.Value = text3; Say(ctx, "[Disguise] Whose weapons = " + text3 + ": both hands of the costume hold that item alone - the other one empty - and yours stay hidden from the machines running no DLL of this mod."); } if (!DisguiseConfig.ConfigShowWeapons.Value) { Say(ctx, "Note: it draws no weapon at all right now (disguise weapons on)."); } } private static void SayWeapons(Terminal? ctx) { IReadOnlyList<string> readOnlyList = GearSkin.Weapons(PrefabResolver.CreaturePrefab()); string text = PrefabResolver.PrefabName(PrefabResolver.CreaturePrefabHash); if (readOnlyList.Count == 0) { Say(ctx, " " + text + " can hold no weapon at all."); return; } string text2 = DisguiseConfig.FixedWeapon() ?? "Creature"; Say(ctx, " Weapons " + text + " can hold: " + string.Join(", ", readOnlyList)); Say(ctx, " Picked now: " + text2 + "."); } private static void SetFly(Terminal? ctx, string? argument) { bool value = FlightControl.IsFlying; if (argument == null) { value = !value; } else if (!TryParseOnOff(argument, out value)) { Say(ctx, "Usage: disguise fly [on|off]"); return; } if (value) { FlightControl.Enable(); } else { FlightControl.Disable(); } if (FlightControl.IsFlying) { Say(ctx, $"[Disguise] Flight ON: the creature is drawn +{FlightControl.Height:0.0} m above you, " + "your own player stays on the ground."); if ((Object)(object)ZNet.instance != (Object)null && !ZNet.instance.IsServer()) { Say(ctx, "Note: the others see it only while the dedicated server runs this DLL (section 1.b)."); } } else { Say(ctx, "[Disguise] Flight OFF: the creature is back on the ground."); } } private static void SetOffset(Terminal? ctx, string[] words) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0081: 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) float x; float y; float z; if (words.Length < 5) { Vector3 offset = PoseControl.Offset; Say(ctx, $"[Disguise] Offset: ({offset.x:0.0}, {offset.y:0.0}, {offset.z:0.0})"); Say(ctx, "Usage: disguise offset <X> <Y> <Z> (world units, 1 = 1 metre; 0 0 0 puts it back)"); } else if (TryReadPose(ctx, words, "Usage: disguise offset <X> <Y> <Z> (world units, 1 = 1 metre; 0 0 0 puts it back)", out x, out y, out z)) { PoseControl.SetOffset(x, y, z); Vector3 offset2 = PoseControl.Offset; Say(ctx, $"[Disguise] Offset set: ({offset2.x:0.0}, {offset2.y:0.0}, {offset2.z:0.0}) - the costume is drawn that far from you."); Say(ctx, "Your player never moves: only the position the others are handed does (needs this DLL on the dedicated server)."); } } private static void SetRotation(Terminal? ctx, string[] words) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0081: 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) float x; float y; float z; if (words.Length < 5) { Vector3 euler = PoseControl.Euler; Say(ctx, $"[Disguise] Rotation: ({euler.x:0.0}, {euler.y:0.0}, {euler.z:0.0})"); Say(ctx, "Usage: disguise rotation <X> <Y> <Z> (degrees; 0 0 0 faces it the way it normally does)"); } else if (TryReadPose(ctx, words, "Usage: disguise rotation <X> <Y> <Z> (degrees; 0 0 0 faces it the way it normally does)", out x, out y, out z)) { PoseControl.SetRotation(x, y, z); Vector3 euler2 = PoseControl.Euler; Say(ctx, $"[Disguise] Rotation set: ({euler2.x:0.0}, {euler2.y:0.0}, {euler2.z:0.0}) - the costume is drawn that way."); Say(ctx, "Your player never turns: only what the others are handed does (needs this DLL on the dedicated server)."); } } private static bool TryReadPose(Terminal? ctx, string[] words, string usage, out float x, out float y, out float z) { x = 0f; y = 0f; z = 0f; if (!TryParseCoordinate(words[2], out x) || !TryParseCoordinate(words[3], out y) || !TryParseCoordinate(words[4], out z)) { Say(ctx, "[Disguise] The three values must be numbers (e.g. 0 1.5 -45)."); Say(ctx, usage); return false; } return true; } private static void SetGui(Terminal? ctx, string? argument) { bool value = DisguiseGui.IsOpen; if (argument == null) { value = !value; } else if (!TryParseOnOff(argument, out value)) { Say(ctx, "Usage: disguise gui [on|off]"); return; } if (value) { string text = DisguiseGui.Show(); if (text != null) { Say(ctx, "[Disguise] Window not opened: " + text); } else { Say(ctx, "[Disguise] Window opened: drag it anywhere, filter or untick a category, click a row to pick a prefab; Close (or the key chord) dismisses it."); } } else { DisguiseGui.Hide(); Say(ctx, "[Disguise] Window closed."); } } private static bool TryParseOnOff(string argument, out bool value) { switch (argument.ToLowerInvariant()) { case "on": case "true": case "1": case "enable": value = true; return true; case "off": case "false": case "0": case "disable": value = false; return true; default: value = false; return false; } } private static void SetFakePosition(Terminal? ctx, string[] words) { //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) if (words.Length >= 3 && IsOffArgument(words[2])) { FakePosition.Clear(); PublishFakePosition(ctx); Say(ctx, "[Disguise] Fake map position removed: the other maps show your real position again."); return; } Vector3 position = default(Vector3); if (words.Length >= 4) { if (!TryParseCoordinate(words[2], out var value) || !TryParseCoordinate(words[3], out var value2)) { PrintFakePositionUsage(ctx); return; } ((Vector3)(ref position))..ctor(value, CurrentHeight(), value2); } else { if (words.Length != 2) { PrintFakePositionUsage(ctx); return; } if ((Object)(object)Player.m_localPlayer == (Object)null) { Say(ctx, "[Disguise] No local player (main menu): nothing to record, use \"disguise setFakePosition <x> <z>\" instead."); return; } position = ((Component)Player.m_localPlayer).transform.position; } FakePosition.Set(position); PublishFakePosition(ctx); Say(ctx, $"[Disguise] Fake map position: ({position.x:0.0}, {position.z:0.0}) - the other maps show you there " + "while you keep moving normally (needs this DLL on the dedicated server)."); Say(ctx, "Your character and your own screen never move: only the position the others see on their map does."); } private static void PrintFakePositionUsage(Terminal? ctx) { Say(ctx, "Usage: disguise setFakePosition record where you stand and show it to the others"); Say(ctx, " disguise setFakePosition <x> <z> use that point of the map plane (X and Z)"); Say(ctx, " disguise setFakePosition off show your real position again"); } private static bool IsOffArgument(string value) { string text = value.ToLowerInvariant(); switch (text) { default: return text == "disable"; case "off": case "clear": case "none": return true; } } internal static bool TryParseCoordinate(string text, out float value) { if (float.TryParse(text, NumberStyles.Float, CultureInfo.InvariantCulture, out value)) { return true; } return float.TryParse(text, NumberStyles.Float, CultureInfo.CurrentCulture, out value); } private static bool TryParseLevel(string text, out int level) { if (!int.TryParse(text, NumberStyles.Integer, CultureInfo.InvariantCulture, out level)) { return false; } level = Mathf.Clamp(level, 1, 9); return true; } private static string NotALevel(string text) { return $"[Disguise] \"{text}\" is not a level: a whole number from {1} to {9}."; } private static float CurrentHeight() { //IL_001d: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)Player.m_localPlayer != (Object)null)) { return 0f; } return ((Component)Player.m_localPlayer).transform.position.y; } private static void PublishFakePosition(Terminal? ctx) { ZDO localPlayerZDO = GameAccess.GetLocalPlayerZDO(); if (localPlayerZDO == null || !localPlayerZDO.IsValid()) { Say(ctx, "Note: no local player right now, it is published when you spawn."); } else { FakePosition.Publish(localPlayerZDO); } } private static void PrintStatus(Terminal? ctx) { //IL_025f: Unknown result type (might be due to invalid IL or missing references) //IL_026e: Unknown result type (might be due to invalid IL or missing references) //IL_027d: Unknown result type (might be due to invalid IL or missing references) //IL_0296: Unknown result type (might be due to invalid IL or missing references) //IL_02a5: Unknown result type (might be due to invalid IL or missing references) //IL_02b4: Unknown result type (might be due to invalid IL or missing references) //IL_02ea: Unknown result type (might be due to invalid IL or missing references) //IL_02ef: Unknown result type (might be due to invalid IL or missing references) //IL_02ff: Unknown result type (might be due to invalid IL or missing references) //IL_030b: Unknown result type (might be due to invalid IL or missing references) //IL_0348: Unknown result type (might be due to invalid IL or missing references) ZDO localPlayerZDO = GameAccess.GetLocalPlayerZDO(); bool flag = (Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer(); Say(ctx, "[Disguise] v0.1.0 - " + (DisguiseConfig.ConfigEnabled.Value ? "enabled" : "disabled") + (flag ? " (this process is a server)" : " (client)")); Say(ctx, " Configured prefab: \"" + DisguiseConfig.ConfigCreature.Value + "\" -> " + PrefabResolver.PrefabName(PrefabResolver.CreaturePrefabHash) + ((PrefabResolver.CreaturePrefabHash == 0) ? " (NOT FOUND)" : "")); Say(ctx, " Save protection: " + (DisguiseConfig.ConfigProtectSave.Value ? "yes" : "no")); Say(ctx, " Own view on your screen: " + (DisguiseConfig.ConfigSeeYourDisguise.Value ? "on" : "off") + " (disguise view)"); Say(ctx, " Item sparkle on your screen: " + (DisguiseConfig.ConfigHideItemSparkle.Value ? "hidden" : "shown") + " (HideItemSparkle, 4 - Own view)"); Say(ctx, " Armor of the costume: " + (DisguiseConfig.ConfigShowArmor.Value ? "shown" : "hidden") + " (disguise armor, 3 - Display)"); DisguiseConfig.ReconcileWhoseWeapons(); object obj; if (DisguiseConfig.ConfigShowWeapons.Value) { if (!DisguiseConfig.PlayerWeapons()) { string text = DisguiseConfig.FixedWeapon(); obj = ((text != null) ? ("fixed, " + text) : "its own"); } else { obj = "yours"; } } else { obj = "hidden"; } string text2 = (string)obj; Say(ctx, " Weapons of the costume: " + text2 + " (disguise weapons, 3 - Display)"); Say(ctx, " On the player list of the others: " + (DisguiseConfig.ConfigHideFromPlayerList.Value ? "hidden" : "shown") + " (disguise offlist, 2 - Multiplayer)"); Say(ctx, " Info logs: " + (DisguiseConfig.ConfigLogs.Value ? "on" : "off") + " (disguise logs)" + (DisguiseConfig.ConfigLogs.Value ? "" : " - warnings and errors are still written")); Say(ctx, " Flight: " + (FlightControl.IsFlying ? $"on, +{FlightControl.Height:0.0} m" : "off") + " (disguise fly, keys in [5 - Flight])"); Say(ctx, $" Pose: offset ({PoseControl.Offset.x:0.0}, {PoseControl.Offset.y:0.0}, {PoseControl.Offset.z:0.0}), " + $"rotation ({PoseControl.Euler.x:0.0}, {PoseControl.Euler.y:0.0}, {PoseControl.Euler.z:0.0}) " + "(disguise offset, disguise rotation)"); Vector3? current = FakePosition.Current; object text3; if (current.HasValue) { Vector3 valueOrDefault = current.GetValueOrDefault(); text3 = $" Fake map position: ({valueOrDefault.x:0.0}, {valueOrDefault.z:0.0}) - the other maps show you there"; } else { text3 = " Fake map position: off (the other maps show your real position)"; } Say(ctx, (string)text3); if (localPlayerZDO != null && localPlayerZDO.IsValid()) { int num = PrefabResolver.DesiredPrefabHash(); Say(ctx, string.Format(" Player ZDO {0}: current prefab = {1}, desired = {2}, marker = {3}", localPlayerZDO.m_uid, PrefabResolver.PrefabName(localPlayerZDO.GetPrefab()), PrefabResolver.PrefabName(num), DisguiseApplier.HasMarker(localPlayerZDO) ? "yes" : "no")); Say(ctx, (localPlayerZDO.GetPrefab() == num) ? " State: OK, your disguise is applied." : (" State: PENDING (automatic re-application in " + Mathf.Max(0.25f, DisguiseConfig.ConfigReassert.Value).ToString("0.0") + " s)")); } else { Say(ctx, " No local player right now."); } Say(ctx, " Install this DLL on the dedicated server AND on your PC, never on other players."); } private static void ToggleFavorite(Terminal? ctx, string name) { string text = ((name.Length > 0) ? name : (DisguiseConfig.ConfigCreature.Value ?? string.Empty)); if (text.Length == 0) { Say(ctx, "[Disguise] Nothing to mark: no prefab is configured."); return; } bool flag = PrefabFavorites.Toggle(text); Say(ctx, flag ? ("[Disguise] \"" + text + "\" added to favorites.") : ("[Disguise] \"" + text + "\" taken off the favorites.")); } private static string RestOf(string[] words, int from) { if (words.Length <= from) { return string.Empty; } return string.Join(" ", words, from, words.Length - from); } private static void ListPrefabs(Terminal? ctx, string? filter) { if ((Object)(object)ZNetScene.instance == (Object)null) { Say(ctx, "[Disguise] ZNetScene unavailable (main menu): the list is available in game."); Disguise.LogInfo("ZNetScene unavailable (main menu): the list is available in game."); return; } List<string> list = PrefabCatalog.Names(filter, null); string text = $"{list.Count} prefab(s)" + ((filter != null) ? (" containing \"" + filter + "\"") : "") + ":"; Say(ctx, "[Disguise] " + text); Disguise.LogInfo(text); int[] array = PrefabCatalog.Counts(filter); string text2 = string.Empty; for (int i = 0; i < array.Length; i++) { text2 = text2 + ((i > 0) ? ", " : string.Empty) + PrefabCatalog.Label((PrefabKind)i) + " " + array[i]; } SayLogged(ctx, " kinds: " + text2); for (int j = 0; j < list.Count; j += 4) { int count = Math.Min(4, list.Count - j); SayLogged(ctx, " " + string.Join(", ", list.GetRange(j, count))); } } private static void SayLogged(Terminal? ctx, string text) { Say(ctx, text); Disguise.LogInfo(text); } private static void Say(Terminal? ctx, string text) { if ((Object)(object)ctx == (Object)null) { GuiStatus.Show(text); return; } try { ctx.AddString(text); } catch (Exception ex) { Disguise.LogWarning(text + " (console display error: " + ex.Message + ")"); } } } internal static class DisguiseConfig { internal const int LevelMin = 1; internal const int LevelMax = 9; internal static ConfigEntry<bool> ConfigEnabled; internal static ConfigEntry<string> ConfigCreature; internal static ConfigEntry<int> ConfigCreatureLevel; internal static ConfigEntry<bool> ConfigProtectSave; internal static ConfigEntry<float> ConfigReassert; internal static ConfigEntry<bool> ConfigLogs; internal static ConfigEntry<bool> ConfigForceRefresh; internal static ConfigEntry<float> ConfigRefreshHold; internal static ConfigEntry<bool> ConfigHideFromPlayerList; internal static ConfigEntry<bool> ConfigShowAsAlly; internal static ConfigEntry<bool> ConfigShowArmor; internal static ConfigEntry<bool> ConfigShowWeapons; internal static ConfigEntry<string> ConfigWhoseWeapons; internal const string WeaponsCreature = "Creature"; internal const string WeaponsPlayer = "Player"; internal static ConfigEntry<bool> ConfigSeeYourDisguise; internal static ConfigEntry<bool> ConfigHideItemSparkle; internal static ConfigEntry<KeyboardShortcut> ConfigFlyEnableKey; internal static ConfigEntry<KeyboardShortcut> ConfigFlyDisableKey; internal static ConfigEntry<KeyboardShortcut> ConfigFlyUpKey; internal static ConfigEntry<KeyboardShortcut> ConfigFlyDownKey; internal static ConfigEntry<float> ConfigFlySpeed; internal static ConfigEntry<bool> ConfigFlyScrollWheel; internal static ConfigEntry<float> ConfigFlyScrollStep; internal static ConfigEntry<KeyboardShortcut> ConfigGuiKey; internal static ConfigEntry<int> ConfigGuiScrollStep; internal static ConfigEntry<bool> ConfigShowMenuEntry; internal static ConfigEntry<float> ConfigWindowX; internal static ConfigEntry<float> ConfigWindowY; internal static ConfigEntry<bool> ConfigShowStatic; internal static ConfigEntry<bool> ConfigShowCreatures; internal static ConfigEntry<bool> ConfigShowItems; internal static ConfigEntry<bool> ConfigShowBuildings; internal static ConfigEntry<bool> ConfigShowNature; internal static ConfigEntry<bool> ConfigShowOther; internal static ConfigEntry<bool> ConfigShowFavorites; internal static ConfigEntry<string> ConfigFavorites; internal static Vector2 WindowPlace => new Vector2(ConfigWindowX.Value, ConfigWindowY.Value); internal static bool PlayerWeapons() { return string.Equals(ConfigWhoseWeapons.Value, "Player", StringComparison.OrdinalIgnoreCase); } internal static string? FixedWeapon() { string text = ConfigWhoseWeapons.Value.Trim(); if (text.Length == 0) { return null; } if (string.Equals(text, "Creature", StringComparison.OrdinalIgnoreCase) || string.Equals(text, "Player", StringComparison.OrdinalIgnoreCase)) { return null; } return text; } internal static void ReconcileWhoseWeapons() { string text = FixedWeapon(); if (text != null) { GameObject val = PrefabResolver.CreaturePrefab(); if ((Object)(object)val != (Object)null && GearSkin.WeaponHash(val, text) == 0) { ConfigWhoseWeapons.Value = "Creature"; } } } internal static void Bind(ConfigFile config) { BindGeneral(config); BindMultiplayer(config); BindDisplay(config); BindOwnView(config); BindFlight(config); BindGui(config); } private static void BindGeneral(ConfigFile config) { //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Expected O, but got Unknown ConfigEnabled = config.Bind<bool>("1 - General", "Enabled", true, "Disguise enabled. To become a visible viking again: \"disguise disable\" in the console (F5), then restart the server. NEVER delete the DLL without disabling this option first (see ProtectSaveFile)."); ConfigCreature = config.Bind<string>("1 - General", "CreaturePrefab", "Greydwarf", "Exact name of the creature prefab (e.g. Troll, Draugr, Wolf, Greydwarf, Blob, Skeleton, Neck, Boar, Eikthyr...). Console command: disguise list to see the available names."); ConfigCreature.SettingChanged += delegate { PrefabResolver.InvalidateCreatureHash(); ReconcileWhoseWeapons(); }; ConfigCreatureLevel = config.Bind<int>("1 - General", "CreatureLevel", 1, new ConfigDescription("The level you appear as: 1 is the plain creature, 2 and 3 the starred ones - another skin where the prefab has one, a star over the costume, and the health of a real creature of that level (Character multiplies it by the level). Beyond the skins a prefab carries, only that health keeps growing. Kept inside 1 to 9, the span the vanilla spawn command gives a creature. Console: disguise level <1-9>, or disguise set <name> [level].", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 9), Array.Empty<object>())); ConfigProtectSave = config.Bind<bool>("1 - General", "ProtectSaveFile", true, "Writes \"Player\" (not the creature prefab) to the world save. The disguise is re-applied automatically on load. Keeps the world usable if the mod is removed."); ConfigReassert = config.Bind<float>("1 - General", "ReassertSeconds", 2f, "Period (seconds) between checks / re-application of the disguise on your own player."); ConfigLogs = config.Bind<bool>("1 - General", "Logs", false, "Write the mod's own informational messages (Info / Debug) to the BepInEx log (BepInEx\\LogOutput.log). Off by default: a working install keeps its log quiet, and \"disguise logs on\" switches these lines on - the command writes the value back, so it stays for the next start. Warnings and errors are always written, whatever this value is: they are what tells a broken install from a working one. Console command: disguise logs [on|off]."); } private static void BindMultiplayer(ConfigFile config) { ConfigForceRefresh = config.Bind<bool>("2 - Multiplayer", "ForceObserverRefresh", true, "Automatically makes other players re-create your object right after a disguise change, so vanilla clients that already saw you switch immediately (requires this DLL on the dedicated server). Without it, they only refresh when you leave and re-enter their zone (~100 m) or after a reconnection."); ConfigRefreshHold = config.Bind<float>("2 - Multiplayer", "RefreshHoldSeconds", 1.5f, "How long (seconds) the server relays your player ZDO with a fake far-away position during an observer refresh. The change becomes visible to other players after roughly this delay. Increase it (e.g. 2.5) on a laggy connection if clients do not refresh."); ConfigHideFromPlayerList = config.Bind<bool>("2 - Multiplayer", "HideFromPlayerList", false, "Leave YOUR entry out of the player list the server broadcasts: every other game then shows you nowhere on that list - the \"Players\" line and count of its F2 overlay, the session player list of its map menu, the pins of its map. The choice travels in your player ZDO (cd_offlist), exactly like the fake map position: only the machine playing can decide, and a server running no DLL of this mod has no patch to leave anything out - while vanilla clients need none of it, they only ever show what the server sends them. The server writes one list for every joining game: while this is on it holds every name but yours, your own F2 too while you play as a client (the host's own F2 reads the server's list, whole again after every write). Nothing of the game itself moves - the server keeps the full list for its own logic, its console and its save. Off, the default, is the plain list. Window: the 'Hide from player list' box under the command buttons. Console command: disguise offlist [on|off]."); ConfigHideFromPlayerList.SettingChanged += delegate { OffList.Publish(GameAccess.GetLocalPlayerZDO()); }; } private static void BindDisplay(ConfigFile config) { //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Expected O, but got Unknown ConfigShowArmor = config.Bind<bool>("3 - Display", "ShowArmor", true, "Draw the costume with the ARMOR a real creature of that prefab is born with - the gear Humanoid.Start gives a creature at spawn (its default items and one pick of each random pool). On, the default, is how a Fuling in the world stands: armor and all. Off draws the costume without it (the weapon is ShowWeapons' half, and the two are read together). It reaches every screen drawing that costume: your own here, the window's picture, and the machines running no DLL of this mod - your ZDO publishes that gear itself while you are disguised, rolled the way the game rolls it so every machine agrees on the same pick. The machines running this DLL are never affected: they keep drawing YOU, with your own equipment. Window: the 'Show armor' box under the command buttons. Console command: disguise armor [on|off]."); string text = LegacyWhoseWeapons(config) ?? "Creature"; ConfigShowWeapons = config.Bind<bool>("3 - Display", "ShowWeapons", true, "Draw the costume with weapons at all. On, the default, is how a Fuling in the world stands - spear and all: its hands hold what WhoseWeapons picks. Off draws it with EMPTY hands, like a creature that spawns unarmed, and your own held items stay hidden from the machines running no DLL of this mod then as well (there would be nothing to glue them to). Like ShowArmor it reaches every screen drawing that costume: your own here, the window's picture, and the machines running no DLL - your ZDO publishes it itself while you are disguised. The machines running this DLL are never affected: they keep drawing YOU, with your own equipment. Window: the 'Show weapons' box under the command buttons. Console command: disguise weapons [on|off]."); ConfigWhoseWeapons = config.Bind<string>("3 - Display", "WhoseWeapons", text, new ConfigDescription("Whose weapons a humanoid costume is held with, while ShowWeapons is on: \"Creature\" (the default) is the one a real creature of that prefab takes into a hand at spawn (MonsterAI takes the best weapon of its inventory), which is how a Fuling in the world stands - spear and all; \"Player\" is the ones YOU hold in both hands, an empty hand drawing nothing; ANY OTHER VALUE is the name of one item out of that creature's own pool (its default items and every random pool), and the hands are then that item ALONE - the other one empty, whatever the roll answers. The window's list holds them all, and a name the configured creature cannot hold is put back to \"Creature\" the moment another creature is configured. It reaches every screen drawing that costume, and it decides what YOUR hands carry there too: hidden from the machines running no DLL of this mod while the costume wears its own, exactly what it shows while it wears yours. Read only while ShowWeapons is on - a costume drawn with empty hands has no weapon for anyone to own. Window: the list at the right of the 'Show weapons' box. Console command: disguise weapons <creature|player|weapon>.", (AcceptableValueBase)null, Array.Empty<object>())); ConfigShowAsAlly = config.Bind<bool>("3 - Display", "ShowAsAlly", false, "Show the creature to the other machines as an ALLY (green health bar) instead of an enemy (red). Only the display changes: EnemyHud asks BaseAI whether your ZDO is an enemy, and this answers no. Their game rules, your own view and your equipment stay exactly as they are."); ConfigShowArmor.SettingChanged += DisplaySettingChanged; ConfigShowWeapons.SettingChanged += DisplaySettingChanged; ConfigWhoseWeapons.SettingChanged += DisplaySettingChanged; ConfigShowAsAlly.SettingChanged += delegate { AllyView.OnSettingChanged(); }; } private static string? LegacyWhoseWeapons(ConfigFile config) { try { string[] array = File.ReadAllLines(config.ConfigFilePath); for (int i = 0; i < array.Length; i++) { string text = array[i].Trim(); if (text.Length == 0 || text[0] == '#' || !text.StartsWith("ShowWeapons", StringComparison.Ordinal)) { continue; } int num = text.IndexOf('='); if (num >= 0) { string a = text.Substring(num + 1).Trim().Trim(new char[1] { '"' }); if (string.Equals(a, "Player", StringComparison.OrdinalIgnoreCase)) { return "Player"; } if (string.Equals(a, "Creature", StringComparison.OrdinalIgnoreCase)) { return "Creature"; } } } } catch (Exception) { } return null; } private static void BindOwnView(ConfigFile config) { ConfigSeeYourDisguise = config.Bind<bool>("4 - Own view", "SeeYourDisguise", true, "Show your disguise on your own screen: your viking is hidden and the creature is drawn in your place, exactly like the other clients see you. Local visuals only - it never changes what the other machines receive. Console command: disg