├── .gitignore
├── FUNDING.yml
├── Properties
└── AssemblyInfo.cs
├── README.md
├── TrackingRotator.csproj
├── TrackingRotator.sln
├── TrackingRotatorMod.cs
├── Utils
├── AMAPIManager.cs
└── UIXManager.cs
└── trackingrotator
/.gitignore:
--------------------------------------------------------------------------------
1 | .vs/
2 | bin/
3 | obj/
4 | *.user
--------------------------------------------------------------------------------
/FUNDING.yml:
--------------------------------------------------------------------------------
1 | patreon: nitrog0d
--------------------------------------------------------------------------------
/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using MelonLoader;
2 | using System.Reflection;
3 | using System.Runtime.InteropServices;
4 | using TrackingRotator;
5 |
6 | [assembly: ComVisible(false)]
7 | [assembly: Guid("652febb3-e7bd-4f5c-8ac4-51e36b19bc9d")]
8 | [assembly: AssemblyTitle(ModBuildInfo.Name)]
9 | [assembly: AssemblyProduct(ModBuildInfo.Name)]
10 | [assembly: AssemblyCopyright("Created by " + ModBuildInfo.Author)]
11 | [assembly: AssemblyVersion(ModBuildInfo.Version)]
12 | [assembly: AssemblyFileVersion(ModBuildInfo.Version)]
13 | [assembly: MelonInfo(typeof(TrackingRotatorMod), ModBuildInfo.Name, ModBuildInfo.Version, ModBuildInfo.Author, ModBuildInfo.DownloadLink)]
14 | [assembly: MelonGame(ModBuildInfo.GameDeveloper, ModBuildInfo.Game)]
15 | [assembly: MelonOptionalDependencies("UIExpansionKit", "ActionMenuApi")]
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # TrackingRotator
2 | [](https://github.com/nitrog0d/TrackingRotator/releases)
3 | [](https://github.com/nitrog0d/TrackingRotator/releases/latest)
4 | [](https://patreon.com/nitrog0d)
5 |
6 | Video preview soon.
7 |
8 | A mod that lets you rotate your tracking, it was made with "play while laying down" in mind.
9 | * UIExpansionKit Integration, first releases and actual algorithm by [nitro.](https://github.com/nitrog0d)
10 | * AssetBundle, AMAPI Integration and Integrations' Management/Organization by [Davi](https://github.com/d-mageek).
11 |
12 | * **Warning:** The VRChat team is not very keen on modding or reverse engineering the game, while the mod does not include anything that would ruin the fun for others, using it may still be a bannable offence.
13 |
14 | * **USE IT AT YOUR OWN RISK**, we are not responsible for any bans or any punishments you may get by using this mod!
15 |
16 | ## Installation
17 | * **Make sure you have run the [MelonLoader Installer](https://github.com/LavaGang/MelonLoader.Installer/releases/latest/download/MelonLoader.Installer.exe) by [LavaGang](https://github.com/LavaGang) first (feel free to join the [VRChat Modding Group Discord](https://discord.gg/jgvc9Fd) for help!).**
18 | * Download the [latest version](https://github.com/nitrog0d/TrackingRotator/releases/latest/download/TrackingRotator.dll) of the mod.
19 | * **Install (at least one is required):**
20 | * [UIExpansionKit](https://github.com/knah/VRCMods) by [knah](https://github.com/knah), or/and
21 | * [ActionMenuApi](https://github.com/gompocp/ActionMenuApi) by [gompo](https://github.com/gompocp).
22 | * Drag/copy the DLL files that you have downloaded into the Mods folder.
23 | * That's it! Now just run the game and the mod should be installed!
24 |
25 | ## How to use
26 | * Open the Quick menu and click the "Tracking rotation" button.
27 | * You can change the rotation value, high precision value and "Reset rotation when a new world loads" in Mod settings (Quick menu -> Settings -> Mod settings).
--------------------------------------------------------------------------------
/TrackingRotator.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {652FEBB3-E7BD-4F5C-8AC4-51E36B19BC9D}
8 | Library
9 | Properties
10 | TrackingRotator
11 | TrackingRotator
12 | v4.7.2
13 | 512
14 | true
15 | D:\Jogos\SteamLibrary\steamapps\common\VRChat\MelonLoader
16 |
17 |
18 | true
19 | full
20 | false
21 | bin\Debug\
22 | DEBUG;TRACE
23 | prompt
24 | 4
25 |
26 |
27 | pdbonly
28 | true
29 | bin\Release\
30 | TRACE
31 | prompt
32 | 4
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 | $(MelonLoaderPath)\..\Mods\ActionMenuApi.dll
43 |
44 |
45 | $(MelonLoaderPath)\Managed\Assembly-CSharp.dll
46 |
47 |
48 | $(MelonLoaderPath)\Managed\Il2Cppmscorlib.dll
49 |
50 |
51 | $(MelonLoaderPath)\MelonLoader.dll
52 |
53 |
54 | $(MelonLoaderPath)\..\Mods\UIExpansionKit.dll
55 |
56 |
57 | $(MelonLoaderPath)\Managed\UnhollowerBaseLib.dll
58 |
59 |
60 | $(MelonLoaderPath)\Managed\UnityEngine.AssetBundleModule.dll
61 |
62 |
63 | $(MelonLoaderPath)\Managed\UnityEngine.CoreModule.dll
64 |
65 |
66 |
67 |
68 |
69 | xcopy /f /y "$(TargetPath)" "$(MelonLoaderPath)\..\Mods\$(TargetFileName)*"
70 |
71 |
--------------------------------------------------------------------------------
/TrackingRotator.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.31005.135
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TrackingRotator", "TrackingRotator.csproj", "{652FEBB3-E7BD-4F5C-8AC4-51E36B19BC9D}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|Any CPU = Debug|Any CPU
11 | Release|Any CPU = Release|Any CPU
12 | EndGlobalSection
13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
14 | {652FEBB3-E7BD-4F5C-8AC4-51E36B19BC9D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15 | {652FEBB3-E7BD-4F5C-8AC4-51E36B19BC9D}.Debug|Any CPU.Build.0 = Debug|Any CPU
16 | {652FEBB3-E7BD-4F5C-8AC4-51E36B19BC9D}.Release|Any CPU.ActiveCfg = Release|Any CPU
17 | {652FEBB3-E7BD-4F5C-8AC4-51E36B19BC9D}.Release|Any CPU.Build.0 = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(SolutionProperties) = preSolution
20 | HideSolutionNode = FALSE
21 | EndGlobalSection
22 | GlobalSection(ExtensibilityGlobals) = postSolution
23 | SolutionGuid = {184DD356-DD8B-4738-9E83-AE05BC75CF89}
24 | EndGlobalSection
25 | EndGlobal
26 |
--------------------------------------------------------------------------------
/TrackingRotatorMod.cs:
--------------------------------------------------------------------------------
1 | using MelonLoader;
2 | using UnityEngine;
3 | using System.Linq;
4 | using UnhollowerRuntimeLib;
5 | using System.Collections;
6 | using TrackingRotator.Utils;
7 | using Il2CppSystem.Reflection;
8 |
9 | namespace TrackingRotator
10 | {
11 | public static class ModBuildInfo {
12 | public const string Name = "TrackingRotator";
13 | public const string Author = "Davi & nitro."; // <3
14 | public const string Version = "1.0.2";
15 | public const string DownloadLink = "https://github.com/nitrog0d/TrackingRotator/releases/latest/download/TrackingRotator.dll";
16 | public const string GameDeveloper = "VRChat";
17 | public const string Game = "VRChat";
18 | }
19 |
20 | public class TrackingRotatorMod : MelonMod
21 | {
22 |
23 | private const string ModCategory = "TrackingRotator";
24 | private const string UIXIntegration = "UIXIntegration";
25 | private const string AMAPIIntegration = "AMAPIIntegration";
26 | private const string RotationValuePref = "RotationValue";
27 | private const string HighPrecisionRotationValuePref = "HighPrecisionRotationValue";
28 | private const string ResetRotationOnSceneChangePref = "ResetRotationOnSceneChange";
29 |
30 | private static float rotationValue = 0f;
31 | private static float highPrecisionRotationValue = 0f;
32 | private static bool resetRotationOnSceneChange, IsUsingUIX, IsUsingAMAPI = false;
33 | private static bool UIXintegration, AMAPIintegration = true;
34 |
35 | public static bool highPrecision = false;
36 | public static Transform transform;
37 | public static Transform cameraTransform;
38 | public static Quaternion originalRotation;
39 |
40 | public override void OnApplicationStart()
41 | {
42 | MelonPreferences.CreateCategory(ModCategory, "Tracking Rotator");
43 | MelonPreferences.CreateEntry(ModCategory, UIXIntegration, true, "Integrate with UiExpansionKit?");
44 | MelonPreferences.CreateEntry(ModCategory, AMAPIIntegration, true, "Integrate with Action Menu?");
45 | MelonPreferences.CreateEntry(ModCategory, RotationValuePref, 22.5f, "Rotation value");
46 | MelonPreferences.CreateEntry(ModCategory, HighPrecisionRotationValuePref, 1f, "High precision rotation value");
47 | MelonPreferences.CreateEntry(ModCategory, ResetRotationOnSceneChangePref, false, "Reset rotation when a new world loads");
48 | OnPreferencesSaved();
49 |
50 | Integrations();
51 |
52 | MelonLogger.Msg("Mod loaded.");
53 | }
54 |
55 | public override void OnPreferencesSaved()
56 | {
57 | UIXintegration = MelonPreferences.GetEntryValue(ModCategory, UIXIntegration);
58 | AMAPIintegration = MelonPreferences.GetEntryValue(ModCategory, AMAPIIntegration);
59 | rotationValue = MelonPreferences.GetEntryValue(ModCategory, RotationValuePref);
60 | highPrecisionRotationValue = MelonPreferences.GetEntryValue(ModCategory, HighPrecisionRotationValuePref);
61 | resetRotationOnSceneChange = MelonPreferences.GetEntryValue(ModCategory, ResetRotationOnSceneChangePref);
62 | }
63 |
64 |
65 | public override void OnSceneWasLoaded(int buildIndex, string sceneName)
66 | {
67 | if (resetRotationOnSceneChange && cameraTransform) cameraTransform.localRotation = originalRotation;
68 | }
69 |
70 | public static IEnumerator WaitForUiInit()
71 | {
72 | while (Object.FindObjectOfType() == null)
73 | yield return null;
74 |
75 | var camera = Object.FindObjectOfType();
76 | var Transform = camera.GetIl2CppType().GetFields(BindingFlags.Public | BindingFlags.Instance).Where(f => f.FieldType == Il2CppType.Of()).ToArray()[0];
77 | cameraTransform = Transform.GetValue(camera).Cast();
78 | originalRotation = cameraTransform.localRotation;
79 | transform = Camera.main.transform;
80 |
81 | if (IsUsingAMAPI) typeof(AMAPIManager).GetMethod("ActionMenuIntegration").Invoke(null, null);
82 | }
83 |
84 | public static void Move(Vector3 direction)
85 | {
86 | cameraTransform.Rotate(direction, highPrecision ? highPrecisionRotationValue : rotationValue, Space.World);
87 | }
88 |
89 | private static void Integrations()
90 | {
91 | if (AMAPIintegration)
92 | {
93 | if (MelonHandler.Mods.Any(x => x.Info.Name.Equals("ActionMenuApi")))
94 | {
95 | Assets.OnApplicationStart();
96 | IsUsingAMAPI = true;
97 | }
98 | else MelonLogger.Warning("For a better experience, please consider using ActionMenuApi.");
99 | }
100 | else MelonLogger.Warning("Integration with ActionMenuApi has been deactivated on Settings.");
101 |
102 | if (UIXintegration)
103 | {
104 | if (MelonHandler.Mods.Any(x => x.Info.Name.Equals("UI Expansion Kit")))
105 | {
106 | typeof(UIXManager).GetMethod("OnApplicationStart").Invoke(null, null);
107 | IsUsingUIX = true;
108 | }
109 | else MelonLogger.Warning("For a better experience, please consider using UIExpansionKit.");
110 | }
111 | else MelonLogger.Warning("Integration with UIExpansionKit has been deactivated on Settings.");
112 |
113 | if (!AMAPIintegration && !UIXintegration)
114 | MelonLogger.Warning("Both integrations (Action Menu and UiExpansionKit) have been deactivated. " +
115 | "The mod cannot run without those, therefore, expect it to fail. If this was not intended, " +
116 | "please consider activating at least one of the integrations on Settings.");
117 |
118 | if (!IsUsingAMAPI && !IsUsingUIX)
119 | {
120 | MelonLogger.Error("Failed to load both integrations with UIExpansionKit and ActionMenuApi! The mod will not be loaded.");
121 | }
122 | else MelonCoroutines.Start(WaitForUiInit());
123 | }
124 | }
125 | }
--------------------------------------------------------------------------------
/Utils/AMAPIManager.cs:
--------------------------------------------------------------------------------
1 | using UnityEngine;
2 | using ActionMenuApi.Api;
3 | using UnhollowerRuntimeLib;
4 | using System.Collections;
5 | using MelonLoader;
6 | using System.IO;
7 | using System.Reflection;
8 | using static TrackingRotator.TrackingRotatorMod;
9 |
10 | namespace TrackingRotator.Utils
11 | {
12 | internal static class AMAPIManager
13 | {
14 | public static void ActionMenuIntegration()
15 | {
16 | VRCActionMenuPage.AddSubMenu(ActionMenuPage.Main, "Tracking Rotator", () =>
17 | {
18 | CustomSubMenu.AddButton("Forward", () => Move(transform.right), Assets.Forward); //X+
19 | CustomSubMenu.AddButton("Backward", () => Move(-transform.right), Assets.Backward); //X-
20 | CustomSubMenu.AddButton("Tilt Left", () => Move(transform.forward), Assets.TLeft); //Z+
21 | CustomSubMenu.AddButton("Tilt Right", () => Move(-transform.forward), Assets.TRight); //Z-
22 | CustomSubMenu.AddButton("Left", () => Move(-transform.up), Assets.Left); //Y-
23 | CustomSubMenu.AddButton("Right", () => Move(transform.up), Assets.Right); //Y+
24 |
25 | CustomSubMenu.AddSubMenu("Other", () =>
26 | {
27 | CustomSubMenu.AddButton("Reset", () => cameraTransform.localRotation = originalRotation, Assets.Reset);
28 | CustomSubMenu.AddToggle("High precision", highPrecision, b => highPrecision = b, Assets.HP);
29 | }, Assets.Other);
30 | }, Assets.Main);
31 | }
32 | }
33 |
34 | // The code below was based on Lily's...
35 | // https://github.com/KortyBoi/VRChat-TeleporterVR/blob/main/Utils/ResourceManager.cs
36 | // And also knah's!
37 | // https://github.com/knah/VRCMods/blob/master/UIExpansionKit
38 | internal static class Assets
39 | {
40 | private static AssetBundle Bundle;
41 | public static Texture2D Main, Forward, Backward, TLeft, TRight, Left, Right, Other, Reset, HP;
42 |
43 | public static void OnApplicationStart() { MelonCoroutines.Start(LoadAssets()); }
44 |
45 | private static IEnumerator LoadAssets()
46 | {
47 | try
48 | {
49 | using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("TrackingRotator.trackingrotator"))
50 | {
51 | using (var memoryStream = new MemoryStream((int)stream.Length))
52 | {
53 | stream.CopyTo(memoryStream);
54 | Bundle = AssetBundle.LoadFromMemory_Internal(memoryStream.ToArray(), 0);
55 | Bundle.hideFlags |= HideFlags.DontUnloadUnusedAsset;
56 | try { Main = LoadTexture("Main.png"); } catch { MelonLogger.Error("Failed to load image from asset bundle: Main.png"); }
57 | try { Forward = LoadTexture("Forward.png"); } catch { MelonLogger.Error("Failed to load image from asset bundle: Forward.png"); }
58 | try { Backward = LoadTexture("Backward.png"); } catch { MelonLogger.Error("Failed to load image from asset bundle: Backward.png"); }
59 | try { TLeft = LoadTexture("TLeft.png"); } catch { MelonLogger.Error("Failed to load image from asset bundle: TLeft.png"); }
60 | try { TRight = LoadTexture("TRight.png"); } catch { MelonLogger.Error("Failed to load image from asset bundle: TRight.png"); }
61 | try { Left = LoadTexture("Left.png"); } catch { MelonLogger.Error("Failed to load image from asset bundle: Left.png"); }
62 | try { Right = LoadTexture("Right.png"); } catch { MelonLogger.Error("Failed to load image from asset bundle: Right.png"); }
63 | try { Other = LoadTexture("Other.png"); } catch { MelonLogger.Error("Failed to load image from asset bundle: Other.png"); }
64 | try { Reset = LoadTexture("Reset.png"); } catch { MelonLogger.Error("Failed to load image from asset bundle: Reset.png"); }
65 | try { HP = LoadTexture("HP.png"); } catch { MelonLogger.Error("Failed to load image from asset bundle: HP.png"); }
66 | }
67 | }
68 | } catch { MelonLogger.Warning("Failed to load AssetBundle! ActionMenuApi will have its icons completely broken."); }
69 | yield break;
70 | }
71 |
72 | private static Texture2D LoadTexture(string Texture)
73 | {
74 | Texture2D Texture2 = Bundle.LoadAsset_Internal(Texture, Il2CppType.Of()).Cast();
75 | Texture2.hideFlags |= HideFlags.DontUnloadUnusedAsset;
76 | Texture2.hideFlags = HideFlags.HideAndDontSave;
77 | return Texture2;
78 | }
79 | }
80 | }
--------------------------------------------------------------------------------
/Utils/UIXManager.cs:
--------------------------------------------------------------------------------
1 | using UIExpansionKit.API;
2 | using static TrackingRotator.TrackingRotatorMod;
3 |
4 | namespace TrackingRotator.Utils
5 | {
6 | internal static class UIXManager
7 | {
8 | public static void OnApplicationStart() => ExpansionKitApi.GetExpandedMenu(ExpandedMenu.QuickMenu).AddSimpleButton("Tracking rotation", ShowRotationMenu);
9 |
10 | // Based on knah's ViewPointTweaker mod, https://github.com/knah/VRCMods/blob/master/ViewPointTweaker
11 | private static ICustomShowableLayoutedMenu rotationMenu = null;
12 | private static void ShowRotationMenu()
13 | {
14 | if (rotationMenu == null)
15 | {
16 | rotationMenu = ExpansionKitApi.CreateCustomQuickMenuPage(LayoutDescription.QuickMenu4Columns);
17 |
18 | rotationMenu.AddSpacer();
19 | rotationMenu.AddSimpleButton("Forward", () => Move(transform.right));
20 | rotationMenu.AddSpacer();
21 | rotationMenu.AddSpacer();
22 |
23 | rotationMenu.AddSimpleButton("Tilt Left", () => Move(transform.forward));
24 | rotationMenu.AddSimpleButton("Reset", () => cameraTransform.localRotation = originalRotation);
25 | rotationMenu.AddSimpleButton("Tilt Right", () => Move(-transform.forward));
26 | rotationMenu.AddSpacer();
27 |
28 | rotationMenu.AddSpacer();
29 | rotationMenu.AddSimpleButton("Backward", () => Move(-transform.right));
30 | rotationMenu.AddSimpleButton("Left", () => Move(-transform.up));
31 | rotationMenu.AddSimpleButton("Right", () => Move(transform.up));
32 |
33 | rotationMenu.AddToggleButton("High precision", b => highPrecision = b, () => highPrecision);
34 | rotationMenu.AddSpacer();
35 | rotationMenu.AddSpacer();
36 | rotationMenu.AddSimpleButton("Back", rotationMenu.Hide);
37 | }
38 |
39 | rotationMenu.Show();
40 | }
41 | }
42 | }
--------------------------------------------------------------------------------
/trackingrotator:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nitrog0d/TrackingRotator/dd0632253089303ede84f4aecedbdf950b1a2052/trackingrotator
--------------------------------------------------------------------------------