feat: 무적 이상상태 기반 구르기 스킬 정리
This commit is contained in:
@@ -93,7 +93,7 @@ namespace Colosseum.Player
|
||||
GUILayout.Label($"사망 상태: {(networkController != null && networkController.IsDead ? "Dead" : "Alive")}");
|
||||
if (TryGetComponent<PlayerActionState>(out var actionState))
|
||||
{
|
||||
GUILayout.Label($"회피 상태: {(actionState.IsEvading ? "Evading" : "Idle")} / 이동:{actionState.CanMove} / 스킬:{actionState.CanUseSkills}");
|
||||
GUILayout.Label($"무적 상태: {(actionState.IsDamageImmune ? "Immune" : "Normal")} / 이동:{actionState.CanMove} / 스킬:{actionState.CanUseSkills}");
|
||||
}
|
||||
GUILayout.Label("입력 예시: 기절 / Data_Abnormality_Player_Stun / 0");
|
||||
|
||||
|
||||
@@ -104,9 +104,9 @@ namespace Colosseum.Player
|
||||
Verify("초기 상태: 사망 아님", !networkController.IsDead);
|
||||
Verify("초기 상태: 이동 가능", actionState.CanMove);
|
||||
Verify("초기 상태: 스킬 사용 가능", actionState.CanUseSkills);
|
||||
Verify("초기 상태: 회피 상태 아님", !actionState.IsEvading);
|
||||
Verify("초기 상태: 무적 상태 아님", !actionState.IsDamageImmune);
|
||||
|
||||
yield return RunEvadeVerification();
|
||||
yield return RunInvincibilitySkillVerification();
|
||||
|
||||
abnormalityManager.ApplyAbnormality(stunData, gameObject);
|
||||
yield return new WaitForSeconds(settleDelay);
|
||||
@@ -234,48 +234,46 @@ namespace Colosseum.Player
|
||||
#endif
|
||||
}
|
||||
|
||||
private IEnumerator RunEvadeVerification()
|
||||
private IEnumerator RunInvincibilitySkillVerification()
|
||||
{
|
||||
SkillData evadeSkill = skillInput != null ? skillInput.GetSkill(6) : null;
|
||||
if (evadeSkill == null)
|
||||
SkillData invincibilitySkill = skillInput != null ? skillInput.GetSkill(6) : null;
|
||||
if (invincibilitySkill == null)
|
||||
{
|
||||
AppendLine("[SKIP] 회피 검증: 회피 슬롯 스킬이 없습니다.");
|
||||
AppendLine("[SKIP] 무적 스킬 검증: 7번 슬롯 스킬이 없습니다.");
|
||||
yield break;
|
||||
}
|
||||
|
||||
if (skillController == null || !skillController.ExecuteSkill(evadeSkill))
|
||||
if (skillController == null || !skillController.ExecuteSkill(invincibilitySkill))
|
||||
{
|
||||
Verify("회피 검증: 스킬 실행 성공", false);
|
||||
Verify("무적 스킬 검증: 스킬 실행 성공", false);
|
||||
yield break;
|
||||
}
|
||||
|
||||
yield return WaitForConditionOrTimeout(() => actionState.IsDamageImmune, GetSkillDuration(invincibilitySkill) + 0.5f);
|
||||
|
||||
float healthBeforeDamage = networkController.Health;
|
||||
|
||||
Verify("무적 적용: IsDamageImmune", actionState.IsDamageImmune);
|
||||
networkController.TakeDamageRpc(15f);
|
||||
yield return new WaitForSeconds(settleDelay);
|
||||
|
||||
Verify("회피 적용: IsEvading", actionState.IsEvading);
|
||||
Verify("회피 적용: 이동 불가", !actionState.CanMove);
|
||||
Verify("회피 적용: 점프 불가", !actionState.CanJump);
|
||||
Verify("회피 적용: 일반 스킬 사용 불가", !actionState.CanUseSkills);
|
||||
Verify("회피 적용: 회피 스킬 연속 사용 불가", !actionState.CanStartSkill(evadeSkill));
|
||||
Verify("무적 적용: 대미지 무시", Mathf.Approximately(networkController.Health, healthBeforeDamage));
|
||||
|
||||
if (silenceData != null)
|
||||
{
|
||||
abnormalityManager.ApplyAbnormality(silenceData, gameObject);
|
||||
yield return WaitForConditionOrTimeout(() => actionState.IsSilenced, settleDelay + 0.5f);
|
||||
|
||||
Verify("회피 중 침묵 적용: IsSilenced", actionState.IsSilenced);
|
||||
Verify("회피 중 침묵 적용: 회피 상태 유지", actionState.IsEvading);
|
||||
Verify("회피 중 침묵 적용: 회피 스킬 신규 사용 불가", !actionState.CanStartSkill(evadeSkill));
|
||||
Verify("무적 중 침묵 적용: IsSilenced", actionState.IsSilenced);
|
||||
Verify("무적 중 침묵 적용: 무적 상태 유지", actionState.IsDamageImmune);
|
||||
Verify("무적 중 침묵 적용: 스킬 신규 사용 불가", !actionState.CanStartSkill(invincibilitySkill));
|
||||
|
||||
abnormalityManager.RemoveAbnormality(silenceData);
|
||||
yield return new WaitForSeconds(settleDelay);
|
||||
}
|
||||
|
||||
yield return WaitForConditionOrTimeout(() => !actionState.IsEvading, GetSkillDuration(evadeSkill) + 1.5f);
|
||||
yield return WaitForConditionOrTimeout(() => !actionState.IsDamageImmune, GetSkillDuration(invincibilitySkill) + 1.5f);
|
||||
|
||||
Verify("회피 해제: IsEvading false", !actionState.IsEvading);
|
||||
Verify("회피 해제: 이동 가능 복구", actionState.CanMove);
|
||||
Verify("회피 해제: 스킬 사용 가능 복구", actionState.CanUseSkills);
|
||||
Verify("회피 해제: 회피 스킬 재사용 가능 복구", actionState.CanStartSkill(evadeSkill));
|
||||
Verify("무적 해제: IsDamageImmune false", !actionState.IsDamageImmune);
|
||||
}
|
||||
|
||||
private float GetSkillDuration(SkillData skill)
|
||||
|
||||
@@ -56,10 +56,9 @@ namespace Colosseum.Player
|
||||
public SkillData CurrentSkill => skillController != null ? skillController.CurrentSkill : null;
|
||||
|
||||
/// <summary>
|
||||
/// 현재 시전 중인 스킬이 회피 스킬일 때의 회피 상태 여부.
|
||||
/// 침묵은 회피 상태 자체를 끊지 않으며, 회피 스킬의 신규 사용만 막습니다.
|
||||
/// 무적 이상상태 여부
|
||||
/// </summary>
|
||||
public bool IsEvading => IsCastingSkill && CurrentSkill != null && CurrentSkill.IsEvadeSkill;
|
||||
public bool IsDamageImmune => abnormalityManager != null && abnormalityManager.IsInvincible;
|
||||
|
||||
/// <summary>
|
||||
/// 입력을 받아도 되는지 여부
|
||||
@@ -79,23 +78,20 @@ namespace Colosseum.Player
|
||||
/// <summary>
|
||||
/// 일반 스킬 시작 가능 여부
|
||||
/// </summary>
|
||||
public bool CanUseSkills => CanReceiveInput && !IsStunned && !IsSilenced && !IsEvading && !BlocksSkillUseForCurrentSkill();
|
||||
public bool CanUseSkills => CanReceiveInput && !IsStunned && !IsSilenced && !BlocksSkillUseForCurrentSkill();
|
||||
|
||||
/// <summary>
|
||||
/// 특정 스킬의 시작 가능 여부.
|
||||
/// 회피 스킬도 일반 스킬과 같은 시작 판정을 사용하되, 현재 시전 중인 스킬의 회피 차단 정책을 따릅니다.
|
||||
/// 스킬 이름과 무관하게 동일한 시작 규칙을 사용합니다.
|
||||
/// </summary>
|
||||
public bool CanStartSkill(SkillData skill)
|
||||
{
|
||||
if (skill == null)
|
||||
return false;
|
||||
|
||||
if (!CanReceiveInput || IsStunned || IsSilenced || IsEvading)
|
||||
if (!CanReceiveInput || IsStunned || IsSilenced)
|
||||
return false;
|
||||
|
||||
if (skill.IsEvadeSkill)
|
||||
return !BlocksEvadeForCurrentSkill();
|
||||
|
||||
return !BlocksSkillUseForCurrentSkill();
|
||||
}
|
||||
|
||||
@@ -148,13 +144,5 @@ namespace Colosseum.Player
|
||||
|
||||
return CurrentSkill == null || CurrentSkill.BlockOtherSkillsWhileCasting;
|
||||
}
|
||||
|
||||
private bool BlocksEvadeForCurrentSkill()
|
||||
{
|
||||
if (!IsCastingSkill)
|
||||
return false;
|
||||
|
||||
return CurrentSkill == null || CurrentSkill.BlockEvadeWhileCasting;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,9 @@ namespace Colosseum.Player
|
||||
[Tooltip("CharacterStats 컴포넌트 (없으면 자동 검색)")]
|
||||
[SerializeField] private CharacterStats characterStats;
|
||||
|
||||
[Tooltip("이상상태 관리자 (없으면 자동 검색)")]
|
||||
[SerializeField] private AbnormalityManager abnormalityManager;
|
||||
|
||||
// 네트워크 동기화 변수
|
||||
private NetworkVariable<float> currentHealth = new NetworkVariable<float>(100f);
|
||||
private NetworkVariable<float> currentMana = new NetworkVariable<float>(50f);
|
||||
@@ -50,6 +53,11 @@ namespace Colosseum.Player
|
||||
characterStats = GetComponent<CharacterStats>();
|
||||
}
|
||||
|
||||
if (abnormalityManager == null)
|
||||
{
|
||||
abnormalityManager = GetComponent<AbnormalityManager>();
|
||||
}
|
||||
|
||||
// 네트워크 변수 변경 콜백 등록
|
||||
currentHealth.OnValueChanged += HandleHealthChanged;
|
||||
currentMana.OnValueChanged += HandleManaChanged;
|
||||
@@ -93,7 +101,7 @@ namespace Colosseum.Player
|
||||
[Rpc(SendTo.Server)]
|
||||
public void TakeDamageRpc(float damage)
|
||||
{
|
||||
if (isDead.Value) return;
|
||||
if (isDead.Value || IsDamageImmune()) return;
|
||||
|
||||
currentHealth.Value = Mathf.Max(0f, currentHealth.Value - damage);
|
||||
|
||||
@@ -159,7 +167,6 @@ namespace Colosseum.Player
|
||||
isDead.Value = true;
|
||||
|
||||
// 사망 시 활성 이상 상태를 정리해 리스폰 시 잔존하지 않게 합니다.
|
||||
var abnormalityManager = GetComponent<AbnormalityManager>();
|
||||
if (abnormalityManager != null)
|
||||
{
|
||||
abnormalityManager.RemoveAllAbnormalities();
|
||||
@@ -202,7 +209,6 @@ namespace Colosseum.Player
|
||||
{
|
||||
if (!IsServer) return;
|
||||
|
||||
var abnormalityManager = GetComponent<AbnormalityManager>();
|
||||
if (abnormalityManager != null)
|
||||
{
|
||||
abnormalityManager.RemoveAllAbnormalities();
|
||||
@@ -244,7 +250,7 @@ namespace Colosseum.Player
|
||||
/// </summary>
|
||||
public float TakeDamage(float damage, object source = null)
|
||||
{
|
||||
if (!IsServer || isDead.Value) return 0f;
|
||||
if (!IsServer || isDead.Value || IsDamageImmune()) return 0f;
|
||||
|
||||
float actualDamage = Mathf.Min(damage, currentHealth.Value);
|
||||
currentHealth.Value = Mathf.Max(0f, currentHealth.Value - damage);
|
||||
@@ -269,6 +275,11 @@ namespace Colosseum.Player
|
||||
|
||||
return actualHeal;
|
||||
}
|
||||
|
||||
private bool IsDamageImmune()
|
||||
{
|
||||
return abnormalityManager != null && abnormalityManager.IsInvincible;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace Colosseum.Player
|
||||
public class PlayerSkillInput : NetworkBehaviour
|
||||
{
|
||||
[Header("Skill Slots")]
|
||||
[Tooltip("각 슬롯에 등록할 스킬 데이터 (6개 + 긴급회피)")]
|
||||
[Tooltip("각 슬롯에 등록할 스킬 데이터 (6개 + 추가 슬롯)")]
|
||||
[SerializeField] private SkillData[] skillSlots = new SkillData[7];
|
||||
|
||||
[Header("References")]
|
||||
|
||||
Reference in New Issue
Block a user