Files
Colosseum/Assets/_Game/Scripts/Combat/ThreatUtility.cs
dal4segno a5e9b78098 feat: 서포트 스킬(힐/보호막/버프) 위협 생성 시스템 추가
- ThreatUtility: 공통 위협 생성 유틸리티 (OverlapSphere 기반 반경 내 적 탐색)
  - OverlapSphereNonAlloc 버퍼 32→256 확장으로 씬 콜라이더 누락 수정
  - 위협 배율 체인: SkillGem × ThreatController × Passive
- HealEffect: flatThreat + (actualHeal × threatPercent) 공식 적용
- ShieldEffect: flatThreat + (actualShield × threatPercent) 공식 적용
- AbnormalityEffect: flatThreat 고정 위협 생성
- EditMode 유닛 테스트 9/9 통과 (SupportThreatTests)
- 테스트 씬 UI 레이아웃 수정 사항 포함
2026-03-28 17:05:57 +09:00

92 lines
3.4 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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;
}
}
}