Files
StoryGame_Unity/Assets/02_Scripts/Communication/Dialog/HiddenBranchResolver.cs
2026-07-24 21:11:32 +09:00

91 lines
3.5 KiB
C#

using System;
using System.Collections.Generic;
using UnityEngine;
// 히든 분기 감시 허브 (씬 오브젝트 불필요, 순수 static).
//
// 대화 노드가 재생되는 동안 DialogPlayer가 Arm(분기 목록)으로 무장하고, 끝나면 Disarm()한다.
// 그 창(무장 구간) 안에서 누군가 Fire(key)를 호출하면 키에 맞는 분기가 골라지고,
// DialogPlayer가 이를 감지해 Next(또는 선택지) 대신 그 분기로 몰래 이동한다.
// Fire를 호출하는 쪽은 캐릭터 부위 터치 존, 증거품 제출 UI 등 무엇이든 될 수 있다.
//
// 무장돼 있지 않으면 어떤 Fire도 무시된다 → 평소엔 존재하지 않는 것처럼 동작(=히든).
public static class HiddenBranchResolver
{
// 지금 무장된 노드의 히든 분기 목록 (null이면 무장 해제 상태)
private static IReadOnlyList<HiddenBranch> _branches;
// 지금 히든 분기를 받을 수 있는 상태인가 (노드 재생 중)
public static bool Armed => _branches != null;
// 이번 무장 구간에서 발동한 분기의 인덱스 (-1이면 아직 발동 안 함)
public static int FiredIndex { get; private set; } = -1;
// 이번 무장 구간에서 발동했는가
public static bool Fired => FiredIndex >= 0;
// 발동 즉시 알림 — 떠 있는 선택지 메뉴를 그 자리에서 취소시키는 용도
public static event Action FiredEvent;
// Enter Play Mode(도메인 리로드 off)에서 이전 상태가 안 남게
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
private static void ResetStatics()
{
_branches = null;
FiredIndex = -1;
FiredEvent = null;
}
// 노드 진입 시 무장.
public static void Arm(IReadOnlyList<HiddenBranch> branches)
{
_branches = branches;
FiredIndex = -1;
}
// 노드 종료 시 해제.
public static void Disarm()
{
_branches = null;
FiredIndex = -1;
}
// 발동시키는 쪽(터치 존·증거품 UI 등)이 호출. 무장 중이고 키가 맞을 때만 1회 발동.
//
// 매칭 규칙: 키가 정확히 일치하는 분기가 최우선, 없으면 Key가 빈 분기(catch-all).
// 덕분에 "정답 키는 각자 분기로, 나머지는 전부 오답 분기로"가 자연스럽게 나온다.
// Destination이 비어 있는 분기는 후보에서 제외한다(아무 데도 못 가는 분기로 새는 것 방지).
//
// 반환: 이번 호출로 실제 발동했으면 true (호출 측이 "그때만" 연출·아이템 소비 등에 쓸 수 있게).
public static bool Fire(string key)
{
if (_branches == null || FiredIndex >= 0) return false;
int exact = -1;
int catchAll = -1;
for (int i = 0; i < _branches.Count; i++)
{
var branch = _branches[i];
if (branch == null || branch.Destination == null) continue;
if (string.IsNullOrEmpty(branch.Key))
{
if (catchAll < 0) catchAll = i; // 첫 catch-all만 사용
}
else if (branch.Key == key)
{
exact = i;
break; // 정확 일치는 더 볼 것 없음
}
}
int picked = exact >= 0 ? exact : catchAll;
if (picked < 0) return false; // 맞는 분기 없음 — 아무 일도 안 일어남
FiredIndex = picked;
FiredEvent?.Invoke();
return true;
}
}