using UnityEngine; using Platformer.Mechanics; namespace EerieVillage.Skills.Effectors { /// /// 적 상태 컴포넌트 통합 파일. /// DoT·Stun·Slow 세 MonoBehaviour를 단일 파일에 정의 (C14 토큰·파일 최소화). /// BT12-Dev Phase 2-B §4-7. /// // ------------------------------------------------------------------------- // EnemyDoTState — 화염 지속 대미지 // ------------------------------------------------------------------------- /// /// 적에게 주기적 DoT(Damage over Time)을 가하는 컴포넌트. /// StatusApplier.ApplyDoT 에서 AddComponent 또는 기존 컴포넌트 재사용. /// 동일 적에 복수 AddDoT 호출 시 마지막 호출로 덮어씌운다 (스택 비허용 — Phase 2 범위). /// public class EnemyDoTState : MonoBehaviour { private int _damagePerTick; private float _duration; private float _interval; private float _elapsed; private float _tickElapsed; private GameObject _fxInstance; // BT12-Dev 2026-05-13 — DoT 시각 이펙트 자식 // BT12-Dev 2026-05-13 — fxPrefab 영역 자식 Instantiate·ParticleSystem.main.duration 영역 DoT 지속 영역 자동 정합 public void AddDoT(int dmg, float duration, float interval, GameObject fxPrefab = null) { _damagePerTick = dmg; _interval = interval; _elapsed = 0f; _tickElapsed = 0f; // 기존 fx 영역 영역 (스택 비허용·마지막 호출 영역 영역) if (_fxInstance != null) { Destroy(_fxInstance); _fxInstance = null; } float fxDuration = duration; if (fxPrefab != null) { _fxInstance = Instantiate(fxPrefab, transform.position, Quaternion.identity, transform); _fxInstance.transform.localPosition = Vector3.zero; _fxInstance.transform.localScale *= 0.5f; // PD 지시 2026-05-13 — 불태우기 이펙트 50% 축소 var ps = _fxInstance.GetComponent(); if (ps == null) ps = _fxInstance.GetComponentInChildren(); if (ps != null) { var main = ps.main; fxDuration = main.duration + main.startLifetime.constantMax; } } // PD 지시 2026-05-13 정정 — data.DotDuration 우선 (fire_loop·glow_loop 영역 5s 영역 영역 영역). // duration > 0 영역 영역 영역 영역, 영역 영역 영역 fx PS 영역 영역. _duration = (duration > 0f) ? duration : ((fxDuration > 0f) ? fxDuration : 0f); } private void Update() { if (_duration <= 0f) return; _elapsed += Time.deltaTime; _tickElapsed += Time.deltaTime; if (_tickElapsed >= _interval) { _tickElapsed = 0f; var hp = GetComponent(); // PD 지시 2026-05-13 — DoT 는 무적 시간 무시·갱신 X (새 투사체 도착 시 무적 막힘 회피) if (hp != null && hp.IsAlive) { hp.DecrementBypassInvuln(_damagePerTick); // PD 지시 2026-05-13 — 도트 피해 시 hit 모션 X·SpriteRenderer 영역 alpha 0.5 + 붉은색 1 frame flash StartCoroutine(FlashDotHurt()); // PD 지시 2026-05-13 — DoT 가 Enemy 를 죽일 때 EnemyDeath Schedule 정합 발화·불태우기 즉시 정리 if (!hp.IsAlive) { var enemy = GetComponent(); if (enemy != null) Platformer.Core.Simulation.Schedule().enemy = enemy; if (_fxInstance != null) Destroy(_fxInstance); Destroy(this); return; } } } if (_elapsed >= _duration) { if (_fxInstance != null) Destroy(_fxInstance); Destroy(this); } } private void OnDestroy() { if (_fxInstance != null) Destroy(_fxInstance); } // PD 지시 2026-05-13 — DoT tick 영역 SpriteRenderer 영역 alpha 0.5·붉은색 1 frame flash 후 원복 System.Collections.IEnumerator FlashDotHurt() { var sr = GetComponent(); if (sr == null) sr = GetComponentInChildren(); if (sr == null) yield break; var orig = sr.color; sr.color = new Color(1f, 0.3f, 0.3f, 0.5f); yield return null; if (sr != null) sr.color = orig; } } // ------------------------------------------------------------------------- // EnemyStunState — 기절 (EnemyController.enabled = false) // ------------------------------------------------------------------------- /// /// 적을 기절시키는 컴포넌트. /// EnemyController.enabled = false 로 patrol·Update 를 일시 정지한다. /// 지속 시간 만료 시 EnemyController.enabled = true 복원. /// public class EnemyStunState : MonoBehaviour { private float _duration; private float _elapsed; private EnemyController _enemy; private void Awake() { _enemy = GetComponent(); } public void ApplyStun(float duration) { _duration = duration; _elapsed = 0f; if (_enemy != null) _enemy.enabled = false; } private void Update() { if (_duration <= 0f) return; _elapsed += Time.deltaTime; if (_elapsed >= _duration) { if (_enemy != null) _enemy.enabled = true; Destroy(this); } } } // ------------------------------------------------------------------------- // EnemySlowState — 감속 (AnimationController.maxSpeed 축소) // ------------------------------------------------------------------------- /// /// 적을 감속시키는 컴포넌트. /// AnimationController.maxSpeed 를 multiplier 배율로 축소하고, 지속 시간 만료 시 원복. /// EnemyController 는 AnimationController 를 RequireComponent 하므로 항상 존재. /// public class EnemySlowState : MonoBehaviour { private float _duration; private float _multiplier; private float _elapsed; private float _origMaxSpeed; private AnimationController _anim; private void Awake() { _anim = GetComponent(); _origMaxSpeed = _anim != null ? _anim.maxSpeed : 0f; } public void ApplySlow(float duration, float multiplier) { _duration = duration; _multiplier = multiplier; _elapsed = 0f; if (_anim != null) _anim.maxSpeed = _origMaxSpeed * _multiplier; } private void Update() { if (_duration <= 0f) return; _elapsed += Time.deltaTime; if (_elapsed >= _duration) { if (_anim != null) _anim.maxSpeed = _origMaxSpeed; Destroy(this); } } } }