diff --git a/src/Config/ConfigManager.cs b/src/Config/ConfigManager.cs index 912d25d..7261083 100644 --- a/src/Config/ConfigManager.cs +++ b/src/Config/ConfigManager.cs @@ -55,6 +55,7 @@ namespace UnityExplorer.Config public static ConfigElement Increase_FOV; public static ConfigElement Decrease_FOV; public static ConfigElement Reset_FOV; + public static ConfigElement Toggle_Animations; // internal configs internal static InternalConfigHandler InternalHandler { get; private set; } @@ -279,6 +280,10 @@ namespace UnityExplorer.Config Reset_FOV = new("Reset FOV", "Resets the field of view of the current freecam to the original one.", KeyCode.KeypadMultiply); + + Toggle_Animations = new("Toggle NPC animations", + "Toggle NPC animations as selected in the Animator panel.", + KeyCode.Keypad0); } } } diff --git a/src/ExplorerBehaviour.cs b/src/ExplorerBehaviour.cs index b2b542d..c294a7e 100644 --- a/src/ExplorerBehaviour.cs +++ b/src/ExplorerBehaviour.cs @@ -139,6 +139,11 @@ namespace UnityExplorer FreeCamPanel.blockFreecamMovementToggle.isOn = !FreeCamPanel.blockFreecamMovementToggle.isOn; } + if (IInputManager.GetKeyDown(ConfigManager.Toggle_Animations.Value)) + { + UIManager.GetPanel(UIManager.Panels.AnimatorPanel).HotkeyToggleAnimators(); + } + if (FreeCamPanel.supportedInput && IInputManager.GetKeyDown(ConfigManager.Toggle_Block_Games_Input.Value)) { FreeCamPanel.blockGamesInputOnFreecamToggle.isOn = !FreeCamPanel.blockGamesInputOnFreecamToggle.isOn; diff --git a/src/Loader/Standalone/Editor/ExplorerEditorBehaviour.cs b/src/Loader/Standalone/Editor/ExplorerEditorBehaviour.cs index 1888191..1c47ca0 100644 --- a/src/Loader/Standalone/Editor/ExplorerEditorBehaviour.cs +++ b/src/Loader/Standalone/Editor/ExplorerEditorBehaviour.cs @@ -52,6 +52,7 @@ namespace UnityExplorer.Loader.Standalone public KeyCode Increase_FOV; public KeyCode Decrease_FOV; public KeyCode Reset_FOV; + public KeyCode Toggle_Animations; internal void Awake() { @@ -105,6 +106,7 @@ namespace UnityExplorer.Loader.Standalone ConfigManager.Increase_FOV.Value = this.Increase_FOV; ConfigManager.Decrease_FOV.Value = this.Decrease_FOV; ConfigManager.Reset_FOV.Value = this.Reset_FOV; + ConfigManager.Toggle_Animations.Value = this.Toggle_Animations; } } } diff --git a/src/UI/Panels/AnimatorPanel.cs b/src/UI/Panels/AnimatorPanel.cs new file mode 100644 index 0000000..9ee9590 --- /dev/null +++ b/src/UI/Panels/AnimatorPanel.cs @@ -0,0 +1,162 @@ +using UnityExplorer.CacheObject; +using UnityExplorer.ObjectExplorer; +using UnityExplorer.Inspectors; +using UniverseLib.UI; +using UniverseLib.UI.Models; +using UniverseLib.UI.ObjectPool; +using UnityEngine; +#if UNHOLLOWER +using UnhollowerRuntimeLib; +#endif +#if INTEROP +using Il2CppInterop.Runtime.Injection; +#endif + +namespace UnityExplorer.UI.Panels +{ + internal class AnimatorPanel : UEPanel + { + public AnimatorPanel(UIBase owner) : base(owner) + { + } + + public override string Name => "Animator"; + public override UIManager.Panels PanelType => UIManager.Panels.AnimatorPanel; + public override int MinWidth => 500; + public override int MinHeight => 500; + public override Vector2 DefaultAnchorMin => new(0.4f, 0.4f); + public override Vector2 DefaultAnchorMax => new(0.6f, 0.6f); + public override bool NavButtonWanted => true; + public override bool ShouldSaveActiveState => true; + List UIElements = new List(); + + internal Dictionary> animators = new Dictionary>(); + internal Dictionary> animatorToggles = new Dictionary>(); + internal Dictionary ignoresMasterToggler = new Dictionary(); + Toggle masterAnimatorToggle = new Toggle(); + + private void FindAllAnimators(){ + // Enable all animators on refresh + if (animators.Count != 0) { + masterAnimatorToggle.isOn = true; // Will also trigger "MasterToggleAnimators(true)" + animators.Clear(); + animatorToggles.Clear(); + ignoresMasterToggler.Clear(); + } + + List animatorsObjects = SearchProvider.UnityObjectSearch("", "UnityEngine.Animator", ChildFilter.Any, SceneFilter.ActivelyLoaded); + List animatorsList = new List(animatorsObjects.Select(obj => obj.TryCast()) + .Where(a => a.GetComponentsInChildren(false).Length != 0 && a.enabled && a.GetComponentsInChildren(false).Length != 0)); + + foreach(Behaviour animator in animatorsList){ + GameObject gObj = animator.gameObject; + string key = getNameWithoutClone(gObj.name); + + if (!animators.ContainsKey(key)){ + animators.Add(key, new List()); + } + + animators[key].Add(animator); + } + + BuildAnimatorsTogglers(); + } + + private string getNameWithoutClone(string name){ + string newName = name; + while (newName.EndsWith("(Clone)")){ + newName = newName.Substring(0, newName.Length - 7); + } + + return newName; + } + + private void BuildAnimatorsTogglers(){ + foreach (var comp in UIElements){ + UnityEngine.Object.Destroy(comp); + //UIElements.Remove(comp); + } + + foreach (KeyValuePair> entry in animators) + { + // A weird canse insensitive "Contains" to identify the player + ignoresMasterToggler[entry.Key] = entry.Key.IndexOf("play", 0, StringComparison.OrdinalIgnoreCase) >= 0; + + GameObject horiGroup = UIFactory.CreateHorizontalGroup(ContentRoot, $"{entry.Key}_Group", false, false, true, false, 4, + default, new Color(1, 1, 1, 0), TextAnchor.MiddleLeft); + UIFactory.SetLayoutElement(horiGroup, minHeight: 25, flexibleWidth: 9999); + UIElements.Add(horiGroup); + + // is Affected by Master Toggler toggle + Toggle ignoresMasterTogglerToggle; + GameObject ignoresMasterTogglerObj = UIFactory.CreateToggle(horiGroup, $"Toggle {entry.Key}", out ignoresMasterTogglerToggle, out Text ignoresMasterTogglerText); + UIFactory.SetLayoutElement(ignoresMasterTogglerObj, minHeight: 25); + ignoresMasterTogglerToggle.isOn = ignoresMasterToggler[entry.Key]; + ignoresMasterTogglerToggle.onValueChanged.AddListener(value => ignoresMasterToggler[entry.Key] = value); + ignoresMasterTogglerText.text = entry.Key; + + foreach (Behaviour entityAnimator in entry.Value){ + Toggle animatorToggle; + GameObject animatorObj = UIFactory.CreateToggle(horiGroup, $"Toggle {entry.Key}", out animatorToggle, out Text animatorToggleText); + + if (!animatorToggles.ContainsKey(entry.Key)){ + animatorToggles.Add(entry.Key, new List()); + } + animatorToggles[entry.Key].Add(animatorToggle); + + UIFactory.SetLayoutElement(animatorObj, minHeight: 25); + animatorToggle.isOn = true; + animatorToggle.onValueChanged.AddListener(value => entityAnimator.enabled = value); + } + + } + } + + public void MasterToggleAnimators(bool enable){ + // Load animators for the first time if there are not any + if (animators.Count == 0) FindAllAnimators(); + foreach (KeyValuePair> entry in animators) + { + if (!ignoresMasterToggler[entry.Key]){ + foreach (Toggle animatorToggle in animatorToggles[entry.Key]){ + animatorToggle.isOn = enable; + } + } + } + } + + public void HotkeyToggleAnimators(){ + masterAnimatorToggle.isOn = !masterAnimatorToggle.isOn; + } + + // ~~~~~~~~ UI construction / callbacks ~~~~~~~~ + + protected override void ConstructPanelContent() + { + GameObject firstGroup = UIFactory.CreateHorizontalGroup(ContentRoot, "MainOptions", false, false, true, true, 3, + default, new Color(1, 1, 1, 0), TextAnchor.MiddleLeft); + UIFactory.SetLayoutElement(firstGroup, minHeight: 25, flexibleWidth: 9999); + //UIElements.Add(horiGroup); + + ButtonRef updateAnimators = UIFactory.CreateButton(firstGroup, "RefreshAnimators", "Refresh Animators"); + UIFactory.SetLayoutElement(updateAnimators.GameObject, minWidth: 150, minHeight: 25); + updateAnimators.OnClick += FindAllAnimators; + + GameObject animatorObj = UIFactory.CreateToggle(firstGroup, $"Master Animation Toggle", out masterAnimatorToggle, out Text masterAnimatorText); + UIFactory.SetLayoutElement(animatorObj, minHeight: 25); + masterAnimatorToggle.isOn = true; + masterAnimatorToggle.onValueChanged.AddListener(value => MasterToggleAnimators(value)); + masterAnimatorText.text = "Master Toggler"; + + GameObject secondGroup = UIFactory.CreateHorizontalGroup(ContentRoot, "MainOptions", false, false, true, true, 3, + default, new Color(1, 1, 1, 0), TextAnchor.MiddleLeft); + UIFactory.SetLayoutElement(firstGroup, minHeight: 25, flexibleWidth: 9999); + + Text isPlayerLabel = UIFactory.CreateLabel(secondGroup, "IsAffectedByMasterToggler", "Ignores Master Toggler", TextAnchor.MiddleLeft, Color.white, false, 15); + UIFactory.SetLayoutElement(isPlayerLabel.gameObject, minHeight: 25, minWidth: 25, flexibleWidth: 0); + + Text enableLabel = UIFactory.CreateLabel(secondGroup, "EnableLabel", "Enable ", TextAnchor.MiddleRight, Color.white, false, 15); + UIFactory.SetLayoutElement(enableLabel.gameObject, minHeight: 25, minWidth: 75, flexibleWidth: 9999); + } + } +} diff --git a/src/UI/Panels/FreeCamPanel.cs b/src/UI/Panels/FreeCamPanel.cs index 7339762..17d4d24 100644 --- a/src/UI/Panels/FreeCamPanel.cs +++ b/src/UI/Panels/FreeCamPanel.cs @@ -358,7 +358,8 @@ namespace UnityExplorer.UI.Panels (supportedInput ? $"- {ConfigManager.Toggle_Block_Games_Input.Value}: Toggle games input on Freecam\n" : "") + $"- {ConfigManager.HUD_Toggle.Value}: HUD toggle\n" + $"- {ConfigManager.Pause.Value}: Pause\n" + - $"- {ConfigManager.Frameskip.Value}: Frameskip\n"; + $"- {ConfigManager.Frameskip.Value}: Frameskip\n" + + $"- {ConfigManager.Toggle_Animations.Value}: Toggle NPC animations"; if (ConfigManager.Frameskip.Value != KeyCode.None) instructions = instructions + $"- {ConfigManager.Screenshot.Value}: Screenshot\n"; diff --git a/src/UI/UIManager.cs b/src/UI/UIManager.cs index c67fa28..3dcbabf 100644 --- a/src/UI/UIManager.cs +++ b/src/UI/UIManager.cs @@ -26,6 +26,7 @@ namespace UnityExplorer.UI LightsManager, CamPaths, PostProcessingPanel, + AnimatorPanel, Misc, } @@ -48,7 +49,7 @@ namespace UnityExplorer.UI public static RectTransform NavBarRect; public static GameObject NavbarTabButtonHolder; - private static readonly Vector2 NAVBAR_DIMENSIONS = new(1500f, 35f); + private static readonly Vector2 NAVBAR_DIMENSIONS = new(1600f, 35f); private static ButtonRef closeBtn; private static TimeScaleWidget timeScaleWidget; @@ -99,6 +100,7 @@ namespace UnityExplorer.UI UIPanels.Add(Panels.LightsManager, new LightsManager(UiBase)); UIPanels.Add(Panels.CamPaths, new CamPaths(UiBase)); UIPanels.Add(Panels.PostProcessingPanel, new PostProcessingPanel(UiBase)); + UIPanels.Add(Panels.AnimatorPanel, new AnimatorPanel(UiBase)); UIPanels.Add(Panels.Misc, new UnityExplorer.UI.Panels.Misc(UiBase)); UIPanels.Add(Panels.Options, new OptionsPanel(UiBase)); UIPanels.Add(Panels.UIInspectorResults, new MouseInspectorResultsPanel(UiBase));