using System; using Platformer.Gameplay; using UnityEngine; using static Platformer.Core.Simulation; namespace Platformer.Mechanics { /// /// Represents the current vital statistics of some game entity. /// /// BT7-Plan PD 지시 2026-04-24 — 하트 분할 시스템 (젤다 방식). 하트 1개 = 4 쿼터(HP). /// 카드(패시브)·성장으로 를 증가시킬 수 있으며, 그에 따라 /// 가 `maxHearts * QuartersPerHeart`로 자동 산정된다. /// /// /// 적 ATK는 쿼터 단위. 피해 1 = 1 쿼터 감소, 피해 2 = 반조각, 피해 4 = 하트 1개 소멸. /// /// /// BT5-Dev Hero1 2026-05-07 — Animator hit/dead/resurrect Trigger 연동 추가. /// OnDamagedEvent · OnDeathEvent · OnResurrectEvent C# event 3종 신설. /// BT12-Dev: ISkillRuntime·IActiveSkill 향후 통합 시 이벤트 hook 영역 활용 가능. /// /// public class Health : MonoBehaviour { /// /// 하트 1개당 HP(쿼터) 수 — 젤다 방식 고정값 4. /// public const int QuartersPerHeart = 4; /// /// 초기 보유 하트 수. Player는 기본 1(= 4 HP), 카드·성장으로 를 통해 증가. /// Enemy는 기본 1(= 4 HP)이나 향후 balance/01 v0.2 수치 테이블에 따라 재설정. /// [Tooltip("초기 보유 하트 수. 하트 1개 = 4 HP.")] public int maxHearts = 1; /// /// 산정된 최대 HP(쿼터 단위). 직접 설정 대신 를 통해 조정한다 (시리얼라이즈 호환 유지). /// BT7-Plan 이전에는 1로 고정되어 있었으나, 현재 PD 지시로 하트 분할 시스템 전환. /// [Tooltip("산정된 최대 HP(쿼터). maxHearts * 4. Inspector에서 직접 수정하지 말 것.")] public int maxHP = QuartersPerHeart; /// /// 무적 시간 (i-frame) 지속 초 단위. 기획 04 §3-2 후보 0.4~0.8s. /// 연속 히트 방지용 (하트 분할 전환 후에도 강한 단일 공격을 한 번에 과도 피해 받지 않도록 유지). /// public float invulnerableDuration = 0.6f; /// /// 현재 무적 상태 여부 (디버그·UX 피드백용 — 깜박임 제어 등에 사용 가능). /// public bool IsInvulnerable => Time.time < invulnerableUntil; /// /// Indicates if the entity should be considered 'alive'. /// public bool IsAlive => currentHP > 0; /// /// 현재 HP(쿼터). HUD는 이 값과 를 함께 읽어 하트 분할 UI를 그린다. /// public int CurrentHP => currentHP; // BT5-Dev Hero1 2026-05-07: C# event 3종 — UI·SE·StateTimer 구독용 /// 피격 시 발화. damage = 실제 감산된 쿼터 수. public event Action OnDamagedEvent; /// 사망(HP=0) 시 발화. public event Action OnDeathEvent; /// 부활 시 발화. public event Action OnResurrectEvent; // BT7-Plan 영역: 부활 룰 충족 여부 — 기획팀 결정 보류. 외부에서 설정 후 Resurrect() 호출. [Tooltip("부활 룰 충족 여부. BT7-Plan 부활 시스템 외부 제어 영역.")] public bool canResurrect = false; int currentHP; float invulnerableUntil = -1f; /// /// Increment the HP of the entity by a single quarter. /// 카드·픽업으로 하트 1/4 회복 (기존 API 호환). /// public void Increment() { currentHP = Mathf.Clamp(currentHP + 1, 0, maxHP); } /// /// 쿼터 단위 회복. 하트 1개 전체 회복은 quarters=4 호출. /// /// 회복할 쿼터 수 (0 이하면 무시). public void Heal(int quarters) { if (quarters <= 0) return; currentHP = Mathf.Clamp(currentHP + quarters, 0, maxHP); } /// /// 단일 피해(기본 1 쿼터). 무적 시간 내에는 호출되어도 스킵된다. /// public void Decrement() { Decrement(1); } /// /// 쿼터 단위 피해 처리. BT7-Plan 2026-04-24 — 적 ATK는 쿼터 단위(`PlayerHP -= EnemyATK`). /// 무적 시간 내에는 완전 스킵(부분 히트도 인정하지 않음 — 연타 방지 원칙). /// BT5-Dev Hero1 2026-05-07 — hit Trigger + dead Bool Animator 연동 추가. /// /// 피해량(쿼터). 1 이상 값만 유효. public void Decrement(int damage) { if (damage <= 0) return; // BT5-Dev 2단계: i-frame 보호 — 무적 시간 내 중복 피격 차단 if (Time.time < invulnerableUntil) { return; } currentHP = Mathf.Clamp(currentHP - damage, 0, maxHP); // 피격 성공 시 무적 시간 활성화 (다음 피격 대비) if (invulnerableDuration > 0f) { invulnerableUntil = Time.time + invulnerableDuration; } // [BT5-Dev Hero1] hit 애니메이션 트리거 (생존 시만 — 사망 시는 dead Bool로 처리) if (currentHP > 0) { var animator = GetComponent(); if (animator != null) animator.SetTrigger("hit"); OnDamagedEvent?.Invoke(damage); } if (currentHP == 0) { // [BT5-Dev Hero1] death State 진입 var animator = GetComponent(); if (animator != null) animator.SetBool("dead", true); OnDeathEvent?.Invoke(); var ev = Schedule(); ev.health = this; } } /// /// 최대 하트 수를 증가시키고 현재 HP도 같은 쿼터만큼 비례 증가. /// BT7-Plan 2026-04-24 — 패시브 카드 `[방호]`·`[회복]` 효과 훅. /// /// 증가시킬 하트 수 (음수 허용하지만 currentHP가 maxHP를 초과하지 않도록 clamp). public void IncreaseMaxHearts(int delta) { if (delta == 0) return; int beforeMax = maxHP; maxHearts = Mathf.Max(0, maxHearts + delta); maxHP = maxHearts * QuartersPerHeart; if (delta > 0) { int added = maxHP - beforeMax; currentHP = Mathf.Clamp(currentHP + added, 0, maxHP); } else { currentHP = Mathf.Clamp(currentHP, 0, maxHP); if (currentHP == 0 && maxHP > 0) { var ev = Schedule(); ev.health = this; } } } /// /// 부활 발동 — death 애니메이션 종료 후 외부(BT7-Plan 부활 룰 영역)에서 호출. /// currentHP 복원 + Animator transition (death → resurrection → idle). /// SOT: 캐릭터_리소스_규칙_v1.md §3.1.4 death → resurrection 시퀀스. /// BT7-Plan 영역: 부활 시 maxHP 회복 룰 — 현재 전체 회복, 향후 부분 회복 룰 결정 시 Resurrect(int) 오버로드 추가. /// public void Resurrect() { if (IsAlive) return; // 이미 살아있으면 무시 currentHP = maxHP; // BT7-Plan 영역 — 부활 시 maxHP 회복 룰 invulnerableUntil = -1f; var animator = GetComponent(); if (animator != null) { animator.SetBool("dead", false); animator.SetTrigger("resurrect"); } OnResurrectEvent?.Invoke(); } /// /// Decrement the HP of the entity until HP reaches 0. /// i-frame 우회하여 즉사 처리 (낙사·승리 이탈 등 시스템 강제 사망). /// BT5-Dev Hero1 2026-05-07 — dead Bool Animator 연동 추가. /// public void Die() { invulnerableUntil = -1f; // i-frame 무효화 if (currentHP > 0) { currentHP = 0; // [BT5-Dev Hero1] death State 진입 var animator = GetComponent(); if (animator != null) animator.SetBool("dead", true); OnDeathEvent?.Invoke(); var ev = Schedule(); ev.health = this; } } void Awake() { // maxHearts가 설정되어 있고 maxHP가 구식 값(1)으로 남아있는 프리팹 호환 처리. // 정상 흐름: maxHP = maxHearts * 4, currentHP = maxHP로 초기화. if (maxHearts <= 0) maxHearts = 1; int expected = maxHearts * QuartersPerHeart; if (maxHP != expected) maxHP = expected; currentHP = maxHP; } } }