attack flow - breaking and blocking input, roll improvements
This commit is contained in:
@@ -13,6 +13,11 @@ namespace Invector.vMelee
|
||||
|
||||
public class bMeleeAttackControl : StateMachineBehaviour
|
||||
{
|
||||
// --- MODIFICATION: Static variable to track the currently active attack state ---
|
||||
private static int activeAttackInstanceId = 0;
|
||||
private int myAttackInstanceId;
|
||||
// --- END MODIFICATION ---
|
||||
|
||||
[Header("Damage Window")]
|
||||
[Tooltip("NormalizedTime of Active Damage")]
|
||||
public float startDamage = 0.05f;
|
||||
@@ -37,10 +42,8 @@ namespace Invector.vMelee
|
||||
public float senselessTime;
|
||||
|
||||
[Header("Attack Flow")]
|
||||
[Tooltip("Check true in the last attack of your combo to reset the FSM attack triggers.")]
|
||||
public bool resetAttackTrigger;
|
||||
[Tooltip("Normalized time point to reset attack triggers. (Used only if Combo Timing Window is disabled)")]
|
||||
public float resetTriggerBeforeTime = 0.5f;
|
||||
[Tooltip("Normalized time point to start allowing the next attack input.")]
|
||||
public float blockInputBeforeTime = 0.5f;
|
||||
|
||||
[Header("Combo & Movement")]
|
||||
[Tooltip("Normalized time to unlock rotation, allowing the player to aim the next attack in a combo. Set to 1 to disable.")]
|
||||
@@ -62,7 +65,6 @@ namespace Invector.vMelee
|
||||
[Tooltip("How close the character should get to the target.")]
|
||||
public float stoppingDistance = 1.2f;
|
||||
|
||||
// --- MODIFIED: Switched to a real-time duration model ---
|
||||
[Header("Combo Timing Window")]
|
||||
[Tooltip("Enable a special timing window at the end of the attack to chain the next combo hit.")]
|
||||
public bool useComboTimingWindow = false;
|
||||
@@ -76,7 +78,6 @@ namespace Invector.vMelee
|
||||
|
||||
[Tooltip("The time scale to use during the combo window for a slow-motion effect.")]
|
||||
public float comboWindowTimeScale = 0.1f;
|
||||
// --- END MODIFICATION ---
|
||||
|
||||
[Header("Slow Motion Settings")]
|
||||
[Tooltip("Enable slow motion effect during this attack based on conditions below.")]
|
||||
@@ -104,25 +105,30 @@ namespace Invector.vMelee
|
||||
|
||||
private bThirdPersonController _characterController;
|
||||
private bool _isRotationLockedByThis;
|
||||
private bool _comboWindowEffectTriggered; // --- NEW ---: Tracks if the slow-mo effect has been fired.
|
||||
private bool _comboWindowEffectTriggered;
|
||||
private Animator _animator;
|
||||
|
||||
override public void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
|
||||
{
|
||||
if (_animator == null) _animator = animator;
|
||||
if (_animator == null) _animator = animator;
|
||||
|
||||
// --- MODIFICATION: Assign a unique ID to this state instance ---
|
||||
myAttackInstanceId = ++activeAttackInstanceId;
|
||||
// --- END MODIFICATION ---
|
||||
|
||||
mFighter = animator.GetComponent<vIAttackListener>();
|
||||
_characterController = animator.GetComponent<bThirdPersonController>();
|
||||
|
||||
if (Player.Instance != null)
|
||||
_autoTargettingInstance = Player.Instance.AutoTarget;
|
||||
|
||||
|
||||
if (_autoTargettingInstance == null && debug)
|
||||
Debug.LogWarning($"({damageType}) AutoTargetting instance not found. Rotation/Target features limited.");
|
||||
|
||||
isAttacking = true;
|
||||
isActive = false;
|
||||
m_hasScaledTime = false;
|
||||
_comboWindowEffectTriggered = false; // --- NEW ---: Reset the flag on enter.
|
||||
_comboWindowEffectTriggered = false;
|
||||
|
||||
if (_characterController != null)
|
||||
{
|
||||
@@ -134,26 +140,54 @@ namespace Invector.vMelee
|
||||
mFighter.OnEnableAttack();
|
||||
|
||||
if (debug)
|
||||
Debug.Log($"({damageType}) OnStateEnter. The trigger that started this state has been consumed by the Animator.");
|
||||
Debug.Log($"({damageType}, ID: {myAttackInstanceId}) OnStateEnter. Now the authoritative state.");
|
||||
|
||||
// --- MODIFICATION: Immediately block input on enter ---
|
||||
// This ensures the new state takes control right away.
|
||||
BlockAttack(true);
|
||||
// --- END MODIFICATION ---
|
||||
|
||||
if (attackTimeScaleStart < 0f) attackTimeScaleStart = startDamage;
|
||||
if (attackTimeScaleEnd < 0f) attackTimeScaleEnd = endDamage;
|
||||
}
|
||||
|
||||
void BlockAttack(bool block)
|
||||
{
|
||||
// --- MODIFICATION: Only allow the authoritative state to change the lock ---
|
||||
if (myAttackInstanceId != activeAttackInstanceId)
|
||||
{
|
||||
if(debug) Debug.Log($"({damageType}, ID: {myAttackInstanceId}) Tried to change block but I am not the active instance ({activeAttackInstanceId}). Ignoring.");
|
||||
return;
|
||||
}
|
||||
// --- END MODIFICATION ---
|
||||
|
||||
if (Player.Instance != null)
|
||||
{
|
||||
var meleeInput = Player.Instance.MeleeCombatInput;
|
||||
if (meleeInput != null)
|
||||
{
|
||||
if (meleeInput.BlockAttack != block) // Only log/change if there is a change
|
||||
{
|
||||
if(debug) Debug.Log($"({damageType}, ID: {myAttackInstanceId}) Setting BlockAttack to: {block}");
|
||||
meleeInput.BlockAttack = block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override public void OnStateUpdate(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
|
||||
{
|
||||
if (Player.Instance.ActiveWeaponTrail)
|
||||
Player.Instance.ActiveWeaponTrail.m_colorMultiplier = Color.white + Color.red * damageMultiplier;
|
||||
|
||||
|
||||
float currentNormalizedTime = stateInfo.normalizedTime % 1;
|
||||
if (currentNormalizedTime == 0 && stateInfo.normalizedTime > 0.5f) currentNormalizedTime = 1f;
|
||||
|
||||
// --- NEW: Centralized combo logic management ---
|
||||
|
||||
ManageComboLogic(currentNormalizedTime);
|
||||
|
||||
|
||||
if (_characterController != null && _characterController.lockRotation)
|
||||
AttemptRotationTowardsAutoTarget(animator);
|
||||
|
||||
AttemptRotationTowardsAutoTarget(animator);
|
||||
|
||||
AttemptPositionLerp(animator);
|
||||
UpdateRotationLock(currentNormalizedTime);
|
||||
|
||||
@@ -173,16 +207,16 @@ namespace Invector.vMelee
|
||||
ActiveDamage(animator, false);
|
||||
}
|
||||
|
||||
if (isAttacking && currentNormalizedTime > endDamage)
|
||||
if (isAttacking && currentNormalizedTime > endDamage)
|
||||
{
|
||||
if (mFighter != null) mFighter.OnDisableAttack();
|
||||
isAttacking = false;
|
||||
isAttacking = false;
|
||||
}
|
||||
}
|
||||
|
||||
override public void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
|
||||
{
|
||||
if (debug) Debug.Log($"({damageType}) OnStateExit.");
|
||||
if (debug) Debug.Log($"({damageType}, ID: {myAttackInstanceId}) OnStateExit.");
|
||||
|
||||
if (isActive)
|
||||
{
|
||||
@@ -195,68 +229,53 @@ namespace Invector.vMelee
|
||||
|
||||
isAttacking = false;
|
||||
m_hasScaledTime = false;
|
||||
|
||||
// The TimeController now reliably handles resetting the time scale via its own coroutine.
|
||||
// A manual reset here is no longer necessary and could cause conflicts.
|
||||
/*
|
||||
if (mFighter != null && resetAttackTrigger)
|
||||
{
|
||||
mFighter.ResetAttackTriggers();
|
||||
if (debug) Debug.Log($"({damageType}) Final trigger reset on exit due to 'Reset Attack Trigger' flag.");
|
||||
}
|
||||
*/
|
||||
|
||||
if (_comboWindowEffectTriggered && TimeController.Instance != null)
|
||||
{
|
||||
_comboWindowEffectTriggered = false;
|
||||
TimeController.Instance.Reset();
|
||||
TimeController.Instance.Reset();
|
||||
}
|
||||
|
||||
if (_characterController != null && _isRotationLockedByThis)
|
||||
{
|
||||
_characterController.lockRotation = false;
|
||||
_isRotationLockedByThis = false;
|
||||
}
|
||||
|
||||
// --- MODIFICATION: When exiting, ensure the attack is unblocked. ---
|
||||
// This is a safety net. If this was the last attack in a combo,
|
||||
// we need to make sure the input is unlocked for future actions.
|
||||
BlockAttack(false);
|
||||
// --- END MODIFICATION ---
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Manages the combo logic, including input window and time scale effects.
|
||||
/// </summary>
|
||||
private void ManageComboLogic(float currentNormalizedTime)
|
||||
{
|
||||
if (!useComboTimingWindow)
|
||||
if (blockInputBeforeTime > 0f)
|
||||
{
|
||||
// Fallback to old logic if new system is disabled for this state
|
||||
if (resetAttackTrigger && currentNormalizedTime >= resetTriggerBeforeTime)
|
||||
if (currentNormalizedTime >= blockInputBeforeTime)
|
||||
{
|
||||
if (mFighter != null)
|
||||
mFighter.ResetAttackTriggers();
|
||||
BlockAttack(false); // Unlock input
|
||||
}
|
||||
else
|
||||
{
|
||||
BlockAttack(true); // Block input
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!useComboTimingWindow) return;
|
||||
|
||||
// --- FIX 2: Corrected window logic ---
|
||||
bool isInsideWindow = currentNormalizedTime >= comboWindowStartTime;
|
||||
|
||||
if (isInsideWindow)
|
||||
{
|
||||
// --- We are INSIDE the combo window ---
|
||||
// We STOP resetting the trigger to allow the player's input to register.
|
||||
|
||||
if (!_comboWindowEffectTriggered && TimeController.Instance != null)
|
||||
{
|
||||
_comboWindowEffectTriggered = true;
|
||||
|
||||
TimeController.Instance.SetTimeScaleForRealTimeSec(comboWindowTimeScale, comboWindowDuration, false);
|
||||
|
||||
if (debug) Debug.Log($"({damageType}) COMBO WINDOW OPEN. Accepting input. Animator mode set to UnscaledTime.");
|
||||
if (debug) Debug.Log($"({damageType}) COMBO WINDOW OPEN. Accepting input.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// --- We are OUTSIDE the combo window ---
|
||||
// We continuously reset the trigger to consume any premature/late input.
|
||||
if (mFighter != null)
|
||||
mFighter.ResetAttackTriggers();
|
||||
}
|
||||
}
|
||||
|
||||
// --- Other methods remain unchanged ---
|
||||
|
||||
Reference in New Issue
Block a user