Files
Colosseum/Assets/_Game/Scripts/Editor/DebugBossMenuItems.cs
dal4segno 904bc88d36 feat: 드로그 보스 AI 및 런타임 상태 구조 재구성
- 드로그 전투 컨텍스트를 BossBehaviorRuntimeState 중심 구조로 정리하고 BossEnemy, 패턴 액션, 조건 노드가 마지막 실행 결과와 phase 상태를 직접 사용하도록 갱신
- BT_Drog와 재빌드 에디터 스크립트를 확장해 phase 전환, 집행 결과 분기, 거리/쿨타임 기반 패턴 선택을 드로그 전용 자산과 노드 파라미터로 재구성
- 드로그 패턴/스킬/이펙트/애니메이션 플레이스홀더 자산을 재생성하고 보스 프리팹이 새 런타임 상태 및 등록 클립 구성을 참조하도록 정리
2026-04-06 13:56:47 +09:00

259 lines
9.4 KiB
C#

using System.Text;
using Colosseum.Enemy;
using Colosseum.Abnormalities;
using UnityEditor;
using UnityEngine;
using UnityEngine.Networking;
namespace Colosseum.Editor
{
/// <summary>
/// MCP (execute_menu_item) 또는 에디터 메뉴에서 호출 가능한 보스 디버그 커맨드.
/// 파라미터가 필요한 연산은 static 필드를 설정한 뒤 Custom MenuItem을 호출합니다.
/// </summary>
public static class DebugBossMenuItems
{
private const string StunAbnormalityPath = "Assets/_Game/Data/Abnormalities/Data_Abnormality_Player_Stun.asset";
private const string SilenceAbnormalityPath = "Assets/_Game/Data/Abnormalities/Data_Abnormality_Player_Silence.asset";
// ── MCP가 설정 후 Custom 메뉴 호출하는 파라미터 필드 ──
/// <summary>
/// Set Boss HP Custom에서 사용할 퍼센트 (0.0 ~ 1.0).
/// MCP는 script_apply_edits로 이 값을 변경한 뒤 Custom 메뉴를 호출합니다.
/// </summary>
public static float customHPPercent = 0.5f;
/// <summary>
/// Force Phase Custom에서 사용할 페이즈 인덱스 (0-based).
/// </summary>
public static int customPhaseIndex = 0;
/// <summary>
/// Apply Shield Custom에서 사용할 보호막 수치.
/// </summary>
public static float customShieldAmount = 1000f;
// ── 보스 HP ──
[MenuItem("Tools/Colosseum/Debug/Boss/Set HP 10%")]
private static void SetHP10() => SetBossHP(0.1f);
[MenuItem("Tools/Colosseum/Debug/Boss/Set HP 25%")]
private static void SetHP25() => SetBossHP(0.25f);
[MenuItem("Tools/Colosseum/Debug/Boss/Set HP 50%")]
private static void SetHP50() => SetBossHP(0.5f);
[MenuItem("Tools/Colosseum/Debug/Boss/Set HP 75%")]
private static void SetHP75() => SetBossHP(0.75f);
[MenuItem("Tools/Colosseum/Debug/Boss/Set HP 100%")]
private static void SetHP100() => SetBossHP(1f);
[MenuItem("Tools/Colosseum/Debug/Boss/Set HP Custom")]
private static void SetHPCustom() => SetBossHP(Mathf.Clamp01(customHPPercent));
[MenuItem("Tools/Colosseum/Debug/Boss/Full Heal")]
private static void FullHeal()
{
BossEnemy boss = FindBoss();
if (boss == null) return;
boss.Heal(boss.MaxHealth);
Debug.Log($"[Debug] 보스 HP 풀회복 | HP={boss.CurrentHealth:F0}/{boss.MaxHealth:F0}");
}
// ── 보스 제어 ──
[MenuItem("Tools/Colosseum/Debug/Boss/Force Phase 0")]
private static void ForcePhase0() => ForcePhase(0);
[MenuItem("Tools/Colosseum/Debug/Boss/Force Phase 1")]
private static void ForcePhase1() => ForcePhase(1);
[MenuItem("Tools/Colosseum/Debug/Boss/Force Phase 2")]
private static void ForcePhase2() => ForcePhase(2);
[MenuItem("Tools/Colosseum/Debug/Boss/Force Phase Custom")]
private static void ForcePhaseCustom() => ForcePhase(customPhaseIndex);
[MenuItem("Tools/Colosseum/Debug/Boss/Restart Current Phase")]
private static void RestartPhase()
{
BossEnemy boss = FindBoss();
if (boss == null) return;
BossBehaviorRuntimeState context = boss.GetComponent<BossBehaviorRuntimeState>();
if (context == null) return;
context.RestartCurrentPhaseTimer();
Debug.Log($"[Debug] 보스 현재 페이즈 재시작 | Phase={context.CurrentPatternPhase}");
}
[MenuItem("Tools/Colosseum/Debug/Boss/Respawn")]
private static void Respawn()
{
BossEnemy boss = FindBoss();
if (boss == null) return;
boss.Respawn();
Debug.Log($"[Debug] 보스 리스폰 | HP={boss.CurrentHealth:F0}/{boss.MaxHealth:F0}");
}
// ── 보호막 ──
[MenuItem("Tools/Colosseum/Debug/Boss/Apply Shield 500")]
private static void ApplyShield500() => ApplyShield(500f);
[MenuItem("Tools/Colosseum/Debug/Boss/Apply Shield 1000")]
private static void ApplyShield1000() => ApplyShield(1000f);
[MenuItem("Tools/Colosseum/Debug/Boss/Apply Shield 5000")]
private static void ApplyShield5000() => ApplyShield(5000f);
[MenuItem("Tools/Colosseum/Debug/Boss/Apply Shield Custom")]
private static void ApplyShieldCustom() => ApplyShield(Mathf.Max(0f, customShieldAmount));
// ── 이상상태 ──
[MenuItem("Tools/Colosseum/Debug/Boss/Apply Stun")]
private static void ApplyStun()
{
BossEnemy boss = FindBoss();
if (boss == null) return;
AbnormalityData data = AssetDatabase.LoadAssetAtPath<AbnormalityData>(StunAbnormalityPath);
if (data == null)
{
Debug.LogWarning($"[Debug] 기절 에셋을 찾지 못했습니다: {StunAbnormalityPath}");
return;
}
AbnormalityManager am = boss.GetComponent<AbnormalityManager>();
if (am != null)
{
am.ApplyAbnormality(data, boss.gameObject);
Debug.Log($"[Debug] 보스 기절 적용 | {data.abnormalityName}");
}
}
[MenuItem("Tools/Colosseum/Debug/Boss/Apply Silence")]
private static void ApplySilence()
{
BossEnemy boss = FindBoss();
if (boss == null) return;
AbnormalityData data = AssetDatabase.LoadAssetAtPath<AbnormalityData>(SilenceAbnormalityPath);
if (data == null)
{
Debug.LogWarning($"[Debug] 침묵 에셋을 찾지 못했습니다: {SilenceAbnormalityPath}");
return;
}
AbnormalityManager am = boss.GetComponent<AbnormalityManager>();
if (am != null)
{
am.ApplyAbnormality(data, boss.gameObject);
Debug.Log($"[Debug] 보스 침묵 적용 | {data.abnormalityName}");
}
}
[MenuItem("Tools/Colosseum/Debug/Boss/Clear Threat")]
private static void ClearThreat()
{
BossEnemy boss = FindBoss();
if (boss == null) return;
boss.ClearAllThreat();
Debug.Log("[Debug] 보스 위협 초기화");
}
// ── 상태 조회 ──
[MenuItem("Tools/Colosseum/Debug/Boss/Log Status")]
private static void LogStatus()
{
BossEnemy boss = FindBoss();
if (boss == null) return;
StringBuilder sb = new StringBuilder();
sb.AppendLine($"[Debug] 보스 상태 | {boss.name}");
sb.AppendLine($" HP: {boss.CurrentHealth:F0} / {boss.MaxHealth:F0} ({(boss.MaxHealth > 0 ? boss.CurrentHealth / boss.MaxHealth * 100f : 0f):F1}%)");
sb.AppendLine($" Shield: {boss.Shield:F0}");
sb.AppendLine($" IsDead: {boss.IsDead}");
BossBehaviorRuntimeState context = boss.GetComponent<BossBehaviorRuntimeState>();
if (context != null)
sb.AppendLine($" Phase: {context.CurrentPatternPhase} / {context.MaxPatternPhase}");
Debug.Log(sb.ToString());
}
// ── 내부 구현 ──
/// <summary>
/// 활성 보스 찾기
/// </summary>
private static BossEnemy FindBoss()
{
if (!EditorApplication.isPlaying)
{
Debug.LogWarning("[Debug] 플레이 모드에서만 사용할 수 있습니다.");
return null;
}
BossEnemy boss = BossEnemy.ActiveBoss != null
? BossEnemy.ActiveBoss
: Object.FindFirstObjectByType<BossEnemy>();
if (boss == null)
Debug.LogWarning("[Debug] 활성 보스를 찾지 못했습니다.");
return boss;
}
/// <summary>
/// 보스 HP를 퍼센트로 설정
/// </summary>
private static void SetBossHP(float percent)
{
BossEnemy boss = FindBoss();
if (boss == null) return;
float targetHP = boss.MaxHealth * percent;
float diff = boss.CurrentHealth - targetHP;
if (diff > 0f)
boss.TakeDamage(diff);
else if (diff < 0f)
boss.Heal(-diff);
Debug.Log($"[Debug] 보스 HP 설정 {percent * 100f:F0}% | HP={boss.CurrentHealth:F0}/{boss.MaxHealth:F0}");
}
/// <summary>
/// 페이즈 강제 전환
/// </summary>
private static void ForcePhase(int index)
{
BossEnemy boss = FindBoss();
if (boss == null) return;
BossBehaviorRuntimeState context = boss.GetComponent<BossBehaviorRuntimeState>();
if (context == null) return;
int targetPhase = Mathf.Clamp(index + 1, 1, context.MaxPatternPhase);
context.SetCurrentPatternPhase(targetPhase);
Debug.Log($"[Debug] 보스 페이즈 강제 전환 | Phase={targetPhase}");
}
/// <summary>
/// 보호막 적용
/// </summary>
private static void ApplyShield(float amount)
{
BossEnemy boss = FindBoss();
if (boss == null) return;
boss.ApplyShield(amount, 30f);
Debug.Log($"[Debug] 보스 보호막 적용 | Amount={amount:F0} | Total={boss.Shield:F0}");
}
}
}