- ThreatUtility: 공통 위협 생성 유틸리티 (OverlapSphere 기반 반경 내 적 탐색) - OverlapSphereNonAlloc 버퍼 32→256 확장으로 씬 콜라이더 누락 수정 - 위협 배율 체인: SkillGem × ThreatController × Passive - HealEffect: flatThreat + (actualHeal × threatPercent) 공식 적용 - ShieldEffect: flatThreat + (actualShield × threatPercent) 공식 적용 - AbnormalityEffect: flatThreat 고정 위협 생성 - EditMode 유닛 테스트 9/9 통과 (SupportThreatTests) - 테스트 씬 UI 레이아웃 수정 사항 포함
92 lines
3.4 KiB
C#
92 lines
3.4 KiB
C#
using System.Collections.Generic;
|
||
|
||
using UnityEngine;
|
||
|
||
using Colosseum.Enemy;
|
||
using Colosseum.Passives;
|
||
using Colosseum.Skills;
|
||
|
||
namespace Colosseum.Combat
|
||
{
|
||
/// <summary>
|
||
/// 힐, 보호막, 버프 등 비공격 스킬에서 위협을 생성할 때 사용하는 공통 유틸리티입니다.
|
||
/// 시전자 주변의 적을 탐색하고, 위협 배율 체인을 적용하여 위협을 누적합니다.
|
||
/// </summary>
|
||
public static class ThreatUtility
|
||
{
|
||
private const float DefaultThreatRadius = 50f;
|
||
|
||
private static readonly Collider[] overlapBuffer = new Collider[256];
|
||
private static readonly HashSet<EnemyBase> processedEnemies = new HashSet<EnemyBase>();
|
||
|
||
/// <summary>
|
||
/// 시전자 주변의 적들에게 위협을 분배합니다.
|
||
/// 위협 공식: baseThreat × (Gem 배율 × ThreatController 배율 × Passive 배율)
|
||
/// </summary>
|
||
/// <param name="caster">시전자 (위협을 얻을 주체)</param>
|
||
/// <param name="baseThreat">배율 적용 전 기본 위협 수치</param>
|
||
/// <param name="radius">위협을 생성할 반경 (기본 50m)</param>
|
||
public static void GenerateThreatOnNearbyEnemies(GameObject caster, float baseThreat, float radius = DefaultThreatRadius)
|
||
{
|
||
if (caster == null || baseThreat <= 0f)
|
||
return;
|
||
|
||
float multiplier = ResolveThreatMultiplier(caster);
|
||
float finalThreat = baseThreat * multiplier;
|
||
|
||
processedEnemies.Clear();
|
||
int hitCount = Physics.OverlapSphereNonAlloc(
|
||
caster.transform.position,
|
||
Mathf.Max(radius, 1f),
|
||
overlapBuffer);
|
||
|
||
for (int i = 0; i < hitCount; i++)
|
||
{
|
||
Collider hit = overlapBuffer[i];
|
||
if (hit == null)
|
||
continue;
|
||
|
||
EnemyBase enemy = hit.GetComponentInParent<EnemyBase>();
|
||
if (enemy == null || processedEnemies.Contains(enemy))
|
||
continue;
|
||
|
||
if (!enemy.UseThreatSystem || enemy.IsDead)
|
||
continue;
|
||
|
||
// 같은 팀(적이 아님)은 제외
|
||
if (Colosseum.Team.IsSameTeam(caster, enemy.gameObject))
|
||
continue;
|
||
|
||
processedEnemies.Add(enemy);
|
||
enemy.AddThreat(caster, finalThreat);
|
||
}
|
||
|
||
if (processedEnemies.Count > 0)
|
||
{
|
||
CombatBalanceTracker.RecordThreat(caster, finalThreat);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 시전자의 전체 위협 배율을 계산합니다.
|
||
/// 체인: SkillGem 배율 × ThreatController 배율 × Passive 배율
|
||
/// </summary>
|
||
public static float ResolveThreatMultiplier(GameObject source)
|
||
{
|
||
if (source == null)
|
||
return 1f;
|
||
|
||
float gemMultiplier = SkillRuntimeModifierUtility.GetThreatMultiplier(source);
|
||
|
||
ThreatController threatController = source.GetComponent<ThreatController>();
|
||
float runtimeMultiplier = threatController != null
|
||
? Mathf.Max(0f, threatController.CurrentThreatMultiplier)
|
||
: 1f;
|
||
|
||
float passiveMultiplier = PassiveRuntimeModifierUtility.GetThreatGeneratedMultiplier(source);
|
||
|
||
return gemMultiplier * runtimeMultiplier * passiveMultiplier;
|
||
}
|
||
}
|
||
}
|