using System.IO; using UnityEditor; using UnityEngine; using Colosseum.Combat.Simulation; namespace Colosseum.Editor { /// /// 허수아비 계산 시뮬레이터 실행 창입니다. /// public sealed class BuildSimulationWindow : EditorWindow { [SerializeField] private BuildSimulationInput buildInput = new BuildSimulationInput(); [SerializeField] private SimulationRuleSet ruleSet = new SimulationRuleSet(); [SerializeField] private RotationPolicy rotationPolicy = new RotationPolicy(); [SerializeField] private SimulationResult lastResult; [SerializeField] private SimulationReportFormat previewFormat = SimulationReportFormat.DetailText; private Vector2 scrollPosition; [MenuItem("Tools/Colosseum/Simulation/Build Simulation Window")] private static void Open() { BuildSimulationWindow window = GetWindow("Build Simulation"); window.minSize = new Vector2(520f, 640f); window.Show(); } private void OnGUI() { scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition); DrawBuildSection(); EditorGUILayout.Space(12f); DrawRuleSection(); EditorGUILayout.Space(12f); DrawRotationSection(); EditorGUILayout.Space(12f); DrawRunSection(); EditorGUILayout.Space(12f); DrawExportSection(); EditorGUILayout.Space(12f); DrawResultSection(); EditorGUILayout.EndScrollView(); } private void DrawBuildSection() { EditorGUILayout.LabelField("Build", EditorStyles.boldLabel); SerializedObject serializedWindow = new SerializedObject(this); SerializedProperty buildProperty = serializedWindow.FindProperty(nameof(buildInput)); DrawProperty(buildProperty, "buildName"); DrawProperty(buildProperty, "strength"); DrawProperty(buildProperty, "dexterity"); DrawProperty(buildProperty, "intelligence"); DrawProperty(buildProperty, "vitality"); DrawProperty(buildProperty, "wisdom"); DrawProperty(buildProperty, "spirit"); DrawProperty(buildProperty, "weapon"); DrawProperty(buildProperty, "directSkillSlots", true); DrawProperty(buildProperty, "passiveTree"); DrawProperty(buildProperty, "selectedPassiveNodes", true); DrawProperty(buildProperty, "passivePreset"); DrawProperty(buildProperty, "loadoutPreset"); serializedWindow.ApplyModifiedProperties(); } private void DrawRuleSection() { EditorGUILayout.LabelField("Simulation", EditorStyles.boldLabel); SerializedObject serializedWindow = new SerializedObject(this); SerializedProperty ruleProperty = serializedWindow.FindProperty(nameof(ruleSet)); DrawProperty(ruleProperty, "ruleName"); DrawProperty(ruleProperty, "durationSeconds"); DrawProperty(ruleProperty, "targetCount"); DrawProperty(ruleProperty, "movementLossSecondsPerCast"); DrawProperty(ruleProperty, "manaRegenPerSecond"); serializedWindow.ApplyModifiedProperties(); } private void DrawRotationSection() { EditorGUILayout.LabelField("Rotation", EditorStyles.boldLabel); SerializedObject serializedWindow = new SerializedObject(this); SerializedProperty rotationProperty = serializedWindow.FindProperty(nameof(rotationPolicy)); DrawProperty(rotationProperty, "policyName"); DrawProperty(rotationProperty, "prioritySlots", true); DrawProperty(rotationProperty, "useFallbackSlot"); DrawProperty(rotationProperty, "fallbackSlotIndex"); DrawProperty(rotationProperty, "delayHighPowerSkillUntilTime"); if (rotationPolicy.DelayHighPowerSkillUntilTime) { DrawProperty(rotationProperty, "highPowerSlotIndex"); DrawProperty(rotationProperty, "highPowerFirstUseTime"); } serializedWindow.ApplyModifiedProperties(); } private void DrawRunSection() { EditorGUILayout.LabelField("Run", EditorStyles.boldLabel); EditorGUILayout.HelpBox("MVP 범위는 단일 대상 피해 계산입니다. 회복/보호막/위협과 버프성 효과는 이후 단계에서 확장합니다.", MessageType.Info); using (new EditorGUI.DisabledScope(buildInput == null || buildInput.LoadoutPreset == null)) { if (GUILayout.Button("Run Simulation", GUILayout.Height(32f))) { lastResult = BuildSimulationEngine.Run(buildInput, ruleSet, rotationPolicy); if (lastResult != null && !string.IsNullOrWhiteSpace(lastResult.DetailText)) { Debug.Log(lastResult.DetailText); } } } } private void DrawResultSection() { EditorGUILayout.LabelField("Preview", EditorStyles.boldLabel); if (lastResult == null) { EditorGUILayout.HelpBox("아직 실행 결과가 없습니다.", MessageType.None); return; } EditorGUILayout.SelectableLabel(lastResult.SummaryLine, EditorStyles.textField, GUILayout.Height(36f)); previewFormat = (SimulationReportFormat)EditorGUILayout.EnumPopup("Format", previewFormat); string previewText = SimulationReportUtility.BuildReport(lastResult, previewFormat); EditorGUILayout.TextArea(previewText, GUILayout.MinHeight(260f)); } private static void DrawProperty(SerializedProperty root, string relativePath, bool includeChildren = false) { SerializedProperty property = root.FindPropertyRelative(relativePath); if (property != null) { EditorGUILayout.PropertyField(property, includeChildren); } } private void DrawExportSection() { EditorGUILayout.LabelField("Export", EditorStyles.boldLabel); if (lastResult == null) { EditorGUILayout.HelpBox("실행 결과가 생기면 복사와 저장 버튼이 활성화됩니다.", MessageType.None); return; } using (new EditorGUILayout.HorizontalScope()) { if (GUILayout.Button("Copy Detail")) { CopyReport(SimulationReportFormat.DetailText); } if (GUILayout.Button("Copy Markdown")) { CopyReport(SimulationReportFormat.Markdown); } if (GUILayout.Button("Copy CSV")) { CopyReport(SimulationReportFormat.Csv); } } using (new EditorGUILayout.HorizontalScope()) { if (GUILayout.Button("Save Markdown")) { SaveReport(SimulationReportFormat.Markdown); } if (GUILayout.Button("Save CSV")) { SaveReport(SimulationReportFormat.Csv); } } } private void CopyReport(SimulationReportFormat format) { string report = SimulationReportUtility.BuildReport(lastResult, format); EditorGUIUtility.systemCopyBuffer = report; Debug.Log($"[BuildSimulation] 결과를 클립보드에 복사했습니다. | Format={format}"); } private void SaveReport(SimulationReportFormat format) { string extension = format == SimulationReportFormat.Csv ? "csv" : "md"; string defaultFileName = SimulationReportUtility.BuildDefaultFileName(lastResult, format); string path = EditorUtility.SaveFilePanel( "시뮬레이션 결과 저장", Application.dataPath, defaultFileName, extension); if (string.IsNullOrWhiteSpace(path)) return; string report = SimulationReportUtility.BuildReport(lastResult, format); File.WriteAllText(path, report); Debug.Log($"[BuildSimulation] 결과 파일을 저장했습니다. | Path={path}"); } } }