feat: 플레이어 행동 상태 통합 및 이상상태 디버그 추가

- PlayerActionState로 이동, 점프, 스킬 입력 가능 여부를 통합
- 기절과 침묵 이상상태 데이터 및 효과 에셋을 추가
- 로컬 플레이어용 이상상태 디버그 HUD와 자동 검증 러너를 추가
- 플레이어 이동, 스킬 입력, 네트워크 상태를 새 행동 상태 기준으로 정리
This commit is contained in:
2026-03-19 18:21:39 +09:00
parent d39e13f032
commit e863d108da
23 changed files with 1286 additions and 25 deletions

View File

@@ -28,23 +28,28 @@ namespace Colosseum.Abnormalities
private int silenceCount;
private float slowMultiplier = 1f;
// 클라이언트 판정용 제어 효과 동기화 변수
private NetworkVariable<int> syncedStunCount = new NetworkVariable<int>(0);
private NetworkVariable<int> syncedSilenceCount = new NetworkVariable<int>(0);
private NetworkVariable<float> syncedSlowMultiplier = new NetworkVariable<float>(1f);
// 네트워크 동기화용 데이터
private NetworkList<AbnormalitySyncData> syncedAbnormalities;
/// <summary>
/// 기절 상태 여부
/// </summary>
public bool IsStunned => stunCount > 0;
public bool IsStunned => GetCurrentStunCount() > 0;
/// <summary>
/// 침묵 상태 여부
/// </summary>
public bool IsSilenced => silenceCount > 0;
public bool IsSilenced => GetCurrentSilenceCount() > 0;
/// <summary>
/// 이동 속도 배율 (1.0 = 기본, 0.5 = 50% 감소)
/// </summary>
public float MoveSpeedMultiplier => slowMultiplier;
public float MoveSpeedMultiplier => GetCurrentSlowMultiplier();
/// <summary>
/// 행동 가능 여부 (기절이 아닐 때)
@@ -102,11 +107,33 @@ namespace Colosseum.Abnormalities
public override void OnNetworkSpawn()
{
syncedAbnormalities.OnListChanged += OnSyncedAbnormalitiesChanged;
syncedStunCount.OnValueChanged += HandleSyncedStunChanged;
syncedSilenceCount.OnValueChanged += HandleSyncedSilenceChanged;
syncedSlowMultiplier.OnValueChanged += HandleSyncedSlowChanged;
if (networkController != null)
{
networkController.OnDeathStateChanged += HandleDeathStateChanged;
}
if (IsServer)
{
SyncControlEffects();
}
}
public override void OnNetworkDespawn()
{
syncedAbnormalities.OnListChanged -= OnSyncedAbnormalitiesChanged;
syncedStunCount.OnValueChanged -= HandleSyncedStunChanged;
syncedSilenceCount.OnValueChanged -= HandleSyncedSilenceChanged;
syncedSlowMultiplier.OnValueChanged -= HandleSyncedSlowChanged;
if (networkController != null)
{
networkController.OnDeathStateChanged -= HandleDeathStateChanged;
}
}
private void Update()
@@ -129,6 +156,12 @@ namespace Colosseum.Abnormalities
return;
}
if (networkController != null && networkController.IsDead)
{
Debug.Log($"[Abnormality] Ignored {data.abnormalityName} because {gameObject.name} is dead");
return;
}
if (IsServer)
{
ApplyAbnormalityInternal(data, source);
@@ -386,6 +419,8 @@ namespace Colosseum.Abnormalities
slowMultiplier = Mathf.Min(slowMultiplier, data.slowMultiplier);
break;
}
SyncControlEffects();
}
private void RemoveControlEffect(AbnormalityData data)
@@ -404,6 +439,8 @@ namespace Colosseum.Abnormalities
RecalculateSlowMultiplier();
break;
}
SyncControlEffects();
}
private void RecalculateSlowMultiplier()
@@ -419,6 +456,22 @@ namespace Colosseum.Abnormalities
}
}
private int GetCurrentStunCount() => IsServer ? stunCount : syncedStunCount.Value;
private int GetCurrentSilenceCount() => IsServer ? silenceCount : syncedSilenceCount.Value;
private float GetCurrentSlowMultiplier() => IsServer ? slowMultiplier : syncedSlowMultiplier.Value;
private void SyncControlEffects()
{
if (!IsServer)
return;
syncedStunCount.Value = stunCount;
syncedSilenceCount.Value = silenceCount;
syncedSlowMultiplier.Value = slowMultiplier;
}
private void SyncAbnormalityAdd(ActiveAbnormality abnormality, GameObject source)
{
var sourceClientId = source != null && source.TryGetComponent<NetworkObject>(out var netObj) ? netObj.OwnerClientId : 0UL;
@@ -464,6 +517,45 @@ namespace Colosseum.Abnormalities
OnAbnormalitiesChanged?.Invoke();
}
private void HandleSyncedStunChanged(int oldValue, int newValue)
{
if (oldValue == newValue)
return;
OnAbnormalitiesChanged?.Invoke();
}
private void HandleSyncedSilenceChanged(int oldValue, int newValue)
{
if (oldValue == newValue)
return;
OnAbnormalitiesChanged?.Invoke();
}
private void HandleSyncedSlowChanged(float oldValue, float newValue)
{
if (Mathf.Approximately(oldValue, newValue))
return;
OnAbnormalitiesChanged?.Invoke();
}
/// <summary>
/// 사망 시 활성 이상 상태를 모두 제거합니다.
/// </summary>
private void HandleDeathStateChanged(bool dead)
{
if (!dead || !IsServer)
return;
if (activeAbnormalities.Count == 0)
return;
RemoveAllAbnormalities();
Debug.Log($"[Abnormality] Cleared all abnormalities on death: {gameObject.name}");
}
private AbnormalityData FindAbnormalityDataById(int instanceId)
{
var allData = Resources.FindObjectsOfTypeAll<AbnormalityData>();