2026-05-09 10:00:27 +00:00
|
|
|
|
using UnityEngine;
|
|
|
|
|
|
using Platformer.Mechanics;
|
|
|
|
|
|
|
|
|
|
|
|
namespace EerieVillage.Skills.Effectors
|
|
|
|
|
|
{
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 상태 효과 일괄 적용기.
|
|
|
|
|
|
/// Projectile.OnTriggerEnter2D 등 타격 판정 후 ActiveSkillData 필드를 읽어
|
|
|
|
|
|
/// DoT·Stun·Slow·Knockback·DebuffStack 을 EnemyController 에 적용한다.
|
|
|
|
|
|
/// BT12-Dev Phase 2-B §4-5.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public static class StatusApplier
|
|
|
|
|
|
{
|
|
|
|
|
|
public static void Apply(ActiveSkillData data, EnemyController enemy)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (data == null || enemy == null) return;
|
|
|
|
|
|
|
2026-05-12 16:48:30 +00:00
|
|
|
|
// DoT (화염·독 등) — DotInterval > 0 영역만 발화 (DotDuration 영역 OnDotFxPrefab.ParticleSystem.main.duration 영역 자동 영역)
|
|
|
|
|
|
// BT12-Dev 2026-05-13 — damage = BaseDamage × DotDamageMultiplier (PD 영역: 25%) + OnDotFxPrefab 자식 spawn
|
|
|
|
|
|
if ((data.DotDuration > 0f || data.OnDotFxPrefab != null) && data.DotInterval > 0f)
|
2026-05-09 10:00:27 +00:00
|
|
|
|
{
|
2026-05-12 16:48:30 +00:00
|
|
|
|
int dotDamage = Mathf.Max(1, Mathf.RoundToInt(data.BaseDamage * data.DotDamageMultiplier));
|
2026-05-13 09:05:13 +00:00
|
|
|
|
ApplyDoT(enemy, dotDamage, data.DotDuration, data.DotInterval, data.OnDotFxPrefab, data.DotFxScale);
|
2026-05-09 10:00:27 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 기절 (스턴)
|
|
|
|
|
|
if (data.StunDuration > 0f)
|
|
|
|
|
|
{
|
|
|
|
|
|
ApplyStun(enemy, data.StunDuration);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 감속 (슬로우)
|
|
|
|
|
|
if (data.SlowDuration > 0f && data.SlowMultiplier < 1.0f)
|
|
|
|
|
|
{
|
|
|
|
|
|
ApplySlow(enemy, data.SlowDuration, data.SlowMultiplier);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 넉백
|
|
|
|
|
|
if (data.KnockbackForce > 0f)
|
|
|
|
|
|
{
|
|
|
|
|
|
ApplyKnockback(enemy, data.KnockbackForce);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 저주 스택 (A08 저주의 화살)
|
|
|
|
|
|
if (data.DebuffStackLimit > 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
DebuffStack.AddStack(enemy, data.DebuffStackLimit, data.BaseDamage);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// -------------------------------------------------------------------
|
|
|
|
|
|
// 내부 헬퍼
|
|
|
|
|
|
// -------------------------------------------------------------------
|
|
|
|
|
|
|
2026-05-13 09:05:13 +00:00
|
|
|
|
private static void ApplyDoT(EnemyController enemy, int damagePerTick, float duration, float interval, GameObject fxPrefab = null, float fxScale = 1f)
|
2026-05-09 10:00:27 +00:00
|
|
|
|
{
|
|
|
|
|
|
var existing = enemy.GetComponent<EnemyDoTState>();
|
|
|
|
|
|
if (existing == null) existing = enemy.gameObject.AddComponent<EnemyDoTState>();
|
2026-05-13 09:05:13 +00:00
|
|
|
|
existing.AddDoT(damagePerTick, duration, interval, fxPrefab, fxScale);
|
2026-05-09 10:00:27 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private static void ApplyStun(EnemyController enemy, float duration)
|
|
|
|
|
|
{
|
|
|
|
|
|
var existing = enemy.GetComponent<EnemyStunState>();
|
|
|
|
|
|
if (existing == null) existing = enemy.gameObject.AddComponent<EnemyStunState>();
|
|
|
|
|
|
existing.ApplyStun(duration);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private static void ApplySlow(EnemyController enemy, float duration, float multiplier)
|
|
|
|
|
|
{
|
|
|
|
|
|
var existing = enemy.GetComponent<EnemySlowState>();
|
|
|
|
|
|
if (existing == null) existing = enemy.gameObject.AddComponent<EnemySlowState>();
|
|
|
|
|
|
existing.ApplySlow(duration, multiplier);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private static void ApplyKnockback(EnemyController enemy, float force)
|
|
|
|
|
|
{
|
|
|
|
|
|
var rb = enemy.GetComponent<Rigidbody2D>();
|
|
|
|
|
|
if (rb != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
// facing 반대 방향 넉백 — 단순 +x 방향 (Phase 2 범위, Phase 2-C에서 방향 정합 예정)
|
|
|
|
|
|
rb.AddForce(Vector2.right * force, ForceMode2D.Impulse);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|