using System.Collections; using System.Collections.Generic; using UnityEngine; namespace TKS { /// /// 가상 실행(dry-run · PD #1098 적 AI 예측 시뮬레이션): Active 동안 ActionResolver 가 연출 없이 한 프레임 안에 끝난다 — /// Fx 이동·대기·투사체 = 즉시 · 팝업·광역 터짐 = 없음 · 유닛 표시(UnitView) 갱신 없음 · 피해 난수 = 분산 0 · 전투 이벤트 카운터 없음. /// 상태는 Snapshot 으로 떠 두고 Restore 로 되돌린다(유닛 · 스킬 상태 · 기믹 레이어). /// public static class Sim { public static bool Active; /// 코루틴을 이 프레임 안에서 끝까지 돌린다(중첩 IEnumerator 포함 · null/기타 yield = 즉시 진행). public static void Run(IEnumerator routine) { if (routine == null) return; var stack = new Stack(); stack.Push(routine); int guard = 0; while (stack.Count > 0) { if (++guard > 200000) { Debug.LogError("[TKS] 가상 실행 루프 상한 초과 — 중단"); return; } var top = stack.Peek(); if (!top.MoveNext()) { stack.Pop(); continue; } var cur = top.Current as IEnumerator; if (cur != null) stack.Push(cur); } } public class UnitSnap { public Unit U; public Vector2 Pos; public int Hp, MaxHp, Atk, Def; public bool Acted, Bonus, Overwatch, Collider, Active; } public class SkillSnap { public UnitSkills S; public int ReadyTurn, BonusMovesLeft, PierceHits; public bool Reserved, Armed, BonusAttack, OwUnlimited; public float OwMult, OwAdd; } public class Snapshot { public readonly List Units = new List(); public readonly List Skills = new List(); public List Timed; public int LogCount; public GimmickLayer.Snapshot Gimmicks; public string Message; } public static Snapshot Take(BattleManager bm) { var s = new Snapshot { Message = bm.LastMessage }; foreach (var u in bm.Units) { if (u == null) continue; s.Units.Add(new UnitSnap { U = u, Pos = u.Position, Hp = u.Hp, MaxHp = u.MaxHp, Atk = u.Atk, Def = u.Def, Acted = u.ActedThisTurn, Bonus = u.BonusMoveAvailable, Overwatch = u.Overwatch, Collider = u.Collider != null && u.Collider.enabled, Active = u.gameObject.activeSelf }); } var sys = bm.Skills; if (sys != null) { foreach (var e in sys.Entries) { s.Skills.Add(new SkillSnap { S = e, ReadyTurn = e.ReadyTurn, BonusMovesLeft = e.BonusMovesLeft, PierceHits = e.PierceHits, Reserved = e.Reserved, Armed = e.Armed, BonusAttack = e.BonusAttack, OwUnlimited = e.OwUnlimited, OwMult = e.OwMult, OwAdd = e.OwAdd }); } s.Timed = new List(sys.TimedEffects); s.LogCount = sys.Log.Count; } if (GimmickLayer.Current != null) s.Gimmicks = GimmickLayer.Current.Take(); return s; } public static void Restore(BattleManager bm, Snapshot s) { foreach (var x in s.Units) { var u = x.U; if (u == null) continue; u.Position = x.Pos; u.Hp = x.Hp; u.MaxHp = x.MaxHp; u.Atk = x.Atk; u.Def = x.Def; u.ActedThisTurn = x.Acted; u.BonusMoveAvailable = x.Bonus; u.Overwatch = x.Overwatch; if (u.Collider != null) u.Collider.enabled = x.Collider; if (u.gameObject.activeSelf != x.Active) u.gameObject.SetActive(x.Active); } var sys = bm.Skills; if (sys != null) { foreach (var k in s.Skills) { var e = k.S; e.ReadyTurn = k.ReadyTurn; e.BonusMovesLeft = k.BonusMovesLeft; e.PierceHits = k.PierceHits; e.Reserved = k.Reserved; e.Armed = k.Armed; e.BonusAttack = k.BonusAttack; e.OwUnlimited = k.OwUnlimited; e.OwMult = k.OwMult; e.OwAdd = k.OwAdd; } if (s.Timed != null) { sys.TimedEffects.Clear(); sys.TimedEffects.AddRange(s.Timed); } if (sys.Log.Count > s.LogCount) sys.Log.RemoveRange(s.LogCount, sys.Log.Count - s.LogCount); } if (GimmickLayer.Current != null && s.Gimmicks != null) GimmickLayer.Current.Restore(s.Gimmicks); bm.LastMessage = s.Message; Physics2D.SyncTransforms(); } } }