Files
Colosseum/Assets/_Game/Scripts/Skills/Effects/ShieldEffect.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.6 KiB
C#
Raw 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 UnityEngine;
using Colosseum.Abnormalities;
using Colosseum.Enemy;
using Colosseum.Player;
using Colosseum.Stats;
using Colosseum.Combat;
using Colosseum.Passives;
using Colosseum.Skills;
namespace Colosseum.Skills.Effects
{
/// <summary>
/// 보호막 효과입니다.
/// 대상에게 일정 시간 동안 피해를 흡수하는 보호막을 부여하며,
/// 보호막 수치에 비례하여 적에게 위협을 생성할 수 있습니다.
/// </summary>
[CreateAssetMenu(fileName = "ShieldEffect", menuName = "Colosseum/Skills/Effects/Shield")]
public class ShieldEffect : SkillEffect
{
[Header("Shield")]
[Tooltip("기본 보호막 수치")]
[Min(0f)] [SerializeField] private float baseShield = 100f;
[Tooltip("회복력 계수")]
[Min(0f)] [SerializeField] private float shieldScaling = 0.5f;
[Tooltip("보호막 지속 시간")]
[Min(0f)] [SerializeField] private float duration = 5f;
[Header("Abnormality")]
[Tooltip("보호막 활성 여부를 나타내는 이상상태 데이터")]
[SerializeField] private AbnormalityData shieldStateAbnormality;
[Header("Threat")]
[Tooltip("보호막 사용 시 항상 생성할 고정 위협 수치")]
[Min(0f)] [SerializeField] private float flatThreatAmount = 5f;
[Tooltip("실제 보호막 수치에 대한 위협 비율 (1.0 = 100%)")]
[Range(0f, 10f)] [SerializeField] private float threatPercentOfShield = 0.5f;
[Tooltip("위협을 생성할 반경 (시전자 기준)")]
[Min(0f)] [SerializeField] private float threatRadius = 50f;
protected override void ApplyEffect(GameObject caster, GameObject target)
{
if (target == null)
return;
float totalShield = CalculateShield(caster);
float actualShield = 0f;
PlayerNetworkController playerNetworkController = target.GetComponent<PlayerNetworkController>();
if (playerNetworkController != null)
{
actualShield = playerNetworkController.ApplyShield(totalShield, duration, shieldStateAbnormality, caster);
CombatBalanceTracker.RecordShield(caster, target, actualShield);
}
else
{
EnemyBase enemyBase = target.GetComponent<EnemyBase>();
if (enemyBase != null)
{
actualShield = enemyBase.ApplyShield(totalShield, duration, shieldStateAbnormality, caster);
CombatBalanceTracker.RecordShield(caster, target, actualShield);
}
}
// 위협 생성: 고정 수치 + (실제 보호막량 × 비율)
float threat = flatThreatAmount + (actualShield * threatPercentOfShield);
if (threat > 0f)
{
ThreatUtility.GenerateThreatOnNearbyEnemies(caster, threat, threatRadius);
}
}
private float CalculateShield(GameObject caster)
{
CharacterStats stats = caster != null ? caster.GetComponent<CharacterStats>() : null;
if (stats == null)
{
return baseShield *
SkillRuntimeModifierUtility.GetShieldMultiplier(caster) *
PassiveRuntimeModifierUtility.GetShieldDoneMultiplier(caster);
}
float resolvedShield = baseShield + (stats.HealPower * shieldScaling);
return resolvedShield *
SkillRuntimeModifierUtility.GetShieldMultiplier(caster) *
PassiveRuntimeModifierUtility.GetShieldDoneMultiplier(caster);
}
}
}