feat: 드로그 보스 AI 및 런타임 상태 구조 재구성
- 드로그 전투 컨텍스트를 BossBehaviorRuntimeState 중심 구조로 정리하고 BossEnemy, 패턴 액션, 조건 노드가 마지막 실행 결과와 phase 상태를 직접 사용하도록 갱신 - BT_Drog와 재빌드 에디터 스크립트를 확장해 phase 전환, 집행 결과 분기, 거리/쿨타임 기반 패턴 선택을 드로그 전용 자산과 노드 파라미터로 재구성 - 드로그 패턴/스킬/이펙트/애니메이션 플레이스홀더 자산을 재생성하고 보스 프리팹이 새 런타임 상태 및 등록 클립 구성을 참조하도록 정리
This commit is contained in:
923
Assets/_Game/Scripts/Editor/RebuildDrogCombatAssets.cs
Normal file
923
Assets/_Game/Scripts/Editor/RebuildDrogCombatAssets.cs
Normal file
@@ -0,0 +1,923 @@
|
||||
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
|
||||
{
|
||||
/// <summary>
|
||||
/// 드로그 기획/패턴 문서를 기준으로 스킬/이펙트/패턴 플레이스홀더 자산을 재구성합니다.
|
||||
/// 애니메이션이 아직 확정되지 않은 단계에서도 BT와 데이터 연결을 먼저 맞추는 용도입니다.
|
||||
/// </summary>
|
||||
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<AbnormalityData>(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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 폴더가 없으면 생성합니다.
|
||||
/// </summary>
|
||||
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));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 지정 경로의 ScriptableObject를 읽거나 새로 생성합니다.
|
||||
/// </summary>
|
||||
private static T LoadOrCreateAsset<T>(string path) where T : ScriptableObject
|
||||
{
|
||||
T asset = AssetDatabase.LoadAssetAtPath<T>(path);
|
||||
if (asset != null)
|
||||
return asset;
|
||||
|
||||
asset = ScriptableObject.CreateInstance<T>();
|
||||
asset.name = Path.GetFileNameWithoutExtension(path);
|
||||
AssetDatabase.CreateAsset(asset, path);
|
||||
return asset;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 드로그 스킬 플레이스홀더용 빈 애니메이션 클립을 보장합니다.
|
||||
/// </summary>
|
||||
private static AnimationClip EnsurePlaceholderClip(string path)
|
||||
{
|
||||
AnimationClip clip = AssetDatabase.LoadAssetAtPath<AnimationClip>(path);
|
||||
if (clip != null)
|
||||
return clip;
|
||||
|
||||
clip = new AnimationClip
|
||||
{
|
||||
name = Path.GetFileNameWithoutExtension(path),
|
||||
frameRate = 60f,
|
||||
};
|
||||
|
||||
AssetDatabase.CreateAsset(clip, path);
|
||||
return clip;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Effect/Skill/Pattern의 공통 Object 리스트를 설정합니다.
|
||||
/// </summary>
|
||||
private static void SetObjectList(SerializedObject serializedObject, string propertyName, IReadOnlyList<UnityEngine.Object> 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];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 범위형 효과의 공통 판정 설정을 적용합니다.
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 범위 피해 효과를 생성하거나 갱신합니다.
|
||||
/// </summary>
|
||||
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<DamageEffect>(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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 범위 다운 효과를 생성하거나 갱신합니다.
|
||||
/// </summary>
|
||||
private static DownEffect CreateDownEffect(
|
||||
string path,
|
||||
float duration,
|
||||
AreaShapeType areaShape,
|
||||
float areaRadius,
|
||||
float fanOriginDistance,
|
||||
float fanRadius,
|
||||
float fanHalfAngle,
|
||||
AreaCenterType areaCenter)
|
||||
{
|
||||
DownEffect effect = LoadOrCreateAsset<DownEffect>(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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 범위 넉백 효과를 생성하거나 갱신합니다.
|
||||
/// </summary>
|
||||
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<KnockbackEffect>(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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 다운 대상 추가 피해 효과를 생성하거나 갱신합니다.
|
||||
/// </summary>
|
||||
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<HitReactionDamageEffect>(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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 빈 애니메이션 상태에서도 즉시 발동 가능한 드로그 스킬 플레이스홀더를 생성하거나 갱신합니다.
|
||||
/// </summary>
|
||||
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<SkillData>(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<UnityEngine.Object>();
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 드로그 보스 패턴 자산을 생성하거나 갱신합니다.
|
||||
/// </summary>
|
||||
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<BossPatternData>(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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 단일 패턴 스텝 데이터를 SerializedProperty에 기록합니다.
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 패턴 스텝 정의를 간단히 구성하기 위한 헬퍼입니다.
|
||||
/// </summary>
|
||||
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,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user