using System; using System.Collections.Generic; using System.IO; using UnityEngine; // 이야기 진행 상태의 중앙 저장소 (메인 진행도 / 호감도 / 대화 이력 / 선택 이력). // DialogCondition이 대화 활성화 판정의 근거로 조회하고, DialogPlayer가 기록한다. // // 저장: Save()가 JSON 파일로 기록한다. DialogPlayer가 대화 완료 시마다 호출한다. // 로드: 플레이 시작 시 항상 빈 상태로 시작한다 (테스트 반복이 꼬이지 않게). // 이어하기를 만들 때 타이틀 화면 등에서 Load()를 호출하면 된다. public static class StoryState { private static int _mainProgress; private static readonly Dictionary _affection = new(); private static readonly HashSet _completedDialogs = new(); private static readonly HashSet _chosenCodes = new(); // 상태가 바뀔 때마다 발행 (호감도 게이지 등 UI 갱신용) public static event Action Changed; // ── 메인 진행도 ────────────────────────────────────────────── public static int MainProgress { get => _mainProgress; set { if (_mainProgress == value) return; _mainProgress = value; Changed?.Invoke(); } } // ── 호감도 ────────────────────────────────────────────────── public static int GetAffection(CharacterData character) => character != null && _affection.TryGetValue(IdOf(character), out var v) ? v : 0; public static void AddAffection(CharacterData character, int delta) { if (character == null || delta == 0) return; _affection[IdOf(character)] = GetAffection(character) + delta; Changed?.Invoke(); } // CharacterData의 Id를 키로 사용 (비어있으면 에셋 이름) private static string IdOf(CharacterData c) => string.IsNullOrEmpty(c.Id) ? c.name : c.Id; // ── 대화 이력 ──────────────────────────────────────────────── public static bool IsDialogCompleted(string groupName) => _completedDialogs.Contains(groupName); // 완료 기록. 처음 완료한 경우에만 true (진행도 보상 중복 방지용) public static bool MarkDialogCompleted(string groupName) { if (string.IsNullOrEmpty(groupName) || !_completedDialogs.Add(groupName)) return false; Changed?.Invoke(); return true; } // ── 선택 이력 ──────────────────────────────────────────────── public static bool HasChosen(string code) => _chosenCodes.Contains(code); public static void RecordChoice(string code) { if (string.IsNullOrEmpty(code) || !_chosenCodes.Add(code)) return; Changed?.Invoke(); } // ── 저장 / 로드 ────────────────────────────────────────────── [Serializable] private class SaveData { public int MainProgress; public List AffectionIds = new(); public List AffectionValues = new(); public List CompletedDialogs = new(); public List ChosenCodes = new(); } private static string SavePath => Path.Combine(Application.persistentDataPath, "story_state.json"); public static void Save() { var data = new SaveData { MainProgress = _mainProgress }; foreach (var kvp in _affection) { data.AffectionIds.Add(kvp.Key); data.AffectionValues.Add(kvp.Value); } data.CompletedDialogs.AddRange(_completedDialogs); data.ChosenCodes.AddRange(_chosenCodes); File.WriteAllText(SavePath, JsonUtility.ToJson(data, prettyPrint: true)); } // 저장 파일이 있으면 불러온다. 성공 여부 반환. public static bool Load() { if (!File.Exists(SavePath)) return false; var data = JsonUtility.FromJson(File.ReadAllText(SavePath)); if (data == null) return false; _mainProgress = data.MainProgress; _affection.Clear(); for (int i = 0; i < data.AffectionIds.Count && i < data.AffectionValues.Count; i++) _affection[data.AffectionIds[i]] = data.AffectionValues[i]; _completedDialogs.Clear(); _completedDialogs.UnionWith(data.CompletedDialogs); _chosenCodes.Clear(); _chosenCodes.UnionWith(data.ChosenCodes); Changed?.Invoke(); return true; } // 새 게임용 초기화 (저장 파일은 다음 Save 때 덮어써짐) public static void ResetAll() { _mainProgress = 0; _affection.Clear(); _completedDialogs.Clear(); _chosenCodes.Clear(); Changed?.Invoke(); } // 플레이 시작마다 초기화 (Enter Play Mode에서 도메인 리로드를 꺼도 이전 값이 안 남게) [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] private static void ResetOnPlay() { _mainProgress = 0; _affection.Clear(); _completedDialogs.Clear(); _chosenCodes.Clear(); Changed = null; } }