feat: 젬 테스트 경로 및 보스 기절 디버그 추가
- 다중 젬 슬롯용 타입을 별도 스크립트로 분리하고 테스트 젬/로드아웃 자산 생성 경로를 정리 - 젬 테스트 전용 공격 스킬과 분리된 애니메이션 자산을 추가해 베이스 스킬 검증 경로를 마련 - PlayerSkillDebugMenu와 MPP 디버그 메뉴를 보강해 젬 프리셋 적용, 원격 테스트, 보스 기절 디버그 메뉴를 추가 - BossCombatBehaviorContext와 공통 BT 액션이 기절 상태를 존중하도록 수정해 보스 추적과 패턴 실행을 중단 - Unity 리프레시와 외부 빌드 통과를 확인하고 드로그전 및 MPP 기준 젬 프리셋 적용 흐름을 검증
This commit is contained in:
@@ -53,6 +53,9 @@ namespace Colosseum.Skills
|
||||
|
||||
// 현재 실행 중인 스킬
|
||||
private SkillData currentSkill;
|
||||
private SkillLoadoutEntry currentLoadoutEntry;
|
||||
private readonly List<SkillEffect> currentCastStartEffects = new();
|
||||
private readonly Dictionary<int, List<SkillEffect>> currentTriggeredEffects = new();
|
||||
private bool skillEndRequested; // OnSkillEnd 이벤트 호출 여부
|
||||
private bool waitingForEndAnimation; // EndAnimation 종료 대기 중
|
||||
|
||||
@@ -66,6 +69,7 @@ namespace Colosseum.Skills
|
||||
public bool UsesRootMotion => currentSkill != null && currentSkill.UseRootMotion;
|
||||
public bool IgnoreRootMotionY => currentSkill != null && currentSkill.IgnoreRootMotionY;
|
||||
public SkillData CurrentSkill => currentSkill;
|
||||
public SkillLoadoutEntry CurrentLoadoutEntry => currentLoadoutEntry;
|
||||
public Animator Animator => animator;
|
||||
public SkillCancelReason LastCancelReason => lastCancelReason;
|
||||
public string LastCancelledSkillName => lastCancelledSkillName;
|
||||
@@ -131,6 +135,15 @@ namespace Colosseum.Skills
|
||||
/// </summary>
|
||||
public bool ExecuteSkill(SkillData skill)
|
||||
{
|
||||
return ExecuteSkill(SkillLoadoutEntry.CreateTemporary(skill));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 슬롯 엔트리 기준으로 스킬 시전
|
||||
/// </summary>
|
||||
public bool ExecuteSkill(SkillLoadoutEntry loadoutEntry)
|
||||
{
|
||||
SkillData skill = loadoutEntry != null ? loadoutEntry.BaseSkill : null;
|
||||
if (skill == null)
|
||||
{
|
||||
Debug.LogWarning("Skill is null!");
|
||||
@@ -157,17 +170,19 @@ namespace Colosseum.Skills
|
||||
return false;
|
||||
}
|
||||
|
||||
currentLoadoutEntry = loadoutEntry != null ? loadoutEntry.CreateCopy() : SkillLoadoutEntry.CreateTemporary(skill);
|
||||
currentSkill = skill;
|
||||
skillEndRequested = false;
|
||||
waitingForEndAnimation = false;
|
||||
lastCancelReason = SkillCancelReason.None;
|
||||
BuildResolvedEffects(currentLoadoutEntry);
|
||||
|
||||
if (debugMode) Debug.Log($"[Skill] Cast: {skill.SkillName}");
|
||||
|
||||
// 쿨타임 시작
|
||||
StartCooldown(skill);
|
||||
StartCooldown(skill, currentLoadoutEntry.GetResolvedCooldown());
|
||||
|
||||
TriggerCastStartEffects(skill);
|
||||
TriggerCastStartEffects();
|
||||
|
||||
// 스킬 애니메이션 재생
|
||||
if (skill.SkillClip != null && animator != null)
|
||||
@@ -176,7 +191,7 @@ namespace Colosseum.Skills
|
||||
PlaySkillClip(skill.SkillClip);
|
||||
}
|
||||
|
||||
TriggerImmediateSelfEffectsIfNeeded(skill);
|
||||
TriggerImmediateSelfEffectsIfNeeded();
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -185,17 +200,17 @@ namespace Colosseum.Skills
|
||||
/// 시전 시작 즉시 발동하는 효과를 실행합니다.
|
||||
/// 서버 권한으로만 처리해 실제 게임플레이 효과가 한 번만 적용되게 합니다.
|
||||
/// </summary>
|
||||
private void TriggerCastStartEffects(SkillData skill)
|
||||
private void TriggerCastStartEffects()
|
||||
{
|
||||
if (skill == null || skill.CastStartEffects == null || skill.CastStartEffects.Count == 0)
|
||||
if (currentSkill == null || currentCastStartEffects.Count == 0)
|
||||
return;
|
||||
|
||||
if (NetworkManager.Singleton != null && !NetworkManager.Singleton.IsServer)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < skill.CastStartEffects.Count; i++)
|
||||
for (int i = 0; i < currentCastStartEffects.Count; i++)
|
||||
{
|
||||
SkillEffect effect = skill.CastStartEffects[i];
|
||||
SkillEffect effect = currentCastStartEffects[i];
|
||||
if (effect == null)
|
||||
continue;
|
||||
|
||||
@@ -208,20 +223,23 @@ namespace Colosseum.Skills
|
||||
/// 애니메이션 이벤트가 없는 자기 자신 대상 효과는 시전 즉시 발동합니다.
|
||||
/// 버프/무적 같은 자기 강화 스킬이 이벤트 누락으로 동작하지 않는 상황을 막기 위한 보정입니다.
|
||||
/// </summary>
|
||||
private void TriggerImmediateSelfEffectsIfNeeded(SkillData skill)
|
||||
private void TriggerImmediateSelfEffectsIfNeeded()
|
||||
{
|
||||
if (skill == null || skill.Effects == null || skill.Effects.Count == 0)
|
||||
if (currentSkill == null || currentTriggeredEffects.Count == 0)
|
||||
return;
|
||||
|
||||
if (NetworkManager.Singleton != null && !NetworkManager.Singleton.IsServer)
|
||||
return;
|
||||
|
||||
if (skill.SkillClip != null && skill.SkillClip.events != null && skill.SkillClip.events.Length > 0)
|
||||
if (currentSkill.SkillClip != null && currentSkill.SkillClip.events != null && currentSkill.SkillClip.events.Length > 0)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < skill.Effects.Count; i++)
|
||||
if (!currentTriggeredEffects.TryGetValue(0, out List<SkillEffect> effectsAtZero))
|
||||
return;
|
||||
|
||||
for (int i = 0; i < effectsAtZero.Count; i++)
|
||||
{
|
||||
SkillEffect effect = skill.Effects[i];
|
||||
SkillEffect effect = effectsAtZero[i];
|
||||
if (effect == null || effect.TargetType != TargetType.Self)
|
||||
continue;
|
||||
|
||||
@@ -230,6 +248,21 @@ namespace Colosseum.Skills
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 현재 슬롯 엔트리 기준으로 시전 시작/트리거 효과를 합성합니다.
|
||||
/// </summary>
|
||||
private void BuildResolvedEffects(SkillLoadoutEntry loadoutEntry)
|
||||
{
|
||||
currentCastStartEffects.Clear();
|
||||
currentTriggeredEffects.Clear();
|
||||
|
||||
if (loadoutEntry == null)
|
||||
return;
|
||||
|
||||
loadoutEntry.CollectCastStartEffects(currentCastStartEffects);
|
||||
loadoutEntry.CollectTriggeredEffects(currentTriggeredEffects);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 스킬 클립으로 Override Controller 생성 후 재생
|
||||
/// </summary>
|
||||
@@ -354,23 +387,28 @@ namespace Colosseum.Skills
|
||||
return;
|
||||
}
|
||||
|
||||
var effects = currentSkill.Effects;
|
||||
if (index < 0 || index >= effects.Count)
|
||||
if (!currentTriggeredEffects.TryGetValue(index, out List<SkillEffect> effects) || effects == null || effects.Count == 0)
|
||||
{
|
||||
if (debugMode) Debug.LogWarning($"[Effect] Invalid index: {index}");
|
||||
return;
|
||||
}
|
||||
|
||||
var effect = effects[index];
|
||||
if (debugMode) Debug.Log($"[Effect] {effect.name} (index {index})");
|
||||
|
||||
// 공격 범위 시각화
|
||||
if (showAreaDebug)
|
||||
for (int i = 0; i < effects.Count; i++)
|
||||
{
|
||||
effect.DrawDebugRange(gameObject, debugDrawDuration);
|
||||
}
|
||||
SkillEffect effect = effects[i];
|
||||
if (effect == null)
|
||||
continue;
|
||||
|
||||
effect.ExecuteOnCast(gameObject);
|
||||
if (debugMode) Debug.Log($"[Effect] {effect.name} (index {index})");
|
||||
|
||||
// 공격 범위 시각화
|
||||
if (showAreaDebug)
|
||||
{
|
||||
effect.DrawDebugRange(gameObject, debugDrawDuration);
|
||||
}
|
||||
|
||||
effect.ExecuteOnCast(gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -408,6 +446,9 @@ namespace Colosseum.Skills
|
||||
|
||||
RestoreBaseController();
|
||||
currentSkill = null;
|
||||
currentLoadoutEntry = null;
|
||||
currentCastStartEffects.Clear();
|
||||
currentTriggeredEffects.Clear();
|
||||
skillEndRequested = false;
|
||||
waitingForEndAnimation = false;
|
||||
return true;
|
||||
@@ -430,9 +471,9 @@ namespace Colosseum.Skills
|
||||
return Mathf.Max(0f, remaining);
|
||||
}
|
||||
|
||||
private void StartCooldown(SkillData skill)
|
||||
private void StartCooldown(SkillData skill, float cooldownDuration)
|
||||
{
|
||||
cooldownTracker[skill] = Time.time + skill.Cooldown;
|
||||
cooldownTracker[skill] = Time.time + cooldownDuration;
|
||||
}
|
||||
|
||||
public void ResetCooldown(SkillData skill)
|
||||
|
||||
Reference in New Issue
Block a user