diff --git a/Assets/Scripts/Characters/LockOnCameraStateOverrideTarget.cs b/Assets/Scripts/Characters/LockOnCameraStateOverrideTarget.cs new file mode 100644 index 000000000..91dc2e470 --- /dev/null +++ b/Assets/Scripts/Characters/LockOnCameraStateOverrideTarget.cs @@ -0,0 +1,16 @@ +using UnityEngine; + +namespace Beyond +{ + /// + /// Marks a target that should override the lock-on camera state when close. + /// + public sealed class LockOnCameraStateOverrideTarget : MonoBehaviour + { + [Tooltip("Camera state to use while locked on this target.")] + public string cameraStateName = "LockOnBig"; + + [Tooltip("Max distance to apply the override. Set per enemy as needed.")] + public float maxDistance = 6f; + } +} diff --git a/Assets/Scripts/Characters/LockOnCameraStateOverrideTarget.cs.meta b/Assets/Scripts/Characters/LockOnCameraStateOverrideTarget.cs.meta new file mode 100644 index 000000000..2c30b9d84 --- /dev/null +++ b/Assets/Scripts/Characters/LockOnCameraStateOverrideTarget.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: d26a247418ec4c849b93fb72ca6279e0 \ No newline at end of file diff --git a/Assets/Scripts/Effects/LockOnCameraStateOverride.cs b/Assets/Scripts/Effects/LockOnCameraStateOverride.cs new file mode 100644 index 000000000..98c1e6cbb --- /dev/null +++ b/Assets/Scripts/Effects/LockOnCameraStateOverride.cs @@ -0,0 +1,156 @@ +using UnityEngine; + +namespace Beyond +{ + /// + /// Overrides camera state while locked-on to specific targets. + /// + [RequireComponent(typeof(bLockOn))] + public sealed class LockOnCameraStateOverride : MonoBehaviour + { + private bLockOn _lockOn; + private bThirdPersonInput _input; + + private bool _isOverriding; + private string _overrideState; + private Transform _overrideTarget; + + private struct CameraStateSnapshot + { + public bool changeCameraState; + public string customCameraState; + public bool smoothCameraState; + } + + private CameraStateSnapshot _previousState; + + private void Awake() + { + _lockOn = GetComponent(); + _input = GetComponent(); + } + + private void OnEnable() + { + if (_lockOn != null) + { + _lockOn.onLockOnTarget.AddListener(OnLockOn); + _lockOn.onUnLockOnTarget.AddListener(OnUnlockOn); + } + } + + private void OnDisable() + { + if (_lockOn != null) + { + _lockOn.onLockOnTarget.RemoveListener(OnLockOn); + _lockOn.onUnLockOnTarget.RemoveListener(OnUnlockOn); + } + + StopOverrideIfNeeded(); + } + + private void Update() + { + if (!_isOverriding) + { + return; + } + + if (_overrideTarget == null) + { + StopOverrideIfNeeded(); + return; + } + + var targetOverride = _overrideTarget.GetComponent(); + if (targetOverride == null) + { + StopOverrideIfNeeded(); + return; + } + + if (!IsWithinDistance(_overrideTarget, targetOverride.maxDistance)) + { + StopOverrideIfNeeded(); + } + } + + private void OnLockOn(Transform target) + { + if (target == null || _input == null) + { + return; + } + + var targetOverride = target.GetComponent(); + if (targetOverride == null) + { + return; + } + + if (!IsWithinDistance(target, targetOverride.maxDistance)) + { + return; + } + + _previousState = new CameraStateSnapshot + { + changeCameraState = _input.changeCameraState, + customCameraState = _input.customCameraState, + smoothCameraState = _input.smoothCameraState + }; + + _overrideState = targetOverride.cameraStateName; + _overrideTarget = target; + + _input.ChangeCameraStateWithLerp(_overrideState); + _isOverriding = true; + } + + private void OnUnlockOn(Transform target) + { + StopOverrideIfNeeded(); + } + + private void StopOverrideIfNeeded() + { + if (!_isOverriding || _input == null) + { + _isOverriding = false; + _overrideTarget = null; + return; + } + + // Only restore if we're still on the override state. + if (_input.changeCameraState && _input.customCameraState == _overrideState) + { + if (_previousState.changeCameraState) + { + _input.changeCameraState = true; + _input.customCameraState = _previousState.customCameraState; + _input.smoothCameraState = _previousState.smoothCameraState; + } + else + { + _input.ResetCameraState(); + } + } + + _isOverriding = false; + _overrideTarget = null; + _overrideState = null; + } + + private bool IsWithinDistance(Transform target, float maxDistance) + { + if (maxDistance <= 0f) + { + return true; + } + + var delta = target.position - transform.position; + return delta.sqrMagnitude <= maxDistance * maxDistance; + } + } +} diff --git a/Assets/Scripts/Effects/LockOnCameraStateOverride.cs.meta b/Assets/Scripts/Effects/LockOnCameraStateOverride.cs.meta new file mode 100644 index 000000000..3200e073f --- /dev/null +++ b/Assets/Scripts/Effects/LockOnCameraStateOverride.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 86ca4effe1f4a4b4dbd243af2bc93a29 \ No newline at end of file diff --git a/Assets/Scripts/InvectorDerivatives/bSimpleCombatAction.cs b/Assets/Scripts/InvectorDerivatives/bSimpleCombatAction.cs index 6f910c21f..bc8a5a249 100644 --- a/Assets/Scripts/InvectorDerivatives/bSimpleCombatAction.cs +++ b/Assets/Scripts/InvectorDerivatives/bSimpleCombatAction.cs @@ -12,9 +12,20 @@ namespace Beyond { if (controller.currentTarget.transform == null) return; + var summoner = controller.gameObject.GetComponent(); + if (summoner != null && !summoner.ShouldEngageMelee()) + { + controller.Stop(); + return; + } + if (controller.targetDistance <= controller.attackDistance && !controller.isBlocking) { controller.Stop(); + if (summoner != null) + { + summoner.NotifyMeleeAttack(); + } controller.Attack(); } else if (!controller.animatorStateInfos.HasAnyTag("Attack", "LockMovement", "CustomAction")) @@ -26,4 +37,4 @@ namespace Beyond else controller.Stop(); } } -} \ No newline at end of file +}