using System; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; namespace TKS { // ---------- 데이터(Resources/ChapterMap/Data/world.json · 원화 세션 chap_install.py 생성 · 좌표 = 맵 px · 왼쪽 위 원점 · y 아래로) ---------- [Serializable] public class WorldKitItem { public string name; public float[] pivot; // 이미지 좌표(위 → 아래) 비율 public float[] refSize; // UI 기준 px public float[] numberCenter; // 번호 자리(이미지 좌표 비율) } [Serializable] public class WorldLandmark { public string ko; public float x; public float y; public string role; } [Serializable] public class WorldChapter { public string id; public int no; public string ko; public string years; public string map; public float[] roadX; public float[] roadY; public WorldLandmark[] landmarks; public int nodes; // 스테이지 수(임시 · 기획 세션이 확정) public int stageLink; // 이 챕터 노드가 쓰는 BalanceConfig.stages 인덱스(임시) } [Serializable] public class WorldData { public float[] mapSize; public float[] viewSize; public float dotSpacing; public float[] flagStars; // x0,y0,x1,y1,… (노드 기준 · y 아래로) public float[] flagBossStars; public float[] flagCursor; public float[] flagBossCursor; public float[] flagFrame; public bool showLabels; // 장소 이름판 표시(그림 속 장소 위치와 맞춘 뒤 켬) public WorldKitItem[] kit; public WorldChapter[] chapters; public const string Path = "ChapterMap/Data/world"; public static WorldData Load() { var t = Resources.Load(Path); if (t == null) return null; try { return JsonUtility.FromJson(t.text); } catch (Exception e) { Debug.LogError("[TKS] world.json 읽기 실패: " + e.Message); return null; } } public WorldKitItem Kit(string name) { if (kit != null) foreach (var k in kit) if (k.name == name) return k; return null; } /// 길(road) 을 호 길이로 n 등분한 노드 자리(맵 px). 첫 칸 = 출발 · 마지막 = 보스. public static Vector2[] Slots(WorldChapter c, int n) { var acc = Arc(c); var res = new Vector2[Mathf.Max(0, n)]; float total = acc[acc.Length - 1]; for (int i = 0; i < res.Length; i++) res[i] = At(c, acc, n <= 1 ? 0f : total * i / (n - 1)); return res; } public static float[] Arc(WorldChapter c) { int m = Mathf.Min(c.roadX.Length, c.roadY.Length); var acc = new float[Mathf.Max(1, m)]; for (int i = 1; i < m; i++) acc[i] = acc[i - 1] + Vector2.Distance(new Vector2(c.roadX[i - 1], c.roadY[i - 1]), new Vector2(c.roadX[i], c.roadY[i])); return acc; } public static Vector2 At(WorldChapter c, float[] acc, float len) { int m = acc.Length; if (m == 1) return new Vector2(c.roadX[0], c.roadY[0]); for (int i = 1; i < m; i++) { if (acc[i] >= len) { float t = (len - acc[i - 1]) / Mathf.Max(acc[i] - acc[i - 1], 1e-4f); return Vector2.Lerp(new Vector2(c.roadX[i - 1], c.roadY[i - 1]), new Vector2(c.roadX[i], c.roadY[i]), t); } } return new Vector2(c.roadX[m - 1], c.roadY[m - 1]); } } public enum NodeState { Lock, Open, Clear } /// 임시 진행도(실행 중 메모리 · 저장 없음). 챕터별 깬 노드 수. public static class WorldProgress { static readonly Dictionary cleared = new Dictionary(); public static int Cleared(int chapter) { int v; return cleared.TryGetValue(chapter, out v) ? v : 0; } public static void MarkCleared(int chapter, int node) { if (node + 1 > Cleared(chapter)) cleared[chapter] = node + 1; } public static void Reset() { cleared.Clear(); } } /// /// 게임 시작 화면 = 챕터 월드맵(스크롤 · 상하좌우로 끌기). 길 위 깃발 노드를 누르면 스테이지 팝업 → 출전 = 전투 시작 · /// 전투 결과를 탭하면 월드맵으로 돌아와 다음 노드가 열린다. 그림·좌표·부품 배치 = world.json · 전투 데이터 = BalanceConfig(C45). /// public class WorldMapScreen : MonoBehaviour { public static WorldMapScreen Instance { get; private set; } const string UiRoot = "ChapterMap/UI/"; const string MapRoot = "ChapterMap/Maps/"; static readonly Color Ink = new Color(0.14f, 0.09f, 0.07f); static readonly Color Paper = new Color(1f, 0.98f, 0.93f); BattleManager battle; BattleHUD hud; SlingshotInput sling; WorldData data; GameObject root; CanvasGroup group; ScrollRect scroll; RectTransform viewport; RectTransform content; Image mapImage; Text bannerText; Button prevButton; Button nextButton; RectTransform cursor; Vector2 cursorBase; readonly List spawned = new List(); readonly List