refactor: 스킬 트리거 효과를 그룹 구조로 변경 — 하나의 애니메이션 이벤트로 여러 effect 발동 가능
- SkillGemTriggeredEffectEntry를 공용 타입 SkillTriggeredEffectEntry로 승격 - SkillData.effects를 List<SkillTriggeredEffectEntry>로 변경 (기존 flat list는 OnValidate에서 자동 마이그레이션) - SkillLoadoutEntry, PlayerSkillInput 등 소비자 코드 그룹 구조 대응 - 기존 스킬 에셋 마이그레이션 완료 Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -67,9 +67,35 @@ namespace Colosseum.Skills
|
||||
/// </summary>
|
||||
private void OnValidate()
|
||||
{
|
||||
MigrateLegacyEffects();
|
||||
RefreshAnimationClips();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 레거시 flat effects 리스트를 grouped triggeredEffects 구조로 마이그레이션합니다.
|
||||
/// </summary>
|
||||
private void MigrateLegacyEffects()
|
||||
{
|
||||
if (effects == null || effects.Count == 0)
|
||||
return;
|
||||
if (triggeredEffects != null && triggeredEffects.Count > 0)
|
||||
return;
|
||||
|
||||
triggeredEffects = new List<SkillTriggeredEffectEntry>();
|
||||
for (int i = 0; i < effects.Count; i++)
|
||||
{
|
||||
SkillEffect effect = effects[i];
|
||||
if (effect == null)
|
||||
continue;
|
||||
|
||||
triggeredEffects.Add(new SkillTriggeredEffectEntry(i, new List<SkillEffect> { effect }));
|
||||
}
|
||||
|
||||
effects.Clear();
|
||||
UnityEditor.EditorUtility.SetDirty(this);
|
||||
Debug.Log($"[SkillData] '{name}' effects 마이그레이션 완료: {triggeredEffects.Count}개 엔트리", this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 애셋 이름 기반으로 매칭되는 애니메이션 클립을 자동 수집합니다.
|
||||
/// SkillData 이름이 'Data_Skill_'으로 시작하면 'Anim_{key}_{순서}' 클립을 찾아 animationClips에 채웁니다.
|
||||
@@ -192,13 +218,18 @@ namespace Colosseum.Skills
|
||||
[Tooltip("이 스킬에 장착 가능한 젬 슬롯 수")]
|
||||
[Min(0)] [SerializeField] private int maxGemSlotCount = 2;
|
||||
|
||||
[Header("효과 목록")]
|
||||
[Header("시전 시작 효과")]
|
||||
[Tooltip("시전 시작 즉시 발동하는 효과 목록. 시전 보호 버프 등에 사용됩니다.")]
|
||||
[SerializeField] private List<SkillEffect> castStartEffects = new List<SkillEffect>();
|
||||
|
||||
[Header("효과 목록")]
|
||||
[Tooltip("애니메이션 이벤트 OnEffect(index)로 발동. 리스트 순서 = 이벤트 인덱스")]
|
||||
[SerializeField] private List<SkillEffect> effects = new List<SkillEffect>();
|
||||
[Header("트리거 효과 목록")]
|
||||
[Tooltip("애니메이션 이벤트 OnEffect(index)로 발동. 각 엔트리의 Trigger Index가 이벤트 인덱스와 매칭됩니다.")]
|
||||
[SerializeField] private List<SkillTriggeredEffectEntry> triggeredEffects = new();
|
||||
|
||||
/// <summary>
|
||||
/// 레거시 flat effects 리스트. OnValidate에서 triggeredEffects로 자동 마이그레이션됩니다.
|
||||
/// </summary>
|
||||
[HideInInspector] [SerializeField] private List<SkillEffect> effects = new List<SkillEffect>();
|
||||
|
||||
// Properties
|
||||
public string SkillName => skillName;
|
||||
@@ -227,7 +258,7 @@ namespace Colosseum.Skills
|
||||
public bool BlockJumpWhileCasting => blockJumpWhileCasting;
|
||||
public bool BlockOtherSkillsWhileCasting => blockOtherSkillsWhileCasting;
|
||||
public IReadOnlyList<SkillEffect> CastStartEffects => castStartEffects;
|
||||
public IReadOnlyList<SkillEffect> Effects => effects;
|
||||
public IReadOnlyList<SkillTriggeredEffectEntry> TriggeredEffects => triggeredEffects;
|
||||
public WeaponTrait AllowedWeaponTraits => allowedWeaponTraits;
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -24,18 +24,27 @@ namespace Colosseum.Skills
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 젬 효과가 발동될 애니메이션 이벤트 인덱스와 효과 목록입니다.
|
||||
/// 애니메이션 이벤트 인덱스와 해당 타이밍에 발동할 효과 목록입니다.
|
||||
/// SkillData와 SkillGemData 모두에서 사용합니다.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class SkillGemTriggeredEffectEntry
|
||||
public class SkillTriggeredEffectEntry
|
||||
{
|
||||
[Tooltip("OnEffect(index)와 매칭되는 애니메이션 이벤트 인덱스")]
|
||||
[Min(0)] [SerializeField] private int triggerIndex = 0;
|
||||
[Tooltip("해당 인덱스에서 함께 실행할 추가 효과")]
|
||||
[Tooltip("해당 인덱스에서 함께 실행할 효과 목록")]
|
||||
[SerializeField] private List<SkillEffect> effects = new();
|
||||
|
||||
public int TriggerIndex => triggerIndex;
|
||||
public IReadOnlyList<SkillEffect> Effects => effects;
|
||||
|
||||
public SkillTriggeredEffectEntry() { }
|
||||
|
||||
public SkillTriggeredEffectEntry(int triggerIndex, List<SkillEffect> effects)
|
||||
{
|
||||
this.triggerIndex = Mathf.Max(0, triggerIndex);
|
||||
this.effects = effects ?? new List<SkillEffect>();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -101,7 +110,7 @@ namespace Colosseum.Skills
|
||||
[Tooltip("시전 시작 시 즉시 발동하는 추가 효과")]
|
||||
[SerializeField] private List<SkillEffect> castStartEffects = new();
|
||||
[Tooltip("애니메이션 이벤트 인덱스별로 발동하는 추가 효과")]
|
||||
[SerializeField] private List<SkillGemTriggeredEffectEntry> triggeredEffects = new();
|
||||
[SerializeField] private List<SkillTriggeredEffectEntry> triggeredEffects = new();
|
||||
|
||||
[Header("이상상태 부여")]
|
||||
[Tooltip("스킬 사용 시 자신에게 즉시 부여할 이상상태")]
|
||||
@@ -127,7 +136,7 @@ namespace Colosseum.Skills
|
||||
public IReadOnlyList<SkillGemCategory> IncompatibleCategories => incompatibleCategories;
|
||||
public IReadOnlyList<SkillGemData> IncompatibleGems => incompatibleGems;
|
||||
public IReadOnlyList<SkillEffect> CastStartEffects => castStartEffects;
|
||||
public IReadOnlyList<SkillGemTriggeredEffectEntry> TriggeredEffects => triggeredEffects;
|
||||
public IReadOnlyList<SkillTriggeredEffectEntry> TriggeredEffects => triggeredEffects;
|
||||
public IReadOnlyList<AbnormalityData> SelfAbnormalities => selfAbnormalities;
|
||||
public IReadOnlyList<SkillGemTriggeredAbnormalityEntry> OnHitAbnormalities => onHitAbnormalities;
|
||||
|
||||
|
||||
@@ -321,15 +321,22 @@ namespace Colosseum.Skills
|
||||
if (destination == null)
|
||||
return;
|
||||
|
||||
if (baseSkill != null && baseSkill.Effects != null)
|
||||
if (baseSkill != null && baseSkill.TriggeredEffects != null)
|
||||
{
|
||||
for (int i = 0; i < baseSkill.Effects.Count; i++)
|
||||
for (int i = 0; i < baseSkill.TriggeredEffects.Count; i++)
|
||||
{
|
||||
SkillEffect effect = baseSkill.Effects[i];
|
||||
if (effect == null)
|
||||
SkillTriggeredEffectEntry entry = baseSkill.TriggeredEffects[i];
|
||||
if (entry == null || entry.Effects == null)
|
||||
continue;
|
||||
|
||||
AddTriggeredEffect(destination, i, effect);
|
||||
for (int j = 0; j < entry.Effects.Count; j++)
|
||||
{
|
||||
SkillEffect effect = entry.Effects[j];
|
||||
if (effect == null)
|
||||
continue;
|
||||
|
||||
AddTriggeredEffect(destination, entry.TriggerIndex, effect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -344,7 +351,7 @@ namespace Colosseum.Skills
|
||||
|
||||
for (int j = 0; j < gem.TriggeredEffects.Count; j++)
|
||||
{
|
||||
SkillGemTriggeredEffectEntry entry = gem.TriggeredEffects[j];
|
||||
SkillTriggeredEffectEntry entry = gem.TriggeredEffects[j];
|
||||
if (entry == null || entry.Effects == null)
|
||||
continue;
|
||||
|
||||
@@ -499,7 +506,7 @@ namespace Colosseum.Skills
|
||||
{
|
||||
if (baseSkill.CastStartEffects != null && CheckEffectsForTargetType(baseSkill.CastStartEffects, type))
|
||||
return true;
|
||||
if (baseSkill.Effects != null && CheckEffectsForTargetType(baseSkill.Effects, type))
|
||||
if (baseSkill.TriggeredEffects != null && CheckTriggeredEntriesForTargetType(baseSkill.TriggeredEffects, type))
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -514,17 +521,22 @@ namespace Colosseum.Skills
|
||||
if (gem.CastStartEffects != null && CheckEffectsForTargetType(gem.CastStartEffects, type))
|
||||
return true;
|
||||
|
||||
if (gem.TriggeredEffects != null)
|
||||
{
|
||||
for (int j = 0; j < gem.TriggeredEffects.Count; j++)
|
||||
{
|
||||
SkillGemTriggeredEffectEntry entry = gem.TriggeredEffects[j];
|
||||
if (entry == null || entry.Effects == null) continue;
|
||||
if (gem.TriggeredEffects != null && CheckTriggeredEntriesForTargetType(gem.TriggeredEffects, type))
|
||||
return true;
|
||||
}
|
||||
|
||||
if (CheckEffectsForTargetType(entry.Effects, type))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool CheckTriggeredEntriesForTargetType(IReadOnlyList<SkillTriggeredEffectEntry> entries, TargetType type)
|
||||
{
|
||||
if (entries == null) return false;
|
||||
|
||||
for (int i = 0; i < entries.Count; i++)
|
||||
{
|
||||
SkillTriggeredEffectEntry entry = entries[i];
|
||||
if (entry != null && CheckEffectsForTargetType(entry.Effects, type))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
Reference in New Issue
Block a user