using System; using System.Collections.Generic; using RoR2.Projectile; using UnityEngine; using UnityEngine.Events; namespace RoR2; public class SoulSpiralProjectile : MonoBehaviour { public static List unassignedSoulSpirals = new List(); [SerializeField] [Header("Basic setup")] public Vector3 offset; [SerializeField] public float initialDegreesFromOwnerForward; [SerializeField] public float degreesPerSecond; [SerializeField] public float radius; [SerializeField] public Vector3 planeNormal = Vector3.up; [SerializeField] public int hitStackPerBuff = 1; [SerializeField] [Tooltip("How much from 'baseline' can we move up or down during our sine wave")] public float verticalSineWaveOffset = 0.25f; [SerializeField] public float sineWavesPerSecond = 1.5f; [Space(20f)] [Header("Easing speeds")] [Tooltip("How long it takes the projectile to ease into its orbit from starting position.")] [SerializeField] public float easeInDuration; [Tooltip("How long it takes the projectile to transition from base speed to boosted speed")] [SerializeField] public float boostEaseDuration; [SerializeField] [Space(20f)] [Header("Boost variables")] [Tooltip("On the last hit, the projectile does more damage.")] public float lastHitDamageMultiplier; [SerializeField] [Tooltip("How long will the spiral spin after being boosted")] public float boostDuration; [SerializeField] [Tooltip("Coefficient for calculating Speed boost - multiplied against the # of boosts we have.")] public float speedBoostCoefficient; [Tooltip("How much should baseDamage be multipled by, if the speed is 100% faster than base.")] [SerializeField] public float damageMultiplier; [Tooltip("Triggered when the orbs are 'boosted'.")] [SerializeField] private UnityEvent OnOrbBoost; private SeekerSoulSpiralManager spiralManager; [HideInInspector] public ProjectileController projectileController; [HideInInspector] public ProjectileDamage projectileDamage; [HideInInspector] public ProjectileOverlapLimitHits projectileOverlapLimitHits; private SoulSpiralGhost _soulSpiralGhost; private Rigidbody rigidBody; private Transform ownerTransform; private float initialStartTimeStamp; private Vector3 initialRadialDirection; private float prevDamage; private float baseDamage; private int baseHitLimit; private const float twoPi = MathF.PI * 2f; private SoulSpiralGhost soulSpiralGhost { get { if (_soulSpiralGhost == null) { if (projectileController == null || projectileController.ghost == null) { return null; } _soulSpiralGhost = projectileController.ghost.GetComponent(); } return _soulSpiralGhost; } set { _soulSpiralGhost = value; } } private void OnEnable() { unassignedSoulSpirals.Add(this); spiralManager = null; projectileController = projectileController ?? GetComponent(); rigidBody = rigidBody ?? GetComponent(); projectileDamage = projectileDamage ?? GetComponent(); projectileOverlapLimitHits = projectileOverlapLimitHits ?? GetComponent(); baseHitLimit = (projectileOverlapLimitHits ? projectileOverlapLimitHits.hitLimit : 3); if (sineWavesPerSecond < 0f) { sineWavesPerSecond = Mathf.Abs(sineWavesPerSecond); } if (sineWavesPerSecond == 0f) { sineWavesPerSecond = 1f; } } private void OnDisable() { if (unassignedSoulSpirals.Contains(this)) { unassignedSoulSpirals.Remove(this); } } public void AssignSpiralOwner(SeekerSoulSpiralManager _spiralManager) { spiralManager = _spiralManager; if (unassignedSoulSpirals.Contains(this)) { unassignedSoulSpirals.Remove(this); } ownerTransform = _spiralManager.seekerBody.transform; SetDefaultVariables(); } private void SetDefaultVariables() { if (hitStackPerBuff < 1) { hitStackPerBuff = 1; } projectileOverlapLimitHits.hitLimit = baseHitLimit + spiralManager.seekerBody.GetBuffCount(DLC2Content.Buffs.ChakraBuff.buffIndex) * hitStackPerBuff; initialStartTimeStamp = Time.fixedTime; initialRadialDirection = Quaternion.AngleAxis(initialDegreesFromOwnerForward, planeNormal) * ownerTransform.forward; baseDamage = projectileDamage.damage; soulSpiralGhost = projectileController.ghost?.GetComponent(); } public void OnLastHit() { soulSpiralGhost?.OnLastHit(); } public void Boost() { soulSpiralGhost?.Boost(); } public void EndBoost() { soulSpiralGhost?.EndBoost(); } public void UpdateProjectile(float currentTime, float degreesRotated, float damageMultiplier, bool doBoost = false, bool doEndBoost = false) { if (!(ownerTransform == null)) { if (doBoost) { OnOrbBoost?.Invoke(); soulSpiralGhost?.Boost(); } if (doEndBoost) { soulSpiralGhost?.EndBoost(); } Vector3 vector = ownerTransform.position + offset + Quaternion.AngleAxis(degreesRotated, planeNormal) * initialRadialDirection * radius; float num = verticalSineWaveOffset; float num2 = (degreesRotated + initialDegreesFromOwnerForward) / degreesPerSecond; float num3 = Mathf.Cos(MathF.PI * 2f * num2 * sineWavesPerSecond); vector.y += num3 * num; if (currentTime - initialStartTimeStamp <= easeInDuration) { float num4 = currentTime - initialStartTimeStamp; vector = Vector3.Lerp(base.transform.position, vector, num4 / easeInDuration); } if ((bool)rigidBody) { rigidBody.MovePosition(vector); } else { base.transform.position = vector; } float num5 = baseDamage * damageMultiplier; if (projectileOverlapLimitHits.IsLastHit()) { num5 *= lastHitDamageMultiplier; } if (num5 != prevDamage) { projectileDamage.damage = num5; prevDamage = num5; } } } public void SetInitialDegreesFromOwnerForward(float degrees, int positionIndex) { initialDegreesFromOwnerForward = degrees; if ((bool)ownerTransform) { initialRadialDirection = Quaternion.AngleAxis(initialDegreesFromOwnerForward, planeNormal) * ownerTransform.forward; } } public void DestroySpiral() { EffectManagerHelper component = GetComponent(); if ((bool)component && component.OwningPool != null) { component.OwningPool.ReturnObject(component); } else { UnityEngine.Object.Destroy(base.gameObject); } } }