feat: 드로그 BT 및 전투 패턴 재구성
- 드로그 BT를 페이즈 전환, 부활 트리거, 가중치 근접 패턴 중심으로 재구성 - 땅 울리기 및 콤보-기본기1_3 패턴/스킬/이펙트를 추가하고 기존 평타 파생 자산을 정리 - 드로그 행동 검증용 PlayMode/Editor 테스트와 관련 런타임 상태 추적을 추가
This commit is contained in:
@@ -2,6 +2,7 @@ using System.Collections.Generic;
|
||||
|
||||
using Colosseum.AI;
|
||||
using Colosseum.Abnormalities;
|
||||
using Colosseum.Player;
|
||||
using Colosseum.Skills;
|
||||
|
||||
using Unity.Behavior;
|
||||
@@ -32,6 +33,8 @@ namespace Colosseum.Enemy
|
||||
[RequireComponent(typeof(SkillController))]
|
||||
public class BossBehaviorRuntimeState : NetworkBehaviour
|
||||
{
|
||||
public static event System.Action<GameObject, GameObject> PlayerRevivedBySkill;
|
||||
|
||||
[Header("References")]
|
||||
[SerializeField] protected BossEnemy bossEnemy;
|
||||
[SerializeField] protected EnemyBase enemyBase;
|
||||
@@ -92,6 +95,9 @@ namespace Colosseum.Enemy
|
||||
protected float currentPhaseStartTime;
|
||||
protected BossPatternExecutionResult lastPatternExecutionResult;
|
||||
protected BossPatternData lastExecutedPattern;
|
||||
protected GameObject lastReviveCaster;
|
||||
protected GameObject lastRevivedTarget;
|
||||
protected float lastReviveEventTime = float.NegativeInfinity;
|
||||
|
||||
/// <summary>
|
||||
/// 현재 전투 대상
|
||||
@@ -202,7 +208,12 @@ namespace Colosseum.Enemy
|
||||
ResetPhaseState();
|
||||
|
||||
if (!IsServer)
|
||||
{
|
||||
enabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
PlayerRevivedBySkill += HandlePlayerRevivedBySkill;
|
||||
}
|
||||
|
||||
protected virtual void Update()
|
||||
@@ -267,6 +278,41 @@ namespace Colosseum.Enemy
|
||||
lastPatternExecutionResult = result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 부활 스킬 사용 사실을 보스 AI에 알립니다.
|
||||
/// </summary>
|
||||
public static void ReportPlayerRevivedBySkill(GameObject caster, GameObject revivedTarget)
|
||||
{
|
||||
PlayerRevivedBySkill?.Invoke(caster, revivedTarget);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 최근 부활 트리거가 아직 유효한지 확인합니다.
|
||||
/// </summary>
|
||||
public bool HasRecentReviveTrigger(float maxAge)
|
||||
{
|
||||
return ResolveRecentReviveTriggerTarget(maxAge) != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 최근 부활 트리거에서 우선 공격할 대상을 반환합니다.
|
||||
/// </summary>
|
||||
public GameObject ResolveRecentReviveTriggerTarget(float maxAge, bool preferCaster = true, bool fallbackToRevivedTarget = true)
|
||||
{
|
||||
if (Time.time - lastReviveEventTime > Mathf.Max(0f, maxAge))
|
||||
return null;
|
||||
|
||||
GameObject preferredTarget = preferCaster ? lastReviveCaster : lastRevivedTarget;
|
||||
if (IsValidReviveTriggerTarget(preferredTarget))
|
||||
return preferredTarget;
|
||||
|
||||
if (!fallbackToRevivedTarget)
|
||||
return null;
|
||||
|
||||
GameObject fallbackTarget = preferCaster ? lastRevivedTarget : lastReviveCaster;
|
||||
return IsValidReviveTriggerTarget(fallbackTarget) ? fallbackTarget : null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 페이즈 커스텀 조건을 기록합니다.
|
||||
/// </summary>
|
||||
@@ -391,14 +437,41 @@ namespace Colosseum.Enemy
|
||||
currentPhaseStartTime = Time.time;
|
||||
lastPatternExecutionResult = BossPatternExecutionResult.None;
|
||||
lastExecutedPattern = null;
|
||||
lastReviveCaster = null;
|
||||
lastRevivedTarget = null;
|
||||
lastReviveEventTime = float.NegativeInfinity;
|
||||
customPhaseConditions.Clear();
|
||||
}
|
||||
|
||||
public override void OnNetworkDespawn()
|
||||
{
|
||||
if (IsServer)
|
||||
PlayerRevivedBySkill -= HandlePlayerRevivedBySkill;
|
||||
|
||||
base.OnNetworkDespawn();
|
||||
}
|
||||
|
||||
protected void HandlePlayerRevivedBySkill(GameObject caster, GameObject revivedTarget)
|
||||
{
|
||||
if (!IsServer)
|
||||
return;
|
||||
|
||||
lastReviveCaster = caster;
|
||||
lastRevivedTarget = revivedTarget;
|
||||
lastReviveEventTime = Time.time;
|
||||
|
||||
LogDebug(nameof(BossBehaviorRuntimeState), $"부활 트리거 기록: 시전자={caster?.name ?? "없음"} / 대상={revivedTarget?.name ?? "없음"}");
|
||||
}
|
||||
|
||||
protected bool IsValidReviveTriggerTarget(GameObject candidate)
|
||||
{
|
||||
if (candidate == null || !candidate.activeInHierarchy)
|
||||
return false;
|
||||
|
||||
PlayerNetworkController player = candidate.GetComponent<PlayerNetworkController>();
|
||||
return player != null && !player.IsDead;
|
||||
}
|
||||
|
||||
private static bool HasAnimatorParameter(Animator animator, string parameterName, AnimatorControllerParameterType parameterType)
|
||||
{
|
||||
if (animator == null || string.IsNullOrEmpty(parameterName))
|
||||
|
||||
Reference in New Issue
Block a user