diff --git a/NAK_CVR_Mods.sln b/NAK_CVR_Mods.sln index 1001075..6a9a721 100644 --- a/NAK_CVR_Mods.sln +++ b/NAK_CVR_Mods.sln @@ -29,6 +29,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ChatBoxExtensions", "ChatBo EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EzCurls", "EzCurls\EzCurls.csproj", "{9D39E900-8F38-485B-8B67-9F75D8FF2C5E}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PhysicsGunMod", "PhysicsGunMod\PhysicsGunMod.csproj", "{F94DDB73-9041-4F5C-AD43-6960701E8417}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -87,6 +89,10 @@ Global {9D39E900-8F38-485B-8B67-9F75D8FF2C5E}.Debug|Any CPU.Build.0 = Debug|Any CPU {9D39E900-8F38-485B-8B67-9F75D8FF2C5E}.Release|Any CPU.ActiveCfg = Release|Any CPU {9D39E900-8F38-485B-8B67-9F75D8FF2C5E}.Release|Any CPU.Build.0 = Release|Any CPU + {F94DDB73-9041-4F5C-AD43-6960701E8417}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F94DDB73-9041-4F5C-AD43-6960701E8417}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F94DDB73-9041-4F5C-AD43-6960701E8417}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F94DDB73-9041-4F5C-AD43-6960701E8417}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/PhysicsGunMod/Components/ObjectSyncBridge.cs b/PhysicsGunMod/Components/ObjectSyncBridge.cs new file mode 100644 index 0000000..ea3e1c7 --- /dev/null +++ b/PhysicsGunMod/Components/ObjectSyncBridge.cs @@ -0,0 +1,91 @@ +using ABI_RC.Core.Savior; +using ABI_RC.Core.Util; +using ABI.CCK.Components; +using UnityEngine; + +namespace NAK.PhysicsGunMod.Components; + +public class ObjectSyncBridge : MonoBehaviour +{ + private PhysicsGunInteractionBehavior _physicsGun; + + private void Start() + { + // find physics gun + if (!TryGetComponent(out _physicsGun)) + { + PhysicsGunMod.Logger.Msg("Failed to find physics gun!"); + Destroy(this); + return; + } + + // listen for events + _physicsGun.OnPreGrabbedObject = o => + { + bool canTakeOwnership = false; + + CVRObjectSync objectSync = o.GetComponentInParent(); + if (objectSync != null + && (objectSync.SyncType == 0 // check if physics synced or synced by us + || objectSync.SyncedByMe)) + canTakeOwnership = true; + + CVRSpawnable spawnable = o.GetComponentInParent(); + if (spawnable != null) + { + CVRSyncHelper.PropData propData = CVRSyncHelper.Props.Find(match => match.InstanceId == spawnable.instanceId); + if (propData != null + && (propData.syncType == 0 // check if physics synced or synced by us + || propData.syncedBy == MetaPort.Instance.ownerId)) + canTakeOwnership = true; + } + + CVRPickupObject pickup = o.GetComponentInParent(); + if (pickup != null + && (pickup.grabbedBy == MetaPort.Instance.ownerId // check if already grabbed by us + || pickup.grabbedBy == "" || !pickup.disallowTheft)) // check if not grabbed or allows theft + canTakeOwnership = true; + + if (!canTakeOwnership // if we can't take ownership, don't grab, unless there is no syncing at all (local object) + && (objectSync || spawnable || pickup )) + return false; + + if (pickup) + { + pickup.grabbedBy = MetaPort.Instance.ownerId; + pickup._grabStartTime = Time.time; + } + if (spawnable) spawnable.isPhysicsSynced = true; + if (objectSync) objectSync.isPhysicsSynced = true; + + return true; + }; + + _physicsGun.OnObjectReleased = o => + { + // CVRObjectSync objectSync = o.GetComponentInParent(); + // if (objectSync != null && objectSync.SyncType == 0) + // objectSync.isPhysicsSynced = false; + // + // CVRSpawnable spawnable = o.GetComponentInParent(); + // if (spawnable != null) + // { + // CVRSyncHelper.PropData propData = CVRSyncHelper.Props.Find(match => match.InstanceId == spawnable.instanceId); + // if (propData != null && (propData.syncType == 0 || propData.syncedBy == MetaPort.Instance.ownerId)) + // spawnable.isPhysicsSynced = false; + // } + + // CVRPickupObject pickup = o.GetComponentInParent(); + // if (pickup != null && pickup.grabbedBy == MetaPort.Instance.ownerId) + // pickup.grabbedBy = ""; + + return false; + }; + } + + private void OnDestroy() + { + // stop listening for events + _physicsGun.OnObjectGrabbed = null; + } +} \ No newline at end of file diff --git a/PhysicsGunMod/Components/PhysicsGunInteractionBehavior.cs b/PhysicsGunMod/Components/PhysicsGunInteractionBehavior.cs new file mode 100644 index 0000000..4364b58 --- /dev/null +++ b/PhysicsGunMod/Components/PhysicsGunInteractionBehavior.cs @@ -0,0 +1,570 @@ +using ABI_RC.Core.Player; +using ABI_RC.Systems.InputManagement; +using UnityEngine; +using UnityEngine.Events; +using UnityEngine.Scripting.APIUpdating; + +/* + * Physics Gun script from repository - https://github.com/Laumania/Unity3d-PhysicsGun + * Created by: + * Mads Laumann, https://github.com/laumania + * WarmedxMints, https://github.com/WarmedxMints + * + * Original/initial script "Gravity Gun": https://pastebin.com/w1G8m3dH + * Original author: Jake Perry, reddit.com/user/nandos13 +*/ + +namespace NAK.PhysicsGunMod.Components; + +public class PhysicsGunInteractionBehavior : MonoBehaviour +{ + public static PhysicsGunInteractionBehavior Instance; + + [Header("LayerMask")] [Tooltip("The layer which the gun can grab objects from")] [SerializeField] + private LayerMask _grabLayer; + + [SerializeField] private Camera _camera; + + [Header("Input Setting")] [Space(10)] public KeyCode Rotate = KeyCode.R; + public KeyCode SnapRotation = KeyCode.LeftShift; + public KeyCode SwitchAxis = KeyCode.Tab; + public KeyCode RotateZ = KeyCode.Space; + public KeyCode RotationSpeedIncrease = KeyCode.LeftControl; + public KeyCode ResetRotation = KeyCode.LeftAlt; + + /// The rigidbody we are currently holding + private Rigidbody _grabbedRigidbody; + + /// The offset vector from the object's position to hit point, in local space + private Vector3 _hitOffsetLocal; + + /// The distance we are holding the object at + private float _currentGrabDistance; + + /// The interpolation state when first grabbed + private RigidbodyInterpolation _initialInterpolationSetting; + + /// The difference between player & object rotation, updated when picked up or when rotated by the player + private Quaternion _rotationDifference; + + /// The start point for the Laser. This will typically be on the end of the gun + [SerializeField] private Transform _laserStartPoint; + + /// Tracks player input to rotate current object. Used and reset every fixedupdate call + private Vector3 _rotationInput = Vector3.zero; + + [Header("Rotation Settings")] [Tooltip("Transform of the player, that rotations should be relative to")] + public Transform PlayerTransform; + + [SerializeField] private float _rotationSenstivity = 10f; + + public float SnapRotationDegrees = 45f; + [SerializeField] private float _snappedRotationSens = 25f; + [SerializeField] private float _rotationSpeed = 10f; + + private Quaternion _desiredRotation = Quaternion.identity; + + [SerializeField] [Tooltip("Input values above this will be considered and intentional change in rotation")] + private float _rotationTollerance = 0.8f; + + private bool m_UserRotation; + + public bool UserRotation + { + get => m_UserRotation; + private set + { + if (m_UserRotation == value) + return; + + m_UserRotation = value; + OnRotation?.Invoke(value); + } + } + + private bool m_SnapRotation; + + private bool _snapRotation + { + get => m_SnapRotation; + set + { + if (m_SnapRotation == value) + return; + + m_SnapRotation = value; + OnRotationSnapped?.Invoke(value); + } + } + + private bool m_RotationAxis; + + private bool _rotationAxis + { + get => m_RotationAxis; + set + { + if (m_RotationAxis == value) + return; + + m_RotationAxis = value; + OnAxisChanged?.Invoke(value); + } + } + + private Vector3 _lockedRot; + + private Vector3 _forward; + private Vector3 _up; + private Vector3 _right; + + //ScrollWheel ObjectMovement + private Vector3 _scrollWheelInput = Vector3.zero; + + [Header("Scroll Wheel Object Movement")] [Space(5)] [SerializeField] + private float _scrollWheelSensitivity = 5f; + + [SerializeField] [Tooltip("The min distance the object can be from the player")] + private float _minObjectDistance = 2.5f; + + /// The maximum distance at which a new object can be picked up + [SerializeField] [Tooltip("The maximum distance at which a new object can be picked up")] + private float _maxGrabDistance = 50f; + + private bool _distanceChanged; + + //Vector3.Zero and Vector2.zero create a new Vector3 each time they are called so these simply save that process and a small amount of cpu runtime. + private readonly Vector3 _zeroVector3 = Vector3.zero; + private readonly Vector3 _oneVector3 = Vector3.one; + private readonly Vector3 _zeroVector2 = Vector2.zero; + + private bool _justReleased; + private bool _wasKinematic; + + [Serializable] + public class BoolEvent : UnityEvent {} + + [Serializable] + public class GrabEvent : UnityEvent {} + + [Header("Events")] [Space(10)] + public BoolEvent OnRotation; + public BoolEvent OnRotationSnapped; + public BoolEvent OnAxisChanged; + + public GrabEvent OnObjectGrabbed; + public Func OnObjectReleased; + + // pre event for OnPreGrabbedObject, return false to cancel grab + public Func OnPreGrabbedObject; + // public Action OnGrabbedObject; + + //public properties for the Axis Arrows. These are optional and can be safely removed + // public Vector3 CurrentForward => _forward; + // public Vector3 CurrentUp => _up; + // public Vector3 CurrentRight => _right; + + /// The transfor of the rigidbody we are holding + public Transform CurrentGrabbedTransform { get; private set; } + + //public properties for the Line Renderer + public Vector3 StartPoint { get; private set; } + public Vector3 MidPoint { get; private set; } + public Vector3 EndPoint { get; private set; } + + private void Start() + { + if (Instance != null + && Instance != this) + { + Destroy(this); + return; + } + Instance = this; + gameObject.AddComponent(); + + _camera = PlayerSetup.Instance.GetActiveCamera().GetComponent(); + PlayerTransform = PlayerSetup.Instance.transform; // TODO: this might be fucked in VR + } + + private void OnDestroy() + { + if (Instance == this) + Instance = null; + } + + private void Update() + { + if (!Input.GetMouseButton(0)) + { + // We are not holding the mouse button. Release the object and return before checking for a new one + if (_grabbedRigidbody != null) ReleaseObject(); + + _justReleased = false; + return; + } + + if (_grabbedRigidbody == null && !_justReleased) + { + // We are not holding an object, look for one to pick up + Ray ray = CenterRay(); + RaycastHit hit; + + //Just so These aren't included in a build +#if UNITY_EDITOR + Debug.DrawRay(ray.origin, ray.direction * _maxGrabDistance, Color.blue, 0.01f); +#endif + if (Physics.Raycast(ray, out hit, _maxGrabDistance, _grabLayer)) + // Don't pick up kinematic rigidbodies (they can't move) + if (hit.rigidbody != null /*&& !hit.rigidbody.isKinematic*/) + { + // Check if we are allowed to pick up this object + if (OnPreGrabbedObject != null + && !OnPreGrabbedObject(hit.rigidbody.gameObject)) + return; + + // Track rigidbody's initial information + _grabbedRigidbody = hit.rigidbody; + _wasKinematic = _grabbedRigidbody.isKinematic; + _grabbedRigidbody.isKinematic = false; + _grabbedRigidbody.freezeRotation = true; + _initialInterpolationSetting = _grabbedRigidbody.interpolation; + _rotationDifference = Quaternion.Inverse(PlayerTransform.rotation) * _grabbedRigidbody.rotation; + _hitOffsetLocal = hit.transform.InverseTransformVector(hit.point - hit.transform.position); + _currentGrabDistance = hit.distance; // Vector3.Distance(ray.origin, hit.point); + CurrentGrabbedTransform = _grabbedRigidbody.transform; + // Set rigidbody's interpolation for proper collision detection when being moved by the player + _grabbedRigidbody.interpolation = RigidbodyInterpolation.Interpolate; + + OnObjectGrabbed?.Invoke(_grabbedRigidbody.gameObject); + +#if UNITY_EDITOR + Debug.DrawRay(hit.point, hit.normal * 10f, Color.red, 10f); +#endif + } + } + else if (_grabbedRigidbody != null) + { + UserRotation = Input.GetKey(Rotate); + + if (Input.GetKeyDown(Rotate)) + _desiredRotation = _grabbedRigidbody.rotation; + + if (Input.GetKey(ResetRotation)) + { + _desiredRotation = Quaternion.identity; + } + + // We are already holding an object, listen for rotation input + if (Input.GetKey(Rotate)) + { + var rotateZ = Input.GetKey(RotateZ); + + var increaseSens = Input.GetKey(RotationSpeedIncrease) ? 2.5f : 1f; + + if (Input.GetKeyDown(SwitchAxis)) + { + _rotationAxis = !_rotationAxis; + + OnAxisChanged?.Invoke(_rotationAxis); + } + + //Snap Object nearest _snapRotationDegrees + if (Input.GetKeyDown(SnapRotation)) + { + _snapRotation = true; + + Vector3 newRot = _grabbedRigidbody.transform.rotation.eulerAngles; + + newRot.x = Mathf.Round(newRot.x / SnapRotationDegrees) * SnapRotationDegrees; + newRot.y = Mathf.Round(newRot.y / SnapRotationDegrees) * SnapRotationDegrees; + newRot.z = Mathf.Round(newRot.z / SnapRotationDegrees) * SnapRotationDegrees; + + Quaternion rot = Quaternion.Euler(newRot); + + _desiredRotation = rot; + //_grabbedRigidbody.MoveRotation(rot); + } + else if (Input.GetKeyUp(SnapRotation)) + { + _snapRotation = false; + } + + var x = Input.GetAxisRaw("Mouse X"); + var y = Input.GetAxisRaw("Mouse Y"); + + if (Mathf.Abs(x) > _rotationTollerance) + { + _rotationInput.x = rotateZ ? 0f : x * _rotationSenstivity * increaseSens; + _rotationInput.z = rotateZ ? x * _rotationSenstivity * increaseSens : 0f; + } + + if (Mathf.Abs(y) > _rotationTollerance) _rotationInput.y = y * _rotationSenstivity * increaseSens; + } + else + { + _snapRotation = false; + } + + var direction = Input.GetAxis("Mouse ScrollWheel"); + + //Optional Keyboard inputs + if (Input.GetKeyDown(KeyCode.T)) + direction = -0.1f; + else if (Input.GetKeyDown(KeyCode.G)) + direction = 0.1f; + + if (Mathf.Abs(direction) > 0 && CheckObjectDistance(direction)) + { + _distanceChanged = true; + _scrollWheelInput = PlayerTransform.forward * (_scrollWheelSensitivity * direction); + } + else + { + _scrollWheelInput = _zeroVector3; + } + + if (Input.GetMouseButtonDown(1)) + { + //To prevent warnings in the inpector + _grabbedRigidbody.collisionDetectionMode = !_wasKinematic + ? CollisionDetectionMode.ContinuousSpeculative + : CollisionDetectionMode.Continuous; + _grabbedRigidbody.isKinematic = _wasKinematic = !_wasKinematic; + + _justReleased = true; + ReleaseObject(); + } + } + } + + private void FixedUpdate() + { + if (_grabbedRigidbody) + { + // We are holding an object, time to rotate & move it + Ray ray = CenterRay(); + + UpdateRotationAxis(); + +#if UNITY_EDITOR + Debug.DrawRay(_grabbedTransform.position, _up * 5f , Color.green); + Debug.DrawRay(_grabbedTransform.position, _right * 5f , Color.red); + Debug.DrawRay(_grabbedTransform.position, _forward * 5f , Color.blue); +#endif + // Apply any intentional rotation input made by the player & clear tracked input + Quaternion intentionalRotation = Quaternion.AngleAxis(_rotationInput.z, _forward) * + Quaternion.AngleAxis(_rotationInput.y, _right) * + Quaternion.AngleAxis(-_rotationInput.x, _up) * _desiredRotation; + Quaternion relativeToPlayerRotation = PlayerTransform.rotation * _rotationDifference; + + if (UserRotation && _snapRotation) + { + //Add mouse movement to vector so we can measure the amount of movement + _lockedRot += _rotationInput; + + //If the mouse has moved far enough to rotate the snapped object + if (Mathf.Abs(_lockedRot.x) > _snappedRotationSens || Mathf.Abs(_lockedRot.y) > _snappedRotationSens || + Mathf.Abs(_lockedRot.z) > _snappedRotationSens) + { + for (var i = 0; i < 3; i++) + if (_lockedRot[i] > _snappedRotationSens) + _lockedRot[i] += SnapRotationDegrees; + else if (_lockedRot[i] < -_snappedRotationSens) + _lockedRot[i] += -SnapRotationDegrees; + else + _lockedRot[i] = 0; + + Quaternion q = Quaternion.AngleAxis(-_lockedRot.x, _up) * + Quaternion.AngleAxis(_lockedRot.y, _right) * + Quaternion.AngleAxis(_lockedRot.z, _forward) * _desiredRotation; + + Vector3 newRot = q.eulerAngles; + + newRot.x = Mathf.Round(newRot.x / SnapRotationDegrees) * SnapRotationDegrees; + newRot.y = Mathf.Round(newRot.y / SnapRotationDegrees) * SnapRotationDegrees; + newRot.z = Mathf.Round(newRot.z / SnapRotationDegrees) * SnapRotationDegrees; + + _desiredRotation = Quaternion.Euler(newRot); + + _lockedRot = _zeroVector2; + } + } + else + { + //Rotate the object to remain consistent with any changes in player's rotation + _desiredRotation = UserRotation ? intentionalRotation : relativeToPlayerRotation; + } + + // Remove all torque, reset rotation input & store the rotation difference for next FixedUpdate call + _grabbedRigidbody.angularVelocity = _zeroVector3; + _rotationInput = _zeroVector2; + _rotationDifference = Quaternion.Inverse(PlayerTransform.rotation) * _desiredRotation; + + // Calculate object's center position based on the offset we stored + // NOTE: We need to convert the local-space point back to world coordinates + // Get the destination point for the point on the object we grabbed + Vector3 holdPoint = ray.GetPoint(_currentGrabDistance) + _scrollWheelInput; + Vector3 centerDestination = holdPoint - CurrentGrabbedTransform.TransformVector(_hitOffsetLocal); + +#if UNITY_EDITOR + Debug.DrawLine(ray.origin, holdPoint, Color.blue, Time.fixedDeltaTime); +#endif + // Find vector from current position to destination + Vector3 toDestination = centerDestination - CurrentGrabbedTransform.position; + + // Calculate force + Vector3 force = toDestination / Time.fixedDeltaTime * 0.3f/* / _grabbedRigidbody.mass*/; + + //force += _scrollWheelInput; + // Remove any existing velocity and add force to move to final position + _grabbedRigidbody.velocity = _zeroVector3; + _grabbedRigidbody.AddForce(force, ForceMode.VelocityChange); + + //Rotate object + RotateGrabbedObject(); + + //We need to recalculte the grabbed distance as the object distance from the player has been changed + if (_distanceChanged) + { + _distanceChanged = false; + _currentGrabDistance = Vector3.Distance(ray.origin, holdPoint); + } + + //Update public properties + StartPoint = _laserStartPoint.transform.position; + MidPoint = holdPoint; + EndPoint = CurrentGrabbedTransform.TransformPoint(_hitOffsetLocal); + } + } + + private void RotateGrabbedObject() + { + if (_grabbedRigidbody == null) + return; + + // lerp to desired rotation + _grabbedRigidbody.MoveRotation(Quaternion.Lerp(_grabbedRigidbody.rotation, _desiredRotation, + Time.fixedDeltaTime * _rotationSpeed)); + } + + //Update Rotation axis based on movement + private void UpdateRotationAxis() + { + if (!_snapRotation) + { + _forward = PlayerTransform.forward; + _right = PlayerTransform.right; + _up = PlayerTransform.up; + + return; + } + + if (_rotationAxis) + { + _forward = CurrentGrabbedTransform.forward; + _right = CurrentGrabbedTransform.right; + _up = CurrentGrabbedTransform.up; + + return; + } + + NearestTranformDirection(CurrentGrabbedTransform, PlayerTransform, ref _up, ref _forward, ref _right); + } + + private void NearestTranformDirection(Transform transformToCheck, Transform referenceTransform, ref Vector3 up, + ref Vector3 forward, ref Vector3 right) + { + var directions = new List + { + transformToCheck.forward, + -transformToCheck.forward, + transformToCheck.up, + -transformToCheck.up, + transformToCheck.right, + -transformToCheck.right + }; + + //Find the up Vector + up = GetDirectionVector(directions, referenceTransform.up); + //Remove Vectors from list to prevent duplicates and the opposite vector being found in case where the player is at around a 45 degree angle to the object + directions.Remove(up); + directions.Remove(-up); + //Find the Forward Vector + forward = GetDirectionVector(directions, referenceTransform.forward); + //Remove used directions + directions.Remove(forward); + directions.Remove(-forward); + + right = GetDirectionVector(directions, referenceTransform.right); + } + + private Vector3 GetDirectionVector(List directions, Vector3 direction) + { + var maxDot = -Mathf.Infinity; + Vector3 ret = Vector3.zero; + + for (var i = 0; i < directions.Count; i++) + { + var dot = Vector3.Dot(direction, directions[i]); + + if (dot > maxDot) + { + ret = directions[i]; + maxDot = dot; + } + } + + return ret; + } + + /// Ray from center of the main camera's viewport forward + private Ray CenterRay() + { + return _camera.ViewportPointToRay(_oneVector3 * 0.5f); + } + + //Check distance is within range when moving object with the scroll wheel + private bool CheckObjectDistance(float direction) + { + Vector3 pointA = PlayerTransform.position; + Vector3 pointB = _grabbedRigidbody.position; + + var distance = Vector3.Distance(pointA, pointB); + + if (direction > 0) + return distance <= _maxGrabDistance; + + if (direction < 0) + return distance >= _minObjectDistance; + + return false; + } + + private void ReleaseObject() + { + if (OnObjectReleased != null) + OnObjectReleased(_grabbedRigidbody.gameObject); + + if (_grabbedRigidbody) + { + //Move rotation to desired rotation in case the lerp hasn't finished + //_grabbedRigidbody.MoveRotation(_desiredRotation); + // Reset the rigidbody to how it was before we grabbed it + _grabbedRigidbody.isKinematic = _wasKinematic; + _grabbedRigidbody.interpolation = _initialInterpolationSetting; + _grabbedRigidbody.freezeRotation = false; + _grabbedRigidbody = null; + } + + _scrollWheelInput = _zeroVector3; + CurrentGrabbedTransform = null; + UserRotation = false; + _snapRotation = false; + StartPoint = _zeroVector3; + MidPoint = _zeroVector3; + EndPoint = _zeroVector3; + + OnObjectGrabbed?.Invoke(null); + } +} \ No newline at end of file diff --git a/PhysicsGunMod/HarmonyPatches.cs b/PhysicsGunMod/HarmonyPatches.cs new file mode 100644 index 0000000..73a618c --- /dev/null +++ b/PhysicsGunMod/HarmonyPatches.cs @@ -0,0 +1,20 @@ +using ABI_RC.Systems.InputManagement; +using HarmonyLib; +using NAK.PhysicsGunMod.Components; +using UnityEngine; + +namespace NAK.PhysicsGunMod.HarmonyPatches; + +internal static class CVRInputManagerPatches +{ + [HarmonyPostfix] + [HarmonyPatch(typeof(CVRInputManager), nameof(CVRInputManager.Update))] + private static void Postfix_CVRInputManager_Update(ref CVRInputManager __instance) + { + if (PhysicsGunInteractionBehavior.Instance == null) + return; + + if (PhysicsGunInteractionBehavior.Instance.UserRotation) + __instance.lookVector = Vector2.zero; + } +} diff --git a/PhysicsGunMod/Main.cs b/PhysicsGunMod/Main.cs new file mode 100644 index 0000000..c3360ba --- /dev/null +++ b/PhysicsGunMod/Main.cs @@ -0,0 +1,43 @@ +using ABI_RC.Core.Util.AssetFiltering; +using MelonLoader; +using NAK.PhysicsGunMod.Components; +using NAK.PhysicsGunMod.HarmonyPatches; + +namespace NAK.PhysicsGunMod; + +public class PhysicsGunMod : MelonMod +{ + internal static MelonLogger.Instance Logger; + + public override void OnInitializeMelon() + { + Logger = LoggerInstance; + + // add to prop whitelist + SharedFilter._spawnableWhitelist.Add(typeof(PhysicsGunInteractionBehavior)); + + // add to event whitelist + SharedFilter._allowedEventComponents.Add(typeof(PhysicsGunInteractionBehavior)); + SharedFilter._allowedEventFunctions.Add(typeof(PhysicsGunInteractionBehavior), new List + { + "set_enabled", + // TODO: expose more methods like release ? + }); + + // apply patches + ApplyPatches(typeof(CVRInputManagerPatches)); + } + + private void ApplyPatches(Type type) + { + try + { + HarmonyInstance.PatchAll(type); + } + catch (Exception e) + { + LoggerInstance.Msg($"Failed while patching {type.Name}!"); + LoggerInstance.Error(e); + } + } +} \ No newline at end of file diff --git a/PhysicsGunMod/ModSettings.cs b/PhysicsGunMod/ModSettings.cs new file mode 100644 index 0000000..86e4b48 --- /dev/null +++ b/PhysicsGunMod/ModSettings.cs @@ -0,0 +1,12 @@ +using MelonLoader; + +namespace NAK.PhysicsGunMod; + +internal static class ModSettings +{ + internal const string ModName = nameof(PhysicsGunMod); + internal const string ASM_SettingsCategory = "Physics Gun Mod"; + + private static readonly MelonPreferences_Category Category = + MelonPreferences.CreateCategory(ModName); +} \ No newline at end of file diff --git a/PhysicsGunMod/PhysicsGunMod.csproj b/PhysicsGunMod/PhysicsGunMod.csproj new file mode 100644 index 0000000..22588f8 --- /dev/null +++ b/PhysicsGunMod/PhysicsGunMod.csproj @@ -0,0 +1,37 @@ + + + + + netstandard2.1 + + + + + + + + + + + + + + + + + + + + + + + $(MsBuildThisFileDirectory)\..\.ManagedLibs\BTKUILib.dll + False + + + $(MsBuildThisFileDirectory)\..\.ManagedLibs\ActionMenu.dll + False + + + + \ No newline at end of file diff --git a/PhysicsGunMod/Properties/AssemblyInfo.cs b/PhysicsGunMod/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..e42ab03 --- /dev/null +++ b/PhysicsGunMod/Properties/AssemblyInfo.cs @@ -0,0 +1,32 @@ +using MelonLoader; +using NAK.PhysicsGunMod.Properties; +using System.Reflection; + +[assembly: AssemblyVersion(AssemblyInfoParams.Version)] +[assembly: AssemblyFileVersion(AssemblyInfoParams.Version)] +[assembly: AssemblyInformationalVersion(AssemblyInfoParams.Version)] +[assembly: AssemblyTitle(nameof(NAK.PhysicsGunMod))] +[assembly: AssemblyCompany(AssemblyInfoParams.Author)] +[assembly: AssemblyProduct(nameof(NAK.PhysicsGunMod))] + +[assembly: MelonInfo( + typeof(NAK.PhysicsGunMod.PhysicsGunMod), + nameof(NAK.PhysicsGunMod), + AssemblyInfoParams.Version, + AssemblyInfoParams.Author, + downloadLink: "https://github.com/NotAKidOnSteam/NAK_CVR_Mods/tree/main/PhysicsGunMod" +)] + +[assembly: MelonGame("Alpha Blend Interactive", "ChilloutVR")] +[assembly: MelonPlatform(MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)] +[assembly: MelonPlatformDomain(MelonPlatformDomainAttribute.CompatibleDomains.MONO)] +[assembly: MelonColor(255, 241, 200, 82)] +[assembly: MelonAuthorColor(255, 114, 17, 25)] +[assembly: HarmonyDontPatchAll] + +namespace NAK.PhysicsGunMod.Properties; +internal static class AssemblyInfoParams +{ + public const string Version = "1.0.0"; + public const string Author = "NotAKidoS"; +} \ No newline at end of file diff --git a/PhysicsGunMod/README.md b/PhysicsGunMod/README.md new file mode 100644 index 0000000..480559e --- /dev/null +++ b/PhysicsGunMod/README.md @@ -0,0 +1,28 @@ +# AvatarScaleMod + +Proof of concept mod to add Avatar Scaling to any avatar. This is local-only, but I may toy with using Mod Network. + +Legit threw this together in three hours. ChilloutVR handles all the hard stuff already with its existing animation-clip-based Avatar Scaling. + +https://github.com/NotAKidOnSteam/NAK_CVR_Mods/assets/37721153/7405cef5-fd68-4103-8c18-b3164029eab1 + +## Notes: +* Constraint scaling partially conflicts with avatars run through my [Avatar Scale Tool](https://github.com/NotAKidOnSteam/AvatarScaleTool). +* This is local-only, at least unless I bother with Mod Network. +* The entire thing is pretty messy and I am unsure of the performance impact, especially with scaling all lights, audio, & constraints. + +## Relevant Feedback Posts: +https://feedback.abinteractive.net/p/built-in-avatar-scaling-system + +This mod is me creating the system I wanted when I wrote the above feedback post. + +--- + +Here is the block of text where I tell you this mod is not affiliated with or endorsed by ABI. +https://documentation.abinteractive.net/official/legal/tos/#7-modding-our-games + +> This mod is an independent creation not affiliated with, supported by, or approved by Alpha Blend Interactive. + +> Use of this mod is done so at the user's own risk and the creator cannot be held responsible for any issues arising from its use. + +> To the best of my knowledge, I have adhered to the Modding Guidelines established by Alpha Blend Interactive. diff --git a/PhysicsGunMod/format.json b/PhysicsGunMod/format.json new file mode 100644 index 0000000..0a4fe82 --- /dev/null +++ b/PhysicsGunMod/format.json @@ -0,0 +1,23 @@ +{ + "_id": 126, + "name": "BetterCalibration", + "modversion": "1.0.0", + "gameversion": "2022r173", + "loaderversion": "0.6.1", + "modtype": "Mod", + "author": "NotAKidoS", + "description": "Mod to improve the calibration process for FBT.", + "searchtags": [ + "IK", + "FBT", + "VRIK", + "calibration", + ], + "requirements": [ + "None" + ], + "downloadlink": "https://github.com/NotAKidOnSteam/NAK_CVR_Mods/releases/download/r3/BetterCalibration.dll", + "sourcelink": "https://github.com/NotAKidOnSteam/NAK_CVR_Mods/tree/main/BetterCalibration/", + "changelog": "", + "embedcolor": "9b59b6" +} \ No newline at end of file