512 lines
20 KiB
C#
512 lines
20 KiB
C#
using Invector;
|
|
using Invector.vCharacterController;
|
|
using Invector.vEventSystems;
|
|
using Invector.vMelee;
|
|
using UnityEngine;
|
|
using System.Collections;
|
|
|
|
namespace Beyond
|
|
{
|
|
[vClassHeader("MELEE INPUT MANAGER", iconName = "inputIcon")]
|
|
public class bMeleeCombatInput : bThirdPersonInput, vIMeleeFighter
|
|
{
|
|
// ... (rest of your existing bMeleeCombatInput variables) ...
|
|
|
|
#region Variables
|
|
|
|
[vEditorToolbar("Inputs")]
|
|
[Header("Melee Inputs")]
|
|
public GenericInput weakAttackInput = new GenericInput("Mouse0", "RB", "RB");
|
|
public GenericInput strongAttackInput = new GenericInput("Alpha1", false, "RT", true, "RT", false);
|
|
public GenericInput blockInput = new GenericInput("R", "R", "R");
|
|
|
|
internal vMeleeManager meleeManager;
|
|
protected bool _isAttacking;
|
|
|
|
public bool isAttacking
|
|
{
|
|
get => _isAttacking || (cc != null && cc.IsAnimatorTag("Attack"));
|
|
protected set { _isAttacking = value; }
|
|
}
|
|
|
|
public bool isBlocking { get; protected set; }
|
|
|
|
public bool isArmed
|
|
{
|
|
get
|
|
{
|
|
return meleeManager != null && (meleeManager.rightWeapon != null || (meleeManager.leftWeapon != null &&
|
|
meleeManager.leftWeapon.meleeType != vMeleeType.OnlyDefense));
|
|
}
|
|
}
|
|
|
|
public bool isEquipping { get; protected set; }
|
|
|
|
[HideInInspector] public bool lockMeleeInput;
|
|
|
|
[Header("Dash Settings")]
|
|
[Tooltip("Speed at which the player rotates towards the target before dashing.")]
|
|
public float dashRotationSpeed = 15f;
|
|
[Tooltip("Angle threshold (degrees). If angle to target is less than this, player dashes immediately without pre-rotation.")]
|
|
public float dashFacingAngleThreshold = 10f;
|
|
[Tooltip("Maximum time (seconds) the pre-dash rotation will attempt before dashing anyway.")]
|
|
public float maxDashRotationTime = 0.5f;
|
|
|
|
private AutoTargetting autoTargeting;
|
|
private bool isRotatingAndDashing = false;
|
|
|
|
public void SetLockMeleeInput(bool value)
|
|
{
|
|
lockMeleeInput = value;
|
|
if (value)
|
|
{
|
|
isAttacking = false;
|
|
isBlocking = false;
|
|
}
|
|
}
|
|
|
|
public override void SetLockAllInput(bool value)
|
|
{
|
|
base.SetLockAllInput(value);
|
|
SetLockMeleeInput(value);
|
|
}
|
|
|
|
private MagicAttacks magicAttacks;
|
|
|
|
#endregion
|
|
|
|
public virtual bool lockInventory
|
|
{
|
|
get { return isAttacking || (cc != null && (cc.isDead || cc.customAction || cc.IsAnimatorTag("special") || cc.isRolling)); }
|
|
}
|
|
|
|
protected override void Start()
|
|
{
|
|
base.Start(); // This will call bThirdPersonInput's Start, which gets 'cc'
|
|
magicAttacks = GetComponent<MagicAttacks>();
|
|
autoTargeting = GetComponent<AutoTargetting>();
|
|
if (autoTargeting == null && Player.Instance != null && Player.Instance.gameObject == this.gameObject)
|
|
{
|
|
autoTargeting = Player.Instance.GetComponent<AutoTargetting>();
|
|
}
|
|
if (autoTargeting == null && cc != null && cc.transform.root != null)
|
|
{
|
|
autoTargeting = cc.transform.root.GetComponentInChildren<AutoTargetting>(true);
|
|
}
|
|
if (autoTargeting == null)
|
|
{
|
|
Debug.LogWarning("bMeleeCombatInput: AutoTargetting component not found. Dash towards target will require manual aiming.");
|
|
}
|
|
}
|
|
|
|
protected override void LateUpdate()
|
|
{
|
|
UpdateMeleeAnimations();
|
|
base.LateUpdate();
|
|
}
|
|
|
|
protected override void FixedUpdate()
|
|
{
|
|
base.FixedUpdate();
|
|
ShowCursor(showCursorOnStart);
|
|
}
|
|
|
|
protected override void InputHandle()
|
|
{
|
|
if (cc == null || cc.isDead) return;
|
|
|
|
base.InputHandle();
|
|
|
|
if (MeleeAttackConditions() && !lockMeleeInput)
|
|
{
|
|
MeleeWeakAttackInput();
|
|
MeleeStrongAttackInput();
|
|
BlockingInput();
|
|
}
|
|
else
|
|
{
|
|
ResetAttackTriggers();
|
|
if (!blockInput.GetButton())
|
|
{
|
|
isBlocking = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
#region MeleeCombat Input Methods
|
|
|
|
public virtual void MeleeWeakAttackInput()
|
|
{
|
|
if (cc.animator == null) return;
|
|
if (weakAttackInput.GetButtonDown() && MeleeAttackStaminaConditions())
|
|
{
|
|
TriggerWeakAttack();
|
|
}
|
|
}
|
|
|
|
public virtual void TriggerWeakAttack()
|
|
{
|
|
cc.animator.SetInteger(vAnimatorParameters.AttackID, AttackID);
|
|
cc.animator.SetTrigger(vAnimatorParameters.WeakAttack);
|
|
if (TimeController.Instance != null)
|
|
{
|
|
TimeController.Instance.Reset(); // Ensure normal time scale on attack start
|
|
}
|
|
}
|
|
|
|
public virtual void MeleeStrongAttackInput()
|
|
{
|
|
if (cc.animator == null) return;
|
|
if (strongAttackInput.GetButtonDown() &&
|
|
(!meleeManager.CurrentActiveAttackWeapon || meleeManager.CurrentActiveAttackWeapon.useStrongAttack) &&
|
|
MeleeAttackStaminaConditions())
|
|
{
|
|
TriggerStrongAttack();
|
|
}
|
|
}
|
|
|
|
public virtual void TriggerStrongAttack()
|
|
{
|
|
cc.animator.SetInteger(vAnimatorParameters.AttackID, AttackID);
|
|
cc.animator.SetTrigger(vAnimatorParameters.StrongAttack);
|
|
}
|
|
|
|
public virtual void BlockingInput()
|
|
{
|
|
if (cc.animator == null) return;
|
|
isBlocking = blockInput.GetButton() && cc.currentStamina > 0 && !cc.customAction &&
|
|
!cc.IsAnimatorTag("special") && !isAttacking;
|
|
}
|
|
|
|
public override void ControlRotation()
|
|
{
|
|
if (cc == null || cc.lockRotation) return;
|
|
|
|
if (cameraMain && !lockUpdateMoveDirection)
|
|
{
|
|
if (!cc.keepDirection)
|
|
{
|
|
cc.UpdateMoveDirection(cameraMain.transform);
|
|
}
|
|
}
|
|
|
|
if (tpCamera != null && tpCamera.lockTarget && cc.isStrafing && !cc.isRolling)
|
|
{
|
|
cc.RotateToPosition(tpCamera.lockTarget.position);
|
|
}
|
|
else
|
|
{
|
|
cc.ControlRotationType();
|
|
}
|
|
}
|
|
|
|
protected override void SprintInput()
|
|
{
|
|
if (lockSprintInput) return; // Check local lock first
|
|
if (sprintInput.useInput && cc != null)
|
|
{
|
|
bool canSprint = cc.useContinuousSprint ? sprintInput.GetButtonDown() : sprintInput.GetButton();
|
|
// Let base class (vThirdPersonInput) handle the cc.Sprint call if we don't override completely
|
|
// For now, let's assume your bThirdPersonInput's SprintInput or vThirdPersonInput's one is what you want.
|
|
// If it was base.SprintInput() before, and you have bThirdPersonInput overriding it, make sure that's intended.
|
|
// For simplicity, directly call cc.Sprint if bThirdPersonInput doesn't add much, or call base.SprintInput() if it does.
|
|
cc.Sprint(canSprint && !isAttacking);
|
|
}
|
|
}
|
|
protected override void CrouchInput()
|
|
{
|
|
if (lockCrouchInput) return;
|
|
base.CrouchInput(); // Call the base class logic from vThirdPersonInput/bThirdPersonInput
|
|
}
|
|
|
|
protected override void StrafeInput()
|
|
{
|
|
if (lockStrafeInput) return;
|
|
base.StrafeInput(); // Call the base class logic from vThirdPersonInput/bThirdPersonInput
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
|
#region Conditions
|
|
|
|
protected virtual bool MeleeAttackStaminaConditions()
|
|
{
|
|
if (meleeManager == null) meleeManager = GetComponent<vMeleeManager>();
|
|
if (meleeManager == null || cc == null) return false;
|
|
var result = cc.currentStamina - meleeManager.GetAttackStaminaCost();
|
|
return result >= 0;
|
|
}
|
|
|
|
public virtual bool MeleeAttackConditions()
|
|
{
|
|
if (meleeManager == null) meleeManager = GetComponent<vMeleeManager>();
|
|
return meleeManager != null && cc != null && cc.isGrounded && !cc.customAction && !cc.IsAnimatorTag("special") &&
|
|
!cc.isJumping && !cc.isCrouching && !cc.isRolling && !isEquipping &&
|
|
(cc.animator != null && !cc.animator.IsInTransition(cc.baseLayer)) && !isRotatingAndDashing;
|
|
}
|
|
|
|
// We override JumpInput, so we'll use the base JumpConditions if needed, or reimplement.
|
|
// vThirdPersonInput.JumpConditions():
|
|
// return !cc.customAction && !cc.isCrouching && cc.isGrounded && cc.GroundAngle() < cc.slopeLimit && cc.currentStamina >= cc.jumpStamina && !cc.isJumping && !cc.isRolling;
|
|
protected override bool JumpConditions() // This is from vThirdPersonInput
|
|
{
|
|
if (cc == null) return false;
|
|
bool baseConditions = !cc.customAction && !cc.isCrouching && cc.isGrounded && cc.GroundAngle() < cc.slopeLimit && cc.currentStamina >= cc.jumpStamina && !cc.isJumping && !cc.isRolling;
|
|
return baseConditions && !isAttacking && !isRotatingAndDashing; // Add our specific conditions
|
|
}
|
|
|
|
// We override RollInput, so we'll use the base RollConditions or reimplement.
|
|
// vThirdPersonInput.RollConditions():
|
|
// return (!cc.isRolling || cc.canRollAgain) && cc.isGrounded && !cc.customAction && cc.currentStamina > cc.rollStamina && !cc.isJumping && !cc.isSliding;
|
|
protected override bool RollConditions() // This is from vThirdPersonInput
|
|
{
|
|
if (cc == null) return false;
|
|
bool baseConditions = (!cc.isRolling || cc.canRollAgain) && cc.isGrounded && !cc.customAction && cc.currentStamina > cc.rollStamina && !cc.isJumping && !cc.isSliding;
|
|
// Add any bMeleeCombatInput specific conditions if necessary, like !isAttacking
|
|
return baseConditions && !isAttacking && !isRotatingAndDashing;
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
|
// MODIFIED JumpInput
|
|
protected override void JumpInput()
|
|
{
|
|
if (lockJumpInput) return; // From bThirdPersonInput
|
|
if (jumpInput.GetButtonDown() && JumpConditions()) // JumpConditions now includes !isAttacking & !isRotatingAndDashing
|
|
{
|
|
cc.Jump(true);
|
|
}
|
|
}
|
|
|
|
// MODIFIED RollInput
|
|
protected override void RollInput()
|
|
{
|
|
if (lockRollInput) return; // From bThirdPersonInput
|
|
if (cc == null) return;
|
|
if (isRotatingAndDashing) return;
|
|
|
|
// Use the overridden RollConditions which now includes !isAttacking and !isRotatingAndDashing
|
|
if (rollInput.GetButtonDown() && RollConditions())
|
|
{
|
|
bThirdPersonController beyondController = cc as bThirdPersonController;
|
|
if (beyondController == null)
|
|
{
|
|
Debug.LogError("cc is not a bThirdPersonController instance! Cannot Dash/Roll.");
|
|
if(cc != null) cc.Roll(); // Fallback to cc's roll if cast fails
|
|
return;
|
|
}
|
|
|
|
bool isInputBackwards = cc.input.z * -1f >= 0 && Mathf.Abs(cc.input.x) < 0.2f; // Or cc.rawInput.z if you use raw input for this decision
|
|
|
|
if (!isInputBackwards)
|
|
{
|
|
beyondController.Roll(); // Or cc.Roll() if bThirdPersonController doesn't override it meaningfully
|
|
}
|
|
else // Forward, neutral, or sideways input
|
|
{
|
|
if (autoTargeting != null && autoTargeting.CurrentTarget != null)
|
|
{
|
|
Transform target = autoTargeting.CurrentTarget.transform;
|
|
Vector3 directionToTarget = (target.position - cc.transform.position);
|
|
directionToTarget.y = 0;
|
|
|
|
if (directionToTarget.sqrMagnitude < 0.01f) // Very close
|
|
{
|
|
beyondController.Dash();
|
|
}
|
|
else
|
|
{
|
|
float angleToTarget = Vector3.Angle(cc.transform.forward, directionToTarget.normalized);
|
|
if (angleToTarget <= dashFacingAngleThreshold)
|
|
{
|
|
beyondController.Dash();
|
|
}
|
|
else
|
|
{
|
|
if (!isRotatingAndDashing) // Should be redundant due to the check at the start of the method
|
|
{
|
|
StartCoroutine(RotateAndDashCoroutine(target, beyondController));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
beyondController.Dash(); // No target, regular dash
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
protected virtual IEnumerator RotateAndDashCoroutine(Transform target, bThirdPersonController controller)
|
|
{
|
|
if (isRotatingAndDashing || controller == null) yield break;
|
|
isRotatingAndDashing = true;
|
|
|
|
bool previousLockRotation = controller.lockRotation;
|
|
controller.lockRotation = true;
|
|
|
|
bool wasStrafing = controller.isStrafing;
|
|
bool originalKeepDirection = controller.keepDirection;
|
|
|
|
if (wasStrafing && controller.isStrafing)
|
|
{
|
|
controller.Strafe();
|
|
}
|
|
controller.keepDirection = false;
|
|
|
|
float startTime = Time.time;
|
|
Vector3 directionToTarget;
|
|
Quaternion targetRotationQuaternion;
|
|
|
|
while (Time.time < startTime + maxDashRotationTime)
|
|
{
|
|
if (target == null || !target.gameObject.activeInHierarchy) break;
|
|
if (controller == null) { isRotatingAndDashing = false; yield break; }
|
|
|
|
directionToTarget = (target.position - controller.transform.position);
|
|
directionToTarget.y = 0;
|
|
|
|
if (directionToTarget.sqrMagnitude < 0.001f) break;
|
|
|
|
targetRotationQuaternion = Quaternion.LookRotation(directionToTarget.normalized);
|
|
controller.transform.rotation = Quaternion.Slerp(controller.transform.rotation, targetRotationQuaternion, Time.deltaTime * dashRotationSpeed);
|
|
|
|
if (Vector3.Angle(controller.transform.forward, directionToTarget.normalized) <= dashFacingAngleThreshold)
|
|
{
|
|
controller.transform.rotation = targetRotationQuaternion;
|
|
break;
|
|
}
|
|
yield return null;
|
|
}
|
|
|
|
if (controller == null) { isRotatingAndDashing = false; yield break; }
|
|
|
|
if (target != null && target.gameObject.activeInHierarchy &&
|
|
(Time.time >= startTime + maxDashRotationTime || Vector3.Angle(controller.transform.forward, (target.position - controller.transform.position).normalized) > 0.1f) )
|
|
{
|
|
directionToTarget = (target.position - controller.transform.position);
|
|
directionToTarget.y = 0;
|
|
if (directionToTarget.sqrMagnitude > 0.001f)
|
|
{
|
|
controller.transform.rotation = Quaternion.LookRotation(directionToTarget.normalized);
|
|
}
|
|
}
|
|
|
|
controller.lockRotation = previousLockRotation;
|
|
if (wasStrafing && !controller.isStrafing)
|
|
{
|
|
controller.Strafe();
|
|
}
|
|
controller.keepDirection = originalKeepDirection;
|
|
|
|
controller.Dash();
|
|
|
|
isRotatingAndDashing = false;
|
|
}
|
|
|
|
#region Melee Methods
|
|
|
|
public virtual void OnEnableAttack()
|
|
{
|
|
if (meleeManager == null) meleeManager = GetComponent<vMeleeManager>();
|
|
if (meleeManager == null || cc == null) return;
|
|
|
|
cc.currentStaminaRecoveryDelay = meleeManager.GetAttackStaminaRecoveryDelay();
|
|
cc.currentStamina -= meleeManager.GetAttackStaminaCost();
|
|
isAttacking = true;
|
|
if(cc!=null) cc.isSprinting = false;
|
|
}
|
|
|
|
public virtual void OnDisableAttack()
|
|
{
|
|
isAttacking = false;
|
|
}
|
|
|
|
public virtual void ResetAttackTriggers()
|
|
{
|
|
if (cc != null && cc.animator != null)
|
|
{
|
|
cc.animator.ResetTrigger(vAnimatorParameters.WeakAttack);
|
|
cc.animator.ResetTrigger(vAnimatorParameters.StrongAttack);
|
|
}
|
|
}
|
|
|
|
public virtual void BreakAttack(int breakAtkID)
|
|
{
|
|
ResetAttackTriggers();
|
|
OnRecoil(breakAtkID);
|
|
}
|
|
|
|
public virtual void OnRecoil(int recoilID)
|
|
{
|
|
if (cc != null && cc.animator != null)
|
|
{
|
|
cc.animator.SetInteger(vAnimatorParameters.RecoilID, recoilID);
|
|
cc.animator.SetTrigger(vAnimatorParameters.TriggerRecoil);
|
|
cc.animator.SetTrigger(vAnimatorParameters.ResetState);
|
|
cc.animator.ResetTrigger(vAnimatorParameters.WeakAttack);
|
|
cc.animator.ResetTrigger(vAnimatorParameters.StrongAttack);
|
|
}
|
|
}
|
|
|
|
public virtual void OnReceiveAttack(vDamage damage, vIMeleeFighter attacker)
|
|
{
|
|
if (cc == null) return;
|
|
if (magicAttacks != null && magicAttacks.shieldEffectIsActive) return;
|
|
|
|
if (!damage.ignoreDefense && isBlocking && meleeManager != null &&
|
|
meleeManager.CanBlockAttack(damage.sender.position))
|
|
{
|
|
var damageReduction = meleeManager.GetDefenseRate();
|
|
if (damageReduction > 0) damage.ReduceDamage(damageReduction);
|
|
if (attacker != null && meleeManager != null && meleeManager.CanBreakAttack())
|
|
{
|
|
attacker.BreakAttack(meleeManager.GetDefenseRecoilID());
|
|
}
|
|
meleeManager.OnDefense();
|
|
cc.currentStaminaRecoveryDelay = damage.staminaRecoveryDelay;
|
|
cc.currentStamina -= damage.staminaBlockCost;
|
|
}
|
|
damage.hitReaction = !isBlocking || damage.ignoreDefense;
|
|
cc.TakeDamage(damage);
|
|
}
|
|
|
|
public virtual vICharacter character { get { return cc; } }
|
|
|
|
#endregion
|
|
|
|
#region Update Animations
|
|
public virtual int defaultMoveSetID { get; set; }
|
|
public virtual bool overrideWeaponMoveSetID { get; set; }
|
|
public virtual int meleeMoveSetID
|
|
{
|
|
get
|
|
{
|
|
if (meleeManager == null) meleeManager = GetComponent<vMeleeManager>();
|
|
int id = meleeManager?.GetMoveSetID() ?? 0;
|
|
if (id == 0 || overrideWeaponMoveSetID) id = defaultMoveSetID;
|
|
return id;
|
|
}
|
|
}
|
|
public virtual void ResetMeleeAnimations()
|
|
{
|
|
if (meleeManager == null || cc == null || cc.animator == null) return;
|
|
cc.animator.SetBool(vAnimatorParameters.IsBlocking, false);
|
|
}
|
|
public virtual int AttackID { get { if (meleeManager == null) meleeManager = GetComponent<vMeleeManager>(); return meleeManager?.GetAttackID() ?? 0; } }
|
|
public virtual int DefenseID { get { if (meleeManager == null) meleeManager = GetComponent<vMeleeManager>(); return meleeManager?.GetDefenseID() ?? 0; } }
|
|
protected virtual void UpdateMeleeAnimations()
|
|
{
|
|
if (cc == null || cc.animator == null || meleeManager == null) return;
|
|
cc.animator.SetInteger(vAnimatorParameters.AttackID, AttackID);
|
|
cc.animator.SetInteger(vAnimatorParameters.DefenseID, DefenseID);
|
|
cc.animator.SetBool(vAnimatorParameters.IsBlocking, isBlocking);
|
|
cc.animator.SetFloat(vAnimatorParameters.MoveSet_ID, meleeMoveSetID, .2f, vTime.deltaTime);
|
|
isEquipping = cc.IsAnimatorTag("IsEquipping");
|
|
}
|
|
#endregion
|
|
}
|
|
} |