204 lines
7.7 KiB
C#
204 lines
7.7 KiB
C#
using UnityEngine;
|
|
using Platformer.Mechanics;
|
|
|
|
namespace EerieVillage.Skills.Effectors
|
|
{
|
|
/// <summary>
|
|
/// 적 상태 컴포넌트 통합 파일.
|
|
/// DoT·Stun·Slow 세 MonoBehaviour를 단일 파일에 정의 (C14 토큰·파일 최소화).
|
|
/// BT12-Dev Phase 2-B §4-7.
|
|
/// </summary>
|
|
|
|
// -------------------------------------------------------------------------
|
|
// EnemyDoTState — 화염 지속 대미지
|
|
// -------------------------------------------------------------------------
|
|
|
|
/// <summary>
|
|
/// 적에게 주기적 DoT(Damage over Time)을 가하는 컴포넌트.
|
|
/// StatusApplier.ApplyDoT 에서 AddComponent 또는 기존 컴포넌트 재사용.
|
|
/// 동일 적에 복수 AddDoT 호출 시 마지막 호출로 덮어씌운다 (스택 비허용 — Phase 2 범위).
|
|
/// </summary>
|
|
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 시각 이펙트 자식
|
|
|
|
// PD 지시 2026-05-13 — fxPrefab 영역 자식 Instantiate·DotFxScale Inspector 배율 적용
|
|
public void AddDoT(int dmg, float duration, float interval, GameObject fxPrefab = null, float fxScale = 1f)
|
|
{
|
|
_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.hideFlags = HideFlags.DontSave; // PD 지시 2026-05-13 — Scene 저장 회피
|
|
_fxInstance.transform.localPosition = Vector3.zero;
|
|
_fxInstance.transform.localScale *= fxScale; // PD 지시 2026-05-13 — Inspector DotFxScale 배율 (기존 hardcoded 0.5 → 필드 이관)
|
|
var ps = _fxInstance.GetComponent<ParticleSystem>();
|
|
if (ps == null) ps = _fxInstance.GetComponentInChildren<ParticleSystem>();
|
|
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<Health>();
|
|
// PD 지시 2026-05-13 — DoT 는 무적 시간 무시·갱신 X (새 투사체 도착 시 무적 막힘 회피)
|
|
if (hp != null && hp.IsAlive)
|
|
{
|
|
hp.DecrementBypassInvuln(_damagePerTick);
|
|
// PD 지시 2026-05-13 — flash 영역 Health 영역 영역 (FlashDotHurt 폐기)
|
|
|
|
// PD 지시 2026-05-13 — DoT 가 Enemy 를 죽일 때 EnemyDeath Schedule 정합 발화·불태우기 즉시 정리
|
|
if (!hp.IsAlive)
|
|
{
|
|
var enemy = GetComponent<Platformer.Mechanics.EnemyController>();
|
|
if (enemy != null)
|
|
Platformer.Core.Simulation.Schedule<Platformer.Gameplay.EnemyDeath>().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<SpriteRenderer>();
|
|
if (sr == null) sr = GetComponentInChildren<SpriteRenderer>();
|
|
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)
|
|
// -------------------------------------------------------------------------
|
|
|
|
/// <summary>
|
|
/// 적을 기절시키는 컴포넌트.
|
|
/// EnemyController.enabled = false 로 patrol·Update 를 일시 정지한다.
|
|
/// 지속 시간 만료 시 EnemyController.enabled = true 복원.
|
|
/// </summary>
|
|
public class EnemyStunState : MonoBehaviour
|
|
{
|
|
private float _duration;
|
|
private float _elapsed;
|
|
private EnemyController _enemy;
|
|
|
|
private void Awake()
|
|
{
|
|
_enemy = GetComponent<EnemyController>();
|
|
}
|
|
|
|
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 축소)
|
|
// -------------------------------------------------------------------------
|
|
|
|
/// <summary>
|
|
/// 적을 감속시키는 컴포넌트.
|
|
/// AnimationController.maxSpeed 를 multiplier 배율로 축소하고, 지속 시간 만료 시 원복.
|
|
/// EnemyController 는 AnimationController 를 RequireComponent 하므로 항상 존재.
|
|
/// </summary>
|
|
public class EnemySlowState : MonoBehaviour
|
|
{
|
|
private float _duration;
|
|
private float _multiplier;
|
|
private float _elapsed;
|
|
private float _origMaxSpeed;
|
|
private AnimationController _anim;
|
|
|
|
private void Awake()
|
|
{
|
|
_anim = GetComponent<AnimationController>();
|
|
_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);
|
|
}
|
|
}
|
|
}
|
|
}
|