attack flow - breaking and blocking input, roll improvements

This commit is contained in:
2025-09-10 12:33:13 +02:00
parent 9d6c237088
commit 058920055b
9 changed files with 1219 additions and 491 deletions

View File

@@ -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 ---