using System;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;
using Colosseum.Abnormalities;
using Colosseum.AI;
using Colosseum.Skills;
using Colosseum.Skills.Effects;
namespace Colosseum.Editor
{
///
/// 드로그 기획/패턴 문서를 기준으로 스킬/이펙트/패턴 플레이스홀더 자산을 재구성합니다.
/// 애니메이션이 아직 확정되지 않은 단계에서도 BT와 데이터 연결을 먼저 맞추는 용도입니다.
///
public static class RebuildDrogCombatAssets
{
private const string AnimationsFolder = "Assets/_Game/Animations";
private const string SkillsFolder = "Assets/_Game/Data/Skills";
private const string PatternsFolder = "Assets/_Game/Data/Patterns";
private const string EffectsFolder = "Assets/_Game/Data/Skills/Effects";
private const string ExecutionTelegraphAbnormalityPath = "Assets/_Game/Data/Abnormalities/Data_Abnormality_Drog_집행준비.asset";
[MenuItem("Tools/Colosseum/Rebuild Drog Combat Assets")]
private static void Rebuild()
{
try
{
EnsureFolder("Assets/_Game");
EnsureFolder("Assets/_Game/Animations");
EnsureFolder("Assets/_Game/Data");
EnsureFolder("Assets/_Game/Data/Skills");
EnsureFolder("Assets/_Game/Data/Patterns");
EnsureFolder("Assets/_Game/Data/Skills/Effects");
AbnormalityData executionTelegraph = AssetDatabase.LoadAssetAtPath(ExecutionTelegraphAbnormalityPath);
if (executionTelegraph == null)
{
Debug.LogError($"[DrogCombatAssets] 집행 전조 이상상태를 찾을 수 없습니다: {ExecutionTelegraphAbnormalityPath}");
return;
}
AnimationClip combo1Clip = EnsurePlaceholderClip($"{AnimationsFolder}/Anim_Drog_연타1_0.anim");
AnimationClip combo2Clip = EnsurePlaceholderClip($"{AnimationsFolder}/Anim_Drog_연타2_0.anim");
AnimationClip slamClip = EnsurePlaceholderClip($"{AnimationsFolder}/Anim_Drog_강타_0.anim");
AnimationClip combo3Clip = EnsurePlaceholderClip($"{AnimationsFolder}/Anim_Drog_연타3_0.anim");
AnimationClip combo4Clip = EnsurePlaceholderClip($"{AnimationsFolder}/Anim_Drog_연타4_0.anim");
AnimationClip stompClip = EnsurePlaceholderClip($"{AnimationsFolder}/Anim_Drog_발구르기_0.anim");
AnimationClip leapPrepareClip = EnsurePlaceholderClip($"{AnimationsFolder}/Anim_Drog_도약_준비_0.anim");
AnimationClip leapAirClip = EnsurePlaceholderClip($"{AnimationsFolder}/Anim_Drog_도약_공중_0.anim");
AnimationClip leapLandingClip = EnsurePlaceholderClip($"{AnimationsFolder}/Anim_Drog_도약_착지_0.anim");
AnimationClip stepClip = EnsurePlaceholderClip($"{AnimationsFolder}/Anim_Drog_밟기_0.anim");
AnimationClip throwClip = EnsurePlaceholderClip($"{AnimationsFolder}/Anim_Drog_투척_0.anim");
AnimationClip roarClip = EnsurePlaceholderClip($"{AnimationsFolder}/Anim_Drog_포효_0.anim");
AnimationClip executionReadyClip = EnsurePlaceholderClip($"{AnimationsFolder}/Anim_Drog_집행_준비_0.anim");
AnimationClip executionHit1Clip = EnsurePlaceholderClip($"{AnimationsFolder}/Anim_Drog_집행_연타1_0.anim");
AnimationClip executionHit2Clip = EnsurePlaceholderClip($"{AnimationsFolder}/Anim_Drog_집행_연타2_0.anim");
AnimationClip executionHit3Clip = EnsurePlaceholderClip($"{AnimationsFolder}/Anim_Drog_집행_연타3_0.anim");
DamageEffect combo1Damage = CreateDamageEffect(
$"{EffectsFolder}/Data_SkillEffect_Drog_연타1_0_데미지.asset",
24f,
DamageType.Physical,
0.75f,
AreaShapeType.Fan,
3.25f,
1.25f,
3.25f,
42f,
AreaCenterType.Caster);
DamageEffect combo2Damage = CreateDamageEffect(
$"{EffectsFolder}/Data_SkillEffect_Drog_연타2_0_데미지.asset",
30f,
DamageType.Physical,
0.9f,
AreaShapeType.Fan,
3.5f,
1.35f,
3.5f,
46f,
AreaCenterType.Caster);
DamageEffect slamDamage = CreateDamageEffect(
$"{EffectsFolder}/Data_SkillEffect_Drog_강타_0_데미지.asset",
48f,
DamageType.Physical,
1.15f,
AreaShapeType.Fan,
3.4f,
1.2f,
3.4f,
32f,
AreaCenterType.Caster);
DownEffect slamDown = CreateDownEffect(
$"{EffectsFolder}/Data_SkillEffect_Drog_강타_1_다운.asset",
1.8f,
AreaShapeType.Fan,
3.4f,
1.2f,
3.4f,
32f,
AreaCenterType.Caster);
DamageEffect combo3Damage = CreateDamageEffect(
$"{EffectsFolder}/Data_SkillEffect_Drog_연타3_0_데미지.asset",
26f,
DamageType.Physical,
0.8f,
AreaShapeType.Fan,
3.6f,
1.3f,
3.6f,
55f,
AreaCenterType.Caster);
DamageEffect combo4Damage = CreateDamageEffect(
$"{EffectsFolder}/Data_SkillEffect_Drog_연타4_0_데미지.asset",
28f,
DamageType.Physical,
0.85f,
AreaShapeType.Fan,
3.8f,
1.35f,
3.8f,
58f,
AreaCenterType.Caster);
DamageEffect stompDamage = CreateDamageEffect(
$"{EffectsFolder}/Data_SkillEffect_Drog_발구르기_0_데미지.asset",
22f,
DamageType.Physical,
0.65f,
AreaShapeType.Sphere,
4.75f,
1f,
4.75f,
180f,
AreaCenterType.Caster);
KnockbackEffect stompKnockback = CreateKnockbackEffect(
$"{EffectsFolder}/Data_SkillEffect_Drog_발구르기_1_넉백.asset",
6f,
1.5f,
0.2f,
AreaShapeType.Sphere,
4.75f,
1f,
4.75f,
180f,
AreaCenterType.Caster);
DamageEffect leapLandingDamage = CreateDamageEffect(
$"{EffectsFolder}/Data_SkillEffect_Drog_도약_착지_0_데미지.asset",
34f,
DamageType.Physical,
0.95f,
AreaShapeType.Sphere,
4.2f,
1f,
4.2f,
180f,
AreaCenterType.Caster);
KnockbackEffect leapLandingKnockback = CreateKnockbackEffect(
$"{EffectsFolder}/Data_SkillEffect_Drog_도약_착지_1_넉백.asset",
8f,
2f,
0.25f,
AreaShapeType.Sphere,
4.2f,
1f,
4.2f,
180f,
AreaCenterType.Caster);
HitReactionDamageEffect stepDamage = CreateHitReactionDamageEffect(
$"{EffectsFolder}/Data_SkillEffect_Drog_밟기_0_피격가중데미지.asset",
52f,
DamageType.Physical,
1.1f,
1.6f,
AreaShapeType.Sphere,
2.8f,
1f,
2.8f,
180f,
AreaCenterType.Caster);
KnockbackEffect stepKnockback = CreateKnockbackEffect(
$"{EffectsFolder}/Data_SkillEffect_Drog_밟기_1_넉백.asset",
5f,
1f,
0.18f,
AreaShapeType.Sphere,
2.8f,
1f,
2.8f,
180f,
AreaCenterType.Caster);
DamageEffect throwDamage = CreateDamageEffect(
$"{EffectsFolder}/Data_SkillEffect_Drog_투척_0_데미지.asset",
28f,
DamageType.Physical,
0.7f,
AreaShapeType.Beam,
12f,
1.2f,
0.75f,
0f,
AreaCenterType.Caster);
DamageEffect executionHit1 = CreateDamageEffect(
$"{EffectsFolder}/Data_SkillEffect_Drog_집행_연타1_0_데미지.asset",
14f,
DamageType.Physical,
0.35f,
AreaShapeType.Sphere,
8.5f,
1f,
8.5f,
180f,
AreaCenterType.Caster);
DamageEffect executionHit2 = CreateDamageEffect(
$"{EffectsFolder}/Data_SkillEffect_Drog_집행_연타2_0_데미지.asset",
17f,
DamageType.Physical,
0.4f,
AreaShapeType.Sphere,
8.5f,
1f,
8.5f,
180f,
AreaCenterType.Caster);
DamageEffect executionHit3 = CreateDamageEffect(
$"{EffectsFolder}/Data_SkillEffect_Drog_집행_연타3_0_데미지.asset",
20f,
DamageType.Physical,
0.45f,
AreaShapeType.Sphere,
8.5f,
1f,
8.5f,
180f,
AreaCenterType.Caster);
SkillData combo1Skill = CreateSkill(
$"{SkillsFolder}/Data_Skill_Drog_연타1.asset",
"연타1",
"기본 루프의 첫 타격입니다.",
combo1Clip,
1f,
SkillCastTargetTrackingMode.FaceTarget,
false,
true,
false,
combo1Damage);
SkillData combo2Skill = CreateSkill(
$"{SkillsFolder}/Data_Skill_Drog_연타2.asset",
"연타2",
"기본 루프의 두 번째 타격입니다.",
combo2Clip,
1f,
SkillCastTargetTrackingMode.FaceTarget,
false,
true,
false,
combo2Damage);
SkillData slamSkill = CreateSkill(
$"{SkillsFolder}/Data_Skill_Drog_강타.asset",
"강타",
"정면 관리 실패를 응징하는 강한 일격입니다.",
slamClip,
1f,
SkillCastTargetTrackingMode.FaceTarget,
false,
true,
false,
slamDamage,
slamDown);
SkillData combo3Skill = CreateSkill(
$"{SkillsFolder}/Data_Skill_Drog_연타3.asset",
"연타3",
"강타로 이어지는 선행 타격입니다.",
combo3Clip,
1f,
SkillCastTargetTrackingMode.FaceTarget,
false,
true,
false,
combo3Damage);
SkillData combo4Skill = CreateSkill(
$"{SkillsFolder}/Data_Skill_Drog_연타4.asset",
"연타4",
"발구르기로 이어지는 압박용 선행 타격입니다.",
combo4Clip,
1f,
SkillCastTargetTrackingMode.FaceTarget,
false,
true,
false,
combo4Damage);
SkillData stompSkill = CreateSkill(
$"{SkillsFolder}/Data_Skill_Drog_발구르기.asset",
"발구르기",
"근접 측후방 전체를 흔드는 광역 압박입니다.",
stompClip,
1f,
SkillCastTargetTrackingMode.None,
false,
true,
false,
stompDamage,
stompKnockback);
SkillData leapPrepareSkill = CreateSkill(
$"{SkillsFolder}/Data_Skill_Drog_도약_준비.asset",
"도약 준비",
"원거리 이탈 대상에게 시선을 고정합니다.",
leapPrepareClip,
1f,
SkillCastTargetTrackingMode.FaceTarget,
false,
true,
false);
SkillData leapAirSkill = CreateSkill(
$"{SkillsFolder}/Data_Skill_Drog_도약_공중.asset",
"도약 공중",
"대상 위치로 도약하는 이동 스텝입니다.",
leapAirClip,
1f,
SkillCastTargetTrackingMode.MoveTowardTarget,
true,
false,
true);
SkillData leapLandingSkill = CreateSkill(
$"{SkillsFolder}/Data_Skill_Drog_도약_착지.asset",
"도약 착지",
"도약 종료 시 주변에 피해와 넉백을 줍니다.",
leapLandingClip,
1f,
SkillCastTargetTrackingMode.None,
false,
true,
false,
leapLandingDamage,
leapLandingKnockback);
SkillData stepSkill = CreateSkill(
$"{SkillsFolder}/Data_Skill_Drog_밟기.asset",
"밟기",
"다운된 대상을 후속 압박으로 처벌합니다.",
stepClip,
1f,
SkillCastTargetTrackingMode.FaceTarget,
false,
true,
false,
stepDamage,
stepKnockback);
SkillData throwSkill = CreateSkill(
$"{SkillsFolder}/Data_Skill_Drog_투척.asset",
"투척",
"부활 시전자나 원거리 대상을 견제하는 유틸리티 공격입니다.",
throwClip,
1f,
SkillCastTargetTrackingMode.FaceTarget,
false,
true,
false,
throwDamage);
SkillData roarSkill = CreateSkill(
$"{SkillsFolder}/Data_Skill_Drog_포효.asset",
"포효",
"Phase 3 진입을 알리는 전환 신호입니다.",
roarClip,
0.9f,
SkillCastTargetTrackingMode.None,
false,
true,
false);
SkillData executionReadySkill = CreateSkill(
$"{SkillsFolder}/Data_Skill_Drog_집행_준비.asset",
"집행 준비",
"집행 돌입 전 자세를 고정합니다.",
executionReadyClip,
0.85f,
SkillCastTargetTrackingMode.None,
false,
true,
false);
SkillData executionHit1Skill = CreateSkill(
$"{SkillsFolder}/Data_Skill_Drog_집행_연타1.asset",
"집행 연타1",
"집행의 첫 압박 타격입니다.",
executionHit1Clip,
1f,
SkillCastTargetTrackingMode.None,
false,
true,
false,
executionHit1);
SkillData executionHit2Skill = CreateSkill(
$"{SkillsFolder}/Data_Skill_Drog_집행_연타2.asset",
"집행 연타2",
"집행의 두 번째 압박 타격입니다.",
executionHit2Clip,
1.1f,
SkillCastTargetTrackingMode.None,
false,
true,
false,
executionHit2);
SkillData executionHit3Skill = CreateSkill(
$"{SkillsFolder}/Data_Skill_Drog_집행_연타3.asset",
"집행 연타3",
"집행의 세 번째 압박 타격입니다.",
executionHit3Clip,
1.2f,
SkillCastTargetTrackingMode.None,
false,
true,
false,
executionHit3);
CreatePattern(
$"{PatternsFolder}/Data_Pattern_Drog_연타1.asset",
"연타1",
PatternCategory.Basic,
false,
true,
TargetResolveMode.HighestThreat,
2.5f,
1,
false,
PatternStepDefinition.CreateSkillStep(combo1Skill));
CreatePattern(
$"{PatternsFolder}/Data_Pattern_Drog_연타2.asset",
"연타2",
PatternCategory.Basic,
false,
true,
TargetResolveMode.HighestThreat,
2.75f,
1,
false,
PatternStepDefinition.CreateSkillStep(combo2Skill));
CreatePattern(
$"{PatternsFolder}/Data_Pattern_Drog_연타3-강타.asset",
"연타3-강타",
PatternCategory.Basic,
false,
true,
TargetResolveMode.HighestThreat,
4.5f,
1,
false,
PatternStepDefinition.CreateSkillStep(combo3Skill),
PatternStepDefinition.CreateWaitStep(0.15f),
PatternStepDefinition.CreateSkillStep(slamSkill));
CreatePattern(
$"{PatternsFolder}/Data_Pattern_Drog_연타4-발구르기.asset",
"연타4-발구르기",
PatternCategory.Basic,
false,
true,
TargetResolveMode.HighestThreat,
5f,
1,
false,
PatternStepDefinition.CreateSkillStep(combo4Skill),
PatternStepDefinition.CreateWaitStep(0.15f),
PatternStepDefinition.CreateSkillStep(stompSkill));
CreatePattern(
$"{PatternsFolder}/Data_Pattern_Drog_밟기.asset",
"밟기",
PatternCategory.Punish,
false,
false,
TargetResolveMode.HighestThreat,
2.5f,
2,
false,
PatternStepDefinition.CreateSkillStep(stepSkill));
CreatePattern(
$"{PatternsFolder}/Data_Pattern_Drog_도약.asset",
"도약",
PatternCategory.Big,
false,
false,
TargetResolveMode.Mobility,
8f,
2,
false,
PatternStepDefinition.CreateSkillStep(leapPrepareSkill),
PatternStepDefinition.CreateWaitStep(0.1f),
PatternStepDefinition.CreateSkillStep(leapAirSkill),
PatternStepDefinition.CreateWaitStep(0.1f),
PatternStepDefinition.CreateSkillStep(leapLandingSkill));
CreatePattern(
$"{PatternsFolder}/Data_Pattern_Drog_투척.asset",
"투척",
PatternCategory.Basic,
false,
false,
TargetResolveMode.Utility,
10f,
2,
false,
PatternStepDefinition.CreateSkillStep(throwSkill));
CreatePattern(
$"{PatternsFolder}/Data_Pattern_Drog_집행.asset",
"집행",
PatternCategory.Big,
true,
false,
TargetResolveMode.HighestThreat,
45f,
3,
false,
PatternStepDefinition.CreateSkillStep(executionReadySkill),
PatternStepDefinition.CreateChargeWaitStep(2.25f, executionTelegraph, 0.1f, 2f),
PatternStepDefinition.CreateSkillStep(executionHit1Skill),
PatternStepDefinition.CreateWaitStep(0.65f),
PatternStepDefinition.CreateSkillStep(executionHit2Skill),
PatternStepDefinition.CreateWaitStep(0.45f),
PatternStepDefinition.CreateSkillStep(executionHit3Skill));
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
Debug.Log("[DrogCombatAssets] 드로그 스킬/이펙트/패턴 플레이스홀더 자산 재구성이 완료되었습니다.");
}
catch (Exception exception)
{
Debug.LogException(exception);
}
}
///
/// 폴더가 없으면 생성합니다.
///
private static void EnsureFolder(string path)
{
if (AssetDatabase.IsValidFolder(path))
return;
string parent = Path.GetDirectoryName(path)?.Replace('\\', '/');
if (string.IsNullOrEmpty(parent))
return;
EnsureFolder(parent);
AssetDatabase.CreateFolder(parent, Path.GetFileName(path));
}
///
/// 지정 경로의 ScriptableObject를 읽거나 새로 생성합니다.
///
private static T LoadOrCreateAsset(string path) where T : ScriptableObject
{
T asset = AssetDatabase.LoadAssetAtPath(path);
if (asset != null)
return asset;
asset = ScriptableObject.CreateInstance();
asset.name = Path.GetFileNameWithoutExtension(path);
AssetDatabase.CreateAsset(asset, path);
return asset;
}
///
/// 드로그 스킬 플레이스홀더용 빈 애니메이션 클립을 보장합니다.
///
private static AnimationClip EnsurePlaceholderClip(string path)
{
AnimationClip clip = AssetDatabase.LoadAssetAtPath(path);
if (clip != null)
return clip;
clip = new AnimationClip
{
name = Path.GetFileNameWithoutExtension(path),
frameRate = 60f,
};
AssetDatabase.CreateAsset(clip, path);
return clip;
}
///
/// Effect/Skill/Pattern의 공통 Object 리스트를 설정합니다.
///
private static void SetObjectList(SerializedObject serializedObject, string propertyName, IReadOnlyList values)
{
SerializedProperty listProperty = serializedObject.FindProperty(propertyName);
listProperty.arraySize = values != null ? values.Count : 0;
for (int i = 0; i < listProperty.arraySize; i++)
{
listProperty.GetArrayElementAtIndex(i).objectReferenceValue = values[i];
}
}
///
/// 범위형 효과의 공통 판정 설정을 적용합니다.
///
private static void ConfigureAreaEffect(
SerializedObject serializedObject,
AreaShapeType areaShape,
float areaRadius,
float fanOriginDistance,
float fanRadius,
float fanHalfAngle,
AreaCenterType areaCenter)
{
serializedObject.FindProperty("targetType").enumValueIndex = (int)TargetType.Area;
serializedObject.FindProperty("targetTeam").enumValueIndex = (int)TargetTeam.Enemy;
serializedObject.FindProperty("areaCenter").enumValueIndex = (int)areaCenter;
serializedObject.FindProperty("areaShape").enumValueIndex = (int)areaShape;
serializedObject.FindProperty("includeCasterInArea").boolValue = false;
serializedObject.FindProperty("areaRadius").floatValue = areaRadius;
serializedObject.FindProperty("fanOriginDistance").floatValue = fanOriginDistance;
serializedObject.FindProperty("fanRadius").floatValue = fanRadius;
serializedObject.FindProperty("fanHalfAngle").floatValue = fanHalfAngle;
}
///
/// 범위 피해 효과를 생성하거나 갱신합니다.
///
private static DamageEffect CreateDamageEffect(
string path,
float baseDamage,
DamageType damageType,
float statScaling,
AreaShapeType areaShape,
float areaRadius,
float fanOriginDistance,
float fanRadius,
float fanHalfAngle,
AreaCenterType areaCenter)
{
DamageEffect effect = LoadOrCreateAsset(path);
SerializedObject serializedObject = new SerializedObject(effect);
ConfigureAreaEffect(serializedObject, areaShape, areaRadius, fanOriginDistance, fanRadius, fanHalfAngle, areaCenter);
serializedObject.FindProperty("baseDamage").floatValue = baseDamage;
serializedObject.FindProperty("damageType").enumValueIndex = (int)damageType;
serializedObject.FindProperty("statScaling").floatValue = statScaling;
serializedObject.ApplyModifiedPropertiesWithoutUndo();
EditorUtility.SetDirty(effect);
return effect;
}
///
/// 범위 다운 효과를 생성하거나 갱신합니다.
///
private static DownEffect CreateDownEffect(
string path,
float duration,
AreaShapeType areaShape,
float areaRadius,
float fanOriginDistance,
float fanRadius,
float fanHalfAngle,
AreaCenterType areaCenter)
{
DownEffect effect = LoadOrCreateAsset(path);
SerializedObject serializedObject = new SerializedObject(effect);
ConfigureAreaEffect(serializedObject, areaShape, areaRadius, fanOriginDistance, fanRadius, fanHalfAngle, areaCenter);
serializedObject.FindProperty("duration").floatValue = duration;
serializedObject.ApplyModifiedPropertiesWithoutUndo();
EditorUtility.SetDirty(effect);
return effect;
}
///
/// 범위 넉백 효과를 생성하거나 갱신합니다.
///
private static KnockbackEffect CreateKnockbackEffect(
string path,
float force,
float upwardForce,
float duration,
AreaShapeType areaShape,
float areaRadius,
float fanOriginDistance,
float fanRadius,
float fanHalfAngle,
AreaCenterType areaCenter)
{
KnockbackEffect effect = LoadOrCreateAsset(path);
SerializedObject serializedObject = new SerializedObject(effect);
ConfigureAreaEffect(serializedObject, areaShape, areaRadius, fanOriginDistance, fanRadius, fanHalfAngle, areaCenter);
serializedObject.FindProperty("force").floatValue = force;
serializedObject.FindProperty("upwardForce").floatValue = upwardForce;
serializedObject.FindProperty("duration").floatValue = duration;
serializedObject.ApplyModifiedPropertiesWithoutUndo();
EditorUtility.SetDirty(effect);
return effect;
}
///
/// 다운 대상 추가 피해 효과를 생성하거나 갱신합니다.
///
private static HitReactionDamageEffect CreateHitReactionDamageEffect(
string path,
float baseDamage,
DamageType damageType,
float statScaling,
float downedDamageMultiplier,
AreaShapeType areaShape,
float areaRadius,
float fanOriginDistance,
float fanRadius,
float fanHalfAngle,
AreaCenterType areaCenter)
{
HitReactionDamageEffect effect = LoadOrCreateAsset(path);
SerializedObject serializedObject = new SerializedObject(effect);
ConfigureAreaEffect(serializedObject, areaShape, areaRadius, fanOriginDistance, fanRadius, fanHalfAngle, areaCenter);
serializedObject.FindProperty("baseDamage").floatValue = baseDamage;
serializedObject.FindProperty("damageType").enumValueIndex = (int)damageType;
serializedObject.FindProperty("statScaling").floatValue = statScaling;
serializedObject.FindProperty("bonusAgainstDownedTarget").boolValue = true;
serializedObject.FindProperty("downedDamageMultiplier").floatValue = downedDamageMultiplier;
serializedObject.ApplyModifiedPropertiesWithoutUndo();
EditorUtility.SetDirty(effect);
return effect;
}
///
/// 빈 애니메이션 상태에서도 즉시 발동 가능한 드로그 스킬 플레이스홀더를 생성하거나 갱신합니다.
///
private static SkillData CreateSkill(
string path,
string skillName,
string description,
AnimationClip clip,
float animationSpeed,
SkillCastTargetTrackingMode trackingMode,
bool useRootMotion,
bool ignoreRootMotionY,
bool jumpToTarget,
params SkillEffect[] castStartEffects)
{
SkillData skill = LoadOrCreateAsset(path);
SerializedObject serializedObject = new SerializedObject(skill);
serializedObject.FindProperty("skillName").stringValue = skillName;
serializedObject.FindProperty("description").stringValue = description;
serializedObject.FindProperty("skillRole").enumValueIndex = (int)SkillRoleType.Attack;
serializedObject.FindProperty("activationType").enumValueIndex = (int)SkillActivationType.Instant;
serializedObject.FindProperty("baseTypes").intValue = (int)SkillBaseType.Attack;
serializedObject.FindProperty("animationSpeed").floatValue = animationSpeed;
serializedObject.FindProperty("useRootMotion").boolValue = useRootMotion;
serializedObject.FindProperty("ignoreRootMotionY").boolValue = ignoreRootMotionY;
serializedObject.FindProperty("jumpToTarget").boolValue = jumpToTarget;
serializedObject.FindProperty("blockMovementWhileCasting").boolValue = true;
serializedObject.FindProperty("blockJumpWhileCasting").boolValue = true;
serializedObject.FindProperty("blockOtherSkillsWhileCasting").boolValue = true;
serializedObject.FindProperty("castTargetTrackingMode").enumValueIndex = (int)trackingMode;
serializedObject.FindProperty("castTargetRotationSpeed").floatValue = 12f;
serializedObject.FindProperty("castTargetStopDistance").floatValue = 2.5f;
serializedObject.FindProperty("cooldown").floatValue = 0f;
serializedObject.FindProperty("manaCost").floatValue = 0f;
serializedObject.FindProperty("maxGemSlotCount").intValue = 0;
serializedObject.FindProperty("triggeredEffects").arraySize = 0;
SetObjectList(serializedObject, "animationClips", new UnityEngine.Object[] { clip });
var effectObjects = new List();
if (castStartEffects != null)
{
for (int i = 0; i < castStartEffects.Length; i++)
{
if (castStartEffects[i] != null)
effectObjects.Add(castStartEffects[i]);
}
}
SetObjectList(serializedObject, "castStartEffects", effectObjects);
serializedObject.ApplyModifiedPropertiesWithoutUndo();
EditorUtility.SetDirty(skill);
return skill;
}
///
/// 드로그 보스 패턴 자산을 생성하거나 갱신합니다.
///
private static BossPatternData CreatePattern(
string path,
string patternName,
PatternCategory category,
bool isSignature,
bool isMelee,
TargetResolveMode targetMode,
float cooldown,
int minPhase,
bool skipJumpStepOnNoTarget,
params PatternStepDefinition[] stepDefinitions)
{
BossPatternData pattern = LoadOrCreateAsset(path);
SerializedObject serializedObject = new SerializedObject(pattern);
serializedObject.FindProperty("patternName").stringValue = patternName;
serializedObject.FindProperty("category").enumValueIndex = (int)category;
serializedObject.FindProperty("isSignature").boolValue = isSignature;
serializedObject.FindProperty("isMelee").boolValue = isMelee;
serializedObject.FindProperty("targetMode").enumValueIndex = (int)targetMode;
serializedObject.FindProperty("cooldown").floatValue = cooldown;
serializedObject.FindProperty("minPhase").intValue = minPhase;
serializedObject.FindProperty("skipJumpStepOnNoTarget").boolValue = skipJumpStepOnNoTarget;
SerializedProperty stepsProperty = serializedObject.FindProperty("steps");
stepsProperty.arraySize = stepDefinitions != null ? stepDefinitions.Length : 0;
for (int i = 0; i < stepsProperty.arraySize; i++)
{
ConfigurePatternStep(stepsProperty.GetArrayElementAtIndex(i), stepDefinitions[i]);
}
serializedObject.ApplyModifiedPropertiesWithoutUndo();
EditorUtility.SetDirty(pattern);
return pattern;
}
///
/// 단일 패턴 스텝 데이터를 SerializedProperty에 기록합니다.
///
private static void ConfigurePatternStep(SerializedProperty stepProperty, PatternStepDefinition definition)
{
stepProperty.FindPropertyRelative("Type").enumValueIndex = (int)definition.StepType;
stepProperty.FindPropertyRelative("Skill").objectReferenceValue = definition.Skill;
stepProperty.FindPropertyRelative("Duration").floatValue = definition.Duration;
SerializedProperty chargeDataProperty = stepProperty.FindPropertyRelative("ChargeData");
if (chargeDataProperty == null)
return;
chargeDataProperty.FindPropertyRelative("requiredDamageRatio").floatValue = definition.RequiredDamageRatio;
chargeDataProperty.FindPropertyRelative("telegraphAbnormality").objectReferenceValue = definition.TelegraphAbnormality;
chargeDataProperty.FindPropertyRelative("staggerDuration").floatValue = definition.StaggerDuration;
}
///
/// 패턴 스텝 정의를 간단히 구성하기 위한 헬퍼입니다.
///
private sealed class PatternStepDefinition
{
public PatternStepType StepType { get; private set; }
public SkillData Skill { get; private set; }
public float Duration { get; private set; }
public AbnormalityData TelegraphAbnormality { get; private set; }
public float RequiredDamageRatio { get; private set; }
public float StaggerDuration { get; private set; }
public static PatternStepDefinition CreateSkillStep(SkillData skill)
{
return new PatternStepDefinition
{
StepType = PatternStepType.Skill,
Skill = skill,
Duration = 0f,
};
}
public static PatternStepDefinition CreateWaitStep(float duration)
{
return new PatternStepDefinition
{
StepType = PatternStepType.Wait,
Duration = duration,
};
}
public static PatternStepDefinition CreateChargeWaitStep(float duration, AbnormalityData telegraphAbnormality, float requiredDamageRatio, float staggerDuration)
{
return new PatternStepDefinition
{
StepType = PatternStepType.ChargeWait,
Duration = duration,
TelegraphAbnormality = telegraphAbnormality,
RequiredDamageRatio = requiredDamageRatio,
StaggerDuration = staggerDuration,
};
}
}
}
}