diff --git a/Assets/Scripts/Skills/Data.meta b/Assets/Scripts/Skills/Data.meta
new file mode 100644
index 0000000..1d83968
--- /dev/null
+++ b/Assets/Scripts/Skills/Data.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 515f8dec2e303b44495b79dc3bb4f5ec
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/Skills/Data/ActiveSkillData.cs b/Assets/Scripts/Skills/Data/ActiveSkillData.cs
new file mode 100644
index 0000000..973022a
--- /dev/null
+++ b/Assets/Scripts/Skills/Data/ActiveSkillData.cs
@@ -0,0 +1,115 @@
+using UnityEngine;
+
+namespace EerieVillage.Skills
+{
+ ///
+ /// 액티브 스킬 ScriptableObject 데이터.
+ /// BT12-Dev v1 §2-2 정합. ActiveCategory 6종·ActiveTrigger 3종.
+ ///
+ [CreateAssetMenu(fileName = "Active_", menuName = "EerieVillage/Skills/Active")]
+ public class ActiveSkillData : SkillDataAsset
+ {
+ [Header("액티브 전용")]
+ /// 카테고리 (A~F) — Projectile·MeleeArea·PlacementPersistent·Minion·Debuff·SpecialJudge
+ public ActiveCategory Category;
+ /// 발동 트리거 — OnTime·OnHit·OnKill
+ public ActiveTrigger Trigger;
+
+ [Tooltip("Lv.1 기본 쿨다운 (초). balance/01 v0.2 BaseCooldown 1.5s 참조")]
+ public float BaseCooldown = 1.5f;
+
+ [Tooltip("Lv.1 기본 대미지 (쿼터 단위). balance/01 v0.2 BaseDamage 10 참조")]
+ public int BaseDamage = 10;
+
+ [Tooltip("판정 박스 기본 크기 (A·B 카테고리). balance/01 v0.2 AttackBoxSize 1.5x1.0 참조")]
+ public Vector2 HitboxSize = new Vector2(1.5f, 1.0f);
+
+ [Tooltip("투사체 발사 오프셋 거리 (A 카테고리)")]
+ public float OffsetDistance = 0.5f;
+
+ [Tooltip("투사체 전용 (A 카테고리). 궤적 타입 — Line·Homing·Arc")]
+ public ProjectileTrajectory Trajectory = ProjectileTrajectory.Line;
+
+ [Tooltip("소환 전용 (D 카테고리). 소환물 프리팹 참조")]
+ public GameObject MinionPrefab;
+
+ [Tooltip("연쇄 타격 횟수 (Homing 등 연쇄 계열)")]
+ public int ChainCount = 0;
+
+ [Tooltip("DoT 지속 시간 (초)")]
+ public float DotDuration = 0f;
+
+ [Tooltip("DoT 타격 간격 (초)")]
+ public float DotInterval = 0.5f;
+
+ [Tooltip("기절 지속 시간 (초)")]
+ public float StunDuration = 0f;
+
+ [Tooltip("감속 지속 시간 (초)")]
+ public float SlowDuration = 0f;
+
+ [Tooltip("감속 배율 (0.5 = 50% 감속)")]
+ [Range(0f, 1f)]
+ public float SlowMultiplier = 0.5f;
+
+ [Tooltip("넉백 힘")]
+ public float KnockbackForce = 0f;
+
+ [Tooltip("동시 최대 인스턴스 수 (C·D 카테고리)")]
+ public int MaxConcurrent = 1;
+
+ [Tooltip("소환물 수명 (초, D 카테고리)")]
+ public float MinionLifetime = 10f;
+
+ [Tooltip("오라 주기 (초, E 카테고리 아우라형)")]
+ public float AuraTickInterval = 0.5f;
+
+ [Tooltip("오라 반경 (E 카테고리 아우라형)")]
+ public float AuraRadius = 3f;
+
+ [Tooltip("크리티컬 대미지 배율")]
+ public float CritDamageMultiplier = 2.0f;
+
+ [Tooltip("무적 프레임 부여 시간 (초, F 카테고리)")]
+ public float IFrameDuration = 0f;
+
+ [Tooltip("상태이상 스택 한도 (E 카테고리)")]
+ public int DebuffStackLimit = 3;
+
+ [Tooltip("확률 판정 전용 (F 카테고리). 발동 확률 0~1")]
+ [Range(0f, 1f)]
+ public float FireProbability = 1.0f;
+ }
+
+ ///
+ /// 액티브 스킬 카테고리 (A~F). BT12-Dev v1 §2-2 정합.
+ ///
+ public enum ActiveCategory
+ {
+ /// A. 투사체
+ Projectile,
+ /// B. 근접·범위
+ MeleeArea,
+ /// C. 설치·지속
+ PlacementPersistent,
+ /// D. 소환
+ Minion,
+ /// E. 상태이상
+ Debuff,
+ /// F. 특수 판정
+ SpecialJudge
+ }
+
+ ///
+ /// 투사체 궤적 타입. BT12-Dev v1 §2-2 정합.
+ ///
+ public enum ProjectileTrajectory
+ {
+ /// 직선
+ Line,
+ /// 유도
+ Homing,
+ /// 곡선
+ Arc
+ }
+}
diff --git a/Assets/Scripts/Skills/Data/ActiveSkillData.cs.meta b/Assets/Scripts/Skills/Data/ActiveSkillData.cs.meta
new file mode 100644
index 0000000..6d0f283
--- /dev/null
+++ b/Assets/Scripts/Skills/Data/ActiveSkillData.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: 69566f3f65e99394d8a0ccd0b395ac77
\ No newline at end of file
diff --git a/Assets/Scripts/Skills/Data/AwakeningSkillData.cs b/Assets/Scripts/Skills/Data/AwakeningSkillData.cs
new file mode 100644
index 0000000..d5d7d9d
--- /dev/null
+++ b/Assets/Scripts/Skills/Data/AwakeningSkillData.cs
@@ -0,0 +1,28 @@
+using UnityEngine;
+
+namespace EerieVillage.Skills
+{
+ ///
+ /// 각성 스킬 ScriptableObject 데이터.
+ /// BT12-Dev v1 §2-2 정합. Phase 2 범위 외 stub — Phase 2-C 이후 필드 확장 예정.
+ ///
+ [CreateAssetMenu(fileName = "Awakening_", menuName = "EerieVillage/Skills/Awakening")]
+ public class AwakeningSkillData : SkillDataAsset
+ {
+ [Header("각성 전용")]
+ /// 진화 패턴 (1 스케일업 · 2 새효과 · 3 다중 발동 · 4 광역 확산)
+ public AwakeningPattern Pattern;
+
+ /// 진화 대상 원 액티브 데이터
+ public ActiveSkillData OriginalActive;
+
+ /// 필요 패시브 후보 (1개 이상 보유로 조건 충족)
+ public PassiveSkillData[] RequiredPassives;
+
+ [Tooltip("각성 후 기본 대미지 (원 액티브 Lv.5 대비 2~3배 권장, balance/01 v0.2 §3 참조)")]
+ public int AwakeningBaseDamage = 25;
+
+ [Tooltip("각성 연출 프리팹 (풀스크린)")]
+ public GameObject AwakeningEffectPrefab;
+ }
+}
diff --git a/Assets/Scripts/Skills/Data/AwakeningSkillData.cs.meta b/Assets/Scripts/Skills/Data/AwakeningSkillData.cs.meta
new file mode 100644
index 0000000..dfeb77b
--- /dev/null
+++ b/Assets/Scripts/Skills/Data/AwakeningSkillData.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: a27193dd65b4d8b4ea089434bcd4f012
\ No newline at end of file
diff --git a/Assets/Scripts/Skills/Data/PassiveSkillData.cs b/Assets/Scripts/Skills/Data/PassiveSkillData.cs
new file mode 100644
index 0000000..7d37c95
--- /dev/null
+++ b/Assets/Scripts/Skills/Data/PassiveSkillData.cs
@@ -0,0 +1,64 @@
+using UnityEngine;
+
+namespace EerieVillage.Skills
+{
+ ///
+ /// 패시브 스킬 ScriptableObject 데이터.
+ /// BT12-Dev v1 §2-2 정합. Phase 2 범위 외 stub — Phase 2-C 이후 필드 확장 예정.
+ ///
+ [CreateAssetMenu(fileName = "Passive_", menuName = "EerieVillage/Skills/Passive")]
+ public class PassiveSkillData : SkillDataAsset
+ {
+ [Header("패시브 전용")]
+ /// 패시브 카테고리 (P-A~P-E)
+ public PassiveCategory Category;
+
+ /// 상시 적용 여부 (true = 장착 즉시 효과 · false = 조건부)
+ public bool IsAlwaysOn = true;
+
+ [Tooltip("대상 스탯 (P-A·P-C 전용)")]
+ public StatType TargetStat;
+
+ [Tooltip("Lv.1 기본 보정값 (대미지 배율·하트 수·확률 등)")]
+ public float BaseModifierValue;
+
+ [Tooltip("조건부 패시브 (P11·P16·P17 등) 트리거 종류")]
+ public PassiveTriggerKind TriggerKind;
+ }
+
+ ///
+ /// 패시브 카테고리 (P-A~P-E). BT12-Dev v1 §2-2 정합.
+ ///
+ public enum PassiveCategory
+ {
+ /// P-A. 스탯 상승
+ StatUp,
+ /// P-B. 주기 단축·증폭
+ CycleAmplify,
+ /// P-C. 생존 강화
+ Survival,
+ /// P-D. 회복
+ Recovery,
+ /// P-E. 자원 확장
+ ResourceExpand
+ }
+
+ ///
+ /// 대상 스탯 종류. BT12-Dev v1 §2-2 정합.
+ ///
+ public enum StatType
+ {
+ Damage,
+ AttackSpeed,
+ MoveSpeed,
+ MaxHearts,
+ CritChance,
+ CritDamage,
+ DamageReduction,
+ Evasion,
+ IFrameExtend,
+ JumpHeight,
+ XPGain,
+ TreasureFind
+ }
+}
diff --git a/Assets/Scripts/Skills/Data/PassiveSkillData.cs.meta b/Assets/Scripts/Skills/Data/PassiveSkillData.cs.meta
new file mode 100644
index 0000000..dc2bb20
--- /dev/null
+++ b/Assets/Scripts/Skills/Data/PassiveSkillData.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: 26160e8ad6b27fd429558597e70481ba
\ No newline at end of file
diff --git a/Assets/Scripts/Skills/Data/SkillDataAsset.cs b/Assets/Scripts/Skills/Data/SkillDataAsset.cs
new file mode 100644
index 0000000..e51aaa0
--- /dev/null
+++ b/Assets/Scripts/Skills/Data/SkillDataAsset.cs
@@ -0,0 +1,61 @@
+using UnityEngine;
+
+namespace EerieVillage.Skills
+{
+ ///
+ /// 모든 스킬 데이터 asset의 공통 base.
+ /// BT12-Dev v1 §2-2 정합.
+ ///
+ public abstract class SkillDataAsset : ScriptableObject
+ {
+ [Header("공통")]
+ /// 카드 식별자 — "A01"·"P12"·"AW19" (CSV v0.3 ID 컬럼)
+ public string CardId;
+ /// 플레이어 표시 이름 (한글) — "진언부(眞言符)"
+ public string DisplayName;
+ /// 영문 표시명 — CSV 2번째 컬럼
+ public string EnglishName;
+ /// UI용 아이콘
+ public Sprite Icon;
+ /// 플레이어 표시 툴팁
+ [TextArea(2, 4)]
+ public string Description;
+ /// 속성 태그 (Flags) — [물리]·[화염] 등
+ public AttributeTag AttributeTags;
+ /// 타입 태그 (Flags) — [근접]·[원거리] 등
+ public TypeTag TypeTags;
+
+ /// 최대 스택 레벨. 기본 5. Upgrade() 상한 기준.
+ [Tooltip("최대 스택 레벨 (1~5). 기본값 5.")]
+ public int maxLevel = 5;
+ }
+
+ ///
+ /// 속성 태그 (Flags enum). BT12-Dev v1 §2-2 정합.
+ ///
+ [System.Flags]
+ public enum AttributeTag
+ {
+ None = 0,
+ Physical = 1 << 0,
+ Fire = 1 << 1,
+ Frost = 1 << 2,
+ Lightning = 1 << 3,
+ Dark = 1 << 4
+ }
+
+ ///
+ /// 타입 태그 (Flags enum). BT12-Dev v1 §2-2 정합.
+ ///
+ [System.Flags]
+ public enum TypeTag
+ {
+ None = 0,
+ Melee = 1 << 0,
+ Ranged = 1 << 1,
+ Area = 1 << 2,
+ Persistent = 1 << 3,
+ Recovery = 1 << 4,
+ Defense = 1 << 5
+ }
+}
diff --git a/Assets/Scripts/Skills/Data/SkillDataAsset.cs.meta b/Assets/Scripts/Skills/Data/SkillDataAsset.cs.meta
new file mode 100644
index 0000000..862681d
--- /dev/null
+++ b/Assets/Scripts/Skills/Data/SkillDataAsset.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: e745a0332e778014fb747e65ab25a5df
\ No newline at end of file
diff --git a/Assets/Scripts/Skills/Events.meta b/Assets/Scripts/Skills/Events.meta
new file mode 100644
index 0000000..5cd442a
--- /dev/null
+++ b/Assets/Scripts/Skills/Events.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 3f85e7a7bd0c8cd49bc6308aee2d80d7
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/Skills/Events/SkillFireEvent.cs b/Assets/Scripts/Skills/Events/SkillFireEvent.cs
new file mode 100644
index 0000000..4db1477
--- /dev/null
+++ b/Assets/Scripts/Skills/Events/SkillFireEvent.cs
@@ -0,0 +1,51 @@
+using Platformer.Core;
+
+namespace EerieVillage.Skills
+{
+ ///
+ /// 스킬 발동 이벤트. Simulation.Event<T> 계승.
+ /// ActiveSkillRuntime.Fire()에서 Simulation.Schedule<SkillFireEvent>() 호출.
+ /// BT12-Dev v1 §3-4 정합.
+ ///
+ /// Phase 2-A: Execute = stub (카테고리 분기 구조만 명시).
+ /// Phase 2-B: 카테고리별 실 발동기 (ProjectileSpawner·AttackHitbox 등) 연결 예정.
+ ///
+ public class SkillFireEvent : Simulation.Event
+ {
+ /// 발동 요청한 액티브 스킬 런타임
+ public ActiveSkillRuntime Runtime;
+
+ /// 플레이어 스킬 인벤토리 (Stats·위치·방향 조회 경로)
+ public PlayerSkillInventory Inventory;
+
+ public override void Execute()
+ {
+ if (Runtime == null) return;
+
+ // Phase 2-B 카테고리별 실 발동기 호출 예정 영역
+ // 현재 Phase 2-A = 구조 stub만 배치.
+ //
+ // switch (Runtime.ActiveData.Category)
+ // {
+ // case ActiveCategory.Projectile:
+ // ProjectileSpawner.Spawn(Runtime, Inventory); break;
+ // case ActiveCategory.MeleeArea:
+ // AttackHitbox.Fire(Runtime, Inventory); break;
+ // case ActiveCategory.PlacementPersistent:
+ // AuraZone.Place(Runtime, Inventory); break;
+ // case ActiveCategory.Minion:
+ // MinionSpawner.Spawn(Runtime, Inventory); break;
+ // case ActiveCategory.Debuff:
+ // DebuffApplier.Apply(Runtime, Inventory); break;
+ // case ActiveCategory.SpecialJudge:
+ // SpecialJudgeHandler.Execute(Runtime, Inventory); break;
+ // }
+ }
+
+ internal override void Cleanup()
+ {
+ Runtime = null;
+ Inventory = null;
+ }
+ }
+}
diff --git a/Assets/Scripts/Skills/Events/SkillFireEvent.cs.meta b/Assets/Scripts/Skills/Events/SkillFireEvent.cs.meta
new file mode 100644
index 0000000..b4f8d80
--- /dev/null
+++ b/Assets/Scripts/Skills/Events/SkillFireEvent.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: 7554b04b9e2dae24cab5b9443ddafa4b
\ No newline at end of file
diff --git a/Assets/Scripts/Skills/Interfaces.meta b/Assets/Scripts/Skills/Interfaces.meta
new file mode 100644
index 0000000..3a97455
--- /dev/null
+++ b/Assets/Scripts/Skills/Interfaces.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: d99e4b20f4f653b4c89e657df4e8af8c
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/Skills/Interfaces/IActiveSkill.cs b/Assets/Scripts/Skills/Interfaces/IActiveSkill.cs
new file mode 100644
index 0000000..103e009
--- /dev/null
+++ b/Assets/Scripts/Skills/Interfaces/IActiveSkill.cs
@@ -0,0 +1,43 @@
+using UnityEngine;
+
+namespace EerieVillage.Skills
+{
+ ///
+ /// 액티브 스킬 런타임 계약. 주기·이벤트 트리거로 효과 발동.
+ /// BT12-Dev v1 §2-1 정합.
+ ///
+ public interface IActiveSkill : ISkillRuntime
+ {
+ /// 기본 쿨다운(초). balance/01 v0.2 BaseCooldown 1.5s 기반
+ float BaseCooldown { get; }
+
+ /// 패시브 보정 후 실제 쿨다운 (연사술 P06 적용 결과)
+ float EffectiveCooldown { get; }
+
+ /// 현재 Cooldown 타이머(초). MonoBehaviour Update가 감산
+ float CooldownRemaining { get; }
+
+ /// 쿨다운 경과 또는 트리거 이벤트 시 호출. 효과 발동 Dispatch
+ void Fire();
+
+ /// 트리거 이벤트 구독 — OnHit·OnKill·OnTime 중 해당
+ ActiveTrigger Trigger { get; }
+
+ /// PlayerSkillInventory.Update가 매 프레임 호출. OnTime 트리거 타이머 감산
+ void Tick(float deltaTime);
+ }
+
+ ///
+ /// 액티브 발동 트리거 분류 — 카테고리·카드에 따라 결정.
+ /// BT12-Dev v1 §2-1 정합.
+ ///
+ public enum ActiveTrigger
+ {
+ /// 주기 타이머 (카테고리 A·B·C·D·E 기본)
+ OnTime,
+ /// 플레이어 피격 시 (카테고리 F·P11 호신부 등)
+ OnHit,
+ /// 적 처치 시 (P16 단전수련 등 패시브도 동일 enum 재사용)
+ OnKill
+ }
+}
diff --git a/Assets/Scripts/Skills/Interfaces/IActiveSkill.cs.meta b/Assets/Scripts/Skills/Interfaces/IActiveSkill.cs.meta
new file mode 100644
index 0000000..b55b84e
--- /dev/null
+++ b/Assets/Scripts/Skills/Interfaces/IActiveSkill.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: 5033e69ecbd4a384789b4e44953fc5ef
\ No newline at end of file
diff --git a/Assets/Scripts/Skills/Interfaces/IAwakeningSkill.cs b/Assets/Scripts/Skills/Interfaces/IAwakeningSkill.cs
new file mode 100644
index 0000000..db01896
--- /dev/null
+++ b/Assets/Scripts/Skills/Interfaces/IAwakeningSkill.cs
@@ -0,0 +1,39 @@
+namespace EerieVillage.Skills
+{
+ ///
+ /// 각성 스킬 런타임 계약. 원 액티브를 대체(또는 강화)하여 진화.
+ /// BT12-Dev v1 §2-1 정합.
+ ///
+ public interface IAwakeningSkill : ISkillRuntime
+ {
+ /// 진화 대상 원 액티브 데이터
+ ActiveSkillData OriginalActive { get; }
+
+ /// 필요 패시브 후보 (1개 이상 보유로 조건 충족)
+ PassiveSkillData[] RequiredPassives { get; }
+
+ ///
+ /// 각성 발동 시 1회 호출. 원 액티브 슬롯 점유 유지하며 진화 형태로 대체.
+ /// 진화 패턴 4종 중 하나의 효과를 적용.
+ ///
+ void Awaken(PlayerSkillInventory inventory);
+
+ /// 진화 패턴 (1 스케일업 · 2 새효과 · 3 다중 발동 · 4 광역 확산)
+ AwakeningPattern Pattern { get; }
+ }
+
+ ///
+ /// 각성 진화 패턴 (기획서 §4-2 4종). BT12-Dev v1 §2-1 정합.
+ ///
+ public enum AwakeningPattern
+ {
+ /// 1. 대미지·범위·속도 대폭 증가
+ ScaleUp,
+ /// 2. 새 효과 추가 (기존 유지 + 부가)
+ AddEffect,
+ /// 3. 발동 수 2배+
+ MultiFire,
+ /// 4. 화면 전체 확산
+ GlobalSpread
+ }
+}
diff --git a/Assets/Scripts/Skills/Interfaces/IAwakeningSkill.cs.meta b/Assets/Scripts/Skills/Interfaces/IAwakeningSkill.cs.meta
new file mode 100644
index 0000000..c6318c9
--- /dev/null
+++ b/Assets/Scripts/Skills/Interfaces/IAwakeningSkill.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: 1f4e6fa02aa3ae043852be5a72495cf8
\ No newline at end of file
diff --git a/Assets/Scripts/Skills/Interfaces/IPassiveSkill.cs b/Assets/Scripts/Skills/Interfaces/IPassiveSkill.cs
new file mode 100644
index 0000000..9380d07
--- /dev/null
+++ b/Assets/Scripts/Skills/Interfaces/IPassiveSkill.cs
@@ -0,0 +1,47 @@
+namespace EerieVillage.Skills
+{
+ ///
+ /// 패시브 스킬 런타임 계약. 장착 즉시 상시 적용 + 일부는 조건부 트리거.
+ /// BT12-Dev v1 §2-1 정합.
+ ///
+ public interface IPassiveSkill : ISkillRuntime
+ {
+ /// 상시 적용 여부 (true = 장착 즉시 효과 · false = 조건부)
+ bool IsAlwaysOn { get; }
+
+ ///
+ /// 장착·해제·Lv 업 시 인벤토리의 PlayerStats에 보정값 반영.
+ /// 스탯 상승형(P-A)·주기 단축(P-B)·생존 강화(P-C)·자원 확장(P-E)은 모두 이 경로.
+ ///
+ void ApplyModifier(PlayerStats stats);
+
+ /// 해제 시 보정값 원복
+ void RemoveModifier(PlayerStats stats);
+
+ ///
+ /// 조건부 패시브(P11 호신부·P16 단전수련 등) 전용.
+ /// OnPlayerDamaged·OnEnemyKilled·OnTimer 이벤트 구독 후 조건 충족 시 효과 발동.
+ ///
+ void OnTrigger(PassiveTriggerContext ctx);
+ }
+
+ /// 패시브 조건부 트리거 컨텍스트. BT12-Dev v1 §2-1 정합.
+ public struct PassiveTriggerContext
+ {
+ public PassiveTriggerKind Kind;
+ /// OnPlayerDamaged 전용
+ public float DamageTaken;
+ /// OnEnemyKilled 전용 (누적)
+ public int KillCount;
+ /// OnTimer 전용
+ public float TimeElapsed;
+ }
+
+ /// 패시브 트리거 종류. BT12-Dev v1 §2-1 정합.
+ public enum PassiveTriggerKind
+ {
+ OnPlayerDamaged,
+ OnEnemyKilled,
+ OnTimer
+ }
+}
diff --git a/Assets/Scripts/Skills/Interfaces/IPassiveSkill.cs.meta b/Assets/Scripts/Skills/Interfaces/IPassiveSkill.cs.meta
new file mode 100644
index 0000000..58fdc8a
--- /dev/null
+++ b/Assets/Scripts/Skills/Interfaces/IPassiveSkill.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: 4229a01f493ce9f468a65556dff9d273
\ No newline at end of file
diff --git a/Assets/Scripts/Skills/Interfaces/ISkillRuntime.cs b/Assets/Scripts/Skills/Interfaces/ISkillRuntime.cs
new file mode 100644
index 0000000..194f31d
--- /dev/null
+++ b/Assets/Scripts/Skills/Interfaces/ISkillRuntime.cs
@@ -0,0 +1,27 @@
+using UnityEngine;
+
+namespace EerieVillage.Skills
+{
+ ///
+ /// 모든 스킬 런타임의 공통 계약.
+ /// PlayerSkillInventory가 보유 스킬을 이 인터페이스 배열로 관리.
+ /// BT12-Dev v1 §2-1 정합.
+ ///
+ public interface ISkillRuntime
+ {
+ /// 참조하는 정적 데이터 (ScriptableObject)
+ SkillDataAsset Data { get; }
+
+ /// 현재 스택 레벨 (1~5). 5 도달 시 각성 조건 1 충족
+ int StackLevel { get; }
+
+ /// 스킬 장착·Lv 업 시 1회 호출 (런타임 초기화)
+ void OnEquip(PlayerSkillInventory inventory);
+
+ /// 스킬 해제 시 1회 호출 (각성 대체 등)
+ void OnUnequip();
+
+ /// 동일 카드 재픽으로 Lv N→N+1 업그레이드
+ void Upgrade();
+ }
+}
diff --git a/Assets/Scripts/Skills/Interfaces/ISkillRuntime.cs.meta b/Assets/Scripts/Skills/Interfaces/ISkillRuntime.cs.meta
new file mode 100644
index 0000000..107177d
--- /dev/null
+++ b/Assets/Scripts/Skills/Interfaces/ISkillRuntime.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: 44888b984c50f9f4d848b4d892c20be2
\ No newline at end of file
diff --git a/Assets/Scripts/Skills/Runtime.meta b/Assets/Scripts/Skills/Runtime.meta
new file mode 100644
index 0000000..4352891
--- /dev/null
+++ b/Assets/Scripts/Skills/Runtime.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 32ceb7645efbb5d489a2ab18dce6043a
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/Skills/Runtime/ActiveSkillRuntime.cs b/Assets/Scripts/Skills/Runtime/ActiveSkillRuntime.cs
new file mode 100644
index 0000000..d4eb7a7
--- /dev/null
+++ b/Assets/Scripts/Skills/Runtime/ActiveSkillRuntime.cs
@@ -0,0 +1,140 @@
+using UnityEngine;
+using Platformer.Core;
+
+namespace EerieVillage.Skills
+{
+ ///
+ /// 액티브 스킬 런타임 구현체. ActiveSkillData.Category 기준 단일 클래스.
+ /// BT12-Dev v1 §4-2 정합.
+ /// - OnTime: Tick()에서 쿨다운 감산 → Fire()
+ /// - OnHit·OnKill: PlayerSkillInventory 이벤트 핸들러에서 Fire() 직접 호출
+ /// - F(SpecialJudge): Fire() 내 확률 판정 선행
+ /// Phase 2-B에서 SkillFireEvent.Execute에 카테고리별 실 발동기 연결 예정.
+ ///
+ public class ActiveSkillRuntime : IActiveSkill
+ {
+ // --- ISkillRuntime ---
+ public SkillDataAsset Data => ActiveData;
+ public int StackLevel { get; private set; } = 1;
+
+ // --- IActiveSkill ---
+ public float BaseCooldown => ActiveData.BaseCooldown;
+
+ ///
+ /// 실제 쿨다운 = BaseCooldown * CooldownMultiplier(P06) / StackLevelFactor.
+ /// 하드캡 0.5s (balance/01 v0.2 §9).
+ ///
+ public float EffectiveCooldown =>
+ Mathf.Max(
+ BaseCooldown * _inventory.Stats.CooldownMultiplier / StackLevelFactor(StackLevel),
+ 0.5f
+ );
+
+ public float CooldownRemaining { get; private set; } = 0f;
+ public ActiveTrigger Trigger => ActiveData.Trigger;
+
+ // --- 내부 ---
+ public ActiveSkillData ActiveData { get; private set; }
+ private PlayerSkillInventory _inventory;
+
+ public ActiveSkillRuntime(ActiveSkillData data)
+ {
+ ActiveData = data;
+ }
+
+ public void OnEquip(PlayerSkillInventory inventory)
+ {
+ _inventory = inventory;
+ CooldownRemaining = ActiveData.BaseCooldown; // 첫 발동은 주기 후
+ }
+
+ public void OnUnequip()
+ {
+ // OnHit·OnKill 이벤트 구독 해제 — Phase 2-D 인벤토리 통합 시 구현
+ }
+
+ public void Upgrade()
+ {
+ if (StackLevel < ActiveData.maxLevel && StackLevel < 5)
+ StackLevel++;
+ // Lv.5 도달 시 각성 조건 1 충족 — Phase 2-D AwakeningManager 연결 예정
+ }
+
+ ///
+ /// PlayerSkillInventory.Update가 매 프레임 호출.
+ /// OnTime 트리거만 처리 (OnHit·OnKill은 인벤토리 이벤트 핸들러에서 Fire() 직접 호출).
+ ///
+ public void Tick(float deltaTime)
+ {
+ if (Trigger != ActiveTrigger.OnTime) return;
+
+ CooldownRemaining -= deltaTime;
+ if (CooldownRemaining <= 0f)
+ {
+ Fire();
+ // 누적 오버플로 방지 (BT7-Dev PlayerAttackTicker와 동일 패턴)
+ CooldownRemaining += EffectiveCooldown;
+ CooldownRemaining = Mathf.Max(CooldownRemaining, 0f);
+ }
+ }
+
+ ///
+ /// 스킬 효과 Dispatch. SkillFireEvent를 Simulation에 Schedule.
+ /// F(SpecialJudge) 카테고리는 확률 판정 선행.
+ /// Phase 2-B에서 SkillFireEvent.Execute에 실 발동기 연결.
+ ///
+ public void Fire()
+ {
+ // F 카테고리 확률 판정
+ if (ActiveData.Category == ActiveCategory.SpecialJudge)
+ {
+ if (Random.value > ActiveData.FireProbability) return;
+ }
+
+ var ev = Simulation.Schedule();
+ ev.Runtime = this;
+ ev.Inventory = _inventory;
+ }
+
+ ///
+ /// StackLevel에 따른 대미지 팩터 (balance/01 v0.2 §3).
+ /// Lv.1=1.0 · Lv.2=1.2 · Lv.3=1.4 · Lv.4=1.6 · Lv.5=2.0
+ ///
+ public static float StackLevelFactor(int lv)
+ {
+ return lv switch
+ {
+ 1 => 1.0f,
+ 2 => 1.2f,
+ 3 => 1.4f,
+ 4 => 1.6f,
+ 5 => 2.0f,
+ _ => 1.0f
+ };
+ }
+
+ ///
+ /// 유효 대미지 산출 (balance/01 v0.2 §3 대미지 공식).
+ /// effectiveDamage = BaseDamage * DamageMultiplier * StackLevelFactor * AttributeMultiplier
+ ///
+ public int CalculateEffectiveDamage()
+ {
+ if (_inventory == null) return ActiveData.BaseDamage;
+
+ var stats = _inventory.Stats;
+ float attrMult = 1.0f;
+ if (ActiveData.AttributeTags != AttributeTag.None &&
+ stats.AttributeMultiplier.TryGetValue(ActiveData.AttributeTags, out float v))
+ {
+ attrMult = v;
+ }
+
+ return Mathf.RoundToInt(
+ ActiveData.BaseDamage *
+ stats.DamageMultiplier *
+ StackLevelFactor(StackLevel) *
+ attrMult
+ );
+ }
+ }
+}
diff --git a/Assets/Scripts/Skills/Runtime/ActiveSkillRuntime.cs.meta b/Assets/Scripts/Skills/Runtime/ActiveSkillRuntime.cs.meta
new file mode 100644
index 0000000..c30483f
--- /dev/null
+++ b/Assets/Scripts/Skills/Runtime/ActiveSkillRuntime.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: 4e5a27b9347cd9e42944c4cef0f378d3
\ No newline at end of file
diff --git a/Assets/Scripts/Skills/Runtime/PlayerSkillInventory.cs b/Assets/Scripts/Skills/Runtime/PlayerSkillInventory.cs
new file mode 100644
index 0000000..33451cf
--- /dev/null
+++ b/Assets/Scripts/Skills/Runtime/PlayerSkillInventory.cs
@@ -0,0 +1,188 @@
+using System.Collections.Generic;
+using System.Linq;
+using UnityEngine;
+using Platformer.Mechanics;
+
+namespace EerieVillage.Skills
+{
+ ///
+ /// Player GameObject에 부착. 장착 스킬 슬롯·Lv·각성 상태 관리 중앙 컴포넌트.
+ /// BT12-Dev v1 §2-3·§4-3·§4-4 정합.
+ /// Phase 2-D에서 SkillCardPlaceholder·LevelUpManager와 정식 통합 예정.
+ ///
+ [RequireComponent(typeof(Health))]
+ public class PlayerSkillInventory : MonoBehaviour
+ {
+ [Header("슬롯 상한 (balance-designer 재확인 — VS 원작 6/6 참고)")]
+ public int ActiveSlotMax = 6;
+ public int PassiveSlotMax = 6;
+
+ // 장착 슬롯 (인덱스 = 슬롯 번호)
+ private readonly List _activeSkills = new List();
+ private readonly List _passiveSkills = new List();
+ private readonly List _awakenedSkills = new List();
+
+ // 각 카드 ID → 런타임 인스턴스 매핑 (재픽 시 Lv 업용)
+ private readonly Dictionary _cardIdToRuntime = new Dictionary();
+
+ // 적 처치 누적 (OnKill 트리거용)
+ private int _totalKillCount = 0;
+
+ // Health 참조 — OnEnable에서 이벤트 구독
+ private Health _health;
+
+ /// 통합 PlayerStats — 패시브 보정이 여기에 누적 적용
+ public PlayerStats Stats { get; private set; }
+
+ /// 적 처치·피격 이벤트 구독자 (OnKill·OnHit 트리거용)
+ public event System.Action OnEnemyKilled;
+ public event System.Action OnPlayerDamaged;
+
+ void Awake()
+ {
+ Stats = new PlayerStats();
+ PlayerStats.Current = Stats;
+ _health = GetComponent();
+ }
+
+ void OnEnable()
+ {
+ if (_health != null)
+ {
+ _health.OnDamagedEvent += OnPlayerDamagedHandler;
+ }
+ }
+
+ void OnDisable()
+ {
+ if (_health != null)
+ {
+ _health.OnDamagedEvent -= OnPlayerDamagedHandler;
+ }
+ }
+
+ void Update()
+ {
+ // 액티브 Cooldown 감산·OnTime 발동
+ foreach (var active in _activeSkills)
+ {
+ active.Tick(Time.deltaTime);
+ }
+
+ // 조건부 패시브 타이머 감산 (P17 감로수 등 OnTimer 계열)
+ foreach (var passive in _passiveSkills)
+ {
+ if (!passive.IsAlwaysOn)
+ {
+ passive.OnTrigger(new PassiveTriggerContext
+ {
+ Kind = PassiveTriggerKind.OnTimer,
+ TimeElapsed = Time.deltaTime
+ });
+ }
+ }
+ }
+
+ ///
+ /// 픽 UI 선택 결과 반영 — cardId로 SkillDataAsset 조회 후 런타임 생성·장착.
+ /// Phase 2-D에서 SkillCardPlaceholder·LevelUpManager 통합 hook 연결 예정.
+ ///
+ public bool AddSkillByCardId(string cardId)
+ {
+ if (string.IsNullOrEmpty(cardId)) return false;
+
+ // 재픽 = Lv 업
+ if (_cardIdToRuntime.TryGetValue(cardId, out var existing))
+ {
+ existing.Upgrade();
+ return true;
+ }
+
+ var data = SkillRuntimeFactory.Resolve(cardId);
+ if (data == null)
+ {
+ Debug.LogWarning($"[PlayerSkillInventory] CardId '{cardId}' 에 해당하는 SkillDataAsset을 찾을 수 없습니다.");
+ return false;
+ }
+
+ var runtime = SkillRuntimeFactory.Create(data);
+ if (runtime == null) return false;
+
+ if (runtime is IActiveSkill active)
+ {
+ if (_activeSkills.Count >= ActiveSlotMax)
+ {
+ Debug.LogWarning($"[PlayerSkillInventory] 액티브 슬롯 초과 (상한 {ActiveSlotMax}).");
+ return false;
+ }
+ runtime.OnEquip(this);
+ _activeSkills.Add(active);
+ _cardIdToRuntime[cardId] = runtime;
+ return true;
+ }
+
+ if (runtime is IPassiveSkill passive)
+ {
+ if (_passiveSkills.Count >= PassiveSlotMax)
+ {
+ Debug.LogWarning($"[PlayerSkillInventory] 패시브 슬롯 초과 (상한 {PassiveSlotMax}).");
+ return false;
+ }
+ runtime.OnEquip(this);
+ passive.ApplyModifier(Stats);
+ _passiveSkills.Add(passive);
+ _cardIdToRuntime[cardId] = runtime;
+ return true;
+ }
+
+ return false;
+ }
+
+ ///
+ /// 적 처치 이벤트 외부 호출 (EnemyController 사망 시 연결 예정 — Phase 2-D).
+ /// OnKill 트리거 액티브·패시브 발동.
+ ///
+ public void NotifyEnemyKilled(EnemyKillContext ctx)
+ {
+ _totalKillCount++;
+ OnEnemyKilled?.Invoke(ctx);
+
+ foreach (var active in _activeSkills.Where(a => a.Trigger == ActiveTrigger.OnKill))
+ active.Fire();
+
+ foreach (var passive in _passiveSkills.Where(p => !p.IsAlwaysOn))
+ passive.OnTrigger(new PassiveTriggerContext
+ {
+ Kind = PassiveTriggerKind.OnEnemyKilled,
+ KillCount = _totalKillCount
+ });
+ }
+
+ // Health.OnDamagedEvent 구독 핸들러
+ private void OnPlayerDamagedHandler(int damage)
+ {
+ OnPlayerDamaged?.Invoke(damage);
+
+ foreach (var active in _activeSkills.Where(a => a.Trigger == ActiveTrigger.OnHit))
+ active.Fire();
+
+ foreach (var passive in _passiveSkills.Where(p => !p.IsAlwaysOn))
+ passive.OnTrigger(new PassiveTriggerContext
+ {
+ Kind = PassiveTriggerKind.OnPlayerDamaged,
+ DamageTaken = damage
+ });
+ }
+
+ // --- 읽기 전용 접근자 (디버그·UI 용) ---
+ public IReadOnlyList ActiveSkills => _activeSkills;
+ public IReadOnlyList PassiveSkills => _passiveSkills;
+ }
+
+ /// 적 처치 컨텍스트 (Phase 2-D에서 EnemyController 참조 추가 예정)
+ public struct EnemyKillContext
+ {
+ public string EnemyId;
+ public int XPReward;
+ }
+}
diff --git a/Assets/Scripts/Skills/Runtime/PlayerSkillInventory.cs.meta b/Assets/Scripts/Skills/Runtime/PlayerSkillInventory.cs.meta
new file mode 100644
index 0000000..c3935e3
--- /dev/null
+++ b/Assets/Scripts/Skills/Runtime/PlayerSkillInventory.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: 3a17b9e800711bc49b02ea17cb0f459c
\ No newline at end of file
diff --git a/Assets/Scripts/Skills/Runtime/PlayerStats.cs b/Assets/Scripts/Skills/Runtime/PlayerStats.cs
new file mode 100644
index 0000000..a54b423
--- /dev/null
+++ b/Assets/Scripts/Skills/Runtime/PlayerStats.cs
@@ -0,0 +1,64 @@
+using System.Collections.Generic;
+
+namespace EerieVillage.Skills
+{
+ ///
+ /// 통합 플레이어 스탯. 패시브 보정이 모두 이 객체에 누적.
+ /// ActiveSkillRuntime의 EffectiveCooldown·EffectiveDamage 산출 입력.
+ /// BT12-Dev v1 §2-3 정합.
+ ///
+ public class PlayerStats
+ {
+ /// 전역 싱글톤 참조 (PlayerSkillInventory가 갱신). Phase 2-D 정식 통합 시 DI 전환 예정.
+ public static PlayerStats Current = new PlayerStats();
+
+ /// 대미지 배율 — P01 봉황격·P02~P05 속성 강화
+ public float DamageMultiplier = 1.0f;
+
+ /// 쿨다운 배율 — P06 연사술 (0.8 = 20% 단축). EffectiveCooldown = BaseCooldown * CooldownMultiplier
+ public float CooldownMultiplier = 1.0f;
+
+ /// 광역 배율 — P07 광역확장
+ public float AreaMultiplier = 1.0f;
+
+ /// 추가 투사체 수 — P08 투사체증폭
+ public int ExtraProjectiles = 0;
+
+ /// 크리티컬 확률 — P09
+ public float CritChance = 0f;
+
+ /// 크리티컬 대미지 배율 — P10
+ public float CritDamage = 1.5f;
+
+ /// 추가 최대 하트 수 — P12·P13
+ public int ExtraMaxHearts = 0;
+
+ /// 피해 감소율 — P14 부적방패
+ public float DamageReduction = 0f;
+
+ /// 회피 확률 — P15 회피술
+ public float EvasionChance = 0f;
+
+ /// i-frame 연장 시간 (초) — P15
+ public float IFrameExtend = 0f;
+
+ /// 이동속도 배율 — P18 질풍보
+ public float MoveSpeedMultiplier = 1.0f;
+
+ /// 경험치 배율 — P19 선견지명
+ public float XPMultiplier = 1.0f;
+
+ /// 보물 발견 보너스 — P20 재물복
+ public float TreasureFindBonus = 0f;
+
+ /// 속성별 대미지 배율 (AttributeTag → float)
+ public Dictionary AttributeMultiplier = new Dictionary
+ {
+ { AttributeTag.Physical, 1.0f },
+ { AttributeTag.Fire, 1.0f },
+ { AttributeTag.Frost, 1.0f },
+ { AttributeTag.Lightning, 1.0f },
+ { AttributeTag.Dark, 1.0f }
+ };
+ }
+}
diff --git a/Assets/Scripts/Skills/Runtime/PlayerStats.cs.meta b/Assets/Scripts/Skills/Runtime/PlayerStats.cs.meta
new file mode 100644
index 0000000..7fda927
--- /dev/null
+++ b/Assets/Scripts/Skills/Runtime/PlayerStats.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: 62d37c6ad2d082741b239d566491f2a0
\ No newline at end of file
diff --git a/Assets/Scripts/Skills/Runtime/SkillRuntimeFactory.cs b/Assets/Scripts/Skills/Runtime/SkillRuntimeFactory.cs
new file mode 100644
index 0000000..2bb72c7
--- /dev/null
+++ b/Assets/Scripts/Skills/Runtime/SkillRuntimeFactory.cs
@@ -0,0 +1,119 @@
+using System.Collections.Generic;
+using UnityEngine;
+
+namespace EerieVillage.Skills
+{
+ ///
+ /// SkillDataAsset 캐시 로드 + ISkillRuntime 인스턴스 생성 팩토리.
+ /// BT12-Dev v1 §3-3 정합.
+ /// - Resolve(cardId): Resources/Skills/ 에서 CardId 기준 조회
+ /// - Create(data): 타입 분기 후 런타임 인스턴스 반환
+ /// Phase 2-C에서 .asset 60종 추가 시 자동 적재.
+ ///
+ public static class SkillRuntimeFactory
+ {
+ private static Dictionary _cache;
+
+ ///
+ /// Resources/Skills/ 하위 SkillDataAsset 전체 캐시 적재.
+ /// 최초 Resolve 호출 시 자동 실행.
+ /// Phase 2-C .asset 추가 전까지 _cache.Count == 0 정상 상태.
+ ///
+ public static void EnsureLoaded()
+ {
+ if (_cache != null) return;
+
+ _cache = new Dictionary();
+ var assets = Resources.LoadAll("Skills");
+ if (assets == null) return;
+
+ foreach (var a in assets)
+ {
+ if (a != null && !string.IsNullOrEmpty(a.CardId) && !_cache.ContainsKey(a.CardId))
+ {
+ _cache[a.CardId] = a;
+ }
+ }
+
+ Debug.Log($"[SkillRuntimeFactory] SkillDataAsset {_cache.Count}종 캐시 적재 완료.");
+ }
+
+ ///
+ /// cardId로 SkillDataAsset 조회. 없으면 null 반환.
+ ///
+ public static SkillDataAsset Resolve(string cardId)
+ {
+ EnsureLoaded();
+ if (string.IsNullOrEmpty(cardId)) return null;
+ return _cache.TryGetValue(cardId, out var data) ? data : null;
+ }
+
+ ///
+ /// SkillDataAsset 타입 분기 후 ISkillRuntime 인스턴스 생성.
+ /// BT12-Dev v1 §3-3 Factory 패턴.
+ ///
+ public static ISkillRuntime Create(SkillDataAsset data)
+ {
+ if (data == null) return null;
+
+ return data switch
+ {
+ ActiveSkillData a => new ActiveSkillRuntime(a),
+ PassiveSkillData p => new PassiveSkillRuntimeStub(p),
+ AwakeningSkillData w => new AwakeningSkillRuntimeStub(w),
+ _ => null
+ };
+ }
+
+ /// 캐시 강제 리셋 (에디터 툴·테스트용)
+ public static void InvalidateCache()
+ {
+ _cache = null;
+ }
+ }
+
+ // -------------------------------------------------------------------------
+ // Phase 2 범위 외 stub 구현체
+ // PassiveSkillRuntime·AwakeningSkillRuntime은 Phase 2-C 이후 정식 구현 예정.
+ // -------------------------------------------------------------------------
+
+ ///
+ /// 패시브 스킬 런타임 stub. Phase 2-C에서 정식 구현으로 교체 예정.
+ ///
+ internal class PassiveSkillRuntimeStub : IPassiveSkill
+ {
+ private readonly PassiveSkillData _data;
+ public PassiveSkillRuntimeStub(PassiveSkillData data) { _data = data; }
+
+ public SkillDataAsset Data => _data;
+ public int StackLevel => 1;
+ public bool IsAlwaysOn => _data.IsAlwaysOn;
+
+ public void OnEquip(PlayerSkillInventory inventory) { }
+ public void OnUnequip() { }
+ public void Upgrade() { }
+ public void ApplyModifier(PlayerStats stats) { }
+ public void RemoveModifier(PlayerStats stats) { }
+ public void OnTrigger(PassiveTriggerContext ctx) { }
+ }
+
+ ///
+ /// 각성 스킬 런타임 stub. Phase 2-E에서 정식 구현으로 교체 예정.
+ ///
+ internal class AwakeningSkillRuntimeStub : IAwakeningSkill
+ {
+ private readonly AwakeningSkillData _data;
+ public AwakeningSkillRuntimeStub(AwakeningSkillData data) { _data = data; }
+
+ public SkillDataAsset Data => _data;
+ public int StackLevel => 1;
+ public ActiveSkillData OriginalActive => _data.OriginalActive;
+ public PassiveSkillData[] RequiredPassives => _data.RequiredPassives;
+ public AwakeningPattern Pattern => _data.Pattern;
+
+ public void OnEquip(PlayerSkillInventory inventory) { }
+ public void OnUnequip() { }
+ public void Upgrade() { }
+ public void Awaken(PlayerSkillInventory inventory) { }
+ }
+}
diff --git a/Assets/Scripts/Skills/Runtime/SkillRuntimeFactory.cs.meta b/Assets/Scripts/Skills/Runtime/SkillRuntimeFactory.cs.meta
new file mode 100644
index 0000000..3e6ee16
--- /dev/null
+++ b/Assets/Scripts/Skills/Runtime/SkillRuntimeFactory.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: ba6e221878bb29d48a49a0504036c2b8
\ No newline at end of file