728 lines
29 KiB
C#
728 lines
29 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using Invector;
|
|
using Invector.vCharacterController;
|
|
using Invector.vCharacterController.vActions;
|
|
using Sirenix.OdinInspector;
|
|
using UnityEngine;
|
|
using UnityEngine.Events;
|
|
using UnityEngine.VFX;
|
|
using DG.Tweening;
|
|
using Invector.vCharacterController.AI.FSMBehaviour;
|
|
using Beyond;
|
|
|
|
namespace Beyond
|
|
{
|
|
[RequireComponent(typeof(Animator))]
|
|
[RequireComponent(typeof(vGenericAnimation))]
|
|
public class MagicAttacks : MonoBehaviour
|
|
{
|
|
public delegate void ActionDelegate();
|
|
|
|
[Serializable]
|
|
public class EffectDesc
|
|
{
|
|
public string name = "MagicPush";
|
|
public string secondaryName = "MagicPush Scroll";
|
|
public float delay = 0f;
|
|
public float startTime = 0f;
|
|
public float endTime = 0f;
|
|
public GameObject effectObject;
|
|
public string animClipName = "MagicPush";
|
|
public ActionDelegate del;
|
|
}
|
|
|
|
[SerializeField] private bEquipArea powersArea;
|
|
public List<EffectDesc> m_effects;
|
|
public EffectDesc selectedEffect;
|
|
|
|
public enum EffectType
|
|
{ MAGIC_PUSH, FLAME_THROWER, SCAN, NOONE, FIREBALL, SHIELD, SILENT_PEEK };
|
|
|
|
public EffectType m_selectedType = EffectType.NOONE;
|
|
public string currentSelectedSpellName = "";
|
|
|
|
private int currentSpellFaithCost = int.MaxValue;
|
|
private int equipAreaSelectedIndex = 0;
|
|
|
|
private Coroutine lastPushRoutine = null;
|
|
private ParticleSystem flame;
|
|
private BoxCollider flameDamager;
|
|
private bLockOn lockOn;
|
|
private const float fireballAimerThreshold = -1.0f;
|
|
private const float fireballAimerHeightAdjuster = 0.1f;
|
|
private const float fireballDamagerDuration = 0.3f;
|
|
private const float fireballTargetYPositionOffset = 0.75f;
|
|
private const int spellLayerIndex = 5;
|
|
|
|
private EffectDesc shield;
|
|
private EffectDesc silentPeek;
|
|
private ShieldEffectController shieldEffectController;
|
|
private ShieldCollisionController shieldCollisionController;
|
|
|
|
private bool canPlayNoFaithClip = true;
|
|
private bool canPlayCantDoClip = true;
|
|
|
|
private AutoTargetting _autoTargettingInstance;
|
|
public UnityAction<Collider> onHitFireball;
|
|
|
|
// Animation State
|
|
public bool isPlaying;
|
|
protected bool triggerOnce;
|
|
protected bThirdPersonInput tpInput;
|
|
internal bool shieldEffectIsActive;
|
|
private bool shieldAnimationIsActive;
|
|
|
|
[Tooltip("Input to trigger the custom animation")]
|
|
public GenericInput actionInput = new GenericInput("L", "L", "L");
|
|
[Tooltip("Name of the animation clip")]
|
|
public string animationClip;
|
|
[Tooltip("Where in the end of the animation will trigger the event OnEndAnimation")]
|
|
public float animationEnd = 0.8f;
|
|
|
|
public UnityEvent OnPlayAnimation;
|
|
public UnityEvent OnEndAnimation;
|
|
|
|
private void Awake()
|
|
{
|
|
tpInput = GetComponent<bThirdPersonInput>();
|
|
lockOn = GetComponent<bLockOn>();
|
|
|
|
if (Player.Instance != null)
|
|
{
|
|
_autoTargettingInstance = Player.Instance.AutoTarget;
|
|
if (_autoTargettingInstance == null)
|
|
{
|
|
Debug.LogWarning("MagicAttacks: AutoTargetting component not found on Player.Instance.AutoTarget.");
|
|
}
|
|
}
|
|
|
|
EffectDesc mpush = m_effects[(int)EffectType.MAGIC_PUSH];
|
|
EffectDesc flameThrowe = m_effects[(int)EffectType.FLAME_THROWER];
|
|
EffectDesc scan = m_effects[(int)EffectType.SCAN];
|
|
EffectDesc fireball = m_effects[(int)EffectType.FIREBALL];
|
|
shield = m_effects[(int)EffectType.SHIELD];
|
|
silentPeek = m_effects[(int)EffectType.SILENT_PEEK];
|
|
|
|
// Setup References
|
|
if (shield.effectObject)
|
|
{
|
|
shieldEffectController = shield.effectObject.GetComponent<ShieldEffectController>();
|
|
shieldCollisionController = shield.effectObject.GetComponentInChildren<ShieldCollisionController>();
|
|
shield.effectObject.SetActive(false);
|
|
}
|
|
|
|
if (mpush.effectObject != null)
|
|
{
|
|
mpush.effectObject.SetActive(false);
|
|
}
|
|
mpush.del = MagicPushAttack;
|
|
|
|
if (flameThrowe.effectObject)
|
|
{
|
|
flameThrowe.effectObject.GetComponent<ParticleSystem>().Stop();
|
|
var ps = flameThrowe.effectObject.GetComponentsInChildren<ParticleSystem>();
|
|
foreach (var p in ps) p.Stop();
|
|
|
|
flame = flameThrowe.effectObject.GetComponent<ParticleSystem>();
|
|
flameDamager = flameThrowe.effectObject.GetComponentInChildren<BoxCollider>();
|
|
}
|
|
flameThrowe.del = FlameThrowerAttack;
|
|
|
|
scan.del = Scan;
|
|
if (scan.effectObject) scan.effectObject.SetActive(false);
|
|
|
|
fireball.del = Fireball;
|
|
if (fireball.effectObject) fireball.effectObject.SetActive(false);
|
|
|
|
shield.del = Shield;
|
|
silentPeek.del = OnSilentPeek;
|
|
}
|
|
|
|
private void OnDisable()
|
|
{
|
|
if (shieldAnimationIsActive)
|
|
{
|
|
DisableShield();
|
|
}
|
|
}
|
|
|
|
private void OnEnable()
|
|
{
|
|
canPlayNoFaithClip = true;
|
|
canPlayCantDoClip = true;
|
|
}
|
|
|
|
protected virtual void LateUpdate()
|
|
{
|
|
TriggerSpellAnimation();
|
|
AnimationBehaviour();
|
|
}
|
|
|
|
protected virtual void TriggerSpellAnimation()
|
|
{
|
|
bool playConditions = !isPlaying && tpInput != null && tpInput.cc != null &&
|
|
!(tpInput.cc.customAction || tpInput.cc.IsAnimatorTag("special") || tpInput.cc.IsAnimatorTag("LockMovement"));
|
|
|
|
if (actionInput.GetButtonDown() && playConditions)
|
|
TryToPlaySpellAnimation();
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------
|
|
// CORE CASTING LOGIC (COSTS & BONUSES)
|
|
// ----------------------------------------------------------------------------------
|
|
public void TryToPlaySpellAnimation()
|
|
{
|
|
selectedEffect = GetCurrentlySelectedPower();
|
|
|
|
if (selectedEffect == shield && shieldAnimationIsActive)
|
|
{
|
|
TryToPlayCantDoThatYetClip();
|
|
return;
|
|
}
|
|
|
|
if (Player.Instance == null)
|
|
{
|
|
Debug.LogError("Player.Instance is null. Cannot cast spell.");
|
|
return;
|
|
}
|
|
|
|
// --- 1. CALCULATE FINAL FAITH COST ---
|
|
float finalCost = currentSpellFaithCost;
|
|
bool isSilentPeek = selectedEffect == m_effects[(int)EffectType.SILENT_PEEK];
|
|
|
|
// Effect: Bloom (General 20% Reduction)
|
|
if (Player.Instance.CurrentTrinketStats.effectBloom)
|
|
{
|
|
finalCost *= 0.8f;
|
|
}
|
|
|
|
// Effect: Angel Eye (Silent Peek is Free)
|
|
if (isSilentPeek && Player.Instance.CurrentTrinketStats.effectAngelEye)
|
|
{
|
|
finalCost = 0f;
|
|
}
|
|
|
|
// Note: Zora's Focus (effectBreeze) logic for Magic Push cost was removed as requested.
|
|
|
|
// --- 2. CHECK & CONSUME ---
|
|
if (selectedEffect != null && finalCost <= Player.Instance.GetCurrentFaithValue())
|
|
{
|
|
Player.Instance.UpdateFaithCurrentValue(-finalCost);
|
|
|
|
// Effect: Growth (Heal on Cast)
|
|
if (Player.Instance.CurrentTrinketStats.effectGrowth)
|
|
{
|
|
int healAmt = Mathf.Max(1, (int)(Player.Instance.MaxHealth * 0.02f));
|
|
Player.Instance.ThirdPersonController.ChangeHealth(healAmt);
|
|
}
|
|
|
|
animationClip = selectedEffect.animClipName;
|
|
|
|
// Snap Rotation
|
|
if (_autoTargettingInstance != null && _autoTargettingInstance.CurrentTarget != null)
|
|
{
|
|
SnapLookTowardsAutoTarget();
|
|
}
|
|
|
|
// Play Animation
|
|
if (tpInput != null && tpInput.cc != null && tpInput.cc.animator != null)
|
|
{
|
|
tpInput.cc.animator.CrossFadeInFixedTime(animationClip, 0.1f);
|
|
OnPlayAnimation.Invoke();
|
|
triggerOnce = true;
|
|
}
|
|
else
|
|
{
|
|
Debug.LogError("Cannot play spell animation: tpInput or its components are null.");
|
|
return;
|
|
}
|
|
|
|
// Invoke Logic (Coroutine)
|
|
selectedEffect.del?.Invoke();
|
|
|
|
// Handle Consumables (Scrolls)
|
|
if (powersArea.equipSlots[equipAreaSelectedIndex].item.destroyAfterUse)
|
|
{
|
|
if (selectedEffect == silentPeek)
|
|
{
|
|
// Logic handled inside coroutine
|
|
}
|
|
else
|
|
{
|
|
powersArea.UseItem(powersArea.equipSlots[equipAreaSelectedIndex]);
|
|
}
|
|
}
|
|
}
|
|
else if (selectedEffect != null)
|
|
{
|
|
TryToPlayNoEnoughFaithClip();
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------
|
|
// SPELL: MAGIC PUSH
|
|
// ----------------------------------------------------------------------------------
|
|
public void MagicPushAttack()
|
|
{
|
|
if (lastPushRoutine != null) StopCoroutine(lastPushRoutine);
|
|
lastPushRoutine = StartCoroutine(MagicPushCoroutine());
|
|
}
|
|
|
|
private IEnumerator MagicPushCoroutine()
|
|
{
|
|
EffectDesc mpush = m_effects[(int)EffectType.MAGIC_PUSH];
|
|
yield return TurnTowardTargetCoroutine(mpush.startTime);
|
|
|
|
// Zora's Focus scaling logic removed from here as requested.
|
|
// Magic Push is now standard size.
|
|
|
|
mpush.effectObject.SetActive(false);
|
|
mpush.effectObject.SetActive(true);
|
|
|
|
// Apply Soulfire Damage
|
|
ApplySoulfireDamage(mpush.effectObject);
|
|
|
|
yield return new WaitForSeconds(mpush.delay);
|
|
yield return new WaitForSeconds(mpush.endTime);
|
|
mpush.effectObject.SetActive(false);
|
|
yield return null;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------
|
|
// SPELL: FLAME THROWER
|
|
// ----------------------------------------------------------------------------------
|
|
public void FlameThrowerAttack()
|
|
{
|
|
StartCoroutine(FlameThrowerhCoroutine());
|
|
}
|
|
|
|
private IEnumerator FlameThrowerhCoroutine()
|
|
{
|
|
EffectDesc flameThrowe = m_effects[(int)EffectType.FLAME_THROWER];
|
|
yield return TurnTowardTargetCoroutine(flameThrowe.startTime);
|
|
|
|
flameDamager.enabled = true;
|
|
ApplySoulfireDamage(flameThrowe.effectObject);
|
|
|
|
flame.Play();
|
|
yield return new WaitForSeconds(flameThrowe.endTime);
|
|
flame.Stop();
|
|
yield return new WaitForSeconds(flameThrowe.delay);
|
|
flameDamager.enabled = false;
|
|
yield return null;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------
|
|
// SPELL: FIREBALL (Soulfire Damage)
|
|
// ----------------------------------------------------------------------------------
|
|
public void Fireball()
|
|
{
|
|
StartCoroutine(FireballCoroutine());
|
|
}
|
|
|
|
private IEnumerator FireballCoroutine()
|
|
{
|
|
EffectDesc fireballDesc = m_effects[(int)EffectType.FIREBALL];
|
|
yield return TurnTowardTargetCoroutine(fireballDesc.startTime);
|
|
|
|
var fireballClone = Instantiate(fireballDesc.effectObject, fireballDesc.effectObject.transform.position, fireballDesc.effectObject.transform.rotation);
|
|
fireballClone.SetActive(true);
|
|
|
|
// Apply Soulfire Damage to the Clone
|
|
ApplySoulfireDamage(fireballClone);
|
|
|
|
RFX4_PhysicsMotion fireballMotionController = fireballClone.GetComponentInChildren<RFX4_PhysicsMotion>();
|
|
if (fireballMotionController != null)
|
|
{
|
|
fireballMotionController.CollisionEnter += EnableBrieflyFireballDamager;
|
|
}
|
|
vObjectDamage fireballDamageComponent = fireballClone.GetComponentInChildren<vObjectDamage>();
|
|
if (fireballDamageComponent != null && onHitFireball != null)
|
|
{
|
|
fireballDamageComponent.onHit.AddListener(onHitFireball);
|
|
}
|
|
|
|
AimFireball(fireballClone);
|
|
|
|
Destroy(fireballClone, 10f);
|
|
yield return null;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------
|
|
// SPELL: SHIELD (Calmness)
|
|
// ----------------------------------------------------------------------------------
|
|
public void Shield()
|
|
{
|
|
StopCoroutine(nameof(ShieldCoroutine));
|
|
StartCoroutine(nameof(ShieldCoroutine));
|
|
}
|
|
|
|
private IEnumerator ShieldCoroutine()
|
|
{
|
|
shieldAnimationIsActive = true;
|
|
yield return new WaitForSeconds(shield.startTime);
|
|
|
|
shieldEffectIsActive = true;
|
|
shieldEffectController.InitializeEffect();
|
|
shield.effectObject.SetActive(true);
|
|
shieldCollisionController.shieldCollider.enabled = true;
|
|
|
|
// Effect: Calmness (Shield lasts 50% longer)
|
|
float finalDuration = shield.endTime;
|
|
if (Player.Instance.CurrentTrinketStats.effectCalmness)
|
|
{
|
|
finalDuration *= 1.5f;
|
|
}
|
|
|
|
yield return new WaitForSeconds(finalDuration);
|
|
|
|
shieldEffectController.DisableEffect();
|
|
yield return new WaitForSeconds(shield.delay / 2f);
|
|
shieldEffectIsActive = false;
|
|
shieldCollisionController.shieldCollider.enabled = false;
|
|
yield return new WaitForSeconds(shield.delay / 2f);
|
|
shield.effectObject.SetActive(false);
|
|
shieldAnimationIsActive = false;
|
|
}
|
|
|
|
private void DisableShield()
|
|
{
|
|
shieldEffectIsActive = false;
|
|
if (shieldCollisionController != null && shieldCollisionController.shieldCollider != null)
|
|
shieldCollisionController.shieldCollider.enabled = false;
|
|
shieldAnimationIsActive = false;
|
|
if (shield != null && shield.effectObject != null)
|
|
shield.effectObject.SetActive(false);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------
|
|
// SPELL: SILENT PEEK (Angel Eye & Zora's Focus)
|
|
// ----------------------------------------------------------------------------------
|
|
[Button]
|
|
private void OnSilentPeek()
|
|
{
|
|
StopCoroutine(nameof(SilentPeekCoroutine));
|
|
StartCoroutine(nameof(SilentPeekCoroutine));
|
|
}
|
|
|
|
private IEnumerator SilentPeekCoroutine()
|
|
{
|
|
EffectDesc peek = m_effects[(int)EffectType.SILENT_PEEK];
|
|
yield return new WaitForSeconds(peek.startTime);
|
|
|
|
// Effect: Zora's Focus (effectBreeze)
|
|
// User Note: "enhanced covert gaze radius and length", "keep it in not fully implemented version"
|
|
// Implementation: We check the flag, but currently do not apply Radius/Length changes.
|
|
if (Player.Instance.CurrentTrinketStats.effectBreeze)
|
|
{
|
|
// Placeholder for future logic:
|
|
// float extendedDuration = peek.endTime * 1.5f;
|
|
// float extendedRadius = currentRadius * 1.3f;
|
|
}
|
|
|
|
if (SilentPeekController.instance.IsActive())
|
|
{
|
|
SilentPeekController.instance.SetActive(false);
|
|
}
|
|
else
|
|
{
|
|
if (peek.effectObject != null)
|
|
{
|
|
peek.effectObject.SetActive(false);
|
|
peek.effectObject.SetActive(true);
|
|
yield return new WaitForSeconds(peek.delay);
|
|
}
|
|
SilentPeekController.instance.SetActive(true, powersArea.equipSlots[equipAreaSelectedIndex].item);
|
|
}
|
|
yield return null;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------
|
|
// SPELL: SCAN
|
|
// ----------------------------------------------------------------------------------
|
|
[Button]
|
|
public void Scan()
|
|
{
|
|
StartCoroutine(ScanCoroutine());
|
|
}
|
|
|
|
private IEnumerator ScanCoroutine()
|
|
{
|
|
EffectDesc scan = m_effects[(int)EffectType.SCAN];
|
|
yield return new WaitForSeconds(scan.startTime);
|
|
float maxRange = 50f;
|
|
float speed = maxRange / (scan.endTime - scan.startTime);
|
|
int mask = 1 << LayerMask.NameToLayer("Triggers") | 1 << LayerMask.NameToLayer("HiddenObject");
|
|
|
|
if (scan.effectObject)
|
|
{
|
|
scan.effectObject.SetActive(true);
|
|
VisualEffect effect = scan.effectObject.GetComponent<VisualEffect>();
|
|
effect.Play();
|
|
}
|
|
|
|
float waveEffectTimer = 0f;
|
|
float waveEffectDuration = scan.endTime - scan.startTime;
|
|
|
|
while (waveEffectTimer < waveEffectDuration)
|
|
{
|
|
Shader.SetGlobalFloat("_WaveTime", speed * waveEffectTimer);
|
|
waveEffectTimer += Time.deltaTime;
|
|
yield return null;
|
|
}
|
|
Shader.SetGlobalFloat("_WaveTime", 0f);
|
|
|
|
var colliders = Physics.OverlapSphere(transform.position, maxRange, mask);
|
|
foreach (var c in colliders)
|
|
{
|
|
var h = c.gameObject.GetComponent<IScannable>();
|
|
if (h != null) h.OnScanned();
|
|
}
|
|
|
|
if (scan.effectObject) scan.effectObject.SetActive(false);
|
|
yield return null;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------
|
|
// HELPERS
|
|
// ----------------------------------------------------------------------------------
|
|
|
|
// Applies Trinket Damage Multiplier (Soulfire) to any vObjectDamage found on object
|
|
private void ApplySoulfireDamage(GameObject obj)
|
|
{
|
|
if (obj == null) return;
|
|
var damageComps = obj.GetComponentsInChildren<vObjectDamage>();
|
|
float mult = Player.Instance.CurrentTrinketStats.soulfireDamageMult;
|
|
|
|
// Only apply if multiplier is significant
|
|
if (Mathf.Abs(mult - 1f) > 0.01f)
|
|
{
|
|
foreach (var comp in damageComps)
|
|
{
|
|
comp.damage.damageValue = Mathf.RoundToInt(comp.damage.damageValue * mult);
|
|
}
|
|
}
|
|
}
|
|
|
|
public EffectDesc GetCurrentlySelectedPower()
|
|
{
|
|
if (powersArea == null || equipAreaSelectedIndex < 0 || equipAreaSelectedIndex >= powersArea.equipSlots.Count ||
|
|
powersArea.equipSlots[equipAreaSelectedIndex] == null || powersArea.equipSlots[equipAreaSelectedIndex].item == null)
|
|
{
|
|
currentSpellFaithCost = int.MaxValue;
|
|
currentSelectedSpellName = "";
|
|
return null;
|
|
}
|
|
|
|
bItem selectedSpellItem = powersArea.equipSlots[equipAreaSelectedIndex].item;
|
|
currentSelectedSpellName = selectedSpellItem.name;
|
|
currentSpellFaithCost = selectedSpellItem.GetItemAttribute(bItemAttributes.Faith).value;
|
|
|
|
return m_effects.Find(effect => effect.name == selectedSpellItem.name || effect.secondaryName == selectedSpellItem.name);
|
|
}
|
|
|
|
public void SelectPowerBasedOnArea(int newIndex)
|
|
{
|
|
equipAreaSelectedIndex = newIndex;
|
|
selectedEffect = GetCurrentlySelectedPower();
|
|
}
|
|
|
|
private void TryToPlayCantDoThatYetClip()
|
|
{
|
|
if (!canPlayCantDoClip) return;
|
|
canPlayCantDoClip = false;
|
|
DOVirtual.DelayedCall(1f, () => canPlayCantDoClip = true);
|
|
|
|
var text = "Spell is already active";
|
|
if (bItemCollectionDisplay.Instance != null)
|
|
bItemCollectionDisplay.Instance.FadeText(text, 4, 0.25f);
|
|
if (Player.Instance != null)
|
|
Player.Instance.PlayICantDoThatYet();
|
|
}
|
|
|
|
private void TryToPlayNoEnoughFaithClip()
|
|
{
|
|
if (!canPlayNoFaithClip) return;
|
|
canPlayNoFaithClip = false;
|
|
DOVirtual.DelayedCall(1.5f, () => canPlayNoFaithClip = true);
|
|
|
|
var text = "Not enough Faith";
|
|
if (bItemCollectionDisplay.Instance != null)
|
|
bItemCollectionDisplay.Instance.FadeText(text, 4, 0.25f);
|
|
if (Player.Instance != null)
|
|
Player.Instance.PlayNoFaithClip();
|
|
}
|
|
|
|
protected virtual void AnimationBehaviour()
|
|
{
|
|
if (tpInput == null || tpInput.cc == null || tpInput.cc.animator == null || string.IsNullOrEmpty(animationClip))
|
|
{
|
|
isPlaying = false;
|
|
return;
|
|
}
|
|
|
|
isPlaying = tpInput.cc.animator.GetCurrentAnimatorStateInfo(spellLayerIndex).IsName(animationClip) ||
|
|
tpInput.cc.animator.GetNextAnimatorStateInfo(spellLayerIndex).IsName(animationClip);
|
|
|
|
if (isPlaying)
|
|
{
|
|
if (tpInput.cc.animator.GetCurrentAnimatorStateInfo(spellLayerIndex).IsName(animationClip) &&
|
|
tpInput.cc.animator.GetCurrentAnimatorStateInfo(spellLayerIndex).normalizedTime >= animationEnd)
|
|
{
|
|
if (triggerOnce)
|
|
{
|
|
triggerOnce = false;
|
|
OnEndAnimation.Invoke();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (triggerOnce) triggerOnce = false;
|
|
}
|
|
}
|
|
|
|
private IEnumerator TurnTowardTargetCoroutine(float maxDuration)
|
|
{
|
|
if (_autoTargettingInstance == null || _autoTargettingInstance.CurrentTarget == null)
|
|
{
|
|
if (maxDuration > 0) yield return new WaitForSeconds(maxDuration);
|
|
yield break;
|
|
}
|
|
|
|
float timeElapsed = 0;
|
|
while (timeElapsed < maxDuration)
|
|
{
|
|
if (_autoTargettingInstance.CurrentTarget != null)
|
|
{
|
|
vFSMBehaviourController currentTarget = _autoTargettingInstance.CurrentTarget;
|
|
Transform playerTransform = transform;
|
|
|
|
float distSqr = (currentTarget.transform.position - playerTransform.position).sqrMagnitude;
|
|
|
|
if (distSqr <= _autoTargettingInstance.maxTargetingDistance * _autoTargettingInstance.maxTargetingDistance)
|
|
{
|
|
if (_autoTargettingInstance.IsTargetInAngle(playerTransform, currentTarget, _autoTargettingInstance.targetingAngleThreshold))
|
|
{
|
|
Vector3 directionToTarget = currentTarget.transform.position - playerTransform.position;
|
|
directionToTarget.y = 0f;
|
|
|
|
if (directionToTarget.sqrMagnitude > 0.0001f)
|
|
{
|
|
Quaternion targetRotation = Quaternion.LookRotation(directionToTarget.normalized);
|
|
playerTransform.rotation = Quaternion.RotateTowards(playerTransform.rotation, targetRotation, Time.deltaTime * _autoTargettingInstance.playerRotationSpeed);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
yield break;
|
|
}
|
|
timeElapsed += Time.deltaTime;
|
|
yield return null;
|
|
}
|
|
}
|
|
|
|
private void SnapLookTowardsAutoTarget()
|
|
{
|
|
if (_autoTargettingInstance == null || _autoTargettingInstance.CurrentTarget == null) return;
|
|
|
|
vFSMBehaviourController currentTarget = _autoTargettingInstance.CurrentTarget;
|
|
Transform playerTransform = transform;
|
|
|
|
float distSqr = (currentTarget.transform.position - playerTransform.position).sqrMagnitude;
|
|
if (distSqr > _autoTargettingInstance.maxTargetingDistance * _autoTargettingInstance.maxTargetingDistance) return;
|
|
|
|
if (!_autoTargettingInstance.IsTargetInAngle(playerTransform, currentTarget, _autoTargettingInstance.targetingAngleThreshold)) return;
|
|
|
|
Vector3 directionToTarget = currentTarget.transform.position - playerTransform.position;
|
|
directionToTarget.y = 0f;
|
|
|
|
if (directionToTarget.sqrMagnitude > 0.0001f)
|
|
{
|
|
playerTransform.rotation = Quaternion.LookRotation(directionToTarget.normalized);
|
|
}
|
|
}
|
|
|
|
private void AimFireball(GameObject fireballClone)
|
|
{
|
|
Vector3 aimDirection = transform.forward;
|
|
|
|
if (_autoTargettingInstance != null && _autoTargettingInstance.CurrentTarget != null)
|
|
{
|
|
vFSMBehaviourController autoTarget = _autoTargettingInstance.CurrentTarget;
|
|
Transform playerTransform = transform;
|
|
|
|
float distSqrToAutoTarget = (autoTarget.transform.position - playerTransform.position).sqrMagnitude;
|
|
|
|
if (distSqrToAutoTarget <= _autoTargettingInstance.maxTargetingDistance * _autoTargettingInstance.maxTargetingDistance &&
|
|
_autoTargettingInstance.IsTargetInAngle(playerTransform, autoTarget, _autoTargettingInstance.targetingAngleThreshold))
|
|
{
|
|
Vector3 targetPosition = autoTarget.transform.position;
|
|
targetPosition.y += fireballTargetYPositionOffset;
|
|
aimDirection = (targetPosition - fireballClone.transform.position).normalized;
|
|
}
|
|
}
|
|
|
|
if (aimDirection == transform.forward && lockOn != null && lockOn.isLockingOn && lockOn.currentTarget != null)
|
|
{
|
|
Vector3 targetPosition = lockOn.currentTarget.position;
|
|
targetPosition.y += fireballTargetYPositionOffset;
|
|
aimDirection = (targetPosition - fireballClone.transform.position).normalized;
|
|
}
|
|
else if (aimDirection == transform.forward && lockOn != null)
|
|
{
|
|
List<Transform> closeEnemies = lockOn.GetNearbyTargets();
|
|
if (closeEnemies.Count > 0)
|
|
{
|
|
Transform bestFallbackTarget = null;
|
|
float minAngle = float.MaxValue;
|
|
|
|
foreach (var enemyTransform in closeEnemies)
|
|
{
|
|
Vector3 directionToEnemyFromPlayer = (enemyTransform.position - transform.position).normalized;
|
|
float angleToEnemy = Vector3.Angle(transform.forward, directionToEnemyFromPlayer);
|
|
|
|
if (Vector3.Dot(transform.forward, directionToEnemyFromPlayer) > fireballAimerThreshold)
|
|
{
|
|
if (angleToEnemy < minAngle)
|
|
{
|
|
minAngle = angleToEnemy;
|
|
bestFallbackTarget = enemyTransform;
|
|
}
|
|
}
|
|
}
|
|
if (bestFallbackTarget != null)
|
|
{
|
|
Vector3 targetPosition = bestFallbackTarget.position;
|
|
targetPosition.y += fireballTargetYPositionOffset;
|
|
aimDirection = (targetPosition - fireballClone.transform.position).normalized;
|
|
}
|
|
}
|
|
}
|
|
|
|
Vector3 finalAimDirection = new Vector3(aimDirection.x, aimDirection.y + fireballAimerHeightAdjuster, aimDirection.z);
|
|
if (finalAimDirection.sqrMagnitude > 0.001f)
|
|
{
|
|
fireballClone.transform.rotation = Quaternion.LookRotation(finalAimDirection.normalized);
|
|
}
|
|
}
|
|
|
|
private void EnableBrieflyFireballDamager(object sender, RFX4_PhysicsMotion.RFX4_CollisionInfo e)
|
|
{
|
|
RFX4_PhysicsMotion rFX4_PhysicsMotion = (RFX4_PhysicsMotion)sender;
|
|
CapsuleCollider collider = rFX4_PhysicsMotion.GetComponentInChildren<CapsuleCollider>();
|
|
if (collider != null) StartCoroutine(EnableBrieflyFireballDamagerCoroutine(collider));
|
|
}
|
|
|
|
private IEnumerator EnableBrieflyFireballDamagerCoroutine(CapsuleCollider collider)
|
|
{
|
|
collider.enabled = true;
|
|
yield return new WaitForSeconds(fireballDamagerDuration);
|
|
collider.enabled = false;
|
|
}
|
|
}
|
|
} |