#if INVECTOR_PLAYABLESANIMATOR using System.Collections.Generic; using InvectorCore.Playables; using UnityEngine; namespace Invector.vMelee { using vEventSystems; /// /// PlayablesAnimator counterpart of . /// Serialized field names match the Animator behaviour so the PDM Animator Controller /// conversion copies the authored values over automatically. /// public class vMeleeAttackControlPDM : PlayablesStateBehaviour { // Anchor for the read-only exit time / end damage note; not real data. [vMeleeAttackControlInfo] [SerializeField] private int infoAnchor; [Tooltip("normalizedTime of Active Damage")] public float startDamage = 0.05f; [Tooltip("normalizedTime of Disable Damage")] public float endDamage = 0.9f; public int damageMultiplier; public int recoilID; public int reactionID; public vAttackType meleeAttackType = vAttackType.Unarmed; [Tooltip("You can use a name as reference to trigger a custom HitDamageParticle")] public string damageType; // Drawn by the vBodyPartList anchor below: a PropertyAttribute on a collection field targets // its elements, not the list, so the list UI has to hang off a scalar field instead. [HideInInspector] [Tooltip("This work with vMeleeManager to active vMeleeAttackObject from bodyPart and id")] public List bodyParts = new List { "RightLowerArm" }; public bool ignoreDefense; public bool activeRagdoll; [vHideInInspector("activeRagdoll"), Tooltip("Time to keep Ragdoll active")] public float senselessTime; [Tooltip("Check true in the last attack of your combo to reset the triggers")] public bool resetAttackTrigger; public bool debug; // Anchor the body part list UI hangs off; not real data. [vBodyPartList] [SerializeField] private int bodyPartsAnchor; // Behaviour instances are cloned per PDM row at runtime, so per-play state is safe here. [System.NonSerialized] private bool isActive; [System.NonSerialized] private bool isAttacking; [System.NonSerialized] private vIAttackListener mFighter; [System.NonSerialized] private vMeleeManager meleeManager; public override void OnStateEnter(in PlayablesStateContext context) { GameObject owner = ResolveOwner(context); mFighter = owner != null ? owner.GetComponent() : null; meleeManager = owner != null ? owner.GetComponent() : null; isActive = false; isAttacking = true; if (mFighter != null) mFighter.OnEnableAttack(); if (debug) Debug.Log("Enter " + damageType); } public override void OnStateUpdate(in PlayablesStateContext context) { float normalizedTime = context.normalizedTime % 1f; if (normalizedTime >= startDamage && normalizedTime <= endDamage && !isActive) { if (debug) Debug.Log(context.RowName + " attack " + damageType + " enable damage in " + System.Math.Round(normalizedTime, 2)); isActive = true; ActiveDamage(true); } else if (normalizedTime > endDamage && isActive) { if (debug) Debug.Log(context.RowName + " attack " + damageType + " disable damage in " + System.Math.Round(normalizedTime, 2)); isActive = false; ActiveDamage(false); } if (normalizedTime > endDamage && isAttacking) { isAttacking = false; if (mFighter != null) mFighter.OnDisableAttack(); } } public override void OnStateExit(in PlayablesStateContext context) { if (debug) Debug.Log("Exit " + damageType); if (isActive) { isActive = false; ActiveDamage(false); } if (isAttacking) { isAttacking = false; if (mFighter != null) mFighter.OnDisableAttack(); } if (mFighter != null && resetAttackTrigger) mFighter.ResetAttackTriggers(); if (debug) Debug.Log(context.RowName + " attack " + damageType + " stateExit"); } private void ActiveDamage(bool value) { if (meleeManager) { meleeManager.SetActiveAttack( bodyParts, meleeAttackType, value, damageMultiplier, recoilID, reactionID, ignoreDefense, activeRagdoll, senselessTime, damageType); } } private static GameObject ResolveOwner(in PlayablesStateContext context) { return context.Animator != null ? context.Animator.gameObject : null; } } } #endif