feat: 보호막 타입 분리 및 드로그 시그니처 전조 정리
- 보호막을 단일 수치에서 타입별 독립 인스턴스 구조로 리팩터링하고 같은 타입만 갱신되도록 정리 - 플레이어/보스 보호막 상태를 이상상태와 연동해 HUD 및 보스 UI에서 타입별로 식별 가능하게 보강 - 드로그 집행 개시 전조를 집행 준비 이상상태 기반으로 재구성하고 관련 데이터와 보스 컨텍스트를 정리 - 전투 밸런스 계측기와 디버그 메뉴를 추가해 피해, 치유, 보호막, 위협, 패턴 사용량 측정 경로를 마련 - 테스트용 보호막 A/B와 시그니처 전조 자산을 추가하고 기본 포트 7777 원복 후 빌드 및 런타임 검증을 완료
This commit is contained in:
332
Assets/_Game/Scripts/Combat/CombatBalanceTracker.cs
Normal file
332
Assets/_Game/Scripts/Combat/CombatBalanceTracker.cs
Normal file
@@ -0,0 +1,332 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
using Unity.Netcode;
|
||||
using UnityEngine;
|
||||
|
||||
using Colosseum.Enemy;
|
||||
using Colosseum.Skills;
|
||||
|
||||
namespace Colosseum.Combat
|
||||
{
|
||||
/// <summary>
|
||||
/// 전투 밸런싱 검증을 위한 런타임 계측기입니다.
|
||||
/// 플레이어/보스 기준으로 대미지, 치유, 보호막, 위협, 패턴 사용량을 누적합니다.
|
||||
/// </summary>
|
||||
public static class CombatBalanceTracker
|
||||
{
|
||||
private sealed class ActorMetrics
|
||||
{
|
||||
public string label;
|
||||
public float totalDamageDealt;
|
||||
public float bossDamageDealt;
|
||||
public float damageTaken;
|
||||
public float healDone;
|
||||
public float healReceived;
|
||||
public float shieldApplied;
|
||||
public float shieldReceived;
|
||||
public float threatGenerated;
|
||||
public readonly Dictionary<string, float> damageBySkill = new Dictionary<string, float>();
|
||||
public readonly Dictionary<string, float> healBySkill = new Dictionary<string, float>();
|
||||
public readonly Dictionary<string, float> shieldBySkill = new Dictionary<string, float>();
|
||||
public readonly Dictionary<string, float> threatBySkill = new Dictionary<string, float>();
|
||||
}
|
||||
|
||||
private static readonly Dictionary<string, ActorMetrics> actorMetrics = new Dictionary<string, ActorMetrics>();
|
||||
private static readonly Dictionary<string, int> bossPatternCounts = new Dictionary<string, int>();
|
||||
private static readonly Dictionary<string, int> bossEventCounts = new Dictionary<string, int>();
|
||||
private static float combatStartTime = -1f;
|
||||
private static float lastEventTime = -1f;
|
||||
|
||||
/// <summary>
|
||||
/// 누적된 전투 계측 데이터를 초기화합니다.
|
||||
/// </summary>
|
||||
public static void Reset()
|
||||
{
|
||||
actorMetrics.Clear();
|
||||
bossPatternCounts.Clear();
|
||||
bossEventCounts.Clear();
|
||||
combatStartTime = -1f;
|
||||
lastEventTime = -1f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 실제 적용된 대미지를 기록합니다.
|
||||
/// </summary>
|
||||
public static void RecordDamage(GameObject source, GameObject target, float actualDamage)
|
||||
{
|
||||
if (actualDamage <= 0f || target == null)
|
||||
return;
|
||||
|
||||
MarkCombatEvent();
|
||||
|
||||
ActorMetrics targetMetrics = GetMetrics(target);
|
||||
targetMetrics.damageTaken += actualDamage;
|
||||
|
||||
if (source == null)
|
||||
return;
|
||||
|
||||
ActorMetrics sourceMetrics = GetMetrics(source);
|
||||
sourceMetrics.totalDamageDealt += actualDamage;
|
||||
|
||||
if (target.GetComponent<BossEnemy>() != null || target.GetComponentInParent<BossEnemy>() != null)
|
||||
{
|
||||
sourceMetrics.bossDamageDealt += actualDamage;
|
||||
}
|
||||
|
||||
AddSkillValue(sourceMetrics.damageBySkill, ResolveSkillLabel(source), actualDamage);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 실제 적용된 회복량을 기록합니다.
|
||||
/// </summary>
|
||||
public static void RecordHeal(GameObject source, GameObject target, float actualHeal)
|
||||
{
|
||||
if (actualHeal <= 0f || target == null)
|
||||
return;
|
||||
|
||||
MarkCombatEvent();
|
||||
|
||||
ActorMetrics targetMetrics = GetMetrics(target);
|
||||
targetMetrics.healReceived += actualHeal;
|
||||
|
||||
if (source == null)
|
||||
return;
|
||||
|
||||
ActorMetrics sourceMetrics = GetMetrics(source);
|
||||
sourceMetrics.healDone += actualHeal;
|
||||
AddSkillValue(sourceMetrics.healBySkill, ResolveSkillLabel(source), actualHeal);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 실제 적용된 보호막 수치를 기록합니다.
|
||||
/// </summary>
|
||||
public static void RecordShield(GameObject source, GameObject target, float actualShield)
|
||||
{
|
||||
if (actualShield <= 0f || target == null)
|
||||
return;
|
||||
|
||||
MarkCombatEvent();
|
||||
|
||||
ActorMetrics targetMetrics = GetMetrics(target);
|
||||
targetMetrics.shieldReceived += actualShield;
|
||||
|
||||
if (source == null)
|
||||
return;
|
||||
|
||||
ActorMetrics sourceMetrics = GetMetrics(source);
|
||||
sourceMetrics.shieldApplied += actualShield;
|
||||
AddSkillValue(sourceMetrics.shieldBySkill, ResolveSkillLabel(source), actualShield);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 실제 적용된 위협 증가량을 기록합니다.
|
||||
/// </summary>
|
||||
public static void RecordThreat(GameObject source, float threatAmount)
|
||||
{
|
||||
if (threatAmount <= 0f || source == null)
|
||||
return;
|
||||
|
||||
MarkCombatEvent();
|
||||
|
||||
ActorMetrics sourceMetrics = GetMetrics(source);
|
||||
sourceMetrics.threatGenerated += threatAmount;
|
||||
AddSkillValue(sourceMetrics.threatBySkill, ResolveSkillLabel(source), threatAmount);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 보스 패턴 사용 횟수를 기록합니다.
|
||||
/// </summary>
|
||||
public static void RecordBossPattern(string patternName)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(patternName))
|
||||
return;
|
||||
|
||||
MarkCombatEvent();
|
||||
AddCount(bossPatternCounts, patternName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 시그니처 성공/실패 같은 보스 전투 이벤트를 기록합니다.
|
||||
/// </summary>
|
||||
public static void RecordBossEvent(string eventName)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(eventName))
|
||||
return;
|
||||
|
||||
MarkCombatEvent();
|
||||
AddCount(bossEventCounts, eventName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 현재 누적 계측 데이터를 보기 좋은 문자열로 반환합니다.
|
||||
/// </summary>
|
||||
public static string BuildSummary()
|
||||
{
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.Append("[Balance] 전투 요약");
|
||||
|
||||
if (combatStartTime >= 0f && lastEventTime >= combatStartTime)
|
||||
{
|
||||
builder.Append(" | Duration=");
|
||||
builder.Append((lastEventTime - combatStartTime).ToString("0.00"));
|
||||
builder.Append("s");
|
||||
}
|
||||
|
||||
if (bossPatternCounts.Count > 0)
|
||||
{
|
||||
builder.AppendLine();
|
||||
builder.Append("보스 패턴: ");
|
||||
AppendCountSummary(builder, bossPatternCounts);
|
||||
}
|
||||
|
||||
if (bossEventCounts.Count > 0)
|
||||
{
|
||||
builder.AppendLine();
|
||||
builder.Append("보스 이벤트: ");
|
||||
AppendCountSummary(builder, bossEventCounts);
|
||||
}
|
||||
|
||||
foreach (KeyValuePair<string, ActorMetrics> pair in actorMetrics)
|
||||
{
|
||||
ActorMetrics metrics = pair.Value;
|
||||
builder.AppendLine();
|
||||
builder.Append("- ");
|
||||
builder.Append(metrics.label);
|
||||
builder.Append(" | BossDmg=");
|
||||
builder.Append(metrics.bossDamageDealt.ToString("0.##"));
|
||||
builder.Append(" | TotalDmg=");
|
||||
builder.Append(metrics.totalDamageDealt.ToString("0.##"));
|
||||
builder.Append(" | Taken=");
|
||||
builder.Append(metrics.damageTaken.ToString("0.##"));
|
||||
builder.Append(" | Heal=");
|
||||
builder.Append(metrics.healDone.ToString("0.##"));
|
||||
builder.Append(" | HealRecv=");
|
||||
builder.Append(metrics.healReceived.ToString("0.##"));
|
||||
builder.Append(" | Shield=");
|
||||
builder.Append(metrics.shieldApplied.ToString("0.##"));
|
||||
builder.Append(" | ShieldRecv=");
|
||||
builder.Append(metrics.shieldReceived.ToString("0.##"));
|
||||
builder.Append(" | Threat=");
|
||||
builder.Append(metrics.threatGenerated.ToString("0.##"));
|
||||
|
||||
AppendSkillBreakdown(builder, "DmgBySkill", metrics.damageBySkill);
|
||||
AppendSkillBreakdown(builder, "HealBySkill", metrics.healBySkill);
|
||||
AppendSkillBreakdown(builder, "ShieldBySkill", metrics.shieldBySkill);
|
||||
AppendSkillBreakdown(builder, "ThreatBySkill", metrics.threatBySkill);
|
||||
}
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
private static void MarkCombatEvent()
|
||||
{
|
||||
if (!Application.isPlaying)
|
||||
return;
|
||||
|
||||
float now = Time.time;
|
||||
if (combatStartTime < 0f)
|
||||
combatStartTime = now;
|
||||
|
||||
lastEventTime = now;
|
||||
}
|
||||
|
||||
private static ActorMetrics GetMetrics(GameObject actor)
|
||||
{
|
||||
string actorLabel = ResolveActorLabel(actor);
|
||||
if (!actorMetrics.TryGetValue(actorLabel, out ActorMetrics metrics))
|
||||
{
|
||||
metrics = new ActorMetrics
|
||||
{
|
||||
label = actorLabel,
|
||||
};
|
||||
|
||||
actorMetrics.Add(actorLabel, metrics);
|
||||
}
|
||||
|
||||
return metrics;
|
||||
}
|
||||
|
||||
private static string ResolveActorLabel(GameObject actor)
|
||||
{
|
||||
if (actor == null)
|
||||
return "Unknown";
|
||||
|
||||
NetworkObject networkObject = actor.GetComponent<NetworkObject>() ?? actor.GetComponentInParent<NetworkObject>();
|
||||
if (networkObject != null)
|
||||
{
|
||||
string roleLabel = actor.GetComponent<BossEnemy>() != null || actor.GetComponentInParent<BossEnemy>() != null
|
||||
? "Boss"
|
||||
: "Actor";
|
||||
|
||||
return $"{roleLabel}:{actor.name}(Owner={networkObject.OwnerClientId})";
|
||||
}
|
||||
|
||||
return actor.name;
|
||||
}
|
||||
|
||||
private static string ResolveSkillLabel(GameObject source)
|
||||
{
|
||||
if (source == null)
|
||||
return "Unknown";
|
||||
|
||||
SkillController skillController = source.GetComponent<SkillController>() ?? source.GetComponentInParent<SkillController>();
|
||||
if (skillController != null && skillController.CurrentSkill != null)
|
||||
return skillController.CurrentSkill.SkillName;
|
||||
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
private static void AddSkillValue(Dictionary<string, float> dictionary, string key, float value)
|
||||
{
|
||||
if (dictionary == null || string.IsNullOrWhiteSpace(key) || value <= 0f)
|
||||
return;
|
||||
|
||||
dictionary.TryGetValue(key, out float currentValue);
|
||||
dictionary[key] = currentValue + value;
|
||||
}
|
||||
|
||||
private static void AddCount(Dictionary<string, int> dictionary, string key)
|
||||
{
|
||||
dictionary.TryGetValue(key, out int currentValue);
|
||||
dictionary[key] = currentValue + 1;
|
||||
}
|
||||
|
||||
private static void AppendCountSummary(StringBuilder builder, Dictionary<string, int> dictionary)
|
||||
{
|
||||
bool first = true;
|
||||
foreach (KeyValuePair<string, int> pair in dictionary)
|
||||
{
|
||||
if (!first)
|
||||
builder.Append(" | ");
|
||||
|
||||
builder.Append(pair.Key);
|
||||
builder.Append('=');
|
||||
builder.Append(pair.Value);
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
|
||||
private static void AppendSkillBreakdown(StringBuilder builder, string label, Dictionary<string, float> dictionary)
|
||||
{
|
||||
if (dictionary == null || dictionary.Count == 0)
|
||||
return;
|
||||
|
||||
builder.Append(" | ");
|
||||
builder.Append(label);
|
||||
builder.Append('=');
|
||||
|
||||
bool first = true;
|
||||
foreach (KeyValuePair<string, float> pair in dictionary)
|
||||
{
|
||||
if (!first)
|
||||
builder.Append(", ");
|
||||
|
||||
builder.Append(pair.Key);
|
||||
builder.Append(':');
|
||||
builder.Append(pair.Value.ToString("0.##"));
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/_Game/Scripts/Combat/CombatBalanceTracker.cs.meta
Normal file
2
Assets/_Game/Scripts/Combat/CombatBalanceTracker.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f9168649dde02654baf3b890d70d8b74
|
||||
271
Assets/_Game/Scripts/Combat/ShieldAbnormalityUtility.cs
Normal file
271
Assets/_Game/Scripts/Combat/ShieldAbnormalityUtility.cs
Normal file
@@ -0,0 +1,271 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
using UnityEngine;
|
||||
|
||||
using Colosseum.Abnormalities;
|
||||
|
||||
namespace Colosseum.Combat
|
||||
{
|
||||
/// <summary>
|
||||
/// 개별 보호막 인스턴스입니다.
|
||||
/// 같은 종류의 보호막은 하나로 합쳐지고, 다른 종류의 보호막은 독립적으로 유지됩니다.
|
||||
/// </summary>
|
||||
[System.Serializable]
|
||||
public sealed class ActiveShield
|
||||
{
|
||||
[SerializeField] private AbnormalityData shieldType;
|
||||
[SerializeField] private float remainingAmount;
|
||||
[SerializeField] private float remainingDuration;
|
||||
[SerializeField] private bool isPermanent;
|
||||
[SerializeField] private GameObject source;
|
||||
|
||||
public AbnormalityData ShieldType => shieldType;
|
||||
public float RemainingAmount => remainingAmount;
|
||||
public float RemainingDuration => isPermanent ? 0f : remainingDuration;
|
||||
public bool IsPermanent => isPermanent;
|
||||
public bool IsExpired => !isPermanent && remainingDuration <= 0f;
|
||||
public bool IsDepleted => remainingAmount <= 0f;
|
||||
public GameObject Source => source;
|
||||
|
||||
public ActiveShield(AbnormalityData shieldType, float amount, float duration, GameObject source)
|
||||
{
|
||||
this.shieldType = shieldType;
|
||||
remainingAmount = Mathf.Max(0f, amount);
|
||||
isPermanent = duration <= 0f;
|
||||
remainingDuration = isPermanent ? 0f : duration;
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 같은 종류 보호막이 다시 적용되었을 때 양을 더하고 지속시간을 갱신합니다.
|
||||
/// </summary>
|
||||
public float Add(float amount, float duration, GameObject source)
|
||||
{
|
||||
float appliedAmount = Mathf.Max(0f, amount);
|
||||
remainingAmount += appliedAmount;
|
||||
|
||||
if (duration <= 0f)
|
||||
{
|
||||
isPermanent = true;
|
||||
remainingDuration = 0f;
|
||||
}
|
||||
else if (!isPermanent)
|
||||
{
|
||||
remainingDuration = duration;
|
||||
}
|
||||
|
||||
if (source != null)
|
||||
{
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
return appliedAmount;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 지속시간을 감소시킵니다.
|
||||
/// </summary>
|
||||
public bool Tick(float deltaTime)
|
||||
{
|
||||
if (isPermanent || deltaTime <= 0f)
|
||||
return false;
|
||||
|
||||
remainingDuration = Mathf.Max(0f, remainingDuration - deltaTime);
|
||||
return IsExpired;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 들어오는 피해를 흡수합니다.
|
||||
/// </summary>
|
||||
public float Consume(float incomingDamage)
|
||||
{
|
||||
if (incomingDamage <= 0f || remainingAmount <= 0f)
|
||||
return 0f;
|
||||
|
||||
float absorbed = Mathf.Min(remainingAmount, incomingDamage);
|
||||
remainingAmount = Mathf.Max(0f, remainingAmount - absorbed);
|
||||
return absorbed;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 대상이 가진 보호막 인스턴스를 관리합니다.
|
||||
/// </summary>
|
||||
[System.Serializable]
|
||||
public sealed class ShieldCollection
|
||||
{
|
||||
private readonly List<ActiveShield> activeShields = new List<ActiveShield>();
|
||||
|
||||
public IReadOnlyList<ActiveShield> ActiveShields => activeShields;
|
||||
|
||||
public float TotalAmount
|
||||
{
|
||||
get
|
||||
{
|
||||
float total = 0f;
|
||||
for (int i = 0; i < activeShields.Count; i++)
|
||||
{
|
||||
total += activeShields[i].RemainingAmount;
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 보호막을 적용합니다. 같은 종류는 자기 자신만 합산 및 갱신합니다.
|
||||
/// </summary>
|
||||
public float ApplyShield(AbnormalityData shieldType, float amount, float duration, GameObject source)
|
||||
{
|
||||
if (amount <= 0f)
|
||||
return 0f;
|
||||
|
||||
ActiveShield existingShield = FindShield(shieldType);
|
||||
if (existingShield != null)
|
||||
{
|
||||
return existingShield.Add(amount, duration, source);
|
||||
}
|
||||
|
||||
activeShields.Add(new ActiveShield(shieldType, amount, duration, source));
|
||||
return amount;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 보호막이 적용된 순서대로 피해를 흡수하고 남은 피해를 반환합니다.
|
||||
/// </summary>
|
||||
public float ConsumeDamage(float incomingDamage)
|
||||
{
|
||||
if (incomingDamage <= 0f || activeShields.Count == 0)
|
||||
return incomingDamage;
|
||||
|
||||
float remainingDamage = incomingDamage;
|
||||
for (int i = 0; i < activeShields.Count && remainingDamage > 0f; i++)
|
||||
{
|
||||
remainingDamage -= activeShields[i].Consume(remainingDamage);
|
||||
}
|
||||
|
||||
CleanupInactiveShields();
|
||||
return Mathf.Max(0f, remainingDamage);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 지속시간 경과를 처리합니다.
|
||||
/// </summary>
|
||||
public bool Tick(float deltaTime)
|
||||
{
|
||||
bool changed = false;
|
||||
|
||||
for (int i = activeShields.Count - 1; i >= 0; i--)
|
||||
{
|
||||
ActiveShield shield = activeShields[i];
|
||||
bool expired = shield.Tick(deltaTime);
|
||||
if (!expired && !shield.IsDepleted)
|
||||
continue;
|
||||
|
||||
activeShields.RemoveAt(i);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 모든 보호막을 제거합니다.
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
activeShields.Clear();
|
||||
}
|
||||
|
||||
private ActiveShield FindShield(AbnormalityData shieldType)
|
||||
{
|
||||
for (int i = 0; i < activeShields.Count; i++)
|
||||
{
|
||||
if (activeShields[i].ShieldType == shieldType)
|
||||
return activeShields[i];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void CleanupInactiveShields()
|
||||
{
|
||||
for (int i = activeShields.Count - 1; i >= 0; i--)
|
||||
{
|
||||
if (!activeShields[i].IsExpired && !activeShields[i].IsDepleted)
|
||||
continue;
|
||||
|
||||
activeShields.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 보호막 수치와 보호막 이상상태를 동기화하는 유틸리티입니다.
|
||||
/// </summary>
|
||||
public static class ShieldAbnormalityUtility
|
||||
{
|
||||
/// <summary>
|
||||
/// 활성 보호막 목록에 맞춰 보호막 이상상태를 적용하거나 제거합니다.
|
||||
/// </summary>
|
||||
public static void SyncShieldAbnormalities(
|
||||
AbnormalityManager abnormalityManager,
|
||||
IReadOnlyList<ActiveShield> activeShields,
|
||||
GameObject defaultSource)
|
||||
{
|
||||
if (abnormalityManager == null)
|
||||
return;
|
||||
|
||||
HashSet<AbnormalityData> desiredShieldStates = new HashSet<AbnormalityData>();
|
||||
Dictionary<AbnormalityData, GameObject> shieldSources = new Dictionary<AbnormalityData, GameObject>();
|
||||
|
||||
if (activeShields != null)
|
||||
{
|
||||
for (int i = 0; i < activeShields.Count; i++)
|
||||
{
|
||||
ActiveShield shield = activeShields[i];
|
||||
if (shield == null || shield.RemainingAmount <= 0f || shield.ShieldType == null)
|
||||
continue;
|
||||
|
||||
desiredShieldStates.Add(shield.ShieldType);
|
||||
|
||||
if (!shieldSources.ContainsKey(shield.ShieldType))
|
||||
{
|
||||
shieldSources.Add(shield.ShieldType, shield.Source != null ? shield.Source : defaultSource);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (AbnormalityData shieldState in desiredShieldStates)
|
||||
{
|
||||
if (abnormalityManager.HasAbnormality(shieldState))
|
||||
continue;
|
||||
|
||||
GameObject source = shieldSources.TryGetValue(shieldState, out GameObject resolvedSource)
|
||||
? resolvedSource
|
||||
: defaultSource;
|
||||
abnormalityManager.ApplyAbnormality(shieldState, source);
|
||||
}
|
||||
|
||||
IReadOnlyList<ActiveAbnormality> activeAbnormalities = abnormalityManager.ActiveAbnormalities;
|
||||
List<AbnormalityData> removeList = new List<AbnormalityData>();
|
||||
|
||||
for (int i = 0; i < activeAbnormalities.Count; i++)
|
||||
{
|
||||
ActiveAbnormality activeAbnormality = activeAbnormalities[i];
|
||||
if (activeAbnormality?.Data == null || !activeAbnormality.Data.isShieldState)
|
||||
continue;
|
||||
|
||||
if (desiredShieldStates.Contains(activeAbnormality.Data))
|
||||
continue;
|
||||
|
||||
removeList.Add(activeAbnormality.Data);
|
||||
}
|
||||
|
||||
for (int i = 0; i < removeList.Count; i++)
|
||||
{
|
||||
abnormalityManager.RemoveAbnormality(removeList[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 69ea46fefb9597e49a4ce68c81ccf0ce
|
||||
Reference in New Issue
Block a user