using System.Collections.Generic; using System.Text; using UnityEngine; namespace Colosseum.Combat.Simulation { /// /// 스킬별 기여도 요약입니다. /// [System.Serializable] public sealed class SimulationSkillBreakdown { [SerializeField] private string skillName; [Min(0)] [SerializeField] private int castCount; [Min(0f)] [SerializeField] private float totalDamage; public string SkillName => skillName; public int CastCount => castCount; public float TotalDamage => totalDamage; public SimulationSkillBreakdown(string skillName, int castCount, float totalDamage) { this.skillName = skillName ?? "Unknown"; this.castCount = Mathf.Max(0, castCount); this.totalDamage = Mathf.Max(0f, totalDamage); } } /// /// 허수아비 계산 시뮬레이터 결과입니다. /// [System.Serializable] public sealed class SimulationResult { [SerializeField] private string summaryLine = string.Empty; [TextArea(8, 30)] [SerializeField] private string detailText = string.Empty; [SerializeField] private string buildLabel = string.Empty; [SerializeField] private string ruleName = string.Empty; [SerializeField] private string rotationName = string.Empty; [Min(0f)] [SerializeField] private float durationSeconds; [Min(0f)] [SerializeField] private float totalDamage; [Min(0f)] [SerializeField] private float averageDps; [Min(0f)] [SerializeField] private float totalManaUsed; [Min(0f)] [SerializeField] private float averageManaPerSecond; [SerializeField] private float firstCycleEndTime = -1f; [SerializeField] private List skillBreakdowns = new List(); [SerializeField] private List warnings = new List(); public string SummaryLine => summaryLine; public string DetailText => detailText; public string BuildLabel => buildLabel; public string RuleName => ruleName; public string RotationName => rotationName; public float DurationSeconds => durationSeconds; public float TotalDamage => totalDamage; public float AverageDps => averageDps; public float TotalManaUsed => totalManaUsed; public float AverageManaPerSecond => averageManaPerSecond; public float FirstCycleEndTime => firstCycleEndTime; public IReadOnlyList SkillBreakdowns => skillBreakdowns; public IReadOnlyList Warnings => warnings; public void FinalizeResult( string buildLabel, string ruleName, string rotationName, float durationSeconds, float totalDamage, float totalManaUsed, float firstCycleEndTime, List breakdowns, List warnings) { this.buildLabel = buildLabel ?? string.Empty; this.ruleName = ruleName ?? string.Empty; this.rotationName = rotationName ?? string.Empty; this.durationSeconds = Mathf.Max(0f, durationSeconds); this.totalDamage = Mathf.Max(0f, totalDamage); averageDps = this.durationSeconds > 0f ? this.totalDamage / this.durationSeconds : 0f; this.totalManaUsed = Mathf.Max(0f, totalManaUsed); averageManaPerSecond = this.durationSeconds > 0f ? this.totalManaUsed / this.durationSeconds : 0f; this.firstCycleEndTime = firstCycleEndTime; skillBreakdowns = breakdowns ?? new List(); this.warnings = warnings ?? new List(); summaryLine = BuildSummaryLine(); detailText = BuildDetailText(); } private string BuildSummaryLine() { StringBuilder builder = new StringBuilder(); builder.Append("[BuildSimulation] "); builder.Append(buildLabel); builder.Append(" | Rule="); builder.Append(ruleName); builder.Append(" | Rotation="); builder.Append(rotationName); builder.Append(" | Dmg="); builder.Append(totalDamage.ToString("0.##")); builder.Append(" | DPS="); builder.Append(averageDps.ToString("0.##")); builder.Append(" | Mana="); builder.Append(totalManaUsed.ToString("0.##")); builder.Append(" | Cycle="); builder.Append(firstCycleEndTime >= 0f ? firstCycleEndTime.ToString("0.##") + "s" : "미완료"); return builder.ToString(); } private string BuildDetailText() { StringBuilder builder = new StringBuilder(); builder.AppendLine(summaryLine); builder.Append("Duration="); builder.Append(durationSeconds.ToString("0.##")); builder.Append("s | ManaPerSec="); builder.Append(averageManaPerSecond.ToString("0.##")); builder.AppendLine(); if (skillBreakdowns.Count > 0) { builder.AppendLine("Skill Breakdown"); for (int i = 0; i < skillBreakdowns.Count; i++) { SimulationSkillBreakdown entry = skillBreakdowns[i]; builder.Append("- "); builder.Append(entry.SkillName); builder.Append(" | Cast="); builder.Append(entry.CastCount); builder.Append(" | Dmg="); builder.Append(entry.TotalDamage.ToString("0.##")); builder.AppendLine(); } } if (warnings.Count > 0) { builder.AppendLine("Warnings"); for (int i = 0; i < warnings.Count; i++) { builder.Append("- "); builder.Append(warnings[i]); builder.AppendLine(); } } return builder.ToString().TrimEnd(); } } }