using System.Collections; using System.Collections.Generic; using System.IO; using UnityEngine; namespace TKS.OutGame { /// /// 스킬 화면 검증 캡처(에디터 DataImporter.CaptureSkills 가 SessionState 로 출력 폴더를 넘기면 생성 · 세이브 = 메모리 전용 신규 유저 · PD #1093 B). /// ① 스킬 레벨업(영웅 상세 → 팝업 · 액티브 Lv+1 뒤) ② 스킬 버튼(전투 · 관우 선택) ③ 액티브 발동(장비 장판 일갈 · 이름 글자 + 플래시 + 밀기) /// ④ 패시브 표시(조운 관통 → 상산의 창 HUD 한 줄). 캡처를 위해 적 1기씩을 아군 앞에 옮겨 둔다(그 밖은 실제 처리). /// public class SkillCaptureDriver : MonoBehaviour { public const string DirKey = "TKS_SKILL_CAPTURE_DIR"; public string OutputDir; public int Width = 720; public int Height = 1280; readonly List log = new List(); int shot = 1; #if UNITY_EDITOR [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)] static void AutoSpawn() { string dir = UnityEditor.SessionState.GetString(DirKey, ""); if (string.IsNullOrEmpty(dir)) return; UnityEditor.SessionState.EraseString(DirKey); var d = new GameObject("SkillCaptureDriver").AddComponent(); d.OutputDir = dir; } #endif IEnumerator Start() { Directory.CreateDirectory(OutputDir); yield return null; yield return null; var root = OutGameRoot.Instance; var cam = Camera.main; if (root == null || cam == null) { Log("FAIL: OutGameRoot 또는 카메라 없음"); Finish(1); yield break; } var s = root.Session; Log("memoryOnly=" + SaveStore.MemoryOnly + " newUser=" + s.IsNewUser + " books=" + s.Save.inv.books.skill + " gold=" + s.Save.gold); // ① 스킬 레벨업: 첫 시작 영웅(★3 = 액티브 상한 +1) 액티브 Lv1 → 2 var id = s.Db.Economy.StartHeroes[0]; var h = s.Def(id); var hs = s.Save.Hero(id); root.ShowHero(id); yield return Wait(); Log("skill cap active=" + SkillRules.Cap(s.Db, h, hs, 0) + " passive=" + SkillRules.Cap(s.Db, h, hs, 1) + " star=" + hs.star + " grade=" + hs.grade); Log("levelup active → " + (SkillRules.LevelUp(s.Db, s.Save, h, hs, 0) ?? "ok") + " lv=" + hs.skillLv[0] + " books=" + s.Save.inv.books.skill + " gold=" + s.Save.gold); s.Commit(); root.HeroDetail.Refresh(); root.HeroDetail.OnSkillUp(); yield return Wait(); yield return Shot(cam, "skill_levelup_" + id); root.CloseModal(); // 전투 1-1 root.ShowLobby(); root.OpenWorldMap(); yield return Wait(); var wm = root.Map; wm.OpenPopup(0); yield return Wait(); wm.ConfirmPopup(); var bm = root.Battle; float t0 = Time.realtimeSinceStartup; while (bm.Phase != BattlePhase.PlayerTurn && Time.realtimeSinceStartup - t0 < 10f) yield return null; var byHero = new Dictionary(); foreach (var u in bm.Units) { if (u == null || u.Faction != Faction.Player) continue; var us = bm.Skills.Of(u); Log(" " + u.DisplayName + " " + u.Type + " active=" + (us != null && us.Active != null ? us.Active.Id + " Lv" + us.ActiveLv : "-") + " passive=" + (us != null && us.Passive != null ? us.Passive.Id + " ×" + us.PassiveMult : "-") + " hp" + u.MaxHp + " @" + u.Position); if (us != null) byHero[us.HeroId] = u; } // ② 스킬 버튼: 관우 선택(다음 행동형 · 사용 가능) Unit gy; if (byHero.TryGetValue("guanyu", out gy)) { bm.Sling.Select(gy); yield return Wait(); Log("button: " + bm.Hud.SkillButtonText); yield return Shot(cam, "skill_button_guanyu"); } // ③ 액티브 발동: 장비 장판 일갈(즉시 · 반경 안 적 밀기 + 이번 적 턴 교전 반응 없음) Unit zf; if (byHero.TryGetValue("zhangfei", out zf)) { var foe = Nearest(bm, zf); if (foe != null) foe.Position = zf.Position + new Vector2(0f, 2f); Vector2 before = foe != null ? foe.Position : Vector2.zero; bm.Sling.Select(zf); yield return null; Log("press: " + bm.Hud.PressSkill()); yield return new WaitForSecondsRealtime(0.18f); yield return Shot(cam, "active_zhangfei"); t0 = Time.realtimeSinceStartup; while (bm.Phase != BattlePhase.PlayerTurn && Time.realtimeSinceStartup - t0 < 10f) yield return null; Log("pushed " + (foe != null ? foe.DisplayName + " " + before + "→" + foe.Position : "-") + " readyTurn=" + bm.Skills.Of(zf).ReadyTurn + " acted=" + zf.ActedThisTurn); } // ④ 패시브 표시: 조운 관통(상산의 창 = 넉백 ×) → HUD 한 줄 Unit zy; if (byHero.TryGetValue("zhaoyun", out zy)) { var foe = Nearest(bm, zy); if (foe != null) { foe.Position = zy.Position + new Vector2(0f, 3.5f); bm.RequestPlayerAction(zy, foe.Position); t0 = Time.realtimeSinceStartup; while (bm.Phase != BattlePhase.PlayerTurn && Time.realtimeSinceStartup - t0 < 10f) yield return null; } bm.Sling.Select(zy); yield return null; Log("log: " + string.Join(" / ", bm.Skills.RecentLog(30f, 8).ToArray())); Log("msg: " + bm.LastMessage); yield return Shot(cam, "passive_zhaoyun"); } Finish(0); } static Unit Nearest(BattleManager bm, Unit from) { Unit best = null; float bd = float.MaxValue; foreach (var e in bm.AliveUnits(Faction.Enemy)) { float d = Vector2.Distance(e.Position, from.Position); if (d < bd) { bd = d; best = e; } } return best; } static IEnumerator Wait() { yield return null; yield return new WaitForSecondsRealtime(0.3f); } IEnumerator Shot(Camera cam, string name) { string file = Path.Combine(OutputDir, shot.ToString("00") + "_" + name + ".png"); shot++; yield return CaptureUtil.Capture(cam, Width, Height, file); Log("shot " + file); } void Log(string s) { log.Add(s); Debug.Log("[SkillCapture] " + s); try { File.WriteAllLines(Path.Combine(OutputDir, "capture_log.txt"), log.ToArray()); } catch { } } void Finish(int code) { Log("finish code=" + code); #if UNITY_EDITOR UnityEditor.EditorApplication.Exit(code); #else Application.Quit(code); #endif } } }