feat: 드로그 BT 및 전투 패턴 재구성

- 드로그 BT를 페이즈 전환, 부활 트리거, 가중치 근접 패턴 중심으로 재구성

- 땅 울리기 및 콤보-기본기1_3 패턴/스킬/이펙트를 추가하고 기존 평타 파생 자산을 정리

- 드로그 행동 검증용 PlayMode/Editor 테스트와 관련 런타임 상태 추적을 추가
This commit is contained in:
2026-04-09 23:21:38 +09:00
parent 1307123029
commit 7776f7ed05
77 changed files with 449522 additions and 345357 deletions

View File

@@ -0,0 +1,115 @@
using System;
using System.Collections.Generic;
using UnityEngine;
using Colosseum.AI.BehaviorActions.Conditions;
namespace Colosseum.AI
{
/// <summary>
/// 가중치 기반 패턴 선택 후보를 표현합니다.
/// </summary>
public readonly struct WeightedPatternCandidate
{
public WeightedPatternCandidate(BossPatternData pattern, float weight)
{
Pattern = pattern;
Weight = weight;
}
public BossPatternData Pattern { get; }
public float Weight { get; }
}
/// <summary>
/// 준비된 패턴 후보 중 하나를 가중치 기반으로 선택합니다.
/// </summary>
public static class WeightedPatternSelector
{
public static bool HasAnyReadyPattern(GameObject owner, IReadOnlyList<WeightedPatternCandidate> candidates)
{
if (owner == null || candidates == null)
return false;
for (int i = 0; i < candidates.Count; i++)
{
WeightedPatternCandidate candidate = candidates[i];
if (candidate.Pattern == null || candidate.Weight <= 0f)
continue;
if (PatternReadyHelper.IsPatternReady(owner, candidate.Pattern))
return true;
}
return false;
}
public static bool TrySelectReadyPattern(GameObject owner, IReadOnlyList<WeightedPatternCandidate> candidates, out BossPatternData selectedPattern)
{
if (owner == null)
{
selectedPattern = null;
return false;
}
return TrySelectPattern(
candidates,
pattern => PatternReadyHelper.IsPatternReady(owner, pattern),
UnityEngine.Random.value,
out selectedPattern);
}
public static bool TrySelectPattern(
IReadOnlyList<WeightedPatternCandidate> candidates,
Predicate<BossPatternData> isPatternReady,
float normalizedRoll,
out BossPatternData selectedPattern)
{
selectedPattern = null;
if (candidates == null || isPatternReady == null)
return false;
List<WeightedPatternCandidate> readyCandidates = new List<WeightedPatternCandidate>(candidates.Count);
float totalWeight = 0f;
for (int i = 0; i < candidates.Count; i++)
{
WeightedPatternCandidate candidate = candidates[i];
if (candidate.Pattern == null || candidate.Weight <= 0f)
continue;
if (!isPatternReady(candidate.Pattern))
continue;
readyCandidates.Add(candidate);
totalWeight += candidate.Weight;
}
if (readyCandidates.Count == 0 || totalWeight <= 0f)
return false;
float clampedRoll = Mathf.Clamp01(normalizedRoll);
float targetWeight = clampedRoll >= 1f
? totalWeight
: totalWeight * clampedRoll;
float cumulativeWeight = 0f;
for (int i = 0; i < readyCandidates.Count; i++)
{
WeightedPatternCandidate candidate = readyCandidates[i];
cumulativeWeight += candidate.Weight;
if (targetWeight < cumulativeWeight || i == readyCandidates.Count - 1)
{
selectedPattern = candidate.Pattern;
return true;
}
}
return false;
}
}
}