using UnityEngine;
using System.Collections;
///
/// 발사된 화살 발사체 (파티클 프리팹에 부착됨)
/// SphereCast 기반 정밀 충돌 감지 (기존 100% 유지)
/// [NEW] 속성 데미지(DoT) 시스템 추가
///
public class PlayerArrow : MonoBehaviour
{
[Header("--- 정밀 피격 판정 설정 ---")]
[SerializeField] private float raycastDistance = 0.8f;
[SerializeField] private float raycastRadius = 0.08f;
[SerializeField] private LayerMask hitLayers;
[SerializeField] private Transform arrowTip;
[Header("--- 디버그 시각화 ---")]
[SerializeField] private bool showDebugRay = true;
// 화살 스탯
private float damage;
private float speed;
private float range;
private Vector3 startPos;
private Vector3 shootDirection;
private bool isFired = false;
private bool hasHit = false;
// [NEW] 속성 데미지 시스템
private ArrowElementType elementType = ArrowElementType.None;
private float elementDamage = 0f;
private float elementDuration = 0f;
// Raycast 추적용
private Vector3 previousPosition;
private Rigidbody rb;
private void Awake()
{
rb = GetComponent();
}
private void Start()
{
if (arrowTip == null)
{
Transform tip = transform.Find("ArrowTip");
if (tip == null) tip = transform.Find("Tip");
arrowTip = tip ?? transform;
}
if (hitLayers.value == 0)
{
hitLayers = LayerMask.GetMask("Enemy", "EnemyHitBox", "Wall", "Ground", "Default");
}
}
///
/// [MODIFIED] 초기화 — 속성 정보 포함 (7개 파라미터)
/// PlayerAttack.OnShootArrow()에서 호출됩니다.
///
public void Initialize(
float dmg,
float arrowSpeed,
float maxRange,
Vector3 direction,
ArrowElementType element,
float elemDmg,
float elemDur)
{
this.damage = dmg;
this.speed = arrowSpeed;
this.range = maxRange;
this.shootDirection = direction.normalized;
this.startPos = transform.position;
this.previousPosition = transform.position;
this.isFired = true;
// [NEW] 속성 정보 저장
this.elementType = element;
this.elementDamage = elemDmg;
this.elementDuration = elemDur;
// 발사 방향으로 회전
if (shootDirection != Vector3.zero)
{
transform.rotation = Quaternion.LookRotation(shootDirection);
}
// Rigidbody 설정
if (rb == null) rb = GetComponent();
if (rb != null)
{
rb.useGravity = false;
rb.isKinematic = false;
rb.collisionDetectionMode = CollisionDetectionMode.ContinuousDynamic;
rb.velocity = shootDirection * speed;
}
Destroy(gameObject, 5f);
}
///
/// [하위 호환성] 방향 없는 3-파라미터 버전
///
public void Initialize(float dmg, float arrowSpeed, float maxRange)
{
Initialize(dmg, arrowSpeed, maxRange, transform.forward,
ArrowElementType.None, 0f, 0f);
}
private void Update()
{
if (!isFired || hasHit) return;
// 사거리 체크
float traveledDistance = Vector3.Distance(startPos, transform.position);
if (traveledDistance >= range)
{
Destroy(gameObject);
return;
}
// 정밀 충돌 감지
CheckPrecisionCollision();
previousPosition = transform.position;
}
///
/// SphereCast 기반 정밀 충돌 (기존 로직 100% 유지)
///
private void CheckPrecisionCollision()
{
Vector3 tipPosition = arrowTip != null ? arrowTip.position : transform.position;
Vector3 direction = rb != null && rb.velocity.magnitude > 0.1f
? rb.velocity.normalized
: transform.forward;
float frameDistance = Vector3.Distance(previousPosition, transform.position);
float checkDistance = Mathf.Max(frameDistance, raycastDistance);
RaycastHit hit;
bool didHit = Physics.SphereCast(
tipPosition,
raycastRadius,
direction,
out hit,
checkDistance,
hitLayers
);
if (showDebugRay)
{
Debug.DrawRay(tipPosition, direction * checkDistance, didHit ? Color.red : Color.green, 0.1f);
}
if (didHit)
{
HandleHit(hit.collider, hit.point);
}
}
///
/// [MODIFIED] 충돌 처리 — 속성 데미지 적용 추가
///
private void HandleHit(Collider hitCollider, Vector3 hitPoint)
{
if (hasHit) return;
hasHit = true;
// 적 감지 (Tag 또는 Layer)
bool isEnemy = hitCollider.CompareTag("Enemy");
// EnemyHitBox 레이어 체크 (레이어가 존재하는 경우에만)
int enemyHitBoxLayerIndex = LayerMask.NameToLayer("EnemyHitBox");
if (!isEnemy && enemyHitBoxLayerIndex != -1)
{
isEnemy = hitCollider.gameObject.layer == enemyHitBoxLayerIndex;
}
if (isEnemy)
{
// MonsterClass를 찾아 데미지 적용
MonsterClass monster = hitCollider.GetComponentInParent();
if (monster == null) monster = hitCollider.GetComponent();
if (monster != null)
{
// 1. 기본 데미지 적용
monster.TakeDamage(damage);
Debug.Log($"적 명중! 기본데미지: {damage}");
// 2. [NEW] 속성 효과 적용
ApplyElementEffect(monster);
}
Destroy(gameObject, 0.05f);
}
else if (hitCollider.CompareTag("Wall") || hitCollider.CompareTag("Ground"))
{
Debug.Log($"벽/바닥 충돌! 위치: {hitPoint}");
Destroy(gameObject, 0.05f);
}
else
{
// 기타 충돌 (Unknown)
Destroy(gameObject, 0.05f);
}
}
///
/// [NEW] 속성 효과 적용
/// MonsterClass에 ApplyStatusEffect 메서드가 있어야 합니다.
///
private void ApplyElementEffect(MonsterClass monster)
{
if (elementType == ArrowElementType.None || elementDamage <= 0f) return;
// MonsterClass에 ApplyStatusEffect가 있는지 확인
switch (elementType)
{
case ArrowElementType.Fire:
monster.ApplyStatusEffect(StatusEffectType.Burn, elementDamage, elementDuration);
Debug.Log($"화염 효과! {elementDamage} 데미지 x {elementDuration}초");
break;
case ArrowElementType.Ice:
monster.ApplyStatusEffect(StatusEffectType.Slow, elementDamage, elementDuration);
Debug.Log($"빙결 효과! 슬로우 {elementDuration}초");
break;
case ArrowElementType.Poison:
monster.ApplyStatusEffect(StatusEffectType.Poison, elementDamage, elementDuration);
Debug.Log($"독 효과! {elementDamage} 데미지 x {elementDuration}초");
break;
case ArrowElementType.Lightning:
monster.ApplyStatusEffect(StatusEffectType.Shock, elementDamage, elementDuration);
Debug.Log($"감전 효과! {elementDamage} 범위 데미지");
break;
}
}
///
/// 백업 충돌 감지 (OnTriggerEnter) — 기존 로직 유지
///
private void OnTriggerEnter(Collider other)
{
if (hasHit) return;
bool isEnemy = other.CompareTag("Enemy");
int enemyHitBoxLayerIndex = LayerMask.NameToLayer("EnemyHitBox");
if (!isEnemy && enemyHitBoxLayerIndex != -1)
{
isEnemy = other.gameObject.layer == enemyHitBoxLayerIndex;
}
if (isEnemy)
{
HandleHit(other, other.ClosestPoint(transform.position));
}
else if (other.CompareTag("Wall") || other.CompareTag("Ground"))
{
hasHit = true;
Destroy(gameObject, 0.05f);
}
}
///
/// Gizmo 디버그 시각화 — 기존 로직 유지 + 속성 색상 추가
///
private void OnDrawGizmos()
{
if (!Application.isPlaying || !isFired || !showDebugRay) return;
Vector3 tipPosition = arrowTip != null ? arrowTip.position : transform.position;
Vector3 direction = rb != null && rb.velocity.magnitude > 0.1f
? rb.velocity.normalized
: transform.forward;
Gizmos.color = hasHit ? Color.red : Color.green;
Gizmos.DrawWireSphere(tipPosition, raycastRadius);
Gizmos.DrawWireSphere(tipPosition + direction * raycastDistance, raycastRadius);
Gizmos.color = Color.yellow;
Gizmos.DrawLine(tipPosition, tipPosition + direction * raycastDistance);
// [NEW] 속성별 색상 표시
switch (elementType)
{
case ArrowElementType.Fire: Gizmos.color = Color.red; break;
case ArrowElementType.Ice: Gizmos.color = Color.cyan; break;
case ArrowElementType.Poison: Gizmos.color = Color.green; break;
case ArrowElementType.Lightning: Gizmos.color = Color.yellow; break;
}
if (elementType != ArrowElementType.None)
{
Gizmos.DrawWireSphere(transform.position, 0.5f);
}
}
}