feat: 허수아비 계산 시뮬레이터 추가
- 빌드 입력, 룰셋, 회전 정책, 결과/리포트 모델을 포함한 데미지 계산 시뮬레이터 기반을 추가 - 단일 실행 창과 배치 전수 조사 창, 플레이어 데미지 스윕 메뉴를 추가 - DamageEffect 계산값 접근자를 열어 기존 전투 공식을 시뮬레이터에서 재사용하도록 정리
This commit is contained in:
219
Assets/_Game/Scripts/Editor/BuildSimulationWindow.cs
Normal file
219
Assets/_Game/Scripts/Editor/BuildSimulationWindow.cs
Normal file
@@ -0,0 +1,219 @@
|
||||
using System.IO;
|
||||
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
using Colosseum.Combat.Simulation;
|
||||
|
||||
namespace Colosseum.Editor
|
||||
{
|
||||
/// <summary>
|
||||
/// 허수아비 계산 시뮬레이터 실행 창입니다.
|
||||
/// </summary>
|
||||
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<BuildSimulationWindow>("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}");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user