feat(BT12-Dev Phase 2-D): BT12-MVP-A 통합 정정 (placeholder → 정식 ActiveSkillData) + Phase 2-B .meta 보충
C49 Phase 2-D — Sonnet 위임 (코드 Write·검증만·git 본 PM 처리 정합·feedback_pm_sonnet_subagent_unauthorized_push 정합). 수정 6 파일: - LevelUpManager.cs (Phase 2-D 정정·_pool 제거·SkillRuntimeFactory.RandomDraw3·HandleCardConfirmed(ActiveSkillData)·PlayerSkillInventory.AddSkillByCardId) - SkillSelectionUI.cs (Show(List<ActiveSkillData>)·_selected·BindSlot·OnCardSelected ActiveSkillData 전환) - SkillCardSlot.cs (Bind(ActiveSkillData)·DisplayName/Description/Icon PascalCase·rarity 배너 갈색 고정) - PlayerController.cs (PlayerSkillInventory 자동 부착·line 100) - Projectile.cs (Layer Enemy 미등재 fallback — EnemyController 컴포넌트 검사·proxy) - SkillRuntimeFactory.cs (RandomDraw3 메서드·Active 카테고리 무작위 3장) 신규 9 .meta (Phase 2-B Sonnet 자율 push 영역 영역 영역 영역 X·Unity Editor Refresh 후 자동 생성·본 commit 보충): - Skills.meta + Effectors.meta + 7 Effectors/*.cs.meta Layer Enemy 영역 = proxy 개선 신호 (C2-2): - 현 시점 = Projectile.OnTriggerEnter2D 영역 EnemyController 컴포넌트 fallback (proxy) - 근본 해결안 = Layer "Enemy" 정식 등재 (별도 PD 안건·후속) 기능: - 적 처치 → EXP +1 → 즉시 레벨업 → 카드 3장 노출 → 선택 → PlayerSkillInventory 등록 → ActiveSkillRuntime Tick → 1.5s 영역 자동 발사 + 부가 효과 (DoT·Stun·Slow·DebuffStack) 기존 영역 변경 X (BT5-Dev·BT7-Dev·Phase 2-A·2-B·2-C·BT12-MVP-A asset 5장·Scene·SkillCardPlaceholder·SkillCardPlaceholderPool·deprecate 차후) Compile error 0건 (read_console·도메인 리로드 정합) C49 — Phase 2-D Sonnet 위임 + Phase 3 본 PM 직접 (단순 반복 카탈로그 v1) C50 — ~95K (PD 사전 승인 70~95K 영역 상한 정합) C19-2 — Sonnet 자율 git X·본 PM 직접 commit·push (feedback 정합) pm-auditor 사전 감사 = Pass + Minor 1 (Layer fallback proxy 명시·본 commit + 대화로그 영역 정정 적용) 후속: - Phase 2-A·2-B·2-C·2-D 영역 PD Play 검증 (자동 발동·레벨업·카드 선택·등록·Tick) - Layer "Enemy" 정식 등재 (별도 PD 안건·근본 해결안) - Phase 2-E EditMode 테스트 - 다른 카테고리 (B·C·D·E·F) 영역 - BT12-MVP-A asset 5장 deprecate (차기) - Screenshots·_Recovery .gitignore (별도)
This commit is contained in:
parent
c01f25a558
commit
d53150b5ed
|
|
@ -96,6 +96,10 @@ namespace Platformer.Mechanics
|
|||
if (GetComponent<EerieVillage.Progression.PlayerProgression>() == null)
|
||||
gameObject.AddComponent<EerieVillage.Progression.PlayerProgression>();
|
||||
|
||||
// Phase 2-D 신규 (2026-05-09) — PlayerSkillInventory 자동 부착 (스킬 인벤토리 영역)
|
||||
if (GetComponent<EerieVillage.Skills.PlayerSkillInventory>() == null)
|
||||
gameObject.AddComponent<EerieVillage.Skills.PlayerSkillInventory>();
|
||||
|
||||
// 사망 시 입력 차단 / 부활 시 입력 복원
|
||||
if (health != null)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using TMPro;
|
||||
using EerieVillage.Progression;
|
||||
using EerieVillage.Skills;
|
||||
|
||||
namespace EerieVillage.MyUI
|
||||
{
|
||||
|
|
@ -9,35 +9,36 @@ namespace EerieVillage.MyUI
|
|||
/// BT12-MVP-A 영역 단일 카드 슬롯 — PD 첨부 예시 ("기술 선택" 화면) 정합.
|
||||
/// SkillSelectionUI 영역 3개 자식.
|
||||
///
|
||||
/// Phase 2-D 정정 (2026-05-09) — SkillCardPlaceholder → ActiveSkillData 전환.
|
||||
/// SkillDataAsset base에 rarity 필드 부재 → 배너 고정 갈색 (BT12-MVP-A Phase 2-2 영역 정합).
|
||||
///
|
||||
/// PD 예시 카드 구성:
|
||||
/// 1. 상단 색상 배너 (등급 — 청록 Common · 노랑 Rare · 빨강 Max)
|
||||
/// 1. 상단 색상 배너 (갈색 고정 — rarity 미지원)
|
||||
/// 2. 카드 이름 (한글)
|
||||
/// 3. 원형 아이콘 + 동심원 빛 효과
|
||||
/// 4. "레벨 N" 또는 "최대" (빨강 강조)
|
||||
/// 4. "레벨 N" 표시
|
||||
/// 5. 효과 설명 3~4 라인
|
||||
/// </summary>
|
||||
public class SkillCardSlot : MonoBehaviour
|
||||
{
|
||||
[Header("PD 예시 정합 — 카드 구성")]
|
||||
[SerializeField] Image _topBanner; // 1. 상단 색상 배너
|
||||
[SerializeField] Image _topBanner; // 1. 상단 색상 배너 (갈색 고정)
|
||||
[SerializeField] TMP_Text _nameText; // 2. 카드 이름 (한글)
|
||||
[SerializeField] Image _icon; // 3. 원형 아이콘
|
||||
[SerializeField] Image _glowEffect; // 3. 동심원 빛 효과
|
||||
[SerializeField] TMP_Text _levelText; // 4. 레벨 N / 최대
|
||||
[SerializeField] TMP_Text _levelText; // 4. 레벨 표시
|
||||
[SerializeField] TMP_Text _descriptionText; // 5. 효과 설명
|
||||
|
||||
[Header("인터랙션")]
|
||||
[SerializeField] Button _clickArea; // 카드 전체 클릭 영역
|
||||
[SerializeField] GameObject _highlightFrame; // 선택 시 활성
|
||||
|
||||
[Header("등급별 색상 (PD 예시 정합)")]
|
||||
[SerializeField] Color _commonColor = new Color(0.3f, 0.7f, 0.7f, 1f); // 청록
|
||||
[SerializeField] Color _rareColor = new Color(0.95f, 0.7f, 0.25f, 1f); // 노랑
|
||||
[SerializeField] Color _maxColor = new Color(0.9f, 0.3f, 0.3f, 1f); // 빨강
|
||||
[Header("배너 고정 색상 (rarity 미지원 — 갈색 통일)")]
|
||||
[SerializeField] Color _bannerColor = new Color(0.5f, 0.35f, 0.15f, 1f); // 갈색 고정
|
||||
|
||||
public SkillCardPlaceholder Card { get; private set; }
|
||||
public ActiveSkillData Card { get; private set; }
|
||||
|
||||
public void Bind(SkillCardPlaceholder card, System.Action onClick)
|
||||
public void Bind(ActiveSkillData card, System.Action onClick)
|
||||
{
|
||||
Card = card;
|
||||
|
||||
|
|
@ -48,42 +49,27 @@ namespace EerieVillage.MyUI
|
|||
}
|
||||
gameObject.SetActive(true);
|
||||
|
||||
// 2. 카드 이름
|
||||
if (_nameText != null) _nameText.text = card.displayName;
|
||||
// 2. 카드 이름 (DisplayName — SkillDataAsset base PascalCase)
|
||||
if (_nameText != null) _nameText.text = card.DisplayName;
|
||||
|
||||
// 3. 아이콘 (sprite null 시 영역 image enable=false 또는 placeholder 영역 잔존)
|
||||
// 3. 아이콘 (sprite null 시 image enable=false)
|
||||
if (_icon != null)
|
||||
{
|
||||
_icon.sprite = card.icon;
|
||||
_icon.enabled = card.icon != null;
|
||||
_icon.sprite = card.Icon;
|
||||
_icon.enabled = card.Icon != null;
|
||||
}
|
||||
|
||||
// 5. 효과 설명
|
||||
if (_descriptionText != null) _descriptionText.text = card.description;
|
||||
if (_descriptionText != null) _descriptionText.text = card.Description;
|
||||
|
||||
// 1. 상단 색상 배너 + 4. 레벨 텍스트 — 등급별 영역
|
||||
Color bannerColor = card.rarity switch
|
||||
{
|
||||
CardRarity.Common => _commonColor,
|
||||
CardRarity.Rare => _rareColor,
|
||||
CardRarity.Max => _maxColor,
|
||||
_ => _commonColor
|
||||
};
|
||||
if (_topBanner != null) _topBanner.color = bannerColor;
|
||||
// 1. 상단 색상 배너 — rarity 필드 부재 → 갈색 고정
|
||||
if (_topBanner != null) _topBanner.color = _bannerColor;
|
||||
|
||||
// 4. 레벨 N / 최대
|
||||
// 4. 레벨 표시 — SkillDataAsset base에 currentLevel 없음 → "레벨 1" 고정 표시
|
||||
if (_levelText != null)
|
||||
{
|
||||
if (card.IsMaxLevel)
|
||||
{
|
||||
_levelText.text = "최대";
|
||||
_levelText.color = _maxColor;
|
||||
}
|
||||
else
|
||||
{
|
||||
_levelText.text = $"레벨 {card.currentLevel}";
|
||||
_levelText.color = Color.white;
|
||||
}
|
||||
_levelText.text = "레벨 1";
|
||||
_levelText.color = Color.white;
|
||||
}
|
||||
|
||||
// 클릭 영역
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ using System.Collections.Generic;
|
|||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using TMPro;
|
||||
using EerieVillage.Progression;
|
||||
using EerieVillage.Skills;
|
||||
|
||||
namespace EerieVillage.MyUI
|
||||
{
|
||||
|
|
@ -10,6 +10,8 @@ namespace EerieVillage.MyUI
|
|||
/// BT12-MVP-A 영역 스킬 선택 UI — PD 첨부 예시 ("기술 선택" 화면) 정합.
|
||||
/// LevelUpManager 영역 호출 — Show(cards, level, onConfirm).
|
||||
///
|
||||
/// Phase 2-D 정정 (2026-05-09) — SkillCardPlaceholder → ActiveSkillData 전환.
|
||||
///
|
||||
/// 화면 구조:
|
||||
/// [Header] "기술 선택" 타이틀 + X 닫기 버튼
|
||||
/// [Body] 카드 3장 가로 배치 (SkillCardSlot ×3)
|
||||
|
|
@ -38,8 +40,8 @@ namespace EerieVillage.MyUI
|
|||
[SerializeField] TMP_Text _pointText; // "남은 포인트: N"
|
||||
[SerializeField] Button _confirmButton; // "확인"
|
||||
|
||||
SkillCardPlaceholder _selected;
|
||||
System.Action<SkillCardPlaceholder> _onConfirm;
|
||||
ActiveSkillData _selected;
|
||||
System.Action<ActiveSkillData> _onConfirm;
|
||||
|
||||
void Awake()
|
||||
{
|
||||
|
|
@ -50,7 +52,7 @@ namespace EerieVillage.MyUI
|
|||
}
|
||||
|
||||
/// <summary>레벨업 시점 호출 — 카드 3장 표시 + 사용자 선택 대기.</summary>
|
||||
public void Show(List<SkillCardPlaceholder> cards, int level, System.Action<SkillCardPlaceholder> onConfirm)
|
||||
public void Show(List<ActiveSkillData> cards, int level, System.Action<ActiveSkillData> onConfirm)
|
||||
{
|
||||
Debug.Log($"[SkillSelectionUI] Show 호출 cards={cards.Count} level={level}");
|
||||
_onConfirm = onConfirm;
|
||||
|
|
@ -86,13 +88,13 @@ namespace EerieVillage.MyUI
|
|||
gameObject.SetActive(false); // BT12-MVP-A 정정 — Canvas root 비활성
|
||||
}
|
||||
|
||||
void BindSlot(SkillCardSlot slot, SkillCardPlaceholder card)
|
||||
void BindSlot(SkillCardSlot slot, ActiveSkillData card)
|
||||
{
|
||||
if (slot == null) return;
|
||||
slot.Bind(card, () => OnCardSelected(slot, card));
|
||||
}
|
||||
|
||||
void OnCardSelected(SkillCardSlot clickedSlot, SkillCardPlaceholder card)
|
||||
void OnCardSelected(SkillCardSlot clickedSlot, ActiveSkillData card)
|
||||
{
|
||||
if (card == null) return;
|
||||
_selected = card;
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ using System.Collections.Generic;
|
|||
using UnityEngine;
|
||||
using Platformer.Mechanics;
|
||||
using EerieVillage.MyUI;
|
||||
using EerieVillage.Skills;
|
||||
|
||||
namespace EerieVillage.Progression
|
||||
{
|
||||
|
|
@ -9,13 +10,13 @@ namespace EerieVillage.Progression
|
|||
/// 레벨업 발화 시 일시정지 + UI 호출 + 카드 선택 결과 수령.
|
||||
/// PlayerProgression.OnLevelUp 구독.
|
||||
///
|
||||
/// Phase 2-D 정정 (2026-05-09) — SkillRuntimeFactory.RandomDraw3() + PlayerSkillInventory.AddSkillByCardId() 정식 통합.
|
||||
/// Phase 2-B 영역 (2026-05-08) — SkillSelectionUI 정식 통합.
|
||||
/// </summary>
|
||||
public class LevelUpManager : MonoBehaviour
|
||||
{
|
||||
public static LevelUpManager Instance { get; private set; }
|
||||
|
||||
[SerializeField] SkillCardPlaceholderPool _pool;
|
||||
[SerializeField] SkillSelectionUI _ui;
|
||||
|
||||
PlayerController _player;
|
||||
|
|
@ -47,8 +48,6 @@ namespace EerieVillage.Progression
|
|||
_progression = _player.gameObject.AddComponent<PlayerProgression>();
|
||||
}
|
||||
_progression.OnLevelUp += HandleLevelUp;
|
||||
|
||||
if (_pool == null) _pool = GetComponent<SkillCardPlaceholderPool>();
|
||||
}
|
||||
|
||||
void OnDestroy()
|
||||
|
|
@ -59,7 +58,7 @@ namespace EerieVillage.Progression
|
|||
|
||||
void HandleLevelUp(int newLevel)
|
||||
{
|
||||
Debug.Log($"[LevelUpManager] HandleLevelUp 호출 Lv.{newLevel} _ui={(_ui == null ? "NULL" : _ui.name)} _pool={(_pool == null ? "NULL" : _pool.name)}");
|
||||
Debug.Log($"[LevelUpManager] HandleLevelUp 호출 Lv.{newLevel} _ui={(_ui == null ? "NULL" : _ui.name)}");
|
||||
if (_isLevelUpActive) return;
|
||||
_isLevelUpActive = true;
|
||||
|
||||
|
|
@ -67,13 +66,11 @@ namespace EerieVillage.Progression
|
|||
Time.timeScale = 0f;
|
||||
if (_player != null) _player.controlEnabled = false;
|
||||
|
||||
// 카드 3장 무작위 추출
|
||||
List<SkillCardPlaceholder> cards = _pool != null
|
||||
? _pool.Draw3Random()
|
||||
: new List<SkillCardPlaceholder>();
|
||||
// Phase 2-D — SkillRuntimeFactory.RandomDraw3()로 카드 3장 추출
|
||||
List<ActiveSkillData> cards = SkillRuntimeFactory.RandomDraw3();
|
||||
Debug.Log($"[LevelUpManager] cards.Count={cards.Count}");
|
||||
|
||||
// Phase 2-B 영역 — SkillSelectionUI 정식 호출
|
||||
// Phase 2-B/D 통합 — SkillSelectionUI 정식 호출
|
||||
if (_ui != null)
|
||||
{
|
||||
Debug.Log($"[LevelUpManager] _ui.Show 호출 → SkillSelectionCanvas 활성 의도");
|
||||
|
|
@ -81,20 +78,39 @@ namespace EerieVillage.Progression
|
|||
}
|
||||
else
|
||||
{
|
||||
// UI 영역 부재 fallback (placeholder asset 5장 미등록 영역 등)
|
||||
Debug.LogWarning($"[LevelUpManager] SkillSelectionUI 부재 — Lv.{newLevel} 카드 {cards.Count}장 영역 자동 확정");
|
||||
// UI 부재 fallback
|
||||
Debug.LogWarning($"[LevelUpManager] SkillSelectionUI 부재 — Lv.{newLevel} 카드 {cards.Count}장 자동 확정");
|
||||
HandleCardConfirmed(cards.Count > 0 ? cards[0] : null);
|
||||
}
|
||||
}
|
||||
|
||||
void HandleCardConfirmed(SkillCardPlaceholder selected)
|
||||
void HandleCardConfirmed(ActiveSkillData selected)
|
||||
{
|
||||
// UI 닫기
|
||||
if (_ui != null) _ui.Hide();
|
||||
|
||||
// 차기 BT12-Dev 영역 = PlayerSkillInventory.AddSkillByCardId(selected.id)
|
||||
// BT12-MVP-A 영역 = UI 닫기·게임 재개만
|
||||
Debug.Log($"[LevelUpManager] 카드 확정 — {(selected != null ? selected.displayName : "NONE")} (효과 적용 X·BT12-Dev 본격 영역)");
|
||||
// Phase 2-D — PlayerSkillInventory.AddSkillByCardId() 정식 호출
|
||||
if (selected != null)
|
||||
{
|
||||
var inventory = _player != null
|
||||
? _player.GetComponent<PlayerSkillInventory>()
|
||||
: null;
|
||||
|
||||
if (inventory != null)
|
||||
{
|
||||
bool ok = inventory.AddSkillByCardId(selected.CardId);
|
||||
Debug.Log($"[LevelUpManager] 카드 확정 — {selected.DisplayName} (AddSkillByCardId={ok})");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning("[LevelUpManager] PlayerSkillInventory 영역 부재 — 스킬 등록 X (PlayerController에 컴포넌트 확인 필요)");
|
||||
Debug.Log($"[LevelUpManager] 카드 확정 — {selected.DisplayName} (인벤토리 X)");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Log("[LevelUpManager] 카드 확정 — NONE");
|
||||
}
|
||||
|
||||
// 일시정지 해제 + 입력 복원
|
||||
Time.timeScale = 1f;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: db426a2a7a87ed74cb21bfd993b652f6
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: ce0153fb6464fb04daa8cff8a814ccc0
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 81f421a8920bffe42b98777d721492fb
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 91ab1ca550a3a834fabe8064779f4c67
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 27b449488cc69b947b2b3b13bf8edd8b
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 93952e314854a4446b4c3b3b3d90e858
|
||||
|
|
@ -45,8 +45,13 @@ namespace EerieVillage.Skills.Effectors
|
|||
{
|
||||
if (_hitTargets.Contains(other)) return;
|
||||
|
||||
// Enemy 레이어 한정
|
||||
if (other.gameObject.layer != LayerMask.NameToLayer("Enemy")) return;
|
||||
// Enemy 레이어 한정.
|
||||
// Phase 2-D fallback (2026-05-09): TagManager에 "Enemy" 레이어 미등재 시 LayerMask.NameToLayer 반환값 = -1.
|
||||
// 레이어 매칭 실패 시 EnemyController 컴포넌트 존재 여부로 대체 판정.
|
||||
int enemyLayer = LayerMask.NameToLayer("Enemy");
|
||||
bool isEnemy = (enemyLayer != -1 && other.gameObject.layer == enemyLayer)
|
||||
|| other.GetComponent<EnemyController>() != null;
|
||||
if (!isEnemy) return;
|
||||
|
||||
var health = other.GetComponent<Health>();
|
||||
if (health == null || !health.IsAlive) return;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,2 @@
|
|||
fileFormatVersion: 2
|
||||
guid: ae9d149946257734593e7840cf591573
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8b322e13e2c8df1429ba815dd2c0628c
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
fileFormatVersion: 2
|
||||
guid: bc7c664d1a9a68641ae6106260d908f2
|
||||
|
|
@ -65,6 +65,32 @@ namespace EerieVillage.Skills
|
|||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 레벨업 시 카드 3장 무작위 추출. Active 카테고리만. Phase 2-D 영역 BT12-MVP-A 통합.
|
||||
/// </summary>
|
||||
public static List<ActiveSkillData> RandomDraw3()
|
||||
{
|
||||
EnsureLoaded();
|
||||
var actives = new List<ActiveSkillData>();
|
||||
foreach (var kvp in _cache)
|
||||
{
|
||||
if (kvp.Value is ActiveSkillData ad) actives.Add(ad);
|
||||
}
|
||||
if (actives.Count == 0) return new List<ActiveSkillData>();
|
||||
|
||||
// 무작위 3장 (중복 X)
|
||||
var pool = new List<ActiveSkillData>(actives);
|
||||
var result = new List<ActiveSkillData>();
|
||||
int draw = Mathf.Min(3, pool.Count);
|
||||
for (int i = 0; i < draw; i++)
|
||||
{
|
||||
int idx = Random.Range(0, pool.Count);
|
||||
result.Add(pool[idx]);
|
||||
pool.RemoveAt(idx);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>캐시 강제 리셋 (에디터 툴·테스트용)</summary>
|
||||
public static void InvalidateCache()
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue