diff --git a/Gizmo/Gizmo/ComfyGizmo.cs b/Gizmo/Gizmo/ComfyGizmo.cs new file mode 100644 index 0000000..46ac594 --- /dev/null +++ b/Gizmo/Gizmo/ComfyGizmo.cs @@ -0,0 +1,336 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using System.Reflection.Emit; + +using BepInEx; +using BepInEx.Logging; +using HarmonyLib; + +using UnityEngine; + +using static Gizmo.PluginConfig; + +namespace Gizmo { + [BepInPlugin(PluginGUID, PluginName, PluginVersion)] + public class ComfyGizmo : BaseUnityPlugin { + public const string PluginGUID = "com.rolopogo.gizmo.comfy"; + public const string PluginName = "ComfyGizmo"; + public const string PluginVersion = "1.5.1"; + + static GameObject _gizmoPrefab = null; + static Transform _gizmoRoot; + + static Transform _xGizmo; + static Transform _yGizmo; + static Transform _zGizmo; + + static Transform _xGizmoRoot; + static Transform _yGizmoRoot; + static Transform _zGizmoRoot; + + static GameObject _comfyGizmo; + static Transform _comfyGizmoRoot; + + static Vector3 _eulerAngles; + static float _rotation; + + static bool _localFrame; + + static float _snapAngle; + + Harmony _harmony; + + public void Awake() { + BindConfig(Config); + + SnapDivisions.SettingChanged += (sender, eventArgs) => _snapAngle = 180f / SnapDivisions.Value; + _snapAngle = 180f / SnapDivisions.Value; + + _gizmoPrefab = LoadGizmoPrefab(); + + _harmony = Harmony.CreateAndPatchAll(Assembly.GetExecutingAssembly(), harmonyInstanceId: PluginGUID); + } + + public void OnDestroy() { + _harmony?.UnpatchSelf(); + } + + [HarmonyPatch(typeof(Game))] + static class GamePatch { + [HarmonyPostfix] + [HarmonyPatch(nameof(Game.Start))] + static void StartPostfix() { + Destroy(_gizmoRoot); + _gizmoRoot = CreateGizmoRoot(); + + Destroy(_comfyGizmo); + _comfyGizmo = new("ComfyGizmo"); + _comfyGizmoRoot = _comfyGizmo.transform; + _localFrame = false; + } + } + + [HarmonyPatch(typeof(Player))] + static class PlayerPatch { + [HarmonyTranspiler] + [HarmonyPatch(nameof(Player.UpdatePlacementGhost))] + static IEnumerable UpdatePlacementGhostTranspiler(IEnumerable instructions) { + if(NewGizmoRotation.Value) { + return new CodeMatcher(instructions) + .MatchForward( + useEnd: false, + new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(Player), nameof(Player.m_placeRotation))), + new CodeMatch(OpCodes.Conv_R4), + new CodeMatch(OpCodes.Mul), + new CodeMatch(OpCodes.Ldc_R4), + new CodeMatch(OpCodes.Call), + new CodeMatch(OpCodes.Stloc_S)) + .Advance(offset: 5) + .InsertAndAdvance(Transpilers.EmitDelegate>(_ => _comfyGizmoRoot.rotation)) + .InstructionEnumeration(); + } else { + return new CodeMatcher(instructions) + .MatchForward( + useEnd: false, + new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(Player), nameof(Player.m_placeRotation))), + new CodeMatch(OpCodes.Conv_R4), + new CodeMatch(OpCodes.Mul), + new CodeMatch(OpCodes.Ldc_R4), + new CodeMatch(OpCodes.Call), + new CodeMatch(OpCodes.Stloc_S)) + .Advance(offset: 5) + .InsertAndAdvance(Transpilers.EmitDelegate>(_ => _xGizmoRoot.rotation)) + .InstructionEnumeration(); + } + + } + + [HarmonyPostfix] + [HarmonyPatch(nameof(Player.UpdatePlacement))] + static void UpdatePlacementPostfix(ref Player __instance, ref bool takeInput) { + if (__instance.m_placementMarkerInstance) { + _gizmoRoot.gameObject.SetActive(ShowGizmoPrefab.Value && __instance.m_placementMarkerInstance.activeSelf); + _gizmoRoot.position = __instance.m_placementMarkerInstance.transform.position + (Vector3.up * 0.5f); + } + + if (!__instance.m_buildPieces || !takeInput) { + return; + } + + if(Input.GetKeyDown(SnapDivisionIncrementKey.Value.MainKey)) { + if(SnapDivisions.Value * 2 <= MaxSnapDivisions) { + MessageHud.instance.ShowMessage(MessageHud.MessageType.TopLeft, $"Snap divisions increased to {SnapDivisions.Value * 2}"); + SnapDivisions.Value = SnapDivisions.Value * 2; + if(ResetRotationOnSnapDivisionChange.Value) { + if(_localFrame) { + ResetRotationsLocalFrame(); + } else { + ResetRotations(); + } + return; + } + } + } + + if (Input.GetKeyDown(SnapDivisionDecrementKey.Value.MainKey)) { + if(Math.Floor(SnapDivisions.Value/2f) == SnapDivisions.Value/2f && SnapDivisions.Value/2 >= MinSnapDivisions) { + MessageHud.instance.ShowMessage(MessageHud.MessageType.TopLeft, $"Snap divisions decreased to {SnapDivisions.Value / 2}"); + SnapDivisions.Value = SnapDivisions.Value / 2; + if (ResetRotationOnSnapDivisionChange.Value) { + if (_localFrame) { + ResetRotationsLocalFrame(); + } else { + ResetRotations(); + } + return; + } + } + } + + if (Input.GetKey(CopyPieceRotationKey.Value.MainKey) && __instance.m_hoveringPiece != null) { + MatchPieceRotation(__instance.m_hoveringPiece); + } + + // Change Rotation Frames + if (Input.GetKeyDown(ChangeRotationModeKey.Value.MainKey)) { + if(_localFrame) { + MessageHud.instance.ShowMessage(MessageHud.MessageType.TopLeft, "Default rotation mode enabled"); + if (ResetRotationOnModeChange.Value) { + ResetRotationsLocalFrame(); + } else { + _eulerAngles = _comfyGizmo.transform.eulerAngles; + ResetGizmoRoot(); + RotateGizmoComponents(_eulerAngles); + } + + } else { + MessageHud.instance.ShowMessage(MessageHud.MessageType.TopLeft, "Local frame rotation mode enabled"); + if (ResetRotationOnModeChange.Value) { + ResetRotations(); + } else { + Quaternion currentRotation = _comfyGizmoRoot.rotation; + ResetGizmoComponents(); + _gizmoRoot.rotation = currentRotation; + } + } + + _localFrame = !_localFrame; + return; + } + + _xGizmo.localScale = Vector3.one; + _yGizmo.localScale = Vector3.one; + _zGizmo.localScale = Vector3.one; + + if (!_localFrame) { + Rotate(); + } else { + RotateLocalFrame(); + } + } + } + + static void Rotate() { + if (Input.GetKey(ResetAllRotationKey.Value.MainKey)) { + ResetRotations(); + } else if (Input.GetKey(XRotationKey.Value.MainKey)) { + HandleAxisInput(ref _eulerAngles.x, _xGizmo); + } else if (Input.GetKey(ZRotationKey.Value.MainKey)) { + HandleAxisInput(ref _eulerAngles.z, _zGizmo); + } else { + HandleAxisInput(ref _eulerAngles.y, _yGizmo); + } + + _comfyGizmo.transform.localRotation = Quaternion.Euler(_eulerAngles); + RotateGizmoComponents(_eulerAngles); + } + + static void RotateLocalFrame() { + if (Input.GetKey(ResetAllRotationKey.Value.MainKey)) { + ResetRotationsLocalFrame(); + return; + } + + _rotation = 0f; + Vector3 rotVector; + + if (Input.GetKey(XRotationKey.Value.MainKey)) { + _xGizmo.localScale = Vector3.one * 1.5f; + rotVector = Vector3.right; + HandleAxisInputLocalFrame(ref _rotation, rotVector, _xGizmo); + } else if (Input.GetKey(ZRotationKey.Value.MainKey)) { + _zGizmo.localScale = Vector3.one * 1.5f; + rotVector = Vector3.forward; + HandleAxisInputLocalFrame(ref _rotation, rotVector, _zGizmo); + } else { + _yGizmo.localScale = Vector3.one * 1.5f; + rotVector = Vector3.up; + HandleAxisInputLocalFrame(ref _rotation, rotVector, _yGizmo); + } + + RotateAxes(_rotation, rotVector); + } + + static void RotateAxes(float rotation, Vector3 rotVector) { + _comfyGizmo.transform.rotation *= Quaternion.AngleAxis(rotation, rotVector); + _gizmoRoot.rotation *= Quaternion.AngleAxis(rotation, rotVector); + } + + static void HandleAxisInput(ref float rotation, Transform gizmo) { + gizmo.localScale = Vector3.one * 1.5f; + rotation += Math.Sign(Input.GetAxis("Mouse ScrollWheel")) * _snapAngle; + + if (Input.GetKey(ResetRotationKey.Value.MainKey)) { + rotation = 0f; + } + } + + static void HandleAxisInputLocalFrame(ref float rotation, Vector3 rotVector, Transform gizmo) { + gizmo.localScale = Vector3.one * 1.5f; + rotation = Math.Sign(Input.GetAxis("Mouse ScrollWheel")) * _snapAngle; + + if (Input.GetKey(ResetRotationKey.Value.MainKey)) { + rotation = 0f; + ResetRotationLocalFrameAxis(rotVector); + } + } + static void MatchPieceRotation(Piece target) { + if (_localFrame) { + _comfyGizmo.transform.rotation = target.GetComponent().localRotation; + _gizmoRoot.rotation = target.GetComponent().localRotation; + } else { + _eulerAngles = target.GetComponent().eulerAngles; + Rotate(); + } + } + static void ResetRotations() { + _eulerAngles = Vector3.zero; + _comfyGizmo.transform.localRotation = Quaternion.Euler(Vector3.zero); + RotateGizmoComponents(Vector3.zero); + } + + static void ResetGizmoComponents() { + _eulerAngles = Vector3.zero; + RotateGizmoComponents(Vector3.zero); + } + + static void ResetGizmoRoot() { + _gizmoRoot.rotation = Quaternion.AngleAxis(0f, Vector3.up); + _gizmoRoot.rotation = Quaternion.AngleAxis(0f, Vector3.right); + _gizmoRoot.rotation = Quaternion.AngleAxis(0f, Vector3.forward); + } + + static void RotateGizmoComponents(Vector3 eulerAngles) { + _xGizmoRoot.localRotation = Quaternion.Euler(eulerAngles.x, 0f, 0f); + _yGizmoRoot.localRotation = Quaternion.Euler(0f, eulerAngles.y, 0f); + _zGizmoRoot.localRotation = Quaternion.Euler(0f, 0f, eulerAngles.z); + } + + static void ResetRotationsLocalFrame() { + ResetRotationLocalFrameAxis(Vector3.up); + ResetRotationLocalFrameAxis(Vector3.right); + ResetRotationLocalFrameAxis(Vector3.forward); + } + + static void ResetRotationLocalFrameAxis(Vector3 axis) { + _comfyGizmo.transform.rotation = Quaternion.AngleAxis(0f, axis); + _gizmoRoot.rotation = Quaternion.AngleAxis(0f, axis); + } + + static GameObject LoadGizmoPrefab() { + AssetBundle bundle = AssetBundle.LoadFromMemory( + GetResource(Assembly.GetExecutingAssembly(), "Gizmo.Resources.gizmos")); + + GameObject prefab = bundle.LoadAsset("GizmoRoot"); + bundle.Unload(unloadAllLoadedObjects: false); + + return prefab; + } + + static byte[] GetResource(Assembly assembly, string resourceName) { + Stream stream = assembly.GetManifestResourceStream(resourceName); + + byte[] data = new byte[stream.Length]; + stream.Read(data, offset: 0, count: (int) stream.Length); + + return data; + } + + static Transform CreateGizmoRoot() { + _gizmoRoot = Instantiate(_gizmoPrefab).transform; + + // ??? Something about quaternions. + _xGizmo = _gizmoRoot.Find("YRoot/ZRoot/XRoot/X"); + _yGizmo = _gizmoRoot.Find("YRoot/Y"); + _zGizmo = _gizmoRoot.Find("YRoot/ZRoot/Z"); + + _xGizmoRoot = _gizmoRoot.Find("YRoot/ZRoot/XRoot"); + _yGizmoRoot = _gizmoRoot.Find("YRoot"); + _zGizmoRoot = _gizmoRoot.Find("YRoot/ZRoot"); + + return _gizmoRoot.transform; + } + } +} diff --git a/Gizmo/Gizmo/Gizmo.csproj b/Gizmo/Gizmo/Gizmo.csproj index 94a51ca..2e506bb 100644 --- a/Gizmo/Gizmo/Gizmo.csproj +++ b/Gizmo/Gizmo/Gizmo.csproj @@ -9,9 +9,10 @@ Properties Gizmo Gizmo - v4.6 + v4.8 512 - true + 9 + true true @@ -30,56 +31,112 @@ prompt 4 + + + + $(HOME)/.steam/steam/steamapps/common/Valheim + $(HOME)/Library/Application Support/Steam/steamapps/common/Valheim/Contents/MacOS + + + + + $([MSBuild]::GetRegistryValueFromView('HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Steam App 892970', 'InstallLocation', null, RegistryView.Registry64, RegistryView.Registry32)) + <_SteamLibraryPath>$([MSBuild]::GetRegistryValueFromView('HKEY_CURRENT_USER\SOFTWARE\Valve\Steam', 'SteamPath', null, RegistryView.Registry32)) + $(_SteamLibraryPath)\steamapps\common\Valheim + C:\Program Files\Steam\steamapps\common\Valheim + C:\Program Files (x86)\Steam\steamapps\common\Valheim + + + + + $(GamePath)\BepInEx\plugins + - ..\..\..\..\..\AppData\Roaming\r2modmanPlus-local\Valheim\profiles\Modded\BepInEx\core\0Harmony.dll + $(GamePath)\BepInEx\core\0Harmony.dll - - ..\..\..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\Valheim\valheim_Data\Managed\assembly_utils.dll + + $(GamePath)\valheim_Data\Managed\publicized_assemblies\assembly_guiutils_publicized.dll - - ..\..\..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\Valheim\valheim_Data\Managed\assembly_valheim.dll + + $(GamePath)\valheim_Data\Managed\publicized_assemblies\assembly_utils_publicized.dll + + + $(GamePath)\valheim_Data\Managed\publicized_assemblies\assembly_valheim_publicized.dll - ..\..\..\..\..\AppData\Roaming\r2modmanPlus-local\Valheim\profiles\Modded\BepInEx\core\BepInEx.dll + $(GamePath)\BepInEx\core\BepInEx.dll - - - - - ..\..\..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\Valheim\valheim_Data\Managed\UnityEngine.dll + $(GamePath)\unstripped_corlib\UnityEngine.dll - ..\..\..\..\..\AppData\Roaming\r2modmanPlus-local\Valheim\profiles\Dev\unstripped_corlib\UnityEngine.AssetBundleModule.dll + $(GamePath)\unstripped_corlib\UnityEngine.AssetBundleModule.dll - ..\..\..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\Valheim\valheim_Data\Managed\UnityEngine.CoreModule.dll + $(GamePath)\unstripped_corlib\UnityEngine.CoreModule.dll - ..\..\..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\Valheim\valheim_Data\Managed\UnityEngine.InputLegacyModule.dll + $(GamePath)\unstripped_corlib\UnityEngine.InputLegacyModule.dll - ..\..\..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\Valheim\valheim_Data\Managed\UnityEngine.PhysicsModule.dll + $(GamePath)\unstripped_corlib\UnityEngine.PhysicsModule.dll + + + $(GamePath)\unstripped_corlib\UnityEngine.TextRenderingModule.dll - ..\..\..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\Valheim\valheim_Data\Managed\UnityEngine.UI.dll + $(GamePath)\unstripped_corlib\UnityEngine.UI.dll - + + - - - - + + + + + + + + + + + + + + + + + + + + + %(AssemblyVersions.Identity) + $(AssemblyVersion.Split('.')[0]) + $(AssemblyVersion.Split('.')[1]) + $(AssemblyVersion.Split('.')[2]) + $(AssemblyName)_v$(Major).$(Minor).$(Patch) + $(OutDir)$(PackageName) + + + + + + + + + + + \ No newline at end of file diff --git a/Gizmo/Gizmo/Plugin.cs b/Gizmo/Gizmo/Plugin.cs deleted file mode 100644 index b35c139..0000000 --- a/Gizmo/Gizmo/Plugin.cs +++ /dev/null @@ -1,124 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Reflection.Emit; -using System.Text; -using System.Threading.Tasks; -using BepInEx; -using BepInEx.Configuration; -using HarmonyLib; -using RoloPogo.Utilities; -using RoloPogo.Utils; -using UnityEngine; - -namespace Gizmo -{ - [BepInPlugin("com.rolopogo.Gizmo","Gizmo", "1.0.0")] - public class Plugin : BaseUnityPlugin - { - public static Plugin instance; - - int xRot; - int yRot; - int zRot; - - Transform gizmoRoot; - - Transform xGizmo; - Transform yGizmo; - Transform zGizmo; - - Transform xGizmoRoot; - Transform yGizmoRoot; - Transform zGizmoRoot; - - private ConfigEntry snapDivisions; - private ConfigEntry xKey; - private ConfigEntry zKey; - private ConfigEntry resetKey; - - float snapAngle => 180f / snapDivisions.Value; - - GameObject gizmoPrefab; - - private void Awake() - { - instance = this; - snapDivisions = Config.Bind("General", "SnapDivisions", 16, "Number of snap angles per 180 degrees. Vanilla uses 8"); - xKey = Config.Bind("General", "xKey", "LeftShift", "Hold this key to rotate in the x plane (red circle)"); - zKey = Config.Bind("General", "zKey", "LeftAlt", "Hold this key to rotate in the z plane (blue circle)"); - resetKey = Config.Bind("General", "resetKey", "V", "Press this key to reset the selected axis to zero rotation"); - - var bundle = AssetBundle.LoadFromMemory(ResourceUtils.GetResource(Assembly.GetExecutingAssembly(), "Gizmo.Resources.gizmos")); - gizmoPrefab = bundle.LoadAsset("GizmoRoot"); - bundle.Unload(false); - - Harmony.CreateAndPatchAll(typeof(UpdatePlacementGhost_Patch)); - Harmony.CreateAndPatchAll(typeof(UpdatePlacement_Patch)); - } - - public void UpdatePlacement(Player player, GameObject placementGhost, bool takeInput) { - if (player != Player.m_localPlayer) return; - - if (!gizmoRoot) - { - gizmoRoot = Instantiate(gizmoPrefab).transform; - xGizmo = gizmoRoot.Find("YRoot/ZRoot/XRoot/X"); - yGizmo = gizmoRoot.Find("YRoot/Y"); - zGizmo = gizmoRoot.Find("YRoot/ZRoot/Z"); - xGizmoRoot = gizmoRoot.Find("YRoot/ZRoot/XRoot"); - yGizmoRoot = gizmoRoot.Find("YRoot"); - zGizmoRoot = gizmoRoot.Find("YRoot/ZRoot"); - } - var marker = player.GetPrivateField("m_placementMarkerInstance"); - if (marker) - { - gizmoRoot.gameObject.SetActive(marker.activeSelf); - gizmoRoot.position = marker.transform.position + Vector3.up * .5f; - } - - if (!player.InPlaceMode()) - return; - - if (!takeInput) - return; - - xGizmo.localScale = Vector3.one; - yGizmo.localScale = Vector3.one; - zGizmo.localScale = Vector3.one; - - var scrollWheelInput = Math.Sign(Input.GetAxis("Mouse ScrollWheel")); - - if (Enum.TryParse(xKey.Value, out var xKeyCode) && Input.GetKey(xKeyCode)) - { - HandleAxisInput(scrollWheelInput, ref xRot, xGizmo); - } - else if (Enum.TryParse(zKey.Value, out var zKeyCode) && Input.GetKey(zKeyCode)) - { - HandleAxisInput(scrollWheelInput, ref zRot, zGizmo); - } - else - { - HandleAxisInput(scrollWheelInput, ref yRot, yGizmo); - } - - xGizmoRoot.localRotation = Quaternion.Euler(xRot * snapAngle, 0, 0); - yGizmoRoot.localRotation = Quaternion.Euler(0, yRot * snapAngle, 0); - zGizmoRoot.localRotation = Quaternion.Euler(0, 0, zRot * snapAngle); - } - - private void HandleAxisInput(int scrollWheelInput, ref int rot, Transform gizmo) - { - gizmo.localScale = Vector3.one * 1.5f; - rot = (rot + scrollWheelInput) % (snapDivisions.Value*2); - if (Enum.TryParse(resetKey.Value, out var resetKeyCode) && Input.GetKey(resetKeyCode)) - rot = 0; - } - - private static Quaternion GetPlacementAngle(float x, float y, float z) - { - return instance.xGizmoRoot.rotation; - } - } -} diff --git a/Gizmo/Gizmo/PluginConfig.cs b/Gizmo/Gizmo/PluginConfig.cs new file mode 100644 index 0000000..705d3a0 --- /dev/null +++ b/Gizmo/Gizmo/PluginConfig.cs @@ -0,0 +1,101 @@ +using BepInEx.Configuration; + +using UnityEngine; + +namespace Gizmo { + public static class PluginConfig { + public static ConfigEntry SnapDivisions { get; private set; } + + public static ConfigEntry XRotationKey; + public static ConfigEntry ZRotationKey; + public static ConfigEntry ResetRotationKey; + public static ConfigEntry ResetAllRotationKey; + public static ConfigEntry ChangeRotationModeKey; + public static ConfigEntry CopyPieceRotationKey; + public static ConfigEntry SnapDivisionIncrementKey; + public static ConfigEntry SnapDivisionDecrementKey; + + public static ConfigEntry ShowGizmoPrefab; + public static ConfigEntry ResetRotationOnModeChange; + public static ConfigEntry ResetRotationOnSnapDivisionChange; + public static ConfigEntry NewGizmoRotation; + + public static int MaxSnapDivisions = 256; + public static int MinSnapDivisions = 2; + + public static void BindConfig(ConfigFile config) { + SnapDivisions = + config.Bind( + "Gizmo", + "snapDivisions", + 16, + new ConfigDescription( + "Number of snap angles per 180 degrees. Vanilla uses 8.", + new AcceptableValueRange(MinSnapDivisions, MaxSnapDivisions))); + + XRotationKey = + config.Bind( + "Keys", + "xRotationKey", + new KeyboardShortcut(KeyCode.LeftShift), + "Hold this key to rotate on the x-axis/plane (red circle)."); + + ZRotationKey = + config.Bind( + "Keys", + "zRotationKey", + new KeyboardShortcut(KeyCode.LeftAlt), + "Hold this key to rotate on the z-axis/plane (blue circle)."); + + ResetRotationKey = + config.Bind( + "Keys", + "resetRotationKey", + new KeyboardShortcut(KeyCode.V), + "Press this key to reset the selected axis to zero rotation."); + + ResetAllRotationKey = + config.Bind( + "Keys", + "resetAllRotationKey", + KeyboardShortcut.Empty, + "Press this key to reset _all axis_ rotations to zero rotation."); + + + ChangeRotationModeKey = + config.Bind( + "Keys", + "changeRotationMode", + new KeyboardShortcut(KeyCode.BackQuote), + "Press this key to toggle rotation modes."); + + CopyPieceRotationKey = + config.Bind( + "Keys", + "copyPieceRotation", + KeyboardShortcut.Empty, + "Press this key to copy targeted piece's rotation."); + + SnapDivisionIncrementKey = + config.Bind( + "Keys", + "snapDivisionIncrement", + new KeyboardShortcut(KeyCode.PageUp), + "Doubles snap divisions from current."); + + SnapDivisionDecrementKey = + config.Bind( + "Keys", + "snapDivisionDecrement", + new KeyboardShortcut(KeyCode.PageDown), + "Doubles snap divisions from current."); + + ShowGizmoPrefab = config.Bind("UI", "showGizmoPrefab", true, "Show the Gizmo prefab in placement mode."); + + ResetRotationOnSnapDivisionChange = config.Bind("Reset", "resetOnSnapDivisionChange", true, "Resets the piece's rotation on snap division change."); + ResetRotationOnModeChange = config.Bind("Reset", "resetOnModeChange", true, "Resets the piece's rotation on mode switch."); + + NewGizmoRotation = config.Bind("Rotation Mode", "newGizmoRotation", false, "Enables post Gizmo v1.2.0 rotation scheme. Restart required for changes to take effect."); + } + } +} diff --git a/Gizmo/Gizmo/Properties/AssemblyInfo.cs b/Gizmo/Gizmo/Properties/AssemblyInfo.cs index 1edf78e..3264e3d 100644 --- a/Gizmo/Gizmo/Properties/AssemblyInfo.cs +++ b/Gizmo/Gizmo/Properties/AssemblyInfo.cs @@ -10,7 +10,7 @@ [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("Gizmo")] -[assembly: AssemblyCopyright("Copyright © 2021")] +[assembly: AssemblyCopyright("Copyright © 2021")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] @@ -32,5 +32,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: AssemblyVersion(Gizmo.ComfyGizmo.PluginVersion)] +[assembly: AssemblyFileVersion(Gizmo.ComfyGizmo.PluginVersion)] diff --git a/Gizmo/Gizmo/README.md b/Gizmo/Gizmo/README.md new file mode 100644 index 0000000..f830449 --- /dev/null +++ b/Gizmo/Gizmo/README.md @@ -0,0 +1,71 @@ +# ComfyGizmo v1.5.0 + +*Comfy-specific version of Gizmo.* + + * Configurable modifier hot-keys for: + * X-axis rotation (default: hold `LeftShift`) + * Z-axis rotation (default: hold `LeftAlt`) + * Reset selected axis rotation (default: `V`) + * Reset **ALL** axis rotations (disabled by default) + * Can disable the Gizmo placement visual. + * Can set the snap angles per 180 degrees from 2 - 256. + * Original Euler-style rotation. + +## Installation + +### Manual + + 1. Unzip `Gizmo.dll` to your `/valheim/BepInEx/plugins/` folder + +### Thunderstore (manual) + + 1. **Disable or uninstall** the existing `Gizmo v1.0.0` mod by Rolo. + 2. **Disable or uninstall** any manually installed `ComfyGizmo_v1.3.0` or earlier. + 3. Go to Settings > Import local mod > select `ComfyGizmo_v1.5.0.zip`. + 4. Click "OK/Import local mod" on the pop-up for information. + +## Changelog + +### 1.5.1 + * Added toggle to allow for v1.2.0 rotation scheme versus v1.3.0 rotation scheme. Restart required on enabling/disabling + * Fixed issue with gizmo visual axes not rotating correctly. + +### 1.5.0 + * Added hotkeys for halving and doubling snap divisions. PageUp and PageDown by default. + * Added toggle for resetting piece rotation on snap division change. Enabled by default. + * Added feature to copy target piece's rotation. + * Added alternate rotation method for using local frame coordinates. + * Added configuration option to rotate using local frame coordinates. + * Hot key added to toggle between default and local frame rotation modes. Default set to back quote. + * Added toggle to reset piece orientation which changing between rotation frames. Enabled by default. + +### 1.4.0 + + * Create a new GameObject `ComfyGizmo` to maintain the current Quaternion rotation state. + * Re-ordered the mapping/assignment of original Gizmo's prefab XYZ roots/transforms per request by Jere. + * Increased the snap-angles maximum from 128 to 256. + * Moved plugin configuration logic into its own class `PluginConfig`. + * Renamed the author field in `manifest.json` to `ComfyMods`. + +### 1.3.1 + + * Try to add compatability with other mods that also transpile `UpdatePlacementGhost`. + +### 1.3.0 + + * Added configuration option for a 'reset all rotations' hot-key (default to un-set). + * Cleaned-up the UpdatePlacementGhost transpiler. + +### 1.2.0 + + * Modified GizmoRoot instantiate to trigger on `Game.Start()`. + +### 1.1.0 + + * Turn SnapDivisions into a slider. + * Moved `Resources.GetResources()` into the main plugin file. + +### 1.0.0 + + * Rewrite of Gizmo for Comfy-specific use. + * Supprot re-binding of modifier keys and simplify code to one file. \ No newline at end of file diff --git a/Gizmo/Gizmo/UpdatePlacementGhost_Patch.cs b/Gizmo/Gizmo/UpdatePlacementGhost_Patch.cs deleted file mode 100644 index 0101dc0..0000000 --- a/Gizmo/Gizmo/UpdatePlacementGhost_Patch.cs +++ /dev/null @@ -1,42 +0,0 @@ -using HarmonyLib; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection.Emit; -using System.Text; -using System.Threading.Tasks; - -namespace Gizmo -{ - [HarmonyPatch(typeof(Player), "UpdatePlacementGhost")] - public static class UpdatePlacementGhost_Patch - { - static IEnumerable Transpiler(IEnumerable instructions) - { - var placementAnglePatched = false; - var codes = new List(instructions); - for (var i = 0; i < codes.Count; i++) - { - if(!placementAnglePatched) - if (codes[i].opcode == OpCodes.Callvirt && - codes[i + 1].opcode == OpCodes.Ldc_R4 && - codes[i + 2].opcode == OpCodes.Ldc_R4 && - codes[i + 3].opcode == OpCodes.Ldarg_0 && - codes[i + 4].opcode == OpCodes.Ldfld && - codes[i + 5].opcode == OpCodes.Conv_R4 && - codes[i + 6].opcode == OpCodes.Mul && - codes[i + 7].opcode == OpCodes.Ldc_R4 && - codes[i + 8].opcode == OpCodes.Call - ) - - { - codes[i + 8] = CodeInstruction.Call(typeof(Plugin), "GetPlacementAngle"); - placementAnglePatched = true; - } - - - } - return codes.AsEnumerable(); - } - } -} diff --git a/Gizmo/Gizmo/UpdatePlacement_Patch.cs b/Gizmo/Gizmo/UpdatePlacement_Patch.cs deleted file mode 100644 index e730091..0000000 --- a/Gizmo/Gizmo/UpdatePlacement_Patch.cs +++ /dev/null @@ -1,20 +0,0 @@ -using HarmonyLib; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using UnityEngine; - -namespace Gizmo -{ - class UpdatePlacement_Patch - { - [HarmonyPatch(typeof(Player), "UpdatePlacement", new Type[] { typeof(bool), typeof(float) })] - [HarmonyPostfix] - private static void Player_UpdatePlacement(Player __instance, GameObject ___m_placementGhost, bool takeInput, float dt) - { - Plugin.instance.UpdatePlacement(__instance, ___m_placementGhost, takeInput); - } - } -} diff --git a/Gizmo/Gizmo/Utilities/ReflectionUtil.cs b/Gizmo/Gizmo/Utilities/ReflectionUtil.cs deleted file mode 100644 index 47daad9..0000000 --- a/Gizmo/Gizmo/Utilities/ReflectionUtil.cs +++ /dev/null @@ -1,321 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using UnityEngine; - -namespace RoloPogo.Utilities -{ - public static class ReflectionUtil - { - private const BindingFlags _allBindingFlags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; - - - /// - /// Sets a field on the target object. specifies the the field belongs to. - /// - /// the object instance - /// the field to set - /// the value to set it to - /// the object the field belongs to - /// thrown when is not a member of - /// thrown when isn't assignable as - public static void SetField(this object obj, string fieldName, object value, Type targetType) - { - var prop = targetType.GetField(fieldName, _allBindingFlags); - if (prop == null) - throw new InvalidOperationException($"{fieldName} is not a member of {targetType.Name}"); - prop.SetValue(obj, value); - } - - /// - /// Gets the value of a field. specifies the the field belongs to. - /// - /// the object instance to pull from - /// the name of the field to read - /// the object the field belongs to - /// the value of the field - /// thrown when is not a member of - /// thrown when isn't assignable as - public static object GetField(this object obj, string fieldName, Type targetType) - { - var prop = targetType.GetField(fieldName, _allBindingFlags); - if (prop == null) - throw new InvalidOperationException($"{fieldName} is not a member of {targetType.Name}"); - return prop.GetValue(obj); - } - - /// - /// Sets a private field on the target object. specifies the the field belongs to. - /// - /// the object instance - /// the field to set - /// the value to set it to - /// the object the field belongs to - /// thrown when is not a member of - /// thrown when isn't assignable as - public static void SetPrivateField(this object obj, string fieldName, object value, Type targetType) - { - var prop = targetType.GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); - if (prop == null) - throw new InvalidOperationException($"{fieldName} is not a member of {targetType.Name}"); - prop.SetValue(obj, value); - } - - /// - /// Gets the value of a private field. specifies the the field belongs to. - /// - /// the type of the field (result casted) - /// the object instance to pull from - /// the name of the field to read - /// the object the field belongs to - /// the value of the field - /// thrown when is not a member of - /// thrown when isn't assignable as - public static T GetPrivateField(this object obj, string fieldName, Type targetType) - { - var prop = targetType.GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance); - if (prop == null) - throw new InvalidOperationException($"{fieldName} is not a member of {targetType.Name}"); - var value = prop.GetValue(obj); - return (T)value; - } - - - /// - /// Sets the value of a property on the target object. specifies the the property belongs to. - /// - /// the object instance - /// the property to set - /// the value to set it to - /// the object the property belongs to - /// thrown when is not a member of - /// thrown when isn't assignable as - public static void SetProperty(this object obj, string propertyName, object value, Type targetType) - { - var prop = targetType.GetProperty(propertyName, _allBindingFlags); - if (prop == null) - throw new InvalidOperationException($"{propertyName} is not a member of {targetType.Name}"); - prop.SetValue(obj, value); - } - - /// - /// Gets the value of a property. specifies the the field belongs to. - /// - /// the object instance to pull from - /// the name of the property to read - /// the object the property belongs to - /// the value of the property - /// thrown when is not a member of - /// thrown when isn't assignable as - public static object GetProperty(this object obj, string propertyName, Type targetType) - { - var prop = targetType.GetProperty(propertyName, _allBindingFlags); - if (prop == null) - throw new InvalidOperationException($"{propertyName} is not a member of {targetType.Name}"); - var value = prop.GetValue(obj); - return value; - } - - - #region Overloads - /// - /// Sets a private field on the target object. - /// - /// the object instance - /// the field to set - /// the value to set it to - /// the object the field belongs to - /// thrown when is not a member of - public static void SetPrivateField(this object obj, string fieldName, object value) => obj.SetPrivateField(fieldName, value, obj.GetType()); - - - /// - /// Gets the value of a private field. - /// - /// the type of te field (result casted) - /// the object instance to pull from - /// the name of the field to read - /// the value of the field - /// thrown when is not a member of - public static T GetPrivateField(this object obj, string fieldName) => obj.GetPrivateField(fieldName, obj.GetType()); - - /// - /// Sets a (potentially) private field on the target object. - /// - /// the object instance - /// the field to set - /// the value to set it to - /// thrown when is not a member of - public static void SetField(this object obj, string fieldName, object value) => obj.SetField(fieldName, value, obj.GetType()); - - /// - /// Gets the value of an object's field. - /// - /// the object instance to pull from - /// the name of the field to read - /// the value of the field - /// thrown when is not a member of - public static object GetField(this object obj, string fieldName) => GetField(obj, fieldName, obj.GetType()); - - - /// - /// Gets the value of a field. specifies the the field belongs to. - /// - /// the type of the field (result casted) - /// the object instance to pull from - /// the name of the field to read - /// the object the field belongs to - /// the value of the field - /// thrown when is not a member of - /// thrown when isn't assignable as - public static T GetField(this object obj, string fieldName, Type targetType) => (T)obj.GetField(fieldName, targetType); - - - /// - /// Gets the casted value of a field. - /// - /// the type of the field (result casted) - /// the object instance to pull from - /// the name of the field to read - /// the value of the field - /// thrown when is not a member of - public static T GetField(this object obj, string fieldName) => (T)GetField(obj, fieldName, obj.GetType()); - - /// - /// Sets the value of a property on the target object. - /// - /// the object instance - /// the property to set - /// the value to set it to - /// thrown when is not a member of - public static void SetProperty(this object obj, string propertyName, object value) => obj.SetProperty(propertyName, value, obj.GetType()); - - /// - /// Gets the value of a property. - /// - /// the object instance to pull from - /// the name of the property to read - /// the value of the property - /// thrown when is not a member of - public static object GetProperty(this object obj, string propertyName) => obj.GetProperty(propertyName, obj.GetType()); - - /// - /// Gets the casted value of a property. - /// - /// they type of the property - /// the object instance to pull from - /// the name of the property to read - /// the value of the property - /// thrown when is not a member of - public static T GetProperty(this object obj, string propertyName) => (T)GetProperty(obj, propertyName); - - #endregion - - - - //Invokes a (static?) private method with name "methodName" and params "methodParams", returns an object of the specified type - public static T InvokeMethod(this object obj, string methodName, params object[] methodParams) => (T)InvokeMethod(obj, methodName, methodParams); - - //Invokes a (static?) private method with name "methodName" and params "methodParams" - public static object InvokeMethod(this object obj, string methodName, params object[] methodParams) - { - MethodInfo method = obj.GetType().GetMethod(methodName, _allBindingFlags); - if (method == null) - throw new InvalidOperationException($"{methodName} is not a member of {obj.GetType().Name}"); - return method.Invoke(obj, methodParams); - } - - //Returns a constructor with the specified parameters to the specified type or object - public static object InvokeConstructor(this object obj, params object[] constructorParams) - { - Type[] types = new Type[constructorParams.Length]; - for (int i = 0; i < constructorParams.Length; i++) types[i] = constructorParams[i].GetType(); - return (obj is Type ? (Type)obj : obj.GetType()) - .GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, types, null) - .Invoke(constructorParams); - } - - //Returns a Type object which can be used to invoke static methods with the above helpers - public static Type GetStaticType(string clazz) - { - return Type.GetType(clazz); - } - - //Returns a list (of strings) of the names of all loaded assemblies - public static IEnumerable ListLoadedAssemblies() - { - return AppDomain.CurrentDomain.GetAssemblies(); - } - - //Returns a list of all loaded namespaces - //TODO: Check up on time complexity here, could potentially be parallelized - public static IEnumerable ListNamespacesInAssembly(Assembly assembly) - { - IEnumerable ret = Enumerable.Empty(); - ret = ret.Concat(assembly.GetTypes() - .Select(t => t.Namespace) - .Distinct() - .Where(n => n != null)); - return ret.Distinct(); - } - - //Returns a list of classes in a namespace - //TODO: Check up on time complexity here, could potentially be parallelized - public static IEnumerable ListClassesInNamespace(string ns) - { - //For each loaded assembly - foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) - { - //If the assembly contains the desired namespace - if (assembly.GetTypes().Where(t => t.Namespace == ns).Any()) - { - //Select the types we want from the namespace and return them - return assembly.GetTypes() - .Where(t => t.IsClass) - .Select(t => t.Name); - } - } - return null; - } - - //(Created by taz?) Copies a component to a destination object, keeping all its field values? - public static Behaviour CopyComponent(Behaviour original, Type originalType, Type overridingType, GameObject destination) - { - Behaviour copy = null; - - try - { - copy = destination.AddComponent(overridingType) as Behaviour; - } - catch (Exception) - { - - } - - copy.enabled = false; - - //Copy types of super classes as well as our class - Type type = originalType; - while (type != typeof(MonoBehaviour)) - { - CopyForType(type, original, copy); - type = type.BaseType; - } - - copy.enabled = true; - return copy; - } - - //(Created by taz?) Copies a Component of Type type, and all its fields - private static void CopyForType(Type type, Component source, Component destination) - { - FieldInfo[] myObjectFields = type.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.GetField | BindingFlags.SetField); - - foreach (FieldInfo fi in myObjectFields) - { - fi.SetValue(destination, fi.GetValue(source)); - } - } - } -} \ No newline at end of file diff --git a/Gizmo/Gizmo/Utilities/ResourceUtils.cs b/Gizmo/Gizmo/Utilities/ResourceUtils.cs deleted file mode 100644 index 459b97a..0000000 --- a/Gizmo/Gizmo/Utilities/ResourceUtils.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Text; -using System.Threading.Tasks; - -namespace RoloPogo.Utils -{ - static class ResourceUtils - { - public static byte[] GetResource(Assembly asm, string ResourceName) - { - System.IO.Stream stream = asm.GetManifestResourceStream(ResourceName); - byte[] data = new byte[stream.Length]; - stream.Read(data, 0, (int)stream.Length); - return data; - } - } -} diff --git a/Gizmo/Gizmo/icon.png b/Gizmo/Gizmo/icon.png new file mode 100644 index 0000000..4e12b03 Binary files /dev/null and b/Gizmo/Gizmo/icon.png differ diff --git a/Gizmo/Gizmo/manifest.json b/Gizmo/Gizmo/manifest.json new file mode 100644 index 0000000..f2322e9 --- /dev/null +++ b/Gizmo/Gizmo/manifest.json @@ -0,0 +1,10 @@ +{ + "name": "Gizmo", + "version_number": "1.5.1", + "author": "ComfyMods", + "website_url": "https://github.com/redseiko/ValheimMods/tree/main/Gizmo/Gizmo", + "description": "Comfy-specific fork of Gizmo.", + "dependencies": [ + "denikson-BepInExPack_Valheim-5.4.1901" + ] +} \ No newline at end of file diff --git a/Gizmo/README.md b/Gizmo/README.md index 17c6430..0538cfb 100644 --- a/Gizmo/README.md +++ b/Gizmo/README.md @@ -1,19 +1,10 @@ -# Gizmo -An intuitive advanced building tool - -Client side only. - -- Rotate objects in all three axes using modifier keys and the scroll wheel -- Intuitive gizmo helps visualize rotations -- Increased rotational snap precision -- Rotate in the Y (Green) plane with no modifier keys -- Rotate in the X (Red) plane with LShift (by default) -- Rotate in the Z (Blue) plane with LAlt (by default) -- Reset an axis by holding its modifier and hitting V (by default) - -Sample Video: https://streamable.com/wio6pk -Source: https://github.com/rolopogo/ValheimMods - -Install with BepInEx - -Copy ExploreTogether.dll into Valheim/BepInEx/plugins \ No newline at end of file +# ComfyGizmo + + * Configurable modifier hot-keys for: + * X-axis rotation (default: hold `LeftShift`) + * Z-axis rotation (default: hold `LeftAlt`) + * Reset selected axis rotation (default: `V`) + * Reset **ALL** axis rotations (disabled by default) + * Can disable the Gizmo placement visual. + * Can set the snap angles per 180 degrees from 2 - 128. + * Original Euler-style rotation. \ No newline at end of file