using UnityEngine;
using Colosseum;
namespace Colosseum.Skills
{
///
/// 스킬 효과의 기본 클래스.
/// 모든 효과는 이 클래스를 상속받아 구현합니다.
/// 효과 발동 타이밍은 애니메이션 이벤트(OnEffect)로 제어합니다.
///
public abstract class SkillEffect : ScriptableObject
{
[Header("대상 설정")]
[Tooltip("Self: 시전자, Area: 범위 내 대상")]
[SerializeField] protected TargetType targetType = TargetType.Self;
[Header("Area Settings (TargetType이 Area일 때)")]
[Tooltip("범위 내에서 공격할 대상 필터")]
[SerializeField] protected TargetTeam targetTeam = TargetTeam.Enemy;
[SerializeField] protected AreaCenterType areaCenter = AreaCenterType.Caster;
[Min(0.1f)] [SerializeField] protected float areaRadius = 3f;
[SerializeField] protected LayerMask targetLayers;
// Properties
public TargetType TargetType => targetType;
public TargetTeam TargetTeam => targetTeam;
///
/// 스킬 시전 시 호출
///
public void ExecuteOnCast(GameObject caster)
{
switch (targetType)
{
case TargetType.Self:
ApplyEffect(caster, caster);
break;
case TargetType.Area:
ExecuteArea(caster);
break;
}
}
///
/// 투사체 충돌 시 호출
///
public void ExecuteOnHit(GameObject caster, GameObject hitTarget)
{
if (IsValidTarget(caster, hitTarget))
{
ApplyEffect(caster, hitTarget);
}
}
///
/// 실제 효과 적용 (상속받은 클래스에서 구현)
///
protected abstract void ApplyEffect(GameObject caster, GameObject target);
///
/// 충돌한 대상이 유효한 타겟인지 확인
///
public bool IsValidTarget(GameObject caster, GameObject target)
{
if (target == null) return false;
// 레이어 체크
if (targetLayers.value != 0 && (targetLayers.value & (1 << target.layer)) == 0)
return false;
// 팀 체크
return IsCorrectTeam(caster, target);
}
private bool IsCorrectTeam(GameObject caster, GameObject target)
{
bool isSameTeam = Team.IsSameTeam(caster, target);
return targetTeam switch
{
TargetTeam.Enemy => !isSameTeam,
TargetTeam.Ally => isSameTeam,
TargetTeam.All => true,
_ => true
};
}
private void ExecuteArea(GameObject caster)
{
Vector3 center = GetAreaCenter(caster);
Collider[] hits = Physics.OverlapSphere(center, areaRadius, targetLayers);
foreach (var hit in hits)
{
if (hit.gameObject == caster) continue;
if (!IsCorrectTeam(caster, hit.gameObject)) continue;
ApplyEffect(caster, hit.gameObject);
}
}
private Vector3 GetAreaCenter(GameObject caster)
{
if (caster == null) return Vector3.zero;
return areaCenter switch
{
AreaCenterType.Caster => caster.transform.position,
AreaCenterType.CasterForward => caster.transform.position + caster.transform.forward * areaRadius,
_ => caster.transform.position
};
}
}
public enum TargetType
{
Self,
Area
}
public enum TargetTeam
{
Enemy,
Ally,
All
}
public enum AreaCenterType
{
Caster,
CasterForward
}
}