TH1/Unity/Assets/Scripts/TH1_Logic/Editor/OssStatisticEditorWindow.cs
2026-03-24 15:04:00 +08:00

602 lines
25 KiB
C#

/*
* @Author: 白哉
* @Description: 编辑器
* @Date: 2025年04月22日 星期二 15:04:14
* @Modify:
*/
using System.Collections.Generic;
using System.IO;
using Logic.Achievement;
using Logic.CrashSight;
using Logic.HeroTask;
using Logic.Multilingual;
using MemoryPack;
using NUnit.Framework;
using RuntimeData;
using TH1_Logic.Collect;
using TH1_Logic.Core;
using TH1_Logic.HeroTask;
using TH1_Logic.Oss;
using UnityEditor;
using UnityEngine;
namespace Logic.Editor
{
public enum OssStatisticType
{
Unit,
Tech,
Empire,
}
public class UnitStatisticLimit
{
public bool LimitUnitType;
public UnitType UnitType;
public bool LimitGiantType;
public GiantType GiantType;
public bool LimitLevel;
public int Level;
public bool LimitCarryUnitType;
public UnitType CarryUnitType;
public bool LimitCarryGiantType;
public GiantType CarryGiantType;
public bool LimitCarryLevel;
public int CarryLevel;
}
public class UnitStatisticResult
{
public float MatchCount; // 有效局数
public float TurnCount; // 有效回合数
public float AppearCount; // 单局出战次数
public float EarliestAppearTurn; // 单局最早出战回合
public float DeathCount; // 单局死亡次数
public float KillCount; // 单局击杀次数
public float TotalDamageDealt; // 单局总造成伤害
public float TotalDamageTaken; // 单局总承受伤害
public float AverageDamageDealt; // 单局平均造成伤害
public float AverageDamageTaken; // 单局平均承受伤害
public float TransformFromCount; // 单局被转化次数(作为来源)
public float TransformToCount; // 单局转化成此单位次数
public void Clear()
{
MatchCount = 0;
TurnCount = 0;
AppearCount = 0;
EarliestAppearTurn = 0;
DeathCount = 0;
KillCount = 0;
TotalDamageDealt = 0;
TotalDamageTaken = 0;
AverageDamageDealt = 0;
AverageDamageTaken = 0;
TransformFromCount = 0;
TransformToCount = 0;
}
}
public class TechStatisticLimit
{
public bool LimitEmpire;
public Empire Empire;
public bool LimitTechType;
public TechType TechType;
}
public class TechStatisticResult
{
public float LearnMatchCount; // 学习局数
public float LearnCount; // 单局平均学习次数
public float LearnTurn; // 单局平均首次学习时间
public void Clear()
{
LearnMatchCount = 0;
LearnCount = 0;
LearnTurn = 0;
}
}
public class EmpireStatisticLimit
{
public bool LimitEmpire;
public Empire Empire;
}
public class EmpireStatisticResult
{
public int MatchCount;
public int TurnCount;
public float CoinPerTurn; // 单局回合金币
public float TechPointPerTurn; // 单局回合科技点
public float CityCount; // 单局城市数量
public float MaxCityLevel; // 单局最高城市等级
public float CityLevel; // 单局城市等级
public float TechCount; // 单局科技数量
public void Clear()
{
MatchCount = 0;
CoinPerTurn = 0;
TechPointPerTurn = 0;
CityCount = 0;
MaxCityLevel = 0;
CityLevel = 0;
TechCount = 0;
}
}
public class OssStatisticEditorWindow : EditorWindow
{
// 滑条
private Vector2 _barPosition;
// 背景
private GUIStyle _redBoxStyle;
private GUIStyle _whiteBoxStyle;
private string Path = "F:/2026.3.19oss";
// 筛选类
private OssStatisticType _statisticType;
private UnitStatisticLimit _unitLimit;
private UnitStatisticResult _unitResult;
private TechStatisticLimit _techLimit;
private TechStatisticResult _techResult;
private EmpireStatisticLimit _empireLimit;
private EmpireStatisticResult _empireResult;
private int _totalFileCount;
private int _validFileCount;
[MenuItem("Tools/OSS数据统计")]
private static void ShowWindow()
{
var window = CreateWindow<OssStatisticEditorWindow>();
window.titleContent = new GUIContent("OSS数据统计");
window.Show();
}
private void OnGUI()
{
_unitLimit ??= new UnitStatisticLimit();
_unitResult ??= new UnitStatisticResult();
_techLimit ??= new TechStatisticLimit();
_techResult ??= new TechStatisticResult();
_empireLimit ??= new EmpireStatisticLimit();
_empireResult ??= new EmpireStatisticResult();
if (_redBoxStyle == null)
{
_redBoxStyle = InspectorUtils.GetHelpBoxStyle();
InspectorUtils.AddBorder(_redBoxStyle, new Color(0.5f, 0.4f, 0.4f, 0.6f));
}
if (_whiteBoxStyle == null)
{
_whiteBoxStyle = InspectorUtils.GetHelpBoxStyle();
InspectorUtils.AddBorder(_whiteBoxStyle, new Color(1f, 1f, 1f, 0.2f));
}
GUI.skin.button.wordWrap = true;
_barPosition = EditorGUILayout.BeginScrollView(_barPosition);
EditorGUILayout.BeginHorizontal();
InspectorUtils.InspectorTextWidthRich($"<b> 筛选类型 : </b>");
_statisticType = (OssStatisticType) EditorGUILayout.EnumPopup(_statisticType, GUILayout.Width(200));
EditorGUILayout.EndHorizontal();
if (_statisticType == OssStatisticType.Unit) OnUnitStatisticGUI();
else if (_statisticType == OssStatisticType.Tech) OnTechStatisticGUI();
else if (_statisticType == OssStatisticType.Empire) OnEmpireStatisticGUI();
EditorGUILayout.EndScrollView();
}
private void OnUnitStatisticGUI()
{
EditorGUILayout.BeginVertical(_whiteBoxStyle);
EditorGUILayout.BeginHorizontal();
InspectorUtils.InspectorTextWidthRich($"<b> 限制 UnitType : </b> ");
_unitLimit.LimitUnitType = EditorGUILayout.Toggle(_unitLimit.LimitUnitType, GUILayout.Width(20));
if (_unitLimit.LimitUnitType) _unitLimit.UnitType = (UnitType) EditorGUILayout.EnumPopup(_unitLimit.UnitType, GUILayout.Width(150));
EditorGUILayout.EndHorizontal();
EditorGUILayout.BeginHorizontal();
InspectorUtils.InspectorTextWidthRich($"<b> 限制 GiantType : </b> ");
_unitLimit.LimitGiantType = EditorGUILayout.Toggle(_unitLimit.LimitGiantType, GUILayout.Width(20));
if (_unitLimit.LimitGiantType) _unitLimit.GiantType = (GiantType) EditorGUILayout.EnumPopup(_unitLimit.GiantType, GUILayout.Width(150));
EditorGUILayout.EndHorizontal();
EditorGUILayout.BeginHorizontal();
InspectorUtils.InspectorTextWidthRich($"<b> 限制 Level : </b> ");
_unitLimit.LimitLevel = EditorGUILayout.Toggle(_unitLimit.LimitLevel, GUILayout.Width(20));
if (_unitLimit.LimitLevel) _unitLimit.Level = EditorGUILayout.IntField(_unitLimit.Level, GUILayout.Width(150));
EditorGUILayout.EndHorizontal();
EditorGUILayout.BeginHorizontal();
InspectorUtils.InspectorTextWidthRich($"<b> 限制 CarryUnitType : </b> ");
_unitLimit.LimitCarryUnitType = EditorGUILayout.Toggle(_unitLimit.LimitCarryUnitType, GUILayout.Width(20));
if (_unitLimit.LimitCarryUnitType) _unitLimit.CarryUnitType = (UnitType) EditorGUILayout.EnumPopup(_unitLimit.CarryUnitType, GUILayout.Width(150));
EditorGUILayout.EndHorizontal();
EditorGUILayout.BeginHorizontal();
InspectorUtils.InspectorTextWidthRich($"<b> 限制 CarryGiantType : </b> ");
_unitLimit.LimitCarryGiantType = EditorGUILayout.Toggle(_unitLimit.LimitCarryGiantType, GUILayout.Width(20));
if (_unitLimit.LimitCarryGiantType) _unitLimit.CarryGiantType = (GiantType) EditorGUILayout.EnumPopup(_unitLimit.CarryGiantType, GUILayout.Width(150));
EditorGUILayout.EndHorizontal();
EditorGUILayout.BeginHorizontal();
InspectorUtils.InspectorTextWidthRich($"<b> 限制 CarryLevel : </b> ");
_unitLimit.LimitCarryLevel = EditorGUILayout.Toggle(_unitLimit.LimitCarryLevel, GUILayout.Width(20));
if (_unitLimit.LimitCarryLevel) _unitLimit.CarryLevel = EditorGUILayout.IntField(_unitLimit.CarryLevel, GUILayout.Width(150));
EditorGUILayout.EndHorizontal();
if (InspectorUtils.InspectorButtonWithTextWidth($"计算!!!"))
{
CalculateUnitStatistic();
}
EditorGUILayout.EndVertical();
EditorGUILayout.Space();
EditorGUILayout.BeginVertical(_redBoxStyle);
if (_unitResult != null)
{
EditorGUILayout.BeginVertical(_redBoxStyle);
InspectorUtils.InspectorTextWidthRich($"<b>===== 统计结果 =====</b>");
InspectorUtils.InspectorTextWidthRich(
$"<b>扫描文件数:</b> {_totalFileCount} <b>有效文件数:</b> {_validFileCount}");
InspectorUtils.InspectorTextWidthRich($"<b>有效局数:</b> {_unitResult.MatchCount}");
InspectorUtils.InspectorTextWidthRich($"<b>单局出战次数:</b> {_unitResult.AppearCount}");
InspectorUtils.InspectorTextWidthRich($"<b>单局最早出战回合:</b> Turn {_unitResult.EarliestAppearTurn}");
InspectorUtils.InspectorTextWidthRich($"<b>单局击杀次数:</b> {_unitResult.KillCount}");
InspectorUtils.InspectorTextWidthRich($"<b>单局死亡次数:</b> {_unitResult.DeathCount}");
InspectorUtils.InspectorTextWidthRich($"<b>单局总造成伤害:</b> {_unitResult.TotalDamageDealt}");
InspectorUtils.InspectorTextWidthRich($"<b>单局总承受伤害:</b> {_unitResult.TotalDamageTaken}");
InspectorUtils.InspectorTextWidthRich($"<b>单局每个单位造成伤害:</b> {_unitResult.AverageDamageDealt}");
InspectorUtils.InspectorTextWidthRich($"<b>单局每个单位承受伤害:</b> {_unitResult.AverageDamageTaken}");
EditorGUILayout.EndVertical();
}
EditorGUILayout.EndVertical();
}
private void OnTechStatisticGUI()
{
EditorGUILayout.BeginVertical(_whiteBoxStyle);
EditorGUILayout.BeginHorizontal();
InspectorUtils.InspectorTextWidthRich($"<b> 限制 Empire : </b> ");
_techLimit.LimitEmpire = EditorGUILayout.Toggle(_techLimit.LimitEmpire, GUILayout.Width(20));
if (_techLimit.LimitEmpire)
{
_techLimit.Empire.Civ = (CivEnum)EditorGUILayout.EnumPopup(_techLimit.Empire.Civ, GUILayout.Width(150));
_techLimit.Empire.Force = (ForceEnum)EditorGUILayout.EnumPopup(_techLimit.Empire.Force, GUILayout.Width(150));
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.BeginHorizontal();
InspectorUtils.InspectorTextWidthRich($"<b> 限制 Tech : </b> ");
_techLimit.LimitEmpire = EditorGUILayout.Toggle(_techLimit.LimitEmpire, GUILayout.Width(20));
if (_techLimit.LimitEmpire) _techLimit.TechType = (TechType) EditorGUILayout.EnumPopup(_techLimit.TechType, GUILayout.Width(150));
EditorGUILayout.EndHorizontal();
if (InspectorUtils.InspectorButtonWithTextWidth($"计算!!!"))
{
CalculateTechStatistic();
}
EditorGUILayout.EndVertical();
EditorGUILayout.Space();
EditorGUILayout.BeginVertical(_redBoxStyle);
if (_techResult != null)
{
EditorGUILayout.BeginVertical(_redBoxStyle);
InspectorUtils.InspectorTextWidthRich($"<b>===== 统计结果 =====</b>");
InspectorUtils.InspectorTextWidthRich(
$"<b>扫描文件数:</b> {_totalFileCount} <b>有效文件数:</b> {_validFileCount}");
InspectorUtils.InspectorTextWidthRich($"<b>有效局数:</b> {_techResult.LearnMatchCount}");
InspectorUtils.InspectorTextWidthRich($"<b>平均学习次数(所有局):</b> {_techResult.LearnCount}");
InspectorUtils.InspectorTextWidthRich($"<b>单局平均首次学习时间(只统计学习局):</b> Turn {_techResult.LearnTurn}");
EditorGUILayout.EndVertical();
}
EditorGUILayout.EndVertical();
}
private void OnEmpireStatisticGUI()
{
EditorGUILayout.BeginVertical(_whiteBoxStyle);
EditorGUILayout.BeginHorizontal();
InspectorUtils.InspectorTextWidthRich($"<b> 限制 Empire : </b> ");
_empireLimit.LimitEmpire = EditorGUILayout.Toggle(_empireLimit.LimitEmpire, GUILayout.Width(20));
if (_empireLimit.LimitEmpire)
{
_empireLimit.Empire.Civ = (CivEnum)EditorGUILayout.EnumPopup(_empireLimit.Empire.Civ, GUILayout.Width(150));
_empireLimit.Empire.Force = (ForceEnum)EditorGUILayout.EnumPopup(_empireLimit.Empire.Force, GUILayout.Width(150));
}
EditorGUILayout.EndHorizontal();
if (InspectorUtils.InspectorButtonWithTextWidth($"计算!!!"))
{
CalculateEmpireStatistic();
}
EditorGUILayout.EndVertical();
EditorGUILayout.Space();
EditorGUILayout.BeginVertical(_redBoxStyle);
if (_empireResult != null)
{
EditorGUILayout.BeginVertical(_redBoxStyle);
InspectorUtils.InspectorTextWidthRich($"<b>===== 统计结果 =====</b>");
InspectorUtils.InspectorTextWidthRich(
$"<b>扫描文件数:</b> {_totalFileCount} <b>有效文件数:</b> {_validFileCount}");
InspectorUtils.InspectorTextWidthRich($"<b>有效局数:</b> {_empireResult.MatchCount}");
InspectorUtils.InspectorTextWidthRich($"<b>单局回合金币(有效局):</b> {_empireResult.CoinPerTurn}");
InspectorUtils.InspectorTextWidthRich($"<b>单局回合科技点(有效局):</b> {_empireResult.TechPointPerTurn}");
InspectorUtils.InspectorTextWidthRich($"<b>单局城市数量(有效局):</b> {_empireResult.CityCount}");
InspectorUtils.InspectorTextWidthRich($"<b>单局最高城市等级(有效局):</b> {_empireResult.MaxCityLevel}");
InspectorUtils.InspectorTextWidthRich($"<b>单局城市等级(有效局):</b> {_empireResult.CityLevel}");
InspectorUtils.InspectorTextWidthRich($"<b>单局科技数量(有效局):</b> {_empireResult.TechCount}");
EditorGUILayout.EndVertical();
}
EditorGUILayout.EndVertical();
}
private void CalculateUnitStatistic()
{
_unitResult = new UnitStatisticResult();
_totalFileCount = 0;
_validFileCount = 0;
if (string.IsNullOrEmpty(Path) || !Directory.Exists(Path))
{
LogSystem.LogError($"路径不存在: {Path}");
return;
}
var files = Directory.GetFiles(Path, "*.dat", SearchOption.AllDirectories);
_totalFileCount = files.Length;
foreach (var file in files)
{
try
{
var bytes = File.ReadAllBytes(file);
var ossData = MemoryPackSerializer.Deserialize<OssData>(bytes);
if (ossData?.CollectData == null) continue;
_validFileCount++;
ProcessCollectData(ossData.CollectData, _unitResult);
}
catch (System.Exception e)
{
LogSystem.LogError($"反序列化失败: {file}\n{e.Message}");
}
}
_unitResult.AppearCount /= _unitResult.MatchCount;
_unitResult.DeathCount /= _unitResult.MatchCount;
_unitResult.KillCount /= _unitResult.MatchCount;
_unitResult.TotalDamageDealt /= _unitResult.MatchCount;
_unitResult.TotalDamageTaken /= _unitResult.MatchCount;
_unitResult.AverageDamageDealt /= _unitResult.MatchCount;
_unitResult.AverageDamageTaken /= _unitResult.MatchCount;
_unitResult.EarliestAppearTurn /= _unitResult.MatchCount;
LogSystem.LogInfo($"统计完成: 共扫描 {_totalFileCount} 个文件,有效 {_validFileCount} 个");
}
private void CalculateTechStatistic()
{
_techResult = new TechStatisticResult();
_totalFileCount = 0;
_validFileCount = 0;
if (string.IsNullOrEmpty(Path) || !Directory.Exists(Path))
{
LogSystem.LogError($"路径不存在: {Path}");
return;
}
var files = Directory.GetFiles(Path, "*.dat", SearchOption.AllDirectories);
_totalFileCount = files.Length;
foreach (var file in files)
{
try
{
var bytes = File.ReadAllBytes(file);
var ossData = MemoryPackSerializer.Deserialize<OssData>(bytes);
if (ossData?.CollectData == null) continue;
_validFileCount++;
ProcessCollectData(ossData.CollectData, _techResult);
}
catch (System.Exception e)
{
LogSystem.LogError($"反序列化失败: {file}\n{e.Message}");
}
}
_techResult.LearnCount /= _techResult.LearnMatchCount;
_techResult.LearnTurn /= _techResult.LearnMatchCount;
LogSystem.LogInfo($"统计完成: 共扫描 {_totalFileCount} 个文件,有效 {_validFileCount} 个");
}
private void CalculateEmpireStatistic()
{
_empireResult = new EmpireStatisticResult();
_totalFileCount = 0;
_validFileCount = 0;
if (string.IsNullOrEmpty(Path) || !Directory.Exists(Path))
{
LogSystem.LogError($"路径不存在: {Path}");
return;
}
var files = Directory.GetFiles(Path, "*.dat", SearchOption.AllDirectories);
_totalFileCount = files.Length;
foreach (var file in files)
{
try
{
var bytes = File.ReadAllBytes(file);
var ossData = MemoryPackSerializer.Deserialize<OssData>(bytes);
if (ossData?.CollectData == null) continue;
_validFileCount++;
ProcessCollectData(ossData.CollectData, _empireResult);
}
catch (System.Exception e)
{
LogSystem.LogError($"反序列化失败: {file}\n{e.Message}");
}
}
_empireResult.CoinPerTurn /= _empireResult.MatchCount;
_empireResult.TechPointPerTurn /= _empireResult.MatchCount;
_empireResult.CityCount /= _empireResult.MatchCount;
_empireResult.MaxCityLevel /= _empireResult.MatchCount;
_empireResult.CityLevel /= _empireResult.MatchCount;
_empireResult.TechCount /= _empireResult.MatchCount;
LogSystem.LogInfo($"统计完成: 共扫描 {_totalFileCount} 个文件,有效 {_validFileCount} 个");
}
private void ProcessCollectData(CollectData collectData, UnitStatisticResult result)
{
// 1. 出战次数 & 最早出战回合:从 AddUnits 统计
var turn = -1;
var appearCount = 0;
foreach (var addUnit in collectData.AddUnits)
{
if (!MatchUnitFullType(addUnit.UnitType)) continue;
appearCount++;
if (turn < 0 || addUnit.Turn < turn) turn = (int)addUnit.Turn;
}
if (appearCount == 0) return;
result.MatchCount++;
result.EarliestAppearTurn += turn;
result.AppearCount += appearCount;
// 2. 伤害统计 & 击杀/死亡:从 Damages 统计
foreach (var dmg in collectData.Damages)
{
// 作为攻击方:造成伤害 & 击杀
if (MatchUnitFullType(dmg.OriginUnitType))
{
result.TotalDamageDealt += dmg.OriginDamage;
result.AverageDamageDealt += dmg.OriginDamage / (float)appearCount;
if (dmg.IsKill) result.KillCount++;
}
// 作为被攻击方:承受伤害 & 死亡
if (MatchUnitFullType(dmg.TargetUnitType))
{
result.TotalDamageTaken += dmg.TargetDamage;
result.AverageDamageTaken += dmg.TargetDamage / (float)appearCount;
if (dmg.IsKill) result.DeathCount++;
}
}
}
private void ProcessCollectData(CollectData collectData, TechStatisticResult result)
{
var learnTurn = -1;
foreach (var techCollectData in collectData.LearnTechs)
{
if (!MatchTech(techCollectData.Empire, techCollectData.TechType)) continue;
result.LearnCount++;
if (learnTurn < 0 || learnTurn > techCollectData.Turn) learnTurn = (int)techCollectData.Turn;
}
if (learnTurn > 0)
{
result.LearnTurn += learnTurn;
result.LearnMatchCount++;
}
LogSystem.LogInfo($"result.LearnCount {result.LearnCount} result.LearnMatchCount{result.LearnMatchCount}");
}
private void ProcessCollectData(CollectData collectData, EmpireStatisticResult result)
{
var resultCache = new EmpireStatisticResult();
foreach (var turnStartData in collectData.OnTurnStarts)
{
if (!MatchEmpire(turnStartData.Empire)) continue;
resultCache.CoinPerTurn += turnStartData.PlayerCoinPerTurn;
resultCache.TechPointPerTurn += turnStartData.PlayerTechPointPerTurn;
resultCache.CityCount += turnStartData.CityCount;
resultCache.MaxCityLevel += turnStartData.MaxCityLevel;
resultCache.CityLevel += turnStartData.AverageCityLevel;
resultCache.TechCount += turnStartData.TechCount;
resultCache.TurnCount++;
}
if (resultCache.TurnCount > 0)
{
result.MatchCount++;
result.CoinPerTurn += resultCache.CoinPerTurn / resultCache.TurnCount;
result.TechPointPerTurn += resultCache.TechPointPerTurn / resultCache.TurnCount;
result.CityCount += resultCache.CityCount / resultCache.TurnCount;
result.MaxCityLevel += resultCache.MaxCityLevel / resultCache.TurnCount;
result.CityLevel += resultCache.CityLevel / resultCache.TurnCount;
result.TechCount += resultCache.TechCount / resultCache.TurnCount;
}
}
private bool MatchUnitFullType(UnitFullType unitFullType)
{
if (_unitLimit.LimitUnitType && unitFullType.UnitType != _unitLimit.UnitType)
return false;
if (_unitLimit.LimitGiantType && unitFullType.GiantType != _unitLimit.GiantType)
return false;
if (_unitLimit.LimitLevel && unitFullType.UnitLevel != _unitLimit.Level)
return false;
return true;
}
private bool MatchTech(Empire empire, TechType techType)
{
if (_techLimit.LimitEmpire && empire != _techLimit.Empire)
return false;
if (_techLimit.LimitTechType && techType != _techLimit.TechType)
return false;
return true;
}
private bool MatchEmpire(Empire empire)
{
if (_empireLimit.LimitEmpire && empire != _empireLimit.Empire)
return false;
return true;
}
}
}