312 lines
10 KiB
C#
312 lines
10 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using Beyond;
|
|
using Invector.vCharacterController.AI;
|
|
using Invector.vCharacterController.AI.FSMBehaviour;
|
|
using UnityEngine;
|
|
|
|
namespace Invector.vMelee
|
|
{
|
|
using vEventSystems;
|
|
|
|
public class bMeleeAttackControl : StateMachineBehaviour
|
|
{
|
|
[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;
|
|
|
|
[HideInInspector] [Header("This work with vMeleeManager to active vMeleeAttackObject from bodyPart and id")]
|
|
public List<string> bodyParts = new List<string> { "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;
|
|
|
|
private bool isActive;
|
|
public bool debug;
|
|
private vIAttackListener mFighter;
|
|
private bool isAttacking;
|
|
//private bool slowMoActive = false;
|
|
public bool useAttackTimeScale = false;
|
|
public float resetTriggerBeforeTime = 0.5f;
|
|
public float attackTimeScale = 0.2f;
|
|
public float attackTimeScaleStart = -1f;
|
|
public float attackTimeScaleEnd = -1f;
|
|
public float maxTargetDistance = 3f;
|
|
public float maxTurnTowardDistance = 6f;
|
|
public float lowHealthTh = 10f;
|
|
private bool m_hasScaledTime = false;
|
|
|
|
|
|
public float degreeThreshold = 20;
|
|
public bool rotatePlayerTowardsTarget;
|
|
private bool m_isRotating;
|
|
public float rotationSpeed = 30f;
|
|
|
|
override public void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
|
|
{
|
|
mFighter = animator.GetComponent<vIAttackListener>();
|
|
isAttacking = true;
|
|
//slowMoActive = false;
|
|
if (mFighter != null)
|
|
mFighter.OnEnableAttack();
|
|
|
|
if (debug)
|
|
Debug.Log("Enter " + damageType);
|
|
|
|
if (attackTimeScaleStart < 0f)
|
|
{
|
|
attackTimeScaleStart = startDamage;
|
|
}
|
|
|
|
if (attackTimeScaleEnd < 0f)
|
|
{
|
|
attackTimeScaleEnd = endDamage;
|
|
}
|
|
}
|
|
|
|
private void LerpRotation()
|
|
{
|
|
if (!rotatePlayerTowardsTarget)
|
|
{
|
|
return;
|
|
}
|
|
|
|
float minDist = maxTurnTowardDistance;
|
|
var enemy = GetNearestEnemy(ref minDist);
|
|
|
|
if (!IsEnemyInAngleRange(enemy))
|
|
{
|
|
return;
|
|
}
|
|
|
|
Transform playerTransform = Player.Instance.transform;
|
|
var toEnemy = enemy.transform.position - playerTransform.position;
|
|
toEnemy.y = 0f;
|
|
toEnemy.Normalize();
|
|
|
|
Quaternion targetRot =
|
|
Quaternion.LookRotation(toEnemy);
|
|
|
|
var rotation = playerTransform.rotation;
|
|
rotation = Quaternion.RotateTowards(rotation, targetRot, Time.deltaTime * rotationSpeed);
|
|
//rotation = Quaternion.Lerp(rotation, targetRot, Time.deltaTime * rotationSpeed);
|
|
playerTransform.rotation = rotation;
|
|
}
|
|
private bool IsEnemyInAngleRange(vFSMBehaviourController ai)
|
|
{
|
|
if (ai == null)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
Vector3 playerFwd = Player.Instance.transform.forward;
|
|
Vector3 target = (ai.transform.forward - playerFwd).normalized;
|
|
|
|
float dot = Vector3.Dot(playerFwd, target );
|
|
float angle = 180f - Mathf.Acos(dot) * Mathf.Rad2Deg;
|
|
//Debug.Log("IsEnemyInAngleRange: angle: "+angle);
|
|
|
|
if (angle > degreeThreshold)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public float NearTargertHealth()
|
|
{
|
|
float health = -1f;
|
|
if (Player.Instance.LockOn.currentTarget != null)
|
|
{
|
|
var aic = Player.Instance.LockOn.currentTarget.GetComponent<vControlAICombat>();
|
|
if (aic != null)
|
|
{
|
|
health = aic.currentHealth;
|
|
return health;
|
|
}
|
|
}
|
|
|
|
float minDist = Single.PositiveInfinity;
|
|
|
|
var ctrl = GetNearestEnemy(ref minDist);
|
|
|
|
if (minDist < maxTargetDistance)
|
|
{
|
|
health = ctrl.aiController.currentHealth;
|
|
}
|
|
|
|
return health;
|
|
}
|
|
|
|
private bool NearHealthLow()
|
|
{
|
|
float h = NearTargertHealth();
|
|
return h > 0f && h < lowHealthTh;
|
|
}
|
|
|
|
private vFSMBehaviourController GetNearestEnemy(ref float minDist)
|
|
{
|
|
var controllers = GameStateManager.Instance.GetActiveCombatcontrollers();
|
|
Vector3 playerPos = Player.Instance.transform.position;
|
|
vFSMBehaviourController ctrl = null;
|
|
foreach (var aic in controllers)
|
|
{
|
|
var dist2 = aic.transform.position - Player.Instance.transform.position;
|
|
dist2.y = 0f;
|
|
if (dist2.magnitude < minDist)
|
|
{
|
|
ctrl = aic;
|
|
minDist = dist2.magnitude;
|
|
}
|
|
}
|
|
|
|
return ctrl;
|
|
}
|
|
|
|
public bool TargetNear()
|
|
{
|
|
var targets = Player.Instance.LockOn.GetNearbyTargets();
|
|
if (targets.Count == 0)
|
|
return false;
|
|
else
|
|
{
|
|
var dist2 = targets[0].position - Player.Instance.transform.position;
|
|
dist2.y = 0f;
|
|
return dist2.magnitude < maxTargetDistance;
|
|
}
|
|
}
|
|
|
|
private void UpdateSlowMo(float normalizedTime)
|
|
{
|
|
if (!m_hasScaledTime)
|
|
{
|
|
if (normalizedTime >= attackTimeScaleStart && normalizedTime <= attackTimeScaleEnd)
|
|
{
|
|
if (TargetNear() && (useAttackTimeScale || NearHealthLow()))
|
|
{
|
|
TimeController.Instance.SetTimeScaleForSec(attackTimeScale, attackTimeScaleEnd - normalizedTime, true);
|
|
//Time.timeScale = attackTimeScale;
|
|
m_hasScaledTime = true;
|
|
}
|
|
}
|
|
} else if (normalizedTime > attackTimeScaleEnd)
|
|
{
|
|
m_hasScaledTime = false;
|
|
}
|
|
}
|
|
|
|
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 nTime = stateInfo.normalizedTime % 1;
|
|
|
|
LerpRotation();
|
|
|
|
if (nTime >= startDamage && nTime <= endDamage && !isActive)
|
|
{
|
|
if (debug)
|
|
Debug.Log(animator.name + " attack " + damageType + " enable damage in " +
|
|
System.Math.Round(stateInfo.normalizedTime % 1, 2));
|
|
isActive = true;
|
|
ActiveDamage(animator, true);
|
|
/*
|
|
if (TargetNear() && (useAttackTimeScale || NearHealthLow()))
|
|
{
|
|
TimeController.Instance.SetTimeScaleForSec(attackTimeScale, endDamage - nTime);
|
|
//Time.timeScale = attackTimeScale;
|
|
m_hasScaledTime = true;
|
|
}
|
|
*/
|
|
}
|
|
else if (nTime > endDamage && isActive)
|
|
{
|
|
if (debug)
|
|
Debug.Log(animator.name + " attack " + damageType + " disable damage in " +
|
|
System.Math.Round(stateInfo.normalizedTime % 1, 2));
|
|
isActive = false;
|
|
ActiveDamage(animator, false);
|
|
if (m_hasScaledTime)
|
|
{
|
|
//Time.timeScale = 1f;
|
|
m_hasScaledTime = false;
|
|
}
|
|
}
|
|
|
|
if (nTime > endDamage && isAttacking)
|
|
{
|
|
isAttacking = false;
|
|
if (mFighter != null)
|
|
mFighter.OnDisableAttack();
|
|
}
|
|
|
|
if (nTime > .1f && nTime < resetTriggerBeforeTime && isAttacking)
|
|
{
|
|
if (mFighter != null)
|
|
mFighter.ResetAttackTriggers();
|
|
}
|
|
|
|
//if (stateInfo.normalizedTime % 1 > allowRotationAt && isAttacking)
|
|
//{
|
|
// isAttacking = false;
|
|
// if (mFighter != null)
|
|
// mFighter.OnDisableAttack();
|
|
//}
|
|
UpdateSlowMo(nTime);
|
|
}
|
|
|
|
override public void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
|
|
{
|
|
if (debug)
|
|
Debug.Log("Exit " + damageType);
|
|
|
|
if (isActive)
|
|
{
|
|
isActive = false;
|
|
ActiveDamage(animator, false);
|
|
}
|
|
|
|
if (isAttacking)
|
|
{
|
|
isAttacking = false;
|
|
if (mFighter != null)
|
|
mFighter.OnDisableAttack();
|
|
}
|
|
|
|
if (mFighter != null && resetAttackTrigger)
|
|
mFighter.ResetAttackTriggers();
|
|
|
|
if (debug) Debug.Log(animator.name + " attack " + damageType + " stateExit");
|
|
}
|
|
|
|
void ActiveDamage(Animator animator, bool value)
|
|
{
|
|
var meleeManager = animator.GetComponent<vMeleeManager>();
|
|
if (meleeManager)
|
|
meleeManager.SetActiveAttack(bodyParts, meleeAttackType, value, damageMultiplier, recoilID, reactionID,
|
|
ignoreDefense, activeRagdoll, senselessTime, damageType);
|
|
}
|
|
}
|
|
} |