using System; using System.Collections.Generic; using UnityEngine; using UnityEngine.Events; using UnityEngine.UIElements; // 증거품창 (역전재판의 법정기록). ItemListUI.uxml을 띄우고 가진 아이템을 좌우 화살표로 넘겨 본다. // // 제시(들이대기): 대화 노드가 히든 분기로 무장돼 있는 동안 아이템 그림을 클릭하면 // HiddenBranchResolver.Fire(아이템 Id)를 쏜다 — DialogTouchZone의 부위 키와 완전히 같은 통로라서, // 노드에 Key = 아이템 Id 인 히든 분기를 달아두면 그 분기가 열린다. 발동하면 창은 자동으로 닫힌다. // 무장돼 있지 않으면(평소) 클릭해도 아무 일도 없다 — 그냥 열람용 창이다. // // 목록은 StoryManager의 아이템 이력(Id)을 ItemDatabase로 복원해 만든다. 획득 순서 = 표시 순서. [RequireComponent(typeof(PanelRenderer))] public class ItemHud : MonoBehaviour { public static ItemHud Instance { get; private set; } [Tooltip("Id → ItemData 복원용 아이템 전체 목록 (게임에 하나)")] [SerializeField] private ItemDatabase _database; [Tooltip("증거품이 하나도 없을 때 설명 칸에 표시할 문구")] [SerializeField] private string _emptyText = "가지고 있는 증거품이 없습니다."; [Tooltip("제시가 실제로 발동했을 때 호출 (연출·사운드용). 발동하지 않은 클릭에는 불리지 않는다")] [SerializeField] private UnityEvent _onPresented; [Header("Slide")] [Tooltip("아이템 전환 슬라이드 전체 시간(초). 0이면 연출 없이 즉시 교체")] [Min(0f)] [SerializeField] private float _slideDuration = 0.16f; [Tooltip("슬라이드로 밀려나는 거리(px)")] [Min(0f)] [SerializeField] private float _slideDistance = 500f; private PanelRenderer _panelRenderer; private VisualElement _root; // 전체 토글 대상(#Body) private VisualElement _itemNode; // 슬라이드 대상 (.ItemNode — 그림+설명 묶음) private Image _image; private Label _label; // Step마다 증가 — 연타하면 진행 중이던 슬라이드가 즉시 물러나고 새 슬라이드가 이어받는다 // (Typewriter의 세대 패턴과 동일) private int _slideGen; // 같은 버전으로 콜백이 중복 호출될 때 헛일을 막는다 (Unity 권장 패턴) private int _uiVersion = -1; // 현재 표시 상태. 리로드로 요소가 새로 만들어져도 이 값으로 복원된다. private bool _visible; private int _index; private readonly List _owned = new(); // 창이 떠 있는가 (DialogTouchZone이 창 뒤 월드 클릭을 무시하는 데 쓴다) public bool IsOpen => _visible; private void Awake() { if (Instance != null && Instance != this) { Destroy(gameObject); return; } Instance = this; _panelRenderer = GetComponent(); _panelRenderer.RegisterUIReloadCallback(OnUIReload); } private void Start() { // 창이 열려 있는 동안 아이템을 얻으면 목록이 바로 갱신되게 if (StoryManager.Instance != null) StoryManager.Instance.Changed += OnStoryChanged; // 증거품창 버튼 (Evidence 액션) — 입력 해석은 InputManager가, 여닫는 행동은 창 자신이 맡는다 if (InputManager.Instance != null) { InputManager.Instance.OnEvidence_Event += Toggle; InputManager.Instance.OnEvidencePrev_Event += OnPrevPressed; InputManager.Instance.OnEvidenceNext_Event += OnNextPressed; } } private void OnDestroy() { if (StoryManager.Instance != null) StoryManager.Instance.Changed -= OnStoryChanged; if (InputManager.Instance != null) { InputManager.Instance.OnEvidence_Event -= Toggle; InputManager.Instance.OnEvidencePrev_Event -= OnPrevPressed; InputManager.Instance.OnEvidenceNext_Event -= OnNextPressed; } if (_panelRenderer != null) _panelRenderer.UnregisterUIReloadCallback(OnUIReload); if (Instance == this) Instance = null; } // UI가 (재)구성될 때마다 요소를 다시 잡고 클릭 콜백을 걸고 현재 상태를 되돌린다. private void OnUIReload(PanelRenderer panelRenderer, VisualElement root, int version) { if (_uiVersion == version) return; _uiVersion = version; _root = root.Q("Body"); _itemNode = root.Q(className: "ItemNode"); _image = root.Q("Image"); _label = root.Q