using BumpCombat.Player; using BumpCombat.Core; using UnityEngine; namespace BumpCombat.Enemies { /// /// A small gameplay projectile used by the stage ranged enemies. The /// projectile owns its travel and lifetime; the enemy owns its warning /// and attack timing. /// public sealed class EnemyProjectile : MonoBehaviour { private PlayerHealth target; private Vector2 direction; private float damage; private float speed; private float remainingDistance; private float hitRadius; private bool resolved; public EnemyController Owner { get; private set; } public static EnemyProjectile Launch( EnemyController owner, Vector2 origin, Vector2 direction, float damage, float speed, float maxDistance, float hitRadius, bool isBeam) { GameObject root = new GameObject( isBeam ? "Necrofire Beam Projectile" : "Skeleton Archer Arrow"); root.transform.position = origin; root.transform.right = direction.sqrMagnitude > 0.0001f ? direction.normalized : Vector2.right; EnemyProjectile projectile = root.AddComponent(); projectile.Owner = owner; projectile.direction = root.transform.right; projectile.damage = Mathf.Max(0f, damage); projectile.speed = Mathf.Max(0f, speed); projectile.remainingDistance = Mathf.Max(0.1f, maxDistance); projectile.hitRadius = Mathf.Max(0.03f, hitRadius); projectile.target = FindAnyObjectByType(); StageEnemyEffectVisual.AttachProjectile( root, isBeam, isBeam ? 1.2f : 0.6f, Mathf.Max(0.04f, projectile.hitRadius * 2f)); return projectile; } private void Update() { if (resolved || target == null || Owner == null || Owner.IsDead || !Owner.isActiveAndEnabled) { Destroy(gameObject); return; } if (!RunManager.GameplayInputEnabled) { return; } if (Time.deltaTime <= 0f) { return; } float distance = speed * Time.deltaTime; Vector2 previousPosition = transform.position; if (distance > 0f) { transform.position += (Vector3)(direction * distance); remainingDistance -= distance; } bool hitTarget = false; if (distance > 0f) { RaycastHit2D[] hits = Physics2D.CircleCastAll( previousPosition, hitRadius, direction, distance, Physics2D.AllLayers); for (int i = 0; i < hits.Length; i++) { if (hits[i].collider != null && hits[i].collider.GetComponentInParent() == target) { hitTarget = true; break; } } } else { Collider2D[] overlaps = Physics2D.OverlapCircleAll( transform.position, hitRadius, Physics2D.AllLayers); for (int i = 0; i < overlaps.Length; i++) { if (overlaps[i] != null && overlaps[i].GetComponentInParent() == target) { hitTarget = true; break; } } } if (hitTarget) { resolved = true; target.TryTakeDamage( damage, ((Vector2)target.transform.position - (Vector2)transform.position).normalized, Owner.PlayerKnockbackDistance); Destroy(gameObject); return; } if (remainingDistance <= 0f) { Destroy(gameObject); } } private void OnDisable() { resolved = true; } } }