diff --git a/Assets/0.SCENE/MainGame.unity b/Assets/0.SCENE/MainGame.unity index 9796cba5..666e92c0 100644 --- a/Assets/0.SCENE/MainGame.unity +++ b/Assets/0.SCENE/MainGame.unity @@ -21938,6 +21938,51 @@ Transform: m_CorrespondingSourceObject: {fileID: 4217812964197428, guid: b45e8c97110606643bd8998450da2825, type: 3} m_PrefabInstance: {fileID: 198358347} m_PrefabAsset: {fileID: 0} +--- !u!1 &198901432 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 198901434} + - component: {fileID: 198901433} + m_Layer: 0 + m_Name: TesterManager + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &198901433 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 198901432} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 8ec3ccb974842c946bcdce257d9ace12, type: 3} + m_Name: + m_EditorClassIdentifier: + testXPAmount: 10 +--- !u!4 &198901434 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 198901432} + serializedVersion: 2 + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1001 &198907952 PrefabInstance: m_ObjectHideFlags: 0 @@ -22148,6 +22193,39 @@ Transform: m_CorrespondingSourceObject: {fileID: 4983517603246104, guid: 12135b7255e321645b821fe0ec7aa669, type: 3} m_PrefabInstance: {fileID: 200110394} m_PrefabAsset: {fileID: 0} +--- !u!1 &200768983 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 200768984} + m_Layer: 0 + m_Name: '[Managers]' + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &200768984 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 200768983} + serializedVersion: 2 + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 216801511} + - {fileID: 449852328} + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1001 &200975691 PrefabInstance: m_ObjectHideFlags: 0 @@ -23713,12 +23791,12 @@ Transform: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 216801509} serializedVersion: 2 - m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 50.813286, y: 24.132154, z: 9.441976} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] - m_Father: {fileID: 0} + m_Father: {fileID: 200768984} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1001 &218055051 PrefabInstance: @@ -48759,12 +48837,12 @@ Transform: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 449852326} serializedVersion: 2 - m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 334.27826, y: 1029.2424, z: -2.218641} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] - m_Father: {fileID: 0} + m_Father: {fileID: 200768984} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1001 &449863017 PrefabInstance: @@ -186366,6 +186444,54 @@ Transform: m_CorrespondingSourceObject: {fileID: 4390990991741872, guid: d9cd6db974fb330429b0c0aac97e153c, type: 3} m_PrefabInstance: {fileID: 1758157735} m_PrefabAsset: {fileID: 0} +--- !u!1 &1759137304 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1759137305} + - component: {fileID: 1759137306} + m_Layer: 0 + m_Name: ObsessionManager + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1759137305 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1759137304} + serializedVersion: 2 + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &1759137306 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1759137304} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: b72410df3c1803e46ae351fb72d69c2e, type: 3} + m_Name: + m_EditorClassIdentifier: + currentXP: 0 + currentLevel: 1 + xpRequirements: 640000002c010000f401000020030000b0040000dc050000d0070000c4090000 + currentRunXP: 0 --- !u!1001 &1759161619 PrefabInstance: m_ObjectHideFlags: 0 @@ -227230,6 +227356,10 @@ PrefabInstance: propertyPath: m_Name value: Player objectReference: {fileID: 0} + - target: {fileID: 395629277865203624, guid: c34da37720b95c84887eda34e2d90e5b, type: 3} + propertyPath: baseMaxHealth + value: 5000 + objectReference: {fileID: 0} - target: {fileID: 647200136043690535, guid: c34da37720b95c84887eda34e2d90e5b, type: 3} propertyPath: m_LocalPosition.x value: 24.5 @@ -227298,6 +227428,10 @@ PrefabInstance: propertyPath: arrowPrefab value: objectReference: {fileID: 147436, guid: 640cca9678bf09a4799b1ab7c403edbc, type: 3} + - target: {fileID: 8965661853020836870, guid: c34da37720b95c84887eda34e2d90e5b, type: 3} + propertyPath: normalRange + value: 15 + objectReference: {fileID: 0} - target: {fileID: 8965661853020836870, guid: c34da37720b95c84887eda34e2d90e5b, type: 3} propertyPath: weaponHitBox value: @@ -227925,16 +228059,17 @@ SceneRoots: - {fileID: 1992891316} - {fileID: 2112919309} - {fileID: 2119519052} - - {fileID: 449852328} - {fileID: 7449230931284885269} - {fileID: 832898906686813968} - {fileID: 480236531} - {fileID: 321682814584122314} - {fileID: 1824697940} - - {fileID: 216801511} - {fileID: 85056390} - {fileID: 1175521096951328089} - {fileID: 600231664} - {fileID: 1927423250} - {fileID: 2067270637} - {fileID: 615584948} + - {fileID: 200768984} + - {fileID: 1759137305} + - {fileID: 198901434} diff --git a/Assets/1.myPrefab/MyMonster/SwordMonster.prefab b/Assets/1.myPrefab/MyMonster/SwordMonster.prefab index 39fa206d..289b441c 100644 --- a/Assets/1.myPrefab/MyMonster/SwordMonster.prefab +++ b/Assets/1.myPrefab/MyMonster/SwordMonster.prefab @@ -2009,7 +2009,7 @@ MonoBehaviour: - {fileID: 0} - {fileID: 0} - {fileID: 0} - dropChance: 30 + dropChance: 100 attackAnimations: - ghoul_attack Monster_Walk: ghoul_walk diff --git a/Assets/Epic Toon FX/Upgrade/Legacy/ETFX URP Upgrade (2020.3.45f1).unitypackage.meta b/Assets/Epic Toon FX/Upgrade/Legacy/ETFX URP Upgrade (2020.3.45f1).unitypackage.meta deleted file mode 100644 index f76c1a3e..00000000 --- a/Assets/Epic Toon FX/Upgrade/Legacy/ETFX URP Upgrade (2020.3.45f1).unitypackage.meta +++ /dev/null @@ -1,14 +0,0 @@ -fileFormatVersion: 2 -guid: 3d7c4217783978e4abe6496ac71eee94 -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: -AssetOrigin: - serializedVersion: 1 - productId: 57772 - packageName: Epic Toon FX - packageVersion: 1.81 - assetPath: Assets/Epic Toon FX/Upgrade/ETFX URP Upgrade (2020.3.45f1).unitypackage - uploadId: 567564 diff --git a/Assets/Scripts/Combat/Components/Health.cs b/Assets/Scripts/Combat/Components/Health.cs index a85febe2..4397f9a5 100644 --- a/Assets/Scripts/Combat/Components/Health.cs +++ b/Assets/Scripts/Combat/Components/Health.cs @@ -7,13 +7,18 @@ public class PlayerHealth : MonoBehaviour, IDamageable [Header("=== 참조 ===")] [SerializeField] private Stats stats; [SerializeField] private Animator animator; - [SerializeField] private PlayerAttack attackScript; // ⭐ 공격 상태 리셋을 위해 추가 + [SerializeField] private PlayerAttack attackScript; + + // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + // 🔗 보스 패턴 스크립트 연결 (Inspector에서 할당) + // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + [Header("🔗 보스 패턴 연결")] + public BossPatternPhases bossPattern; public bool IsDead { get; private set; } public bool isHit { get; private set; } public bool isInvincible; // 대시 중 무적 플래그 - // ⭐ 기존 OnHit 이벤트와 아래 OnHit() 함수의 이름 충돌을 피하기 위해 이름을 OnHitEvent로 변경함 public event Action OnHitEvent, OnDead; public event Action OnHealthChanged; @@ -30,7 +35,6 @@ public class PlayerHealth : MonoBehaviour, IDamageable Debug.Log($"[UI Sync] 초기 체력 설정 완료: {_currentHealth}/{stats.MaxHealth}"); } if (animator == null) animator = GetComponent(); - // 만약 인스펙터에서 할당 안했다면 자동으로 찾아옴 if (attackScript == null) attackScript = GetComponent(); } @@ -43,13 +47,27 @@ public class PlayerHealth : MonoBehaviour, IDamageable } } + // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + // ⭐ 데미지를 받는 함수 + // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ public void TakeDamage(float amount) { + // 무적이거나 이미 죽었으면 데미지 무시 if (isInvincible || IsDead) return; _currentHealth = Mathf.Max(0, _currentHealth - amount); OnHealthChanged?.Invoke(_currentHealth, stats.MaxHealth); + // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + // 🔗 NEW: 보스 패턴 시스템에 "피격당함"을 알림 + // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + if (bossPattern != null) + { + // BossPatternPhases 스크립트의 OnPlayerHit() 함수 호출 + // 이 함수 안에서 "패턴 진행 중인지" 체크하고 XP 감점 처리 + bossPattern.OnPlayerHit(); + } + // ⭐ 피격 시 트리거 및 상태 리셋 함수 호출 OnHit(); @@ -60,7 +78,7 @@ public class PlayerHealth : MonoBehaviour, IDamageable } // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - // ⭐ [추가] 피격 시 공격 상태 리셋 (핵심!) + // ⭐ 피격 시 공격 상태 리셋 // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ public void OnHit() { @@ -94,12 +112,21 @@ public class PlayerHealth : MonoBehaviour, IDamageable public void OnHitEnd() { isHit = false; } + // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + // ⭐ 사망 처리 + // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ private void Die() { IsDead = true; Cursor.visible = true; Cursor.lockState = CursorLockMode.None; OnDead?.Invoke(); + + // ★ ★ ★ NEW: 사망 시 임시 XP를 영구 XP로 전환 ★ ★ ★ + if (ObsessionSystem.instance != null) + { + ObsessionSystem.instance.OnDeathConvertXP(); + } } public void Heal(float amount) diff --git a/Assets/Epic Toon FX/Upgrade/ETFX URP Upgrade (6000.0.11f1).unitypackage.meta b/Assets/Scripts/Obsession.meta similarity index 67% rename from Assets/Epic Toon FX/Upgrade/ETFX URP Upgrade (6000.0.11f1).unitypackage.meta rename to Assets/Scripts/Obsession.meta index ffa13bfe..912926e2 100644 --- a/Assets/Epic Toon FX/Upgrade/ETFX URP Upgrade (6000.0.11f1).unitypackage.meta +++ b/Assets/Scripts/Obsession.meta @@ -1,5 +1,6 @@ fileFormatVersion: 2 -guid: 5f14e783a5ba44a45a317ffae28af4e7 +guid: c10a35178a7c421419b7729f335527ce +folderAsset: yes DefaultImporter: externalObjects: {} userData: diff --git a/Assets/Scripts/Obsession/BossPatternPhases.cs b/Assets/Scripts/Obsession/BossPatternPhases.cs new file mode 100644 index 00000000..3909c3da --- /dev/null +++ b/Assets/Scripts/Obsession/BossPatternPhases.cs @@ -0,0 +1,123 @@ +using UnityEngine; + +public class BossPatternPhases : MonoBehaviour +{ + // [Header]는 유니티 Inspector 창에서 폴더처럼 보이게 해줌 + [Header("🔧 패턴 설정")] + [SerializeField] private int totalPhases = 3; // 전체 패턴 구간 수 (Inspector에서 조절 가능) + + [Header("💎 XP 설정")] + [SerializeField] private int phaseClearXP = 10; // 한 구간 통과 시 주는 XP + [SerializeField] private int fullClearBonus = 30; // 모든 구간 통과 시 보너스 XP + [SerializeField] private int hitPenalty = -5; // 피격 시 감점 XP (0으로 하면 감점 없음) + + // [SerializeField]는 인스펙터에서 실시간으로 볼 수 있게 해줌 + [Header("🚩 상태 확인용")] + [SerializeField] private int currentPhase = 0; // 현재 진행 중인 구간 (0: 시작 전) + [SerializeField] private bool isPatternActive = false; // 패턴이 진행 중인지 + [SerializeField] private bool wasHitThisPhase = false; // 이번 구간 중 피격당했는지 + + // ★ 초기화 + private void Start() + { + ResetPattern(); + } + + // ★ 패턴 전체 리셋 (새로운 패턴 시작 시 호출) + public void ResetPattern() + { + currentPhase = 0; + wasHitThisPhase = false; + isPatternActive = false; + Debug.Log("[보스] 패턴 초기화 완료!"); + } + + // ★ 패턴 시작 (보스 AI에서 호출) + public void StartPattern() + { + if (currentPhase >= totalPhases) + { + Debug.Log("이미 모든 패턴을 완료했어요!"); + return; + } + + isPatternActive = true; + wasHitThisPhase = false; + Debug.Log($"🔔 [페이즈 {currentPhase + 1}] 시작! 회피하세요!"); + + // ★ ★ ★ 여기서 보스의 애니메이션/이펙트 시작하면 됨 ★ ★ ★ + } + + // ★ 플레이어가 피격당했을 때 호출 (PlayerHealth에서 연결) + public void OnPlayerHit() + { + if (!isPatternActive) return; + + wasHitThisPhase = true; + Debug.Log($"💥 [페이즈 {currentPhase + 1}] 중 피격당함! (XP 감점)"); + + // 피격 시 감점 처리 (필요하면) + if (hitPenalty != 0 && ObsessionSystem.instance != null) + { + ObsessionSystem.instance.AddRunXP(hitPenalty); + } + } + + // ★ 현재 구간을 성공적으로 통과했을 때 호출 (보스 AI에서 호출) + public void ClearCurrentPhase() + { + if (!isPatternActive) return; + + // 피격당했는지 확인 + if (wasHitThisPhase) + { + Debug.Log($"❌ [페이즈 {currentPhase + 1}] 실패... 다음 구간 진행"); + } + else + { + // ★ 성공 시 XP 지급 + if (ObsessionSystem.instance != null) + { + ObsessionSystem.instance.AddRunXP(phaseClearXP); + Debug.Log($"✅ [페이즈 {currentPhase + 1}] 성공! +{phaseClearXP}XP"); + } + } + + // 다음 구간으로 + currentPhase++; + wasHitThisPhase = false; + + // 모든 구간을 완료했는가? + if (currentPhase >= totalPhases) + { + Debug.Log("🎉 모든 패턴 완료!"); + FullClearBonus(); + isPatternActive = false; + } + } + + // ★ 모든 구간 통과 시 보너스 + private void FullClearBonus() + { + if (ObsessionSystem.instance != null) + { + ObsessionSystem.instance.AddRunXP(fullClearBonus); + Debug.Log($"🌟 전체 패턴 클리어 보너스 +{fullClearBonus}XP!"); + } + } + + // ★ 디버그용: 현재 상태를 콘솔에 출력 + private void Update() + { + if (Input.GetKeyDown(KeyCode.P)) + { + Debug.Log($"[패턴 상태] 현재 구간: {currentPhase + 1}/{totalPhases}, 활성: {isPatternActive}"); + } + } + + // BossPatternPhases.cs에 추가 + public bool IsPatternActive() + { + return isPatternActive; + } +} \ No newline at end of file diff --git a/Assets/Scripts/Obsession/BossPatternPhases.cs.meta b/Assets/Scripts/Obsession/BossPatternPhases.cs.meta new file mode 100644 index 00000000..88affc67 --- /dev/null +++ b/Assets/Scripts/Obsession/BossPatternPhases.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d25122754c748e14e87b23597aa9c862 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Obsession/BossRoomTrigger.cs b/Assets/Scripts/Obsession/BossRoomTrigger.cs new file mode 100644 index 00000000..79c81aaa --- /dev/null +++ b/Assets/Scripts/Obsession/BossRoomTrigger.cs @@ -0,0 +1,19 @@ +using UnityEngine; + +public class BossRoomTrigger : MonoBehaviour +{ + private void OnTriggerEnter(Collider other) + { + if (other.CompareTag("Player")) + { + // ★ 보스 방에 처음 들어가면 보너스 XP + if (ObsessionSystem.instance != null) + { + ObsessionSystem.instance.AddRunXP(50); // 보스 도달 보너스 + Debug.Log("보스 방 도달! +50XP 보너스 획득!"); + } + + Destroy(this); // 한 번만 주고 제거 + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/Obsession/BossRoomTrigger.cs.meta b/Assets/Scripts/Obsession/BossRoomTrigger.cs.meta new file mode 100644 index 00000000..0d717c94 --- /dev/null +++ b/Assets/Scripts/Obsession/BossRoomTrigger.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 258239f4a8a7c504086a5c90175a23a9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Obsession/ObsessionSystem.cs b/Assets/Scripts/Obsession/ObsessionSystem.cs new file mode 100644 index 00000000..6afe7c67 --- /dev/null +++ b/Assets/Scripts/Obsession/ObsessionSystem.cs @@ -0,0 +1,118 @@ +using UnityEngine; + +public class ObsessionSystem : MonoBehaviour +{ + public static ObsessionSystem instance; + + [SerializeField] private int currentXP = 0; // ★ 영구 XP (저장되는 진짜 진행도) + [SerializeField] private int currentLevel = 1; + [SerializeField] private int[] xpRequirements = { 100, 300, 500, 800, 1200 }; + + // ★ ★ ★ NEW: 이번 런에서 쌓은 임시 XP ★ ★ ★ + [SerializeField] private int currentRunXP = 0; // 이 값은 게임 껐다 키면 사라져요! + + private void Awake() + { + if (instance == null) + { + instance = this; + DontDestroyOnLoad(gameObject); + } + else + { + Destroy(gameObject); + } + } + + private void Start() + { + LoadData(); + Debug.Log($"[집착 시스템] 시작! 현재 레벨: {currentLevel}, XP: {currentXP}"); + + // ★ ★ ★ NEW: 새 런 시작할 때 임시 XP는 0으로 초기화 ★ ★ ★ + currentRunXP = 0; + Debug.Log($"[새 런 시작] 이번 런에 쌓을 임시 XP: 0"); + } + + // ★ ★ ★ NEW: 플레이 중 실시간으로 임시 XP 추가 ★ ★ ★ + public void AddRunXP(int amount) + { + currentRunXP += amount; + Debug.Log($"[런 XP 획득] +{amount}XP (이번 런 총: {currentRunXP})"); + } + + // ★ ★ ★ NEW: 사망 시 임시 XP를 영구 XP로 변환 ★ ★ ★ + public void OnDeathConvertXP() + { + Debug.Log($"[사망] 이번 런 성과: {currentRunXP}XP를 영구 XP로 전환!"); + + // 임시 XP를 영구 XP에 추가 + AddXP(currentRunXP); + + // 임시 XP는 초기화 (다음 런을 위해) + currentRunXP = 0; + } + + // 기존 AddXP 함수 (이제는 사망할 때만 호출될 거예요) + public void AddXP(int amount) + { + currentXP += amount; + Debug.Log($"[영구 XP 획득] +{amount}XP (현재: {currentXP})"); + CheckLevelUp(); + SaveData(); + } + + private void CheckLevelUp() + { + if (currentLevel - 1 < xpRequirements.Length) + { + int requiredXP = xpRequirements[currentLevel - 1]; + if (currentXP >= requiredXP) + { + currentLevel++; + Debug.Log($"🎉 [레벨업!] 레벨 {currentLevel - 1} → {currentLevel}"); + UnlockRewards(currentLevel); + } + } + } + + private void UnlockRewards(int level) + { + // 보상 코드는 기존과 동일 + } + + private void SaveData() + { + PlayerPrefs.SetInt("ObsessionLevel", currentLevel); + PlayerPrefs.SetInt("ObsessionXP", currentXP); + PlayerPrefs.Save(); + } + + private void LoadData() + { + currentLevel = PlayerPrefs.GetInt("ObsessionLevel", 1); + currentXP = PlayerPrefs.GetInt("ObsessionXP", 0); + } + + // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + // 🧪 테스트용: 현재 상태를 읽는 함수들 + // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + // 현재 런 XP 확인 + public int GetCurrentRunXP() + { + return currentRunXP; + } + + // 현재 영구 XP 확인 + public int GetCurrentXP() + { + return currentXP; + } + + // 현재 레벨 확인 + public int GetCurrentLevel() + { + return currentLevel; + } +} \ No newline at end of file diff --git a/Assets/Scripts/Obsession/ObsessionSystem.cs.meta b/Assets/Scripts/Obsession/ObsessionSystem.cs.meta new file mode 100644 index 00000000..655057ed --- /dev/null +++ b/Assets/Scripts/Obsession/ObsessionSystem.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b72410df3c1803e46ae351fb72d69c2e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Obsession/ObsessionTester.cs b/Assets/Scripts/Obsession/ObsessionTester.cs new file mode 100644 index 00000000..d1f62ef4 --- /dev/null +++ b/Assets/Scripts/Obsession/ObsessionTester.cs @@ -0,0 +1,97 @@ +using UnityEngine; + +public class ObsessionTester : MonoBehaviour +{ + // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + // 🧪 이 스크립트는 테스트용입니다! + // 키보드로 XP 시스템을 테스트할 수 있어요. + // 나중에 보스가 완성되면 이 스크립트는 삭제해도 됩니다. + // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + [Header("🔧 테스트 설정")] + [SerializeField] private int testXPAmount = 10; // 테스트로 줄 XP 양 + + private void Start() + { + Debug.Log("========================================"); + Debug.Log("🧪 [테스트 모드] 키보드 조작법:"); + Debug.Log(" [1] 런 XP +10 (보스 맞추기 시뮬레이션)"); + Debug.Log(" [2] 런 XP +50 (보스 도달 시뮬레이션)"); + Debug.Log(" [3] 런 XP -5 (패턴 중 피격 시뮬레이션)"); + Debug.Log(" [4] 사망 시뮬레이션 (XP 전환)"); + Debug.Log(" [5] 현재 상태 확인"); + Debug.Log(" [0] 저장 데이터 초기화 (처음부터 다시)"); + Debug.Log("========================================"); + } + + private void Update() + { + // ObsessionSystem이 없으면 테스트 불가 + if (ObsessionSystem.instance == null) + { + if (Input.anyKeyDown) + { + Debug.LogError("❌ ObsessionSystem이 씬에 없어요! GameManager 오브젝트에 추가하세요."); + } + return; + } + + // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + // [1] 보스 맞추기 시뮬레이션 (+10 XP) + // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + if (Input.GetKeyDown(KeyCode.Alpha1)) + { + ObsessionSystem.instance.AddRunXP(10); + Debug.Log("🎯 [테스트] 보스 맞추기! +10 런 XP"); + } + + // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + // [2] 보스 도달 시뮬레이션 (+50 XP) + // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + if (Input.GetKeyDown(KeyCode.Alpha2)) + { + ObsessionSystem.instance.AddRunXP(50); + Debug.Log("🚪 [테스트] 보스 방 도달! +50 런 XP"); + } + + // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + // [3] 패턴 중 피격 시뮬레이션 (-5 XP) + // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + if (Input.GetKeyDown(KeyCode.Alpha3)) + { + ObsessionSystem.instance.AddRunXP(-5); + Debug.Log("💥 [테스트] 패턴 중 피격! -5 런 XP"); + } + + // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + // [4] 사망 시뮬레이션 (런 XP → 영구 XP 전환) + // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + if (Input.GetKeyDown(KeyCode.Alpha4)) + { + Debug.Log("💀 [테스트] 사망! 런 XP를 영구 XP로 전환..."); + ObsessionSystem.instance.OnDeathConvertXP(); + } + + // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + // [5] 현재 상태 확인 + // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + if (Input.GetKeyDown(KeyCode.Alpha5)) + { + Debug.Log("========================================"); + Debug.Log("📊 [현재 상태]"); + Debug.Log($" 런 XP: {ObsessionSystem.instance.GetCurrentRunXP()}"); + Debug.Log($" 영구 XP: {ObsessionSystem.instance.GetCurrentXP()}"); + Debug.Log($" 레벨: {ObsessionSystem.instance.GetCurrentLevel()}"); + Debug.Log("========================================"); + } + + // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + // [0] 저장 데이터 초기화 (처음부터 다시) + // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + if (Input.GetKeyDown(KeyCode.Alpha0)) + { + PlayerPrefs.DeleteAll(); + Debug.Log("🗑️ [테스트] 모든 저장 데이터 삭제! 게임을 다시 시작하세요."); + } + } +} diff --git a/Assets/Scripts/Obsession/ObsessionTester.cs.meta b/Assets/Scripts/Obsession/ObsessionTester.cs.meta new file mode 100644 index 00000000..6723099d --- /dev/null +++ b/Assets/Scripts/Obsession/ObsessionTester.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8ec3ccb974842c946bcdce257d9ace12 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Obsession/RoomClear.cs b/Assets/Scripts/Obsession/RoomClear.cs new file mode 100644 index 00000000..9b6ba5c8 --- /dev/null +++ b/Assets/Scripts/Obsession/RoomClear.cs @@ -0,0 +1,14 @@ +using UnityEngine; + +public class RoomClear : MonoBehaviour +{ + public void ClearRoom() + { + // ★ 방을 무사히 클리어하면 + if (ObsessionSystem.instance != null) + { + ObsessionSystem.instance.AddRunXP(20); + Debug.Log("방 클리어! +20XP"); + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/Obsession/RoomClear.cs.meta b/Assets/Scripts/Obsession/RoomClear.cs.meta new file mode 100644 index 00000000..27bb012c --- /dev/null +++ b/Assets/Scripts/Obsession/RoomClear.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3cf4125d95a36504c8afc9b89338a9a2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Player/Upgrade/Data/CardData.cs b/Assets/Scripts/Player/Upgrade/Data/CardData.cs index 2bbac2bc..a3745453 100644 --- a/Assets/Scripts/Player/Upgrade/Data/CardData.cs +++ b/Assets/Scripts/Player/Upgrade/Data/CardData.cs @@ -9,6 +9,10 @@ public abstract class CardData : ScriptableObject { public Sprite icon; + // ⭐ 추가: 이 카드가 나타나기 위해 필요한 '복수의 집착' 레벨 + // 기본값을 1로 설정하면 처음부터 등장합니다. + public int requiredObsessionLevel = 1; + // UI 표시용 public abstract string GetText();