using System.Collections; using UnityEngine; namespace Hovl { public class HS_ProjectileMover : MonoBehaviour { [SerializeField] protected float speed = 15f; [SerializeField] protected float hitOffset = 0f; [SerializeField] protected bool UseFirePointRotation; [SerializeField] protected Vector3 rotationOffset = Vector3.zero; [Header("Effects")] [SerializeField] protected GameObject hit; [SerializeField] protected ParticleSystem hitPS; [SerializeField] protected GameObject flash; [SerializeField] protected ParticleSystem projectilePS; [SerializeField] protected GameObject[] Detached; [Header("Components")] [SerializeField] protected Rigidbody rb; [SerializeField] protected Collider col; [SerializeField] protected Light lightSourse; [Header("Lifetime")] [SerializeField] protected bool notDestroy = false; [SerializeField] protected float lifeTime = 5f; [SerializeField] protected float detachedLifeTime = 1f; protected bool initialized; protected bool collided; protected Coroutine lifeRoutine; protected Coroutine disableAfterHitRoutine; [System.Serializable] protected class DetachedState { public GameObject obj; public Transform originalParent; public Vector3 localPosition; public Quaternion localRotation; public Vector3 localScale; } protected DetachedState[] detachedStates; protected virtual void Awake() { if (rb == null) rb = GetComponent(); if (col == null) col = GetComponent(); SetupDetachedCache(); } protected virtual void Start() { initialized = true; if (flash != null) flash.transform.SetParent(null, true); StartLifeTimer(); } protected virtual void OnEnable() { collided = false; if (!initialized) return; StopRunningCoroutines(); if (lightSourse != null) lightSourse.enabled = true; if (col != null) col.enabled = true; if (rb != null) { rb.constraints = RigidbodyConstraints.None; #if UNITY_6000_0_OR_NEWER rb.linearVelocity = Vector3.zero; #else rb.velocity = Vector3.zero; #endif rb.angularVelocity = Vector3.zero; } if (projectilePS != null) { projectilePS.Clear(true); projectilePS.Play(true); } if (notDestroy) RestoreDetachedObjects(); StartLifeTimer(); } protected virtual void OnDisable() { StopRunningCoroutines(); } protected virtual void StopRunningCoroutines() { if (lifeRoutine != null) { StopCoroutine(lifeRoutine); lifeRoutine = null; } if (disableAfterHitRoutine != null) { StopCoroutine(disableAfterHitRoutine); disableAfterHitRoutine = null; } } protected virtual void SetupDetachedCache() { if (Detached == null || Detached.Length == 0) return; detachedStates = new DetachedState[Detached.Length]; for (int i = 0; i < Detached.Length; i++) { GameObject obj = Detached[i]; if (obj == null) continue; detachedStates[i] = new DetachedState { obj = obj, originalParent = obj.transform.parent, localPosition = obj.transform.localPosition, localRotation = obj.transform.localRotation, localScale = obj.transform.localScale }; } } protected virtual void RestoreDetachedObjects() { if (detachedStates == null || detachedStates.Length == 0) return; for (int i = 0; i < detachedStates.Length; i++) { DetachedState state = detachedStates[i]; if (state == null || state.obj == null) continue; Transform t = state.obj.transform; t.SetParent(state.originalParent, false); t.localPosition = state.localPosition; t.localRotation = state.localRotation; t.localScale = state.localScale; ParticleSystem[] systems = state.obj.GetComponentsInChildren(true); for (int j = 0; j < systems.Length; j++) { ParticleSystem ps = systems[j]; if (ps == null) continue; ps.Clear(true); ps.Play(true); } } } protected virtual void StartLifeTimer() { if (lifeRoutine != null) StopCoroutine(lifeRoutine); lifeRoutine = StartCoroutine(LifeTimerRoutine(lifeTime)); } protected virtual IEnumerator LifeTimerRoutine(float time) { yield return new WaitForSeconds(time); if (notDestroy) { if (gameObject.activeSelf) gameObject.SetActive(false); } else { Destroy(gameObject); } } protected virtual void FixedUpdate() { if (collided || rb == null || speed == 0f) return; #if UNITY_6000_0_OR_NEWER rb.linearVelocity = transform.forward * speed; #else rb.velocity = transform.forward * speed; #endif } protected virtual void OnCollisionEnter(Collision collision) { if (collided) return; collided = true; StopRunningCoroutines(); if (rb != null) { #if UNITY_6000_0_OR_NEWER rb.linearVelocity = Vector3.zero; #else rb.velocity = Vector3.zero; #endif rb.angularVelocity = Vector3.zero; rb.constraints = RigidbodyConstraints.FreezeAll; } if (lightSourse != null) lightSourse.enabled = false; if (col != null) col.enabled = false; if (projectilePS != null) projectilePS.Stop(true, ParticleSystemStopBehavior.StopEmittingAndClear); if (collision.contactCount > 0) SpawnHit(collision.contacts[0]); ReleaseDetachedObjects(); float endDelay = 1f; if (hitPS != null) endDelay = Mathf.Max(hitPS.main.duration, 0.05f); if (notDestroy) disableAfterHitRoutine = StartCoroutine(DisableAfterHit(endDelay)); else Destroy(gameObject, endDelay); } protected virtual IEnumerator DisableAfterHit(float delay) { yield return new WaitForSeconds(delay); if (gameObject != null && gameObject.activeSelf) gameObject.SetActive(false); } protected virtual void SpawnHit(ContactPoint contact) { if (hit == null) return; Vector3 pos = contact.point + contact.normal * hitOffset; Quaternion rot = Quaternion.FromToRotation(Vector3.up, contact.normal); hit.transform.position = pos; hit.transform.rotation = rot; if (UseFirePointRotation) hit.transform.rotation = transform.rotation * Quaternion.Euler(0f, 180f, 0f); else if (rotationOffset != Vector3.zero) hit.transform.rotation = Quaternion.Euler(rotationOffset); else hit.transform.LookAt(contact.point + contact.normal); if (hitPS != null) { hitPS.Clear(true); hitPS.Play(true); } } protected virtual void ReleaseDetachedObjects() { if (detachedStates == null || detachedStates.Length == 0) return; for (int i = 0; i < detachedStates.Length; i++) { DetachedState state = detachedStates[i]; if (state == null || state.obj == null) continue; Transform t = state.obj.transform; t.SetParent(null, true); ParticleSystem[] systems = state.obj.GetComponentsInChildren(true); for (int j = 0; j < systems.Length; j++) { ParticleSystem ps = systems[j]; if (ps == null) continue; ps.Stop(true, ParticleSystemStopBehavior.StopEmitting); } if (!notDestroy) Destroy(state.obj, detachedLifeTime); } } } }