using UnityEngine; using System.Collections; public class Arrow : MonoBehaviour { [Header("Arrow Settings")] public float flightSpeed = 20f; public GameObject arrowModel; // Assign arrow 3D model private Vector3 targetPosition; private Character targetCharacter; private int damage; private bool isFlying = false; private bool hasLanded = false; private Vector3 impactDirection; private Collider targetCollider; private Vector3 arrowStartPosition; public void Initialize(Vector3 startPos, Vector3 hitPoint, Character target, int damageAmount) { transform.position = startPos; arrowStartPosition = startPos; targetCharacter = target; targetPosition = hitPoint; damage = damageAmount; // Store direction and collider for later impactDirection = (hitPoint - startPos).normalized; targetCollider = target.GetComponent(); transform.LookAt(targetPosition); StartCoroutine(FlyToTarget()); } public void InitializeMiss(Vector3 startPos, Vector3 missPos) { transform.position = startPos; arrowStartPosition = startPos; targetCharacter = null; // Raycast down from missPos to find ground RaycastHit groundHit; if (Physics.Raycast(missPos + Vector3.up * 10f, Vector3.down, out groundHit, 50f, LayerMask.GetMask("Ground"))) { targetPosition = groundHit.point; } else { // No ground found, destroy arrow Destroy(gameObject); return; } // Point arrow toward miss position on ground Vector3 direction = (targetPosition - startPos).normalized; transform.LookAt(targetPosition); StartCoroutine(FlyToTarget()); } private IEnumerator FlyToTarget() { isFlying = true; Vector3 startPosition = transform.position; float journeyLength = Vector3.Distance(startPosition, targetPosition); float journeyTime = journeyLength / flightSpeed; float elapsedTime = 0f; while (elapsedTime < journeyTime && !hasLanded) { elapsedTime += Time.deltaTime; float fractionOfJourney = elapsedTime / journeyTime; // Move arrow toward target transform.position = Vector3.Lerp(startPosition, targetPosition, fractionOfJourney); yield return null; } // Arrow has reached target Land(journeyLength); } private void Land(float journeyLength = 2f) { isFlying = false; hasLanded = true; // If we hit a character, apply damage if (targetCharacter != null && targetCharacter.IsTargetable && damage > 0) { targetCharacter.TakeDamage(damage); Debug.Log($"=== ARROW IMPACT ==="); Debug.Log($"Arrow hits {targetCharacter.CharacterName} for {damage} damage!"); Debug.Log($"Target Health After: {targetCharacter.CurrentHealth}/{targetCharacter.MaxHealth}"); Debug.Log($"=== END ARROW IMPACT ==="); // Raycast from current arrow position forward to find actual hit point on collider Vector3 rayOrigin = arrowStartPosition; Vector3 rayDir = impactDirection; RaycastHit hitInfo; Vector3 finalHitPoint = transform.position; if (targetCollider != null && targetCollider.Raycast(new Ray(rayOrigin, rayDir), out hitInfo, journeyLength)) { finalHitPoint = hitInfo.point; } // Move arrow to the actual hit point transform.position = finalHitPoint; // Stick arrow to target at the hit point and preserve world position/rotation Quaternion worldRot = transform.rotation; transform.SetParent(targetCharacter.transform, true); transform.position = finalHitPoint; transform.rotation = worldRot; Debug.Log($"Arrow hits {targetCharacter.CharacterName} at {finalHitPoint} for {damage} damage!"); } else { Debug.Log("=== ARROW MISS ==="); Debug.Log("Arrow misses and lands on the ground!"); Debug.Log($"=== END ARROW MISS ==="); } // Make arrow inactive but visible Collider arrowCollider = GetComponent(); if (arrowCollider != null) { arrowCollider.enabled = false; } // Disable rigidbody if present Rigidbody arrowRb = GetComponent(); if (arrowRb != null) { arrowRb.isKinematic = true; } } public bool IsFlying => isFlying; public bool HasLanded => hasLanded; }